From 53020be895c78d1bb3d0d6693e958f134dc8cbb7 Mon Sep 17 00:00:00 2001 From: Charles Cooper Date: Mon, 19 Feb 2024 16:40:12 -0500 Subject: [PATCH 1/4] fix: assembly dead code eliminator it was not aggressive enough, and could leave some instructions which mangle the assembly so it can't be turned into bytecode --- vyper/ir/compile_ir.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/vyper/ir/compile_ir.py b/vyper/ir/compile_ir.py index 8ce8c887f1..eb40c03fe9 100644 --- a/vyper/ir/compile_ir.py +++ b/vyper/ir/compile_ir.py @@ -809,14 +809,18 @@ def _prune_unreachable_code(assembly): # unreachable changed = False i = 0 - while i < len(assembly) - 2: + while i < len(assembly) - 1: instr = assembly[i] if isinstance(instr, list): instr = assembly[i][-1] - if assembly[i] in _TERMINAL_OPS and not ( - is_symbol(assembly[i + 1]) or isinstance(assembly[i + 1], list) - ): + is_terminal = assembly[i] in _TERMINAL_OPS + next_is_jumpdest = ( + i + 2 < len(assembly) and is_symbol(assembly[i + 1]) and assembly[i + 2] == "JUMPDEST" + ) + next_is_list = isinstance(assembly[i + 1], list) + + if is_terminal and not (next_is_jumpdest or next_is_list): changed = True del assembly[i + 1] else: @@ -1230,7 +1234,6 @@ def assembly_to_evm_with_symbol_map(assembly, pc_ofst=0, insert_compiler_metadat if is_symbol_map_indicator(assembly[i + 1]): # Don't increment pc as the symbol itself doesn't go into code if item in symbol_map: - print(assembly) raise CompilerPanic(f"duplicate jumpdest {item}") symbol_map[item] = pc From 69d0372d261dfcbd438f1321805001211d70f172 Mon Sep 17 00:00:00 2001 From: Charles Cooper Date: Tue, 20 Feb 2024 07:54:37 -0500 Subject: [PATCH 2/4] use a tighter loop --- vyper/ir/compile_ir.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/vyper/ir/compile_ir.py b/vyper/ir/compile_ir.py index eb40c03fe9..41b9021934 100644 --- a/vyper/ir/compile_ir.py +++ b/vyper/ir/compile_ir.py @@ -810,21 +810,21 @@ def _prune_unreachable_code(assembly): changed = False i = 0 while i < len(assembly) - 1: - instr = assembly[i] - if isinstance(instr, list): - instr = assembly[i][-1] - - is_terminal = assembly[i] in _TERMINAL_OPS - next_is_jumpdest = ( - i + 2 < len(assembly) and is_symbol(assembly[i + 1]) and assembly[i + 2] == "JUMPDEST" - ) - next_is_list = isinstance(assembly[i + 1], list) + if assembly[i] in _TERMINAL_OPS: + # find the next jumpdest or sublist + for j in range(i + 1, len(assembly)): + next_is_jumpdest = ( + j < len(assembly) - 1 + and is_symbol(assembly[j]) + and assembly[j + 1] == "JUMPDEST" + ) + next_is_list = isinstance(assembly[j], list) + if next_is_jumpdest or next_is_list: + break + changed = j > i + 1 + del assembly[i + 1 : j] - if is_terminal and not (next_is_jumpdest or next_is_list): - changed = True - del assembly[i + 1] - else: - i += 1 + i += 1 return changed From 44f28f39b03db67dac37e20f5268022109b1a892 Mon Sep 17 00:00:00 2001 From: Charles Cooper Date: Tue, 20 Feb 2024 08:05:59 -0500 Subject: [PATCH 3/4] fix an off-by-one --- tests/unit/compiler/venom/test_duplicate_operands.py | 2 +- vyper/ir/compile_ir.py | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/unit/compiler/venom/test_duplicate_operands.py b/tests/unit/compiler/venom/test_duplicate_operands.py index b96c7f3351..5ba2adafc8 100644 --- a/tests/unit/compiler/venom/test_duplicate_operands.py +++ b/tests/unit/compiler/venom/test_duplicate_operands.py @@ -24,4 +24,4 @@ def test_duplicate_operands(): asm = generate_assembly_experimental(ctx, optimize=OptimizationLevel.CODESIZE) - assert asm == ["PUSH1", 10, "DUP1", "DUP1", "DUP1", "ADD", "MUL", "STOP", "REVERT"] + assert asm == ["PUSH1", 10, "DUP1", "DUP1", "DUP1", "ADD", "MUL", "STOP"] diff --git a/vyper/ir/compile_ir.py b/vyper/ir/compile_ir.py index 41b9021934..8b09ae454f 100644 --- a/vyper/ir/compile_ir.py +++ b/vyper/ir/compile_ir.py @@ -821,6 +821,10 @@ def _prune_unreachable_code(assembly): next_is_list = isinstance(assembly[j], list) if next_is_jumpdest or next_is_list: break + else: + # fixup an off-by-one if we made it to the end of the assembly + # without finding an jumpdest or sublist + j = len(assembly) changed = j > i + 1 del assembly[i + 1 : j] From 2be68d7dc776884068c31c006da510e13d0c8cd3 Mon Sep 17 00:00:00 2001 From: Charles Cooper Date: Tue, 20 Feb 2024 08:06:10 -0500 Subject: [PATCH 4/4] use optimize=gas in a test --- tests/unit/compiler/venom/test_duplicate_operands.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/compiler/venom/test_duplicate_operands.py b/tests/unit/compiler/venom/test_duplicate_operands.py index 5ba2adafc8..437185cc72 100644 --- a/tests/unit/compiler/venom/test_duplicate_operands.py +++ b/tests/unit/compiler/venom/test_duplicate_operands.py @@ -22,6 +22,6 @@ def test_duplicate_operands(): bb.append_instruction("mul", sum_, op) bb.append_instruction("stop") - asm = generate_assembly_experimental(ctx, optimize=OptimizationLevel.CODESIZE) + asm = generate_assembly_experimental(ctx, optimize=OptimizationLevel.GAS) assert asm == ["PUSH1", 10, "DUP1", "DUP1", "DUP1", "ADD", "MUL", "STOP"]