Skip to content

Commit 700eee7

Browse files
committed
[mlir][evm] Lower addmod/mulmod builtins
1 parent 9da9575 commit 700eee7

File tree

5 files changed

+202
-0
lines changed

5 files changed

+202
-0
lines changed

libsolidity/codegen/mlir/Target/EVM/YulToStandard.cpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,38 @@ struct ExpOpLowering : public OpRewritePattern<yul::ExpOp> {
186186
}
187187
};
188188

189+
struct AddModOpLowering : public OpRewritePattern<yul::AddModOp> {
190+
using OpRewritePattern<yul::AddModOp>::OpRewritePattern;
191+
192+
LogicalResult matchAndRewrite(yul::AddModOp op,
193+
PatternRewriter &r) const override {
194+
evm::Builder evmB(r, op.getLoc());
195+
196+
r.replaceOpWithNewOp<LLVM::IntrCallOp>(
197+
op, llvm::Intrinsic::evm_addmod,
198+
/*resTy=*/r.getIntegerType(256),
199+
/*ins=*/ValueRange{op.getX(), op.getY(), op.getMod()}, "evm.addmod");
200+
201+
return success();
202+
}
203+
};
204+
205+
struct MulModOpLowering : public OpRewritePattern<yul::MulModOp> {
206+
using OpRewritePattern<yul::MulModOp>::OpRewritePattern;
207+
208+
LogicalResult matchAndRewrite(yul::MulModOp op,
209+
PatternRewriter &r) const override {
210+
evm::Builder evmB(r, op.getLoc());
211+
212+
r.replaceOpWithNewOp<LLVM::IntrCallOp>(
213+
op, llvm::Intrinsic::evm_mulmod,
214+
/*resTy=*/r.getIntegerType(256),
215+
/*ins=*/ValueRange{op.getX(), op.getY(), op.getMod()}, "evm.mulmod");
216+
217+
return success();
218+
}
219+
};
220+
189221
struct LogOpLowering : public OpRewritePattern<yul::LogOp> {
190222
using OpRewritePattern<yul::LogOp>::OpRewritePattern;
191223

@@ -851,6 +883,8 @@ struct ObjectOpLowering : public OpRewritePattern<yul::ObjectOp> {
851883
void evm::populateYulPats(RewritePatternSet &pats) {
852884
pats.add<
853885
// clang-format off
886+
AddModOpLowering,
887+
MulModOpLowering,
854888
UpdFreePtrOpLowering,
855889
Keccak256OpLowering,
856890
DivOpLowering,

libsolidity/codegen/mlir/Yul/YulOps.td

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,20 @@ def Yul_ExpOp : Yul_Op<"exp", [Pure]> {
360360
let assemblyFormat = "$base `,` $exp attr-dict";
361361
}
362362

363+
def Yul_AddModOp : Yul_Op<"addmod", [Pure]> {
364+
let summary = "Represents the `addmod` call in yul";
365+
let arguments = (ins I256:$x, I256:$y, I256:$mod);
366+
let results = (outs I256:$out);
367+
let assemblyFormat = "$x `,` $y `,` $mod attr-dict";
368+
}
369+
370+
def Yul_MulModOp : Yul_Op<"mulmod", [Pure]> {
371+
let summary = "Represents the `mulmod` call in yul";
372+
let arguments = (ins I256:$x, I256:$y, I256:$mod);
373+
let results = (outs I256:$out);
374+
let assemblyFormat = "$x `,` $y `,` $mod attr-dict";
375+
}
376+
363377
def Yul_LogOp : Yul_Op<"log"> {
364378
let summary = "Represents the `log*` calls in yul";
365379
let arguments = (ins I256:$addr, I256:$size, Variadic<I256>:$topics);

libsolidity/codegen/mlir/YulToMLIR.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,8 @@ void YulToMLIRPass::populateBuiltinGenMap() {
313313
bExt.genI256Const(0)));
314314
return resVals;
315315
};
316+
defSimpleBuiltinGen<AddModOp>("addmod");
317+
defSimpleBuiltinGen<MulModOp>("mulmod");
316318
defSimpleBuiltinGen<MLoadOp>("mload");
317319
defSimpleBuiltinGenNoRet<LoadImmutableOp>("loadimmutable");
318320
defSimpleBuiltinGenNoRet<MStoreOp>("mstore");
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// RUN: solc --strict-assembly --mlir-action=print-std-mlir --mlir-target=evm --mmlir --mlir-print-debuginfo %s | FileCheck %s
2+
3+
object "Test" {
4+
code {
5+
let x := mload(0)
6+
let y := mload(32)
7+
let m := mload(64)
8+
mstore(0, addmod(x, y, m))
9+
mstore(32, mulmod(x, y, m))
10+
return(0, 64)
11+
}
12+
}
13+
// NOTE: Assertions have been autogenerated by test/updFileCheckTest.py
14+
// CHECK: #Prague = #sol<EvmVersion Prague>
15+
// CHECK-NEXT: module @Test attributes {sol.evm_version = #Prague} {
16+
// CHECK-NEXT: func.func @".unreachable"() attributes {llvm.linkage = #llvm.linkage<private>, passthrough = ["nofree", "null_pointer_is_valid"]} {
17+
// CHECK-NEXT: llvm.unreachable loc(#loc1)
18+
// CHECK-NEXT: } loc(#loc)
19+
// CHECK-NEXT: func.func @__entry() attributes {llvm.linkage = #llvm.linkage<external>, passthrough = ["nofree", "null_pointer_is_valid"]} {
20+
// CHECK-NEXT: %c64_i256 = arith.constant 64 : i256 loc(#loc)
21+
// CHECK-NEXT: %c32_i256 = arith.constant 32 : i256 loc(#loc)
22+
// CHECK-NEXT: %c1_i256 = arith.constant 1 : i256 loc(#loc)
23+
// CHECK-NEXT: %c0_i256 = arith.constant 0 : i256 loc(#loc)
24+
// CHECK-NEXT: %0 = llvm.inttoptr %c0_i256 : i256 to !llvm.ptr<1> loc(#loc2)
25+
// CHECK-NEXT: %1 = llvm.load %0 {alignment = 1 : i64} : !llvm.ptr<1> -> i256 loc(#loc2)
26+
// CHECK-NEXT: %2 = llvm.alloca %c1_i256 x i256 {alignment = 32 : i64} : (i256) -> !llvm.ptr loc(#loc3)
27+
// CHECK-NEXT: llvm.store %1, %2 {alignment = 32 : i64} : i256, !llvm.ptr loc(#loc4)
28+
// CHECK-NEXT: %3 = llvm.inttoptr %c32_i256 : i256 to !llvm.ptr<1> loc(#loc5)
29+
// CHECK-NEXT: %4 = llvm.load %3 {alignment = 1 : i64} : !llvm.ptr<1> -> i256 loc(#loc5)
30+
// CHECK-NEXT: %5 = llvm.alloca %c1_i256 x i256 {alignment = 32 : i64} : (i256) -> !llvm.ptr loc(#loc6)
31+
// CHECK-NEXT: llvm.store %4, %5 {alignment = 32 : i64} : i256, !llvm.ptr loc(#loc7)
32+
// CHECK-NEXT: %6 = llvm.inttoptr %c64_i256 : i256 to !llvm.ptr<1> loc(#loc8)
33+
// CHECK-NEXT: %7 = llvm.load %6 {alignment = 1 : i64} : !llvm.ptr<1> -> i256 loc(#loc8)
34+
// CHECK-NEXT: %8 = llvm.alloca %c1_i256 x i256 {alignment = 32 : i64} : (i256) -> !llvm.ptr loc(#loc9)
35+
// CHECK-NEXT: llvm.store %7, %8 {alignment = 32 : i64} : i256, !llvm.ptr loc(#loc10)
36+
// CHECK-NEXT: %9 = llvm.load %2 {alignment = 32 : i64} : !llvm.ptr -> i256 loc(#loc11)
37+
// CHECK-NEXT: %10 = llvm.load %5 {alignment = 32 : i64} : !llvm.ptr -> i256 loc(#loc12)
38+
// CHECK-NEXT: %11 = llvm.load %8 {alignment = 32 : i64} : !llvm.ptr -> i256 loc(#loc13)
39+
// CHECK-NEXT: %12 = "llvm.intrcall"(%9, %10, %11) <{id = 3684 : i32, name = "evm.addmod"}> : (i256, i256, i256) -> i256 loc(#loc14)
40+
// CHECK-NEXT: %13 = llvm.inttoptr %c0_i256 : i256 to !llvm.ptr<1> loc(#loc15)
41+
// CHECK-NEXT: llvm.store %12, %13 {alignment = 1 : i64} : i256, !llvm.ptr<1> loc(#loc15)
42+
// CHECK-NEXT: %14 = llvm.load %2 {alignment = 32 : i64} : !llvm.ptr -> i256 loc(#loc16)
43+
// CHECK-NEXT: %15 = llvm.load %5 {alignment = 32 : i64} : !llvm.ptr -> i256 loc(#loc17)
44+
// CHECK-NEXT: %16 = llvm.load %8 {alignment = 32 : i64} : !llvm.ptr -> i256 loc(#loc18)
45+
// CHECK-NEXT: %17 = "llvm.intrcall"(%14, %15, %16) <{id = 3730 : i32, name = "evm.mulmod"}> : (i256, i256, i256) -> i256 loc(#loc19)
46+
// CHECK-NEXT: %18 = llvm.inttoptr %c32_i256 : i256 to !llvm.ptr<1> loc(#loc20)
47+
// CHECK-NEXT: llvm.store %17, %18 {alignment = 1 : i64} : i256, !llvm.ptr<1> loc(#loc20)
48+
// CHECK-NEXT: %19 = llvm.inttoptr %c0_i256 : i256 to !llvm.ptr<1> loc(#loc1)
49+
// CHECK-NEXT: "llvm.intrcall"(%19, %c64_i256) <{id = 3735 : i32, name = "evm.return"}> : (!llvm.ptr<1>, i256) -> () loc(#loc1)
50+
// CHECK-NEXT: call @".unreachable"() : () -> () loc(#loc1)
51+
// CHECK-NEXT: llvm.unreachable loc(#loc)
52+
// CHECK-NEXT: } loc(#loc)
53+
// CHECK-NEXT: } loc(#loc)
54+
// CHECK-NEXT: #loc = loc(unknown)
55+
// CHECK-NEXT: #loc1 = loc({{.*}}:9:4)
56+
// CHECK-NEXT: #loc2 = loc({{.*}}:4:13)
57+
// CHECK-NEXT: #loc3 = loc({{.*}}:4:8)
58+
// CHECK-NEXT: #loc4 = loc({{.*}}:4:4)
59+
// CHECK-NEXT: #loc5 = loc({{.*}}:5:13)
60+
// CHECK-NEXT: #loc6 = loc({{.*}}:5:8)
61+
// CHECK-NEXT: #loc7 = loc({{.*}}:5:4)
62+
// CHECK-NEXT: #loc8 = loc({{.*}}:6:13)
63+
// CHECK-NEXT: #loc9 = loc({{.*}}:6:8)
64+
// CHECK-NEXT: #loc10 = loc({{.*}}:6:4)
65+
// CHECK-NEXT: #loc11 = loc({{.*}}:7:21)
66+
// CHECK-NEXT: #loc12 = loc({{.*}}:7:24)
67+
// CHECK-NEXT: #loc13 = loc({{.*}}:7:27)
68+
// CHECK-NEXT: #loc14 = loc({{.*}}:7:14)
69+
// CHECK-NEXT: #loc15 = loc({{.*}}:7:4)
70+
// CHECK-NEXT: #loc16 = loc({{.*}}:8:22)
71+
// CHECK-NEXT: #loc17 = loc({{.*}}:8:25)
72+
// CHECK-NEXT: #loc18 = loc({{.*}}:8:28)
73+
// CHECK-NEXT: #loc19 = loc({{.*}}:8:15)
74+
// CHECK-NEXT: #loc20 = loc({{.*}}:8:4)
75+
// CHECK-EMPTY:
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
// RUN: solc --strict-assembly --mlir-action=print-init --mmlir --mlir-print-debuginfo %s | FileCheck %s
2+
3+
object "Test" {
4+
code {
5+
let x := mload(0)
6+
let y := mload(32)
7+
let m := mload(64)
8+
mstore(0, addmod(x, y, m))
9+
mstore(32, mulmod(x, y, m))
10+
return(0, 64)
11+
}
12+
}
13+
// NOTE: Assertions have been autogenerated by test/updFileCheckTest.py
14+
// CHECK: #Prague = #sol<EvmVersion Prague>
15+
// CHECK-NEXT: module attributes {sol.evm_version = #Prague} {
16+
// CHECK-NEXT: yul.object @Test {
17+
// CHECK-NEXT: %c0_i256 = arith.constant 0 : i256 loc(#loc1)
18+
// CHECK-NEXT: %0 = yul.mload %c0_i256 loc(#loc2)
19+
// CHECK-NEXT: %c1_i256 = arith.constant 1 : i256 loc(#loc3)
20+
// CHECK-NEXT: %1 = llvm.alloca %c1_i256 x i256 {alignment = 32 : i64} : (i256) -> !llvm.ptr loc(#loc4)
21+
// CHECK-NEXT: llvm.store %0, %1 {alignment = 32 : i64} : i256, !llvm.ptr loc(#loc3)
22+
// CHECK-NEXT: %c32_i256 = arith.constant 32 : i256 loc(#loc5)
23+
// CHECK-NEXT: %2 = yul.mload %c32_i256 loc(#loc6)
24+
// CHECK-NEXT: %c1_i256_0 = arith.constant 1 : i256 loc(#loc7)
25+
// CHECK-NEXT: %3 = llvm.alloca %c1_i256_0 x i256 {alignment = 32 : i64} : (i256) -> !llvm.ptr loc(#loc8)
26+
// CHECK-NEXT: llvm.store %2, %3 {alignment = 32 : i64} : i256, !llvm.ptr loc(#loc7)
27+
// CHECK-NEXT: %c64_i256 = arith.constant 64 : i256 loc(#loc9)
28+
// CHECK-NEXT: %4 = yul.mload %c64_i256 loc(#loc10)
29+
// CHECK-NEXT: %c1_i256_1 = arith.constant 1 : i256 loc(#loc11)
30+
// CHECK-NEXT: %5 = llvm.alloca %c1_i256_1 x i256 {alignment = 32 : i64} : (i256) -> !llvm.ptr loc(#loc12)
31+
// CHECK-NEXT: llvm.store %4, %5 {alignment = 32 : i64} : i256, !llvm.ptr loc(#loc11)
32+
// CHECK-NEXT: %c0_i256_2 = arith.constant 0 : i256 loc(#loc13)
33+
// CHECK-NEXT: %6 = llvm.load %1 {alignment = 32 : i64} : !llvm.ptr -> i256 loc(#loc14)
34+
// CHECK-NEXT: %7 = llvm.load %3 {alignment = 32 : i64} : !llvm.ptr -> i256 loc(#loc15)
35+
// CHECK-NEXT: %8 = llvm.load %5 {alignment = 32 : i64} : !llvm.ptr -> i256 loc(#loc16)
36+
// CHECK-NEXT: %9 = yul.addmod %6, %7, %8 loc(#loc17)
37+
// CHECK-NEXT: yul.mstore %c0_i256_2, %9 loc(#loc18)
38+
// CHECK-NEXT: %c32_i256_3 = arith.constant 32 : i256 loc(#loc19)
39+
// CHECK-NEXT: %10 = llvm.load %1 {alignment = 32 : i64} : !llvm.ptr -> i256 loc(#loc20)
40+
// CHECK-NEXT: %11 = llvm.load %3 {alignment = 32 : i64} : !llvm.ptr -> i256 loc(#loc21)
41+
// CHECK-NEXT: %12 = llvm.load %5 {alignment = 32 : i64} : !llvm.ptr -> i256 loc(#loc22)
42+
// CHECK-NEXT: %13 = yul.mulmod %10, %11, %12 loc(#loc23)
43+
// CHECK-NEXT: yul.mstore %c32_i256_3, %13 loc(#loc24)
44+
// CHECK-NEXT: %c0_i256_4 = arith.constant 0 : i256 loc(#loc25)
45+
// CHECK-NEXT: %c64_i256_5 = arith.constant 64 : i256 loc(#loc26)
46+
// CHECK-NEXT: yul.return %c0_i256_4, %c64_i256_5 loc(#loc27)
47+
// CHECK-NEXT: } loc(#loc)
48+
// CHECK-NEXT: } loc(#loc)
49+
// CHECK-NEXT: #loc = loc(unknown)
50+
// CHECK-NEXT: #loc1 = loc({{.*}}:4:19)
51+
// CHECK-NEXT: #loc2 = loc({{.*}}:4:13)
52+
// CHECK-NEXT: #loc3 = loc({{.*}}:4:4)
53+
// CHECK-NEXT: #loc4 = loc({{.*}}:4:8)
54+
// CHECK-NEXT: #loc5 = loc({{.*}}:5:19)
55+
// CHECK-NEXT: #loc6 = loc({{.*}}:5:13)
56+
// CHECK-NEXT: #loc7 = loc({{.*}}:5:4)
57+
// CHECK-NEXT: #loc8 = loc({{.*}}:5:8)
58+
// CHECK-NEXT: #loc9 = loc({{.*}}:6:19)
59+
// CHECK-NEXT: #loc10 = loc({{.*}}:6:13)
60+
// CHECK-NEXT: #loc11 = loc({{.*}}:6:4)
61+
// CHECK-NEXT: #loc12 = loc({{.*}}:6:8)
62+
// CHECK-NEXT: #loc13 = loc({{.*}}:7:11)
63+
// CHECK-NEXT: #loc14 = loc({{.*}}:7:21)
64+
// CHECK-NEXT: #loc15 = loc({{.*}}:7:24)
65+
// CHECK-NEXT: #loc16 = loc({{.*}}:7:27)
66+
// CHECK-NEXT: #loc17 = loc({{.*}}:7:14)
67+
// CHECK-NEXT: #loc18 = loc({{.*}}:7:4)
68+
// CHECK-NEXT: #loc19 = loc({{.*}}:8:11)
69+
// CHECK-NEXT: #loc20 = loc({{.*}}:8:22)
70+
// CHECK-NEXT: #loc21 = loc({{.*}}:8:25)
71+
// CHECK-NEXT: #loc22 = loc({{.*}}:8:28)
72+
// CHECK-NEXT: #loc23 = loc({{.*}}:8:15)
73+
// CHECK-NEXT: #loc24 = loc({{.*}}:8:4)
74+
// CHECK-NEXT: #loc25 = loc({{.*}}:9:11)
75+
// CHECK-NEXT: #loc26 = loc({{.*}}:9:14)
76+
// CHECK-NEXT: #loc27 = loc({{.*}}:9:4)
77+
// CHECK-EMPTY:

0 commit comments

Comments
 (0)