[llvm] [ConstraintElimination] Scale comparisons against exact right shifts (PR #223212)
via llvm-commits
llvm-commits at lists.llvm.org
Sat Sep 12 23:28:05 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-llvm-transforms
Author: Alina Sbirlea (alinas)
<details>
<summary>Changes</summary>
When forming constraints for relational comparisons where an operand is an exact right shift (e.g. X < (Sub >> k)), scale the comparison by multiplying the opposite side by (1 << k), yielding (1 << k) * X < Sub. If both operands share the same exact right shift, decompose the unshifted sub-expressions directly.
Added lit tests in llvm/test/Transforms/ConstraintElimination/exact-shr-scaling.ll verifying comparisons with exact shifts on RHS, LHS, and both sides.
Gemini assisted changes.
---
Full diff: https://github.com/llvm/llvm-project/pull/223212.diff
2 Files Affected:
- (modified) llvm/lib/Transforms/Scalar/ConstraintElimination.cpp (+34)
- (added) llvm/test/Transforms/ConstraintElimination/exact-shr-scaling.ll (+59)
``````````diff
diff --git a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
index ed95686e9e5d9..0c613593cfe14 100644
--- a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
+++ b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
@@ -796,6 +796,40 @@ ConstraintInfo::getConstraint(CmpInst::Predicate Pred, Value *Op0, Value *Op1,
IsSigned, DL);
auto BDec = decompose(Op1->stripPointerCastsSameRepresentation(), *this,
IsSigned, DL);
+
+ // For relational comparisons where an operand is an exact right shift
+ // (e.g. X < (Sub >> k)), scale the comparison by multiplying the opposite
+ // side by (1 << k), yielding (1 << k) * X < Sub. If both operands share the
+ // same exact right shift, decompose the unshifted sub-expressions directly.
+ Value *Sub0 = nullptr, *Sub1 = nullptr;
+ ConstantInt *Shift0 = nullptr, *Shift1 = nullptr;
+ bool IsShift0 =
+ match(Op0, m_Exact(m_Shr(m_Value(Sub0), m_ConstantInt(Shift0)))) &&
+ canUseSExt(Shift0);
+ bool IsShift1 =
+ match(Op1, m_Exact(m_Shr(m_Value(Sub1), m_ConstantInt(Shift1)))) &&
+ canUseSExt(Shift1);
+ if (IsShift0 && IsShift1 && Shift0->getValue() == Shift1->getValue()) {
+ ADec = decompose(Sub0->stripPointerCastsSameRepresentation(), *this,
+ IsSigned, DL);
+ BDec = decompose(Sub1->stripPointerCastsSameRepresentation(), *this,
+ IsSigned, DL);
+ } else if (IsShift1 && Shift1->getSExtValue() > 0 &&
+ Shift1->getSExtValue() < 63) {
+ int64_t Factor = int64_t(1) << Shift1->getSExtValue();
+ if (BDec.Vars.size() <= 1 && !ADec.mul(Factor)) {
+ BDec = decompose(Sub1->stripPointerCastsSameRepresentation(), *this,
+ IsSigned, DL);
+ }
+ } else if (IsShift0 && Shift0->getSExtValue() > 0 &&
+ Shift0->getSExtValue() < 63) {
+ int64_t Factor = int64_t(1) << Shift0->getSExtValue();
+ if (ADec.Vars.size() <= 1 && !BDec.mul(Factor)) {
+ ADec = decompose(Sub0->stripPointerCastsSameRepresentation(), *this,
+ IsSigned, DL);
+ }
+ }
+
int64_t Offset1 = ADec.Offset;
int64_t Offset2 = BDec.Offset;
if (MulOverflow(Offset1, int64_t(-1), Offset1))
diff --git a/llvm/test/Transforms/ConstraintElimination/exact-shr-scaling.ll b/llvm/test/Transforms/ConstraintElimination/exact-shr-scaling.ll
new file mode 100644
index 0000000000000..9958f90cb97e7
--- /dev/null
+++ b/llvm/test/Transforms/ConstraintElimination/exact-shr-scaling.ll
@@ -0,0 +1,59 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt -passes=constraint-elimination -S %s | FileCheck %s
+
+declare void @llvm.assume(i1 noundef)
+
+; When one operand is an exact right shift (Sub >> k), scale comparison by multiplying
+; the opposite side by (1 << k), proving iv < (Sub >> 2) when (4 * iv) < Sub.
+define i1 @test_scale_rhs_exact_shift(i64 %iv, i64 %sub) {
+; CHECK-LABEL: define i1 @test_scale_rhs_exact_shift(
+; CHECK-SAME: i64 [[IV:%.*]], i64 [[SUB:%.*]]) {
+; CHECK-NEXT: [[SCALED:%.*]] = mul nuw i64 [[IV]], 4
+; CHECK-NEXT: [[C:%.*]] = icmp ult i64 [[SCALED]], [[SUB]]
+; CHECK-NEXT: call void @llvm.assume(i1 [[C]])
+; CHECK-NEXT: [[SIZE:%.*]] = ashr exact i64 [[SUB]], 2
+; CHECK-NEXT: ret i1 true
+;
+ %scaled = mul nuw i64 %iv, 4
+ %c = icmp ult i64 %scaled, %sub
+ call void @llvm.assume(i1 %c)
+ %size = ashr exact i64 %sub, 2
+ %check = icmp ult i64 %iv, %size
+ ret i1 %check
+}
+
+; Both operands have matching exact right shift: Sub0 < Sub1 implies (Sub0 >> 2) < (Sub1 >> 2).
+define i1 @test_scale_both_exact_shifts(i64 %sub0, i64 %sub1) {
+; CHECK-LABEL: define i1 @test_scale_both_exact_shifts(
+; CHECK-SAME: i64 [[SUB0:%.*]], i64 [[SUB1:%.*]]) {
+; CHECK-NEXT: [[C:%.*]] = icmp ult i64 [[SUB0]], [[SUB1]]
+; CHECK-NEXT: call void @llvm.assume(i1 [[C]])
+; CHECK-NEXT: [[SH0:%.*]] = ashr exact i64 [[SUB0]], 2
+; CHECK-NEXT: [[SH1:%.*]] = ashr exact i64 [[SUB1]], 2
+; CHECK-NEXT: ret i1 true
+;
+ %c = icmp ult i64 %sub0, %sub1
+ call void @llvm.assume(i1 %c)
+ %sh0 = ashr exact i64 %sub0, 2
+ %sh1 = ashr exact i64 %sub1, 2
+ %check = icmp ult i64 %sh0, %sh1
+ ret i1 %check
+}
+
+; Exact shift on LHS: (Sub >> 2) <= iv implies Sub <= (4 * iv).
+define i1 @test_scale_lhs_exact_shift(i64 %iv, i64 %sub) {
+; CHECK-LABEL: define i1 @test_scale_lhs_exact_shift(
+; CHECK-SAME: i64 [[IV:%.*]], i64 [[SUB:%.*]]) {
+; CHECK-NEXT: [[SIZE:%.*]] = ashr exact i64 [[SUB]], 2
+; CHECK-NEXT: [[C:%.*]] = icmp ule i64 [[SIZE]], [[IV]]
+; CHECK-NEXT: call void @llvm.assume(i1 [[C]])
+; CHECK-NEXT: [[SCALED:%.*]] = mul nuw i64 [[IV]], 4
+; CHECK-NEXT: ret i1 true
+;
+ %size = ashr exact i64 %sub, 2
+ %c = icmp ule i64 %size, %iv
+ call void @llvm.assume(i1 %c)
+ %scaled = mul nuw i64 %iv, 4
+ %check = icmp ule i64 %sub, %scaled
+ ret i1 %check
+}
``````````
</details>
https://github.com/llvm/llvm-project/pull/223212
More information about the llvm-commits
mailing list