forked from vyperlang/vyper
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: implement new IR for vyper (venom IR) (vyperlang#3659)
this commit implements a new IR for the vyper compiler. most of the implementation is self-contained in the `./vyper/venom/` directory. Venom IR is LLVM-"inspired", although we do not use LLVM on account of: 1) not wanting to introduce a large external dependency 2) no EVM backend exists for LLVM, so we would have to write one ourselves. see prior work at https://github.com/etclabscore/evm_llvm. fundamentally, LLVM is architected to target register machines; an EVM backend could conceivably be implmented, but it would always feel "bolted" on. 3) integration with LLVM would invariably be very complex 4) one advantage of using LLVM is getting multiple backends "for free", but in our case, none of the backends we are interested in (particularly EVM) have LLVM implementations. that being said, Venom is close enough to LLVM that it would seem fairly straightforward to pass "in-and-out" of LLVM, converting to LLVM to take advantage of its optimization passes and/or analysis utilities, and then converting back to Venom for final EVM emission, if that becomes desirable down the line. it could even provided as an "extra" -- if LLVM is installed on the system and enabled for the build, pass to LLVM for extra optimization, but otherwise the compiler being self-contained. for more details about the design and architecture of Venom IR, see `./vyper/venom/README.md`. note that this commit specifically focuses on the architecture, design and implementation of Venom. that is, more focus was spent on architecting the Venom compiler itself. the Vyper frontend does not emit Venom natively yet, Venom emission is implemented as a translation step from the current s-expr based IR to Venom. the translation is not feature-complete, and may have bugs. that being said, vyper compilation via Venom is experimentally available by passing the `--experimental-codegen` flag to vyper on the CLI. incrementally refactoring the codegen to use Venom instead of the earlier s-expr IR will be the next area of focus of development. --------- Co-authored-by: Charles Cooper <[email protected]>
- Loading branch information
1 parent
9a982bd
commit cbac5ab
Showing
28 changed files
with
2,994 additions
and
30 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
from vyper.compiler.settings import OptimizationLevel | ||
from vyper.venom import generate_assembly_experimental | ||
from vyper.venom.basicblock import IRLiteral | ||
from vyper.venom.function import IRFunction | ||
|
||
|
||
def test_duplicate_operands(): | ||
""" | ||
Test the duplicate operands code generation. | ||
The venom code: | ||
%1 = 10 | ||
%2 = add %1, %1 | ||
%3 = mul %1, %2 | ||
stop | ||
Should compile to: [PUSH1, 10, DUP1, DUP1, DUP1, ADD, MUL, STOP] | ||
""" | ||
ctx = IRFunction() | ||
|
||
op = ctx.append_instruction("store", [IRLiteral(10)]) | ||
sum = ctx.append_instruction("add", [op, op]) | ||
ctx.append_instruction("mul", [sum, op]) | ||
ctx.append_instruction("stop", [], False) | ||
|
||
asm = generate_assembly_experimental(ctx, OptimizationLevel.CODESIZE) | ||
|
||
assert asm == ["PUSH1", 10, "DUP1", "DUP1", "DUP1", "ADD", "MUL", "STOP", "REVERT"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
from vyper.venom.analysis import calculate_cfg | ||
from vyper.venom.basicblock import IRLiteral | ||
from vyper.venom.function import IRBasicBlock, IRFunction, IRLabel | ||
from vyper.venom.passes.normalization import NormalizationPass | ||
|
||
|
||
def test_multi_entry_block_1(): | ||
ctx = IRFunction() | ||
|
||
finish_label = IRLabel("finish") | ||
target_label = IRLabel("target") | ||
block_1_label = IRLabel("block_1", ctx) | ||
|
||
op = ctx.append_instruction("store", [IRLiteral(10)]) | ||
acc = ctx.append_instruction("add", [op, op]) | ||
ctx.append_instruction("jnz", [acc, finish_label, block_1_label], False) | ||
|
||
block_1 = IRBasicBlock(block_1_label, ctx) | ||
ctx.append_basic_block(block_1) | ||
acc = ctx.append_instruction("add", [acc, op]) | ||
op = ctx.append_instruction("store", [IRLiteral(10)]) | ||
ctx.append_instruction("mstore", [acc, op], False) | ||
ctx.append_instruction("jnz", [acc, finish_label, target_label], False) | ||
|
||
target_bb = IRBasicBlock(target_label, ctx) | ||
ctx.append_basic_block(target_bb) | ||
ctx.append_instruction("mul", [acc, acc]) | ||
ctx.append_instruction("jmp", [finish_label], False) | ||
|
||
finish_bb = IRBasicBlock(finish_label, ctx) | ||
ctx.append_basic_block(finish_bb) | ||
ctx.append_instruction("stop", [], False) | ||
|
||
calculate_cfg(ctx) | ||
assert not ctx.normalized, "CFG should not be normalized" | ||
|
||
NormalizationPass.run_pass(ctx) | ||
|
||
assert ctx.normalized, "CFG should be normalized" | ||
|
||
finish_bb = ctx.get_basic_block(finish_label.value) | ||
cfg_in = list(finish_bb.cfg_in.keys()) | ||
assert cfg_in[0].label.value == "target", "Should contain target" | ||
assert cfg_in[1].label.value == "finish_split_global", "Should contain finish_split_global" | ||
assert cfg_in[2].label.value == "finish_split_block_1", "Should contain finish_split_block_1" | ||
|
||
|
||
# more complicated one | ||
def test_multi_entry_block_2(): | ||
ctx = IRFunction() | ||
|
||
finish_label = IRLabel("finish") | ||
target_label = IRLabel("target") | ||
block_1_label = IRLabel("block_1", ctx) | ||
block_2_label = IRLabel("block_2", ctx) | ||
|
||
op = ctx.append_instruction("store", [IRLiteral(10)]) | ||
acc = ctx.append_instruction("add", [op, op]) | ||
ctx.append_instruction("jnz", [acc, finish_label, block_1_label], False) | ||
|
||
block_1 = IRBasicBlock(block_1_label, ctx) | ||
ctx.append_basic_block(block_1) | ||
acc = ctx.append_instruction("add", [acc, op]) | ||
op = ctx.append_instruction("store", [IRLiteral(10)]) | ||
ctx.append_instruction("mstore", [acc, op], False) | ||
ctx.append_instruction("jnz", [acc, target_label, finish_label], False) | ||
|
||
block_2 = IRBasicBlock(block_2_label, ctx) | ||
ctx.append_basic_block(block_2) | ||
acc = ctx.append_instruction("add", [acc, op]) | ||
op = ctx.append_instruction("store", [IRLiteral(10)]) | ||
ctx.append_instruction("mstore", [acc, op], False) | ||
# switch the order of the labels, for fun | ||
ctx.append_instruction("jnz", [acc, finish_label, target_label], False) | ||
|
||
target_bb = IRBasicBlock(target_label, ctx) | ||
ctx.append_basic_block(target_bb) | ||
ctx.append_instruction("mul", [acc, acc]) | ||
ctx.append_instruction("jmp", [finish_label], False) | ||
|
||
finish_bb = IRBasicBlock(finish_label, ctx) | ||
ctx.append_basic_block(finish_bb) | ||
ctx.append_instruction("stop", [], False) | ||
|
||
calculate_cfg(ctx) | ||
assert not ctx.normalized, "CFG should not be normalized" | ||
|
||
NormalizationPass.run_pass(ctx) | ||
|
||
assert ctx.normalized, "CFG should be normalized" | ||
|
||
finish_bb = ctx.get_basic_block(finish_label.value) | ||
cfg_in = list(finish_bb.cfg_in.keys()) | ||
assert cfg_in[0].label.value == "target", "Should contain target" | ||
assert cfg_in[1].label.value == "finish_split_global", "Should contain finish_split_global" | ||
assert cfg_in[2].label.value == "finish_split_block_1", "Should contain finish_split_block_1" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
def test_stack_at_external_return(): | ||
""" | ||
TODO: USE BOA DO GENERATE THIS TEST | ||
""" | ||
pass |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.