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]: basic check #4483

Draft
wants to merge 4 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
70 changes: 70 additions & 0 deletions tests/unit/compiler/venom/test_venom_parser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
from tests.venom_utils import parse_from_basic_block
from vyper.venom.check_venom import VenomSemanticErrorType, check_venom


def test_venom_parser():
code = """
main:
%1 = 1
ret %1
"""

ctx = parse_from_basic_block(code)
errors = check_venom(ctx)

assert errors == []


def test_venom_parser_not_terminated():
code = """
bb0:
%1 = 1
bb1:
jmp @bb0
bb2:
stop
bb3:
calldataload 10, 20, 30
"""

ctx = parse_from_basic_block(code)
errors = check_venom(ctx)

all(e.error_type == VenomSemanticErrorType.NotTerminatedBasicBlock for e in errors)
assert len(errors) == 2
assert list(e.metadata.label.name for e in errors) == ["bb0", "bb3"]


def test_venom_parser_nonexistant_var():
code = """
main:
ret %1
"""

ctx = parse_from_basic_block(code)
errors = check_venom(ctx)

assert len(errors) == 1


def test_venom_parser_nonexistant_var2():
code = """
main:
%par = param
%1 = 1
jnz %par, @br1, @br2
br1:
%2 = 2
jmp @join
br2:
%3 = 3
jmp @join
join:
ret %1, %2, %3
"""

ctx = parse_from_basic_block(code)
errors = check_venom(ctx)

assert len(errors) == 2
assert {e.metadata.name for e in errors} == {"%2", "%3"}
10 changes: 9 additions & 1 deletion vyper/venom/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@

from vyper.codegen.ir_node import IRnode
from vyper.compiler.settings import OptimizationLevel, Settings
from vyper.exceptions import CompilerPanic
from vyper.venom.analysis.analysis import IRAnalysesCache
from vyper.venom.check_venom import check_venom_fn
from vyper.venom.context import IRContext
from vyper.venom.function import IRFunction
from vyper.venom.ir_node_to_venom import ir_node_to_venom
Expand Down Expand Up @@ -54,6 +56,12 @@
FloatAllocas(ac, fn).run_pass()

SimplifyCFGPass(ac, fn).run_pass()

errors = check_venom_fn(fn)
if errors != []: # pragma: nocover
print(errors)
raise CompilerPanic("venom sematic errors" + str(errors))

MakeSSA(ac, fn).run_pass()
# run algebraic opts before mem2var to reduce some pointer arithmetic
AlgebraicOptimizationPass(ac, fn).run_pass()
Expand Down Expand Up @@ -87,7 +95,7 @@

StoreExpansionPass(ac, fn).run_pass()

if optimize == OptimizationLevel.CODESIZE:
if optimize == OptimizationLevel.CODESIZE:https://www.youtube.com/watch?v=KuhCoUfMWx0

Check failure

Code scanning / CodeQL

Syntax error Error

Syntax Error (in Python 3).
ReduceLiteralsCodesize(ac, fn).run_pass()

DFTPass(ac, fn).run_pass()
Expand Down
72 changes: 72 additions & 0 deletions vyper/venom/check_venom.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
from dataclasses import dataclass
from enum import Enum
from typing import Any

from vyper.exceptions import CompilerPanic
from vyper.venom.analysis import IRAnalysesCache, LivenessAnalysis
from vyper.venom.basicblock import IRBasicBlock
from vyper.venom.context import IRContext
from vyper.venom.function import IRFunction


class VenomSemanticErrorType(Enum):
NotTerminatedBasicBlock = 1
NotDefinedVar = 2

def __repr__(self) -> str:
if self == VenomSemanticErrorType.NotTerminatedBasicBlock:
return "basic block does not terminate"
elif self == VenomSemanticErrorType.NotDefinedVar:
return "var not defined"
else:
raise CompilerPanic("unknown venom semantic error")


@dataclass
class VenomSemanticError:
error_type: VenomSemanticErrorType
metadata: Any


def check_venom_fn(fn: IRFunction) -> list[VenomSemanticError]:
errors = []

# check that all the bbs are terminated
for bb in fn.get_basic_blocks():
if not bb.is_terminated:
errors.append(VenomSemanticError(VenomSemanticErrorType.NotTerminatedBasicBlock, bb))

if errors != []:
return errors

ac = IRAnalysesCache(fn)
ac.request_analysis(LivenessAnalysis)
for bb in fn.get_basic_blocks():
e = _handle_incorect_liveness(bb)
errors.extend(e)
return errors


def check_venom(context: IRContext) -> list[VenomSemanticError]:
errors: list[VenomSemanticError] = []

for fn in context.functions.values():
errors.extend(check_venom_fn(fn))

return errors


def _handle_incorect_liveness(bb: IRBasicBlock) -> list[VenomSemanticError]:
errors = []
defined_here = set()
for inst in bb.instructions:
if inst.output is not None:
defined_here.add(inst.output)

before_live = set().union(*(in_bb.out_vars for in_bb in bb.cfg_in))

undef_vars = bb.instructions[-1].liveness.difference(before_live.union(defined_here))

for var in undef_vars:
errors.append(VenomSemanticError(VenomSemanticErrorType.NotDefinedVar, var))
return errors
Loading