From 312b566605522d620648973e201d36c503412fa3 Mon Sep 17 00:00:00 2001 From: Charles Cooper Date: Wed, 12 Mar 2025 13:45:01 +0100 Subject: [PATCH 01/11] bugfix: remove old inst.output from dfg in InstUpdater.update --- vyper/venom/analysis/dfg.py | 4 +++ vyper/venom/passes/machinery/inst_updater.py | 34 +++++++++++++++++--- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/vyper/venom/analysis/dfg.py b/vyper/venom/analysis/dfg.py index fec8f65e2a..27fe6bbda7 100644 --- a/vyper/venom/analysis/dfg.py +++ b/vyper/venom/analysis/dfg.py @@ -31,8 +31,12 @@ def get_producing_instruction(self, op: IRVariable) -> Optional[IRInstruction]: return self._dfg_outputs.get(op) def set_producing_instruction(self, op: IRVariable, inst: IRInstruction): + # should this check if inst.output is already in dfg_outputs? self._dfg_outputs[op] = inst + def remove_producing_instruction(self, op: IRVariable): + del self._dfg_outputs[op] + def add_use(self, op: IRVariable, inst: IRInstruction): uses = self._dfg_inputs.setdefault(op, OrderedSet()) uses.add(inst) diff --git a/vyper/venom/passes/machinery/inst_updater.py b/vyper/venom/passes/machinery/inst_updater.py index 5c687c4056..547ae16fe1 100644 --- a/vyper/venom/passes/machinery/inst_updater.py +++ b/vyper/venom/passes/machinery/inst_updater.py @@ -1,3 +1,5 @@ +from typing import Optional + from vyper.venom.analysis import DFGAnalysis from vyper.venom.basicblock import NO_OUTPUT_INSTRUCTIONS, IRInstruction, IROperand, IRVariable @@ -16,7 +18,24 @@ def update_operands(self, inst: IRInstruction, replace_dict: dict[IROperand, IRO new_operands = [replace_dict[op] if op in replace_dict else op for op in old_operands] self.update(inst, inst.opcode, new_operands) - def update(self, inst: IRInstruction, opcode: str, new_operands: list[IROperand]): + def update_output(self, inst: IRInstruction, new_output: Optional[IRVariable]): + assert (new_output is None) == (inst.opcode in NO_OUTPUT_INSTRUCTIONS) + + if inst.output is not None: + self.dfg.remove_producing_instruction(inst.output) + + if new_output is not None: + self.dfg.set_producing_instruction(new_output, inst) + + inst.output = new_output + + def update( + self, + inst: IRInstruction, + opcode: str, + new_operands: list[IROperand], + new_output: Optional[IRVariable] = None, + ): assert opcode != "phi" # sanity assert all(isinstance(op, IROperand) for op in new_operands) @@ -33,9 +52,16 @@ def update(self, inst: IRInstruction, opcode: str, new_operands: list[IROperand] if isinstance(op, IRVariable): self.dfg.add_use(op, inst) - if opcode in NO_OUTPUT_INSTRUCTIONS and inst.output is not None: - assert len(uses := self.dfg.get_uses(inst.output)) == 0, (inst, uses) - inst.output = None + if opcode in NO_OUTPUT_INSTRUCTIONS: + if inst.output is not None: + assert new_output is None + assert len(uses := self.dfg.get_uses(inst.output)) == 0, (inst, uses) + # remove from dfg + self.update_output(inst, None) + else: + # new_output is None is sentinel meaning "no change" + if new_output is not None: + self.update_output(inst, new_output) inst.opcode = opcode inst.operands = new_operands From 8271d47b44cbc4a1ea3e2a84b2d738fa2c2dd1ca Mon Sep 17 00:00:00 2001 From: Charles Cooper Date: Wed, 12 Mar 2025 14:04:09 +0100 Subject: [PATCH 02/11] update load elimination: don't invalidate dfg --- vyper/venom/passes/load_elimination.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/vyper/venom/passes/load_elimination.py b/vyper/venom/passes/load_elimination.py index 9b3a18fba0..89a4a3191d 100644 --- a/vyper/venom/passes/load_elimination.py +++ b/vyper/venom/passes/load_elimination.py @@ -36,7 +36,6 @@ def run_pass(self): self._process_bb(bb, None, "calldataload", None) self.analyses_cache.invalidate_analysis(LivenessAnalysis) - self.analyses_cache.invalidate_analysis(DFGAnalysis) def equivalent(self, op1, op2): return op1 == op2 @@ -72,8 +71,7 @@ def _handle_load(self, inst): self._lattice[ptr] = inst.output if existing_value is not None: - inst.opcode = "store" - inst.operands = [existing_value] + self.updater.store(inst, existing_value) def _handle_store(self, inst, store_opcode): # mstore [val, ptr] From adeebd141f63a80816f276360a0dee37b056ed5f Mon Sep 17 00:00:00 2001 From: Charles Cooper Date: Wed, 12 Mar 2025 20:40:32 +0100 Subject: [PATCH 03/11] update branch optimizer: don't invalidate dfg --- vyper/venom/analysis/cfg.py | 4 +++- vyper/venom/passes/branch_optimization.py | 14 +++++++------- vyper/venom/passes/simplify_cfg.py | 3 ++- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/vyper/venom/analysis/cfg.py b/vyper/venom/analysis/cfg.py index 77aa2cb362..a7336ac0af 100644 --- a/vyper/venom/analysis/cfg.py +++ b/vyper/venom/analysis/cfg.py @@ -75,7 +75,9 @@ def invalidate(self): bb.cfg_out = OrderedSet() bb.out_vars = OrderedSet() + # just to be on the safe side, but this is probably not needed. + self.analyses_cache.invalidate_analysis(DFGAnalysis) + self.analyses_cache.invalidate_analysis(DominatorTreeAnalysis) self.analyses_cache.invalidate_analysis(LivenessAnalysis) - self.analyses_cache.invalidate_analysis(DFGAnalysis) self._dfs = None diff --git a/vyper/venom/passes/branch_optimization.py b/vyper/venom/passes/branch_optimization.py index 920dc5e431..140c7f8ae3 100644 --- a/vyper/venom/passes/branch_optimization.py +++ b/vyper/venom/passes/branch_optimization.py @@ -1,6 +1,5 @@ from vyper.venom.analysis import CFGAnalysis, DFGAnalysis, LivenessAnalysis -from vyper.venom.basicblock import IRInstruction -from vyper.venom.passes.base_pass import IRPass +from vyper.venom.passes.base_pass import InstUpdater, IRPass class BranchOptimizationPass(IRPass): @@ -26,17 +25,18 @@ def _optimize_branches(self) -> None: prev_inst = self.dfg.get_producing_instruction(cond) if cost_a >= cost_b and prev_inst.opcode == "iszero": new_cond = prev_inst.operands[0] - term_inst.operands = [new_cond, term_inst.operands[2], term_inst.operands[1]] + new_operands = [new_cond, term_inst.operands[2], term_inst.operands[1]] + self.updater.update(term_inst, term_inst.opcode, new_operands) elif cost_a > cost_b: - new_cond = fn.get_next_variable() - inst = IRInstruction("iszero", [term_inst.operands[0]], output=new_cond) - bb.insert_instruction(inst, index=-1) - term_inst.operands = [new_cond, term_inst.operands[2], term_inst.operands[1]] + new_cond = self.updater.add_before(term_inst, "iszero", [term_inst.operands[0]]) + new_operands = [new_cond, term_inst.operands[2], term_inst.operands[1]] + self.updater.update(term_inst, term_inst.opcode, new_operands) def run_pass(self): self.liveness = self.analyses_cache.request_analysis(LivenessAnalysis) self.cfg = self.analyses_cache.request_analysis(CFGAnalysis) self.dfg = self.analyses_cache.request_analysis(DFGAnalysis) + self.updater = InstUpdater(self.dfg) self._optimize_branches() diff --git a/vyper/venom/passes/simplify_cfg.py b/vyper/venom/passes/simplify_cfg.py index 10535c2144..307be30e52 100644 --- a/vyper/venom/passes/simplify_cfg.py +++ b/vyper/venom/passes/simplify_cfg.py @@ -1,6 +1,6 @@ from vyper.exceptions import CompilerPanic from vyper.utils import OrderedSet -from vyper.venom.analysis import CFGAnalysis +from vyper.venom.analysis import CFGAnalysis, DFGAnalysis from vyper.venom.basicblock import IRBasicBlock, IRLabel from vyper.venom.passes.base_pass import IRPass @@ -138,3 +138,4 @@ def run_pass(self): raise CompilerPanic("Too many iterations collapsing chained blocks") self.analyses_cache.invalidate_analysis(CFGAnalysis) + self.analyses_cache.invalidate_analysis(DFGAnalysis) From 2f26dba5093186289cfc1d87b3bc6e6ad598427a Mon Sep 17 00:00:00 2001 From: Charles Cooper Date: Wed, 12 Mar 2025 20:54:32 +0100 Subject: [PATCH 04/11] fix: invalidate dfg after MakeSSA --- vyper/venom/passes/make_ssa.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/vyper/venom/passes/make_ssa.py b/vyper/venom/passes/make_ssa.py index 6b404da407..36e0adb0a7 100644 --- a/vyper/venom/passes/make_ssa.py +++ b/vyper/venom/passes/make_ssa.py @@ -1,5 +1,5 @@ from vyper.utils import OrderedSet -from vyper.venom.analysis import CFGAnalysis, DominatorTreeAnalysis, LivenessAnalysis +from vyper.venom.analysis import CFGAnalysis, DominatorTreeAnalysis, LivenessAnalysis, DFGAnalysis from vyper.venom.basicblock import IRBasicBlock, IRInstruction, IROperand, IRVariable from vyper.venom.passes.base_pass import IRPass @@ -29,6 +29,7 @@ def run_pass(self): self._remove_degenerate_phis(fn.entry) self.analyses_cache.invalidate_analysis(LivenessAnalysis) + self.analyses_cache.invalidate_analysis(DFGAnalysis) def _add_phi_nodes(self): """ From a69910d0c6fca7fb814413bbbdc53b49ad7101ad Mon Sep 17 00:00:00 2001 From: Charles Cooper Date: Wed, 12 Mar 2025 20:55:05 +0100 Subject: [PATCH 05/11] add new_output= kwarg to updater.store() --- vyper/venom/passes/machinery/inst_updater.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/vyper/venom/passes/machinery/inst_updater.py b/vyper/venom/passes/machinery/inst_updater.py index 547ae16fe1..296f1ea8cc 100644 --- a/vyper/venom/passes/machinery/inst_updater.py +++ b/vyper/venom/passes/machinery/inst_updater.py @@ -52,6 +52,9 @@ def update( if isinstance(op, IRVariable): self.dfg.add_use(op, inst) + inst.opcode = opcode + inst.operands = new_operands + if opcode in NO_OUTPUT_INSTRUCTIONS: if inst.output is not None: assert new_output is None @@ -63,9 +66,6 @@ def update( if new_output is not None: self.update_output(inst, new_output) - inst.opcode = opcode - inst.operands = new_operands - def nop(self, inst: IRInstruction): inst.annotation = str(inst) # copy IRInstruction.make_nop() self.update(inst, "nop", []) @@ -74,8 +74,8 @@ def remove(self, inst: IRInstruction): self.nop(inst) # for dfg updates and checks inst.parent.remove_instruction(inst) - def store(self, inst: IRInstruction, op: IROperand): - self.update(inst, "store", [op]) + def store(self, inst: IRInstruction, op: IROperand, new_output:Optional[IRVariable]=None): + self.update(inst, "store", [op], new_output=new_output) def add_before(self, inst: IRInstruction, opcode: str, args: list[IROperand]) -> IRVariable: """ From f0e85e4be123a1e9e74de53ca81d0aa477d9f9e6 Mon Sep 17 00:00:00 2001 From: Charles Cooper Date: Wed, 12 Mar 2025 20:55:21 +0100 Subject: [PATCH 06/11] add support for NO_OUTPUT_INSTRUCTIONS in add_before --- vyper/venom/passes/machinery/inst_updater.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/vyper/venom/passes/machinery/inst_updater.py b/vyper/venom/passes/machinery/inst_updater.py index 296f1ea8cc..ef6d509cd0 100644 --- a/vyper/venom/passes/machinery/inst_updater.py +++ b/vyper/venom/passes/machinery/inst_updater.py @@ -83,13 +83,17 @@ def add_before(self, inst: IRInstruction, opcode: str, args: list[IROperand]) -> """ assert opcode != "phi" index = inst.parent.instructions.index(inst) - var = inst.parent.parent.get_next_variable() + + var = None + if opcode not in NO_OUTPUT_INSTRUCTIONS: + var = inst.parent.parent.get_next_variable() + operands = list(args) - # TODO: add support for NO_OUTPUT_INSTRUCTIONS new_inst = IRInstruction(opcode, operands, output=var) inst.parent.insert_instruction(new_inst, index) for op in new_inst.operands: if isinstance(op, IRVariable): self.dfg.add_use(op, new_inst) - self.dfg.set_producing_instruction(var, new_inst) + if var is not None: + self.dfg.set_producing_instruction(var, new_inst) return var From f4858f4aa435735f8e54779419c3abf5ac04ba05 Mon Sep 17 00:00:00 2001 From: Charles Cooper Date: Wed, 12 Mar 2025 20:55:34 +0100 Subject: [PATCH 07/11] update mem2var: don't invalidate dfg --- vyper/venom/passes/mem2var.py | 34 ++++++++++++---------------------- 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/vyper/venom/passes/mem2var.py b/vyper/venom/passes/mem2var.py index 1ecf25c468..03d54ef126 100644 --- a/vyper/venom/passes/mem2var.py +++ b/vyper/venom/passes/mem2var.py @@ -2,7 +2,7 @@ from vyper.venom.analysis import CFGAnalysis, DFGAnalysis, LivenessAnalysis from vyper.venom.basicblock import IRInstruction, IRVariable from vyper.venom.function import IRFunction -from vyper.venom.passes.base_pass import IRPass +from vyper.venom.passes.base_pass import IRPass, InstUpdater class Mem2Var(IRPass): @@ -16,15 +16,15 @@ class Mem2Var(IRPass): def run_pass(self): self.analyses_cache.request_analysis(CFGAnalysis) dfg = self.analyses_cache.request_analysis(DFGAnalysis) + self.updater = InstUpdater(dfg) self.var_name_count = 0 - for var, inst in dfg.outputs.items(): + for var, inst in dfg.outputs.copy().items(): if inst.opcode == "alloca": self._process_alloca_var(dfg, inst, var) elif inst.opcode == "palloca": self._process_palloca_var(dfg, inst, var) - self.analyses_cache.invalidate_analysis(DFGAnalysis) self.analyses_cache.invalidate_analysis(LivenessAnalysis) def _mk_varname(self, varname: str, alloca_id: int): @@ -46,20 +46,13 @@ def _process_alloca_var(self, dfg: DFGAnalysis, alloca_inst, var: IRVariable): alloca_id = alloca_inst.operands[2] var_name = self._mk_varname(var.value, alloca_id.value) var = IRVariable(var_name) - for inst in uses: + for inst in uses.copy(): if inst.opcode == "mstore": - inst.opcode = "store" - inst.output = var - inst.operands = [inst.operands[0]] + self.updater.store(inst, inst.operands[0], new_output=var) elif inst.opcode == "mload": - inst.opcode = "store" - inst.operands = [var] + self.updater.store(inst, var) elif inst.opcode == "return": - bb = inst.parent - idx = len(bb.instructions) - 1 - assert inst == bb.instructions[idx] # sanity - new_inst = IRInstruction("mstore", [var, inst.operands[1]]) - bb.insert_instruction(new_inst, idx) + self.updater.add_before(inst, "mstore", [var, inst.operands[1]]) def _process_palloca_var(self, dfg: DFGAnalysis, palloca_inst: IRInstruction, var: IRVariable): """ @@ -75,15 +68,12 @@ def _process_palloca_var(self, dfg: DFGAnalysis, palloca_inst: IRInstruction, va var = IRVariable(var_name) # some value given to us by the calling convention - palloca_inst.opcode = "mload" - palloca_inst.operands = [ofst] - palloca_inst.output = var + # TODO: maybe better to not touch the palloca instruction, + # and instead "add_after" the palloca instruction. + self.updater.update(palloca_inst, "mload", [ofst], new_output=var) for inst in uses: if inst.opcode == "mstore": - inst.opcode = "store" - inst.output = var - inst.operands = [inst.operands[0]] + self.updater.store(inst, inst.operands[0], new_output=var) elif inst.opcode == "mload": - inst.opcode = "store" - inst.operands = [var] + self.updater.store(inst, var) From 8508c3c021c859043b789505671770a08b624243 Mon Sep 17 00:00:00 2001 From: Charles Cooper Date: Wed, 12 Mar 2025 20:57:13 +0100 Subject: [PATCH 08/11] fix uses copy --- vyper/venom/passes/mem2var.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vyper/venom/passes/mem2var.py b/vyper/venom/passes/mem2var.py index 03d54ef126..802657f1ba 100644 --- a/vyper/venom/passes/mem2var.py +++ b/vyper/venom/passes/mem2var.py @@ -72,7 +72,7 @@ def _process_palloca_var(self, dfg: DFGAnalysis, palloca_inst: IRInstruction, va # and instead "add_after" the palloca instruction. self.updater.update(palloca_inst, "mload", [ofst], new_output=var) - for inst in uses: + for inst in uses.copy(): if inst.opcode == "mstore": self.updater.store(inst, inst.operands[0], new_output=var) elif inst.opcode == "mload": From 3eacd7ec1c47cde23cca271a12412978aaa81689 Mon Sep 17 00:00:00 2001 From: Charles Cooper Date: Wed, 12 Mar 2025 21:06:30 +0100 Subject: [PATCH 09/11] fix lint --- vyper/venom/passes/algebraic_optimization.py | 2 ++ vyper/venom/passes/machinery/inst_updater.py | 6 ++++-- vyper/venom/passes/make_ssa.py | 2 +- vyper/venom/passes/mem2var.py | 2 +- vyper/venom/passes/memmerging.py | 2 ++ 5 files changed, 10 insertions(+), 4 deletions(-) diff --git a/vyper/venom/passes/algebraic_optimization.py b/vyper/venom/passes/algebraic_optimization.py index 37807e02f3..36ca399ed0 100644 --- a/vyper/venom/passes/algebraic_optimization.py +++ b/vyper/venom/passes/algebraic_optimization.py @@ -284,6 +284,7 @@ def _handle_inst_peephole(self, inst: IRInstruction): # (saves codesize, not gas) if lit_eq(operands[0], -1): var = self.updater.add_before(inst, "not", [operands[1]]) + assert var is not None # help mypy self.updater.update(inst, "iszero", [var]) return @@ -291,6 +292,7 @@ def _handle_inst_peephole(self, inst: IRInstruction): # (eq x y) has the same truthyness as (iszero (xor x y)) tmp = self.updater.add_before(inst, "xor", [operands[0], operands[1]]) + assert tmp is not None # help mypy self.updater.update(inst, "iszero", [tmp]) return diff --git a/vyper/venom/passes/machinery/inst_updater.py b/vyper/venom/passes/machinery/inst_updater.py index ef6d509cd0..5dde1e51bb 100644 --- a/vyper/venom/passes/machinery/inst_updater.py +++ b/vyper/venom/passes/machinery/inst_updater.py @@ -74,10 +74,12 @@ def remove(self, inst: IRInstruction): self.nop(inst) # for dfg updates and checks inst.parent.remove_instruction(inst) - def store(self, inst: IRInstruction, op: IROperand, new_output:Optional[IRVariable]=None): + def store(self, inst: IRInstruction, op: IROperand, new_output: Optional[IRVariable] = None): self.update(inst, "store", [op], new_output=new_output) - def add_before(self, inst: IRInstruction, opcode: str, args: list[IROperand]) -> IRVariable: + def add_before( + self, inst: IRInstruction, opcode: str, args: list[IROperand] + ) -> Optional[IRVariable]: """ Insert another instruction before the given instruction """ diff --git a/vyper/venom/passes/make_ssa.py b/vyper/venom/passes/make_ssa.py index 36e0adb0a7..c148d5e64a 100644 --- a/vyper/venom/passes/make_ssa.py +++ b/vyper/venom/passes/make_ssa.py @@ -1,5 +1,5 @@ from vyper.utils import OrderedSet -from vyper.venom.analysis import CFGAnalysis, DominatorTreeAnalysis, LivenessAnalysis, DFGAnalysis +from vyper.venom.analysis import CFGAnalysis, DFGAnalysis, DominatorTreeAnalysis, LivenessAnalysis from vyper.venom.basicblock import IRBasicBlock, IRInstruction, IROperand, IRVariable from vyper.venom.passes.base_pass import IRPass diff --git a/vyper/venom/passes/mem2var.py b/vyper/venom/passes/mem2var.py index 802657f1ba..a2bec97fa3 100644 --- a/vyper/venom/passes/mem2var.py +++ b/vyper/venom/passes/mem2var.py @@ -2,7 +2,7 @@ from vyper.venom.analysis import CFGAnalysis, DFGAnalysis, LivenessAnalysis from vyper.venom.basicblock import IRInstruction, IRVariable from vyper.venom.function import IRFunction -from vyper.venom.passes.base_pass import IRPass, InstUpdater +from vyper.venom.passes.base_pass import InstUpdater, IRPass class Mem2Var(IRPass): diff --git a/vyper/venom/passes/memmerging.py b/vyper/venom/passes/memmerging.py index 3f7a8a3559..4982e074a9 100644 --- a/vyper/venom/passes/memmerging.py +++ b/vyper/venom/passes/memmerging.py @@ -137,6 +137,7 @@ def _optimize_copy(self, bb: IRBasicBlock, copy_opcode: str, load_opcode: str): # we are converting an mcopy into an mload+mstore (mload+mstore # is 1 byte smaller than mcopy). val = self.updater.add_before(inst, load_opcode, [IRLiteral(copy.src)]) + assert val is not None # help mypy self.updater.update(inst, "mstore", [val, IRLiteral(copy.dst)]) for inst in copy.insts[:-1]: @@ -292,6 +293,7 @@ def _optimize_memzero(self, bb: IRBasicBlock): self.updater.update(inst, "mstore", new_ops) else: calldatasize = self.updater.add_before(inst, "calldatasize", []) + assert calldatasize is not None # help mypy new_ops = [IRLiteral(copy.length), calldatasize, IRLiteral(copy.dst)] self.updater.update(inst, "calldatacopy", new_ops) From a5f20f39d0c8cb200481b536fb92511333f58c0a Mon Sep 17 00:00:00 2001 From: Charles Cooper Date: Fri, 14 Mar 2025 12:17:58 +0100 Subject: [PATCH 10/11] remove update_output method --- vyper/venom/passes/machinery/inst_updater.py | 27 +++++++------------- 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/vyper/venom/passes/machinery/inst_updater.py b/vyper/venom/passes/machinery/inst_updater.py index 5dde1e51bb..35728809c2 100644 --- a/vyper/venom/passes/machinery/inst_updater.py +++ b/vyper/venom/passes/machinery/inst_updater.py @@ -18,17 +18,6 @@ def update_operands(self, inst: IRInstruction, replace_dict: dict[IROperand, IRO new_operands = [replace_dict[op] if op in replace_dict else op for op in old_operands] self.update(inst, inst.opcode, new_operands) - def update_output(self, inst: IRInstruction, new_output: Optional[IRVariable]): - assert (new_output is None) == (inst.opcode in NO_OUTPUT_INSTRUCTIONS) - - if inst.output is not None: - self.dfg.remove_producing_instruction(inst.output) - - if new_output is not None: - self.dfg.set_producing_instruction(new_output, inst) - - inst.output = new_output - def update( self, inst: IRInstruction, @@ -52,19 +41,21 @@ def update( if isinstance(op, IRVariable): self.dfg.add_use(op, inst) - inst.opcode = opcode - inst.operands = new_operands - if opcode in NO_OUTPUT_INSTRUCTIONS: if inst.output is not None: assert new_output is None assert len(uses := self.dfg.get_uses(inst.output)) == 0, (inst, uses) - # remove from dfg - self.update_output(inst, None) + self.dfg.remove_producing_instruction(inst.output) + inst.output = None else: # new_output is None is sentinel meaning "no change" - if new_output is not None: - self.update_output(inst, new_output) + if new_output is not None and new_output != inst.output: + self.dfg.remove_producing_instruction(inst.output) + self.dfg.set_producing_instruction(new_output, inst) + inst.output = new_output + + inst.opcode = opcode + inst.operands = new_operands def nop(self, inst: IRInstruction): inst.annotation = str(inst) # copy IRInstruction.make_nop() From 7439c6f33e2f33a6a928bff2efd5f53acc8f1980 Mon Sep 17 00:00:00 2001 From: Charles Cooper Date: Mon, 17 Mar 2025 19:24:17 +0100 Subject: [PATCH 11/11] fix for no output instructions --- vyper/venom/passes/machinery/inst_updater.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/vyper/venom/passes/machinery/inst_updater.py b/vyper/venom/passes/machinery/inst_updater.py index 35728809c2..b841b2f7f9 100644 --- a/vyper/venom/passes/machinery/inst_updater.py +++ b/vyper/venom/passes/machinery/inst_updater.py @@ -50,7 +50,8 @@ def update( else: # new_output is None is sentinel meaning "no change" if new_output is not None and new_output != inst.output: - self.dfg.remove_producing_instruction(inst.output) + if inst.output is not None: + self.dfg.remove_producing_instruction(inst.output) self.dfg.set_producing_instruction(new_output, inst) inst.output = new_output