[llvm] [AMDGPU] Fold redundant inf/nan checks into frexp instructions (PR #207262)
via llvm-commits
llvm-commits at lists.llvm.org
Fri Aug 7 22:52:03 PDT 2026
https://github.com/addmisol updated https://github.com/llvm/llvm-project/pull/207262
>From 359cbdb0f743e6b403cfcf617932407b3786a622 Mon Sep 17 00:00:00 2001
From: addmisol <addmisol9 at gmail.com>
Date: Fri, 3 Jul 2026 00:51:51 +0530
Subject: [PATCH 1/4] Fix for frexp
Signed-off-by: addmisol <addmisol9 at gmail.com>
---
llvm/lib/Target/AMDGPU/SIISelLowering.cpp | 155 ++++++++++++++-
llvm/lib/Target/AMDGPU/SIISelLowering.h | 1 +
.../CodeGen/AMDGPU/frexp-inf-nan-combine.ll | 181 ++++++++++++++++++
3 files changed, 329 insertions(+), 8 deletions(-)
create mode 100644 llvm/test/CodeGen/AMDGPU/frexp-inf-nan-combine.ll
diff --git a/llvm/lib/Target/AMDGPU/SIISelLowering.cpp b/llvm/lib/Target/AMDGPU/SIISelLowering.cpp
index 8a8a3b97e8e08..c34ab2442dc2e 100644
--- a/llvm/lib/Target/AMDGPU/SIISelLowering.cpp
+++ b/llvm/lib/Target/AMDGPU/SIISelLowering.cpp
@@ -15,7 +15,6 @@
#include "AMDGPU.h"
#include "AMDGPUInstrInfo.h"
#include "AMDGPULaneMaskUtils.h"
-#include "AMDGPUMemoryUtils.h"
#include "AMDGPUSelectionDAGInfo.h"
#include "AMDGPUTargetMachine.h"
#include "GCNSubtarget.h"
@@ -9863,13 +9862,6 @@ SDValue SITargetLowering::lowerBUILD_VECTOR(SDValue Op,
bool SITargetLowering::isOffsetFoldingLegal(
const GlobalAddressSDNode *GA) const {
- // Named barriers have fixed, non-relocated LDS addresses, so a constant
- // offset into an array of them can be folded into the address.
- if (GA->getAddressSpace() == AMDGPUAS::LOCAL_ADDRESS) {
- const auto *GV = dyn_cast<GlobalVariable>(GA->getGlobal());
- return GV && AMDGPU::isNamedBarrier(*GV);
- }
-
// OSes that use ELF REL relocations (instead of RELA) can only store a
// 32-bit addend in the instruction, so it is not safe to allow offset folding
// which can create arbitrary 64-bit addends. (This is only a problem for
@@ -18515,6 +18507,151 @@ SDValue SITargetLowering::performClampCombine(SDNode *N,
return SDValue(CSrc, 0);
}
+// Try to fold select of inf/nan check with frexp result.
+// The AMDGPU frexp instructions return 0 for inf/nan inputs (except on SI
+// which has hasFractBug). So patterns like:
+// select (fcmp uno x, 0), 0, (frexp_exp x) -> frexp_exp x
+// select (fcmp oeq |x|, inf), 0, (frexp_exp x) -> frexp_exp x
+// select (fcmp ueq |x|, inf), 0, (frexp_exp x) -> frexp_exp x
+// select (is_fpclass x, finite_mask), frexp, 0 -> frexp_exp x
+// can be simplified to just the frexp result.
+SDValue SITargetLowering::performFrexpSelectCombine(SDNode *N,
+ DAGCombinerInfo &DCI) const {
+ // This optimization only applies when the hardware handles inf/nan correctly.
+ if (Subtarget->hasFractBug())
+ return SDValue();
+
+ SDValue Cond = N->getOperand(0);
+ SDValue TrueVal = N->getOperand(1);
+ SDValue FalseVal = N->getOperand(2);
+
+ // Determine which value is 0 and which might be the frexp result.
+ // Pattern 1: select cond, 0, frexp_result (cond true -> return 0)
+ // Pattern 2: select cond, frexp_result, 0 (cond false -> return 0)
+ SDValue FrexpVal;
+ bool CondSelectsZero; // If true, condition=true selects zero
+
+ auto isZero = [](SDValue V) {
+ if (auto *C = dyn_cast<ConstantSDNode>(V))
+ return C->isZero();
+ if (auto *C = dyn_cast<ConstantFPSDNode>(V))
+ return C->isZero();
+ return false;
+ };
+
+ if (isZero(TrueVal)) {
+ FrexpVal = FalseVal;
+ CondSelectsZero = true;
+ } else if (isZero(FalseVal)) {
+ FrexpVal = TrueVal;
+ CondSelectsZero = false;
+ } else {
+ return SDValue();
+ }
+
+ // Check if FrexpVal comes from amdgcn_frexp_exp or amdgcn_frexp_mant.
+ if (FrexpVal.getOpcode() != ISD::INTRINSIC_WO_CHAIN)
+ return SDValue();
+
+ unsigned IID = FrexpVal.getConstantOperandVal(0);
+ if (IID != Intrinsic::amdgcn_frexp_exp && IID != Intrinsic::amdgcn_frexp_mant)
+ return SDValue();
+
+ SDValue FrexpInput = FrexpVal.getOperand(1);
+
+ // Helper to strip fabs/fneg/fcopysign from a value.
+ auto peekFPSignOps = [](SDValue Val) {
+ if (Val.getOpcode() == ISD::FNEG)
+ Val = Val.getOperand(0);
+ if (Val.getOpcode() == ISD::FABS)
+ Val = Val.getOperand(0);
+ if (Val.getOpcode() == ISD::FCOPYSIGN)
+ Val = Val.getOperand(0);
+ return Val;
+ };
+
+ // The frexp intrinsics ignore sign, so we can strip sign ops when comparing.
+ SDValue FrexpInputStripped = peekFPSignOps(FrexpInput);
+
+ bool IsNonFiniteTest = false;
+
+ // Handle AMDGPUISD::FP_CLASS or ISD::IS_FPCLASS conditions.
+ // These test specific floating-point classes using a bitmask.
+ if (Cond.getOpcode() == AMDGPUISD::FP_CLASS ||
+ Cond.getOpcode() == ISD::IS_FPCLASS) {
+ SDValue ClassInput = Cond.getOperand(0);
+ SDValue ClassInputStripped = peekFPSignOps(ClassInput);
+
+ if (ClassInputStripped != FrexpInputStripped)
+ return SDValue();
+
+ auto *MaskNode = dyn_cast<ConstantSDNode>(Cond.getOperand(1));
+ if (!MaskNode)
+ return SDValue();
+
+ unsigned Mask = MaskNode->getZExtValue();
+
+ // fcFinite = all finite classes (not inf, not nan)
+ // If the mask tests for finite values and selects frexp when true,
+ // we can fold away the select since frexp returns 0 for non-finite.
+ constexpr unsigned fcFinite = 0x1F8; // fcPosNormal|fcNegNormal|fcPosSubnormal|fcNegSubnormal|fcPosZero|fcNegZero
+ constexpr unsigned fcInfNan = 0x207; // fcPosInf|fcNegInf|fcSNan|fcQNan
+
+ if (Mask == fcFinite) {
+ // is_fpclass(x, finite) selects frexp when x is finite
+ // frexp already returns 0 for non-finite, so select frexp, 0 -> frexp
+ IsNonFiniteTest = !CondSelectsZero;
+ } else if (Mask == fcInfNan || Mask == 0x3 || Mask == 0x204) {
+ // is_fpclass(x, inf|nan) or is_fpclass(x, nan) or is_fpclass(x, inf)
+ // selects 0 when x is non-finite
+ IsNonFiniteTest = CondSelectsZero;
+ }
+ } else if (Cond.getOpcode() == ISD::SETCC) {
+ // Handle SETCC conditions for inf/nan tests.
+ ISD::CondCode CC = cast<CondCodeSDNode>(Cond.getOperand(2))->get();
+ SDValue CondLHS = Cond.getOperand(0);
+ SDValue CondRHS = Cond.getOperand(1);
+ SDValue CondLHSStripped = peekFPSignOps(CondLHS);
+
+ auto isInfConstant = [](SDValue V) {
+ auto *CFP = dyn_cast<ConstantFPSDNode>(V);
+ return CFP && CFP->getValueAPF().isInfinity();
+ };
+
+ if (CC == ISD::SETUO) {
+ // fcmp uno x, y - true if either x or y is NaN
+ SDValue CondRHSStripped = peekFPSignOps(CondRHS);
+ if (CondLHSStripped == FrexpInputStripped ||
+ CondRHSStripped == FrexpInputStripped) {
+ IsNonFiniteTest = CondSelectsZero;
+ }
+ } else if ((CC == ISD::SETOEQ || CC == ISD::SETUEQ) &&
+ isInfConstant(CondRHS)) {
+ // fcmp oeq/ueq |x|, inf - true if x is inf (or inf/nan for ueq)
+ if (CondLHSStripped == FrexpInputStripped)
+ IsNonFiniteTest = CondSelectsZero;
+ } else if ((CC == ISD::SETONE || CC == ISD::SETUNE) &&
+ isInfConstant(CondRHS)) {
+ // fcmp one/une |x|, inf - true if x is NOT inf
+ if (CondLHSStripped == FrexpInputStripped)
+ IsNonFiniteTest = !CondSelectsZero;
+ } else if (CC == ISD::SETO) {
+ // fcmp ord x, y - true if both are NOT NaN
+ SDValue CondRHSStripped = peekFPSignOps(CondRHS);
+ if (CondLHSStripped == FrexpInputStripped ||
+ CondRHSStripped == FrexpInputStripped) {
+ IsNonFiniteTest = !CondSelectsZero;
+ }
+ }
+ }
+
+ if (!IsNonFiniteTest)
+ return SDValue();
+
+ // The select can be eliminated - just return the frexp result directly.
+ return FrexpVal;
+}
+
SDValue SITargetLowering::performSelectCombine(SDNode *N,
DAGCombinerInfo &DCI) const {
@@ -18639,6 +18776,8 @@ SDValue SITargetLowering::PerformDAGCombine(SDNode *N,
case ISD::SETCC:
return performSetCCCombine(N, DCI);
case ISD::SELECT:
+ if (auto Res = performFrexpSelectCombine(N, DCI))
+ return Res;
if (auto Res = performSelectCombine(N, DCI))
return Res;
break;
diff --git a/llvm/lib/Target/AMDGPU/SIISelLowering.h b/llvm/lib/Target/AMDGPU/SIISelLowering.h
index c98426cdac0b1..0db6ee1f3fad8 100644
--- a/llvm/lib/Target/AMDGPU/SIISelLowering.h
+++ b/llvm/lib/Target/AMDGPU/SIISelLowering.h
@@ -231,6 +231,7 @@ class SITargetLowering final : public AMDGPUTargetLowering {
SDValue performExtractVectorEltCombine(SDNode *N, DAGCombinerInfo &DCI) const;
SDValue performInsertVectorEltCombine(SDNode *N, DAGCombinerInfo &DCI) const;
SDValue performFPRoundCombine(SDNode *N, DAGCombinerInfo &DCI) const;
+ SDValue performFrexpSelectCombine(SDNode *N, DAGCombinerInfo &DCI) const;
SDValue performSelectCombine(SDNode *N, DAGCombinerInfo &DCI) const;
SDValue reassociateScalarOps(SDNode *N, SelectionDAG &DAG) const;
diff --git a/llvm/test/CodeGen/AMDGPU/frexp-inf-nan-combine.ll b/llvm/test/CodeGen/AMDGPU/frexp-inf-nan-combine.ll
new file mode 100644
index 0000000000000..b0b64a3fd707b
--- /dev/null
+++ b/llvm/test/CodeGen/AMDGPU/frexp-inf-nan-combine.ll
@@ -0,0 +1,181 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc -mtriple=amdgcn-amd-amdhsa -mcpu=gfx1100 < %s | FileCheck -check-prefix=GFX11 %s
+
+; Test that redundant inf/nan checks are folded into frexp instructions.
+; The AMDGPU frexp instructions already return 0 for inf/nan inputs.
+
+declare {float, i32} @llvm.frexp.f32.i32(float)
+declare {double, i32} @llvm.frexp.f64.i32(double)
+declare {half, i16} @llvm.frexp.f16.i16(half)
+declare float @llvm.fabs.f32(float)
+declare double @llvm.fabs.f64(double)
+declare half @llvm.fabs.f16(half)
+
+; Pattern 1: select (fcmp uno x, 0), 0, (frexp_exp x)
+; NaN check - should fold to just frexp_exp
+define i32 @frexp_nan_clamp_exp_f32(float %x) {
+; GFX11-LABEL: frexp_nan_clamp_exp_f32:
+; GFX11: ; %bb.0:
+; GFX11-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX11-NEXT: v_frexp_exp_i32_f32_e32 v0, v0
+; GFX11-NEXT: s_setpc_b64 s[30:31]
+ %frexp = call {float, i32} @llvm.frexp.f32.i32(float %x)
+ %exp = extractvalue {float, i32} %frexp, 1
+ %is_nan = fcmp uno float %x, 0.0
+ %result = select i1 %is_nan, i32 0, i32 %exp
+ ret i32 %result
+}
+
+; Pattern 2: select (fcmp oeq |x|, inf), 0, (frexp_exp x)
+; Inf check - should fold to just frexp_exp
+define i32 @frexp_inf_clamp_exp_f32(float %x) {
+; GFX11-LABEL: frexp_inf_clamp_exp_f32:
+; GFX11: ; %bb.0:
+; GFX11-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX11-NEXT: v_frexp_exp_i32_f32_e32 v0, v0
+; GFX11-NEXT: s_setpc_b64 s[30:31]
+ %frexp = call {float, i32} @llvm.frexp.f32.i32(float %x)
+ %exp = extractvalue {float, i32} %frexp, 1
+ %abs = call float @llvm.fabs.f32(float %x)
+ %is_inf = fcmp oeq float %abs, 0x7FF0000000000000
+ %result = select i1 %is_inf, i32 0, i32 %exp
+ ret i32 %result
+}
+
+; Pattern 3: select (fcmp ueq |x|, inf), 0, (frexp_exp x)
+; Inf-or-NaN check - should fold to just frexp_exp
+define i32 @frexp_inf_or_nan_clamp_exp_f32(float %x) {
+; GFX11-LABEL: frexp_inf_or_nan_clamp_exp_f32:
+; GFX11: ; %bb.0:
+; GFX11-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX11-NEXT: v_frexp_exp_i32_f32_e32 v0, v0
+; GFX11-NEXT: s_setpc_b64 s[30:31]
+ %frexp = call { float, i32 } @llvm.frexp.f32.i32(float %x)
+ %exp = extractvalue { float, i32 } %frexp, 1
+ %abs = call float @llvm.fabs.f32(float %x)
+ %is_non_finite = fcmp ueq float %abs, 0x7FF0000000000000
+ %result = select i1 %is_non_finite, i32 0, i32 %exp
+ ret i32 %result
+}
+
+; Test with frexp_mant instead of frexp_exp
+define float @frexp_nan_clamp_mant_f32(float %x) {
+; GFX11-LABEL: frexp_nan_clamp_mant_f32:
+; GFX11: ; %bb.0:
+; GFX11-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX11-NEXT: v_frexp_mant_f32_e32 v0, v0
+; GFX11-NEXT: s_setpc_b64 s[30:31]
+ %frexp = call {float, i32} @llvm.frexp.f32.i32(float %x)
+ %mant = extractvalue {float, i32} %frexp, 0
+ %is_nan = fcmp uno float %x, 0.0
+ %result = select i1 %is_nan, float 0.0, float %mant
+ ret float %result
+}
+
+; Test with reversed select operands: select (fcmp ord x, 0), frexp, 0
+define i32 @frexp_ord_clamp_exp_f32(float %x) {
+; GFX11-LABEL: frexp_ord_clamp_exp_f32:
+; GFX11: ; %bb.0:
+; GFX11-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX11-NEXT: v_frexp_exp_i32_f32_e32 v0, v0
+; GFX11-NEXT: s_setpc_b64 s[30:31]
+ %frexp = call {float, i32} @llvm.frexp.f32.i32(float %x)
+ %exp = extractvalue {float, i32} %frexp, 1
+ %is_ord = fcmp ord float %x, 0.0
+ %result = select i1 %is_ord, i32 %exp, i32 0
+ ret i32 %result
+}
+
+; Test with fcmp one |x|, inf (not inf check -> select frexp)
+define i32 @frexp_not_inf_clamp_exp_f32(float %x) {
+; GFX11-LABEL: frexp_not_inf_clamp_exp_f32:
+; GFX11: ; %bb.0:
+; GFX11-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX11-NEXT: v_frexp_exp_i32_f32_e32 v0, v0
+; GFX11-NEXT: s_setpc_b64 s[30:31]
+ %frexp = call {float, i32} @llvm.frexp.f32.i32(float %x)
+ %exp = extractvalue {float, i32} %frexp, 1
+ %abs = call float @llvm.fabs.f32(float %x)
+ %is_not_inf = fcmp one float %abs, 0x7FF0000000000000
+ %result = select i1 %is_not_inf, i32 %exp, i32 0
+ ret i32 %result
+}
+
+; Test f64
+define i32 @frexp_nan_clamp_exp_f64(double %x) {
+; GFX11-LABEL: frexp_nan_clamp_exp_f64:
+; GFX11: ; %bb.0:
+; GFX11-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX11-NEXT: v_frexp_exp_i32_f64_e32 v0, v[0:1]
+; GFX11-NEXT: s_setpc_b64 s[30:31]
+ %frexp = call {double, i32} @llvm.frexp.f64.i32(double %x)
+ %exp = extractvalue {double, i32} %frexp, 1
+ %is_nan = fcmp uno double %x, 0.0
+ %result = select i1 %is_nan, i32 0, i32 %exp
+ ret i32 %result
+}
+
+; Test f16
+define i16 @frexp_nan_clamp_exp_f16(half %x) {
+; GFX11-LABEL: frexp_nan_clamp_exp_f16:
+; GFX11: ; %bb.0:
+; GFX11-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX11-NEXT: v_frexp_exp_i16_f16_e32 v0.l, v0.l
+; GFX11-NEXT: s_setpc_b64 s[30:31]
+ %frexp = call {half, i16} @llvm.frexp.f16.i16(half %x)
+ %exp = extractvalue {half, i16} %frexp, 1
+ %is_nan = fcmp uno half %x, 0.0
+ %result = select i1 %is_nan, i16 0, i16 %exp
+ ret i16 %result
+}
+
+; Negative test: different input to frexp vs comparison
+define i32 @frexp_nan_different_input(float %x, float %y) {
+; GFX11-LABEL: frexp_nan_different_input:
+; GFX11: ; %bb.0:
+; GFX11-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX11-NEXT: v_frexp_exp_i32_f32_e32 v0, v0
+; GFX11-NEXT: v_cmp_o_f32_e32 vcc_lo, v1, v1
+; GFX11-NEXT: s_delay_alu instid0(VALU_DEP_2)
+; GFX11-NEXT: v_cndmask_b32_e32 v0, 0, v0, vcc_lo
+; GFX11-NEXT: s_setpc_b64 s[30:31]
+ %frexp = call {float, i32} @llvm.frexp.f32.i32(float %x)
+ %exp = extractvalue {float, i32} %frexp, 1
+ %is_nan = fcmp uno float %y, 0.0
+ %result = select i1 %is_nan, i32 0, i32 %exp
+ ret i32 %result
+}
+
+; Negative test: non-zero constant in select
+define i32 @frexp_nan_nonzero_const(float %x) {
+; GFX11-LABEL: frexp_nan_nonzero_const:
+; GFX11: ; %bb.0:
+; GFX11-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX11-NEXT: v_frexp_exp_i32_f32_e32 v1, v0
+; GFX11-NEXT: v_cmp_o_f32_e32 vcc_lo, v0, v0
+; GFX11-NEXT: s_delay_alu instid0(VALU_DEP_2)
+; GFX11-NEXT: v_cndmask_b32_e32 v0, 42, v1, vcc_lo
+; GFX11-NEXT: s_setpc_b64 s[30:31]
+ %frexp = call {float, i32} @llvm.frexp.f32.i32(float %x)
+ %exp = extractvalue {float, i32} %frexp, 1
+ %is_nan = fcmp uno float %x, 0.0
+ %result = select i1 %is_nan, i32 42, i32 %exp
+ ret i32 %result
+}
+
+; Negative test: unrelated comparison (not inf/nan test)
+define i32 @frexp_lt_zero_not_folded(float %x) {
+; GFX11-LABEL: frexp_lt_zero_not_folded:
+; GFX11: ; %bb.0:
+; GFX11-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX11-NEXT: v_frexp_exp_i32_f32_e32 v1, v0
+; GFX11-NEXT: v_cmp_ngt_f32_e32 vcc_lo, 0, v0
+; GFX11-NEXT: s_delay_alu instid0(VALU_DEP_2)
+; GFX11-NEXT: v_cndmask_b32_e32 v0, 0, v1, vcc_lo
+; GFX11-NEXT: s_setpc_b64 s[30:31]
+ %frexp = call {float, i32} @llvm.frexp.f32.i32(float %x)
+ %exp = extractvalue {float, i32} %frexp, 1
+ %is_lt_zero = fcmp olt float %x, 0.0
+ %result = select i1 %is_lt_zero, i32 0, i32 %exp
+ ret i32 %result
+}
>From acaf59734ab1488f9e0fe9f9a004e1cc23b5a56e Mon Sep 17 00:00:00 2001
From: addmisol <addmisol9 at gmail.com>
Date: Fri, 3 Jul 2026 01:03:55 +0530
Subject: [PATCH 2/4] rebase
Signed-off-by: addmisol <addmisol9 at gmail.com>
---
llvm/lib/Target/AMDGPU/SIISelLowering.cpp | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/llvm/lib/Target/AMDGPU/SIISelLowering.cpp b/llvm/lib/Target/AMDGPU/SIISelLowering.cpp
index c34ab2442dc2e..b9f242adf4d27 100644
--- a/llvm/lib/Target/AMDGPU/SIISelLowering.cpp
+++ b/llvm/lib/Target/AMDGPU/SIISelLowering.cpp
@@ -15,6 +15,7 @@
#include "AMDGPU.h"
#include "AMDGPUInstrInfo.h"
#include "AMDGPULaneMaskUtils.h"
+#include "AMDGPUMemoryUtils.h"
#include "AMDGPUSelectionDAGInfo.h"
#include "AMDGPUTargetMachine.h"
#include "GCNSubtarget.h"
@@ -9862,6 +9863,13 @@ SDValue SITargetLowering::lowerBUILD_VECTOR(SDValue Op,
bool SITargetLowering::isOffsetFoldingLegal(
const GlobalAddressSDNode *GA) const {
+ // Named barriers have fixed, non-relocated LDS addresses, so a constant
+ // offset into an array of them can be folded into the address.
+ if (GA->getAddressSpace() == AMDGPUAS::LOCAL_ADDRESS) {
+ const auto *GV = dyn_cast<GlobalVariable>(GA->getGlobal());
+ return GV && AMDGPU::isNamedBarrier(*GV);
+ }
+
// OSes that use ELF REL relocations (instead of RELA) can only store a
// 32-bit addend in the instruction, so it is not safe to allow offset folding
// which can create arbitrary 64-bit addends. (This is only a problem for
>From 1c10d8ae7113783069da2cec300a54a9695d2b95 Mon Sep 17 00:00:00 2001
From: addmisol <addmisol9 at gmail.com>
Date: Sat, 8 Aug 2026 09:20:51 +0530
Subject: [PATCH 3/4] Add test for other arch
Signed-off-by: addmisol <addmisol9 at gmail.com>
---
.../CodeGen/AMDGPU/frexp-inf-nan-combine.ll | 347 ++++++++++++++++++
1 file changed, 347 insertions(+)
diff --git a/llvm/test/CodeGen/AMDGPU/frexp-inf-nan-combine.ll b/llvm/test/CodeGen/AMDGPU/frexp-inf-nan-combine.ll
index b0b64a3fd707b..c2a763e58e2c2 100644
--- a/llvm/test/CodeGen/AMDGPU/frexp-inf-nan-combine.ll
+++ b/llvm/test/CodeGen/AMDGPU/frexp-inf-nan-combine.ll
@@ -1,8 +1,13 @@
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc -mtriple=amdgcn-amd-amdhsa -mcpu=gfx900 < %s | FileCheck -check-prefix=GFX9 %s
+; RUN: llc -mtriple=amdgcn-amd-amdhsa -mcpu=gfx1030 < %s | FileCheck -check-prefix=GFX10 %s
; RUN: llc -mtriple=amdgcn-amd-amdhsa -mcpu=gfx1100 < %s | FileCheck -check-prefix=GFX11 %s
+; RUN: llc -mtriple=amdgcn-amd-amdhsa -mcpu=gfx950 < %s | FileCheck -check-prefix=GFX950 %s
+; RUN: llc -mtriple=amdgcn-- -mcpu=tahiti < %s | FileCheck -check-prefix=SI %s
; Test that redundant inf/nan checks are folded into frexp instructions.
; The AMDGPU frexp instructions already return 0 for inf/nan inputs.
+; This optimization should NOT apply on SI (Southern Islands) which has hasFractBug.
declare {float, i32} @llvm.frexp.f32.i32(float)
declare {double, i32} @llvm.frexp.f64.i32(double)
@@ -14,11 +19,40 @@ declare half @llvm.fabs.f16(half)
; Pattern 1: select (fcmp uno x, 0), 0, (frexp_exp x)
; NaN check - should fold to just frexp_exp
define i32 @frexp_nan_clamp_exp_f32(float %x) {
+; GFX9-LABEL: frexp_nan_clamp_exp_f32:
+; GFX9: ; %bb.0:
+; GFX9-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX9-NEXT: v_frexp_exp_i32_f32_e32 v0, v0
+; GFX9-NEXT: s_setpc_b64 s[30:31]
+;
+; GFX10-LABEL: frexp_nan_clamp_exp_f32:
+; GFX10: ; %bb.0:
+; GFX10-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX10-NEXT: v_frexp_exp_i32_f32_e32 v0, v0
+; GFX10-NEXT: s_setpc_b64 s[30:31]
+;
; GFX11-LABEL: frexp_nan_clamp_exp_f32:
; GFX11: ; %bb.0:
; GFX11-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
; GFX11-NEXT: v_frexp_exp_i32_f32_e32 v0, v0
; GFX11-NEXT: s_setpc_b64 s[30:31]
+;
+; GFX950-LABEL: frexp_nan_clamp_exp_f32:
+; GFX950: ; %bb.0:
+; GFX950-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX950-NEXT: v_frexp_exp_i32_f32_e32 v0, v0
+; GFX950-NEXT: s_setpc_b64 s[30:31]
+;
+; SI-LABEL: frexp_nan_clamp_exp_f32:
+; SI: ; %bb.0:
+; SI-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; SI-NEXT: s_mov_b32 s4, 0x7f800000
+; SI-NEXT: v_cmp_lt_f32_e64 s[4:5], |v0|, s4
+; SI-NEXT: v_cmp_o_f32_e32 vcc, v0, v0
+; SI-NEXT: v_frexp_exp_i32_f32_e32 v1, v0
+; SI-NEXT: s_and_b64 vcc, vcc, s[4:5]
+; SI-NEXT: v_cndmask_b32_e32 v0, 0, v1, vcc
+; SI-NEXT: s_setpc_b64 s[30:31]
%frexp = call {float, i32} @llvm.frexp.f32.i32(float %x)
%exp = extractvalue {float, i32} %frexp, 1
%is_nan = fcmp uno float %x, 0.0
@@ -29,11 +63,38 @@ define i32 @frexp_nan_clamp_exp_f32(float %x) {
; Pattern 2: select (fcmp oeq |x|, inf), 0, (frexp_exp x)
; Inf check - should fold to just frexp_exp
define i32 @frexp_inf_clamp_exp_f32(float %x) {
+; GFX9-LABEL: frexp_inf_clamp_exp_f32:
+; GFX9: ; %bb.0:
+; GFX9-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX9-NEXT: v_frexp_exp_i32_f32_e32 v0, v0
+; GFX9-NEXT: s_setpc_b64 s[30:31]
+;
+; GFX10-LABEL: frexp_inf_clamp_exp_f32:
+; GFX10: ; %bb.0:
+; GFX10-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX10-NEXT: v_frexp_exp_i32_f32_e32 v0, v0
+; GFX10-NEXT: s_setpc_b64 s[30:31]
+;
; GFX11-LABEL: frexp_inf_clamp_exp_f32:
; GFX11: ; %bb.0:
; GFX11-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
; GFX11-NEXT: v_frexp_exp_i32_f32_e32 v0, v0
; GFX11-NEXT: s_setpc_b64 s[30:31]
+;
+; GFX950-LABEL: frexp_inf_clamp_exp_f32:
+; GFX950: ; %bb.0:
+; GFX950-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX950-NEXT: v_frexp_exp_i32_f32_e32 v0, v0
+; GFX950-NEXT: s_setpc_b64 s[30:31]
+;
+; SI-LABEL: frexp_inf_clamp_exp_f32:
+; SI: ; %bb.0:
+; SI-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; SI-NEXT: s_mov_b32 s4, 0x7f800000
+; SI-NEXT: v_frexp_exp_i32_f32_e32 v1, v0
+; SI-NEXT: v_cmp_lt_f32_e64 vcc, |v0|, s4
+; SI-NEXT: v_cndmask_b32_e32 v0, 0, v1, vcc
+; SI-NEXT: s_setpc_b64 s[30:31]
%frexp = call {float, i32} @llvm.frexp.f32.i32(float %x)
%exp = extractvalue {float, i32} %frexp, 1
%abs = call float @llvm.fabs.f32(float %x)
@@ -45,11 +106,41 @@ define i32 @frexp_inf_clamp_exp_f32(float %x) {
; Pattern 3: select (fcmp ueq |x|, inf), 0, (frexp_exp x)
; Inf-or-NaN check - should fold to just frexp_exp
define i32 @frexp_inf_or_nan_clamp_exp_f32(float %x) {
+; GFX9-LABEL: frexp_inf_or_nan_clamp_exp_f32:
+; GFX9: ; %bb.0:
+; GFX9-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX9-NEXT: v_frexp_exp_i32_f32_e32 v0, v0
+; GFX9-NEXT: s_setpc_b64 s[30:31]
+;
+; GFX10-LABEL: frexp_inf_or_nan_clamp_exp_f32:
+; GFX10: ; %bb.0:
+; GFX10-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX10-NEXT: v_frexp_exp_i32_f32_e32 v0, v0
+; GFX10-NEXT: s_setpc_b64 s[30:31]
+;
; GFX11-LABEL: frexp_inf_or_nan_clamp_exp_f32:
; GFX11: ; %bb.0:
; GFX11-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
; GFX11-NEXT: v_frexp_exp_i32_f32_e32 v0, v0
; GFX11-NEXT: s_setpc_b64 s[30:31]
+;
+; GFX950-LABEL: frexp_inf_or_nan_clamp_exp_f32:
+; GFX950: ; %bb.0:
+; GFX950-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX950-NEXT: v_frexp_exp_i32_f32_e32 v0, v0
+; GFX950-NEXT: s_setpc_b64 s[30:31]
+;
+; SI-LABEL: frexp_inf_or_nan_clamp_exp_f32:
+; SI: ; %bb.0:
+; SI-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; SI-NEXT: s_mov_b32 s4, 0x7f800000
+; SI-NEXT: s_movk_i32 s6, 0x1f8
+; SI-NEXT: v_cmp_lt_f32_e64 s[4:5], |v0|, s4
+; SI-NEXT: v_cmp_class_f32_e64 s[6:7], v0, s6
+; SI-NEXT: v_frexp_exp_i32_f32_e32 v1, v0
+; SI-NEXT: s_and_b64 vcc, s[6:7], s[4:5]
+; SI-NEXT: v_cndmask_b32_e32 v0, 0, v1, vcc
+; SI-NEXT: s_setpc_b64 s[30:31]
%frexp = call { float, i32 } @llvm.frexp.f32.i32(float %x)
%exp = extractvalue { float, i32 } %frexp, 1
%abs = call float @llvm.fabs.f32(float %x)
@@ -60,11 +151,40 @@ define i32 @frexp_inf_or_nan_clamp_exp_f32(float %x) {
; Test with frexp_mant instead of frexp_exp
define float @frexp_nan_clamp_mant_f32(float %x) {
+; GFX9-LABEL: frexp_nan_clamp_mant_f32:
+; GFX9: ; %bb.0:
+; GFX9-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX9-NEXT: v_frexp_mant_f32_e32 v0, v0
+; GFX9-NEXT: s_setpc_b64 s[30:31]
+;
+; GFX10-LABEL: frexp_nan_clamp_mant_f32:
+; GFX10: ; %bb.0:
+; GFX10-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX10-NEXT: v_frexp_mant_f32_e32 v0, v0
+; GFX10-NEXT: s_setpc_b64 s[30:31]
+;
; GFX11-LABEL: frexp_nan_clamp_mant_f32:
; GFX11: ; %bb.0:
; GFX11-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
; GFX11-NEXT: v_frexp_mant_f32_e32 v0, v0
; GFX11-NEXT: s_setpc_b64 s[30:31]
+;
+; GFX950-LABEL: frexp_nan_clamp_mant_f32:
+; GFX950: ; %bb.0:
+; GFX950-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX950-NEXT: v_frexp_mant_f32_e32 v0, v0
+; GFX950-NEXT: s_setpc_b64 s[30:31]
+;
+; SI-LABEL: frexp_nan_clamp_mant_f32:
+; SI: ; %bb.0:
+; SI-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; SI-NEXT: s_mov_b32 s4, 0x7f800000
+; SI-NEXT: v_frexp_mant_f32_e32 v1, v0
+; SI-NEXT: v_cmp_lt_f32_e64 vcc, |v0|, s4
+; SI-NEXT: v_cndmask_b32_e32 v1, v0, v1, vcc
+; SI-NEXT: v_cmp_o_f32_e32 vcc, v0, v0
+; SI-NEXT: v_cndmask_b32_e32 v0, 0, v1, vcc
+; SI-NEXT: s_setpc_b64 s[30:31]
%frexp = call {float, i32} @llvm.frexp.f32.i32(float %x)
%mant = extractvalue {float, i32} %frexp, 0
%is_nan = fcmp uno float %x, 0.0
@@ -74,11 +194,40 @@ define float @frexp_nan_clamp_mant_f32(float %x) {
; Test with reversed select operands: select (fcmp ord x, 0), frexp, 0
define i32 @frexp_ord_clamp_exp_f32(float %x) {
+; GFX9-LABEL: frexp_ord_clamp_exp_f32:
+; GFX9: ; %bb.0:
+; GFX9-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX9-NEXT: v_frexp_exp_i32_f32_e32 v0, v0
+; GFX9-NEXT: s_setpc_b64 s[30:31]
+;
+; GFX10-LABEL: frexp_ord_clamp_exp_f32:
+; GFX10: ; %bb.0:
+; GFX10-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX10-NEXT: v_frexp_exp_i32_f32_e32 v0, v0
+; GFX10-NEXT: s_setpc_b64 s[30:31]
+;
; GFX11-LABEL: frexp_ord_clamp_exp_f32:
; GFX11: ; %bb.0:
; GFX11-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
; GFX11-NEXT: v_frexp_exp_i32_f32_e32 v0, v0
; GFX11-NEXT: s_setpc_b64 s[30:31]
+;
+; GFX950-LABEL: frexp_ord_clamp_exp_f32:
+; GFX950: ; %bb.0:
+; GFX950-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX950-NEXT: v_frexp_exp_i32_f32_e32 v0, v0
+; GFX950-NEXT: s_setpc_b64 s[30:31]
+;
+; SI-LABEL: frexp_ord_clamp_exp_f32:
+; SI: ; %bb.0:
+; SI-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; SI-NEXT: s_mov_b32 s4, 0x7f800000
+; SI-NEXT: v_cmp_lt_f32_e64 s[4:5], |v0|, s4
+; SI-NEXT: v_cmp_o_f32_e32 vcc, v0, v0
+; SI-NEXT: v_frexp_exp_i32_f32_e32 v1, v0
+; SI-NEXT: s_and_b64 vcc, vcc, s[4:5]
+; SI-NEXT: v_cndmask_b32_e32 v0, 0, v1, vcc
+; SI-NEXT: s_setpc_b64 s[30:31]
%frexp = call {float, i32} @llvm.frexp.f32.i32(float %x)
%exp = extractvalue {float, i32} %frexp, 1
%is_ord = fcmp ord float %x, 0.0
@@ -88,11 +237,41 @@ define i32 @frexp_ord_clamp_exp_f32(float %x) {
; Test with fcmp one |x|, inf (not inf check -> select frexp)
define i32 @frexp_not_inf_clamp_exp_f32(float %x) {
+; GFX9-LABEL: frexp_not_inf_clamp_exp_f32:
+; GFX9: ; %bb.0:
+; GFX9-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX9-NEXT: v_frexp_exp_i32_f32_e32 v0, v0
+; GFX9-NEXT: s_setpc_b64 s[30:31]
+;
+; GFX10-LABEL: frexp_not_inf_clamp_exp_f32:
+; GFX10: ; %bb.0:
+; GFX10-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX10-NEXT: v_frexp_exp_i32_f32_e32 v0, v0
+; GFX10-NEXT: s_setpc_b64 s[30:31]
+;
; GFX11-LABEL: frexp_not_inf_clamp_exp_f32:
; GFX11: ; %bb.0:
; GFX11-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
; GFX11-NEXT: v_frexp_exp_i32_f32_e32 v0, v0
; GFX11-NEXT: s_setpc_b64 s[30:31]
+;
+; GFX950-LABEL: frexp_not_inf_clamp_exp_f32:
+; GFX950: ; %bb.0:
+; GFX950-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX950-NEXT: v_frexp_exp_i32_f32_e32 v0, v0
+; GFX950-NEXT: s_setpc_b64 s[30:31]
+;
+; SI-LABEL: frexp_not_inf_clamp_exp_f32:
+; SI: ; %bb.0:
+; SI-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; SI-NEXT: s_mov_b32 s4, 0x7f800000
+; SI-NEXT: s_movk_i32 s6, 0x1f8
+; SI-NEXT: v_cmp_lt_f32_e64 s[4:5], |v0|, s4
+; SI-NEXT: v_cmp_class_f32_e64 s[6:7], v0, s6
+; SI-NEXT: v_frexp_exp_i32_f32_e32 v1, v0
+; SI-NEXT: s_and_b64 vcc, s[6:7], s[4:5]
+; SI-NEXT: v_cndmask_b32_e32 v0, 0, v1, vcc
+; SI-NEXT: s_setpc_b64 s[30:31]
%frexp = call {float, i32} @llvm.frexp.f32.i32(float %x)
%exp = extractvalue {float, i32} %frexp, 1
%abs = call float @llvm.fabs.f32(float %x)
@@ -103,11 +282,41 @@ define i32 @frexp_not_inf_clamp_exp_f32(float %x) {
; Test f64
define i32 @frexp_nan_clamp_exp_f64(double %x) {
+; GFX9-LABEL: frexp_nan_clamp_exp_f64:
+; GFX9: ; %bb.0:
+; GFX9-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX9-NEXT: v_frexp_exp_i32_f64_e32 v0, v[0:1]
+; GFX9-NEXT: s_setpc_b64 s[30:31]
+;
+; GFX10-LABEL: frexp_nan_clamp_exp_f64:
+; GFX10: ; %bb.0:
+; GFX10-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX10-NEXT: v_frexp_exp_i32_f64_e32 v0, v[0:1]
+; GFX10-NEXT: s_setpc_b64 s[30:31]
+;
; GFX11-LABEL: frexp_nan_clamp_exp_f64:
; GFX11: ; %bb.0:
; GFX11-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
; GFX11-NEXT: v_frexp_exp_i32_f64_e32 v0, v[0:1]
; GFX11-NEXT: s_setpc_b64 s[30:31]
+;
+; GFX950-LABEL: frexp_nan_clamp_exp_f64:
+; GFX950: ; %bb.0:
+; GFX950-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX950-NEXT: v_frexp_exp_i32_f64_e32 v0, v[0:1]
+; GFX950-NEXT: s_setpc_b64 s[30:31]
+;
+; SI-LABEL: frexp_nan_clamp_exp_f64:
+; SI: ; %bb.0:
+; SI-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; SI-NEXT: v_and_b32_e32 v2, 0x7fffffff, v1
+; SI-NEXT: s_mov_b32 s4, 0x7ff00000
+; SI-NEXT: v_cmp_gt_i32_e32 vcc, s4, v2
+; SI-NEXT: v_cmp_o_f64_e64 s[4:5], v[0:1], v[0:1]
+; SI-NEXT: v_frexp_exp_i32_f64_e32 v0, v[0:1]
+; SI-NEXT: s_and_b64 vcc, s[4:5], vcc
+; SI-NEXT: v_cndmask_b32_e32 v0, 0, v0, vcc
+; SI-NEXT: s_setpc_b64 s[30:31]
%frexp = call {double, i32} @llvm.frexp.f64.i32(double %x)
%exp = extractvalue {double, i32} %frexp, 1
%is_nan = fcmp uno double %x, 0.0
@@ -117,11 +326,41 @@ define i32 @frexp_nan_clamp_exp_f64(double %x) {
; Test f16
define i16 @frexp_nan_clamp_exp_f16(half %x) {
+; GFX9-LABEL: frexp_nan_clamp_exp_f16:
+; GFX9: ; %bb.0:
+; GFX9-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX9-NEXT: v_frexp_exp_i16_f16_e32 v0, v0
+; GFX9-NEXT: s_setpc_b64 s[30:31]
+;
+; GFX10-LABEL: frexp_nan_clamp_exp_f16:
+; GFX10: ; %bb.0:
+; GFX10-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX10-NEXT: v_frexp_exp_i16_f16_e32 v0, v0
+; GFX10-NEXT: s_setpc_b64 s[30:31]
+;
; GFX11-LABEL: frexp_nan_clamp_exp_f16:
; GFX11: ; %bb.0:
; GFX11-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
; GFX11-NEXT: v_frexp_exp_i16_f16_e32 v0.l, v0.l
; GFX11-NEXT: s_setpc_b64 s[30:31]
+;
+; GFX950-LABEL: frexp_nan_clamp_exp_f16:
+; GFX950: ; %bb.0:
+; GFX950-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX950-NEXT: v_frexp_exp_i16_f16_e32 v0, v0
+; GFX950-NEXT: s_setpc_b64 s[30:31]
+;
+; SI-LABEL: frexp_nan_clamp_exp_f16:
+; SI: ; %bb.0:
+; SI-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; SI-NEXT: v_cvt_f32_f16_e32 v0, v0
+; SI-NEXT: s_mov_b32 s4, 0x7f800000
+; SI-NEXT: v_cmp_lt_f32_e64 s[4:5], |v0|, s4
+; SI-NEXT: v_cmp_o_f32_e32 vcc, v0, v0
+; SI-NEXT: v_frexp_exp_i32_f32_e32 v1, v0
+; SI-NEXT: s_and_b64 vcc, vcc, s[4:5]
+; SI-NEXT: v_cndmask_b32_e32 v0, 0, v1, vcc
+; SI-NEXT: s_setpc_b64 s[30:31]
%frexp = call {half, i16} @llvm.frexp.f16.i16(half %x)
%exp = extractvalue {half, i16} %frexp, 1
%is_nan = fcmp uno half %x, 0.0
@@ -131,6 +370,22 @@ define i16 @frexp_nan_clamp_exp_f16(half %x) {
; Negative test: different input to frexp vs comparison
define i32 @frexp_nan_different_input(float %x, float %y) {
+; GFX9-LABEL: frexp_nan_different_input:
+; GFX9: ; %bb.0:
+; GFX9-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX9-NEXT: v_frexp_exp_i32_f32_e32 v0, v0
+; GFX9-NEXT: v_cmp_o_f32_e32 vcc, v1, v1
+; GFX9-NEXT: v_cndmask_b32_e32 v0, 0, v0, vcc
+; GFX9-NEXT: s_setpc_b64 s[30:31]
+;
+; GFX10-LABEL: frexp_nan_different_input:
+; GFX10: ; %bb.0:
+; GFX10-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX10-NEXT: v_frexp_exp_i32_f32_e32 v0, v0
+; GFX10-NEXT: v_cmp_o_f32_e32 vcc_lo, v1, v1
+; GFX10-NEXT: v_cndmask_b32_e32 v0, 0, v0, vcc_lo
+; GFX10-NEXT: s_setpc_b64 s[30:31]
+;
; GFX11-LABEL: frexp_nan_different_input:
; GFX11: ; %bb.0:
; GFX11-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
@@ -139,6 +394,26 @@ define i32 @frexp_nan_different_input(float %x, float %y) {
; GFX11-NEXT: s_delay_alu instid0(VALU_DEP_2)
; GFX11-NEXT: v_cndmask_b32_e32 v0, 0, v0, vcc_lo
; GFX11-NEXT: s_setpc_b64 s[30:31]
+;
+; GFX950-LABEL: frexp_nan_different_input:
+; GFX950: ; %bb.0:
+; GFX950-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX950-NEXT: v_frexp_exp_i32_f32_e32 v0, v0
+; GFX950-NEXT: v_cmp_o_f32_e32 vcc, v1, v1
+; GFX950-NEXT: s_nop 1
+; GFX950-NEXT: v_cndmask_b32_e32 v0, 0, v0, vcc
+; GFX950-NEXT: s_setpc_b64 s[30:31]
+;
+; SI-LABEL: frexp_nan_different_input:
+; SI: ; %bb.0:
+; SI-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; SI-NEXT: s_mov_b32 s4, 0x7f800000
+; SI-NEXT: v_cmp_lt_f32_e64 s[4:5], |v0|, s4
+; SI-NEXT: v_cmp_o_f32_e32 vcc, v1, v1
+; SI-NEXT: v_frexp_exp_i32_f32_e32 v0, v0
+; SI-NEXT: s_and_b64 vcc, vcc, s[4:5]
+; SI-NEXT: v_cndmask_b32_e32 v0, 0, v0, vcc
+; SI-NEXT: s_setpc_b64 s[30:31]
%frexp = call {float, i32} @llvm.frexp.f32.i32(float %x)
%exp = extractvalue {float, i32} %frexp, 1
%is_nan = fcmp uno float %y, 0.0
@@ -148,6 +423,22 @@ define i32 @frexp_nan_different_input(float %x, float %y) {
; Negative test: non-zero constant in select
define i32 @frexp_nan_nonzero_const(float %x) {
+; GFX9-LABEL: frexp_nan_nonzero_const:
+; GFX9: ; %bb.0:
+; GFX9-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX9-NEXT: v_frexp_exp_i32_f32_e32 v1, v0
+; GFX9-NEXT: v_cmp_o_f32_e32 vcc, v0, v0
+; GFX9-NEXT: v_cndmask_b32_e32 v0, 42, v1, vcc
+; GFX9-NEXT: s_setpc_b64 s[30:31]
+;
+; GFX10-LABEL: frexp_nan_nonzero_const:
+; GFX10: ; %bb.0:
+; GFX10-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX10-NEXT: v_frexp_exp_i32_f32_e32 v1, v0
+; GFX10-NEXT: v_cmp_o_f32_e32 vcc_lo, v0, v0
+; GFX10-NEXT: v_cndmask_b32_e32 v0, 42, v1, vcc_lo
+; GFX10-NEXT: s_setpc_b64 s[30:31]
+;
; GFX11-LABEL: frexp_nan_nonzero_const:
; GFX11: ; %bb.0:
; GFX11-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
@@ -156,6 +447,26 @@ define i32 @frexp_nan_nonzero_const(float %x) {
; GFX11-NEXT: s_delay_alu instid0(VALU_DEP_2)
; GFX11-NEXT: v_cndmask_b32_e32 v0, 42, v1, vcc_lo
; GFX11-NEXT: s_setpc_b64 s[30:31]
+;
+; GFX950-LABEL: frexp_nan_nonzero_const:
+; GFX950: ; %bb.0:
+; GFX950-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX950-NEXT: v_frexp_exp_i32_f32_e32 v1, v0
+; GFX950-NEXT: v_cmp_o_f32_e32 vcc, v0, v0
+; GFX950-NEXT: s_nop 1
+; GFX950-NEXT: v_cndmask_b32_e32 v0, 42, v1, vcc
+; GFX950-NEXT: s_setpc_b64 s[30:31]
+;
+; SI-LABEL: frexp_nan_nonzero_const:
+; SI: ; %bb.0:
+; SI-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; SI-NEXT: s_mov_b32 s4, 0x7f800000
+; SI-NEXT: v_frexp_exp_i32_f32_e32 v1, v0
+; SI-NEXT: v_cmp_lt_f32_e64 vcc, |v0|, s4
+; SI-NEXT: v_cndmask_b32_e32 v1, 0, v1, vcc
+; SI-NEXT: v_cmp_o_f32_e32 vcc, v0, v0
+; SI-NEXT: v_cndmask_b32_e32 v0, 42, v1, vcc
+; SI-NEXT: s_setpc_b64 s[30:31]
%frexp = call {float, i32} @llvm.frexp.f32.i32(float %x)
%exp = extractvalue {float, i32} %frexp, 1
%is_nan = fcmp uno float %x, 0.0
@@ -165,6 +476,22 @@ define i32 @frexp_nan_nonzero_const(float %x) {
; Negative test: unrelated comparison (not inf/nan test)
define i32 @frexp_lt_zero_not_folded(float %x) {
+; GFX9-LABEL: frexp_lt_zero_not_folded:
+; GFX9: ; %bb.0:
+; GFX9-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX9-NEXT: v_frexp_exp_i32_f32_e32 v1, v0
+; GFX9-NEXT: v_cmp_ngt_f32_e32 vcc, 0, v0
+; GFX9-NEXT: v_cndmask_b32_e32 v0, 0, v1, vcc
+; GFX9-NEXT: s_setpc_b64 s[30:31]
+;
+; GFX10-LABEL: frexp_lt_zero_not_folded:
+; GFX10: ; %bb.0:
+; GFX10-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX10-NEXT: v_frexp_exp_i32_f32_e32 v1, v0
+; GFX10-NEXT: v_cmp_ngt_f32_e32 vcc_lo, 0, v0
+; GFX10-NEXT: v_cndmask_b32_e32 v0, 0, v1, vcc_lo
+; GFX10-NEXT: s_setpc_b64 s[30:31]
+;
; GFX11-LABEL: frexp_lt_zero_not_folded:
; GFX11: ; %bb.0:
; GFX11-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
@@ -173,6 +500,26 @@ define i32 @frexp_lt_zero_not_folded(float %x) {
; GFX11-NEXT: s_delay_alu instid0(VALU_DEP_2)
; GFX11-NEXT: v_cndmask_b32_e32 v0, 0, v1, vcc_lo
; GFX11-NEXT: s_setpc_b64 s[30:31]
+;
+; GFX950-LABEL: frexp_lt_zero_not_folded:
+; GFX950: ; %bb.0:
+; GFX950-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX950-NEXT: v_frexp_exp_i32_f32_e32 v1, v0
+; GFX950-NEXT: v_cmp_ngt_f32_e32 vcc, 0, v0
+; GFX950-NEXT: s_nop 1
+; GFX950-NEXT: v_cndmask_b32_e32 v0, 0, v1, vcc
+; GFX950-NEXT: s_setpc_b64 s[30:31]
+;
+; SI-LABEL: frexp_lt_zero_not_folded:
+; SI: ; %bb.0:
+; SI-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; SI-NEXT: s_mov_b32 s4, 0x7f800000
+; SI-NEXT: v_cmp_lt_f32_e64 s[4:5], |v0|, s4
+; SI-NEXT: v_cmp_ngt_f32_e32 vcc, 0, v0
+; SI-NEXT: v_frexp_exp_i32_f32_e32 v1, v0
+; SI-NEXT: s_and_b64 vcc, vcc, s[4:5]
+; SI-NEXT: v_cndmask_b32_e32 v0, 0, v1, vcc
+; SI-NEXT: s_setpc_b64 s[30:31]
%frexp = call {float, i32} @llvm.frexp.f32.i32(float %x)
%exp = extractvalue {float, i32} %frexp, 1
%is_lt_zero = fcmp olt float %x, 0.0
>From 146cb4af43d83395e18ec8bf3f2c0f4c72b0b30b Mon Sep 17 00:00:00 2001
From: addmisol <addmisol9 at gmail.com>
Date: Sat, 8 Aug 2026 11:18:21 +0530
Subject: [PATCH 4/4] Fix: clang format
Signed-off-by: addmisol <addmisol9 at gmail.com>
---
llvm/lib/Target/AMDGPU/SIISelLowering.cpp | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/llvm/lib/Target/AMDGPU/SIISelLowering.cpp b/llvm/lib/Target/AMDGPU/SIISelLowering.cpp
index b9f242adf4d27..8f20cc6499c4a 100644
--- a/llvm/lib/Target/AMDGPU/SIISelLowering.cpp
+++ b/llvm/lib/Target/AMDGPU/SIISelLowering.cpp
@@ -18523,8 +18523,9 @@ SDValue SITargetLowering::performClampCombine(SDNode *N,
// select (fcmp ueq |x|, inf), 0, (frexp_exp x) -> frexp_exp x
// select (is_fpclass x, finite_mask), frexp, 0 -> frexp_exp x
// can be simplified to just the frexp result.
-SDValue SITargetLowering::performFrexpSelectCombine(SDNode *N,
- DAGCombinerInfo &DCI) const {
+SDValue
+SITargetLowering::performFrexpSelectCombine(SDNode *N,
+ DAGCombinerInfo &DCI) const {
// This optimization only applies when the hardware handles inf/nan correctly.
if (Subtarget->hasFractBug())
return SDValue();
@@ -18602,7 +18603,8 @@ SDValue SITargetLowering::performFrexpSelectCombine(SDNode *N,
// fcFinite = all finite classes (not inf, not nan)
// If the mask tests for finite values and selects frexp when true,
// we can fold away the select since frexp returns 0 for non-finite.
- constexpr unsigned fcFinite = 0x1F8; // fcPosNormal|fcNegNormal|fcPosSubnormal|fcNegSubnormal|fcPosZero|fcNegZero
+ constexpr unsigned fcFinite =
+ 0x1F8; // fcPosNormal|fcNegNormal|fcPosSubnormal|fcNegSubnormal|fcPosZero|fcNegZero
constexpr unsigned fcInfNan = 0x207; // fcPosInf|fcNegInf|fcSNan|fcQNan
if (Mask == fcFinite) {
More information about the llvm-commits
mailing list