Skip to content

Commit 9da9575

Browse files
committed
[mlir][evm] Lower bitwise, shift, 'exp', and 'mul' builtins
1 parent f2f12b8 commit 9da9575

File tree

11 files changed

+731
-36
lines changed

11 files changed

+731
-36
lines changed

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

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,134 @@ struct Keccak256OpLowering : public OpRewritePattern<yul::Keccak256Op> {
5858
}
5959
};
6060

61+
struct DivOpLowering : public OpRewritePattern<yul::DivOp> {
62+
using OpRewritePattern<yul::DivOp>::OpRewritePattern;
63+
64+
LogicalResult matchAndRewrite(yul::DivOp op,
65+
PatternRewriter &r) const override {
66+
evm::Builder evmB(r, op.getLoc());
67+
68+
r.replaceOpWithNewOp<LLVM::IntrCallOp>(
69+
op, llvm::Intrinsic::evm_div,
70+
/*resTy=*/r.getIntegerType(256),
71+
/*ins=*/ValueRange{op.getDividend(), op.getDivisor()}, "evm.div");
72+
73+
return success();
74+
}
75+
};
76+
77+
struct SDivOpLowering : public OpRewritePattern<yul::SDivOp> {
78+
using OpRewritePattern<yul::SDivOp>::OpRewritePattern;
79+
80+
LogicalResult matchAndRewrite(yul::SDivOp op,
81+
PatternRewriter &r) const override {
82+
evm::Builder evmB(r, op.getLoc());
83+
84+
r.replaceOpWithNewOp<LLVM::IntrCallOp>(
85+
op, llvm::Intrinsic::evm_sdiv,
86+
/*resTy=*/r.getIntegerType(256),
87+
/*ins=*/ValueRange{op.getDividend(), op.getDivisor()}, "evm.sdiv");
88+
89+
return success();
90+
}
91+
};
92+
93+
struct ModOpLowering : public OpRewritePattern<yul::ModOp> {
94+
using OpRewritePattern<yul::ModOp>::OpRewritePattern;
95+
96+
LogicalResult matchAndRewrite(yul::ModOp op,
97+
PatternRewriter &r) const override {
98+
evm::Builder evmB(r, op.getLoc());
99+
100+
r.replaceOpWithNewOp<LLVM::IntrCallOp>(
101+
op, llvm::Intrinsic::evm_mod,
102+
/*resTy=*/r.getIntegerType(256),
103+
/*ins=*/ValueRange{op.getValue(), op.getMod()}, "evm.mod");
104+
105+
return success();
106+
}
107+
};
108+
109+
struct SModOpLowering : public OpRewritePattern<yul::SModOp> {
110+
using OpRewritePattern<yul::SModOp>::OpRewritePattern;
111+
112+
LogicalResult matchAndRewrite(yul::SModOp op,
113+
PatternRewriter &r) const override {
114+
evm::Builder evmB(r, op.getLoc());
115+
116+
r.replaceOpWithNewOp<LLVM::IntrCallOp>(
117+
op, llvm::Intrinsic::evm_smod,
118+
/*resTy=*/r.getIntegerType(256),
119+
/*ins=*/ValueRange{op.getValue(), op.getMod()}, "evm.smod");
120+
121+
return success();
122+
}
123+
};
124+
125+
struct ShlOpLowering : public OpRewritePattern<yul::ShlOp> {
126+
using OpRewritePattern<yul::ShlOp>::OpRewritePattern;
127+
128+
LogicalResult matchAndRewrite(yul::ShlOp op,
129+
PatternRewriter &r) const override {
130+
evm::Builder evmB(r, op.getLoc());
131+
132+
r.replaceOpWithNewOp<LLVM::IntrCallOp>(
133+
op, llvm::Intrinsic::evm_shl,
134+
/*resTy=*/r.getIntegerType(256),
135+
/*ins=*/ValueRange{op.getShift(), op.getVal()}, "evm.shl");
136+
137+
return success();
138+
}
139+
};
140+
141+
struct ShrOpLowering : public OpRewritePattern<yul::ShrOp> {
142+
using OpRewritePattern<yul::ShrOp>::OpRewritePattern;
143+
144+
LogicalResult matchAndRewrite(yul::ShrOp op,
145+
PatternRewriter &r) const override {
146+
evm::Builder evmB(r, op.getLoc());
147+
148+
r.replaceOpWithNewOp<LLVM::IntrCallOp>(
149+
op, llvm::Intrinsic::evm_shr,
150+
/*resTy=*/r.getIntegerType(256),
151+
/*ins=*/ValueRange{op.getShift(), op.getVal()}, "evm.shr");
152+
153+
return success();
154+
}
155+
};
156+
157+
struct SarOpLowering : public OpRewritePattern<yul::SarOp> {
158+
using OpRewritePattern<yul::SarOp>::OpRewritePattern;
159+
160+
LogicalResult matchAndRewrite(yul::SarOp op,
161+
PatternRewriter &r) const override {
162+
evm::Builder evmB(r, op.getLoc());
163+
164+
r.replaceOpWithNewOp<LLVM::IntrCallOp>(
165+
op, llvm::Intrinsic::evm_sar,
166+
/*resTy=*/r.getIntegerType(256),
167+
/*ins=*/ValueRange{op.getShift(), op.getVal()}, "evm.sar");
168+
169+
return success();
170+
}
171+
};
172+
173+
struct ExpOpLowering : public OpRewritePattern<yul::ExpOp> {
174+
using OpRewritePattern<yul::ExpOp>::OpRewritePattern;
175+
176+
LogicalResult matchAndRewrite(yul::ExpOp op,
177+
PatternRewriter &r) const override {
178+
evm::Builder evmB(r, op.getLoc());
179+
180+
r.replaceOpWithNewOp<LLVM::IntrCallOp>(
181+
op, llvm::Intrinsic::evm_exp,
182+
/*resTy=*/r.getIntegerType(256),
183+
/*ins=*/ValueRange{op.getBase(), op.getExp()}, "evm.exp");
184+
185+
return success();
186+
}
187+
};
188+
61189
struct LogOpLowering : public OpRewritePattern<yul::LogOp> {
62190
using OpRewritePattern<yul::LogOp>::OpRewritePattern;
63191

@@ -725,6 +853,14 @@ void evm::populateYulPats(RewritePatternSet &pats) {
725853
// clang-format off
726854
UpdFreePtrOpLowering,
727855
Keccak256OpLowering,
856+
DivOpLowering,
857+
SDivOpLowering,
858+
ModOpLowering,
859+
SModOpLowering,
860+
ShlOpLowering,
861+
ShrOpLowering,
862+
SarOpLowering,
863+
ExpOpLowering,
728864
LogOpLowering,
729865
AddressOpLowering,
730866
CallerOpLowering,

libsolidity/codegen/mlir/Yul/YulOps.td

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,62 @@ def Yul_Keccak256Op : Yul_Op<"keccak256"> {
304304
let assemblyFormat = "$addr `,` $size attr-dict";
305305
}
306306

307+
def Yul_DivOp : Yul_Op<"div", [Pure]> {
308+
let summary = "Represents the `div` call in yul";
309+
let arguments = (ins I256:$dividend, I256:$divisor);
310+
let results = (outs I256:$out);
311+
let assemblyFormat = "$dividend `,` $divisor attr-dict";
312+
}
313+
314+
def Yul_SDivOp : Yul_Op<"sdiv", [Pure]> {
315+
let summary = "Represents the `sdiv` call in yul";
316+
let arguments = (ins I256:$dividend, I256:$divisor);
317+
let results = (outs I256:$out);
318+
let assemblyFormat = "$dividend `,` $divisor attr-dict";
319+
}
320+
321+
def Yul_ModOp : Yul_Op<"mod", [Pure]> {
322+
let summary = "Represents the `mod` call in yul";
323+
let arguments = (ins I256:$value, I256:$mod);
324+
let results = (outs I256:$out);
325+
let assemblyFormat = "$value `,` $mod attr-dict";
326+
}
327+
328+
def Yul_SModOp : Yul_Op<"smod", [Pure]> {
329+
let summary = "Represents the `smod` call in yul";
330+
let arguments = (ins I256:$value, I256:$mod);
331+
let results = (outs I256:$out);
332+
let assemblyFormat = "$value `,` $mod attr-dict";
333+
}
334+
335+
def Yul_ShlOp : Yul_Op<"shl", [Pure]> {
336+
let summary = "Represents the `shl` call in yul";
337+
let arguments = (ins I256:$shift, I256:$val);
338+
let results = (outs I256:$out);
339+
let assemblyFormat = "$shift `,` $val attr-dict";
340+
}
341+
342+
def Yul_ShrOp : Yul_Op<"shr", [Pure]> {
343+
let summary = "Represents the `shr` call in yul";
344+
let arguments = (ins I256:$shift, I256:$val);
345+
let results = (outs I256:$out);
346+
let assemblyFormat = "$shift `,` $val attr-dict";
347+
}
348+
349+
def Yul_SarOp : Yul_Op<"sar", [Pure]> {
350+
let summary = "Represents the `sar` call in yul";
351+
let arguments = (ins I256:$shift, I256:$val);
352+
let results = (outs I256:$out);
353+
let assemblyFormat = "$shift `,` $val attr-dict";
354+
}
355+
356+
def Yul_ExpOp : Yul_Op<"exp", [Pure]> {
357+
let summary = "Represents the `exp` call in yul";
358+
let arguments = (ins I256:$base, I256:$exp);
359+
let results = (outs I256:$out);
360+
let assemblyFormat = "$base `,` $exp attr-dict";
361+
}
362+
307363
def Yul_LogOp : Yul_Op<"log"> {
308364
let summary = "Represents the `log*` calls in yul";
309365
let arguments = (ins I256:$addr, I256:$size, Variadic<I256>:$topics);

libsolidity/codegen/mlir/YulToMLIR.cpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,17 @@ void YulToMLIRPass::populateBuiltinGenMap() {
280280
using namespace mlir::yul;
281281
defSimpleBuiltinGen<arith::AddIOp>("add");
282282
defSimpleBuiltinGen<arith::SubIOp>("sub");
283-
defSimpleBuiltinGen<arith::ShRUIOp, /*reverseArgs=*/true>("shr");
283+
defSimpleBuiltinGen<arith::MulIOp>("mul");
284+
defSimpleBuiltinGen<arith::AndIOp>("and");
285+
defSimpleBuiltinGen<arith::OrIOp>("or");
286+
defSimpleBuiltinGen<arith::XOrIOp>("xor");
287+
defSimpleBuiltinGen<DivOp>("div");
288+
defSimpleBuiltinGen<SDivOp>("sdiv");
289+
defSimpleBuiltinGen<ModOp>("mod");
290+
defSimpleBuiltinGen<SModOp>("smod");
291+
defSimpleBuiltinGen<ShrOp, /*reverseArgs=*/true>("shr");
292+
defSimpleBuiltinGen<ShlOp, /*reverseArgs=*/true>("shl");
293+
defSimpleBuiltinGen<SarOp, /*reverseArgs=*/true>("sar");
284294
defCmpBuiltinGen<arith::CmpIPredicate::ult>("lt");
285295
defCmpBuiltinGen<arith::CmpIPredicate::slt>("slt");
286296
defCmpBuiltinGen<arith::CmpIPredicate::ugt>("gt");
@@ -330,6 +340,7 @@ void YulToMLIRPass::populateBuiltinGenMap() {
330340
defSimpleBuiltinGenNoRet<RevertOp>("revert");
331341
defSimpleBuiltinGenNoRet<StopOp>("stop");
332342
defSimpleBuiltinGen<Keccak256Op>("keccak256");
343+
defSimpleBuiltinGen<ExpOp>("exp");
333344
defSimpleBuiltinGen<CallValOp>("callvalue");
334345
defSimpleBuiltinGen<AddressOp>("address");
335346
defSimpleBuiltinGen<CallerOp>("caller");
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
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 a := mload(0)
6+
let b := mload(32)
7+
mstore(64, div(a, b))
8+
mstore(96, sdiv(a, b))
9+
mstore(128, mod(a, b))
10+
mstore(160, smod(a, b))
11+
return(64, 128)
12+
}
13+
}
14+
// NOTE: Assertions have been autogenerated by test/updFileCheckTest.py
15+
// CHECK: #Prague = #sol<EvmVersion Prague>
16+
// CHECK-NEXT: module @Test attributes {sol.evm_version = #Prague} {
17+
// CHECK-NEXT: func.func @".unreachable"() attributes {llvm.linkage = #llvm.linkage<private>, passthrough = ["nofree", "null_pointer_is_valid"]} {
18+
// CHECK-NEXT: llvm.unreachable loc(#loc1)
19+
// CHECK-NEXT: } loc(#loc)
20+
// CHECK-NEXT: func.func @__entry() attributes {llvm.linkage = #llvm.linkage<external>, passthrough = ["nofree", "null_pointer_is_valid"]} {
21+
// CHECK-NEXT: %c160_i256 = arith.constant 160 : i256 loc(#loc)
22+
// CHECK-NEXT: %c128_i256 = arith.constant 128 : i256 loc(#loc)
23+
// CHECK-NEXT: %c96_i256 = arith.constant 96 : i256 loc(#loc)
24+
// CHECK-NEXT: %c64_i256 = arith.constant 64 : i256 loc(#loc)
25+
// CHECK-NEXT: %c32_i256 = arith.constant 32 : i256 loc(#loc)
26+
// CHECK-NEXT: %c1_i256 = arith.constant 1 : i256 loc(#loc)
27+
// CHECK-NEXT: %c0_i256 = arith.constant 0 : i256 loc(#loc2)
28+
// CHECK-NEXT: %0 = llvm.inttoptr %c0_i256 : i256 to !llvm.ptr<1> loc(#loc3)
29+
// CHECK-NEXT: %1 = llvm.load %0 {alignment = 1 : i64} : !llvm.ptr<1> -> i256 loc(#loc3)
30+
// CHECK-NEXT: %2 = llvm.alloca %c1_i256 x i256 {alignment = 32 : i64} : (i256) -> !llvm.ptr loc(#loc4)
31+
// CHECK-NEXT: llvm.store %1, %2 {alignment = 32 : i64} : i256, !llvm.ptr loc(#loc5)
32+
// CHECK-NEXT: %3 = llvm.inttoptr %c32_i256 : i256 to !llvm.ptr<1> loc(#loc6)
33+
// CHECK-NEXT: %4 = llvm.load %3 {alignment = 1 : i64} : !llvm.ptr<1> -> i256 loc(#loc6)
34+
// CHECK-NEXT: %5 = llvm.alloca %c1_i256 x i256 {alignment = 32 : i64} : (i256) -> !llvm.ptr loc(#loc7)
35+
// CHECK-NEXT: llvm.store %4, %5 {alignment = 32 : i64} : i256, !llvm.ptr loc(#loc8)
36+
// CHECK-NEXT: %6 = llvm.load %2 {alignment = 32 : i64} : !llvm.ptr -> i256 loc(#loc9)
37+
// CHECK-NEXT: %7 = llvm.load %5 {alignment = 32 : i64} : !llvm.ptr -> i256 loc(#loc10)
38+
// CHECK-NEXT: %8 = "llvm.intrcall"(%6, %7) <{id = 3707 : i32, name = "evm.div"}> : (i256, i256) -> i256 loc(#loc11)
39+
// CHECK-NEXT: %9 = llvm.inttoptr %c64_i256 : i256 to !llvm.ptr<1> loc(#loc12)
40+
// CHECK-NEXT: llvm.store %8, %9 {alignment = 1 : i64} : i256, !llvm.ptr<1> loc(#loc12)
41+
// CHECK-NEXT: %10 = llvm.load %2 {alignment = 32 : i64} : !llvm.ptr -> i256 loc(#loc13)
42+
// CHECK-NEXT: %11 = llvm.load %5 {alignment = 32 : i64} : !llvm.ptr -> i256 loc(#loc14)
43+
// CHECK-NEXT: %12 = "llvm.intrcall"(%10, %11) <{id = 3739 : i32, name = "evm.sdiv"}> : (i256, i256) -> i256 loc(#loc15)
44+
// CHECK-NEXT: %13 = llvm.inttoptr %c96_i256 : i256 to !llvm.ptr<1> loc(#loc16)
45+
// CHECK-NEXT: llvm.store %12, %13 {alignment = 1 : i64} : i256, !llvm.ptr<1> loc(#loc16)
46+
// CHECK-NEXT: %14 = llvm.load %2 {alignment = 32 : i64} : !llvm.ptr -> i256 loc(#loc17)
47+
// CHECK-NEXT: %15 = llvm.load %5 {alignment = 32 : i64} : !llvm.ptr -> i256 loc(#loc18)
48+
// CHECK-NEXT: %16 = "llvm.intrcall"(%14, %15) <{id = 3727 : i32, name = "evm.mod"}> : (i256, i256) -> i256 loc(#loc19)
49+
// CHECK-NEXT: %17 = llvm.inttoptr %c128_i256 : i256 to !llvm.ptr<1> loc(#loc20)
50+
// CHECK-NEXT: llvm.store %16, %17 {alignment = 1 : i64} : i256, !llvm.ptr<1> loc(#loc20)
51+
// CHECK-NEXT: %18 = llvm.load %2 {alignment = 32 : i64} : !llvm.ptr -> i256 loc(#loc21)
52+
// CHECK-NEXT: %19 = llvm.load %5 {alignment = 32 : i64} : !llvm.ptr -> i256 loc(#loc22)
53+
// CHECK-NEXT: %20 = "llvm.intrcall"(%18, %19) <{id = 3746 : i32, name = "evm.smod"}> : (i256, i256) -> i256 loc(#loc23)
54+
// CHECK-NEXT: %21 = llvm.inttoptr %c160_i256 : i256 to !llvm.ptr<1> loc(#loc24)
55+
// CHECK-NEXT: llvm.store %20, %21 {alignment = 1 : i64} : i256, !llvm.ptr<1> loc(#loc24)
56+
// CHECK-NEXT: %22 = llvm.inttoptr %c64_i256 : i256 to !llvm.ptr<1> loc(#loc1)
57+
// CHECK-NEXT: "llvm.intrcall"(%22, %c128_i256) <{id = 3735 : i32, name = "evm.return"}> : (!llvm.ptr<1>, i256) -> () loc(#loc1)
58+
// CHECK-NEXT: call @".unreachable"() : () -> () loc(#loc1)
59+
// CHECK-NEXT: llvm.unreachable loc(#loc)
60+
// CHECK-NEXT: } loc(#loc)
61+
// CHECK-NEXT: } loc(#loc)
62+
// CHECK-NEXT: #loc = loc(unknown)
63+
// CHECK-NEXT: #loc1 = loc({{.*}}:10:4)
64+
// CHECK-NEXT: #loc2 = loc({{.*}}:4:19)
65+
// CHECK-NEXT: #loc3 = loc({{.*}}:4:13)
66+
// CHECK-NEXT: #loc4 = loc({{.*}}:4:8)
67+
// CHECK-NEXT: #loc5 = loc({{.*}}:4:4)
68+
// CHECK-NEXT: #loc6 = loc({{.*}}:5:13)
69+
// CHECK-NEXT: #loc7 = loc({{.*}}:5:8)
70+
// CHECK-NEXT: #loc8 = loc({{.*}}:5:4)
71+
// CHECK-NEXT: #loc9 = loc({{.*}}:6:19)
72+
// CHECK-NEXT: #loc10 = loc({{.*}}:6:22)
73+
// CHECK-NEXT: #loc11 = loc({{.*}}:6:15)
74+
// CHECK-NEXT: #loc12 = loc({{.*}}:6:4)
75+
// CHECK-NEXT: #loc13 = loc({{.*}}:7:20)
76+
// CHECK-NEXT: #loc14 = loc({{.*}}:7:23)
77+
// CHECK-NEXT: #loc15 = loc({{.*}}:7:15)
78+
// CHECK-NEXT: #loc16 = loc({{.*}}:7:4)
79+
// CHECK-NEXT: #loc17 = loc({{.*}}:8:20)
80+
// CHECK-NEXT: #loc18 = loc({{.*}}:8:23)
81+
// CHECK-NEXT: #loc19 = loc({{.*}}:8:16)
82+
// CHECK-NEXT: #loc20 = loc({{.*}}:8:4)
83+
// CHECK-NEXT: #loc21 = loc({{.*}}:9:21)
84+
// CHECK-NEXT: #loc22 = loc({{.*}}:9:24)
85+
// CHECK-NEXT: #loc23 = loc({{.*}}:9:16)
86+
// CHECK-NEXT: #loc24 = loc({{.*}}:9:4)
87+
// CHECK-EMPTY:
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
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+
mstore(0, exp(mload(0), mload(32)))
6+
}
7+
}
8+
// NOTE: Assertions have been autogenerated by test/updFileCheckTest.py
9+
// CHECK: #Prague = #sol<EvmVersion Prague>
10+
// CHECK-NEXT: module @Test attributes {sol.evm_version = #Prague} {
11+
// CHECK-NEXT: func.func @__entry() attributes {llvm.linkage = #llvm.linkage<external>, passthrough = ["nofree", "null_pointer_is_valid"]} {
12+
// CHECK-NEXT: %c32_i256 = arith.constant 32 : i256 loc(#loc)
13+
// CHECK-NEXT: %c0_i256 = arith.constant 0 : i256 loc(#loc)
14+
// CHECK-NEXT: %0 = llvm.inttoptr %c0_i256 : i256 to !llvm.ptr<1> loc(#loc1)
15+
// CHECK-NEXT: %1 = llvm.load %0 {alignment = 1 : i64} : !llvm.ptr<1> -> i256 loc(#loc1)
16+
// CHECK-NEXT: %2 = llvm.inttoptr %c32_i256 : i256 to !llvm.ptr<1> loc(#loc2)
17+
// CHECK-NEXT: %3 = llvm.load %2 {alignment = 1 : i64} : !llvm.ptr<1> -> i256 loc(#loc2)
18+
// CHECK-NEXT: %4 = "llvm.intrcall"(%1, %3) <{id = 3708 : i32, name = "evm.exp"}> : (i256, i256) -> i256 loc(#loc3)
19+
// CHECK-NEXT: %5 = llvm.inttoptr %c0_i256 : i256 to !llvm.ptr<1> loc(#loc4)
20+
// CHECK-NEXT: llvm.store %4, %5 {alignment = 1 : i64} : i256, !llvm.ptr<1> loc(#loc4)
21+
// CHECK-NEXT: llvm.unreachable loc(#loc)
22+
// CHECK-NEXT: } loc(#loc)
23+
// CHECK-NEXT: } loc(#loc)
24+
// CHECK-NEXT: #loc = loc(unknown)
25+
// CHECK-NEXT: #loc1 = loc({{.*}}:4:18)
26+
// CHECK-NEXT: #loc2 = loc({{.*}}:4:28)
27+
// CHECK-NEXT: #loc3 = loc({{.*}}:4:14)
28+
// CHECK-NEXT: #loc4 = loc({{.*}}:4:4)
29+
// CHECK-EMPTY:

0 commit comments

Comments
 (0)