[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