Skip to content

Commit

Permalink
fix: handle evm_version in compiler settings
Browse files Browse the repository at this point in the history
ensure evm_version gets properly passed in through compiler passes
  • Loading branch information
charles-cooper committed Oct 18, 2023
1 parent 3201bf3 commit 7171aee
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 51 deletions.
61 changes: 32 additions & 29 deletions boa/vyper/compiler_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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):
Expand Down
56 changes: 34 additions & 22 deletions boa/vyper/contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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):
Expand All @@ -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
Expand Down

0 comments on commit 7171aee

Please sign in to comment.