[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