Switch Android to LLVM ORCv2 JIT and remove ORCv1 support Fixes: b/171897980 Change-Id: I3e2623bc05f1afc3c30b0f0c5fba18bdddf62e92 Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/49968 Reviewed-by: Jason Macnak <natsu@google.com> Reviewed-by: Alexis Hétu <sugoi@google.com> Tested-by: Jason Macnak <natsu@google.com> Kokoro-Result: kokoro <noreply+kokoro@google.com>
diff --git a/src/Android.bp b/src/Android.bp index 87b939a..5ccc5b4 100644 --- a/src/Android.bp +++ b/src/Android.bp
@@ -194,7 +194,7 @@ "Reactor/Debug.cpp", "Reactor/EmulatedIntrinsics.cpp", "Reactor/ExecutableMemory.cpp", - "Reactor/LLVMJIT_ORCv1.cpp", // TODO(b/171897980): Upgrade to ORCv2 + "Reactor/LLVMJIT.cpp", "Reactor/LLVMReactor.cpp", "Reactor/OptimalIntrinsics.cpp", "Reactor/Reactor.cpp", @@ -226,7 +226,7 @@ "Reactor/Debug.cpp", "Reactor/EmulatedIntrinsics.cpp", "Reactor/ExecutableMemory.cpp", - "Reactor/LLVMJIT_ORCv1.cpp", // TODO(b/171897980): Upgrade to ORCv2 + "Reactor/LLVMJIT.cpp", "Reactor/LLVMReactor.cpp", "Reactor/OptimalIntrinsics.cpp", "Reactor/Reactor.cpp",
diff --git a/src/Reactor/LLVMJIT_ORCv1.cpp b/src/Reactor/LLVMJIT_ORCv1.cpp deleted file mode 100644 index 5303311..0000000 --- a/src/Reactor/LLVMJIT_ORCv1.cpp +++ /dev/null
@@ -1,710 +0,0 @@ -// Copyright 2020 The SwiftShader Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// TODO(b/171897980): Deprecate this file, which uses the deprecated ORCv1 API, -// in favor of LLVMJIT.cpp, which uses ORCv2, once all platforms support it. -#include "llvm/Config/llvm-config.h" -#if LLVM_VERSION_MAJOR <= 10 - -# include "LLVMReactor.hpp" - -# include "Debug.hpp" -# include "ExecutableMemory.hpp" -# include "Routine.hpp" - -// TODO(b/143539525): Eliminate when warning has been fixed. -# ifdef _MSC_VER -__pragma(warning(push)) - __pragma(warning(disable : 4146)) // unary minus operator applied to unsigned type, result still unsigned -# endif - -# include "llvm/Analysis/LoopPass.h" -# include "llvm/ExecutionEngine/ExecutionEngine.h" -# include "llvm/ExecutionEngine/JITSymbol.h" -# include "llvm/ExecutionEngine/Orc/CompileUtils.h" -# include "llvm/ExecutionEngine/Orc/IRCompileLayer.h" -# include "llvm/ExecutionEngine/Orc/LambdaResolver.h" -# include "llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h" -# include "llvm/ExecutionEngine/RTDyldMemoryManager.h" -# include "llvm/ExecutionEngine/SectionMemoryManager.h" -# include "llvm/IR/Constants.h" -# include "llvm/IR/DataLayout.h" -# include "llvm/IR/Function.h" -# include "llvm/IR/GlobalVariable.h" -# include "llvm/IR/LegacyPassManager.h" -# include "llvm/IR/Mangler.h" -# include "llvm/IR/Module.h" -# include "llvm/IR/Verifier.h" -# include "llvm/Support/Compiler.h" -# include "llvm/Support/Error.h" -# include "llvm/Support/Host.h" -# include "llvm/Support/TargetSelect.h" -# include "llvm/Target/TargetOptions.h" -# include "llvm/Transforms/Coroutines.h" -# include "llvm/Transforms/IPO.h" -# include "llvm/Transforms/IPO/PassManagerBuilder.h" -# include "llvm/Transforms/InstCombine/InstCombine.h" -# include "llvm/Transforms/Scalar.h" -# include "llvm/Transforms/Scalar/GVN.h" - -# ifdef _MSC_VER - __pragma(warning(pop)) -# endif - -# include <atomic> -# include <unordered_map> - -# if defined(_WIN64) - extern "C" void __chkstk(); -# elif defined(_WIN32) -extern "C" void _chkstk(); -# endif - -# if __has_feature(memory_sanitizer) -# include <sanitizer/msan_interface.h> -# endif - -# ifdef __ARM_EABI__ -extern "C" signed __aeabi_idivmod(); -# endif - -namespace { - -// JITGlobals is a singleton that holds all the immutable machine specific -// information for the host device. -class JITGlobals -{ -public: - using TargetMachineSPtr = std::shared_ptr<llvm::TargetMachine>; - - static JITGlobals *get(); - - const std::string mcpu; - const std::vector<std::string> mattrs; - const char *const march; - const llvm::TargetOptions targetOptions; - const llvm::DataLayout dataLayout; - - TargetMachineSPtr createTargetMachine(rr::Optimization::Level optlevel); - -private: - static JITGlobals create(); - static llvm::CodeGenOpt::Level toLLVM(rr::Optimization::Level level); - JITGlobals(const char *mcpu, - const std::vector<std::string> &mattrs, - const char *march, - const llvm::TargetOptions &targetOptions, - const llvm::DataLayout &dataLayout); - JITGlobals(const JITGlobals &) = default; -}; - -JITGlobals *JITGlobals::get() -{ - static JITGlobals instance = create(); - return &instance; -} - -JITGlobals::TargetMachineSPtr JITGlobals::createTargetMachine(rr::Optimization::Level optlevel) -{ -# ifdef ENABLE_RR_DEBUG_INFO - auto llvmOptLevel = toLLVM(rr::Optimization::Level::None); -# else // ENABLE_RR_DEBUG_INFO - auto llvmOptLevel = toLLVM(optlevel); -# endif // ENABLE_RR_DEBUG_INFO - - return TargetMachineSPtr(llvm::EngineBuilder() - .setOptLevel(llvmOptLevel) - .setMCPU(mcpu) - .setMArch(march) - .setMAttrs(mattrs) - .setTargetOptions(targetOptions) - .selectTarget()); -} - -JITGlobals JITGlobals::create() -{ - struct LLVMInitializer - { - LLVMInitializer() - { - llvm::InitializeNativeTarget(); - llvm::InitializeNativeTargetAsmPrinter(); - llvm::InitializeNativeTargetAsmParser(); - } - }; - static LLVMInitializer initializeLLVM; - - auto mcpu = llvm::sys::getHostCPUName(); - - llvm::StringMap<bool> features; - bool ok = llvm::sys::getHostCPUFeatures(features); - -# if defined(__i386__) || defined(__x86_64__) || \ - (defined(__linux__) && (defined(__arm__) || defined(__aarch64__))) - ASSERT_MSG(ok, "llvm::sys::getHostCPUFeatures returned false"); -# else - (void)ok; // getHostCPUFeatures always returns false on other platforms -# endif - - std::vector<std::string> mattrs; - for(auto &feature : features) - { - if(feature.second) { mattrs.push_back(feature.first().str()); } - } - - const char *march = nullptr; -# if defined(__x86_64__) - march = "x86-64"; -# elif defined(__i386__) - march = "x86"; -# elif defined(__aarch64__) - march = "arm64"; -# elif defined(__arm__) - march = "arm"; -# elif defined(__mips__) -# if defined(__mips64) - march = "mips64el"; -# else - march = "mipsel"; -# endif -# elif defined(__powerpc64__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ - march = "ppc64le"; -# else -# error "unknown architecture" -# endif - - llvm::TargetOptions targetOptions; - targetOptions.UnsafeFPMath = false; - - auto targetMachine = std::unique_ptr<llvm::TargetMachine>( - llvm::EngineBuilder() - .setOptLevel(llvm::CodeGenOpt::None) - .setMCPU(mcpu) - .setMArch(march) - .setMAttrs(mattrs) - .setTargetOptions(targetOptions) - .selectTarget()); - - auto dataLayout = targetMachine->createDataLayout(); - - return JITGlobals(mcpu.data(), mattrs, march, targetOptions, dataLayout); -} - -llvm::CodeGenOpt::Level JITGlobals::toLLVM(rr::Optimization::Level level) -{ - switch(level) - { - case rr::Optimization::Level::None: return llvm::CodeGenOpt::None; - case rr::Optimization::Level::Less: return llvm::CodeGenOpt::Less; - case rr::Optimization::Level::Default: return llvm::CodeGenOpt::Default; - case rr::Optimization::Level::Aggressive: return llvm::CodeGenOpt::Aggressive; - default: UNREACHABLE("Unknown Optimization Level %d", int(level)); - } - return llvm::CodeGenOpt::Default; -} - -JITGlobals::JITGlobals(const char *mcpu, - const std::vector<std::string> &mattrs, - const char *march, - const llvm::TargetOptions &targetOptions, - const llvm::DataLayout &dataLayout) - : mcpu(mcpu) - , mattrs(mattrs) - , march(march) - , targetOptions(targetOptions) - , dataLayout(dataLayout) -{ -} - -class MemoryMapper final : public llvm::SectionMemoryManager::MemoryMapper -{ -public: - MemoryMapper() {} - ~MemoryMapper() final {} - - llvm::sys::MemoryBlock allocateMappedMemory( - llvm::SectionMemoryManager::AllocationPurpose purpose, - size_t numBytes, const llvm::sys::MemoryBlock *const nearBlock, - unsigned flags, std::error_code &errorCode) final - { - errorCode = std::error_code(); - - // Round up numBytes to page size. - size_t pageSize = rr::memoryPageSize(); - numBytes = (numBytes + pageSize - 1) & ~(pageSize - 1); - - bool need_exec = - purpose == llvm::SectionMemoryManager::AllocationPurpose::Code; - void *addr = rr::allocateMemoryPages( - numBytes, flagsToPermissions(flags), need_exec); - if(!addr) - return llvm::sys::MemoryBlock(); - return llvm::sys::MemoryBlock(addr, numBytes); - } - - std::error_code protectMappedMemory(const llvm::sys::MemoryBlock &block, - unsigned flags) - { - // Round down base address to align with a page boundary. This matches - // DefaultMMapper behavior. - void *addr = block.base(); - size_t size = block.allocatedSize(); - size_t pageSize = rr::memoryPageSize(); - addr = reinterpret_cast<void *>( - reinterpret_cast<uintptr_t>(addr) & ~(pageSize - 1)); - size += reinterpret_cast<uintptr_t>(block.base()) - - reinterpret_cast<uintptr_t>(addr); - - rr::protectMemoryPages(addr, size, flagsToPermissions(flags)); - return std::error_code(); - } - - std::error_code releaseMappedMemory(llvm::sys::MemoryBlock &block) - { - size_t size = block.allocatedSize(); - - rr::deallocateMemoryPages(block.base(), size); - return std::error_code(); - } - -private: - int flagsToPermissions(unsigned flags) - { - int result = 0; - if(flags & llvm::sys::Memory::MF_READ) - { - result |= rr::PERMISSION_READ; - } - if(flags & llvm::sys::Memory::MF_WRITE) - { - result |= rr::PERMISSION_WRITE; - } - if(flags & llvm::sys::Memory::MF_EXEC) - { - result |= rr::PERMISSION_EXECUTE; - } - return result; - } -}; - -template<typename T> -T alignUp(T val, T alignment) -{ - return alignment * ((val + alignment - 1) / alignment); -} - -void *alignedAlloc(size_t size, size_t alignment) -{ - ASSERT(alignment < 256); - auto allocation = new uint8_t[size + sizeof(uint8_t) + alignment]; - auto aligned = allocation; - aligned += sizeof(uint8_t); // Make space for the base-address offset. - aligned = reinterpret_cast<uint8_t *>(alignUp(reinterpret_cast<uintptr_t>(aligned), alignment)); // align - auto offset = static_cast<uint8_t>(aligned - allocation); - aligned[-1] = offset; - return aligned; -} - -void alignedFree(void *ptr) -{ - auto aligned = reinterpret_cast<uint8_t *>(ptr); - auto offset = aligned[-1]; - auto allocation = aligned - offset; - delete[] allocation; -} - -template<typename T> -static void atomicLoad(void *ptr, void *ret, llvm::AtomicOrdering ordering) -{ - *reinterpret_cast<T *>(ret) = std::atomic_load_explicit<T>(reinterpret_cast<std::atomic<T> *>(ptr), rr::atomicOrdering(ordering)); -} - -template<typename T> -static void atomicStore(void *ptr, void *val, llvm::AtomicOrdering ordering) -{ - std::atomic_store_explicit<T>(reinterpret_cast<std::atomic<T> *>(ptr), *reinterpret_cast<T *>(val), rr::atomicOrdering(ordering)); -} - -# ifdef __ANDROID__ -template<typename F> -static uint32_t sync_fetch_and_op(uint32_t volatile *ptr, uint32_t val, F f) -{ - // Build an arbitrary op out of looped CAS - for(;;) - { - uint32_t expected = *ptr; - uint32_t desired = f(expected, val); - - if(expected == __sync_val_compare_and_swap_4(ptr, expected, desired)) - { - return expected; - } - } -} -# endif - -void *resolveExternalSymbol(const char *name) -{ - struct Atomic - { - static void load(size_t size, void *ptr, void *ret, llvm::AtomicOrdering ordering) - { - switch(size) - { - case 1: atomicLoad<uint8_t>(ptr, ret, ordering); break; - case 2: atomicLoad<uint16_t>(ptr, ret, ordering); break; - case 4: atomicLoad<uint32_t>(ptr, ret, ordering); break; - case 8: atomicLoad<uint64_t>(ptr, ret, ordering); break; - default: - UNIMPLEMENTED_NO_BUG("Atomic::load(size: %d)", int(size)); - } - } - static void store(size_t size, void *ptr, void *ret, llvm::AtomicOrdering ordering) - { - switch(size) - { - case 1: atomicStore<uint8_t>(ptr, ret, ordering); break; - case 2: atomicStore<uint16_t>(ptr, ret, ordering); break; - case 4: atomicStore<uint32_t>(ptr, ret, ordering); break; - case 8: atomicStore<uint64_t>(ptr, ret, ordering); break; - default: - UNIMPLEMENTED_NO_BUG("Atomic::store(size: %d)", int(size)); - } - } - }; - - struct F - { - static void nop() {} - static void neverCalled() { UNREACHABLE("Should never be called"); } - - static void *coroutine_alloc_frame(size_t size) { return alignedAlloc(size, 16); } - static void coroutine_free_frame(void *ptr) { alignedFree(ptr); } - -# ifdef __ANDROID__ - // forwarders since we can't take address of builtins - static void sync_synchronize() { __sync_synchronize(); } - static uint32_t sync_fetch_and_add_4(uint32_t *ptr, uint32_t val) { return __sync_fetch_and_add_4(ptr, val); } - static uint32_t sync_fetch_and_and_4(uint32_t *ptr, uint32_t val) { return __sync_fetch_and_and_4(ptr, val); } - static uint32_t sync_fetch_and_or_4(uint32_t *ptr, uint32_t val) { return __sync_fetch_and_or_4(ptr, val); } - static uint32_t sync_fetch_and_xor_4(uint32_t *ptr, uint32_t val) { return __sync_fetch_and_xor_4(ptr, val); } - static uint32_t sync_fetch_and_sub_4(uint32_t *ptr, uint32_t val) { return __sync_fetch_and_sub_4(ptr, val); } - static uint32_t sync_lock_test_and_set_4(uint32_t *ptr, uint32_t val) { return __sync_lock_test_and_set_4(ptr, val); } - static uint32_t sync_val_compare_and_swap_4(uint32_t *ptr, uint32_t expected, uint32_t desired) { return __sync_val_compare_and_swap_4(ptr, expected, desired); } - - static uint32_t sync_fetch_and_max_4(uint32_t *ptr, uint32_t val) - { - return sync_fetch_and_op(ptr, val, [](int32_t a, int32_t b) { return std::max(a, b); }); - } - static uint32_t sync_fetch_and_min_4(uint32_t *ptr, uint32_t val) - { - return sync_fetch_and_op(ptr, val, [](int32_t a, int32_t b) { return std::min(a, b); }); - } - static uint32_t sync_fetch_and_umax_4(uint32_t *ptr, uint32_t val) - { - return sync_fetch_and_op(ptr, val, [](uint32_t a, uint32_t b) { return std::max(a, b); }); - } - static uint32_t sync_fetch_and_umin_4(uint32_t *ptr, uint32_t val) - { - return sync_fetch_and_op(ptr, val, [](uint32_t a, uint32_t b) { return std::min(a, b); }); - } -# endif - }; - - class Resolver - { - public: - using FunctionMap = std::unordered_map<std::string, void *>; - - FunctionMap functions; - - Resolver() - { -# ifdef ENABLE_RR_PRINT - functions.emplace("rr::DebugPrintf", reinterpret_cast<void *>(rr::DebugPrintf)); -# endif - functions.emplace("nop", reinterpret_cast<void *>(F::nop)); - functions.emplace("floorf", reinterpret_cast<void *>(floorf)); - functions.emplace("nearbyintf", reinterpret_cast<void *>(nearbyintf)); - functions.emplace("truncf", reinterpret_cast<void *>(truncf)); - functions.emplace("printf", reinterpret_cast<void *>(printf)); - functions.emplace("puts", reinterpret_cast<void *>(puts)); - functions.emplace("fmodf", reinterpret_cast<void *>(fmodf)); - - functions.emplace("sinf", reinterpret_cast<void *>(sinf)); - functions.emplace("cosf", reinterpret_cast<void *>(cosf)); - functions.emplace("asinf", reinterpret_cast<void *>(asinf)); - functions.emplace("acosf", reinterpret_cast<void *>(acosf)); - functions.emplace("atanf", reinterpret_cast<void *>(atanf)); - functions.emplace("sinhf", reinterpret_cast<void *>(sinhf)); - functions.emplace("coshf", reinterpret_cast<void *>(coshf)); - functions.emplace("tanhf", reinterpret_cast<void *>(tanhf)); - functions.emplace("asinhf", reinterpret_cast<void *>(asinhf)); - functions.emplace("acoshf", reinterpret_cast<void *>(acoshf)); - functions.emplace("atanhf", reinterpret_cast<void *>(atanhf)); - functions.emplace("atan2f", reinterpret_cast<void *>(atan2f)); - functions.emplace("powf", reinterpret_cast<void *>(powf)); - functions.emplace("expf", reinterpret_cast<void *>(expf)); - functions.emplace("logf", reinterpret_cast<void *>(logf)); - functions.emplace("exp2f", reinterpret_cast<void *>(exp2f)); - functions.emplace("log2f", reinterpret_cast<void *>(log2f)); - - functions.emplace("sin", reinterpret_cast<void *>(static_cast<double (*)(double)>(sin))); - functions.emplace("cos", reinterpret_cast<void *>(static_cast<double (*)(double)>(cos))); - functions.emplace("asin", reinterpret_cast<void *>(static_cast<double (*)(double)>(asin))); - functions.emplace("acos", reinterpret_cast<void *>(static_cast<double (*)(double)>(acos))); - functions.emplace("atan", reinterpret_cast<void *>(static_cast<double (*)(double)>(atan))); - functions.emplace("sinh", reinterpret_cast<void *>(static_cast<double (*)(double)>(sinh))); - functions.emplace("cosh", reinterpret_cast<void *>(static_cast<double (*)(double)>(cosh))); - functions.emplace("tanh", reinterpret_cast<void *>(static_cast<double (*)(double)>(tanh))); - functions.emplace("asinh", reinterpret_cast<void *>(static_cast<double (*)(double)>(asinh))); - functions.emplace("acosh", reinterpret_cast<void *>(static_cast<double (*)(double)>(acosh))); - functions.emplace("atanh", reinterpret_cast<void *>(static_cast<double (*)(double)>(atanh))); - functions.emplace("atan2", reinterpret_cast<void *>(static_cast<double (*)(double, double)>(atan2))); - functions.emplace("pow", reinterpret_cast<void *>(static_cast<double (*)(double, double)>(pow))); - functions.emplace("exp", reinterpret_cast<void *>(static_cast<double (*)(double)>(exp))); - functions.emplace("log", reinterpret_cast<void *>(static_cast<double (*)(double)>(log))); - functions.emplace("exp2", reinterpret_cast<void *>(static_cast<double (*)(double)>(exp2))); - functions.emplace("log2", reinterpret_cast<void *>(static_cast<double (*)(double)>(log2))); - - functions.emplace("atomic_load", reinterpret_cast<void *>(Atomic::load)); - functions.emplace("atomic_store", reinterpret_cast<void *>(Atomic::store)); - - // FIXME(b/119409619): use an allocator here so we can control all memory allocations - functions.emplace("coroutine_alloc_frame", reinterpret_cast<void *>(F::coroutine_alloc_frame)); - functions.emplace("coroutine_free_frame", reinterpret_cast<void *>(F::coroutine_free_frame)); - -# ifdef __APPLE__ - functions.emplace("sincosf_stret", reinterpret_cast<void *>(__sincosf_stret)); -# elif defined(__linux__) - functions.emplace("sincosf", reinterpret_cast<void *>(sincosf)); -# elif defined(_WIN64) - functions.emplace("chkstk", reinterpret_cast<void *>(__chkstk)); -# elif defined(_WIN32) - functions.emplace("chkstk", reinterpret_cast<void *>(_chkstk)); -# endif - -# ifdef __ARM_EABI__ - functions.emplace("aeabi_idivmod", reinterpret_cast<void *>(__aeabi_idivmod)); -# endif -# ifdef __ANDROID__ - functions.emplace("aeabi_unwind_cpp_pr0", reinterpret_cast<void *>(F::neverCalled)); - functions.emplace("sync_synchronize", reinterpret_cast<void *>(F::sync_synchronize)); - functions.emplace("sync_fetch_and_add_4", reinterpret_cast<void *>(F::sync_fetch_and_add_4)); - functions.emplace("sync_fetch_and_and_4", reinterpret_cast<void *>(F::sync_fetch_and_and_4)); - functions.emplace("sync_fetch_and_or_4", reinterpret_cast<void *>(F::sync_fetch_and_or_4)); - functions.emplace("sync_fetch_and_xor_4", reinterpret_cast<void *>(F::sync_fetch_and_xor_4)); - functions.emplace("sync_fetch_and_sub_4", reinterpret_cast<void *>(F::sync_fetch_and_sub_4)); - functions.emplace("sync_lock_test_and_set_4", reinterpret_cast<void *>(F::sync_lock_test_and_set_4)); - functions.emplace("sync_val_compare_and_swap_4", reinterpret_cast<void *>(F::sync_val_compare_and_swap_4)); - functions.emplace("sync_fetch_and_max_4", reinterpret_cast<void *>(F::sync_fetch_and_max_4)); - functions.emplace("sync_fetch_and_min_4", reinterpret_cast<void *>(F::sync_fetch_and_min_4)); - functions.emplace("sync_fetch_and_umax_4", reinterpret_cast<void *>(F::sync_fetch_and_umax_4)); - functions.emplace("sync_fetch_and_umin_4", reinterpret_cast<void *>(F::sync_fetch_and_umin_4)); -# endif -# if __has_feature(memory_sanitizer) - functions.emplace("msan_unpoison", reinterpret_cast<void *>(__msan_unpoison)); -# endif - } - }; - - static Resolver resolver; - - // Trim off any underscores from the start of the symbol. LLVM likes - // to append these on macOS. - const char *trimmed = name; - while(trimmed[0] == '_') { trimmed++; } - - auto it = resolver.functions.find(trimmed); - // Missing functions will likely make the module fail in exciting non-obvious ways. - ASSERT_MSG(it != resolver.functions.end(), "Missing external function: '%s'", name); - return it->second; -} - -// JITRoutine is a rr::Routine that holds a LLVM JIT session, compiler and -// object layer as each routine may require different target machine -// settings and no Reactor routine directly links against another. -class JITRoutine : public rr::Routine -{ - using ObjLayer = llvm::orc::LegacyRTDyldObjectLinkingLayer; - using CompileLayer = llvm::orc::LegacyIRCompileLayer<ObjLayer, llvm::orc::SimpleCompiler>; - -public: -# if defined(__clang__) -// TODO(bclayton): Switch to new JIT -// error: 'LegacyIRCompileLayer' is deprecated: ORCv1 layers (layers with the 'Legacy' prefix) are deprecated. -// Please use the ORCv2 IRCompileLayer instead [-Werror,-Wdeprecated-declarations] -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wdeprecated-declarations" -# elif defined(__GNUC__) -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wdeprecated-declarations" -# endif - - JITRoutine( - std::unique_ptr<llvm::Module> module, - llvm::Function **funcs, - size_t count, - const rr::Config &config) - : resolver(createLegacyLookupResolver( - session, - [&](const llvm::StringRef &name) { - void *func = resolveExternalSymbol(name.str().c_str()); - if(func != nullptr) - { - return llvm::JITSymbol( - reinterpret_cast<uintptr_t>(func), llvm::JITSymbolFlags::Absolute); - } - return objLayer.findSymbol(name, true); - }, - [](llvm::Error err) { - if(err) - { - // TODO: Log the symbol resolution errors. - return; - } - })) - , targetMachine(JITGlobals::get()->createTargetMachine(config.getOptimization().getLevel())) - , compileLayer(objLayer, llvm::orc::SimpleCompiler(*targetMachine)) - , objLayer( - session, - [this](llvm::orc::VModuleKey) { - return ObjLayer::Resources{ std::make_shared<llvm::SectionMemoryManager>(&memoryMapper), resolver }; - }, - ObjLayer::NotifyLoadedFtor(), - [](llvm::orc::VModuleKey, const llvm::object::ObjectFile &Obj, const llvm::RuntimeDyld::LoadedObjectInfo &L) { -# ifdef ENABLE_RR_DEBUG_INFO - rr::DebugInfo::NotifyObjectEmitted(Obj, L); -# endif // ENABLE_RR_DEBUG_INFO - }, - [](llvm::orc::VModuleKey, const llvm::object::ObjectFile &Obj) { -# ifdef ENABLE_RR_DEBUG_INFO - rr::DebugInfo::NotifyFreeingObject(Obj); -# endif // ENABLE_RR_DEBUG_INFO - }) - , addresses(count) - { - -# if defined(__clang__) -# pragma clang diagnostic pop -# elif defined(__GNUC__) -# pragma GCC diagnostic pop -# endif - - std::vector<std::string> mangledNames(count); - for(size_t i = 0; i < count; i++) - { - auto func = funcs[i]; - static std::atomic<size_t> numEmittedFunctions = { 0 }; - std::string name = "f" + llvm::Twine(numEmittedFunctions++).str(); - func->setName(name); - func->setLinkage(llvm::GlobalValue::ExternalLinkage); - func->setDoesNotThrow(); - - llvm::raw_string_ostream mangledNameStream(mangledNames[i]); - llvm::Mangler::getNameWithPrefix(mangledNameStream, name, JITGlobals::get()->dataLayout); - } - - auto moduleKey = session.allocateVModule(); - - // Once the module is passed to the compileLayer, the - // llvm::Functions are freed. Make sure funcs are not referenced - // after this point. - funcs = nullptr; - - llvm::cantFail(compileLayer.addModule(moduleKey, std::move(module))); - - // Resolve the function addresses. - for(size_t i = 0; i < count; i++) - { - auto symbol = compileLayer.findSymbolIn(moduleKey, mangledNames[i], false); - if(auto address = symbol.getAddress()) - { - addresses[i] = reinterpret_cast<void *>(static_cast<intptr_t>(address.get())); - } - } - } - - const void *getEntry(int index) const override - { - return addresses[index]; - } - -private: - std::shared_ptr<llvm::orc::SymbolResolver> resolver; - std::shared_ptr<llvm::TargetMachine> targetMachine; - llvm::orc::ExecutionSession session; - CompileLayer compileLayer; - MemoryMapper memoryMapper; - ObjLayer objLayer; - std::vector<const void *> addresses; -}; - -} // anonymous namespace - -namespace rr { - -JITBuilder::JITBuilder(const rr::Config &config) - : config(config) - , module(new llvm::Module("", context)) - , builder(new llvm::IRBuilder<>(context)) -{ - module->setDataLayout(JITGlobals::get()->dataLayout); -} - -void JITBuilder::optimize(const rr::Config &cfg) -{ - -# ifdef ENABLE_RR_DEBUG_INFO - if(debugInfo != nullptr) - { - return; // Don't optimize if we're generating debug info. - } -# endif // ENABLE_RR_DEBUG_INFO - - std::unique_ptr<llvm::legacy::PassManager> passManager( - new llvm::legacy::PassManager()); - - for(auto pass : cfg.getOptimization().getPasses()) - { - switch(pass) - { - case rr::Optimization::Pass::Disabled: break; - case rr::Optimization::Pass::CFGSimplification: passManager->add(llvm::createCFGSimplificationPass()); break; - case rr::Optimization::Pass::LICM: passManager->add(llvm::createLICMPass()); break; - case rr::Optimization::Pass::AggressiveDCE: passManager->add(llvm::createAggressiveDCEPass()); break; - case rr::Optimization::Pass::GVN: passManager->add(llvm::createGVNPass()); break; - case rr::Optimization::Pass::InstructionCombining: passManager->add(llvm::createInstructionCombiningPass()); break; - case rr::Optimization::Pass::Reassociate: passManager->add(llvm::createReassociatePass()); break; - case rr::Optimization::Pass::DeadStoreElimination: passManager->add(llvm::createDeadStoreEliminationPass()); break; - case rr::Optimization::Pass::SCCP: passManager->add(llvm::createSCCPPass()); break; - case rr::Optimization::Pass::ScalarReplAggregates: passManager->add(llvm::createSROAPass()); break; - case rr::Optimization::Pass::EarlyCSEPass: passManager->add(llvm::createEarlyCSEPass()); break; - default: - UNREACHABLE("pass: %d", int(pass)); - } - } - - passManager->run(*module); -} - -std::shared_ptr<rr::Routine> JITBuilder::acquireRoutine(llvm::Function **funcs, size_t count, const rr::Config &cfg) -{ - ASSERT(module); - return std::make_shared<JITRoutine>(std::move(module), funcs, count, cfg); -} - -} // namespace rr - -#endif // LLVM_VERSION_MAJOR >= 11