Skip to content

Commit adfd59f

Browse files
[InstCombine] Introduce foldICmpBinOpWithConstantViaTruthTable folding
Match icmps of binops where both operands are select with constant arms, i.e., `icmp pred (select A ? C1 : C2) binop (select B ? C3 : C4), C5`. Fold such patterns by creating a truth table of the possible four constant variants, and materialize back the optimal logic from it via `createLogicFromTable` helper. This also generalizes an existing fold, which has therefore been dropped. Proofs: https://alive2.llvm.org/ce/z/NS7Vzu. Fixes: #138212.
1 parent 1bfd94b commit adfd59f

File tree

4 files changed

+66
-83
lines changed

4 files changed

+66
-83
lines changed

llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp

Lines changed: 45 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1966,30 +1966,6 @@ Instruction *InstCombinerImpl::foldICmpAndConstant(ICmpInst &Cmp,
19661966
return new ICmpInst(NewPred, X, SubOne(cast<Constant>(Cmp.getOperand(1))));
19671967
}
19681968

1969-
// If we are testing the intersection of 2 select-of-nonzero-constants with no
1970-
// common bits set, it's the same as checking if exactly one select condition
1971-
// is set:
1972-
// ((A ? TC : FC) & (B ? TC : FC)) == 0 --> xor A, B
1973-
// ((A ? TC : FC) & (B ? TC : FC)) != 0 --> not(xor A, B)
1974-
// TODO: Generalize for non-constant values.
1975-
// TODO: Handle signed/unsigned predicates.
1976-
// TODO: Handle other bitwise logic connectors.
1977-
// TODO: Extend to handle a non-zero compare constant.
1978-
if (C.isZero() && (Pred == CmpInst::ICMP_EQ || And->hasOneUse())) {
1979-
assert(Cmp.isEquality() && "Not expecting non-equality predicates");
1980-
Value *A, *B;
1981-
const APInt *TC, *FC;
1982-
if (match(X, m_Select(m_Value(A), m_APInt(TC), m_APInt(FC))) &&
1983-
match(Y,
1984-
m_Select(m_Value(B), m_SpecificInt(*TC), m_SpecificInt(*FC))) &&
1985-
!TC->isZero() && !FC->isZero() && !TC->intersects(*FC)) {
1986-
Value *R = Builder.CreateXor(A, B);
1987-
if (Pred == CmpInst::ICMP_NE)
1988-
R = Builder.CreateNot(R);
1989-
return replaceInstUsesWith(Cmp, R);
1990-
}
1991-
}
1992-
19931969
// ((zext i1 X) & Y) == 0 --> !((trunc Y) & X)
19941970
// ((zext i1 X) & Y) != 0 --> ((trunc Y) & X)
19951971
// ((zext i1 X) & Y) == 1 --> ((trunc Y) & X)
@@ -3110,6 +3086,44 @@ static Value *createLogicFromTable(const std::bitset<4> &Table, Value *Op0,
31103086
return nullptr;
31113087
}
31123088

3089+
Instruction *InstCombinerImpl::foldICmpBinOpWithConstantViaTruthTable(
3090+
ICmpInst &Cmp, BinaryOperator *BO, const APInt &C) {
3091+
Value *A, *B;
3092+
Constant *C1, *C2, *C3, *C4;
3093+
if (!(match(BO->getOperand(0),
3094+
m_Select(m_Value(A), m_Constant(C1), m_Constant(C2)))) ||
3095+
!match(BO->getOperand(1),
3096+
m_Select(m_Value(B), m_Constant(C3), m_Constant(C4))) ||
3097+
Cmp.getType() != A->getType())
3098+
return nullptr;
3099+
3100+
std::bitset<4> Table;
3101+
auto ComputeTable = [&](bool First, bool Second) -> std::optional<bool> {
3102+
Constant *L = First ? C1 : C2;
3103+
Constant *R = Second ? C3 : C4;
3104+
if (auto *Res = ConstantFoldBinaryOpOperands(BO->getOpcode(), L, R, DL)) {
3105+
auto *Val = Res->getType()->isVectorTy() ? Res->getSplatValue() : Res;
3106+
if (auto *CI = dyn_cast_or_null<ConstantInt>(Val))
3107+
return ICmpInst::compare(CI->getValue(), C, Cmp.getPredicate());
3108+
}
3109+
return std::nullopt;
3110+
};
3111+
3112+
for (unsigned I = 0; I < 4; ++I) {
3113+
bool First = (I >> 1) & 1;
3114+
bool Second = I & 1;
3115+
if (auto Res = ComputeTable(First, Second))
3116+
Table[I] = *Res;
3117+
else
3118+
return nullptr;
3119+
}
3120+
3121+
// Synthesize optimal logic.
3122+
if (auto *Cond = createLogicFromTable(Table, A, B, Builder, BO->hasOneUse()))
3123+
return replaceInstUsesWith(Cmp, Cond);
3124+
return nullptr;
3125+
}
3126+
31133127
/// Fold icmp (add X, Y), C.
31143128
Instruction *InstCombinerImpl::foldICmpAddConstant(ICmpInst &Cmp,
31153129
BinaryOperator *Add,
@@ -4014,7 +4028,13 @@ Instruction *InstCombinerImpl::foldICmpBinOpWithConstant(ICmpInst &Cmp,
40144028
}
40154029

40164030
// TODO: These folds could be refactored to be part of the above calls.
4017-
return foldICmpBinOpEqualityWithConstant(Cmp, BO, C);
4031+
if (Instruction *I = foldICmpBinOpEqualityWithConstant(Cmp, BO, C))
4032+
return I;
4033+
4034+
// Fall back to handling `icmp pred (select A ? C1 : C2) binop (select B ? C3
4035+
// : C4), C5` pattern, by computing a truth table of the four constant
4036+
// variants.
4037+
return foldICmpBinOpWithConstantViaTruthTable(Cmp, BO, C);
40184038
}
40194039

40204040
static Instruction *

llvm/lib/Transforms/InstCombine/InstCombineInternal.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -736,6 +736,9 @@ class LLVM_LIBRARY_VISIBILITY InstCombinerImpl final
736736
Instruction *foldICmpShlConstConst(ICmpInst &I, Value *ShAmt, const APInt &C1,
737737
const APInt &C2);
738738

739+
Instruction *foldICmpBinOpWithConstantViaTruthTable(ICmpInst &Cmp,
740+
BinaryOperator *BO,
741+
const APInt &C);
739742
Instruction *foldICmpBinOpEqualityWithConstant(ICmpInst &Cmp,
740743
BinaryOperator *BO,
741744
const APInt &C);

llvm/test/Transforms/InstCombine/icmp-binop.ll

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -362,10 +362,7 @@ define i1 @test_icmp_sgt_and_negpow2_invalid_c(i32 %add) {
362362

363363
define i1 @icmp_eq_or_of_selects_with_constant(i1 %a, i1 %b) {
364364
; CHECK-LABEL: @icmp_eq_or_of_selects_with_constant(
365-
; CHECK-NEXT: [[S1:%.*]] = select i1 [[A:%.*]], i64 65536, i64 0
366-
; CHECK-NEXT: [[S2:%.*]] = select i1 [[B:%.*]], i64 256, i64 0
367-
; CHECK-NEXT: [[OR:%.*]] = or disjoint i64 [[S1]], [[S2]]
368-
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i64 [[OR]], 65792
365+
; CHECK-NEXT: [[CMP:%.*]] = and i1 [[A:%.*]], [[B:%.*]]
369366
; CHECK-NEXT: ret i1 [[CMP]]
370367
;
371368
%s1 = select i1 %a, i64 65536, i64 0
@@ -377,10 +374,8 @@ define i1 @icmp_eq_or_of_selects_with_constant(i1 %a, i1 %b) {
377374

378375
define i1 @icmp_slt_and_of_selects_with_constant(i1 %a, i1 %b) {
379376
; CHECK-LABEL: @icmp_slt_and_of_selects_with_constant(
380-
; CHECK-NEXT: [[S1:%.*]] = select i1 [[A:%.*]], i8 1, i8 -4
381-
; CHECK-NEXT: [[S2:%.*]] = select i1 [[B:%.*]], i8 1, i8 -3
382-
; CHECK-NEXT: [[AND:%.*]] = and i8 [[S1]], [[S2]]
383-
; CHECK-NEXT: [[CMP:%.*]] = icmp slt i8 [[AND]], -2
377+
; CHECK-NEXT: [[TMP1:%.*]] = or i1 [[A:%.*]], [[B:%.*]]
378+
; CHECK-NEXT: [[CMP:%.*]] = xor i1 [[TMP1]], true
384379
; CHECK-NEXT: ret i1 [[CMP]]
385380
;
386381
%s1 = select i1 %a, i8 1, i8 254
@@ -392,11 +387,7 @@ define i1 @icmp_slt_and_of_selects_with_constant(i1 %a, i1 %b) {
392387

393388
define i1 @icmp_sge_add_of_selects_with_constant(i1 %a, i1 %b) {
394389
; CHECK-LABEL: @icmp_sge_add_of_selects_with_constant(
395-
; CHECK-NEXT: [[S1:%.*]] = select i1 [[A:%.*]], i8 -8, i8 7
396-
; CHECK-NEXT: [[S2:%.*]] = select i1 [[B:%.*]], i8 16, i8 0
397-
; CHECK-NEXT: [[ADD:%.*]] = add nsw i8 [[S1]], [[S2]]
398-
; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i8 [[ADD]], -10
399-
; CHECK-NEXT: ret i1 [[CMP]]
390+
; CHECK-NEXT: ret i1 true
400391
;
401392
%s1 = select i1 %a, i8 248, i8 7
402393
%s2 = select i1 %b, i8 16, i8 0
@@ -411,7 +402,7 @@ define i1 @icmp_eq_or_of_selects_with_constant_multiuse_foldable(i1 %a, i1 %b) {
411402
; CHECK-NEXT: [[S2:%.*]] = select i1 [[B:%.*]], i64 256, i64 0
412403
; CHECK-NEXT: [[OR:%.*]] = or disjoint i64 [[S1]], [[S2]]
413404
; CHECK-NEXT: call void @use64(i64 [[OR]])
414-
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i64 [[OR]], 65792
405+
; CHECK-NEXT: [[CMP:%.*]] = and i1 [[A]], [[B]]
415406
; CHECK-NEXT: ret i1 [[CMP]]
416407
;
417408
%s1 = select i1 %a, i64 65536, i64 0
@@ -424,10 +415,7 @@ define i1 @icmp_eq_or_of_selects_with_constant_multiuse_foldable(i1 %a, i1 %b) {
424415

425416
define <2 x i1> @icmp_eq_or_of_selects_with_constant_vectorized(<2 x i1> %a, <2 x i1> %b) {
426417
; CHECK-LABEL: @icmp_eq_or_of_selects_with_constant_vectorized(
427-
; CHECK-NEXT: [[S1:%.*]] = select <2 x i1> [[A:%.*]], <2 x i64> splat (i64 65536), <2 x i64> zeroinitializer
428-
; CHECK-NEXT: [[S2:%.*]] = select <2 x i1> [[B:%.*]], <2 x i64> splat (i64 256), <2 x i64> zeroinitializer
429-
; CHECK-NEXT: [[OR:%.*]] = or disjoint <2 x i64> [[S1]], [[S2]]
430-
; CHECK-NEXT: [[CMP:%.*]] = icmp eq <2 x i64> [[OR]], splat (i64 65792)
418+
; CHECK-NEXT: [[CMP:%.*]] = and <2 x i1> [[A:%.*]], [[B:%.*]]
431419
; CHECK-NEXT: ret <2 x i1> [[CMP]]
432420
;
433421
%s1 = select <2 x i1> %a, <2 x i64> <i64 65536, i64 65536>, <2 x i64> zeroinitializer

llvm/test/Transforms/InstCombine/icmp-select.ll

Lines changed: 12 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -328,10 +328,7 @@ define i1 @select_constants_and_icmp_eq0_common_bit(i1 %x, i1 %y) {
328328

329329
define i1 @select_constants_and_icmp_eq0_no_common_op1(i1 %x, i1 %y) {
330330
; CHECK-LABEL: @select_constants_and_icmp_eq0_no_common_op1(
331-
; CHECK-NEXT: [[S1:%.*]] = select i1 [[X:%.*]], i8 16, i8 3
332-
; CHECK-NEXT: [[S2:%.*]] = select i1 [[Y:%.*]], i8 24, i8 3
333-
; CHECK-NEXT: [[AND:%.*]] = and i8 [[S1]], [[S2]]
334-
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i8 [[AND]], 0
331+
; CHECK-NEXT: [[CMP:%.*]] = xor i1 [[X:%.*]], [[Y:%.*]]
335332
; CHECK-NEXT: ret i1 [[CMP]]
336333
;
337334
%s1 = select i1 %x, i8 16, i8 3
@@ -345,10 +342,7 @@ define i1 @select_constants_and_icmp_eq0_no_common_op1(i1 %x, i1 %y) {
345342

346343
define i1 @select_constants_and_icmp_eq0_no_common_op2(i1 %x, i1 %y) {
347344
; CHECK-LABEL: @select_constants_and_icmp_eq0_no_common_op2(
348-
; CHECK-NEXT: [[S1:%.*]] = select i1 [[X:%.*]], i8 16, i8 3
349-
; CHECK-NEXT: [[S2:%.*]] = select i1 [[Y:%.*]], i8 16, i8 7
350-
; CHECK-NEXT: [[AND:%.*]] = and i8 [[S1]], [[S2]]
351-
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i8 [[AND]], 0
345+
; CHECK-NEXT: [[CMP:%.*]] = xor i1 [[X:%.*]], [[Y:%.*]]
352346
; CHECK-NEXT: ret i1 [[CMP]]
353347
;
354348
%s1 = select i1 %x, i8 16, i8 3
@@ -387,14 +381,9 @@ define i1 @select_constants_and_icmp_eq0_zero_fval(i1 %x, i1 %y) {
387381
ret i1 %cmp
388382
}
389383

390-
; TODO: x & y
391-
392384
define i1 @select_constants_and_icmp_eq_tval(i1 %x, i1 %y) {
393385
; CHECK-LABEL: @select_constants_and_icmp_eq_tval(
394-
; CHECK-NEXT: [[S1:%.*]] = select i1 [[X:%.*]], i8 6, i8 1
395-
; CHECK-NEXT: [[S2:%.*]] = select i1 [[Y:%.*]], i8 6, i8 1
396-
; CHECK-NEXT: [[AND:%.*]] = and i8 [[S1]], [[S2]]
397-
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i8 [[AND]], 6
386+
; CHECK-NEXT: [[CMP:%.*]] = and i1 [[X:%.*]], [[Y:%.*]]
398387
; CHECK-NEXT: ret i1 [[CMP]]
399388
;
400389
%s1 = select i1 %x, i8 6, i8 1
@@ -404,14 +393,10 @@ define i1 @select_constants_and_icmp_eq_tval(i1 %x, i1 %y) {
404393
ret i1 %cmp
405394
}
406395

407-
; TODO: ~(x | y)
408-
409396
define i1 @select_constants_and_icmp_eq_fval(i1 %x, i1 %y) {
410397
; CHECK-LABEL: @select_constants_and_icmp_eq_fval(
411-
; CHECK-NEXT: [[S1:%.*]] = select i1 [[X:%.*]], i8 12, i8 3
412-
; CHECK-NEXT: [[S2:%.*]] = select i1 [[Y:%.*]], i8 12, i8 3
413-
; CHECK-NEXT: [[AND:%.*]] = and i8 [[S1]], [[S2]]
414-
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i8 [[AND]], 3
398+
; CHECK-NEXT: [[TMP1:%.*]] = or i1 [[X:%.*]], [[Y:%.*]]
399+
; CHECK-NEXT: [[CMP:%.*]] = xor i1 [[TMP1]], true
415400
; CHECK-NEXT: ret i1 [[CMP]]
416401
;
417402
%s1 = select i1 %x, i8 12, i8 3
@@ -512,10 +497,8 @@ define i1 @select_constants_and_icmp_ne0_common_bit(i1 %x, i1 %y) {
512497

513498
define i1 @select_constants_and_icmp_ne0_no_common_op1(i1 %x, i1 %y) {
514499
; CHECK-LABEL: @select_constants_and_icmp_ne0_no_common_op1(
515-
; CHECK-NEXT: [[S1:%.*]] = select i1 [[X:%.*]], i8 16, i8 3
516-
; CHECK-NEXT: [[S2:%.*]] = select i1 [[Y:%.*]], i8 24, i8 3
517-
; CHECK-NEXT: [[AND:%.*]] = and i8 [[S1]], [[S2]]
518-
; CHECK-NEXT: [[CMP:%.*]] = icmp ne i8 [[AND]], 0
500+
; CHECK-NEXT: [[TMP1:%.*]] = xor i1 [[X:%.*]], [[Y:%.*]]
501+
; CHECK-NEXT: [[CMP:%.*]] = xor i1 [[TMP1]], true
519502
; CHECK-NEXT: ret i1 [[CMP]]
520503
;
521504
%s1 = select i1 %x, i8 16, i8 3
@@ -529,10 +512,8 @@ define i1 @select_constants_and_icmp_ne0_no_common_op1(i1 %x, i1 %y) {
529512

530513
define i1 @select_constants_and_icmp_ne0_no_common_op2(i1 %x, i1 %y) {
531514
; CHECK-LABEL: @select_constants_and_icmp_ne0_no_common_op2(
532-
; CHECK-NEXT: [[S1:%.*]] = select i1 [[X:%.*]], i8 16, i8 3
533-
; CHECK-NEXT: [[S2:%.*]] = select i1 [[Y:%.*]], i8 16, i8 7
534-
; CHECK-NEXT: [[AND:%.*]] = and i8 [[S1]], [[S2]]
535-
; CHECK-NEXT: [[CMP:%.*]] = icmp ne i8 [[AND]], 0
515+
; CHECK-NEXT: [[TMP1:%.*]] = xor i1 [[X:%.*]], [[Y:%.*]]
516+
; CHECK-NEXT: [[CMP:%.*]] = xor i1 [[TMP1]], true
536517
; CHECK-NEXT: ret i1 [[CMP]]
537518
;
538519
%s1 = select i1 %x, i8 16, i8 3
@@ -571,14 +552,10 @@ define i1 @select_constants_and_icmp_ne0_zero_fval(i1 %x, i1 %y) {
571552
ret i1 %cmp
572553
}
573554

574-
; TODO: ~(x & y)
575-
576555
define i1 @select_constants_and_icmp_ne_tval(i1 %x, i1 %y) {
577556
; CHECK-LABEL: @select_constants_and_icmp_ne_tval(
578-
; CHECK-NEXT: [[S1:%.*]] = select i1 [[X:%.*]], i8 6, i8 1
579-
; CHECK-NEXT: [[S2:%.*]] = select i1 [[Y:%.*]], i8 6, i8 1
580-
; CHECK-NEXT: [[AND:%.*]] = and i8 [[S1]], [[S2]]
581-
; CHECK-NEXT: [[CMP:%.*]] = icmp ne i8 [[AND]], 6
557+
; CHECK-NEXT: [[TMP1:%.*]] = and i1 [[X:%.*]], [[Y:%.*]]
558+
; CHECK-NEXT: [[CMP:%.*]] = xor i1 [[TMP1]], true
582559
; CHECK-NEXT: ret i1 [[CMP]]
583560
;
584561
%s1 = select i1 %x, i8 6, i8 1
@@ -588,14 +565,9 @@ define i1 @select_constants_and_icmp_ne_tval(i1 %x, i1 %y) {
588565
ret i1 %cmp
589566
}
590567

591-
; TODO: (x | y)
592-
593568
define i1 @select_constants_and_icmp_ne_fval(i1 %x, i1 %y) {
594569
; CHECK-LABEL: @select_constants_and_icmp_ne_fval(
595-
; CHECK-NEXT: [[S1:%.*]] = select i1 [[X:%.*]], i8 12, i8 3
596-
; CHECK-NEXT: [[S2:%.*]] = select i1 [[Y:%.*]], i8 12, i8 3
597-
; CHECK-NEXT: [[AND:%.*]] = and i8 [[S1]], [[S2]]
598-
; CHECK-NEXT: [[CMP:%.*]] = icmp ne i8 [[AND]], 3
570+
; CHECK-NEXT: [[CMP:%.*]] = or i1 [[X:%.*]], [[Y:%.*]]
599571
; CHECK-NEXT: ret i1 [[CMP]]
600572
;
601573
%s1 = select i1 %x, i8 12, i8 3

0 commit comments

Comments
 (0)