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

Merged
merged 149 commits into from
Nov 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
149 commits
Select commit Hold shift + click to select a range
919346b
τεμπ
harkal Sep 11, 2024
f4c7ac5
revert `get_phi_depth()` changes
harkal Sep 18, 2024
cc50efe
revert change that slipped in from another branch
harkal Sep 18, 2024
28eed40
`StackModel` refactor
harkal Sep 19, 2024
e871868
feat[venom]: add small heuristic for cleaning input stack
charles-cooper Sep 20, 2024
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
63246e7
Merge branch 'master' into feat/stack2mem
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
eea930b
find roots and make terminator last to process
harkal Oct 1, 2024
826e821
remove sort
harkal Oct 1, 2024
2e628b6
fix
harkal Oct 2, 2024
cbf47ef
cleanup
harkal Oct 2, 2024
9b791f3
Merge remote-tracking branch 'origin-vyper/master' into feat/stack2mem
harkal Oct 2, 2024
843a127
fix deps
harkal Oct 2, 2024
a99ccf6
remove store expanstion
harkal Oct 2, 2024
d8d747b
Revert "remove store expanstion"
harkal Oct 2, 2024
ba0b912
lint
harkal Oct 2, 2024
da7c00a
python 3.11 support
harkal Oct 2, 2024
d8a63c9
cleanup
harkal Oct 2, 2024
face008
cleanup
harkal Oct 2, 2024
3836b87
wip
harkal Oct 4, 2024
015278a
Merge branch 'master' into feat/dft_upgrade
charles-cooper Oct 5, 2024
9191c6a
Merge branch 'master' into feat/stack2mem
harkal Oct 5, 2024
1d6b762
Merge branch 'master' into feat/dft_upgrade
harkal Oct 5, 2024
ff9e43c
Merge branch 'feat/dft_upgrade' of github.com:harkal/vyper into feat/…
harkal Oct 5, 2024
baadeed
debug
charles-cooper Oct 5, 2024
d5cc045
wip - improve heuristic
charles-cooper Oct 5, 2024
0a1a001
wip barriers
charles-cooper Oct 6, 2024
6a72efe
refactor offspring count
charles-cooper Oct 6, 2024
ddde336
an improvement
charles-cooper Oct 6, 2024
6a8d595
Merge pull request #8 from charles-cooper/dft-shenanigans
harkal Oct 7, 2024
defdf8d
cleanup
harkal Oct 8, 2024
0b899e4
remove deadcode and debuging
harkal Oct 8, 2024
9e2c428
Merge remote-tracking branch 'origin-vyper/master' into feat/dft_upgrade
harkal Oct 8, 2024
747e54b
Merge branch 'master' into feat/venom-pops
harkal Oct 8, 2024
919dc22
Merge branch 'master' into feat/venom-pops
harkal Oct 8, 2024
2b49132
Merge branch 'feat/venom-pops' of github.com:charles-cooper/vyper int…
harkal Oct 8, 2024
26592bc
Merge branch 'devel' into feat/dft_upgrade
harkal Oct 8, 2024
7406e85
Merge branch 'master' into feat/dft_upgrade
harkal Oct 10, 2024
b4c6998
Merge branch 'master' into feat/dft_upgrade
harkal Oct 10, 2024
3192f9c
Merge branch 'master' into feat/dft_upgrade
charles-cooper Oct 13, 2024
e46d946
Merge branch 'feat/dft_upgrade' of github.com:harkal/vyper into feat/…
harkal Oct 16, 2024
ef84d70
Merge branch 'master' into feat/dft_upgrade
harkal Oct 16, 2024
1c98c23
fix Effects.__iter__() for python3.10
charles-cooper Oct 16, 2024
b450deb
Merge branch 'master' into feat/dft_upgrade
harkal Oct 16, 2024
7523cb1
bring back sorting and offsprings
harkal Oct 16, 2024
d2ed247
merge barriers
harkal Oct 16, 2024
b4b22cf
Merge branch 'master' into feat/stack2mem
harkal Oct 16, 2024
52efbaf
refactor to the new pass importing
harkal Oct 16, 2024
9212c86
add back SCCP
harkal Oct 16, 2024
414ca33
fixes and cleanup
harkal Oct 16, 2024
f48ded6
Merge branch 'feat/stack2mem' into feat/dft_upgrade
harkal Oct 23, 2024
caaae55
heuristic update
harkal Oct 23, 2024
a29efac
Revert "Merge branch 'feat/stack2mem' into feat/dft_upgrade"
harkal Oct 23, 2024
02b5082
cleanup
harkal Oct 23, 2024
aec32a0
refactor
harkal Oct 23, 2024
267732c
fix test
harkal Oct 23, 2024
acd5d77
add `sha3_64` to effects
harkal Oct 23, 2024
40e2e94
lint
harkal Oct 23, 2024
5b4b9c1
Merge branch 'master' into feat/dft_upgrade
harkal Oct 29, 2024
9b7b01e
refactor
harkal Oct 30, 2024
0925547
Merge branch 'master' into feat/dft_upgrade
harkal Oct 30, 2024
6891e7f
lint
harkal Oct 30, 2024
f7c6e94
improve offspring cost - ignore store chains
charles-cooper Nov 10, 2024
0725890
Merge branch 'master' into feat/dft_upgrade
charles-cooper Nov 12, 2024
a80c7bd
Merge branch 'master' into feat/dft_upgrade
charles-cooper Nov 12, 2024
39c27bb
add a comment
charles-cooper Nov 12, 2024
1f53984
Merge branch 'master' into feat/dft_upgrade
charles-cooper Nov 12, 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/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
8 changes: 7 additions & 1 deletion vyper/venom/analysis/dfg.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from vyper.utils import OrderedSet
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 @@ -20,6 +20,12 @@ def __init__(self, analyses_cache: IRAnalysesCache, function: IRFunction):
def get_uses(self, op: IRVariable) -> OrderedSet[IRInstruction]:
return self._dfg_inputs.get(op, OrderedSet())

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 @@ -60,7 +60,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
65 changes: 59 additions & 6 deletions vyper/venom/basicblock.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,6 @@ class IRInstruction:
# 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 @@ -219,7 +218,6 @@ def __init__(
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 @@ -236,6 +234,22 @@ def is_commutative(self) -> bool:
def is_bb_terminator(self) -> bool:
return self.opcode in BB_TERMINATORS

@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)

Expand Down Expand Up @@ -321,6 +335,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 +365,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}"


def _ir_operand_from_value(val: Any) -> IROperand:
Expand Down Expand Up @@ -477,6 +502,34 @@ def remove_instruction(self, instruction: IRInstruction) -> None:
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
6 changes: 6 additions & 0 deletions vyper/venom/effects.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ class Effects(Flag):
BALANCE = auto()
EXTCODE = auto()

def __iter__(self):
# python3.10 doesn't have an iter implementation. we can
# remove this once we drop python3.10 support.
return (m for m in self.__class__.__members__.values() if m in self)


EMPTY = Effects(0)
ALL = ~EMPTY
Expand Down Expand Up @@ -68,6 +73,7 @@ class Effects(Flag):
"revert": MEMORY,
"return": MEMORY,
"sha3": MEMORY,
"sha3_64": MEMORY,
"msize": MSIZE,
}

Expand Down
167 changes: 112 additions & 55 deletions vyper/venom/passes/dft.py
Original file line number Diff line number Diff line change
@@ -1,81 +1,138 @@
from collections import defaultdict

import vyper.venom.effects as effects
from vyper.utils import OrderedSet
from vyper.venom.analysis import DFGAnalysis
from vyper.venom.basicblock import IRBasicBlock, IRInstruction, IRVariable
from vyper.venom.analysis import DFGAnalysis, IRAnalysesCache, LivenessAnalysis
from vyper.venom.basicblock import IRBasicBlock, IRInstruction
from vyper.venom.function import IRFunction
from vyper.venom.passes.base_pass import IRPass


class DFTPass(IRPass):
function: IRFunction
inst_order: dict[IRInstruction, int]
inst_order_num: int
inst_offspring: dict[IRInstruction, OrderedSet[IRInstruction]]
visited_instructions: OrderedSet[IRInstruction]
ida: dict[IRInstruction, OrderedSet[IRInstruction]]

def __init__(self, analyses_cache: IRAnalysesCache, function: IRFunction):
super().__init__(analyses_cache, function)
self.inst_offspring = {}

def run_pass(self) -> None:
self.inst_offspring = {}
self.visited_instructions: OrderedSet[IRInstruction] = OrderedSet()

self.dfg = self.analyses_cache.request_analysis(DFGAnalysis)
basic_blocks = list(self.function.get_basic_blocks())

self.function.clear_basic_blocks()
for bb in basic_blocks:
self._process_basic_block(bb)

self.analyses_cache.invalidate_analysis(LivenessAnalysis)

def _process_basic_block(self, bb: IRBasicBlock) -> None:
self.function.append_basic_block(bb)

self._calculate_dependency_graphs(bb)
self.instructions = list(bb.pseudo_instructions)
non_phi_instructions = list(bb.non_phi_instructions)

self.visited_instructions = OrderedSet()
for inst in non_phi_instructions:
self._calculate_instruction_offspring(inst)

# Compute entry points in the graph of instruction dependencies
entry_instructions: OrderedSet[IRInstruction] = OrderedSet(non_phi_instructions)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think there is a performance error here. in the previous version, we would iterate into children with get_uses to bring instructions closer to their uses. but here we have no possibility to reorder the entry instructions.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can rearrange the entry instruction processing order. We are just not doing it just yet.

for inst in non_phi_instructions:
to_remove = self.ida.get(inst, OrderedSet())
if len(to_remove) > 0:
entry_instructions.dropmany(to_remove)

entry_instructions_list = list(entry_instructions)

def _process_instruction_r(self, bb: IRBasicBlock, inst: IRInstruction, offset: int = 0):
for op in inst.get_outputs():
assert isinstance(op, IRVariable), f"expected variable, got {op}"
uses = self.dfg.get_uses(op)
# Move the terminator instruction to the end of the list
self._move_terminator_to_end(entry_instructions_list)

for uses_this in uses:
if uses_this.parent != inst.parent or uses_this.fence_id != inst.fence_id:
# don't reorder across basic block or fence boundaries
continue
self.visited_instructions = OrderedSet()
for inst in entry_instructions_list:
self._process_instruction_r(self.instructions, inst)

# if the instruction is a terminator, we need to place
# it at the end of the basic block
# along with all the instructions that "lead" to it
self._process_instruction_r(bb, uses_this, offset)
bb.instructions = self.instructions
assert bb.is_terminated, f"Basic block should be terminated {bb}"

def _move_terminator_to_end(self, instructions: list[IRInstruction]) -> None:
terminator = next((inst for inst in instructions if inst.is_bb_terminator), None)
if terminator is None:
raise ValueError(f"Basic block should have a terminator instruction {self.function}")
instructions.remove(terminator)
instructions.append(terminator)

def _process_instruction_r(self, instructions: list[IRInstruction], inst: IRInstruction):
if inst in self.visited_instructions:
return
self.visited_instructions.add(inst)
self.inst_order_num += 1

if inst.is_bb_terminator:
offset = len(bb.instructions)

if inst.opcode == "phi":
# phi instructions stay at the beginning of the basic block
# and no input processing is needed
# bb.instructions.append(inst)
self.inst_order[inst] = 0
if inst.is_pseudo:
return

for op in inst.get_input_variables():
target = self.dfg.get_producing_instruction(op)
assert target is not None, f"no producing instruction for {op}"
if target.parent != inst.parent or target.fence_id != inst.fence_id:
# don't reorder across basic block or fence boundaries
continue
self._process_instruction_r(bb, target, offset)
children = list(self.ida[inst])

self.inst_order[inst] = self.inst_order_num + offset
def key(x):
cost = inst.operands.index(x.output) if x.output in inst.operands else 0
return cost - len(self.inst_offspring[x]) * 0.5

def _process_basic_block(self, bb: IRBasicBlock) -> None:
self.function.append_basic_block(bb)
# heuristic: sort by size of child dependency graph
children.sort(key=key)

for inst in bb.instructions:
inst.fence_id = self.fence_id
if inst.is_volatile:
self.fence_id += 1
for dep_inst in children:
self._process_instruction_r(instructions, dep_inst)

# We go throught the instructions and calculate the order in which they should be executed
# based on the data flow graph. This order is stored in the inst_order dictionary.
# We then sort the instructions based on this order.
self.inst_order = {}
self.inst_order_num = 0
for inst in bb.instructions:
self._process_instruction_r(bb, inst)
instructions.append(inst)

bb.instructions.sort(key=lambda x: self.inst_order[x])
def _calculate_dependency_graphs(self, bb: IRBasicBlock) -> None:
# ida: instruction dependency analysis
self.ida = defaultdict(OrderedSet)

def run_pass(self) -> None:
self.dfg = self.analyses_cache.request_analysis(DFGAnalysis)
non_phis = list(bb.non_phi_instructions)

self.fence_id = 0
self.visited_instructions: OrderedSet[IRInstruction] = OrderedSet()
#
# Compute dependency graph
#
last_write_effects: dict[effects.Effects, IRInstruction] = {}
last_read_effects: dict[effects.Effects, IRInstruction] = {}

basic_blocks = list(self.function.get_basic_blocks())
for inst in non_phis:
for op in inst.operands:
dep = self.dfg.get_producing_instruction(op)
if dep is not None and dep.parent == bb:
self.ida[inst].add(dep)

self.function.clear_basic_blocks()
for bb in basic_blocks:
self._process_basic_block(bb)
write_effects = inst.get_write_effects()
read_effects = inst.get_read_effects()

for write_effect in write_effects:
if write_effect in last_read_effects:
self.ida[inst].add(last_read_effects[write_effect])
last_write_effects[write_effect] = inst

for read_effect in read_effects:
if read_effect in last_write_effects and last_write_effects[read_effect] != inst:
self.ida[inst].add(last_write_effects[read_effect])
last_read_effects[read_effect] = inst

def _calculate_instruction_offspring(self, inst: IRInstruction):
if inst in self.inst_offspring:
return self.inst_offspring[inst]

self.inst_offspring[inst] = self.ida[inst].copy()

deps = self.ida[inst]
for dep_inst in deps:
assert inst.parent == dep_inst.parent
if dep_inst.opcode == "store":
continue
res = self._calculate_instruction_offspring(dep_inst)
self.inst_offspring[inst] |= res

return self.inst_offspring[inst]
8 changes: 6 additions & 2 deletions vyper/venom/venom_to_assembly.py
Original file line number Diff line number Diff line change
Expand Up @@ -600,10 +600,14 @@ def dup(self, assembly, stack, depth):
assembly.append(_evm_dup_for(depth))

def swap_op(self, assembly, stack, op):
return self.swap(assembly, stack, stack.get_depth(op))
depth = stack.get_depth(op)
assert depth is not StackModel.NOT_IN_STACK, f"Cannot swap non-existent operand {op}"
return self.swap(assembly, stack, depth)

def dup_op(self, assembly, stack, op):
self.dup(assembly, stack, stack.get_depth(op))
depth = stack.get_depth(op)
assert depth is not StackModel.NOT_IN_STACK, f"Cannot dup non-existent operand {op}"
self.dup(assembly, stack, depth)


def _evm_swap_for(depth: int) -> str:
Expand Down
Loading