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

Make node iteration stricter #4617

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

gezalore
Copy link
Member

This is a prep patch for an experiment on removing m_iterpp (which I have in a hacked up form, and does not actually help much, 1.2% memory saving, yay...). Even if we don't land that, I think we want these changes that makes iteration a little bit stricter. Notably, there are assertions (only under VL_DEBUG, due to being in hot code), that ensure that we don't recursively iterate the same node. We used to do this in a few places (which I fixed), but this is generally not safe due to the recursive call to iterateAndNext overwriting m_iterpp, and hence any subsequent edits would be unsafe.

Apart from the direct recursion, we also need to prevent addHereThisAsNext from updating the iteration pointer. I think this is a good thing. Previously it used to force iteration to restart on the nodes inserted before the currently iterated node, which would also mean that the currently iterated node would then be visited again, as it became a successor of the inserted node. With this patch, iteration continues with the edited node only if it's replacing the current node, or if the new node is a successor (nextp) of the current node, but not if the edit is adding the node before the current node. I fixed the few places (V3Premit, V3Task) where we used to rely on the old behaviour. The fix is simply to explicitly iterate the new nodes you insert before the current node.

Patch is performance neutral.

@gezalore
Copy link
Member Author

Note: I will review code involving addHereThisAsNext if you hare happy with it otherwise

Comment on lines +2100 to +2104
std::function<bool(AstNode*)> isRecursive = [&](AstNode* np) -> bool {
return np && np->exists([&](AstNodeVarRef* refp) {
return refp->varp() == nodep || isRecursive(refp->varp()->valuep());
});
};
Copy link
Member

Choose a reason for hiding this comment

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

I preferred the old form as if we ever have another way to get to the var twice it will detect, and also IMO was easier to understand. Was this needed to prevent a new assertion?

Copy link
Member Author

Choose a reason for hiding this comment

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

This is needed because of the assertions: you can't call iterateAndNext on the same node twice (recursively). Doing so was never really safe to do due to the later call stomping m_iterpp hence edits in the higher call would have been borked.

Comment on lines +2757 to +2758
// Simplify the value
iterate(itemp->valuep());
Copy link
Member

Choose a reason for hiding this comment

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

This may change itemp, so you shouldn't record itemp in a variable. (Generally true throughout V3Width - avoid remembering node pointers across iterate.)

Copy link
Member Author

Choose a reason for hiding this comment

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

This is V3Const, and we are iterating the the child node valuep(), not itemp? In this case, we know itemp is not changed as it is an AstEnumItem, which should not ever be changed, only it's childern (the enum vlaue)?

src/V3Ast.cpp Show resolved Hide resolved
@gezalore
Copy link
Member Author

I have thought of a way to make it all work with the old behaviour of addHereThisAsNext, however I'm somewhat conflicted as I think the old behaviour is still a bit risky. At the same time, you could consider addHereThisAsNext to be a 'replace' followed by an 'addNext' of the old node, so some could argue fixing up the iterator is reasonable (as it is reasonable for 'replace').

At the same time, if the old node did not happen to be iterated, then the new one would not be either (if inserted earlier in the tree), so whether the inserted node is iterated is not obvious. I think it would more likely catch bugs where a recursive iteration is missing if the iterator was not fixed up (like with this patch), so you are more likely to realize using simpler tests that a recursive iteration is needed.

For example, a recursive iteration was missing in V3Premit when the temporary is introduced in a while loop condition, resulting in a ShiftR not being converted to a ShiftROvr. With this PR applied, the replacement is performed:

image

With that in mind I'm leaning towards not fixing up the iteration on addHereThisAsNext

This is a prep patch for an experiment on removing m_iterpp (which I
have in a hacked up form, and does not actually help much, 1.2% memory
saving, yay...). Even if we don't land that, I think we want these
changes that makes iteration a little bit stricter. Notably, there are
assertions (only under VL_DEBUG, due to being in hot code), that ensure
that we don't recursively iterate the same node. We used to do this in a
few places (which I fixed), but this is generally not safe due to the
recursive call to iterateAndNext overwriting m_iterpp, and hence any
subsequent edits would be unsafe.

Apart from the direct recursion, we also need to prevent
addHereThisAsNext from updating the iteration pointer. I think this is a
good thing. Previously it used to force iteration to restart on the
nodes inserted *before* the currently iterated node, which would also
mean that the currently iterated node would then be visited again, as it
became a successor of the inserted node. With this patch, iteration
continues with the edited node only if it's replacing the current node,
or if the new node is a successor (nextp) of the current node, but not
if the edit is adding the node before the current node. I fixed the few
places (V3Premit, V3Task) where we used to rely on the old behaviour.
The fix is simply to explicitly iterate the new nodes you insert before
the current node.

Patch is performance neutral.
@wsnyder
Copy link
Member

wsnyder commented Oct 27, 2023

I'd lean towards reiterating on addHereAsNext, because it seems more similar to the "replace" and is less risky. E.g. the insert might have a subtree that was not pre-visited and still needs to be. The downside is mainly performance, which is reasonable sacrifice.

The general throwing of an assert on double-visit scares me a bit as I suspect there will be fallout, but with all assertions it does seem better to get these cleaned up.

gezalore added a commit to gezalore/verilator that referenced this pull request Dec 3, 2023
- Enable creating constant pool entries for RHS of simple
  var = const assignments
- Never extract ArraySel (it's just pointer arithmetic)
- Remove unnecessary AstTraceInc precond child tree
- Always fully recurse expressions (fix transplanted from verilator#4617)
- General cleanup

Overall the patch is performance neutral to slightly positive, but saves
~10% peak Verialtor memory usage due to not creating temporaries (which
are later expanded) for any ArraySels.
@gezalore gezalore mentioned this pull request Dec 3, 2023
gezalore added a commit to gezalore/verilator that referenced this pull request Dec 3, 2023
- Enable creating constant pool entries for RHS of simple
  var = const assignments
- Never extract ArraySel (it's just pointer arithmetic)
- Remove unnecessary AstTraceInc precond child tree
- Always fully recurse expressions (fix transplanted from verilator#4617)
- General cleanup

Overall the patch is performance neutral to slightly positive, but saves
~10% peak Verialtor memory usage due to not creating temporaries (which
are later expanded) for any ArraySels.
gezalore added a commit to gezalore/verilator that referenced this pull request Dec 3, 2023
- Enable creating constant pool entries for RHS of simple
  var = const assignments
- Never extract ArraySel (it's just pointer arithmetic)
- Remove unnecessary AstTraceInc precond child tree
- Always fully recurse expressions (fix transplanted from verilator#4617)
- General cleanup

Overall the patch is performance neutral to slightly positive, but saves
~10% peak Verialtor memory usage due to not creating temporaries (which
are later expanded) for any ArraySels.
gezalore added a commit to gezalore/verilator that referenced this pull request Dec 3, 2023
- Enable creating constant pool entries for RHS of simple
  var = const assignments
- Never extract ArraySel (it's just pointer arithmetic)
- Remove unnecessary AstTraceInc precond child tree
- Always fully recurse expressions (fix transplanted from verilator#4617)
- General cleanup

Overall the patch is performance neutral to slightly positive, but saves
~10% peak Verialtor memory usage due to not creating temporaries (which
are later expanded) for any ArraySels.
gezalore added a commit to gezalore/verilator that referenced this pull request Dec 3, 2023
- Enable creating constant pool entries for RHS of simple
  var = const assignments
- Never extract ArraySel (it's just pointer arithmetic)
- Remove unnecessary AstTraceInc precond child tree
- Always fully recurse expressions (fix transplanted from verilator#4617)
- General cleanup

Overall the patch is performance neutral to slightly positive, but saves
~10% peak Verialtor memory usage due to not creating temporaries (which
are later expanded) for any ArraySels.
wsnyder pushed a commit that referenced this pull request Dec 6, 2023
- Enable creating constant pool entries for RHS of simple
  var = const assignments
- Never extract ArraySel (it's just pointer arithmetic)
- Remove unnecessary AstTraceInc precond child tree
- Always fully recurse expressions (fix transplanted from #4617)
- General cleanup

Overall the patch is performance neutral to slightly positive, but saves
~10% peak Verialtor memory usage due to not creating temporaries (which
are later expanded) for any ArraySels.
toddstrader pushed a commit that referenced this pull request Dec 13, 2023
- Enable creating constant pool entries for RHS of simple
  var = const assignments
- Never extract ArraySel (it's just pointer arithmetic)
- Remove unnecessary AstTraceInc precond child tree
- Always fully recurse expressions (fix transplanted from #4617)
- General cleanup

Overall the patch is performance neutral to slightly positive, but saves
~10% peak Verialtor memory usage due to not creating temporaries (which
are later expanded) for any ArraySels.
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.

None yet

2 participants