Skip to content

Commit

Permalink
feat[venom]: optimize mem2var and store/variable elimination pass seq…
Browse files Browse the repository at this point in the history
…uences (#4032)

The `Mem2Var` pass needs to operate on SSA form. This commit 
updates the order of passes to accommodate for this. Additionally
we add an extra `StoreElimination` pass after `SCCP` to further
reduce the generated code size.

Additionally, it updates the `RemoveUnusedVariablesPass` to eliminate
all unused variables without the need to be run multiple times.

---------

Co-authored-by: Charles Cooper <[email protected]>
  • Loading branch information
harkal and charles-cooper authored May 20, 2024
1 parent 1cfdfda commit ac19367
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 11 deletions.
3 changes: 2 additions & 1 deletion vyper/venom/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,12 @@ def _run_passes(fn: IRFunction, optimize: OptimizationLevel) -> None:
ac = IRAnalysesCache(fn)

SimplifyCFGPass(ac, fn).run_pass()
Mem2Var(ac, fn).run_pass()
MakeSSA(ac, fn).run_pass()
StoreElimination(ac, fn).run_pass()
Mem2Var(ac, fn).run_pass()
MakeSSA(ac, fn).run_pass()
SCCP(ac, fn).run_pass()
StoreElimination(ac, fn).run_pass()
SimplifyCFGPass(ac, fn).run_pass()
RemoveUnusedVariablesPass(ac, fn).run_pass()
DFTPass(ac, fn).run_pass()
Expand Down
45 changes: 35 additions & 10 deletions vyper/venom/passes/remove_unused_variables.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,47 @@
from vyper.venom.analysis.dfg import DFGAnalysis
from vyper.venom.analysis.liveness import LivenessAnalysis
from vyper.venom.basicblock import IRBasicBlock
from vyper.venom.passes.base_pass import IRPass


class RemoveUnusedVariablesPass(IRPass):
def run_pass(self):
removeList = set()

self.analyses_cache.request_analysis(LivenessAnalysis)

for bb in self.function.get_basic_blocks():
for i, inst in enumerate(bb.instructions[:-1]):
if inst.volatile:
continue
next_liveness = bb.instructions[i + 1].liveness
if (inst.output and inst.output not in next_liveness) or inst.opcode == "nop":
removeList.add(inst)

bb.instructions = [inst for inst in bb.instructions if inst not in removeList]
self._remove_unused_variables(bb)

self.analyses_cache.invalidate_analysis(DFGAnalysis)

def _remove_unused_variables(self, bb: IRBasicBlock):
"""
Remove the instructions of a basicblock that produce output that is never used.
"""
i = 0
while i < len(bb.instructions) - 1:
inst = bb.instructions[i]
i += 1

# Skip volatile instructions
if inst.volatile:
continue

# Skip instructions without output
if inst.output is None:
continue

# Skip instructions that produce output that is used
next_liveness = bb.instructions[i].liveness
if inst.output in next_liveness:
continue

# Remove the rest
del bb.instructions[i - 1]

# backtrack to the *previous* instruction, in case we removed
# an instruction which had prevented the previous instruction
# from being removed
i -= 2

# don't go beyond 0 though
i = max(0, i)

0 comments on commit ac19367

Please sign in to comment.