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