[llvm-branch-commits] [llvm] [InstCombine] Fold fdiv by splat of pow/exp/powi into fmul (PR #227238)

Harrison Hao via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Tue Sep 29 04:16:58 PDT 2026


https://github.com/harrisonGPU updated https://github.com/llvm/llvm-project/pull/227238

>From 84013e32c88648789086ba9a84ecdda7a90cd249 Mon Sep 17 00:00:00 2001
From: Harrison Hao <tsworld1314 at gmail.com>
Date: Tue, 29 Sep 2026 16:51:08 +0800
Subject: [PATCH] [InstCombine] Fold fdiv by splat of pow/exp/powi into fmul

Extend foldFDivPowDivisor to look through a one-use splat divisor. The
exponent is negated on the scalar and the result is splatted again:

$$\frac{Z}{\mathrm{splat}(x^{y})} \to Z \cdot \mathrm{splat}(x^{-y})$$

$$\frac{Z}{\mathrm{splat}(e^{y})} \to Z \cdot \mathrm{splat}(e^{-y})$$

$$\frac{Z}{\mathrm{splat}(2^{y})} \to Z \cdot \mathrm{splat}(2^{-y})$$

$$\frac{Z}{\mathrm{splat}(x^{n})} \to Z \cdot \mathrm{splat}(x^{-n}), \quad n \in \mathbb{Z}\ (\mathrm{powi})$$

Same FMF requirements as the scalar fold: reassoc and arcp, plus ninf
for powi. This removes the reciprocal, e.g. v_rcp on AMDGPU.
---
 .../InstCombine/InstCombineMulDivRem.cpp      | 38 ++++++++++++++-----
 llvm/test/Transforms/InstCombine/fdiv.ll      | 25 +++++++-----
 2 files changed, 43 insertions(+), 20 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp b/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp
index 16a7b98666d743..cf3bf555855917 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp
@@ -2107,17 +2107,31 @@ static Instruction *foldFDivConstantDividend(BinaryOperator &I) {
 /// Negate the exponent of pow/exp to fold division-by-pow() into multiply.
 static Instruction *foldFDivPowDivisor(BinaryOperator &I,
                                        InstCombiner::BuilderTy &Builder) {
-  Value *Op0 = I.getOperand(0), *Op1 = I.getOperand(1);
-  auto *II = dyn_cast<IntrinsicInst>(Op1);
-  if (!II || !II->hasOneUse() || !I.hasAllowReassoc() ||
-      !I.hasAllowReciprocal())
-    return nullptr;
-
   // Z / pow(X, Y) --> Z * pow(X, -Y)
   // Z / exp{2}(Y) --> Z * exp{2}(-Y)
+  // Z / splat(pow(X, Y)) --> Z * splat(pow(X, -Y))
   // In the general case, this creates an extra instruction, but fmul allows
   // for better canonicalization and optimization than fdiv.
+  if (!I.hasAllowReassoc() || !I.hasAllowReciprocal())
+    return nullptr;
+
+  Value *Op0 = I.getOperand(0);
+  Value *Op1 = I.getOperand(1);
+
+  Value *Divisor = Op1;
+  Value *Splat = nullptr;
+  if (match(Op1,
+            m_OneUse(m_Shuffle(
+                m_OneUse(m_InsertElt(m_Value(), m_Value(Splat), m_ZeroInt())),
+                m_Value(), m_ZeroMask()))))
+    Divisor = Splat;
+
+  auto *II = dyn_cast<IntrinsicInst>(Divisor);
+  if (!II || !II->hasOneUse())
+    return nullptr;
+
   Intrinsic::ID IID = II->getIntrinsicID();
+  SmallVector<Type *, 2> Tys = {II->getType()};
   SmallVector<Value *> Args;
   switch (IID) {
   case Intrinsic::pow:
@@ -2134,9 +2148,8 @@ static Instruction *foldFDivPowDivisor(BinaryOperator &I,
       return nullptr;
     Args.push_back(II->getArgOperand(0));
     Args.push_back(Builder.CreateNeg(II->getArgOperand(1)));
-    Type *Tys[] = {I.getType(), II->getArgOperand(1)->getType()};
-    Value *Pow = Builder.CreateIntrinsic(IID, Tys, Args, &I);
-    return BinaryOperator::CreateFMulFMF(Op0, Pow, &I);
+    Tys.push_back(II->getArgOperand(1)->getType());
+    break;
   }
   case Intrinsic::exp:
   case Intrinsic::exp2:
@@ -2145,7 +2158,12 @@ static Instruction *foldFDivPowDivisor(BinaryOperator &I,
   default:
     return nullptr;
   }
-  Value *Pow = Builder.CreateIntrinsic(IID, I.getType(), Args, &I);
+
+  Value *Pow = Builder.CreateIntrinsic(IID, Tys, Args, &I);
+  if (Pow->getType() != I.getType())
+    Pow = Builder.CreateVectorSplat(
+        cast<VectorType>(I.getType())->getElementCount(), Pow);
+
   return BinaryOperator::CreateFMulFMF(Op0, Pow, &I);
 }
 
diff --git a/llvm/test/Transforms/InstCombine/fdiv.ll b/llvm/test/Transforms/InstCombine/fdiv.ll
index 212d1e15e76b5b..c38ffe0fd4c88b 100644
--- a/llvm/test/Transforms/InstCombine/fdiv.ll
+++ b/llvm/test/Transforms/InstCombine/fdiv.ll
@@ -941,10 +941,11 @@ declare void @use_v2f32(<2 x float>)
 
 define <2 x float> @pow_divisor_splat(float %x, float %y, <2 x float> %z) {
 ; CHECK-LABEL: @pow_divisor_splat(
-; CHECK-NEXT:    [[P:%.*]] = call float @llvm.pow.f32(float [[X:%.*]], float [[Y:%.*]])
+; CHECK-NEXT:    [[TMP1:%.*]] = fneg reassoc arcp float [[Y:%.*]]
+; CHECK-NEXT:    [[P:%.*]] = call reassoc arcp float @llvm.pow.f32(float [[X:%.*]], float [[TMP1]])
 ; CHECK-NEXT:    [[INS:%.*]] = insertelement <2 x float> poison, float [[P]], i64 0
 ; CHECK-NEXT:    [[SPLAT:%.*]] = shufflevector <2 x float> [[INS]], <2 x float> poison, <2 x i32> zeroinitializer
-; CHECK-NEXT:    [[R:%.*]] = fdiv reassoc arcp <2 x float> [[Z:%.*]], [[SPLAT]]
+; CHECK-NEXT:    [[R:%.*]] = fmul reassoc arcp <2 x float> [[Z:%.*]], [[SPLAT]]
 ; CHECK-NEXT:    ret <2 x float> [[R]]
 ;
   %p = call float @llvm.pow.f32(float %x, float %y)
@@ -956,10 +957,11 @@ define <2 x float> @pow_divisor_splat(float %x, float %y, <2 x float> %z) {
 
 define <vscale x 2 x float> @pow_divisor_splat_scalable(float %x, float %y, <vscale x 2 x float> %z) {
 ; CHECK-LABEL: @pow_divisor_splat_scalable(
-; CHECK-NEXT:    [[P:%.*]] = call float @llvm.pow.f32(float [[X:%.*]], float [[Y:%.*]])
+; CHECK-NEXT:    [[TMP1:%.*]] = fneg reassoc arcp float [[Y:%.*]]
+; CHECK-NEXT:    [[P:%.*]] = call reassoc arcp float @llvm.pow.f32(float [[X:%.*]], float [[TMP1]])
 ; CHECK-NEXT:    [[INS:%.*]] = insertelement <vscale x 2 x float> poison, float [[P]], i64 0
 ; CHECK-NEXT:    [[SPLAT:%.*]] = shufflevector <vscale x 2 x float> [[INS]], <vscale x 2 x float> poison, <vscale x 2 x i32> zeroinitializer
-; CHECK-NEXT:    [[R:%.*]] = fdiv reassoc arcp <vscale x 2 x float> [[Z:%.*]], [[SPLAT]]
+; CHECK-NEXT:    [[R:%.*]] = fmul reassoc arcp <vscale x 2 x float> [[Z:%.*]], [[SPLAT]]
 ; CHECK-NEXT:    ret <vscale x 2 x float> [[R]]
 ;
   %p = call float @llvm.pow.f32(float %x, float %y)
@@ -1073,10 +1075,11 @@ define <2 x float> @pow_recip_splat(float %x, float %y) {
 
 define <2 x float> @exp_divisor_splat(float %y, <2 x float> %z) {
 ; CHECK-LABEL: @exp_divisor_splat(
-; CHECK-NEXT:    [[P:%.*]] = call float @llvm.exp.f32(float [[Y:%.*]])
+; CHECK-NEXT:    [[TMP1:%.*]] = fneg reassoc arcp float [[Y:%.*]]
+; CHECK-NEXT:    [[P:%.*]] = call reassoc arcp float @llvm.exp.f32(float [[TMP1]])
 ; CHECK-NEXT:    [[INS:%.*]] = insertelement <2 x float> poison, float [[P]], i64 0
 ; CHECK-NEXT:    [[SPLAT:%.*]] = shufflevector <2 x float> [[INS]], <2 x float> poison, <2 x i32> zeroinitializer
-; CHECK-NEXT:    [[R:%.*]] = fdiv reassoc arcp <2 x float> [[Z:%.*]], [[SPLAT]]
+; CHECK-NEXT:    [[R:%.*]] = fmul reassoc arcp <2 x float> [[Z:%.*]], [[SPLAT]]
 ; CHECK-NEXT:    ret <2 x float> [[R]]
 ;
   %p = call float @llvm.exp.f32(float %y)
@@ -1088,10 +1091,11 @@ define <2 x float> @exp_divisor_splat(float %y, <2 x float> %z) {
 
 define <2 x float> @exp2_divisor_splat(float %y, <2 x float> %z) {
 ; CHECK-LABEL: @exp2_divisor_splat(
-; CHECK-NEXT:    [[P:%.*]] = call float @llvm.exp2.f32(float [[Y:%.*]])
+; CHECK-NEXT:    [[TMP1:%.*]] = fneg reassoc arcp float [[Y:%.*]]
+; CHECK-NEXT:    [[P:%.*]] = call reassoc arcp float @llvm.exp2.f32(float [[TMP1]])
 ; CHECK-NEXT:    [[INS:%.*]] = insertelement <2 x float> poison, float [[P]], i64 0
 ; CHECK-NEXT:    [[SPLAT:%.*]] = shufflevector <2 x float> [[INS]], <2 x float> poison, <2 x i32> zeroinitializer
-; CHECK-NEXT:    [[R:%.*]] = fdiv reassoc arcp <2 x float> [[Z:%.*]], [[SPLAT]]
+; CHECK-NEXT:    [[R:%.*]] = fmul reassoc arcp <2 x float> [[Z:%.*]], [[SPLAT]]
 ; CHECK-NEXT:    ret <2 x float> [[R]]
 ;
   %p = call float @llvm.exp2.f32(float %y)
@@ -1103,10 +1107,11 @@ define <2 x float> @exp2_divisor_splat(float %y, <2 x float> %z) {
 
 define <2 x float> @powi_divisor_splat(float %x, i32 %y, <2 x float> %z) {
 ; CHECK-LABEL: @powi_divisor_splat(
-; CHECK-NEXT:    [[P:%.*]] = call float @llvm.powi.f32.i32(float [[X:%.*]], i32 [[Y:%.*]])
+; CHECK-NEXT:    [[TMP1:%.*]] = sub i32 0, [[Y:%.*]]
+; CHECK-NEXT:    [[P:%.*]] = call reassoc ninf arcp float @llvm.powi.f32.i32(float [[X:%.*]], i32 [[TMP1]])
 ; CHECK-NEXT:    [[INS:%.*]] = insertelement <2 x float> poison, float [[P]], i64 0
 ; CHECK-NEXT:    [[SPLAT:%.*]] = shufflevector <2 x float> [[INS]], <2 x float> poison, <2 x i32> zeroinitializer
-; CHECK-NEXT:    [[R:%.*]] = fdiv reassoc ninf arcp <2 x float> [[Z:%.*]], [[SPLAT]]
+; CHECK-NEXT:    [[R:%.*]] = fmul reassoc ninf arcp <2 x float> [[Z:%.*]], [[SPLAT]]
 ; CHECK-NEXT:    ret <2 x float> [[R]]
 ;
   %p = call float @llvm.powi.f32.i32(float %x, i32 %y)



More information about the llvm-branch-commits mailing list