15a440378SAlex Zinenko //===- ExecutionEngine.cpp - MLIR Execution engine and utils --------------===// 25a440378SAlex Zinenko // 330857107SMehdi Amini // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 456222a06SMehdi Amini // See https://llvm.org/LICENSE.txt for license information. 556222a06SMehdi Amini // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 65a440378SAlex Zinenko // 756222a06SMehdi Amini //===----------------------------------------------------------------------===// 85a440378SAlex Zinenko // 95a440378SAlex Zinenko // This file implements the execution engine for MLIR modules based on LLVM Orc 105a440378SAlex Zinenko // JIT engine. 115a440378SAlex Zinenko // 125a440378SAlex Zinenko //===----------------------------------------------------------------------===// 135a440378SAlex Zinenko #include "mlir/ExecutionEngine/ExecutionEngine.h" 145a440378SAlex Zinenko #include "mlir/IR/Function.h" 155a440378SAlex Zinenko #include "mlir/IR/Module.h" 1606e81010SJacques Pienaar #include "mlir/Support/FileUtilities.h" 175a440378SAlex Zinenko #include "mlir/Target/LLVMIR.h" 185a440378SAlex Zinenko 19fe3594f7SNicolas Vasilache #include "llvm/Bitcode/BitcodeReader.h" 20fe3594f7SNicolas Vasilache #include "llvm/Bitcode/BitcodeWriter.h" 21c3f0ed7bSRiver Riddle #include "llvm/ExecutionEngine/JITEventListener.h" 22fe3594f7SNicolas Vasilache #include "llvm/ExecutionEngine/ObjectCache.h" 235a440378SAlex Zinenko #include "llvm/ExecutionEngine/Orc/CompileUtils.h" 245a440378SAlex Zinenko #include "llvm/ExecutionEngine/Orc/ExecutionUtils.h" 255a440378SAlex Zinenko #include "llvm/ExecutionEngine/Orc/IRCompileLayer.h" 268093f17aSAlex Zinenko #include "llvm/ExecutionEngine/Orc/IRTransformLayer.h" 275a440378SAlex Zinenko #include "llvm/ExecutionEngine/Orc/JITTargetMachineBuilder.h" 285a440378SAlex Zinenko #include "llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h" 295a440378SAlex Zinenko #include "llvm/ExecutionEngine/SectionMemoryManager.h" 305a440378SAlex Zinenko #include "llvm/IR/IRBuilder.h" 3153bb528bSMehdi Amini #include "llvm/Support/Debug.h" 325a440378SAlex Zinenko #include "llvm/Support/Error.h" 335a440378SAlex Zinenko #include "llvm/Support/TargetRegistry.h" 3406e81010SJacques Pienaar #include "llvm/Support/ToolOutputFile.h" 355a440378SAlex Zinenko 3653bb528bSMehdi Amini #define DEBUG_TYPE "execution-engine" 3753bb528bSMehdi Amini 385a440378SAlex Zinenko using namespace mlir; 39fe3594f7SNicolas Vasilache using llvm::dbgs; 405a440378SAlex Zinenko using llvm::Error; 41fe3594f7SNicolas Vasilache using llvm::errs; 425a440378SAlex Zinenko using llvm::Expected; 43fe3594f7SNicolas Vasilache using llvm::LLVMContext; 44fe3594f7SNicolas Vasilache using llvm::MemoryBuffer; 45fe3594f7SNicolas Vasilache using llvm::MemoryBufferRef; 46fe3594f7SNicolas Vasilache using llvm::Module; 47fe3594f7SNicolas Vasilache using llvm::SectionMemoryManager; 48fe3594f7SNicolas Vasilache using llvm::StringError; 49fe3594f7SNicolas Vasilache using llvm::Triple; 50fe3594f7SNicolas Vasilache using llvm::orc::DynamicLibrarySearchGenerator; 51fe3594f7SNicolas Vasilache using llvm::orc::ExecutionSession; 52fe3594f7SNicolas Vasilache using llvm::orc::IRCompileLayer; 53fe3594f7SNicolas Vasilache using llvm::orc::JITTargetMachineBuilder; 54fe3594f7SNicolas Vasilache using llvm::orc::RTDyldObjectLinkingLayer; 55fe3594f7SNicolas Vasilache using llvm::orc::ThreadSafeModule; 56fe3594f7SNicolas Vasilache using llvm::orc::TMOwningSimpleCompiler; 576aa5cc8bSNicolas Vasilache 582666b973SRiver Riddle /// Wrap a string into an llvm::StringError. 592666b973SRiver Riddle static Error make_string_error(const Twine &message) { 60fe3594f7SNicolas Vasilache return llvm::make_error<StringError>(message.str(), 615a440378SAlex Zinenko llvm::inconvertibleErrorCode()); 625a440378SAlex Zinenko } 635a440378SAlex Zinenko 64fe3594f7SNicolas Vasilache void SimpleObjectCache::notifyObjectCompiled(const Module *M, 65fe3594f7SNicolas Vasilache MemoryBufferRef ObjBuffer) { 6606e81010SJacques Pienaar cachedObjects[M->getModuleIdentifier()] = MemoryBuffer::getMemBufferCopy( 67fe3594f7SNicolas Vasilache ObjBuffer.getBuffer(), ObjBuffer.getBufferIdentifier()); 68fe3594f7SNicolas Vasilache } 69fe3594f7SNicolas Vasilache 70fe3594f7SNicolas Vasilache std::unique_ptr<MemoryBuffer> SimpleObjectCache::getObject(const Module *M) { 7106e81010SJacques Pienaar auto I = cachedObjects.find(M->getModuleIdentifier()); 7206e81010SJacques Pienaar if (I == cachedObjects.end()) { 7353bb528bSMehdi Amini LLVM_DEBUG(dbgs() << "No object for " << M->getModuleIdentifier() 7453bb528bSMehdi Amini << " in cache. Compiling.\n"); 75fe3594f7SNicolas Vasilache return nullptr; 76fe3594f7SNicolas Vasilache } 7753bb528bSMehdi Amini LLVM_DEBUG(dbgs() << "Object for " << M->getModuleIdentifier() 7853bb528bSMehdi Amini << " loaded from cache.\n"); 79fe3594f7SNicolas Vasilache return MemoryBuffer::getMemBuffer(I->second->getMemBufferRef()); 80fe3594f7SNicolas Vasilache } 81fe3594f7SNicolas Vasilache 824562e389SRiver Riddle void SimpleObjectCache::dumpToObjectFile(StringRef outputFilename) { 8306e81010SJacques Pienaar // Set up the output file. 8406e81010SJacques Pienaar std::string errorMessage; 8506e81010SJacques Pienaar auto file = openOutputFile(outputFilename, &errorMessage); 8606e81010SJacques Pienaar if (!file) { 8706e81010SJacques Pienaar llvm::errs() << errorMessage << "\n"; 8806e81010SJacques Pienaar return; 8906e81010SJacques Pienaar } 9006e81010SJacques Pienaar 9106e81010SJacques Pienaar // Dump the object generated for a single module to the output file. 9206e81010SJacques Pienaar assert(cachedObjects.size() == 1 && "Expected only one object entry."); 9306e81010SJacques Pienaar auto &cachedObject = cachedObjects.begin()->second; 9406e81010SJacques Pienaar file->os() << cachedObject->getBuffer(); 9506e81010SJacques Pienaar file->keep(); 9606e81010SJacques Pienaar } 9706e81010SJacques Pienaar 984562e389SRiver Riddle void ExecutionEngine::dumpToObjectFile(StringRef filename) { 9906e81010SJacques Pienaar cache->dumpToObjectFile(filename); 10006e81010SJacques Pienaar } 10106e81010SJacques Pienaar 1025a440378SAlex Zinenko // Setup LLVM target triple from the current machine. 103fe3594f7SNicolas Vasilache bool ExecutionEngine::setupTargetTriple(Module *llvmModule) { 1045a440378SAlex Zinenko // Setup the machine properties from the current architecture. 1055a440378SAlex Zinenko auto targetTriple = llvm::sys::getDefaultTargetTriple(); 1065a440378SAlex Zinenko std::string errorMessage; 1075a440378SAlex Zinenko auto target = llvm::TargetRegistry::lookupTarget(targetTriple, errorMessage); 1085a440378SAlex Zinenko if (!target) { 109fe3594f7SNicolas Vasilache errs() << "NO target: " << errorMessage << "\n"; 1105a440378SAlex Zinenko return true; 1115a440378SAlex Zinenko } 112d732aaf2SMLIR Team std::unique_ptr<llvm::TargetMachine> machine( 113d732aaf2SMLIR Team target->createTargetMachine(targetTriple, "generic", "", {}, {})); 1145a440378SAlex Zinenko llvmModule->setDataLayout(machine->createDataLayout()); 1155a440378SAlex Zinenko llvmModule->setTargetTriple(targetTriple); 1165a440378SAlex Zinenko return false; 1175a440378SAlex Zinenko } 1185a440378SAlex Zinenko 1195a440378SAlex Zinenko static std::string makePackedFunctionName(StringRef name) { 1205a440378SAlex Zinenko return "_mlir_" + name.str(); 1215a440378SAlex Zinenko } 1225a440378SAlex Zinenko 1235a440378SAlex Zinenko // For each function in the LLVM module, define an interface function that wraps 1245a440378SAlex Zinenko // all the arguments of the original function and all its results into an i8** 1255a440378SAlex Zinenko // pointer to provide a unified invocation interface. 126df186507SBenjamin Kramer static void packFunctionArguments(Module *module) { 1275a440378SAlex Zinenko auto &ctx = module->getContext(); 1285a440378SAlex Zinenko llvm::IRBuilder<> builder(ctx); 1294562e389SRiver Riddle DenseSet<llvm::Function *> interfaceFunctions; 1305a440378SAlex Zinenko for (auto &func : module->getFunctionList()) { 1315a440378SAlex Zinenko if (func.isDeclaration()) { 1325a440378SAlex Zinenko continue; 1335a440378SAlex Zinenko } 1345a440378SAlex Zinenko if (interfaceFunctions.count(&func)) { 1355a440378SAlex Zinenko continue; 1365a440378SAlex Zinenko } 1375a440378SAlex Zinenko 1385a440378SAlex Zinenko // Given a function `foo(<...>)`, define the interface function 1395a440378SAlex Zinenko // `mlir_foo(i8**)`. 1405a440378SAlex Zinenko auto newType = llvm::FunctionType::get( 1415a440378SAlex Zinenko builder.getVoidTy(), builder.getInt8PtrTy()->getPointerTo(), 1425a440378SAlex Zinenko /*isVarArg=*/false); 1435a440378SAlex Zinenko auto newName = makePackedFunctionName(func.getName()); 144c46b0feaSRiver Riddle auto funcCst = module->getOrInsertFunction(newName, newType); 1454562e389SRiver Riddle llvm::Function *interfaceFunc = cast<llvm::Function>(funcCst.getCallee()); 1465a440378SAlex Zinenko interfaceFunctions.insert(interfaceFunc); 1475a440378SAlex Zinenko 1485a440378SAlex Zinenko // Extract the arguments from the type-erased argument list and cast them to 1495a440378SAlex Zinenko // the proper types. 1505a440378SAlex Zinenko auto bb = llvm::BasicBlock::Create(ctx); 1515a440378SAlex Zinenko bb->insertInto(interfaceFunc); 1525a440378SAlex Zinenko builder.SetInsertPoint(bb); 1535a440378SAlex Zinenko llvm::Value *argList = interfaceFunc->arg_begin(); 1544562e389SRiver Riddle SmallVector<llvm::Value *, 8> args; 1555a440378SAlex Zinenko args.reserve(llvm::size(func.args())); 1565a440378SAlex Zinenko for (auto &indexedArg : llvm::enumerate(func.args())) { 1575a440378SAlex Zinenko llvm::Value *argIndex = llvm::Constant::getIntegerValue( 1584562e389SRiver Riddle builder.getInt64Ty(), APInt(64, indexedArg.index())); 1595a440378SAlex Zinenko llvm::Value *argPtrPtr = builder.CreateGEP(argList, argIndex); 1605a440378SAlex Zinenko llvm::Value *argPtr = builder.CreateLoad(argPtrPtr); 1615a440378SAlex Zinenko argPtr = builder.CreateBitCast( 1625a440378SAlex Zinenko argPtr, indexedArg.value().getType()->getPointerTo()); 1635a440378SAlex Zinenko llvm::Value *arg = builder.CreateLoad(argPtr); 1645a440378SAlex Zinenko args.push_back(arg); 1655a440378SAlex Zinenko } 1665a440378SAlex Zinenko 1675a440378SAlex Zinenko // Call the implementation function with the extracted arguments. 1685a440378SAlex Zinenko llvm::Value *result = builder.CreateCall(&func, args); 1695a440378SAlex Zinenko 1705a440378SAlex Zinenko // Assuming the result is one value, potentially of type `void`. 1715a440378SAlex Zinenko if (!result->getType()->isVoidTy()) { 1725a440378SAlex Zinenko llvm::Value *retIndex = llvm::Constant::getIntegerValue( 1734562e389SRiver Riddle builder.getInt64Ty(), APInt(64, llvm::size(func.args()))); 1745a440378SAlex Zinenko llvm::Value *retPtrPtr = builder.CreateGEP(argList, retIndex); 1755a440378SAlex Zinenko llvm::Value *retPtr = builder.CreateLoad(retPtrPtr); 1765a440378SAlex Zinenko retPtr = builder.CreateBitCast(retPtr, result->getType()->getPointerTo()); 1775a440378SAlex Zinenko builder.CreateStore(result, retPtr); 1785a440378SAlex Zinenko } 1795a440378SAlex Zinenko 1805a440378SAlex Zinenko // The interface function returns void. 1815a440378SAlex Zinenko builder.CreateRetVoid(); 1825a440378SAlex Zinenko } 1835a440378SAlex Zinenko } 1845a440378SAlex Zinenko 18506e81010SJacques Pienaar ExecutionEngine::ExecutionEngine(bool enableObjectCache) 186c3f0ed7bSRiver Riddle : cache(enableObjectCache ? nullptr : new SimpleObjectCache()), 187c3f0ed7bSRiver Riddle gdbListener(llvm::JITEventListener::createGDBRegistrationListener()) {} 18806e81010SJacques Pienaar 18906e81010SJacques Pienaar Expected<std::unique_ptr<ExecutionEngine>> ExecutionEngine::create( 19006e81010SJacques Pienaar ModuleOp m, std::function<Error(llvm::Module *)> transformer, 191713ab0ddSUday Bondhugula Optional<llvm::CodeGenOpt::Level> jitCodeGenOptLevel, 19206e81010SJacques Pienaar ArrayRef<StringRef> sharedLibPaths, bool enableObjectCache) { 19306e81010SJacques Pienaar auto engine = std::make_unique<ExecutionEngine>(enableObjectCache); 1945a440378SAlex Zinenko 195fe3594f7SNicolas Vasilache std::unique_ptr<llvm::LLVMContext> ctx(new llvm::LLVMContext); 196206e55ccSRiver Riddle auto llvmModule = translateModuleToLLVMIR(m); 1975a440378SAlex Zinenko if (!llvmModule) 1985a440378SAlex Zinenko return make_string_error("could not convert to LLVM IR"); 1995a440378SAlex Zinenko // FIXME: the triple should be passed to the translation or dialect conversion 2005a440378SAlex Zinenko // instead of this. Currently, the LLVM module created above has no triple 2015a440378SAlex Zinenko // associated with it. 2025a440378SAlex Zinenko setupTargetTriple(llvmModule.get()); 2035a440378SAlex Zinenko packFunctionArguments(llvmModule.get()); 2045a440378SAlex Zinenko 205fe3594f7SNicolas Vasilache // Clone module in a new LLVMContext since translateModuleToLLVMIR buries 206fe3594f7SNicolas Vasilache // ownership too deeply. 207fe3594f7SNicolas Vasilache // TODO(zinenko): Reevaluate model of ownership of LLVMContext in LLVMDialect. 208fe3594f7SNicolas Vasilache SmallVector<char, 1> buffer; 209fe3594f7SNicolas Vasilache { 210fe3594f7SNicolas Vasilache llvm::raw_svector_ostream os(buffer); 211fe3594f7SNicolas Vasilache WriteBitcodeToFile(*llvmModule, os); 212fe3594f7SNicolas Vasilache } 2134562e389SRiver Riddle llvm::MemoryBufferRef bufferRef(StringRef(buffer.data(), buffer.size()), 214fe3594f7SNicolas Vasilache "cloned module buffer"); 215fe3594f7SNicolas Vasilache auto expectedModule = parseBitcodeFile(bufferRef, *ctx); 216fe3594f7SNicolas Vasilache if (!expectedModule) 217fe3594f7SNicolas Vasilache return expectedModule.takeError(); 218fe3594f7SNicolas Vasilache std::unique_ptr<Module> deserModule = std::move(*expectedModule); 2196d60d869SRiver Riddle auto dataLayout = deserModule->getDataLayout(); 220fe3594f7SNicolas Vasilache 221fe3594f7SNicolas Vasilache // Callback to create the object layer with symbol resolution to current 222fe3594f7SNicolas Vasilache // process and dynamically linked libraries. 223fe3594f7SNicolas Vasilache auto objectLinkingLayerCreator = [&](ExecutionSession &session, 224fe3594f7SNicolas Vasilache const Triple &TT) { 225fe3594f7SNicolas Vasilache auto objectLayer = std::make_unique<RTDyldObjectLinkingLayer>( 226fe3594f7SNicolas Vasilache session, []() { return std::make_unique<SectionMemoryManager>(); }); 227c3f0ed7bSRiver Riddle objectLayer->setNotifyLoaded( 228c3f0ed7bSRiver Riddle [engine = engine.get()]( 229c3f0ed7bSRiver Riddle llvm::orc::VModuleKey, const llvm::object::ObjectFile &object, 230c3f0ed7bSRiver Riddle const llvm::RuntimeDyld::LoadedObjectInfo &objectInfo) { 231c3f0ed7bSRiver Riddle uint64_t key = static_cast<uint64_t>( 232c3f0ed7bSRiver Riddle reinterpret_cast<uintptr_t>(object.getData().data())); 233c3f0ed7bSRiver Riddle engine->gdbListener->notifyObjectLoaded(key, object, objectInfo); 234c3f0ed7bSRiver Riddle }); 235fe3594f7SNicolas Vasilache 236fe3594f7SNicolas Vasilache // Resolve symbols from shared libraries. 237fe3594f7SNicolas Vasilache for (auto libPath : sharedLibPaths) { 238fe3594f7SNicolas Vasilache auto mb = llvm::MemoryBuffer::getFile(libPath); 239fe3594f7SNicolas Vasilache if (!mb) { 240fe3594f7SNicolas Vasilache errs() << "Fail to create MemoryBuffer for: " << libPath << "\n"; 241fe3594f7SNicolas Vasilache continue; 242fe3594f7SNicolas Vasilache } 243a7504226SRiver Riddle auto &JD = session.createBareJITDylib(std::string(libPath)); 244fe3594f7SNicolas Vasilache auto loaded = DynamicLibrarySearchGenerator::Load( 245fe3594f7SNicolas Vasilache libPath.data(), dataLayout.getGlobalPrefix()); 246fe3594f7SNicolas Vasilache if (!loaded) { 247e38fe4a7SChristian Sigg errs() << "Could not load " << libPath << ":\n " << loaded.takeError() 248e38fe4a7SChristian Sigg << "\n"; 249fe3594f7SNicolas Vasilache continue; 250fe3594f7SNicolas Vasilache } 251fe3594f7SNicolas Vasilache JD.addGenerator(std::move(*loaded)); 252fe3594f7SNicolas Vasilache cantFail(objectLayer->add(JD, std::move(mb.get()))); 253fe3594f7SNicolas Vasilache } 254fe3594f7SNicolas Vasilache 255fe3594f7SNicolas Vasilache return objectLayer; 256fe3594f7SNicolas Vasilache }; 257fe3594f7SNicolas Vasilache 258fe3594f7SNicolas Vasilache // Callback to inspect the cache and recompile on demand. This follows Lang's 259fe3594f7SNicolas Vasilache // LLJITWithObjectCache example. 260fe3594f7SNicolas Vasilache auto compileFunctionCreator = [&](JITTargetMachineBuilder JTMB) 2617984b474SAlex Zinenko -> Expected<std::unique_ptr<IRCompileLayer::IRCompiler>> { 262713ab0ddSUday Bondhugula if (jitCodeGenOptLevel) 263713ab0ddSUday Bondhugula JTMB.setCodeGenOptLevel(jitCodeGenOptLevel.getValue()); 264fe3594f7SNicolas Vasilache auto TM = JTMB.createTargetMachine(); 265fe3594f7SNicolas Vasilache if (!TM) 266fe3594f7SNicolas Vasilache return TM.takeError(); 2677984b474SAlex Zinenko return std::make_unique<TMOwningSimpleCompiler>(std::move(*TM), 2687984b474SAlex Zinenko engine->cache.get()); 269fe3594f7SNicolas Vasilache }; 270fe3594f7SNicolas Vasilache 271fe3594f7SNicolas Vasilache // Create the LLJIT by calling the LLJITBuilder with 2 callbacks. 272fe3594f7SNicolas Vasilache auto jit = 273fe3594f7SNicolas Vasilache cantFail(llvm::orc::LLJITBuilder() 274fe3594f7SNicolas Vasilache .setCompileFunctionCreator(compileFunctionCreator) 275fe3594f7SNicolas Vasilache .setObjectLinkingLayerCreator(objectLinkingLayerCreator) 276fe3594f7SNicolas Vasilache .create()); 277fe3594f7SNicolas Vasilache 278fe3594f7SNicolas Vasilache // Add a ThreadSafemodule to the engine and return. 279fe3594f7SNicolas Vasilache ThreadSafeModule tsm(std::move(deserModule), std::move(ctx)); 280cf26e5faSNicolas Vasilache if (transformer) 281cf26e5faSNicolas Vasilache cantFail(tsm.withModuleDo( 282cf26e5faSNicolas Vasilache [&](llvm::Module &module) { return transformer(&module); })); 283fe3594f7SNicolas Vasilache cantFail(jit->addIRModule(std::move(tsm))); 284fe3594f7SNicolas Vasilache engine->jit = std::move(jit); 2855a440378SAlex Zinenko 2866d60d869SRiver Riddle // Resolve symbols that are statically linked in the current process. 2876d60d869SRiver Riddle llvm::orc::JITDylib &mainJD = engine->jit->getMainJITDylib(); 2886d60d869SRiver Riddle mainJD.addGenerator( 2896d60d869SRiver Riddle cantFail(DynamicLibrarySearchGenerator::GetForCurrentProcess( 2906d60d869SRiver Riddle dataLayout.getGlobalPrefix()))); 2916d60d869SRiver Riddle 292e7111fd6SJacques Pienaar return std::move(engine); 2935a440378SAlex Zinenko } 2945a440378SAlex Zinenko 2955a440378SAlex Zinenko Expected<void (*)(void **)> ExecutionEngine::lookup(StringRef name) const { 2965a440378SAlex Zinenko auto expectedSymbol = jit->lookup(makePackedFunctionName(name)); 297*d7fbfbb1SAlex Zinenko 298*d7fbfbb1SAlex Zinenko // JIT lookup may return an Error referring to strings stored internally by 299*d7fbfbb1SAlex Zinenko // the JIT. If the Error outlives the ExecutionEngine, it would want have a 300*d7fbfbb1SAlex Zinenko // dangling reference, which is currently caught by an assertion inside JIT 301*d7fbfbb1SAlex Zinenko // thanks to hand-rolled reference counting. Rewrap the error message into a 302*d7fbfbb1SAlex Zinenko // string before returning. Alternatively, ORC JIT should consider copying 303*d7fbfbb1SAlex Zinenko // the string into the error message. 304*d7fbfbb1SAlex Zinenko if (!expectedSymbol) { 305*d7fbfbb1SAlex Zinenko std::string errorMessage; 306*d7fbfbb1SAlex Zinenko llvm::raw_string_ostream os(errorMessage); 307*d7fbfbb1SAlex Zinenko llvm::handleAllErrors(expectedSymbol.takeError(), 308*d7fbfbb1SAlex Zinenko [&os](llvm::ErrorInfoBase &ei) { ei.log(os); }); 309*d7fbfbb1SAlex Zinenko return make_string_error(os.str()); 310*d7fbfbb1SAlex Zinenko } 311*d7fbfbb1SAlex Zinenko 3125a440378SAlex Zinenko auto rawFPtr = expectedSymbol->getAddress(); 3135a440378SAlex Zinenko auto fptr = reinterpret_cast<void (*)(void **)>(rawFPtr); 3145a440378SAlex Zinenko if (!fptr) 3155a440378SAlex Zinenko return make_string_error("looked up function is null"); 3165a440378SAlex Zinenko return fptr; 3175a440378SAlex Zinenko } 318629f5b7fSNicolas Vasilache 319fe3594f7SNicolas Vasilache Error ExecutionEngine::invoke(StringRef name, MutableArrayRef<void *> args) { 320629f5b7fSNicolas Vasilache auto expectedFPtr = lookup(name); 321629f5b7fSNicolas Vasilache if (!expectedFPtr) 322629f5b7fSNicolas Vasilache return expectedFPtr.takeError(); 323629f5b7fSNicolas Vasilache auto fptr = *expectedFPtr; 324629f5b7fSNicolas Vasilache 325629f5b7fSNicolas Vasilache (*fptr)(args.data()); 326629f5b7fSNicolas Vasilache 327fe3594f7SNicolas Vasilache return Error::success(); 328629f5b7fSNicolas Vasilache } 329