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]: new DFTPass algorithm #4255

Draft
wants to merge 83 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
83 commits
Select commit Hold shift + click to select a range
e97bc88
print correct liveness and fence
harkal Jun 3, 2024
12487fc
improve ordering
harkal Jun 3, 2024
185ae23
r fences
harkal Jun 3, 2024
d8e6c74
remove old code
harkal Jun 3, 2024
4a87e93
adapt fensing
harkal Jun 3, 2024
24ad003
more
harkal Jun 3, 2024
5b7f60c
improve ordering and grouping of instructions in DFTPass
harkal Jun 4, 2024
2f0b4af
wip
harkal Jun 4, 2024
1f94e46
wip
harkal Jun 4, 2024
f12192c
squash
harkal Jun 11, 2024
d89adb6
wip - ament
harkal Jun 11, 2024
a581a09
pass all tests
harkal Jun 12, 2024
948f7f1
preserve last child if terminator
harkal Jun 12, 2024
8c99eee
cleanups
harkal Jun 12, 2024
58182ab
wip
harkal Jun 18, 2024
6efbce8
wip
harkal Jun 18, 2024
57353b2
no merge node
harkal Jun 18, 2024
8bac9f5
fix
harkal Jun 18, 2024
a332610
fix
harkal Jun 18, 2024
56fed17
gda
harkal Jun 19, 2024
93c7dcf
groups
harkal Jun 19, 2024
1958f48
wip
harkal Jun 19, 2024
ce584ac
wip
harkal Jun 19, 2024
81143c6
wip
harkal Jun 19, 2024
44e6880
better dep traversal
harkal Jun 19, 2024
ebeb9ee
wip
harkal Jun 19, 2024
fce4086
before circle breaking
harkal Jun 19, 2024
e7ce364
cycles detector
harkal Jun 19, 2024
58d7387
wip
harkal Jun 19, 2024
644e124
wip
harkal Jun 19, 2024
9f8511f
fix
harkal Jun 20, 2024
26c129c
wip
harkal Jun 20, 2024
ec29d76
grouping refactor
harkal Jun 20, 2024
f30b11c
cleanup
harkal Jun 20, 2024
44ebf21
spelling
harkal Jun 20, 2024
a8ffb79
`DUP1 SWAP1` optimization
harkal Jun 21, 2024
192ee8f
small cleanup and store expantion (not working)
harkal Jun 25, 2024
958974b
cleanup and add comments
harkal Jun 25, 2024
9e64c6e
lint
harkal Jun 25, 2024
3cd1e8b
fixes
harkal Sep 24, 2024
a008fe8
refactor
harkal Sep 24, 2024
23f2b65
lint
harkal Sep 24, 2024
e3c3ba4
remove store expantion pass
harkal Sep 24, 2024
ba88b4e
refactor
harkal Sep 24, 2024
1f0ae6a
Merge branch 'master' into feat/dft_upgrade
harkal Sep 26, 2024
3e08f83
properly hash
harkal Sep 26, 2024
3d3a4cf
review dead code
harkal Sep 26, 2024
8ccb393
refactor
harkal Sep 26, 2024
1bb6da6
`get_uses_in_bb()` utility method
harkal Sep 26, 2024
b61f9b9
refactor
harkal Sep 26, 2024
f259618
refactor
harkal Sep 26, 2024
e04285f
remove force
harkal Sep 26, 2024
588a818
refactor/cleanup
harkal Sep 26, 2024
d4f3cf0
combine children to allow for ordering heuristics later
harkal Sep 26, 2024
72b10b5
disable liveness print out for debuging purposes
harkal Sep 26, 2024
918cd5f
Merge branch 'master' into feat/dft_upgrade
harkal Sep 27, 2024
3d26886
work
harkal Sep 27, 2024
6f77fa8
lint
harkal Sep 27, 2024
7367822
wip
harkal Sep 27, 2024
0a9240b
wip
harkal Sep 27, 2024
7f89a8b
w
harkal Sep 27, 2024
ceffef6
remove duplicate `assert`, `assert_unreachable` from `VOLATILE_INSTRU…
harkal Sep 27, 2024
3f45f5e
new dep
harkal Sep 27, 2024
8cb64da
no groups
harkal Sep 27, 2024
2987d36
refactor[venom]: add effects to instructions
charles-cooper Sep 27, 2024
b1425ca
wip
harkal Sep 27, 2024
cfbb0eb
wip
harkal Sep 27, 2024
2a9a3ce
push iszero-assert together
harkal Sep 27, 2024
65b1351
cleanup
harkal Sep 27, 2024
dae696a
Revert "remove store expantion pass"
harkal Sep 28, 2024
db87ff8
enable store expansion
harkal Sep 28, 2024
d025298
improve naming
harkal Sep 28, 2024
e9b5303
fix get_write_effects()
charles-cooper Sep 28, 2024
36ff613
effects
harkal Sep 28, 2024
283d93a
effect deps
harkal Sep 28, 2024
f94e103
work
harkal Sep 28, 2024
8be0bef
Update vyper/venom/effects.py
charles-cooper Sep 30, 2024
fcb688f
update effects to be an enum.Flag
charles-cooper Sep 30, 2024
94aeeb0
effects magic
harkal Oct 1, 2024
b9046cc
Merge remote-tracking branch 'origin-vyper/master' into feat/dft_upgrade
harkal Oct 1, 2024
bdc1b4d
wip
harkal Oct 1, 2024
62616cd
disable effects
harkal Oct 1, 2024
805b079
Merge remote-tracking branch 'origin-charles/refactor/effects-analysi…
harkal Oct 1, 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
3 changes: 3 additions & 0 deletions vyper/ir/compile_ir.py
Original file line number Diff line number Diff line change
Expand Up @@ -1033,6 +1033,9 @@ def _stack_peephole_opts(assembly):
if assembly[i] == "SWAP1" and assembly[i + 1].lower() in COMMUTATIVE_OPS:
changed = True
del assembly[i]
if assembly[i] == "DUP1" and assembly[i + 1] == "SWAP1":
changed = True
del assembly[i + 1]
i += 1

return changed
Expand Down
2 changes: 1 addition & 1 deletion vyper/semantics/analysis/module.py
Original file line number Diff line number Diff line change
Expand Up @@ -898,7 +898,7 @@ def _import_to_path(level: int, module_str: str) -> PurePath:
base_path = "../" * (level - 1)
elif level == 1:
base_path = "./"
return PurePath(f"{base_path}{module_str.replace('.','/')}/")
return PurePath(f"{base_path}{module_str.replace('.', '/')}/")


# can add more, e.g. "vyper.builtins.interfaces", etc.
Expand Down
2 changes: 2 additions & 0 deletions vyper/venom/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

from typing import Optional

from vyper.venom.passes.store_expansion import StoreExpansionPass
from vyper.codegen.ir_node import IRnode
from vyper.compiler.settings import OptimizationLevel
from vyper.venom.analysis.analysis import IRAnalysesCache
Expand Down Expand Up @@ -56,6 +57,7 @@ def _run_passes(fn: IRFunction, optimize: OptimizationLevel) -> None:
BranchOptimizationPass(ac, fn).run_pass()
ExtractLiteralsPass(ac, fn).run_pass()
RemoveUnusedVariablesPass(ac, fn).run_pass()
StoreExpansionPass(ac, fn).run_pass()
DFTPass(ac, fn).run_pass()


Expand Down
2 changes: 1 addition & 1 deletion vyper/venom/analysis/analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,9 @@ def request_analysis(self, analysis_cls: Type[IRAnalysis], *args, **kwargs):
if analysis_cls in self.analyses_cache:
return self.analyses_cache[analysis_cls]
analysis = analysis_cls(self, self.function)
self.analyses_cache[analysis_cls] = analysis
analysis.analyze(*args, **kwargs)

self.analyses_cache[analysis_cls] = analysis
return analysis

def invalidate_analysis(self, analysis_cls: Type[IRAnalysis]):
Expand Down
2 changes: 1 addition & 1 deletion vyper/venom/analysis/cfg.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def analyze(self) -> None:
for bb in fn.get_basic_blocks():
assert len(bb.instructions) > 0, "Basic block should not be empty"
last_inst = bb.instructions[-1]
assert last_inst.is_bb_terminator, f"Last instruction should be a terminator {bb}"
assert last_inst.is_bb_terminator, "Last instruction should be a terminator"

for inst in bb.instructions:
if inst.opcode in CFG_ALTERING_INSTRUCTIONS:
Expand Down
8 changes: 7 additions & 1 deletion vyper/venom/analysis/dfg.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from vyper.venom.analysis.analysis import IRAnalysesCache, IRAnalysis
from vyper.venom.analysis.liveness import LivenessAnalysis
from vyper.venom.basicblock import IRInstruction, IRVariable
from vyper.venom.basicblock import IRBasicBlock, IRInstruction, IRVariable
from vyper.venom.function import IRFunction


Expand All @@ -19,6 +19,12 @@ def __init__(self, analyses_cache: IRAnalysesCache, function: IRFunction):
def get_uses(self, op: IRVariable) -> list[IRInstruction]:
return self._dfg_inputs.get(op, [])

def get_uses_in_bb(self, op: IRVariable, bb: IRBasicBlock):
"""
Get uses of a given variable in a specific basic block.
"""
return [inst for inst in self.get_uses(op) if inst.parent == bb]

# the instruction which produces this variable.
def get_producing_instruction(self, op: IRVariable) -> Optional[IRInstruction]:
return self._dfg_outputs.get(op)
Expand Down
2 changes: 1 addition & 1 deletion vyper/venom/analysis/liveness.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ def _calculate_out_vars(self, bb: IRBasicBlock) -> bool:
Compute out_vars of basic block.
Returns True if out_vars changed
"""
out_vars = bb.out_vars
out_vars = bb.out_vars.copy()
bb.out_vars = OrderedSet()
for out_bb in bb.cfg_out:
target_vars = self.input_vars_from(bb, out_bb)
Expand Down
80 changes: 72 additions & 8 deletions vyper/venom/basicblock.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from typing import TYPE_CHECKING, Any, Iterator, Optional, Union

import vyper.venom.effects as effects
from vyper.codegen.ir_node import IRnode
from vyper.utils import OrderedSet

Expand All @@ -21,8 +22,6 @@
"istore",
"tload",
"tstore",
"assert",
"assert_unreachable",
"mstore",
"mload",
"calldatacopy",
Expand Down Expand Up @@ -210,7 +209,6 @@
# set of live variables at this instruction
liveness: OrderedSet[IRVariable]
parent: "IRBasicBlock"
fence_id: int
annotation: Optional[str]
ast_source: Optional[IRnode]
error_msg: Optional[str]
Expand All @@ -227,7 +225,6 @@
self.operands = list(operands) # in case we get an iterator
self.output = output
self.liveness = OrderedSet()
self.fence_id = -1
self.annotation = None
self.ast_source = None
self.error_msg = None
Expand All @@ -239,6 +236,34 @@
@property
def is_bb_terminator(self) -> bool:
return self.opcode in BB_TERMINATORS

def get_read_effects(self):

Check warning

Code scanning / CodeQL

Variable defined multiple times Warning

This assignment to 'get_read_effects' is unnecessary as it is
redefined
before this value is used.
return effects.reads.get(self.opcode, ())

def get_write_effects(self):

Check warning

Code scanning / CodeQL

Variable defined multiple times Warning

This assignment to 'get_write_effects' is unnecessary as it is
redefined
before this value is used.
return effects.writes.get(self.opcode, ())

@property
def is_phi(self) -> bool:
return self.opcode == "phi"

@property
def is_param(self) -> bool:
return self.opcode == "param"

@property
def is_pseudo(self) -> bool:
"""
Check if instruction is pseudo, i.e. not an actual instruction but
a construct for intermediate representation like phi and param.
"""
return self.is_phi or self.is_param

def get_read_effects(self):
return effects.reads.get(self.opcode, effects.EMPTY)

def get_write_effects(self):
return effects.writes.get(self.opcode, effects.EMPTY)

def get_label_operands(self) -> Iterator[IRLabel]:
"""
Expand Down Expand Up @@ -319,6 +344,20 @@
return inst.ast_source
return self.parent.parent.ast_source

def str_short(self) -> str:
s = ""
if self.output:
s += f"{self.output} = "
opcode = f"{self.opcode} " if self.opcode != "store" else ""
s += opcode
operands = self.operands
if opcode not in ["jmp", "jnz", "invoke"]:
operands = list(reversed(operands))
s += ", ".join(
[(f"label %{op}" if isinstance(op, IRLabel) else str(op)) for op in operands]
)
return s

def __repr__(self) -> str:
s = ""
if self.output:
Expand All @@ -335,10 +374,7 @@
if self.annotation:
s += f" <{self.annotation}>"

if self.liveness:
return f"{s: <30} # {self.liveness}"

return s
return f"{s: <30}"


def _ir_operand_from_value(val: Any) -> IROperand:
Expand Down Expand Up @@ -477,6 +513,34 @@
def clear_instructions(self) -> None:
self.instructions = []

@property
def phi_instructions(self) -> Iterator[IRInstruction]:
for inst in self.instructions:
if inst.opcode == "phi":
yield inst
else:
return

@property
def non_phi_instructions(self) -> Iterator[IRInstruction]:
return (inst for inst in self.instructions if inst.opcode != "phi")

@property
def param_instructions(self) -> Iterator[IRInstruction]:
for inst in self.instructions:
if inst.opcode == "param":
yield inst
else:
return

@property
def pseudo_instructions(self) -> Iterator[IRInstruction]:
return (inst for inst in self.instructions if inst.is_pseudo)

@property
def body_instructions(self) -> Iterator[IRInstruction]:
return (inst for inst in self.instructions[:-1] if not inst.is_pseudo)

def replace_operands(self, replacements: dict) -> None:
"""
Update operands with replacements.
Expand Down
59 changes: 59 additions & 0 deletions vyper/venom/effects.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
from enum import Flag, auto


class Effects(Flag):
STORAGE = auto()
TRANSIENT = auto()
MEMORY = auto()
IMMUTABLES = auto()
BALANCE = auto()
RETURNDATA = auto()


EMPTY = Effects(0)
ALL = ~EMPTY
STORAGE = Effects.STORAGE
TRANSIENT = Effects.TRANSIENT
MEMORY = Effects.MEMORY
IMMUTABLES = Effects.IMMUTABLES
BALANCE = Effects.BALANCE
RETURNDATA = Effects.RETURNDATA


writes = {
"sstore": STORAGE,
"tstore": TRANSIENT,
"mstore": MEMORY,
"istore": IMMUTABLES,
"call": ALL,
"delegatecall": ALL,
"staticcall": MEMORY | RETURNDATA,
"create": ALL,
"create2": ALL,
"invoke": ALL, # could be smarter, look up the effects of the invoked function
"dloadbytes": MEMORY,
"returndatacopy": MEMORY,
"calldatacopy": MEMORY,
"codecopy": MEMORY,
"extcodecopy": MEMORY,
"mcopy": MEMORY,
}

reads = {
"sload": STORAGE,
"tload": TRANSIENT,
"iload": IMMUTABLES,
"mload": MEMORY,
"mcopy": MEMORY,
"call": ALL,
"delegatecall": ALL,
"staticcall": ALL,
"returndatasize": RETURNDATA,
"returndatacopy": RETURNDATA,
"balance": BALANCE,
"selfbalance": BALANCE,
"log": MEMORY,
"revert": MEMORY,
"return": MEMORY,
"sha3": MEMORY,
}
Loading
Loading