1 //===- ExecutionEngine.h - MLIR Execution engine and utils -----*- C++ -*--===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This file provides a JIT-backed execution engine for MLIR modules. 10 // 11 //===----------------------------------------------------------------------===// 12 13 #ifndef MLIR_EXECUTIONENGINE_EXECUTIONENGINE_H_ 14 #define MLIR_EXECUTIONENGINE_EXECUTIONENGINE_H_ 15 16 #include "mlir/Support/LLVM.h" 17 #include "llvm/ExecutionEngine/ObjectCache.h" 18 #include "llvm/ExecutionEngine/Orc/LLJIT.h" 19 #include "llvm/ExecutionEngine/SectionMemoryManager.h" 20 #include "llvm/IR/LLVMContext.h" 21 #include "llvm/Support/Error.h" 22 23 #include <functional> 24 #include <memory> 25 26 namespace llvm { 27 template <typename T> 28 class Expected; 29 class Module; 30 class ExecutionEngine; 31 class JITEventListener; 32 class MemoryBuffer; 33 } // namespace llvm 34 35 namespace mlir { 36 37 class ModuleOp; 38 39 /// A simple object cache following Lang's LLJITWithObjectCache example. 40 class SimpleObjectCache : public llvm::ObjectCache { 41 public: 42 void notifyObjectCompiled(const llvm::Module *m, 43 llvm::MemoryBufferRef objBuffer) override; 44 std::unique_ptr<llvm::MemoryBuffer> getObject(const llvm::Module *m) override; 45 46 /// Dump cached object to output file `filename`. 47 void dumpToObjectFile(StringRef filename); 48 49 private: 50 llvm::StringMap<std::unique_ptr<llvm::MemoryBuffer>> cachedObjects; 51 }; 52 53 struct ExecutionEngineOptions { 54 /// If `llvmModuleBuilder` is provided, it will be used to create LLVM module 55 /// from the given MLIR module. Otherwise, a default `translateModuleToLLVMIR` 56 /// function will be used to translate MLIR module to LLVM IR. 57 llvm::function_ref<std::unique_ptr<llvm::Module>(ModuleOp, 58 llvm::LLVMContext &)> 59 llvmModuleBuilder = nullptr; 60 61 /// If `transformer` is provided, it will be called on the LLVM module during 62 /// JIT-compilation and can be used, e.g., for reporting or optimization. 63 llvm::function_ref<llvm::Error(llvm::Module *)> transformer = {}; 64 65 /// `jitCodeGenOptLevel`, when provided, is used as the optimization level for 66 /// target code generation. 67 Optional<llvm::CodeGenOpt::Level> jitCodeGenOptLevel = llvm::None; 68 69 /// If `sharedLibPaths` are provided, the underlying JIT-compilation will 70 /// open and link the shared libraries for symbol resolution. 71 ArrayRef<StringRef> sharedLibPaths = {}; 72 73 /// Specifies an existing `sectionMemoryMapper` to be associated with the 74 /// compiled code. If none is provided, a default memory mapper that directly 75 /// calls into the operating system is used. 76 llvm::SectionMemoryManager::MemoryMapper *sectionMemoryMapper = nullptr; 77 78 /// If `enableObjectCache` is set, the JIT compiler will create one to store 79 /// the object generated for the given module. The contents of the cache can 80 /// be dumped to a file via the `dumpToObjectfile` method. 81 bool enableObjectCache = false; 82 83 /// If enable `enableGDBNotificationListener` is set, the JIT compiler will 84 /// notify the llvm's global GDB notification listener. 85 bool enableGDBNotificationListener = true; 86 87 /// If `enablePerfNotificationListener` is set, the JIT compiler will notify 88 /// the llvm's global Perf notification listener. 89 bool enablePerfNotificationListener = true; 90 }; 91 92 /// JIT-backed execution engine for MLIR modules. Assumes the module can be 93 /// converted to LLVM IR. For each function, creates a wrapper function with 94 /// the fixed interface 95 /// 96 /// void _mlir_funcName(void **) 97 /// 98 /// where the only argument is interpreted as a list of pointers to the actual 99 /// arguments of the function, followed by a pointer to the result. This allows 100 /// the engine to provide the caller with a generic function pointer that can 101 /// be used to invoke the JIT-compiled function. 102 class ExecutionEngine { 103 public: 104 ExecutionEngine(bool enableObjectCache, bool enableGDBNotificationListener, 105 bool enablePerfNotificationListener); 106 107 /// Creates an execution engine for the given module. 108 static llvm::Expected<std::unique_ptr<ExecutionEngine>> 109 create(ModuleOp m, const ExecutionEngineOptions &options = {}); 110 111 /// Looks up a packed-argument function wrapping the function with the given 112 /// name and returns a pointer to it. Propagates errors in case of failure. 113 llvm::Expected<void (*)(void **)> lookupPacked(StringRef name) const; 114 115 /// Looks up the original function with the given name and returns a 116 /// pointer to it. This is not necesarily a packed function. Propagates 117 /// errors in case of failure. 118 llvm::Expected<void *> lookup(StringRef name) const; 119 120 /// Invokes the function with the given name passing it the list of opaque 121 /// pointers to the actual arguments. 122 llvm::Error invokePacked(StringRef name, 123 MutableArrayRef<void *> args = llvm::None); 124 125 /// Trait that defines how a given type is passed to the JIT code. This 126 /// defaults to passing the address but can be specialized. 127 template <typename T> 128 struct Argument { packArgument129 static void pack(SmallVectorImpl<void *> &args, T &val) { 130 args.push_back(&val); 131 } 132 }; 133 134 /// Tag to wrap an output parameter when invoking a jitted function. 135 template <typename T> 136 struct Result { ResultResult137 Result(T &result) : value(result) {} 138 T &value; 139 }; 140 141 /// Helper function to wrap an output operand when using 142 /// ExecutionEngine::invoke. 143 template <typename T> result(T & t)144 static Result<T> result(T &t) { 145 return Result<T>(t); 146 } 147 148 // Specialization for output parameter: their address is forwarded directly to 149 // the native code. 150 template <typename T> 151 struct Argument<Result<T>> { 152 static void pack(SmallVectorImpl<void *> &args, Result<T> &result) { 153 args.push_back(&result.value); 154 } 155 }; 156 157 /// Invokes the function with the given name passing it the list of arguments 158 /// by value. Function result can be obtain through output parameter using the 159 /// `Result` wrapper defined above. For example: 160 /// 161 /// func @foo(%arg0 : i32) -> i32 attributes { llvm.emit_c_interface } 162 /// 163 /// can be invoked: 164 /// 165 /// int32_t result = 0; 166 /// llvm::Error error = jit->invoke("foo", 42, 167 /// result(result)); 168 template <typename... Args> 169 llvm::Error invoke(StringRef funcName, Args... args) { 170 const std::string adapterName = 171 std::string("_mlir_ciface_") + funcName.str(); 172 llvm::SmallVector<void *> argsArray; 173 // Pack every arguments in an array of pointers. Delegate the packing to a 174 // trait so that it can be overridden per argument type. 175 // TODO: replace with a fold expression when migrating to C++17. 176 int dummy[] = {0, ((void)Argument<Args>::pack(argsArray, args), 0)...}; 177 (void)dummy; 178 return invokePacked(adapterName, argsArray); 179 } 180 181 /// Set the target triple on the module. This is implicitly done when creating 182 /// the engine. 183 static bool setupTargetTriple(llvm::Module *llvmModule); 184 185 /// Dump object code to output file `filename`. 186 void dumpToObjectFile(StringRef filename); 187 188 /// Register symbols with this ExecutionEngine. 189 void registerSymbols( 190 llvm::function_ref<llvm::orc::SymbolMap(llvm::orc::MangleAndInterner)> 191 symbolMap); 192 193 private: 194 /// Ordering of llvmContext and jit is important for destruction purposes: the 195 /// jit must be destroyed before the context. 196 llvm::LLVMContext llvmContext; 197 198 /// Underlying LLJIT. 199 std::unique_ptr<llvm::orc::LLJIT> jit; 200 201 /// Underlying cache. 202 std::unique_ptr<SimpleObjectCache> cache; 203 204 /// GDB notification listener. 205 llvm::JITEventListener *gdbListener; 206 207 /// Perf notification listener. 208 llvm::JITEventListener *perfListener; 209 }; 210 211 } // namespace mlir 212 213 #endif // MLIR_EXECUTIONENGINE_EXECUTIONENGINE_H_ 214