[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