Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat[ir]: return IRnode from context.new_variable() #3905

Closed
wants to merge 34 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
1c6d6f0
make settings into a global object
charles-cooper Mar 21, 2024
319be84
do not perform deallocation in venom pipeline
charles-cooper Mar 21, 2024
a047691
wip -- change new_variable to return IRnode with alloca metadata instead
charles-cooper Mar 21, 2024
b79403a
pass thru alloca in gep
charles-cooper Mar 21, 2024
685164c
fix a fixture scope
charles-cooper Mar 21, 2024
73a68f1
use more add_ofst / fix instances where IRnodes are not expected
charles-cooper Mar 21, 2024
21eefb3
fix some more instances
charles-cooper Mar 21, 2024
5518af3
fix a comment
charles-cooper Mar 21, 2024
4a3bd4b
fix bad evm_version fixture
charles-cooper Mar 23, 2024
b37d4d9
fix some fixtures
charles-cooper Mar 23, 2024
a5dd7bb
small fix
charles-cooper Mar 23, 2024
597d00a
fix another builtin bug
charles-cooper Mar 23, 2024
75169cc
fix typo
charles-cooper Mar 23, 2024
aa19207
fix a bad from_list
charles-cooper Mar 23, 2024
ab9d60f
last fixes
charles-cooper Mar 23, 2024
0f75973
simplify alloca, lint
charles-cooper Mar 23, 2024
3221c1c
update alloca check
charles-cooper Mar 23, 2024
5adc7d4
fix with variable shadowing
charles-cooper Mar 23, 2024
42289dd
update alloca generation
charles-cooper Mar 24, 2024
593d2ee
remove memory elision code
charles-cooper Mar 24, 2024
b22ab61
remove mload and mstore handling in ir_node_to_venom
charles-cooper Mar 24, 2024
77427f6
lint
charles-cooper Mar 24, 2024
607502d
fix buf in revert with reason
charles-cooper Mar 24, 2024
24d7eac
fix a variable lookup
charles-cooper Mar 24, 2024
df3b5b8
Merge branch 'master' into venom-alloca
charles-cooper Apr 2, 2024
f7d9432
fix order of arguments
charles-cooper Apr 2, 2024
e41e6d6
fix bad variable
charles-cooper Apr 2, 2024
00bc095
fix another variable
charles-cooper Apr 2, 2024
5c3231b
remove mstore from passthru instructions, add argument names
charles-cooper Apr 2, 2024
7569a34
another variable rename
charles-cooper Apr 2, 2024
4a6fd78
revert symbols change
charles-cooper Apr 3, 2024
b0e918e
fix operand order
charles-cooper Apr 3, 2024
1a27693
fix a defaults test
charles-cooper Apr 3, 2024
d372ad9
wip -- different kind of alloca for internal function params
charles-cooper Apr 3, 2024
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
41 changes: 25 additions & 16 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
from vyper.ast.grammar import parse_vyper_source
from vyper.codegen.ir_node import IRnode
from vyper.compiler.input_bundle import FilesystemInputBundle, InputBundle
from vyper.compiler.settings import OptimizationLevel, Settings, _set_debug_mode
from vyper.compiler.settings import OptimizationLevel, Settings, set_global_settings
from vyper.ir import compile_ir, optimizer
from vyper.utils import ERC5202_PREFIX

Expand Down Expand Up @@ -79,17 +79,17 @@ def output_formats():
return output_formats


@pytest.fixture(scope="module")
@pytest.fixture(scope="session")
def optimize(pytestconfig):
flag = pytestconfig.getoption("optimize")
return OptimizationLevel.from_string(flag)


@pytest.fixture(scope="session", autouse=True)
@pytest.fixture(scope="session")
def debug(pytestconfig):
debug = pytestconfig.getoption("enable_compiler_debug_mode")
assert isinstance(debug, bool)
_set_debug_mode(debug)
return debug


@pytest.fixture(scope="session")
Expand All @@ -99,6 +99,27 @@ def experimental_codegen(pytestconfig):
return ret


@pytest.fixture(scope="session")
def evm_version(pytestconfig):
# note: we configure the evm version that we emit code for,
# but eth-tester is only configured with the latest mainnet
# version. luckily, evms are backwards compatible.
evm_version_str = pytestconfig.getoption("evm_version")
assert isinstance(evm_version_str, str)
return evm_version_str


@pytest.fixture(scope="session", autouse=True)
def global_settings(evm_version, experimental_codegen, optimize, debug):
settings = Settings(
optimize=optimize,
evm_version=evm_version,
experimental_codegen=experimental_codegen,
debug=debug,
)
set_global_settings(settings)


@pytest.fixture(autouse=True)
def check_venom_xfail(request, experimental_codegen):
if not experimental_codegen:
Expand All @@ -122,18 +143,6 @@ def _xfail(*args, **kwargs):
return _xfail


@pytest.fixture(scope="session", autouse=True)
def evm_version(pytestconfig):
# note: we configure the evm version that we emit code for,
# but eth-tester is only configured with the latest mainnet
# version.
evm_version_str = pytestconfig.getoption("evm_version")
evm.DEFAULT_EVM_VERSION = evm_version_str
# this should get overridden by anchor_evm_version,
# but set it anyway
evm.active_evm_version = evm.EVM_VERSIONS[evm_version_str]


@pytest.fixture
def chdir_tmp_path(tmp_path):
# this is useful for when you want imports to have relpaths
Expand Down
12 changes: 6 additions & 6 deletions tests/unit/cli/vyper_json/test_get_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ def test_unknown_evm():


@pytest.mark.parametrize(
"evm_version",
"evm_version_str",
[
"homestead",
"tangerineWhistle",
Expand All @@ -22,11 +22,11 @@ def test_unknown_evm():
"berlin",
],
)
def test_early_evm(evm_version):
def test_early_evm(evm_version_str):
with pytest.raises(JSONError):
get_evm_version({"settings": {"evmVersion": evm_version}})
get_evm_version({"settings": {"evmVersion": evm_version_str}})


@pytest.mark.parametrize("evm_version", ["london", "paris", "shanghai", "cancun"])
def test_valid_evm(evm_version):
assert evm_version == get_evm_version({"settings": {"evmVersion": evm_version}})
@pytest.mark.parametrize("evm_version_str", ["london", "paris", "shanghai", "cancun"])
def test_valid_evm(evm_version_str):
assert evm_version_str == get_evm_version({"settings": {"evmVersion": evm_version_str}})
11 changes: 5 additions & 6 deletions tests/unit/compiler/ir/test_optimize_ir.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
import pytest

from vyper.codegen.ir_node import IRnode
from vyper.evm.opcodes import EVM_VERSIONS, anchor_evm_version
from vyper.evm.opcodes import version_check
from vyper.exceptions import StaticAssertionException
from vyper.ir import optimizer

POST_CANCUN = {k: v for k, v in EVM_VERSIONS.items() if v >= EVM_VERSIONS["cancun"]}


optimize_list = [
(["eq", 1, 2], [0]),
(["lt", 1, 2], [1]),
Expand Down Expand Up @@ -368,9 +365,9 @@ def test_operator_set_values():


@pytest.mark.parametrize("ir", mload_merge_list)
@pytest.mark.parametrize("evm_version", list(POST_CANCUN.keys()))
def test_mload_merge(ir, evm_version):
with anchor_evm_version(evm_version):
# TODO: use something like pytest.mark.post_cancun
if version_check(begin="cancun"):
optimized = optimizer.optimize(IRnode.from_list(ir[0]))
if ir[1] is None:
# no-op, assert optimizer does nothing
Expand All @@ -379,3 +376,5 @@ def test_mload_merge(ir, evm_version):
expected = IRnode.from_list(ir[1])

assert optimized == expected
else:
pytest.skip("no mcopy available")
9 changes: 4 additions & 5 deletions tests/unit/compiler/test_default_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,10 @@ def test_default_opt_level():
assert OptimizationLevel.default() == OptimizationLevel.GAS


def test_codegen_opt_level():
assert core._opt_level == OptimizationLevel.GAS
assert core._opt_gas() is True
assert core._opt_none() is False
assert core._opt_codesize() is False
def test_codegen_opt_level(optimize):
assert core._opt_gas() == (optimize == OptimizationLevel.GAS)
assert core._opt_none() == (optimize == OptimizationLevel.NONE)
assert core._opt_codesize() == (optimize == OptimizationLevel.CODESIZE)


def test_debug_mode(pytestconfig):
Expand Down
10 changes: 0 additions & 10 deletions tests/unit/compiler/test_opcodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,6 @@
from vyper.exceptions import CompilerPanic


@pytest.fixture(params=list(opcodes.EVM_VERSIONS))
def evm_version(request):
default = opcodes.active_evm_version
try:
opcodes.active_evm_version = opcodes.EVM_VERSIONS[request.param]
yield request.param
finally:
opcodes.active_evm_version = default


def test_opcodes():
code = """
@external
Expand Down
89 changes: 46 additions & 43 deletions vyper/builtins/functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -247,26 +247,26 @@ def _build_adhoc_slice_node(sub: IRnode, start: IRnode, length: IRnode, context:

dst_typ = BytesT(length.value)
# allocate a buffer for the return value
np = context.new_internal_variable(dst_typ)
buf = context.new_internal_variable(dst_typ)

# `msg.data` by `calldatacopy`
if sub.value == "~calldata":
node = [
"seq",
_make_slice_bounds_check(start, length, "calldatasize"),
["mstore", np, length],
["calldatacopy", np + 32, start, length],
np,
["mstore", buf, length],
["calldatacopy", add_ofst(buf, 32), start, length],
buf,
]

# `self.code` by `codecopy`
elif sub.value == "~selfcode":
node = [
"seq",
_make_slice_bounds_check(start, length, "codesize"),
["mstore", np, length],
["codecopy", np + 32, start, length],
np,
["mstore", buf, length],
["codecopy", add_ofst(buf, 32), start, length],
buf,
]

# `<address>.code` by `extcodecopy`
Expand All @@ -279,9 +279,9 @@ def _build_adhoc_slice_node(sub: IRnode, start: IRnode, length: IRnode, context:
[
"seq",
_make_slice_bounds_check(start, length, ["extcodesize", "_extcode_address"]),
["mstore", np, length],
["extcodecopy", "_extcode_address", np + 32, start, length],
np,
["mstore", buf, length],
["extcodecopy", "_extcode_address", add_ofst(buf, 32), start, length],
buf,
],
]

Expand Down Expand Up @@ -551,10 +551,8 @@ def build_IR(self, expr, context):

# respect API of copy_bytes
bufsize = dst_maxlen + 32
buf = context.new_internal_variable(BytesT(bufsize))

# Node representing the position of the output in memory
dst = IRnode.from_list(buf, typ=ret_typ, location=MEMORY, annotation="concat destination")
dst = context.new_internal_variable(BytesT(bufsize))
dst.annotation = "concat destination"

ret = ["seq"]
# stack item representing our current offset in the dst buffer
Expand Down Expand Up @@ -782,9 +780,9 @@ def build_IR(self, expr, args, kwargs, context):
# clear output memory first, ecrecover can return 0 bytes
["mstore", output_buf, 0],
["mstore", input_buf, args[0]],
["mstore", input_buf + 32, args[1]],
["mstore", input_buf + 64, args[2]],
["mstore", input_buf + 96, args[3]],
["mstore", add_ofst(input_buf, 32), args[1]],
["mstore", add_ofst(input_buf, 64), args[2]],
["mstore", add_ofst(input_buf, 96), args[3]],
["staticcall", "gas", 1, input_buf, 128, output_buf, 32],
["mload", output_buf],
],
Expand All @@ -798,9 +796,7 @@ def build_IR(self, expr, _args, kwargs, context):
args_tuple = ir_tuple_from_args(_args)

args_t = args_tuple.typ
input_buf = IRnode.from_list(
context.new_internal_variable(args_t), typ=args_t, location=MEMORY
)
input_buf = context.new_internal_variable(args_t)
ret_t = self._return_type

ret = ["seq"]
Expand Down Expand Up @@ -1149,9 +1145,7 @@ def build_IR(self, expr, args, kwargs, context):
args_ofst = add_ofst(input_buf, 32)
args_len = ["mload", input_buf]

output_node = IRnode.from_list(
context.new_internal_variable(BytesT(outsize)), typ=BytesT(outsize), location=MEMORY
)
output_node = context.new_internal_variable(BytesT(outsize))

bool_ty = BoolT()

Expand Down Expand Up @@ -1757,8 +1751,8 @@ def _build_create_IR(self, expr, args, context, value, salt):
return [
"seq",
["mstore", buf, forwarder_preamble],
["mstore", ["add", buf, preamble_length], aligned_target],
["mstore", ["add", buf, preamble_length + 20], forwarder_post],
["mstore", add_ofst(buf, preamble_length), aligned_target],
["mstore", add_ofst(buf, preamble_length + 20), forwarder_post],
_create_ir(value, buf, buf_len, salt=salt),
]

Expand Down Expand Up @@ -1864,9 +1858,7 @@ def _build_create_IR(self, expr, args, context, value, salt, code_offset, raw_ar
# pretend we allocated enough memory for the encoder
# (we didn't, but we are clobbering unused memory so it's safe.)
bufsz = to_encode.typ.abi_type.size_bound()
argbuf = IRnode.from_list(
context.new_internal_variable(get_type_for_exact_size(bufsz)), location=MEMORY
)
argbuf = context.new_internal_variable(get_type_for_exact_size(bufsz))

# return a complex expression which writes to memory and returns
# the length of the encoded data
Expand Down Expand Up @@ -2113,13 +2105,17 @@ def build_IR(self, expr, args, kwargs, context):
# clobber val, and return it as a pointer
[
"seq",
["mstore", ["sub", buf + n_digits, i], i],
["set", val, ["sub", buf + n_digits, i]],
["mstore", ["sub", add_ofst(buf, n_digits), i], i],
["set", val, ["sub", add_ofst(buf, n_digits), i]],
"break",
],
[
"seq",
["mstore", ["sub", buf + n_digits, i], ["add", 48, ["mod", val, 10]]],
[
"mstore",
["sub", add_ofst(buf, n_digits), i],
["add", 48, ["mod", val, 10]],
],
["set", val, ["div", val, 10]],
],
],
Expand All @@ -2135,7 +2131,7 @@ def build_IR(self, expr, args, kwargs, context):
ret = [
"if",
["eq", val, 0],
["seq", ["mstore", buf + 1, ord("0")], ["mstore", buf, 1], buf],
["seq", ["mstore", add_ofst(buf, 1), ord("0")], ["mstore", buf, 1], buf],
["seq", ret, val],
]

Expand Down Expand Up @@ -2313,7 +2309,7 @@ def build_IR(self, expr, args, kwargs, context):

ret = ["seq"]
ret.append(["mstore", buf, method_id])
encode = abi_encode(buf + 32, args_as_tuple, context, buflen, returns_len=True)
encode = abi_encode(add_ofst(buf, 32), args_as_tuple, context, buflen, returns_len=True)

else:
method_id = method_id_int("log(string,bytes)")
Expand All @@ -2327,15 +2323,17 @@ def build_IR(self, expr, args, kwargs, context):
ret.append(["mstore", schema_buf, len(schema)])

# TODO use Expr.make_bytelike, or better have a `bytestring` IRnode type
ret.append(["mstore", schema_buf + 32, bytes_to_int(schema.ljust(32, b"\x00"))])
ret.append(
["mstore", add_ofst(schema_buf, 32), bytes_to_int(schema.ljust(32, b"\x00"))]
)

payload_buflen = args_abi_t.size_bound()
payload_t = BytesT(payload_buflen)

# 32 bytes extra space for the method id
payload_buf = context.new_internal_variable(payload_t)
encode_payload = abi_encode(
payload_buf + 32, args_as_tuple, context, payload_buflen, returns_len=True
add_ofst(payload_buf, 32), args_as_tuple, context, payload_buflen, returns_len=True
)

ret.append(["mstore", payload_buf, encode_payload])
Expand All @@ -2350,11 +2348,13 @@ def build_IR(self, expr, args, kwargs, context):
buflen = 32 + args_as_tuple.typ.abi_type.size_bound()
buf = context.new_internal_variable(get_type_for_exact_size(buflen))
ret.append(["mstore", buf, method_id])
encode = abi_encode(buf + 32, args_as_tuple, context, buflen, returns_len=True)
encode = abi_encode(add_ofst(buf, 32), args_as_tuple, context, buflen, returns_len=True)

# debug address that tooling uses
CONSOLE_ADDRESS = 0x000000000000000000636F6E736F6C652E6C6F67
ret.append(["staticcall", "gas", CONSOLE_ADDRESS, buf + 28, ["add", 4, encode], 0, 0])
ret.append(
["staticcall", "gas", CONSOLE_ADDRESS, add_ofst(buf, 28), ["add", 4, encode], 0, 0]
)

return IRnode.from_list(ret, annotation="print:" + sig)

Expand Down Expand Up @@ -2457,15 +2457,19 @@ def build_IR(self, expr, args, kwargs, context):
# <32 bytes length> | <4 bytes method_id> | <everything else>
# write the unaligned method_id first, then we will
# overwrite the 28 bytes of zeros with the bytestring length
ret += [["mstore", buf + 4, method_id]]
ret += [["mstore", add_ofst(buf, 4), method_id]]
# abi encode, and grab length as stack item
length = abi_encode(buf + 36, encode_input, context, returns_len=True, bufsz=maxlen)
length = abi_encode(
add_ofst(buf, 36), encode_input, context, returns_len=True, bufsz=maxlen
)
# write the output length to where bytestring stores its length
ret += [["mstore", buf, ["add", length, 4]]]

else:
# abi encode and grab length as stack item
length = abi_encode(buf + 32, encode_input, context, returns_len=True, bufsz=maxlen)
length = abi_encode(
add_ofst(buf, 32), encode_input, context, returns_len=True, bufsz=maxlen
)
# write the output length to where bytestring stores its length
ret += [["mstore", buf, length]]

Expand Down Expand Up @@ -2550,13 +2554,12 @@ def build_IR(self, expr, args, kwargs, context):
# input validation

output_buf = context.new_internal_variable(wrapped_typ)
output = IRnode.from_list(output_buf, typ=wrapped_typ, location=MEMORY)

# sanity check buffer size for wrapped output type will not buffer overflow
assert wrapped_typ.memory_bytes_required == output_typ.memory_bytes_required
ret.append(make_setter(output, to_decode))
ret.append(make_setter(output_buf, to_decode))

ret.append(output)
ret.append(output_buf)
# finalize. set the type and location for the return buffer.
# (note: unwraps the tuple type if necessary)
ret = IRnode.from_list(ret, typ=output_typ, location=MEMORY)
Expand Down
Loading
Loading