From 84dea3fb5d387ae22b82dc4179d3361162e54ce6 Mon Sep 17 00:00:00 2001 From: Harry Kalogirou Date: Fri, 30 May 2025 12:26:35 +0300 Subject: [PATCH 1/5] concept implementation for mload only --- vyper/venom/passes/dft.py | 57 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/vyper/venom/passes/dft.py b/vyper/venom/passes/dft.py index 02570bb403..12468ebc65 100644 --- a/vyper/venom/passes/dft.py +++ b/vyper/venom/passes/dft.py @@ -1,9 +1,12 @@ from collections import defaultdict +from typing import Dict, List +from vyper.venom.analysis.mem_ssa import MemSSA, MemSSAAbstract, MemoryDef, MemoryPhi, MemoryUse +from vyper.venom.memory_location import MemoryLocation import vyper.venom.effects as effects from vyper.utils import OrderedSet from vyper.venom.analysis import DFGAnalysis, LivenessAnalysis -from vyper.venom.basicblock import IRBasicBlock, IRInstruction +from vyper.venom.basicblock import IRBasicBlock, IRInstruction, IRVariable from vyper.venom.function import IRFunction from vyper.venom.passes.base_pass import IRPass @@ -17,11 +20,16 @@ class DFTPass(IRPass): # "effect dependency analysis" eda: dict[IRInstruction, OrderedSet[IRInstruction]] + effective_reaching_defs: dict[MemoryUse, MemoryDef] + defs_to_uses: Dict[MemoryDef, List[MemoryUse]] + def run_pass(self) -> None: self.data_offspring = {} self.visited_instructions: OrderedSet[IRInstruction] = OrderedSet() self.dfg = self.analyses_cache.force_analysis(DFGAnalysis) + self.mem_ssa = self.analyses_cache.force_analysis(MemSSA) + self._calculate_effective_reaching_defs() for bb in self.function.get_basic_blocks(): self._process_basic_block(bb) @@ -110,6 +118,15 @@ def _calculate_dependency_graphs(self, bb: IRBasicBlock) -> None: self.eda[inst].add(last_read_effects[write_effect]) last_write_effects[write_effect] = inst + if inst.opcode == "mload": + mem_use = self.mem_ssa.get_memory_use(inst) + mem_def = self.effective_reaching_defs.get(mem_use, None) + if mem_def is not None: + self.eda[inst].add(mem_def.inst) + elif effects.MEMORY in last_write_effects and last_write_effects[effects.MEMORY] != inst: + self.eda[inst].add(last_write_effects[effects.MEMORY]) + continue + for read_effect in read_effects: if read_effect in last_write_effects and last_write_effects[read_effect] != inst: self.eda[inst].add(last_write_effects[read_effect]) @@ -128,3 +145,41 @@ def _calculate_data_offspring(self, inst: IRInstruction): self.data_offspring[inst] |= res return self.data_offspring[inst] + + def _calculate_effective_reaching_defs(self): + self.effective_reaching_defs = {} + self.defs_to_uses: Dict[MemoryDef, List[MemoryUse]] = {} + for mem_use in self.mem_ssa.get_memory_uses(): + if mem_use.inst.opcode != "mload": + continue + if isinstance(mem_use.inst.operands[0], IRVariable): + continue + mem_def = self._walk_for_effective_reaching_def(mem_use.reaching_def, mem_use.loc, OrderedSet()) + self.effective_reaching_defs[mem_use] = mem_def + if mem_def not in self.defs_to_uses: + self.defs_to_uses[mem_def] = [] + self.defs_to_uses[mem_def].append(mem_use) + + + def _walk_for_effective_reaching_def(self, current: MemoryUse, query_loc: MemoryLocation, visited: OrderedSet[MemoryUse]) -> MemoryUse: + while current is not None: + if current in visited: + break + visited.add(current) + + if isinstance(current, MemoryDef): + if self.mem_ssa.memalias.may_alias(query_loc, current.loc): + return current + if isinstance(current, MemoryPhi): + reaching_defs = [] + for access, _ in current.operands: + reaching_def = self._walk_for_effective_reaching_def(access, query_loc, visited) + if reaching_def: + reaching_defs.append(reaching_def) + if len(reaching_defs) == 1: + return reaching_defs[0] + return current + + current = current.reaching_def + + return MemSSAAbstract.live_on_entry From 9297d6cb9263affe7b312f0b1b704f7b9ef54293 Mon Sep 17 00:00:00 2001 From: Harry Kalogirou Date: Fri, 30 May 2025 12:31:18 +0300 Subject: [PATCH 2/5] cleanup --- vyper/venom/passes/dft.py | 38 +++++++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/vyper/venom/passes/dft.py b/vyper/venom/passes/dft.py index 12468ebc65..8758025c89 100644 --- a/vyper/venom/passes/dft.py +++ b/vyper/venom/passes/dft.py @@ -1,13 +1,20 @@ from collections import defaultdict -from typing import Dict, List +from typing import Dict, List, Optional -from vyper.venom.analysis.mem_ssa import MemSSA, MemSSAAbstract, MemoryDef, MemoryPhi, MemoryUse -from vyper.venom.memory_location import MemoryLocation import vyper.venom.effects as effects from vyper.utils import OrderedSet from vyper.venom.analysis import DFGAnalysis, LivenessAnalysis +from vyper.venom.analysis.mem_ssa import ( + MemoryAccess, + MemoryDef, + MemoryPhi, + MemoryUse, + MemSSA, + MemSSAAbstract, +) from vyper.venom.basicblock import IRBasicBlock, IRInstruction, IRVariable from vyper.venom.function import IRFunction +from vyper.venom.memory_location import MemoryLocation from vyper.venom.passes.base_pass import IRPass @@ -122,8 +129,11 @@ def _calculate_dependency_graphs(self, bb: IRBasicBlock) -> None: mem_use = self.mem_ssa.get_memory_use(inst) mem_def = self.effective_reaching_defs.get(mem_use, None) if mem_def is not None: - self.eda[inst].add(mem_def.inst) - elif effects.MEMORY in last_write_effects and last_write_effects[effects.MEMORY] != inst: + self.eda[inst].add(mem_def.inst) + elif ( + effects.MEMORY in last_write_effects + and last_write_effects[effects.MEMORY] != inst + ): self.eda[inst].add(last_write_effects[effects.MEMORY]) continue @@ -145,7 +155,7 @@ def _calculate_data_offspring(self, inst: IRInstruction): self.data_offspring[inst] |= res return self.data_offspring[inst] - + def _calculate_effective_reaching_defs(self): self.effective_reaching_defs = {} self.defs_to_uses: Dict[MemoryDef, List[MemoryUse]] = {} @@ -154,21 +164,26 @@ def _calculate_effective_reaching_defs(self): continue if isinstance(mem_use.inst.operands[0], IRVariable): continue - mem_def = self._walk_for_effective_reaching_def(mem_use.reaching_def, mem_use.loc, OrderedSet()) + mem_def = self._walk_for_effective_reaching_def( + mem_use.reaching_def, mem_use.loc, OrderedSet() + ) self.effective_reaching_defs[mem_use] = mem_def if mem_def not in self.defs_to_uses: self.defs_to_uses[mem_def] = [] self.defs_to_uses[mem_def].append(mem_use) - - def _walk_for_effective_reaching_def(self, current: MemoryUse, query_loc: MemoryLocation, visited: OrderedSet[MemoryUse]) -> MemoryUse: + def _walk_for_effective_reaching_def( + self, mem_access: MemoryAccess, query_loc: MemoryLocation, visited: OrderedSet[MemoryAccess] + ) -> Optional[MemoryDef]: + current: Optional[MemoryAccess] = mem_access while current is not None: if current in visited: break visited.add(current) - + if isinstance(current, MemoryDef): if self.mem_ssa.memalias.may_alias(query_loc, current.loc): + assert isinstance(current, MemoryDef) return current if isinstance(current, MemoryPhi): reaching_defs = [] @@ -178,8 +193,9 @@ def _walk_for_effective_reaching_def(self, current: MemoryUse, query_loc: Memory reaching_defs.append(reaching_def) if len(reaching_defs) == 1: return reaching_defs[0] + assert isinstance(current, MemoryDef) return current - + current = current.reaching_def return MemSSAAbstract.live_on_entry From 822e7259209519bfece4508593cfaa89ed0a47cc Mon Sep 17 00:00:00 2001 From: Harry Kalogirou Date: Sun, 1 Jun 2025 17:51:43 +0300 Subject: [PATCH 3/5] live_on_entry type acrobatics --- vyper/venom/analysis/mem_ssa.py | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/vyper/venom/analysis/mem_ssa.py b/vyper/venom/analysis/mem_ssa.py index c1db8789ba..25cb063b18 100644 --- a/vyper/venom/analysis/mem_ssa.py +++ b/vyper/venom/analysis/mem_ssa.py @@ -60,15 +60,6 @@ def __hash__(self) -> int: def __repr__(self) -> str: return f"{self.__class__.__name__}({self.id_str})" - -class LiveOnEntry(MemoryAccess): - """ - For type checking purposes - """ - - pass - - class MemoryDef(MemoryAccess): """Represents a definition of memory state""" @@ -103,6 +94,15 @@ def __init__(self, id: int, block: IRBasicBlock): self.block = block self.operands: list[tuple[MemoryPhiOperand, IRBasicBlock]] = [] +class LiveOnEntry(MemoryDef): + """ + For type checking purposes + """ + + def __init__(self, id: int, addr_space: AddrSpace): + super().__init__(id, IRInstruction("nop", []), addr_space) + + # Type aliases for signatures in this module MemoryDefOrUse = MemoryDef | MemoryUse @@ -123,6 +123,7 @@ class MemSSAAbstract(IRAnalysis): addr_space: AddrSpace mem_alias_type: type[MemoryAliasAnalysisAbstract] + live_on_entry: LiveOnEntry = None def __init__(self, analyses_cache, function): super().__init__(analyses_cache, function) @@ -130,7 +131,7 @@ def __init__(self, analyses_cache, function): self.next_id = 1 # Start from 1 since 0 will be live_on_entry # live_on_entry node - self.live_on_entry = LiveOnEntry(0) + self.live_on_entry = LiveOnEntry(0, self.addr_space) self.memory_defs: dict[IRBasicBlock, list[MemoryDef]] = {} self.memory_uses: dict[IRBasicBlock, list[MemoryUse]] = {} From 3b7cdb89c3ffa8a153feb0a94bd1a132667ffc6d Mon Sep 17 00:00:00 2001 From: Harry Kalogirou Date: Sun, 1 Jun 2025 19:59:09 +0300 Subject: [PATCH 4/5] wip --- vyper/venom/passes/dft.py | 46 ++++++++++++++++++++++++--------------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/vyper/venom/passes/dft.py b/vyper/venom/passes/dft.py index 8758025c89..af1b226e46 100644 --- a/vyper/venom/passes/dft.py +++ b/vyper/venom/passes/dft.py @@ -5,6 +5,7 @@ from vyper.utils import OrderedSet from vyper.venom.analysis import DFGAnalysis, LivenessAnalysis from vyper.venom.analysis.mem_ssa import ( + LiveOnEntry, MemoryAccess, MemoryDef, MemoryPhi, @@ -125,23 +126,34 @@ def _calculate_dependency_graphs(self, bb: IRBasicBlock) -> None: self.eda[inst].add(last_read_effects[write_effect]) last_write_effects[write_effect] = inst - if inst.opcode == "mload": - mem_use = self.mem_ssa.get_memory_use(inst) - mem_def = self.effective_reaching_defs.get(mem_use, None) - if mem_def is not None: - self.eda[inst].add(mem_def.inst) - elif ( - effects.MEMORY in last_write_effects - and last_write_effects[effects.MEMORY] != inst - ): - self.eda[inst].add(last_write_effects[effects.MEMORY]) - continue - for read_effect in read_effects: + if read_effect == effects.MEMORY: + self._handle_memory_effect(inst, last_write_effects, last_read_effects) + continue if read_effect in last_write_effects and last_write_effects[read_effect] != inst: self.eda[inst].add(last_write_effects[read_effect]) last_read_effects[read_effect] = inst + def _handle_memory_effect( + self, + inst: IRInstruction, + last_write_effects: dict[effects.Effects, IRInstruction], + last_read_effects: dict[effects.Effects, IRInstruction], + ) -> None: + mem_use = self.mem_ssa.get_memory_use(inst) + mem_def = self.effective_reaching_defs.get(mem_use, None) + + if mem_def is not None and isinstance(mem_def, MemoryDef): + if mem_def.inst.parent == inst.parent: + self.eda[inst].add(mem_def.inst) + elif ( + effects.MEMORY in last_write_effects + and last_write_effects[effects.MEMORY] != inst + ): + self.eda[inst].add(last_write_effects[effects.MEMORY]) + + last_read_effects[effects.MEMORY] = inst + def _calculate_data_offspring(self, inst: IRInstruction): if inst in self.data_offspring: return self.data_offspring[inst] @@ -162,8 +174,8 @@ def _calculate_effective_reaching_defs(self): for mem_use in self.mem_ssa.get_memory_uses(): if mem_use.inst.opcode != "mload": continue - if isinstance(mem_use.inst.operands[0], IRVariable): - continue + #if isinstance(mem_use.inst.operands[0], IRVariable): + # continue mem_def = self._walk_for_effective_reaching_def( mem_use.reaching_def, mem_use.loc, OrderedSet() ) @@ -174,7 +186,7 @@ def _calculate_effective_reaching_defs(self): def _walk_for_effective_reaching_def( self, mem_access: MemoryAccess, query_loc: MemoryLocation, visited: OrderedSet[MemoryAccess] - ) -> Optional[MemoryDef]: + ) -> Optional[MemoryDef | MemoryPhi | LiveOnEntry]: current: Optional[MemoryAccess] = mem_access while current is not None: if current in visited: @@ -183,8 +195,8 @@ def _walk_for_effective_reaching_def( if isinstance(current, MemoryDef): if self.mem_ssa.memalias.may_alias(query_loc, current.loc): - assert isinstance(current, MemoryDef) return current + if isinstance(current, MemoryPhi): reaching_defs = [] for access, _ in current.operands: @@ -193,7 +205,7 @@ def _walk_for_effective_reaching_def( reaching_defs.append(reaching_def) if len(reaching_defs) == 1: return reaching_defs[0] - assert isinstance(current, MemoryDef) + return current current = current.reaching_def From 08c582cf8778075b6e12edec473913f4341d07bb Mon Sep 17 00:00:00 2001 From: Harry Kalogirou Date: Sun, 1 Jun 2025 20:05:57 +0300 Subject: [PATCH 5/5] wip --- vyper/venom/passes/dft.py | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/vyper/venom/passes/dft.py b/vyper/venom/passes/dft.py index af1b226e46..6eb3f2dd88 100644 --- a/vyper/venom/passes/dft.py +++ b/vyper/venom/passes/dft.py @@ -12,6 +12,8 @@ MemoryUse, MemSSA, MemSSAAbstract, + StorageSSA, + TransientSSA, ) from vyper.venom.basicblock import IRBasicBlock, IRInstruction, IRVariable from vyper.venom.function import IRFunction @@ -36,8 +38,14 @@ def run_pass(self) -> None: self.visited_instructions: OrderedSet[IRInstruction] = OrderedSet() self.dfg = self.analyses_cache.force_analysis(DFGAnalysis) - self.mem_ssa = self.analyses_cache.force_analysis(MemSSA) - self._calculate_effective_reaching_defs() + self.mem_ssa = { + effects.MEMORY: self.analyses_cache.force_analysis(MemSSA), + effects.STORAGE: self.analyses_cache.force_analysis(StorageSSA), + effects.TRANSIENT: self.analyses_cache.force_analysis(TransientSSA), + } + self._calculate_effective_reaching_defs(effects.MEMORY) + self._calculate_effective_reaching_defs(effects.STORAGE) + self._calculate_effective_reaching_defs(effects.TRANSIENT) for bb in self.function.get_basic_blocks(): self._process_basic_block(bb) @@ -128,7 +136,13 @@ def _calculate_dependency_graphs(self, bb: IRBasicBlock) -> None: for read_effect in read_effects: if read_effect == effects.MEMORY: - self._handle_memory_effect(inst, last_write_effects, last_read_effects) + self._handle_memory_effect(inst, effects.MEMORY, last_write_effects, last_read_effects) + continue + if read_effect == effects.STORAGE: + self._handle_memory_effect(inst, effects.STORAGE, last_write_effects, last_read_effects) + continue + if read_effect == effects.TRANSIENT: + self._handle_memory_effect(inst, effects.TRANSIENT, last_write_effects, last_read_effects) continue if read_effect in last_write_effects and last_write_effects[read_effect] != inst: self.eda[inst].add(last_write_effects[read_effect]) @@ -137,10 +151,11 @@ def _calculate_dependency_graphs(self, bb: IRBasicBlock) -> None: def _handle_memory_effect( self, inst: IRInstruction, + effect: effects.Effects, last_write_effects: dict[effects.Effects, IRInstruction], last_read_effects: dict[effects.Effects, IRInstruction], ) -> None: - mem_use = self.mem_ssa.get_memory_use(inst) + mem_use = self.mem_ssa[effect].get_memory_use(inst) mem_def = self.effective_reaching_defs.get(mem_use, None) if mem_def is not None and isinstance(mem_def, MemoryDef): @@ -168,16 +183,16 @@ def _calculate_data_offspring(self, inst: IRInstruction): return self.data_offspring[inst] - def _calculate_effective_reaching_defs(self): + def _calculate_effective_reaching_defs(self, effect: effects.Effects): self.effective_reaching_defs = {} self.defs_to_uses: Dict[MemoryDef, List[MemoryUse]] = {} - for mem_use in self.mem_ssa.get_memory_uses(): + for mem_use in self.mem_ssa[effect].get_memory_uses(): if mem_use.inst.opcode != "mload": continue #if isinstance(mem_use.inst.operands[0], IRVariable): # continue mem_def = self._walk_for_effective_reaching_def( - mem_use.reaching_def, mem_use.loc, OrderedSet() + mem_use.reaching_def, mem_use.loc, OrderedSet(), effect ) self.effective_reaching_defs[mem_use] = mem_def if mem_def not in self.defs_to_uses: @@ -185,7 +200,7 @@ def _calculate_effective_reaching_defs(self): self.defs_to_uses[mem_def].append(mem_use) def _walk_for_effective_reaching_def( - self, mem_access: MemoryAccess, query_loc: MemoryLocation, visited: OrderedSet[MemoryAccess] + self, mem_access: MemoryAccess, query_loc: MemoryLocation, visited: OrderedSet[MemoryAccess], effect: effects.Effects ) -> Optional[MemoryDef | MemoryPhi | LiveOnEntry]: current: Optional[MemoryAccess] = mem_access while current is not None: @@ -194,13 +209,13 @@ def _walk_for_effective_reaching_def( visited.add(current) if isinstance(current, MemoryDef): - if self.mem_ssa.memalias.may_alias(query_loc, current.loc): + if self.mem_ssa[effect].memalias.may_alias(query_loc, current.loc): return current if isinstance(current, MemoryPhi): reaching_defs = [] for access, _ in current.operands: - reaching_def = self._walk_for_effective_reaching_def(access, query_loc, visited) + reaching_def = self._walk_for_effective_reaching_def(access, query_loc, visited, effect) if reaching_def: reaching_defs.append(reaching_def) if len(reaching_defs) == 1: