[clang] [llvm] [InstCombine] Fold fcmp of two selects with same constant pair (PR #186591)
via cfe-commits
cfe-commits at lists.llvm.org
Sat Mar 14 05:28:44 PDT 2026
https://github.com/FYLGQ created https://github.com/llvm/llvm-project/pull/186591
Fold `fcmp une/one/oeq/ueq (select C1, K1, K2), (select C2, K1, K2)`
into `xor C1, C2` (or its negation), when K1 and K2 are distinct
non-NaN float constants.
Since the selects can only produce values from {K1, K2}, equality
comparison reduces to checking whether the two boolean conditions agree.
Also handles the case where one select has swapped true/false values.
alive2: https://alive2.llvm.org/ce/z/JQ_Poy
Fixes #186558
>From 128f0bf6edc86c0f1c1fd279466afccc95e1ebfc Mon Sep 17 00:00:00 2001
From: FYLGQ <799648409 at qq.com>
Date: Sat, 24 Jan 2026 10:58:51 +0800
Subject: [PATCH 1/6] fix-constant-eval-complex-discard
---
clang/lib/AST/ByteCode/Compiler.cpp | 3 ++-
clang/lib/AST/ByteCode/constant-eval-complex-discard.c | 6 ++++++
2 files changed, 8 insertions(+), 1 deletion(-)
create mode 100644 clang/lib/AST/ByteCode/constant-eval-complex-discard.c
diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp
index 272d08f5e455c..d864fdc5d7e6d 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -7406,7 +7406,8 @@ bool Compiler<Emitter>::emitComplexComparison(const Expr *LHS, const Expr *RHS,
const BinaryOperator *E) {
assert(E->isComparisonOp());
assert(!Initializing);
- assert(!DiscardResult);
+ if (DiscardResult)
+ return true;
PrimType ElemT;
bool LHSIsComplex;
diff --git a/clang/lib/AST/ByteCode/constant-eval-complex-discard.c b/clang/lib/AST/ByteCode/constant-eval-complex-discard.c
new file mode 100644
index 0000000000000..1d111fc049cc1
--- /dev/null
+++ b/clang/lib/AST/ByteCode/constant-eval-complex-discard.c
@@ -0,0 +1,6 @@
+// RUN: %clang_cc1 -std=c11 -fsyntax-only -fexperimental-new-constant-interpreter %s
+
+void foo(void) {
+ // Complex comparison evaluated in a discarded context.
+ (void)(0 && (1i == 1i));
+}
>From b71ec175022ef25291c4bffb5bea65748730c430 Mon Sep 17 00:00:00 2001
From: FYLGQ <799648409 at qq.com>
Date: Sat, 24 Jan 2026 12:36:01 +0800
Subject: [PATCH 2/6] Move test file constant-eval-complex-discard.c to
clang/test/AST/ByteCode
---
clang/{lib => test}/AST/ByteCode/constant-eval-complex-discard.c | 0
1 file changed, 0 insertions(+), 0 deletions(-)
rename clang/{lib => test}/AST/ByteCode/constant-eval-complex-discard.c (100%)
diff --git a/clang/lib/AST/ByteCode/constant-eval-complex-discard.c b/clang/test/AST/ByteCode/constant-eval-complex-discard.c
similarity index 100%
rename from clang/lib/AST/ByteCode/constant-eval-complex-discard.c
rename to clang/test/AST/ByteCode/constant-eval-complex-discard.c
>From ca0d29573e2182cc78f0036f17542a94b8231c50 Mon Sep 17 00:00:00 2001
From: FYLGQ <799648409 at qq.com>
Date: Sun, 25 Jan 2026 17:47:25 +0800
Subject: [PATCH 3/6] [clang][bytecode] Fix crash on discarded complex
comparison
---
clang/lib/AST/ByteCode/Compiler.cpp | 7 ++++++-
.../AST/ByteCode/constant-eval-complex-discard.c | 6 ------
.../ByteCode/constant-eval-complex-discard.cpp | 16 ++++++++++++++++
3 files changed, 22 insertions(+), 7 deletions(-)
delete mode 100644 clang/test/AST/ByteCode/constant-eval-complex-discard.c
create mode 100644 clang/test/AST/ByteCode/constant-eval-complex-discard.cpp
diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp
index d864fdc5d7e6d..5db454c797565 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -7406,8 +7406,13 @@ bool Compiler<Emitter>::emitComplexComparison(const Expr *LHS, const Expr *RHS,
const BinaryOperator *E) {
assert(E->isComparisonOp());
assert(!Initializing);
- if (DiscardResult)
+if (DiscardResult) {
+ if (!this->discard(LHS))
+ return false;
+ if (!this->discard(RHS))
+ return false;
return true;
+}
PrimType ElemT;
bool LHSIsComplex;
diff --git a/clang/test/AST/ByteCode/constant-eval-complex-discard.c b/clang/test/AST/ByteCode/constant-eval-complex-discard.c
deleted file mode 100644
index 1d111fc049cc1..0000000000000
--- a/clang/test/AST/ByteCode/constant-eval-complex-discard.c
+++ /dev/null
@@ -1,6 +0,0 @@
-// RUN: %clang_cc1 -std=c11 -fsyntax-only -fexperimental-new-constant-interpreter %s
-
-void foo(void) {
- // Complex comparison evaluated in a discarded context.
- (void)(0 && (1i == 1i));
-}
diff --git a/clang/test/AST/ByteCode/constant-eval-complex-discard.cpp b/clang/test/AST/ByteCode/constant-eval-complex-discard.cpp
new file mode 100644
index 0000000000000..6c2cf5739ba13
--- /dev/null
+++ b/clang/test/AST/ByteCode/constant-eval-complex-discard.cpp
@@ -0,0 +1,16 @@
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify -fexperimental-new-constant-interpreter %s
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %s
+
+// expected-no-diagnostics
+
+void test_no_crash() {
+ _Complex int x = 1i;
+ (void)(x == 1i);
+}
+
+constexpr int test_side_effect() {
+ int k = 0;
+ (void)(1i == (++k, 1i));
+ return k;
+}
+static_assert(test_side_effect() == 1);
\ No newline at end of file
>From 6710445dfdb18ef977729196156a841abcb42925 Mon Sep 17 00:00:00 2001
From: FYLGQ <799648409 at qq.com>
Date: Mon, 26 Jan 2026 02:19:54 +0800
Subject: [PATCH 4/6] Address review comments: simplify code and move test
---
clang/lib/AST/ByteCode/Compiler.cpp | 9 ++-------
clang/test/AST/ByteCode/complex.cpp | 13 +++++++++++++
.../ByteCode/constant-eval-complex-discard.cpp | 16 ----------------
3 files changed, 15 insertions(+), 23 deletions(-)
delete mode 100644 clang/test/AST/ByteCode/constant-eval-complex-discard.cpp
diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp
index 5db454c797565..2cbc97bd8a574 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -7406,13 +7406,8 @@ bool Compiler<Emitter>::emitComplexComparison(const Expr *LHS, const Expr *RHS,
const BinaryOperator *E) {
assert(E->isComparisonOp());
assert(!Initializing);
-if (DiscardResult) {
- if (!this->discard(LHS))
- return false;
- if (!this->discard(RHS))
- return false;
- return true;
-}
+if (DiscardResult)
+ return this->discard(LHS) && this->discard(RHS);
PrimType ElemT;
bool LHSIsComplex;
diff --git a/clang/test/AST/ByteCode/complex.cpp b/clang/test/AST/ByteCode/complex.cpp
index 41e5dc0605c23..9bf000d65466c 100644
--- a/clang/test/AST/ByteCode/complex.cpp
+++ b/clang/test/AST/ByteCode/complex.cpp
@@ -439,4 +439,17 @@ namespace Discard {
}
static_assert((V(), true));
+ void test_discard_complex_comparison() {
+ _Complex int x = 1i;
+ (void)(x == 1i);
+ x == 1i;
+ (void)(x != 1i);
+ }
+
+ constexpr int test_side_effect() {
+ int k = 0;
+ (void)(1i == (++k, 1i));
+ return k;
+ }
+ static_assert(test_side_effect() == 1);
}
diff --git a/clang/test/AST/ByteCode/constant-eval-complex-discard.cpp b/clang/test/AST/ByteCode/constant-eval-complex-discard.cpp
deleted file mode 100644
index 6c2cf5739ba13..0000000000000
--- a/clang/test/AST/ByteCode/constant-eval-complex-discard.cpp
+++ /dev/null
@@ -1,16 +0,0 @@
-// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify -fexperimental-new-constant-interpreter %s
-// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %s
-
-// expected-no-diagnostics
-
-void test_no_crash() {
- _Complex int x = 1i;
- (void)(x == 1i);
-}
-
-constexpr int test_side_effect() {
- int k = 0;
- (void)(1i == (++k, 1i));
- return k;
-}
-static_assert(test_side_effect() == 1);
\ No newline at end of file
>From ffb84d7846ecb602956ed4de14b3dc7389f04e4c Mon Sep 17 00:00:00 2001
From: FYLGQ <799648409 at qq.com>
Date: Mon, 26 Jan 2026 07:54:17 +0800
Subject: [PATCH 5/6] Fix code formatting
---
clang/lib/AST/ByteCode/Compiler.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp
index 2cbc97bd8a574..00ff60dd825d3 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -7406,7 +7406,7 @@ bool Compiler<Emitter>::emitComplexComparison(const Expr *LHS, const Expr *RHS,
const BinaryOperator *E) {
assert(E->isComparisonOp());
assert(!Initializing);
-if (DiscardResult)
+ if (DiscardResult)
return this->discard(LHS) && this->discard(RHS);
PrimType ElemT;
>From e67ccea2d452ab2d91119616d5697b044691014a Mon Sep 17 00:00:00 2001
From: FYLGQ <799648409 at qq.com>
Date: Sat, 14 Mar 2026 20:23:03 +0800
Subject: [PATCH 6/6] [InstCombine] Fold fcmp of two selects with same constant
pair
Fold fcmp une/one/oeq/ueq (select C1, K1, K2), (select C2, K1, K2)
into xor C1, C2 (or its negation), when K1 and K2 are distinct
non-NaN float constants.
Since the selects can only produce values from {K1, K2}, equality
comparison reduces to checking whether the two boolean conditions agree.
Also handles the case where one select has swapped true/false values.
alive2: https://alive2.llvm.org/ce/z/JQ_Poy
Fixes #186558.
---
.../InstCombine/InstCombineCompares.cpp | 91 +++++++++
.../InstCombine/InstCombineInternal.h | 1 +
.../InstCombine/fcmp-select-sign.ll | 189 ++++++++++++++++++
3 files changed, 281 insertions(+)
create mode 100644 llvm/test/Transforms/InstCombine/fcmp-select-sign.ll
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index 0ca4e4495e6e4..f93d22cf2e1c2 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -8260,6 +8260,94 @@ Instruction *InstCombinerImpl::foldFCmpIntToFPConst(FCmpInst &I,
ConstantInt::get(LHSI->getOperand(0)->getType(), RHSInt));
}
+/// Fold fcmp pred (select C1, K1, K2), (select C2, K1, K2)
+/// where K1 and K2 are non-NaN float constants and K1 != K2.
+///
+/// Since the selects produce values from the set {K1, K2}, the fcmp can be
+/// reduced to a boolean operation on C1 and C2:
+/// une/one → xor C1, C2
+/// oeq/ueq → not(xor C1, C2) i.e. icmp eq C1, C2
+///
+/// Also handles the "swapped" case where one select uses (K2, K1):
+/// fcmp une (select C1, K1, K2), (select C2, K2, K1) → not(xor C1, C2)
+///
+/// alive2: https://alive2.llvm.org/ce/z/JQ_Poy
+Instruction *InstCombinerImpl::foldFCmpSelectOfConstants(FCmpInst &I) {
+ Value *Op0 = I.getOperand(0), *Op1 = I.getOperand(1);
+ Value *C1, *C2;
+ const APFloat *TVal0, *FVal0, *TVal1, *FVal1;
+
+ // Match: fcmp pred (select C1, K1a, K2a), (select C2, K1b, K2b)
+ if (!match(Op0, m_Select(m_Value(C1), m_APFloat(TVal0), m_APFloat(FVal0))) ||
+ !match(Op1, m_Select(m_Value(C2), m_APFloat(TVal1), m_APFloat(FVal1))))
+ return nullptr;
+
+ // All constants must be non-NaN.
+ if (TVal0->isNaN() || FVal0->isNaN() || TVal1->isNaN() || FVal1->isNaN())
+ return nullptr;
+
+ // The two selects must use the same pair of constants {K1, K2} where K1≠K2.
+ // Case 1 "same order": (select C1, K1, K2) vs (select C2, K1, K2)
+ // une → xor C1, C2
+ // Case 2 "swapped order": (select C1, K1, K2) vs (select C2, K2, K1)
+ // une → not(xor C1, C2)
+ bool SameOrder;
+ if (*TVal0 == *TVal1 && *FVal0 == *FVal1) {
+ SameOrder = true;
+ } else if (*TVal0 == *FVal1 && *FVal0 == *TVal1) {
+ SameOrder = false;
+ } else {
+ return nullptr;
+ }
+
+ // K1 must differ from K2, otherwise the select is trivially constant.
+ if (*TVal0 == *FVal0)
+ return nullptr;
+
+ CmpInst::Predicate Pred = I.getPredicate();
+
+ // Determine the boolean relationship.
+ // For "same order" selects:
+ // Results are equal iff C1 == C2, unequal iff C1 != C2.
+ // For "swapped order" selects:
+ // Results are equal iff C1 != C2, unequal iff C1 == C2.
+ //
+ // We only fold equality predicates (une, one, oeq, ueq).
+ // The ordered/unordered distinction doesn't matter here because the
+ // select outputs are known non-NaN, so the fcmp is never "unordered".
+ // Hence une == one and ueq == oeq for these operands.
+ bool IsNE;
+ switch (Pred) {
+ case FCmpInst::FCMP_UNE:
+ case FCmpInst::FCMP_ONE:
+ IsNE = true;
+ break;
+ case FCmpInst::FCMP_OEQ:
+ case FCmpInst::FCMP_UEQ:
+ IsNE = false;
+ break;
+ default:
+ // TODO: Could extend to relational predicates (olt, ogt, ...) using
+ // the known ordering of K1 vs K2, but equality is the common case.
+ return nullptr;
+ }
+
+ // Compute: do we want XOR or XNOR?
+ // SameOrder + NE → xor (different condition ↔ different value)
+ // SameOrder + EQ → not xor
+ // Swapped + NE → not xor (different condition ↔ same value)
+ // Swapped + EQ → xor
+ bool WantXor = (SameOrder == IsNE);
+
+ Value *Xor = Builder.CreateXor(C1, C2);
+ if (WantXor)
+ return replaceInstUsesWith(I, Xor);
+
+ // not(xor C1, C2)
+ Value *Not = Builder.CreateNot(Xor);
+ return replaceInstUsesWith(I, Not);
+}
+
/// Fold (C / X) < 0.0 --> X < 0.0 if possible. Swap predicate if necessary.
static Instruction *foldFCmpReciprocalAndZero(FCmpInst &I, Instruction *LHSI,
Constant *RHSC) {
@@ -8938,6 +9026,9 @@ Instruction *InstCombinerImpl::visitFCmpInst(FCmpInst &I) {
if (Instruction *R = foldFCmpWithFloorAndCeil(I, *this))
return R;
+ if (Instruction *R = foldFCmpSelectOfConstants(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 8c8b300bb2002..6cb44ad6dbfb4 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
+++ b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
@@ -720,6 +720,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 *foldFCmpSelectOfConstants(FCmpInst &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..096680cb81d18
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/fcmp-select-sign.ll
@@ -0,0 +1,189 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+;; ============================================================
+;; Core pattern: fcmp une (select C1, K, K'), (select C2, K, K') → xor
+;; ============================================================
+
+define i1 @fcmp_une_select_same_consts(double %a, double %b) {
+; CHECK-LABEL: @fcmp_une_select_same_consts(
+; CHECK-NEXT: [[C0:%.*]] = fcmp ult double [[B:%.*]], 0.000000e+00
+; CHECK-NEXT: [[C1:%.*]] = fcmp ult double [[A:%.*]], 0.000000e+00
+; CHECK-NEXT: [[RES:%.*]] = xor i1 [[C0]], [[C1]]
+; CHECK-NEXT: ret i1 [[RES]]
+;
+ %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 → not(xor) = same sign check
+;; ============================================================
+
+define i1 @fcmp_oeq_select_same_consts(double %a, double %b) {
+; CHECK-LABEL: @fcmp_oeq_select_same_consts(
+; CHECK-NEXT: [[C0:%.*]] = fcmp ult double [[B:%.*]], 0.000000e+00
+; CHECK-NEXT: [[C1:%.*]] = fcmp oge double [[A:%.*]], 0.000000e+00
+; CHECK-NEXT: [[XOR:%.*]] = xor i1 [[C0]], [[C1]]
+; CHECK-NEXT: ret i1 [[XOR]]
+;
+ %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) → also xor
+;; ============================================================
+
+define i1 @fcmp_one_select_same_consts(double %a, double %b) {
+; CHECK-LABEL: @fcmp_one_select_same_consts(
+; CHECK-NEXT: [[C0:%.*]] = fcmp ult double [[B:%.*]], 0.000000e+00
+; CHECK-NEXT: [[C1:%.*]] = fcmp ult double [[A:%.*]], 0.000000e+00
+; CHECK-NEXT: [[RES:%.*]] = xor i1 [[C0]], [[C1]]
+; CHECK-NEXT: ret i1 [[RES]]
+;
+ %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
+}
+
+;; ============================================================
+;; Swapped constants: (select C1, K1, K2) vs (select C2, K2, K1)
+;; une with swapped → not(xor)
+;; ============================================================
+
+define i1 @fcmp_une_select_swapped_consts(double %a, double %b) {
+; CHECK-LABEL: @fcmp_une_select_swapped_consts(
+; CHECK-NEXT: [[C0:%.*]] = fcmp ult double [[B:%.*]], 0.000000e+00
+; CHECK-NEXT: [[C1:%.*]] = fcmp oge double [[A:%.*]], 0.000000e+00
+; CHECK-NEXT: [[XOR:%.*]] = xor i1 [[C0]], [[C1]]
+; CHECK-NEXT: ret i1 [[XOR]]
+;
+ %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: [[C0:%.*]] = fcmp olt double [[B:%.*]], 5.000000e+00
+; CHECK-NEXT: [[C1:%.*]] = fcmp olt double [[A:%.*]], 5.000000e+00
+; CHECK-NEXT: [[RES:%.*]] = xor i1 [[C0]], [[C1]]
+; CHECK-NEXT: ret i1 [[RES]]
+;
+ %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: [[C0:%.*]] = fcmp ult float [[B:%.*]], 0.000000e+00
+; CHECK-NEXT: [[C1:%.*]] = fcmp ult float [[A:%.*]], 0.000000e+00
+; CHECK-NEXT: [[RES:%.*]] = xor i1 [[C0]], [[C1]]
+; CHECK-NEXT: ret i1 [[RES]]
+;
+ %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
+}
+
+;; ============================================================
+;; Negative test: constants are the same (K1 == K2) → no fold
+;; ============================================================
+
+define i1 @fcmp_une_select_same_val_no_fold(double %a, double %b) {
+; CHECK-LABEL: @fcmp_une_select_same_val_no_fold(
+; 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
+}
+
+;; ============================================================
+;; Negative test: different constant pairs → no fold
+;; ============================================================
+
+define i1 @fcmp_une_select_diff_pairs_no_fold(double %a, double %b) {
+; CHECK-LABEL: @fcmp_une_select_diff_pairs_no_fold(
+; 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
+}
+
+;; ============================================================
+;; Negative test: relational predicate (olt) → no fold (yet)
+;; ============================================================
+
+define i1 @fcmp_olt_select_no_fold(double %a, double %b) {
+; CHECK-LABEL: @fcmp_olt_select_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: [[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
+ %v4 = fcmp olt 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: [[C0:%.*]] = fcmp ult <2 x double> [[B:%.*]], zeroinitializer
+; CHECK-NEXT: [[C1:%.*]] = fcmp ult <2 x double> [[A:%.*]], zeroinitializer
+; CHECK-NEXT: [[RES:%.*]] = xor <2 x i1> [[C0]], [[C1]]
+; CHECK-NEXT: ret <2 x i1> [[RES]]
+;
+ %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
+}
More information about the cfe-commits
mailing list