[llvm] 51a9052 - [X86] Extract select to min/max combining into separate function (NFC)
Nikita Popov via llvm-commits
llvm-commits at lists.llvm.org
Mon Mar 9 07:30:55 PDT 2026
Author: Nikita Popov
Date: 2026-03-09T15:30:42+01:00
New Revision: 51a9052f1cc1c0c6120ea646239cbf3e5e21b0ed
URL: https://github.com/llvm/llvm-project/commit/51a9052f1cc1c0c6120ea646239cbf3e5e21b0ed
DIFF: https://github.com/llvm/llvm-project/commit/51a9052f1cc1c0c6120ea646239cbf3e5e21b0ed.diff
LOG: [X86] Extract select to min/max combining into separate function (NFC)
Added:
Modified:
llvm/lib/Target/X86/X86ISelLowering.cpp
Removed:
################################################################################
diff --git a/llvm/lib/Target/X86/X86ISelLowering.cpp b/llvm/lib/Target/X86/X86ISelLowering.cpp
index c7dd6c76c18f5..4ce343922a02e 100644
--- a/llvm/lib/Target/X86/X86ISelLowering.cpp
+++ b/llvm/lib/Target/X86/X86ISelLowering.cpp
@@ -48139,6 +48139,182 @@ static SDValue commuteSelect(SDNode *N, SelectionDAG &DAG, const SDLoc &DL,
return DAG.getSelect(DL, LHS.getValueType(), Cond, RHS, LHS);
}
+// If we have SSE[12] support, try to form min/max nodes. SSE min/max
+// instructions match the semantics of the common C idiom x<y?x:y but not
+// x<=y?x:y, because of how they handle negative zero (which can be
+// ignored in unsafe-math mode).
+// We also try to create v2f32 min/max nodes, which we later widen to v4f32.
+static SDValue combineSelectToMinMax(SelectionDAG &DAG,
+ const X86Subtarget &Subtarget, SDNode *N) {
+ SDLoc DL(N);
+ SDValue Cond = N->getOperand(0);
+ SDValue LHS = N->getOperand(1);
+ SDValue RHS = N->getOperand(2);
+ EVT VT = LHS.getValueType();
+ const TargetLowering &TLI = DAG.getTargetLoweringInfo();
+ if ((Cond.getOpcode() != ISD::SETCC &&
+ Cond.getOpcode() != ISD::STRICT_FSETCCS) ||
+ !VT.isFloatingPoint() || VT == MVT::f80 || VT == MVT::f128 ||
+ isSoftF16(VT, Subtarget) || (!TLI.isTypeLegal(VT) && VT != MVT::v2f32) ||
+ ((VT == MVT::v8f16 || VT == MVT::v16f16) && !Subtarget.hasVLX()) ||
+ (!Subtarget.hasSSE2() &&
+ (!Subtarget.hasSSE1() || VT.getScalarType() != MVT::f32)))
+ return SDValue();
+
+ bool IsStrict = Cond->isStrictFPOpcode();
+ ISD::CondCode CC =
+ cast<CondCodeSDNode>(Cond.getOperand(IsStrict ? 3 : 2))->get();
+ SDValue Op0 = Cond.getOperand(IsStrict ? 1 : 0);
+ SDValue Op1 = Cond.getOperand(IsStrict ? 2 : 1);
+
+ unsigned Opcode = 0;
+ // Check for x CC y ? x : y.
+ if (DAG.isEqualTo(LHS, Op0) && DAG.isEqualTo(RHS, Op1)) {
+ switch (CC) {
+ default:
+ break;
+ case ISD::SETULT:
+ // Converting this to a min would handle NaNs incorrectly, and swapping
+ // the operands would cause it to handle comparisons between positive
+ // and negative zero incorrectly.
+ if (!DAG.isKnownNeverNaN(LHS) || !DAG.isKnownNeverNaN(RHS)) {
+ if (!DAG.getTarget().Options.NoSignedZerosFPMath &&
+ !(DAG.isKnownNeverZeroFloat(LHS) || DAG.isKnownNeverZeroFloat(RHS)))
+ break;
+ std::swap(LHS, RHS);
+ }
+ Opcode = X86ISD::FMIN;
+ break;
+ case ISD::SETOLE:
+ // Converting this to a min would handle comparisons between positive
+ // and negative zero incorrectly.
+ if (!DAG.getTarget().Options.NoSignedZerosFPMath &&
+ !DAG.isKnownNeverZeroFloat(LHS) && !DAG.isKnownNeverZeroFloat(RHS))
+ break;
+ Opcode = X86ISD::FMIN;
+ break;
+ case ISD::SETULE:
+ // Converting this to a min would handle both negative zeros and NaNs
+ // incorrectly, but we can swap the operands to fix both.
+ std::swap(LHS, RHS);
+ [[fallthrough]];
+ case ISD::SETOLT:
+ case ISD::SETLT:
+ case ISD::SETLE:
+ Opcode = X86ISD::FMIN;
+ break;
+
+ case ISD::SETOGE:
+ // Converting this to a max would handle comparisons between positive
+ // and negative zero incorrectly.
+ if (!DAG.getTarget().Options.NoSignedZerosFPMath &&
+ !DAG.isKnownNeverZeroFloat(LHS) && !DAG.isKnownNeverZeroFloat(RHS))
+ break;
+ Opcode = X86ISD::FMAX;
+ break;
+ case ISD::SETUGT:
+ // Converting this to a max would handle NaNs incorrectly, and swapping
+ // the operands would cause it to handle comparisons between positive
+ // and negative zero incorrectly.
+ if (!DAG.isKnownNeverNaN(LHS) || !DAG.isKnownNeverNaN(RHS)) {
+ if (!DAG.getTarget().Options.NoSignedZerosFPMath &&
+ !(DAG.isKnownNeverZeroFloat(LHS) || DAG.isKnownNeverZeroFloat(RHS)))
+ break;
+ std::swap(LHS, RHS);
+ }
+ Opcode = X86ISD::FMAX;
+ break;
+ case ISD::SETUGE:
+ // Converting this to a max would handle both negative zeros and NaNs
+ // incorrectly, but we can swap the operands to fix both.
+ std::swap(LHS, RHS);
+ [[fallthrough]];
+ case ISD::SETOGT:
+ case ISD::SETGT:
+ case ISD::SETGE:
+ Opcode = X86ISD::FMAX;
+ break;
+ }
+ // Check for x CC y ? y : x -- a min/max with reversed arms.
+ } else if (DAG.isEqualTo(LHS, Op1) && DAG.isEqualTo(RHS, Op0)) {
+ switch (CC) {
+ default:
+ break;
+ case ISD::SETOGE:
+ // Converting this to a min would handle comparisons between positive
+ // and negative zero incorrectly, and swapping the operands would
+ // cause it to handle NaNs incorrectly.
+ if (!DAG.getTarget().Options.NoSignedZerosFPMath &&
+ !(DAG.isKnownNeverZeroFloat(LHS) || DAG.isKnownNeverZeroFloat(RHS))) {
+ if (!DAG.isKnownNeverNaN(LHS) || !DAG.isKnownNeverNaN(RHS))
+ break;
+ std::swap(LHS, RHS);
+ }
+ Opcode = X86ISD::FMIN;
+ break;
+ case ISD::SETUGT:
+ // Converting this to a min would handle NaNs incorrectly.
+ if (!DAG.isKnownNeverNaN(LHS) || !DAG.isKnownNeverNaN(RHS))
+ break;
+ Opcode = X86ISD::FMIN;
+ break;
+ case ISD::SETUGE:
+ // Converting this to a min would handle both negative zeros and NaNs
+ // incorrectly, but we can swap the operands to fix both.
+ std::swap(LHS, RHS);
+ [[fallthrough]];
+ case ISD::SETOGT:
+ case ISD::SETGT:
+ case ISD::SETGE:
+ Opcode = X86ISD::FMIN;
+ break;
+
+ case ISD::SETULT:
+ // Converting this to a max would handle NaNs incorrectly.
+ if (!DAG.isKnownNeverNaN(LHS) || !DAG.isKnownNeverNaN(RHS))
+ break;
+ Opcode = X86ISD::FMAX;
+ break;
+ case ISD::SETOLE:
+ // Converting this to a max would handle comparisons between positive
+ // and negative zero incorrectly, and swapping the operands would
+ // cause it to handle NaNs incorrectly.
+ if (!DAG.getTarget().Options.NoSignedZerosFPMath &&
+ !DAG.isKnownNeverZeroFloat(LHS) && !DAG.isKnownNeverZeroFloat(RHS)) {
+ if (!DAG.isKnownNeverNaN(LHS) || !DAG.isKnownNeverNaN(RHS))
+ break;
+ std::swap(LHS, RHS);
+ }
+ Opcode = X86ISD::FMAX;
+ break;
+ case ISD::SETULE:
+ // Converting this to a max would handle both negative zeros and NaNs
+ // incorrectly, but we can swap the operands to fix both.
+ std::swap(LHS, RHS);
+ [[fallthrough]];
+ case ISD::SETOLT:
+ case ISD::SETLT:
+ case ISD::SETLE:
+ Opcode = X86ISD::FMAX;
+ break;
+ }
+ }
+
+ if (!Opcode)
+ return SDValue();
+
+ // Propagate fast-math-flags.
+ SelectionDAG::FlagInserter FlagsInserter(DAG, N->getFlags());
+ if (IsStrict) {
+ SDValue Ret = DAG.getNode(
+ Opcode == X86ISD::FMIN ? X86ISD::STRICT_FMIN : X86ISD::STRICT_FMAX, DL,
+ {N->getValueType(0), MVT::Other}, {Cond.getOperand(0), LHS, RHS});
+ DAG.ReplaceAllUsesOfValueWith(Cond.getValue(1), Ret.getValue(1));
+ return Ret;
+ }
+ return DAG.getNode(Opcode, DL, N->getValueType(0), LHS, RHS);
+}
+
/// Do target-specific dag combines on SELECT and VSELECT nodes.
static SDValue combineSelect(SDNode *N, SelectionDAG &DAG,
TargetLowering::DAGCombinerInfo &DCI,
@@ -48236,173 +48412,8 @@ static SDValue combineSelect(SDNode *N, SelectionDAG &DAG,
}
}
- // If we have SSE[12] support, try to form min/max nodes. SSE min/max
- // instructions match the semantics of the common C idiom x<y?x:y but not
- // x<=y?x:y, because of how they handle negative zero (which can be
- // ignored in unsafe-math mode).
- // We also try to create v2f32 min/max nodes, which we later widen to v4f32.
- if ((Cond.getOpcode() == ISD::SETCC ||
- Cond.getOpcode() == ISD::STRICT_FSETCCS) &&
- VT.isFloatingPoint() && VT != MVT::f80 && VT != MVT::f128 &&
- !isSoftF16(VT, Subtarget) && (TLI.isTypeLegal(VT) || VT == MVT::v2f32) &&
- ((VT != MVT::v8f16 && VT != MVT::v16f16) || Subtarget.hasVLX()) &&
- (Subtarget.hasSSE2() ||
- (Subtarget.hasSSE1() && VT.getScalarType() == MVT::f32))) {
- bool IsStrict = Cond->isStrictFPOpcode();
- ISD::CondCode CC =
- cast<CondCodeSDNode>(Cond.getOperand(IsStrict ? 3 : 2))->get();
- SDValue Op0 = Cond.getOperand(IsStrict ? 1 : 0);
- SDValue Op1 = Cond.getOperand(IsStrict ? 2 : 1);
-
- unsigned Opcode = 0;
- // Check for x CC y ? x : y.
- if (DAG.isEqualTo(LHS, Op0) && DAG.isEqualTo(RHS, Op1)) {
- switch (CC) {
- default: break;
- case ISD::SETULT:
- // Converting this to a min would handle NaNs incorrectly, and swapping
- // the operands would cause it to handle comparisons between positive
- // and negative zero incorrectly.
- if (!DAG.isKnownNeverNaN(LHS) || !DAG.isKnownNeverNaN(RHS)) {
- if (!DAG.getTarget().Options.NoSignedZerosFPMath &&
- !(DAG.isKnownNeverZeroFloat(LHS) ||
- DAG.isKnownNeverZeroFloat(RHS)))
- break;
- std::swap(LHS, RHS);
- }
- Opcode = X86ISD::FMIN;
- break;
- case ISD::SETOLE:
- // Converting this to a min would handle comparisons between positive
- // and negative zero incorrectly.
- if (!DAG.getTarget().Options.NoSignedZerosFPMath &&
- !DAG.isKnownNeverZeroFloat(LHS) && !DAG.isKnownNeverZeroFloat(RHS))
- break;
- Opcode = X86ISD::FMIN;
- break;
- case ISD::SETULE:
- // Converting this to a min would handle both negative zeros and NaNs
- // incorrectly, but we can swap the operands to fix both.
- std::swap(LHS, RHS);
- [[fallthrough]];
- case ISD::SETOLT:
- case ISD::SETLT:
- case ISD::SETLE:
- Opcode = X86ISD::FMIN;
- break;
-
- case ISD::SETOGE:
- // Converting this to a max would handle comparisons between positive
- // and negative zero incorrectly.
- if (!DAG.getTarget().Options.NoSignedZerosFPMath &&
- !DAG.isKnownNeverZeroFloat(LHS) && !DAG.isKnownNeverZeroFloat(RHS))
- break;
- Opcode = X86ISD::FMAX;
- break;
- case ISD::SETUGT:
- // Converting this to a max would handle NaNs incorrectly, and swapping
- // the operands would cause it to handle comparisons between positive
- // and negative zero incorrectly.
- if (!DAG.isKnownNeverNaN(LHS) || !DAG.isKnownNeverNaN(RHS)) {
- if (!DAG.getTarget().Options.NoSignedZerosFPMath &&
- !(DAG.isKnownNeverZeroFloat(LHS) ||
- DAG.isKnownNeverZeroFloat(RHS)))
- break;
- std::swap(LHS, RHS);
- }
- Opcode = X86ISD::FMAX;
- break;
- case ISD::SETUGE:
- // Converting this to a max would handle both negative zeros and NaNs
- // incorrectly, but we can swap the operands to fix both.
- std::swap(LHS, RHS);
- [[fallthrough]];
- case ISD::SETOGT:
- case ISD::SETGT:
- case ISD::SETGE:
- Opcode = X86ISD::FMAX;
- break;
- }
- // Check for x CC y ? y : x -- a min/max with reversed arms.
- } else if (DAG.isEqualTo(LHS, Op1) && DAG.isEqualTo(RHS, Op0)) {
- switch (CC) {
- default: break;
- case ISD::SETOGE:
- // Converting this to a min would handle comparisons between positive
- // and negative zero incorrectly, and swapping the operands would
- // cause it to handle NaNs incorrectly.
- if (!DAG.getTarget().Options.NoSignedZerosFPMath &&
- !(DAG.isKnownNeverZeroFloat(LHS) ||
- DAG.isKnownNeverZeroFloat(RHS))) {
- if (!DAG.isKnownNeverNaN(LHS) || !DAG.isKnownNeverNaN(RHS))
- break;
- std::swap(LHS, RHS);
- }
- Opcode = X86ISD::FMIN;
- break;
- case ISD::SETUGT:
- // Converting this to a min would handle NaNs incorrectly.
- if (!DAG.isKnownNeverNaN(LHS) || !DAG.isKnownNeverNaN(RHS))
- break;
- Opcode = X86ISD::FMIN;
- break;
- case ISD::SETUGE:
- // Converting this to a min would handle both negative zeros and NaNs
- // incorrectly, but we can swap the operands to fix both.
- std::swap(LHS, RHS);
- [[fallthrough]];
- case ISD::SETOGT:
- case ISD::SETGT:
- case ISD::SETGE:
- Opcode = X86ISD::FMIN;
- break;
-
- case ISD::SETULT:
- // Converting this to a max would handle NaNs incorrectly.
- if (!DAG.isKnownNeverNaN(LHS) || !DAG.isKnownNeverNaN(RHS))
- break;
- Opcode = X86ISD::FMAX;
- break;
- case ISD::SETOLE:
- // Converting this to a max would handle comparisons between positive
- // and negative zero incorrectly, and swapping the operands would
- // cause it to handle NaNs incorrectly.
- if (!DAG.getTarget().Options.NoSignedZerosFPMath &&
- !DAG.isKnownNeverZeroFloat(LHS) &&
- !DAG.isKnownNeverZeroFloat(RHS)) {
- if (!DAG.isKnownNeverNaN(LHS) || !DAG.isKnownNeverNaN(RHS))
- break;
- std::swap(LHS, RHS);
- }
- Opcode = X86ISD::FMAX;
- break;
- case ISD::SETULE:
- // Converting this to a max would handle both negative zeros and NaNs
- // incorrectly, but we can swap the operands to fix both.
- std::swap(LHS, RHS);
- [[fallthrough]];
- case ISD::SETOLT:
- case ISD::SETLT:
- case ISD::SETLE:
- Opcode = X86ISD::FMAX;
- break;
- }
- }
-
- if (Opcode) {
- // Propagate fast-math-flags.
- SelectionDAG::FlagInserter FlagsInserter(DAG, N->getFlags());
- if (IsStrict) {
- SDValue Ret = DAG.getNode(Opcode == X86ISD::FMIN ? X86ISD::STRICT_FMIN
- : X86ISD::STRICT_FMAX,
- DL, {N->getValueType(0), MVT::Other},
- {Cond.getOperand(0), LHS, RHS});
- DAG.ReplaceAllUsesOfValueWith(Cond.getValue(1), Ret.getValue(1));
- return Ret;
- }
- return DAG.getNode(Opcode, DL, N->getValueType(0), LHS, RHS);
- }
- }
+ if (SDValue Res = combineSelectToMinMax(DAG, Subtarget, N))
+ return Res;
// Some mask scalar intrinsics rely on checking if only one bit is set
// and implement it in C code like this:
More information about the llvm-commits
mailing list