1- from vyper .utils import OrderedSet
21from vyper .venom .analysis .cfg import CFGAnalysis
32from vyper .venom .analysis .dfg import DFGAnalysis
43from vyper .venom .analysis .liveness import LivenessAnalysis
5- from vyper .venom .basicblock import IRBasicBlock , IRInstruction , IRVariable
4+ from vyper .venom .basicblock import IRInstruction , IRVariable
65from vyper .venom .function import IRFunction
76from vyper .venom .passes .base_pass import IRPass
87
@@ -14,21 +13,27 @@ class Mem2Var(IRPass):
1413 """
1514
1615 function : IRFunction
17- defs : dict [IRVariable , OrderedSet [IRBasicBlock ]]
1816
1917 def run_pass (self ):
2018 self .analyses_cache .request_analysis (CFGAnalysis )
2119 dfg = self .analyses_cache .request_analysis (DFGAnalysis )
2220
2321 self .var_name_count = 0
2422 for var , inst in dfg .outputs .items ():
25- if inst .opcode != "alloca" :
26- continue
27- self ._process_alloca_var (dfg , var )
23+ if inst .opcode == "alloca" :
24+ self ._process_alloca_var (dfg , var )
25+ elif inst .opcode == "palloca" :
26+ self ._process_palloca_var (dfg , inst , var )
2827
2928 self .analyses_cache .invalidate_analysis (DFGAnalysis )
3029 self .analyses_cache .invalidate_analysis (LivenessAnalysis )
3130
31+ def _mk_varname (self , varname : str ):
32+ varname = varname .removeprefix ("%" )
33+ varname = f"var{ varname } _{ self .var_name_count } "
34+ self .var_name_count += 1
35+ return varname
36+
3237 def _process_alloca_var (self , dfg : DFGAnalysis , var : IRVariable ):
3338 """
3439 Process alloca allocated variable. If it is only used by mstore/mload/return
@@ -40,8 +45,7 @@ def _process_alloca_var(self, dfg: DFGAnalysis, var: IRVariable):
4045 elif all ([inst .opcode == "mstore" for inst in uses ]):
4146 return
4247 elif all ([inst .opcode in ["mstore" , "mload" , "return" ] for inst in uses ]):
43- var_name = f"addr{ var .name } _{ self .var_name_count } "
44- self .var_name_count += 1
48+ var_name = self ._mk_varname (var .name )
4549 for inst in uses :
4650 if inst .opcode == "mstore" :
4751 inst .opcode = "store"
@@ -52,7 +56,32 @@ def _process_alloca_var(self, dfg: DFGAnalysis, var: IRVariable):
5256 inst .operands = [IRVariable (var_name )]
5357 elif inst .opcode == "return" :
5458 bb = inst .parent
55- idx = bb .instructions .index (inst )
59+ idx = len (bb .instructions ) - 1
60+ assert inst == bb .instructions [idx ] # sanity
5661 bb .insert_instruction (
5762 IRInstruction ("mstore" , [IRVariable (var_name ), inst .operands [1 ]]), idx
5863 )
64+
65+ def _process_palloca_var (self , dfg : DFGAnalysis , palloca_inst : IRInstruction , var : IRVariable ):
66+ """
67+ Process alloca allocated variable. If it is only used by mstore/mload
68+ instructions, it is promoted to a stack variable. Otherwise, it is left as is.
69+ """
70+ uses = dfg .get_uses (var )
71+ if not all (inst .opcode in ["mstore" , "mload" ] for inst in uses ):
72+ return
73+
74+ var_name = self ._mk_varname (var .name )
75+
76+ palloca_inst .opcode = "mload"
77+ palloca_inst .operands = [palloca_inst .operands [0 ]]
78+ palloca_inst .output = IRVariable (var_name )
79+
80+ for inst in uses :
81+ if inst .opcode == "mstore" :
82+ inst .opcode = "store"
83+ inst .output = IRVariable (var_name )
84+ inst .operands = [inst .operands [0 ]]
85+ elif inst .opcode == "mload" :
86+ inst .opcode = "store"
87+ inst .operands = [IRVariable (var_name )]
0 commit comments