This patch fixes two constant propagation problems in GVN for assume intrinsic instruction. The first problem is filed in PR25285: when there are multiple instances of the same assume instructions in a basic block with a loop back-edge pointing to itself, the first assume instruction is incorrectly constant-propagated with the value "TRUE" and eliminated later on. The second problem is that, the value "TRUE" is incorrectly constant-propagated to the successor blocks that are not dominated by the basic block containing the assume instruction.
Details
Diff Detail
Event Timeline
Something is wrong with the review, because I can't look at code that wasn't modified.
I think the diff was not generated with -U99999.
include/llvm/Transforms/Utils/Local.h | ||
---|---|---|
320 | I'm not sure this wording change makes sense. It doesn't care whether it's the end of the block that dominates or not. | |
lib/Transforms/Scalar/GVN.cpp | ||
2137 | If there are multiple edges to the same BB, we should not do the replacement at all (we lack post-dominance info necessary to be safe in that case). | |
2201 | If this problem only occurs with assume instructions, we shouldn't change the generic code. | |
2277 | Same comment as above :) | |
lib/Transforms/Utils/Local.cpp | ||
1540 | This is definitely not correct. (Even if this change was correct, you've also now made the replacement functions completely inconsistent with each other.) |
Also note, it's possible to have a case where root.start dominates but it
is not safe to propagate due to multiple edges from that block to to
root.getEnd.
The whole reason for having the edge is to avoid this case, AFAIK.
So i'm pretty sure using root.getStart() is just going to cause incorrect
transforms.
Note that all of propagateEquality is really somewhat of a hack to avoid
using proper control dependence, and at some point, we'll likely want to
just rip it out and use post-dominators to do this *right*.
I don't know whether the optimization capability we lose here by avoiding
assume in some cases makes us want to do that more (I leave that question
to data and chandler)
I don't have much time right now to do it because of exams, but if it won't
get accept until friday, I will take a look.
lib/Transforms/Scalar/GVN.cpp | ||
---|---|---|
2137 | Note that, there is a condition about "multiple edges to the same BB": it is from Root.Start to Root.End. That's when DominatesByEdge is set to false, and that's exactly the reason why DominatesByEdge was introduced as a parameter to this function. See the example added before @_Z1ii in test/Transforms/GVN/assume-equal.ll ; This test checks if constant propagation works for multiple node edges %cmp = icmp eq i32 %p, 42 call void @llvm.assume(i1 %cmp) ; CHECK: br i1 true, label %bb2, label %bb2 br i1 %cmp, label %bb2, label %bb2 bb2: ; CHECK: br i1 true, label %bb2, label %bb2 br i1 %cmp, label %bb2, label %bb2 ; CHECK: ret i32 42 ret i32 %p } I was trying to make the comments in more detail. But if it is confusing, we can improve it. | |
2201 | This is a general problem in GVN::propagateEquality with the following code:unsigned NumReplacements = DominatesByEdge ? replaceDominatedUsesWith(LHS, RHS, *DT, Root) : replaceDominatedUsesWith(LHS, RHS, *DT, Root.getStart()); When DominatesByEdge is TRUE, it passes the edge "Root" to replaceDominatedUsesWith, and we are fine. Since, if the edge "Root" dominates a USE, then Root.Start must dominates the USE. When DominatesByEdge is FALSE, it passes the Root.End to replaceDominatedUsesWith, and we have a problem here. Since, if Root.End dominates a USE, it does not mean that Root.Start dominates the USE. We show in the test case _Z1im below. In this case, we ideally need "RootDominatesEnd" (in GVN::propagateEquality) to further test before calling replaceDominatedWith(...Root.End), e.g.: unsigned NumReplacements = DominatesByEdge ? replaceDominatedUsesWith(LHS, RHS, *DT, Root) : RootDominatesEnd? replaceDominatedUsesWith(LHS, RHS, *DT, Root.getEnd()) : 0; However, when we cannot use the current RootDominatesEnd defined as below to test: // For speed, compute a conservative fast approximation to // DT->dominates(Root, Root.getEnd()); bool RootDominatesEnd = isOnlyReachableViaThisEdge(Root, DT); The reason is that, it only works when DominatesByEdge=TRUE, not when DominatesByEdge =FALSE where there are multiple edges (e.g., 2 edges) from Root.Start to Root.End, see @_Z1ii in test/Transforms/GVN/assume-equal.ll again. And, we cannot use the following either, boot RootDominatesEnd = DT->dominates(Root, Root.getEnd()); since, it ran into the problem Prazek saw before, giving an assert when DominatesByEdge=FALSE. see @_Z1ii in test/Transforms/GVN/assume-equal.ll again Now, If we modify RootDominatesEnd to bool RootDominatesEnd = DT.dominates(Root.Start(), Root.End()), then it can be used: unsigned NumReplacements = DominatesByEdge ? replaceDominatedUsesWith(LHS, RHS, *DT, Root) : RootDominatesEnd? replaceDominatedUsesWith(LHS, RHS, *DT, Root.getEnd()) : 0; I didn't post this change, because 1) DT.dominates will slow down as comments show 2) the name RootDominatesEnd is not proper. In fact, the simplest way is to
These two changes together will give exactly the same effect as when passing an edge, i.e. ,replaceDominatedUsesWith(..... BasicBlockEdge Root). Note that, currently GVN is the only caller of replaceDominatedUsesWith( ..., BasicBlock* BB). So, it is safe to change to use "properlyDominates". Although it is a general problem with propagateEquality, I don't have any test cases other than those with assume instructions. The reason is that,
| |
lib/Transforms/Utils/Local.cpp | ||
1540 | Since propagateEquality in GVN is the only caller, that's why we changed the comments to inform other users that, this function will replace USES from the END of the BB. If the called wants to replace the uses in the BB itself, the other overloaded interface replaceDomiantedUsesWith(..... BasicBlockEdge Root) can be used. |
I actually agree with Daniel's comments, especially about the proposal to rewrite propagateEquality. I would see my fix as a necessary co-implementation with Piotr's earlier patch http://reviews.llvm.org/D12170, since my changes only happen in that particular commit.
Probably we can submit this "necessary" fix as well as the test cases (for "assume") first, and then think of restructure the propagateEquality.
I update this new patch to fix my previously wrong comments pointed out by Daniel. Thanks for that:)
I like Ray's plan of first making the targeted stability fix and then deciding how best to restructure propagateEquality to make it clearer. The bug illustrated by the @_Z1im test case is pretty egregious and ought to be fixed promptly IMO.
Daniel, you suggested possibly splitting propagateEquality, but there is a lot of functionality in there that you'd like to share between the assume intrinsic caller and the other callers. For example, the code that infers A == 1 && B == 1 from A && B == 1 applies equally regardless of whether A && B == 1 came from "assume(A && B)" or "if (A && B)".
I think the fundamental difference between assume and the other callers of propagateEquality is that for assume, the equality property becomes known true immediately following the assume instruction while for the other callers, the equality property becomes known true along the outgoing edge of the block ending in the conditional branch or switch. It shouldn't be hard to make that distinction in the interface to propagateEquality. Perhaps something like this:
/// The given values are known to be equal at a certain point, P, in the program. /// When DominatesByEdge is true, that point is the CFG edge Root. /// When DominatesByEdge is false, that point is the Instruction Instr. /// Exploit this, for example by replacing 'LHS' with 'RHS' at all uses dominated by P. /// Returns whether a change was made. bool GVN::propagateEquality(Value *LHS, Value *RHS, const BasicBlockEdge &Root, Instruction *Instr, bool DominatesByEdge) {
When DominatesByEdge is true, Instr would be ignored. And when DominatesByEdge is false, Root would be ignored. Then the dominance check for the assume case becomes one of "does this instruction dominate" rather than "does this block dominate". (Note that this lets us get rid of the "for each successor" loop inside processAssumeIntrinsic, which is pretty artificial. I suspect it was written that way just to make it easier to reuse propagateEquality.)
To start with, I'm fine with committing this patch if we agree to address
the other issues, *somehow*, in a followup (either by y'all or by Piotr).
Hi Piotr,
Did you get a time to look at the fix I uploaded for the second time? Does it look good to you?
Daniel’s suggestion on restructuring the propagateEquality is good and necessary, since many developers find this function confusing.
Dave and I tried the idea he mentioned below, i.e., the new interface, but it seems that it does not help much to eliminate the code confusion in that function, and not simpler than the current fix.
I am wondering if you have any plan to rewrite this function. Are you familiar with the code? If not, do you know who is familiar with this code, and can assist us in doing the rewriting. We can all help if needed, but we need an expert first.
Thank you very much!
Regards,
Ray
From: Daniel Berlin [mailto:dberlin@dberlin.org]
Sent: Thursday, January 14, 2016 3:42 PM
To: reviews+D16100+public+7f8d41cf7877574b@reviews.llvm.org
Cc: Zhang, Yuanrui <yuanrui.zhang@intel.com>; Nick Lewycky <nlewycky@google.com>; Piotr Padlewski <piotr.padlewski@gmail.com>; Kreitzer, David L <david.l.kreitzer@intel.com>; llvm-commits <llvm-commits@lists.llvm.org>
Subject: Re: [PATCH] D16100: Fix for two constant propagation problems in GVN with assume intrinsic instruction
To start with, I'm fine with committing this patch if we agree to address the other issues, *somehow*, in a followup (either by y'all or by Piotr).
I'm not sure this wording change makes sense.
It doesn't care whether it's the end of the block that dominates or not.
And by definition, uses in the same-block as their definitions must be dominated by those defs.