diff --git a/boa/vyper/compiler_utils.py b/boa/vyper/compiler_utils.py index 545d4e70..c639aedb 100644 --- a/boa/vyper/compiler_utils.py +++ b/boa/vyper/compiler_utils.py @@ -5,6 +5,7 @@ from vyper.ast.utils import parse_to_ast from vyper.codegen.function_definitions import generate_ir_for_function from vyper.codegen.ir_node import IRnode +from vyper.evm.opcodes import anchor_evm_version from vyper.exceptions import InvalidType from vyper.ir import compile_ir, optimizer from vyper.semantics.analysis.utils import get_exact_type_from_node @@ -22,45 +23,47 @@ def compile_vyper_function(vyper_function, contract): """ compiler_data = contract.compiler_data - global_ctx = contract.global_ctx - ifaces = compiler_data.interface_codes - ast = parse_to_ast(vyper_function, ifaces) - vy_ast.folding.fold(ast) - # override namespace and add wrapper code at the top - with contract.override_vyper_namespace(): - analysis.add_module_namespace(ast, ifaces) - analysis.validate_functions(ast) + with anchor_evm_version(compiler_data.settings.evm_version): + global_ctx = contract.global_ctx + ifaces = compiler_data.interface_codes + ast = parse_to_ast(vyper_function, ifaces) + vy_ast.folding.fold(ast) - ast = ast.body[0] - func_t = ast._metadata["type"] + # override namespace and add wrapper code at the top + with contract.override_vyper_namespace(): + analysis.add_module_namespace(ast, ifaces) + analysis.validate_functions(ast) + + ast = ast.body[0] + func_t = ast._metadata["type"] - external_func_info = generate_ir_for_function(ast, global_ctx, False) - ir = external_func_info.common_ir + external_func_info = generate_ir_for_function(ast, global_ctx, False) + ir = external_func_info.common_ir - entry_label = func_t._ir_info.external_function_base_entry_label + entry_label = func_t._ir_info.external_function_base_entry_label - ir = ["seq", ["goto", entry_label], ir] + ir = ["seq", ["goto", entry_label], ir] - # use a dummy method id - ir = ["with", _METHOD_ID_VAR, 0, ir] + # use a dummy method id + ir = ["with", _METHOD_ID_VAR, 0, ir] - # first mush it with the rest of the IR in the contract to ensure - # all labels are present, and then optimize all together - # (use unoptimized IR, ir_executor can't handle optimized selector tables) - _, contract_runtime = contract.unoptimized_ir - ir = IRnode.from_list(["seq", ir, contract_runtime]) - ir = optimizer.optimize(ir) + # first mush it with the rest of the IR in the contract to ensure + # all labels are present, and then optimize all together + # (use unoptimized IR, ir_executor can't handle optimized selector tables) + _, contract_runtime = contract.unoptimized_ir + ir = IRnode.from_list(["seq", ir, contract_runtime]) + ir = optimizer.optimize(ir) - assembly = compile_ir.compile_to_assembly(ir) - bytecode, source_map = compile_ir.assembly_to_evm(assembly) - bytecode += contract.data_section - typ = func_t.return_type + assembly = compile_ir.compile_to_assembly(ir) + bytecode, source_map = compile_ir.assembly_to_evm(assembly) + bytecode += contract.data_section + typ = func_t.return_type - # generate the IR executor - ir_executor = executor_from_ir(ir, compiler_data) + # generate the IR executor + ir_executor = executor_from_ir(ir, compiler_data) - return ast, ir_executor, bytecode, source_map, typ + return ast, ir_executor, bytecode, source_map, typ def generate_bytecode_for_internal_fn(fn): diff --git a/boa/vyper/contract.py b/boa/vyper/contract.py index a78daf42..54d908cb 100644 --- a/boa/vyper/contract.py +++ b/boa/vyper/contract.py @@ -24,6 +24,7 @@ from vyper.codegen.module import generate_ir_for_module from vyper.compiler import output as compiler_output from vyper.compiler.settings import OptimizationLevel +from vyper.evm.opcodes import anchor_evm_version from vyper.exceptions import VyperException from vyper.ir.optimizer import optimize from vyper.semantics.analysis.data_positions import set_data_positions @@ -64,7 +65,8 @@ def __init__(self, compiler_data, filename=None): # force compilation so that if there are any errors in the contract, # we fail at load rather than at deploy time. - _ = compiler_data.bytecode + with anchor_evm_version(compiler_data.settings.evm_version): + _ = compiler_data.bytecode self.filename = filename @@ -609,9 +611,10 @@ def global_ctx(self): @property def source_map(self): if self._source_map is None: - _, self._source_map = compile_ir.assembly_to_evm( - self.compiler_data.assembly_runtime - ) + with anchor_evm_version(self.compiler_data.settings.evm_version): + _, self._source_map = compile_ir.assembly_to_evm( + self.compiler_data.assembly_runtime + ) return self._source_map def find_error_meta(self, computation): @@ -756,24 +759,27 @@ def _ast_module(self): module = copy.deepcopy(self.compiler_data.vyper_module) # do the same thing as vyper_module_folded but skip getter expansion - vy_ast.folding.fold(module) - with vy_ns.get_namespace().enter_scope(): - analysis.add_module_namespace(module, self.compiler_data.interface_codes) - analysis.validate_functions(module) - # we need to cache the namespace right here(!). - # set_data_positions will modify the type definitions in place. - self._cache_namespace(vy_ns.get_namespace()) + with anchor_evm_version(self.compiler_data.settings.evm_version): + vy_ast.folding.fold(module) + with vy_ns.get_namespace().enter_scope(): + analysis.add_module_namespace( + module, self.compiler_data.interface_codes + ) + analysis.validate_functions(module) + # we need to cache the namespace right here(!). + # set_data_positions will modify the type definitions in place. + self._cache_namespace(vy_ns.get_namespace()) - vy_ast.expansion.remove_unused_statements(module) - # calculate slots for all storage variables, tagging - # the types in the namespace. - set_data_positions(module, storage_layout_overrides=None) + vy_ast.expansion.remove_unused_statements(module) + # calculate slots for all storage variables, tagging + # the types in the namespace. + set_data_positions(module, storage_layout_overrides=None) - # ensure _ir_info is generated for all functions in this copied/shadow - # namespace - _ = generate_ir_for_module(GlobalContext(module)) + # ensure _ir_info is generated for all functions in this copied/shadow + # namespace + _ = generate_ir_for_module(GlobalContext(module)) - return module + return module # the global namespace is expensive to compute, so cache it def _cache_namespace(self, namespace): @@ -805,8 +811,11 @@ def override_vyper_namespace(self): # eliminator might prune a dead function (which we want to eval) @cached_property def unoptimized_assembly(self): - runtime = self.compiler_data.ir_runtime - return compile_ir.compile_to_assembly(runtime, optimize=OptimizationLevel.NONE) + with anchor_evm_version(self.compiler_data.settings.evm_version): + runtime = self.compiler_data.ir_runtime + return compile_ir.compile_to_assembly( + runtime, optimize=OptimizationLevel.NONE + ) @cached_property def data_section_size(self): @@ -829,12 +838,15 @@ def unoptimized_bytecode(self): @cached_property def unoptimized_ir(self): - with anchor_opt_level(OptimizationLevel.NONE): + with anchor_opt_level(OptimizationLevel.NONE), anchor_evm_version( + self.compiler_data.settings.evm_version + ): return generate_ir_for_module(self.compiler_data.global_ctx) @cached_property def ir_executor(self): _, ir_runtime = self.unoptimized_ir + # TODO: check if this needs anchor_evm_version return executor_from_ir(ir_runtime, self.compiler_data) @contextlib.contextmanager