From f70ab255be4955372a587bf22e136cd32036c6d0 Mon Sep 17 00:00:00 2001 From: Charles Cooper Date: Sun, 27 Apr 2025 16:11:07 +0200 Subject: [PATCH 1/3] fix[venom]: fix inliner return items this fixes the function inliner returning items. currently in vyper, functions do not return stack items, so there was an assumption that `ret` had no arguments besides the return pc. this commit adds the store instructions so that the return items are set properly. it also fixes a bug in InstUpdater, which is that invoke can have no outputs. --- vyper/venom/passes/function_inliner.py | 11 +++++++++++ vyper/venom/passes/machinery/inst_updater.py | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/vyper/venom/passes/function_inliner.py b/vyper/venom/passes/function_inliner.py index 28f8352c9f..400ced6356 100644 --- a/vyper/venom/passes/function_inliner.py +++ b/vyper/venom/passes/function_inliner.py @@ -141,6 +141,17 @@ def _inline_call_site(self, func: IRFunction, call_site: IRInstruction) -> None: inst.opcode = "store" inst.operands = [inst.operands[0]] elif inst.opcode == "ret": + if call_site.output is not None: + # only handle 1 output from invoke.. the other + # is the return pc. if there are more in the future, + # we need to loop, 1 store for every output. + assert len(inst.operands) == 2, inst + return_value = inst.operands[0] + set_output_inst = IRInstruction( + "store", [return_value], output=call_site.output + ) + call_site_return.insert_instruction(set_output_inst, index=0) + inst.opcode = "jmp" inst.operands = [call_site_return.label] diff --git a/vyper/venom/passes/machinery/inst_updater.py b/vyper/venom/passes/machinery/inst_updater.py index 2a8564a7d4..54d538e1f0 100644 --- a/vyper/venom/passes/machinery/inst_updater.py +++ b/vyper/venom/passes/machinery/inst_updater.py @@ -43,7 +43,7 @@ def update( if isinstance(op, IRVariable): self.dfg.add_use(op, inst) - if opcode in NO_OUTPUT_INSTRUCTIONS: + if opcode in NO_OUTPUT_INSTRUCTIONS and opcode != "invoke": if inst.output is not None: assert new_output is None assert len(uses := self.dfg.get_uses(inst.output)) == 0, (inst, uses) From 9467b7dd762cc7f9c94300b994acd0f90dc4389f Mon Sep 17 00:00:00 2001 From: Charles Cooper Date: Sun, 27 Apr 2025 21:31:51 +0200 Subject: [PATCH 2/3] fix basicblock repr --- vyper/venom/basicblock.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/vyper/venom/basicblock.py b/vyper/venom/basicblock.py index 7e7f1a00fe..ff007f6bc1 100644 --- a/vyper/venom/basicblock.py +++ b/vyper/venom/basicblock.py @@ -802,7 +802,10 @@ def copy(self) -> IRBasicBlock: def __repr__(self) -> str: printer = ir_printer.get() - s = f"{repr(self.label)}: ; OUT={[bb.label for bb in self.out_bbs]}\n" + s = f"{repr(self.label)}:" + if self.is_terminated: + s += f" ; OUT={[bb.label for bb in self.out_bbs]}" + s += "\n" if printer and hasattr(printer, "_pre_block"): s += printer._pre_block(self) for inst in self.instructions: From 24b16f52f8e800b9a767a01f4acc38420696af5a Mon Sep 17 00:00:00 2001 From: Charles Cooper Date: Wed, 28 May 2025 14:01:50 +0200 Subject: [PATCH 3/3] fix last param well-formedness --- vyper/venom/venom_to_assembly.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/vyper/venom/venom_to_assembly.py b/vyper/venom/venom_to_assembly.py index 4c5a2bfcda..b89f6b0688 100644 --- a/vyper/venom/venom_to_assembly.py +++ b/vyper/venom/venom_to_assembly.py @@ -155,6 +155,7 @@ def generate_evm(self, no_optimize: bool = False) -> list[str]: for ctx in self.ctxs: for fn in ctx.functions.values(): + print(fn) ac = IRAnalysesCache(fn) NormalizationPass(ac, fn).run_pass() @@ -283,20 +284,22 @@ def _emit_input_operands( seen.add(op) def _prepare_stack_for_function(self, asm, fn: IRFunction, stack: StackModel): - last_param = None + params = [] for inst in fn.entry.instructions: if inst.opcode != "param": # note: always well defined if the bb is terminated next_liveness = self.liveness.live_vars_at(inst) break - last_param = inst + params.append(inst) assert inst.output is not None # help mypy stack.push(inst.output) + # find live params + live_params = [param for param in params if param in next_liveness] # no params (only applies for global entry function) - if last_param is None: + if len(live_params) == 0: return to_pop: list[IRVariable] = [] @@ -307,6 +310,7 @@ def _prepare_stack_for_function(self, asm, fn: IRFunction, stack: StackModel): self.popmany(asm, to_pop, stack) + last_param = live_params[-1] self._optimistic_swap(asm, last_param, next_liveness, stack) def popmany(self, asm, to_pop: Iterable[IRVariable], stack):