diff options
-rw-r--r-- | llvm/lib/Transforms/Scalar/RewriteStatepointsForGC.cpp | 68 | ||||
-rw-r--r-- | llvm/test/Transforms/RewriteStatepointsForGC/deopt-intrinsic.ll | 16 |
2 files changed, 66 insertions, 18 deletions
diff --git a/llvm/lib/Transforms/Scalar/RewriteStatepointsForGC.cpp b/llvm/lib/Transforms/Scalar/RewriteStatepointsForGC.cpp index 9377c7a2b94..8c331547428 100644 --- a/llvm/lib/Transforms/Scalar/RewriteStatepointsForGC.cpp +++ b/llvm/lib/Transforms/Scalar/RewriteStatepointsForGC.cpp @@ -1278,6 +1278,9 @@ namespace { class DeferredReplacement { AssertingVH<Instruction> Old; AssertingVH<Instruction> New; + bool IsDeoptimize = false; + + DeferredReplacement() {} public: explicit DeferredReplacement(Instruction *Old, Instruction *New) : @@ -1285,18 +1288,40 @@ public: 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. void doReplacement() { Instruction *OldI = Old; Instruction *NewI = New; assert(OldI != NewI && "Disallowed at construction?!"); + assert(!IsDeoptimize || !New && "Deoptimize instrinsics are not replaced!"); Old = nullptr; New = nullptr; if (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(); } }; @@ -1330,6 +1355,7 @@ makeStatepointExplicitImpl(const CallSite CS, /* to replace */ Flags |= uint32_t(StatepointFlags::GCTransition); TransitionArgs = TransitionBundle->Inputs; } + bool IsDeoptimize = false; StatepointDirectives SD = parseStatepointDirectivesFromAttrs(CS.getAttributes()); @@ -1348,7 +1374,7 @@ makeStatepointExplicitImpl(const CallSite CS, /* to replace */ SmallVector<Type *, 8> DomainTy; for (Value *Arg : CallArgs) DomainTy.push_back(Arg->getType()); - auto *FTy = FunctionType::get(F->getReturnType(), DomainTy, + auto *FTy = FunctionType::get(Type::getVoidTy(F->getContext()), DomainTy, /* isVarArg = */ false); // 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. CallTarget = 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!"); - 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); + if (IsDeoptimize) { + // If we're wrapping an @llvm.experimental.deoptimize in a statepoint, we + // transform the tail-call like structure to a call to a void function + // followed by unreachable to get better codegen. + Replacements.push_back( + DeferredReplacement::createDeoptimizeReplacement(CS.getInstruction())); } 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; diff --git a/llvm/test/Transforms/RewriteStatepointsForGC/deopt-intrinsic.ll b/llvm/test/Transforms/RewriteStatepointsForGC/deopt-intrinsic.ll index 9a8f58c6ecb..ef0e2bd61af 100644 --- a/llvm/test/Transforms/RewriteStatepointsForGC/deopt-intrinsic.ll +++ b/llvm/test/Transforms/RewriteStatepointsForGC/deopt-intrinsic.ll @@ -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" declare i32 @llvm.experimental.deoptimize.i32(...) +declare void @llvm.experimental.deoptimize.isVoid(...) define i32 @caller_0(i32 addrspace(1)* %ptr) gc "statepoint-example" { ; 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: %v = call i32(...) @llvm.experimental.deoptimize.i32() [ "deopt"(i32 0, i32 addrspace(1)* %ptr) ] ret i32 %v @@ -16,8 +18,18 @@ entry: define i32 @caller_1(i32 addrspace(1)* %ptr) gc "statepoint-example" { ; 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: %v = call i32(...) @llvm.experimental.deoptimize.i32(i32 50, i32 addrspace(1)* %ptr) [ "deopt"(i32 0) ] 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 +} |