19 #include "llvm/ExecutionEngine/JITEventListener.h"
20 #include "llvm/ExecutionEngine/ObjectCache.h"
21 #include "llvm/ExecutionEngine/Orc/CompileUtils.h"
22 #include "llvm/ExecutionEngine/Orc/ExecutionUtils.h"
23 #include "llvm/ExecutionEngine/Orc/IRCompileLayer.h"
24 #include "llvm/ExecutionEngine/Orc/IRTransformLayer.h"
25 #include "llvm/ExecutionEngine/Orc/JITTargetMachineBuilder.h"
26 #include "llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h"
27 #include "llvm/IR/IRBuilder.h"
28 #include "llvm/MC/TargetRegistry.h"
29 #include "llvm/Support/Debug.h"
30 #include "llvm/Support/Error.h"
31 #include "llvm/Support/ToolOutputFile.h"
32 #include "llvm/TargetParser/Host.h"
33 #include "llvm/TargetParser/SubtargetFeature.h"
35 #define DEBUG_TYPE "execution-engine"
42 using llvm::LLVMContext;
43 using llvm::MemoryBuffer;
44 using llvm::MemoryBufferRef;
46 using llvm::SectionMemoryManager;
47 using llvm::StringError;
49 using llvm::orc::DynamicLibrarySearchGenerator;
50 using llvm::orc::ExecutionSession;
51 using llvm::orc::IRCompileLayer;
52 using llvm::orc::JITTargetMachineBuilder;
53 using llvm::orc::MangleAndInterner;
54 using llvm::orc::RTDyldObjectLinkingLayer;
55 using llvm::orc::SymbolMap;
56 using llvm::orc::ThreadSafeModule;
57 using llvm::orc::TMOwningSimpleCompiler;
61 return llvm::make_error<StringError>(message.str(),
62 llvm::inconvertibleErrorCode());
66 MemoryBufferRef objBuffer) {
67 cachedObjects[m->getModuleIdentifier()] = MemoryBuffer::getMemBufferCopy(
68 objBuffer.getBuffer(), objBuffer.getBufferIdentifier());
72 auto i = cachedObjects.find(m->getModuleIdentifier());
73 if (i == cachedObjects.end()) {
74 LLVM_DEBUG(dbgs() <<
"No object for " << m->getModuleIdentifier()
75 <<
" in cache. Compiling.\n");
78 LLVM_DEBUG(dbgs() <<
"Object for " << m->getModuleIdentifier()
79 <<
" loaded from cache.\n");
80 return MemoryBuffer::getMemBuffer(i->second->getMemBufferRef());
85 std::string errorMessage;
88 llvm::errs() << errorMessage <<
"\n";
93 assert(cachedObjects.size() == 1 &&
"Expected only one object entry.");
94 auto &cachedObject = cachedObjects.begin()->second;
95 file->os() << cachedObject->getBuffer();
102 if (cache ==
nullptr) {
103 llvm::errs() <<
"cannot dump ExecutionEngine object code to file: "
104 "object cache is disabled\n";
110 if (cache->isEmpty()) {
111 for (std::string &functionName : functionNames) {
114 llvm::errs() <<
"Could not compile " << functionName <<
":\n "
115 <<
result.takeError() <<
"\n";
120 cache->dumpToObjectFile(filename);
125 auto &mainJitDylib = jit->getMainJITDylib();
126 cantFail(mainJitDylib.define(
127 absoluteSymbols(symbolMap(llvm::orc::MangleAndInterner(
128 mainJitDylib.getExecutionSession(), jit->getDataLayout())))));
132 llvm::TargetMachine *tm) {
133 llvmModule->setDataLayout(tm->createDataLayout());
134 llvmModule->setTargetTriple(tm->getTargetTriple());
138 return "_mlir_" + name.str();
145 auto &ctx = module->getContext();
146 llvm::IRBuilder<> builder(ctx);
148 for (
auto &func : module->getFunctionList()) {
149 if (func.isDeclaration()) {
152 if (interfaceFunctions.count(&func)) {
162 auto funcCst = module->getOrInsertFunction(newName, newType);
163 llvm::Function *interfaceFunc = cast<llvm::Function>(funcCst.getCallee());
164 interfaceFunctions.insert(interfaceFunc);
168 auto *bb = llvm::BasicBlock::Create(ctx);
169 bb->insertInto(interfaceFunc);
170 builder.SetInsertPoint(bb);
171 llvm::Value *argList = interfaceFunc->arg_begin();
173 args.reserve(llvm::size(func.args()));
175 llvm::Value *argIndex = llvm::Constant::getIntegerValue(
176 builder.getInt64Ty(), APInt(64, index));
177 llvm::Value *argPtrPtr =
178 builder.CreateGEP(builder.getPtrTy(), argList, argIndex);
179 llvm::Value *argPtr = builder.CreateLoad(builder.getPtrTy(), argPtrPtr);
180 llvm::Type *argTy = arg.getType();
181 llvm::Value *load = builder.CreateLoad(argTy, argPtr);
182 args.push_back(load);
186 llvm::Value *result = builder.CreateCall(&func, args);
189 if (!result->getType()->isVoidTy()) {
190 llvm::Value *retIndex = llvm::Constant::getIntegerValue(
191 builder.getInt64Ty(), APInt(64, llvm::size(func.args())));
192 llvm::Value *retPtrPtr =
193 builder.CreateGEP(builder.getPtrTy(), argList, retIndex);
194 llvm::Value *retPtr = builder.CreateLoad(builder.getPtrTy(), retPtrPtr);
195 builder.CreateStore(result, retPtr);
199 builder.CreateRetVoid();
204 bool enableGDBNotificationListener,
205 bool enablePerfNotificationListener)
208 gdbListener(enableGDBNotificationListener
209 ?
llvm::JITEventListener::createGDBRegistrationListener()
211 perfListener(nullptr) {
212 if (enablePerfNotificationListener) {
213 if (
auto *listener = llvm::JITEventListener::createPerfJITEventListener())
214 perfListener = listener;
215 else if (
auto *listener =
216 llvm::JITEventListener::createIntelJITEventListener())
217 perfListener = listener;
225 if (jit && !jit->getTargetTriple().isAArch64())
226 llvm::consumeError(jit->deinitialize(jit->getMainJITDylib()));
234 std::unique_ptr<llvm::TargetMachine> tm) {
235 auto engine = std::make_unique<ExecutionEngine>(
237 options.enablePerfNotificationListener);
240 if (
options.enableObjectDump) {
242 StringRef funcName = funcOp.getSymName();
243 engine->functionNames.push_back(funcName.str());
247 std::unique_ptr<llvm::LLVMContext> ctx(
new llvm::LLVMContext);
248 auto llvmModule =
options.llvmModuleBuilder
249 ?
options.llvmModuleBuilder(m, *ctx)
257 auto tmBuilderOrError = llvm::orc::JITTargetMachineBuilder::detectHost();
258 if (!tmBuilderOrError)
259 return tmBuilderOrError.takeError();
261 auto tmOrError = tmBuilderOrError->createTargetMachine();
263 return tmOrError.takeError();
264 tm = std::move(tmOrError.get());
275 auto dataLayout = llvmModule->getDataLayout();
280 options.sharedLibPaths, std::back_inserter(sharedLibPaths),
281 [](StringRef libPath) {
282 SmallString<256> absPath(libPath.begin(), libPath.end());
283 cantFail(llvm::errorCodeToError(llvm::sys::fs::make_absolute(absPath)));
290 llvm::StringMap<void *> exportSymbols;
294 for (
auto &libPath : sharedLibPaths) {
295 auto lib = llvm::sys::DynamicLibrary::getPermanentLibrary(
296 libPath.str().str().c_str());
301 if (!initSym || !destroySim) {
302 jitDyLibPaths.push_back(libPath);
307 initFn(exportSymbols);
310 destroyFns.push_back(destroyFn);
312 engine->destroyFns = std::move(destroyFns);
316 auto objectLinkingLayerCreator = [&](ExecutionSession &session) {
317 auto objectLayer = std::make_unique<RTDyldObjectLinkingLayer>(
318 session, [sectionMemoryMapper =
319 options.sectionMemoryMapper](
const MemoryBuffer &) {
320 return std::make_unique<SectionMemoryManager>(sectionMemoryMapper);
324 if (engine->gdbListener)
325 objectLayer->registerJITEventListener(*engine->gdbListener);
326 if (engine->perfListener)
327 objectLayer->registerJITEventListener(*engine->perfListener);
332 const llvm::Triple &targetTriple = llvmModule->getTargetTriple();
333 if (targetTriple.isOSBinFormatCOFF()) {
334 objectLayer->setOverrideObjectFlagsWithResponsibilityFlags(
true);
335 objectLayer->setAutoClaimResponsibilityForObjectSymbols(
true);
339 for (
auto &libPath : jitDyLibPaths) {
340 auto mb = llvm::MemoryBuffer::getFile(libPath);
342 errs() <<
"Failed to create MemoryBuffer for: " << libPath
343 <<
"\nError: " << mb.getError().message() <<
"\n";
346 auto &jd = session.createBareJITDylib(std::string(libPath));
347 auto loaded = DynamicLibrarySearchGenerator::Load(
348 libPath.str().c_str(), dataLayout.getGlobalPrefix());
350 errs() <<
"Could not load " << libPath <<
":\n " << loaded.takeError()
354 jd.addGenerator(std::move(*loaded));
355 cantFail(objectLayer->add(jd, std::move(mb.get())));
363 auto compileFunctionCreator = [&](JITTargetMachineBuilder jtmb)
364 ->
Expected<std::unique_ptr<IRCompileLayer::IRCompiler>> {
365 if (
options.jitCodeGenOptLevel)
366 jtmb.setCodeGenOptLevel(*
options.jitCodeGenOptLevel);
367 return std::make_unique<TMOwningSimpleCompiler>(std::move(tm),
368 engine->cache.get());
373 cantFail(llvm::orc::LLJITBuilder()
374 .setCompileFunctionCreator(compileFunctionCreator)
375 .setObjectLinkingLayerCreator(objectLinkingLayerCreator)
376 .setDataLayout(dataLayout)
380 ThreadSafeModule tsm(std::move(llvmModule), std::move(ctx));
382 cantFail(tsm.withModuleDo(
383 [&](llvm::Module &module) { return options.transformer(&module); }));
384 cantFail(jit->addIRModule(std::move(tsm)));
385 engine->jit = std::move(jit);
388 llvm::orc::JITDylib &mainJD = engine->jit->getMainJITDylib();
390 cantFail(DynamicLibrarySearchGenerator::GetForCurrentProcess(
391 dataLayout.getGlobalPrefix())));
394 auto runtimeSymbolMap = [&](llvm::orc::MangleAndInterner interner) {
395 auto symbolMap = llvm::orc::SymbolMap();
396 for (
auto &exportSymbol : exportSymbols)
397 symbolMap[interner(exportSymbol.getKey())] = {
398 llvm::orc::ExecutorAddr::fromPtr(exportSymbol.getValue()),
399 llvm::JITSymbolFlags::Exported};
402 engine->registerSymbols(runtimeSymbolMap);
403 return std::move(engine);
410 return result.takeError();
411 return reinterpret_cast<void (*)(
void **)
>(result.get());
415 auto expectedSymbol = jit->lookup(name);
423 if (!expectedSymbol) {
424 std::string errorMessage;
425 llvm::raw_string_ostream os(errorMessage);
426 llvm::handleAllErrors(expectedSymbol.takeError(),
427 [&os](llvm::ErrorInfoBase &ei) { ei.log(os); });
431 if (
void *fptr = expectedSymbol->toPtr<
void *>())
441 return expectedFPtr.takeError();
442 auto fptr = *expectedFPtr;
444 (*fptr)(args.data());
446 return Error::success();
454 if (!jit->getTargetTriple().isAArch64())
455 cantFail(jit->initialize(jit->getMainJITDylib()));
456 isInitialized =
true;
static void packFunctionArguments(Module *module)
static Error makeStringError(const Twine &message)
Wrap a string into an llvm::StringError.
static std::string makePackedFunctionName(StringRef name)
static llvm::ManagedStatic< PassManagerOptions > options
llvm::Expected< void(*)(void **)> lookupPacked(StringRef name) const
Looks up a packed-argument function wrapping the function with the given name and returns a pointer t...
void(*)(llvm::StringMap< void * > &) LibraryInitFn
Function type for init functions of shared libraries.
void(*)() LibraryDestroyFn
Function type for destroy functions of shared libraries.
void registerSymbols(llvm::function_ref< llvm::orc::SymbolMap(llvm::orc::MangleAndInterner)> symbolMap)
Register symbols with this ExecutionEngine.
llvm::Expected< void * > lookup(StringRef name) const
Looks up the original function with the given name and returns a pointer to it.
static llvm::Expected< std::unique_ptr< ExecutionEngine > > create(Operation *op, const ExecutionEngineOptions &options={}, std::unique_ptr< llvm::TargetMachine > tm=nullptr)
Creates an execution engine for the given MLIR IR.
void dumpToObjectFile(StringRef filename)
Dump object code to output file filename.
static constexpr const char *const kLibraryInitFnName
Name of init functions of shared libraries.
static Result< T > result(T &t)
Helper function to wrap an output operand when using ExecutionEngine::invoke.
static constexpr const char *const kLibraryDestroyFnName
Name of destroy functions of shared libraries.
llvm::Error invokePacked(StringRef name, MutableArrayRef< void * > args={})
Invokes the function with the given name passing it the list of opaque pointers to the actual argumen...
ExecutionEngine(bool enableObjectDump, bool enableGDBNotificationListener, bool enablePerfNotificationListener)
static void setupTargetTripleAndDataLayout(llvm::Module *llvmModule, llvm::TargetMachine *tm)
Set the target triple and the data layout for the input module based on the input TargetMachine.
void initialize()
Initialize the ExecutionEngine.
Operation is the basic unit of execution within MLIR.
Region & getRegion(unsigned index)
Returns the region held by this operation at position 'index'.
iterator_range< OpIterator > getOps()
A simple object cache following Lang's LLJITWithObjectCache example.
bool isEmpty()
Returns true if cache hasn't been populated yet.
void notifyObjectCompiled(const llvm::Module *m, llvm::MemoryBufferRef objBuffer) override
void dumpToObjectFile(StringRef filename)
Dump cached object to output file filename.
std::unique_ptr< llvm::MemoryBuffer > getObject(const llvm::Module *m) override
The OpAsmOpInterface, see OpAsmInterface.td for more details.
constexpr void enumerate(std::tuple< Tys... > &tuple, CallbackT &&callback)
Include the generated interface declarations.
std::unique_ptr< llvm::Module > translateModuleToLLVMIR(Operation *module, llvm::LLVMContext &llvmContext, llvm::StringRef name="LLVMDialectModule", bool disableVerification=false)
Translates a given LLVM dialect module into an LLVM IR module living in the given context.
std::unique_ptr< llvm::ToolOutputFile > openOutputFile(llvm::StringRef outputFilename, std::string *errorMessage=nullptr)
Open the file specified by its name for writing.
auto get(MLIRContext *context, Ts &&...params)
Helper method that injects context only if needed, this helps unify some of the attribute constructio...