[llvm] [X86] Add SETCC to isGuaranteedNotToBeUndefOrPoisonForTargetNode (PR #192362)
Feng Zou via llvm-commits
llvm-commits at lists.llvm.org
Thu Apr 16 23:32:31 PDT 2026
https://github.com/fzou1 updated https://github.com/llvm/llvm-project/pull/192362
>From 24e38aaa5d41d9a23e27f42a5ef5fd98b4d41eac Mon Sep 17 00:00:00 2001
From: "Zou, Feng" <feng.zou at intel.com>
Date: Sat, 11 Apr 2026 19:52:43 +0530
Subject: [PATCH 1/3] [X86][APX] Combine and/or with freeze to ccmp/ctest
Resolve https://github.com/llvm/llvm-project/issues/191716.
---
llvm/lib/Target/X86/X86ISelLowering.cpp | 30 +++++++++++++++----
llvm/test/CodeGen/X86/apx/ccmp.ll | 38 +++++++++++++++++++++++++
llvm/test/CodeGen/X86/apx/ctest.ll | 37 ++++++++++++++++++++++++
3 files changed, 100 insertions(+), 5 deletions(-)
diff --git a/llvm/lib/Target/X86/X86ISelLowering.cpp b/llvm/lib/Target/X86/X86ISelLowering.cpp
index 6a00f57e78dd3..24df6a477ec35 100644
--- a/llvm/lib/Target/X86/X86ISelLowering.cpp
+++ b/llvm/lib/Target/X86/X86ISelLowering.cpp
@@ -52341,21 +52341,41 @@ static SDValue combineX86SubCmpForFlags(SDNode *N, SDValue Flag,
static SDValue combineAndOrForCcmpCtest(SDNode *N, SelectionDAG &DAG,
TargetLowering::DAGCombinerInfo &DCI,
const X86Subtarget &ST) {
- // and/or(setcc(cc0, flag0), setcc(cc1, sub (X, Y)))
+ // Combine AND/OR of two SETCC nodes into CCMP/CTEST:
+ //
+ // and/or(setcc(cc0, flag0), setcc(cc1, sub(X, Y)))
// ->
// setcc(cc1, ccmp(X, Y, ~cflags/cflags, cc0/~cc0, flag0))
-
- // and/or(setcc(cc0, flag0), setcc(cc1, cmp (X, 0)))
+ //
+ // and/or(setcc(cc0, flag0), setcc(cc1, cmp(X, 0)))
// ->
// setcc(cc1, ctest(X, X, ~cflags/cflags, cc0/~cc0, flag0))
//
// where cflags is determined by cc1.
+ //
+ // Fast-math canonicalization can introduce FREEZE nodes around SETCC
+ // operations to prevent poison propagation from unordered FP comparisons.
+ // We peek through single-use FREEZE nodes when matching:
+ //
+ // and/or(freeze(setcc(cc0, flag0)), freeze(setcc(cc1, sub(X, Y))))
+ // ->
+ // setcc(cc1, ccmp(X, Y, ~cflags/cflags, cc0/~cc0, flag0))
+ //
+ // and/or(freeze(setcc(cc0, flag0)), freeze(setcc(cc1, cmp(X, 0))))
+ // ->
+ // setcc(cc1, ctest(X, X, ~cflags/cflags, cc0/~cc0, flag0))
if (!ST.hasCCMP())
return SDValue();
- SDValue SetCC0 = N->getOperand(0);
- SDValue SetCC1 = N->getOperand(1);
+ auto PeekThroughFreeze = [](SDValue N) -> SDValue {
+ if (N.getOpcode() == ISD::FREEZE && N.hasOneUse())
+ return N.getOperand(0);
+ return N;
+ };
+
+ SDValue SetCC0 = PeekThroughFreeze(N->getOperand(0));
+ SDValue SetCC1 = PeekThroughFreeze(N->getOperand(1));
if (SetCC0.getOpcode() != X86ISD::SETCC ||
SetCC1.getOpcode() != X86ISD::SETCC)
return SDValue();
diff --git a/llvm/test/CodeGen/X86/apx/ccmp.ll b/llvm/test/CodeGen/X86/apx/ccmp.ll
index a9cf0c060e729..af4ef1b19189f 100644
--- a/llvm/test/CodeGen/X86/apx/ccmp.ll
+++ b/llvm/test/CodeGen/X86/apx/ccmp.ll
@@ -1816,5 +1816,43 @@ if.end: ; preds = %entry, %if.then
ret void
}
+; Fast-math patterns with CCMP instructions.
+; Fast-math canonicalization can introduce FREEZE nodes around SETCC operations,
+; and these tests verify that CCMP pattern matching works through those FREEZE
+; nodes.
+define i32 @test_or_fp_int(double %a, double %b, i32 %c, i32 %d) {
+; CHECK-LABEL: test_or_fp_int:
+; CHECK: # %bb.0:
+; CHECK-NEXT: ucomisd %xmm1, %xmm0 # encoding: [0x66,0x0f,0x2e,0xc1]
+; CHECK-NEXT: ccmpal {dfv=} %esi, %edi # encoding: [0x62,0xf4,0x04,0x07,0x39,0xf7]
+; CHECK-NEXT: setne %al # encoding: [0x0f,0x95,0xc0]
+; CHECK-NEXT: movzbl %al, %eax # encoding: [0x0f,0xb6,0xc0]
+; CHECK-NEXT: retq # encoding: [0xc3]
+;
+; NDD-LABEL: test_or_fp_int:
+; NDD: # %bb.0:
+; NDD-NEXT: ucomisd %xmm1, %xmm0 # encoding: [0x66,0x0f,0x2e,0xc1]
+; NDD-NEXT: ccmpal {dfv=} %esi, %edi # encoding: [0x62,0xf4,0x04,0x07,0x39,0xf7]
+; NDD-NEXT: setne %al # encoding: [0x0f,0x95,0xc0]
+; NDD-NEXT: movzbl %al, %eax # encoding: [0x0f,0xb6,0xc0]
+; NDD-NEXT: retq # encoding: [0xc3]
+;
+; SETZUCC-LABEL: test_or_fp_int:
+; SETZUCC: # %bb.0:
+; SETZUCC-NEXT: cmpl %esi, %edi # encoding: [0x39,0xf7]
+; SETZUCC-NEXT: setzune %al # encoding: [0x62,0xf4,0x7f,0x18,0x45,0xc0]
+; SETZUCC-NEXT: ucomisd %xmm1, %xmm0 # encoding: [0x66,0x0f,0x2e,0xc1]
+; SETZUCC-NEXT: setzube %cl # encoding: [0x62,0xf4,0x7f,0x18,0x46,0xc1]
+; SETZUCC-NEXT: orb %al, %cl # encoding: [0x08,0xc1]
+; SETZUCC-NEXT: movzbl %cl, %eax # encoding: [0x0f,0xb6,0xc1]
+; SETZUCC-NEXT: andl $1, %eax # encoding: [0x83,0xe0,0x01]
+; SETZUCC-NEXT: retq # encoding: [0xc3]
+ %cmp = icmp ne i32 %c, %d
+ %cmp1 = fcmp fast ole double %a, %b
+ %sel = select i1 %cmp, i1 true, i1 %cmp1
+ %ext = zext i1 %sel to i32
+ ret i32 %ext
+}
+
declare dso_local void @foo(...)
declare {i64, i1} @llvm.ssub.with.overflow.i64(i64, i64) nounwind readnone
diff --git a/llvm/test/CodeGen/X86/apx/ctest.ll b/llvm/test/CodeGen/X86/apx/ctest.ll
index 0bd4ac1862ca1..11dffe4049f7d 100644
--- a/llvm/test/CodeGen/X86/apx/ctest.ll
+++ b/llvm/test/CodeGen/X86/apx/ctest.ll
@@ -1427,4 +1427,41 @@ bb:
ret void
}
+; Fast-math patterns with CTEST instructions.
+; Fast-math canonicalization can introduce FREEZE nodes around SETCC operations,
+; and these tests verify that CTEST pattern matching works through those FREEZE
+; nodes.
+define i32 @test_and_fp_int(double %a, i32 %b) {
+; CHECK-LABEL: test_and_fp_int:
+; CHECK: # %bb.0:
+; CHECK-NEXT: ucomisd {{\.?LCPI[0-9]+_[0-9]+}}(%rip), %xmm0
+; CHECK-NEXT: ctestbl {dfv=zf} %edi, %edi
+; CHECK-NEXT: setne %al
+; CHECK-NEXT: movzbl %al, %eax
+; CHECK-NEXT: retq
+;
+; NDD-LABEL: test_and_fp_int:
+; NDD: # %bb.0:
+; NDD-NEXT: ucomisd {{\.?LCPI[0-9]+_[0-9]+}}(%rip), %xmm0
+; NDD-NEXT: ctestbl {dfv=zf} %edi, %edi
+; NDD-NEXT: setne %al
+; NDD-NEXT: movzbl %al, %eax
+; NDD-NEXT: retq
+;
+; SETZUCC-LABEL: test_and_fp_int:
+; SETZUCC: # %bb.0:
+; SETZUCC-NEXT: testl %edi, %edi # encoding: [0x85,0xff]
+; SETZUCC-NEXT: setzune %al # encoding: [0x62,0xf4,0x7f,0x18,0x45,0xc0]
+; SETZUCC-NEXT: ucomisd {{\.?LCPI[0-9]+_[0-9]+}}(%rip), %xmm0 # encoding: [0x66,0x0f,0x2e,0x05,A,A,A,A]
+; SETZUCC-NEXT: # fixup A - offset: 4, value: {{\.?LCPI[0-9]+_[0-9]+}}, kind: reloc_riprel_4byte
+; SETZUCC-NEXT: setzub %cl # encoding: [0x62,0xf4,0x7f,0x18,0x42,0xc1]
+; SETZUCC-NEXT: andb %al, %cl # encoding: [0x20,0xc1]
+; SETZUCC-NEXT: movzbl %cl, %eax # encoding: [0x0f,0xb6,0xc1]
+; SETZUCC-NEXT: retq # encoding: [0xc3]
+ %cmp = icmp ne i32 %b, 0
+ %cmp1 = fcmp fast olt double %a, 1.500000e+00
+ %sel = select i1 %cmp, i1 %cmp1, i1 false
+ %ext = zext i1 %sel to i32
+ ret i32 %ext
+}
declare dso_local void @foo(...)
>From 9a9d8befe8e534de99589d0576dac93bc2cceadc Mon Sep 17 00:00:00 2001
From: "Zou, Feng" <feng.zou at intel.com>
Date: Wed, 15 Apr 2026 10:41:56 +0530
Subject: [PATCH 2/3] [X86] Add SETCC to
isGuaranteedNotToBeUndefOrPoisonForTargetNode
X86ISD::SETCC always produces a well-defined result (0 or 1) based on
EFLAGS and cannot produce undef or poison values. Adding it to
isGuaranteedNotToBeUndefOrPoisonForTargetNode allows the optimizer to
eliminate FREEZE nodes around SETCC operations. With that, CCMP/CTEST
pattern matching works naturally with fastmath comparisons.
---
llvm/lib/Target/X86/X86ISelLowering.cpp | 32 ++++++-------------------
llvm/test/CodeGen/X86/apx/ccmp.ll | 7 +++---
llvm/test/CodeGen/X86/apx/ctest.ll | 6 ++---
llvm/test/CodeGen/X86/freeze-binary.ll | 20 ++++++++--------
4 files changed, 23 insertions(+), 42 deletions(-)
diff --git a/llvm/lib/Target/X86/X86ISelLowering.cpp b/llvm/lib/Target/X86/X86ISelLowering.cpp
index 24df6a477ec35..811f84e41cbba 100644
--- a/llvm/lib/Target/X86/X86ISelLowering.cpp
+++ b/llvm/lib/Target/X86/X86ISelLowering.cpp
@@ -45755,6 +45755,8 @@ bool X86TargetLowering::isGuaranteedNotToBeUndefOrPoisonForTargetNode(
case X86ISD::GlobalBaseReg:
case X86ISD::Wrapper:
case X86ISD::WrapperRIP:
+ // SETCC always produces a well-defined result (0 or 1) based on EFLAGS.
+ case X86ISD::SETCC:
return true;
case X86ISD::PACKSS:
case X86ISD::PACKUS: {
@@ -52341,41 +52343,21 @@ static SDValue combineX86SubCmpForFlags(SDNode *N, SDValue Flag,
static SDValue combineAndOrForCcmpCtest(SDNode *N, SelectionDAG &DAG,
TargetLowering::DAGCombinerInfo &DCI,
const X86Subtarget &ST) {
- // Combine AND/OR of two SETCC nodes into CCMP/CTEST:
- //
- // and/or(setcc(cc0, flag0), setcc(cc1, sub(X, Y)))
+ // and/or(setcc(cc0, flag0), setcc(cc1, sub (X, Y)))
// ->
// setcc(cc1, ccmp(X, Y, ~cflags/cflags, cc0/~cc0, flag0))
- //
- // and/or(setcc(cc0, flag0), setcc(cc1, cmp(X, 0)))
+
+ // and/or(setcc(cc0, flag0), setcc(cc1, cmp (X, 0)))
// ->
// setcc(cc1, ctest(X, X, ~cflags/cflags, cc0/~cc0, flag0))
//
// where cflags is determined by cc1.
- //
- // Fast-math canonicalization can introduce FREEZE nodes around SETCC
- // operations to prevent poison propagation from unordered FP comparisons.
- // We peek through single-use FREEZE nodes when matching:
- //
- // and/or(freeze(setcc(cc0, flag0)), freeze(setcc(cc1, sub(X, Y))))
- // ->
- // setcc(cc1, ccmp(X, Y, ~cflags/cflags, cc0/~cc0, flag0))
- //
- // and/or(freeze(setcc(cc0, flag0)), freeze(setcc(cc1, cmp(X, 0))))
- // ->
- // setcc(cc1, ctest(X, X, ~cflags/cflags, cc0/~cc0, flag0))
if (!ST.hasCCMP())
return SDValue();
- auto PeekThroughFreeze = [](SDValue N) -> SDValue {
- if (N.getOpcode() == ISD::FREEZE && N.hasOneUse())
- return N.getOperand(0);
- return N;
- };
-
- SDValue SetCC0 = PeekThroughFreeze(N->getOperand(0));
- SDValue SetCC1 = PeekThroughFreeze(N->getOperand(1));
+ SDValue SetCC0 = N->getOperand(0);
+ SDValue SetCC1 = N->getOperand(1);
if (SetCC0.getOpcode() != X86ISD::SETCC ||
SetCC1.getOpcode() != X86ISD::SETCC)
return SDValue();
diff --git a/llvm/test/CodeGen/X86/apx/ccmp.ll b/llvm/test/CodeGen/X86/apx/ccmp.ll
index af4ef1b19189f..2c62e608223bc 100644
--- a/llvm/test/CodeGen/X86/apx/ccmp.ll
+++ b/llvm/test/CodeGen/X86/apx/ccmp.ll
@@ -1817,9 +1817,9 @@ if.end: ; preds = %entry, %if.then
}
; Fast-math patterns with CCMP instructions.
-; Fast-math canonicalization can introduce FREEZE nodes around SETCC operations,
-; and these tests verify that CCMP pattern matching works through those FREEZE
-; nodes.
+; Fast-math canonicalization can introduce FREEZE nodes around SETCC operations.
+; With SETCC in isGuaranteedNotToBeUndefOrPoisonForTargetNode, these FREEZE
+; nodes are eliminated early, allowing CCMP pattern matching to work naturally.
define i32 @test_or_fp_int(double %a, double %b, i32 %c, i32 %d) {
; CHECK-LABEL: test_or_fp_int:
; CHECK: # %bb.0:
@@ -1845,7 +1845,6 @@ define i32 @test_or_fp_int(double %a, double %b, i32 %c, i32 %d) {
; SETZUCC-NEXT: setzube %cl # encoding: [0x62,0xf4,0x7f,0x18,0x46,0xc1]
; SETZUCC-NEXT: orb %al, %cl # encoding: [0x08,0xc1]
; SETZUCC-NEXT: movzbl %cl, %eax # encoding: [0x0f,0xb6,0xc1]
-; SETZUCC-NEXT: andl $1, %eax # encoding: [0x83,0xe0,0x01]
; SETZUCC-NEXT: retq # encoding: [0xc3]
%cmp = icmp ne i32 %c, %d
%cmp1 = fcmp fast ole double %a, %b
diff --git a/llvm/test/CodeGen/X86/apx/ctest.ll b/llvm/test/CodeGen/X86/apx/ctest.ll
index 11dffe4049f7d..8d5e36ed513f5 100644
--- a/llvm/test/CodeGen/X86/apx/ctest.ll
+++ b/llvm/test/CodeGen/X86/apx/ctest.ll
@@ -1428,9 +1428,9 @@ bb:
}
; Fast-math patterns with CTEST instructions.
-; Fast-math canonicalization can introduce FREEZE nodes around SETCC operations,
-; and these tests verify that CTEST pattern matching works through those FREEZE
-; nodes.
+; Fast-math canonicalization can introduce FREEZE nodes around SETCC operations.
+; With SETCC in isGuaranteedNotToBeUndefOrPoisonForTargetNode, these FREEZE
+; nodes are eliminated early, allowing CTEST pattern matching to work naturally.
define i32 @test_and_fp_int(double %a, i32 %b) {
; CHECK-LABEL: test_and_fp_int:
; CHECK: # %bb.0:
diff --git a/llvm/test/CodeGen/X86/freeze-binary.ll b/llvm/test/CodeGen/X86/freeze-binary.ll
index 46b2571e196bb..15e9409a46eaa 100644
--- a/llvm/test/CodeGen/X86/freeze-binary.ll
+++ b/llvm/test/CodeGen/X86/freeze-binary.ll
@@ -866,11 +866,11 @@ define i32 @freeze_ssubo(i32 %a0, i32 %a1, i8 %a2, i8 %a3) nounwind {
; X86-LABEL: freeze_ssubo:
; X86: # %bb.0:
; X86-NEXT: movl {{[0-9]+}}(%esp), %eax
-; X86-NEXT: movzbl {{[0-9]+}}(%esp), %edx
-; X86-NEXT: xorl %ecx, %ecx
-; X86-NEXT: addb {{[0-9]+}}(%esp), %dl
-; X86-NEXT: setb %cl
-; X86-NEXT: andl $1, %ecx
+; X86-NEXT: movzbl {{[0-9]+}}(%esp), %ecx
+; X86-NEXT: xorl %edx, %edx
+; X86-NEXT: addb {{[0-9]+}}(%esp), %cl
+; X86-NEXT: setb %dl
+; X86-NEXT: movzbl %dl, %ecx
; X86-NEXT: subl %ecx, %eax
; X86-NEXT: subl {{[0-9]+}}(%esp), %eax
; X86-NEXT: retl
@@ -898,11 +898,11 @@ define i32 @freeze_usubo(i32 %a0, i32 %a1, i8 %a2, i8 %a3) nounwind {
; X86-LABEL: freeze_usubo:
; X86: # %bb.0:
; X86-NEXT: movl {{[0-9]+}}(%esp), %eax
-; X86-NEXT: movzbl {{[0-9]+}}(%esp), %edx
-; X86-NEXT: xorl %ecx, %ecx
-; X86-NEXT: addb {{[0-9]+}}(%esp), %dl
-; X86-NEXT: setb %cl
-; X86-NEXT: andl $1, %ecx
+; X86-NEXT: movzbl {{[0-9]+}}(%esp), %ecx
+; X86-NEXT: xorl %edx, %edx
+; X86-NEXT: addb {{[0-9]+}}(%esp), %cl
+; X86-NEXT: setb %dl
+; X86-NEXT: movzbl %dl, %ecx
; X86-NEXT: subl %ecx, %eax
; X86-NEXT: subl {{[0-9]+}}(%esp), %eax
; X86-NEXT: retl
>From a50d4e5e6604fe49f39d90424a460c7fe514ce7d Mon Sep 17 00:00:00 2001
From: Feng Zou <feng.zou at intel.com>
Date: Fri, 17 Apr 2026 14:29:20 +0800
Subject: [PATCH 3/3] Add SETCC and SETCC_CARRY handling.
---
llvm/lib/Target/X86/X86ISelLowering.cpp | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/llvm/lib/Target/X86/X86ISelLowering.cpp b/llvm/lib/Target/X86/X86ISelLowering.cpp
index 811f84e41cbba..f514cd62a3775 100644
--- a/llvm/lib/Target/X86/X86ISelLowering.cpp
+++ b/llvm/lib/Target/X86/X86ISelLowering.cpp
@@ -45755,8 +45755,10 @@ bool X86TargetLowering::isGuaranteedNotToBeUndefOrPoisonForTargetNode(
case X86ISD::GlobalBaseReg:
case X86ISD::Wrapper:
case X86ISD::WrapperRIP:
- // SETCC always produces a well-defined result (0 or 1) based on EFLAGS.
+ // SETCC/SETCC_CARRY always produces a well-defined result based on
+ // EFLAGS/carry flag.
case X86ISD::SETCC:
+ case X86ISD::SETCC_CARRY:
return true;
case X86ISD::PACKSS:
case X86ISD::PACKUS: {
@@ -45890,6 +45892,11 @@ bool X86TargetLowering::canCreateUndefOrPoisonForTargetNode(
case X86ISD::GF2P8AFFINEQB:
case X86ISD::GF2P8MULB:
return false;
+ // SETCC/SETCC_CARRY always produces a well-defined result based on
+ // EFLAGS/carry flag.
+ case X86ISD::SETCC:
+ case X86ISD::SETCC_CARRY:
+ return false;
case ISD::INTRINSIC_WO_CHAIN:
switch (Op->getConstantOperandVal(0)) {
case Intrinsic::x86_sse2_pmadd_wd:
More information about the llvm-commits
mailing list