[llvm] [ValueTracking] Infer implied conditions through masked bit tests (PR #219626)
via llvm-commits
llvm-commits at lists.llvm.org
Fri Aug 28 21:35:47 PDT 2026
https://github.com/benscharl created https://github.com/llvm/llvm-project/pull/219626
Fixes #214397
`isImpliedCondICmps` failed when one compare constrains X and the other tests a value derived from X. For example:
`X u<13` implies `(X & 16) == 0`
This adds an additional check. When both compares have constant RHS operands and the second compare's LHS is `and (L0, C)`, following strategy is applied: L0's range is derived from the first compare and the mask is applied. If the resulting range satisfies the second predicate for all values, the implication is correct. If the inverse predicate is satisfied for all values, the implication is false. Otherwise the result is unknown.
Alive2: https://alive2.llvm.org/ce/z/aa62Yy
Test Changes:
Eight negative tests in `llvm/test/Transforms/InstCombine/icmp-power2-and-icmp-shifted-mask.ll` from #54856 now fold. The new outputs are correct: https://alive2.llvm.org/ce/z/9oIb-I. These tests cannot be preserved by adjusting the constants.
>From efe0ddd89003fb36fe9e0256900a2f9ab4d19979 Mon Sep 17 00:00:00 2001
From: benscharl <benscharl42 at gmail.com>
Date: Fri, 28 Aug 2026 21:49:48 -0400
Subject: [PATCH 1/2] Add precommit tests
---
.../Transforms/InstCombine/nested-select.ll | 154 ++++++++++++++++++
1 file changed, 154 insertions(+)
diff --git a/llvm/test/Transforms/InstCombine/nested-select.ll b/llvm/test/Transforms/InstCombine/nested-select.ll
index b8a7d4e8ab789..507eeda4b9653 100644
--- a/llvm/test/Transforms/InstCombine/nested-select.ll
+++ b/llvm/test/Transforms/InstCombine/nested-select.ll
@@ -595,3 +595,157 @@ entry:
%and = and <4 x i1> %sel, %b
ret <4 x i1> %and
}
+
+; x <u 13 => bit 4 is clear => (x & 16) == 0 is true
+define i8 @test_mask_implied_equal(i8 %x) {
+; CHECK-LABEL: @test_mask_implied_equal(
+; CHECK-NEXT: [[CMP1:%.*]] = icmp ult i8 [[X:%.*]], 13
+; CHECK-NEXT: [[MASK:%.*]] = and i8 [[X]], 16
+; CHECK-NEXT: [[CMP2:%.*]] = icmp eq i8 [[MASK]], 0
+; CHECK-NEXT: [[INNER:%.*]] = select i1 [[CMP2]], i8 1, i8 2
+; CHECK-NEXT: [[R:%.*]] = select i1 [[CMP1]], i8 [[INNER]], i8 0
+; CHECK-NEXT: ret i8 [[R]]
+;
+ %cmp1 = icmp ult i8 %x, 13
+ %mask = and i8 %x, 16
+ %cmp2 = icmp eq i8 %mask, 0
+ %inner = select i1 %cmp2, i8 1, i8 2
+ %r = select i1 %cmp1, i8 %inner, i8 0
+ ret i8 %r
+}
+
+define i8 @test_mask_implied_swapped_equal(i8 %x) {
+; CHECK-LABEL: @test_mask_implied_swapped_equal(
+; CHECK-NEXT: [[CMP1:%.*]] = icmp ult i8 [[X:%.*]], 13
+; CHECK-NEXT: [[MASK:%.*]] = and i8 [[X]], 16
+; CHECK-NEXT: [[CMP2:%.*]] = icmp eq i8 [[MASK]], 0
+; CHECK-NEXT: [[INNER:%.*]] = select i1 [[CMP2]], i8 1, i8 2
+; CHECK-NEXT: [[R:%.*]] = select i1 [[CMP1]], i8 [[INNER]], i8 0
+; CHECK-NEXT: ret i8 [[R]]
+;
+ %cmp1 = icmp ult i8 %x, 13
+ %mask = and i8 16, %x
+ %cmp2 = icmp eq i8 %mask, 0
+ %inner = select i1 %cmp2, i8 1, i8 2
+ %r = select i1 %cmp1, i8 %inner, i8 0
+ ret i8 %r
+}
+
+define i8 @test_mask_no_implication_fail(i8 %x) {
+; CHECK-LABEL: @test_mask_no_implication_fail(
+; CHECK-NEXT: [[CMP1:%.*]] = icmp ult i8 [[X:%.*]], 20
+; CHECK-NEXT: [[MASK:%.*]] = and i8 [[X]], 16
+; CHECK-NEXT: [[CMP2:%.*]] = icmp eq i8 [[MASK]], 0
+; CHECK-NEXT: [[INNER:%.*]] = select i1 [[CMP2]], i8 1, i8 2
+; CHECK-NEXT: [[R:%.*]] = select i1 [[CMP1]], i8 [[INNER]], i8 0
+; CHECK-NEXT: ret i8 [[R]]
+;
+ %cmp1 = icmp ult i8 %x, 20
+ %mask = and i8 %x, 16
+ %cmp2 = icmp eq i8 %mask, 0
+ %inner = select i1 %cmp2, i8 1, i8 2
+ %r = select i1 %cmp1, i8 %inner, i8 0
+ ret i8 %r
+}
+
+define i8 @test_mask_boundary_true(i8 %x) {
+; CHECK-LABEL: @test_mask_boundary_true(
+; CHECK-NEXT: [[CMP1:%.*]] = icmp ult i8 [[X:%.*]], 16
+; CHECK-NEXT: [[MASK:%.*]] = and i8 [[X]], 16
+; CHECK-NEXT: [[CMP2:%.*]] = icmp eq i8 [[MASK]], 0
+; CHECK-NEXT: [[INNER:%.*]] = select i1 [[CMP2]], i8 1, i8 2
+; CHECK-NEXT: [[R:%.*]] = select i1 [[CMP1]], i8 [[INNER]], i8 0
+; CHECK-NEXT: ret i8 [[R]]
+;
+ %cmp1 = icmp ult i8 %x, 16
+ %mask = and i8 %x, 16
+ %cmp2 = icmp eq i8 %mask, 0
+ %inner = select i1 %cmp2, i8 1, i8 2
+ %r = select i1 %cmp1, i8 %inner, i8 0
+ ret i8 %r
+}
+
+define i8 @test_mask_boundary_fail(i8 %x) {
+; CHECK-LABEL: @test_mask_boundary_fail(
+; CHECK-NEXT: [[CMP1:%.*]] = icmp ult i8 [[X:%.*]], 17
+; CHECK-NEXT: [[MASK:%.*]] = and i8 [[X]], 16
+; CHECK-NEXT: [[CMP2_NOT:%.*]] = icmp eq i8 [[MASK]], 0
+; CHECK-NEXT: [[INNER:%.*]] = select i1 [[CMP2_NOT]], i8 2, i8 1
+; CHECK-NEXT: [[R:%.*]] = select i1 [[CMP1]], i8 [[INNER]], i8 0
+; CHECK-NEXT: ret i8 [[R]]
+;
+ %cmp1 = icmp ult i8 %x, 17
+ %mask = and i8 %x, 16
+ %cmp2 = icmp ne i8 %mask, 0
+ %inner = select i1 %cmp2, i8 1, i8 2
+ %r = select i1 %cmp1, i8 %inner, i8 0
+ ret i8 %r
+}
+
+define <2 x i8> @test_mask_splat_true(<2 x i8> %x) {
+; CHECK-LABEL: @test_mask_splat_true(
+; CHECK-NEXT: [[CMP1:%.*]] = icmp ult <2 x i8> [[X:%.*]], splat (i8 13)
+; CHECK-NEXT: [[MASK:%.*]] = and <2 x i8> [[X]], splat (i8 16)
+; CHECK-NEXT: [[CMP2:%.*]] = icmp eq <2 x i8> [[MASK]], zeroinitializer
+; CHECK-NEXT: [[INNER:%.*]] = select <2 x i1> [[CMP2]], <2 x i8> splat (i8 1), <2 x i8> splat (i8 2)
+; CHECK-NEXT: [[R:%.*]] = select <2 x i1> [[CMP1]], <2 x i8> [[INNER]], <2 x i8> zeroinitializer
+; CHECK-NEXT: ret <2 x i8> [[R]]
+;
+ %cmp1 = icmp ult <2 x i8> %x, splat (i8 13)
+ %mask = and <2 x i8> %x, splat (i8 16)
+ %cmp2 = icmp eq <2 x i8> %mask, zeroinitializer
+ %inner = select <2 x i1> %cmp2, <2 x i8> splat (i8 1), <2 x i8> splat (i8 2)
+ %r = select <2 x i1> %cmp1, <2 x i8> %inner, <2 x i8> zeroinitializer
+ ret <2 x i8> %r
+}
+
+define <2 x i8> @test_mask_splat_fail(<2 x i8> %x) {
+; CHECK-LABEL: @test_mask_splat_fail(
+; CHECK-NEXT: [[CMP1:%.*]] = icmp ult <2 x i8> [[X:%.*]], splat (i8 20)
+; CHECK-NEXT: [[MASK:%.*]] = and <2 x i8> [[X]], splat (i8 16)
+; CHECK-NEXT: [[CMP2:%.*]] = icmp eq <2 x i8> [[MASK]], zeroinitializer
+; CHECK-NEXT: [[INNER:%.*]] = select <2 x i1> [[CMP2]], <2 x i8> splat (i8 1), <2 x i8> splat (i8 2)
+; CHECK-NEXT: [[R:%.*]] = select <2 x i1> [[CMP1]], <2 x i8> [[INNER]], <2 x i8> zeroinitializer
+; CHECK-NEXT: ret <2 x i8> [[R]]
+;
+ %cmp1 = icmp ult <2 x i8> %x, splat (i8 20)
+ %mask = and <2 x i8> %x, splat (i8 16)
+ %cmp2 = icmp eq <2 x i8> %mask, zeroinitializer
+ %inner = select <2 x i1> %cmp2, <2 x i8> splat (i8 1), <2 x i8> splat (i8 2)
+ %r = select <2 x i1> %cmp1, <2 x i8> %inner, <2 x i8> zeroinitializer
+ ret <2 x i8> %r
+}
+
+define i8 @test_mask_signed_true(i8 %x) {
+;
+; CHECK-LABEL: @test_mask_signed_true(
+; CHECK-NEXT: [[CMP1_INV:%.*]] = icmp sgt i8 [[X:%.*]], -1
+; CHECK-NEXT: [[R:%.*]] = zext i1 [[CMP1_INV]] to i8
+; CHECK-NEXT: ret i8 [[R]]
+;
+ %cmp1 = icmp sgt i8 %x, -1
+ %mask = and i8 %x, -128
+ %cmp2 = icmp eq i8 %mask, 0
+ %inner = select i1 %cmp2, i8 1, i8 2
+ %r = select i1 %cmp1, i8 %inner, i8 0
+ ret i8 %r
+}
+
+define i8 @test_mask_signed_fail(i8 %x) {
+; CHECK-LABEL: @test_mask_signed_fail(
+; CHECK-NEXT: [[CMP1:%.*]] = icmp slt i8 [[X:%.*]], 13
+; CHECK-NEXT: [[MASK:%.*]] = and i8 [[X]], 16
+; CHECK-NEXT: [[CMP2:%.*]] = icmp eq i8 [[MASK]], 0
+; CHECK-NEXT: [[INNER:%.*]] = select i1 [[CMP2]], i8 1, i8 2
+; CHECK-NEXT: [[R:%.*]] = select i1 [[CMP1]], i8 [[INNER]], i8 0
+; CHECK-NEXT: ret i8 [[R]]
+;
+ %cmp1 = icmp slt i8 %x, 13
+ %mask = and i8 %x, 16
+ %cmp2 = icmp eq i8 %mask, 0
+ %inner = select i1 %cmp2, i8 1, i8 2
+ %r = select i1 %cmp1, i8 %inner, i8 0
+ ret i8 %r
+}
+
+
>From a213fb849cc61433dded57427baef8e16d13bc98 Mon Sep 17 00:00:00 2001
From: benscharl <benscharl42 at gmail.com>
Date: Fri, 28 Aug 2026 23:44:57 -0400
Subject: [PATCH 2/2] [ValueTracking] Infer implied conditions through masked
bit tests
Fixed #214397
`isImpliedCondICmps` failed when one compare constrains X and the other tests a value derived from X. For example:
`X u<13` implies `(X & 16) == 0`
This adds an additional check. When both compares have constant RHS operands and the second compare's LHS is `and (L0, C)`, following strategy is applied: L0's range is derived from the first compare and the mask is applied. If the resulting range satisfies the second predicate for all values, the implication is correct. If the inverse predicate is satisfied for all values, the implication is false. Otherwise the result is unknown.
Alive2: https://alive2.llvm.org/ce/z/aa62Yy
Test Changes:
Eight negative tests in `llvm/test/Transforms/InstCombine/icmp-power2-and-icmp-shifted-mask.ll` from #54856 now fold. The new outputs are correct: https://alive2.llvm.org/ce/z/9oIb-I. These tests cannot be preserved by adjusting the constants.
---
llvm/lib/Analysis/ValueTracking.cpp | 14 ++++++
.../icmp-power2-and-icmp-shifted-mask.ll | 49 +++++--------------
.../Transforms/InstCombine/nested-select.ll | 20 ++------
llvm/unittests/Analysis/ValueTrackingTest.cpp | 48 ++++++++++++++++++
4 files changed, 79 insertions(+), 52 deletions(-)
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index e028768f3a487..6c1fc97cfbf7d 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -9928,6 +9928,20 @@ isImpliedCondICmps(CmpPredicate LPred, const Value *L0, const Value *L1,
if (auto P = CmpPredicate::getMatching(LPred, RPred))
return isImpliedCondOperands(*P, L0, L1, R0, R1);
+ // L0 u< C sets limits to L0's bits which may imply (L0 & Mask) pred RC
+ // Example: L0 u< 13 => (L0 & 16) == 0
+ const APInt *LC, *RC, *MaskC;
+ if (match(L1, m_APInt(LC)) && match(R1, m_APInt(RC)) &&
+ match(R0, m_c_And(m_Specific(L0), m_APInt(MaskC)))) {
+ ConstantRange LCRange = ConstantRange::makeExactICmpRegion(LPred, *LC);
+ ConstantRange MaskedCRange = LCRange.binaryAnd(*MaskC);
+ if (MaskedCRange.icmp(RPred, ConstantRange(*RC)))
+ return true;
+ if (MaskedCRange.icmp(ICmpInst::getInversePredicate(RPred),
+ ConstantRange(*RC)))
+ return false;
+ }
+
return std::nullopt;
}
diff --git a/llvm/test/Transforms/InstCombine/icmp-power2-and-icmp-shifted-mask.ll b/llvm/test/Transforms/InstCombine/icmp-power2-and-icmp-shifted-mask.ll
index edbaffa6a12b9..a9bfba7376cba 100644
--- a/llvm/test/Transforms/InstCombine/icmp-power2-and-icmp-shifted-mask.ll
+++ b/llvm/test/Transforms/InstCombine/icmp-power2-and-icmp-shifted-mask.ll
@@ -246,14 +246,12 @@ define i1 @icmp_power2_and_icmp_shifted_mask_swapped_256_239_gap_in_mask_fail(i3
ret i1 %t4
}
-; ((X u< 0x08) & ((X & 0x70) != 0x70)) -> no change
+; ((X u< 0x08) & ((X & 0x70) != 0x70)) -> (X u< 0x08)
+; X u< 0x08 implies (X & 0x70) == 0 => second compare is always true
define i1 @icmp_power2_and_icmp_shifted_mask_8_112_mask_to_left_fail(i32 %x) {
; CHECK-LABEL: @icmp_power2_and_icmp_shifted_mask_8_112_mask_to_left_fail(
; CHECK-NEXT: [[T1:%.*]] = icmp ult i32 [[X:%.*]], 8
-; CHECK-NEXT: [[T2:%.*]] = and i32 [[X]], 112
-; CHECK-NEXT: [[T3:%.*]] = icmp ne i32 [[T2]], 112
-; CHECK-NEXT: [[T4:%.*]] = and i1 [[T1]], [[T3]]
-; CHECK-NEXT: ret i1 [[T4]]
+; CHECK-NEXT: ret i1 [[T1]]
;
%t1 = icmp ult i32 %x, 8
%t2 = and i32 %x, 112
@@ -265,10 +263,7 @@ define i1 @icmp_power2_and_icmp_shifted_mask_8_112_mask_to_left_fail(i32 %x) {
define i1 @icmp_power2_and_icmp_shifted_mask_swapped_8_112_mask_to_left_fail(i32 %x) {
; CHECK-LABEL: @icmp_power2_and_icmp_shifted_mask_swapped_8_112_mask_to_left_fail(
; CHECK-NEXT: [[T1:%.*]] = icmp ult i32 [[X:%.*]], 8
-; CHECK-NEXT: [[T2:%.*]] = and i32 [[X]], 112
-; CHECK-NEXT: [[T3:%.*]] = icmp ne i32 [[T2]], 112
-; CHECK-NEXT: [[T4:%.*]] = and i1 [[T3]], [[T1]]
-; CHECK-NEXT: ret i1 [[T4]]
+; CHECK-NEXT: ret i1 [[T1]]
;
%t1 = icmp ult i32 %x, 8
%t2 = and i32 %x, 112
@@ -277,14 +272,11 @@ define i1 @icmp_power2_and_icmp_shifted_mask_swapped_8_112_mask_to_left_fail(i32
ret i1 %t4
}
-; ((X u< 0x08) & ((X & 0x38) != 0x38))) -> no change
+; ((X u< 0x08) & ((X & 0x38) != 0x38))) -> (X u< 0x08)
define i1 @icmp_power2_and_icmp_shifted_mask_8_56_mask_overlap_fail(i32 %x) {
; CHECK-LABEL: @icmp_power2_and_icmp_shifted_mask_8_56_mask_overlap_fail(
; CHECK-NEXT: [[T1:%.*]] = icmp ult i32 [[X:%.*]], 8
-; CHECK-NEXT: [[T2:%.*]] = and i32 [[X]], 56
-; CHECK-NEXT: [[T3:%.*]] = icmp ne i32 [[T2]], 56
-; CHECK-NEXT: [[T4:%.*]] = and i1 [[T1]], [[T3]]
-; CHECK-NEXT: ret i1 [[T4]]
+; CHECK-NEXT: ret i1 [[T1]]
;
%t1 = icmp ult i32 %x, 8
%t2 = and i32 %x, 56
@@ -296,10 +288,7 @@ define i1 @icmp_power2_and_icmp_shifted_mask_8_56_mask_overlap_fail(i32 %x) {
define i1 @icmp_power2_and_icmp_shifted_mask_swapped_8_56_mask_overlap_fail(i32 %x) {
; CHECK-LABEL: @icmp_power2_and_icmp_shifted_mask_swapped_8_56_mask_overlap_fail(
; CHECK-NEXT: [[T1:%.*]] = icmp ult i32 [[X:%.*]], 8
-; CHECK-NEXT: [[T2:%.*]] = and i32 [[X]], 56
-; CHECK-NEXT: [[T3:%.*]] = icmp ne i32 [[T2]], 56
-; CHECK-NEXT: [[T4:%.*]] = and i1 [[T3]], [[T1]]
-; CHECK-NEXT: ret i1 [[T4]]
+; CHECK-NEXT: ret i1 [[T1]]
;
%t1 = icmp ult i32 %x, 8
%t2 = and i32 %x, 56
@@ -308,14 +297,11 @@ define i1 @icmp_power2_and_icmp_shifted_mask_swapped_8_56_mask_overlap_fail(i32
ret i1 %t4
}
-; ((X u< 0x08) & ((X & 0x18) != 0x18)) -> no change
+; ((X u< 0x08) & ((X & 0x18) != 0x18)) -> (X u< 0x08)
define i1 @icmp_power2_and_icmp_shifted_mask_8_24_mask_overlap_fail(i32 %x) {
; CHECK-LABEL: @icmp_power2_and_icmp_shifted_mask_8_24_mask_overlap_fail(
; CHECK-NEXT: [[T1:%.*]] = icmp ult i32 [[X:%.*]], 8
-; CHECK-NEXT: [[T2:%.*]] = and i32 [[X]], 24
-; CHECK-NEXT: [[T3:%.*]] = icmp ne i32 [[T2]], 24
-; CHECK-NEXT: [[T4:%.*]] = and i1 [[T1]], [[T3]]
-; CHECK-NEXT: ret i1 [[T4]]
+; CHECK-NEXT: ret i1 [[T1]]
;
%t1 = icmp ult i32 %x, 8
%t2 = and i32 %x, 24
@@ -327,10 +313,7 @@ define i1 @icmp_power2_and_icmp_shifted_mask_8_24_mask_overlap_fail(i32 %x) {
define i1 @icmp_power2_and_icmp_shifted_mask_swapped_8_24_mask_overlap_fail(i32 %x) {
; CHECK-LABEL: @icmp_power2_and_icmp_shifted_mask_swapped_8_24_mask_overlap_fail(
; CHECK-NEXT: [[T1:%.*]] = icmp ult i32 [[X:%.*]], 8
-; CHECK-NEXT: [[T2:%.*]] = and i32 [[X]], 24
-; CHECK-NEXT: [[T3:%.*]] = icmp ne i32 [[T2]], 24
-; CHECK-NEXT: [[T4:%.*]] = and i1 [[T3]], [[T1]]
-; CHECK-NEXT: ret i1 [[T4]]
+; CHECK-NEXT: ret i1 [[T1]]
;
%t1 = icmp ult i32 %x, 8
%t2 = and i32 %x, 24
@@ -339,14 +322,11 @@ define i1 @icmp_power2_and_icmp_shifted_mask_swapped_8_24_mask_overlap_fail(i32
ret i1 %t4
}
-; ((X u< 0x8) & ((X & 0xC) != 0xC)) -> no change
+; ((X u< 0x8) & ((X & 0xC) != 0xC)) -> (X u< 0x8)
define i1 @icmp_power2_and_icmp_shifted_mask_8_12_mask_overlap_fail(i32 %x) {
; CHECK-LABEL: @icmp_power2_and_icmp_shifted_mask_8_12_mask_overlap_fail(
; CHECK-NEXT: [[T1:%.*]] = icmp ult i32 [[X:%.*]], 8
-; CHECK-NEXT: [[T2:%.*]] = and i32 [[X]], 12
-; CHECK-NEXT: [[T3:%.*]] = icmp ne i32 [[T2]], 12
-; CHECK-NEXT: [[T4:%.*]] = and i1 [[T1]], [[T3]]
-; CHECK-NEXT: ret i1 [[T4]]
+; CHECK-NEXT: ret i1 [[T1]]
;
%t1 = icmp ult i32 %x, 8
%t2 = and i32 %x, 12
@@ -358,10 +338,7 @@ define i1 @icmp_power2_and_icmp_shifted_mask_8_12_mask_overlap_fail(i32 %x) {
define i1 @icmp_power2_and_icmp_shifted_mask_swapped_8_12_mask_overlap_fail(i32 %x) {
; CHECK-LABEL: @icmp_power2_and_icmp_shifted_mask_swapped_8_12_mask_overlap_fail(
; CHECK-NEXT: [[T1:%.*]] = icmp ult i32 [[X:%.*]], 8
-; CHECK-NEXT: [[T2:%.*]] = and i32 [[X]], 12
-; CHECK-NEXT: [[T3:%.*]] = icmp ne i32 [[T2]], 12
-; CHECK-NEXT: [[T4:%.*]] = and i1 [[T3]], [[T1]]
-; CHECK-NEXT: ret i1 [[T4]]
+; CHECK-NEXT: ret i1 [[T1]]
;
%t1 = icmp ult i32 %x, 8
%t2 = and i32 %x, 12
diff --git a/llvm/test/Transforms/InstCombine/nested-select.ll b/llvm/test/Transforms/InstCombine/nested-select.ll
index 507eeda4b9653..67cd608cad4e8 100644
--- a/llvm/test/Transforms/InstCombine/nested-select.ll
+++ b/llvm/test/Transforms/InstCombine/nested-select.ll
@@ -600,10 +600,7 @@ entry:
define i8 @test_mask_implied_equal(i8 %x) {
; CHECK-LABEL: @test_mask_implied_equal(
; CHECK-NEXT: [[CMP1:%.*]] = icmp ult i8 [[X:%.*]], 13
-; CHECK-NEXT: [[MASK:%.*]] = and i8 [[X]], 16
-; CHECK-NEXT: [[CMP2:%.*]] = icmp eq i8 [[MASK]], 0
-; CHECK-NEXT: [[INNER:%.*]] = select i1 [[CMP2]], i8 1, i8 2
-; CHECK-NEXT: [[R:%.*]] = select i1 [[CMP1]], i8 [[INNER]], i8 0
+; CHECK-NEXT: [[R:%.*]] = zext i1 [[CMP1]] to i8
; CHECK-NEXT: ret i8 [[R]]
;
%cmp1 = icmp ult i8 %x, 13
@@ -617,10 +614,7 @@ define i8 @test_mask_implied_equal(i8 %x) {
define i8 @test_mask_implied_swapped_equal(i8 %x) {
; CHECK-LABEL: @test_mask_implied_swapped_equal(
; CHECK-NEXT: [[CMP1:%.*]] = icmp ult i8 [[X:%.*]], 13
-; CHECK-NEXT: [[MASK:%.*]] = and i8 [[X]], 16
-; CHECK-NEXT: [[CMP2:%.*]] = icmp eq i8 [[MASK]], 0
-; CHECK-NEXT: [[INNER:%.*]] = select i1 [[CMP2]], i8 1, i8 2
-; CHECK-NEXT: [[R:%.*]] = select i1 [[CMP1]], i8 [[INNER]], i8 0
+; CHECK-NEXT: [[R:%.*]] = zext i1 [[CMP1]] to i8
; CHECK-NEXT: ret i8 [[R]]
;
%cmp1 = icmp ult i8 %x, 13
@@ -651,10 +645,7 @@ define i8 @test_mask_no_implication_fail(i8 %x) {
define i8 @test_mask_boundary_true(i8 %x) {
; CHECK-LABEL: @test_mask_boundary_true(
; CHECK-NEXT: [[CMP1:%.*]] = icmp ult i8 [[X:%.*]], 16
-; CHECK-NEXT: [[MASK:%.*]] = and i8 [[X]], 16
-; CHECK-NEXT: [[CMP2:%.*]] = icmp eq i8 [[MASK]], 0
-; CHECK-NEXT: [[INNER:%.*]] = select i1 [[CMP2]], i8 1, i8 2
-; CHECK-NEXT: [[R:%.*]] = select i1 [[CMP1]], i8 [[INNER]], i8 0
+; CHECK-NEXT: [[R:%.*]] = zext i1 [[CMP1]] to i8
; CHECK-NEXT: ret i8 [[R]]
;
%cmp1 = icmp ult i8 %x, 16
@@ -685,10 +676,7 @@ define i8 @test_mask_boundary_fail(i8 %x) {
define <2 x i8> @test_mask_splat_true(<2 x i8> %x) {
; CHECK-LABEL: @test_mask_splat_true(
; CHECK-NEXT: [[CMP1:%.*]] = icmp ult <2 x i8> [[X:%.*]], splat (i8 13)
-; CHECK-NEXT: [[MASK:%.*]] = and <2 x i8> [[X]], splat (i8 16)
-; CHECK-NEXT: [[CMP2:%.*]] = icmp eq <2 x i8> [[MASK]], zeroinitializer
-; CHECK-NEXT: [[INNER:%.*]] = select <2 x i1> [[CMP2]], <2 x i8> splat (i8 1), <2 x i8> splat (i8 2)
-; CHECK-NEXT: [[R:%.*]] = select <2 x i1> [[CMP1]], <2 x i8> [[INNER]], <2 x i8> zeroinitializer
+; CHECK-NEXT: [[R:%.*]] = zext <2 x i1> [[CMP1]] to <2 x i8>
; CHECK-NEXT: ret <2 x i8> [[R]]
;
%cmp1 = icmp ult <2 x i8> %x, splat (i8 13)
diff --git a/llvm/unittests/Analysis/ValueTrackingTest.cpp b/llvm/unittests/Analysis/ValueTrackingTest.cpp
index a969b8fdd866f..f33d236b0c096 100644
--- a/llvm/unittests/Analysis/ValueTrackingTest.cpp
+++ b/llvm/unittests/Analysis/ValueTrackingTest.cpp
@@ -2680,6 +2680,54 @@ TEST_F(ValueTrackingTest, IsImpliedConditionOr2) {
EXPECT_EQ(isImpliedCondition(A, A4, DL, false), std::nullopt);
}
+TEST_F(ValueTrackingTest, IsImpliedConditionBitMask) {
+ parseAssembly(R"(
+ define void @test(i32 %x) {
+ %A = icmp ult i32 %x, 13
+ ; x u< 13 => bits 4 and above are zero
+ %masked = and i32 %x, 16
+ %masked_swapped = and i32 16, %x
+ %A2 = icmp eq i32 %masked, 0
+ %A3 = icmp ne i32 %masked, 0
+ %A4 = icmp eq i32 %masked_swapped, 0
+ %A5 = icmp ne i32 %masked_swapped, 0
+ ret void
+ }
+ )");
+ const DataLayout &DL = M->getDataLayout();
+ EXPECT_EQ(isImpliedCondition(A, A2, DL, true), true);
+ EXPECT_EQ(isImpliedCondition(A, A3, DL, true), false);
+ EXPECT_EQ(isImpliedCondition(A, A4, DL, true), true);
+ EXPECT_EQ(isImpliedCondition(A, A5, DL, true), false);
+}
+
+TEST_F(ValueTrackingTest, IsImpliedConditionBitMaskNoImplication) {
+ parseAssembly(R"(
+ define void @test(i32 %x) {
+ %A = icmp ult i32 %x, 20
+ ; bit 4 is set in 16..19 => nothing implied
+ %masked = and i32 %x, 16
+ %A2 = icmp eq i32 %masked, 0
+ ret void
+ }
+ )");
+ const DataLayout &DL = M->getDataLayout();
+ EXPECT_EQ(isImpliedCondition(A, A2, DL, true), std::nullopt);
+}
+
+TEST_F(ValueTrackingTest, IsImpliedConditionBitMaskSigned) {
+ parseAssembly(R"(
+ define void @test(i32 %x) {
+ %A = icmp sgt i32 %x, -1
+ %masked = and i32 %x, -2147483648
+ %A2 = icmp eq i32 %masked, 0
+ ret void
+ }
+ )");
+ const DataLayout &DL = M->getDataLayout();
+ EXPECT_EQ(isImpliedCondition(A, A2, DL, true), true);
+}
+
TEST_F(ComputeKnownBitsTest, KnownNonZeroShift) {
// %q is known nonzero without known bits.
// Because %q is nonzero, %A[0] is known to be zero.
More information about the llvm-commits
mailing list