[Mlir-commits] [mlir] [mlir][math] Fix the sign of zero-base results in the fpowi expansion (PR #237452)
llvmlistbot at llvm.org
llvmlistbot at llvm.org
Fri Oct 30 18:28:22 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-mlir-math
Author: Ben Limpanukorn (benlimpa)
<details>
<summary>Changes</summary>
For a constant negative exponent, `--math-expand-ops` expands `math.fpowi` as `1 / x^|n|`. However, it gives a wrong result for `(+0.0)^-1`, `(+0.0)^-2` and `(-0.0)^-2`, which become -inf, but should be +inf.
To avoid dividing by zero, the pass selects +inf if `x^|n|` compares `oeq` to +0.0 and -inf if it compares `oeq` to -0.0 (added in #<!-- -->87081). However, because `oeq` treats +0.0 and -0.0 as equal, both comparisons hold for either zero and -inf is always selected.
This patch replaces the two comparisons with one `oeq` comparison against +0.0 and selects `copysign(+inf, x^|n|)` when it holds. Since `x^|n|` is computed by repeated multiplication, a zero result has the correct sign, e.g. `(-0.0) * (-0.0) = +0.0`.
It also updates the two `fpowi` CHECK sequences in `expand-math.mlir` and adds runtime cases to `test-expand-math-approx.mlir` for +0.0, -0.0 and 2.0 with exponents -1 and -2. Before this change, every zero base printed -inf.
Assisted-by: Claude Code:claude-opus-5-5
---
Full diff: https://github.com/llvm/llvm-project/pull/237452.diff
3 Files Affected:
- (modified) mlir/lib/Dialect/Math/Transforms/ExpandOps.cpp (+4-12)
- (modified) mlir/test/Dialect/Math/expand-math.mlir (+6-12)
- (modified) mlir/test/mlir-runner/test-expand-math-approx.mlir (+41)
``````````diff
diff --git a/mlir/lib/Dialect/Math/Transforms/ExpandOps.cpp b/mlir/lib/Dialect/Math/Transforms/ExpandOps.cpp
index 41816abf07339..593681d3d9f83 100644
--- a/mlir/lib/Dialect/Math/Transforms/ExpandOps.cpp
+++ b/mlir/lib/Dialect/Math/Transforms/ExpandOps.cpp
@@ -393,30 +393,22 @@ static LogicalResult convertFPowIOp(math::FPowIOp op,
base = arith::MulFOp::create(b, baseType, base, base);
}
- // Make sure not to introduce UB in case of negative power.
+ // Make sure not to introduce UB in case of negative power. A zero `res`
+ // (`oeq` holds for both +0.0 and -0.0) gives an infinity with its sign.
if (isNegative) {
auto &sem = dyn_cast<mlir::FloatType>(getElementTypeOrSelf(baseType))
.getFloatSemantics();
Value zero =
createFloatConst(op->getLoc(), baseType,
APFloat::getZero(sem, /*Negative=*/false), rewriter);
- Value negZero =
- createFloatConst(op->getLoc(), baseType,
- APFloat::getZero(sem, /*Negative=*/true), rewriter);
Value posInfinity =
createFloatConst(op->getLoc(), baseType,
APFloat::getInf(sem, /*Negative=*/false), rewriter);
- Value negInfinity =
- createFloatConst(op->getLoc(), baseType,
- APFloat::getInf(sem, /*Negative=*/true), rewriter);
Value zeroEqCheck =
arith::CmpFOp::create(b, arith::CmpFPredicate::OEQ, res, zero);
- Value negZeroEqCheck =
- arith::CmpFOp::create(b, arith::CmpFPredicate::OEQ, res, negZero);
+ Value signedInfinity = math::CopySignOp::create(b, posInfinity, res);
res = arith::DivFOp::create(b, baseType, one, res);
- res =
- arith::SelectOp::create(b, op->getLoc(), zeroEqCheck, posInfinity, res);
- res = arith::SelectOp::create(b, op->getLoc(), negZeroEqCheck, negInfinity,
+ res = arith::SelectOp::create(b, op->getLoc(), zeroEqCheck, signedInfinity,
res);
}
diff --git a/mlir/test/Dialect/Math/expand-math.mlir b/mlir/test/Dialect/Math/expand-math.mlir
index 84b0bb0aa314a..35293c5b75b95 100644
--- a/mlir/test/Dialect/Math/expand-math.mlir
+++ b/mlir/test/Dialect/Math/expand-math.mlir
@@ -633,17 +633,14 @@ func.func @math_fpowi_neg_odd_power(%0 : tensor<8xf32>) -> tensor<8xf32> {
// CHECK-SAME: (%[[ARG0:.*]]: tensor<8xf32>) -> tensor<8xf32> {
// CHECK-DAG: %[[CST1:.*]] = arith.constant dense<1.000000e+00> : tensor<8xf32>
// CHECK-DAG: %[[CST0:.*]] = arith.constant dense<0.000000e+00> : tensor<8xf32>
-// CHECK-DAG: %[[CSTNEG0:.*]] = arith.constant dense<-0.000000e+00> : tensor<8xf32>
// CHECK-DAG: %[[CSTINF:.*]] = arith.constant dense<0x7F800000> : tensor<8xf32>
-// CHECK-DAG: %[[CSTNEGINF:.*]] = arith.constant dense<0xFF800000> : tensor<8xf32>
// CHECK: %[[SQ:.*]] = arith.mulf %[[ARG0]], %[[ARG0]] : tensor<8xf32>
// CHECK: %[[CUBE:.*]] = arith.mulf %[[SQ]], %[[ARG0]] : tensor<8xf32>
// CHECK: %[[CMP0:.*]] = arith.cmpf oeq, %[[CUBE]], %[[CST0]] : tensor<8xf32>
-// CHECK: %[[CMPNEG0:.*]] = arith.cmpf oeq, %[[CUBE]], %[[CSTNEG0]] : tensor<8xf32>
+// CHECK: %[[SIGNEDINF:.*]] = math.copysign %[[CSTINF]], %[[CUBE]] : tensor<8xf32>
// CHECK: %[[INV:.*]] = arith.divf %[[CST1]], %[[CUBE]] : tensor<8xf32>
-// CHECK: %[[UB1:.*]] = arith.select %[[CMP0]], %[[CSTINF]], %[[INV]] : tensor<8xi1>, tensor<8xf32>
-// CHECK: %[[UB2:.*]] = arith.select %[[CMPNEG0]], %[[CSTNEGINF]], %[[UB1]] : tensor<8xi1>, tensor<8xf32>
-// CHECK: return %[[UB2]] : tensor<8xf32>
+// CHECK: %[[RES:.*]] = arith.select %[[CMP0]], %[[SIGNEDINF]], %[[INV]] : tensor<8xi1>, tensor<8xf32>
+// CHECK: return %[[RES]] : tensor<8xf32>
// -----
@@ -656,17 +653,14 @@ func.func @math_fpowi_neg_even_power(%0 : tensor<8xf32>) -> tensor<8xf32> {
// CHECK-SAME: (%[[ARG0:.*]]: tensor<8xf32>) -> tensor<8xf32> {
// CHECK-DAG: %[[CST1:.*]] = arith.constant dense<1.000000e+00> : tensor<8xf32>
// CHECK-DAG: %[[CST0:.*]] = arith.constant dense<0.000000e+00> : tensor<8xf32>
-// CHECK-DAG: %[[CSTNEG0:.*]] = arith.constant dense<-0.000000e+00> : tensor<8xf32>
// CHECK-DAG: %[[CSTINF:.*]] = arith.constant dense<0x7F800000> : tensor<8xf32>
-// CHECK-DAG: %[[CSTNEGINF:.*]] = arith.constant dense<0xFF800000> : tensor<8xf32>
// CHECK: %[[SQ:.*]] = arith.mulf %[[ARG0]], %[[ARG0]] : tensor<8xf32>
// CHECK: %[[PW4:.*]] = arith.mulf %[[SQ]], %[[SQ]] : tensor<8xf32>
// CHECK: %[[CMP0:.*]] = arith.cmpf oeq, %[[PW4]], %[[CST0]] : tensor<8xf32>
-// CHECK: %[[CMPNEG0:.*]] = arith.cmpf oeq, %[[PW4]], %[[CSTNEG0]] : tensor<8xf32>
+// CHECK: %[[SIGNEDINF:.*]] = math.copysign %[[CSTINF]], %[[PW4]] : tensor<8xf32>
// CHECK: %[[INV:.*]] = arith.divf %[[CST1]], %[[PW4]] : tensor<8xf32>
-// CHECK: %[[UB1:.*]] = arith.select %[[CMP0]], %[[CSTINF]], %[[INV]] : tensor<8xi1>, tensor<8xf32>
-// CHECK: %[[UB2:.*]] = arith.select %[[CMPNEG0]], %[[CSTNEGINF]], %[[UB1]] : tensor<8xi1>, tensor<8xf32>
-// CHECK: return %[[UB2]] : tensor<8xf32>
+// CHECK: %[[RES:.*]] = arith.select %[[CMP0]], %[[SIGNEDINF]], %[[INV]] : tensor<8xi1>, tensor<8xf32>
+// CHECK: return %[[RES]] : tensor<8xf32>
// -----
diff --git a/mlir/test/mlir-runner/test-expand-math-approx.mlir b/mlir/test/mlir-runner/test-expand-math-approx.mlir
index 06b3171a2349e..28d92175f2e80 100644
--- a/mlir/test/mlir-runner/test-expand-math-approx.mlir
+++ b/mlir/test/mlir-runner/test-expand-math-approx.mlir
@@ -262,6 +262,46 @@ func.func @powf() {
return
}
+// -------------------------------------------------------------------------- //
+// fpowi.
+// -------------------------------------------------------------------------- //
+
+func.func @func_fpowi_neg1_f32(%a : f32) {
+ %n = arith.constant -1 : i32
+ %r = math.fpowi %a, %n : f32, i32
+ vector.print %r : f32
+ return
+}
+
+func.func @func_fpowi_neg2_f32(%a : f32) {
+ %n = arith.constant -2 : i32
+ %r = math.fpowi %a, %n : f32, i32
+ vector.print %r : f32
+ return
+}
+
+func.func @fpowi() {
+ %zero = arith.constant 0.0 : f32
+ %neg_zero = arith.constant -0.0 : f32
+ %two = arith.constant 2.0 : f32
+
+ // CHECK-NEXT: {{^}}inf
+ call @func_fpowi_neg1_f32(%zero) : (f32) -> ()
+ // CHECK-NEXT: {{^}}-inf
+ call @func_fpowi_neg1_f32(%neg_zero) : (f32) -> ()
+ // CHECK-NEXT: {{^}}0.5
+ call @func_fpowi_neg1_f32(%two) : (f32) -> ()
+
+ // CHECK-NEXT: {{^}}inf
+ call @func_fpowi_neg2_f32(%zero) : (f32) -> ()
+ // CHECK-NEXT: {{^}}inf
+ call @func_fpowi_neg2_f32(%neg_zero) : (f32) -> ()
+ // CHECK-NEXT: {{^}}0.25
+ call @func_fpowi_neg2_f32(%two) : (f32) -> ()
+
+ return
+}
+
// -------------------------------------------------------------------------- //
// roundeven.
// -------------------------------------------------------------------------- //
@@ -890,6 +930,7 @@ func.func @main() {
call @exp2f() : () -> ()
call @roundf() : () -> ()
call @powf() : () -> ()
+ call @fpowi() : () -> ()
call @roundeven() : () -> ()
call @sinh() : () -> ()
call @cosh() : () -> ()
``````````
</details>
https://github.com/llvm/llvm-project/pull/237452
More information about the Mlir-commits
mailing list