[llvm] 119b31f - [DAGCombiner] Fold NaN-guard fptosi/fptoui select to saturating variant (#201435)
via llvm-commits
llvm-commits at lists.llvm.org
Tue Jul 21 07:40:58 PDT 2026
Author: Adel Ejjeh
Date: 2026-07-21T10:40:53-04:00
New Revision: 119b31fd30644157b9429d122f777c6ae2e34a09
URL: https://github.com/llvm/llvm-project/commit/119b31fd30644157b9429d122f777c6ae2e34a09
DIFF: https://github.com/llvm/llvm-project/commit/119b31fd30644157b9429d122f777c6ae2e34a09.diff
LOG: [DAGCombiner] Fold NaN-guard fptosi/fptoui select to saturating variant (#201435)
Fold select (setcc X, 0, SETUO), 0, (fp_to_sint/fp_to_uint X) to
fp_to_sint_sat/fp_to_uint_sat in the generic DAG combiner, gated by
shouldConvertFpToSat. Also handles the SETO form with swapped arms, and
looks through an AND mask on the conversion result. Lit tests added for
AMDGPU and AArch64.
Assisted-by: Claude Code
Added:
llvm/test/CodeGen/AArch64/fptoi-nan-guard-fold.ll
llvm/test/CodeGen/AMDGPU/fptoi-nan-guard-fold.ll
Modified:
llvm/include/llvm/CodeGen/SDPatternMatch.h
llvm/include/llvm/CodeGen/TargetLowering.h
llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
Removed:
################################################################################
diff --git a/llvm/include/llvm/CodeGen/SDPatternMatch.h b/llvm/include/llvm/CodeGen/SDPatternMatch.h
index b98b8837ad4f5..e2e20b14bb7f7 100644
--- a/llvm/include/llvm/CodeGen/SDPatternMatch.h
+++ b/llvm/include/llvm/CodeGen/SDPatternMatch.h
@@ -1367,6 +1367,17 @@ inline SpecificFP_match m_SpecificFP(double V) {
return SpecificFP_match(APFloat(V));
}
+struct AnyZeroFP_match {
+ template <typename MatchContext> bool match(const MatchContext &, SDValue N) {
+ if (ConstantFPSDNode *C = isConstOrConstSplatFP(N))
+ return C->isZero();
+ return false;
+ }
+};
+
+/// Match a floating-point +0.0 or -0.0 constant or splat.
+inline AnyZeroFP_match m_AnyZeroFP() { return AnyZeroFP_match(); }
+
struct Negative_match {
template <typename MatchContext>
bool match(const MatchContext &Ctx, SDValue N) {
diff --git a/llvm/include/llvm/CodeGen/TargetLowering.h b/llvm/include/llvm/CodeGen/TargetLowering.h
index 9a6dd7735421e..d8cf1c7ac6130 100644
--- a/llvm/include/llvm/CodeGen/TargetLowering.h
+++ b/llvm/include/llvm/CodeGen/TargetLowering.h
@@ -3623,8 +3623,9 @@ class LLVM_ABI TargetLoweringBase {
/// passed to the fp16 to fp conversion library function.
virtual bool shouldKeepZExtForFP16Conv() const { return false; }
- /// Should we generate fp_to_si_sat and fp_to_ui_sat from type FPVT to type VT
- /// from min(max(fptoi)) saturation patterns.
+ /// Should we generate fp_to_si_sat and fp_to_ui_sat from type FPVT to type
+ /// VT. Used when folding idioms into a saturating fp-to-int conversion, such
+ /// as min(max(fptoi)) clamps or NaN-guarded selects.
virtual bool shouldConvertFpToSat(unsigned Op, EVT FPVT, EVT VT) const {
return isOperationLegalOrCustom(Op, VT);
}
diff --git a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
index f22710cb37815..bc8b41ebc94f8 100644
--- a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
@@ -6310,6 +6310,58 @@ static SDValue PerformUMinFpToSatCombine(SDValue N0, SDValue N1, SDValue N2,
return DAG.getZExtOrTrunc(Sat, SDLoc(N0), N3.getValueType());
}
+// Fold a NaN-guard select of fp_to_sint/fp_to_uint into the saturating
+// variant, which returns 0 for NaN.
+static SDValue performNanGuardFpToSatCombine(SDNode *N, SelectionDAG &DAG) {
+ EVT VT = N->getValueType(0);
+ SDLoc DL(N);
+
+ // Match an isnan-guarded select, requiring the compare to be single-use.
+ // The guarded value is fp_to_sint/fp_to_uint of X, optionally masked by an
+ // AND:
+ // select (setcc X, 0.0, uno), 0, (fp_to_sint/uint X)
+ // select (setcc X, 0.0, ord), (fp_to_sint/uint X), 0
+ // select (setcc X, 0.0, uno), 0, (and (fp_to_sint/uint X), M)
+ // select (setcc X, 0.0, ord), (and (fp_to_sint/uint X), M), 0
+ SDValue X, GuardedVal;
+ if (!sd_match(N,
+ m_SelectLike(m_OneUse(m_SetCC(m_Value(X), m_AnyZeroFP(),
+ m_SpecificCondCode(ISD::SETUO))),
+ m_Zero(), m_Value(GuardedVal))) &&
+ !sd_match(N,
+ m_SelectLike(m_OneUse(m_SetCC(m_Value(X), m_AnyZeroFP(),
+ m_SpecificCondCode(ISD::SETO))),
+ m_Value(GuardedVal), m_Zero())))
+ return SDValue();
+
+ // The guarded value must be fp_to_sint/fp_to_uint of the same X, optionally
+ // masked by a (commutative) AND.
+ SDValue Mask;
+ unsigned NewOpc;
+ if (sd_match(GuardedVal, m_FPToSI(m_Specific(X))) ||
+ sd_match(GuardedVal, m_And(m_FPToSI(m_Specific(X)), m_Value(Mask))))
+ NewOpc = ISD::FP_TO_SINT_SAT;
+ else if (sd_match(GuardedVal, m_FPToUI(m_Specific(X))) ||
+ sd_match(GuardedVal, m_And(m_FPToUI(m_Specific(X)), m_Value(Mask))))
+ NewOpc = ISD::FP_TO_UINT_SAT;
+ else
+ return SDValue();
+
+ if (!DAG.getTargetLoweringInfo().shouldConvertFpToSat(NewOpc,
+ X.getValueType(), VT))
+ return SDValue();
+
+ SDValue Sat =
+ DAG.getNode(NewOpc, DL, VT, X, DAG.getValueType(VT.getScalarType()));
+ if (Mask) {
+ // For NaN inputs the saturating conversion yields 0, so (and 0, Mask) must
+ // stay 0 to match the original select. A poison Mask would make it poison,
+ // so freeze Mask to guarantee a defined value.
+ Sat = DAG.getNode(ISD::AND, DL, VT, Sat, DAG.getFreeze(Mask));
+ }
+ return Sat;
+}
+
SDValue DAGCombiner::visitIMINMAX(SDNode *N) {
SDValue N0 = N->getOperand(0);
SDValue N1 = N->getOperand(1);
@@ -13447,6 +13499,9 @@ SDValue DAGCombiner::visitSELECT(SDNode *N) {
}
}
+ if (SDValue S = performNanGuardFpToSatCombine(N, DAG))
+ return S;
+
if (TLI.isOperationLegal(ISD::SELECT_CC, VT) ||
(!LegalOperations &&
TLI.isOperationLegalOrCustom(ISD::SELECT_CC, VT))) {
@@ -14468,6 +14523,8 @@ SDValue DAGCombiner::visitVSELECT(SDNode *N) {
return S;
if (SDValue S = PerformUMinFpToSatCombine(LHS, RHS, N1, N2, CC, DAG))
return S;
+ if (SDValue S = performNanGuardFpToSatCombine(N, DAG))
+ return S;
// If this select has a condition (setcc) with narrower operands than the
// select, try to widen the compare to match the select width.
diff --git a/llvm/test/CodeGen/AArch64/fptoi-nan-guard-fold.ll b/llvm/test/CodeGen/AArch64/fptoi-nan-guard-fold.ll
new file mode 100644
index 0000000000000..5ad8a812bf33b
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/fptoi-nan-guard-fold.ll
@@ -0,0 +1,172 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc -mtriple=aarch64 < %s | FileCheck %s
+
+; The generic DAG combiner folds select(setcc X, 0, SETUO), 0, (fp_to_sint X)
+; into fp_to_sint_sat when the target supports it. On AArch64, fcvtzs/fcvtzu
+; natively saturate for NaN, so fp_to_sint_sat lowers to a single instruction.
+
+;; Positive tests
+
+define i32 @nan_guard_fptosi_f32(float %x) {
+; CHECK-LABEL: nan_guard_fptosi_f32:
+; CHECK: // %bb.0:
+; CHECK-NEXT: fcvtzs w0, s0
+; CHECK-NEXT: ret
+ %cmp = fcmp uno float %x, 0.000000e+00
+ %conv = fptosi float %x to i32
+ %sel = select i1 %cmp, i32 0, i32 %conv
+ ret i32 %sel
+}
+
+define i32 @nan_guard_fptosi_ord_f32(float %x) {
+; CHECK-LABEL: nan_guard_fptosi_ord_f32:
+; CHECK: // %bb.0:
+; CHECK-NEXT: fcvtzs w0, s0
+; CHECK-NEXT: ret
+ %cmp = fcmp ord float %x, 0.000000e+00
+ %conv = fptosi float %x to i32
+ %sel = select i1 %cmp, i32 %conv, i32 0
+ ret i32 %sel
+}
+
+define i32 @nan_guard_fptoui_f32(float %x) {
+; CHECK-LABEL: nan_guard_fptoui_f32:
+; CHECK: // %bb.0:
+; CHECK-NEXT: fcvtzu w0, s0
+; CHECK-NEXT: ret
+ %cmp = fcmp uno float %x, 0.000000e+00
+ %conv = fptoui float %x to i32
+ %sel = select i1 %cmp, i32 0, i32 %conv
+ ret i32 %sel
+}
+
+define i32 @nan_guard_fptoui_ord_f32(float %x) {
+; CHECK-LABEL: nan_guard_fptoui_ord_f32:
+; CHECK: // %bb.0:
+; CHECK-NEXT: fcvtzu w0, s0
+; CHECK-NEXT: ret
+ %cmp = fcmp ord float %x, 0.000000e+00
+ %conv = fptoui float %x to i32
+ %sel = select i1 %cmp, i32 %conv, i32 0
+ ret i32 %sel
+}
+
+define i32 @nan_guard_fptosi_and_mask(float %x) {
+; CHECK-LABEL: nan_guard_fptosi_and_mask:
+; CHECK: // %bb.0:
+; CHECK-NEXT: fcvtzs w8, s0
+; CHECK-NEXT: and w0, w8, #0x3
+; CHECK-NEXT: ret
+ %cmp = fcmp uno float %x, 0.000000e+00
+ %conv = fptosi float %x to i32
+ %and = and i32 %conv, 3
+ %sel = select i1 %cmp, i32 0, i32 %and
+ ret i32 %sel
+}
+
+; AND mask with a non-constant operand.
+define i32 @nan_guard_fptosi_and_mask_var(float %x, i32 %m) {
+; CHECK-LABEL: nan_guard_fptosi_and_mask_var:
+; CHECK: // %bb.0:
+; CHECK-NEXT: fcvtzs w8, s0
+; CHECK-NEXT: and w0, w8, w0
+; CHECK-NEXT: ret
+ %cmp = fcmp uno float %x, 0.000000e+00
+ %conv = fptosi float %x to i32
+ %and = and i32 %conv, %m
+ %sel = select i1 %cmp, i32 0, i32 %and
+ ret i32 %sel
+}
+
+; AND mask with a non-constant operand, commuted.
+define i32 @nan_guard_fptosi_and_mask_commuted(float %x, i32 %m) {
+; CHECK-LABEL: nan_guard_fptosi_and_mask_commuted:
+; CHECK: // %bb.0:
+; CHECK-NEXT: fcvtzs w8, s0
+; CHECK-NEXT: and w0, w8, w0
+; CHECK-NEXT: ret
+ %cmp = fcmp uno float %x, 0.000000e+00
+ %conv = fptosi float %x to i32
+ %and = and i32 %m, %conv
+ %sel = select i1 %cmp, i32 0, i32 %and
+ ret i32 %sel
+}
+
+define i32 @nan_guard_fptosi_f64(double %x) {
+; CHECK-LABEL: nan_guard_fptosi_f64:
+; CHECK: // %bb.0:
+; CHECK-NEXT: fcvtzs w0, d0
+; CHECK-NEXT: ret
+ %cmp = fcmp uno double %x, 0.000000e+00
+ %conv = fptosi double %x to i32
+ %sel = select i1 %cmp, i32 0, i32 %conv
+ ret i32 %sel
+}
+
+define <4 x i32> @nan_guard_fptosi_v4f32(<4 x float> %x) {
+; CHECK-LABEL: nan_guard_fptosi_v4f32:
+; CHECK: // %bb.0:
+; CHECK-NEXT: fcvtzs v0.4s, v0.4s
+; CHECK-NEXT: ret
+ %cmp = fcmp uno <4 x float> %x, zeroinitializer
+ %conv = fptosi <4 x float> %x to <4 x i32>
+ %sel = select <4 x i1> %cmp, <4 x i32> zeroinitializer, <4 x i32> %conv
+ ret <4 x i32> %sel
+}
+
+; Vector case with a non-constant AND mask.
+define <4 x i32> @nan_guard_fptosi_v4f32_and_mask(<4 x float> %x, <4 x i32> %m) {
+; CHECK-LABEL: nan_guard_fptosi_v4f32_and_mask:
+; CHECK: // %bb.0:
+; CHECK-NEXT: fcvtzs v0.4s, v0.4s
+; CHECK-NEXT: and v0.16b, v0.16b, v1.16b
+; CHECK-NEXT: ret
+ %cmp = fcmp uno <4 x float> %x, zeroinitializer
+ %conv = fptosi <4 x float> %x to <4 x i32>
+ %and = and <4 x i32> %conv, %m
+ %sel = select <4 x i1> %cmp, <4 x i32> zeroinitializer, <4 x i32> %and
+ ret <4 x i32> %sel
+}
+
+;; Negative tests
+
+define i32 @nan_guard_nonzero_constant(float %x) {
+; CHECK-LABEL: nan_guard_nonzero_constant:
+; CHECK: // %bb.0:
+; CHECK-NEXT: fcvtzs w8, s0
+; CHECK-NEXT: fcmp s0, s0
+; CHECK-NEXT: mov w9, #42 // =0x2a
+; CHECK-NEXT: csel w0, w9, w8, vs
+; CHECK-NEXT: ret
+ %cmp = fcmp uno float %x, 0.000000e+00
+ %conv = fptosi float %x to i32
+ %sel = select i1 %cmp, i32 42, i32 %conv
+ ret i32 %sel
+}
+
+define i32 @nan_guard_mismatched_operands(float %x, float %y) {
+; CHECK-LABEL: nan_guard_mismatched_operands:
+; CHECK: // %bb.0:
+; CHECK-NEXT: fcvtzs w8, s1
+; CHECK-NEXT: fcmp s0, s0
+; CHECK-NEXT: csel w0, wzr, w8, vs
+; CHECK-NEXT: ret
+ %cmp = fcmp uno float %x, 0.000000e+00
+ %conv = fptosi float %y to i32
+ %sel = select i1 %cmp, i32 0, i32 %conv
+ ret i32 %sel
+}
+
+; Compare is a
diff erent predicate (not uno/ord) -- should not fold.
+define i32 @nan_guard_wrong_predicate(float %x) {
+; CHECK-LABEL: nan_guard_wrong_predicate:
+; CHECK: // %bb.0:
+; CHECK-NEXT: fcvtzs w8, s0
+; CHECK-NEXT: fcmp s0, #0.0
+; CHECK-NEXT: csel w0, wzr, w8, mi
+; CHECK-NEXT: ret
+ %cmp = fcmp olt float %x, 0.000000e+00
+ %conv = fptosi float %x to i32
+ %sel = select i1 %cmp, i32 0, i32 %conv
+ ret i32 %sel
+}
diff --git a/llvm/test/CodeGen/AMDGPU/fptoi-nan-guard-fold.ll b/llvm/test/CodeGen/AMDGPU/fptoi-nan-guard-fold.ll
new file mode 100644
index 0000000000000..64131bebea9c8
--- /dev/null
+++ b/llvm/test/CodeGen/AMDGPU/fptoi-nan-guard-fold.ll
@@ -0,0 +1,212 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc -mtriple=amdgcn-amd-amdhsa -mcpu=gfx90a < %s | FileCheck %s
+
+; The generic DAG combiner folds select(setcc X, 0, SETUO), 0, (fp_to_sint X)
+; into fp_to_sint_sat when the target supports it. On AMDGPU, the V_CVT
+; instructions already return 0 for NaN, so fp_to_sint_sat lowers to a single
+; conversion instruction with no extra compare/select overhead.
+
+;; Positive tests
+
+define i32 @nan_guard_fptosi_f32(float %x) {
+; CHECK-LABEL: nan_guard_fptosi_f32:
+; CHECK: ; %bb.0:
+; CHECK-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; CHECK-NEXT: v_cvt_i32_f32_e32 v0, v0
+; CHECK-NEXT: s_setpc_b64 s[30:31]
+ %cmp = fcmp uno float %x, 0.000000e+00
+ %conv = fptosi float %x to i32
+ %sel = select i1 %cmp, i32 0, i32 %conv
+ ret i32 %sel
+}
+
+define i32 @nan_guard_fptosi_ord_f32(float %x) {
+; CHECK-LABEL: nan_guard_fptosi_ord_f32:
+; CHECK: ; %bb.0:
+; CHECK-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; CHECK-NEXT: v_cvt_i32_f32_e32 v0, v0
+; CHECK-NEXT: s_setpc_b64 s[30:31]
+ %cmp = fcmp ord float %x, 0.000000e+00
+ %conv = fptosi float %x to i32
+ %sel = select i1 %cmp, i32 %conv, i32 0
+ ret i32 %sel
+}
+
+define i32 @nan_guard_fptoui_f32(float %x) {
+; CHECK-LABEL: nan_guard_fptoui_f32:
+; CHECK: ; %bb.0:
+; CHECK-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; CHECK-NEXT: v_cvt_u32_f32_e32 v0, v0
+; CHECK-NEXT: s_setpc_b64 s[30:31]
+ %cmp = fcmp uno float %x, 0.000000e+00
+ %conv = fptoui float %x to i32
+ %sel = select i1 %cmp, i32 0, i32 %conv
+ ret i32 %sel
+}
+
+define i32 @nan_guard_fptoui_ord_f32(float %x) {
+; CHECK-LABEL: nan_guard_fptoui_ord_f32:
+; CHECK: ; %bb.0:
+; CHECK-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; CHECK-NEXT: v_cvt_u32_f32_e32 v0, v0
+; CHECK-NEXT: s_setpc_b64 s[30:31]
+ %cmp = fcmp ord float %x, 0.000000e+00
+ %conv = fptoui float %x to i32
+ %sel = select i1 %cmp, i32 %conv, i32 0
+ ret i32 %sel
+}
+
+define i32 @nan_guard_fptosi_and_mask(float %x) {
+; CHECK-LABEL: nan_guard_fptosi_and_mask:
+; CHECK: ; %bb.0:
+; CHECK-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; CHECK-NEXT: v_cvt_i32_f32_e32 v0, v0
+; CHECK-NEXT: v_and_b32_e32 v0, 3, v0
+; CHECK-NEXT: s_setpc_b64 s[30:31]
+ %cmp = fcmp uno float %x, 0.000000e+00
+ %conv = fptosi float %x to i32
+ %and = and i32 %conv, 3
+ %sel = select i1 %cmp, i32 0, i32 %and
+ ret i32 %sel
+}
+
+; AND mask with a non-constant operand.
+define i32 @nan_guard_fptosi_and_mask_var(float %x, i32 %m) {
+; CHECK-LABEL: nan_guard_fptosi_and_mask_var:
+; CHECK: ; %bb.0:
+; CHECK-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; CHECK-NEXT: v_cvt_i32_f32_e32 v0, v0
+; CHECK-NEXT: v_and_b32_e32 v0, v0, v1
+; CHECK-NEXT: s_setpc_b64 s[30:31]
+ %cmp = fcmp uno float %x, 0.000000e+00
+ %conv = fptosi float %x to i32
+ %and = and i32 %conv, %m
+ %sel = select i1 %cmp, i32 0, i32 %and
+ ret i32 %sel
+}
+
+; AND mask with a non-constant operand, commuted.
+define i32 @nan_guard_fptosi_and_mask_commuted(float %x, i32 %m) {
+; CHECK-LABEL: nan_guard_fptosi_and_mask_commuted:
+; CHECK: ; %bb.0:
+; CHECK-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; CHECK-NEXT: v_cvt_i32_f32_e32 v0, v0
+; CHECK-NEXT: v_and_b32_e32 v0, v0, v1
+; CHECK-NEXT: s_setpc_b64 s[30:31]
+ %cmp = fcmp uno float %x, 0.000000e+00
+ %conv = fptosi float %x to i32
+ %and = and i32 %m, %conv
+ %sel = select i1 %cmp, i32 0, i32 %and
+ ret i32 %sel
+}
+
+define i32 @nan_guard_fptosi_f64(double %x) {
+; CHECK-LABEL: nan_guard_fptosi_f64:
+; CHECK: ; %bb.0:
+; CHECK-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; CHECK-NEXT: v_cvt_i32_f64_e32 v0, v[0:1]
+; CHECK-NEXT: s_setpc_b64 s[30:31]
+ %cmp = fcmp uno double %x, 0.000000e+00
+ %conv = fptosi double %x to i32
+ %sel = select i1 %cmp, i32 0, i32 %conv
+ ret i32 %sel
+}
+
+;; Negative tests
+
+define i32 @nan_guard_nonzero_constant(float %x) {
+; CHECK-LABEL: nan_guard_nonzero_constant:
+; CHECK: ; %bb.0:
+; CHECK-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; CHECK-NEXT: v_cvt_i32_f32_e32 v1, v0
+; CHECK-NEXT: v_cmp_o_f32_e32 vcc, v0, v0
+; CHECK-NEXT: v_cndmask_b32_e32 v0, 42, v1, vcc
+; CHECK-NEXT: s_setpc_b64 s[30:31]
+ %cmp = fcmp uno float %x, 0.000000e+00
+ %conv = fptosi float %x to i32
+ %sel = select i1 %cmp, i32 42, i32 %conv
+ ret i32 %sel
+}
+
+define i32 @nan_guard_mismatched_operands(float %x, float %y) {
+; CHECK-LABEL: nan_guard_mismatched_operands:
+; CHECK: ; %bb.0:
+; CHECK-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; CHECK-NEXT: v_cvt_i32_f32_e32 v1, v1
+; CHECK-NEXT: v_cmp_o_f32_e32 vcc, v0, v0
+; CHECK-NEXT: v_cndmask_b32_e32 v0, 0, v1, vcc
+; CHECK-NEXT: s_setpc_b64 s[30:31]
+ %cmp = fcmp uno float %x, 0.000000e+00
+ %conv = fptosi float %y to i32
+ %sel = select i1 %cmp, i32 0, i32 %conv
+ ret i32 %sel
+}
+
+; Compare is a
diff erent predicate (not uno/ord) -- should not fold.
+define i32 @nan_guard_wrong_predicate(float %x) {
+; CHECK-LABEL: nan_guard_wrong_predicate:
+; CHECK: ; %bb.0:
+; CHECK-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; CHECK-NEXT: v_cvt_i32_f32_e32 v1, v0
+; CHECK-NEXT: v_cmp_ngt_f32_e32 vcc, 0, v0
+; CHECK-NEXT: v_cndmask_b32_e32 v0, 0, v1, vcc
+; CHECK-NEXT: s_setpc_b64 s[30:31]
+ %cmp = fcmp olt float %x, 0.000000e+00
+ %conv = fptosi float %x to i32
+ %sel = select i1 %cmp, i32 0, i32 %conv
+ ret i32 %sel
+}
+
+; The vector cases below do not fold: AMDGPU lowers vector fp_to_sint_sat /
+; fp_to_uint_sat via Expand, so shouldConvertFpToSat is false and the guard is
+; preserved. (On targets with a native vector op, e.g. AArch64, they fold.)
+define <4 x i32> @nan_guard_fptosi_v4f32(<4 x float> %x) {
+; CHECK-LABEL: nan_guard_fptosi_v4f32:
+; CHECK: ; %bb.0:
+; CHECK-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; CHECK-NEXT: v_cvt_i32_f32_e32 v5, v0
+; CHECK-NEXT: v_cvt_i32_f32_e32 v7, v1
+; CHECK-NEXT: v_cvt_i32_f32_e32 v6, v2
+; CHECK-NEXT: v_cvt_i32_f32_e32 v4, v3
+; CHECK-NEXT: v_cmp_o_f32_e32 vcc, v0, v0
+; CHECK-NEXT: v_cndmask_b32_e32 v0, 0, v5, vcc
+; CHECK-NEXT: v_cmp_o_f32_e32 vcc, v1, v1
+; CHECK-NEXT: v_cndmask_b32_e32 v1, 0, v7, vcc
+; CHECK-NEXT: v_cmp_o_f32_e32 vcc, v2, v2
+; CHECK-NEXT: v_cndmask_b32_e32 v2, 0, v6, vcc
+; CHECK-NEXT: v_cmp_o_f32_e32 vcc, v3, v3
+; CHECK-NEXT: v_cndmask_b32_e32 v3, 0, v4, vcc
+; CHECK-NEXT: s_setpc_b64 s[30:31]
+ %cmp = fcmp uno <4 x float> %x, zeroinitializer
+ %conv = fptosi <4 x float> %x to <4 x i32>
+ %sel = select <4 x i1> %cmp, <4 x i32> zeroinitializer, <4 x i32> %conv
+ ret <4 x i32> %sel
+}
+
+define <4 x i32> @nan_guard_fptosi_v4f32_and_mask(<4 x float> %x, <4 x i32> %m) {
+; CHECK-LABEL: nan_guard_fptosi_v4f32_and_mask:
+; CHECK: ; %bb.0:
+; CHECK-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; CHECK-NEXT: v_cvt_i32_f32_e32 v11, v0
+; CHECK-NEXT: v_cvt_i32_f32_e32 v10, v1
+; CHECK-NEXT: v_cvt_i32_f32_e32 v9, v2
+; CHECK-NEXT: v_cvt_i32_f32_e32 v8, v3
+; CHECK-NEXT: v_and_b32_e32 v4, v11, v4
+; CHECK-NEXT: v_cmp_o_f32_e32 vcc, v0, v0
+; CHECK-NEXT: v_and_b32_e32 v5, v10, v5
+; CHECK-NEXT: v_cndmask_b32_e32 v0, 0, v4, vcc
+; CHECK-NEXT: v_cmp_o_f32_e32 vcc, v1, v1
+; CHECK-NEXT: v_and_b32_e32 v6, v9, v6
+; CHECK-NEXT: v_cndmask_b32_e32 v1, 0, v5, vcc
+; CHECK-NEXT: v_cmp_o_f32_e32 vcc, v2, v2
+; CHECK-NEXT: v_and_b32_e32 v7, v8, v7
+; CHECK-NEXT: v_cndmask_b32_e32 v2, 0, v6, vcc
+; CHECK-NEXT: v_cmp_o_f32_e32 vcc, v3, v3
+; CHECK-NEXT: v_cndmask_b32_e32 v3, 0, v7, vcc
+; CHECK-NEXT: s_setpc_b64 s[30:31]
+ %cmp = fcmp uno <4 x float> %x, zeroinitializer
+ %conv = fptosi <4 x float> %x to <4 x i32>
+ %and = and <4 x i32> %conv, %m
+ %sel = select <4 x i1> %cmp, <4 x i32> zeroinitializer, <4 x i32> %and
+ ret <4 x i32> %sel
+}
More information about the llvm-commits
mailing list