Skip to content

Commit 9c98b3e

Browse files
charles-cooperharkalHodanPlodky
authored
feat[venom]: add load elimination pass (#4265)
add primitive `sload`/`tload`/`mload` elimination. keeps latest (m/s/t)load or (m/s/t)store in a one-element "lattice", and weakens the (m/s/t)load to a `store` instruction if there is a hit on the same key. --------- Co-authored-by: Harry Kalogirou <[email protected]> Co-authored-by: HodanPlodky <[email protected]>
1 parent fadd4de commit 9c98b3e

File tree

5 files changed

+187
-0
lines changed

5 files changed

+187
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
from tests.venom_utils import assert_ctx_eq, parse_from_basic_block
2+
from vyper.venom.analysis.analysis import IRAnalysesCache
3+
from vyper.venom.passes.load_elimination import LoadElimination
4+
5+
6+
def _check_pre_post(pre, post):
7+
ctx = parse_from_basic_block(pre)
8+
9+
for fn in ctx.functions.values():
10+
ac = IRAnalysesCache(fn)
11+
LoadElimination(ac, fn).run_pass()
12+
13+
assert_ctx_eq(ctx, parse_from_basic_block(post))
14+
15+
16+
def _check_no_change(pre):
17+
_check_pre_post(pre, pre)
18+
19+
20+
def test_simple_load_elimination():
21+
pre = """
22+
main:
23+
%ptr = 11
24+
%1 = mload %ptr
25+
26+
%2 = mload %ptr
27+
28+
stop
29+
"""
30+
post = """
31+
main:
32+
%ptr = 11
33+
%1 = mload %ptr
34+
35+
%2 = %1
36+
37+
stop
38+
"""
39+
_check_pre_post(pre, post)
40+
41+
42+
def test_equivalent_var_elimination():
43+
"""
44+
Test that the lattice can "peer through" equivalent vars
45+
"""
46+
pre = """
47+
main:
48+
%1 = 11
49+
%2 = %1
50+
%3 = mload %1
51+
52+
%4 = mload %2
53+
54+
stop
55+
"""
56+
post = """
57+
main:
58+
%1 = 11
59+
%2 = %1
60+
%3 = mload %1
61+
62+
%4 = %3 # %2 == %1
63+
64+
stop
65+
"""
66+
_check_pre_post(pre, post)
67+
68+
69+
def test_elimination_barrier():
70+
"""
71+
Check for barrier between load/load
72+
"""
73+
pre = """
74+
main:
75+
%1 = 11
76+
%2 = mload %1
77+
%3 = %100
78+
# fence - writes to memory
79+
staticcall %3, %3, %3, %3
80+
%4 = mload %1
81+
"""
82+
_check_no_change(pre)
83+
84+
85+
def test_store_load_elimination():
86+
"""
87+
Check that lattice stores the result of mstores (even through
88+
equivalent variables)
89+
"""
90+
pre = """
91+
main:
92+
%val = 55
93+
%ptr1 = 11
94+
%ptr2 = %ptr1
95+
mstore %ptr1, %val
96+
97+
%3 = mload %ptr2
98+
99+
stop
100+
"""
101+
post = """
102+
main:
103+
%val = 55
104+
%ptr1 = 11
105+
%ptr2 = %ptr1
106+
mstore %ptr1, %val
107+
108+
%3 = %val
109+
110+
stop
111+
"""
112+
_check_pre_post(pre, post)
113+
114+
115+
def test_store_load_barrier():
116+
"""
117+
Check for barrier between store/load
118+
"""
119+
pre = """
120+
main:
121+
%ptr = 11
122+
%val = 55
123+
mstore %ptr, %val
124+
%3 = %100 ; arbitrary
125+
# fence
126+
staticcall %3, %3, %3, %3
127+
%4 = mload %ptr
128+
"""
129+
_check_no_change(pre)

vyper/venom/__init__.py

+4
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
BranchOptimizationPass,
1616
DFTPass,
1717
FloatAllocas,
18+
LoadElimination,
1819
LowerDloadPass,
1920
MakeSSA,
2021
Mem2Var,
@@ -59,9 +60,12 @@ def _run_passes(fn: IRFunction, optimize: OptimizationLevel) -> None:
5960
Mem2Var(ac, fn).run_pass()
6061
MakeSSA(ac, fn).run_pass()
6162
SCCP(ac, fn).run_pass()
63+
64+
LoadElimination(ac, fn).run_pass()
6265
StoreElimination(ac, fn).run_pass()
6366
MemMergePass(ac, fn).run_pass()
6467
SimplifyCFGPass(ac, fn).run_pass()
68+
6569
LowerDloadPass(ac, fn).run_pass()
6670
AlgebraicOptimizationPass(ac, fn).run_pass()
6771
# NOTE: MakeSSA is after algebraic optimization it currently produces

vyper/venom/passes/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from .dft import DFTPass
44
from .float_allocas import FloatAllocas
55
from .literals_codesize import ReduceLiteralsCodesize
6+
from .load_elimination import LoadElimination
67
from .lower_dload import LowerDloadPass
78
from .make_ssa import MakeSSA
89
from .mem2var import Mem2Var
+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
from vyper.venom.analysis import DFGAnalysis, LivenessAnalysis, VarEquivalenceAnalysis
2+
from vyper.venom.effects import Effects
3+
from vyper.venom.passes.base_pass import IRPass
4+
5+
6+
class LoadElimination(IRPass):
7+
"""
8+
Eliminate sloads, mloads and tloads
9+
"""
10+
11+
# should this be renamed to EffectsElimination?
12+
13+
def run_pass(self):
14+
self.equivalence = self.analyses_cache.request_analysis(VarEquivalenceAnalysis)
15+
16+
for bb in self.function.get_basic_blocks():
17+
self._process_bb(bb, Effects.MEMORY, "mload", "mstore")
18+
self._process_bb(bb, Effects.TRANSIENT, "tload", "tstore")
19+
self._process_bb(bb, Effects.STORAGE, "sload", "sstore")
20+
21+
self.analyses_cache.invalidate_analysis(LivenessAnalysis)
22+
self.analyses_cache.invalidate_analysis(DFGAnalysis)
23+
24+
def equivalent(self, op1, op2):
25+
return op1 == op2 or self.equivalence.equivalent(op1, op2)
26+
27+
def _process_bb(self, bb, eff, load_opcode, store_opcode):
28+
# not really a lattice even though it is not really inter-basic block;
29+
# we may generalize in the future
30+
lattice = ()
31+
32+
for inst in bb.instructions:
33+
if eff in inst.get_write_effects():
34+
lattice = ()
35+
36+
if inst.opcode == store_opcode:
37+
# mstore [val, ptr]
38+
val, ptr = inst.operands
39+
lattice = (ptr, val)
40+
41+
if inst.opcode == load_opcode:
42+
prev_lattice = lattice
43+
(ptr,) = inst.operands
44+
lattice = (ptr, inst.output)
45+
if not prev_lattice:
46+
continue
47+
if not self.equivalent(ptr, prev_lattice[0]):
48+
continue
49+
inst.opcode = "store"
50+
inst.operands = [prev_lattice[1]]

vyper/venom/passes/store_elimination.py

+3
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ class StoreElimination(IRPass):
99
and removes the `store` instruction.
1010
"""
1111

12+
# TODO: consider renaming `store` instruction, since it is confusing
13+
# with LoadElimination
14+
1215
def run_pass(self):
1316
self.analyses_cache.request_analysis(CFGAnalysis)
1417
dfg = self.analyses_cache.request_analysis(DFGAnalysis)

0 commit comments

Comments
 (0)