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[venom]: binopt #4281

Open
wants to merge 54 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
123c07d
start binopt
HodanPlodky Oct 7, 2024
bb80541
better way of doing it
HodanPlodky Oct 8, 2024
79e3880
idea with equivalence analysis
HodanPlodky Oct 8, 2024
59dcdf5
more rules
HodanPlodky Oct 9, 2024
b543c36
all the rules from the original optimizer should be done
HodanPlodky Oct 9, 2024
f4650e3
all the rules from the original optimizer were not done so I added so…
HodanPlodky Oct 9, 2024
32ff283
lint
HodanPlodky Oct 9, 2024
ba0e245
different order
HodanPlodky Oct 9, 2024
5e74741
fixed test which no longer needed to be expected to fail
HodanPlodky Oct 10, 2024
3868042
removed _handle_offsets and fully moved it to the rest of the bin opt…
HodanPlodky Oct 10, 2024
1105344
truthy rules start
HodanPlodky Oct 10, 2024
8e4500e
adapted test for out of bounds check to take account static asserts e…
HodanPlodky Oct 10, 2024
eebed21
adapted test for out of bounds check to take account static asserts e…
HodanPlodky Oct 10, 2024
41960e8
Merge branch 'master' into feat/binopt
HodanPlodky Oct 10, 2024
d025563
Merge branch 'master' into feat/binopt
HodanPlodky Oct 10, 2024
4c5ee06
truthy continuation
HodanPlodky Oct 11, 2024
63fdfd5
truthy fix of eq
HodanPlodky Oct 13, 2024
3a6dbf9
more comparison ops opt
HodanPlodky Oct 13, 2024
a4e152b
better check of dict from charles
HodanPlodky Oct 15, 2024
ec0b5f2
Merge branch 'master' into feat/binopt
HodanPlodky Oct 15, 2024
c28dc4a
quick fix after merge
HodanPlodky Oct 15, 2024
2f1367f
more rules
HodanPlodky Oct 15, 2024
6150fc7
Merge branch 'master' into feat/binopt
HodanPlodky Oct 15, 2024
a4e996a
used commutative
HodanPlodky Oct 15, 2024
6f3a06f
better adding of additional instructions
HodanPlodky Oct 15, 2024
052f6bb
better rules handling start
HodanPlodky Oct 16, 2024
db0d608
better rules handling start
HodanPlodky Oct 16, 2024
83dec6a
more rules moved
HodanPlodky Oct 16, 2024
e52f8e8
Merge branch 'master' into feat/binopt
HodanPlodky Oct 17, 2024
9620ed1
more rules and more way to implement them
HodanPlodky Oct 17, 2024
372317c
more rules in different way
HodanPlodky Oct 17, 2024
7e69bf5
more rules in different way
HodanPlodky Oct 18, 2024
d084c1f
move some of the binopt into the sccp
HodanPlodky Oct 23, 2024
b2c8bd2
fix of the negative number rules
HodanPlodky Oct 24, 2024
db29ad5
removed the opts from algebraic ops
HodanPlodky Oct 24, 2024
9655460
lint + cleanup
HodanPlodky Oct 24, 2024
4b0ec3b
moved test for offsets from algebraic optimizer to sccp test
HodanPlodky Oct 24, 2024
8989fd9
added jnz to truthy
HodanPlodky Oct 24, 2024
9603690
uses calc on fly
HodanPlodky Oct 24, 2024
7bf4667
uses calc on fly fix
HodanPlodky Oct 24, 2024
22df052
sccp reduced recalc
HodanPlodky Oct 24, 2024
53d4122
used dfg instead of the different uses computation
HodanPlodky Oct 25, 2024
c63ab04
Merge branch 'master' into feat/binopt
HodanPlodky Oct 28, 2024
137cb1e
dfg calculation on fly and assert weird behaviour
HodanPlodky Oct 28, 2024
1e0aa69
sccp static assert fix
HodanPlodky Oct 29, 2024
b99ea0e
lint and comments
HodanPlodky Oct 29, 2024
4da9851
cleanup of the algebraic pass
HodanPlodky Oct 30, 2024
051d291
added the explanation for pass in except branch in test
HodanPlodky Oct 30, 2024
b140669
test sccp cleanup after debug
HodanPlodky Oct 30, 2024
565deee
sccp cleanup (comparison ops)
HodanPlodky Oct 30, 2024
39b5ee1
sccp cleanup (recalc)
HodanPlodky Oct 30, 2024
f0b7a86
lint
HodanPlodky Oct 30, 2024
180aa28
dfg cleanup
HodanPlodky Oct 30, 2024
fafc256
Merge branch 'master' into feat/binopt
HodanPlodky Oct 30, 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
19 changes: 15 additions & 4 deletions tests/functional/builtins/codegen/test_slice.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@
from vyper.compiler import compile_code
from vyper.compiler.settings import OptimizationLevel, Settings
from vyper.evm.opcodes import version_check
from vyper.exceptions import ArgumentException, CompilerPanic, TypeMismatch
from vyper.exceptions import (
ArgumentException,
CompilerPanic,
StaticAssertionException,
TypeMismatch,
)

_fun_bytes32_bounds = [(0, 32), (3, 29), (27, 5), (0, 5), (5, 3), (30, 2)]

Expand Down Expand Up @@ -533,9 +538,15 @@ def do_slice():

@pytest.mark.parametrize("bad_code", oob_fail_list)
def test_slice_buffer_oob_reverts(bad_code, get_contract, tx_failed):
c = get_contract(bad_code)
with tx_failed():
c.do_slice()
try:
c = get_contract(bad_code)
with tx_failed():
c.do_slice()
except StaticAssertionException:
Fixed Show fixed Hide fixed
# it should be ok if we
# catch the assert in compile time
# since it supposed to be revert
pass


# tests all 3 adhoc locations: `msg.data`, `self.code`, `<address>.code`
Expand Down
4 changes: 0 additions & 4 deletions tests/functional/codegen/features/test_constructor.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
import pytest

from tests.evm_backends.base_env import _compile
from vyper.exceptions import StackTooDeep
from vyper.utils import method_id


Expand Down Expand Up @@ -169,7 +166,6 @@ def get_foo() -> uint256:
assert c.get_foo() == 39


@pytest.mark.venom_xfail(raises=StackTooDeep, reason="stack scheduler regression")
def test_nested_dynamic_array_constructor_arg_2(env, get_contract):
code = """
foo: int128
Expand Down
51 changes: 0 additions & 51 deletions tests/unit/compiler/venom/test_algebraic_optimizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,57 +128,6 @@ def test_interleaved_case(interleave_point):
assert bb.instructions[-1].operands[0] == op3


def test_offsets():
ctx = IRContext()
fn = ctx.create_function("_global")

bb = fn.get_basic_block()

br1 = IRBasicBlock(IRLabel("then"), fn)
fn.append_basic_block(br1)
br2 = IRBasicBlock(IRLabel("else"), fn)
fn.append_basic_block(br2)

p1 = bb.append_instruction("param")
op1 = bb.append_instruction("store", 32)
op2 = bb.append_instruction("add", 0, IRLabel("mem"))
op3 = bb.append_instruction("store", 64)
bb.append_instruction("dloadbytes", op1, op2, op3)
op5 = bb.append_instruction("mload", op3)
op6 = bb.append_instruction("iszero", op5)
bb.append_instruction("jnz", op6, br1.label, br2.label)

op01 = br1.append_instruction("store", 32)
op02 = br1.append_instruction("add", 0, IRLabel("mem"))
op03 = br1.append_instruction("store", 64)
br1.append_instruction("dloadbytes", op01, op02, op03)
op05 = br1.append_instruction("mload", op03)
op06 = br1.append_instruction("iszero", op05)
br1.append_instruction("return", p1, op06)

op11 = br2.append_instruction("store", 32)
op12 = br2.append_instruction("add", 0, IRLabel("mem"))
op13 = br2.append_instruction("store", 64)
br2.append_instruction("dloadbytes", op11, op12, op13)
op15 = br2.append_instruction("mload", op13)
op16 = br2.append_instruction("iszero", op15)
br2.append_instruction("return", p1, op16)

ac = IRAnalysesCache(fn)
MakeSSA(ac, fn).run_pass()
AlgebraicOptimizationPass(ac, fn).run_pass()
RemoveUnusedVariablesPass(ac, fn).run_pass()

offset_count = 0
for bb in fn.get_basic_blocks():
for instruction in bb.instructions:
assert instruction.opcode != "add"
if instruction.opcode == "offset":
offset_count += 1

assert offset_count == 3


# Test the case of https://github.com/vyperlang/vyper/issues/4288
def test_ssa_after_algebraic_optimization():
code = """
Expand Down
57 changes: 54 additions & 3 deletions tests/unit/compiler/venom/test_sccp.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ def test_cont_jump_case():
op1 = bb.append_instruction("store", 32)
op2 = bb.append_instruction("store", 64)
op3 = bb.append_instruction("add", op1, op2)
bb.append_instruction("jnz", op3, br1.label, br2.label)
bb.append_instruction("jnz", p1, br1.label, br2.label)

br1.append_instruction("add", op3, 10)
br1.append_instruction("stop")
Expand Down Expand Up @@ -149,7 +149,7 @@ def test_cont_phi_case():
op1 = bb.append_instruction("store", 32)
op2 = bb.append_instruction("store", 64)
op3 = bb.append_instruction("add", op1, op2)
bb.append_instruction("jnz", op3, br1.label, br2.label)
bb.append_instruction("jnz", p1, br1.label, br2.label)

op4 = br1.append_instruction("add", op3, 10)
br1.append_instruction("jmp", join.label)
Expand Down Expand Up @@ -209,7 +209,7 @@ def test_cont_phi_const_case():
assert sccp.lattice[IRVariable("%4")].value == 96
assert sccp.lattice[IRVariable("%5", version=1)].value == 106
assert sccp.lattice[IRVariable("%5", version=2)].value == 97
assert sccp.lattice[IRVariable("%5")].value == 2
assert sccp.lattice[IRVariable("%5")] == LatticeEnum.BOTTOM


def test_phi_reduction_after_unreachable_block():
Expand Down Expand Up @@ -241,3 +241,54 @@ def test_phi_reduction_after_unreachable_block():
assert join.instructions[0].operands == [op1]

assert join.instructions[1].opcode == "stop"


def test_sccp_offsets_opt():
ctx = IRContext()
fn = ctx.create_function("_global")

bb = fn.get_basic_block()

br1 = IRBasicBlock(IRLabel("then"), fn)
fn.append_basic_block(br1)
br2 = IRBasicBlock(IRLabel("else"), fn)
fn.append_basic_block(br2)

p1 = bb.append_instruction("param")
op1 = bb.append_instruction("store", 32)
op2 = bb.append_instruction("add", 0, IRLabel("mem"))
op3 = bb.append_instruction("store", 64)
bb.append_instruction("dloadbytes", op1, op2, op3)
op5 = bb.append_instruction("mload", op3)
op6 = bb.append_instruction("iszero", op5)
bb.append_instruction("jnz", op6, br1.label, br2.label)

op01 = br1.append_instruction("store", 32)
op02 = br1.append_instruction("add", 0, IRLabel("mem"))
op03 = br1.append_instruction("store", 64)
br1.append_instruction("dloadbytes", op01, op02, op03)
op05 = br1.append_instruction("mload", op03)
op06 = br1.append_instruction("iszero", op05)
br1.append_instruction("return", p1, op06)

op11 = br2.append_instruction("store", 32)
op12 = br2.append_instruction("add", 0, IRLabel("mem"))
op13 = br2.append_instruction("store", 64)
br2.append_instruction("dloadbytes", op11, op12, op13)
op15 = br2.append_instruction("mload", op13)
op16 = br2.append_instruction("iszero", op15)
br2.append_instruction("return", p1, op16)

ac = IRAnalysesCache(fn)
MakeSSA(ac, fn).run_pass()
SCCP(ac, fn).run_pass()
# RemoveUnusedVariablesPass(ac, fn).run_pass()

offset_count = 0
for bb in fn.get_basic_blocks():
for instruction in bb.instructions:
assert instruction.opcode != "add"
if instruction.opcode == "offset":
offset_count += 1

assert offset_count == 3
2 changes: 1 addition & 1 deletion vyper/venom/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ def _run_passes(fn: IRFunction, optimize: OptimizationLevel) -> None:
MakeSSA(ac, fn).run_pass()
SCCP(ac, fn).run_pass()
StoreElimination(ac, fn).run_pass()
SimplifyCFGPass(ac, fn).run_pass()
AlgebraicOptimizationPass(ac, fn).run_pass()
SimplifyCFGPass(ac, fn).run_pass()
# NOTE: MakeSSA is after algebraic optimization it currently produces
# smaller code by adding some redundant phi nodes. This is not a
# problem for us, but we need to be aware of it, and should be
Expand Down
26 changes: 9 additions & 17 deletions vyper/venom/passes/algebraic_optimization.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from vyper.venom.analysis import DFGAnalysis, LivenessAnalysis
from vyper.venom.basicblock import IRInstruction, IRLabel, IRLiteral, IROperand
from vyper.venom.analysis.dfg import DFGAnalysis
from vyper.venom.analysis.liveness import LivenessAnalysis
from vyper.venom.basicblock import IRInstruction, IROperand, IRVariable
from vyper.venom.passes.base_pass import IRPass


Expand All @@ -11,6 +12,8 @@ class AlgebraicOptimizationPass(IRPass):
* iszero chains
"""

dfg: DFGAnalysis

def _optimize_iszero_chains(self) -> None:
fn = self.function
for bb in fn.get_basic_blocks():
Expand All @@ -23,6 +26,7 @@ def _optimize_iszero_chains(self) -> None:
if iszero_count == 0:
continue

assert isinstance(inst.output, IRVariable)
for use_inst in self.dfg.get_uses(inst.output):
opcode = use_inst.opcode

Expand All @@ -48,6 +52,8 @@ def _get_iszero_chain(self, op: IROperand) -> list[IRInstruction]:
chain: list[IRInstruction] = []

while True:
if not isinstance(op, IRVariable):
break
inst = self.dfg.get_producing_instruction(op)
if inst is None or inst.opcode != "iszero":
break
Expand All @@ -57,24 +63,10 @@ def _get_iszero_chain(self, op: IROperand) -> list[IRInstruction]:
chain.reverse()
return chain

def _handle_offsets(self):
for bb in self.function.get_basic_blocks():
for inst in bb.instructions:
# check if the instruction is of the form
# `add <ptr> <label>`
# this works only if store chains have been eliminated.
if (
inst.opcode == "add"
and isinstance(inst.operands[0], IRLiteral)
and isinstance(inst.operands[1], IRLabel)
):
inst.opcode = "offset"

def run_pass(self):
self.dfg = self.analyses_cache.request_analysis(DFGAnalysis)
self.dfg = self.analyses_cache.request_analysis(DFGAnalysis) # type: ignore

self._optimize_iszero_chains()
self._handle_offsets()

self.analyses_cache.invalidate_analysis(DFGAnalysis)
self.analyses_cache.invalidate_analysis(LivenessAnalysis)
Loading
Loading