[llvm] 764457a - [InstCombine] Fold cmp of select-of-constants via truth table (#186591)

via llvm-commits llvm-commits at lists.llvm.org
Mon Mar 30 05:19:11 PDT 2026


Author: FYLGQ
Date: 2026-03-30T14:19:05+02:00
New Revision: 764457a6e9ca1949482a7914fb928b482d0785b2

URL: https://github.com/llvm/llvm-project/commit/764457a6e9ca1949482a7914fb928b482d0785b2
DIFF: https://github.com/llvm/llvm-project/commit/764457a6e9ca1949482a7914fb928b482d0785b2.diff

LOG: [InstCombine] Fold cmp of select-of-constants via truth table (#186591)

This patch adds a generic InstCombine fold for:
cmp pred (select C1, TV1, FV1), (select C2, TV2, FV2)
when all select arms are constants and the comparison can be
constant-folded for all four combinations of C1/C2. The fold computes a
4-entry truth table and synthesizes a boolean expression using
createLogicFromTable.
This generalizes patterns like:
fcmp une (select C1, -1.0, 1.0), (select C2, -1.0, 1.0) -> xor C1, C2
The transform bails out for mixed vector results like:
<i1 true, i1 false>

alive2: https://alive2.llvm.org/ce/z/JQ_Poy

Fixes #186558

Added: 
    llvm/test/Transforms/InstCombine/fcmp-select-sign.ll

Modified: 
    llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
    llvm/lib/Transforms/InstCombine/InstCombineInternal.h

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index 7911ef8c9aa03..ff1868f3a5840 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -7847,6 +7847,11 @@ Instruction *InstCombinerImpl::visitICmpInst(ICmpInst &I) {
     }
   }
 
+  // Fold icmp pred (select C1, TV1, FV1), (select C2, TV2, FV2)
+  // when all select arms are constants, via truth table.
+  if (Instruction *R = foldCmpSelectOfConstants(I))
+    return R;
+
   // In case of a comparison with two select instructions having the same
   // condition, check whether one of the resulting branches can be simplified.
   // If so, just compare the other branch and select the appropriate result.
@@ -8310,6 +8315,57 @@ Instruction *InstCombinerImpl::foldFCmpIntToFPConst(FCmpInst &I,
                       ConstantInt::get(LHSI->getOperand(0)->getType(), RHSInt));
 }
 
+/// Fold fcmp/icmp pred (select C1, TV1, FV1), (select C2, TV2, FV2)
+/// where all true/false values are constants that allow the compare to be
+/// constant-folded for every combination of C1 and C2.
+/// We compute a 4-entry truth table and use createLogicFromTable to
+/// synthesize a boolean expression of C1 and C2.
+Instruction *InstCombinerImpl::foldCmpSelectOfConstants(CmpInst &I) {
+  Value *Op0 = I.getOperand(0), *Op1 = I.getOperand(1);
+  Value *C1, *C2;
+  Constant *TV1, *FV1, *TV2, *FV2;
+
+  if (!match(Op0, m_Select(m_Value(C1), m_Constant(TV1), m_Constant(FV1))) ||
+      !match(Op1, m_Select(m_Value(C2), m_Constant(TV2), m_Constant(FV2))))
+    return nullptr;
+
+  if (I.getType() != C1->getType() || I.getType() != C2->getType())
+    return nullptr;
+
+  unsigned Pred = I.getPredicate();
+  const DataLayout &DL = I.getDataLayout();
+
+  Constant *Res00 = ConstantFoldCompareInstOperands(Pred, FV1, FV2, DL);
+  Constant *Res01 = ConstantFoldCompareInstOperands(Pred, FV1, TV2, DL);
+  Constant *Res10 = ConstantFoldCompareInstOperands(Pred, TV1, FV2, DL);
+  Constant *Res11 = ConstantFoldCompareInstOperands(Pred, TV1, TV2, DL);
+
+  if (!Res00 || !Res01 || !Res10 || !Res11)
+    return nullptr;
+
+  if ((!Res00->isNullValue() && !Res00->isAllOnesValue()) ||
+      (!Res01->isNullValue() && !Res01->isAllOnesValue()) ||
+      (!Res10->isNullValue() && !Res10->isAllOnesValue()) ||
+      (!Res11->isNullValue() && !Res11->isAllOnesValue()))
+    return nullptr;
+
+  std::bitset<4> Table;
+  if (!Res00->isNullValue())
+    Table.set(0);
+  if (!Res01->isNullValue())
+    Table.set(1);
+  if (!Res10->isNullValue())
+    Table.set(2);
+  if (!Res11->isNullValue())
+    Table.set(3);
+
+  Value *Res = createLogicFromTable(Table, C1, C2, Builder,
+                                    Op0->hasOneUse() && Op1->hasOneUse());
+  if (!Res)
+    return nullptr;
+  return replaceInstUsesWith(I, Res);
+}
+
 /// Fold (C / X) < 0.0 --> X < 0.0 if possible. Swap predicate if necessary.
 static Instruction *foldFCmpReciprocalAndZero(FCmpInst &I, Instruction *LHSI,
                                               Constant *RHSC) {
@@ -9020,6 +9076,9 @@ Instruction *InstCombinerImpl::visitFCmpInst(FCmpInst &I) {
   if (Instruction *R = foldFCmpWithFloorAndCeil(I, *this))
     return R;
 
+  if (Instruction *R = foldCmpSelectOfConstants(I))
+    return R;
+
   if (match(Op0, m_FNeg(m_Value(X)))) {
     // fcmp pred (fneg X), C --> fcmp swap(pred) X, -C
     Constant *C;

diff  --git a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
index b21b7be2d9191..160f766b60973 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
+++ b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
@@ -707,6 +707,7 @@ class LLVM_LIBRARY_VISIBILITY InstCombinerImpl final
   Instruction *foldFCmpIntToFPConst(FCmpInst &I, Instruction *LHSI,
                                     Constant *RHSC);
   Instruction *foldICmpAddOpConst(Value *X, const APInt &C, CmpPredicate Pred);
+  Instruction *foldCmpSelectOfConstants(CmpInst &I);
   Instruction *foldICmpWithCastOp(ICmpInst &ICmp);
   Instruction *foldICmpWithZextOrSext(ICmpInst &ICmp);
 

diff  --git a/llvm/test/Transforms/InstCombine/fcmp-select-sign.ll b/llvm/test/Transforms/InstCombine/fcmp-select-sign.ll
new file mode 100644
index 0000000000000..ac286040aafa7
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/fcmp-select-sign.ll
@@ -0,0 +1,428 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+declare void @use_double(double)
+
+;; ============================================================
+;; Core pattern: fcmp une (select C1, K, K'), (select C2, K, K') → xor
+;; Truth table: {(0,0):false, (0,1):true, (1,0):true, (1,1):false} = 0b0110
+;; ============================================================
+
+define i1 @fcmp_une_select_same_consts(double %a, double %b) {
+; CHECK-LABEL: @fcmp_une_select_same_consts(
+; CHECK-NEXT:    [[V0:%.*]] = fcmp ult double [[B:%.*]], 0.000000e+00
+; CHECK-NEXT:    [[V2:%.*]] = fcmp ult double [[A:%.*]], 0.000000e+00
+; CHECK-NEXT:    [[V4:%.*]] = xor i1 [[V0]], [[V2]]
+; CHECK-NEXT:    ret i1 [[V4]]
+;
+  %v0 = fcmp ult double %b, 0.000000e+00
+  %v1 = select i1 %v0, double -1.000000e+00, double 1.000000e+00
+  %v2 = fcmp ult double %a, 0.000000e+00
+  %v3 = select i1 %v2, double -1.000000e+00, double 1.000000e+00
+  %v4 = fcmp une double %v1, %v3
+  ret i1 %v4
+}
+
+;; ============================================================
+;; fcmp oeq → xnor
+;; Truth table: 0b1001
+;; ============================================================
+
+define i1 @fcmp_oeq_select_same_consts(double %a, double %b) {
+; CHECK-LABEL: @fcmp_oeq_select_same_consts(
+; CHECK-NEXT:    [[V0:%.*]] = fcmp ult double [[B:%.*]], 0.000000e+00
+; CHECK-NEXT:    [[TMP1:%.*]] = fcmp oge double [[A:%.*]], 0.000000e+00
+; CHECK-NEXT:    [[V4:%.*]] = xor i1 [[V0]], [[TMP1]]
+; CHECK-NEXT:    ret i1 [[V4]]
+;
+  %v0 = fcmp ult double %b, 0.000000e+00
+  %v1 = select i1 %v0, double -1.000000e+00, double 1.000000e+00
+  %v2 = fcmp ult double %a, 0.000000e+00
+  %v3 = select i1 %v2, double -1.000000e+00, double 1.000000e+00
+  %v4 = fcmp oeq double %v1, %v3
+  ret i1 %v4
+}
+
+;; ============================================================
+;; fcmp one (ordered not-equal) → xor (same as une for non-NaN)
+;; ============================================================
+
+define i1 @fcmp_one_select_same_consts(double %a, double %b) {
+; CHECK-LABEL: @fcmp_one_select_same_consts(
+; CHECK-NEXT:    [[V0:%.*]] = fcmp ult double [[B:%.*]], 0.000000e+00
+; CHECK-NEXT:    [[V2:%.*]] = fcmp ult double [[A:%.*]], 0.000000e+00
+; CHECK-NEXT:    [[V4:%.*]] = xor i1 [[V0]], [[V2]]
+; CHECK-NEXT:    ret i1 [[V4]]
+;
+  %v0 = fcmp ult double %b, 0.000000e+00
+  %v1 = select i1 %v0, double -1.000000e+00, double 1.000000e+00
+  %v2 = fcmp ult double %a, 0.000000e+00
+  %v3 = select i1 %v2, double -1.000000e+00, double 1.000000e+00
+  %v4 = fcmp one double %v1, %v3
+  ret i1 %v4
+}
+
+;; ============================================================
+;; fcmp ueq → xnor (same as oeq for non-NaN)
+;; ============================================================
+
+define i1 @fcmp_ueq_select_same_consts(double %a, double %b) {
+; CHECK-LABEL: @fcmp_ueq_select_same_consts(
+; CHECK-NEXT:    [[V0:%.*]] = fcmp ult double [[B:%.*]], 0.000000e+00
+; CHECK-NEXT:    [[TMP1:%.*]] = fcmp oge double [[A:%.*]], 0.000000e+00
+; CHECK-NEXT:    [[V4:%.*]] = xor i1 [[V0]], [[TMP1]]
+; CHECK-NEXT:    ret i1 [[V4]]
+;
+  %v0 = fcmp ult double %b, 0.000000e+00
+  %v1 = select i1 %v0, double -1.000000e+00, double 1.000000e+00
+  %v2 = fcmp ult double %a, 0.000000e+00
+  %v3 = select i1 %v2, double -1.000000e+00, double 1.000000e+00
+  %v4 = fcmp ueq double %v1, %v3
+  ret i1 %v4
+}
+
+;; ============================================================
+;; Swapped constants: (select C1, K1, K2) vs (select C2, K2, K1)
+;; une with swapped → truth table 0b1001 → xnor
+;; ============================================================
+
+define i1 @fcmp_une_select_swapped_consts(double %a, double %b) {
+; CHECK-LABEL: @fcmp_une_select_swapped_consts(
+; CHECK-NEXT:    [[V0:%.*]] = fcmp ult double [[B:%.*]], 0.000000e+00
+; CHECK-NEXT:    [[TMP1:%.*]] = fcmp oge double [[A:%.*]], 0.000000e+00
+; CHECK-NEXT:    [[V4:%.*]] = xor i1 [[V0]], [[TMP1]]
+; CHECK-NEXT:    ret i1 [[V4]]
+;
+  %v0 = fcmp ult double %b, 0.000000e+00
+  %v1 = select i1 %v0, double -1.000000e+00, double 1.000000e+00
+  %v2 = fcmp ult double %a, 0.000000e+00
+  %v3 = select i1 %v2, double 1.000000e+00, double -1.000000e+00
+  %v4 = fcmp une double %v1, %v3
+  ret i1 %v4
+}
+
+;; ============================================================
+;; Different constant pairs (not just -1/+1)
+;; ============================================================
+
+define i1 @fcmp_une_select_other_consts(double %a, double %b) {
+; CHECK-LABEL: @fcmp_une_select_other_consts(
+; CHECK-NEXT:    [[V0:%.*]] = fcmp olt double [[B:%.*]], 5.000000e+00
+; CHECK-NEXT:    [[V2:%.*]] = fcmp olt double [[A:%.*]], 5.000000e+00
+; CHECK-NEXT:    [[V4:%.*]] = xor i1 [[V0]], [[V2]]
+; CHECK-NEXT:    ret i1 [[V4]]
+;
+  %v0 = fcmp olt double %b, 5.000000e+00
+  %v1 = select i1 %v0, double 4.200000e+01, double 1.337000e+02
+  %v2 = fcmp olt double %a, 5.000000e+00
+  %v3 = select i1 %v2, double 4.200000e+01, double 1.337000e+02
+  %v4 = fcmp une double %v1, %v3
+  ret i1 %v4
+}
+
+;; ============================================================
+;; Float type (not just double)
+;; ============================================================
+
+define i1 @fcmp_une_select_float(float %a, float %b) {
+; CHECK-LABEL: @fcmp_une_select_float(
+; CHECK-NEXT:    [[V0:%.*]] = fcmp ult float [[B:%.*]], 0.000000e+00
+; CHECK-NEXT:    [[V2:%.*]] = fcmp ult float [[A:%.*]], 0.000000e+00
+; CHECK-NEXT:    [[V4:%.*]] = xor i1 [[V0]], [[V2]]
+; CHECK-NEXT:    ret i1 [[V4]]
+;
+  %v0 = fcmp ult float %b, 0.000000e+00
+  %v1 = select i1 %v0, float -1.000000e+00, float 1.000000e+00
+  %v2 = fcmp ult float %a, 0.000000e+00
+  %v3 = select i1 %v2, float -1.000000e+00, float 1.000000e+00
+  %v4 = fcmp une float %v1, %v3
+  ret i1 %v4
+}
+
+;; ============================================================
+;; Relational predicate: olt → now folds via truth table
+;; Truth table: 0b0100 → and(C1, not(C2))
+;; ============================================================
+
+define i1 @fcmp_olt_select(double %a, double %b) {
+; CHECK-LABEL: @fcmp_olt_select(
+; CHECK-NEXT:    [[V2:%.*]] = fcmp ult double [[A:%.*]], 0.000000e+00
+; CHECK-NEXT:    [[V3:%.*]] = fcmp oge double [[A1:%.*]], 0.000000e+00
+; CHECK-NEXT:    [[V4:%.*]] = and i1 [[V2]], [[V3]]
+; CHECK-NEXT:    ret i1 [[V4]]
+;
+  %v0 = fcmp ult double %b, 0.000000e+00
+  %v1 = select i1 %v0, double -1.000000e+00, double 1.000000e+00
+  %v2 = fcmp ult double %a, 0.000000e+00
+  %v3 = select i1 %v2, double -1.000000e+00, double 1.000000e+00
+  %v4 = fcmp olt double %v1, %v3
+  ret i1 %v4
+}
+
+;; ============================================================
+;; Relational predicate: ogt → truth table 0b0010
+;; → and(not(C1), C2)
+;; ============================================================
+
+define i1 @fcmp_ogt_select(double %a, double %b) {
+; CHECK-LABEL: @fcmp_ogt_select(
+; CHECK-NEXT:    [[V0:%.*]] = fcmp oge double [[B:%.*]], 0.000000e+00
+; CHECK-NEXT:    [[V2:%.*]] = fcmp ult double [[A:%.*]], 0.000000e+00
+; CHECK-NEXT:    [[V4:%.*]] = and i1 [[V0]], [[V2]]
+; CHECK-NEXT:    ret i1 [[V4]]
+;
+  %v0 = fcmp ult double %b, 0.000000e+00
+  %v1 = select i1 %v0, double -1.000000e+00, double 1.000000e+00
+  %v2 = fcmp ult double %a, 0.000000e+00
+  %v3 = select i1 %v2, double -1.000000e+00, double 1.000000e+00
+  %v4 = fcmp ogt double %v1, %v3
+  ret i1 %v4
+}
+
+;; ============================================================
+;; Vector type
+;; ============================================================
+
+define <2 x i1> @fcmp_une_select_vec(<2 x double> %a, <2 x double> %b) {
+; CHECK-LABEL: @fcmp_une_select_vec(
+; CHECK-NEXT:    [[V0:%.*]] = fcmp ult <2 x double> [[B:%.*]], zeroinitializer
+; CHECK-NEXT:    [[V2:%.*]] = fcmp ult <2 x double> [[A:%.*]], zeroinitializer
+; CHECK-NEXT:    [[V4:%.*]] = xor <2 x i1> [[V0]], [[V2]]
+; CHECK-NEXT:    ret <2 x i1> [[V4]]
+;
+  %v0 = fcmp ult <2 x double> %b, zeroinitializer
+  %v1 = select <2 x i1> %v0, <2 x double> <double -1.0, double -1.0>, <2 x double> <double 1.0, double 1.0>
+  %v2 = fcmp ult <2 x double> %a, zeroinitializer
+  %v3 = select <2 x i1> %v2, <2 x double> <double -1.0, double -1.0>, <2 x double> <double 1.0, double 1.0>
+  %v4 = fcmp une <2 x double> %v1, %v3
+  ret <2 x i1> %v4
+}
+
+;; ============================================================
+;; Negative: vector constant folding can yield a mixed mask like
+;; <i1 true, i1 false>, so the truth-table fold must bail out.
+;; ============================================================
+
+define <2 x i1> @fcmp_une_select_vec_mixed_mask_no_fold(<2 x i1> %c0, <2 x i1> %c1) {
+; CHECK-LABEL: @fcmp_une_select_vec_mixed_mask_no_fold(
+; CHECK-NEXT:    [[S0:%.*]] = select <2 x i1> [[C0:%.*]], <2 x double> <double 1.000000e+00, double 2.000000e+00>, <2 x double> <double 3.000000e+00, double 4.000000e+00>
+; CHECK-NEXT:    [[S1:%.*]] = select <2 x i1> [[C1:%.*]], <2 x double> <double 1.000000e+00, double 9.000000e+00>, <2 x double> <double 8.000000e+00, double 4.000000e+00>
+; CHECK-NEXT:    [[R:%.*]] = fcmp une <2 x double> [[S0]], [[S1]]
+; CHECK-NEXT:    ret <2 x i1> [[R]]
+  %s0 = select <2 x i1> %c0,
+               <2 x double> <double 1.0, double 2.0>,
+               <2 x double> <double 3.0, double 4.0>
+  %s1 = select <2 x i1> %c1,
+               <2 x double> <double 1.0, double 9.0>,
+               <2 x double> <double 8.0, double 4.0>
+  %r = fcmp une <2 x double> %s0, %s1
+  ret <2 x i1> %r
+}
+
+;; ============================================================
+;; Multi-use: une (xor) still folds — xor is single instruction
+;; ============================================================
+
+define i1 @fcmp_une_select_multi_use(double %a, double %b) {
+; CHECK-LABEL: @fcmp_une_select_multi_use(
+; CHECK-NEXT:    [[V0:%.*]] = fcmp ult double [[B:%.*]], 0.000000e+00
+; CHECK-NEXT:    [[V1:%.*]] = select i1 [[V0]], double -1.000000e+00, double 1.000000e+00
+; CHECK-NEXT:    [[V2:%.*]] = fcmp ult double [[A:%.*]], 0.000000e+00
+; CHECK-NEXT:    [[V3:%.*]] = select i1 [[V2]], double -1.000000e+00, double 1.000000e+00
+; CHECK-NEXT:    call void @use_double(double [[V1]])
+; CHECK-NEXT:    call void @use_double(double [[V3]])
+; CHECK-NEXT:    [[V4:%.*]] = xor i1 [[V0]], [[V2]]
+; CHECK-NEXT:    ret i1 [[V4]]
+;
+  %v0 = fcmp ult double %b, 0.000000e+00
+  %v1 = select i1 %v0, double -1.000000e+00, double 1.000000e+00
+  %v2 = fcmp ult double %a, 0.000000e+00
+  %v3 = select i1 %v2, double -1.000000e+00, double 1.000000e+00
+  call void @use_double(double %v1)
+  call void @use_double(double %v3)
+  %v4 = fcmp une double %v1, %v3
+  ret i1 %v4
+}
+
+;; ============================================================
+;; Negative: multi-use + olt (needs 2 instructions, HasOneUse=false)
+;; → createLogicFromTable returns nullptr
+;; ============================================================
+
+define i1 @fcmp_olt_select_multi_use_no_fold(double %a, double %b) {
+; CHECK-LABEL: @fcmp_olt_select_multi_use_no_fold(
+; CHECK-NEXT:    [[V0:%.*]] = fcmp ult double [[B:%.*]], 0.000000e+00
+; CHECK-NEXT:    [[V1:%.*]] = select i1 [[V0]], double -1.000000e+00, double 1.000000e+00
+; CHECK-NEXT:    [[V2:%.*]] = fcmp ult double [[A:%.*]], 0.000000e+00
+; CHECK-NEXT:    [[V3:%.*]] = select i1 [[V2]], double -1.000000e+00, double 1.000000e+00
+; CHECK-NEXT:    call void @use_double(double [[V1]])
+; CHECK-NEXT:    call void @use_double(double [[V3]])
+; CHECK-NEXT:    [[V4:%.*]] = fcmp olt double [[V1]], [[V3]]
+; CHECK-NEXT:    ret i1 [[V4]]
+;
+  %v0 = fcmp ult double %b, 0.000000e+00
+  %v1 = select i1 %v0, double -1.000000e+00, double 1.000000e+00
+  %v2 = fcmp ult double %a, 0.000000e+00
+  %v3 = select i1 %v2, double -1.000000e+00, double 1.000000e+00
+  call void @use_double(double %v1)
+  call void @use_double(double %v3)
+  %v4 = fcmp olt double %v1, %v3
+  ret i1 %v4
+}
+
+;; ============================================================
+;; Negative: constants are the same (K1 == K2)
+;; Selects simplify to constant, fcmp une → false
+;; ============================================================
+
+define i1 @fcmp_une_select_same_val(double %a, double %b) {
+; CHECK-LABEL: @fcmp_une_select_same_val(
+; CHECK-NEXT:    ret i1 false
+;
+  %v0 = fcmp ult double %b, 0.000000e+00
+  %v1 = select i1 %v0, double 1.000000e+00, double 1.000000e+00
+  %v2 = fcmp ult double %a, 0.000000e+00
+  %v3 = select i1 %v2, double 1.000000e+00, double 1.000000e+00
+  %v4 = fcmp une double %v1, %v3
+  ret i1 %v4
+}
+
+;; ============================================================
+;; Different constant pairs across selects
+;; All 4 combinations yield une=true → constant true
+;; ============================================================
+
+define i1 @fcmp_une_select_
diff _pairs(double %a, double %b) {
+; CHECK-LABEL: @fcmp_une_select_
diff _pairs(
+; CHECK-NEXT:    ret i1 true
+;
+  %v0 = fcmp ult double %b, 0.000000e+00
+  %v1 = select i1 %v0, double -1.000000e+00, double 1.000000e+00
+  %v2 = fcmp ult double %a, 0.000000e+00
+  %v3 = select i1 %v2, double -2.000000e+00, double 3.000000e+00
+  %v4 = fcmp une double %v1, %v3
+  ret i1 %v4
+}
+
+;; ============================================================
+;; icmp ne with select of integer constants → xor
+;; Truth table: 0b0110
+;; ============================================================
+
+define i1 @icmp_ne_select_consts(i32 %a, i32 %b) {
+; CHECK-LABEL: @icmp_ne_select_consts(
+; CHECK-NEXT:    [[TMP1:%.*]] = xor i32 [[B:%.*]], [[A:%.*]]
+; CHECK-NEXT:    [[R:%.*]] = icmp slt i32 [[TMP1]], 0
+; CHECK-NEXT:    ret i1 [[R]]
+;
+  %s1 = icmp slt i32 %a, 0
+  %v1 = select i1 %s1, i32 -1, i32 1
+  %s2 = icmp slt i32 %b, 0
+  %v2 = select i1 %s2, i32 -1, i32 1
+  %r = icmp ne i32 %v1, %v2
+  ret i1 %r
+}
+
+;; ============================================================
+;; icmp eq with select of integer constants → xnor
+;; Truth table: 0b1001
+;; ============================================================
+
+define i1 @icmp_eq_select_consts(i32 %a, i32 %b) {
+; CHECK-LABEL: @icmp_eq_select_consts(
+; CHECK-NEXT:    [[TMP1:%.*]] = xor i32 [[B:%.*]], [[A:%.*]]
+; CHECK-NEXT:    [[R:%.*]] = icmp sgt i32 [[TMP1]], -1
+; CHECK-NEXT:    ret i1 [[R]]
+;
+  %s1 = icmp slt i32 %a, 0
+  %v1 = select i1 %s1, i32 -1, i32 1
+  %s2 = icmp slt i32 %b, 0
+  %v2 = select i1 %s2, i32 -1, i32 1
+  %r = icmp eq i32 %v1, %v2
+  ret i1 %r
+}
+
+;; ============================================================
+;; icmp slt with select of integer constants
+;; Truth table: 0b0100 → and(C1, not(C2))
+;; ============================================================
+
+define i1 @icmp_slt_select_consts(i32 %a, i32 %b) {
+; CHECK-LABEL: @icmp_slt_select_consts(
+; CHECK-NEXT:    [[S1_INV:%.*]] = icmp slt i32 [[A:%.*]], 0
+; CHECK-NEXT:    [[S2_INV:%.*]] = icmp sgt i32 [[B:%.*]], -1
+; CHECK-NEXT:    [[R:%.*]] = select i1 [[S1_INV]], i1 [[S2_INV]], i1 false
+; CHECK-NEXT:    ret i1 [[R]]
+;
+  %s1 = icmp slt i32 %a, 0
+  %v1 = select i1 %s1, i32 -1, i32 1
+  %s2 = icmp slt i32 %b, 0
+  %v2 = select i1 %s2, i32 -1, i32 1
+  %r = icmp slt i32 %v1, %v2
+  ret i1 %r
+}
+
+;; ============================================================
+;; icmp with swapped integer constants
+;; icmp ne (select C1, -1, 1), (select C2, 1, -1)
+;; Truth table: 0b1001 → xnor
+;; ============================================================
+
+define i1 @icmp_ne_select_swapped_consts(i32 %a, i32 %b) {
+; CHECK-LABEL: @icmp_ne_select_swapped_consts(
+; CHECK-NEXT:    [[TMP1:%.*]] = xor i32 [[B:%.*]], [[A:%.*]]
+; CHECK-NEXT:    [[R:%.*]] = icmp sgt i32 [[TMP1]], -1
+; CHECK-NEXT:    ret i1 [[R]]
+;
+  %s1 = icmp slt i32 %a, 0
+  %v1 = select i1 %s1, i32 -1, i32 1
+  %s2 = icmp slt i32 %b, 0
+  %v2 = select i1 %s2, i32 1, i32 -1
+  %r = icmp ne i32 %v1, %v2
+  ret i1 %r
+}
+
+;; ============================================================
+;; Negative: vector condition should not fold via truth table.
+;; ConstantFoldCompareInstOperands may yield a mixed mask like
+;; <i1 true, i1 false>, so we must bail out.
+;; ============================================================
+
+define <2 x i1> @icmp_eq_select_vec_cond_no_fold(<2 x i1> %c1, <2 x i1> %c2) {
+; CHECK-LABEL: @icmp_eq_select_vec_cond_no_fold(
+; CHECK-NEXT:    [[S1:%.*]] = select <2 x i1> [[C1:%.*]], <2 x i32> <i32 1, i32 2>, <2 x i32> <i32 3, i32 4>
+; CHECK-NEXT:    [[S2:%.*]] = select <2 x i1> [[C2:%.*]], <2 x i32> <i32 1, i32 9>, <2 x i32> <i32 8, i32 4>
+; CHECK-NEXT:    [[R:%.*]] = icmp eq <2 x i32> [[S1]], [[S2]]
+; CHECK-NEXT:    ret <2 x i1> [[R]]
+;
+  %s1 = select <2 x i1> %c1,
+               <2 x i32> <i32 1, i32 2>,
+               <2 x i32> <i32 3, i32 4>
+  %s2 = select <2 x i1> %c2,
+               <2 x i32> <i32 1, i32 9>,
+               <2 x i32> <i32 8, i32 4>
+  %r = icmp eq <2 x i32> %s1, %s2
+  ret <2 x i1> %r
+}
+
+;; ============================================================
+;; Negative: mixed vector/scalar select conditions should not
+;; fold via truth table.
+;; ============================================================
+
+define <2 x i1> @icmp_eq_mixed_cond_no_fold(<2 x i1> %c1, i1 %c2) {
+; CHECK-LABEL: @icmp_eq_mixed_cond_no_fold(
+; CHECK-NEXT:    [[S1:%.*]] = select <2 x i1> [[C1:%.*]], <2 x i32> <i32 1, i32 2>, <2 x i32> <i32 3, i32 4>
+; CHECK-NEXT:    [[S2:%.*]] = select i1 [[C2:%.*]], <2 x i32> <i32 1, i32 9>, <2 x i32> <i32 8, i32 4>
+; CHECK-NEXT:    [[R:%.*]] = icmp eq <2 x i32> [[S1]], [[S2]]
+; CHECK-NEXT:    ret <2 x i1> [[R]]
+;
+  %s1 = select <2 x i1> %c1,
+               <2 x i32> <i32 1, i32 2>,
+               <2 x i32> <i32 3, i32 4>
+  %s2 = select i1 %c2,
+               <2 x i32> <i32 1, i32 9>,
+               <2 x i32> <i32 8, i32 4>
+  %r = icmp eq <2 x i32> %s1, %s2
+  ret <2 x i1> %r
+}


        


More information about the llvm-commits mailing list