[Mlir-commits] [mlir] [mlir][math] Fold exp(a) / exp(b) into exp(a - b) (PR #220010)

Md Abdullah Shahneous Bari llvmlistbot at llvm.org
Wed Sep 2 07:48:38 PDT 2026


https://github.com/mshahneo updated https://github.com/llvm/llvm-project/pull/220010

>From 82afd1b58a9feccf7b7881e06d1f641c2aa5cc7e Mon Sep 17 00:00:00 2001
From: "Shahneous Bari, Md Abdullah" <md.abdullah.shahneous.bari at intel.com>
Date: Mon, 31 Aug 2026 15:42:32 +0000
Subject: [PATCH 1/2] [mlir][math] Fold exp(a) / exp(b) into exp(a - b)

Add ExpQuotientStrengthReduction to the Math algebraic simplification
patterns, rewriting `exp(a) / exp(b)` into `exp(a - b)` (and likewise for
`exp2`). This trades a division and an exponential for a subtraction, and
shows up in online-softmax kernels, where the running-max correction term
is computed as a quotient of two exponentials.

The rewrite is not value-safe, so it is gated on `arcp` and `reassoc` on
the division: it is the fused form of the two steps LLVM's InstCombine
uses to reach the same result, `Z / exp(Y) --> Z * exp(-Y)` (needs `arcp`)
followed by `exp(X) * exp(Y) --> exp(X + Y)` (needs `reassoc`). Besides
rounding, it also changes the intermediate overflow behaviour: for a large
`a == b` the original expression is `inf / inf`, i.e. NaN, while the folded
one is `exp(0.0)`, i.e. 1.0.

Because a new exponential is created, the pattern additionally requires
that at least one of the two exponentials it feeds on dies with the
division, so the number of exponentials never grows. This is the fused
equivalent of the `isOnlyUserOfAnyOperand()` check InstCombine applies to
`exp(X) * exp(Y)`; it is marginally stronger than InstCombine, which
requires the divisor specifically to have a single use.

The new `exp` only carries the fast-math flags common to both exponentials
it replaces, so its accuracy contract cannot be loosened by flags that
only the division had.

The pattern is rooted on `arith.divf` rather than being an `arith`
canonicalization, since `arith` cannot depend on `math`.

Co-Authored-By: Claude Opus 5 <noreply at anthropic.com>
---
 .../Transforms/AlgebraicSimplification.cpp    |  62 ++++++++++
 .../Math/algebraic-simplification.mlir        | 111 ++++++++++++++++++
 2 files changed, 173 insertions(+)

diff --git a/mlir/lib/Dialect/Math/Transforms/AlgebraicSimplification.cpp b/mlir/lib/Dialect/Math/Transforms/AlgebraicSimplification.cpp
index 7ce7b2153ffb4..9993e30d35d09 100644
--- a/mlir/lib/Dialect/Math/Transforms/AlgebraicSimplification.cpp
+++ b/mlir/lib/Dialect/Math/Transforms/AlgebraicSimplification.cpp
@@ -242,6 +242,65 @@ PowIStrengthReduction<PowIOpTy, DivOpTy, MulOpTy>::matchAndRewrite(
   return success();
 }
 
+//----------------------------------------------------------------------------//
+// ExpOp/Exp2Op quotient strength reduction.
+//----------------------------------------------------------------------------//
+
+namespace {
+/// Replaces `exp(a) / exp(b)` with `exp(a - b)`, and likewise for `exp2`,
+/// trading a division and an exponential for a subtraction.
+template <typename ExpOpTy>
+struct ExpQuotientStrengthReduction : public OpRewritePattern<arith::DivFOp> {
+public:
+  using OpRewritePattern::OpRewritePattern;
+
+  LogicalResult matchAndRewrite(arith::DivFOp op,
+                                PatternRewriter &rewriter) const final {
+    auto numerator = op.getLhs().getDefiningOp<ExpOpTy>();
+    auto denominator = op.getRhs().getDefiningOp<ExpOpTy>();
+    if (!numerator || !denominator)
+      return failure();
+
+    // The rewrite is only valid when the division may be turned into a
+    // reciprocal multiplication and then reassociated with the exponentials:
+    //   exp(a) / exp(b) --> exp(a) * exp(-b) --> exp(a + -b)
+    // This mirrors LLVM's InstCombine, which reaches the same result with
+    // `arcp` for the first step and `reassoc` for the second. Note that the
+    // rewrite also changes the overflow behaviour: for a large `a == b` the
+    // original expression is `inf / inf`, i.e. NaN, while the folded one is
+    // `exp(0.0)`, i.e. 1.0.
+    arith::FastMathFlags fmf = op.getFastmath();
+    if (!bitEnumContainsAll(fmf, arith::FastMathFlags::arcp |
+                                     arith::FastMathFlags::reassoc))
+      return failure();
+
+    // The rewrite introduces a new exponential, so it is only profitable if at
+    // least one of the two it feeds on dies with the division; the exponential
+    // count then never grows while a division is traded for a subtraction.
+    // This is the fused equivalent of the `isOnlyUserOfAnyOperand()` check
+    // LLVM's InstCombine applies to `exp(X) * exp(Y) --> exp(X + Y)`.
+    Operation *divOp = op;
+    auto diesWithDivision = [divOp](Operation *exp) {
+      return llvm::all_of(exp->getUsers(),
+                          [divOp](Operation *user) { return user == divOp; });
+    };
+    if (!diesWithDivision(numerator) && !diesWithDivision(denominator))
+      return failure();
+
+    // Do not loosen the accuracy contract of the exponentials being replaced:
+    // the new one may only carry the flags that both of them carry.
+    arith::FastMathFlags expFmf =
+        numerator.getFastmath() & denominator.getFastmath();
+
+    Value exponent =
+        arith::SubFOp::create(rewriter, op.getLoc(), numerator.getOperand(),
+                              denominator.getOperand(), fmf);
+    rewriter.replaceOpWithNewOp<ExpOpTy>(op, exponent, expFmf);
+    return success();
+  }
+};
+} // namespace
+
 //----------------------------------------------------------------------------//
 
 void mlir::populateMathAlgebraicSimplificationPatterns(
@@ -252,4 +311,7 @@ void mlir::populateMathAlgebraicSimplificationPatterns(
       PowIStrengthReduction<math::FPowIOp, arith::DivFOp, arith::MulFOp>,
       PowIStrengthReduction<complex::PowiOp, complex::DivOp, complex::MulOp>>(
       patterns.getContext(), /*exponentThreshold=*/8);
+  patterns.add<ExpQuotientStrengthReduction<math::ExpOp>,
+               ExpQuotientStrengthReduction<math::Exp2Op>>(
+      patterns.getContext());
 }
diff --git a/mlir/test/Dialect/Math/algebraic-simplification.mlir b/mlir/test/Dialect/Math/algebraic-simplification.mlir
index 7342600748967..2b712fd42a7cd 100644
--- a/mlir/test/Dialect/Math/algebraic-simplification.mlir
+++ b/mlir/test/Dialect/Math/algebraic-simplification.mlir
@@ -349,3 +349,114 @@ func.func @fpowi_exp_three(%arg0: f32, %arg1: vector<4xf32>) -> (f32, vector<4xf
   %3 = math.fpowi %arg1, %vm1 : vector<4xf32>, vector<4xi32>
   return %0, %1, %2, %3 : f32, vector<4xf32>, f32, vector<4xf32>
 }
+
+// CHECK-LABEL: @exp_quotient(
+// CHECK-SAME: %[[ARG0:.+]]: f32, %[[ARG1:.+]]: f32,
+// CHECK-SAME: %[[ARG2:.+]]: vector<4xf32>, %[[ARG3:.+]]: vector<4xf32>
+func.func @exp_quotient(%arg0: f32, %arg1: f32, %arg2: vector<4xf32>,
+                        %arg3: vector<4xf32>) -> (f32, vector<4xf32>) {
+  // CHECK: %[[SSUB:.*]] = arith.subf %[[ARG0]], %[[ARG1]] fastmath<fast> : f32
+  // CHECK: %[[SCALAR:.*]] = math.exp %[[SSUB]] fastmath<fast> : f32
+  // CHECK: %[[VSUB:.*]] = arith.subf %[[ARG2]], %[[ARG3]] fastmath<fast> : vector<4xf32>
+  // CHECK: %[[VECTOR:.*]] = math.exp2 %[[VSUB]] fastmath<fast> : vector<4xf32>
+  // CHECK: return %[[SCALAR]], %[[VECTOR]]
+  %0 = math.exp %arg0 fastmath<fast> : f32
+  %1 = math.exp %arg1 fastmath<fast> : f32
+  %2 = arith.divf %0, %1 fastmath<fast> : f32
+  %3 = math.exp2 %arg2 fastmath<fast> : vector<4xf32>
+  %4 = math.exp2 %arg3 fastmath<fast> : vector<4xf32>
+  %5 = arith.divf %3, %4 fastmath<fast> : vector<4xf32>
+  return %2, %5 : f32, vector<4xf32>
+}
+
+// The rewrite only needs `arcp` and `reassoc` on the division; the flags of the
+// new `exp` are the ones shared by both exponentials it replaces.
+// CHECK-LABEL: @exp_quotient_minimal_fastmath(
+// CHECK-SAME: %[[ARG0:.+]]: f32, %[[ARG1:.+]]: f32
+func.func @exp_quotient_minimal_fastmath(%arg0: f32, %arg1: f32) -> f32 {
+  // CHECK: %[[SUB:.*]] = arith.subf %[[ARG0]], %[[ARG1]] fastmath<reassoc,arcp> : f32
+  // CHECK: %[[EXP:.*]] = math.exp %[[SUB]] fastmath<afn> : f32
+  // CHECK: return %[[EXP]]
+  %0 = math.exp %arg0 fastmath<afn,ninf> : f32
+  %1 = math.exp %arg1 fastmath<afn> : f32
+  %2 = arith.divf %0, %1 fastmath<reassoc,arcp> : f32
+  return %2 : f32
+}
+
+// Negative test - the numerator is still needed, but folding remains
+// profitable because it removes the division.
+// CHECK-LABEL: @exp_quotient_numerator_multiple_uses(
+// CHECK-SAME: %[[ARG0:.+]]: f32, %[[ARG1:.+]]: f32
+func.func @exp_quotient_numerator_multiple_uses(%arg0: f32, %arg1: f32) -> (f32, f32) {
+  // CHECK: %[[NUM:.*]] = math.exp %[[ARG0]] fastmath<fast> : f32
+  // CHECK: %[[SUB:.*]] = arith.subf %[[ARG0]], %[[ARG1]] fastmath<fast> : f32
+  // CHECK: %[[EXP:.*]] = math.exp %[[SUB]] fastmath<fast> : f32
+  // CHECK: return %[[EXP]], %[[NUM]]
+  %0 = math.exp %arg0 fastmath<fast> : f32
+  %1 = math.exp %arg1 fastmath<fast> : f32
+  %2 = arith.divf %0, %1 fastmath<fast> : f32
+  return %2, %0 : f32, f32
+}
+
+// The divisor is still needed, but folding remains profitable because the
+// numerator dies and the division becomes a subtraction.
+// CHECK-LABEL: @exp_quotient_denominator_multiple_uses(
+// CHECK-SAME: %[[ARG0:.+]]: f32, %[[ARG1:.+]]: f32
+func.func @exp_quotient_denominator_multiple_uses(%arg0: f32, %arg1: f32) -> (f32, f32) {
+  // CHECK: %[[DEN:.*]] = math.exp %[[ARG1]] fastmath<fast> : f32
+  // CHECK: %[[SUB:.*]] = arith.subf %[[ARG0]], %[[ARG1]] fastmath<fast> : f32
+  // CHECK: %[[EXP:.*]] = math.exp %[[SUB]] fastmath<fast> : f32
+  // CHECK: return %[[EXP]], %[[DEN]]
+  %0 = math.exp %arg0 fastmath<fast> : f32
+  %1 = math.exp %arg1 fastmath<fast> : f32
+  %2 = arith.divf %0, %1 fastmath<fast> : f32
+  return %2, %1 : f32, f32
+}
+
+// Negative test - neither exponential dies, so the fold would add a third one.
+// CHECK-LABEL: @exp_quotient_both_multiple_uses(
+func.func @exp_quotient_both_multiple_uses(%arg0: f32, %arg1: f32) -> (f32, f32, f32) {
+  // CHECK-COUNT-2: math.exp
+  // CHECK-NOT: math.exp
+  // CHECK: arith.divf
+  %0 = math.exp %arg0 fastmath<fast> : f32
+  %1 = math.exp %arg1 fastmath<fast> : f32
+  %2 = arith.divf %0, %1 fastmath<fast> : f32
+  return %2, %0, %1 : f32, f32, f32
+}
+
+// Both operands come from the same exponential, which therefore dies.
+// CHECK-LABEL: @exp_quotient_same_exponential(
+// CHECK-SAME: %[[ARG0:.+]]: f32
+func.func @exp_quotient_same_exponential(%arg0: f32) -> f32 {
+  // CHECK: %[[SUB:.*]] = arith.subf %[[ARG0]], %[[ARG0]] fastmath<fast> : f32
+  // CHECK: %[[EXP:.*]] = math.exp %[[SUB]] fastmath<fast> : f32
+  // CHECK: return %[[EXP]]
+  %0 = math.exp %arg0 fastmath<fast> : f32
+  %1 = arith.divf %0, %0 fastmath<fast> : f32
+  return %1 : f32
+}
+
+// Negative test - not enough fastmath flags on the division.
+// CHECK-LABEL: @exp_quotient_not_enough_fastmath(
+func.func @exp_quotient_not_enough_fastmath(%arg0: f32, %arg1: f32, %arg2: f32,
+                                            %arg3: f32) -> (f32, f32) {
+  // CHECK-COUNT-2: arith.divf
+  %0 = math.exp %arg0 fastmath<fast> : f32
+  %1 = math.exp %arg1 fastmath<fast> : f32
+  %2 = arith.divf %0, %1 fastmath<reassoc> : f32
+  %3 = math.exp %arg2 fastmath<fast> : f32
+  %4 = math.exp %arg3 fastmath<fast> : f32
+  %5 = arith.divf %3, %4 fastmath<arcp> : f32
+  return %2, %5 : f32, f32
+}
+
+// Negative test - mismatched exponential bases.
+// CHECK-LABEL: @exp_quotient_mixed_bases(
+func.func @exp_quotient_mixed_bases(%arg0: f32, %arg1: f32) -> f32 {
+  // CHECK: arith.divf
+  %0 = math.exp %arg0 fastmath<fast> : f32
+  %1 = math.exp2 %arg1 fastmath<fast> : f32
+  %2 = arith.divf %0, %1 fastmath<fast> : f32
+  return %2 : f32
+}

>From 2546a607c4dc6dd76dbdb8f0670a93fb7ac6d059 Mon Sep 17 00:00:00 2001
From: "Shahneous Bari, Md Abdullah" <md.abdullah.shahneous.bari at intel.com>
Date: Tue, 1 Sep 2026 15:36:33 +0000
Subject: [PATCH 2/2] [mlir][math] Derive fast-math flags per new op in the exp
 quotient fold

Address review feedback: `nnan` and `ninf` are assumptions about the values
an operation sees rather than permissions about how to compute it, so they
cannot be handed to an operation whose operands changed.

Neither operation created by the fold sees the values of the one it
replaces, and both assumptions break in f32:
 - the subtraction consumes the exponents instead of the exponentials, so
   `ninf` holds for `exp(-inf) / exp(0.0)`, i.e. `0.0 / 1.0`, while the
   subtraction `-inf - 0.0` is infinite;
 - the new exponential may overflow where neither of the old ones does:
   `exp(80.0)` and `exp(-80.0)` are both finite while `exp(80.0 - -80.0)`
   is not;
 - `a - b` is NaN for `a == b == +inf`, where both exponentials are `inf`
   rather than NaN, so `nnan` does not carry over either.

Clear both flags and derive the rest separately for the two new
operations: the subtraction takes the division's flags, the exponential the
ones common to both exponentials it replaces. All remaining flags only
license how a value may be computed, so they still propagate.

Add mixed fast-math tests covering the dropped assumptions and the
independent derivation of the two flag sets.

Co-Authored-By: Claude Opus 5 <noreply at anthropic.com>
---
 .../Transforms/AlgebraicSimplification.cpp    | 27 +++++++--
 .../Math/algebraic-simplification.mlir        | 60 ++++++++++++++-----
 2 files changed, 68 insertions(+), 19 deletions(-)

diff --git a/mlir/lib/Dialect/Math/Transforms/AlgebraicSimplification.cpp b/mlir/lib/Dialect/Math/Transforms/AlgebraicSimplification.cpp
index 9993e30d35d09..45343ce09134c 100644
--- a/mlir/lib/Dialect/Math/Transforms/AlgebraicSimplification.cpp
+++ b/mlir/lib/Dialect/Math/Transforms/AlgebraicSimplification.cpp
@@ -287,14 +287,31 @@ struct ExpQuotientStrengthReduction : public OpRewritePattern<arith::DivFOp> {
     if (!diesWithDivision(numerator) && !diesWithDivision(denominator))
       return failure();
 
-    // Do not loosen the accuracy contract of the exponentials being replaced:
-    // the new one may only carry the flags that both of them carry.
-    arith::FastMathFlags expFmf =
-        numerator.getFastmath() & denominator.getFastmath();
+    // `nnan` and `ninf` are assumptions about the values an operation sees, and
+    // neither new operation sees the values its source did, so they cannot be
+    // carried over:
+    //  - the subtraction consumes the exponents instead of the exponentials.
+    //    `ninf` holds for `exp(-inf) / exp(0.0)`, i.e. `0.0 / 1.0`, while the
+    //    subtraction `-inf - 0.0` is infinite.
+    //  - the new exponential may overflow where neither of the old ones does.
+    //    For f32 `exp(80.0)` and `exp(-80.0)` are both finite while
+    //    `exp(80.0 - -80.0)` is not.
+    // All remaining flags only license *how* a value may be computed, so they
+    // stay. Derive them separately for the two new operations.
+    constexpr arith::FastMathFlags valueAssumptions =
+        arith::FastMathFlags::nnan | arith::FastMathFlags::ninf;
+
+    // The subtraction takes the place of the division.
+    arith::FastMathFlags subFmf = bitEnumClear(fmf, valueAssumptions);
+
+    // The new exponential may not be given a weaker accuracy contract than the
+    // ones it replaces, so it only keeps the flags common to both of them.
+    arith::FastMathFlags expFmf = bitEnumClear(
+        numerator.getFastmath() & denominator.getFastmath(), valueAssumptions);
 
     Value exponent =
         arith::SubFOp::create(rewriter, op.getLoc(), numerator.getOperand(),
-                              denominator.getOperand(), fmf);
+                              denominator.getOperand(), subFmf);
     rewriter.replaceOpWithNewOp<ExpOpTy>(op, exponent, expFmf);
     return success();
   }
diff --git a/mlir/test/Dialect/Math/algebraic-simplification.mlir b/mlir/test/Dialect/Math/algebraic-simplification.mlir
index 2b712fd42a7cd..897147ee35441 100644
--- a/mlir/test/Dialect/Math/algebraic-simplification.mlir
+++ b/mlir/test/Dialect/Math/algebraic-simplification.mlir
@@ -355,10 +355,10 @@ func.func @fpowi_exp_three(%arg0: f32, %arg1: vector<4xf32>) -> (f32, vector<4xf
 // CHECK-SAME: %[[ARG2:.+]]: vector<4xf32>, %[[ARG3:.+]]: vector<4xf32>
 func.func @exp_quotient(%arg0: f32, %arg1: f32, %arg2: vector<4xf32>,
                         %arg3: vector<4xf32>) -> (f32, vector<4xf32>) {
-  // CHECK: %[[SSUB:.*]] = arith.subf %[[ARG0]], %[[ARG1]] fastmath<fast> : f32
-  // CHECK: %[[SCALAR:.*]] = math.exp %[[SSUB]] fastmath<fast> : f32
-  // CHECK: %[[VSUB:.*]] = arith.subf %[[ARG2]], %[[ARG3]] fastmath<fast> : vector<4xf32>
-  // CHECK: %[[VECTOR:.*]] = math.exp2 %[[VSUB]] fastmath<fast> : vector<4xf32>
+  // CHECK: %[[SSUB:.*]] = arith.subf %[[ARG0]], %[[ARG1]] fastmath<reassoc,nsz,arcp,contract,afn> : f32
+  // CHECK: %[[SCALAR:.*]] = math.exp %[[SSUB]] fastmath<reassoc,nsz,arcp,contract,afn> : f32
+  // CHECK: %[[VSUB:.*]] = arith.subf %[[ARG2]], %[[ARG3]] fastmath<reassoc,nsz,arcp,contract,afn> : vector<4xf32>
+  // CHECK: %[[VECTOR:.*]] = math.exp2 %[[VSUB]] fastmath<reassoc,nsz,arcp,contract,afn> : vector<4xf32>
   // CHECK: return %[[SCALAR]], %[[VECTOR]]
   %0 = math.exp %arg0 fastmath<fast> : f32
   %1 = math.exp %arg1 fastmath<fast> : f32
@@ -369,8 +369,9 @@ func.func @exp_quotient(%arg0: f32, %arg1: f32, %arg2: vector<4xf32>,
   return %2, %5 : f32, vector<4xf32>
 }
 
-// The rewrite only needs `arcp` and `reassoc` on the division; the flags of the
-// new `exp` are the ones shared by both exponentials it replaces.
+// The rewrite only needs `arcp` and `reassoc` on the division. The subtraction
+// takes the division's flags, the new `exp` the ones shared by both
+// exponentials it replaces.
 // CHECK-LABEL: @exp_quotient_minimal_fastmath(
 // CHECK-SAME: %[[ARG0:.+]]: f32, %[[ARG1:.+]]: f32
 func.func @exp_quotient_minimal_fastmath(%arg0: f32, %arg1: f32) -> f32 {
@@ -383,14 +384,45 @@ func.func @exp_quotient_minimal_fastmath(%arg0: f32, %arg1: f32) -> f32 {
   return %2 : f32
 }
 
-// Negative test - the numerator is still needed, but folding remains
-// profitable because it removes the division.
+// `nnan` and `ninf` are assumptions about the values an operation sees, and
+// neither new operation sees the values of the ones it replaces, so they are
+// dropped rather than propagated: here `ninf` holds for the division for
+// `%arg0 = -inf`, `%arg1 = 0.0`, but not for the subtraction.
+// CHECK-LABEL: @exp_quotient_drops_value_assumptions(
+// CHECK-SAME: %[[ARG0:.+]]: f32, %[[ARG1:.+]]: f32
+func.func @exp_quotient_drops_value_assumptions(%arg0: f32, %arg1: f32) -> f32 {
+  // CHECK: %[[SUB:.*]] = arith.subf %[[ARG0]], %[[ARG1]] fastmath<reassoc,arcp> : f32
+  // CHECK: %[[EXP:.*]] = math.exp %[[SUB]] fastmath<afn> : f32
+  // CHECK: return %[[EXP]]
+  %0 = math.exp %arg0 fastmath<nnan,ninf,afn> : f32
+  %1 = math.exp %arg1 fastmath<nnan,ninf,afn> : f32
+  %2 = arith.divf %0, %1 fastmath<nnan,ninf,reassoc,arcp> : f32
+  return %2 : f32
+}
+
+// The two new operations derive their flags independently: the subtraction from
+// the division, the exponential from the two exponentials, and neither inherits
+// flags that only the other side carries.
+// CHECK-LABEL: @exp_quotient_mixed_fastmath(
+// CHECK-SAME: %[[ARG0:.+]]: f32, %[[ARG1:.+]]: f32
+func.func @exp_quotient_mixed_fastmath(%arg0: f32, %arg1: f32) -> f32 {
+  // CHECK: %[[SUB:.*]] = arith.subf %[[ARG0]], %[[ARG1]] fastmath<reassoc,arcp,contract> : f32
+  // CHECK: %[[EXP:.*]] = math.exp %[[SUB]] fastmath<nsz,contract> : f32
+  // CHECK: return %[[EXP]]
+  %0 = math.exp %arg0 fastmath<nsz,contract,afn> : f32
+  %1 = math.exp %arg1 fastmath<nsz,contract,ninf> : f32
+  %2 = arith.divf %0, %1 fastmath<reassoc,arcp,contract,nnan> : f32
+  return %2 : f32
+}
+
+// The numerator is still needed, but folding remains profitable because the
+// division becomes a subtraction.
 // CHECK-LABEL: @exp_quotient_numerator_multiple_uses(
 // CHECK-SAME: %[[ARG0:.+]]: f32, %[[ARG1:.+]]: f32
 func.func @exp_quotient_numerator_multiple_uses(%arg0: f32, %arg1: f32) -> (f32, f32) {
   // CHECK: %[[NUM:.*]] = math.exp %[[ARG0]] fastmath<fast> : f32
-  // CHECK: %[[SUB:.*]] = arith.subf %[[ARG0]], %[[ARG1]] fastmath<fast> : f32
-  // CHECK: %[[EXP:.*]] = math.exp %[[SUB]] fastmath<fast> : f32
+  // CHECK: %[[SUB:.*]] = arith.subf %[[ARG0]], %[[ARG1]] fastmath<reassoc,nsz,arcp,contract,afn> : f32
+  // CHECK: %[[EXP:.*]] = math.exp %[[SUB]] fastmath<reassoc,nsz,arcp,contract,afn> : f32
   // CHECK: return %[[EXP]], %[[NUM]]
   %0 = math.exp %arg0 fastmath<fast> : f32
   %1 = math.exp %arg1 fastmath<fast> : f32
@@ -404,8 +436,8 @@ func.func @exp_quotient_numerator_multiple_uses(%arg0: f32, %arg1: f32) -> (f32,
 // CHECK-SAME: %[[ARG0:.+]]: f32, %[[ARG1:.+]]: f32
 func.func @exp_quotient_denominator_multiple_uses(%arg0: f32, %arg1: f32) -> (f32, f32) {
   // CHECK: %[[DEN:.*]] = math.exp %[[ARG1]] fastmath<fast> : f32
-  // CHECK: %[[SUB:.*]] = arith.subf %[[ARG0]], %[[ARG1]] fastmath<fast> : f32
-  // CHECK: %[[EXP:.*]] = math.exp %[[SUB]] fastmath<fast> : f32
+  // CHECK: %[[SUB:.*]] = arith.subf %[[ARG0]], %[[ARG1]] fastmath<reassoc,nsz,arcp,contract,afn> : f32
+  // CHECK: %[[EXP:.*]] = math.exp %[[SUB]] fastmath<reassoc,nsz,arcp,contract,afn> : f32
   // CHECK: return %[[EXP]], %[[DEN]]
   %0 = math.exp %arg0 fastmath<fast> : f32
   %1 = math.exp %arg1 fastmath<fast> : f32
@@ -429,8 +461,8 @@ func.func @exp_quotient_both_multiple_uses(%arg0: f32, %arg1: f32) -> (f32, f32,
 // CHECK-LABEL: @exp_quotient_same_exponential(
 // CHECK-SAME: %[[ARG0:.+]]: f32
 func.func @exp_quotient_same_exponential(%arg0: f32) -> f32 {
-  // CHECK: %[[SUB:.*]] = arith.subf %[[ARG0]], %[[ARG0]] fastmath<fast> : f32
-  // CHECK: %[[EXP:.*]] = math.exp %[[SUB]] fastmath<fast> : f32
+  // CHECK: %[[SUB:.*]] = arith.subf %[[ARG0]], %[[ARG0]] fastmath<reassoc,nsz,arcp,contract,afn> : f32
+  // CHECK: %[[EXP:.*]] = math.exp %[[SUB]] fastmath<reassoc,nsz,arcp,contract,afn> : f32
   // CHECK: return %[[EXP]]
   %0 = math.exp %arg0 fastmath<fast> : f32
   %1 = arith.divf %0, %0 fastmath<fast> : f32



More information about the Mlir-commits mailing list