summaryrefslogtreecommitdiffstats
path: root/llvm/lib
diff options
context:
space:
mode:
authorGor Nishanov <GorNishanov@gmail.com>2017-08-25 02:25:10 +0000
committerGor Nishanov <GorNishanov@gmail.com>2017-08-25 02:25:10 +0000
commite29e94cf87c89772d62419750e786b29f814ad74 (patch)
tree3353c9073684748f3dd2d530f1b2a5212b59705a /llvm/lib
parent7b5a8bf643523b9e6e5ff2daf34673da7b7dd38a (diff)
downloadbcm5719-llvm-e29e94cf87c89772d62419750e786b29f814ad74.tar.gz
bcm5719-llvm-e29e94cf87c89772d62419750e786b29f814ad74.zip
[coroutines] Add support for symmetric control transfer (musttail on coro.resumes followed by a suspend)
Summary: Add musttail to any resume instructions that is immediately followed by a suspend (i.e. ret). We do this even in -O0 to support guaranteed tail call for symmetrical coroutine control transfer (C++ Coroutines TS extension). This transformation is done only in the resume part of the coroutine that has identical signature and calling convention as the coro.resume call. Reviewers: GorNishanov Reviewed By: GorNishanov Subscribers: EricWF, majnemer, llvm-commits Differential Revision: https://reviews.llvm.org/D37125 llvm-svn: 311751
Diffstat (limited to 'llvm/lib')
-rw-r--r--llvm/lib/Transforms/Coroutines/CoroSplit.cpp88
1 files changed, 88 insertions, 0 deletions
diff --git a/llvm/lib/Transforms/Coroutines/CoroSplit.cpp b/llvm/lib/Transforms/Coroutines/CoroSplit.cpp
index 173dc05f058..f36827e0ffa 100644
--- a/llvm/lib/Transforms/Coroutines/CoroSplit.cpp
+++ b/llvm/lib/Transforms/Coroutines/CoroSplit.cpp
@@ -27,6 +27,7 @@
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/IR/Verifier.h"
#include "llvm/Transforms/Scalar.h"
+#include "llvm/Transforms/Utils/BasicBlockUtils.h"
#include "llvm/Transforms/Utils/Cloning.h"
#include "llvm/Transforms/Utils/Local.h"
#include "llvm/Transforms/Utils/ValueMapper.h"
@@ -400,6 +401,91 @@ static void postSplitCleanup(Function &F) {
FPM.doFinalization();
}
+// Assuming we arrived at the block NewBlock from Prev instruction, store
+// PHI's incoming values in the ResolvedValues map.
+static void
+scanPHIsAndUpdateValueMap(Instruction *Prev, BasicBlock *NewBlock,
+ DenseMap<Value *, Value *> &ResolvedValues) {
+ auto *PrevBB = Prev->getParent();
+ auto *I = &*NewBlock->begin();
+ while (auto PN = dyn_cast<PHINode>(I)) {
+ auto V = PN->getIncomingValueForBlock(PrevBB);
+ // See if we already resolved it.
+ auto VI = ResolvedValues.find(V);
+ if (VI != ResolvedValues.end())
+ V = VI->second;
+ // Remember the value.
+ ResolvedValues[PN] = V;
+ I = I->getNextNode();
+ }
+}
+
+// Replace a sequence of branches leading to a ret, with a clone of a ret
+// instruction. Suspend instruction represented by a switch, track the PHI
+// values and select the correct case successor when possible.
+static bool simplifyTerminatorLeadingToRet(Instruction *InitialInst) {
+ DenseMap<Value *, Value *> ResolvedValues;
+
+ Instruction *I = InitialInst;
+ while (isa<TerminatorInst>(I)) {
+ if (isa<ReturnInst>(I)) {
+ if (I != InitialInst)
+ ReplaceInstWithInst(InitialInst, I->clone());
+ return true;
+ }
+ if (auto *BR = dyn_cast<BranchInst>(I)) {
+ if (BR->isUnconditional()) {
+ BasicBlock *BB = BR->getSuccessor(0);
+ scanPHIsAndUpdateValueMap(I, BB, ResolvedValues);
+ I = BB->getFirstNonPHIOrDbgOrLifetime();
+ continue;
+ }
+ } else if (auto *SI = dyn_cast<SwitchInst>(I)) {
+ Value *V = SI->getCondition();
+ auto it = ResolvedValues.find(V);
+ if (it != ResolvedValues.end())
+ V = it->second;
+ if (ConstantInt *Cond = dyn_cast<ConstantInt>(V)) {
+ BasicBlock *BB = SI->findCaseValue(Cond)->getCaseSuccessor();
+ scanPHIsAndUpdateValueMap(I, BB, ResolvedValues);
+ I = BB->getFirstNonPHIOrDbgOrLifetime();
+ continue;
+ }
+ }
+ return false;
+ }
+ return false;
+}
+
+// Add musttail to any resume instructions that is immediately followed by a
+// suspend (i.e. ret). We do this even in -O0 to support guaranteed tail call
+// for symmetrical coroutine control transfer (C++ Coroutines TS extension).
+// This transformation is done only in the resume part of the coroutine that has
+// identical signature and calling convention as the coro.resume call.
+static void addMustTailToCoroResumes(Function &F) {
+ bool changed = false;
+
+ // Collect potential resume instructions.
+ SmallVector<CallInst *, 4> Resumes;
+ for (auto &I : instructions(F))
+ if (auto *Call = dyn_cast<CallInst>(&I))
+ if (auto *CalledValue = Call->getCalledValue())
+ // CoroEarly pass replaced coro resumes with indirect calls to an
+ // address return by CoroSubFnInst intrinsic. See if it is one of those.
+ if (isa<CoroSubFnInst>(CalledValue->stripPointerCasts()))
+ Resumes.push_back(Call);
+
+ // Set musttail on those that are followed by a ret instruction.
+ for (CallInst *Call : Resumes)
+ if (simplifyTerminatorLeadingToRet(Call->getNextNode())) {
+ Call->setTailCallKind(llvm::CallInst::TCK_MustTail);
+ changed = true;
+ }
+
+ if (changed)
+ removeUnreachableBlocks(F);
+}
+
// Coroutine has no suspend points. Remove heap allocation for the coroutine
// frame if possible.
static void handleNoSuspendCoroutine(CoroBeginInst *CoroBegin, Type *FrameTy) {
@@ -608,6 +694,8 @@ static void splitCoroutine(Function &F, CallGraph &CG, CallGraphSCC &SCC) {
postSplitCleanup(*DestroyClone);
postSplitCleanup(*CleanupClone);
+ addMustTailToCoroResumes(*ResumeClone);
+
// Store addresses resume/destroy/cleanup functions in the coroutine frame.
updateCoroFrame(Shape, ResumeClone, DestroyClone, CleanupClone);
OpenPOWER on IntegriCloud