[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