From e21f3e8b1f7d5a5b5b40fb73ab4c6a2946e878ef Mon Sep 17 00:00:00 2001 From: Charles Cooper Date: Sat, 28 Sep 2024 11:08:03 -0400 Subject: [PATCH] refactor[venom]: remove `dup_requirements` analysis (#4262) this commit updates `next_liveness` to reflect `out_vars` when the instruction is the last instruction in the basic block. after this change, membership in `dup_requirements` is the same as membership in `next_liveness`, so we can remove the `dup_requirements` analysis. --- vyper/venom/analysis/dup_requirements.py | 15 --------------- vyper/venom/basicblock.py | 2 -- vyper/venom/venom_to_assembly.py | 24 ++++++++++++++---------- 3 files changed, 14 insertions(+), 27 deletions(-) delete mode 100644 vyper/venom/analysis/dup_requirements.py diff --git a/vyper/venom/analysis/dup_requirements.py b/vyper/venom/analysis/dup_requirements.py deleted file mode 100644 index 7afb315035..0000000000 --- a/vyper/venom/analysis/dup_requirements.py +++ /dev/null @@ -1,15 +0,0 @@ -from vyper.utils import OrderedSet -from vyper.venom.analysis.analysis import IRAnalysis - - -class DupRequirementsAnalysis(IRAnalysis): - def analyze(self): - for bb in self.function.get_basic_blocks(): - last_liveness = bb.out_vars - for inst in reversed(bb.instructions): - inst.dup_requirements = OrderedSet() - ops = inst.get_input_variables() - for op in ops: - if op in last_liveness: - inst.dup_requirements.add(op) - last_liveness = inst.liveness diff --git a/vyper/venom/basicblock.py b/vyper/venom/basicblock.py index d6fb9560cd..1199579b3f 100644 --- a/vyper/venom/basicblock.py +++ b/vyper/venom/basicblock.py @@ -209,7 +209,6 @@ class IRInstruction: output: Optional[IROperand] # set of live variables at this instruction liveness: OrderedSet[IRVariable] - dup_requirements: OrderedSet[IRVariable] parent: "IRBasicBlock" fence_id: int annotation: Optional[str] @@ -228,7 +227,6 @@ def __init__( self.operands = list(operands) # in case we get an iterator self.output = output self.liveness = OrderedSet() - self.dup_requirements = OrderedSet() self.fence_id = -1 self.annotation = None self.ast_source = None diff --git a/vyper/venom/venom_to_assembly.py b/vyper/venom/venom_to_assembly.py index 41a76319d7..390fab8e7c 100644 --- a/vyper/venom/venom_to_assembly.py +++ b/vyper/venom/venom_to_assembly.py @@ -12,7 +12,6 @@ ) from vyper.utils import MemoryPositions, OrderedSet from vyper.venom.analysis.analysis import IRAnalysesCache -from vyper.venom.analysis.dup_requirements import DupRequirementsAnalysis from vyper.venom.analysis.liveness import LivenessAnalysis from vyper.venom.basicblock import ( IRBasicBlock, @@ -153,7 +152,6 @@ def generate_evm(self, no_optimize: bool = False) -> list[str]: NormalizationPass(ac, fn).run_pass() self.liveness_analysis = ac.request_analysis(LivenessAnalysis) - ac.request_analysis(DupRequirementsAnalysis) assert fn.normalized, "Non-normalized CFG!" @@ -231,7 +229,12 @@ def _stack_reorder( return cost def _emit_input_operands( - self, assembly: list, inst: IRInstruction, ops: list[IROperand], stack: StackModel + self, + assembly: list, + inst: IRInstruction, + ops: list[IROperand], + stack: StackModel, + next_liveness: OrderedSet[IRVariable], ) -> None: # PRE: we already have all the items on the stack that have # been scheduled to be killed. now it's just a matter of emitting @@ -241,7 +244,7 @@ def _emit_input_operands( # it with something that is wanted if ops and stack.height > 0 and stack.peek(0) not in ops: for op in ops: - if isinstance(op, IRVariable) and op not in inst.dup_requirements: + if isinstance(op, IRVariable) and op not in next_liveness: self.swap_op(assembly, stack, op) break @@ -264,7 +267,7 @@ def _emit_input_operands( stack.push(op) continue - if op in inst.dup_requirements and op not in emitted_ops: + if op in next_liveness and op not in emitted_ops: self.dup_op(assembly, stack, op) if op in emitted_ops: @@ -288,7 +291,9 @@ def _generate_evm_for_basicblock_r( all_insts = sorted(basicblock.instructions, key=lambda x: x.opcode != "param") for i, inst in enumerate(all_insts): - next_liveness = all_insts[i + 1].liveness if i + 1 < len(all_insts) else OrderedSet() + next_liveness = ( + all_insts[i + 1].liveness if i + 1 < len(all_insts) else basicblock.out_vars + ) asm.extend(self._generate_evm_for_instruction(inst, stack, next_liveness)) @@ -327,10 +332,9 @@ def clean_stack_from_cfg_in( self.pop(asm, stack) def _generate_evm_for_instruction( - self, inst: IRInstruction, stack: StackModel, next_liveness: OrderedSet = None + self, inst: IRInstruction, stack: StackModel, next_liveness: OrderedSet ) -> list[str]: assembly: list[str | int] = [] - next_liveness = next_liveness or OrderedSet() opcode = inst.opcode # @@ -375,7 +379,7 @@ def _generate_evm_for_instruction( # example, for `%56 = %label1 %13 %label2 %14`, we will # find an instance of %13 *or* %14 in the stack and replace it with %56. to_be_replaced = stack.peek(depth) - if to_be_replaced in inst.dup_requirements: + if to_be_replaced in next_liveness: # %13/%14 is still live(!), so we make a copy of it self.dup(assembly, stack, depth) stack.poke(0, ret) @@ -390,7 +394,7 @@ def _generate_evm_for_instruction( return apply_line_numbers(inst, assembly) # Step 2: Emit instruction's input operands - self._emit_input_operands(assembly, inst, operands, stack) + self._emit_input_operands(assembly, inst, operands, stack, next_liveness) # Step 3: Reorder stack before join points if opcode == "jmp":