[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