summaryrefslogtreecommitdiffstats
path: root/llvm/lib
diff options
context:
space:
mode:
authorSanjoy Das <sanjoy@playingwithpointers.com>2016-03-11 19:08:34 +0000
committerSanjoy Das <sanjoy@playingwithpointers.com>2016-03-11 19:08:34 +0000
commitb51325dbdb10f0b4ab2b9c5ec4a979dffc246794 (patch)
tree5ee98ad08e88068ab4ad4151d67d74bb2ddea6ea /llvm/lib
parentebce18b6fc00b21826d1077399976d2447798ba7 (diff)
downloadbcm5719-llvm-b51325dbdb10f0b4ab2b9c5ec4a979dffc246794.tar.gz
bcm5719-llvm-b51325dbdb10f0b4ab2b9c5ec4a979dffc246794.zip
Introduce @llvm.experimental.deoptimize
Summary: This intrinsic, together with deoptimization operand bundles, allow frontends to express transfer of control and frame-local state from one (typically more specialized, hence faster) version of a function into another (typically more generic, hence slower) version. In languages with a fully integrated managed runtime this intrinsic can be used to implement "uncommon trap" like functionality. In unmanaged languages like C and C++, this intrinsic can be used to represent the slow paths of specialized functions. Note: this change does not address how `@llvm.experimental_deoptimize` is lowered. That will be done in a later change. Reviewers: chandlerc, rnk, atrick, reames Subscribers: llvm-commits, kmod, mjacob, maksfb, mcrosier, JosephTremoulet Differential Revision: http://reviews.llvm.org/D17732 llvm-svn: 263281
Diffstat (limited to 'llvm/lib')
-rw-r--r--llvm/lib/IR/BasicBlock.cpp15
-rw-r--r--llvm/lib/IR/Verifier.cpp23
-rw-r--r--llvm/lib/Transforms/Utils/InlineFunction.cpp65
3 files changed, 102 insertions, 1 deletions
diff --git a/llvm/lib/IR/BasicBlock.cpp b/llvm/lib/IR/BasicBlock.cpp
index 89a1d744441..9f806fad680 100644
--- a/llvm/lib/IR/BasicBlock.cpp
+++ b/llvm/lib/IR/BasicBlock.cpp
@@ -162,6 +162,21 @@ CallInst *BasicBlock::getTerminatingMustTailCall() {
return nullptr;
}
+CallInst *BasicBlock::getTerminatingDeoptimizeCall() {
+ if (InstList.empty())
+ return nullptr;
+ auto *RI = dyn_cast<ReturnInst>(&InstList.back());
+ if (!RI || RI == &InstList.front())
+ return nullptr;
+
+ if (auto *CI = dyn_cast_or_null<CallInst>(RI->getPrevNode()))
+ if (Function *F = CI->getCalledFunction())
+ if (F->getIntrinsicID() == Intrinsic::experimental_deoptimize)
+ return CI;
+
+ return nullptr;
+}
+
Instruction* BasicBlock::getFirstNonPHI() {
for (Instruction &I : *this)
if (!isa<PHINode>(I))
diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index 48792fbfc42..9d9fe7d376d 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -4082,6 +4082,29 @@ void Verifier::visitIntrinsicCallSite(Intrinsic::ID ID, CallSite CS) {
"masked_store: vector mask must be same length as data", CS);
break;
}
+
+ case Intrinsic::experimental_deoptimize: {
+ Assert(CS.isCall(), "experimental_deoptimize cannot be invoked", CS);
+ Assert(CS.countOperandBundlesOfType(LLVMContext::OB_deopt) == 1,
+ "experimental_deoptimize must have exactly one "
+ "\"deopt\" operand bundle");
+ Assert(CS.getType() == CS.getInstruction()->getFunction()->getReturnType(),
+ "experimental_deoptimize return type must match caller return type");
+
+ if (CS.isCall()) {
+ auto *DeoptCI = CS.getInstruction();
+ auto *RI = dyn_cast<ReturnInst>(DeoptCI->getNextNode());
+ Assert(RI,
+ "calls to experimental_deoptimize must be followed by a return");
+
+ if (!CS.getType()->isVoidTy() && RI)
+ Assert(RI->getReturnValue() == DeoptCI,
+ "calls to experimental_deoptimize must be followed by a return "
+ "of the value computed by experimental_deoptimize");
+ }
+
+ break;
+ }
};
}
diff --git a/llvm/lib/Transforms/Utils/InlineFunction.cpp b/llvm/lib/Transforms/Utils/InlineFunction.cpp
index 491b18e6fc7..31cf5fbdc85 100644
--- a/llvm/lib/Transforms/Utils/InlineFunction.cpp
+++ b/llvm/lib/Transforms/Utils/InlineFunction.cpp
@@ -427,6 +427,15 @@ static BasicBlock *HandleCallsInBlockInlinedThroughInvoke(
if (!CI || CI->doesNotThrow() || isa<InlineAsm>(CI->getCalledValue()))
continue;
+ // We do not need to (and in fact, cannot) convert possibly throwing calls
+ // to @llvm.experimental_deoptimize into invokes. The caller's "segment" of
+ // the deoptimization continuation attached to the newly inlined
+ // @llvm.experimental_deoptimize call should contain the exception handling
+ // logic, if any.
+ if (auto *F = CI->getCalledFunction())
+ if (F->getIntrinsicID() == Intrinsic::experimental_deoptimize)
+ continue;
+
if (auto FuncletBundle = CI->getOperandBundle(LLVMContext::OB_funclet)) {
// This call is nested inside a funclet. If that funclet has an unwind
// destination within the inlinee, then unwinding out of this call would
@@ -1613,7 +1622,7 @@ bool llvm::InlineFunction(CallSite CS, InlineFunctionInfo &IFI,
replaceDbgDeclareForAlloca(AI, AI, DIB, /*Deref=*/false);
}
- bool InlinedMustTailCalls = false;
+ bool InlinedMustTailCalls = false, InlinedDeoptimizeCalls = false;
if (InlinedFunctionInfo.ContainsCalls) {
CallInst::TailCallKind CallSiteTailKind = CallInst::TCK_None;
if (CallInst *CI = dyn_cast<CallInst>(TheCall))
@@ -1626,6 +1635,10 @@ bool llvm::InlineFunction(CallSite CS, InlineFunctionInfo &IFI,
if (!CI)
continue;
+ if (Function *F = CI->getCalledFunction())
+ InlinedDeoptimizeCalls |=
+ F->getIntrinsicID() == Intrinsic::experimental_deoptimize;
+
// We need to reduce the strength of any inlined tail calls. For
// musttail, we have to avoid introducing potential unbounded stack
// growth. For example, if functions 'f' and 'g' are mutually recursive
@@ -1799,6 +1812,56 @@ bool llvm::InlineFunction(CallSite CS, InlineFunctionInfo &IFI,
}
}
+ if (InlinedDeoptimizeCalls) {
+ // We need to at least remove the deoptimizing returns from the Return set,
+ // so that the control flow from those returns does not get merged into the
+ // caller (but terminate it instead). If the caller's return type does not
+ // match the callee's return type, we also need to change the return type of
+ // the intrinsic.
+ if (Caller->getReturnType() == TheCall->getType()) {
+ auto NewEnd = remove_if(Returns, [](ReturnInst *RI) {
+ return RI->getParent()->getTerminatingDeoptimizeCall() != nullptr;
+ });
+ Returns.erase(NewEnd, Returns.end());
+ } else {
+ SmallVector<ReturnInst *, 8> NormalReturns;
+ Function *NewDeoptIntrinsic = Intrinsic::getDeclaration(
+ Caller->getParent(), Intrinsic::experimental_deoptimize,
+ {Caller->getReturnType()});
+
+ for (ReturnInst *RI : Returns) {
+ CallInst *DeoptCall = RI->getParent()->getTerminatingDeoptimizeCall();
+ if (!DeoptCall) {
+ NormalReturns.push_back(RI);
+ continue;
+ }
+
+ auto *CurBB = RI->getParent();
+ RI->eraseFromParent();
+
+ SmallVector<Value *, 4> CallArgs(DeoptCall->arg_begin(),
+ DeoptCall->arg_end());
+
+ SmallVector<OperandBundleDef, 1> OpBundles;
+ DeoptCall->getOperandBundlesAsDefs(OpBundles);
+ DeoptCall->eraseFromParent();
+ assert(!OpBundles.empty() &&
+ "Expected at least the deopt operand bundle");
+
+ IRBuilder<> Builder(CurBB);
+ Value *NewDeoptCall =
+ Builder.CreateCall(NewDeoptIntrinsic, CallArgs, OpBundles);
+ if (NewDeoptCall->getType()->isVoidTy())
+ Builder.CreateRetVoid();
+ else
+ Builder.CreateRet(NewDeoptCall);
+ }
+
+ // Leave behind the normal returns so we can merge control flow.
+ std::swap(Returns, NormalReturns);
+ }
+ }
+
// Handle any inlined musttail call sites. In order for a new call site to be
// musttail, the source of the clone and the inlined call site must have been
// musttail. Therefore it's safe to return without merging control into the
OpenPOWER on IntegriCloud