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]: scheduler optimization #4161

Draft
wants to merge 46 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 39 commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
7ddc527
print correct liveness and fence
harkal Jun 3, 2024
544ff3e
improve ordering
harkal Jun 3, 2024
53db5f4
r fences
harkal Jun 3, 2024
5164553
remove old code
harkal Jun 3, 2024
314a6dd
adapt fensing
harkal Jun 3, 2024
8d3d9c5
more
harkal Jun 3, 2024
6513aa4
improve ordering and grouping of instructions in DFTPass
harkal Jun 4, 2024
125ae90
wip
harkal Jun 4, 2024
ec0edc9
wip
harkal Jun 4, 2024
0263781
squash
harkal Jun 11, 2024
bdc42ae
wip - ament
harkal Jun 11, 2024
2fe69e1
pass all tests
harkal Jun 12, 2024
aafabb5
preserve last child if terminator
harkal Jun 12, 2024
1a67bd8
cleanups
harkal Jun 12, 2024
5eef71d
Merge branch 'master' into feat/scheduler_optimization
harkal Jun 12, 2024
84c90c6
Merge branch 'master' into feat/scheduler_optimization
harkal Jun 12, 2024
b5e5a89
Merge branch 'master' into feat/scheduler_optimization
harkal Jun 16, 2024
cb55fad
Merge branch 'master' into feat/scheduler_optimization
harkal Jun 17, 2024
dc4fb9c
Merge branch 'master' into feat/scheduler_optimization
harkal Jun 17, 2024
9f79c53
wip
harkal Jun 18, 2024
c653a16
wip
harkal Jun 18, 2024
5bafd28
no merge node
harkal Jun 18, 2024
972cd65
fix
harkal Jun 18, 2024
807ec2f
fix
harkal Jun 18, 2024
c1a3d5d
gda
harkal Jun 19, 2024
04cb066
groups
harkal Jun 19, 2024
91feaa4
wip
harkal Jun 19, 2024
a012bca
wip
harkal Jun 19, 2024
fbbfefc
wip
harkal Jun 19, 2024
57c5d60
better dep traversal
harkal Jun 19, 2024
ae9a30b
wip
harkal Jun 19, 2024
d82c3b4
before circle breaking
harkal Jun 19, 2024
5380b87
cycles detector
harkal Jun 19, 2024
73f266b
wip
harkal Jun 19, 2024
c2c5064
wip
harkal Jun 19, 2024
511b78b
fix
harkal Jun 20, 2024
9f60acb
wip
harkal Jun 20, 2024
23b2dae
grouping refactor
harkal Jun 20, 2024
750379c
cleanup
harkal Jun 20, 2024
21e20c7
Merge remote-tracking branch 'origin-vyper/master' into feat/schedule…
harkal Jun 20, 2024
2547153
spelling
harkal Jun 20, 2024
2755ecb
`DUP1 SWAP1` optimization
harkal Jun 21, 2024
996f486
small cleanup and store expantion (not working)
harkal Jun 25, 2024
61308d7
cleanup and add comments
harkal Jun 25, 2024
78b0aac
lint
harkal Jun 25, 2024
7687d8e
Merge branch 'master' into feat/scheduler_optimization
harkal Sep 19, 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
31 changes: 31 additions & 0 deletions vyper/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -591,3 +591,34 @@ def annotate_source_code(
cleanup_lines += [""] * (num_lines - len(cleanup_lines))

return "\n".join(cleanup_lines)


def find_cycles_in_directed_graph(graph):
visit_state = {} # Visit states: 0 = Not visited, 1 = Visiting, 2 = Visited
stack = [] # Stack to keep track of the current path
all_cycles = [] # List to store all detected cycles

def dfs(node):
vs = visit_state.get(node, 0)
if vs == 1: # Cycle detected
cycle_index = stack.index(node)
cycle_nodes = stack[cycle_index:]
all_cycles.append(cycle_nodes)
return
elif vs == 2: # Already fully visited
return

visit_state[node] = 1
stack.append(node)

for neighbor in graph.get(node, []):
dfs(neighbor)

stack.pop()
visit_state[node] = 2

for node in graph:
if visit_state.get(node, 0) == 0:
dfs(node)

return all_cycles
18 changes: 17 additions & 1 deletion vyper/venom/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
from vyper.codegen.ir_node import IRnode
from vyper.compiler.settings import OptimizationLevel
from vyper.venom.analysis.analysis import IRAnalysesCache
from vyper.venom.analysis.dfg import DFGAnalysis
from vyper.venom.analysis.liveness import LivenessAnalysis
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 @@ -39,6 +41,9 @@
return compiler.generate_evm(optimize == OptimizationLevel.NONE)


count = 0


def _run_passes(fn: IRFunction, optimize: OptimizationLevel) -> None:
# Run passes on Venom IR
# TODO: Add support for optimization levels
Expand All @@ -56,7 +61,18 @@
BranchOptimizationPass(ac, fn).run_pass()
ExtractLiteralsPass(ac, fn).run_pass()
RemoveUnusedVariablesPass(ac, fn).run_pass()
DFTPass(ac, fn).run_pass()
dft = DFTPass(ac, fn)
dft.run_pass()

# ac.force_analysis(LivenessAnalysis)
# global count
# if count == 1:
# #dft = ac.request_analysis(DFGAnalysis)
# print(dft.ida_as_graph())

# import sys
# sys.exit(1)
Fixed Show fixed Hide fixed
# count += 1


def generate_ir(ir: IRnode, optimize: OptimizationLevel) -> IRContext:
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
41 changes: 35 additions & 6 deletions vyper/venom/basicblock.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,6 @@ class IRInstruction:
liveness: OrderedSet[IRVariable]
dup_requirements: OrderedSet[IRVariable]
parent: "IRBasicBlock"
fence_id: int
annotation: Optional[str]
ast_source: Optional[IRnode]
error_msg: Optional[str]
Expand All @@ -229,7 +228,6 @@ def __init__(
self.output = output
self.liveness = OrderedSet()
self.dup_requirements = OrderedSet()
self.fence_id = -1
self.annotation = None
self.ast_source = None
self.error_msg = None
Expand All @@ -242,6 +240,10 @@ def is_volatile(self) -> bool:
def is_bb_terminator(self) -> bool:
return self.opcode in BB_TERMINATORS

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

def get_label_operands(self) -> Iterator[IRLabel]:
"""
Get all labels in instruction.
Expand Down Expand Up @@ -321,6 +323,20 @@ def get_ast_source(self) -> Optional[IRnode]:
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 @@ -337,10 +353,7 @@ def __repr__(self) -> str:
if self.annotation:
s += f" <{self.annotation}>"

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

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


def _ir_operand_from_value(val: Any) -> IROperand:
Expand Down Expand Up @@ -479,6 +492,22 @@ def remove_instruction(self, instruction: IRInstruction) -> None:
def clear_instructions(self) -> None:
self.instructions = []

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

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

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

def replace_operands(self, replacements: dict) -> None:
"""
Update operands with replacements.
Expand Down
Loading
Loading