mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-23 11:13:28 +01:00
[NewGVN] Split OpPHI detection and creation.
It also adds a check making sure PHIs for operands are all in the same block. Patch by Daniel Berlin <dberlin@dberlin.org> Reviewers: dberlin, davide Differential Revision: https://reviews.llvm.org/D43865 llvm-svn: 330444
This commit is contained in:
parent
8dc720bb33
commit
39281bf454
@ -2724,9 +2724,14 @@ NewGVN::makePossiblePHIOfOps(Instruction *I,
|
|||||||
MemAccess->getDefiningAccess()->getBlock() == I->getParent())
|
MemAccess->getDefiningAccess()->getBlock() == I->getParent())
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
SmallPtrSet<const Value *, 10> VisitedOps;
|
|
||||||
// Convert op of phis to phi of ops
|
// Convert op of phis to phi of ops
|
||||||
for (auto *Op : I->operand_values()) {
|
SmallPtrSet<const Value *, 10> VisitedOps;
|
||||||
|
SmallVector<Value *, 4> Ops(I->operand_values());
|
||||||
|
BasicBlock *SamePHIBlock = nullptr;
|
||||||
|
PHINode *OpPHI = nullptr;
|
||||||
|
if (!DebugCounter::shouldExecute(PHIOfOpsCounter))
|
||||||
|
return nullptr;
|
||||||
|
for (auto *Op : Ops) {
|
||||||
if (!isa<PHINode>(Op)) {
|
if (!isa<PHINode>(Op)) {
|
||||||
auto *ValuePHI = RealToTemp.lookup(Op);
|
auto *ValuePHI = RealToTemp.lookup(Op);
|
||||||
if (!ValuePHI)
|
if (!ValuePHI)
|
||||||
@ -2734,116 +2739,125 @@ NewGVN::makePossiblePHIOfOps(Instruction *I,
|
|||||||
DEBUG(dbgs() << "Found possible dependent phi of ops\n");
|
DEBUG(dbgs() << "Found possible dependent phi of ops\n");
|
||||||
Op = ValuePHI;
|
Op = ValuePHI;
|
||||||
}
|
}
|
||||||
auto *OpPHI = cast<PHINode>(Op);
|
OpPHI = cast<PHINode>(Op);
|
||||||
// No point in doing this for one-operand phis.
|
if (!SamePHIBlock) {
|
||||||
if (OpPHI->getNumOperands() == 1)
|
SamePHIBlock = getBlockForValue(OpPHI);
|
||||||
continue;
|
} else if (SamePHIBlock != getBlockForValue(OpPHI)) {
|
||||||
if (!DebugCounter::shouldExecute(PHIOfOpsCounter))
|
|
||||||
return nullptr;
|
|
||||||
SmallVector<ValPair, 4> Ops;
|
|
||||||
SmallPtrSet<Value *, 4> Deps;
|
|
||||||
auto *PHIBlock = getBlockForValue(OpPHI);
|
|
||||||
RevisitOnReachabilityChange[PHIBlock].reset(InstrToDFSNum(I));
|
|
||||||
for (unsigned PredNum = 0; PredNum < OpPHI->getNumOperands(); ++PredNum) {
|
|
||||||
auto *PredBB = OpPHI->getIncomingBlock(PredNum);
|
|
||||||
Value *FoundVal = nullptr;
|
|
||||||
// We could just skip unreachable edges entirely but it's tricky to do
|
|
||||||
// with rewriting existing phi nodes.
|
|
||||||
if (ReachableEdges.count({PredBB, PHIBlock})) {
|
|
||||||
// Clone the instruction, create an expression from it that is
|
|
||||||
// translated back into the predecessor, and see if we have a leader.
|
|
||||||
Instruction *ValueOp = I->clone();
|
|
||||||
SmallPtrSet<Value *, 4> CurrentDeps;
|
|
||||||
if (MemAccess)
|
|
||||||
TempToMemory.insert({ValueOp, MemAccess});
|
|
||||||
bool SafeForPHIOfOps = true;
|
|
||||||
VisitedOps.clear();
|
|
||||||
for (auto &Op : ValueOp->operands()) {
|
|
||||||
auto *OrigOp = &*Op;
|
|
||||||
// When these operand changes, it could change whether there is a
|
|
||||||
// leader for us or not, so we have to add additional users.
|
|
||||||
if (isa<PHINode>(Op)) {
|
|
||||||
Op = Op->DoPHITranslation(PHIBlock, PredBB);
|
|
||||||
if (Op != OrigOp && Op != I)
|
|
||||||
CurrentDeps.insert(Op);
|
|
||||||
} else if (auto *ValuePHI = RealToTemp.lookup(Op)) {
|
|
||||||
if (getBlockForValue(ValuePHI) == PHIBlock)
|
|
||||||
Op = ValuePHI->getIncomingValueForBlock(PredBB);
|
|
||||||
}
|
|
||||||
// If we phi-translated the op, it must be safe.
|
|
||||||
SafeForPHIOfOps =
|
|
||||||
SafeForPHIOfOps &&
|
|
||||||
(Op != OrigOp || OpIsSafeForPHIOfOps(Op, PHIBlock, VisitedOps));
|
|
||||||
}
|
|
||||||
// FIXME: For those things that are not safe we could generate
|
|
||||||
// expressions all the way down, and see if this comes out to a
|
|
||||||
// constant. For anything where that is true, and unsafe, we should
|
|
||||||
// have made a phi-of-ops (or value numbered it equivalent to something)
|
|
||||||
// for the pieces already.
|
|
||||||
FoundVal = !SafeForPHIOfOps ? nullptr
|
|
||||||
: findLeaderForInst(ValueOp, Visited,
|
|
||||||
MemAccess, I, PredBB);
|
|
||||||
ValueOp->deleteValue();
|
|
||||||
if (!FoundVal) {
|
|
||||||
// We failed to find a leader for the current ValueOp, but this might
|
|
||||||
// change in case of the translated operands change.
|
|
||||||
if (SafeForPHIOfOps)
|
|
||||||
for (auto Dep : CurrentDeps)
|
|
||||||
addAdditionalUsers(Dep, I);
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
Deps.insert(CurrentDeps.begin(), CurrentDeps.end());
|
|
||||||
} else {
|
|
||||||
DEBUG(dbgs() << "Skipping phi of ops operand for incoming block "
|
|
||||||
<< getBlockName(PredBB)
|
|
||||||
<< " because the block is unreachable\n");
|
|
||||||
FoundVal = UndefValue::get(I->getType());
|
|
||||||
RevisitOnReachabilityChange[PHIBlock].set(InstrToDFSNum(I));
|
|
||||||
}
|
|
||||||
|
|
||||||
Ops.push_back({FoundVal, PredBB});
|
|
||||||
DEBUG(dbgs() << "Found phi of ops operand " << *FoundVal << " in "
|
|
||||||
<< getBlockName(PredBB) << "\n");
|
|
||||||
}
|
|
||||||
for (auto Dep : Deps)
|
|
||||||
addAdditionalUsers(Dep, I);
|
|
||||||
sortPHIOps(Ops);
|
|
||||||
auto *E = performSymbolicPHIEvaluation(Ops, I, PHIBlock);
|
|
||||||
if (isa<ConstantExpression>(E) || isa<VariableExpression>(E)) {
|
|
||||||
DEBUG(dbgs()
|
DEBUG(dbgs()
|
||||||
<< "Not creating real PHI of ops because it simplified to existing "
|
<< "PHIs for operands are not all in the same block, aborting\n");
|
||||||
"value or constant\n");
|
return nullptr;
|
||||||
return E;
|
|
||||||
}
|
}
|
||||||
auto *ValuePHI = RealToTemp.lookup(I);
|
// No point in doing this for one-operand phis.
|
||||||
bool NewPHI = false;
|
if (OpPHI->getNumOperands() == 1) {
|
||||||
if (!ValuePHI) {
|
OpPHI = nullptr;
|
||||||
ValuePHI =
|
continue;
|
||||||
PHINode::Create(I->getType(), OpPHI->getNumOperands(), "phiofops");
|
|
||||||
addPhiOfOps(ValuePHI, PHIBlock, I);
|
|
||||||
NewPHI = true;
|
|
||||||
NumGVNPHIOfOpsCreated++;
|
|
||||||
}
|
}
|
||||||
if (NewPHI) {
|
}
|
||||||
for (auto PHIOp : Ops)
|
|
||||||
ValuePHI->addIncoming(PHIOp.first, PHIOp.second);
|
|
||||||
} else {
|
|
||||||
TempToBlock[ValuePHI] = PHIBlock;
|
|
||||||
unsigned int i = 0;
|
|
||||||
for (auto PHIOp : Ops) {
|
|
||||||
ValuePHI->setIncomingValue(i, PHIOp.first);
|
|
||||||
ValuePHI->setIncomingBlock(i, PHIOp.second);
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
RevisitOnReachabilityChange[PHIBlock].set(InstrToDFSNum(I));
|
|
||||||
DEBUG(dbgs() << "Created phi of ops " << *ValuePHI << " for " << *I
|
|
||||||
<< "\n");
|
|
||||||
|
|
||||||
|
if (!OpPHI)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
SmallVector<ValPair, 4> PHIOps;
|
||||||
|
SmallPtrSet<Value *, 4> Deps;
|
||||||
|
auto *PHIBlock = getBlockForValue(OpPHI);
|
||||||
|
RevisitOnReachabilityChange[PHIBlock].reset(InstrToDFSNum(I));
|
||||||
|
for (unsigned PredNum = 0; PredNum < OpPHI->getNumOperands(); ++PredNum) {
|
||||||
|
auto *PredBB = OpPHI->getIncomingBlock(PredNum);
|
||||||
|
Value *FoundVal = nullptr;
|
||||||
|
SmallPtrSet<Value *, 4> CurrentDeps;
|
||||||
|
// We could just skip unreachable edges entirely but it's tricky to do
|
||||||
|
// with rewriting existing phi nodes.
|
||||||
|
if (ReachableEdges.count({PredBB, PHIBlock})) {
|
||||||
|
// Clone the instruction, create an expression from it that is
|
||||||
|
// translated back into the predecessor, and see if we have a leader.
|
||||||
|
Instruction *ValueOp = I->clone();
|
||||||
|
if (MemAccess)
|
||||||
|
TempToMemory.insert({ValueOp, MemAccess});
|
||||||
|
bool SafeForPHIOfOps = true;
|
||||||
|
VisitedOps.clear();
|
||||||
|
for (auto &Op : ValueOp->operands()) {
|
||||||
|
auto *OrigOp = &*Op;
|
||||||
|
// When these operand changes, it could change whether there is a
|
||||||
|
// leader for us or not, so we have to add additional users.
|
||||||
|
if (isa<PHINode>(Op)) {
|
||||||
|
Op = Op->DoPHITranslation(PHIBlock, PredBB);
|
||||||
|
if (Op != OrigOp && Op != I)
|
||||||
|
CurrentDeps.insert(Op);
|
||||||
|
} else if (auto *ValuePHI = RealToTemp.lookup(Op)) {
|
||||||
|
if (getBlockForValue(ValuePHI) == PHIBlock)
|
||||||
|
Op = ValuePHI->getIncomingValueForBlock(PredBB);
|
||||||
|
}
|
||||||
|
// If we phi-translated the op, it must be safe.
|
||||||
|
SafeForPHIOfOps =
|
||||||
|
SafeForPHIOfOps &&
|
||||||
|
(Op != OrigOp || OpIsSafeForPHIOfOps(Op, PHIBlock, VisitedOps));
|
||||||
|
}
|
||||||
|
// FIXME: For those things that are not safe we could generate
|
||||||
|
// expressions all the way down, and see if this comes out to a
|
||||||
|
// constant. For anything where that is true, and unsafe, we should
|
||||||
|
// have made a phi-of-ops (or value numbered it equivalent to something)
|
||||||
|
// for the pieces already.
|
||||||
|
FoundVal = !SafeForPHIOfOps ? nullptr
|
||||||
|
: findLeaderForInst(ValueOp, Visited,
|
||||||
|
MemAccess, I, PredBB);
|
||||||
|
ValueOp->deleteValue();
|
||||||
|
if (!FoundVal) {
|
||||||
|
// We failed to find a leader for the current ValueOp, but this might
|
||||||
|
// change in case of the translated operands change.
|
||||||
|
if (SafeForPHIOfOps)
|
||||||
|
for (auto Dep : CurrentDeps)
|
||||||
|
addAdditionalUsers(Dep, I);
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
Deps.insert(CurrentDeps.begin(), CurrentDeps.end());
|
||||||
|
} else {
|
||||||
|
DEBUG(dbgs() << "Skipping phi of ops operand for incoming block "
|
||||||
|
<< getBlockName(PredBB)
|
||||||
|
<< " because the block is unreachable\n");
|
||||||
|
FoundVal = UndefValue::get(I->getType());
|
||||||
|
RevisitOnReachabilityChange[PHIBlock].set(InstrToDFSNum(I));
|
||||||
|
}
|
||||||
|
|
||||||
|
PHIOps.push_back({FoundVal, PredBB});
|
||||||
|
DEBUG(dbgs() << "Found phi of ops operand " << *FoundVal << " in "
|
||||||
|
<< getBlockName(PredBB) << "\n");
|
||||||
|
}
|
||||||
|
for (auto Dep : Deps)
|
||||||
|
addAdditionalUsers(Dep, I);
|
||||||
|
sortPHIOps(PHIOps);
|
||||||
|
auto *E = performSymbolicPHIEvaluation(PHIOps, I, PHIBlock);
|
||||||
|
if (isa<ConstantExpression>(E) || isa<VariableExpression>(E)) {
|
||||||
|
DEBUG(dbgs()
|
||||||
|
<< "Not creating real PHI of ops because it simplified to existing "
|
||||||
|
"value or constant\n");
|
||||||
return E;
|
return E;
|
||||||
}
|
}
|
||||||
return nullptr;
|
auto *ValuePHI = RealToTemp.lookup(I);
|
||||||
|
bool NewPHI = false;
|
||||||
|
if (!ValuePHI) {
|
||||||
|
ValuePHI =
|
||||||
|
PHINode::Create(I->getType(), OpPHI->getNumOperands(), "phiofops");
|
||||||
|
addPhiOfOps(ValuePHI, PHIBlock, I);
|
||||||
|
NewPHI = true;
|
||||||
|
NumGVNPHIOfOpsCreated++;
|
||||||
|
}
|
||||||
|
if (NewPHI) {
|
||||||
|
for (auto PHIOp : PHIOps)
|
||||||
|
ValuePHI->addIncoming(PHIOp.first, PHIOp.second);
|
||||||
|
} else {
|
||||||
|
TempToBlock[ValuePHI] = PHIBlock;
|
||||||
|
unsigned int i = 0;
|
||||||
|
for (auto PHIOp : PHIOps) {
|
||||||
|
ValuePHI->setIncomingValue(i, PHIOp.first);
|
||||||
|
ValuePHI->setIncomingBlock(i, PHIOp.second);
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RevisitOnReachabilityChange[PHIBlock].set(InstrToDFSNum(I));
|
||||||
|
DEBUG(dbgs() << "Created phi of ops " << *ValuePHI << " for " << *I << "\n");
|
||||||
|
|
||||||
|
return E;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The algorithm initially places the values of the routine in the TOP
|
// The algorithm initially places the values of the routine in the TOP
|
||||||
|
@ -54,3 +54,54 @@ critedge: ; preds = %lr.ph, %bb1
|
|||||||
end:
|
end:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
; In this test case a temporary PhiOfOps node gets moved to BB with more
|
||||||
|
; predecessors, so a new one needs to be created.
|
||||||
|
define void @test2() {
|
||||||
|
; CHECK-LABEL: @test2(
|
||||||
|
; CHECK-NEXT: br label [[BB1:%.*]]
|
||||||
|
; CHECK: bb1:
|
||||||
|
; CHECK-NEXT: [[STOREMERGE:%.*]] = phi i32 [ 0, [[TMP0:%.*]] ], [ [[ADD:%.*]], [[CRITEDGE:%.*]] ]
|
||||||
|
; CHECK-NEXT: [[CMP1:%.*]] = icmp eq i32 [[STOREMERGE]], 0
|
||||||
|
; CHECK-NEXT: br i1 [[CMP1]], label [[LR_PH:%.*]], label [[CRITEDGE]]
|
||||||
|
; CHECK: lr.ph:
|
||||||
|
; CHECK-NEXT: br i1 undef, label [[SPLIT1:%.*]], label [[SPLIT2:%.*]]
|
||||||
|
; CHECK: split1:
|
||||||
|
; CHECK-NEXT: br label [[CRITEDGE]]
|
||||||
|
; CHECK: split2:
|
||||||
|
; CHECK-NEXT: br label [[CRITEDGE]]
|
||||||
|
; CHECK: critedge:
|
||||||
|
; CHECK-NEXT: [[PHIOFOPS1:%.*]] = phi i1 [ false, [[BB1]] ], [ true, [[SPLIT2]] ], [ true, [[SPLIT1]] ]
|
||||||
|
; CHECK-NEXT: [[PHIOFOPS:%.*]] = phi i1 [ [[CMP1]], [[BB1]] ], [ true, [[SPLIT2]] ], [ true, [[SPLIT1]] ]
|
||||||
|
; CHECK-NEXT: [[LCSSA:%.*]] = phi i32 [ 0, [[BB1]] ], [ -1, [[SPLIT1]] ], [ -1, [[SPLIT2]] ]
|
||||||
|
; CHECK-NEXT: [[ADD]] = add nsw i32 [[STOREMERGE]], -1
|
||||||
|
; CHECK-NEXT: br i1 [[PHIOFOPS]], label [[BB1]], label [[EXIT:%.*]]
|
||||||
|
; CHECK: exit:
|
||||||
|
; CHECK-NEXT: ret void
|
||||||
|
;
|
||||||
|
br label %bb1
|
||||||
|
|
||||||
|
bb1: ; preds = %critedge, %0
|
||||||
|
%storemerge = phi i32 [ 0, %0 ], [ %add, %critedge ]
|
||||||
|
%cmp1 = icmp eq i32 %storemerge, 0
|
||||||
|
br i1 %cmp1, label %lr.ph, label %critedge
|
||||||
|
|
||||||
|
lr.ph: ; preds = %bb1
|
||||||
|
br i1 undef, label %split1, label %split2
|
||||||
|
|
||||||
|
split1: ; preds = %lr.ph
|
||||||
|
br label %critedge
|
||||||
|
|
||||||
|
split2: ; preds = %lr.ph
|
||||||
|
br label %critedge
|
||||||
|
|
||||||
|
critedge: ; preds = %split1, %split2, %bb1
|
||||||
|
%lcssa = phi i32 [ 0, %bb1 ], [ -1, %split1 ], [ -1, %split2 ]
|
||||||
|
%cmp2 = icmp ne i32 %lcssa, 0
|
||||||
|
%brmerge = or i1 %cmp1, %cmp2
|
||||||
|
%add = add nsw i32 %storemerge, -1
|
||||||
|
br i1 %brmerge, label %bb1, label %exit
|
||||||
|
|
||||||
|
exit: ; preds = %critedge
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user