[llvm] 37ecc8c - [ValueTracking] Compute known bits of and/or recurrences from start and step (#226164)
via llvm-commits
llvm-commits at lists.llvm.org
Tue Oct 6 04:53:05 PDT 2026
Author: Konstantin Bogdanov
Date: 2026-10-06T12:52:58+01:00
New Revision: 37ecc8c1b63e55cfcaea5dde492c2fec2653d6c4
URL: https://github.com/llvm/llvm-project/commit/37ecc8c1b63e55cfcaea5dde492c2fec2653d6c4
DIFF: https://github.com/llvm/llvm-project/commit/37ecc8c1b63e55cfcaea5dde492c2fec2653d6c4.diff
LOG: [ValueTracking] Compute known bits of and/or recurrences from start and step (#226164)
For simple phi recurrences of the form `%iv = %iv op %step` we currently
only derive trailing zero bits for and/or, in a case shared with
add/sub/mul. This moves and/or into their own case and propagates full
known bits:
* or: bits that are zero in both the start value and the step stay zero,
and bits that are one in the start value stay one.
* and: bits that are zero in the start value stay zero, and bits that
are one in both the start value and the step stay one.
The step only applies from the second iteration on, so every fact must
also hold for the start value alone. This subsumes the trailing-zeros
rule for these operations. The nsw handling of the add/sub/mul case
never applied to them.
The two AMDGPU tests are adjusted to use an opaque start value for their
recurrences, so that the improved known bits don't fold away the
patterns they were written to test.
The first commit precommits the InstCombine tests.
Split out of #222334. This overlaps with #225775.
Added:
Modified:
llvm/lib/Analysis/ValueTracking.cpp
llvm/test/CodeGen/AMDGPU/GlobalISel/is-safe-to-sink-bug.ll
llvm/test/CodeGen/AMDGPU/absdiff.ll
llvm/test/Transforms/InstCombine/recurrence.ll
Removed:
################################################################################
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index f68bd0ded7151..4fe9bda1ce67a 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -1919,13 +1919,33 @@ static void computeKnownBitsFromOperator(const Operator *I,
break;
}
+ case Instruction::And: {
+ // Bits that are zero in the start value stay zero, and bits that are
+ // one in both the start value and the step stay one.
+ 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: {
+ // Bits that are zero in both the start value and the step stay zero,
+ // and bits that are one in the start value stay one.
+ KnownBits KnownStep(BitWidth);
+ computeKnownBitsForRecurrenceOperands(P, Start, Step, DemandedElts,
+ KnownStart, KnownStep, Q, Depth);
+ Known.Zero |= KnownStart.Zero & KnownStep.Zero;
+ Known.One |= KnownStart.One;
+ 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.
diff --git a/llvm/test/CodeGen/AMDGPU/GlobalISel/is-safe-to-sink-bug.ll b/llvm/test/CodeGen/AMDGPU/GlobalISel/is-safe-to-sink-bug.ll
index e99eff192a001..46591052abc11 100644
--- a/llvm/test/CodeGen/AMDGPU/GlobalISel/is-safe-to-sink-bug.ll
+++ b/llvm/test/CodeGen/AMDGPU/GlobalISel/is-safe-to-sink-bug.ll
@@ -26,27 +26,26 @@ define amdgpu_ps void @_amdgpu_ps_main(i1 %arg) {
; CHECK-NEXT: s_cmp_ge_i32 s1, 0
; CHECK-NEXT: s_cbranch_scc1 .LBB0_5
; CHECK-NEXT: ; %bb.1: ; %bb2.preheader
-; CHECK-NEXT: s_mov_b32 s3, 0
-; CHECK-NEXT: s_mov_b32 s1, 0
; CHECK-NEXT: s_mov_b32 s2, 0
+; CHECK-NEXT: s_mov_b32 s3, 0
; CHECK-NEXT: s_branch .LBB0_3
; CHECK-NEXT: .p2align 6
; CHECK-NEXT: .LBB0_2: ; %bb6
; CHECK-NEXT: ; in Loop: Header=BB0_3 Depth=1
; CHECK-NEXT: s_or_b32 exec_lo, exec_lo, s0
; CHECK-NEXT: v_cmp_ne_u32_e64 s0, 0, v0
-; CHECK-NEXT: s_or_b32 s4, s3, 1
-; CHECK-NEXT: s_and_b32 s0, s0, s2
-; CHECK-NEXT: s_cmp_lt_i32 s3, 0
-; CHECK-NEXT: s_mov_b32 s3, s4
+; CHECK-NEXT: s_or_b32 s4, s1, 1
+; CHECK-NEXT: s_and_b32 s0, s0, s3
+; CHECK-NEXT: s_cmp_lt_i32 s1, 0
; CHECK-NEXT: s_cselect_b32 s5, 1, 0
-; CHECK-NEXT: s_andn2_b32 s2, s2, exec_lo
-; CHECK-NEXT: s_or_b32 s2, s2, s0
+; CHECK-NEXT: s_andn2_b32 s1, s3, exec_lo
+; CHECK-NEXT: s_or_b32 s3, s1, s0
+; CHECK-NEXT: s_mov_b32 s1, s4
; CHECK-NEXT: s_cmp_lg_u32 s5, 0
; CHECK-NEXT: s_cbranch_scc0 .LBB0_5
; CHECK-NEXT: .LBB0_3: ; %bb2
; CHECK-NEXT: ; =>This Inner Loop Header: Depth=1
-; CHECK-NEXT: v_mov_b32_e32 v0, s1
+; CHECK-NEXT: v_mov_b32_e32 v0, s2
; CHECK-NEXT: s_and_saveexec_b32 s0, vcc_lo
; CHECK-NEXT: s_cbranch_execz .LBB0_2
; CHECK-NEXT: ; %bb.4: ; %bb5
@@ -70,7 +69,7 @@ bb:
bb2:
%i3 = phi i1 [ %i9, %bb6 ], [ false, %bb ]
- %i4 = phi i32 [ %i10, %bb6 ], [ 0, %bb ]
+ %i4 = phi i32 [ %i10, %bb6 ], [ %i, %bb ]
br i1 %arg, label %bb5, label %bb6
bb5:
diff --git a/llvm/test/CodeGen/AMDGPU/abs
diff .ll b/llvm/test/CodeGen/AMDGPU/abs
diff .ll
index d97a16ff189c1..6670459880ad2 100644
--- a/llvm/test/CodeGen/AMDGPU/abs
diff .ll
+++ b/llvm/test/CodeGen/AMDGPU/abs
diff .ll
@@ -2,18 +2,17 @@
; RUN: llc -mtriple=amdgpu9.00-amd-amdpal < %s | FileCheck %s
-define amdgpu_gs float @abs
diff _valu_input_regression() {
+define amdgpu_gs float @abs
diff _valu_input_regression(i32 inreg %start) {
; CHECK-LABEL: abs
diff _valu_input_regression:
; CHECK: ; %bb.0: ; %bb
-; CHECK-NEXT: s_mov_b32 s1, 0
; CHECK-NEXT: .LBB0_1: ; %bb1
; CHECK-NEXT: ; =>This Inner Loop Header: Depth=1
-; CHECK-NEXT: s_mov_b32 s0, s1
-; CHECK-NEXT: s_or_b32 s1, s1, 1
-; CHECK-NEXT: s_cmp_gt_i32 s0, 0
+; CHECK-NEXT: s_mov_b32 s1, s0
+; CHECK-NEXT: s_or_b32 s0, s0, 1
+; CHECK-NEXT: s_cmp_gt_i32 s1, 0
; CHECK-NEXT: s_cbranch_scc1 .LBB0_1
; CHECK-NEXT: ; %bb.2: ; %bb11
-; CHECK-NEXT: v_med3_i32 v0, s0, 0, 1
+; CHECK-NEXT: v_med3_i32 v0, s1, 0, 1
; CHECK-NEXT: v_sub_u32_e32 v0, 0, v0
; CHECK-NEXT: v_sub_u32_e32 v1, 0, v0
; CHECK-NEXT: v_max_i32_e32 v0, v0, v1
@@ -22,7 +21,7 @@ bb:
br label %bb1
bb1: ; preds = %bb1, %bb
- %i = phi i32 [ 0, %bb ], [ %i9, %bb1 ]
+ %i = phi i32 [ %start, %bb ], [ %i9, %bb1 ]
%i2 = phi i32 [ 0, %bb ], [ %i5, %bb1 ]
%i3 = or i32 %i2, 1
%i4 = or i32 %i3, 0
diff --git a/llvm/test/Transforms/InstCombine/recurrence.ll b/llvm/test/Transforms/InstCombine/recurrence.ll
index 090e22c803b76..11918ed51f55d 100644
--- a/llvm/test/Transforms/InstCombine/recurrence.ll
+++ b/llvm/test/Transforms/InstCombine/recurrence.ll
@@ -570,3 +570,169 @@ exit:
}
declare void @use(i64)
+
+; The high bits of an or recurrence are known zero if they are zero in both
+; the start value and the step.
+define i32 @test_or_known_zero_high_bits(ptr %p, i64 %n) {
+;
+; CHECK-LABEL: @test_or_known_zero_high_bits(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: br label [[LOOP:%.*]]
+; CHECK: loop:
+; CHECK-NEXT: [[IV:%.*]] = phi i64 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ]
+; CHECK-NEXT: [[ACC:%.*]] = phi i32 [ 0, [[ENTRY]] ], [ [[ACC_NEXT:%.*]], [[LOOP]] ]
+; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds i8, ptr [[P:%.*]], i64 [[IV]]
+; CHECK-NEXT: [[X:%.*]] = load i8, ptr [[GEP]], align 1
+; CHECK-NEXT: [[X_EXT:%.*]] = zext i8 [[X]] to i32
+; CHECK-NEXT: [[ACC_NEXT]] = or i32 [[ACC]], [[X_EXT]]
+; CHECK-NEXT: [[IV_NEXT]] = add i64 [[IV]], 1
+; CHECK-NEXT: [[EC:%.*]] = icmp eq i64 [[IV_NEXT]], [[N:%.*]]
+; CHECK-NEXT: br i1 [[EC]], label [[EXIT:%.*]], label [[LOOP]]
+; CHECK: exit:
+; CHECK-NEXT: ret i32 [[ACC_NEXT]]
+;
+entry:
+ br label %loop
+
+loop:
+ %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop ]
+ %acc = phi i32 [ 0, %entry ], [ %acc.next, %loop ]
+ %gep = getelementptr inbounds i8, ptr %p, i64 %iv
+ %x = load i8, ptr %gep
+ %x.ext = zext i8 %x to i32
+ %acc.next = or i32 %acc, %x.ext
+ %iv.next = add i64 %iv, 1
+ %ec = icmp eq i64 %iv.next, %n
+ br i1 %ec, label %exit, label %loop
+
+exit:
+ %masked = and i32 %acc.next, 255
+ ret i32 %masked
+}
+
+; Negative test: the start value has unknown high bits.
+define i32 @test_or_unknown_start(ptr %p, i64 %n, i32 %start) {
+;
+; CHECK-LABEL: @test_or_unknown_start(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: br label [[LOOP:%.*]]
+; CHECK: loop:
+; CHECK-NEXT: [[IV:%.*]] = phi i64 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ]
+; CHECK-NEXT: [[ACC:%.*]] = phi i32 [ [[START:%.*]], [[ENTRY]] ], [ [[ACC_NEXT:%.*]], [[LOOP]] ]
+; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds i8, ptr [[P:%.*]], i64 [[IV]]
+; CHECK-NEXT: [[X:%.*]] = load i8, ptr [[GEP]], align 1
+; CHECK-NEXT: [[X_EXT:%.*]] = zext i8 [[X]] to i32
+; CHECK-NEXT: [[ACC_NEXT]] = or i32 [[ACC]], [[X_EXT]]
+; CHECK-NEXT: [[IV_NEXT]] = add i64 [[IV]], 1
+; CHECK-NEXT: [[EC:%.*]] = icmp eq i64 [[IV_NEXT]], [[N:%.*]]
+; CHECK-NEXT: br i1 [[EC]], label [[EXIT:%.*]], label [[LOOP]]
+; CHECK: exit:
+; CHECK-NEXT: [[MASKED:%.*]] = and i32 [[ACC_NEXT]], 255
+; CHECK-NEXT: ret i32 [[MASKED]]
+;
+entry:
+ br label %loop
+
+loop:
+ %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop ]
+ %acc = phi i32 [ %start, %entry ], [ %acc.next, %loop ]
+ %gep = getelementptr inbounds i8, ptr %p, i64 %iv
+ %x = load i8, ptr %gep
+ %x.ext = zext i8 %x to i32
+ %acc.next = or i32 %acc, %x.ext
+ %iv.next = add i64 %iv, 1
+ %ec = icmp eq i64 %iv.next, %n
+ br i1 %ec, label %exit, label %loop
+
+exit:
+ %masked = and i32 %acc.next, 255
+ ret i32 %masked
+}
+
+; Bits that are one in the start value stay one in an or recurrence, but bits
+; that are only one in the step do not apply to the first iteration.
+define i64 @test_or_known_one_start(i64 %a) {
+;
+; CHECK-LABEL: @test_or_known_one_start(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: br label [[LOOP:%.*]]
+; CHECK: loop:
+; CHECK-NEXT: tail call void @use(i64 1)
+; CHECK-NEXT: br label [[LOOP]]
+;
+entry:
+ br label %loop
+
+loop:
+ %iv = phi i64 [ 1, %entry ], [ %iv.next, %loop ]
+ %iv.next = or i64 %iv, %a
+ %bit0 = and i64 %iv, 1
+ tail call void @use(i64 %bit0)
+ br label %loop
+}
+
+; The high bits of an and recurrence are known zero if they are zero in the
+; start value.
+define i32 @test_and_known_zero_start(ptr %p, i64 %n) {
+;
+; CHECK-LABEL: @test_and_known_zero_start(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: br label [[LOOP:%.*]]
+; CHECK: loop:
+; CHECK-NEXT: [[IV:%.*]] = phi i64 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ]
+; CHECK-NEXT: [[ACC:%.*]] = phi i32 [ 255, [[ENTRY]] ], [ [[ACC_NEXT:%.*]], [[LOOP]] ]
+; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds [4 x i8], ptr [[P:%.*]], i64 [[IV]]
+; CHECK-NEXT: [[X:%.*]] = load i32, ptr [[GEP]], align 4
+; CHECK-NEXT: [[ACC_NEXT]] = and i32 [[ACC]], [[X]]
+; CHECK-NEXT: [[IV_NEXT]] = add i64 [[IV]], 1
+; CHECK-NEXT: [[EC:%.*]] = icmp eq i64 [[IV_NEXT]], [[N:%.*]]
+; CHECK-NEXT: br i1 [[EC]], label [[EXIT:%.*]], label [[LOOP]]
+; CHECK: exit:
+; CHECK-NEXT: ret i32 [[ACC_NEXT]]
+;
+entry:
+ br label %loop
+
+loop:
+ %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop ]
+ %acc = phi i32 [ 255, %entry ], [ %acc.next, %loop ]
+ %gep = getelementptr inbounds i32, ptr %p, i64 %iv
+ %x = load i32, ptr %gep
+ %acc.next = and i32 %acc, %x
+ %iv.next = add i64 %iv, 1
+ %ec = icmp eq i64 %iv.next, %n
+ br i1 %ec, label %exit, label %loop
+
+exit:
+ %masked = and i32 %acc.next, 255
+ ret i32 %masked
+}
+
+; Negative test: an and recurrence does not inherit the zero bits of the step,
+; because the step does not apply to the first iteration.
+define i32 @test_and_step_zero_bits(i32 %start) {
+;
+; CHECK-LABEL: @test_and_step_zero_bits(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: br label [[LOOP:%.*]]
+; CHECK: loop:
+; CHECK-NEXT: tail call void @use(i64 0)
+; CHECK-NEXT: br i1 false, label [[LOOP]], label [[EXIT:%.*]]
+; CHECK: exit:
+; CHECK-NEXT: [[MASKED:%.*]] = and i32 [[START:%.*]], 4
+; CHECK-NEXT: ret i32 [[MASKED]]
+;
+entry:
+ br label %loop
+
+loop:
+ %iv = phi i32 [ %start, %entry ], [ %iv.next, %loop ]
+ %iv.next = and i32 %iv, 4
+ %masked = and i32 %iv, 4
+ tail call void @use(i64 0)
+ br i1 false, label %loop, label %exit
+
+exit:
+ ret i32 %masked
+}
+
More information about the llvm-commits
mailing list