Skip to content

Commit

Permalink
Merge branch 'master' into feat/improve-volatile
Browse files Browse the repository at this point in the history
  • Loading branch information
charles-cooper authored Dec 30, 2024
2 parents 0ad9a33 + 194d60a commit 353eb7c
Show file tree
Hide file tree
Showing 26 changed files with 638 additions and 115 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/pull-request.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ jobs:
# docs: documentation
# test: test suite
# lang: language changes
# stdlib: changes to the stdlib
# ux: language changes (UX)
# tool: integration
# ir: (old) IR/codegen changes
Expand All @@ -43,6 +44,7 @@ jobs:
docs
test
lang
stdlib
ux
tool
ir
Expand Down
105 changes: 105 additions & 0 deletions tests/functional/codegen/test_interfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -876,3 +876,108 @@ def bar() -> uint256:
input_bundle = make_input_bundle({"lib1.vy": lib1})
c = get_contract(main, input_bundle=input_bundle)
assert c.bar() == 1


def test_interface_with_flags():
code = """
struct MyStruct:
a: address
flag Foo:
BOO
MOO
POO
event Transfer:
sender: indexed(address)
@external
def bar():
pass
flag BAR:
BIZ
BAZ
BOO
@external
@view
def foo(s: MyStruct) -> MyStruct:
return s
"""

out = compile_code(code, contract_path="code.vy", output_formats=["interface"])["interface"]

assert "# Flags" in out
assert "flag Foo:" in out
assert "flag BAR" in out
assert "BOO" in out
assert "MOO" in out

compile_code(out, contract_path="code.vyi", output_formats=["interface"])


vyi_filenames = [
"test__test.vyi",
"test__t.vyi",
"t__test.vyi",
"t__t.vyi",
"t_t.vyi",
"test_test.vyi",
"t_test.vyi",
"test_t.vyi",
"_test_t__t_tt_.vyi",
"foo_bar_baz.vyi",
]


@pytest.mark.parametrize("vyi_filename", vyi_filenames)
def test_external_interface_names(vyi_filename):
code = """
@external
def foo():
...
"""

compile_code(code, contract_path=vyi_filename, output_formats=["external_interface"])


def test_external_interface_with_flag():
code = """
flag Foo:
Blah
@external
def foo() -> Foo:
...
"""

out = compile_code(code, contract_path="test__test.vyi", output_formats=["external_interface"])[
"external_interface"
]
assert "-> Foo:" in out


def test_external_interface_compiles_again():
code = """
@external
def foo() -> uint256:
...
@external
def bar(a:int32) -> uint256:
...
"""

out = compile_code(code, contract_path="test.vyi", output_formats=["external_interface"])[
"external_interface"
]
compile_code(out, contract_path="test.vyi", output_formats=["external_interface"])


@pytest.mark.xfail
def test_weird_interface_name():
# based on comment https://github.com/vyperlang/vyper/pull/4290#discussion_r1884137428
# we replace "_" for "" which results in an interface without name
out = compile_code("", contract_path="_.vyi", output_formats=["external_interface"])[
"external_interface"
]
assert "interface _:" in out
121 changes: 120 additions & 1 deletion tests/functional/venom/parser/test_parsing.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from tests.venom_utils import assert_ctx_eq
from tests.venom_utils import assert_bb_eq, assert_ctx_eq
from vyper.venom.basicblock import IRBasicBlock, IRLabel, IRLiteral, IRVariable
from vyper.venom.context import DataItem, DataSection, IRContext
from vyper.venom.function import IRFunction
Expand Down Expand Up @@ -231,3 +231,122 @@ def test_multi_function_and_data():
]

assert_ctx_eq(parsed_ctx, expected_ctx)


def test_phis():
# @external
# def _loop() -> uint256:
# res: uint256 = 9
# for i: uint256 in range(res, bound=10):
# res = res + i
# return res
source = """
function __main_entry {
__main_entry: ; IN=[] OUT=[fallback, 1_then] => {}
%27 = 0
%1 = calldataload %27
%28 = %1
%29 = 224
%2 = shr %29, %28
%31 = %2
%30 = 1729138561
%4 = xor %30, %31
%32 = %4
jnz %32, @fallback, @1_then
; (__main_entry)
1_then: ; IN=[__main_entry] OUT=[4_condition] => {%11, %var8_0}
%6 = callvalue
%33 = %6
%7 = iszero %33
%34 = %7
assert %34
%var8_0 = 9
%11 = 0
nop
jmp @4_condition
; (__main_entry)
4_condition: ; IN=[1_then, 5_body] OUT=[5_body, 7_exit] => {%11:3, %var8_0:2}
%var8_0:2 = phi @1_then, %var8_0, @5_body, %var8_0:3
%11:3 = phi @1_then, %11, @5_body, %11:4
%35 = %11:3
%36 = 9
%15 = xor %36, %35
%37 = %15
jnz %37, @5_body, @7_exit
; (__main_entry)
5_body: ; IN=[4_condition] OUT=[4_condition] => {%11:4, %var8_0:3}
%38 = %11:3
%39 = %var8_0:2
%22 = add %39, %38
%41 = %22
%40 = %var8_0:2
%24 = gt %40, %41
%42 = %24
%25 = iszero %42
%43 = %25
assert %43
%var8_0:3 = %22
%44 = %11:3
%45 = 1
%11:4 = add %45, %44
jmp @4_condition
; (__main_entry)
7_exit: ; IN=[4_condition] OUT=[] => {}
%46 = %var8_0:2
%47 = 64
mstore %47, %46
%48 = 32
%49 = 64
return %49, %48
; (__main_entry)
fallback: ; IN=[__main_entry] OUT=[] => {}
%50 = 0
%51 = 0
revert %51, %50
stop
; (__main_entry)
} ; close function __main_entry
"""
ctx = parse_venom(source)

expected_ctx = IRContext()
expected_ctx.add_function(entry_fn := IRFunction(IRLabel("__main_entry")))

expect_bb = IRBasicBlock(IRLabel("4_condition"), entry_fn)
entry_fn.append_basic_block(expect_bb)

expect_bb.append_instruction(
"phi",
IRLabel("1_then"),
IRVariable("%var8_0"),
IRLabel("5_body"),
IRVariable("%var8_0:3"),
ret=IRVariable("var8_0:2"),
)
expect_bb.append_instruction(
"phi",
IRLabel("1_then"),
IRVariable("%11"),
IRLabel("5_body"),
IRVariable("%11:4"),
ret=IRVariable("11:3"),
)
expect_bb.append_instruction("store", IRVariable("11:3"), ret=IRVariable("%35"))
expect_bb.append_instruction("store", IRLiteral(9), ret=IRVariable("%36"))
expect_bb.append_instruction("xor", IRVariable("%35"), IRVariable("%36"), ret=IRVariable("%15"))
expect_bb.append_instruction("store", IRVariable("%15"), ret=IRVariable("%37"))
expect_bb.append_instruction("jnz", IRVariable("%37"), IRLabel("5_body"), IRLabel("7_exit"))
# other basic blocks omitted for brevity

parsed_fn = next(iter(ctx.functions.values()))
assert_bb_eq(parsed_fn.get_basic_block(expect_bb.label.name), expect_bb)
98 changes: 97 additions & 1 deletion tests/functional/venom/test_venom_repr.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import copy
import glob
import textwrap

import pytest

from tests.venom_utils import assert_ctx_eq, parse_venom
from vyper.compiler import compile_code
from vyper.compiler.phases import generate_bytecode
from vyper.compiler.settings import OptimizationLevel
from vyper.venom import generate_assembly_experimental, run_passes_on
from vyper.venom.context import IRContext

"""
Expand All @@ -16,15 +21,106 @@ def get_example_vy_filenames():


@pytest.mark.parametrize("vy_filename", get_example_vy_filenames())
def test_round_trip(vy_filename, optimize, request):
def test_round_trip_examples(vy_filename, debug, optimize, compiler_settings, request):
"""
Check all examples round trip
"""
path = f"examples/{vy_filename}"
with open(path) as f:
vyper_source = f.read()

if debug and optimize == OptimizationLevel.CODESIZE:
# FIXME: some round-trips fail when debug is enabled due to labels
# not getting pinned
request.node.add_marker(pytest.mark.xfail(strict=False))

_round_trip_helper(vyper_source, optimize, compiler_settings)


# pure vyper sources
vyper_sources = [
"""
@external
def _loop() -> uint256:
res: uint256 = 9
for i: uint256 in range(res, bound=10):
res = res + i
return res
"""
]


@pytest.mark.parametrize("vyper_source", vyper_sources)
def test_round_trip_sources(vyper_source, debug, optimize, compiler_settings, request):
"""
Test vyper_sources round trip
"""
vyper_source = textwrap.dedent(vyper_source)

if debug and optimize == OptimizationLevel.CODESIZE:
# FIXME: some round-trips fail when debug is enabled due to labels
# not getting pinned
request.node.add_marker(pytest.mark.xfail(strict=False))

_round_trip_helper(vyper_source, optimize, compiler_settings)


def _round_trip_helper(vyper_source, optimize, compiler_settings):
# helper function to test venom round-tripping thru the parser
# use two helpers because run_passes_on and
# generate_assembly_experimental are both destructive (mutating) on
# the IRContext
_helper1(vyper_source, optimize)
_helper2(vyper_source, optimize, compiler_settings)


def _helper1(vyper_source, optimize):
"""
Check that we are able to run passes on the round-tripped venom code
and that it is valid (generates bytecode)
"""
# note: compiling any later stage than bb_runtime like `asm` or
# `bytecode` modifies the bb_runtime data structure in place and results
# in normalization of the venom cfg (which breaks again make_ssa)
out = compile_code(vyper_source, output_formats=["bb_runtime"])

bb_runtime = out["bb_runtime"]
venom_code = IRContext.__repr__(bb_runtime)

ctx = parse_venom(venom_code)

assert_ctx_eq(bb_runtime, ctx)

# check it's valid to run venom passes+analyses
# (note this breaks bytecode equality, in the future we should
# test that separately)
run_passes_on(ctx, optimize)

# test we can generate assembly+bytecode
asm = generate_assembly_experimental(ctx)
generate_bytecode(asm, compiler_metadata=None)


def _helper2(vyper_source, optimize, compiler_settings):
"""
Check that we can compile to bytecode, and without running venom passes,
that the output bytecode is equal to going through the normal vyper pipeline
"""
settings = copy.copy(compiler_settings)
# bytecode equivalence only makes sense if we use venom pipeline
settings.experimental_codegen = True

out = compile_code(vyper_source, settings=settings, output_formats=["bb_runtime"])
bb_runtime = out["bb_runtime"]
venom_code = IRContext.__repr__(bb_runtime)

ctx = parse_venom(venom_code)

assert_ctx_eq(bb_runtime, ctx)

# test we can generate assembly+bytecode
asm = generate_assembly_experimental(ctx, optimize=optimize)
bytecode = generate_bytecode(asm, compiler_metadata=None)

out = compile_code(vyper_source, settings=settings, output_formats=["bytecode_runtime"])
assert "0x" + bytecode.hex() == out["bytecode_runtime"]
2 changes: 2 additions & 0 deletions tests/unit/ast/test_ast_dict.py
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,7 @@ def foo():
"node_id": 0,
"path": "main.vy",
"source_id": 1,
"is_interface": False,
"type": {
"name": "main.vy",
"type_decl_node": {"node_id": 0, "source_id": 1},
Expand Down Expand Up @@ -1175,6 +1176,7 @@ def foo():
"node_id": 0,
"path": "lib1.vy",
"source_id": 0,
"is_interface": False,
"type": {
"name": "lib1.vy",
"type_decl_node": {"node_id": 0, "source_id": 0},
Expand Down
Loading

0 comments on commit 353eb7c

Please sign in to comment.