[llvm] [ConstraintElimination] Exact right shifts extension (PR #223207)
Alina Sbirlea via llvm-commits
llvm-commits at lists.llvm.org
Sat Sep 12 22:49:22 PDT 2026
https://github.com/alinas created https://github.com/llvm/llvm-project/pull/223207
Decompose exact right shifts when terms are divisible by shift factor
Extend ConstraintElimination::decompose() to look through exact right shifts (ashr exact / lshr exact) when the decomposed operand's constant offset and variable coefficients are all evenly divisible by the shift factor (1 << Shift). For example, (4 * a + 8) >> 2 decomposes into a + 2, allowing comparisons like ((4 * a + 8) >> 2) > a to be proven and folded to true.
Added lit tests in llvm/test/Transforms/ConstraintElimination/exact-shr.ll covering positive folds and negative cases (non-divisible coefficients, non-divisible offsets, and non-exact shifts).
Gemini assisted change.
>From 5e09ac80f236106eb62c36b374842fe59cecdd0d Mon Sep 17 00:00:00 2001
From: Alina Sbirlea <asbirlea at google.com>
Date: Sun, 13 Sep 2026 03:57:55 +0000
Subject: [PATCH] [ConstraintElimination] Decompose exact right shifts when
terms are divisible by shift factor
Extend ConstraintElimination::decompose() to look through exact right shifts
(ashr exact / lshr exact) when the decomposed operand's constant offset and
variable coefficients are all evenly divisible by the shift factor (1 << Shift).
For example, (4 * a + 8) >> 2 decomposes into a + 2, allowing comparisons like
((4 * a + 8) >> 2) > a to be proven and folded to true.
Added lit tests in llvm/test/Transforms/ConstraintElimination/exact-shr.ll
covering positive folds and negative cases (non-divisible coefficients,
non-divisible offsets, and non-exact shifts).
---
.../Scalar/ConstraintElimination.cpp | 18 ++++
.../ConstraintElimination/exact-shr.ll | 97 +++++++++++++++++++
2 files changed, 115 insertions(+)
create mode 100644 llvm/test/Transforms/ConstraintElimination/exact-shr.ll
diff --git a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
index ed95686e9e5d9..c6849621bb20f 100644
--- a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
+++ b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
@@ -728,6 +728,24 @@ static Decomposition decompose(Value *V, const ConstraintInfo &Info,
return V;
}
+ if (match(V, m_Exact(m_Shr(m_Value(Op0), m_ConstantInt(CI)))) &&
+ canUseSExt(CI)) {
+ if (CI->getSExtValue() >= 0 && CI->getSExtValue() < 63) {
+ int64_t Shift = CI->getSExtValue();
+ int64_t Factor = int64_t(1) << Shift;
+ auto Result = decompose(Op0, Info, IsSigned, DL);
+ if (Result.Offset % Factor == 0 &&
+ all_of(Result.Vars, [Factor](const DecompEntry &E) {
+ return E.Coefficient % Factor == 0;
+ })) {
+ Result.Offset /= Factor;
+ for (auto &E : Result.Vars)
+ E.Coefficient /= Factor;
+ return Result;
+ }
+ }
+ }
+
if (match(V, m_Sub(m_Value(Op0), m_Value(Op1)))) {
// a - b can be decomposed when there is no unsigned wrap (either known via
// flag or proven as precondition).
diff --git a/llvm/test/Transforms/ConstraintElimination/exact-shr.ll b/llvm/test/Transforms/ConstraintElimination/exact-shr.ll
new file mode 100644
index 0000000000000..b73518da260e4
--- /dev/null
+++ b/llvm/test/Transforms/ConstraintElimination/exact-shr.ll
@@ -0,0 +1,97 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt -passes=constraint-elimination -S %s | FileCheck %s
+
+; Tests for exact right-shift decomposition in ConstraintElimination::decompose().
+; Uses an addition (%add2 = add nuw %shift, 1) to ensure the fold relies on
+; decomposing %shift directly rather than comparison scaling.
+
+define i1 @test_exact_lshr_decompose_mul_add(i64 %a) {
+; CHECK-LABEL: define i1 @test_exact_lshr_decompose_mul_add(
+; CHECK-SAME: i64 [[A:%.*]]) {
+; CHECK-NEXT: [[MUL:%.*]] = mul nuw i64 [[A]], 4
+; CHECK-NEXT: [[ADD:%.*]] = add nuw i64 [[MUL]], 8
+; CHECK-NEXT: [[SHIFT:%.*]] = lshr exact i64 [[ADD]], 2
+; CHECK-NEXT: [[ADD2:%.*]] = add nuw i64 [[SHIFT]], 1
+; CHECK-NEXT: ret i1 true
+;
+ %mul = mul nuw i64 %a, 4
+ %add = add nuw i64 %mul, 8
+ %shift = lshr exact i64 %add, 2
+ %add2 = add nuw i64 %shift, 1
+ %cmp = icmp ugt i64 %add2, %a
+ ret i1 %cmp
+}
+
+define i1 @test_exact_ashr_decompose(i64 %a) {
+; CHECK-LABEL: define i1 @test_exact_ashr_decompose(
+; CHECK-SAME: i64 [[A:%.*]]) {
+; CHECK-NEXT: [[MUL:%.*]] = mul nuw i64 [[A]], 8
+; CHECK-NEXT: [[ADD:%.*]] = add nuw i64 [[MUL]], 16
+; CHECK-NEXT: [[SHIFT:%.*]] = ashr exact i64 [[ADD]], 3
+; CHECK-NEXT: [[ADD2:%.*]] = add nuw i64 [[SHIFT]], 1
+; CHECK-NEXT: ret i1 true
+;
+ %mul = mul nuw i64 %a, 8
+ %add = add nuw i64 %mul, 16
+ %shift = ashr exact i64 %add, 3
+ %add2 = add nuw i64 %shift, 1
+ %cmp = icmp ugt i64 %add2, %a
+ ret i1 %cmp
+}
+
+; Negative: coefficient not divisible by shift factor (2 % 4 != 0)
+define i1 @test_exact_shr_coeff_not_divisible(i64 %a) {
+; CHECK-LABEL: define i1 @test_exact_shr_coeff_not_divisible(
+; CHECK-SAME: i64 [[A:%.*]]) {
+; CHECK-NEXT: [[MUL:%.*]] = mul nuw i64 [[A]], 2
+; CHECK-NEXT: [[ADD:%.*]] = add nuw i64 [[MUL]], 8
+; CHECK-NEXT: [[SHIFT:%.*]] = lshr exact i64 [[ADD]], 2
+; CHECK-NEXT: [[ADD2:%.*]] = add nuw i64 [[SHIFT]], 1
+; CHECK-NEXT: [[CMP:%.*]] = icmp ugt i64 [[ADD2]], [[A]]
+; CHECK-NEXT: ret i1 [[CMP]]
+;
+ %mul = mul nuw i64 %a, 2
+ %add = add nuw i64 %mul, 8
+ %shift = lshr exact i64 %add, 2
+ %add2 = add nuw i64 %shift, 1
+ %cmp = icmp ugt i64 %add2, %a
+ ret i1 %cmp
+}
+
+; Negative: offset not divisible by shift factor (7 % 4 != 0)
+define i1 @test_exact_shr_offset_not_divisible(i64 %a) {
+; CHECK-LABEL: define i1 @test_exact_shr_offset_not_divisible(
+; CHECK-SAME: i64 [[A:%.*]]) {
+; CHECK-NEXT: [[MUL:%.*]] = mul nuw i64 [[A]], 4
+; CHECK-NEXT: [[ADD:%.*]] = add nuw i64 [[MUL]], 7
+; CHECK-NEXT: [[SHIFT:%.*]] = lshr exact i64 [[ADD]], 2
+; CHECK-NEXT: [[ADD2:%.*]] = add nuw i64 [[SHIFT]], 1
+; CHECK-NEXT: [[CMP:%.*]] = icmp ugt i64 [[ADD2]], [[A]]
+; CHECK-NEXT: ret i1 [[CMP]]
+;
+ %mul = mul nuw i64 %a, 4
+ %add = add nuw i64 %mul, 7
+ %shift = lshr exact i64 %add, 2
+ %add2 = add nuw i64 %shift, 1
+ %cmp = icmp ugt i64 %add2, %a
+ ret i1 %cmp
+}
+
+; Negative: shift is not exact
+define i1 @test_shr_not_exact(i64 %a) {
+; CHECK-LABEL: define i1 @test_shr_not_exact(
+; CHECK-SAME: i64 [[A:%.*]]) {
+; CHECK-NEXT: [[MUL:%.*]] = mul nuw i64 [[A]], 4
+; CHECK-NEXT: [[ADD:%.*]] = add nuw i64 [[MUL]], 8
+; CHECK-NEXT: [[SHIFT:%.*]] = lshr i64 [[ADD]], 2
+; CHECK-NEXT: [[ADD2:%.*]] = add nuw i64 [[SHIFT]], 1
+; CHECK-NEXT: [[CMP:%.*]] = icmp ugt i64 [[ADD2]], [[A]]
+; CHECK-NEXT: ret i1 [[CMP]]
+;
+ %mul = mul nuw i64 %a, 4
+ %add = add nuw i64 %mul, 8
+ %shift = lshr i64 %add, 2
+ %add2 = add nuw i64 %shift, 1
+ %cmp = icmp ugt i64 %add2, %a
+ ret i1 %cmp
+}
More information about the llvm-commits
mailing list