With this patch we compute the MemorySSA once and update it in the code generator.
Passes make check-all and test-suite on x86_64-linux.
Details
Diff Detail
Event Timeline
llvm/lib/Transforms/Scalar/GVNHoist.cpp | ||
---|---|---|
757 | What do you mean here? | |
766 | Are you guaranteed that anything that conflicted with the old store conflicts with this store by your hoist safety rules? it might be worth either giving an example here, or adding a testcase. The two problems are: 1 = MemoryDef<liveOnEntry> later If the hoist point is 1, and you simply move the store, you will now have: later This is wrong. 3 is now disconnected from the graph of reaching defs. Additionally, if the hoist point is 2, you end up with: later The memoryphi is now wrong (and you disconnected 2 from the reaching graph). |
llvm/lib/Transforms/Scalar/GVNHoist.cpp | ||
---|---|---|
757 | My first patch had both MemUses and MemDefs handled in the same way. | |
766 | I added that comment above Def to make sure it is clear that hoisting guarantees these properties: // The definition of this ld/st will not change: ld/st hoisting is // legal when the ld/st is not moved past its current definition. MemoryAccess *Def = OldMemAcc->getDefiningAccess();
Cannot happen: we cannot move the store "3" past store "2" as they are on the same chain (dependent.)
Cannot happen either as there is a use of "2" (the phi node) past which "3" cannot be moved. |
Let me think how to fix this in MSSA.
I will probably need some help in reviewing those changes, or suggestions.
Thanks!
It looks like the bug is because we do not update the block of a memory access: this patch fixes a crash where we were still referring to the block that used to contain an instruction that got hoisted.
--- a/llvm/lib/Transforms/Scalar/GVNHoist.cpp +++ b/llvm/lib/Transforms/Scalar/GVNHoist.cpp @@ -307,7 +307,8 @@ private: for (User *U : Def->users()) if (auto *MU = dyn_cast<MemoryUse>(U)) { - BasicBlock *UBB = MU->getBlock(); + // FIXME: MU->getBlock() does not get updated when we move the instruction. + BasicBlock *UBB = MU->getMemoryInst()->getParent(); // Only analyze uses in BB. if (BB != UBB) continue;
Why not having a MemoryAccess contain a reference to the instruction itself when we have one, and for MemoryPhi nodes we could reference the BB in which they occur:
--- a/llvm/include/llvm/Transforms/Utils/MemorySSA.h +++ b/llvm/include/llvm/Transforms/Utils/MemorySSA.h @@ -167,7 +167,7 @@ protected: private: MemoryAccess(const MemoryAccess &); void operator=(const MemoryAccess &); - BasicBlock *Block; + Value *InstructionOrBlock; }; template <>
Updated patch does not pass ninja check as it regresses in terms of number of hoisted instructions.
It may be because the MSSA still contains references to the BBs that used to contain ld/st and the dependence analysis is not able to move the remaining ld/st past these "ghost" ld/st that have been already hoisted.
Oh, i see what you are trying to do.
MemorySSA is not actually part of the IR right now, so you can't use moveBefore :(
It has it's own access lists, etc.
As you say, it would not be enough to simply to change what it stores, there are places we keep the BB.
For now, the right thing to do is to call removeAccess on the instruction, call moveBefore on the IR, then createMemoryAccessAfter <hoistpt's memoryaccess>.
Then replaceAllUses with the newInstruction.
The real solution to making this not messy would be to make MemorySSA part of the IR.
I will push for that.
llvm/lib/Transforms/Scalar/GVNHoist.cpp | ||
---|---|---|
748 | This will not work. |
Updated the patch as I first wrote it: I think this is the natural way to update an SSA variable.
This fails because when invoking:
+ MSSA->removeMemoryAccess(OldMemAcc);
we go from:
; MemoryUse(2)
%2 = load i32, i32* %v.addr, align 4
to no annotation at all:
%2 = load i32, i32* %v.addr, align 4
This is because removeMemoryAccess calls removeFromLookups which does:
Value *MemoryInst; if (MemoryUseOrDef *MUD = dyn_cast<MemoryUseOrDef>(MA)) { MemoryInst = MUD->getMemoryInst(); } else { MemoryInst = MA->getBlock(); } ValueToMemoryAccess.erase(MemoryInst);
and erase() removes both the new and old MemoryAccess'es leaving the instruction without MSSA annotation.
For loads this can be fixed by first removing the OldMemoryAccess and then creating the NewMemoryAccess.
This works for loads because there are no users of a MemoryUse.
However for a store, we need to keep both the old and new MemoryAccess'es at the same time, to be able to rename the uses of old with new.
I think the solution would be for "MSSA->removeMemoryAccess(OldMemAcc);" to only invalidate and remove "OldMemAcc" without touching "NewMemAcc" that should remain attached to the instruction.
Still has some problems of merging instruction annotations, and it does not seem to have issues with MSSA.
lib/Transforms/Scalar/GVNHoist.cpp | ||
---|---|---|
311 ↗ | (On Diff #66217) | Note that if I remove this change there are tests in the test-suite that fail. I will reduce a testcase for this. |
lib/Transforms/Scalar/GVNHoist.cpp | |||||
---|---|---|---|---|---|
759 ↗ | (On Diff #66217) | The comment is not correct for stores. A
C C has load %p2 You will have: A: 1 = MemoryDef(liveOnEntry) store %p2 B: 2 = MemoryDef(1) store %p2 C: 3= MemoryPhi(1, 2) MemoryUse(3) load %p2 If you hoist the store in B to A, your code will give: A: 1 = MemoryDef(liveOnEntry) store %p2 2 = MemoryDef(1) store %p2 B: C: 3= MemoryPhi(1, 2) MemoryUse(3) This is not correct, it should be MemoryPhi(2, 2) at worst, and in MemorySSA, we require you kill the phi. code to do that: MemoryAccess *SamePhiOp = nullptr; for (auto &Op : Phi->incoming_values()) { if (!SamePhiOp) SamePhiOp = cast<MemoryAccess>(Op.get()); else if (SamePhiOp != Op.get()) { SamePhiOp = nullptr; break; } } // If this has some value, it means all operands of the phi are now the // same if (SamePhiOp) { Phi->replaceAllUsesWith(SamePhiOp); MSSA->removeMemoryAccess(Phi); } Please make sure this happens, and use print-memoryssa to verify memoryssa is updated properly in testcases. |
Updated patch addressing all comments from Danny.
Passes check and llvm test-suite on x86_64-linux.
This will not work.
You have to removeAccess(Repl), then createMemoryAccessAfter (access for hoist point).