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

fix[venom]: simplify cfg phi rename #4181

Open
wants to merge 8 commits into
base: master
Choose a base branch
from

Conversation

HodanPlodky
Copy link
Contributor

What I did

Fix for issues #4162 and #4070

How I did it

Removed the phi nodes which were not in use after SCCP pass. This could happen when the SCCP would replace jnz by jmp instruction and by this removing need for phi instruction (this is the case in #4162). More over the when merging the basic block in the SimplifyCFGPass there could be problem when renaming the labels in phi nodes since it went only into the first cfg_out basic block (this is the case in #4070)

How to verify it

So far both of the POC from compile correctly

Commit message

fix[venom]: phi label renaming and unused phi nodes removal

@charles-cooper
Copy link
Member

shouldn't we fix up the phi nodes during sccp directly?

@HodanPlodky HodanPlodky changed the title Fix/simplify cfg phi rename fix/simplify cfg phi rename Aug 15, 2024
@HodanPlodky HodanPlodky changed the title fix/simplify cfg phi rename fix[venom]: simplify cfg phi rename Aug 15, 2024
@HodanPlodky HodanPlodky marked this pull request as ready for review August 16, 2024 15:18
@@ -305,6 +311,9 @@ def _replace_constants(self, inst: IRInstruction):
inst.opcode = "jmp"
inst.operands = [target]
self.cfg_dirty = True
for bb in inst.parent.cfg_out:
if bb.label == target:
self.cfg_phi_fix_needed.add(bb)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why not self._fix_phi_bb_r(bb, OrderedSet()) here and remove all the need for self.cfg_phi_fix_needed and iterating later?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The reason was not to iterate over some basic block multiple times. Since solutions only detects start from which the occurrence of faulty phi node could be possible but not the end there could be a lot of overlap. In this case since I iterate over possible positions of the faulty phi nodes at the end I visit every possible basic block only once.

Copy link

codecov bot commented Sep 17, 2024

Codecov Report

Attention: Patch coverage is 70.45455% with 13 lines in your changes missing coverage. Please review.

Project coverage is 88.22%. Comparing base (03095ce) to head (a867595).

Files with missing lines Patch % Lines
vyper/venom/passes/sccp/sccp.py 70.58% 9 Missing and 1 partial ⚠️
vyper/venom/passes/simplify_cfg.py 70.00% 1 Missing and 2 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #4181      +/-   ##
==========================================
- Coverage   91.35%   88.22%   -3.14%     
==========================================
  Files         109      109              
  Lines       15641    15672      +31     
  Branches     3443     3454      +11     
==========================================
- Hits        14289    13826     -463     
- Misses        920     1314     +394     
- Partials      432      532     +100     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Comment on lines +366 to +367
from_src_bb = filter(lambda x: x[0] == src_bb.label, phi_inst.phi_operands)
operands = list(map(lambda x: x[1], from_src_bb))
Copy link
Member

Choose a reason for hiding this comment

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

please use a list comprehension here

Suggested change
from_src_bb = filter(lambda x: x[0] == src_bb.label, phi_inst.phi_operands)
operands = list(map(lambda x: x[1], from_src_bb))
operands = [op for label, op in phi_inst.phi_operands if label == src_bb.label]


assert len(operands) == 1
assert isinstance(operands[0], IRVariable)
assert phi_inst.output is not None
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
assert phi_inst.output is not None
assert phi_inst.output is not None

@@ -305,6 +311,9 @@ def _replace_constants(self, inst: IRInstruction):
inst.opcode = "jmp"
inst.operands = [target]
self.cfg_dirty = True
for bb in inst.parent.cfg_out:
if bb.label == target:
self.cfg_phi_fix_needed.add(bb)
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
self.cfg_phi_fix_needed.add(bb)
self.cfg_phi_fix_needed.add(bb)
break

for next_bb in bb.cfg_out:
self._fix_phi_bb_r(next_bb, visited)

def _fix_phi_node_inst(self, phi_inst: IRInstruction):
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
def _fix_phi_node_inst(self, phi_inst: IRInstruction):
def _fix_phi_inst(self, phi_inst: IRInstruction):

@@ -329,6 +338,40 @@ def _replace_constants(self, inst: IRInstruction):
if isinstance(lat, IRLiteral):
inst.operands[i] = lat

def _fix_phi_nodes(self):
visited: OrderedSet[IRBasicBlock] = OrderedSet()
Copy link
Member

Choose a reason for hiding this comment

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

why keep track of visited, instead of just iterating over fn.get_basic_blocks()?

assert len(operands) == 1
assert isinstance(operands[0], IRVariable)
assert phi_inst.output is not None
phi_inst.output.value = operands[0]
Copy link
Member

Choose a reason for hiding this comment

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

why modify the instruction if it gets removed on the next line?

self._fix_phi_bb_r(next_bb, visited)

def _fix_phi_node_inst(self, phi_inst: IRInstruction):
if len(phi_inst.parent.cfg_in) != 1:
Copy link
Member

Choose a reason for hiding this comment

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

when can this happen? shouldn't we handle this case?

@charles-cooper
Copy link
Member

i pushed some cleanup here: HodanPlodky#2. it feels like a kludge though, since it seems like we really should be fixing up these phi nodes inside of _handle_SSA_work_item.

assert isinstance(operands[0], IRVariable)
assert phi_inst.output is not None
phi_inst.output.value = operands[0]
phi_inst.parent.remove_instruction(phi_inst)
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 it might be a bug to remove the instruction. i think the instruction is dead when the value has been propagated from previous blocks, but it might not be dead if one of the parent blocks has been pruned, but it is not a constant in the other block.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants