Index: lib/Transforms/NaCl/LowerEmAsyncify.cpp |
diff --git a/lib/Transforms/NaCl/LowerEmAsyncify.cpp b/lib/Transforms/NaCl/LowerEmAsyncify.cpp |
deleted file mode 100644 |
index c9128086ae445fa11707b7a17009e2af6eeb13d1..0000000000000000000000000000000000000000 |
--- a/lib/Transforms/NaCl/LowerEmAsyncify.cpp |
+++ /dev/null |
@@ -1,725 +0,0 @@ |
-//===- LowerEmAsyncify - transform asynchronous functions for Emscripten/JS -----------===// |
-// |
-// The LLVM Compiler Infrastructure |
-// |
-// This file is distributed under the University of Illinois Open Source |
-// License. See LICENSE.TXT for details. |
-// |
-//===----------------------------------------------------------------------===// |
-// |
-// Lu Wang <coolwanglu@gmail.com> |
-// |
-// In JS we don't have functions like sleep(), which is on the other hand very popuar in C/C++ etc. |
-// This pass tries to convert funcitons calling sleep() into a valid form in JavaScript |
-// The basic idea is to split the callee at the place where sleep() is called, |
-// then the first half may schedule the second half using setTimeout. |
-// But we need to pay lots of attention to analyzing/saving/restoring context variables and return values |
-// |
-//===----------------------------------------------------------------------===// |
- |
-#include "llvm/ADT/SmallPtrSet.h" |
-#include "llvm/ADT/DenseMap.h" |
-#include "llvm/IR/Dominators.h" |
-#include "llvm/IR/DataLayout.h" |
-#include "llvm/IR/Instructions.h" |
-#include "llvm/IR/Function.h" |
-#include "llvm/IR/Module.h" |
-#include "llvm/IR/Value.h" |
-#include "llvm/IR/CallSite.h" |
-#include "llvm/Support/CommandLine.h" |
-#include "llvm/IR/InstIterator.h" |
-#include "llvm/Transforms/NaCl.h" |
-#include "llvm/Transforms/Utils/BasicBlockUtils.h" |
-#include "llvm/Transforms/Utils/Cloning.h" |
-#include "llvm/Transforms/Utils/Local.h" // for DemoteRegToStack, removeUnreachableBlocks |
-#include "llvm/Transforms/Utils/PromoteMemToReg.h" // for PromoteMemToReg |
-#include "llvm/Transforms/Utils/ValueMapper.h" |
-#include "llvm/Pass.h" |
- |
-#include <vector> |
- |
-#ifdef NDEBUG |
-#undef assert |
-#define assert(x) { if (!(x)) report_fatal_error(#x); } |
-#endif |
- |
-using namespace llvm; |
- |
-static cl::list<std::string> |
-AsyncifyFunctions("emscripten-asyncify-functions", |
- cl::desc("Functions that call one of these functions, directly or indirectly, will be asyncified"), |
- cl::CommaSeparated); |
- |
-static cl::list<std::string> |
-AsyncifyWhiteList("emscripten-asyncify-whitelist", |
- cl::desc("Functions that should not be asyncified"), |
- cl::CommaSeparated); |
- |
-namespace { |
- class LowerEmAsyncify: public ModulePass { |
- Module *TheModule; |
- |
- public: |
- static char ID; // Pass identification, replacement for typeid |
- explicit LowerEmAsyncify() : ModulePass(ID), TheModule(NULL) { |
- initializeLowerEmAsyncifyPass(*PassRegistry::getPassRegistry()); |
- } |
- virtual ~LowerEmAsyncify() { } |
- bool runOnModule(Module &M); |
- |
- private: |
- const DataLayout *DL; |
- |
- Type *Void, *I1, *I32, *I32Ptr; |
- FunctionType *VFunction, *I1Function, *I32PFunction; |
- FunctionType *VI32PFunction, *I32PI32Function; |
- FunctionType *CallbackFunctionType; |
- |
- Function *AllocAsyncCtxFunction, *ReallocAsyncCtxFunction, *FreeAsyncCtxFunction; |
- Function *CheckAsyncFunction; |
- Function *DoNotUnwindFunction, *DoNotUnwindAsyncFunction; |
- Function *GetAsyncReturnValueAddrFunction; |
- |
- void initTypesAndFunctions(void); |
- |
- typedef std::vector<Instruction *> Instructions; |
- typedef DenseMap<Function*, Instructions> FunctionInstructionsMap; |
- typedef std::vector<Value*> Values; |
- typedef SmallPtrSet<BasicBlock*, 16> BasicBlockSet; |
- |
- // all the information we want for an async call |
- struct AsyncCallEntry { |
- Instruction *AsyncCallInst; // calling an async function |
- BasicBlock *AfterCallBlock; // the block we should continue on after getting the return value of AsynCallInst |
- CallInst *AllocAsyncCtxInst; // where we allocate the async ctx before the async call, in the original function |
- Values ContextVariables; // those need to be saved and restored for the async call |
- StructType *ContextStructType; // The structure constructing all the context variables |
- BasicBlock *SaveAsyncCtxBlock; // the block in which we save all the variables |
- Function *CallbackFunc; // the callback function for this async call, which is converted from the original function |
- }; |
- |
- BasicBlockSet FindReachableBlocksFrom(BasicBlock *src); |
- |
- // Find everything that we should save and restore for the async call |
- // save them to Entry.ContextVariables |
- void FindContextVariables(AsyncCallEntry & Entry); |
- |
- // The essential function |
- // F is now in the sync form, transform it into an async form that is valid in JS |
- void transformAsyncFunction(Function &F, Instructions const& AsyncCalls); |
- |
- bool IsFunctionPointerCall(const Instruction *I); |
- }; |
-} |
- |
-char LowerEmAsyncify::ID = 0; |
-INITIALIZE_PASS(LowerEmAsyncify, "loweremasyncify", |
- "Lower async functions for js/emscripten", |
- false, false) |
- |
-bool LowerEmAsyncify::runOnModule(Module &M) { |
- TheModule = &M; |
- DL = &M.getDataLayout(); |
- |
- std::set<std::string> WhiteList(AsyncifyWhiteList.begin(), AsyncifyWhiteList.end()); |
- |
- /* |
- * collect all the functions that should be asyncified |
- * any function that _might_ call an async function is also async |
- */ |
- std::vector<Function*> AsyncFunctionsPending; |
- for(unsigned i = 0; i < AsyncifyFunctions.size(); ++i) { |
- std::string const& AFName = AsyncifyFunctions[i]; |
- Function *F = TheModule->getFunction(AFName); |
- if (F && !WhiteList.count(F->getName())) { |
- AsyncFunctionsPending.push_back(F); |
- } |
- } |
- |
- // No function needed to transform |
- if (AsyncFunctionsPending.empty()) return false; |
- |
- // Walk through the call graph and find all the async functions |
- FunctionInstructionsMap AsyncFunctionCalls; |
- { |
- // pessimistic: consider all indirect calls as possibly async |
- // TODO: deduce based on function types |
- for (Module::iterator FI = TheModule->begin(), FE = TheModule->end(); FI != FE; ++FI) { |
- if (WhiteList.count(FI->getName())) continue; |
- |
- bool has_indirect_call = false; |
- for (inst_iterator I = inst_begin(FI), E = inst_end(FI); I != E; ++I) { |
- if (IsFunctionPointerCall(&*I)) { |
- has_indirect_call = true; |
- AsyncFunctionCalls[FI].push_back(&*I); |
- } |
- } |
- |
- if (has_indirect_call) AsyncFunctionsPending.push_back(FI); |
- } |
- |
- while (!AsyncFunctionsPending.empty()) { |
- Function *CurFunction = AsyncFunctionsPending.back(); |
- AsyncFunctionsPending.pop_back(); |
- |
- for (Value::user_iterator UI = CurFunction->user_begin(), E = CurFunction->user_end(); UI != E; ++UI) { |
- ImmutableCallSite ICS(*UI); |
- if (!ICS) continue; |
- // we only need those instructions calling the function |
- // if the function address is used for other purpose, we don't care |
- if (CurFunction != ICS.getCalledValue()->stripPointerCasts()) continue; |
- // Now I is either CallInst or InvokeInst |
- Instruction *I = cast<Instruction>(*UI); |
- Function *F = I->getParent()->getParent(); |
- if (AsyncFunctionCalls.count(F) == 0) { |
- AsyncFunctionsPending.push_back(F); |
- } |
- AsyncFunctionCalls[F].push_back(I); |
- } |
- } |
- } |
- |
- // exit if no async function is found at all |
- if (AsyncFunctionCalls.empty()) return false; |
- |
- initTypesAndFunctions(); |
- |
- for (FunctionInstructionsMap::iterator I = AsyncFunctionCalls.begin(), E = AsyncFunctionCalls.end(); |
- I != E; ++I) { |
- transformAsyncFunction(*(I->first), I->second); |
- } |
- |
- return true; |
-} |
- |
-void LowerEmAsyncify::initTypesAndFunctions(void) { |
- // Data types |
- Void = Type::getVoidTy(TheModule->getContext()); |
- I1 = Type::getInt1Ty(TheModule->getContext()); |
- I32 = Type::getInt32Ty(TheModule->getContext()); |
- I32Ptr = Type::getInt32PtrTy(TheModule->getContext()); |
- |
- // Function types |
- SmallVector<Type*, 2> ArgTypes; |
- VFunction = FunctionType::get(Void, false); |
- I1Function = FunctionType::get(I1, false); |
- I32PFunction = FunctionType::get(I32Ptr, false); |
- |
- ArgTypes.clear(); |
- ArgTypes.push_back(I32Ptr); |
- VI32PFunction = FunctionType::get(Void, ArgTypes, false); |
- |
- ArgTypes.clear(); |
- ArgTypes.push_back(I32); |
- I32PI32Function = FunctionType::get(I32Ptr, ArgTypes, false); |
- |
- CallbackFunctionType = VI32PFunction; |
- |
- // Functions |
- CheckAsyncFunction = Function::Create( |
- I1Function, |
- GlobalValue::ExternalLinkage, |
- "emscripten_check_async", |
- TheModule |
- ); |
- |
- AllocAsyncCtxFunction = Function::Create( |
- I32PI32Function, |
- GlobalValue::ExternalLinkage, |
- "emscripten_alloc_async_context", |
- TheModule |
- ); |
- |
- ReallocAsyncCtxFunction = Function::Create( |
- I32PI32Function, |
- GlobalValue::ExternalLinkage, |
- "emscripten_realloc_async_context", |
- TheModule |
- ); |
- |
- FreeAsyncCtxFunction = Function::Create( |
- VI32PFunction, |
- GlobalValue::ExternalLinkage, |
- "emscripten_free_async_context", |
- TheModule |
- ); |
- |
- DoNotUnwindFunction = Function::Create( |
- VFunction, |
- GlobalValue::ExternalLinkage, |
- "emscripten_do_not_unwind", |
- TheModule |
- ); |
- |
- DoNotUnwindAsyncFunction = Function::Create( |
- VFunction, |
- GlobalValue::ExternalLinkage, |
- "emscripten_do_not_unwind_async", |
- TheModule |
- ); |
- |
- GetAsyncReturnValueAddrFunction = Function::Create( |
- I32PFunction, |
- GlobalValue::ExternalLinkage, |
- "emscripten_get_async_return_value_addr", |
- TheModule |
- ); |
-} |
- |
-LowerEmAsyncify::BasicBlockSet LowerEmAsyncify::FindReachableBlocksFrom(BasicBlock *src) { |
- BasicBlockSet ReachableBlockSet; |
- std::vector<BasicBlock*> pending; |
- ReachableBlockSet.insert(src); |
- pending.push_back(src); |
- while (!pending.empty()) { |
- BasicBlock *CurBlock = pending.back(); |
- pending.pop_back(); |
- for (succ_iterator SI = succ_begin(CurBlock), SE = succ_end(CurBlock); SI != SE; ++SI) { |
- if (ReachableBlockSet.count(*SI) == 0) { |
- ReachableBlockSet.insert(*SI); |
- pending.push_back(*SI); |
- } |
- } |
- } |
- return ReachableBlockSet; |
-} |
- |
-void LowerEmAsyncify::FindContextVariables(AsyncCallEntry & Entry) { |
- BasicBlock *AfterCallBlock = Entry.AfterCallBlock; |
- |
- Function & F = *AfterCallBlock->getParent(); |
- |
- // Create a new entry block as if in the callback function |
- // theck check variables that no longer properly dominate their uses |
- BasicBlock *EntryBlock = BasicBlock::Create(TheModule->getContext(), "", &F, &F.getEntryBlock()); |
- BranchInst::Create(AfterCallBlock, EntryBlock); |
- |
- DominatorTreeWrapperPass DTW; |
- DTW.runOnFunction(F); |
- DominatorTree& DT = DTW.getDomTree(); |
- |
- // These blocks may be using some values defined at or before AsyncCallBlock |
- BasicBlockSet Ramifications = FindReachableBlocksFrom(AfterCallBlock); |
- |
- SmallPtrSet<Value*, 256> ContextVariables; |
- Values Pending; |
- |
- // Examine the instructions, find all variables that we need to store in the context |
- for (BasicBlockSet::iterator RI = Ramifications.begin(), RE = Ramifications.end(); RI != RE; ++RI) { |
- for (BasicBlock::iterator I = (*RI)->begin(), E = (*RI)->end(); I != E; ++I) { |
- for (unsigned i = 0, NumOperands = I->getNumOperands(); i < NumOperands; ++i) { |
- Value *O = I->getOperand(i); |
- if (Instruction *Inst = dyn_cast<Instruction>(O)) { |
- if (Inst == Entry.AsyncCallInst) continue; // for the original async call, we will load directly from async return value |
- if (ContextVariables.count(Inst) != 0) continue; // already examined |
- |
- if (!DT.dominates(Inst, I->getOperandUse(i))) { |
- // `I` is using `Inst`, yet `Inst` does not dominate `I` if we arrive directly at AfterCallBlock |
- // so we need to save `Inst` in the context |
- ContextVariables.insert(Inst); |
- Pending.push_back(Inst); |
- } |
- } else if (Argument *Arg = dyn_cast<Argument>(O)) { |
- // count() should be as fast/slow as insert, so just insert here |
- ContextVariables.insert(Arg); |
- } |
- } |
- } |
- } |
- |
- // restore F |
- EntryBlock->eraseFromParent(); |
- |
- Entry.ContextVariables.clear(); |
- Entry.ContextVariables.reserve(ContextVariables.size()); |
- for (SmallPtrSet<Value*, 256>::iterator I = ContextVariables.begin(), E = ContextVariables.end(); I != E; ++I) { |
- Entry.ContextVariables.push_back(*I); |
- } |
-} |
- |
-/* |
- * Consider that F contains a call to G, both of which are async: |
- * |
- * function F: |
- * ... |
- * %0 = G(%1, %2, ...); |
- * ... |
- * return %%; |
- * |
- * We want to convert F and generate F__asyn_cb |
- * they are similar, but with minor yet important differences |
- * Note those `main func only` and `callback func only` instructions |
- |
-////////////////////////////////////////////////////////// |
- function F: |
- ... |
- ctx = alloc_ctx(len, sp); // main func only |
- // TODO |
- // we could also do this only after an async call |
- // but in that case we will need to pass ctx to the function |
- // since ctx is no longer in the top async stack frame |
- %0 = G(%1, %2, ...); |
- if (async) { // G was async |
- save context variables in ctx |
- register F.async_cb as the callback in frame |
- return without unwinding the stack frame |
- } else { // G was sync |
- // use %0 as normal |
- free_ctx(ctx); // main func only |
- // ctx is freed here, because so far F is still a sync function |
- // and we don't want any side effects |
- ... |
- async return value = %%; |
- return & normally unwind the stack frame // main func only |
- } |
-////////////////////////////////////////////////////////// |
- |
- * And here's F.async_cb |
- |
-////////////////////////////////////////////////////////// |
- function F.async_cb(ctx): |
- load variables from ctx // callback func only |
- goto resume_point; // callback func only |
- ... |
- ctx = realloc_ctx(len); // callback func only |
- // realloc_ctx is different from alloc_ctx |
- // which reused the current async stack frame |
- // we want to keep the saved stack pointer |
- %0 = G(%1, %2, ...); |
- if (async) { |
- save context variables in ctx |
- register F.async_cb as the callback |
- return without unwinding the stack frame |
- } else { |
- resume_point: |
- %0'= either $0 or the async return value // callback func only |
- ... |
- async return value = %% |
- return restore the stack pointer back to the value stored in F // callback func only |
- // no need to free the ctx |
- // the scheduler will be aware of this return and handle the stack frames |
- } |
-////////////////////////////////////////////////////////// |
- |
- */ |
- |
-void LowerEmAsyncify::transformAsyncFunction(Function &F, Instructions const& AsyncCalls) { |
- assert(!AsyncCalls.empty()); |
- |
- // Pass 0 |
- // collect all the return instructions from the original function |
- // will use later |
- std::vector<ReturnInst*> OrigReturns; |
- for (inst_iterator I = inst_begin(&F), E = inst_end(&F); I != E; ++I) { |
- if (ReturnInst *RI = dyn_cast<ReturnInst>(&*I)) { |
- OrigReturns.push_back(RI); |
- } |
- } |
- |
- // Pass 1 |
- // Scan each async call and make the basic structure: |
- // All these will be cloned into the callback functions |
- // - allocate the async context before calling an async function |
- // - check async right after calling an async function, save context & return if async, continue if not |
- // - retrieve the async return value and free the async context if the called function turns out to be sync |
- std::vector<AsyncCallEntry> AsyncCallEntries; |
- AsyncCallEntries.reserve(AsyncCalls.size()); |
- for (Instructions::const_iterator I = AsyncCalls.begin(), E = AsyncCalls.end(); I != E; ++I) { |
- // prepare blocks |
- Instruction *CurAsyncCall = *I; |
- |
- // The block containing the async call |
- BasicBlock *CurBlock = CurAsyncCall->getParent(); |
- // The block should run after the async call |
- BasicBlock *AfterCallBlock = SplitBlock(CurBlock, CurAsyncCall->getNextNode()); |
- // The block where we store the context and return |
- BasicBlock *SaveAsyncCtxBlock = BasicBlock::Create(TheModule->getContext(), "SaveAsyncCtx", &F, AfterCallBlock); |
- // return a dummy value at the end, to make the block valid |
- new UnreachableInst(TheModule->getContext(), SaveAsyncCtxBlock); |
- |
- // allocate the context before making the call |
- // we don't know the size yet, will fix it later |
- // we cannot insert the instruction later because, |
- // we need to make sure that all the instructions and blocks are fixed before we can generate DT and find context variables |
- // In CallHandler.h `sp` will be put as the second parameter |
- // such that we can take a note of the original sp |
- CallInst *AllocAsyncCtxInst = CallInst::Create(AllocAsyncCtxFunction, Constant::getNullValue(I32), "AsyncCtx", CurAsyncCall); |
- |
- // Right after the call |
- // check async and return if so |
- // TODO: we can define truly async functions and partial async functions |
- { |
- // remove old terminator, which came from SplitBlock |
- CurBlock->getTerminator()->eraseFromParent(); |
- // go to SaveAsyncCtxBlock if the previous call is async |
- // otherwise just continue to AfterCallBlock |
- CallInst *CheckAsync = CallInst::Create(CheckAsyncFunction, "IsAsync", CurBlock); |
- BranchInst::Create(SaveAsyncCtxBlock, AfterCallBlock, CheckAsync, CurBlock); |
- } |
- |
- // take a note of this async call |
- AsyncCallEntry CurAsyncCallEntry; |
- CurAsyncCallEntry.AsyncCallInst = CurAsyncCall; |
- CurAsyncCallEntry.AfterCallBlock = AfterCallBlock; |
- CurAsyncCallEntry.AllocAsyncCtxInst = AllocAsyncCtxInst; |
- CurAsyncCallEntry.SaveAsyncCtxBlock = SaveAsyncCtxBlock; |
- // create an empty function for the callback, which will be constructed later |
- CurAsyncCallEntry.CallbackFunc = Function::Create(CallbackFunctionType, F.getLinkage(), F.getName() + "__async_cb", TheModule); |
- AsyncCallEntries.push_back(CurAsyncCallEntry); |
- } |
- |
- |
- // Pass 2 |
- // analyze the context variables and construct SaveAsyncCtxBlock for each async call |
- // also calculate the size of the context and allocate the async context accordingly |
- for (std::vector<AsyncCallEntry>::iterator EI = AsyncCallEntries.begin(), EE = AsyncCallEntries.end(); EI != EE; ++EI) { |
- AsyncCallEntry & CurEntry = *EI; |
- |
- // Collect everything to be saved |
- FindContextVariables(CurEntry); |
- |
- // Pack the variables as a struct |
- { |
- // TODO: sort them from large memeber to small ones, in order to make the struct compact even when aligned |
- SmallVector<Type*, 8> Types; |
- Types.push_back(CallbackFunctionType->getPointerTo()); |
- for (Values::iterator VI = CurEntry.ContextVariables.begin(), VE = CurEntry.ContextVariables.end(); VI != VE; ++VI) { |
- Types.push_back((*VI)->getType()); |
- } |
- CurEntry.ContextStructType = StructType::get(TheModule->getContext(), Types); |
- } |
- |
- // fix the size of allocation |
- CurEntry.AllocAsyncCtxInst->setOperand(0, |
- ConstantInt::get(I32, DL->getTypeStoreSize(CurEntry.ContextStructType))); |
- |
- // construct SaveAsyncCtxBlock |
- { |
- // fill in SaveAsyncCtxBlock |
- // temporarily remove the terminator for convenience |
- CurEntry.SaveAsyncCtxBlock->getTerminator()->eraseFromParent(); |
- assert(CurEntry.SaveAsyncCtxBlock->empty()); |
- |
- Type *AsyncCtxAddrTy = CurEntry.ContextStructType->getPointerTo(); |
- BitCastInst *AsyncCtxAddr = new BitCastInst(CurEntry.AllocAsyncCtxInst, AsyncCtxAddrTy, "AsyncCtxAddr", CurEntry.SaveAsyncCtxBlock); |
- SmallVector<Value*, 2> Indices; |
- // store the callback |
- { |
- Indices.push_back(ConstantInt::get(I32, 0)); |
- Indices.push_back(ConstantInt::get(I32, 0)); |
- GetElementPtrInst *AsyncVarAddr = GetElementPtrInst::Create(AsyncCtxAddrTy, AsyncCtxAddr, Indices, "", CurEntry.SaveAsyncCtxBlock); |
- new StoreInst(CurEntry.CallbackFunc, AsyncVarAddr, CurEntry.SaveAsyncCtxBlock); |
- } |
- // store the context variables |
- for (size_t i = 0; i < CurEntry.ContextVariables.size(); ++i) { |
- Indices.clear(); |
- Indices.push_back(ConstantInt::get(I32, 0)); |
- Indices.push_back(ConstantInt::get(I32, i + 1)); // the 0th element is the callback function |
- GetElementPtrInst *AsyncVarAddr = GetElementPtrInst::Create(AsyncCtxAddrTy, AsyncCtxAddr, Indices, "", CurEntry.SaveAsyncCtxBlock); |
- new StoreInst(CurEntry.ContextVariables[i], AsyncVarAddr, CurEntry.SaveAsyncCtxBlock); |
- } |
- // to exit the block, we want to return without unwinding the stack frame |
- CallInst::Create(DoNotUnwindFunction, "", CurEntry.SaveAsyncCtxBlock); |
- ReturnInst::Create(TheModule->getContext(), |
- (F.getReturnType()->isVoidTy() ? 0 : Constant::getNullValue(F.getReturnType())), |
- CurEntry.SaveAsyncCtxBlock); |
- } |
- } |
- |
- // Pass 3 |
- // now all the SaveAsyncCtxBlock's have been constructed |
- // we can clone F and construct callback functions |
- // we could not construct the callbacks in Pass 2 because we need _all_ those SaveAsyncCtxBlock's appear in _each_ callback |
- for (std::vector<AsyncCallEntry>::iterator EI = AsyncCallEntries.begin(), EE = AsyncCallEntries.end(); EI != EE; ++EI) { |
- AsyncCallEntry & CurEntry = *EI; |
- |
- Function *CurCallbackFunc = CurEntry.CallbackFunc; |
- ValueToValueMapTy VMap; |
- |
- // Add the entry block |
- // load variables from the context |
- // also update VMap for CloneFunction |
- BasicBlock *EntryBlock = BasicBlock::Create(TheModule->getContext(), "AsyncCallbackEntry", CurCallbackFunc); |
- std::vector<LoadInst *> LoadedAsyncVars; |
- { |
- Type *AsyncCtxAddrTy = CurEntry.ContextStructType->getPointerTo(); |
- BitCastInst *AsyncCtxAddr = new BitCastInst(CurCallbackFunc->arg_begin(), AsyncCtxAddrTy, "AsyncCtx", EntryBlock); |
- SmallVector<Value*, 2> Indices; |
- for (size_t i = 0; i < CurEntry.ContextVariables.size(); ++i) { |
- Indices.clear(); |
- Indices.push_back(ConstantInt::get(I32, 0)); |
- Indices.push_back(ConstantInt::get(I32, i + 1)); // the 0th element of AsyncCtx is the callback function |
- GetElementPtrInst *AsyncVarAddr = GetElementPtrInst::Create(AsyncCtxAddrTy, AsyncCtxAddr, Indices, "", EntryBlock); |
- LoadedAsyncVars.push_back(new LoadInst(AsyncVarAddr, "", EntryBlock)); |
- // we want the argument to be replaced by the loaded value |
- if (isa<Argument>(CurEntry.ContextVariables[i])) |
- VMap[CurEntry.ContextVariables[i]] = LoadedAsyncVars.back(); |
- } |
- } |
- |
- // we don't need any argument, just leave dummy entries there to cheat CloneFunctionInto |
- for (Function::const_arg_iterator AI = F.arg_begin(), AE = F.arg_end(); AI != AE; ++AI) { |
- if (VMap.count(AI) == 0) |
- VMap[AI] = Constant::getNullValue(AI->getType()); |
- } |
- |
- // Clone the function |
- { |
- SmallVector<ReturnInst*, 8> Returns; |
- CloneFunctionInto(CurCallbackFunc, &F, VMap, false, Returns); |
- |
- // return type of the callback functions is always void |
- // need to fix the return type |
- if (!F.getReturnType()->isVoidTy()) { |
- // for those return instructions that are from the original function |
- // it means we are 'truly' leaving this function |
- // need to store the return value right before ruturn |
- for (size_t i = 0; i < OrigReturns.size(); ++i) { |
- ReturnInst *RI = cast<ReturnInst>(VMap[OrigReturns[i]]); |
- // Need to store the return value into the global area |
- CallInst *RawRetValAddr = CallInst::Create(GetAsyncReturnValueAddrFunction, "", RI); |
- BitCastInst *RetValAddr = new BitCastInst(RawRetValAddr, F.getReturnType()->getPointerTo(), "AsyncRetValAddr", RI); |
- new StoreInst(RI->getOperand(0), RetValAddr, RI); |
- } |
- // we want to unwind the stack back to where it was before the original function as called |
- // but we don't actually need to do this here |
- // at this point it must be true that no callback is pended |
- // so the scheduler will correct the stack pointer and pop the frame |
- // here we just fix the return type |
- for (size_t i = 0; i < Returns.size(); ++i) { |
- ReplaceInstWithInst(Returns[i], ReturnInst::Create(TheModule->getContext())); |
- } |
- } |
- } |
- |
- // the callback function does not have any return value |
- // so clear all the attributes for return |
- { |
- AttributeSet Attrs = CurCallbackFunc->getAttributes(); |
- CurCallbackFunc->setAttributes( |
- Attrs.removeAttributes(TheModule->getContext(), AttributeSet::ReturnIndex, Attrs.getRetAttributes()) |
- ); |
- } |
- |
- // in the callback function, we never allocate a new async frame |
- // instead we reuse the existing one |
- for (std::vector<AsyncCallEntry>::iterator EI = AsyncCallEntries.begin(), EE = AsyncCallEntries.end(); EI != EE; ++EI) { |
- Instruction *I = cast<Instruction>(VMap[EI->AllocAsyncCtxInst]); |
- ReplaceInstWithInst(I, CallInst::Create(ReallocAsyncCtxFunction, I->getOperand(0), "ReallocAsyncCtx")); |
- } |
- |
- // mapped entry point & async call |
- BasicBlock *ResumeBlock = cast<BasicBlock>(VMap[CurEntry.AfterCallBlock]); |
- Instruction *MappedAsyncCall = cast<Instruction>(VMap[CurEntry.AsyncCallInst]); |
- |
- // To save space, for each async call in the callback function, we just ignore the sync case, and leave it to the scheduler |
- // TODO need an option for this |
- { |
- for (std::vector<AsyncCallEntry>::iterator EI = AsyncCallEntries.begin(), EE = AsyncCallEntries.end(); EI != EE; ++EI) { |
- AsyncCallEntry & CurEntry = *EI; |
- Instruction *MappedAsyncCallInst = cast<Instruction>(VMap[CurEntry.AsyncCallInst]); |
- BasicBlock *MappedAsyncCallBlock = MappedAsyncCallInst->getParent(); |
- BasicBlock *MappedAfterCallBlock = cast<BasicBlock>(VMap[CurEntry.AfterCallBlock]); |
- |
- // for the sync case of the call, go to NewBlock (instead of MappedAfterCallBlock) |
- BasicBlock *NewBlock = BasicBlock::Create(TheModule->getContext(), "", CurCallbackFunc, MappedAfterCallBlock); |
- MappedAsyncCallBlock->getTerminator()->setSuccessor(1, NewBlock); |
- // store the return value |
- if (!MappedAsyncCallInst->use_empty()) { |
- CallInst *RawRetValAddr = CallInst::Create(GetAsyncReturnValueAddrFunction, "", NewBlock); |
- BitCastInst *RetValAddr = new BitCastInst(RawRetValAddr, MappedAsyncCallInst->getType()->getPointerTo(), "AsyncRetValAddr", NewBlock); |
- new StoreInst(MappedAsyncCallInst, RetValAddr, NewBlock); |
- } |
- // tell the scheduler that we want to keep the current async stack frame |
- CallInst::Create(DoNotUnwindAsyncFunction, "", NewBlock); |
- // finally we go to the SaveAsyncCtxBlock, to register the callbac, save the local variables and leave |
- BasicBlock *MappedSaveAsyncCtxBlock = cast<BasicBlock>(VMap[CurEntry.SaveAsyncCtxBlock]); |
- BranchInst::Create(MappedSaveAsyncCtxBlock, NewBlock); |
- } |
- } |
- |
- std::vector<AllocaInst*> ToPromote; |
- // applying loaded variables in the entry block |
- { |
- BasicBlockSet ReachableBlocks = FindReachableBlocksFrom(ResumeBlock); |
- for (size_t i = 0; i < CurEntry.ContextVariables.size(); ++i) { |
- Value *OrigVar = CurEntry.ContextVariables[i]; |
- if (isa<Argument>(OrigVar)) continue; // already processed |
- Value *CurVar = VMap[OrigVar]; |
- assert(CurVar != MappedAsyncCall); |
- if (Instruction *Inst = dyn_cast<Instruction>(CurVar)) { |
- if (ReachableBlocks.count(Inst->getParent())) { |
- // Inst could be either defined or loaded from the async context |
- // Do the dirty works in memory |
- // TODO: might need to check the safety first |
- // TODO: can we create phi directly? |
- AllocaInst *Addr = DemoteRegToStack(*Inst, false); |
- new StoreInst(LoadedAsyncVars[i], Addr, EntryBlock); |
- ToPromote.push_back(Addr); |
- } else { |
- // The parent block is not reachable, which means there is no confliction |
- // it's safe to replace Inst with the loaded value |
- assert(Inst != LoadedAsyncVars[i]); // this should only happen when OrigVar is an Argument |
- Inst->replaceAllUsesWith(LoadedAsyncVars[i]); |
- } |
- } |
- } |
- } |
- |
- // resolve the return value of the previous async function |
- // it could be the value just loaded from the global area |
- // or directly returned by the function (in its sync case) |
- if (!CurEntry.AsyncCallInst->use_empty()) { |
- // load the async return value |
- CallInst *RawRetValAddr = CallInst::Create(GetAsyncReturnValueAddrFunction, "", EntryBlock); |
- BitCastInst *RetValAddr = new BitCastInst(RawRetValAddr, MappedAsyncCall->getType()->getPointerTo(), "AsyncRetValAddr", EntryBlock); |
- LoadInst *RetVal = new LoadInst(RetValAddr, "AsyncRetVal", EntryBlock); |
- |
- AllocaInst *Addr = DemoteRegToStack(*MappedAsyncCall, false); |
- new StoreInst(RetVal, Addr, EntryBlock); |
- ToPromote.push_back(Addr); |
- } |
- |
- // TODO remove unreachable blocks before creating phi |
- |
- // We go right to ResumeBlock from the EntryBlock |
- BranchInst::Create(ResumeBlock, EntryBlock); |
- |
- /* |
- * Creating phi's |
- * Normal stack frames and async stack frames are interleaving with each other. |
- * In a callback function, if we call an async function, we might need to realloc the async ctx. |
- * at this point we don't want anything stored after the ctx, |
- * such that we can free and extend the ctx by simply update STACKTOP. |
- * Therefore we don't want any alloca's in callback functions. |
- * |
- */ |
- if (!ToPromote.empty()) { |
- DominatorTreeWrapperPass DTW; |
- DTW.runOnFunction(*CurCallbackFunc); |
- PromoteMemToReg(ToPromote, DTW.getDomTree()); |
- } |
- |
- removeUnreachableBlocks(*CurCallbackFunc); |
- } |
- |
- // Pass 4 |
- // Here are modifications to the original function, which we won't want to be cloned into the callback functions |
- for (std::vector<AsyncCallEntry>::iterator EI = AsyncCallEntries.begin(), EE = AsyncCallEntries.end(); EI != EE; ++EI) { |
- AsyncCallEntry & CurEntry = *EI; |
- // remove the frame if no async functinon has been called |
- CallInst::Create(FreeAsyncCtxFunction, CurEntry.AllocAsyncCtxInst, "", CurEntry.AfterCallBlock->getFirstNonPHI()); |
- } |
-} |
- |
-bool LowerEmAsyncify::IsFunctionPointerCall(const Instruction *I) { |
- // mostly from CallHandler.h |
- ImmutableCallSite CS(I); |
- if (!CS) return false; // not call nor invoke |
- const Value *CV = CS.getCalledValue()->stripPointerCasts(); |
- return !isa<const Function>(CV); |
-} |
- |
-ModulePass *llvm::createLowerEmAsyncifyPass() { |
- return new LowerEmAsyncify(); |
-} |