[llvm] [AMDGPU] Fix pow with a negative base (PR #222248)
via llvm-commits
llvm-commits at lists.llvm.org
Tue Sep 8 23:16:38 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-backend-amdgpu
Author: Arseniy Obolenskiy (aobolensk)
<details>
<summary>Changes</summary>
v_log_f32 of a negative value is NaN, so the old expansion returned NaN for every negative base
Take the log of `|x|` and fix up the sign
---
Patch is 710.47 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/222248.diff
13 Files Affected:
- (modified) llvm/lib/Target/AMDGPU/AMDGPUISelLowering.cpp (+64-5)
- (modified) llvm/lib/Target/AMDGPU/AMDGPUISelLowering.h (+1)
- (modified) llvm/lib/Target/AMDGPU/AMDGPUInstructionSelector.cpp (+1-1)
- (modified) llvm/lib/Target/AMDGPU/AMDGPULegalizerInfo.cpp (+52-34)
- (modified) llvm/lib/Target/AMDGPU/R600ISelLowering.cpp (+2)
- (modified) llvm/lib/Target/AMDGPU/SIInstructions.td (-5)
- (added) llvm/test/CodeGen/AMDGPU/GlobalISel/fma-mix-true16.ll (+74)
- (modified) llvm/test/CodeGen/AMDGPU/GlobalISel/fpow.ll (+4233-1163)
- (modified) llvm/test/CodeGen/AMDGPU/GlobalISel/legalize-fpow.mir (+846-208)
- (modified) llvm/test/CodeGen/AMDGPU/GlobalISel/legalize-fpowi.mir (+120-28)
- (modified) llvm/test/CodeGen/AMDGPU/GlobalISel/llvm.powi.ll (+236-80)
- (modified) llvm/test/CodeGen/AMDGPU/fpow.ll (+4211-462)
- (modified) llvm/test/CodeGen/AMDGPU/llvm.powi.ll (+252-29)
``````````diff
diff --git a/llvm/lib/Target/AMDGPU/AMDGPUISelLowering.cpp b/llvm/lib/Target/AMDGPU/AMDGPUISelLowering.cpp
index 43a6cf7bd7229..97a13012305e4 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPUISelLowering.cpp
+++ b/llvm/lib/Target/AMDGPU/AMDGPUISelLowering.cpp
@@ -412,9 +412,9 @@ AMDGPUTargetLowering::AMDGPUTargetLowering(const TargetMachine &TM,
// Library functions. These default to Expand, but we have instructions
// for them.
- setOperationAction({ISD::FCEIL, ISD::FPOW, ISD::FABS, ISD::FFLOOR,
- ISD::FROUNDEVEN, ISD::FTRUNC},
- {MVT::f16, MVT::f32}, Legal);
+ setOperationAction(
+ {ISD::FCEIL, ISD::FABS, ISD::FFLOOR, ISD::FROUNDEVEN, ISD::FTRUNC},
+ {MVT::f16, MVT::f32}, Legal);
setOperationAction({ISD::FMINNUM, ISD::FMAXNUM}, MVT::f32, Legal);
setOperationAction(ISD::FLOG2, MVT::f32, Custom);
@@ -423,8 +423,8 @@ AMDGPUTargetLowering::AMDGPUTargetLowering(const TargetMachine &TM,
{MVT::f16, MVT::f32, MVT::f64}, Expand);
setOperationAction(
- {ISD::FLOG, ISD::FLOG10, ISD::FEXP, ISD::FEXP2, ISD::FEXP10}, MVT::f32,
- Custom);
+ {ISD::FLOG, ISD::FLOG10, ISD::FEXP, ISD::FEXP2, ISD::FEXP10, ISD::FPOW},
+ MVT::f32, Custom);
setOperationAction({ISD::FEXP, ISD::FEXP2, ISD::FEXP10}, MVT::f64, Custom);
setOperationAction(ISD::FNEARBYINT, {MVT::f16, MVT::f32, MVT::f64}, Custom);
@@ -1461,6 +1461,8 @@ SDValue AMDGPUTargetLowering::LowerOperation(SDValue Op,
return lowerFEXP(Op, DAG);
case ISD::FEXP2:
return lowerFEXP2(Op, DAG);
+ case ISD::FPOW:
+ return lowerFPOW(Op, DAG);
case ISD::SINT_TO_FP: return LowerSINT_TO_FP(Op, DAG);
case ISD::UINT_TO_FP: return LowerUINT_TO_FP(Op, DAG);
case ISD::FP_TO_FP16: return LowerFP_TO_FP16(Op, DAG);
@@ -3310,6 +3312,63 @@ SDValue AMDGPUTargetLowering::lowerFEXP(SDValue Op, SelectionDAG &DAG) const {
return R;
}
+// No pow instruction or libcall to fall back on. fmul_legacy returns 0 for a
+// zero operand even against an infinity or a NaN, so pow(x, 0) and pow(1, y)
+// fall out as exp2(0) = 1.
+SDValue AMDGPUTargetLowering::lowerFPOW(SDValue Op, SelectionDAG &DAG) const {
+ EVT VT = Op.getValueType();
+ assert(VT == MVT::f32);
+
+ SDLoc SL(Op);
+ SDValue X = Op.getOperand(0);
+ SDValue Y = Op.getOperand(1);
+ SDNodeFlags Flags = Op->getFlags();
+
+ // Fast expansion: ignores denormals, NaN for a negative base.
+ if (allowApproxFunc(DAG, Flags)) {
+ SDValue Log = DAG.getNode(AMDGPUISD::LOG, SL, VT, X, Flags);
+ SDValue Mul = DAG.getNode(AMDGPUISD::FMUL_LEGACY, SL, VT, Y, Log, Flags);
+ return DAG.getNode(AMDGPUISD::EXP, SL, VT, Mul, Flags);
+ }
+
+ SDValue Abs = DAG.getNode(ISD::FABS, SL, VT, X, Flags);
+ SDValue Log = DAG.getNode(ISD::FLOG2, SL, VT, Abs, Flags);
+ SDValue Mul = DAG.getNode(AMDGPUISD::FMUL_LEGACY, SL, VT, Y, Log, Flags);
+ SDValue R = DAG.getNode(ISD::FEXP2, SL, VT, Mul, Flags);
+
+ EVT SetCCVT = getSetCCResultType(DAG.getDataLayout(), *DAG.getContext(), VT);
+ SDValue One = DAG.getConstantFP(1.0, SL, VT);
+
+ // Infinities count as integers, and every f32 >= 2^24 in magnitude is even.
+ SDValue YTrunc = DAG.getNode(ISD::FTRUNC, SL, VT, Y);
+ SDValue YIsInt = DAG.getSetCC(SL, SetCCVT, YTrunc, Y, ISD::SETOEQ);
+ SDValue YHalf =
+ DAG.getNode(ISD::FMUL, SL, VT, Y, DAG.getConstantFP(0.5, SL, VT));
+ SDValue YHalfTrunc = DAG.getNode(ISD::FTRUNC, SL, VT, YHalf);
+ SDValue YIsOdd =
+ DAG.getNode(ISD::AND, SL, SetCCVT, YIsInt,
+ DAG.getSetCC(SL, SetCCVT, YHalfTrunc, YHalf, ISD::SETONE));
+
+ // pow(-x, odd y) = -pow(x, y).
+ R = DAG.getNode(ISD::FCOPYSIGN, SL, VT, R,
+ DAG.getNode(ISD::SELECT, SL, VT, YIsOdd, X, One));
+
+ if (Flags.hasNoNaNs())
+ return R;
+
+ // A negative finite base to a non-integral power is NaN. -inf is excluded:
+ // the core already gives pow(+inf, y).
+ SDValue XNegFinite = DAG.getNode(
+ ISD::IS_FPCLASS, SL, SetCCVT, X,
+ DAG.getTargetConstant(fcNegNormal | fcNegSubnormal, SL, MVT::i32));
+ // Not a SETONE compare: pow(-1, NaN) needs the NaN-true behavior of !SETOEQ.
+ SDValue NegNonInt = DAG.getNode(ISD::AND, SL, SetCCVT, XNegFinite,
+ DAG.getNOT(SL, YIsInt, SetCCVT));
+ SDValue NaN =
+ DAG.getConstantFP(APFloat::getQNaN(VT.getFltSemantics()), SL, VT);
+ return DAG.getNode(ISD::SELECT, SL, VT, NegNonInt, NaN, R);
+}
+
static bool isCtlzOpc(unsigned Opc) {
return Opc == ISD::CTLZ || Opc == ISD::CTLZ_ZERO_POISON;
}
diff --git a/llvm/lib/Target/AMDGPU/AMDGPUISelLowering.h b/llvm/lib/Target/AMDGPU/AMDGPUISelLowering.h
index 33ca92d5b7a51..9316f83fac06e 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPUISelLowering.h
+++ b/llvm/lib/Target/AMDGPU/AMDGPUISelLowering.h
@@ -87,6 +87,7 @@ class AMDGPUTargetLowering : public TargetLowering {
SDNodeFlags Flags) const;
SDValue lowerFEXP(SDValue Op, SelectionDAG &DAG) const;
SDValue lowerFEXPF64(SDValue Op, SelectionDAG &DAG) const;
+ SDValue lowerFPOW(SDValue Op, SelectionDAG &DAG) const;
SDValue lowerCTLZResults(SDValue Op, SelectionDAG &DAG) const;
diff --git a/llvm/lib/Target/AMDGPU/AMDGPUInstructionSelector.cpp b/llvm/lib/Target/AMDGPU/AMDGPUInstructionSelector.cpp
index 7475d7dbec091..99aa8ea34d222 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPUInstructionSelector.cpp
+++ b/llvm/lib/Target/AMDGPU/AMDGPUInstructionSelector.cpp
@@ -7129,7 +7129,7 @@ AMDGPUInstructionSelector::selectVOP3PMadMixModsImpl(MachineOperand &Root,
unsigned Mods;
std::tie(Src, Mods) = selectVOP3ModsImpl(Root.getReg());
- if (mi_match(Src, *MRI, m_GFPExt(m_Reg(Src)))) {
+ if (!STI.useRealTrue16Insts() && mi_match(Src, *MRI, m_GFPExt(m_Reg(Src)))) {
assert(MRI->getType(Src) == LLT::scalar(16));
// Only change Src if src modifier could be gained. In such cases new Src
diff --git a/llvm/lib/Target/AMDGPU/AMDGPULegalizerInfo.cpp b/llvm/lib/Target/AMDGPU/AMDGPULegalizerInfo.cpp
index 49d055461ccb1..36fbec46c2014 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPULegalizerInfo.cpp
+++ b/llvm/lib/Target/AMDGPU/AMDGPULegalizerInfo.cpp
@@ -729,7 +729,6 @@ AMDGPULegalizerInfo::AMDGPULegalizerInfo(const GCNSubtarget &ST_,
const std::initializer_list<LLT> FPTypesPK16_64 = {F32, F64, F16, V2F16,
V2F64};
- const LLT MinExtendedFPTy = ST.has16BitInsts() ? F16 : F32;
const LLT I1 = LLT::integer(1);
const LLT I16 = LLT::integer(16);
const LLT I32 = LLT::integer(32);
@@ -1355,17 +1354,12 @@ AMDGPULegalizerInfo::AMDGPULegalizerInfo(const GCNSubtarget &ST_,
FCmpBuilder.widenScalarToNextPow2(1).minScalar(1, F32).scalarize(0);
- // FIXME: fpow has a selection pattern that should move to custom lowering.
- auto &ExpOps = getActionDefinitionsBuilder(G_FPOW);
- if (ST.has16BitInsts())
- ExpOps.customFor({{F32}, {F16}});
- else
- ExpOps.customFor({F32});
- ExpOps.clampScalar(0, MinExtendedFPTy, F32).scalarize(0);
+ getActionDefinitionsBuilder(G_FPOW)
+ .customFor({F32})
+ .clampScalar(0, F32, F32)
+ .scalarize(0);
- getActionDefinitionsBuilder(G_FPOWI)
- .clampScalar(0, MinExtendedFPTy, F32)
- .lower();
+ getActionDefinitionsBuilder(G_FPOWI).clampScalar(0, F32, F32).lower();
getActionDefinitionsBuilder(G_FLOG2)
.legalFor(ST.has16BitInsts(), {F16})
@@ -4281,37 +4275,61 @@ bool AMDGPULegalizerInfo::legalizeFExp(MachineInstr &MI,
return true;
}
+// Keep in sync with AMDGPUTargetLowering::lowerFPOW, which documents this.
bool AMDGPULegalizerInfo::legalizeFPow(MachineInstr &MI,
MachineIRBuilder &B) const {
Register Dst = MI.getOperand(0).getReg();
- Register Src0 = MI.getOperand(1).getReg();
- Register Src1 = MI.getOperand(2).getReg();
+ Register X = MI.getOperand(1).getReg();
+ Register Y = MI.getOperand(2).getReg();
unsigned Flags = MI.getFlags();
- LLT Ty = B.getMRI()->getType(Dst);
+ assert(B.getMRI()->getType(Dst) == F32);
- if (Ty == F32) {
- auto Log = B.buildFLog2(F32, Src0, Flags);
- auto Mul = B.buildIntrinsic(Intrinsic::amdgcn_fmul_legacy, {F32})
- .addUse(Log.getReg(0))
- .addUse(Src1)
+ // Fast expansion: ignores denormals, NaN for a negative base.
+ if (allowApproxFunc(B.getMF(), Flags)) {
+ auto Log = B.buildIntrinsic(Intrinsic::amdgcn_log, {F32})
+ .addUse(X)
.setMIFlags(Flags);
- B.buildFExp2(Dst, Mul, Flags);
- } else if (Ty == F16) {
- // There's no f16 fmul_legacy, so we need to convert for it.
- auto Log = B.buildFLog2(F16, Src0, Flags);
- auto Ext0 = B.buildFPExt(F32, Log, Flags);
- auto Ext1 = B.buildFPExt(F32, Src1, Flags);
auto Mul = B.buildIntrinsic(Intrinsic::amdgcn_fmul_legacy, {F32})
- .addUse(Ext0.getReg(0))
- .addUse(Ext1.getReg(0))
+ .addUse(Y)
+ .addUse(Log.getReg(0))
.setMIFlags(Flags);
- // The f32 product is finite whenever the original fpow was, but it can
- // still be outside the f16 range. Drop ninf from the truncation and from
- // the exp2, since neither can assume a finite value here.
- unsigned FlagsNoNInf = Flags & ~MachineInstr::FmNoInfs;
- B.buildFExp2(Dst, B.buildFPTrunc(F16, Mul, FlagsNoNInf), FlagsNoNInf);
- } else
- return false;
+ buildExp(B, Dst, Mul.getReg(0), Flags);
+ MI.eraseFromParent();
+ return true;
+ }
+
+ auto Abs = B.buildFAbs(F32, X, Flags);
+ auto Log = B.buildFLog2(F32, Abs, Flags);
+ auto Mul = B.buildIntrinsic(Intrinsic::amdgcn_fmul_legacy, {F32})
+ .addUse(Y)
+ .addUse(Log.getReg(0))
+ .setMIFlags(Flags);
+ Register R = B.buildFExp2(F32, Mul, Flags).getReg(0);
+
+ auto One = B.buildFConstant(F32, 1.0);
+
+ auto YTrunc = B.buildIntrinsicTrunc(F32, Y);
+ auto YIsInt = B.buildFCmp(CmpInst::FCMP_OEQ, S1, YTrunc, Y);
+ auto YHalf = B.buildFMul(F32, Y, B.buildFConstant(F32, 0.5));
+ auto YHalfTrunc = B.buildIntrinsicTrunc(F32, YHalf);
+ auto YIsOdd = B.buildAnd(
+ S1, YIsInt, B.buildFCmp(CmpInst::FCMP_ONE, S1, YHalfTrunc, YHalf));
+
+ // pow(-x, odd y) = -pow(x, y).
+ auto Sign = B.buildSelect(F32, YIsOdd, X, One);
+ if (Flags & MachineInstr::FmNoNans) {
+ B.buildFCopysign(Dst, R, Sign);
+ MI.eraseFromParent();
+ return true;
+ }
+ R = B.buildFCopysign(F32, R, Sign).getReg(0);
+
+ // A negative finite base to a non-integral power is NaN. Not an ONE compare:
+ // pow(-1, NaN) needs the NaN-true behavior of !OEQ.
+ auto XNegFinite = B.buildIsFPClass(S1, X, fcNegNormal | fcNegSubnormal);
+ auto NegNonInt = B.buildAnd(S1, XNegFinite, B.buildNot(S1, YIsInt));
+ auto NaN = B.buildFConstant(F32, APFloat::getQNaN(APFloat::IEEEsingle()));
+ B.buildSelect(Dst, NegNonInt, NaN, R);
MI.eraseFromParent();
return true;
diff --git a/llvm/lib/Target/AMDGPU/R600ISelLowering.cpp b/llvm/lib/Target/AMDGPU/R600ISelLowering.cpp
index ad088c81335ef..7595f285b5eea 100644
--- a/llvm/lib/Target/AMDGPU/R600ISelLowering.cpp
+++ b/llvm/lib/Target/AMDGPU/R600ISelLowering.cpp
@@ -110,6 +110,8 @@ R600TargetLowering::R600TargetLowering(const TargetMachine &TM,
setOperationAction({ISD::FCEIL, ISD::FTRUNC, ISD::FROUNDEVEN, ISD::FFLOOR},
MVT::f64, Custom);
+ setOperationAction(ISD::FPOW, MVT::f32, Legal);
+
setOperationAction(ISD::SELECT_CC, {MVT::f32, MVT::i32}, Custom);
setOperationAction(ISD::SETCC, {MVT::i32, MVT::f32}, Expand);
diff --git a/llvm/lib/Target/AMDGPU/SIInstructions.td b/llvm/lib/Target/AMDGPU/SIInstructions.td
index af08bf0861215..fbe3c9f5ac20b 100644
--- a/llvm/lib/Target/AMDGPU/SIInstructions.td
+++ b/llvm/lib/Target/AMDGPU/SIInstructions.td
@@ -2757,11 +2757,6 @@ def : GCNPat <(i1 imm:$imm),
/********** Intrinsic Patterns **********/
/********** ================== **********/
-def : GCNPat <
- (f32 (fpow (VOP3Mods f32:$src0, i32:$src0_mods), (VOP3Mods f32:$src1, i32:$src1_mods))),
- (V_EXP_F32_e64 SRCMODS.NONE, (V_MUL_LEGACY_F32_e64 $src1_mods, $src1, SRCMODS.NONE, (V_LOG_F32_e64 $src0_mods, $src0), 0, 0))
->;
-
def : GCNPat <
(i32 (sext i1:$src0)),
(V_CNDMASK_B32_e64 /*src0mod*/(i32 0), /*src0*/(i32 0),
diff --git a/llvm/test/CodeGen/AMDGPU/GlobalISel/fma-mix-true16.ll b/llvm/test/CodeGen/AMDGPU/GlobalISel/fma-mix-true16.ll
new file mode 100644
index 0000000000000..01c99e7731ef3
--- /dev/null
+++ b/llvm/test/CodeGen/AMDGPU/GlobalISel/fma-mix-true16.ll
@@ -0,0 +1,74 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc -global-isel -mtriple=amdgcn -mcpu=gfx1100 -mattr=+real-true16 < %s | FileCheck -check-prefixes=GFX11-TRUE16 %s
+; RUN: llc -global-isel -mtriple=amdgcn -mcpu=gfx1100 -mattr=-real-true16 < %s | FileCheck -check-prefixes=GFX11-FAKE16 %s
+
+; With real true16 instructions s16 values live in 16-bit register classes,
+; and folding an fpext into a mix instruction's 32-bit source operand leaves
+; an unselectable 16-bit def. Check that the fold is skipped instead of
+; crashing the selector.
+
+define float @v_mul_f32_fpext_f16(half %x, half %y) {
+; GFX11-TRUE16-LABEL: v_mul_f32_fpext_f16:
+; GFX11-TRUE16: ; %bb.0:
+; GFX11-TRUE16-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX11-TRUE16-NEXT: v_cvt_f32_f16_e32 v0, v0.l
+; GFX11-TRUE16-NEXT: v_cvt_f32_f16_e32 v1, v1.l
+; GFX11-TRUE16-NEXT: s_delay_alu instid0(VALU_DEP_1)
+; GFX11-TRUE16-NEXT: v_mul_f32_e32 v0, v0, v1
+; GFX11-TRUE16-NEXT: s_setpc_b64 s[30:31]
+;
+; GFX11-FAKE16-LABEL: v_mul_f32_fpext_f16:
+; GFX11-FAKE16: ; %bb.0:
+; GFX11-FAKE16-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX11-FAKE16-NEXT: v_fma_mix_f32 v0, v0, v1, neg(0) op_sel_hi:[1,1,0]
+; GFX11-FAKE16-NEXT: s_setpc_b64 s[30:31]
+ %ex = fpext half %x to float
+ %ey = fpext half %y to float
+ %mul = fmul float %ex, %ey
+ ret float %mul
+}
+
+define float @v_fma_f32_fpext_f16(half %x, half %y, float %z) {
+; GFX11-TRUE16-LABEL: v_fma_f32_fpext_f16:
+; GFX11-TRUE16: ; %bb.0:
+; GFX11-TRUE16-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX11-TRUE16-NEXT: v_cvt_f32_f16_e32 v0, v0.l
+; GFX11-TRUE16-NEXT: v_cvt_f32_f16_e32 v1, v1.l
+; GFX11-TRUE16-NEXT: s_delay_alu instid0(VALU_DEP_1)
+; GFX11-TRUE16-NEXT: v_fma_f32 v0, v0, v1, v2
+; GFX11-TRUE16-NEXT: s_setpc_b64 s[30:31]
+;
+; GFX11-FAKE16-LABEL: v_fma_f32_fpext_f16:
+; GFX11-FAKE16: ; %bb.0:
+; GFX11-FAKE16-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX11-FAKE16-NEXT: v_fma_mix_f32 v0, v0, v1, v2 op_sel_hi:[1,1,0]
+; GFX11-FAKE16-NEXT: s_setpc_b64 s[30:31]
+ %ex = fpext half %x to float
+ %ey = fpext half %y to float
+ %fma = call float @llvm.fma.f32(float %ex, float %ey, float %z)
+ ret float %fma
+}
+
+define float @v_fma_f32_fpext_fneg_f16(half %x, half %y, float %z) {
+; GFX11-TRUE16-LABEL: v_fma_f32_fpext_fneg_f16:
+; GFX11-TRUE16: ; %bb.0:
+; GFX11-TRUE16-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX11-TRUE16-NEXT: v_cvt_f32_f16_e64 v0, -v0.l
+; GFX11-TRUE16-NEXT: v_cvt_f32_f16_e32 v1, v1.l
+; GFX11-TRUE16-NEXT: s_delay_alu instid0(VALU_DEP_1)
+; GFX11-TRUE16-NEXT: v_fma_f32 v0, v0, v1, v2
+; GFX11-TRUE16-NEXT: s_setpc_b64 s[30:31]
+;
+; GFX11-FAKE16-LABEL: v_fma_f32_fpext_fneg_f16:
+; GFX11-FAKE16: ; %bb.0:
+; GFX11-FAKE16-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX11-FAKE16-NEXT: v_fma_mix_f32 v0, -v0, v1, v2 op_sel_hi:[1,1,0]
+; GFX11-FAKE16-NEXT: s_setpc_b64 s[30:31]
+ %neg.x = fneg half %x
+ %ex = fpext half %neg.x to float
+ %ey = fpext half %y to float
+ %fma = call float @llvm.fma.f32(float %ex, float %ey, float %z)
+ ret float %fma
+}
+
+declare float @llvm.fma.f32(float, float, float)
diff --git a/llvm/test/CodeGen/AMDGPU/GlobalISel/fpow.ll b/llvm/test/CodeGen/AMDGPU/GlobalISel/fpow.ll
index b84ec70d3e35d..6a48a4b2a219f 100644
--- a/llvm/test/CodeGen/AMDGPU/GlobalISel/fpow.ll
+++ b/llvm/test/CodeGen/AMDGPU/GlobalISel/fpow.ll
@@ -11,116 +11,190 @@ define float @v_pow_f32(float %x, float %y) {
; GFX6: ; %bb.0:
; GFX6-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
; GFX6-NEXT: v_mov_b32_e32 v2, 0x800000
-; GFX6-NEXT: v_cmp_lt_f32_e32 vcc, v0, v2
+; GFX6-NEXT: v_cmp_lt_f32_e64 vcc, |v0|, v2
; GFX6-NEXT: v_cndmask_b32_e64 v2, 0, 1, vcc
; GFX6-NEXT: v_lshlrev_b32_e32 v2, 5, v2
-; GFX6-NEXT: v_ldexp_f32_e32 v0, v0, v2
-; GFX6-NEXT: v_log_f32_e32 v0, v0
-; GFX6-NEXT: v_mov_b32_e32 v2, 0x42000000
-; GFX6-NEXT: v_cndmask_b32_e32 v2, 0, v2, vcc
-; GFX6-NEXT: v_sub_f32_e32 v0, v0, v2
-; GFX6-NEXT: v_mul_legacy_f32_e32 v0, v0, v1
-; GFX6-NEXT: v_mov_b32_e32 v1, 0xc2fc0000
-; GFX6-NEXT: v_mov_b32_e32 v2, 0x42800000
-; GFX6-NEXT: v_cmp_lt_f32_e32 vcc, v0, v1
-; GFX6-NEXT: v_cndmask_b32_e32 v1, 0, v2, vcc
-; GFX6-NEXT: v_add_f32_e32 v0, v0, v1
-; GFX6-NEXT: v_exp_f32_e32 v0, v0
-; GFX6-NEXT: v_not_b32_e32 v1, 63
-; GFX6-NEXT: v_cndmask_b32_e32 v1, 0, v1, vcc
-; GFX6-NEXT: v_ldexp_f32_e32 v0, v0, v1
+; GFX6-NEXT: v_ldexp_f32_e64 v2, |v0|, v2
+; GFX6-NEXT: v_log_f32_e32 v2, v2
+; GFX6-NEXT: v_mov_b32_e32 v3, 0x42000000
+; GFX6-NEXT: v_cndmask_b32_e32 v3, 0, v3, vcc
+; GFX6-NEXT: v_mov_b32_e32 v4, 0x42800000
+; GFX6-NEXT: v_sub_f32_e32 v2, v2, v3
+; GFX6-NEXT: v_mul_legacy_f32_e32 v2, v1, v2
+; GFX6-NEXT: v_mov_b32_e32 v3, 0xc2fc0000
+; GFX6-NEXT: v_cmp_lt_f32_e32 vcc, v2, v3
+; GFX6-NEXT: v_cndmask_b32_e32 v3, 0, v4, vcc
+; GFX6-NEXT: v_add_f32_e32 v2, v2, v3
+; GFX6-NEXT: v_exp_f32_e32 v2, v2
+; GFX6-NEXT: v_not_b32_e32 v3, 63
+; GFX6-NEXT: v_cndmask_b32_e32 v3, 0, v3, vcc
+; GFX6-NEXT: v_ldexp_f32_e32 v2, v2, v3
+; GFX6-NEXT: v_trunc_f32_e32 v3, v1
+; GFX6-NEXT: v_cmp_eq_f32_e32 vcc, v3, v1
+; GFX6-NEXT: v_mul_f32_e32 v1, 0.5, v1
+; GFX6-NEXT: v_trunc_f32_e32 v3, v1
+; GFX6-NEXT: v_cmp_lg_f32_e64 s[4:5], v3, v1
+; GFX6-NEXT: s_and_b64 s[4:5], vcc, s[4:5]
+; GFX6-NEXT: v_cndmask_b32_e64 v1, 1.0, v0, s[4:5]
+; GFX6-NEXT: v_and_b32_e32 v2, 0x7fffffff, v2
+; GFX6-NEXT: v_and_b32_e32 v1, 0x80000000, v1
+; GFX6-NEXT: v_cmp_class_f32_e64 s[4:5], v0, 24
+; GFX6-NEXT: s_xor_b64 s[6:7], vcc, exec
+; GFX6-NEXT: v_or_b32_e32 v1, v2, v1
+; GFX6-NEXT: v_mov_b32_e32 v0, 0x7fc00000
+; GFX6-NEXT: s_and_b64 vcc, s[4:5], s[6:7]
+; GFX6-NEXT: v_cndmask_b32_e32 v0, v1, v0, vcc
; GFX6-NEXT: s_setpc_b64 s[30:31]
;
; GFX8-LABEL: v_pow_f32:
; GFX8: ; %bb.0:
; GFX8-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
; GFX8-NEXT: v_mov_b32_e32 v2, 0x800000
-; GFX8-NEXT: v_cmp_lt_f32_e32 vcc, v0, v2
+; GFX8-NEXT: v_cmp_lt_f32_e64 vcc, |v0|, v2
; GFX8-NEXT: v_cndmask_b32_e64 v2, 0, 1, vcc
; GFX8-NEXT: v_lshlrev_b32_e32 v2, 5, v2
-; GFX8-NEXT: v_ldexp_f32 v0, v0, v2
-; GFX8-NEXT: v_log_f32_e32 v0, v0
-; GFX8-NEXT: v_mov_b32_e32 v2, 0x42000000
-; GFX8-NEXT: v_cndmask_b32_e32 v2, 0, v2, vcc
-; GFX8-NEXT: v_sub_f32_e32 v0, v0, v2
-; GFX8-NEXT: v_mul_legacy_f32_e32 v0, v0, v1
-; GFX8-NEXT: v_mov_b32_e32 v1, 0xc2fc0000
-; GFX8-NEXT: v_mov_b32_e32 v2, 0x42800000
-; GFX8-NEXT: v_cmp_lt_f32_e32 vcc, v0, v1
-; GFX8-NEXT: v_cndmask_b32_e32 v1, 0, v2, vcc
-; GFX8-NEXT: v_add_f32_e32 v0, v0, v1
-; GFX8-NEXT: v_exp_f32_e32 v0, v0
-; GFX8-NEXT: v_not_b32_e32 v1, 63
-; GFX8-NEXT: v_cndmask_b32_e32 v1, 0, v1, vcc
-; GFX8-NEXT: v_ldexp_f32 v0, v0, v1
+; GFX8-NEXT: v_ldexp_f32 v2, |v0|, v2
+; GFX8-NEXT: v_log_f32_e32 v2, v2
+; GFX8-NEXT: v_mov_b32_e32 v3, 0x42000000
+; GFX8-NEXT: v_cndmask_b32_e32 v3, 0, v3, vcc
+; GFX8-NEXT: v_mov_b32_e32 v4, 0x42800000
+; GFX8-NEXT: v_sub_f32_e32 v2, v2, v3
+; GFX8-NEXT: v_mul_legacy_f32_e32 v2, v1, v2
+; GFX8-NEXT: v_mov_b32_e32 v3, 0xc2fc0000
+; GFX8-NEXT: v_cmp_lt_f32_e32 vcc, v2, v3
+; GFX8-NEXT: v_cndmask_b32_e32 v3, 0, v4, vcc
+; GFX8-NEXT: v_add_f32_e32 v2, v2, v3
+; GFX8-NEXT: v_exp_f32_e32 v2, v2
+; GFX8-NEXT: v_not_b32_e32 v3, 63
+; GFX8-NEXT: v_cndmask_b32_e32 v3, 0, v3, vcc
+; GFX8-NEXT: v_ldexp_f32 v2, v2, v3
+; GFX8-NEXT: v_trunc_f32_e32 v3, v1
+; GFX8-NEXT: v_cmp_eq_f32_e32 vcc, v3, v1
+; GFX8-NEXT: v_mul_f32_e32 v1, 0.5, v1
+; GFX8-NEXT: v_trunc_f32_e32 v3, v1
+; GFX8-NEXT: v_cmp_lg_f32_e64 s[4:5], v3, v1
+; GFX8-NEXT: s_and_b64 s[4:5], v...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/222248
More information about the llvm-commits
mailing list