Skip to content

Commit

Permalink
Merge branch 'master' into feat/compile-solely-vyi
Browse files Browse the repository at this point in the history
  • Loading branch information
sandbubbles committed Dec 26, 2024
2 parents 37666bc + c9d8f5b commit 574bee1
Show file tree
Hide file tree
Showing 25 changed files with 1,042 additions and 251 deletions.
207 changes: 162 additions & 45 deletions tests/functional/venom/parser/test_parsing.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from tests.venom_utils import assert_ctx_eq
from vyper.venom.basicblock import IRBasicBlock, IRInstruction, IRLabel, IRLiteral, IRVariable
from vyper.venom.context import IRContext
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
from vyper.venom.parser import parse_venom

Expand All @@ -11,8 +11,6 @@ def test_single_bb():
main:
stop
}
[data]
"""

parsed_ctx = parse_venom(source)
Expand All @@ -38,8 +36,6 @@ def test_multi_bb_single_fn():
has_callvalue:
revert 0, 0
}
[data]
"""

parsed_ctx = parse_venom(source)
Expand All @@ -61,8 +57,6 @@ def test_multi_bb_single_fn():
has_callvalue_bb.append_instruction("revert", IRLiteral(0), IRLiteral(0))
has_callvalue_bb.append_instruction("stop")

start_fn.last_variable = 4

assert_ctx_eq(parsed_ctx, expected_ctx)


Expand All @@ -74,15 +68,16 @@ def test_data_section():
stop
}
[data]
dbname @selector_buckets
db @selector_bucket_0
db @fallback
db @selector_bucket_2
db @selector_bucket_3
db @fallback
db @selector_bucket_5
db @selector_bucket_6
data readonly {
dbsection selector_buckets:
db @selector_bucket_0
db @fallback
db @selector_bucket_2
db @selector_bucket_3
db @fallback
db @selector_bucket_5
db @selector_bucket_6
}
"""
)

Expand All @@ -91,14 +86,18 @@ def test_data_section():
entry_fn.get_basic_block("entry").append_instruction("stop")

expected_ctx.data_segment = [
IRInstruction("dbname", [IRLabel("selector_buckets")]),
IRInstruction("db", [IRLabel("selector_bucket_0")]),
IRInstruction("db", [IRLabel("fallback")]),
IRInstruction("db", [IRLabel("selector_bucket_2")]),
IRInstruction("db", [IRLabel("selector_bucket_3")]),
IRInstruction("db", [IRLabel("fallback")]),
IRInstruction("db", [IRLabel("selector_bucket_5")]),
IRInstruction("db", [IRLabel("selector_bucket_6")]),
DataSection(
IRLabel("selector_buckets"),
[
DataItem(IRLabel("selector_bucket_0")),
DataItem(IRLabel("fallback")),
DataItem(IRLabel("selector_bucket_2")),
DataItem(IRLabel("selector_bucket_3")),
DataItem(IRLabel("fallback")),
DataItem(IRLabel("selector_bucket_5")),
DataItem(IRLabel("selector_bucket_6")),
],
)
]

assert_ctx_eq(parsed_ctx, expected_ctx)
Expand Down Expand Up @@ -126,8 +125,6 @@ def test_multi_function():
has_value:
revert 0, 0
}
[data]
"""
)

Expand Down Expand Up @@ -157,8 +154,6 @@ def test_multi_function():
value_bb.append_instruction("revert", IRLiteral(0), IRLiteral(0))
value_bb.append_instruction("stop")

check_fn.last_variable = 2

assert_ctx_eq(parsed_ctx, expected_ctx)


Expand All @@ -185,13 +180,14 @@ def test_multi_function_and_data():
revert 0, 0
}
[data]
dbname @selector_buckets
db @selector_bucket_0
db @fallback
db @selector_bucket_2
db @selector_bucket_3
db @selector_bucket_6
data readonly {
dbsection selector_buckets:
db @selector_bucket_0
db @fallback
db @selector_bucket_2
db @selector_bucket_3
db @selector_bucket_6
}
"""
)

Expand Down Expand Up @@ -221,15 +217,136 @@ def test_multi_function_and_data():
value_bb.append_instruction("revert", IRLiteral(0), IRLiteral(0))
value_bb.append_instruction("stop")

check_fn.last_variable = 2

expected_ctx.data_segment = [
IRInstruction("dbname", [IRLabel("selector_buckets")]),
IRInstruction("db", [IRLabel("selector_bucket_0")]),
IRInstruction("db", [IRLabel("fallback")]),
IRInstruction("db", [IRLabel("selector_bucket_2")]),
IRInstruction("db", [IRLabel("selector_bucket_3")]),
IRInstruction("db", [IRLabel("selector_bucket_6")]),
DataSection(
IRLabel("selector_buckets"),
[
DataItem(IRLabel("selector_bucket_0")),
DataItem(IRLabel("fallback")),
DataItem(IRLabel("selector_bucket_2")),
DataItem(IRLabel("selector_bucket_3")),
DataItem(IRLabel("selector_bucket_6")),
],
)
]

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)
114 changes: 114 additions & 0 deletions tests/functional/venom/test_venom_repr.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
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.venom import generate_assembly_experimental, run_passes_on
from vyper.venom.context import IRContext

"""
Check that venom text format round-trips through parser
"""


def get_example_vy_filenames():
return glob.glob("**/*.vy", root_dir="examples/", recursive=True)


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

_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, optimize, compiler_settings):
"""
Test vyper_sources round trip
"""
vyper_source = textwrap.dedent(vyper_source)
_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"]
Loading

0 comments on commit 574bee1

Please sign in to comment.