Skip to content

Commit 2fa2dcc

Browse files
feat[venom]: implement new calling convention (#4482)
this commit updates the IRnode->venom translator to use stack arguments when the arguments are word-size and under a certain threshold. to ensure that all the contracts in `tests/functional/examples/thirdparty/` compile, the threshold is set to 6. we could relax this later if we have interprocedural optimizations which can migrate the stack arguments back to memory. it also updates the inliner and mem2var to handle cases from the new calling convention. --------- Co-authored-by: Charles Cooper <[email protected]>
1 parent 3a19f5c commit 2fa2dcc

File tree

11 files changed

+308
-43
lines changed

11 files changed

+308
-43
lines changed
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
def test_call_unused_param_return_tuple(get_contract):
2+
code = """
3+
@internal
4+
def _foo(a: uint256, b: uint256) -> (uint256, uint256):
5+
return a, b
6+
7+
@external
8+
def foo() -> (uint256, uint256):
9+
return self._foo(1, 2)
10+
"""
11+
12+
c = get_contract(code)
13+
14+
assert c.foo() == (1, 2)
15+
16+
17+
def test_returning_immutables(get_contract):
18+
"""
19+
This test checks that we can return an immutable from an internal function, which results in
20+
the immutable being copied into the return buffer with `dloadbytes`.
21+
"""
22+
contract = """
23+
a: immutable(uint256)
24+
25+
@deploy
26+
def __init__():
27+
a = 5
28+
29+
@internal
30+
def get_my_immutable() -> uint256:
31+
return a
32+
33+
@external
34+
def get_immutable() -> uint256:
35+
return self.get_my_immutable()
36+
"""
37+
c = get_contract(contract)
38+
assert c.get_immutable() == 5

tests/functional/venom/parser/test_parsing.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ def test_multi_function():
151151
expected_ctx.add_function(entry_fn := IRFunction(IRLabel("entry")))
152152

153153
entry_bb = entry_fn.get_basic_block("entry")
154-
entry_bb.append_instruction("invoke", IRLabel("check_cv"))
154+
entry_bb.append_invoke_instruction([IRLabel("check_cv")], returns=False)
155155
entry_bb.append_instruction("jmp", IRLabel("wow"))
156156

157157
entry_fn.append_basic_block(wow_bb := IRBasicBlock(IRLabel("wow"), entry_fn))
@@ -213,7 +213,7 @@ def test_multi_function_and_data():
213213
expected_ctx.add_function(entry_fn := IRFunction(IRLabel("entry")))
214214

215215
entry_bb = entry_fn.get_basic_block("entry")
216-
entry_bb.append_instruction("invoke", IRLabel("check_cv"))
216+
entry_bb.append_invoke_instruction([IRLabel("check_cv")], returns=False)
217217
entry_bb.append_instruction("jmp", IRLabel("wow"))
218218

219219
entry_fn.append_basic_block(wow_bb := IRBasicBlock(IRLabel("wow"), entry_fn))

tests/functional/venom/test_venom_repr.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ def test_round_trip_examples(vy_filename, debug, optimize, compiler_settings, re
2525
"""
2626
Check all examples round trip
2727
"""
28+
if not compiler_settings.experimental_codegen:
29+
pytest.skip("tests n/a when venom is not enabled")
30+
2831
path = f"examples/{vy_filename}"
2932
with open(path) as f:
3033
vyper_source = f.read()
@@ -55,6 +58,9 @@ def test_round_trip_sources(vyper_source, debug, optimize, compiler_settings, re
5558
"""
5659
Test vyper_sources round trip
5760
"""
61+
if not compiler_settings.experimental_codegen:
62+
pytest.skip("tests n/a when venom is not enabled")
63+
5864
vyper_source = textwrap.dedent(vyper_source)
5965

6066
if debug and optimize == OptimizationLevel.CODESIZE:

tests/unit/compiler/test_source_map.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ def foo(i: uint256):
143143
144144
@pure
145145
def bar(i: uint256) -> String[85]:
146+
# ensure the mod doesn't get erased
146147
return concat("foo foo", uint2str(i))
147148
"""
148149
error_map = compile_code(code, output_formats=["source_map"])["source_map"]["error_map"]

vyper/codegen/arithmetic.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,8 @@ def safe_div(x, y):
334334
def safe_mod(x, y):
335335
typ = x.typ
336336
MOD = "smod" if typ.is_signed else "mod"
337+
# TODO: (force) propagate safemod error msg down to all children,
338+
# overriding the "clamp" error msg.
337339
return IRnode.from_list([MOD, x, clamp("gt", y, 0)], error_msg="safemod")
338340

339341

vyper/codegen/self_call.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ def ir_for_self_call(stmt_expr, context):
8585
alloca = dataclasses.replace(alloca, _callsite=return_label)
8686
irnode = var.as_ir_node()
8787
irnode.passthrough_metadata["alloca"] = alloca
88+
irnode.passthrough_metadata["callsite_func"] = func_t
8889
arg_items.append(irnode)
8990
args_dst = IRnode.from_list(arg_items, typ=dst_tuple_t)
9091
else:

vyper/venom/basicblock.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,6 @@
7676
"selfdestruct",
7777
"stop",
7878
"invalid",
79-
"invoke",
8079
"jmp",
8180
"djmp",
8281
"jnz",
@@ -389,7 +388,7 @@ def remove_phi_operand(self, label: IRLabel) -> None:
389388
def code_size_cost(self) -> int:
390389
if self.opcode in ("ret", "param"):
391390
return 0
392-
if self.opcode == "store":
391+
if self.opcode in ("store", "palloca", "alloca", "calloca"):
393392
return 1
394393
return 2
395394

@@ -680,6 +679,12 @@ def __repr__(self) -> str:
680679
if printer and hasattr(printer, "_post_instruction"):
681680
s += printer._post_instruction(inst)
682681
s += "\n"
682+
683+
if len(self.instructions) > 30:
684+
s += f" ; {self.label}\n"
685+
if len(self.instructions) > 30 or self.parent.num_basic_blocks > 5:
686+
s += f" ; ({self.parent.name})\n\n"
687+
683688
return s
684689

685690

vyper/venom/function.py

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,16 @@
1313
from vyper.venom.context import IRContext
1414

1515

16-
@dataclass
16+
@dataclass(frozen=True)
1717
class IRParameter:
1818
name: str
19-
index: int
20-
offset: int
21-
size: int
22-
call_site_var: Optional[IRVariable]
23-
func_var: Optional[IRVariable]
24-
addr_var: Optional[IRVariable]
19+
index: int # needed?
20+
offset: int # needed?
21+
size: int # needed?
22+
id_: int
23+
call_site_var: Optional[IRVariable] # needed?
24+
func_var: IRVariable
25+
addr_var: Optional[IRVariable] # needed?
2526

2627

2728
class IRFunction:
@@ -136,9 +137,9 @@ def pop_source(self):
136137
assert len(self._error_msg_stack) > 0, "Empty error stack"
137138
self._error_msg_stack.pop()
138139

139-
def get_param_at_offset(self, offset: int) -> Optional[IRParameter]:
140+
def get_param_by_id(self, id_: int) -> Optional[IRParameter]:
140141
for param in self.args:
141-
if param.offset == offset:
142+
if param.id_ == id_:
142143
return param
143144
return None
144145

0 commit comments

Comments
 (0)