Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions tests/unit/cli/vyper_json/test_compile_json.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,11 +208,13 @@ def test_compile_json(input_json, input_bundle, experimental_codegen):
"object": data["bytecode"],
"opcodes": data["opcodes"],
"sourceMap": data["source_map"],
"symbolMap": data["symbol_map"],
},
"deployedBytecode": {
"object": data["bytecode_runtime"],
"opcodes": data["opcodes_runtime"],
"sourceMap": data["source_map_runtime"],
"symbolMap": data["symbol_map_runtime"],
},
"methodIdentifiers": data["method_identifiers"],
},
Expand Down
43 changes: 43 additions & 0 deletions tests/unit/compiler/test_symbol_map.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
from vyper.compiler import compile_code
from vyper.compiler.settings import Settings

TEST_CODE = """
@internal
def foo(a: uint256) -> uint256:
return a + 1

# force foo to not be inlined
@external
def bar(a: uint256) -> uint256:
return self.foo(a)

@external
def baz(a: uint256) -> uint256:
return self.foo(a + 1)
"""


def test_simple_map():
code = TEST_CODE
output = compile_code(
code,
output_formats=["symbol_map_runtime", "metadata"],
settings=Settings(experimental_codegen=True),
)
meta = output["metadata"]
symbol_map = output["symbol_map_runtime"]
foo_meta_ent = None
assert "function_info" in meta, "missing function info in metadata"
function_infos = meta["function_info"]
assert isinstance(function_infos, dict), "function info is not a dict"
for _, v in function_infos.items():
if v["name"] == "foo" and v["visibility"] == "internal":
foo_meta_ent = v
break
assert foo_meta_ent is not None, "didn't find entry for foo"
assert "venom_via_stack" in foo_meta_ent, "no stack info"
assert foo_meta_ent.get("venom_return_via_stack", False), "unexpected non-stack return"
assert foo_meta_ent["venom_via_stack"] == ["a"]
foo_id = foo_meta_ent["_ir_identifier"]
symbol_map_key = foo_id + "_runtime"
assert symbol_map_key in symbol_map, "missing constant start for foo()"
2 changes: 2 additions & 0 deletions vyper/cli/vyper_compile.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
userdoc - Natspec user documentation
devdoc - Natspec developer documentation
metadata - Contract metadata (intended for use by tooling developers)
symbol_map_runtime - Symbol values in runtime bytecode (intended for use by tooling developers)
symbol_map - Symbol values in deployable bytecode (intended for use by tooling developers)
combined_json - All of the above format options combined as single JSON output
layout - Storage layout of a Vyper contract
ast - AST (not yet annotated) in JSON format
Expand Down
6 changes: 6 additions & 0 deletions vyper/cli/vyper_json.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,11 @@
"evm.bytecode.object": "bytecode",
"evm.bytecode.opcodes": "opcodes",
"evm.bytecode.sourceMap": "source_map",
"evm.bytecode.symbolMap": "symbol_map",
"evm.deployedBytecode.object": "bytecode_runtime",
"evm.deployedBytecode.opcodes": "opcodes_runtime",
"evm.deployedBytecode.sourceMap": "source_map_runtime",
"evm.deployedBytecode.symbolMap": "symbol_map_runtime",
"interface": "interface",
"ir": "ir_dict",
"ir_runtime": "ir_runtime_dict",
Expand Down Expand Up @@ -425,6 +427,8 @@ def format_to_output_dict(compiler_data: dict) -> dict:
evm["opcodes"] = data["opcodes"]
if "source_map" in data:
evm["sourceMap"] = data["source_map"]
if "symbol_map" in data:
evm["symbolMap"] = data["symbol_map"]

if any(i + "_runtime" in data for i in evm_keys + pc_maps_keys):
evm = output_contracts.setdefault("evm", {}).setdefault("deployedBytecode", {})
Expand All @@ -434,6 +438,8 @@ def format_to_output_dict(compiler_data: dict) -> dict:
evm["opcodes"] = data["opcodes_runtime"]
if "source_map_runtime" in data:
evm["sourceMap"] = data["source_map_runtime"]
if "symbol_map_runtime" in data:
evm["symbolMap"] = data["symbol_map_runtime"]

if any(i in data for i in VENOM_KEYS):
venom = {}
Expand Down
2 changes: 2 additions & 0 deletions vyper/compiler/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@
"blueprint_bytecode": output.build_blueprint_bytecode_output,
"opcodes": output.build_opcodes_output,
"opcodes_runtime": output.build_opcodes_runtime_output,
"symbol_map": output.build_symbol_map,
"symbol_map_runtime": output.build_symbol_map_runtime,
}

INTERFACE_OUTPUT_FORMATS = [
Expand Down
22 changes: 22 additions & 0 deletions vyper/compiler/output.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@
from vyper.compiler.phases import CompilerData
from vyper.compiler.utils import build_gas_estimates
from vyper.evm import opcodes
from vyper.evm.assembler.symbols import resolve_symbols
from vyper.exceptions import VyperException
from vyper.ir import compile_ir
from vyper.semantics.types.function import ContractFunctionT, FunctionVisibility, StateMutability
from vyper.typing import StorageLayout
from vyper.utils import safe_relpath
from vyper.venom.ir_node_to_venom import _pass_via_stack, _returns_word
from vyper.warnings import ContractSizeLimit, vyper_warn


Expand Down Expand Up @@ -265,6 +267,14 @@ def _to_dict(func_t):
ret["source_id"] = func_t.decl_node.module_node.source_id
ret["function_id"] = func_t._function_id

if func_t.is_internal and compiler_data.settings.experimental_codegen:
pass_via_stack = _pass_via_stack(func_t)
pass_via_stack_list = [
arg for (arg, is_stack_arg) in pass_via_stack.items() if is_stack_arg
]
ret["venom_via_stack"] = pass_via_stack_list
ret["venom_return_via_stack"] = _returns_word(func_t)

keep_keys = {
"name",
"return_type",
Expand All @@ -279,6 +289,8 @@ def _to_dict(func_t):
"module_path",
"source_id",
"function_id",
"venom_via_stack",
"venom_return_via_stack",
}
ret = {k: v for k, v in ret.items() if k in keep_keys}
return ret
Expand Down Expand Up @@ -438,6 +450,16 @@ def _compress_source_map(ast_map, jump_map, bytecode_size):
return ";".join(ret)


def build_symbol_map(compiler_data: CompilerData) -> dict[str, int]:
sym, _, _ = resolve_symbols(compiler_data.assembly)
return {k.label: v for (k, v) in sym.items()}


def build_symbol_map_runtime(compiler_data: CompilerData) -> dict[str, int]:
sym, _, _ = resolve_symbols(compiler_data.assembly_runtime)
return {k.label: v for (k, v) in sym.items()}


def build_bytecode_output(compiler_data: CompilerData) -> str:
return f"0x{compiler_data.bytecode.hex()}"

Expand Down
2 changes: 1 addition & 1 deletion vyper/venom/ir_node_to_venom.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ def _append_return_args(fn: IRFunction, ofst: int = 0, size: int = 0):
# func_t: ContractFunctionT
@functools.lru_cache(maxsize=1024)
def _pass_via_stack(func_t) -> dict[str, bool]:
# returns a dict which returns True if a given argument (referered to
# returns a dict which returns True if a given argument (referred to
# by name) should be passed via the stack
if not ENABLE_NEW_CALL_CONV:
return {arg.name: False for arg in func_t.arguments}
Expand Down
Loading