Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor[venom]: refactor sccp pass to use dfg #4329

Merged
merged 3 commits into from
Oct 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 10 additions & 8 deletions vyper/venom/analysis/dfg.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
from typing import Optional

from vyper.utils import OrderedSet
from vyper.venom.analysis.analysis import IRAnalysesCache, IRAnalysis
from vyper.venom.analysis.liveness import LivenessAnalysis
from vyper.venom.basicblock import IRInstruction, IRVariable
from vyper.venom.function import IRFunction


class DFGAnalysis(IRAnalysis):
_dfg_inputs: dict[IRVariable, list[IRInstruction]]
_dfg_inputs: dict[IRVariable, OrderedSet[IRInstruction]]
_dfg_outputs: dict[IRVariable, IRInstruction]

def __init__(self, analyses_cache: IRAnalysesCache, function: IRFunction):
Expand All @@ -16,19 +17,19 @@ def __init__(self, analyses_cache: IRAnalysesCache, function: IRFunction):
self._dfg_outputs = dict()

# return uses of a given variable
def get_uses(self, op: IRVariable) -> list[IRInstruction]:
return self._dfg_inputs.get(op, [])
def get_uses(self, op: IRVariable) -> OrderedSet[IRInstruction]:
return self._dfg_inputs.get(op, OrderedSet())

# the instruction which produces this variable.
def get_producing_instruction(self, op: IRVariable) -> Optional[IRInstruction]:
return self._dfg_outputs.get(op)

def add_use(self, op: IRVariable, inst: IRInstruction):
uses = self._dfg_inputs.setdefault(op, [])
uses.append(inst)
uses = self._dfg_inputs.setdefault(op, OrderedSet())
uses.add(inst)

def remove_use(self, op: IRVariable, inst: IRInstruction):
uses = self._dfg_inputs.get(op, [])
uses: OrderedSet = self._dfg_inputs.get(op, OrderedSet())
uses.remove(inst)

@property
Expand All @@ -48,10 +49,11 @@ def analyze(self):
res = inst.get_outputs()

for op in operands:
inputs = self._dfg_inputs.setdefault(op, [])
inputs.append(inst)
inputs = self._dfg_inputs.setdefault(op, OrderedSet())
inputs.add(inst)

for op in res: # type: ignore
assert isinstance(op, IRVariable)
self._dfg_outputs[op] = inst

def as_graph(self) -> str:
Expand Down
28 changes: 7 additions & 21 deletions vyper/venom/passes/sccp/sccp.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

from vyper.exceptions import CompilerPanic, StaticAssertionException
from vyper.utils import OrderedSet
from vyper.venom.analysis import CFGAnalysis, DominatorTreeAnalysis, IRAnalysesCache
from vyper.venom.analysis import CFGAnalysis, DFGAnalysis, DominatorTreeAnalysis, IRAnalysesCache
from vyper.venom.basicblock import (
IRBasicBlock,
IRInstruction,
Expand Down Expand Up @@ -51,7 +51,7 @@ class SCCP(IRPass):

fn: IRFunction
dom: DominatorTreeAnalysis
uses: dict[IRVariable, OrderedSet[IRInstruction]]
dfg: DFGAnalysis
lattice: Lattice
work_list: list[WorkListItem]
cfg_in_exec: dict[IRBasicBlock, OrderedSet[IRBasicBlock]]
Expand All @@ -67,14 +67,16 @@ def __init__(self, analyses_cache: IRAnalysesCache, function: IRFunction):
def run_pass(self):
self.fn = self.function
self.dom = self.analyses_cache.request_analysis(DominatorTreeAnalysis)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i was looking at this and i think technically we don't even need dominator analysis, we just use it for dfs walk

self._compute_uses()
self.dfg = self.analyses_cache.request_analysis(DFGAnalysis)
self._calculate_sccp(self.fn.entry)
self._propagate_constants()

if self.cfg_dirty:
self.analyses_cache.force_analysis(CFGAnalysis)
self._fix_phi_nodes()

self.analyses_cache.invalidate_analysis(DFGAnalysis)

def _calculate_sccp(self, entry: IRBasicBlock):
"""
This method is the main entry point for the SCCP algorithm. It
Expand All @@ -92,7 +94,7 @@ def _calculate_sccp(self, entry: IRBasicBlock):
self.work_list.append(FlowWorkItem(dummy, entry))

# Initialize the lattice with TOP values for all variables
for v in self.uses.keys():
for v in self.dfg._dfg_outputs:
self.lattice[v] = LatticeEnum.TOP

# Iterate over the work list until it is empty
Expand Down Expand Up @@ -258,25 +260,9 @@ def _eval(self, inst) -> LatticeItem:
return ret # type: ignore

def _add_ssa_work_items(self, inst: IRInstruction):
for target_inst in self._get_uses(inst.output): # type: ignore
for target_inst in self.dfg.get_uses(inst.output): # type: ignore
self.work_list.append(SSAWorkListItem(target_inst))

def _compute_uses(self):
"""
This method computes the uses for each variable in the IR.
It iterates over the dominator tree and collects all the
instructions that use each variable.
"""
self.uses = {}
for bb in self.dom.dfs_walk:
for var, insts in bb.get_uses().items():
self._get_uses(var).update(insts)

def _get_uses(self, var: IRVariable):
if var not in self.uses:
self.uses[var] = OrderedSet()
return self.uses[var]

def _propagate_constants(self):
"""
This method iterates over the IR and replaces constant values
Expand Down
Loading