[llvm] [ValueTracking] Improve `and` and `or` recurrence handling (PR #225775)

Ömer Sinan Ağacan via llvm-commits llvm-commits at lists.llvm.org
Wed Sep 23 06:51:44 PDT 2026


https://github.com/osa1 created https://github.com/llvm/llvm-project/pull/225775

None

>From f0227dd126d3dff2850dba1b1669d32a2fd5e504 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=96mer=20Sinan=20A=C4=9Facan?= <omer at osa1.net>
Date: Wed, 23 Sep 2026 14:27:06 +0100
Subject: [PATCH 1/3] Add tests

---
 .../test/Transforms/InstCombine/recurrence.ll | 118 ++++++++++++++++++
 1 file changed, 118 insertions(+)

diff --git a/llvm/test/Transforms/InstCombine/recurrence.ll b/llvm/test/Transforms/InstCombine/recurrence.ll
index 090e22c803b76..5c2b5b56f438c 100644
--- a/llvm/test/Transforms/InstCombine/recurrence.ll
+++ b/llvm/test/Transforms/InstCombine/recurrence.ll
@@ -81,6 +81,64 @@ loop:                                             ; preds = %loop, %entry
   br label %loop
 }
 
+; The mask should be removed based on Start's ones.
+define i64 @test_or_start_known_ones(i32 %s, ptr %p) {
+; CHECK-LABEL: @test_or_start_known_ones(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[S_EXT:%.*]] = zext i32 [[S:%.*]] to i64
+; CHECK-NEXT:    [[START:%.*]] = or disjoint i64 [[S_EXT]], -4294967296
+; CHECK-NEXT:    br label [[LOOP:%.*]]
+; CHECK:       loop:
+; CHECK-NEXT:    [[IV:%.*]] = phi i64 [ [[START]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ]
+; CHECK-NEXT:    [[STEP:%.*]] = load volatile i64, ptr [[P:%.*]], align 4
+; CHECK-NEXT:    [[IV_NEXT]] = or i64 [[IV]], [[STEP]]
+; CHECK-NEXT:    [[IV_NEXT_MASKED:%.*]] = or i64 [[IV_NEXT]], -4294967296
+; CHECK-NEXT:    tail call void @use(i64 [[IV_NEXT_MASKED]])
+; CHECK-NEXT:    br label [[LOOP]]
+;
+entry:
+  %s.ext = zext i32 %s to i64
+  %start = or i64 %s.ext, u0xFFFFFFFF00000000
+  br label %loop
+
+loop:
+  %iv = phi i64 [ %start, %entry ], [ %iv.next, %loop ]
+  %step = load volatile i64, ptr %p
+  %iv.next = or i64 %iv, %step
+  %iv.next.masked = or i64 %iv.next, u0xFFFFFFFF00000000
+  tail call void @use(i64 %iv.next.masked)
+  br label %loop
+}
+
+; The mask should be removed based on the known zeros of Start and Step.
+define i64 @test_or_step_known_zeros(i32 %s, ptr %p) {
+; CHECK-LABEL: @test_or_step_known_zeros(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[START:%.*]] = zext i32 [[S:%.*]] to i64
+; CHECK-NEXT:    br label [[LOOP:%.*]]
+; CHECK:       loop:
+; CHECK-NEXT:    [[IV:%.*]] = phi i64 [ [[START]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ]
+; CHECK-NEXT:    [[STEP_I32:%.*]] = load volatile i32, ptr [[P:%.*]], align 4
+; CHECK-NEXT:    [[STEP:%.*]] = zext i32 [[STEP_I32]] to i64
+; CHECK-NEXT:    [[IV_NEXT]] = or i64 [[IV]], [[STEP]]
+; CHECK-NEXT:    [[IV_NEXT_MASKED:%.*]] = and i64 [[IV]], -4294967296
+; CHECK-NEXT:    tail call void @use(i64 [[IV_NEXT_MASKED]])
+; CHECK-NEXT:    br label [[LOOP]]
+;
+entry:
+  %start = zext i32 %s to i64
+  br label %loop
+
+loop:
+  %iv = phi i64 [ %start, %entry ], [ %iv.next, %loop ]
+  %step.i32 = load volatile i32, ptr %p
+  %step = zext i32 %step.i32 to i64
+  %iv.next = or i64 %iv, %step
+  %iv.next.masked = and i64 %iv.next, u0xFFFFFFFF00000000
+  tail call void @use(i64 %iv.next.masked)
+  br label %loop
+}
+
 define i64 @test_and(i64 %a) {
 ; CHECK-LABEL: @test_and(
 ; CHECK-NEXT:  entry:
@@ -162,6 +220,66 @@ loop:                                             ; preds = %loop, %entry
   br label %loop
 }
 
+; The mask should be removed based on Start's zeros.
+define i64 @test_and_start_known_zeros(i32 %s, ptr %p) {
+; CHECK-LABEL: @test_and_start_known_zeros(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[START:%.*]] = zext i32 [[S:%.*]] to i64
+; CHECK-NEXT:    br label [[LOOP:%.*]]
+; CHECK:       loop:
+; CHECK-NEXT:    [[IV:%.*]] = phi i64 [ [[START]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ]
+; CHECK-NEXT:    [[STEP:%.*]] = load volatile i64, ptr [[P:%.*]], align 4
+; CHECK-NEXT:    [[IV_NEXT]] = and i64 [[IV]], [[STEP]]
+; CHECK-NEXT:    [[IV_NEXT_MASKED:%.*]] = and i64 [[IV_NEXT]], 4294967295
+; CHECK-NEXT:    tail call void @use(i64 [[IV_NEXT_MASKED]])
+; CHECK-NEXT:    br label [[LOOP]]
+;
+entry:
+  %start = zext i32 %s to i64
+  br label %loop
+
+loop:
+  %iv = phi i64 [ %start, %entry ], [ %iv.next, %loop ]
+  %step = load volatile i64, ptr %p
+  %iv.next = and i64 %iv, %step
+  %iv.next.masked = and i64 %iv.next, u0x00000000FFFFFFFF
+  tail call void @use(i64 %iv.next.masked)
+  br label %loop
+}
+
+; The mask should be removed based on the known ones of Start and Step.
+define i64 @test_and_step_known_ones(i32 %s, ptr %p) {
+; CHECK-LABEL: @test_and_step_known_ones(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[START_ZEXT:%.*]] = zext i32 [[S:%.*]] to i64
+; CHECK-NEXT:    [[START:%.*]] = or disjoint i64 [[START_ZEXT]], -4294967296
+; CHECK-NEXT:    br label [[LOOP:%.*]]
+; CHECK:       loop:
+; CHECK-NEXT:    [[IV:%.*]] = phi i64 [ [[START]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ]
+; CHECK-NEXT:    [[STEP_I32:%.*]] = load volatile i32, ptr [[P:%.*]], align 4
+; CHECK-NEXT:    [[STEP_ZEXT:%.*]] = zext i32 [[STEP_I32]] to i64
+; CHECK-NEXT:    [[STEP:%.*]] = or disjoint i64 [[STEP_ZEXT]], -4294967296
+; CHECK-NEXT:    [[IV_NEXT]] = and i64 [[IV]], [[STEP]]
+; CHECK-NEXT:    [[IV_NEXT_MASKED:%.*]] = or i64 [[IV_NEXT]], -4294967296
+; CHECK-NEXT:    tail call void @use(i64 [[IV_NEXT_MASKED]])
+; CHECK-NEXT:    br label [[LOOP]]
+;
+entry:
+  %start.zext = zext i32 %s to i64
+  %start = or i64 %start.zext, u0xFFFFFFFF00000000
+  br label %loop
+
+loop:
+  %iv = phi i64 [ %start, %entry ], [ %iv.next, %loop ]
+  %step.i32 = load volatile i32, ptr %p
+  %step.zext = zext i32 %step.i32 to i64
+  %step = or i64 %step.zext, u0xFFFFFFFF00000000
+  %iv.next = and i64 %iv, %step
+  %iv.next.masked = or i64 %iv.next, u0xFFFFFFFF00000000
+  tail call void @use(i64 %iv.next.masked)
+  br label %loop
+}
+
 declare i32 @get_step()
 
 define i1 @test_loop_variant_step_with_condition() {

>From 9d1a270877ae5eb8e850ffbf930d6852943ce61b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=96mer=20Sinan=20A=C4=9Facan?= <omer at osa1.net>
Date: Wed, 23 Sep 2026 14:27:53 +0100
Subject: [PATCH 2/3] Improve analysis

---
 llvm/lib/Analysis/ValueTracking.cpp | 26 ++++++++++++++++++++++++--
 1 file changed, 24 insertions(+), 2 deletions(-)

diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index ed1e2f7467e19..2fdf3c4154dd1 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -1899,13 +1899,35 @@ static void computeKnownBitsFromOperator(const Operator *I,
         break;
       }
 
+      case Instruction::And: {
+        // Start's zeros will always stay zero, ones can change.
+        KnownBits KnownStep(BitWidth);
+        computeKnownBitsForRecurrenceOperands(P, Start, Step, DemandedElts,
+                                              KnownStart, KnownStep, Q, Depth);
+
+        Known.Zero = KnownStart.Zero;
+        Known.One = KnownStart.One & KnownStep.One;
+
+        break;
+      }
+
+      case Instruction::Or: {
+        // Start's ones will always stay one, zeros can change.
+        KnownBits KnownStep(BitWidth);
+        computeKnownBitsForRecurrenceOperands(P, Start, Step, DemandedElts,
+                                              KnownStart, KnownStep, Q, Depth);
+
+        Known.One = KnownStart.One;
+        Known.Zero = KnownStart.Zero & KnownStep.Zero;
+
+        break;
+      }
+
       // Check for operations that have the property that if
       // both their operands have low zero bits, the result
       // will have low zero bits.
       case Instruction::Add:
       case Instruction::Sub:
-      case Instruction::And:
-      case Instruction::Or:
       case Instruction::Mul: {
         // Ok, we have a recurrence of the form {Start,op,Step}. Check for low
         // zero bits.

>From 5f656770a588d21c12d1e0f69a60c553359dca94 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=96mer=20Sinan=20A=C4=9Facan?= <omer at osa1.net>
Date: Wed, 23 Sep 2026 14:28:23 +0100
Subject: [PATCH 3/3] Update tests

---
 llvm/test/Transforms/InstCombine/recurrence.ll | 16 ++++------------
 1 file changed, 4 insertions(+), 12 deletions(-)

diff --git a/llvm/test/Transforms/InstCombine/recurrence.ll b/llvm/test/Transforms/InstCombine/recurrence.ll
index 5c2b5b56f438c..853e178a86a44 100644
--- a/llvm/test/Transforms/InstCombine/recurrence.ll
+++ b/llvm/test/Transforms/InstCombine/recurrence.ll
@@ -92,8 +92,7 @@ define i64 @test_or_start_known_ones(i32 %s, ptr %p) {
 ; CHECK-NEXT:    [[IV:%.*]] = phi i64 [ [[START]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ]
 ; CHECK-NEXT:    [[STEP:%.*]] = load volatile i64, ptr [[P:%.*]], align 4
 ; CHECK-NEXT:    [[IV_NEXT]] = or i64 [[IV]], [[STEP]]
-; CHECK-NEXT:    [[IV_NEXT_MASKED:%.*]] = or i64 [[IV_NEXT]], -4294967296
-; CHECK-NEXT:    tail call void @use(i64 [[IV_NEXT_MASKED]])
+; CHECK-NEXT:    tail call void @use(i64 [[IV_NEXT]])
 ; CHECK-NEXT:    br label [[LOOP]]
 ;
 entry:
@@ -114,15 +113,10 @@ loop:
 define i64 @test_or_step_known_zeros(i32 %s, ptr %p) {
 ; CHECK-LABEL: @test_or_step_known_zeros(
 ; CHECK-NEXT:  entry:
-; CHECK-NEXT:    [[START:%.*]] = zext i32 [[S:%.*]] to i64
 ; CHECK-NEXT:    br label [[LOOP:%.*]]
 ; CHECK:       loop:
-; CHECK-NEXT:    [[IV:%.*]] = phi i64 [ [[START]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ]
 ; CHECK-NEXT:    [[STEP_I32:%.*]] = load volatile i32, ptr [[P:%.*]], align 4
-; CHECK-NEXT:    [[STEP:%.*]] = zext i32 [[STEP_I32]] to i64
-; CHECK-NEXT:    [[IV_NEXT]] = or i64 [[IV]], [[STEP]]
-; CHECK-NEXT:    [[IV_NEXT_MASKED:%.*]] = and i64 [[IV]], -4294967296
-; CHECK-NEXT:    tail call void @use(i64 [[IV_NEXT_MASKED]])
+; CHECK-NEXT:    tail call void @use(i64 0)
 ; CHECK-NEXT:    br label [[LOOP]]
 ;
 entry:
@@ -230,8 +224,7 @@ define i64 @test_and_start_known_zeros(i32 %s, ptr %p) {
 ; CHECK-NEXT:    [[IV:%.*]] = phi i64 [ [[START]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ]
 ; CHECK-NEXT:    [[STEP:%.*]] = load volatile i64, ptr [[P:%.*]], align 4
 ; CHECK-NEXT:    [[IV_NEXT]] = and i64 [[IV]], [[STEP]]
-; CHECK-NEXT:    [[IV_NEXT_MASKED:%.*]] = and i64 [[IV_NEXT]], 4294967295
-; CHECK-NEXT:    tail call void @use(i64 [[IV_NEXT_MASKED]])
+; CHECK-NEXT:    tail call void @use(i64 [[IV_NEXT]])
 ; CHECK-NEXT:    br label [[LOOP]]
 ;
 entry:
@@ -260,8 +253,7 @@ define i64 @test_and_step_known_ones(i32 %s, ptr %p) {
 ; CHECK-NEXT:    [[STEP_ZEXT:%.*]] = zext i32 [[STEP_I32]] to i64
 ; CHECK-NEXT:    [[STEP:%.*]] = or disjoint i64 [[STEP_ZEXT]], -4294967296
 ; CHECK-NEXT:    [[IV_NEXT]] = and i64 [[IV]], [[STEP]]
-; CHECK-NEXT:    [[IV_NEXT_MASKED:%.*]] = or i64 [[IV_NEXT]], -4294967296
-; CHECK-NEXT:    tail call void @use(i64 [[IV_NEXT_MASKED]])
+; CHECK-NEXT:    tail call void @use(i64 [[IV_NEXT]])
 ; CHECK-NEXT:    br label [[LOOP]]
 ;
 entry:



More information about the llvm-commits mailing list