forked from OSchip/llvm-project
NewGVN: Clean up how we handle the INITIAL class so that everything in
it is dead or unreachable, as it should be. This also makes the leader of INITIAL undef, enabling us to handle irreducibility properly. Summary: This lets us verify, more than we do now, that we didn't screw up value numbering. Reviewers: davide Subscribers: Prazek, llvm-commits Differential Revision: https://reviews.llvm.org/D29842 llvm-svn: 294844
This commit is contained in:
parent
bcb6622c95
commit
b79f53669a
|
@ -213,6 +213,11 @@ class NewGVN : public FunctionPass {
|
||||||
ArrayRecycler<Value *> ArgRecycler;
|
ArrayRecycler<Value *> ArgRecycler;
|
||||||
|
|
||||||
// Congruence class info.
|
// Congruence class info.
|
||||||
|
|
||||||
|
// This class is called INITIAL in the paper. It is the class everything
|
||||||
|
// startsout in, and represents any value. Being an optimistic analysis,
|
||||||
|
// anything in the INITIAL class has the value TOP, which is indeterminate and
|
||||||
|
// equivalent to everything.
|
||||||
CongruenceClass *InitialClass;
|
CongruenceClass *InitialClass;
|
||||||
std::vector<CongruenceClass *> CongruenceClasses;
|
std::vector<CongruenceClass *> CongruenceClasses;
|
||||||
unsigned NextCongruenceNum;
|
unsigned NextCongruenceNum;
|
||||||
|
@ -691,8 +696,15 @@ const CallExpression *NewGVN::createCallExpression(CallInst *CI,
|
||||||
// return it. Otherwise, return the operand itself.
|
// return it. Otherwise, return the operand itself.
|
||||||
Value *NewGVN::lookupOperandLeader(Value *V) const {
|
Value *NewGVN::lookupOperandLeader(Value *V) const {
|
||||||
CongruenceClass *CC = ValueToClass.lookup(V);
|
CongruenceClass *CC = ValueToClass.lookup(V);
|
||||||
if (CC && (CC != InitialClass))
|
if (CC) {
|
||||||
|
// Everything in INITIAL is represneted by undef, as it can be any value.
|
||||||
|
// We do have to make sure we get the type right though, so we can't set the
|
||||||
|
// RepLeader to undef.
|
||||||
|
if (CC == InitialClass)
|
||||||
|
return UndefValue::get(V->getType());
|
||||||
return CC->RepStoredValue ? CC->RepStoredValue : CC->RepLeader;
|
return CC->RepStoredValue ? CC->RepStoredValue : CC->RepLeader;
|
||||||
|
}
|
||||||
|
|
||||||
return V;
|
return V;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -957,9 +969,8 @@ const Expression *NewGVN::performSymbolicAggrValueEvaluation(Instruction *I) {
|
||||||
// expression.
|
// expression.
|
||||||
assert(II->getNumArgOperands() == 2 &&
|
assert(II->getNumArgOperands() == 2 &&
|
||||||
"Expect two args for recognised intrinsics.");
|
"Expect two args for recognised intrinsics.");
|
||||||
return createBinaryExpression(Opcode, EI->getType(),
|
return createBinaryExpression(
|
||||||
II->getArgOperand(0),
|
Opcode, EI->getType(), II->getArgOperand(0), II->getArgOperand(1));
|
||||||
II->getArgOperand(1));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1200,7 +1211,7 @@ void NewGVN::moveValueToNewCongruenceClass(Instruction *I,
|
||||||
}
|
}
|
||||||
|
|
||||||
// We don't need to sort members if there is only 1, and we don't care about
|
// We don't need to sort members if there is only 1, and we don't care about
|
||||||
// sorting the initial class because everything either gets out of it or is
|
// sorting the INITIAL class because everything either gets out of it or is
|
||||||
// unreachable.
|
// unreachable.
|
||||||
if (OldClass->Members.size() == 1 || OldClass == InitialClass) {
|
if (OldClass->Members.size() == 1 || OldClass == InitialClass) {
|
||||||
OldClass->RepLeader = *(OldClass->Members.begin());
|
OldClass->RepLeader = *(OldClass->Members.begin());
|
||||||
|
@ -1229,7 +1240,7 @@ void NewGVN::moveValueToNewCongruenceClass(Instruction *I,
|
||||||
void NewGVN::performCongruenceFinding(Instruction *I, const Expression *E) {
|
void NewGVN::performCongruenceFinding(Instruction *I, const Expression *E) {
|
||||||
ValueToExpression[I] = E;
|
ValueToExpression[I] = E;
|
||||||
// This is guaranteed to return something, since it will at least find
|
// This is guaranteed to return something, since it will at least find
|
||||||
// INITIAL.
|
// TOP.
|
||||||
|
|
||||||
CongruenceClass *IClass = ValueToClass[I];
|
CongruenceClass *IClass = ValueToClass[I];
|
||||||
assert(IClass && "Should have found a IClass");
|
assert(IClass && "Should have found a IClass");
|
||||||
|
@ -1418,8 +1429,7 @@ void NewGVN::processOutgoingEdges(TerminatorInst *TI, BasicBlock *B) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// The algorithm initially places the values of the routine in the INITIAL
|
// The algorithm initially places the values of the routine in the INITIAL
|
||||||
// congruence
|
// congruence class. The leader of INITIAL is the undetermined value `TOP`.
|
||||||
// class. The leader of INITIAL is the undetermined value `TOP`.
|
|
||||||
// When the algorithm has finished, values still in INITIAL are unreachable.
|
// When the algorithm has finished, values still in INITIAL are unreachable.
|
||||||
void NewGVN::initializeCongruenceClasses(Function &F) {
|
void NewGVN::initializeCongruenceClasses(Function &F) {
|
||||||
// FIXME now i can't remember why this is 2
|
// FIXME now i can't remember why this is 2
|
||||||
|
@ -1433,8 +1443,11 @@ void NewGVN::initializeCongruenceClasses(Function &F) {
|
||||||
MemoryAccessToClass[MP] = InitialClass;
|
MemoryAccessToClass[MP] = InitialClass;
|
||||||
|
|
||||||
for (auto &I : B) {
|
for (auto &I : B) {
|
||||||
InitialValues.insert(&I);
|
// Don't insert void terminators into the class
|
||||||
ValueToClass[&I] = InitialClass;
|
if (!isa<TerminatorInst>(I) || !I.getType()->isVoidTy()) {
|
||||||
|
InitialValues.insert(&I);
|
||||||
|
ValueToClass[&I] = InitialClass;
|
||||||
|
}
|
||||||
// All memory accesses are equivalent to live on entry to start. They must
|
// All memory accesses are equivalent to live on entry to start. They must
|
||||||
// be initialized to something so that initial changes are noticed. For
|
// be initialized to something so that initial changes are noticed. For
|
||||||
// the maximal answer, we initialize them all to be the same as
|
// the maximal answer, we initialize them all to be the same as
|
||||||
|
@ -1585,7 +1598,8 @@ void NewGVN::valueNumberInstruction(Instruction *I) {
|
||||||
performCongruenceFinding(I, Symbolized);
|
performCongruenceFinding(I, Symbolized);
|
||||||
} else {
|
} else {
|
||||||
// Handle terminators that return values. All of them produce values we
|
// Handle terminators that return values. All of them produce values we
|
||||||
// don't currently understand.
|
// don't currently understand. We don't place non-value producing
|
||||||
|
// terminators in a class.
|
||||||
if (!I->getType()->isVoidTy()) {
|
if (!I->getType()->isVoidTy()) {
|
||||||
auto *Symbolized = createUnknownExpression(I);
|
auto *Symbolized = createUnknownExpression(I);
|
||||||
performCongruenceFinding(I, Symbolized);
|
performCongruenceFinding(I, Symbolized);
|
||||||
|
@ -2198,12 +2212,20 @@ bool NewGVN::eliminateInstructions(Function &F) {
|
||||||
// dead store elimination.
|
// dead store elimination.
|
||||||
SmallVector<ValueDFS, 8> PossibleDeadStores;
|
SmallVector<ValueDFS, 8> PossibleDeadStores;
|
||||||
|
|
||||||
// FIXME: We should eventually be able to replace everything still
|
if (CC->Dead)
|
||||||
// in the initial class with undef, as they should be unreachable.
|
|
||||||
// Right now, initial still contains some things we skip value
|
|
||||||
// numbering of (UNREACHABLE's, for example).
|
|
||||||
if (CC == InitialClass || CC->Dead)
|
|
||||||
continue;
|
continue;
|
||||||
|
// Everything still in the INITIAL class is unreachable or dead.
|
||||||
|
if (CC == InitialClass) {
|
||||||
|
#ifndef NDEBUG
|
||||||
|
for (auto M : CC->Members)
|
||||||
|
assert((!ReachableBlocks.count(cast<Instruction>(M)->getParent()) ||
|
||||||
|
InstructionsToErase.count(cast<Instruction>(M))) &&
|
||||||
|
"Everything in INITIAL should be unreachable or dead at this "
|
||||||
|
"point");
|
||||||
|
#endif
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
assert(CC->RepLeader && "We should have had a leader");
|
assert(CC->RepLeader && "We should have had a leader");
|
||||||
|
|
||||||
// If this is a leader that is always available, and it's a
|
// If this is a leader that is always available, and it's a
|
||||||
|
|
|
@ -228,8 +228,8 @@ bb23: ; preds = %bb4
|
||||||
}
|
}
|
||||||
|
|
||||||
;; This is an irreducible test case that will cause a memoryphi node loop
|
;; This is an irreducible test case that will cause a memoryphi node loop
|
||||||
;; in the two block.
|
;; in the two blocks.
|
||||||
;; it's equivalent to something like
|
;; It's equivalent to something like
|
||||||
;; *a = 0
|
;; *a = 0
|
||||||
;; if (<....>) goto loopmiddle
|
;; if (<....>) goto loopmiddle
|
||||||
;; loopstart:
|
;; loopstart:
|
||||||
|
@ -245,8 +245,8 @@ bb23: ; preds = %bb4
|
||||||
;; Both loads should equal 0, but it requires being
|
;; Both loads should equal 0, but it requires being
|
||||||
;; completely optimistic about MemoryPhis, otherwise
|
;; completely optimistic about MemoryPhis, otherwise
|
||||||
;; we will not be able to see through the cycle.
|
;; we will not be able to see through the cycle.
|
||||||
define i8 @quux(i8* noalias %arg, i8* noalias %arg2) {
|
define i8 @irreducible_memoryphi(i8* noalias %arg, i8* noalias %arg2) {
|
||||||
; CHECK-LABEL: @quux(
|
; CHECK-LABEL: @irreducible_memoryphi(
|
||||||
; CHECK-NEXT: bb:
|
; CHECK-NEXT: bb:
|
||||||
; CHECK-NEXT: store i8 0, i8* [[ARG:%.*]]
|
; CHECK-NEXT: store i8 0, i8* [[ARG:%.*]]
|
||||||
; CHECK-NEXT: br i1 undef, label [[BB2:%.*]], label [[BB1:%.*]]
|
; CHECK-NEXT: br i1 undef, label [[BB2:%.*]], label [[BB1:%.*]]
|
||||||
|
@ -274,6 +274,40 @@ bb3: ; preds = %bb2
|
||||||
%tmp3 = add i8 %tmp, %tmp2
|
%tmp3 = add i8 %tmp, %tmp2
|
||||||
ret i8 %tmp3
|
ret i8 %tmp3
|
||||||
}
|
}
|
||||||
|
;; This is an irreducible test case that will cause a phi node loop
|
||||||
|
;; in the two blocks
|
||||||
|
;;
|
||||||
|
;; It should return 0, but it requires being
|
||||||
|
;; completely optimistic about phis, otherwise
|
||||||
|
;; we will not be able to see through the cycle.
|
||||||
|
define i32 @irreducible_phi(i32 %arg) {
|
||||||
|
; CHECK-LABEL: @irreducible_phi(
|
||||||
|
; CHECK-NEXT: bb:
|
||||||
|
; CHECK-NEXT: br i1 undef, label [[BB2:%.*]], label [[BB1:%.*]]
|
||||||
|
; CHECK: bb1:
|
||||||
|
; CHECK-NEXT: br label [[BB2]]
|
||||||
|
; CHECK: bb2:
|
||||||
|
; CHECK-NEXT: br i1 undef, label [[BB1]], label [[BB3:%.*]]
|
||||||
|
; CHECK: bb3:
|
||||||
|
; CHECK-NEXT: ret i32 0
|
||||||
|
;
|
||||||
|
bb:
|
||||||
|
%tmp = add i32 0, %arg
|
||||||
|
br i1 undef, label %bb2, label %bb1
|
||||||
|
|
||||||
|
bb1: ; preds = %bb2, %bb
|
||||||
|
%phi1 = phi i32 [%tmp, %bb], [%phi2, %bb2]
|
||||||
|
br label %bb2
|
||||||
|
|
||||||
|
bb2: ; preds = %bb1, %bb
|
||||||
|
%phi2 = phi i32 [%tmp, %bb], [%phi1, %bb1]
|
||||||
|
br i1 undef, label %bb1, label %bb3
|
||||||
|
|
||||||
|
bb3: ; preds = %bb2
|
||||||
|
; This should be zero
|
||||||
|
%tmp3 = sub i32 %tmp, %phi2
|
||||||
|
ret i32 %tmp3
|
||||||
|
}
|
||||||
attributes #0 = { nounwind ssp uwtable "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" }
|
attributes #0 = { nounwind ssp uwtable "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" }
|
||||||
|
|
||||||
!llvm.ident = !{!0, !0, !0}
|
!llvm.ident = !{!0, !0, !0}
|
||||||
|
|
Loading…
Reference in New Issue