forked from OSchip/llvm-project
Ignore/Drop droppable uses for code-sinking in InstCombine
Summary: This patch allows code-sinking in InstCombine to be performed when instruction have uses in llvm.assume. Use are considered droppable when it is preferable to modify the User such that the use disappears rather than to prevent a transformation because of the use. for now uses are considered droppable if they are in an llvm.assume. Reviewers: jdoerfert, nikic, spatel, lebedev.ri, sstefan1 Reviewed By: jdoerfert Subscribers: hiraditya, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D73832
This commit is contained in:
parent
b539f18c56
commit
f1a9efabcb
|
@ -3352,7 +3352,7 @@ Instruction *InstCombiner::visitFreeze(FreezeInst &I) {
|
|||
/// instruction past all of the instructions between it and the end of its
|
||||
/// block.
|
||||
static bool TryToSinkInstruction(Instruction *I, BasicBlock *DestBlock) {
|
||||
assert(I->hasOneUse() && "Invariants didn't hold!");
|
||||
assert(I->getSingleUndroppableUse() && "Invariants didn't hold!");
|
||||
BasicBlock *SrcBlock = I->getParent();
|
||||
|
||||
// Cannot move control-flow-involving, volatile loads, vaarg, etc.
|
||||
|
@ -3385,6 +3385,15 @@ static bool TryToSinkInstruction(Instruction *I, BasicBlock *DestBlock) {
|
|||
if (Scan->mayWriteToMemory())
|
||||
return false;
|
||||
}
|
||||
|
||||
I->dropDroppableUses([DestBlock](const Use *U) {
|
||||
if (auto *I = dyn_cast<Instruction>(U->getUser()))
|
||||
return I->getParent() != DestBlock;
|
||||
return true;
|
||||
});
|
||||
/// FIXME: We could remove droppable uses that are not dominated by
|
||||
/// the new position.
|
||||
|
||||
BasicBlock::iterator InsertPos = DestBlock->getFirstInsertionPt();
|
||||
I->moveBefore(&*InsertPos);
|
||||
++NumSunkInst;
|
||||
|
@ -3488,44 +3497,46 @@ bool InstCombiner::run() {
|
|||
}
|
||||
|
||||
// See if we can trivially sink this instruction to a successor basic block.
|
||||
if (EnableCodeSinking && I->hasOneUse()) {
|
||||
BasicBlock *BB = I->getParent();
|
||||
Instruction *UserInst = cast<Instruction>(*I->user_begin());
|
||||
BasicBlock *UserParent;
|
||||
if (EnableCodeSinking)
|
||||
if (Use *SingleUse = I->getSingleUndroppableUse()) {
|
||||
BasicBlock *BB = I->getParent();
|
||||
Instruction *UserInst = cast<Instruction>(SingleUse->getUser());
|
||||
BasicBlock *UserParent;
|
||||
|
||||
// Get the block the use occurs in.
|
||||
if (PHINode *PN = dyn_cast<PHINode>(UserInst))
|
||||
UserParent = PN->getIncomingBlock(*I->use_begin());
|
||||
else
|
||||
UserParent = UserInst->getParent();
|
||||
// Get the block the use occurs in.
|
||||
if (PHINode *PN = dyn_cast<PHINode>(UserInst))
|
||||
UserParent = PN->getIncomingBlock(*I->use_begin());
|
||||
else
|
||||
UserParent = UserInst->getParent();
|
||||
|
||||
if (UserParent != BB) {
|
||||
bool UserIsSuccessor = false;
|
||||
// See if the user is one of our successors.
|
||||
for (succ_iterator SI = succ_begin(BB), E = succ_end(BB); SI != E; ++SI)
|
||||
if (*SI == UserParent) {
|
||||
UserIsSuccessor = true;
|
||||
break;
|
||||
}
|
||||
if (UserParent != BB) {
|
||||
bool UserIsSuccessor = false;
|
||||
// See if the user is one of our successors.
|
||||
for (succ_iterator SI = succ_begin(BB), E = succ_end(BB); SI != E;
|
||||
++SI)
|
||||
if (*SI == UserParent) {
|
||||
UserIsSuccessor = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// If the user is one of our immediate successors, and if that successor
|
||||
// only has us as a predecessors (we'd have to split the critical edge
|
||||
// otherwise), we can keep going.
|
||||
if (UserIsSuccessor && UserParent->getUniquePredecessor()) {
|
||||
// Okay, the CFG is simple enough, try to sink this instruction.
|
||||
if (TryToSinkInstruction(I, UserParent)) {
|
||||
LLVM_DEBUG(dbgs() << "IC: Sink: " << *I << '\n');
|
||||
MadeIRChange = true;
|
||||
// We'll add uses of the sunk instruction below, but since sinking
|
||||
// can expose opportunities for it's *operands* add them to the
|
||||
// worklist
|
||||
for (Use &U : I->operands())
|
||||
if (Instruction *OpI = dyn_cast<Instruction>(U.get()))
|
||||
Worklist.push(OpI);
|
||||
// If the user is one of our immediate successors, and if that
|
||||
// successor only has us as a predecessors (we'd have to split the
|
||||
// critical edge otherwise), we can keep going.
|
||||
if (UserIsSuccessor && UserParent->getUniquePredecessor()) {
|
||||
// Okay, the CFG is simple enough, try to sink this instruction.
|
||||
if (TryToSinkInstruction(I, UserParent)) {
|
||||
LLVM_DEBUG(dbgs() << "IC: Sink: " << *I << '\n');
|
||||
MadeIRChange = true;
|
||||
// We'll add uses of the sunk instruction below, but since sinking
|
||||
// can expose opportunities for it's *operands* add them to the
|
||||
// worklist
|
||||
for (Use &U : I->operands())
|
||||
if (Instruction *OpI = dyn_cast<Instruction>(U.get()))
|
||||
Worklist.push(OpI);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now that we have an instruction, try combining it to simplify it.
|
||||
Builder.SetInsertPoint(I);
|
||||
|
|
|
@ -289,9 +289,9 @@ define i1 @nonnull3(i32** %a, i1 %control) {
|
|||
; CHECK-LABEL: @nonnull3(
|
||||
; CHECK-NEXT: entry:
|
||||
; CHECK-NEXT: [[LOAD:%.*]] = load i32*, i32** [[A:%.*]], align 8
|
||||
; CHECK-NEXT: [[CMP:%.*]] = icmp ne i32* [[LOAD]], null
|
||||
; CHECK-NEXT: br i1 [[CONTROL:%.*]], label [[TAKEN:%.*]], label [[NOT_TAKEN:%.*]]
|
||||
; CHECK: taken:
|
||||
; CHECK-NEXT: [[CMP:%.*]] = icmp ne i32* [[LOAD]], null
|
||||
; CHECK-NEXT: tail call void @llvm.assume(i1 [[CMP]])
|
||||
; CHECK-NEXT: ret i1 false
|
||||
; CHECK: not_taken:
|
||||
|
@ -414,6 +414,117 @@ define i32 @PR40940(<4 x i8> %x) {
|
|||
ret i32 %t2
|
||||
}
|
||||
|
||||
define i1 @nonnull3A(i32** %a, i1 %control) {
|
||||
; CHECK-LABEL: @nonnull3A(
|
||||
; CHECK-NEXT: entry:
|
||||
; CHECK-NEXT: [[LOAD:%.*]] = load i32*, i32** [[A:%.*]], align 8
|
||||
; CHECK-NEXT: br i1 [[CONTROL:%.*]], label [[TAKEN:%.*]], label [[NOT_TAKEN:%.*]]
|
||||
; CHECK: taken:
|
||||
; CHECK-NEXT: [[CMP:%.*]] = icmp ne i32* [[LOAD]], null
|
||||
; CHECK-NEXT: call void @llvm.assume(i1 [[CMP]])
|
||||
; CHECK-NEXT: ret i1 true
|
||||
; CHECK: not_taken:
|
||||
; CHECK-NEXT: [[RVAL_2:%.*]] = icmp sgt i32* [[LOAD]], null
|
||||
; CHECK-NEXT: ret i1 [[RVAL_2]]
|
||||
;
|
||||
entry:
|
||||
%load = load i32*, i32** %a
|
||||
%cmp = icmp ne i32* %load, null
|
||||
br i1 %control, label %taken, label %not_taken
|
||||
taken:
|
||||
call void @llvm.assume(i1 %cmp)
|
||||
ret i1 %cmp
|
||||
not_taken:
|
||||
call void @llvm.assume(i1 %cmp)
|
||||
%rval.2 = icmp sgt i32* %load, null
|
||||
ret i1 %rval.2
|
||||
}
|
||||
|
||||
define i1 @nonnull3B(i32** %a, i1 %control) {
|
||||
; CHECK-LABEL: @nonnull3B(
|
||||
; CHECK-NEXT: entry:
|
||||
; CHECK-NEXT: br i1 [[CONTROL:%.*]], label [[TAKEN:%.*]], label [[NOT_TAKEN:%.*]]
|
||||
; CHECK: taken:
|
||||
; CHECK-NEXT: [[LOAD:%.*]] = load i32*, i32** [[A:%.*]], align 8
|
||||
; CHECK-NEXT: [[CMP:%.*]] = icmp ne i32* [[LOAD]], null
|
||||
; CHECK-NEXT: call void @llvm.assume(i1 [[CMP]]) [ "nonnull"(i32* [[LOAD]]), "nonnull"(i1 [[CMP]]) ]
|
||||
; CHECK-NEXT: ret i1 true
|
||||
; CHECK: not_taken:
|
||||
; CHECK-NEXT: ret i1 [[CONTROL]]
|
||||
;
|
||||
entry:
|
||||
%load = load i32*, i32** %a
|
||||
%cmp = icmp ne i32* %load, null
|
||||
br i1 %control, label %taken, label %not_taken
|
||||
taken:
|
||||
call void @llvm.assume(i1 %cmp) ["nonnull"(i32* %load), "nonnull"(i1 %cmp)]
|
||||
ret i1 %cmp
|
||||
not_taken:
|
||||
call void @llvm.assume(i1 %cmp) ["nonnull"(i32* %load), "nonnull"(i1 %cmp)]
|
||||
ret i1 %control
|
||||
}
|
||||
|
||||
declare i1 @tmp1(i1)
|
||||
|
||||
define i1 @nonnull3C(i32** %a, i1 %control) {
|
||||
; CHECK-LABEL: @nonnull3C(
|
||||
; CHECK-NEXT: entry:
|
||||
; CHECK-NEXT: br i1 [[CONTROL:%.*]], label [[TAKEN:%.*]], label [[NOT_TAKEN:%.*]]
|
||||
; CHECK: taken:
|
||||
; CHECK-NEXT: [[LOAD:%.*]] = load i32*, i32** [[A:%.*]], align 8
|
||||
; CHECK-NEXT: [[CMP:%.*]] = icmp ne i32* [[LOAD]], null
|
||||
; CHECK-NEXT: [[CMP2:%.*]] = call i1 @tmp1(i1 [[CMP]])
|
||||
; CHECK-NEXT: br label [[EXIT:%.*]]
|
||||
; CHECK: exit:
|
||||
; CHECK-NEXT: ret i1 [[CMP2]]
|
||||
; CHECK: not_taken:
|
||||
; CHECK-NEXT: ret i1 [[CONTROL]]
|
||||
;
|
||||
entry:
|
||||
%load = load i32*, i32** %a
|
||||
%cmp = icmp ne i32* %load, null
|
||||
br i1 %control, label %taken, label %not_taken
|
||||
taken:
|
||||
%cmp2 = call i1 @tmp1(i1 %cmp)
|
||||
br label %exit
|
||||
exit:
|
||||
; FIXME: this shouldn't be dropped because it is still dominated by the new position of %load
|
||||
call void @llvm.assume(i1 %cmp) ["nonnull"(i32* %load), "nonnull"(i1 %cmp)]
|
||||
ret i1 %cmp2
|
||||
not_taken:
|
||||
call void @llvm.assume(i1 %cmp)
|
||||
ret i1 %control
|
||||
}
|
||||
|
||||
define i1 @nonnull3D(i32** %a, i1 %control) {
|
||||
; CHECK-LABEL: @nonnull3D(
|
||||
; CHECK-NEXT: entry:
|
||||
; CHECK-NEXT: br i1 [[CONTROL:%.*]], label [[TAKEN:%.*]], label [[NOT_TAKEN:%.*]]
|
||||
; CHECK: taken:
|
||||
; CHECK-NEXT: [[LOAD:%.*]] = load i32*, i32** [[A:%.*]], align 8
|
||||
; CHECK-NEXT: [[CMP:%.*]] = icmp ne i32* [[LOAD]], null
|
||||
; CHECK-NEXT: [[CMP2:%.*]] = call i1 @tmp1(i1 [[CMP]])
|
||||
; CHECK-NEXT: br label [[EXIT:%.*]]
|
||||
; CHECK: exit:
|
||||
; CHECK-NEXT: ret i1 [[CMP2]]
|
||||
; CHECK: not_taken:
|
||||
; CHECK-NEXT: call void @llvm.assume(i1 true) [ "ignore"(i32* undef), "ignore"(i1 undef), "nonnull"(i1 [[CONTROL]]) ]
|
||||
; CHECK-NEXT: ret i1 [[CONTROL]]
|
||||
;
|
||||
entry:
|
||||
%load = load i32*, i32** %a
|
||||
%cmp = icmp ne i32* %load, null
|
||||
br i1 %control, label %taken, label %not_taken
|
||||
taken:
|
||||
%cmp2 = call i1 @tmp1(i1 %cmp)
|
||||
br label %exit
|
||||
exit:
|
||||
ret i1 %cmp2
|
||||
not_taken:
|
||||
call void @llvm.assume(i1 %cmp) ["nonnull"(i32* %load), "nonnull"(i1 %cmp), "nonnull"(i1 %control)]
|
||||
ret i1 %control
|
||||
}
|
||||
|
||||
declare void @llvm.dbg.value(metadata, metadata, metadata)
|
||||
|
||||
!llvm.dbg.cu = !{!0}
|
||||
|
|
Loading…
Reference in New Issue