mirror of
https://github.com/RPCS3/llvm.git
synced 2025-02-13 14:46:15 +00:00
[RS4GC] Better codegen for deoptimize calls
Don't emit a gc.result for a statepoint lowered from @llvm.experimental.deoptimize since the call into __llvm_deoptimize is effectively noreturn. Instead follow the corresponding gc.statepoint with an "unreachable". git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@265485 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
5a3e3ec846
commit
0ed4be724f
@ -1278,6 +1278,9 @@ namespace {
|
|||||||
class DeferredReplacement {
|
class DeferredReplacement {
|
||||||
AssertingVH<Instruction> Old;
|
AssertingVH<Instruction> Old;
|
||||||
AssertingVH<Instruction> New;
|
AssertingVH<Instruction> New;
|
||||||
|
bool IsDeoptimize = false;
|
||||||
|
|
||||||
|
DeferredReplacement() {}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit DeferredReplacement(Instruction *Old, Instruction *New) :
|
explicit DeferredReplacement(Instruction *Old, Instruction *New) :
|
||||||
@ -1285,18 +1288,40 @@ public:
|
|||||||
assert(Old != New && "Not allowed!");
|
assert(Old != New && "Not allowed!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static DeferredReplacement createDeoptimizeReplacement(Instruction *Old) {
|
||||||
|
#ifndef NDEBUG
|
||||||
|
auto *F = cast<CallInst>(Old)->getCalledFunction();
|
||||||
|
assert(F && F->getIntrinsicID() == Intrinsic::experimental_deoptimize &&
|
||||||
|
"Only way to construct a deoptimize deferred replacement");
|
||||||
|
#endif
|
||||||
|
DeferredReplacement D;
|
||||||
|
D.Old = Old;
|
||||||
|
D.IsDeoptimize = true;
|
||||||
|
return D;
|
||||||
|
}
|
||||||
|
|
||||||
/// Does the task represented by this instance.
|
/// Does the task represented by this instance.
|
||||||
void doReplacement() {
|
void doReplacement() {
|
||||||
Instruction *OldI = Old;
|
Instruction *OldI = Old;
|
||||||
Instruction *NewI = New;
|
Instruction *NewI = New;
|
||||||
|
|
||||||
assert(OldI != NewI && "Disallowed at construction?!");
|
assert(OldI != NewI && "Disallowed at construction?!");
|
||||||
|
assert(!IsDeoptimize || !New && "Deoptimize instrinsics are not replaced!");
|
||||||
|
|
||||||
Old = nullptr;
|
Old = nullptr;
|
||||||
New = nullptr;
|
New = nullptr;
|
||||||
|
|
||||||
if (NewI)
|
if (NewI)
|
||||||
OldI->replaceAllUsesWith(NewI);
|
OldI->replaceAllUsesWith(NewI);
|
||||||
|
|
||||||
|
if (IsDeoptimize) {
|
||||||
|
// Note: we've inserted instructions, so the call to llvm.deoptimize may
|
||||||
|
// not necessarilly be followed by the matching return.
|
||||||
|
auto *RI = cast<ReturnInst>(OldI->getParent()->getTerminator());
|
||||||
|
new UnreachableInst(RI->getContext(), RI);
|
||||||
|
RI->eraseFromParent();
|
||||||
|
}
|
||||||
|
|
||||||
OldI->eraseFromParent();
|
OldI->eraseFromParent();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -1330,6 +1355,7 @@ makeStatepointExplicitImpl(const CallSite CS, /* to replace */
|
|||||||
Flags |= uint32_t(StatepointFlags::GCTransition);
|
Flags |= uint32_t(StatepointFlags::GCTransition);
|
||||||
TransitionArgs = TransitionBundle->Inputs;
|
TransitionArgs = TransitionBundle->Inputs;
|
||||||
}
|
}
|
||||||
|
bool IsDeoptimize = false;
|
||||||
|
|
||||||
StatepointDirectives SD =
|
StatepointDirectives SD =
|
||||||
parseStatepointDirectivesFromAttrs(CS.getAttributes());
|
parseStatepointDirectivesFromAttrs(CS.getAttributes());
|
||||||
@ -1348,7 +1374,7 @@ makeStatepointExplicitImpl(const CallSite CS, /* to replace */
|
|||||||
SmallVector<Type *, 8> DomainTy;
|
SmallVector<Type *, 8> DomainTy;
|
||||||
for (Value *Arg : CallArgs)
|
for (Value *Arg : CallArgs)
|
||||||
DomainTy.push_back(Arg->getType());
|
DomainTy.push_back(Arg->getType());
|
||||||
auto *FTy = FunctionType::get(F->getReturnType(), DomainTy,
|
auto *FTy = FunctionType::get(Type::getVoidTy(F->getContext()), DomainTy,
|
||||||
/* isVarArg = */ false);
|
/* isVarArg = */ false);
|
||||||
|
|
||||||
// Note: CallTarget can be a bitcast instruction of a symbol if there are
|
// Note: CallTarget can be a bitcast instruction of a symbol if there are
|
||||||
@ -1357,6 +1383,8 @@ makeStatepointExplicitImpl(const CallSite CS, /* to replace */
|
|||||||
// was doing when generating this kind of IR.
|
// was doing when generating this kind of IR.
|
||||||
CallTarget =
|
CallTarget =
|
||||||
F->getParent()->getOrInsertFunction("__llvm_deoptimize", FTy);
|
F->getParent()->getOrInsertFunction("__llvm_deoptimize", FTy);
|
||||||
|
|
||||||
|
IsDeoptimize = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1440,22 +1468,30 @@ makeStatepointExplicitImpl(const CallSite CS, /* to replace */
|
|||||||
}
|
}
|
||||||
assert(Token && "Should be set in one of the above branches!");
|
assert(Token && "Should be set in one of the above branches!");
|
||||||
|
|
||||||
Token->setName("statepoint_token");
|
if (IsDeoptimize) {
|
||||||
if (!CS.getType()->isVoidTy() && !CS.getInstruction()->use_empty()) {
|
// If we're wrapping an @llvm.experimental.deoptimize in a statepoint, we
|
||||||
StringRef Name =
|
// transform the tail-call like structure to a call to a void function
|
||||||
CS.getInstruction()->hasName() ? CS.getInstruction()->getName() : "";
|
// followed by unreachable to get better codegen.
|
||||||
CallInst *GCResult = Builder.CreateGCResult(Token, CS.getType(), Name);
|
Replacements.push_back(
|
||||||
GCResult->setAttributes(CS.getAttributes().getRetAttributes());
|
DeferredReplacement::createDeoptimizeReplacement(CS.getInstruction()));
|
||||||
|
|
||||||
// We cannot RAUW or delete CS.getInstruction() because it could be in the
|
|
||||||
// live set of some other safepoint, in which case that safepoint's
|
|
||||||
// PartiallyConstructedSafepointRecord will hold a raw pointer to this
|
|
||||||
// llvm::Instruction. Instead, we defer the replacement and deletion to
|
|
||||||
// after the live sets have been made explicit in the IR, and we no longer
|
|
||||||
// have raw pointers to worry about.
|
|
||||||
Replacements.emplace_back(CS.getInstruction(), GCResult);
|
|
||||||
} else {
|
} else {
|
||||||
Replacements.emplace_back(CS.getInstruction(), nullptr);
|
Token->setName("statepoint_token");
|
||||||
|
if (!CS.getType()->isVoidTy() && !CS.getInstruction()->use_empty()) {
|
||||||
|
StringRef Name =
|
||||||
|
CS.getInstruction()->hasName() ? CS.getInstruction()->getName() : "";
|
||||||
|
CallInst *GCResult = Builder.CreateGCResult(Token, CS.getType(), Name);
|
||||||
|
GCResult->setAttributes(CS.getAttributes().getRetAttributes());
|
||||||
|
|
||||||
|
// We cannot RAUW or delete CS.getInstruction() because it could be in the
|
||||||
|
// live set of some other safepoint, in which case that safepoint's
|
||||||
|
// PartiallyConstructedSafepointRecord will hold a raw pointer to this
|
||||||
|
// llvm::Instruction. Instead, we defer the replacement and deletion to
|
||||||
|
// after the live sets have been made explicit in the IR, and we no longer
|
||||||
|
// have raw pointers to worry about.
|
||||||
|
Replacements.emplace_back(CS.getInstruction(), GCResult);
|
||||||
|
} else {
|
||||||
|
Replacements.emplace_back(CS.getInstruction(), nullptr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Result.StatepointToken = Token;
|
Result.StatepointToken = Token;
|
||||||
|
@ -4,10 +4,12 @@ target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"
|
|||||||
target triple = "x86_64-apple-macosx10.11.0"
|
target triple = "x86_64-apple-macosx10.11.0"
|
||||||
|
|
||||||
declare i32 @llvm.experimental.deoptimize.i32(...)
|
declare i32 @llvm.experimental.deoptimize.i32(...)
|
||||||
|
declare void @llvm.experimental.deoptimize.isVoid(...)
|
||||||
|
|
||||||
define i32 @caller_0(i32 addrspace(1)* %ptr) gc "statepoint-example" {
|
define i32 @caller_0(i32 addrspace(1)* %ptr) gc "statepoint-example" {
|
||||||
; CHECK-LABEL: @caller_0(
|
; CHECK-LABEL: @caller_0(
|
||||||
; CHECK: @llvm.experimental.gc.statepoint.p0f_i32f(i64 2882400000, i32 0, i32 ()* @__llvm_deoptimize, i32 0
|
; CHECK: @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 2882400000, i32 0, void ()* @__llvm_deoptimize, i32 0
|
||||||
|
; CHECK: unreachable
|
||||||
entry:
|
entry:
|
||||||
%v = call i32(...) @llvm.experimental.deoptimize.i32() [ "deopt"(i32 0, i32 addrspace(1)* %ptr) ]
|
%v = call i32(...) @llvm.experimental.deoptimize.i32() [ "deopt"(i32 0, i32 addrspace(1)* %ptr) ]
|
||||||
ret i32 %v
|
ret i32 %v
|
||||||
@ -16,8 +18,18 @@ entry:
|
|||||||
|
|
||||||
define i32 @caller_1(i32 addrspace(1)* %ptr) gc "statepoint-example" {
|
define i32 @caller_1(i32 addrspace(1)* %ptr) gc "statepoint-example" {
|
||||||
; CHECK-LABEL: @caller_1
|
; CHECK-LABEL: @caller_1
|
||||||
; CHECK: @llvm.experimental.gc.statepoint.p0f_i32i32p1i32f(i64 2882400000, i32 0, i32 (i32, i32 addrspace(1)*)* bitcast (i32 ()* @__llvm_deoptimize to i32 (i32, i32 addrspace(1)*)*), i32 2, i32 0, i32 50, i32 addrspace(1)* %ptr
|
; CHECK: @llvm.experimental.gc.statepoint.p0f_isVoidi32p1i32f(i64 2882400000, i32 0, void (i32, i32 addrspace(1)*)* bitcast (void ()* @__llvm_deoptimize to void (i32, i32 addrspace(1)*)*), i32 2, i32 0, i32 50, i32 addrspace(1)* %ptr
|
||||||
|
; CHECK: unreachable
|
||||||
entry:
|
entry:
|
||||||
%v = call i32(...) @llvm.experimental.deoptimize.i32(i32 50, i32 addrspace(1)* %ptr) [ "deopt"(i32 0) ]
|
%v = call i32(...) @llvm.experimental.deoptimize.i32(i32 50, i32 addrspace(1)* %ptr) [ "deopt"(i32 0) ]
|
||||||
ret i32 %v
|
ret i32 %v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
define void @caller_2(i32 addrspace(1)* %ptr) gc "statepoint-example" {
|
||||||
|
; CHECK-LABEL: @caller_2(
|
||||||
|
; CHECK: @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 2882400000, i32 0, void ()* @__llvm_deoptimize, i32 0
|
||||||
|
; CHECK: unreachable
|
||||||
|
entry:
|
||||||
|
call void(...) @llvm.experimental.deoptimize.isVoid() [ "deopt"(i32 0, i32 addrspace(1)* %ptr) ]
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user