/* -*- C++ -*- * Serene Programming Language * * Copyright (c) 2019-2021 Sameer Rahmani * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 2. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /** * Commentary: */ #ifndef SERENE_JIT_H #define SERENE_JIT_H #include "serene/errors.h" #include "serene/export.h" #include "serene/slir/generatable.h" #include "serene/utils.h" #include #include #include #include #include #include #include #include #define JIT_LOG(...) \ DEBUG_WITH_TYPE("JIT", llvm::dbgs() << "[JIT]: " << __VA_ARGS__ << "\n"); namespace serene { class SERENE_EXPORT JIT; using MaybeJIT = llvm::Optional>; /// A simple object cache following Lang's LLJITWithObjectCache example and /// MLIR's SimpelObjectCache. class ObjectCache : public llvm::ObjectCache { public: /// Cache the given `objBuffer` for the given module `m`. The buffer contains /// the combiled objects of the module void notifyObjectCompiled(const llvm::Module *m, llvm::MemoryBufferRef objBuffer) override; // Lookup the cache for the given module `m` or returen a nullptr. std::unique_ptr getObject(const llvm::Module *m) override; /// Dump cached object to output file `filename`. void dumpToObjectFile(llvm::StringRef filename); private: llvm::StringMap> cachedObjects; }; class JIT { // TODO: Should the JIT own the context ??? Namespace &ns; std::unique_ptr engine; std::unique_ptr cache; /// GDB notification listener. llvm::JITEventListener *gdbListener; /// Perf notification listener. llvm::JITEventListener *perfListener; public: JIT(Namespace &ns, bool enableObjectCache = true, bool enableGDBNotificationListener = true, bool enablePerfNotificationListener = true); static MaybeJIT make(Namespace &ns, mlir::ArrayRef sharedLibPaths = {}, mlir::Optional jitCodeGenOptLevel = llvm::None, bool enableObjectCache = true, bool enableGDBNotificationListener = true, bool enablePerfNotificationListener = true); /// Looks up a packed-argument function with the given name and returns a /// pointer to it. Propagates errors in case of failure. llvm::Expected lookup(llvm::StringRef name) const; /// Invokes the function with the given name passing it the list of opaque /// pointers to the actual arguments. llvm::Error invokePacked(llvm::StringRef name, llvm::MutableArrayRef args = llvm::None); /// Trait that defines how a given type is passed to the JIT code. This /// defaults to passing the address but can be specialized. template struct Argument { static void pack(llvm::SmallVectorImpl &args, T &val) { args.push_back(&val); } }; /// Tag to wrap an output parameter when invoking a jitted function. template struct FnResult { FnResult(T &result) : value(result) {} T &value; }; /// Helper function to wrap an output operand when using /// ExecutionEngine::invoke. template static FnResult result(T &t) { return FnResult(t); } // Specialization for output parameter: their address is forwarded directly to // the native code. template struct Argument> { static void pack(llvm::SmallVectorImpl &args, FnResult &result) { args.push_back(&result.value); } }; /// Invokes the function with the given name passing it the list of arguments /// by value. Function result can be obtain through output parameter using the /// `FnResult` wrapper defined above. For example: /// /// func @foo(%arg0 : i32) -> i32 attributes { llvm.emit_c_interface } /// /// can be invoked: /// /// int32_t result = 0; /// llvm::Error error = jit->invoke("foo", 42, /// result(result)); template llvm::Error invoke(llvm::StringRef funcName, Args... args) { const std::string adapterName = std::string("") + funcName.str(); llvm::SmallVector argsArray; // Pack every arguments in an array of pointers. Delegate the packing to a // trait so that it can be overridden per argument type. // TODO: replace with a fold expression when migrating to C++17. int dummy[] = {0, ((void)Argument::pack(argsArray, args), 0)...}; (void)dummy; return invokePacked(adapterName, argsArray); }; /// Dump object code to output file `filename`. void dumpToObjectFile(llvm::StringRef filename); /// Register symbols with this ExecutionEngine. void registerSymbols( llvm::function_ref symbolMap); }; } // namespace serene #endif