From bee2416a32bdd377cda1132810f353e67460477b Mon Sep 17 00:00:00 2001 From: Harry Kalogirou Date: Mon, 15 Jan 2024 08:40:24 +0200 Subject: [PATCH 01/19] Generalize the conversion of IR nodes to venom Make the functions handle the case there is no deploy instructions --- vyper/venom/__init__.py | 6 ++++-- vyper/venom/ir_node_to_venom.py | 13 ++++++++----- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/vyper/venom/__init__.py b/vyper/venom/__init__.py index 570aba771a..07c2011e22 100644 --- a/vyper/venom/__init__.py +++ b/vyper/venom/__init__.py @@ -65,7 +65,9 @@ def generate_ir(ir: IRnode, optimize: OptimizationLevel) -> tuple[IRFunction, IR # Convert "old" IR to "new" IR ctx, ctx_runtime = convert_ir_basicblock(ir) - _run_passes(ctx, optimize) - _run_passes(ctx_runtime, optimize) + if ctx: + _run_passes(ctx, optimize) + if ctx_runtime: + _run_passes(ctx_runtime, optimize) return ctx, ctx_runtime diff --git a/vyper/venom/ir_node_to_venom.py b/vyper/venom/ir_node_to_venom.py index c86d3a3d67..10a994b5ba 100644 --- a/vyper/venom/ir_node_to_venom.py +++ b/vyper/venom/ir_node_to_venom.py @@ -99,14 +99,17 @@ def _findIRnode(ir: IRnode, value: str) -> Optional[IRnode]: def convert_ir_basicblock(ir: IRnode) -> tuple[IRFunction, IRFunction]: + runtime_ir = ir deploy_ir = _findIRnode(ir, "deploy") - assert deploy_ir is not None - deploy_venom = IRFunction() - _convert_ir_basicblock(deploy_venom, ir, {}, OrderedSet(), {}) - deploy_venom.get_basic_block().append_instruction("stop") + deploy_venom = None + if deploy_ir is not None: + deploy_venom = IRFunction() + _convert_ir_basicblock(deploy_venom, ir, {}, OrderedSet(), {}) + deploy_venom.get_basic_block().append_instruction("stop") + + runtime_ir = deploy_ir.args[1] - runtime_ir = deploy_ir.args[1] runtime_venom = IRFunction() _convert_ir_basicblock(runtime_venom, runtime_ir, {}, OrderedSet(), {}) From 2dc412c0d677d938af248b3faa1473c7f4a965f5 Mon Sep 17 00:00:00 2001 From: Harry Kalogirou Date: Mon, 15 Jan 2024 08:43:54 +0200 Subject: [PATCH 02/19] Fix unterminated basic blocks in IR to Venom conversion --- vyper/venom/ir_node_to_venom.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/vyper/venom/ir_node_to_venom.py b/vyper/venom/ir_node_to_venom.py index 10a994b5ba..f66db7e924 100644 --- a/vyper/venom/ir_node_to_venom.py +++ b/vyper/venom/ir_node_to_venom.py @@ -115,8 +115,11 @@ def convert_ir_basicblock(ir: IRnode) -> tuple[IRFunction, IRFunction]: # Connect unterminated blocks to the next with a jump for i, bb in enumerate(runtime_venom.basic_blocks): - if not bb.is_terminated and i < len(runtime_venom.basic_blocks) - 1: - bb.append_instruction("jmp", runtime_venom.basic_blocks[i + 1].label) + if not bb.is_terminated: + if i < len(runtime_venom.basic_blocks) - 1: + bb.append_instruction("jmp", runtime_venom.basic_blocks[i + 1].label) + else: + bb.append_instruction("stop") return deploy_venom, runtime_venom From 5dd5fe65af431946152b72f8c417bba5d059f903 Mon Sep 17 00:00:00 2001 From: Harry Kalogirou Date: Mon, 15 Jan 2024 08:52:29 +0200 Subject: [PATCH 03/19] Update IRFunction's __str__ method to remove trailing whitespace --- vyper/venom/function.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vyper/venom/function.py b/vyper/venom/function.py index 9f26fa8ec0..771dcf73ce 100644 --- a/vyper/venom/function.py +++ b/vyper/venom/function.py @@ -163,4 +163,4 @@ def __repr__(self) -> str: str += "Data segment:\n" for inst in self.data_segment: str += f"{inst}\n" - return str + return str.strip() From f338abe5734b6e8c2e98e4e07390f27a5675e30b Mon Sep 17 00:00:00 2001 From: Harry Kalogirou Date: Mon, 15 Jan 2024 08:52:56 +0200 Subject: [PATCH 04/19] Add unit test for converting basic block in Venom compiler --- .../venom/test_convert_basicblock_simple.py | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 tests/unit/compiler/venom/test_convert_basicblock_simple.py diff --git a/tests/unit/compiler/venom/test_convert_basicblock_simple.py b/tests/unit/compiler/venom/test_convert_basicblock_simple.py new file mode 100644 index 0000000000..f98a660545 --- /dev/null +++ b/tests/unit/compiler/venom/test_convert_basicblock_simple.py @@ -0,0 +1,23 @@ +from vyper.codegen.ir_node import IRnode +from vyper.compiler.settings import OptimizationLevel +from vyper.venom import generate_ir + + +def test_simple(get_contract_from_ir): + ir = ["calldatacopy", 32, 0, ["calldatasize"]] + ir = IRnode.from_list(ir) + print(ir) + deploy, runtime = generate_ir(ir, OptimizationLevel.NONE) + assert deploy is None + assert runtime is not None + assert len(runtime.basic_blocks) == 1 + bb = runtime.basic_blocks[0] + assert len(bb.instructions) == 3 + + correct_venom = """IRFunction: __global +__global: IN=[] OUT=[] => {} + %1 = calldatasize + calldatacopy %1, 0, 32 + stop""" + + assert str(runtime) == correct_venom From bcc63adcc58f4678dd7c8872dc3738f3c65cbcbe Mon Sep 17 00:00:00 2001 From: Harry Kalogirou Date: Mon, 15 Jan 2024 08:59:46 +0200 Subject: [PATCH 05/19] Refactor code for cleaner venom output and lint stuff --- vyper/venom/__init__.py | 4 +++- vyper/venom/basicblock.py | 4 ++-- vyper/venom/ir_node_to_venom.py | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/vyper/venom/__init__.py b/vyper/venom/__init__.py index 07c2011e22..1a9b7cf172 100644 --- a/vyper/venom/__init__.py +++ b/vyper/venom/__init__.py @@ -61,7 +61,9 @@ def _run_passes(ctx: IRFunction, optimize: OptimizationLevel) -> None: break -def generate_ir(ir: IRnode, optimize: OptimizationLevel) -> tuple[IRFunction, IRFunction]: +def generate_ir( + ir: IRnode, optimize: OptimizationLevel +) -> tuple[Optional[IRFunction], Optional[IRFunction]]: # Convert "old" IR to "new" IR ctx, ctx_runtime = convert_ir_basicblock(ir) diff --git a/vyper/venom/basicblock.py b/vyper/venom/basicblock.py index 598b8af7d5..f86e9b330c 100644 --- a/vyper/venom/basicblock.py +++ b/vyper/venom/basicblock.py @@ -410,8 +410,8 @@ def copy(self): def __repr__(self) -> str: s = ( f"{repr(self.label)}: IN={[bb.label for bb in self.cfg_in]}" - f" OUT={[bb.label for bb in self.cfg_out]} => {self.out_vars} \n" + f" OUT={[bb.label for bb in self.cfg_out]} => {self.out_vars}\n" ) for instruction in self.instructions: - s += f" {instruction}\n" + s += f" {str(instruction).strip()}\n" return s diff --git a/vyper/venom/ir_node_to_venom.py b/vyper/venom/ir_node_to_venom.py index f66db7e924..517a2569df 100644 --- a/vyper/venom/ir_node_to_venom.py +++ b/vyper/venom/ir_node_to_venom.py @@ -98,7 +98,7 @@ def _findIRnode(ir: IRnode, value: str) -> Optional[IRnode]: return None -def convert_ir_basicblock(ir: IRnode) -> tuple[IRFunction, IRFunction]: +def convert_ir_basicblock(ir: IRnode) -> tuple[Optional[IRFunction], Optional[IRFunction]]: runtime_ir = ir deploy_ir = _findIRnode(ir, "deploy") From d6af7fe12ae858029bf0fbe4a36073e2ce37b6c5 Mon Sep 17 00:00:00 2001 From: Harry Kalogirou Date: Mon, 15 Jan 2024 09:00:03 +0200 Subject: [PATCH 06/19] Clean up whitespace test_convert_basicblock_simple.py --- .../venom/test_convert_basicblock_simple.py | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/tests/unit/compiler/venom/test_convert_basicblock_simple.py b/tests/unit/compiler/venom/test_convert_basicblock_simple.py index f98a660545..adfa49c163 100644 --- a/tests/unit/compiler/venom/test_convert_basicblock_simple.py +++ b/tests/unit/compiler/venom/test_convert_basicblock_simple.py @@ -3,20 +3,16 @@ from vyper.venom import generate_ir -def test_simple(get_contract_from_ir): +def test_simple(): ir = ["calldatacopy", 32, 0, ["calldatasize"]] - ir = IRnode.from_list(ir) - print(ir) - deploy, runtime = generate_ir(ir, OptimizationLevel.NONE) + ir_node = IRnode.from_list(ir) + deploy, runtime = generate_ir(ir_node, OptimizationLevel.NONE) assert deploy is None assert runtime is not None - assert len(runtime.basic_blocks) == 1 - bb = runtime.basic_blocks[0] - assert len(bb.instructions) == 3 correct_venom = """IRFunction: __global -__global: IN=[] OUT=[] => {} - %1 = calldatasize +__global: IN=[] OUT=[] => {} + %1 = calldatasize calldatacopy %1, 0, 32 stop""" From 33e5b5a7bab62b338b80b1084dbdbc3d9e1fed19 Mon Sep 17 00:00:00 2001 From: Harry Kalogirou Date: Mon, 15 Jan 2024 14:11:15 +0200 Subject: [PATCH 07/19] Add new test cases for simple conversion in test_convert_basicblock_simple.py --- .../venom/test_convert_basicblock_simple.py | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/tests/unit/compiler/venom/test_convert_basicblock_simple.py b/tests/unit/compiler/venom/test_convert_basicblock_simple.py index adfa49c163..be379b133f 100644 --- a/tests/unit/compiler/venom/test_convert_basicblock_simple.py +++ b/tests/unit/compiler/venom/test_convert_basicblock_simple.py @@ -17,3 +17,39 @@ def test_simple(): stop""" assert str(runtime) == correct_venom + + +def test_simple_2(): + ir = [ + "seq", + [ + "seq", + [ + "mstore", + ["add", 64, 0], + [ + "with", + "x", + ["calldataload", ["add", 4, 0]], + [ + "with", + "ans", + ["add", "x", 1], + ["seq", ["assert", ["ge", "ans", "x"]], "ans"], + ], + ], + ], + ], + 32, + ] + ir_node = IRnode.from_list(ir) + deploy, runtime = generate_ir(ir_node, OptimizationLevel.NONE) + assert deploy is None + assert runtime is not None + + print(runtime) + + +if __name__ == "__main__": + test_simple() + test_simple_2() From 3ec3a51a367c2b8a83671c80d34a3d9a0623d7f5 Mon Sep 17 00:00:00 2001 From: Harry Kalogirou Date: Mon, 15 Jan 2024 14:11:49 +0200 Subject: [PATCH 08/19] clean up ir to venom --- vyper/venom/ir_node_to_venom.py | 152 +++++++++++++------------------- 1 file changed, 63 insertions(+), 89 deletions(-) diff --git a/vyper/venom/ir_node_to_venom.py b/vyper/venom/ir_node_to_venom.py index 517a2569df..51f9acf251 100644 --- a/vyper/venom/ir_node_to_venom.py +++ b/vyper/venom/ir_node_to_venom.py @@ -102,18 +102,20 @@ def convert_ir_basicblock(ir: IRnode) -> tuple[Optional[IRFunction], Optional[IR runtime_ir = ir deploy_ir = _findIRnode(ir, "deploy") + # 1. Convert deploy IR to Venom IR deploy_venom = None if deploy_ir is not None: deploy_venom = IRFunction() - _convert_ir_basicblock(deploy_venom, ir, {}, OrderedSet(), {}) + _convert_ir_bb(deploy_venom, ir, {}, OrderedSet(), {}) deploy_venom.get_basic_block().append_instruction("stop") runtime_ir = deploy_ir.args[1] + # 2. Convert runtime IR to Venom IR runtime_venom = IRFunction() - _convert_ir_basicblock(runtime_venom, runtime_ir, {}, OrderedSet(), {}) + _convert_ir_bb(runtime_venom, runtime_ir, {}, OrderedSet(), {}) - # Connect unterminated blocks to the next with a jump + # 3. Patch up basic blocks. Connect unterminated blocks to the next with a jump for i, bb in enumerate(runtime_venom.basic_blocks): if not bb.is_terminated: if i < len(runtime_venom.basic_blocks) - 1: @@ -133,8 +135,7 @@ def _convert_binary_op( swap: bool = False, ) -> Optional[IRVariable]: ir_args = ir.args[::-1] if swap else ir.args - arg_0 = _convert_ir_basicblock(ctx, ir_args[0], symbols, variables, allocated_variables) - arg_1 = _convert_ir_basicblock(ctx, ir_args[1], symbols, variables, allocated_variables) + arg_0, arg_1 = _convert_ir_bb_list(ctx, ir_args, symbols, variables, allocated_variables) return ctx.get_basic_block().append_instruction(str(ir.value), arg_1, arg_0) @@ -171,14 +172,12 @@ def _handle_self_call( if arg.is_literal: sym = symbols.get(f"&{arg.value}", None) if sym is None: - ret = _convert_ir_basicblock(ctx, arg, symbols, variables, allocated_variables) + ret = _convert_ir_bb(ctx, arg, symbols, variables, allocated_variables) ret_args.append(ret) else: ret_args.append(sym) # type: ignore else: - ret = _convert_ir_basicblock( - ctx, arg._optimized, symbols, variables, allocated_variables - ) + ret = _convert_ir_bb(ctx, arg._optimized, symbols, variables, allocated_variables) if arg.location and arg.location.load_op == "calldataload": bb = ctx.get_basic_block() ret = bb.append_instruction(arg.location.load_op, ret) @@ -231,9 +230,7 @@ def _convert_ir_simple_node( variables: OrderedSet, allocated_variables: dict[str, IRVariable], ) -> Optional[IRVariable]: - args = [ - _convert_ir_basicblock(ctx, arg, symbols, variables, allocated_variables) for arg in ir.args - ] + args = [_convert_ir_bb(ctx, arg, symbols, variables, allocated_variables) for arg in ir.args] return ctx.get_basic_block().append_instruction(ir.value, *args) # type: ignore @@ -272,7 +269,16 @@ def _append_return_for_stack_operand( bb.append_instruction("return", last_ir, new_var) # type: ignore -def _convert_ir_basicblock(ctx, ir, symbols, variables, allocated_variables): +def _convert_ir_bb_list(ctx, ir, symbols, variables, allocated_variables): + ret = [] + for ir_node in ir: + venom = _convert_ir_bb(ctx, ir_node, symbols, variables, allocated_variables) + ret.append(venom) + return ret + + +def _convert_ir_bb(ctx, ir, symbols, variables, allocated_variables): + assert isinstance(ir, IRnode) assert isinstance(variables, OrderedSet) global _break_target, _continue_target @@ -320,35 +326,22 @@ def _convert_ir_basicblock(ctx, ir, symbols, variables, allocated_variables): ret = None for ir_node in ir.args: # NOTE: skip the last one - ret = _convert_ir_basicblock(ctx, ir_node, symbols, variables, allocated_variables) + ret = _convert_ir_bb(ctx, ir_node, symbols, variables, allocated_variables) return ret elif ir.value in ["staticcall", "call"]: # external call idx = 0 - gas = _convert_ir_basicblock(ctx, ir.args[idx], symbols, variables, allocated_variables) - address = _convert_ir_basicblock( - ctx, ir.args[idx + 1], symbols, variables, allocated_variables - ) + gas = _convert_ir_bb(ctx, ir.args[idx], symbols, variables, allocated_variables) + address = _convert_ir_bb(ctx, ir.args[idx + 1], symbols, variables, allocated_variables) value = None if ir.value == "call": - value = _convert_ir_basicblock( - ctx, ir.args[idx + 2], symbols, variables, allocated_variables - ) + value = _convert_ir_bb(ctx, ir.args[idx + 2], symbols, variables, allocated_variables) else: idx -= 1 - argsOffset = _convert_ir_basicblock( - ctx, ir.args[idx + 3], symbols, variables, allocated_variables - ) - argsSize = _convert_ir_basicblock( - ctx, ir.args[idx + 4], symbols, variables, allocated_variables - ) - retOffset = _convert_ir_basicblock( - ctx, ir.args[idx + 5], symbols, variables, allocated_variables - ) - retSize = _convert_ir_basicblock( - ctx, ir.args[idx + 6], symbols, variables, allocated_variables + argsOffset, argsSize, retOffset, retSize = _convert_ir_bb_list( + ctx, ir.args[idx + 3 : idx + 7], symbols, variables, allocated_variables ) if isinstance(argsOffset, IRLiteral): @@ -383,7 +376,7 @@ def _convert_ir_basicblock(ctx, ir, symbols, variables, allocated_variables): current_bb = ctx.get_basic_block() # convert the condition - cont_ret = _convert_ir_basicblock(ctx, cond, symbols, variables, allocated_variables) + cont_ret = _convert_ir_bb(ctx, cond, symbols, variables, allocated_variables) else_block = IRBasicBlock(ctx.get_next_label(), ctx) ctx.append_basic_block(else_block) @@ -392,7 +385,7 @@ def _convert_ir_basicblock(ctx, ir, symbols, variables, allocated_variables): else_ret_val = None else_syms = symbols.copy() if len(ir.args) == 3: - else_ret_val = _convert_ir_basicblock( + else_ret_val = _convert_ir_bb( ctx, ir.args[2], else_syms, variables, allocated_variables.copy() ) if isinstance(else_ret_val, IRLiteral): @@ -404,9 +397,7 @@ def _convert_ir_basicblock(ctx, ir, symbols, variables, allocated_variables): then_block = IRBasicBlock(ctx.get_next_label(), ctx) ctx.append_basic_block(then_block) - then_ret_val = _convert_ir_basicblock( - ctx, ir.args[1], symbols, variables, allocated_variables - ) + then_ret_val = _convert_ir_bb(ctx, ir.args[1], symbols, variables, allocated_variables) if isinstance(then_ret_val, IRLiteral): then_ret_val = ctx.get_basic_block().append_instruction("store", then_ret_val) @@ -444,7 +435,7 @@ def _convert_ir_basicblock(ctx, ir, symbols, variables, allocated_variables): return if_ret elif ir.value == "with": - ret = _convert_ir_basicblock( + ret = _convert_ir_bb( ctx, ir.args[1], symbols, variables, allocated_variables ) # initialization @@ -458,27 +449,25 @@ def _convert_ir_basicblock(ctx, ir, symbols, variables, allocated_variables): else: with_symbols[sym.value] = ret # type: ignore - return _convert_ir_basicblock( - ctx, ir.args[2], with_symbols, variables, allocated_variables - ) # body + return _convert_ir_bb(ctx, ir.args[2], with_symbols, variables, allocated_variables) # body elif ir.value == "goto": _append_jmp(ctx, IRLabel(ir.args[0].value)) elif ir.value == "djump": - args = [_convert_ir_basicblock(ctx, ir.args[0], symbols, variables, allocated_variables)] + args = [_convert_ir_bb(ctx, ir.args[0], symbols, variables, allocated_variables)] for target in ir.args[1:]: args.append(IRLabel(target.value)) ctx.get_basic_block().append_instruction("djmp", *args) _new_block(ctx) elif ir.value == "set": sym = ir.args[0] - arg_1 = _convert_ir_basicblock(ctx, ir.args[1], symbols, variables, allocated_variables) + arg_1 = _convert_ir_bb(ctx, ir.args[1], symbols, variables, allocated_variables) new_var = ctx.get_basic_block().append_instruction("store", arg_1) # type: ignore symbols[sym.value] = new_var elif ir.value == "calldatacopy": - arg_0 = _convert_ir_basicblock(ctx, ir.args[0], symbols, variables, allocated_variables) - arg_1 = _convert_ir_basicblock(ctx, ir.args[1], symbols, variables, allocated_variables) - size = _convert_ir_basicblock(ctx, ir.args[2], symbols, variables, allocated_variables) + arg_0, arg_1, size = _convert_ir_bb_list( + ctx, ir.args, symbols, variables, allocated_variables + ) new_v = arg_0 var = ( @@ -498,9 +487,9 @@ def _convert_ir_basicblock(ctx, ir, symbols, variables, allocated_variables): return new_v elif ir.value == "codecopy": - arg_0 = _convert_ir_basicblock(ctx, ir.args[0], symbols, variables, allocated_variables) - arg_1 = _convert_ir_basicblock(ctx, ir.args[1], symbols, variables, allocated_variables) - size = _convert_ir_basicblock(ctx, ir.args[2], symbols, variables, allocated_variables) + arg_0, arg_1, size = _convert_ir_bb_list( + ctx, ir.args, symbols, variables, allocated_variables + ) ctx.get_basic_block().append_instruction("codecopy", size, arg_1, arg_0) # type: ignore elif ir.value == "symbol": @@ -515,10 +504,10 @@ def _convert_ir_basicblock(ctx, ir, symbols, variables, allocated_variables): elif isinstance(c, bytes): ctx.append_data("db", [c]) # type: ignore elif isinstance(c, IRnode): - data = _convert_ir_basicblock(ctx, c, symbols, variables, allocated_variables) + data = _convert_ir_bb(ctx, c, symbols, variables, allocated_variables) ctx.append_data("db", [data]) # type: ignore elif ir.value == "assert": - arg_0 = _convert_ir_basicblock(ctx, ir.args[0], symbols, variables, allocated_variables) + arg_0 = _convert_ir_bb(ctx, ir.args[0], symbols, variables, allocated_variables) current_bb = ctx.get_basic_block() current_bb.append_instruction("assert", arg_0) elif ir.value == "label": @@ -528,7 +517,7 @@ def _convert_ir_basicblock(ctx, ir, symbols, variables, allocated_variables): bb.append_instruction("jmp", label) bb = IRBasicBlock(label, ctx) ctx.append_basic_block(bb) - _convert_ir_basicblock(ctx, ir.args[2], symbols, variables, allocated_variables) + _convert_ir_bb(ctx, ir.args[2], symbols, variables, allocated_variables) elif ir.value == "exit_to": func_t = ir.passthrough_metadata.get("func_t", None) assert func_t is not None, "exit_to without func_t" @@ -551,15 +540,11 @@ def _convert_ir_basicblock(ctx, ir, symbols, variables, allocated_variables): deleted = symbols[f"&{ret_var.value}"] del symbols[f"&{ret_var.value}"] for arg in ir.args[2:]: - last_ir = _convert_ir_basicblock( - ctx, arg, symbols, variables, allocated_variables - ) + last_ir = _convert_ir_bb(ctx, arg, symbols, variables, allocated_variables) if deleted is not None: symbols[f"&{ret_var.value}"] = deleted - ret_ir = _convert_ir_basicblock( - ctx, ret_var, symbols, variables, allocated_variables - ) + ret_ir = _convert_ir_bb(ctx, ret_var, symbols, variables, allocated_variables) bb = ctx.get_basic_block() @@ -618,12 +603,11 @@ def _convert_ir_basicblock(ctx, ir, symbols, variables, allocated_variables): bb.append_instruction("ret", ret_by_value, symbols["return_pc"]) elif ir.value == "revert": - arg_0 = _convert_ir_basicblock(ctx, ir.args[0], symbols, variables, allocated_variables) - arg_1 = _convert_ir_basicblock(ctx, ir.args[1], symbols, variables, allocated_variables) + arg_0, arg_1 = _convert_ir_bb_list(ctx, ir.args, symbols, variables, allocated_variables) ctx.get_basic_block().append_instruction("revert", arg_1, arg_0) elif ir.value == "dload": - arg_0 = _convert_ir_basicblock(ctx, ir.args[0], symbols, variables, allocated_variables) + arg_0 = _convert_ir_bb(ctx, ir.args[0], symbols, variables, allocated_variables) bb = ctx.get_basic_block() src = bb.append_instruction("add", arg_0, IRLabel("code_end")) @@ -631,11 +615,10 @@ def _convert_ir_basicblock(ctx, ir, symbols, variables, allocated_variables): return bb.append_instruction("mload", MemoryPositions.FREE_VAR_SPACE) elif ir.value == "dloadbytes": - dst = _convert_ir_basicblock(ctx, ir.args[0], symbols, variables, allocated_variables) - src_offset = _convert_ir_basicblock( - ctx, ir.args[1], symbols, variables, allocated_variables + dst, src_offset, len_ = _convert_ir_bb_list( + ctx, ir.args, symbols, variables, allocated_variables ) - len_ = _convert_ir_basicblock(ctx, ir.args[2], symbols, variables, allocated_variables) + bb = ctx.get_basic_block() src = bb.append_instruction("add", src_offset, IRLabel("code_end")) bb.append_instruction("dloadbytes", len_, src, dst) @@ -684,9 +667,7 @@ def _convert_ir_basicblock(ctx, ir, symbols, variables, allocated_variables): else: return bb.append_instruction("mload", sym_ir.value) else: - new_var = _convert_ir_basicblock( - ctx, sym_ir, symbols, variables, allocated_variables - ) + new_var = _convert_ir_bb(ctx, sym_ir, symbols, variables, allocated_variables) # # Old IR gets it's return value as a reference in the stack # New IR gets it's return value in stack in case of 32 bytes or less @@ -698,8 +679,7 @@ def _convert_ir_basicblock(ctx, ir, symbols, variables, allocated_variables): return bb.append_instruction("mload", new_var) elif ir.value == "mstore": - sym_ir = _convert_ir_basicblock(ctx, ir.args[0], symbols, variables, allocated_variables) - arg_1 = _convert_ir_basicblock(ctx, ir.args[1], symbols, variables, allocated_variables) + sym_ir, arg_1 = _convert_ir_bb_list(ctx, ir.args, symbols, variables, allocated_variables) bb = ctx.get_basic_block() @@ -748,11 +728,10 @@ def _convert_ir_basicblock(ctx, ir, symbols, variables, allocated_variables): return arg_1 elif ir.value in ["sload", "iload"]: - arg_0 = _convert_ir_basicblock(ctx, ir.args[0], symbols, variables, allocated_variables) + arg_0 = _convert_ir_bb(ctx, ir.args[0], symbols, variables, allocated_variables) return ctx.get_basic_block().append_instruction(ir.value, arg_0) elif ir.value in ["sstore", "istore"]: - arg_0 = _convert_ir_basicblock(ctx, ir.args[0], symbols, variables, allocated_variables) - arg_1 = _convert_ir_basicblock(ctx, ir.args[1], symbols, variables, allocated_variables) + arg_0, arg_1 = _convert_ir_bb_list(ctx, ir.args, symbols, variables, allocated_variables) ctx.get_basic_block().append_instruction(ir.value, arg_1, arg_0) elif ir.value == "unique_symbol": sym = ir.args[0] @@ -773,14 +752,14 @@ def emit_body_block(): global _break_target, _continue_target old_targets = _break_target, _continue_target _break_target, _continue_target = exit_block, increment_block - _convert_ir_basicblock(ctx, body, symbols, variables, allocated_variables) + _convert_ir_bb(ctx, body, symbols, variables, allocated_variables) _break_target, _continue_target = old_targets sym = ir.args[0] - start = _convert_ir_basicblock(ctx, ir.args[1], symbols, variables, allocated_variables) - end = _convert_ir_basicblock(ctx, ir.args[2], symbols, variables, allocated_variables) - # "bound" is not used - _ = _convert_ir_basicblock(ctx, ir.args[3], symbols, variables, allocated_variables) + start, end, _ = _convert_ir_bb_list( + ctx, ir.args[:3], symbols, variables, allocated_variables + ) + body = ir.args[4] entry_block = ctx.get_basic_block() @@ -857,23 +836,20 @@ def emit_body_block(): return ctx.get_basic_block().append_instruction("returndatasize") elif ir.value == "returndatacopy": assert len(ir.args) == 3, "returndatacopy with wrong number of arguments" - arg_0 = _convert_ir_basicblock(ctx, ir.args[0], symbols, variables, allocated_variables) - arg_1 = _convert_ir_basicblock(ctx, ir.args[1], symbols, variables, allocated_variables) - size = _convert_ir_basicblock(ctx, ir.args[2], symbols, variables, allocated_variables) + arg_0 = _convert_ir_bb(ctx, ir.args[0], symbols, variables, allocated_variables) + arg_1 = _convert_ir_bb(ctx, ir.args[1], symbols, variables, allocated_variables) + size = _convert_ir_bb(ctx, ir.args[2], symbols, variables, allocated_variables) new_var = ctx.get_basic_block().append_instruction("returndatacopy", arg_1, size) symbols[f"&{arg_0.value}"] = new_var return new_var elif ir.value == "selfdestruct": - arg_0 = _convert_ir_basicblock(ctx, ir.args[0], symbols, variables, allocated_variables) + arg_0 = _convert_ir_bb(ctx, ir.args[0], symbols, variables, allocated_variables) ctx.get_basic_block().append_instruction("selfdestruct", arg_0) elif isinstance(ir.value, str) and ir.value.startswith("log"): args = reversed( - [ - _convert_ir_basicblock(ctx, arg, symbols, variables, allocated_variables) - for arg in ir.args - ] + [_convert_ir_bb(ctx, arg, symbols, variables, allocated_variables) for arg in ir.args] ) topic_count = int(ir.value[3:]) assert topic_count >= 0 and topic_count <= 4, "invalid topic count" @@ -901,9 +877,7 @@ def _convert_ir_opcode( inst_args = [] for arg in ir.args: if isinstance(arg, IRnode): - inst_args.append( - _convert_ir_basicblock(ctx, arg, symbols, variables, allocated_variables) - ) + inst_args.append(_convert_ir_bb(ctx, arg, symbols, variables, allocated_variables)) ctx.get_basic_block().append_instruction(opcode, *inst_args) From 33d1c72d20b6aa38ee5ae92e07f5bfc6373bfe1b Mon Sep 17 00:00:00 2001 From: Harry Kalogirou Date: Mon, 15 Jan 2024 21:50:59 +0200 Subject: [PATCH 09/19] Fix conditional block termination issue --- vyper/venom/ir_node_to_venom.py | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/vyper/venom/ir_node_to_venom.py b/vyper/venom/ir_node_to_venom.py index 51f9acf251..cdddff0d89 100644 --- a/vyper/venom/ir_node_to_venom.py +++ b/vyper/venom/ir_node_to_venom.py @@ -377,6 +377,7 @@ def _convert_ir_bb(ctx, ir, symbols, variables, allocated_variables): # convert the condition cont_ret = _convert_ir_bb(ctx, cond, symbols, variables, allocated_variables) + current_bb = ctx.get_basic_block() else_block = IRBasicBlock(ctx.get_next_label(), ctx) ctx.append_basic_block(else_block) @@ -392,6 +393,7 @@ def _convert_ir_bb(ctx, ir, symbols, variables, allocated_variables): assert isinstance(else_ret_val.value, int) # help mypy else_ret_val = ctx.get_basic_block().append_instruction("store", else_ret_val) after_else_syms = else_syms.copy() + else_block = ctx.get_basic_block() # convert "then" then_block = IRBasicBlock(ctx.get_next_label(), ctx) @@ -404,21 +406,24 @@ def _convert_ir_bb(ctx, ir, symbols, variables, allocated_variables): current_bb.append_instruction("jnz", cont_ret, then_block.label, else_block.label) after_then_syms = symbols.copy() + then_block = ctx.get_basic_block() # exit bb exit_label = ctx.get_next_label() - bb = IRBasicBlock(exit_label, ctx) - bb = ctx.append_basic_block(bb) + exit_bb = IRBasicBlock(exit_label, ctx) + exit_bb = ctx.append_basic_block(exit_bb) if_ret = None if then_ret_val is not None and else_ret_val is not None: - if_ret = bb.append_instruction( + if_ret = exit_bb.append_instruction( "phi", then_block.label, then_ret_val, else_block.label, else_ret_val ) common_symbols = _get_symbols_common(after_then_syms, after_else_syms) for sym, val in common_symbols.items(): - ret = bb.append_instruction("phi", then_block.label, val[0], else_block.label, val[1]) + ret = exit_bb.append_instruction( + "phi", then_block.label, val[0], else_block.label, val[1] + ) old_var = symbols.get(sym, None) symbols[sym] = ret if old_var is not None: @@ -427,10 +432,10 @@ def _convert_ir_bb(ctx, ir, symbols, variables, allocated_variables): allocated_variables[idx] = ret # type: ignore if not else_block.is_terminated: - else_block.append_instruction("jmp", bb.label) + else_block.append_instruction("jmp", exit_bb.label) if not then_block.is_terminated: - then_block.append_instruction("jmp", bb.label) + then_block.append_instruction("jmp", exit_bb.label) return if_ret @@ -757,7 +762,7 @@ def emit_body_block(): sym = ir.args[0] start, end, _ = _convert_ir_bb_list( - ctx, ir.args[:3], symbols, variables, allocated_variables + ctx, ir.args[1:4], symbols, variables, allocated_variables ) body = ir.args[4] @@ -813,8 +818,9 @@ def emit_body_block(): jump_up_block.append_instruction("jmp", increment_block.label) ctx.append_basic_block(jump_up_block) - increment_block.append_instruction(IRInstruction("add", ret, 1)) - increment_block.insert_instruction[-1].output = counter_inc_var + increment_block.insert_instruction( + IRInstruction("add", [ret, IRLiteral(1)], counter_inc_var), 0 + ) increment_block.append_instruction("jmp", cond_block.label) ctx.append_basic_block(increment_block) @@ -836,9 +842,9 @@ def emit_body_block(): return ctx.get_basic_block().append_instruction("returndatasize") elif ir.value == "returndatacopy": assert len(ir.args) == 3, "returndatacopy with wrong number of arguments" - arg_0 = _convert_ir_bb(ctx, ir.args[0], symbols, variables, allocated_variables) - arg_1 = _convert_ir_bb(ctx, ir.args[1], symbols, variables, allocated_variables) - size = _convert_ir_bb(ctx, ir.args[2], symbols, variables, allocated_variables) + arg_0, arg_1, size = _convert_ir_bb_list( + ctx, ir.args, symbols, variables, allocated_variables + ) new_var = ctx.get_basic_block().append_instruction("returndatacopy", arg_1, size) From 978316cd94b270c4c562119cde793d584a803e58 Mon Sep 17 00:00:00 2001 From: Harry Kalogirou Date: Tue, 16 Jan 2024 15:49:11 +0200 Subject: [PATCH 10/19] Add _get_symbols_accessed function to ir_node_to_venom.py --- vyper/venom/ir_node_to_venom.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/vyper/venom/ir_node_to_venom.py b/vyper/venom/ir_node_to_venom.py index cdddff0d89..798885e243 100644 --- a/vyper/venom/ir_node_to_venom.py +++ b/vyper/venom/ir_node_to_venom.py @@ -87,6 +87,18 @@ def _get_symbols_common(a: dict, b: dict) -> dict: return ret +def _get_symbols_accessed(a: dict, b: dict) -> list: + ret = [] + # preserves the ordering in `a` + for k in a.keys(): + if k not in b: + continue + if a[k] != b[k]: + continue + ret.append(a[k]) + return ret + + def _findIRnode(ir: IRnode, value: str) -> Optional[IRnode]: if ir.value == value: return ir @@ -753,7 +765,7 @@ def _convert_ir_bb(ctx, ir, symbols, variables, allocated_variables): # 5) increment block # 6) exit block # TODO: Add the extra bounds check after clarify - def emit_body_block(): + def emit_body_blocks(): global _break_target, _continue_target old_targets = _break_target, _continue_target _break_target, _continue_target = exit_block, increment_block @@ -789,10 +801,9 @@ def emit_body_block(): cont_ret = cond_block.append_instruction("iszero", xor_ret) ctx.append_basic_block(cond_block) - # Do a dry run to get the symbols needing phi nodes start_syms = symbols.copy() ctx.append_basic_block(body_block) - emit_body_block() + emit_body_blocks() end_syms = symbols.copy() diff_syms = _get_symbols_common(start_syms, end_syms) From 1eae31b9287874c5f2c9e199f03ad297e0c9e09d Mon Sep 17 00:00:00 2001 From: Harry Kalogirou Date: Tue, 16 Jan 2024 15:49:43 +0200 Subject: [PATCH 11/19] Remove unused function _get_symbols_accessed() --- vyper/venom/ir_node_to_venom.py | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/vyper/venom/ir_node_to_venom.py b/vyper/venom/ir_node_to_venom.py index 798885e243..ff2d1f84bc 100644 --- a/vyper/venom/ir_node_to_venom.py +++ b/vyper/venom/ir_node_to_venom.py @@ -87,18 +87,6 @@ def _get_symbols_common(a: dict, b: dict) -> dict: return ret -def _get_symbols_accessed(a: dict, b: dict) -> list: - ret = [] - # preserves the ordering in `a` - for k in a.keys(): - if k not in b: - continue - if a[k] != b[k]: - continue - ret.append(a[k]) - return ret - - def _findIRnode(ir: IRnode, value: str) -> Optional[IRnode]: if ir.value == value: return ir From 428c910a4de951768d066272197daf2651935742 Mon Sep 17 00:00:00 2001 From: Harry Kalogirou Date: Wed, 17 Jan 2024 16:48:19 +0200 Subject: [PATCH 12/19] liveness calculation and fixed a bug * Converted algorithm to iterative * fixed a bug related to simple loops --- vyper/venom/analysis.py | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/vyper/venom/analysis.py b/vyper/venom/analysis.py index eed579463e..b268d0ba85 100644 --- a/vyper/venom/analysis.py +++ b/vyper/venom/analysis.py @@ -42,10 +42,12 @@ def _reset_liveness(ctx: IRFunction) -> None: inst.liveness = OrderedSet() -def _calculate_liveness_bb(bb: IRBasicBlock) -> None: +def _calculate_liveness(bb: IRBasicBlock) -> bool: """ Compute liveness of each instruction in the basic block. + Returns True if liveness changed """ + orig_liveness = bb.instructions[0].liveness.copy() liveness = bb.out_vars.copy() for instruction in reversed(bb.instructions): ops = instruction.get_inputs() @@ -60,29 +62,31 @@ def _calculate_liveness_bb(bb: IRBasicBlock) -> None: liveness.remove(out) instruction.liveness = liveness + return orig_liveness != bb.instructions[0].liveness -def _calculate_liveness_r(bb: IRBasicBlock, visited: dict) -> None: - assert isinstance(visited, dict) - for out_bb in bb.cfg_out: - if visited.get(bb) == out_bb: - continue - visited[bb] = out_bb - - # recurse - _calculate_liveness_r(out_bb, visited) +def _calculate_out_vars(bb: IRBasicBlock) -> bool: + """ + Compute out_vars of basic block. + Returns True if out_vars changed + """ + out_vars = bb.out_vars.copy() + for out_bb in bb.cfg_out: target_vars = input_vars_from(bb, out_bb) - - # the output stack layout for bb. it produces a stack layout - # which works for all possible cfg_outs from the bb. bb.out_vars = bb.out_vars.union(target_vars) - - _calculate_liveness_bb(bb) + return out_vars != bb.out_vars def calculate_liveness(ctx: IRFunction) -> None: _reset_liveness(ctx) - _calculate_liveness_r(ctx.basic_blocks[0], dict()) + while True: + changed = False + for bb in ctx.basic_blocks: + changed |= _calculate_out_vars(bb) + changed |= _calculate_liveness(bb) + + if changed is False: + break # calculate the input variables into self from source From e7d2b40635377c662f2dcfef091fbbc1f4370ccb Mon Sep 17 00:00:00 2001 From: Harry Kalogirou Date: Wed, 17 Jan 2024 16:49:15 +0200 Subject: [PATCH 13/19] update test --- .../compiler/venom/test_convert_basicblock_simple.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/tests/unit/compiler/venom/test_convert_basicblock_simple.py b/tests/unit/compiler/venom/test_convert_basicblock_simple.py index be379b133f..c3e5bd80c8 100644 --- a/tests/unit/compiler/venom/test_convert_basicblock_simple.py +++ b/tests/unit/compiler/venom/test_convert_basicblock_simple.py @@ -10,13 +10,9 @@ def test_simple(): assert deploy is None assert runtime is not None - correct_venom = """IRFunction: __global -__global: IN=[] OUT=[] => {} - %1 = calldatasize - calldatacopy %1, 0, 32 - stop""" - - assert str(runtime) == correct_venom + bb = runtime.basic_blocks[0] + assert bb.instructions[0].opcode == "calldatasize" + assert bb.instructions[1].opcode == "calldatacopy" def test_simple_2(): From 353fefa599e95e6bcc693e969fb57f11383d08a4 Mon Sep 17 00:00:00 2001 From: Harry Kalogirou Date: Wed, 17 Jan 2024 16:52:58 +0200 Subject: [PATCH 14/19] Add test for liveness in a simple loop --- .../compiler/venom/test_liveness_simple_loop.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 tests/unit/compiler/venom/test_liveness_simple_loop.py diff --git a/tests/unit/compiler/venom/test_liveness_simple_loop.py b/tests/unit/compiler/venom/test_liveness_simple_loop.py new file mode 100644 index 0000000000..822cab5d2e --- /dev/null +++ b/tests/unit/compiler/venom/test_liveness_simple_loop.py @@ -0,0 +1,17 @@ +import pytest +import vyper + +source = """ +@external +def foo(a: uint256): + _numBids: uint256 = 20 + b: uint256 = 10 + + for i: uint256 in range(128): + b = 1 + _numBids +""" + + +def test_liveness_simple_loop(): + vyper.compile_code(source, ["opcodes"]) + assert True From 0cc222ad18df54678a0c084f4f15985168b94002 Mon Sep 17 00:00:00 2001 From: Harry Kalogirou Date: Wed, 17 Jan 2024 16:54:33 +0200 Subject: [PATCH 15/19] Remove unused import statement --- tests/unit/compiler/venom/test_liveness_simple_loop.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/unit/compiler/venom/test_liveness_simple_loop.py b/tests/unit/compiler/venom/test_liveness_simple_loop.py index 822cab5d2e..d92a50596a 100644 --- a/tests/unit/compiler/venom/test_liveness_simple_loop.py +++ b/tests/unit/compiler/venom/test_liveness_simple_loop.py @@ -1,4 +1,3 @@ -import pytest import vyper source = """ From e1a9330572576eeee6284d012d243f26beab57f7 Mon Sep 17 00:00:00 2001 From: Harry Kalogirou Date: Wed, 17 Jan 2024 19:50:48 +0200 Subject: [PATCH 16/19] Remove unused variable in ir_node_to_venom.py --- vyper/venom/ir_node_to_venom.py | 1 - 1 file changed, 1 deletion(-) diff --git a/vyper/venom/ir_node_to_venom.py b/vyper/venom/ir_node_to_venom.py index ff2d1f84bc..cd1b9df76e 100644 --- a/vyper/venom/ir_node_to_venom.py +++ b/vyper/venom/ir_node_to_venom.py @@ -373,7 +373,6 @@ def _convert_ir_bb(ctx, ir, symbols, variables, allocated_variables): return bb.append_instruction(ir.value, *args) elif ir.value == "if": cond = ir.args[0] - current_bb = ctx.get_basic_block() # convert the condition cont_ret = _convert_ir_bb(ctx, cond, symbols, variables, allocated_variables) From 788a36a1d159b5b49e84ff2653e10477e70293d2 Mon Sep 17 00:00:00 2001 From: Harry Kalogirou Date: Wed, 17 Jan 2024 19:56:00 +0200 Subject: [PATCH 17/19] Review comments resolution --- vyper/venom/analysis.py | 2 +- vyper/venom/ir_node_to_venom.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/vyper/venom/analysis.py b/vyper/venom/analysis.py index b268d0ba85..daebd2560c 100644 --- a/vyper/venom/analysis.py +++ b/vyper/venom/analysis.py @@ -85,7 +85,7 @@ def calculate_liveness(ctx: IRFunction) -> None: changed |= _calculate_out_vars(bb) changed |= _calculate_liveness(bb) - if changed is False: + if not changed: break diff --git a/vyper/venom/ir_node_to_venom.py b/vyper/venom/ir_node_to_venom.py index cd1b9df76e..9c687c4e96 100644 --- a/vyper/venom/ir_node_to_venom.py +++ b/vyper/venom/ir_node_to_venom.py @@ -137,7 +137,8 @@ def _convert_binary_op( ir_args = ir.args[::-1] if swap else ir.args arg_0, arg_1 = _convert_ir_bb_list(ctx, ir_args, symbols, variables, allocated_variables) - return ctx.get_basic_block().append_instruction(str(ir.value), arg_1, arg_0) + assert isinstance(ir.value, str) # mypy hint + return ctx.get_basic_block().append_instruction(ir.value, arg_1, arg_0) def _append_jmp(ctx: IRFunction, label: IRLabel) -> None: From 2897d9776b05b52c743763bd187a1173890c95f6 Mon Sep 17 00:00:00 2001 From: Charles Cooper Date: Wed, 17 Jan 2024 16:24:46 -0500 Subject: [PATCH 18/19] simplify convert_ir_basicblock - rename it to ir_node_to_venom - fix bb well-formedness for deploy ir - don't do deploy IR detection; rely on caller in CompilerData to call for both deploy and runtime IR - remove findIRnode, it's no longer needed --- .../venom/test_convert_basicblock_simple.py | 24 +++------- vyper/compiler/phases.py | 5 ++- vyper/venom/__init__.py | 16 +++---- vyper/venom/ir_node_to_venom.py | 44 +++++-------------- 4 files changed, 27 insertions(+), 62 deletions(-) diff --git a/tests/unit/compiler/venom/test_convert_basicblock_simple.py b/tests/unit/compiler/venom/test_convert_basicblock_simple.py index c3e5bd80c8..fdaa341a81 100644 --- a/tests/unit/compiler/venom/test_convert_basicblock_simple.py +++ b/tests/unit/compiler/venom/test_convert_basicblock_simple.py @@ -1,16 +1,14 @@ from vyper.codegen.ir_node import IRnode -from vyper.compiler.settings import OptimizationLevel -from vyper.venom import generate_ir +from vyper.venom.ir_node_to_venom import ir_node_to_venom def test_simple(): - ir = ["calldatacopy", 32, 0, ["calldatasize"]] + ir = IRnode.from_list(["calldatacopy", 32, 0, ["calldatasize"]]) ir_node = IRnode.from_list(ir) - deploy, runtime = generate_ir(ir_node, OptimizationLevel.NONE) - assert deploy is None - assert runtime is not None + venom = ir_node_to_venom(ir_node) + assert venom is not None - bb = runtime.basic_blocks[0] + bb = venom.basic_blocks[0] assert bb.instructions[0].opcode == "calldatasize" assert bb.instructions[1].opcode == "calldatacopy" @@ -39,13 +37,5 @@ def test_simple_2(): 32, ] ir_node = IRnode.from_list(ir) - deploy, runtime = generate_ir(ir_node, OptimizationLevel.NONE) - assert deploy is None - assert runtime is not None - - print(runtime) - - -if __name__ == "__main__": - test_simple() - test_simple_2() + venom = ir_node_to_venom(ir_node) + assert venom is not None diff --git a/vyper/compiler/phases.py b/vyper/compiler/phases.py index ba6ccbda20..5b7decec7b 100644 --- a/vyper/compiler/phases.py +++ b/vyper/compiler/phases.py @@ -197,7 +197,10 @@ def function_signatures(self) -> dict[str, ContractFunctionT]: @cached_property def venom_functions(self): - return generate_ir(self.ir_nodes, self.settings.optimize) + deploy_ir, runtime_ir = self._ir_output + deploy_venom = generate_ir(deploy_ir, self.settings.optimize) + runtime_venom = generate_ir(runtime_ir, self.settings.optimize) + return deploy_venom, runtime_venom @cached_property def assembly(self) -> list: diff --git a/vyper/venom/__init__.py b/vyper/venom/__init__.py index 1a9b7cf172..d1c2d0c342 100644 --- a/vyper/venom/__init__.py +++ b/vyper/venom/__init__.py @@ -12,7 +12,7 @@ ir_pass_remove_unreachable_blocks, ) from vyper.venom.function import IRFunction -from vyper.venom.ir_node_to_venom import convert_ir_basicblock +from vyper.venom.ir_node_to_venom import ir_node_to_venom from vyper.venom.passes.constant_propagation import ir_pass_constant_propagation from vyper.venom.passes.dft import DFTPass from vyper.venom.venom_to_assembly import VenomCompiler @@ -61,15 +61,9 @@ def _run_passes(ctx: IRFunction, optimize: OptimizationLevel) -> None: break -def generate_ir( - ir: IRnode, optimize: OptimizationLevel -) -> tuple[Optional[IRFunction], Optional[IRFunction]]: +def generate_ir(ir: IRnode, optimize: OptimizationLevel) -> IRFunction: # Convert "old" IR to "new" IR - ctx, ctx_runtime = convert_ir_basicblock(ir) + ctx = ir_node_to_venom(ir) + _run_passes(ctx, optimize) - if ctx: - _run_passes(ctx, optimize) - if ctx_runtime: - _run_passes(ctx_runtime, optimize) - - return ctx, ctx_runtime + return ctx diff --git a/vyper/venom/ir_node_to_venom.py b/vyper/venom/ir_node_to_venom.py index 9c687c4e96..6b47ac2415 100644 --- a/vyper/venom/ir_node_to_venom.py +++ b/vyper/venom/ir_node_to_venom.py @@ -87,43 +87,21 @@ def _get_symbols_common(a: dict, b: dict) -> dict: return ret -def _findIRnode(ir: IRnode, value: str) -> Optional[IRnode]: - if ir.value == value: - return ir - for arg in ir.args: - if isinstance(arg, IRnode): - ret = _findIRnode(arg, value) - if ret is not None: - return ret - return None - - -def convert_ir_basicblock(ir: IRnode) -> tuple[Optional[IRFunction], Optional[IRFunction]]: - runtime_ir = ir - deploy_ir = _findIRnode(ir, "deploy") - - # 1. Convert deploy IR to Venom IR - deploy_venom = None - if deploy_ir is not None: - deploy_venom = IRFunction() - _convert_ir_bb(deploy_venom, ir, {}, OrderedSet(), {}) - deploy_venom.get_basic_block().append_instruction("stop") - - runtime_ir = deploy_ir.args[1] - - # 2. Convert runtime IR to Venom IR - runtime_venom = IRFunction() - _convert_ir_bb(runtime_venom, runtime_ir, {}, OrderedSet(), {}) - - # 3. Patch up basic blocks. Connect unterminated blocks to the next with a jump - for i, bb in enumerate(runtime_venom.basic_blocks): +# convert IRnode directly to venom +def ir_node_to_venom(ir: IRnode) -> IRFunction: + ctx = IRFunction() + _convert_ir_bb(ctx, ir, {}, OrderedSet(), {}) + + # Patch up basic blocks. Connect unterminated blocks to the next with + # a jump. terminate final basic block with STOP. + for i, bb in enumerate(ctx.basic_blocks): if not bb.is_terminated: - if i < len(runtime_venom.basic_blocks) - 1: - bb.append_instruction("jmp", runtime_venom.basic_blocks[i + 1].label) + if i < len(ctx.basic_blocks) - 1: + bb.append_instruction("jmp", ctx.basic_blocks[i + 1].label) else: bb.append_instruction("stop") - return deploy_venom, runtime_venom + return ctx def _convert_binary_op( From cc4c1743367fd40fb9b823fb9ea7d8f46e2ddbfe Mon Sep 17 00:00:00 2001 From: Charles Cooper Date: Wed, 17 Jan 2024 16:36:06 -0500 Subject: [PATCH 19/19] remove redundant test --- .../compiler/venom/test_liveness_simple_loop.py | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 tests/unit/compiler/venom/test_liveness_simple_loop.py diff --git a/tests/unit/compiler/venom/test_liveness_simple_loop.py b/tests/unit/compiler/venom/test_liveness_simple_loop.py deleted file mode 100644 index d92a50596a..0000000000 --- a/tests/unit/compiler/venom/test_liveness_simple_loop.py +++ /dev/null @@ -1,16 +0,0 @@ -import vyper - -source = """ -@external -def foo(a: uint256): - _numBids: uint256 = 20 - b: uint256 = 10 - - for i: uint256 in range(128): - b = 1 + _numBids -""" - - -def test_liveness_simple_loop(): - vyper.compile_code(source, ["opcodes"]) - assert True