This is an archive of the discontinued LLVM Phabricator instance.

[Dominators] Always recalculate postdominators when update yields different roots
ClosedPublic

Authored by kuhar on Feb 9 2018, 1:22 PM.

Details

Summary

This patch makes postdominators always recalculate the tree when an update causes to change the tree roots.
As @dmgreen noticed in D41298, the previous implementation was not conservative enough and it was possible to end up with a PostDomTree that was different than a freshly computed one.
The patch also compares postdominators with a freshly computed tree at the end of full verification to make sure we don't hit similar issues in the future.

This should (ideally) be also backported to 6.0 before the release, although I don't have any reports of this causing an observable error. It should be safe to do it even if it's late in the release, as the change only makes the current behavior more conservative.

Diff Detail

Repository
rL LLVM

Event Timeline

kuhar created this revision.Feb 9 2018, 1:22 PM

Thanks for this! I obviously think this looks fine, but I will let someone who knows more about this to check it's OK too.

But any idea why verifyRoots did not catch this case already? If the newly calculated roots are different from what is in the PDT.

grosser accepted this revision.Feb 12 2018, 5:11 AM

That's a good improvement. Thanks for adding it.

include/llvm/Support/GenericDomTreeConstruction.h
706 ↗(On Diff #133678)

which NODE within (without the 's')

This revision is now accepted and ready to land.Feb 12 2018, 5:11 AM
kuhar added a comment.Feb 12 2018, 5:21 AM

But any idea why verifyRoots did not catch this case already? If the newly calculated roots are different from what is in the PDT.

The reasoning behind the original implementation was that there are multiple correct postdominator trees for some CFG-s (in particular, when there are infinite loops), but we wanted to have just one canonical postdomtree that we end up with, no matter if we get it as a result of an update or by fresh construction. I though that it would be sufficient to check whether the new set of roots if also a root for the current CFG, but your example with InsertReachable2 showed there are cases were it's not enough and we have to look deeper. And I think that your previous patch to the root selection made some cases like this that crashed the original implementation to silently continue instead, which is how we ended up in the current situation.

brzycki accepted this revision.Feb 12 2018, 7:41 AM

LGTM. Out of curiosity does this change impact compile time?

kuhar added a comment.Feb 12 2018, 7:50 AM

LGTM. Out of curiosity does this change impact compile time?

I haven't checked, but from my previous experiments I doubt it does -- forming or to infinite loops are very rare, and this piece of code was only executed a few times when compiling clang or sqlite. I don't have numbers for it, but could get them later this week if someone's really interested.

I haven't checked, but from my previous experiments I doubt it does -- forming or to infinite loops are very rare, and this piece of code was only executed a few times when compiling clang or sqlite. I don't have numbers for it, but could get them later this week if someone's really interested.

Don't spend too much time on it if you're confident it's not an issue. I was just curious in case you had already run the numbers. PDT is used less than DT all around the middle end which is also why I suspect you haven't seen this issue in 6.0.

But any idea why verifyRoots did not catch this case already?

Ah, I see. The roots were the same as newly calculated roots, but roots and the tree didn't match up. What do you think about adding a check for that to verifyRoots? (Not in this commit, I'm just throwing around ideas here). Or do you think a check against the recalculated tree is good enough? Something like:

for each node in tree:
  if node in roots:
    check node->idom == virtual root
  else
    check node->idom != virtual root

But any idea why verifyRoots did not catch this case already?

Ah, I see. The roots were the same as newly calculated roots, but roots and the tree didn't match up. What do you think about adding a check for that to verifyRoots? (Not in this commit, I'm just throwing around ideas here). Or do you think a check against the recalculated tree is good enough? Something like:

for each node in tree:
  if node in roots:
    check node->idom == virtual root
  else
    check node->idom != virtual root

Yes, I think that is missing from the current implementation.

This revision was automatically updated to reflect the committed changes.
kuhar added a comment.Feb 12 2018, 4:21 PM

But any idea why verifyRoots did not catch this case already?

Ah, I see. The roots were the same as newly calculated roots, but roots and the tree didn't match up. What do you think about adding a check for that to verifyRoots? (Not in this commit, I'm just throwing around ideas here). Or do you think a check against the recalculated tree is good enough? Something like:

for each node in tree:
  if node in roots:
    check node->idom == virtual root
  else
    check node->idom != virtual root

Yes, I think that is missing from the current implementation.

Forgot to mention: your pseudocode is not entirely correct -- there can be nodes connected to the virtual root that are not tree roots (e.g. diamond: entry, if then, else, exit) . But all roots have to be connected to the virtual root.