Skip to content
This repository was archived by the owner on Mar 28, 2020. It is now read-only.

Commit 3e55f3d

Browse files
committed
GVN-hoist: fix store past load dependence analysis (PR30216)
To hoist stores past loads, we used to search for potential conflicting loads on the hoisting path by following a MemorySSA def-def link from the store to be hoisted to the previous defining memory access, and from there we followed the def-use chains to all the uses that occur on the hoisting path. The problem is that the def-def link may point to a store that does not alias with the store to be hoisted, and so the loads that are walked may not alias with the store to be hoisted, and even as in the testcase of PR30216, the loads that may alias with the store to be hoisted are not visited. The current patch visits all loads on the path from the store to be hoisted to the hoisting position and uses the alias analysis to ask whether the store may alias the load. I was not able to use the MemorySSA functionality to ask for whether load and store are clobbered: I'm not sure which function to call, so I used a call to AA->isNoAlias(). Store past store is still working as before using a MemorySSA query: I added an extra test to pr30216.ll to make sure store past store does not regress. Differential Revision: https://reviews.llvm.org/D24517 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@282168 91177308-0d34-0410-b5e6-96231b3b80d8
1 parent 3132c38 commit 3e55f3d

File tree

2 files changed

+85
-29
lines changed

2 files changed

+85
-29
lines changed

lib/Transforms/Scalar/GVNHoist.cpp

Lines changed: 33 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -329,49 +329,53 @@ class GVNHoist {
329329
return I1DFS < I2DFS;
330330
}
331331

332-
// Return true when there are users of Def in BB.
333-
bool hasMemoryUseOnPath(MemoryAccess *Def, const BasicBlock *BB,
334-
const Instruction *OldPt) {
335-
const BasicBlock *DefBB = Def->getBlock();
332+
// Return true when there are memory uses of Def in BB.
333+
bool hasMemoryUseOnPath(const Instruction *NewPt, MemoryDef *Def, const BasicBlock *BB) {
334+
const Instruction *OldPt = Def->getMemoryInst();
336335
const BasicBlock *OldBB = OldPt->getParent();
336+
const BasicBlock *NewBB = NewPt->getParent();
337337

338-
for (User *U : Def->users())
339-
if (auto *MU = dyn_cast<MemoryUse>(U)) {
340-
// FIXME: MU->getBlock() does not get updated when we move the instruction.
341-
BasicBlock *UBB = MU->getMemoryInst()->getParent();
342-
// Only analyze uses in BB.
343-
if (BB != UBB)
344-
continue;
345-
346-
// A use in the same block as the Def is on the path.
347-
if (UBB == DefBB) {
348-
assert(MSSA->locallyDominates(Def, MU) && "def not dominating use");
349-
return true;
350-
}
338+
bool ReachedNewPt = false;
339+
MemoryLocation DefLoc = MemoryLocation::get(OldPt);
340+
const MemorySSA::AccessList *Acc = MSSA->getBlockAccesses(BB);
341+
for (const MemoryAccess &MA : *Acc) {
342+
auto *MU = dyn_cast<MemoryUse>(&MA);
343+
if (!MU)
344+
continue;
351345

352-
if (UBB != OldBB)
353-
return true;
346+
// Do not check whether MU aliases Def when MU occurs after OldPt.
347+
if (BB == OldBB && firstInBB(OldPt, MU->getMemoryInst()))
348+
break;
354349

355-
// It is only harmful to hoist when the use is before OldPt.
356-
if (firstInBB(MU->getMemoryInst(), OldPt))
357-
return true;
350+
// Do not check whether MU aliases Def when MU occurs before NewPt.
351+
if (BB == NewBB) {
352+
if (!ReachedNewPt) {
353+
if (firstInBB(MU->getMemoryInst(), NewPt))
354+
continue;
355+
ReachedNewPt = true;
356+
}
358357
}
359358

359+
if (!AA->isNoAlias(DefLoc, MemoryLocation::get(MU->getMemoryInst())))
360+
return true;
361+
}
362+
360363
return false;
361364
}
362365

363366
// Return true when there are exception handling or loads of memory Def
364-
// between OldPt and NewPt.
367+
// between Def and NewPt. This function is only called for stores: Def is
368+
// the MemoryDef of the store to be hoisted.
365369

366370
// Decrement by 1 NBBsOnAllPaths for each block between HoistPt and BB, and
367371
// return true when the counter NBBsOnAllPaths reaces 0, except when it is
368372
// initialized to -1 which is unlimited.
369-
bool hasEHOrLoadsOnPath(const Instruction *NewPt, const Instruction *OldPt,
370-
MemoryAccess *Def, int &NBBsOnAllPaths) {
373+
bool hasEHOrLoadsOnPath(const Instruction *NewPt, MemoryDef *Def,
374+
int &NBBsOnAllPaths) {
371375
const BasicBlock *NewBB = NewPt->getParent();
372-
const BasicBlock *OldBB = OldPt->getParent();
376+
const BasicBlock *OldBB = Def->getBlock();
373377
assert(DT->dominates(NewBB, OldBB) && "invalid path");
374-
assert(DT->dominates(Def->getBlock(), NewBB) &&
378+
assert(DT->dominates(Def->getDefiningAccess()->getBlock(), NewBB) &&
375379
"def does not dominate new hoisting point");
376380

377381
// Walk all basic blocks reachable in depth-first iteration on the inverse
@@ -390,7 +394,7 @@ class GVNHoist {
390394
return true;
391395

392396
// Check that we do not move a store past loads.
393-
if (hasMemoryUseOnPath(Def, *I, OldPt))
397+
if (hasMemoryUseOnPath(NewPt, Def, *I))
394398
return true;
395399

396400
// Stop walk once the limit is reached.
@@ -473,7 +477,7 @@ class GVNHoist {
473477

474478
// Check for unsafe hoistings due to side effects.
475479
if (K == InsKind::Store) {
476-
if (hasEHOrLoadsOnPath(NewPt, OldPt, D, NBBsOnAllPaths))
480+
if (hasEHOrLoadsOnPath(NewPt, dyn_cast<MemoryDef>(U), NBBsOnAllPaths))
477481
return false;
478482
} else if (hasEHOnPath(NewBB, OldBB, NBBsOnAllPaths))
479483
return false;
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
; RUN: opt -S -gvn-hoist < %s | FileCheck %s
2+
3+
; Make sure the two stores @B do not get hoisted past the load @B.
4+
5+
; CHECK-LABEL: define i8* @Foo
6+
; CHECK: store
7+
; CHECK: store
8+
; CHECK: load
9+
; CHECK: store
10+
11+
@A = external global i8
12+
@B = external global i8*
13+
14+
define i8* @Foo() {
15+
store i8 0, i8* @A
16+
br i1 undef, label %if.then, label %if.else
17+
18+
if.then:
19+
store i8* null, i8** @B
20+
ret i8* null
21+
22+
if.else:
23+
%1 = load i8*, i8** @B
24+
store i8* null, i8** @B
25+
ret i8* %1
26+
}
27+
28+
; Make sure the two stores @B do not get hoisted past the store @GlobalVar.
29+
30+
; CHECK-LABEL: define i8* @Fun
31+
; CHECK: store
32+
; CHECK: store
33+
; CHECK: store
34+
; CHECK: store
35+
; CHECK: load
36+
37+
@GlobalVar = internal global i8 0
38+
39+
define i8* @Fun() {
40+
store i8 0, i8* @A
41+
br i1 undef, label %if.then, label %if.else
42+
43+
if.then:
44+
store i8* null, i8** @B
45+
ret i8* null
46+
47+
if.else:
48+
store i8 0, i8* @GlobalVar
49+
store i8* null, i8** @B
50+
%1 = load i8*, i8** @B
51+
ret i8* %1
52+
}

0 commit comments

Comments
 (0)