[llvm] [X86] Canonicalize ordered FP compares to unordered with NoNaNs to preserve memory folding (PR #215361)

via llvm-commits llvm-commits at lists.llvm.org
Mon Aug 10 11:48:04 PDT 2026


https://github.com/AZero13 updated https://github.com/llvm/llvm-project/pull/215361

>From 6695807ae4778fabcaaff2003c98799b0c1392f2 Mon Sep 17 00:00:00 2001
From: AZero13 <gfunni234 at gmail.com>
Date: Mon, 10 Aug 2026 14:12:08 -0400
Subject: [PATCH 1/2] Precommit test for X86 NoNaNs FSETCC memory fold

---
 .../CodeGen/X86/fno-honor-nans-memory-fold.ll | 44 +++++++++++++++++++
 1 file changed, 44 insertions(+)
 create mode 100644 llvm/test/CodeGen/X86/fno-honor-nans-memory-fold.ll

diff --git a/llvm/test/CodeGen/X86/fno-honor-nans-memory-fold.ll b/llvm/test/CodeGen/X86/fno-honor-nans-memory-fold.ll
new file mode 100644
index 0000000000000..48cb9d93f91eb
--- /dev/null
+++ b/llvm/test/CodeGen/X86/fno-honor-nans-memory-fold.ll
@@ -0,0 +1,44 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc < %s -mtriple=x86_64-unknown-unknown -mattr=+avx | FileCheck %s --check-prefix=AVX
+; RUN: llc < %s -mtriple=x86_64-unknown-unknown -mattr=-avx | FileCheck %s --check-prefix=SSE
+
+define float @compare_flip(float %A) {
+; AVX-LABEL: compare_flip:
+; AVX:       # %bb.0:
+; AVX-NEXT:    vmovss {{.*#+}} xmm1 = [1.00999999E+0,0.0E+0,0.0E+0,0.0E+0]
+; AVX-NEXT:    vcmpless %xmm0, %xmm1, %xmm0
+; AVX-NEXT:    vmovss {{.*#+}} xmm1 = [1.0E+0,0.0E+0,0.0E+0,0.0E+0]
+; AVX-NEXT:    vandps %xmm1, %xmm0, %xmm0
+; AVX-NEXT:    retq
+;
+; SSE-LABEL: compare_flip:
+; SSE:       # %bb.0:
+; SSE-NEXT:    movss {{.*#+}} xmm1 = [1.00999999E+0,0.0E+0,0.0E+0,0.0E+0]
+; SSE-NEXT:    cmpless %xmm0, %xmm1
+; SSE-NEXT:    movss {{.*#+}} xmm0 = [1.0E+0,0.0E+0,0.0E+0,0.0E+0]
+; SSE-NEXT:    andps %xmm1, %xmm0
+; SSE-NEXT:    retq
+  %cmp = fcmp nnan oge float %A, 1.01
+  %sel = select i1 %cmp, float 1.0, float 0.0
+  ret float %sel
+}
+
+define <4 x float> @compare_flip_vec(<4 x float> %A) {
+; AVX-LABEL: compare_flip_vec:
+; AVX:       # %bb.0:
+; AVX-NEXT:    vbroadcastss {{.*#+}} xmm1 = [1.00999999E+0,1.00999999E+0,1.00999999E+0,1.00999999E+0]
+; AVX-NEXT:    vcmpleps %xmm0, %xmm1, %xmm0
+; AVX-NEXT:    vandps {{\.?LCPI[0-9]+_[0-9]+}}(%rip), %xmm0, %xmm0
+; AVX-NEXT:    retq
+;
+; SSE-LABEL: compare_flip_vec:
+; SSE:       # %bb.0:
+; SSE-NEXT:    movaps {{.*#+}} xmm1 = [1.00999999E+0,1.00999999E+0,1.00999999E+0,1.00999999E+0]
+; SSE-NEXT:    cmpleps %xmm0, %xmm1
+; SSE-NEXT:    andps {{\.?LCPI[0-9]+_[0-9]+}}(%rip), %xmm1
+; SSE-NEXT:    movaps %xmm1, %xmm0
+; SSE-NEXT:    retq
+  %cmp = fcmp nnan oge <4 x float> %A, <float 1.01, float 1.01, float 1.01, float 1.01>
+  %sel = select <4 x i1> %cmp, <4 x float> <float 1.0, float 1.0, float 1.0, float 1.0>, <4 x float> zeroinitializer
+  ret <4 x float> %sel
+}

>From f332cbbb579fcfbc21539c51b8f9f9a91637ccc8 Mon Sep 17 00:00:00 2001
From: AZero13 <gfunni234 at gmail.com>
Date: Mon, 10 Aug 2026 14:34:31 -0400
Subject: [PATCH 2/2] [X86] Canonicalize ordered FP compares to unordered with
 NoNaNs to preserve memory folding

Under `-fno-honor-nans` (or with `nnan` flags), InstCombine canonicalizes unordered FP comparisons (like `UGE`) to their ordered equivalents (like `OGE`).

However, X86 lacks native condition codes for certain ordered compares (like `OGE`). To handle this, `translateX86FSETCC` historically maps `OGE` to `LE` and swaps the operands.

Because X86 compare instructions only accept memory operands in the second position, this swap forces constants out of the memory slot and into a register (`VMOVSS`), completely destroying memory folding opportunities.
---
 llvm/lib/Target/X86/X86ISelLowering.cpp       | 42 +++++++-
 .../CodeGen/X86/fno-honor-nans-memory-fold.ll | 97 ++++++++++++++++---
 llvm/test/CodeGen/X86/movmsk-cmp.ll           | 10 +-
 3 files changed, 129 insertions(+), 20 deletions(-)

diff --git a/llvm/lib/Target/X86/X86ISelLowering.cpp b/llvm/lib/Target/X86/X86ISelLowering.cpp
index dfda1157a720e..7cc84a8ec1b79 100644
--- a/llvm/lib/Target/X86/X86ISelLowering.cpp
+++ b/llvm/lib/Target/X86/X86ISelLowering.cpp
@@ -24220,10 +24220,38 @@ static bool cheapX86FSETCC_SSE(ISD::CondCode SetCCOpcode) {
 /// Turns an ISD::CondCode into a value suitable for SSE floating-point mask
 /// CMPs.
 static unsigned translateX86FSETCC(ISD::CondCode SetCCOpcode, SDValue &Op0,
-                                   SDValue &Op1, bool &IsAlwaysSignaling) {
+                                   SDValue &Op1, bool &IsAlwaysSignaling,
+                                   bool NoNaNs = false) {
   unsigned SSECC;
   bool Swap = false;
 
+  if (NoNaNs) {
+    // Under NoNaNs, ordered and unordered comparisons are semantically
+    // equivalent. Map ordered comparisons to their unordered counterparts
+    // (and vice-versa) to avoid operand swapping in translateX86FSETCC,
+    // preserving memory folding opportunities for constants.
+    // Both SETOGT/SETOGE and SETGT/SETGE (non-O forms) are handled as DAG
+    // combines or lowering may produce non-ordered condition codes.
+    switch (SetCCOpcode) {
+    case ISD::SETOGT:
+    case ISD::SETGT:
+      SetCCOpcode = ISD::SETUGT;
+      break;
+    case ISD::SETOGE:
+    case ISD::SETGE:
+      SetCCOpcode = ISD::SETUGE;
+      break;
+    case ISD::SETULE:
+      SetCCOpcode = ISD::SETLE;
+      break;
+    case ISD::SETULT:
+      SetCCOpcode = ISD::SETLT;
+      break;
+    default:
+      break;
+    }
+  }
+
   // SSE Condition code mapping:
   //  0 - EQ
   //  1 - LT
@@ -24489,7 +24517,10 @@ static SDValue LowerVSETCC(SDValue Op, const X86Subtarget &Subtarget,
 
     SDValue Cmp;
     bool IsAlwaysSignaling;
-    unsigned SSECC = translateX86FSETCC(Cond, Op0, Op1, IsAlwaysSignaling);
+    bool NoNaNs = Op->getFlags().hasNoNaNs() ||
+                  (DAG.isKnownNeverNaN(Op0) && DAG.isKnownNeverNaN(Op1));
+    unsigned SSECC =
+        translateX86FSETCC(Cond, Op0, Op1, IsAlwaysSignaling, NoNaNs);
     if ((Cond == ISD::SETO || Cond == ISD::SETUO) && Op0 != Op1) {
       if (DAG.isKnownNeverNaN(Op1))
         Op1 = Op0;
@@ -25603,10 +25634,13 @@ SDValue X86TargetLowering::LowerSELECT(SDValue Op, SelectionDAG &DAG) const {
   if (Cond.getOpcode() == ISD::SETCC && isScalarFPTypeInSSEReg(VT) &&
       VT == Cond.getOperand(0).getSimpleValueType() && Cond->hasOneUse()) {
     SDValue CondOp0 = Cond.getOperand(0), CondOp1 = Cond.getOperand(1);
-    bool IsAlwaysSignaling;
+    bool IsAlwaysSignaling = false;
+    bool NoNaNs =
+        Cond->getFlags().hasNoNaNs() ||
+        (DAG.isKnownNeverNaN(CondOp0) && DAG.isKnownNeverNaN(CondOp1));
     unsigned SSECC =
         translateX86FSETCC(cast<CondCodeSDNode>(Cond.getOperand(2))->get(),
-                           CondOp0, CondOp1, IsAlwaysSignaling);
+                           CondOp0, CondOp1, IsAlwaysSignaling, NoNaNs);
 
     if (Subtarget.hasAVX512()) {
       SDValue Cmp =
diff --git a/llvm/test/CodeGen/X86/fno-honor-nans-memory-fold.ll b/llvm/test/CodeGen/X86/fno-honor-nans-memory-fold.ll
index 48cb9d93f91eb..ddd9fe015b113 100644
--- a/llvm/test/CodeGen/X86/fno-honor-nans-memory-fold.ll
+++ b/llvm/test/CodeGen/X86/fno-honor-nans-memory-fold.ll
@@ -5,17 +5,15 @@
 define float @compare_flip(float %A) {
 ; AVX-LABEL: compare_flip:
 ; AVX:       # %bb.0:
-; AVX-NEXT:    vmovss {{.*#+}} xmm1 = [1.00999999E+0,0.0E+0,0.0E+0,0.0E+0]
-; AVX-NEXT:    vcmpless %xmm0, %xmm1, %xmm0
+; AVX-NEXT:    vcmpnltss {{\.?LCPI[0-9]+_[0-9]+}}(%rip), %xmm0, %xmm0
 ; AVX-NEXT:    vmovss {{.*#+}} xmm1 = [1.0E+0,0.0E+0,0.0E+0,0.0E+0]
 ; AVX-NEXT:    vandps %xmm1, %xmm0, %xmm0
 ; AVX-NEXT:    retq
 ;
 ; SSE-LABEL: compare_flip:
 ; SSE:       # %bb.0:
-; SSE-NEXT:    movss {{.*#+}} xmm1 = [1.00999999E+0,0.0E+0,0.0E+0,0.0E+0]
-; SSE-NEXT:    cmpless %xmm0, %xmm1
-; SSE-NEXT:    movss {{.*#+}} xmm0 = [1.0E+0,0.0E+0,0.0E+0,0.0E+0]
+; SSE-NEXT:    cmpnltss {{\.?LCPI[0-9]+_[0-9]+}}(%rip), %xmm0
+; SSE-NEXT:    movss {{.*#+}} xmm1 = [1.0E+0,0.0E+0,0.0E+0,0.0E+0]
 ; SSE-NEXT:    andps %xmm1, %xmm0
 ; SSE-NEXT:    retq
   %cmp = fcmp nnan oge float %A, 1.01
@@ -26,19 +24,96 @@ define float @compare_flip(float %A) {
 define <4 x float> @compare_flip_vec(<4 x float> %A) {
 ; AVX-LABEL: compare_flip_vec:
 ; AVX:       # %bb.0:
-; AVX-NEXT:    vbroadcastss {{.*#+}} xmm1 = [1.00999999E+0,1.00999999E+0,1.00999999E+0,1.00999999E+0]
-; AVX-NEXT:    vcmpleps %xmm0, %xmm1, %xmm0
+; AVX-NEXT:    vcmpnltps {{\.?LCPI[0-9]+_[0-9]+}}(%rip), %xmm0, %xmm0
 ; AVX-NEXT:    vandps {{\.?LCPI[0-9]+_[0-9]+}}(%rip), %xmm0, %xmm0
 ; AVX-NEXT:    retq
 ;
 ; SSE-LABEL: compare_flip_vec:
 ; SSE:       # %bb.0:
-; SSE-NEXT:    movaps {{.*#+}} xmm1 = [1.00999999E+0,1.00999999E+0,1.00999999E+0,1.00999999E+0]
-; SSE-NEXT:    cmpleps %xmm0, %xmm1
-; SSE-NEXT:    andps {{\.?LCPI[0-9]+_[0-9]+}}(%rip), %xmm1
-; SSE-NEXT:    movaps %xmm1, %xmm0
+; SSE-NEXT:    cmpnltps {{\.?LCPI[0-9]+_[0-9]+}}(%rip), %xmm0
+; SSE-NEXT:    andps {{\.?LCPI[0-9]+_[0-9]+}}(%rip), %xmm0
 ; SSE-NEXT:    retq
   %cmp = fcmp nnan oge <4 x float> %A, <float 1.01, float 1.01, float 1.01, float 1.01>
   %sel = select <4 x i1> %cmp, <4 x float> <float 1.0, float 1.0, float 1.0, float 1.0>, <4 x float> zeroinitializer
   ret <4 x float> %sel
 }
+
+define float @compare_ogt(float %A) {
+; AVX-LABEL: compare_ogt:
+; AVX:       # %bb.0:
+; AVX-NEXT:    vcmpnless {{\.?LCPI[0-9]+_[0-9]+}}(%rip), %xmm0, %xmm0
+; AVX-NEXT:    vmovss {{.*#+}} xmm1 = [1.0E+0,0.0E+0,0.0E+0,0.0E+0]
+; AVX-NEXT:    vandps %xmm1, %xmm0, %xmm0
+; AVX-NEXT:    retq
+;
+; SSE-LABEL: compare_ogt:
+; SSE:       # %bb.0:
+; SSE-NEXT:    cmpnless {{\.?LCPI[0-9]+_[0-9]+}}(%rip), %xmm0
+; SSE-NEXT:    movss {{.*#+}} xmm1 = [1.0E+0,0.0E+0,0.0E+0,0.0E+0]
+; SSE-NEXT:    andps %xmm1, %xmm0
+; SSE-NEXT:    retq
+  %cmp = fcmp nnan ogt float %A, 1.01
+  %sel = select i1 %cmp, float 1.0, float 0.0
+  ret float %sel
+}
+
+define float @compare_ult(float %A) {
+; AVX-LABEL: compare_ult:
+; AVX:       # %bb.0:
+; AVX-NEXT:    vcmpltss {{\.?LCPI[0-9]+_[0-9]+}}(%rip), %xmm0, %xmm0
+; AVX-NEXT:    vmovss {{.*#+}} xmm1 = [1.0E+0,0.0E+0,0.0E+0,0.0E+0]
+; AVX-NEXT:    vandps %xmm1, %xmm0, %xmm0
+; AVX-NEXT:    retq
+;
+; SSE-LABEL: compare_ult:
+; SSE:       # %bb.0:
+; SSE-NEXT:    cmpltss {{\.?LCPI[0-9]+_[0-9]+}}(%rip), %xmm0
+; SSE-NEXT:    movss {{.*#+}} xmm1 = [1.0E+0,0.0E+0,0.0E+0,0.0E+0]
+; SSE-NEXT:    andps %xmm1, %xmm0
+; SSE-NEXT:    retq
+  %cmp = fcmp nnan ult float %A, 1.01
+  %sel = select i1 %cmp, float 1.0, float 0.0
+  ret float %sel
+}
+
+define float @compare_ule(float %A) {
+; AVX-LABEL: compare_ule:
+; AVX:       # %bb.0:
+; AVX-NEXT:    vcmpless {{\.?LCPI[0-9]+_[0-9]+}}(%rip), %xmm0, %xmm0
+; AVX-NEXT:    vmovss {{.*#+}} xmm1 = [1.0E+0,0.0E+0,0.0E+0,0.0E+0]
+; AVX-NEXT:    vandps %xmm1, %xmm0, %xmm0
+; AVX-NEXT:    retq
+;
+; SSE-LABEL: compare_ule:
+; SSE:       # %bb.0:
+; SSE-NEXT:    cmpless {{\.?LCPI[0-9]+_[0-9]+}}(%rip), %xmm0
+; SSE-NEXT:    movss {{.*#+}} xmm1 = [1.0E+0,0.0E+0,0.0E+0,0.0E+0]
+; SSE-NEXT:    andps %xmm1, %xmm0
+; SSE-NEXT:    retq
+  %cmp = fcmp nnan ule float %A, 1.01
+  %sel = select i1 %cmp, float 1.0, float 0.0
+  ret float %sel
+}
+
+define float @compare_never_nan(i32 %i) {
+; AVX-LABEL: compare_never_nan:
+; AVX:       # %bb.0:
+; AVX-NEXT:    vcvtsi2ss %edi, %xmm15, %xmm0
+; AVX-NEXT:    vcmpnltss {{\.?LCPI[0-9]+_[0-9]+}}(%rip), %xmm0, %xmm0
+; AVX-NEXT:    vmovss {{.*#+}} xmm1 = [1.0E+0,0.0E+0,0.0E+0,0.0E+0]
+; AVX-NEXT:    vandps %xmm1, %xmm0, %xmm0
+; AVX-NEXT:    retq
+;
+; SSE-LABEL: compare_never_nan:
+; SSE:       # %bb.0:
+; SSE-NEXT:    cvtsi2ss %edi, %xmm1
+; SSE-NEXT:    cmpnltss {{\.?LCPI[0-9]+_[0-9]+}}(%rip), %xmm1
+; SSE-NEXT:    movss {{.*#+}} xmm0 = [1.0E+0,0.0E+0,0.0E+0,0.0E+0]
+; SSE-NEXT:    andps %xmm1, %xmm0
+; SSE-NEXT:    retq
+  %A = sitofp i32 %i to float
+  %cmp = fcmp oge float %A, 1.01
+  %sel = select i1 %cmp, float 1.0, float 0.0
+  ret float %sel
+}
+
diff --git a/llvm/test/CodeGen/X86/movmsk-cmp.ll b/llvm/test/CodeGen/X86/movmsk-cmp.ll
index 09854d197dcd8..781f8eb285233 100644
--- a/llvm/test/CodeGen/X86/movmsk-cmp.ll
+++ b/llvm/test/CodeGen/X86/movmsk-cmp.ll
@@ -4421,8 +4421,8 @@ define i32 @PR39665_c_ray(<2 x double> %x, <2 x double> %y) {
 define i32 @PR39665_c_ray_select(<2 x double> %x, <2 x double> %y) {
 ; SSE-LABEL: PR39665_c_ray_select:
 ; SSE:       # %bb.0:
-; SSE-NEXT:    cmpltpd %xmm0, %xmm1
-; SSE-NEXT:    movmskpd %xmm1, %eax
+; SSE-NEXT:    cmpnlepd %xmm1, %xmm0
+; SSE-NEXT:    movmskpd %xmm0, %eax
 ; SSE-NEXT:    cmpl $3, %eax
 ; SSE-NEXT:    movl $42, %ecx
 ; SSE-NEXT:    movl $99, %eax
@@ -4431,7 +4431,7 @@ define i32 @PR39665_c_ray_select(<2 x double> %x, <2 x double> %y) {
 ;
 ; AVX1OR2-LABEL: PR39665_c_ray_select:
 ; AVX1OR2:       # %bb.0:
-; AVX1OR2-NEXT:    vcmpltpd %xmm0, %xmm1, %xmm0
+; AVX1OR2-NEXT:    vcmpnlepd %xmm1, %xmm0, %xmm0
 ; AVX1OR2-NEXT:    vpcmpeqd %xmm1, %xmm1, %xmm1
 ; AVX1OR2-NEXT:    vtestpd %xmm1, %xmm0
 ; AVX1OR2-NEXT:    movl $42, %ecx
@@ -4443,7 +4443,7 @@ define i32 @PR39665_c_ray_select(<2 x double> %x, <2 x double> %y) {
 ; KNL:       # %bb.0:
 ; KNL-NEXT:    # kill: def $xmm1 killed $xmm1 def $zmm1
 ; KNL-NEXT:    # kill: def $xmm0 killed $xmm0 def $zmm0
-; KNL-NEXT:    vcmpltpd %zmm0, %zmm1, %k0
+; KNL-NEXT:    vcmpnlepd %zmm1, %zmm0, %k0
 ; KNL-NEXT:    knotw %k0, %k0
 ; KNL-NEXT:    kmovw %k0, %eax
 ; KNL-NEXT:    testb $3, %al
@@ -4455,7 +4455,7 @@ define i32 @PR39665_c_ray_select(<2 x double> %x, <2 x double> %y) {
 ;
 ; SKX-LABEL: PR39665_c_ray_select:
 ; SKX:       # %bb.0:
-; SKX-NEXT:    vcmpltpd %xmm0, %xmm1, %k0
+; SKX-NEXT:    vcmpnlepd %xmm1, %xmm0, %k0
 ; SKX-NEXT:    knotw %k0, %k0
 ; SKX-NEXT:    kmovd %k0, %eax
 ; SKX-NEXT:    testb $3, %al



More information about the llvm-commits mailing list