forked from OSchip/llvm-project
[StatepointLowering] Remove distinction between call and invoke safepoints
There is no point in having invoke safepoints handled differently than the call safepoints. All relevant decisions could be made by looking at whether or not gc.result and gc.relocate lay in a same basic block. This change will allow to lower call safepoints with relocates and results in a different basic blocks. See test case for example. Differential Revision: http://reviews.llvm.org/D14158 llvm-svn: 252028
This commit is contained in:
parent
24519d98b3
commit
35fe692025
|
@ -899,7 +899,8 @@ void SelectionDAGBuilder::visit(const Instruction &I) {
|
||||||
|
|
||||||
visit(I.getOpcode(), I);
|
visit(I.getOpcode(), I);
|
||||||
|
|
||||||
if (!isa<TerminatorInst>(&I) && !HasTailCall)
|
if (!isa<TerminatorInst>(&I) && !HasTailCall &&
|
||||||
|
!isStatepoint(&I)) // statepoints handle their exports internally
|
||||||
CopyToExportRegsIfNeeded(&I);
|
CopyToExportRegsIfNeeded(&I);
|
||||||
|
|
||||||
CurInst = nullptr;
|
CurInst = nullptr;
|
||||||
|
|
|
@ -343,13 +343,17 @@ lowerCallFromStatepoint(ImmutableStatepoint ISP, const BasicBlock *EHPadBB,
|
||||||
|
|
||||||
assert(CallEnd->getOpcode() == ISD::CALLSEQ_END && "expected!");
|
assert(CallEnd->getOpcode() == ISD::CALLSEQ_END && "expected!");
|
||||||
|
|
||||||
if (HasDef) {
|
// Export the result value if needed
|
||||||
if (CS.isInvoke()) {
|
const Instruction *GCResult = ISP.getGCResult();
|
||||||
// Result value will be used in different basic block for invokes
|
if (HasDef && GCResult) {
|
||||||
// so we need to export it now. But statepoint call has a different type
|
if (GCResult->getParent() != CS.getParent()) {
|
||||||
// than the actual call. It means that standard exporting mechanism will
|
// Result value will be used in a different basic block so we need to
|
||||||
// create register of the wrong type. So instead we need to create
|
// export it now.
|
||||||
// register with correct type and save value into it manually.
|
// Default exporting mechanism will not work here because statepoint call
|
||||||
|
// has a different type than the actual call. It means that by default
|
||||||
|
// llvm will create export register of the wrong type (always i32 in our
|
||||||
|
// case). So instead we need to create export register with correct type
|
||||||
|
// manually.
|
||||||
// TODO: To eliminate this problem we can remove gc.result intrinsics
|
// TODO: To eliminate this problem we can remove gc.result intrinsics
|
||||||
// completely and make statepoint call to return a tuple.
|
// completely and make statepoint call to return a tuple.
|
||||||
unsigned Reg = Builder.FuncInfo.CreateRegs(ISP.getActualReturnType());
|
unsigned Reg = Builder.FuncInfo.CreateRegs(ISP.getActualReturnType());
|
||||||
|
@ -363,8 +367,9 @@ lowerCallFromStatepoint(ImmutableStatepoint ISP, const BasicBlock *EHPadBB,
|
||||||
PendingExports.push_back(Chain);
|
PendingExports.push_back(Chain);
|
||||||
Builder.FuncInfo.ValueMap[CS.getInstruction()] = Reg;
|
Builder.FuncInfo.ValueMap[CS.getInstruction()] = Reg;
|
||||||
} else {
|
} else {
|
||||||
// The value of the statepoint itself will be the value of call itself.
|
// Result value will be used in a same basic block. Don't export it or
|
||||||
// We'll replace the actually call node shortly. gc_result will grab
|
// perform any explicit register copies.
|
||||||
|
// We'll replace the actuall call node shortly. gc_result will grab
|
||||||
// this value.
|
// this value.
|
||||||
Builder.setValue(CS.getInstruction(), ReturnValue);
|
Builder.setValue(CS.getInstruction(), ReturnValue);
|
||||||
}
|
}
|
||||||
|
@ -611,7 +616,8 @@ static void lowerStatepointMetaArgs(SmallVectorImpl<SDValue> &Ops,
|
||||||
// uses of the corresponding values so that it would automatically
|
// uses of the corresponding values so that it would automatically
|
||||||
// export them. Relocates of the spilled values does not use original
|
// export them. Relocates of the spilled values does not use original
|
||||||
// value.
|
// value.
|
||||||
if (StatepointSite.getCallSite().isInvoke())
|
if (RelocateOpers.getUnderlyingCallSite().getParent() !=
|
||||||
|
StatepointInstr->getParent())
|
||||||
Builder.ExportFromCurrentBlock(V);
|
Builder.ExportFromCurrentBlock(V);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -638,15 +644,13 @@ void SelectionDAGBuilder::LowerStatepoint(
|
||||||
ImmutableCallSite CS(ISP.getCallSite());
|
ImmutableCallSite CS(ISP.getCallSite());
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
// Consistency check. Don't do this for invokes. It would be too
|
// Consistency check. Check only relocates in the same basic block as thier
|
||||||
// expensive to preserve this information across different basic blocks
|
// statepoint.
|
||||||
if (!CS.isInvoke()) {
|
|
||||||
for (const User *U : CS->users()) {
|
for (const User *U : CS->users()) {
|
||||||
const CallInst *Call = cast<CallInst>(U);
|
const CallInst *Call = cast<CallInst>(U);
|
||||||
if (isGCRelocate(Call))
|
if (isGCRelocate(Call) && Call->getParent() == CS.getParent())
|
||||||
StatepointLowering.scheduleRelocCall(*Call);
|
StatepointLowering.scheduleRelocCall(*Call);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
|
@ -827,8 +831,9 @@ void SelectionDAGBuilder::visitGCResult(const CallInst &CI) {
|
||||||
Instruction *I = cast<Instruction>(CI.getArgOperand(0));
|
Instruction *I = cast<Instruction>(CI.getArgOperand(0));
|
||||||
assert(isStatepoint(I) && "first argument must be a statepoint token");
|
assert(isStatepoint(I) && "first argument must be a statepoint token");
|
||||||
|
|
||||||
if (isa<InvokeInst>(I)) {
|
if (I->getParent() != CI.getParent()) {
|
||||||
// For invokes we should have stored call result in a virtual register.
|
// Statepoint is in different basic block so we should have stored call
|
||||||
|
// result in a virtual register.
|
||||||
// We can not use default getValue() functionality to copy value from this
|
// We can not use default getValue() functionality to copy value from this
|
||||||
// register because statepoint and actuall call return types can be
|
// register because statepoint and actuall call return types can be
|
||||||
// different, and getValue() will use CopyFromReg of the wrong type,
|
// different, and getValue() will use CopyFromReg of the wrong type,
|
||||||
|
@ -851,9 +856,10 @@ void SelectionDAGBuilder::visitGCRelocate(const CallInst &CI) {
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
// Consistency check
|
// Consistency check
|
||||||
// We skip this check for invoke statepoints. It would be too expensive to
|
// We skip this check for relocates not in the same basic block as thier
|
||||||
// preserve validation info through different basic blocks.
|
// statepoint. It would be too expensive to preserve validation info through
|
||||||
if (!RelocateOpers.isTiedToInvoke()) {
|
// different basic blocks.
|
||||||
|
if (RelocateOpers.getStatepoint()->getParent() == CI.getParent()) {
|
||||||
StatepointLowering.relocCallVisited(CI);
|
StatepointLowering.relocCallVisited(CI);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -100,6 +100,31 @@ entry:
|
||||||
ret i1 %call1
|
ret i1 %call1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare void @consume(i32 addrspace(1)* %obj)
|
||||||
|
|
||||||
|
define i1 @test_cross_bb(i32 addrspace(1)* %a, i1 %external_cond) gc "statepoint-example" {
|
||||||
|
; CHECK-LABEL: test_cross_bb
|
||||||
|
; CHECK: movq
|
||||||
|
; CHECK: callq return_i1
|
||||||
|
; CHECK: %left
|
||||||
|
; CHECK: movq
|
||||||
|
; CHECK-NEXT: callq consume
|
||||||
|
; CHECK: retq
|
||||||
|
entry:
|
||||||
|
%safepoint_token = tail call i32 (i64, i32, i1 ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_i1f(i64 0, i32 0, i1 ()* @return_i1, i32 0, i32 0, i32 0, i32 0, i32 addrspace(1)* %a)
|
||||||
|
br i1 %external_cond, label %left, label %right
|
||||||
|
|
||||||
|
left:
|
||||||
|
%call1 = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(i32 %safepoint_token, i32 7, i32 7)
|
||||||
|
%call2 = call zeroext i1 @llvm.experimental.gc.result.i1(i32 %safepoint_token)
|
||||||
|
call void @consume(i32 addrspace(1)* %call1)
|
||||||
|
ret i1 %call2
|
||||||
|
|
||||||
|
right:
|
||||||
|
ret i1 true
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
declare i32 @llvm.experimental.gc.statepoint.p0f_i1f(i64, i32, i1 ()*, i32, i32, ...)
|
declare i32 @llvm.experimental.gc.statepoint.p0f_i1f(i64, i32, i1 ()*, i32, i32, ...)
|
||||||
declare i1 @llvm.experimental.gc.result.i1(i32)
|
declare i1 @llvm.experimental.gc.result.i1(i32)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue