bool InlineMalloc::runOnFunction(Function& F) { Function* Malloc = F.getParent()->getFunction("gcmalloc"); if (!Malloc || Malloc->isDeclaration()) return false; bool Changed = false; for (Function::iterator BI = F.begin(), BE = F.end(); BI != BE; BI++) { BasicBlock *Cur = BI; for (BasicBlock::iterator II = Cur->begin(), IE = Cur->end(); II != IE;) { Instruction *I = II; II++; CallSite Call = CallSite::get(I); Instruction* CI = Call.getInstruction(); if (CI) { Function* Temp = Call.getCalledFunction(); if (Temp == Malloc) { if (dyn_cast<Constant>(Call.getArgument(0))) { InlineFunctionInfo IFI(NULL, mvm::MvmModule::TheTargetData); Changed |= InlineFunction(Call, IFI); break; } } } } } return Changed; }
void inlineFunctionCalls(Function* f, TargetData* targetData) { //WFVOPENCL_DEBUG( outs() << " inlining function calls... "; ); bool functionChanged = true; while (functionChanged) { functionChanged = false; for (Function::iterator BB=f->begin(); BB!=f->end(); ++BB) { bool blockChanged = false; for (BasicBlock::iterator I=BB->begin(); !blockChanged && I!=BB->end();) { if (!isa<CallInst>(I)) { ++I; continue; } CallInst* call = cast<CallInst>(I++); Function* callee = call->getCalledFunction(); if (!callee) { //errs() << "ERROR: could not inline function call: " << *call; continue; } if (callee->getAttributes().hasAttrSomewhere(Attribute::NoInline)) { //WFVOPENCL_DEBUG( outs() << " function '" << callee->getNameStr() << "' has attribute 'no inline', ignored call!\n"; ); continue; } const std::string calleeName = callee->getNameStr(); //possibly deleted by InlineFunction() InlineFunctionInfo IFI(NULL, targetData); blockChanged = InlineFunction(call, IFI); //WFVOPENCL_DEBUG( //if (blockChanged) outs() << " inlined call to function '" << calleeName << "'\n"; //else errs() << " inlining of call to function '" << calleeName << "' FAILED!\n"; //); functionChanged |= blockChanged; } } } //WFVOPENCL_DEBUG( outs() << "done.\n"; ); }
// ============================================================================= // andOOPIsGone (formerly: createProcess) // // Formerly, OOP permitted the same SC_{METHOD,THREAD} functions to apply // to each copy of a SC_MODULE. Aaaaand it's gone ! // (but OTOH we enable better optimizations) // Creates a new C-style function that calls the old member function with the // given sc_module. The call is then inlined. // FIXME: assumes the method is non-virtual and that sc_module is the first // inherited class of the SC_MODULE // ============================================================================= Function *TwetoPassImpl::andOOPIsGone(Function * oldProc, sc_core::sc_module * initiatorMod) { if (!oldProc) return NULL; // can't statically optimize if the address of the module isn't predictible // TODO: also handle already-static variables, which also have // fixed $pc-relative addresses if (staticopt == optlevel && !permalloc::is_from (initiatorMod)) return NULL; LLVMContext & context = getGlobalContext(); FunctionType *funType = oldProc->getFunctionType(); Type *type = funType->getParamType(0); FunctionType *newProcType = FunctionType::get(oldProc->getReturnType(), ArrayRef < Type * >(), false); // Create the new function std::ostringstream id; id << proc_counter++; std::string name = oldProc->getName().str() + std::string("_clone_") + id.str(); Function *newProc = Function::Create(newProcType, Function::ExternalLinkage, name, this->llvmMod); assert(newProc->empty()); newProc->addFnAttr(Attribute::InlineHint); // Create call to old function BasicBlock *bb = BasicBlock::Create(context, "entry", newProc); IRBuilder <> *irb = new IRBuilder <> (context); irb->SetInsertPoint(bb); Value* thisAddr = createRelocatablePointer (type, initiatorMod, irb); CallInst *ci = irb->CreateCall(oldProc, ArrayRef < Value * >(std::vector<Value*>(1,thisAddr))); //bb->getInstList().insert(ci, thisAddr); if (ci->getType()->isVoidTy()) irb->CreateRetVoid(); else irb->CreateRet(ci); // The function should be valid now verifyFunction(*newProc); { // Inline the call DataLayout *td = new DataLayout(this->llvmMod); InlineFunctionInfo i(NULL, td); bool success = InlineFunction(ci, i); assert(success); verifyFunction(*newProc); } // further optimize the function inlineBasicIO (initiatorMod, newProc); newProc->dump(); return newProc; }
void GNUstep::IMPCacher::SpeculativelyInline(Instruction *call, Function *function) { BasicBlock *beforeCallBB = call->getParent(); BasicBlock *callBB = SplitBlock(beforeCallBB, call, Owner); BasicBlock *inlineBB = BasicBlock::Create(Context, "inline", callBB->getParent()); BasicBlock::iterator iter = call; iter++; BasicBlock *afterCallBB = SplitBlock(iter->getParent(), iter, Owner); removeTerminator(beforeCallBB); // Put a branch before the call, testing whether the callee really is the // function IRBuilder<> B = IRBuilder<>(beforeCallBB); Value *callee = isa<CallInst>(call) ? cast<CallInst>(call)->getCalledValue() : cast<InvokeInst>(call)->getCalledValue(); const FunctionType *FTy = function->getFunctionType(); const FunctionType *calleeTy = cast<FunctionType>( cast<PointerType>(callee->getType())->getElementType()); if (calleeTy != FTy) { callee = B.CreateBitCast(callee, function->getType()); } Value *isInlineValid = B.CreateICmpEQ(callee, function); B.CreateCondBr(isInlineValid, inlineBB, callBB); // In the inline BB, add a copy of the call, but this time calling the real // version. Instruction *inlineCall = call->clone(); Value *inlineResult= inlineCall; inlineBB->getInstList().push_back(inlineCall); B.SetInsertPoint(inlineBB); if (calleeTy != FTy) { for (unsigned i=0 ; i<FTy->getNumParams() ; i++) { LLVMType *callType = calleeTy->getParamType(i); LLVMType *argType = FTy->getParamType(i); if (callType != argType) { inlineCall->setOperand(i, new BitCastInst(inlineCall->getOperand(i), argType, "", inlineCall)); } } if (FTy->getReturnType() != calleeTy->getReturnType()) { if (FTy->getReturnType() == Type::getVoidTy(Context)) { inlineResult = Constant::getNullValue(calleeTy->getReturnType()); } else { inlineResult = new BitCastInst(inlineCall, calleeTy->getReturnType(), "", inlineBB); } } } B.CreateBr(afterCallBB); // Unify the return values if (call->getType() != Type::getVoidTy(Context)) { PHINode *phi = CreatePHI(call->getType(), 2, "", afterCallBB->begin()); call->replaceAllUsesWith(phi); phi->addIncoming(call, callBB); phi->addIncoming(inlineResult, inlineBB); } // Really do the real inlining InlineFunctionInfo IFI(0, 0); if (CallInst *c = dyn_cast<CallInst>(inlineCall)) { c->setCalledFunction(function); InlineFunction(c, IFI); } else if (InvokeInst *c = dyn_cast<InvokeInst>(inlineCall)) { c->setCalledFunction(function); InlineFunction(c, IFI); } }
Function* generateFunctionWrapperWithParams(const std::string& wrapper_name, Function* f, Module* mod, std::vector<const Type*>& additionalParams, const bool inlineCall) { assert (f && mod); assert (f->getParent()); if (f->getParent() != mod) { errs() << "WARNING: generateFunctionWrapper(): module '" << mod->getModuleIdentifier() << "' is not the parent of function '" << f->getNameStr() << "' (parent: '" << f->getParent()->getModuleIdentifier() << "')!\n"; } // first make sure there is no function with that name in mod // TODO: Implement logic from LLVM tutorial that checks for matching extern // declaration without body and, in this case, goes on. if (mod->getFunction(wrapper_name)) { errs() << "ERROR: generateFunctionWrapper(): Function with name '" << wrapper_name << "' already exists in module '" << mod->getModuleIdentifier() << "'!\n"; return NULL; } // warn if f has a return value if (!f->getReturnType()->isVoidTy()) { errs() << "WARNING: generateFunctionWrapper(): target function '" << f->getNameStr() << "' must not have a return type (ignored)!\n"; } LLVMContext& context = mod->getContext(); IRBuilder<> builder(context); // determine all arguments of f std::vector<const Argument*> oldArgs; std::vector<const Type*> oldArgTypes; for (Function::const_arg_iterator A=f->arg_begin(), AE=f->arg_end(); A!=AE; ++A) { oldArgs.push_back(A); oldArgTypes.push_back(A->getType()); } // create a struct type with a member for each argument const StructType* argStructType = StructType::get(context, oldArgTypes, false); // create function //const FunctionType* fType = TypeBuilder<void(void*), true>::get(context); std::vector<const Type*> params; params.push_back(PointerType::getUnqual(argStructType)); for (std::vector<const Type*>::const_iterator it=additionalParams.begin(), E=additionalParams.end(); it!=E; ++it) { params.push_back(*it); } const FunctionType* fType = FunctionType::get(Type::getVoidTy(context), params, false); Function* wrapper = Function::Create(fType, Function::ExternalLinkage, wrapper_name, mod); // set name of argument Argument* arg_str = wrapper->arg_begin(); arg_str->setName("arg_struct"); // create entry block BasicBlock* entryBB = BasicBlock::Create(context, "entry", wrapper); builder.SetInsertPoint(entryBB); // create extractions of arguments out of the struct SmallVector<Value*, 8> extractedArgs; for (unsigned i=0, e=oldArgTypes.size(); i<e; ++i) { // create GEP std::vector<Value*> indices; indices.push_back(Constant::getNullValue(Type::getInt32Ty(context))); // step through pointer indices.push_back(ConstantInt::get(context, APInt(32, i))); // index of argument Value* gep = builder.CreateGEP(arg_str, indices.begin(), indices.end(), ""); // create load LoadInst* load = builder.CreateLoad(gep, false, ""); // store as argument for call to f extractedArgs.push_back(load); } // create the call to f CallInst* call = builder.CreateCall(f, extractedArgs.begin(), extractedArgs.end(), ""); // the function returns void builder.CreateRetVoid(); //wrapper->addAttribute(0, Attribute::NoUnwind); // function does not unwind stack -> why is there an index required ??? wrapper->setDoesNotCapture(1, true); // arg ptr does not capture wrapper->setDoesNotAlias(1, true); // arg ptr does not alias // inline call if required if (inlineCall) { InlineFunctionInfo IFI(NULL, new TargetData(mod)); const bool success = InlineFunction(call, IFI); if (!success) { errs() << "WARNING: could not inline function call inside wrapper: " << *call << "\n"; } assert (success); } //verifyFunction(*wrapper, NULL); return wrapper; }
// ============================================================================= // createProcess // // Create a new function that contains a call to the old function. // We inline the call in order to clone the old function's implementation. // ============================================================================= Function *TLMBasicPassImpl::createProcess(Function *oldProc, sc_core::sc_module *initiatorMod) { LLVMContext &context = getGlobalContext(); IntegerType *intType; if (this->is64Bit) { intType = Type::getInt64Ty(context); } else { intType = Type::getInt32Ty(context); } // Retrieve a pointer to the initiator module ConstantInt *initiatorModVal = ConstantInt::getSigned(intType,reinterpret_cast<intptr_t>(initiatorMod)); FunctionType *funType = oldProc->getFunctionType(); Type *type = funType->getParamType(0); IntToPtrInst *thisAddr = new IntToPtrInst(initiatorModVal, type, ""); // Compute the type of the new function FunctionType *oldProcType = oldProc->getFunctionType(); Value **argsBegin = new Value*[1]; Value **argsEnd = argsBegin; *argsEnd++ = thisAddr; const unsigned argsSize = argsEnd-argsBegin; Value **args = argsBegin; assert(oldProcType->getNumParams()==argsSize); assert(!oldProc->isDeclaration()); std::vector<Type*> argTypes; for (unsigned i = 0; i!=argsSize; ++i) argTypes.push_back(oldProcType->getParamType(i)); FunctionType *newProcType = FunctionType::get(oldProc->getReturnType(), ArrayRef<Type*>(argTypes), false); // Create the new function std::ostringstream id; id << proc_counter++; std::string name = oldProc->getName().str()+std::string("_clone_")+id.str(); Function *newProc = Function::Create(newProcType, Function::ExternalLinkage, name, this->llvmMod); assert(newProc->empty()); newProc->addFnAttr(Attributes::InlineHint); { // Set name of newfunc arguments and complete args Function::arg_iterator nai = newProc->arg_begin(); Function::arg_iterator oai = oldProc->arg_begin(); for (unsigned i = 0; i!=argsSize; ++i, ++oai) { nai->setName(oai->getName()); args[i] = nai; ++nai; } assert(nai==newProc->arg_end()); assert(oai==oldProc->arg_end()); } // Create call to old function BasicBlock *bb = BasicBlock::Create(context, "entry", newProc); IRBuilder<> *irb = new IRBuilder<>(context); irb->SetInsertPoint(bb); CallInst *ci = irb->CreateCall(oldProc, ArrayRef<Value*>(argsBegin, argsEnd)); bb->getInstList().insert(ci, thisAddr); if (ci->getType()->isVoidTy()) irb->CreateRetVoid(); else irb->CreateRet(ci); // The function should be valid now verifyFunction(*newProc); { // Inline the call DataLayout *td = new DataLayout(this->llvmMod); InlineFunctionInfo i(NULL, td); bool success = InlineFunction(ci, i); assert(success); verifyFunction(*newProc); } //newProc->dump(); return newProc; }
// ============================================================================= // replaceCallsInProcess // // Replace indirect calls to write() or read() by direct calls // in the given process. // ============================================================================= void TLMBasicPassImpl::replaceCallsInProcess(sc_core::sc_module *initiatorMod, sc_core::sc_process_b *proc) { // Get associate function std::string fctName = proc->func_process; std::string modType = typeid(*initiatorMod).name(); std::string mainFctName = "_ZN" + modType + utostr(fctName.size()) + fctName + "Ev"; Function *oldProcf = this->llvmMod->getFunction(mainFctName); if (oldProcf==NULL) return; // We do not modifie the original function // Instead, we create a clone. Function *procf = createProcess(oldProcf, initiatorMod); void *funPtr = this->engine->getPointerToFunction(procf); sc_core::SC_ENTRY_FUNC_OPT scfun = reinterpret_cast<sc_core::SC_ENTRY_FUNC_OPT>(funPtr); proc->m_semantics_p = scfun; std::string procfName = procf->getName(); MSG(" Replace in the process's function : "+procfName+"\n"); std::ostringstream oss; sc_core::sc_module *targetMod; std::vector<CallInfo*> *work = new std::vector<CallInfo*>; inst_iterator ii; for (ii = inst_begin(procf); ii!=inst_end(procf); ii++) { Instruction &i = *ii; CallSite cs(&i); if (cs.getInstruction()) { // Candidate for a replacement Function *oldfun = cs.getCalledFunction(); if (oldfun!=NULL && !oldfun->isDeclaration()) { std::string name = oldfun->getName(); // === Write === if (!strcmp(name.c_str(), wFunName.c_str())) { CallInfo *info = new CallInfo(); info->oldcall = dyn_cast<CallInst>(cs.getInstruction()); MSG(" Checking adress : "); // Retrieve the adress argument by executing // the appropriated piece of code SCJit *scjit = new SCJit(this->llvmMod, this->elab); Process *irProc = this->elab->getProcess(proc); scjit->setCurrentProcess(irProc); bool jitErr = false; info->addrArg = cs.getArgument(1); int value = scjit->jitInt(procf, info->oldcall, info->addrArg, &jitErr); if(jitErr) { std::cout << " cannot get the address value!" << std::endl; } else { oss.str(""); oss << std::hex << value; MSG("0x"+oss.str()+"\n"); basic::addr_t a = static_cast<basic::addr_t>(value); // Checking address alignment if(value % sizeof(basic::data_t)) { std::cerr << " unaligned write : " << std::hex << value << std::endl; abort(); } // Retreive the target module using the address targetMod = getTargetModule(initiatorMod, a); // Save informations to build a new call later FunctionType *writeFunType = this->basicWriteFun->getFunctionType(); info->targetType = writeFunType->getParamType(0); LLVMContext &context = getGlobalContext(); IntegerType *intType; if (this->is64Bit) { intType = Type::getInt64Ty(context); } else { intType = Type::getInt32Ty(context); } info->targetModVal = ConstantInt::getSigned(intType, reinterpret_cast<intptr_t>(targetMod)); info->dataArg = cs.getArgument(2); work->push_back(info); } } else // === Read === if (!strcmp(name.c_str(), rFunName.c_str())) { // Not yet supported } } } } // Before //procf->dump(); // Replace calls std::vector<CallInfo*>::iterator it; for (it = work->begin(); it!=work->end(); ++it) { CallInfo *i = *it; LLVMContext &context = getGlobalContext(); FunctionType *writeFunType = this->writeFun->getFunctionType(); IntegerType *i64 = Type::getInt64Ty(context); // Get a pointer to the target module basic::target_module_base *tmb = dynamic_cast<basic::target_module_base*>(targetMod); Value *ptr = ConstantInt::getSigned(i64, reinterpret_cast<intptr_t>(tmb)); IntToPtrInst *modPtr = new IntToPtrInst(ptr, writeFunType->getParamType(0), "myitp", i->oldcall); // Get a the address value LoadInst *addr = new LoadInst(i->addrArg, "", i->oldcall); // Create the new call Value *args[] = {modPtr, addr, i->dataArg}; i->newcall = CallInst::Create(this->writeFun, ArrayRef<Value*>(args, 3)); // Replace the old call BasicBlock::iterator it(i->oldcall); ReplaceInstWithInst(i->oldcall->getParent()->getInstList(), it, i->newcall); i->oldcall->replaceAllUsesWith(i->newcall); // Inline the new call DataLayout *td = new DataLayout(this->llvmMod); InlineFunctionInfo ifi(NULL, td); bool success = InlineFunction(i->newcall, ifi); if(!success) { MSG(" The call cannot be inlined (it's not an error :D)"); } MSG(" Call optimized (^_-)\n"); callOptCounter++; } //std::cout << "==================================\n"; // Run preloaded passes on the function to propagate constants funPassManager->run(*procf); // After //procf->dump(); // Check if the function is corrupt verifyFunction(*procf); this->engine->recompileAndRelinkFunction(procf); }
/// inlineFuctions - Walk all call sites in all functions supplied by /// client. Inline as many call sites as possible. Delete completely /// inlined functions. void BasicInlinerImpl::inlineFunctions() { // Scan through and identify all call sites ahead of time so that we only // inline call sites in the original functions, not call sites that result // from inlining other functions. std::vector<CallSite> CallSites; for (std::vector<Function *>::iterator FI = Functions.begin(), FE = Functions.end(); FI != FE; ++FI) { Function *F = *FI; for (Function::iterator BB = F->begin(), E = F->end(); BB != E; ++BB) for (BasicBlock::iterator I = BB->begin(); I != BB->end(); ++I) { CallSite CS(cast<Value>(I)); if (CS && CS.getCalledFunction() && !CS.getCalledFunction()->isDeclaration()) CallSites.push_back(CS); } } DEBUG(dbgs() << ": " << CallSites.size() << " call sites.\n"); // Inline call sites. bool Changed = false; do { Changed = false; for (unsigned index = 0; index != CallSites.size() && !CallSites.empty(); ++index) { CallSite CS = CallSites[index]; if (Function *Callee = CS.getCalledFunction()) { // Eliminate calls that are never inlinable. if (Callee->isDeclaration() || CS.getInstruction()->getParent()->getParent() == Callee) { CallSites.erase(CallSites.begin() + index); --index; continue; } InlineCost IC = CA.getInlineCost(CS, NeverInline); if (IC.isAlways()) { DEBUG(dbgs() << " Inlining: cost=always" <<", call: " << *CS.getInstruction()); } else if (IC.isNever()) { DEBUG(dbgs() << " NOT Inlining: cost=never" <<", call: " << *CS.getInstruction()); continue; } else { int Cost = IC.getValue(); if (Cost >= (int) BasicInlineThreshold) { DEBUG(dbgs() << " NOT Inlining: cost = " << Cost << ", call: " << *CS.getInstruction()); continue; } else { DEBUG(dbgs() << " Inlining: cost = " << Cost << ", call: " << *CS.getInstruction()); } } // Inline InlineFunctionInfo IFI(0, TD); if (InlineFunction(CS, IFI)) { Callee->removeDeadConstantUsers(); if (Callee->isDefTriviallyDead()) DeadFunctions.insert(Callee); Changed = true; CallSites.erase(CallSites.begin() + index); --index; } } } } while (Changed); // Remove completely inlined functions from module. for(SmallPtrSet<Function *, 8>::iterator I = DeadFunctions.begin(), E = DeadFunctions.end(); I != E; ++I) { Function *D = *I; Module *M = D->getParent(); M->getFunctionList().remove(D); } }
void JitCodeSpecializer::_inline_function_calls(SpecializationContext& context) const { // This method implements the main code fusion functionality. // It works as follows: // Throughout the fusion process, a working list of call sites (i.e., function calls) is maintained. // This queue is initialized with all call instructions from the initial root function. // Each call site is then handled in one of the following ways: // - Direct Calls: // Direct calls explicitly encode the name of the called function. If a function implementation can be located in // the JitRepository repository by that name the function is inlined (i.e., the call instruction is replaced with // the function body from the repository. Calls to functions not available in the repository (e.g., calls into the // C++ standard library) require a corresponding function declaration to be inserted into the module. // This is necessary to avoid any unresolved function calls, which would invalidate the module and cause the // just-in-time compiler to reject it. With an appropriate declaration, however, the just-in-time compiler is able // to locate the machine code of these functions in the Hyrise binary when compiling and linking the module. // - Indirect Calls // Indirect calls do not reference their called function by name. Instead, the address of the target function is // either loaded from memory or computed. The code specialization unit analyzes the instructions surrounding the // call and tries to infer the name of the called function. If this succeeds, the call is handled like a regular // direct call. If not, no specialization of the call can be performed. // In this case, the target address computed for the call at runtime will point to the correct machine code in the // Hyrise binary and the pipeline will still execute successfully. // // Whenever a call instruction is replaced by the corresponding function body, the inlining algorithm reports back // any new call sites encountered in the process. These are pushed to the end of the working queue. Once this queue // is empty, the operator fusion process is completed. std::queue<llvm::CallSite> call_sites; // Initialize the call queue with all call and invoke instructions from the root function. _visit<llvm::CallInst>(*context.root_function, [&](llvm::CallInst& inst) { call_sites.push(llvm::CallSite(&inst)); }); _visit<llvm::InvokeInst>(*context.root_function, [&](llvm::InvokeInst& inst) { call_sites.push(llvm::CallSite(&inst)); }); while (!call_sites.empty()) { auto& call_site = call_sites.front(); // Resolve indirect (virtual) function calls if (call_site.isIndirectCall()) { const auto called_value = call_site.getCalledValue(); // Get the runtime location of the called function (i.e., the compiled machine code of the function) const auto called_runtime_value = std::dynamic_pointer_cast<const JitKnownRuntimePointer>(GetRuntimePointerForValue(called_value, context)); if (called_runtime_value && called_runtime_value->is_valid()) { // LLVM implements virtual function calls via virtual function tables // (see https://en.wikipedia.org/wiki/Virtual_method_table). // The resolution of virtual calls starts with a pointer to the object that the virtual call should be performed // on. This object contains a pointer to its vtable (usually at offset 0). The vtable contains a list of // function pointers (one for each virtual function). // In the LLVM bitcode, a virtual call is resolved through a number of LLVM pointer operations: // - a load instruction to dereference the vtable pointer in the object // - a getelementptr instruction to select the correct virtual function in the vtable // - another load instruction to dereference the virtual function pointer from the table // When analyzing a virtual call, the code specializer works backwards starting from the pointer to the called // function (called_runtime_value). // Moving "up" one level (undoing one load operation) yields the pointer to the function pointer in the // vtable. The total_offset of this pointer corresponds to the index in the vtable that is used for the virtual // call. // Moving "up" another level yields the pointer to the object that the virtual function is called on. Using RTTI // the class name of the object can be determined. // These two pieces of information (the class name and the vtable index) are sufficient to unambiguously // identify the called virtual function and locate it in the bitcode repository. // Determine the vtable index and class name of the virtual call const auto vtable_index = called_runtime_value->up().total_offset() / context.module->getDataLayout().getPointerSize(); const auto instance = reinterpret_cast<JitRTTIHelper*>(called_runtime_value->up().up().base().address()); const auto class_name = typeid(*instance).name(); // If the called function can be located in the repository, the virtual call is replaced by a direct call to // that function. if (const auto repo_function = _repository.get_vtable_entry(class_name, vtable_index)) { call_site.setCalledFunction(repo_function); } } else { // The virtual call could not be resolved. There is nothing we can inline so we move on. call_sites.pop(); continue; } } auto function = call_site.getCalledFunction(); // ignore invalid functions if (!function) { call_sites.pop(); continue; } const auto function_name = function->getName().str(); const auto function_has_opossum_namespace = boost::starts_with(function_name, "_ZNK7opossum") || boost::starts_with(function_name, "_ZN7opossum"); // A note about "__clang_call_terminate": // __clang_call_terminate is generated / used internally by clang to call the std::terminate function when exception // handling fails. For some unknown reason this function cannot be resolved in the Hyrise binary when jit-compiling // bitcode that uses the function. The function is, however, present in the bitcode repository. // We thus always inline this function from the repository. // All function that are not in the opossum:: namespace are not considered for inlining. Instead, a function // declaration (without a function body) is created. if (!function_has_opossum_namespace && function_name != "__clang_call_terminate") { context.llvm_value_map[function] = _create_function_declaration(context, *function, function->getName()); call_sites.pop(); continue; } // Determine whether the first function argument is a pointer/reference to an object, but the runtime location // for this object cannot be determined. // This is the case for member functions that are called within a loop body. These functions may be called on // different objects in different loop iterations. // If two specialization passes are performed, these functions should be inlined after loop unrolling has been // performed (i.e., during the second pass). auto first_argument = call_site.arg_begin(); auto first_argument_cannot_be_resolved = first_argument->get()->getType()->isPointerTy() && !GetRuntimePointerForValue(first_argument->get(), context)->is_valid(); if (first_argument_cannot_be_resolved && function_name != "__clang_call_terminate") { call_sites.pop(); continue; } // We create a mapping from values in the source module to values in the target module. // This mapping is passed to LLVM's cloning function and ensures that all references to other functions, global // variables, and function arguments refer to values in the target module and NOT in the source module. // This way the module stays self-contained and valid. context.llvm_value_map.clear(); // Map called functions _visit<const llvm::Function>(*function, [&](const auto& fn) { if (fn.isDeclaration() && !context.llvm_value_map.count(&fn)) { context.llvm_value_map[&fn] = _create_function_declaration(context, fn, fn.getName()); } }); // Map global variables _visit<const llvm::GlobalVariable>(*function, [&](auto& global) { if (!context.llvm_value_map.count(&global)) { context.llvm_value_map[&global] = _clone_global_variable(context, global); } }); // Map function arguments auto function_arg = function->arg_begin(); auto call_arg = call_site.arg_begin(); for (; function_arg != function->arg_end() && call_arg != call_site.arg_end(); ++function_arg, ++call_arg) { context.llvm_value_map[function_arg] = call_arg->get(); } // Instruct LLVM to perform the function inlining and push all new call sites to the working queue llvm::InlineFunctionInfo info; if (InlineFunction(call_site, info, nullptr, false, nullptr, context)) { for (const auto& new_call_site : info.InlinedCallSites) { call_sites.push(new_call_site); } } // clear runtime_value_map to allow multiple inlining of same function auto runtime_this = context.runtime_value_map[context.root_function->arg_begin()]; context.runtime_value_map.clear(); context.runtime_value_map[context.root_function->arg_begin()] = runtime_this; call_sites.pop(); } }