This is an archive of the discontinued LLVM Phabricator instance.

[GVN] Handle removal of first implicit CF instruction correctly
AbandonedPublic

Authored by mkazantsev on Oct 16 2017, 2:23 AM.

Details

Summary

When erasing an instruction, we can erase the one which is the first implicit
control flow instruction in the block. This will invalidate the respective map.
This patch ensures that we re-calculate the FirstImplicitControlFlowInsts for
a block every time we remove the first implicit CF instruction.

It is the fix for bug introduced in https://reviews.llvm.org/D37460; these two
patches should be merged together and are only separated for convenience
of review.

Diff Detail

Event Timeline

mkazantsev created this revision.Oct 16 2017, 2:23 AM
mkazantsev planned changes to this revision.Oct 16 2017, 2:39 AM

Factor out common code.

dberlin added inline comments.Oct 16 2017, 7:37 AM
lib/Transforms/Scalar/GVN.cpp
1204–1206

Please don't do this.
Just mark them all for deletion, unless there is a very good reason not to.

2108–2110

If you are going to try to abstract the deletion, you should do all the updating this wants (IE MD->removeInstrruction, etc)

Please don't call it eraseFromPaernt, that's meaningless. Please call it deleteInstruction or something

2314

Ditto

dberlin added inline comments.Oct 16 2017, 11:10 PM
lib/Transforms/Scalar/GVN.cpp
1204–1206

On the bug it sounded like you think not erasing immediately causes further problems.
Could you expand a bit?
It's hard to see how that could cause failures with this one since we would erase them after processing the current instruction.

The one in performScalarPRE, it looks like instructions marked to be erased are not erased after performing scalar PRE because it's done after GVN.

2314

I can see how this may cause assertion failures (though it's silly that we do it this way), so you may have to leave this as a direct deleteInstruction call

mkazantsev added inline comments.Oct 17 2017, 1:13 AM
lib/Transforms/Scalar/GVN.cpp
1204–1206

No matter when we erase, we should update the implicit CF map after we do it. When we used OrderedInstructions we assumed that the invariant "first instruction with implicit CF" preserves, no matter what happens. But it appeared that it doesn't (for example, if there are two calls into readnone function with the same args in different blocks; then the dominated call can be eliminated).

Even if we mark for deletion, we should update the map immediately to be able to hoist across the marked instruction. Marking alone does not solve the problem, it will still need the same code that also updates the map. If marking is only needed for uniformity of GVN code, this can be done separately from fixing the actual bug.

mkazantsev added inline comments.Oct 17 2017, 3:24 AM
lib/Transforms/Scalar/GVN.cpp
1204–1206

Ok, it seems impossible here. NewInsts cannot contain such instructions since they were just created. I will assert on that.

mkazantsev marked 5 inline comments as done.

Followed Daniel's advices.

dberlin edited edge metadata.Oct 17 2017, 8:00 AM

Both of the deletions are in PRE, after it has been successful. If either the inserted or the deleted instructions were implicit control flow, PRE should have been unsafe. It's probably worth noting this in a comment

"Even if we mark for deletion, we should update the map immediately to be able to hoist across the marked instruction. Marking alone does not solve the problem, it will still need the same code that also updates the map. If marking is only needed for uniformity of GVN code, this can be done separately from fixing the actual bug."
The instructions will be deleted and the map updated as soon as we return from this function up the call stack.
It will not prevent further anything, because

  1. the hoisting has already happened (and all the functions in this stack are going to return and then the next thing that will happen is we will delete the dead instructions)
  2. The marked instruction could not have blocked hoisting anyway, or else it would not have been safe to PRE.

Yes, I agree. In the current version of the patch these facts are enforced by asserts. Thanks for clarification!

reames requested changes to this revision.Oct 26 2017, 10:18 AM
reames added inline comments.
lib/Transforms/Scalar/GVN.cpp
1204–1206

This assertion doesn't look correct. Your message says the instruction doesn't have implicit control flow, the actual assert says it's not the first in the basic block. One or the other needs fixed.

1206

This looks highly suspicious. Why remove from MD, but not erase? I think you want to either a) remove the MD->removeInstruction (because it's handled on actual deletion), or b) leave a good comment here.

Also, if replacing eraseInstruction with markForDeletion really is NFC, it should be a standalone patch which this is rebased on.

2109

The switch to a range iteration, and addition of the assertion is a separable change. Please commit that as a change of it's own and rebase the patch to reduce the diff for review.

2114

This can be written as:
InvalidateImplicitCF |= new_condition;

2309

Hm, optional larger code organization suggestion. Could we introduce two operations: one which invalidates and one which rebuilds? This would make the invariants of the FillICFI function more clear and would make this code a bit more readable.

If we could arrange to have the ICFI lazily rebuild on access to the block, that might be an overall cleaner scheme.

Again, both parts are optional.

2402

This assert doesn't look like it belongs in the lamda. Lift out to containing function.

This revision now requires changes to proceed.Oct 26 2017, 10:18 AM
dberlin added inline comments.Oct 26 2017, 11:01 AM
lib/Transforms/Scalar/GVN.cpp
2114
|= wouldn't short circuit however, since it's bitwise and not logical
So it would have to be InvalidateImplicitCF = x || y
reames added inline comments.Oct 26 2017, 2:50 PM
lib/Transforms/Scalar/GVN.cpp
2114

If we're actually worried about reducing the evaluation time of the RHS - not sure we are - we can manually LICM the expression "FirstICFI.lookup(I->getParent())" since this is all the same BB.

I'm not sure the minor time savings of the short circuit is worth the code complexity honestly. I'd also expect a decent compiler to recognize that this is actually boolean. If we don't get that case, dang we really should.

mkazantsev edited the summary of this revision. (Show Details)Oct 26 2017, 10:11 PM
mkazantsev abandoned this revision.Oct 27 2017, 4:08 AM
mkazantsev marked 2 inline comments as done.

Given that https://reviews.llvm.org/D37460 and this one should only go together, it makes more sense to merge them into one. I am abandoning this one; the fixed changes from here will be merged into https://reviews.llvm.org/D37460. Abandoning this one.

lib/Transforms/Scalar/GVN.cpp
1204–1206

The intention here is to make sure that we don't need to take actions updating this map here. Actually this assertion doesn't bring in anything new because all map-related stuff is handled within markInstructionForDeletion.

1206

Thanks for pointing out. Submitted as separate change: https://reviews.llvm.org/D39369

2109

Commited as separate NFC: https://reviews.llvm.org/rL316748

2114

Reworked this piece to avoid both redundan execution and dangling pointers.

2309

I will consider ractoring out ICF-related stuff into a separate structure in follow-up patches.

2402

I wasn't certain that InstrsToErase always contains instructions from one block at this point, but it seems it does. Hoisted the assert out.