packages feed

llvm-hs-9.0.0: src/LLVM/Internal/FFI/OrcJITC.cpp

// We ignore deprecations until we get around to updating to the new OrcJIT API
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#pragma clang diagnostic ignored "-Wdeprecated-declarations"

#include "llvm/Support/Error.h"

#include "LLVM/Internal/FFI/ErrorHandling.hpp"
#include "LLVM/Internal/FFI/OrcJIT.h"
#include "LLVM/Internal/FFI/Target.hpp"
#include "llvm/ExecutionEngine/JITSymbol.h"
#include "llvm/ExecutionEngine/Orc/CompileOnDemandLayer.h"
#include "llvm/ExecutionEngine/Orc/CompileUtils.h"
#include "llvm/ExecutionEngine/Orc/IRCompileLayer.h"
#include "llvm/ExecutionEngine/Orc/IRTransformLayer.h"
#include "llvm/ExecutionEngine/Orc/IndirectionUtils.h"
#include "llvm/ExecutionEngine/Orc/LambdaResolver.h"
#include "llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h"
#include "llvm/ExecutionEngine/SectionMemoryManager.h"
#include "llvm/IR/Mangler.h"

#include <type_traits>
#include <unordered_map>

#include "llvm-c/Object.h"
#include "llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h"

using namespace llvm;
using namespace orc;

static_assert(std::is_same<VModuleKey, uint64_t>::value,
              "VModuleKey should be uint64_t");

#define SYMBOL_CASE(x)                                                         \
    static_assert((unsigned)LLVMJITSymbolFlag##x ==                            \
                      (unsigned)llvm::JITSymbolFlags::FlagNames::x,            \
                  "JITSymbolFlag values should agree");
LLVM_HS_FOR_EACH_JIT_SYMBOL_FLAG(SYMBOL_CASE)

typedef std::shared_ptr<SymbolResolver> *LLVMSymbolResolverRef;

// We want to allow users to choose themselves which layers they want to use.
// However, the LLVM API requires that this is selected statically via template
// arguments. We convert this static polymorphism to runtime polymorphism by
// creating an LinkingLayer and a CompileLayer class which use virtual dispatch
// to select the concrete layer.

class LinkingLayer {
  public:
    using ObjectPtr = std::unique_ptr<MemoryBuffer>;
    virtual ~LinkingLayer(){};
    virtual Error addObject(VModuleKey k, ObjectPtr objBuffer) = 0;
    virtual Error removeObject(VModuleKey k) = 0;
    virtual JITSymbol findSymbol(StringRef name, bool exportedSymbolsOnly) = 0;
    virtual JITSymbol findSymbolIn(VModuleKey k, StringRef name,
                                   bool exportedSymbolsOnly) = 0;
    virtual Error emitAndFinalize(VModuleKey k) = 0;
};

template <typename T> class LinkingLayerT : public LinkingLayer {
  public:
    LinkingLayerT(T data_) : data(std::move(data_)) {}
    Error addObject(VModuleKey k, ObjectPtr objBuffer) override {
        return data.addObject(k, std::move(objBuffer));
    }
    Error removeObject(VModuleKey k) override { return data.removeObject(k); }
    JITSymbol findSymbol(StringRef name, bool exportedSymbolsOnly) override {
        return data.findSymbol(name, exportedSymbolsOnly);
    }
    JITSymbol findSymbolIn(VModuleKey k, StringRef name,
                           bool exportedSymbolsOnly) override {
        return data.findSymbolIn(k, name, exportedSymbolsOnly);
    }
    Error emitAndFinalize(VModuleKey k) override {
        return data.emitAndFinalize(k);
    }

  private:
    T data;
};

class CompileLayer {
  public:
    virtual ~CompileLayer(){};
    virtual JITSymbol findSymbol(StringRef Name, bool ExportedSymbolsOnly) = 0;
    virtual JITSymbol findSymbolIn(VModuleKey K, StringRef Name,
                                   bool ExportedSymbolsOnly) = 0;
    virtual Error addModule(VModuleKey K, std::unique_ptr<Module> Module) = 0;
    virtual Error removeModule(VModuleKey K) = 0;
};

template <typename T> class CompileLayerT : public CompileLayer {
  public:
    template <typename... Arg>
    CompileLayerT(Arg &&... arg) : data{std::forward<Arg>(arg)...} {}
    JITSymbol findSymbol(StringRef Name, bool ExportedSymbolsOnly) override {
        return data.findSymbol(Name, ExportedSymbolsOnly);
    }
    JITSymbol findSymbolIn(VModuleKey K, StringRef Name,
                           bool ExportedSymbolsOnly) override {
        return data.findSymbolIn(K, Name, ExportedSymbolsOnly);
    }
    Error addModule(VModuleKey K, std::unique_ptr<Module> Module) override {
        return data.addModule(K, std::move(Module));
    }
    Error removeModule(VModuleKey K) override { return data.removeModule(K); }

  private:
    T data;
};

typedef llvm::orc::LegacyCompileOnDemandLayer<CompileLayer> LLVMCompileOnDemandLayer;
typedef LLVMCompileOnDemandLayer *LLVMCompileOnDemandLayerRef;

typedef llvm::orc::LegacyIRTransformLayer<
    CompileLayer,
    std::function<std::unique_ptr<Module>(std::unique_ptr<Module>)>>
    LLVMIRTransformLayer;

typedef llvm::orc::JITCompileCallbackManager *LLVMJITCompileCallbackManagerRef;

typedef llvm::JITSymbol *LLVMJITSymbolRef;

typedef llvm::orc::IndirectStubsManager *LLVMIndirectStubsManagerRef;
typedef std::function<std::unique_ptr<llvm::orc::IndirectStubsManager>()>
    *LLVMIndirectStubsManagerBuilderRef;

static std::string mangle(StringRef name, LLVMTargetDataRef dataLayout) {
    std::string mangledName;
    {
        raw_string_ostream mangledNameStream(mangledName);
        Mangler::getNameWithPrefix(mangledNameStream, name,
                                   *unwrap(dataLayout));
    }
    return mangledName;
}

// LLVM doesn’t declare this function in a header so we need to copy it here
static inline object::OwningBinary<object::ObjectFile> *
unwrap(LLVMObjectFileRef OF) {
    return reinterpret_cast<object::OwningBinary<object::ObjectFile> *>(OF);
}

static JITSymbolFlags unwrap(LLVMJITSymbolFlags f) {
    JITSymbolFlags flags = JITSymbolFlags::None;
#define ENUM_CASE(x)                                                           \
    if (f & LLVMJITSymbolFlag##x)                                              \
        flags |= JITSymbolFlags::x;
    LLVM_HS_FOR_EACH_JIT_SYMBOL_FLAG(ENUM_CASE)
#undef ENUM_CASE
    return flags;
}

static LLVMJITSymbolFlags wrap(JITSymbolFlags f) {
    unsigned r = 0;
#define ENUM_CASE(x)                                                           \
    if (f & JITSymbolFlags::x)                       \
        r |= (unsigned)LLVMJITSymbolFlag##x;
    LLVM_HS_FOR_EACH_JIT_SYMBOL_FLAG(ENUM_CASE)
#undef ENUM_CASE
    return LLVMJITSymbolFlags(r);
}

extern "C" {

ExecutionSession *LLVM_Hs_createExecutionSession() {
    return new ExecutionSession();
}

void LLVM_Hs_disposeExecutionSession(ExecutionSession *es) { delete es; }

VModuleKey LLVM_Hs_allocateVModule(ExecutionSession *es) {
    return es->allocateVModule();
}

void LLVM_Hs_releaseVModule(ExecutionSession *es, VModuleKey k) {
    es->releaseVModule(k);
}

/* Constructor functions for the different compile layers */

CompileLayer *LLVM_Hs_createIRCompileLayer(LinkingLayer *linkingLayer,
                                           LLVMTargetMachineRef tm) {
    TargetMachine *tmm = unwrap(tm);
    return new CompileLayerT<LegacyIRCompileLayer<LinkingLayer, SimpleCompiler>>(
        LegacyIRCompileLayer<LinkingLayer, SimpleCompiler>(*linkingLayer,
                                                           SimpleCompiler(*tmm)));
}

CompileLayer *LLVM_Hs_createCompileOnDemandLayer(
    ExecutionSession *es, CompileLayer *compileLayer,
    LLVMSymbolResolverRef (*getSymbolResolver)(VModuleKey k),
    void (*setSymbolResolver)(VModuleKey k, LLVMSymbolResolverRef r),
    void (*partitioningFtor)(llvm::Function *, std::set<llvm::Function *> *set),
    LLVMJITCompileCallbackManagerRef callbackManager,
    LLVMIndirectStubsManagerBuilderRef stubsManager,
    LLVMBool cloneStubsIntoPartitions) {
    std::function<std::shared_ptr<SymbolResolver>(VModuleKey)>
        getSymbolResolverFn =
            [getSymbolResolver](VModuleKey k) { return *getSymbolResolver(k); };
    std::function<void(VModuleKey, std::shared_ptr<SymbolResolver>)>
        setSymbolResolverFn =
            [setSymbolResolver](VModuleKey k,
                                std::shared_ptr<SymbolResolver> r) {
                setSymbolResolver(k, new std::shared_ptr<SymbolResolver>(r));
            };
    return new CompileLayerT<LLVMCompileOnDemandLayer>(
        *es, *compileLayer, getSymbolResolverFn, setSymbolResolverFn,
        [partitioningFtor](llvm::Function &f) -> std::set<llvm::Function *> {
            std::set<llvm::Function *> result;
            partitioningFtor(&f, &result);
            return result;
        },
        *callbackManager, *stubsManager,
        static_cast<bool>(cloneStubsIntoPartitions));
}

CompileLayer *LLVM_Hs_createIRTransformLayer(CompileLayer *compileLayer,
                                             Module *(*transform)(Module *)) {
    std::function<std::unique_ptr<Module>(std::unique_ptr<Module>)> transform_ =
        [transform](std::unique_ptr<Module> module) {
            return std::unique_ptr<Module>(transform(module.release()));
        };
    return new CompileLayerT<LLVMIRTransformLayer>(*compileLayer, transform_);
}

/* Functions that work on all compile layers */

void LLVM_Hs_CompileLayer_dispose(CompileLayer *compileLayer) {
    delete compileLayer;
}

LLVMJITSymbolRef LLVM_Hs_CompileLayer_findSymbol(CompileLayer *compileLayer,
                                                 const char *name,
                                                 LLVMBool exportedSymbolsOnly) {
    JITSymbol symbol = compileLayer->findSymbol(name, exportedSymbolsOnly);
    return new JITSymbol(std::move(symbol));
}

LLVMJITSymbolRef
LLVM_Hs_CompileLayer_findSymbolIn(CompileLayer *compileLayer, VModuleKey k,
                                  const char *name,
                                  LLVMBool exportedSymbolsOnly) {
    JITSymbol symbol = compileLayer->findSymbolIn(k, name, exportedSymbolsOnly);
    return new JITSymbol(std::move(symbol));
}

void LLVM_Hs_CompileLayer_addModule(CompileLayer *compileLayer,
                                    LLVMTargetDataRef dataLayout, VModuleKey k,
                                    LLVMModuleRef module, char **errorMessage) {
    std::unique_ptr<Module> mod{unwrap(module)};
    if (mod->getDataLayout().isDefault()) {
        mod->setDataLayout(*unwrap(dataLayout));
    }
    if (auto err = compileLayer->addModule(k, std::move(mod))) {
        std::string errString = toString(std::move(err));
        *errorMessage = strdup(errString.c_str());
    }
    *errorMessage = nullptr;
}

void LLVM_Hs_CompileLayer_removeModule(CompileLayer *compileLayer,
                                       VModuleKey k) {
    if (compileLayer->removeModule(k)) {
        // TODO handle failure
    }
}

/* Constructor functions for the different object layers */

LinkingLayer *LLVM_Hs_createObjectLinkingLayer(
    ExecutionSession *es, LLVMSymbolResolverRef (*symbolResolver)(VModuleKey)) {
    return new LinkingLayerT<LegacyRTDyldObjectLinkingLayer>(
        LegacyRTDyldObjectLinkingLayer(*es, [symbolResolver](VModuleKey k) {
            return LegacyRTDyldObjectLinkingLayer::Resources{
                std::make_shared<SectionMemoryManager>(), *symbolResolver(k)};
        }));
}

/* Fuctions that work on all object layers */

void LLVM_Hs_LinkingLayer_dispose(LinkingLayer *linkingLayer) {
    delete linkingLayer;
}

void LLVM_Hs_disposeJITSymbol(LLVMJITSymbolRef symbol) { delete symbol; }

LLVMJITSymbolRef LLVM_Hs_LinkingLayer_findSymbol(LinkingLayer *linkingLayer,
                                                 const char *name,
                                                 LLVMBool exportedSymbolsOnly) {
    JITSymbol symbol = linkingLayer->findSymbol(name, exportedSymbolsOnly);
    return new JITSymbol(std::move(symbol));
}

LLVMJITSymbolRef
LLVM_Hs_LinkingLayer_findSymbolIn(LinkingLayer *linkingLayer, VModuleKey k,
                                  const char *name,
                                  LLVMBool exportedSymbolsOnly) {
    JITSymbol symbol = linkingLayer->findSymbolIn(k, name, exportedSymbolsOnly);
    return new JITSymbol(std::move(symbol));
}

LLVMSymbolResolverRef LLVM_Hs_createLambdaResolver(
    ExecutionSession *es,
    void (*rawResolverFn)(const char *, LLVMJITSymbolRef)) {
    std::function<JITSymbol(const std::string &name)> resolverFn =
        [rawResolverFn](const std::string &name) -> JITSymbol {
        JITSymbol symbol(nullptr);
        rawResolverFn(name.c_str(), &symbol);
        return symbol;
    };
    return new std::shared_ptr<SymbolResolver>(
        createLegacyLookupResolver(*es, resolverFn, [](Error err) {
            cantFail(std::move(err), "lookupFlags failed");
        }));
}

void LLVM_Hs_disposeSymbolResolver(LLVMSymbolResolverRef resolver) {
    delete resolver;
}

void LLVM_Hs_LinkingLayer_addObject(LinkingLayer *linkLayer, VModuleKey k,
                                    LLVMObjectFileRef objRef,
                                    char **errorMessage) {

    std::unique_ptr<MemoryBuffer> objBuffer =
        unwrap(objRef)->takeBinary().second;
    *errorMessage = nullptr;
    if (auto err = linkLayer->addObject(k, std::move(objBuffer))) {
        std::string error = toString(std::move(err));
        *errorMessage = strdup(error.c_str());
        return;
    }
}

JITTargetAddress LLVM_Hs_JITSymbol_getAddress(LLVMJITSymbolRef symbol,
                                              char **errorMessage) {
    *errorMessage = nullptr;
    if (auto addrOrErr = symbol->getAddress()) {
        return *addrOrErr;
    } else {
        std::string error = toString(addrOrErr.takeError());
        *errorMessage = strdup(error.c_str());
        return 0;
    }
}

LLVMJITSymbolFlags LLVM_Hs_JITSymbol_getFlags(LLVMJITSymbolRef symbol) {
    return wrap(symbol->getFlags());
}

const char *LLVM_Hs_JITSymbol_getErrorMsg(LLVMJITSymbolRef symbol) {
    if (!symbol) {
        Error err = symbol->takeError();
        return strdup(toString(std::move(err)).c_str());
    }
    return strdup("");
}

void LLVM_Hs_setJITSymbol(LLVMJITSymbolRef symbol, JITTargetAddress addr,
                          LLVMJITSymbolFlags flags) {
    *symbol = JITSymbol(addr, unwrap(flags));
}

void LLVM_Hs_getMangledSymbol(char **mangledSymbol, const char *symbol,
                              LLVMTargetDataRef dataLayout) {
    std::string mangled = mangle(symbol, dataLayout);
    *mangledSymbol = new char[mangled.size() + 1];
    strcpy(*mangledSymbol, mangled.c_str());
}

void LLVM_Hs_disposeMangledSymbol(char *mangledSymbol) {
    delete[] mangledSymbol;
}

LLVMJITCompileCallbackManagerRef LLVM_Hs_createLocalCompileCallbackManager(
    ExecutionSession *es, const char *triple, JITTargetAddress errorHandler) {
    // We copy the string so that it can be freed on the Haskell side.
    std::string tripleStr(triple);
    auto ccMgr =
        llvm::orc::createLocalCompileCallbackManager(Triple(std::move(tripleStr)), *es, errorHandler);
    if (!ccMgr) {
        std::string errMsg;
        raw_string_ostream errStream(errMsg);
        errStream << ccMgr.takeError();
        reportFatalError(errStream.str());
    }
    return std::move(*ccMgr).release();
}

void LLVM_Hs_disposeCallbackManager(
    LLVMJITCompileCallbackManagerRef callbackManager) {
    delete callbackManager;
}

LLVMIndirectStubsManagerBuilderRef
LLVM_Hs_createLocalIndirectStubsManagerBuilder(const char *triple) {
    // We copy the string so that it can be freed on the Haskell side.
    std::string tripleStr(triple);
    return new std::function<std::unique_ptr<IndirectStubsManager>()>(
        llvm::orc::createLocalIndirectStubsManagerBuilder(
            Triple(std::move(tripleStr))));
}

void LLVM_Hs_disposeIndirectStubsManagerBuilder(
    LLVMIndirectStubsManagerBuilderRef stubsManager) {
    delete stubsManager;
}

void LLVM_Hs_insertFun(std::set<llvm::Function *> *set, llvm::Function *f) {
    set->insert(f);
}
}