Skip to content

Commit b3732b7

Browse files
j2kuncopybara-github
authored andcommitted
polynomial approx: implement square and multiply for math.fpowi
PiperOrigin-RevId: 961260919
1 parent 899c880 commit b3732b7

3 files changed

Lines changed: 92 additions & 2 deletions

File tree

lib/Transforms/PolynomialApproximation/PolynomialApproximation.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "mlir/include/mlir/Analysis/DataFlow/Utils.h" // from @llvm-project
2222
#include "mlir/include/mlir/Dialect/Arith/IR/Arith.h" // from @llvm-project
2323
#include "mlir/include/mlir/Dialect/Math/IR/Math.h" // from @llvm-project
24+
#include "mlir/include/mlir/IR/Builders.h" // from @llvm-project
2425
#include "mlir/include/mlir/IR/BuiltinAttributeInterfaces.h" // from @llvm-project
2526
#include "mlir/include/mlir/IR/BuiltinAttributes.h" // from @llvm-project
2627
#include "mlir/include/mlir/IR/BuiltinTypeInterfaces.h" // from @llvm-project
@@ -628,6 +629,47 @@ struct ReluViaCompositeSign : public OpRewritePattern<arith::MaximumFOp> {
628629
DataFlowSolver* solver;
629630
};
630631

632+
// Use a square and multiply algorithm for x^n where n is a constant.
633+
struct SquareAndMultiplyForPowOp : public OpRewritePattern<math::FPowIOp> {
634+
SquareAndMultiplyForPowOp(MLIRContext* context)
635+
: OpRewritePattern<math::FPowIOp>(context, /*benefit=*/2) {}
636+
637+
LogicalResult matchAndRewrite(math::FPowIOp op,
638+
PatternRewriter& rewriter) const override {
639+
ImplicitLocOpBuilder b(op.getLoc(), rewriter);
640+
Value base = op.getLhs();
641+
Value exp = op.getRhs();
642+
643+
APInt expVal;
644+
if (!matchPattern(exp, m_ConstantInt(&expVal))) {
645+
return rewriter.notifyMatchFailure(
646+
op, "exponent is not a single-valued constant");
647+
}
648+
if (expVal.isNegative()) {
649+
return op.emitOpError("negative exponent not supported");
650+
}
651+
652+
int64_t expInt = static_cast<int64_t>(expVal.getSExtValue());
653+
if (expInt == 0) {
654+
rewriter.replaceOp(
655+
op, arith::ConstantOp::create(b, b.getOneAttr(base.getType())));
656+
return success();
657+
}
658+
659+
auto res = base;
660+
int highestBit = expVal.getActiveBits() - 1;
661+
for (int i = highestBit - 1; i >= 0; --i) {
662+
res = arith::MulFOp::create(b, res, res);
663+
if ((expInt >> i) & 1) {
664+
res = arith::MulFOp::create(b, res, base);
665+
}
666+
}
667+
668+
rewriter.replaceOp(op, res);
669+
return success();
670+
}
671+
};
672+
631673
struct PolynomialApproximation
632674
: impl::PolynomialApproximationBase<PolynomialApproximation> {
633675
using PolynomialApproximationBase::PolynomialApproximationBase;
@@ -647,6 +689,7 @@ struct PolynomialApproximation
647689

648690
// High priority patterns
649691
patterns.add<ExpOpTaylorApproximation>(context, &solver, /*k=*/7);
692+
patterns.add<SquareAndMultiplyForPowOp>(context);
650693
if (useCompositeRelu) {
651694
patterns.add<ReluViaCompositeSign>(context, &solver);
652695
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// RUN: heir-opt --split-input-file --polynomial-approximation %s | FileCheck %s
2+
3+
// CHECK: @test_fpowi_four
4+
func.func @test_fpowi_four(%x: tensor<4xf32>) -> tensor<4xf32> {
5+
// CHECK-COUNT-2: arith.mulf
6+
// CHECK: return
7+
%c2 = arith.constant dense<4> : tensor<4xi64>
8+
%0 = math.fpowi %x, %c2 : tensor<4xf32>, tensor<4xi64>
9+
return %0 : tensor<4xf32>
10+
}
11+
12+
// -----
13+
14+
// CHECK: @test_fpowi_zero
15+
func.func @test_fpowi_zero(%x: tensor<4xf32>) -> tensor<4xf32> {
16+
// CHECK: arith.constant dense<1
17+
%c0 = arith.constant dense<0> : tensor<4xi64>
18+
%0 = math.fpowi %x, %c0 : tensor<4xf32>, tensor<4xi64>
19+
return %0 : tensor<4xf32>
20+
}
21+
22+
// -----
23+
24+
// CHECK: @test_fpowi_one
25+
// CHECK-SAME: %[[arg:.*]]: tensor<4xf32>
26+
func.func @test_fpowi_one(%x: tensor<4xf32>) -> tensor<4xf32> {
27+
// CHECK: return %[[arg]]
28+
%c1 = arith.constant dense<1> : tensor<4xi64>
29+
%0 = math.fpowi %x, %c1 : tensor<4xf32>, tensor<4xi64>
30+
return %0 : tensor<4xf32>
31+
}
32+
33+
// -----
34+
35+
// CHECK: @test_fpowi_five
36+
// CHECK-SAME: %[[arg:.*]]: tensor<4xf32>
37+
func.func @test_fpowi_five(%x: tensor<4xf32>) -> tensor<4xf32> {
38+
// CHECK: %[[arg1:.*]] = arith.mulf %[[arg]], %[[arg]]
39+
// CHECK: %[[arg2:.*]] = arith.mulf %[[arg1]], %[[arg1]]
40+
// CHECK: %[[arg3:.*]] = arith.mulf %[[arg2]], %[[arg]]
41+
// CHECK: return %[[arg3]]
42+
%c1 = arith.constant dense<5> : tensor<4xi64>
43+
%0 = math.fpowi %x, %c1 : tensor<4xf32>, tensor<4xi64>
44+
return %0 : tensor<4xf32>
45+
}

tests/Transforms/polynomial_approximation/polynomial_approximation.mlir

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,8 +115,9 @@ func.func @test_sqrt_default_params(%x: f32 {secret.secret}) -> f32 {
115115

116116
// CHECK: @test_fpowi_tensor
117117
func.func @test_fpowi_tensor(%x: tensor<1x5xf32> {secret.secret}) -> tensor<1x5xf32> {
118-
// CHECK: polynomial.eval
118+
// CHECK: arith.mulf
119119
// CHECK-NOT: math.fpowi
120+
// CHECK-NOT: polynomial.eval
120121
%cst = arith.constant dense<2> : tensor<1x5xi64>
121122
%0 = math.fpowi %x, %cst : tensor<1x5xf32>, tensor<1x5xi64>
122123
return %0 : tensor<1x5xf32>
@@ -126,8 +127,9 @@ func.func @test_fpowi_tensor(%x: tensor<1x5xf32> {secret.secret}) -> tensor<1x5x
126127

127128
// CHECK: @test_fpowi_scalar
128129
func.func @test_fpowi_scalar(%x: f32 {secret.secret}) -> f32 {
129-
// CHECK: polynomial.eval
130+
// CHECK: arith.mulf
130131
// CHECK-NOT: math.fpowi
132+
// CHECK-NOT: polynomial.eval
131133
%cst = arith.constant 2 : i32
132134
%0 = math.fpowi %x, %cst : f32, i32
133135
return %0 : f32

0 commit comments

Comments
 (0)