From dab020db2c992ebf86dc544ea83fa4a8baecffc1 Mon Sep 17 00:00:00 2001 From: Harry Kalogirou Date: Fri, 15 Mar 2024 13:08:15 +0200 Subject: [PATCH 1/5] dfg testing --- vyper/venom/analysis.py | 5 +++-- vyper/venom/passes/dft.py | 19 +++++++++++++++++-- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/vyper/venom/analysis.py b/vyper/venom/analysis.py index b3b01eacec..240632d199 100644 --- a/vyper/venom/analysis.py +++ b/vyper/venom/analysis.py @@ -1,3 +1,4 @@ +from typing import Optional from vyper.exceptions import CompilerPanic from vyper.utils import OrderedSet from vyper.venom.basicblock import ( @@ -144,8 +145,8 @@ def get_uses(self, op: IRVariable) -> list[IRInstruction]: return self._dfg_inputs.get(op, []) # the instruction which produces this variable. - def get_producing_instruction(self, op: IRVariable) -> IRInstruction: - return self._dfg_outputs[op] + def get_producing_instruction(self, op: IRVariable) -> Optional[IRInstruction]: + return self._dfg_outputs.get(op) @classmethod def build_dfg(cls, ctx: IRFunction) -> "DFG": diff --git a/vyper/venom/passes/dft.py b/vyper/venom/passes/dft.py index 26994bd27f..9e4ac0a24d 100644 --- a/vyper/venom/passes/dft.py +++ b/vyper/venom/passes/dft.py @@ -6,6 +6,9 @@ # DataFlow Transformation +run = 0 + + class DFTPass(IRPass): def _process_instruction_r(self, bb: IRBasicBlock, inst: IRInstruction): if inst in self.visited_instructions: @@ -18,9 +21,9 @@ def _process_instruction_r(self, bb: IRBasicBlock, inst: IRInstruction): bb.instructions.append(inst) return - for op in inst.get_inputs(): + for op in inst.liveness: target = self.dfg.get_producing_instruction(op) - if target.parent != inst.parent or target.fence_id != inst.fence_id: + if target is None or target.parent != inst.parent or target.fence_id != inst.fence_id: # don't reorder across basic block or fence boundaries continue self._process_instruction_r(bb, target) @@ -41,9 +44,21 @@ def _process_basic_block(self, bb: IRBasicBlock) -> None: for inst in instructions: self._process_instruction_r(bb, inst) + # bb.instructions.append(instructions[-1]) + def _run_pass(self, ctx: IRFunction) -> None: self.ctx = ctx self.dfg = DFG.build_dfg(ctx) + # return + # global run + # if run == 2: + # print(self.dfg) + # # print(self.ctx.as_graph()) + # import sys + + # sys.exit(0) + # run += 1 + self.fence_id = 0 self.visited_instructions: OrderedSet[IRInstruction] = OrderedSet() From 24730adcbea9e6be95631fcffea73cf8870b70fd Mon Sep 17 00:00:00 2001 From: Harry Kalogirou Date: Mon, 18 Mar 2024 20:37:10 +0200 Subject: [PATCH 2/5] optimize double swaps --- vyper/venom/venom_to_assembly.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/vyper/venom/venom_to_assembly.py b/vyper/venom/venom_to_assembly.py index 456e8a11ab..b3161dfb2f 100644 --- a/vyper/venom/venom_to_assembly.py +++ b/vyper/venom/venom_to_assembly.py @@ -530,10 +530,20 @@ def pop(self, assembly, stack, num=1): assembly.extend(["POP"] * num) def swap(self, assembly, stack, depth): + # Swaps of the top is no op if depth == 0: return + + inst = _evm_swap_for(depth) + + # Double swaps cancel each other out + if len(assembly) > 0 and inst == assembly[-1]: + assembly.pop() + stack.swap(depth) + return + stack.swap(depth) - assembly.append(_evm_swap_for(depth)) + assembly.append(inst) def dup(self, assembly, stack, depth): stack.dup(depth) From 1cd994c45ae30891f47edd218f0a267214fc2c13 Mon Sep 17 00:00:00 2001 From: Harry Kalogirou Date: Tue, 19 Mar 2024 11:09:45 +0200 Subject: [PATCH 3/5] cleanup --- vyper/compiler/phases.py | 3 --- vyper/venom/dominators.py | 15 --------------- vyper/venom/passes/dft.py | 15 --------------- vyper/venom/passes/make_ssa.py | 2 -- 4 files changed, 35 deletions(-) diff --git a/vyper/compiler/phases.py b/vyper/compiler/phases.py index abf834946a..d794185195 100644 --- a/vyper/compiler/phases.py +++ b/vyper/compiler/phases.py @@ -114,9 +114,6 @@ def __init__( _ = self._generate_ast # force settings to be calculated - # to force experimental codegen, uncomment: - # self.settings.experimental_codegen = True - @cached_property def source_code(self): return self.file_input.source_code diff --git a/vyper/venom/dominators.py b/vyper/venom/dominators.py index e5b03c1bde..6a7690ffb6 100644 --- a/vyper/venom/dominators.py +++ b/vyper/venom/dominators.py @@ -64,11 +64,6 @@ def _compute_dominators(self): self.dominators[bb] = new_dominators changed = True - # for bb in basic_blocks: - # print(bb.label) - # for dom in self.dominators[bb]: - # print(" ", dom.label) - def _compute_idoms(self): """ Compute immediate dominators @@ -85,11 +80,6 @@ def _compute_idoms(self): for dom, target in self.idoms.items(): self.dominated[target].add(dom) - # for dom, targets in self.dominated.items(): - # print(dom.label) - # for t in targets: - # print(" ", t.label) - def _compute_df(self): """ Compute dominance frontier @@ -105,11 +95,6 @@ def _compute_df(self): self.df[runner].add(bb) runner = self.idoms[runner] - # for bb in self.dfs: - # print(bb.label) - # for df in self.df[bb]: - # print(" ", df.label) - def dominance_frontier(self, basic_blocks: list[IRBasicBlock]) -> OrderedSet[IRBasicBlock]: """ Compute dominance frontier of a set of basic blocks. diff --git a/vyper/venom/passes/dft.py b/vyper/venom/passes/dft.py index 9e4ac0a24d..ba30065ff4 100644 --- a/vyper/venom/passes/dft.py +++ b/vyper/venom/passes/dft.py @@ -5,10 +5,6 @@ from vyper.venom.passes.base_pass import IRPass -# DataFlow Transformation -run = 0 - - class DFTPass(IRPass): def _process_instruction_r(self, bb: IRBasicBlock, inst: IRInstruction): if inst in self.visited_instructions: @@ -44,20 +40,9 @@ def _process_basic_block(self, bb: IRBasicBlock) -> None: for inst in instructions: self._process_instruction_r(bb, inst) - # bb.instructions.append(instructions[-1]) - def _run_pass(self, ctx: IRFunction) -> None: self.ctx = ctx self.dfg = DFG.build_dfg(ctx) - # return - # global run - # if run == 2: - # print(self.dfg) - # # print(self.ctx.as_graph()) - # import sys - - # sys.exit(0) - # run += 1 self.fence_id = 0 self.visited_instructions: OrderedSet[IRInstruction] = OrderedSet() diff --git a/vyper/venom/passes/make_ssa.py b/vyper/venom/passes/make_ssa.py index b28d316d2f..965b0e0945 100644 --- a/vyper/venom/passes/make_ssa.py +++ b/vyper/venom/passes/make_ssa.py @@ -139,8 +139,6 @@ def _remove_degenerate_phis(self, entry: IRBasicBlock): entry.instructions.remove(inst) elif new_ops_len == 2: entry.instructions.remove(inst) - # inst.opcode = "store" - # inst.operands = [new_ops[1]] else: inst.operands = new_ops From 25fa225c71bac8e6f7ae2b8acbaaf913039ee9a0 Mon Sep 17 00:00:00 2001 From: Harry Kalogirou Date: Tue, 19 Mar 2024 15:25:32 +0200 Subject: [PATCH 4/5] cleanup --- vyper/venom/passes/simplify_cfg.py | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/vyper/venom/passes/simplify_cfg.py b/vyper/venom/passes/simplify_cfg.py index e4c3c9a1cd..56816c5324 100644 --- a/vyper/venom/passes/simplify_cfg.py +++ b/vyper/venom/passes/simplify_cfg.py @@ -16,11 +16,13 @@ def _merge_blocks(self, a: IRBasicBlock, b: IRBasicBlock): else: inst.parent = a a.instructions.append(inst) - a.cfg_out = b.cfg_out - for n in b.cfg_out: - n.remove_cfg_in(b) - n.add_cfg_in(a) + # Update CFG + a.cfg_out = b.cfg_out + if len(b.cfg_out) > 0: + next = b.cfg_out.first() + next.remove_cfg_in(b) + next.add_cfg_in(a) self.ctx.basic_blocks.remove(b) @@ -29,20 +31,26 @@ def _merge_jump(self, a: IRBasicBlock, b: IRBasicBlock): jump_inst = a.instructions[-1] assert b.label in jump_inst.operands, f"{b.label} {jump_inst.operands}" jump_inst.operands[jump_inst.operands.index(b.label)] = next.label + + # Update CFG a.remove_cfg_out(b) a.add_cfg_out(next) next.remove_cfg_in(b) next.add_cfg_in(a) + self.ctx.basic_blocks.remove(b) def _collapse_chained_blocks_r(self, bb: IRBasicBlock): + """ + DFS into the cfg and collapse blocks with a single predecessor to the predecessor + """ if len(bb.cfg_out) == 1: next = bb.cfg_out.first() if len(next.cfg_in) == 1: self._merge_blocks(bb, next) self._collapse_chained_blocks_r(bb) return - elif len(bb.cfg_out) == 2: + elif len(bb.cfg_out) > 1: bb_out = bb.cfg_out.copy() for next in bb_out: if len(next.cfg_in) == 1 and len(next.cfg_out) == 1 and len(next.instructions) == 1: @@ -58,6 +66,9 @@ def _collapse_chained_blocks_r(self, bb: IRBasicBlock): self._collapse_chained_blocks_r(bb_out) def _collapse_chained_blocks(self, entry: IRBasicBlock): + """ + Collapse blocks with a single predecessor to their predecessor + """ self.visited = OrderedSet() self._collapse_chained_blocks_r(entry) From 84f1b695101a8ff8fc3f3aa24be01732c8d5252f Mon Sep 17 00:00:00 2001 From: Harry Kalogirou Date: Tue, 19 Mar 2024 15:28:08 +0200 Subject: [PATCH 5/5] docs --- vyper/venom/passes/make_ssa.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/vyper/venom/passes/make_ssa.py b/vyper/venom/passes/make_ssa.py index 965b0e0945..4169451c2e 100644 --- a/vyper/venom/passes/make_ssa.py +++ b/vyper/venom/passes/make_ssa.py @@ -7,6 +7,10 @@ class MakeSSA(IRPass): + """ + This pass converts the function into Static Single Assignment (SSA) form. + """ + dom: DominatorTree defs: dict[IRVariable, OrderedSet[IRBasicBlock]] @@ -29,6 +33,9 @@ def _run_pass(self, ctx: IRFunction, entry: IRBasicBlock) -> int: return 0 def _add_phi_nodes(self): + """ + Add phi nodes to the function. + """ self._compute_defs() self.work = {var: 0 for var in self.dom.dfs} self.has_already = {var: 0 for var in self.dom.dfs} @@ -84,6 +91,9 @@ def _add_phi(self, var: IRVariable, basic_block: IRBasicBlock) -> bool: return True def _rename_vars(self, basic_block: IRBasicBlock): + """ + Rename variables in the basic block. This follows the placement of phi nodes. + """ outs = [] for inst in basic_block.instructions: new_ops = [] @@ -148,6 +158,9 @@ def _remove_degenerate_phis(self, entry: IRBasicBlock): self._remove_degenerate_phis(bb) def _compute_defs(self): + """ + Compute the definition points of variables in the function. + """ self.defs = {} for bb in self.dom.dfs: assignments = bb.get_assignments()