[Mlir-commits] [mlir] [mlir][SPIR-V] Fix math.powf lowering for non-integer exponents (PR #197727)

Arseniy Obolenskiy llvmlistbot at llvm.org
Fri May 15 07:13:38 PDT 2026


https://github.com/aobolensk updated https://github.com/llvm/llvm-project/pull/197727

>From 0bd717b615bbe64a440857b6066127def63fa213 Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Thu, 14 May 2026 17:50:02 +0200
Subject: [PATCH 1/4] [mlir][SPIR-V] Fix math.powf lowering for non-integer
 exponents

The ConvertFToS usage only works when y is an integer. Use it only for integer constants, for others: lower as GL.Exp(y * GL.Log(x)).
---
 .../Conversion/MathToSPIRV/MathToSPIRV.cpp    | 78 +++++++------------
 .../MathToSPIRV/math-to-gl-spirv.mlir         | 65 ++++++++++++----
 2 files changed, 77 insertions(+), 66 deletions(-)

diff --git a/mlir/lib/Conversion/MathToSPIRV/MathToSPIRV.cpp b/mlir/lib/Conversion/MathToSPIRV/MathToSPIRV.cpp
index 01285c6c0ec09..c973b2b927f9c 100644
--- a/mlir/lib/Conversion/MathToSPIRV/MathToSPIRV.cpp
+++ b/mlir/lib/Conversion/MathToSPIRV/MathToSPIRV.cpp
@@ -15,6 +15,7 @@
 #include "mlir/Dialect/SPIRV/IR/SPIRVOps.h"
 #include "mlir/Dialect/SPIRV/Transforms/SPIRVConversion.h"
 #include "mlir/IR/BuiltinTypes.h"
+#include "mlir/IR/Matchers.h"
 #include "mlir/IR/TypeUtilities.h"
 #include "mlir/Transforms/DialectConversion.h"
 #include "llvm/ADT/STLExtras.h"
@@ -360,62 +361,41 @@ struct PowFOpPattern final : public OpConversionPattern<math::PowFOp> {
     if (!dstType)
       return failure();
 
-    // Get the scalar float type.
-    FloatType scalarFloatType;
-    if (auto scalarType = dyn_cast<FloatType>(powfOp.getType())) {
-      scalarFloatType = scalarType;
-    } else if (auto vectorType = dyn_cast<VectorType>(powfOp.getType())) {
-      scalarFloatType = cast<FloatType>(vectorType.getElementType());
-    } else {
-      return failure();
-    }
-
-    // Get int type of the same shape as the float type.
-    Type scalarIntType = rewriter.getIntegerType(32);
-    Type intType = scalarIntType;
+    Location loc = powfOp.getLoc();
     auto operandType = adaptor.getRhs().getType();
-    if (auto vectorType = dyn_cast<VectorType>(operandType)) {
-      auto shape = vectorType.getShape();
-      intType = VectorType::get(shape, scalarIntType);
+
+    // ConvertFToS-based parity needs an integer-valued exponent. Otherwise
+    // fall back to exp(y*log(x)), which yields NaN for x<0 (matches C).
+    auto isIntegerValuedConstant = [](Value v) -> bool {
+      Attribute attr;
+      if (!matchPattern(v, m_Constant(&attr)))
+        return false;
+      if (auto fAttr = dyn_cast<FloatAttr>(attr))
+        return fAttr.getValue().isInteger();
+      if (auto dense = dyn_cast<DenseFPElementsAttr>(attr))
+        return llvm::all_of(dense.getValues<APFloat>(),
+                            [](const APFloat &v) { return v.isInteger(); });
+      return false;
+    };
+
+    if (!isIntegerValuedConstant(adaptor.getRhs())) {
+      Value log = spirv::GLLogOp::create(rewriter, loc, adaptor.getLhs());
+      Value mul = spirv::FMulOp::create(rewriter, loc, adaptor.getRhs(), log);
+      rewriter.replaceOpWithNewOp<spirv::GLExpOp>(powfOp, mul);
+      return success();
     }
 
-    // Per GL Pow extended instruction spec:
-    // "Result is undefined if x < 0. Result is undefined if x = 0 and y <= 0."
-    Location loc = powfOp.getLoc();
+    // GL.Pow is undefined for x < 0; take abs and conditionally negate the
+    // result when the exponent is odd.
+    Type intType = rewriter.getIntegerType(32);
+    if (auto vectorType = dyn_cast<VectorType>(operandType))
+      intType = VectorType::get(vectorType.getShape(), intType);
+
     Value zero = spirv::ConstantOp::getZero(operandType, loc, rewriter);
     Value lessThan =
         spirv::FOrdLessThanOp::create(rewriter, loc, adaptor.getLhs(), zero);
+    Value abs = spirv::GLFAbsOp::create(rewriter, loc, adaptor.getLhs());
 
-    // Per C/C++ spec:
-    // > pow(base, exponent) returns NaN (and raises FE_INVALID) if base is
-    // > finite and negative and exponent is finite and non-integer.
-    // Calculate the reminder from the exponent and check whether it is zero.
-    Value floatOne = spirv::ConstantOp::getOne(operandType, loc, rewriter);
-    Value expRem =
-        spirv::FRemOp::create(rewriter, loc, adaptor.getRhs(), floatOne);
-    Value expRemNonZero =
-        spirv::FOrdNotEqualOp::create(rewriter, loc, expRem, zero);
-    Value cmpNegativeWithFractionalExp =
-        spirv::LogicalAndOp::create(rewriter, loc, expRemNonZero, lessThan);
-    // Create NaN result and replace base value if conditions are met.
-    const auto &floatSemantics = scalarFloatType.getFloatSemantics();
-    const auto nan = APFloat::getNaN(floatSemantics);
-    Attribute nanAttr = rewriter.getFloatAttr(scalarFloatType, nan);
-    if (auto vectorType = dyn_cast<VectorType>(operandType))
-      nanAttr = DenseElementsAttr::get(vectorType, nan);
-
-    Value nanValue =
-        spirv::ConstantOp::create(rewriter, loc, operandType, nanAttr);
-    Value lhs =
-        spirv::SelectOp::create(rewriter, loc, cmpNegativeWithFractionalExp,
-                                nanValue, adaptor.getLhs());
-    Value abs = spirv::GLFAbsOp::create(rewriter, loc, lhs);
-
-    // TODO: The following just forcefully casts y into an integer value in
-    // order to properly propagate the sign, assuming integer y cases. It
-    // doesn't cover other cases and should be fixed.
-
-    // Cast exponent to integer and calculate exponent % 2 != 0.
     Value intRhs =
         spirv::ConvertFToSOp::create(rewriter, loc, intType, adaptor.getRhs());
     Value intOne = spirv::ConstantOp::getOne(intType, loc, rewriter);
diff --git a/mlir/test/Conversion/MathToSPIRV/math-to-gl-spirv.mlir b/mlir/test/Conversion/MathToSPIRV/math-to-gl-spirv.mlir
index 8eb533eeff2a9..e3fce6fa40dfd 100644
--- a/mlir/test/Conversion/MathToSPIRV/math-to-gl-spirv.mlir
+++ b/mlir/test/Conversion/MathToSPIRV/math-to-gl-spirv.mlir
@@ -183,45 +183,76 @@ func.func @ctlz_vector2(%val: vector<2xi32>) -> vector<2xi32> {
   return %0 : vector<2xi32>
 }
 
+// Dynamic exponent: exp(y * log(x)); yields NaN for x<0.
 // CHECK-LABEL: @powf_scalar
 //  CHECK-SAME: (%[[LHS:.+]]: f32, %[[RHS:.+]]: f32)
 func.func @powf_scalar(%lhs: f32, %rhs: f32) -> f32 {
+  // CHECK: %[[LOG:.+]] = spirv.GL.Log %[[LHS]] : f32
+  // CHECK: %[[MUL:.+]] = spirv.FMul %[[RHS]], %[[LOG]] : f32
+  // CHECK: %[[EXP:.+]] = spirv.GL.Exp %[[MUL]] : f32
+  %0 = math.powf %lhs, %rhs : f32
+  // CHECK: return %[[EXP]]
+  return %0: f32
+}
+
+// CHECK-LABEL: @powf_vector
+func.func @powf_vector(%lhs: vector<4xf32>, %rhs: vector<4xf32>) -> vector<4xf32> {
+  // CHECK: spirv.GL.Log %{{.*}} : vector<4xf32>
+  // CHECK: spirv.FMul %{{.*}} : vector<4xf32>
+  // CHECK: spirv.GL.Exp %{{.*}} : vector<4xf32>
+  %0 = math.powf %lhs, %rhs : vector<4xf32>
+  return %0: vector<4xf32>
+}
+
+// Constant integer exponent: parity-based path preserves sign (pow(-2,3)=-8).
+// CHECK-LABEL: @powf_const_int_exp
+//  CHECK-SAME: (%[[LHS:.+]]: f32)
+func.func @powf_const_int_exp(%lhs: f32) -> f32 {
+  // CHECK: %[[RHS:.+]] = arith.constant 3.000000e+00 : f32
   // CHECK: %[[F0:.+]] = spirv.Constant 0.000000e+00 : f32
   // CHECK: %[[LT:.+]] = spirv.FOrdLessThan %[[LHS]], %[[F0]] : f32
-  // CHECK: %[[F1:.+]] = spirv.Constant 1.000000e+00 : f32
-  // CHECK: %[[REM:.+]] = spirv.FRem %[[RHS]], %[[F1]] : f32
-  // CHECK: %[[IS_FRACTION:.+]] = spirv.FOrdNotEqual %[[REM]], %[[F0]] : f32
-  // CHECK: %[[AND:.+]] = spirv.LogicalAnd %[[IS_FRACTION]], %[[LT]] : i1
-  // CHECK: %[[NAN:.+]] = spirv.Constant 0x7FC00000 : f32
-  // CHECK: %[[NEW_LHS:.+]] = spirv.Select %[[AND]], %[[NAN]], %[[LHS]] : i1, f32
-  // CHECK: %[[ABS:.+]] = spirv.GL.FAbs %[[NEW_LHS]] : f32
-  // CHECK: %[[IRHS:.+]] = spirv.ConvertFToS
+  // CHECK: %[[ABS:.+]] = spirv.GL.FAbs %[[LHS]] : f32
+  // CHECK: %[[IRHS:.+]] = spirv.ConvertFToS %[[RHS]] : f32 to i32
   // CHECK: %[[CST1:.+]] = spirv.Constant 1 : i32
-  // CHECK: %[[REM:.+]] = spirv.BitwiseAnd %[[IRHS]]
+  // CHECK: %[[REM:.+]] = spirv.BitwiseAnd %[[IRHS]], %[[CST1]] : i32
   // CHECK: %[[ODD:.+]] = spirv.IEqual %[[REM]], %[[CST1]] : i32
   // CHECK: %[[POW:.+]] = spirv.GL.Pow %[[ABS]], %[[RHS]] : f32
   // CHECK: %[[NEG:.+]] = spirv.FNegate %[[POW]] : f32
   // CHECK: %[[SNEG:.+]] = spirv.LogicalAnd %[[LT]], %[[ODD]] : i1
   // CHECK: %[[SEL:.+]] = spirv.Select %[[SNEG]], %[[NEG]], %[[POW]] : i1, f32
-  %0 = math.powf %lhs, %rhs : f32
+  %c = arith.constant 3.0 : f32
+  %0 = math.powf %lhs, %c : f32
   // CHECK: return %[[SEL]]
   return %0: f32
 }
 
-// CHECK-LABEL: @powf_vector
-func.func @powf_vector(%lhs: vector<4xf32>, %rhs: vector<4xf32>) -> vector<4xf32> {
+// Constant non-integer exponent: falls into the dynamic exp(y*log(x)) path.
+// CHECK-LABEL: @powf_const_frac_exp
+//  CHECK-SAME: (%[[LHS:.+]]: f32)
+func.func @powf_const_frac_exp(%lhs: f32) -> f32 {
+  // CHECK: %[[RHS:.+]] = arith.constant 2.500000e+00 : f32
+  // CHECK: %[[LOG:.+]] = spirv.GL.Log %[[LHS]] : f32
+  // CHECK: %[[MUL:.+]] = spirv.FMul %[[RHS]], %[[LOG]] : f32
+  // CHECK: %[[EXP:.+]] = spirv.GL.Exp %[[MUL]] : f32
+  %c = arith.constant 2.5 : f32
+  %0 = math.powf %lhs, %c : f32
+  // CHECK: return %[[EXP]]
+  return %0: f32
+}
+
+// Splat constant integer-valued vector exponent: parity-based path.
+// CHECK-LABEL: @powf_const_int_exp_vector
+func.func @powf_const_int_exp_vector(%lhs: vector<4xf32>) -> vector<4xf32> {
   // CHECK: spirv.FOrdLessThan
-  // CHECK: spirv.FRem
-  // CHECK: spirv.FOrdNotEqual
-  // CHECK: spirv.LogicalAnd
-  // CHECK: spirv.Select
   // CHECK: spirv.GL.FAbs
+  // CHECK: spirv.ConvertFToS %{{.*}} : vector<4xf32> to vector<4xi32>
   // CHECK: spirv.BitwiseAnd %{{.*}} : vector<4xi32>
   // CHECK: spirv.IEqual %{{.*}} : vector<4xi32>
   // CHECK: spirv.GL.Pow %{{.*}}: vector<4xf32>
   // CHECK: spirv.FNegate
   // CHECK: spirv.Select
-  %0 = math.powf %lhs, %rhs : vector<4xf32>
+  %c = arith.constant dense<3.0> : vector<4xf32>
+  %0 = math.powf %lhs, %c : vector<4xf32>
   return %0: vector<4xf32>
 }
 

>From 6bd6c760f8cac2c8f3c75d485f9ce6b29d2ac40a Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Thu, 14 May 2026 18:23:50 +0200
Subject: [PATCH 2/4] Address review comments

---
 mlir/lib/Conversion/MathToSPIRV/MathToSPIRV.cpp | 17 +++++++++++------
 1 file changed, 11 insertions(+), 6 deletions(-)

diff --git a/mlir/lib/Conversion/MathToSPIRV/MathToSPIRV.cpp b/mlir/lib/Conversion/MathToSPIRV/MathToSPIRV.cpp
index c973b2b927f9c..c6cac1013069a 100644
--- a/mlir/lib/Conversion/MathToSPIRV/MathToSPIRV.cpp
+++ b/mlir/lib/Conversion/MathToSPIRV/MathToSPIRV.cpp
@@ -19,6 +19,7 @@
 #include "mlir/IR/TypeUtilities.h"
 #include "mlir/Transforms/DialectConversion.h"
 #include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/TypeSwitch.h"
 #include "llvm/Support/FormatVariadic.h"
 
 #define DEBUG_TYPE "math-to-spirv-pattern"
@@ -370,12 +371,16 @@ struct PowFOpPattern final : public OpConversionPattern<math::PowFOp> {
       Attribute attr;
       if (!matchPattern(v, m_Constant(&attr)))
         return false;
-      if (auto fAttr = dyn_cast<FloatAttr>(attr))
-        return fAttr.getValue().isInteger();
-      if (auto dense = dyn_cast<DenseFPElementsAttr>(attr))
-        return llvm::all_of(dense.getValues<APFloat>(),
-                            [](const APFloat &v) { return v.isInteger(); });
-      return false;
+      return TypeSwitch<Attribute, bool>(attr)
+          .Case<FloatAttr>([](FloatAttr a) { return a.getValue().isInteger(); })
+          .Case<SplatElementsAttr>([](SplatElementsAttr a) {
+            return a.getSplatValue<APFloat>().isInteger();
+          })
+          .Case<DenseFPElementsAttr>([](DenseFPElementsAttr a) {
+            return llvm::all_of(a.getValues<APFloat>(),
+                                [](const APFloat &v) { return v.isInteger(); });
+          })
+          .Default(false);
     };
 
     if (!isIntegerValuedConstant(adaptor.getRhs())) {

>From fb56fac57ba222d23c731cc9c0ef7657c20db1ac Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Thu, 14 May 2026 19:37:58 +0200
Subject: [PATCH 3/4] rm template args

---
 mlir/lib/Conversion/MathToSPIRV/MathToSPIRV.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/mlir/lib/Conversion/MathToSPIRV/MathToSPIRV.cpp b/mlir/lib/Conversion/MathToSPIRV/MathToSPIRV.cpp
index c6cac1013069a..b788c1ea96ba7 100644
--- a/mlir/lib/Conversion/MathToSPIRV/MathToSPIRV.cpp
+++ b/mlir/lib/Conversion/MathToSPIRV/MathToSPIRV.cpp
@@ -372,11 +372,11 @@ struct PowFOpPattern final : public OpConversionPattern<math::PowFOp> {
       if (!matchPattern(v, m_Constant(&attr)))
         return false;
       return TypeSwitch<Attribute, bool>(attr)
-          .Case<FloatAttr>([](FloatAttr a) { return a.getValue().isInteger(); })
-          .Case<SplatElementsAttr>([](SplatElementsAttr a) {
+          .Case([](FloatAttr a) { return a.getValue().isInteger(); })
+          .Case([](SplatElementsAttr a) {
             return a.getSplatValue<APFloat>().isInteger();
           })
-          .Case<DenseFPElementsAttr>([](DenseFPElementsAttr a) {
+          .Case([](DenseElementsAttr a) {
             return llvm::all_of(a.getValues<APFloat>(),
                                 [](const APFloat &v) { return v.isInteger(); });
           })

>From 7e71a0628a443bfd89e5286ed0360abb8a9a9895 Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Fri, 15 May 2026 16:10:09 +0200
Subject: [PATCH 4/4] Address comments

---
 .../Conversion/MathToSPIRV/MathToSPIRV.cpp    | 96 ++++++++++++-------
 .../MathToSPIRV/math-to-gl-spirv.mlir         | 58 +++++++----
 2 files changed, 102 insertions(+), 52 deletions(-)

diff --git a/mlir/lib/Conversion/MathToSPIRV/MathToSPIRV.cpp b/mlir/lib/Conversion/MathToSPIRV/MathToSPIRV.cpp
index b788c1ea96ba7..58d1476777e22 100644
--- a/mlir/lib/Conversion/MathToSPIRV/MathToSPIRV.cpp
+++ b/mlir/lib/Conversion/MathToSPIRV/MathToSPIRV.cpp
@@ -365,25 +365,46 @@ struct PowFOpPattern final : public OpConversionPattern<math::PowFOp> {
     Location loc = powfOp.getLoc();
     auto operandType = adaptor.getRhs().getType();
 
-    // ConvertFToS-based parity needs an integer-valued exponent. Otherwise
-    // fall back to exp(y*log(x)), which yields NaN for x<0 (matches C).
-    auto isIntegerValuedConstant = [](Value v) -> bool {
-      Attribute attr;
-      if (!matchPattern(v, m_Constant(&attr)))
-        return false;
-      return TypeSwitch<Attribute, bool>(attr)
-          .Case([](FloatAttr a) { return a.getValue().isInteger(); })
-          .Case([](SplatElementsAttr a) {
-            return a.getSplatValue<APFloat>().isInteger();
-          })
-          .Case([](DenseElementsAttr a) {
-            return llvm::all_of(a.getValues<APFloat>(),
-                                [](const APFloat &v) { return v.isInteger(); });
-          })
-          .Default(false);
+    // Parity-based lowering requires an integer-valued constant exponent.
+    // Otherwise fall back to exp(y*log(x)), which yields NaN for x<0 (matches
+    // C).
+    auto isOdd = [](const APFloat &v) {
+      APSInt i(/*BitWidth=*/64, /*isUnsigned=*/false);
+      bool ignored;
+      v.convertToInteger(i, APFloat::rmTowardZero, &ignored);
+      return i[0];
     };
 
-    if (!isIntegerValuedConstant(adaptor.getRhs())) {
+    Attribute rhsAttr;
+    SmallVector<bool> oddMask;
+    bool isIntegerValued = false;
+    if (matchPattern(adaptor.getRhs(), m_Constant(&rhsAttr))) {
+      isIntegerValued = TypeSwitch<Attribute, bool>(rhsAttr)
+                            .Case([&](FloatAttr a) {
+                              if (!a.getValue().isInteger())
+                                return false;
+                              oddMask.push_back(isOdd(a.getValue()));
+                              return true;
+                            })
+                            .Case([&](SplatElementsAttr a) {
+                              APFloat v = a.getSplatValue<APFloat>();
+                              if (!v.isInteger())
+                                return false;
+                              oddMask.push_back(isOdd(v));
+                              return true;
+                            })
+                            .Case([&](DenseElementsAttr a) {
+                              for (const APFloat &v : a.getValues<APFloat>()) {
+                                if (!v.isInteger())
+                                  return false;
+                                oddMask.push_back(isOdd(v));
+                              }
+                              return true;
+                            })
+                            .Default(false);
+    }
+
+    if (!isIntegerValued) {
       Value log = spirv::GLLogOp::create(rewriter, loc, adaptor.getLhs());
       Value mul = spirv::FMulOp::create(rewriter, loc, adaptor.getRhs(), log);
       rewriter.replaceOpWithNewOp<spirv::GLExpOp>(powfOp, mul);
@@ -391,29 +412,36 @@ struct PowFOpPattern final : public OpConversionPattern<math::PowFOp> {
     }
 
     // GL.Pow is undefined for x < 0; take abs and conditionally negate the
-    // result when the exponent is odd.
-    Type intType = rewriter.getIntegerType(32);
-    if (auto vectorType = dyn_cast<VectorType>(operandType))
-      intType = VectorType::get(vectorType.getShape(), intType);
+    // result for lanes whose exponent is odd.
+    Value abs = spirv::GLFAbsOp::create(rewriter, loc, adaptor.getLhs());
+    Value pow = spirv::GLPowOp::create(rewriter, loc, abs, adaptor.getRhs());
+
+    // No odd-parity element: result has the same sign as |lhs|^rhs >= 0.
+    if (llvm::none_of(oddMask, [](bool b) { return b; })) {
+      rewriter.replaceOp(powfOp, pow);
+      return success();
+    }
 
     Value zero = spirv::ConstantOp::getZero(operandType, loc, rewriter);
     Value lessThan =
         spirv::FOrdLessThanOp::create(rewriter, loc, adaptor.getLhs(), zero);
-    Value abs = spirv::GLFAbsOp::create(rewriter, loc, adaptor.getLhs());
+    Value negate = spirv::FNegateOp::create(rewriter, loc, pow);
 
-    Value intRhs =
-        spirv::ConvertFToSOp::create(rewriter, loc, intType, adaptor.getRhs());
-    Value intOne = spirv::ConstantOp::getOne(intType, loc, rewriter);
-    Value bitwiseAndOne =
-        spirv::BitwiseAndOp::create(rewriter, loc, intRhs, intOne);
-    Value isOdd = spirv::IEqualOp::create(rewriter, loc, bitwiseAndOne, intOne);
+    Value shouldNegate;
+    if (llvm::all_of(oddMask, [](bool b) { return b; })) {
+      // Every lane has odd exponent: negate iff lhs < 0.
+      shouldNegate = lessThan;
+    } else {
+      // Mixed parity (non-splat dense vector): AND lhs<0 with a per-element
+      // constant odd-mask.
+      auto vecType = cast<VectorType>(operandType);
+      auto maskType = VectorType::get(vecType.getShape(), rewriter.getI1Type());
+      Value oddConst = spirv::ConstantOp::create(
+          rewriter, loc, maskType, DenseElementsAttr::get(maskType, oddMask));
+      shouldNegate =
+          spirv::LogicalAndOp::create(rewriter, loc, lessThan, oddConst);
+    }
 
-    // calculate pow based on abs(lhs)^rhs.
-    Value pow = spirv::GLPowOp::create(rewriter, loc, abs, adaptor.getRhs());
-    Value negate = spirv::FNegateOp::create(rewriter, loc, pow);
-    // if the exponent is odd and lhs < 0, negate the result.
-    Value shouldNegate =
-        spirv::LogicalAndOp::create(rewriter, loc, lessThan, isOdd);
     rewriter.replaceOpWithNewOp<spirv::SelectOp>(powfOp, shouldNegate, negate,
                                                  pow);
     return success();
diff --git a/mlir/test/Conversion/MathToSPIRV/math-to-gl-spirv.mlir b/mlir/test/Conversion/MathToSPIRV/math-to-gl-spirv.mlir
index e3fce6fa40dfd..08d7822d04cc1 100644
--- a/mlir/test/Conversion/MathToSPIRV/math-to-gl-spirv.mlir
+++ b/mlir/test/Conversion/MathToSPIRV/math-to-gl-spirv.mlir
@@ -204,28 +204,37 @@ func.func @powf_vector(%lhs: vector<4xf32>, %rhs: vector<4xf32>) -> vector<4xf32
   return %0: vector<4xf32>
 }
 
-// Constant integer exponent: parity-based path preserves sign (pow(-2,3)=-8).
-// CHECK-LABEL: @powf_const_int_exp
+// Constant odd integer exponent: parity is known statically, so the lowering
+// drops the runtime FToS/BitwiseAnd/IEqual/LogicalAnd parity computation.
+// CHECK-LABEL: @powf_const_odd_int_exp
 //  CHECK-SAME: (%[[LHS:.+]]: f32)
-func.func @powf_const_int_exp(%lhs: f32) -> f32 {
+func.func @powf_const_odd_int_exp(%lhs: f32) -> f32 {
   // CHECK: %[[RHS:.+]] = arith.constant 3.000000e+00 : f32
-  // CHECK: %[[F0:.+]] = spirv.Constant 0.000000e+00 : f32
-  // CHECK: %[[LT:.+]] = spirv.FOrdLessThan %[[LHS]], %[[F0]] : f32
   // CHECK: %[[ABS:.+]] = spirv.GL.FAbs %[[LHS]] : f32
-  // CHECK: %[[IRHS:.+]] = spirv.ConvertFToS %[[RHS]] : f32 to i32
-  // CHECK: %[[CST1:.+]] = spirv.Constant 1 : i32
-  // CHECK: %[[REM:.+]] = spirv.BitwiseAnd %[[IRHS]], %[[CST1]] : i32
-  // CHECK: %[[ODD:.+]] = spirv.IEqual %[[REM]], %[[CST1]] : i32
   // CHECK: %[[POW:.+]] = spirv.GL.Pow %[[ABS]], %[[RHS]] : f32
+  // CHECK: %[[F0:.+]] = spirv.Constant 0.000000e+00 : f32
+  // CHECK: %[[LT:.+]] = spirv.FOrdLessThan %[[LHS]], %[[F0]] : f32
   // CHECK: %[[NEG:.+]] = spirv.FNegate %[[POW]] : f32
-  // CHECK: %[[SNEG:.+]] = spirv.LogicalAnd %[[LT]], %[[ODD]] : i1
-  // CHECK: %[[SEL:.+]] = spirv.Select %[[SNEG]], %[[NEG]], %[[POW]] : i1, f32
+  // CHECK: %[[SEL:.+]] = spirv.Select %[[LT]], %[[NEG]], %[[POW]] : i1, f32
   %c = arith.constant 3.0 : f32
   %0 = math.powf %lhs, %c : f32
   // CHECK: return %[[SEL]]
   return %0: f32
 }
 
+// Constant even integer exponent: result is non-negative, no select needed.
+// CHECK-LABEL: @powf_const_even_int_exp
+//  CHECK-SAME: (%[[LHS:.+]]: f32)
+func.func @powf_const_even_int_exp(%lhs: f32) -> f32 {
+  // CHECK: %[[RHS:.+]] = arith.constant 4.000000e+00 : f32
+  // CHECK: %[[ABS:.+]] = spirv.GL.FAbs %[[LHS]] : f32
+  // CHECK: %[[POW:.+]] = spirv.GL.Pow %[[ABS]], %[[RHS]] : f32
+  %c = arith.constant 4.0 : f32
+  %0 = math.powf %lhs, %c : f32
+  // CHECK: return %[[POW]]
+  return %0: f32
+}
+
 // Constant non-integer exponent: falls into the dynamic exp(y*log(x)) path.
 // CHECK-LABEL: @powf_const_frac_exp
 //  CHECK-SAME: (%[[LHS:.+]]: f32)
@@ -240,15 +249,12 @@ func.func @powf_const_frac_exp(%lhs: f32) -> f32 {
   return %0: f32
 }
 
-// Splat constant integer-valued vector exponent: parity-based path.
-// CHECK-LABEL: @powf_const_int_exp_vector
-func.func @powf_const_int_exp_vector(%lhs: vector<4xf32>) -> vector<4xf32> {
-  // CHECK: spirv.FOrdLessThan
+// Splat constant odd integer-valued vector exponent: uniform odd parity.
+// CHECK-LABEL: @powf_const_odd_int_exp_vector
+func.func @powf_const_odd_int_exp_vector(%lhs: vector<4xf32>) -> vector<4xf32> {
   // CHECK: spirv.GL.FAbs
-  // CHECK: spirv.ConvertFToS %{{.*}} : vector<4xf32> to vector<4xi32>
-  // CHECK: spirv.BitwiseAnd %{{.*}} : vector<4xi32>
-  // CHECK: spirv.IEqual %{{.*}} : vector<4xi32>
   // CHECK: spirv.GL.Pow %{{.*}}: vector<4xf32>
+  // CHECK: spirv.FOrdLessThan
   // CHECK: spirv.FNegate
   // CHECK: spirv.Select
   %c = arith.constant dense<3.0> : vector<4xf32>
@@ -256,6 +262,22 @@ func.func @powf_const_int_exp_vector(%lhs: vector<4xf32>) -> vector<4xf32> {
   return %0: vector<4xf32>
 }
 
+// Mixed-parity constant integer-valued vector exponent: per-element odd-mask
+// constant is materialized and AND-ed with lhs<0.
+// CHECK-LABEL: @powf_const_mixed_int_exp_vector
+func.func @powf_const_mixed_int_exp_vector(%lhs: vector<4xf32>) -> vector<4xf32> {
+  // CHECK: spirv.GL.FAbs
+  // CHECK: spirv.GL.Pow %{{.*}}: vector<4xf32>
+  // CHECK: spirv.FOrdLessThan
+  // CHECK: spirv.FNegate
+  // CHECK: %[[ODD:.+]] = spirv.Constant dense<[true, false, true, false]> : vector<4xi1>
+  // CHECK: spirv.LogicalAnd %{{.*}}, %[[ODD]] : vector<4xi1>
+  // CHECK: spirv.Select
+  %c = arith.constant dense<[3.0, 2.0, 5.0, 4.0]> : vector<4xf32>
+  %0 = math.powf %lhs, %c : vector<4xf32>
+  return %0: vector<4xf32>
+}
+
 // CHECK-LABEL: @round_scalar
 func.func @round_scalar(%x: f32) -> f32 {
   // CHECK: %[[ZERO:.+]] = spirv.Constant 0.000000e+00



More information about the Mlir-commits mailing list