Beispiel #1
0
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;
}
Beispiel #2
0
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"; );
}
Beispiel #3
0
// =============================================================================
// 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;
}
Beispiel #4
0
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);
    }
}
Beispiel #5
0
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);
}
Beispiel #8
0
/// 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);
    }
}
Beispiel #9
0
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();
  }
}