summaryrefslogtreecommitdiff
path: root/clang-r353983e/include/llvm/ExecutionEngine/Orc/IndirectionUtils.h
diff options
context:
space:
mode:
Diffstat (limited to 'clang-r353983e/include/llvm/ExecutionEngine/Orc/IndirectionUtils.h')
-rw-r--r--clang-r353983e/include/llvm/ExecutionEngine/Orc/IndirectionUtils.h493
1 files changed, 493 insertions, 0 deletions
diff --git a/clang-r353983e/include/llvm/ExecutionEngine/Orc/IndirectionUtils.h b/clang-r353983e/include/llvm/ExecutionEngine/Orc/IndirectionUtils.h
new file mode 100644
index 00000000..2ea9ac1c
--- /dev/null
+++ b/clang-r353983e/include/llvm/ExecutionEngine/Orc/IndirectionUtils.h
@@ -0,0 +1,493 @@
+//===- IndirectionUtils.h - Utilities for adding indirections ---*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Contains utilities for adding indirections and breaking up modules.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_EXECUTIONENGINE_ORC_INDIRECTIONUTILS_H
+#define LLVM_EXECUTIONENGINE_ORC_INDIRECTIONUTILS_H
+
+#include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/Twine.h"
+#include "llvm/ExecutionEngine/JITSymbol.h"
+#include "llvm/ExecutionEngine/Orc/Core.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/Memory.h"
+#include "llvm/Support/Process.h"
+#include "llvm/Transforms/Utils/ValueMapper.h"
+#include <algorithm>
+#include <cassert>
+#include <cstdint>
+#include <functional>
+#include <map>
+#include <memory>
+#include <system_error>
+#include <utility>
+#include <vector>
+
+namespace llvm {
+
+class Constant;
+class Function;
+class FunctionType;
+class GlobalAlias;
+class GlobalVariable;
+class Module;
+class PointerType;
+class Triple;
+class Value;
+
+namespace orc {
+
+/// Base class for pools of compiler re-entry trampolines.
+/// These trampolines are callable addresses that save all register state
+/// before calling a supplied function to return the trampoline landing
+/// address, then restore all state before jumping to that address. They
+/// are used by various ORC APIs to support lazy compilation
+class TrampolinePool {
+public:
+ virtual ~TrampolinePool() {}
+
+ /// Get an available trampoline address.
+ /// Returns an error if no trampoline can be created.
+ virtual Expected<JITTargetAddress> getTrampoline() = 0;
+
+private:
+ virtual void anchor();
+};
+
+/// A trampoline pool for trampolines within the current process.
+template <typename ORCABI> class LocalTrampolinePool : public TrampolinePool {
+public:
+ using GetTrampolineLandingFunction =
+ std::function<JITTargetAddress(JITTargetAddress TrampolineAddr)>;
+
+ /// Creates a LocalTrampolinePool with the given RunCallback function.
+ /// Returns an error if this function is unable to correctly allocate, write
+ /// and protect the resolver code block.
+ static Expected<std::unique_ptr<LocalTrampolinePool>>
+ Create(GetTrampolineLandingFunction GetTrampolineLanding) {
+ Error Err = Error::success();
+
+ auto LTP = std::unique_ptr<LocalTrampolinePool>(
+ new LocalTrampolinePool(std::move(GetTrampolineLanding), Err));
+
+ if (Err)
+ return std::move(Err);
+ return std::move(LTP);
+ }
+
+ /// Get a free trampoline. Returns an error if one can not be provide (e.g.
+ /// because the pool is empty and can not be grown).
+ Expected<JITTargetAddress> getTrampoline() override {
+ std::lock_guard<std::mutex> Lock(LTPMutex);
+ if (AvailableTrampolines.empty()) {
+ if (auto Err = grow())
+ return std::move(Err);
+ }
+ assert(!AvailableTrampolines.empty() && "Failed to grow trampoline pool");
+ auto TrampolineAddr = AvailableTrampolines.back();
+ AvailableTrampolines.pop_back();
+ return TrampolineAddr;
+ }
+
+ /// Returns the given trampoline to the pool for re-use.
+ void releaseTrampoline(JITTargetAddress TrampolineAddr) {
+ std::lock_guard<std::mutex> Lock(LTPMutex);
+ AvailableTrampolines.push_back(TrampolineAddr);
+ }
+
+private:
+ static JITTargetAddress reenter(void *TrampolinePoolPtr, void *TrampolineId) {
+ LocalTrampolinePool<ORCABI> *TrampolinePool =
+ static_cast<LocalTrampolinePool *>(TrampolinePoolPtr);
+ return TrampolinePool->GetTrampolineLanding(static_cast<JITTargetAddress>(
+ reinterpret_cast<uintptr_t>(TrampolineId)));
+ }
+
+ LocalTrampolinePool(GetTrampolineLandingFunction GetTrampolineLanding,
+ Error &Err)
+ : GetTrampolineLanding(std::move(GetTrampolineLanding)) {
+
+ ErrorAsOutParameter _(&Err);
+
+ /// Try to set up the resolver block.
+ std::error_code EC;
+ ResolverBlock = sys::OwningMemoryBlock(sys::Memory::allocateMappedMemory(
+ ORCABI::ResolverCodeSize, nullptr,
+ sys::Memory::MF_READ | sys::Memory::MF_WRITE, EC));
+ if (EC) {
+ Err = errorCodeToError(EC);
+ return;
+ }
+
+ ORCABI::writeResolverCode(static_cast<uint8_t *>(ResolverBlock.base()),
+ &reenter, this);
+
+ EC = sys::Memory::protectMappedMemory(ResolverBlock.getMemoryBlock(),
+ sys::Memory::MF_READ |
+ sys::Memory::MF_EXEC);
+ if (EC) {
+ Err = errorCodeToError(EC);
+ return;
+ }
+ }
+
+ Error grow() {
+ assert(this->AvailableTrampolines.empty() && "Growing prematurely?");
+
+ std::error_code EC;
+ auto TrampolineBlock =
+ sys::OwningMemoryBlock(sys::Memory::allocateMappedMemory(
+ sys::Process::getPageSize(), nullptr,
+ sys::Memory::MF_READ | sys::Memory::MF_WRITE, EC));
+ if (EC)
+ return errorCodeToError(EC);
+
+ unsigned NumTrampolines =
+ (sys::Process::getPageSize() - ORCABI::PointerSize) /
+ ORCABI::TrampolineSize;
+
+ uint8_t *TrampolineMem = static_cast<uint8_t *>(TrampolineBlock.base());
+ ORCABI::writeTrampolines(TrampolineMem, ResolverBlock.base(),
+ NumTrampolines);
+
+ for (unsigned I = 0; I < NumTrampolines; ++I)
+ this->AvailableTrampolines.push_back(
+ static_cast<JITTargetAddress>(reinterpret_cast<uintptr_t>(
+ TrampolineMem + (I * ORCABI::TrampolineSize))));
+
+ if (auto EC = sys::Memory::protectMappedMemory(
+ TrampolineBlock.getMemoryBlock(),
+ sys::Memory::MF_READ | sys::Memory::MF_EXEC))
+ return errorCodeToError(EC);
+
+ TrampolineBlocks.push_back(std::move(TrampolineBlock));
+ return Error::success();
+ }
+
+ GetTrampolineLandingFunction GetTrampolineLanding;
+
+ std::mutex LTPMutex;
+ sys::OwningMemoryBlock ResolverBlock;
+ std::vector<sys::OwningMemoryBlock> TrampolineBlocks;
+ std::vector<JITTargetAddress> AvailableTrampolines;
+};
+
+/// Target-independent base class for compile callback management.
+class JITCompileCallbackManager {
+public:
+ using CompileFunction = std::function<JITTargetAddress()>;
+
+ virtual ~JITCompileCallbackManager() = default;
+
+ /// Reserve a compile callback.
+ Expected<JITTargetAddress> getCompileCallback(CompileFunction Compile);
+
+ /// Execute the callback for the given trampoline id. Called by the JIT
+ /// to compile functions on demand.
+ JITTargetAddress executeCompileCallback(JITTargetAddress TrampolineAddr);
+
+protected:
+ /// Construct a JITCompileCallbackManager.
+ JITCompileCallbackManager(std::unique_ptr<TrampolinePool> TP,
+ ExecutionSession &ES,
+ JITTargetAddress ErrorHandlerAddress)
+ : TP(std::move(TP)), ES(ES),
+ CallbacksJD(ES.createJITDylib("<Callbacks>")),
+ ErrorHandlerAddress(ErrorHandlerAddress) {}
+
+ void setTrampolinePool(std::unique_ptr<TrampolinePool> TP) {
+ this->TP = std::move(TP);
+ }
+
+private:
+ std::mutex CCMgrMutex;
+ std::unique_ptr<TrampolinePool> TP;
+ ExecutionSession &ES;
+ JITDylib &CallbacksJD;
+ JITTargetAddress ErrorHandlerAddress;
+ std::map<JITTargetAddress, SymbolStringPtr> AddrToSymbol;
+ size_t NextCallbackId = 0;
+};
+
+/// Manage compile callbacks for in-process JITs.
+template <typename ORCABI>
+class LocalJITCompileCallbackManager : public JITCompileCallbackManager {
+public:
+ /// Create a new LocalJITCompileCallbackManager.
+ static Expected<std::unique_ptr<LocalJITCompileCallbackManager>>
+ Create(ExecutionSession &ES, JITTargetAddress ErrorHandlerAddress) {
+ Error Err = Error::success();
+ auto CCMgr = std::unique_ptr<LocalJITCompileCallbackManager>(
+ new LocalJITCompileCallbackManager(ES, ErrorHandlerAddress, Err));
+ if (Err)
+ return std::move(Err);
+ return std::move(CCMgr);
+ }
+
+private:
+ /// Construct a InProcessJITCompileCallbackManager.
+ /// @param ErrorHandlerAddress The address of an error handler in the target
+ /// process to be used if a compile callback fails.
+ LocalJITCompileCallbackManager(ExecutionSession &ES,
+ JITTargetAddress ErrorHandlerAddress,
+ Error &Err)
+ : JITCompileCallbackManager(nullptr, ES, ErrorHandlerAddress) {
+ ErrorAsOutParameter _(&Err);
+ auto TP = LocalTrampolinePool<ORCABI>::Create(
+ [this](JITTargetAddress TrampolineAddr) {
+ return executeCompileCallback(TrampolineAddr);
+ });
+
+ if (!TP) {
+ Err = TP.takeError();
+ return;
+ }
+
+ setTrampolinePool(std::move(*TP));
+ }
+};
+
+/// Base class for managing collections of named indirect stubs.
+class IndirectStubsManager {
+public:
+ /// Map type for initializing the manager. See init.
+ using StubInitsMap = StringMap<std::pair<JITTargetAddress, JITSymbolFlags>>;
+
+ virtual ~IndirectStubsManager() = default;
+
+ /// Create a single stub with the given name, target address and flags.
+ virtual Error createStub(StringRef StubName, JITTargetAddress StubAddr,
+ JITSymbolFlags StubFlags) = 0;
+
+ /// Create StubInits.size() stubs with the given names, target
+ /// addresses, and flags.
+ virtual Error createStubs(const StubInitsMap &StubInits) = 0;
+
+ /// Find the stub with the given name. If ExportedStubsOnly is true,
+ /// this will only return a result if the stub's flags indicate that it
+ /// is exported.
+ virtual JITEvaluatedSymbol findStub(StringRef Name, bool ExportedStubsOnly) = 0;
+
+ /// Find the implementation-pointer for the stub.
+ virtual JITEvaluatedSymbol findPointer(StringRef Name) = 0;
+
+ /// Change the value of the implementation pointer for the stub.
+ virtual Error updatePointer(StringRef Name, JITTargetAddress NewAddr) = 0;
+
+private:
+ virtual void anchor();
+};
+
+/// IndirectStubsManager implementation for the host architecture, e.g.
+/// OrcX86_64. (See OrcArchitectureSupport.h).
+template <typename TargetT>
+class LocalIndirectStubsManager : public IndirectStubsManager {
+public:
+ Error createStub(StringRef StubName, JITTargetAddress StubAddr,
+ JITSymbolFlags StubFlags) override {
+ std::lock_guard<std::mutex> Lock(StubsMutex);
+ if (auto Err = reserveStubs(1))
+ return Err;
+
+ createStubInternal(StubName, StubAddr, StubFlags);
+
+ return Error::success();
+ }
+
+ Error createStubs(const StubInitsMap &StubInits) override {
+ std::lock_guard<std::mutex> Lock(StubsMutex);
+ if (auto Err = reserveStubs(StubInits.size()))
+ return Err;
+
+ for (auto &Entry : StubInits)
+ createStubInternal(Entry.first(), Entry.second.first,
+ Entry.second.second);
+
+ return Error::success();
+ }
+
+ JITEvaluatedSymbol findStub(StringRef Name, bool ExportedStubsOnly) override {
+ std::lock_guard<std::mutex> Lock(StubsMutex);
+ auto I = StubIndexes.find(Name);
+ if (I == StubIndexes.end())
+ return nullptr;
+ auto Key = I->second.first;
+ void *StubAddr = IndirectStubsInfos[Key.first].getStub(Key.second);
+ assert(StubAddr && "Missing stub address");
+ auto StubTargetAddr =
+ static_cast<JITTargetAddress>(reinterpret_cast<uintptr_t>(StubAddr));
+ auto StubSymbol = JITEvaluatedSymbol(StubTargetAddr, I->second.second);
+ if (ExportedStubsOnly && !StubSymbol.getFlags().isExported())
+ return nullptr;
+ return StubSymbol;
+ }
+
+ JITEvaluatedSymbol findPointer(StringRef Name) override {
+ std::lock_guard<std::mutex> Lock(StubsMutex);
+ auto I = StubIndexes.find(Name);
+ if (I == StubIndexes.end())
+ return nullptr;
+ auto Key = I->second.first;
+ void *PtrAddr = IndirectStubsInfos[Key.first].getPtr(Key.second);
+ assert(PtrAddr && "Missing pointer address");
+ auto PtrTargetAddr =
+ static_cast<JITTargetAddress>(reinterpret_cast<uintptr_t>(PtrAddr));
+ return JITEvaluatedSymbol(PtrTargetAddr, I->second.second);
+ }
+
+ Error updatePointer(StringRef Name, JITTargetAddress NewAddr) override {
+ using AtomicIntPtr = std::atomic<uintptr_t>;
+
+ std::lock_guard<std::mutex> Lock(StubsMutex);
+ auto I = StubIndexes.find(Name);
+ assert(I != StubIndexes.end() && "No stub pointer for symbol");
+ auto Key = I->second.first;
+ AtomicIntPtr *AtomicStubPtr = reinterpret_cast<AtomicIntPtr *>(
+ IndirectStubsInfos[Key.first].getPtr(Key.second));
+ *AtomicStubPtr = static_cast<uintptr_t>(NewAddr);
+ return Error::success();
+ }
+
+private:
+ Error reserveStubs(unsigned NumStubs) {
+ if (NumStubs <= FreeStubs.size())
+ return Error::success();
+
+ unsigned NewStubsRequired = NumStubs - FreeStubs.size();
+ unsigned NewBlockId = IndirectStubsInfos.size();
+ typename TargetT::IndirectStubsInfo ISI;
+ if (auto Err =
+ TargetT::emitIndirectStubsBlock(ISI, NewStubsRequired, nullptr))
+ return Err;
+ for (unsigned I = 0; I < ISI.getNumStubs(); ++I)
+ FreeStubs.push_back(std::make_pair(NewBlockId, I));
+ IndirectStubsInfos.push_back(std::move(ISI));
+ return Error::success();
+ }
+
+ void createStubInternal(StringRef StubName, JITTargetAddress InitAddr,
+ JITSymbolFlags StubFlags) {
+ auto Key = FreeStubs.back();
+ FreeStubs.pop_back();
+ *IndirectStubsInfos[Key.first].getPtr(Key.second) =
+ reinterpret_cast<void *>(static_cast<uintptr_t>(InitAddr));
+ StubIndexes[StubName] = std::make_pair(Key, StubFlags);
+ }
+
+ std::mutex StubsMutex;
+ std::vector<typename TargetT::IndirectStubsInfo> IndirectStubsInfos;
+ using StubKey = std::pair<uint16_t, uint16_t>;
+ std::vector<StubKey> FreeStubs;
+ StringMap<std::pair<StubKey, JITSymbolFlags>> StubIndexes;
+};
+
+/// Create a local compile callback manager.
+///
+/// The given target triple will determine the ABI, and the given
+/// ErrorHandlerAddress will be used by the resulting compile callback
+/// manager if a compile callback fails.
+Expected<std::unique_ptr<JITCompileCallbackManager>>
+createLocalCompileCallbackManager(const Triple &T, ExecutionSession &ES,
+ JITTargetAddress ErrorHandlerAddress);
+
+/// Create a local indriect stubs manager builder.
+///
+/// The given target triple will determine the ABI.
+std::function<std::unique_ptr<IndirectStubsManager>()>
+createLocalIndirectStubsManagerBuilder(const Triple &T);
+
+/// Build a function pointer of FunctionType with the given constant
+/// address.
+///
+/// Usage example: Turn a trampoline address into a function pointer constant
+/// for use in a stub.
+Constant *createIRTypedAddress(FunctionType &FT, JITTargetAddress Addr);
+
+/// Create a function pointer with the given type, name, and initializer
+/// in the given Module.
+GlobalVariable *createImplPointer(PointerType &PT, Module &M, const Twine &Name,
+ Constant *Initializer);
+
+/// Turn a function declaration into a stub function that makes an
+/// indirect call using the given function pointer.
+void makeStub(Function &F, Value &ImplPointer);
+
+/// Promotes private symbols to global hidden, and renames to prevent clashes
+/// with other promoted symbols. The same SymbolPromoter instance should be
+/// used for all symbols to be added to a single JITDylib.
+class SymbolLinkagePromoter {
+public:
+ /// Promote symbols in the given module. Returns the set of global values
+ /// that have been renamed/promoted.
+ std::vector<GlobalValue *> operator()(Module &M);
+
+private:
+ unsigned NextId = 0;
+};
+
+/// Clone a function declaration into a new module.
+///
+/// This function can be used as the first step towards creating a callback
+/// stub (see makeStub), or moving a function body (see moveFunctionBody).
+///
+/// If the VMap argument is non-null, a mapping will be added between F and
+/// the new declaration, and between each of F's arguments and the new
+/// declaration's arguments. This map can then be passed in to moveFunction to
+/// move the function body if required. Note: When moving functions between
+/// modules with these utilities, all decls should be cloned (and added to a
+/// single VMap) before any bodies are moved. This will ensure that references
+/// between functions all refer to the versions in the new module.
+Function *cloneFunctionDecl(Module &Dst, const Function &F,
+ ValueToValueMapTy *VMap = nullptr);
+
+/// Move the body of function 'F' to a cloned function declaration in a
+/// different module (See related cloneFunctionDecl).
+///
+/// If the target function declaration is not supplied via the NewF parameter
+/// then it will be looked up via the VMap.
+///
+/// This will delete the body of function 'F' from its original parent module,
+/// but leave its declaration.
+void moveFunctionBody(Function &OrigF, ValueToValueMapTy &VMap,
+ ValueMaterializer *Materializer = nullptr,
+ Function *NewF = nullptr);
+
+/// Clone a global variable declaration into a new module.
+GlobalVariable *cloneGlobalVariableDecl(Module &Dst, const GlobalVariable &GV,
+ ValueToValueMapTy *VMap = nullptr);
+
+/// Move global variable GV from its parent module to cloned global
+/// declaration in a different module.
+///
+/// If the target global declaration is not supplied via the NewGV parameter
+/// then it will be looked up via the VMap.
+///
+/// This will delete the initializer of GV from its original parent module,
+/// but leave its declaration.
+void moveGlobalVariableInitializer(GlobalVariable &OrigGV,
+ ValueToValueMapTy &VMap,
+ ValueMaterializer *Materializer = nullptr,
+ GlobalVariable *NewGV = nullptr);
+
+/// Clone a global alias declaration into a new module.
+GlobalAlias *cloneGlobalAliasDecl(Module &Dst, const GlobalAlias &OrigA,
+ ValueToValueMapTy &VMap);
+
+/// Clone module flags metadata into the destination module.
+void cloneModuleFlagsMetadata(Module &Dst, const Module &Src,
+ ValueToValueMapTy &VMap);
+
+} // end namespace orc
+
+} // end namespace llvm
+
+#endif // LLVM_EXECUTIONENGINE_ORC_INDIRECTIONUTILS_H