swr/rast: Start refactoring of builder/packetizer.
[mesa.git] / src / gallium / drivers / swr / rasterizer / jitter / JitManager.cpp
index de856c4a09504cc82d7bcd72b5321ae38b3c9b21..bfb1d2e0c8b078db98a616538894d336abe876aa 100644 (file)
 * Notes:
 * 
 ******************************************************************************/
-#if defined(_WIN32)
-#pragma warning(disable: 4800 4146 4244 4267 4355 4996)
-#endif
+#include "jit_pch.hpp"
 
-#include "jit_api.h"
 #include "JitManager.h"
+#include "jit_api.h"
 #include "fetch_jit.h"
 
-#if defined(_WIN32)
-#include "llvm/ADT/Triple.h"
-#endif
-#include "llvm/IR/Function.h"
-#include "llvm/Support/DynamicLibrary.h"
-
-#include "llvm/Support/MemoryBuffer.h"
-#include "llvm/Support/SourceMgr.h"
-
-#include "llvm/Analysis/CFGPrinter.h"
-#include "llvm/IRReader/IRReader.h"
-
-#if LLVM_USE_INTEL_JITEVENTS
-#include "llvm/ExecutionEngine/JITEventListener.h"
-#endif
-
 #include "core/state.h"
-#include "common/containers.hpp"
 
-#include "state_llvm.h"
+#include "gen_state_llvm.h"
 
 #include <sstream>
 #if defined(_WIN32)
 #define INTEL_OUTPUT_DIR "c:\\Intel"
 #define SWR_OUTPUT_DIR INTEL_OUTPUT_DIR "\\SWR"
 #define JITTER_OUTPUT_DIR SWR_OUTPUT_DIR "\\Jitter"
+#endif // _WIN32
+
+#if defined(__APPLE__) || defined(FORCE_LINUX) || defined(__linux__) || defined(__gnu_linux__)
+#include <pwd.h>
+#include <sys/stat.h>
 #endif
 
+
 using namespace llvm;
+using namespace SwrJit;
 
 //////////////////////////////////////////////////////////////////////////
 /// @brief Contructor for JitManager.
 /// @param simdWidth - SIMD width to be used in generated program.
-JitManager::JitManager(uint32_t simdWidth, const char *arch)
+JitManager::JitManager(uint32_t simdWidth, const char *arch, const char* core)
     : mContext(), mBuilder(mContext), mIsModuleFinalized(true), mJitNumber(0), mVWidth(simdWidth), mArch(arch)
 {
     InitializeNativeTarget();
@@ -82,78 +70,40 @@ JitManager::JitManager(uint32_t simdWidth, const char *arch)
     tOpts.AllowFPOpFusion = FPOpFusion::Fast;
     tOpts.NoInfsFPMath = false;
     tOpts.NoNaNsFPMath = false;
-    tOpts.UnsafeFPMath = true;
-#if defined(_DEBUG)
-#if LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR < 7
-    tOpts.NoFramePointerElim = true;
-#endif
-#endif
+    tOpts.UnsafeFPMath = false;
 
     //tOpts.PrintMachineCode    = true;
 
-    std::stringstream fnName("JitModule", std::ios_base::in | std::ios_base::out | std::ios_base::ate);
-    fnName << mJitNumber++;
-    std::unique_ptr<Module> newModule(new Module(fnName.str(), mContext));
-    mpCurrentModule = newModule.get();
+    mCore = std::string(core);
+    std::transform(mCore.begin(), mCore.end(), mCore.begin(), ::tolower);
 
-    auto &&EB = EngineBuilder(std::move(newModule));
-    EB.setTargetOptions(tOpts);
-    EB.setOptLevel(CodeGenOpt::Aggressive);
+    std::unique_ptr<Module> newModule(new Module("", mContext));
+    mpCurrentModule = newModule.get();
 
     StringRef hostCPUName;
 
-    // force JIT to use the same CPU arch as the rest of swr
-    if(mArch.AVX512F())
-    {
-        assert(0 && "Implement AVX512 jitter");
-        hostCPUName = sys::getHostCPUName();
-        if (mVWidth == 0)
-        {
-            mVWidth = 16;
-        }
-    }
-    else if(mArch.AVX2())
-    {
-        hostCPUName = StringRef("core-avx2");
-        if (mVWidth == 0)
-        {
-            mVWidth = 8;
-        }
-    }
-    else if(mArch.AVX())
-    {
-        if (mArch.F16C())
-        {
-            hostCPUName = StringRef("core-avx-i");
-        }
-        else
-        {
-            hostCPUName = StringRef("corei7-avx");
-        }
-        if (mVWidth == 0)
-        {
-            mVWidth = 8;
-        }
-    }
-    else
-    {
-        hostCPUName = sys::getHostCPUName();
-        if (mVWidth == 0)
-        {
-            mVWidth = 8; // 4?
-        }
-    }
-
-    EB.setMCPU(hostCPUName);
+    hostCPUName = sys::getHostCPUName();
 
 #if defined(_WIN32)
     // Needed for MCJIT on windows
     Triple hostTriple(sys::getProcessTriple());
-    hostTriple.setObjectFormat(Triple::ELF);
+    hostTriple.setObjectFormat(Triple::COFF);
     mpCurrentModule->setTargetTriple(hostTriple.getTriple());
 #endif // _WIN32
 
-    mpExec = EB.create();
+    auto optLevel = CodeGenOpt::Aggressive;
+
+    mpExec = EngineBuilder(std::move(newModule))
+        .setTargetOptions(tOpts)
+        .setOptLevel(optLevel)
+        .setMCPU(hostCPUName)
+        .create();
+
+    if (KNOB_JIT_ENABLE_CACHE)
+    {
+        mCache.Init(this, hostCPUName, optLevel);
+        mpExec->setObjectCache(&mCache);
+    }
 
 #if LLVM_USE_INTEL_JITEVENTS
     JITEventListener *vTune = JITEventListener::createIntelJITEventListener();
@@ -164,23 +114,41 @@ JitManager::JitManager(uint32_t simdWidth, const char *arch)
     mInt8Ty = Type::getInt8Ty(mContext);
     mInt32Ty = Type::getInt32Ty(mContext);   // int type
     mInt64Ty = Type::getInt64Ty(mContext);   // int type
-    mV4FP32Ty = StructType::get(mContext, std::vector<Type*>(4, mFP32Ty), false); // vector4 float type (represented as structure)
-    mV4Int32Ty = StructType::get(mContext, std::vector<Type*>(4, mInt32Ty), false); // vector4 int type
 
     // fetch function signature
+#if USE_SIMD16_SHADERS
+    // typedef void(__cdecl *PFN_FETCH_FUNC)(SWR_FETCH_CONTEXT& fetchInfo, simd16vertex& out);
+#else
     // typedef void(__cdecl *PFN_FETCH_FUNC)(SWR_FETCH_CONTEXT& fetchInfo, simdvertex& out);
+#endif
     std::vector<Type*> fsArgs;
+
+    // llvm5 is picky and does not take a void * type
+    fsArgs.push_back(PointerType::get(Gen_SWR_FETCH_CONTEXT(this), 0));
+
     fsArgs.push_back(PointerType::get(Gen_SWR_FETCH_CONTEXT(this), 0));
+#if USE_SIMD16_SHADERS
+    fsArgs.push_back(PointerType::get(Gen_simd16vertex(this), 0));
+#else
     fsArgs.push_back(PointerType::get(Gen_simdvertex(this), 0));
+#endif
 
     mFetchShaderTy = FunctionType::get(Type::getVoidTy(mContext), fsArgs, false);
 
     mSimtFP32Ty = VectorType::get(mFP32Ty, mVWidth);
     mSimtInt32Ty = VectorType::get(mInt32Ty, mVWidth);
 
-    mSimdVectorTy = StructType::get(mContext, std::vector<Type*>(4, mSimtFP32Ty), false);
-    mSimdVectorInt32Ty = StructType::get(mContext, std::vector<Type*>(4, mSimtInt32Ty), false);
+    mSimdVectorTy = ArrayType::get(mSimtFP32Ty, 4);
+    mSimdVectorInt32Ty = ArrayType::get(mSimtInt32Ty, 4);
+
+#if USE_SIMD16_SHADERS
+    mSimd16FP32Ty = ArrayType::get(mSimtFP32Ty, 2);
+    mSimd16Int32Ty = ArrayType::get(mSimtInt32Ty, 2);
+
+    mSimd16VectorFP32Ty = ArrayType::get(mSimd16FP32Ty, 4);
+    mSimd16VectorInt32Ty = ArrayType::get(mSimd16Int32Ty, 4);
 
+#endif
 #if defined(_WIN32)
     // explicitly instantiate used symbols from potentially staticly linked libs
     sys::DynamicLibrary::AddSymbol("exp2f", &exp2f);
@@ -193,16 +161,10 @@ JitManager::JitManager(uint32_t simdWidth, const char *arch)
 #if defined(_WIN32)
     if (KNOB_DUMP_SHADER_IR)
     {
-        CreateDirectory(INTEL_OUTPUT_DIR, NULL);
-        CreateDirectory(SWR_OUTPUT_DIR, NULL);
-        CreateDirectory(JITTER_OUTPUT_DIR, NULL);
+        CreateDirectoryPath(INTEL_OUTPUT_DIR);
+        CreateDirectoryPath(SWR_OUTPUT_DIR);
+        CreateDirectoryPath(JITTER_OUTPUT_DIR);
     }
-
-    ///@todo Figure out a better solution for this.
-    // Redirect stdin, stdout, and stderr to attached console.
-    freopen("CONIN$", "r", stdin);
-    freopen("CONOUT$", "w", stdout);
-    freopen("CONOUT$", "w", stderr);
 #endif
 }
 
@@ -212,14 +174,12 @@ void JitManager::SetupNewModule()
 {
     SWR_ASSERT(mIsModuleFinalized == true && "Current module is not finalized!");
     
-    std::stringstream fnName("JitModule", std::ios_base::in | std::ios_base::out | std::ios_base::ate);
-    fnName << mJitNumber++;
-    std::unique_ptr<Module> newModule(new Module(fnName.str(), mContext));
+    std::unique_ptr<Module> newModule(new Module("", mContext));
     mpCurrentModule = newModule.get();
 #if defined(_WIN32)
     // Needed for MCJIT on windows
     Triple hostTriple(sys::getProcessTriple());
-    hostTriple.setObjectFormat(Triple::ELF);
+    hostTriple.setObjectFormat(Triple::COFF);
     newModule->setTargetTriple(hostTriple.getTriple());
 #endif // _WIN32
 
@@ -227,65 +187,226 @@ void JitManager::SetupNewModule()
     mIsModuleFinalized = false;
 }
 
-//////////////////////////////////////////////////////////////////////////
-/// @brief Create new LLVM module from IR.
-bool JitManager::SetupModuleFromIR(const uint8_t *pIR)
+
+DIType* JitManager::CreateDebugStructType(StructType* pType, const std::string& name, DIFile* pFile, uint32_t lineNum,
+    const std::vector<std::pair<std::string, uint32_t>>& members)
 {
-    std::unique_ptr<MemoryBuffer> pMem = MemoryBuffer::getMemBuffer(StringRef((const char*)pIR), "");
+    DIBuilder builder(*mpCurrentModule);
+    SmallVector<Metadata*, 8> ElemTypes;
+    DataLayout DL = DataLayout(mpCurrentModule);
+    uint32_t size = DL.getTypeAllocSizeInBits(pType);
+    uint32_t alignment = DL.getABITypeAlignment(pType);
+    DINode::DIFlags flags = DINode::DIFlags::FlagPublic;
 
-    SMDiagnostic Err;
-    std::unique_ptr<Module> newModule = parseIR(pMem.get()->getMemBufferRef(), Err, mContext);
+    DICompositeType* pDIStructTy = builder.createStructType(pFile, name, pFile, lineNum, size, alignment,
+        flags, nullptr, builder.getOrCreateArray(ElemTypes));
 
-    if (newModule == nullptr)
+    // Register mapping now to break loops (in case struct contains itself or pointers to itself)
+    mDebugStructMap[pType] = pDIStructTy;
+
+    uint32_t idx = 0;
+    for (auto& elem : pType->elements())
     {
-        SWR_ASSERT(0, "Parse failed! Check Err for details.");
-        return false;
+        std::string name = members[idx].first;
+        uint32_t lineNum = members[idx].second;
+        size = DL.getTypeAllocSizeInBits(elem);
+        alignment = DL.getABITypeAlignment(elem);
+        uint32_t offset = DL.getStructLayout(pType)->getElementOffsetInBits(idx);
+        llvm::DIType* pDebugTy = GetDebugType(elem);
+        ElemTypes.push_back(builder.createMemberType(pDIStructTy, name, pFile, lineNum, size, alignment, offset, flags, pDebugTy));
+
+        idx++;
     }
 
-    mpCurrentModule = newModule.get();
-#if defined(_WIN32)
-    // Needed for MCJIT on windows
-    Triple hostTriple(sys::getProcessTriple());
-    hostTriple.setObjectFormat(Triple::ELF);
-    newModule->setTargetTriple(hostTriple.getTriple());
-#endif // _WIN32
+    pDIStructTy->replaceElements(builder.getOrCreateArray(ElemTypes));
+    return pDIStructTy;
+}
 
-    mpExec->addModule(std::move(newModule));
-    mIsModuleFinalized = false;
+DIType* JitManager::GetDebugArrayType(Type* pTy)
+{
+    DIBuilder builder(*mpCurrentModule);
+    DataLayout DL = DataLayout(mpCurrentModule);
+    ArrayType* pArrayTy = cast<ArrayType>(pTy);
+    uint32_t size = DL.getTypeAllocSizeInBits(pArrayTy);
+    uint32_t alignment = DL.getABITypeAlignment(pArrayTy);
+
+    SmallVector<Metadata*, 8> Elems;
+    Elems.push_back(builder.getOrCreateSubrange(0, pArrayTy->getNumElements()));
+    return builder.createArrayType(size, alignment, GetDebugType(pArrayTy->getElementType()), builder.getOrCreateArray(Elems));
+}
+
+// Create a DIType from llvm Type
+DIType* JitManager::GetDebugType(Type* pTy)
+{
+    DIBuilder builder(*mpCurrentModule);
+    Type::TypeID id = pTy->getTypeID();
+
+    switch (id)
+    {
+    case Type::VoidTyID: return builder.createUnspecifiedType("void"); break;
+    case Type::HalfTyID: return builder.createBasicType("float16", 16, dwarf::DW_ATE_float); break;
+    case Type::FloatTyID: return builder.createBasicType("float", 32, dwarf::DW_ATE_float); break;
+    case Type::DoubleTyID: return builder.createBasicType("double", 64, dwarf::DW_ATE_float); break;
+    case Type::IntegerTyID: return GetDebugIntegerType(pTy); break;
+    case Type::StructTyID: return GetDebugStructType(pTy); break;
+    case Type::ArrayTyID: return GetDebugArrayType(pTy); break;
+    case Type::PointerTyID: return builder.createPointerType(GetDebugType(pTy->getPointerElementType()), 64, 64); break;
+    case Type::VectorTyID: return GetDebugVectorType(pTy); break;
+    case Type::FunctionTyID: return GetDebugFunctionType(pTy); break;
+    default: SWR_ASSERT(false, "Unimplemented llvm type");
+    }
+    return nullptr;
+}
+
+// Create a DISubroutineType from an llvm FunctionType
+DIType* JitManager::GetDebugFunctionType(Type* pTy)
+{
+    SmallVector<Metadata*, 8> ElemTypes;
+    FunctionType* pFuncTy = cast<FunctionType>(pTy);
+    DIBuilder builder(*mpCurrentModule);
+
+    // Add result type
+    ElemTypes.push_back(GetDebugType(pFuncTy->getReturnType()));
+
+    // Add arguments
+    for (auto& param : pFuncTy->params())
+    {
+        ElemTypes.push_back(GetDebugType(param));
+    }
+
+    return builder.createSubroutineType(builder.getOrCreateTypeArray(ElemTypes));
+}
+
+DIType* JitManager::GetDebugIntegerType(Type* pTy)
+{
+    DIBuilder builder(*mpCurrentModule);
+    IntegerType* pIntTy = cast<IntegerType>(pTy);
+    switch (pIntTy->getBitWidth())
+    {
+    case 1: return builder.createBasicType("int1", 1, dwarf::DW_ATE_unsigned); break;
+    case 8: return builder.createBasicType("int8", 8, dwarf::DW_ATE_signed); break;
+    case 16: return builder.createBasicType("int16", 16, dwarf::DW_ATE_signed); break;
+    case 32: return builder.createBasicType("int", 32, dwarf::DW_ATE_signed); break;
+    case 64: return builder.createBasicType("int64", 64, dwarf::DW_ATE_signed); break;
+    case 128: return builder.createBasicType("int128", 128, dwarf::DW_ATE_signed); break;
+    default: SWR_ASSERT(false, "Unimplemented integer bit width");
+    }
+    return nullptr;
+}
+
+DIType* JitManager::GetDebugVectorType(Type* pTy)
+{
+    DIBuilder builder(*mpCurrentModule);
+    VectorType* pVecTy = cast<VectorType>(pTy);
+    DataLayout DL = DataLayout(mpCurrentModule);
+    uint32_t size = DL.getTypeAllocSizeInBits(pVecTy);
+    uint32_t alignment = DL.getABITypeAlignment(pVecTy);
+    SmallVector<Metadata*, 1> Elems;
+    Elems.push_back(builder.getOrCreateSubrange(0, pVecTy->getVectorNumElements()));
+
+    return builder.createVectorType(size, alignment, GetDebugType(pVecTy->getVectorElementType()), builder.getOrCreateArray(Elems));
 
-    return true;
 }
 
 //////////////////////////////////////////////////////////////////////////
-/// @brief Dump function to file.
-void JitManager::DumpToFile(Function *f, const char *fileName)
+/// @brief Dump function x86 assembly to file.
+/// @note This should only be called after the module has been jitted to x86 and the
+///       module will not be further accessed.
+void JitManager::DumpAsm(Function* pFunction, const char* fileName)
 {
     if (KNOB_DUMP_SHADER_IR)
     {
+
 #if defined(_WIN32)
         DWORD pid = GetCurrentProcessId();
-        TCHAR procname[MAX_PATH];
-        GetModuleFileName(NULL, procname, MAX_PATH);
+        char procname[MAX_PATH];
+        GetModuleFileNameA(NULL, procname, MAX_PATH);
         const char* pBaseName = strrchr(procname, '\\');
         std::stringstream outDir;
         outDir << JITTER_OUTPUT_DIR << pBaseName << "_" << pid << std::ends;
-        CreateDirectory(outDir.str().c_str(), NULL);
+        CreateDirectoryPath(outDir.str().c_str());
 #endif
 
+        std::error_code EC;
+        Module* pModule = pFunction->getParent();
+        const char *funcName = pFunction->getName().data();
+        char fName[256];
+#if defined(_WIN32)
+        sprintf(fName, "%s\\%s.%s.asm", outDir.str().c_str(), funcName, fileName);
+#else
+        sprintf(fName, "%s.%s.asm", funcName, fileName);
+#endif
+
+        raw_fd_ostream filestream(fName, EC, llvm::sys::fs::F_None);
+
+        legacy::PassManager* pMPasses = new legacy::PassManager();
+        auto* pTarget = mpExec->getTargetMachine();
+        pTarget->Options.MCOptions.AsmVerbose = true;
+        pTarget->addPassesToEmitFile(*pMPasses, filestream, TargetMachine::CGFT_AssemblyFile);
+        pMPasses->run(*pModule);
+        delete pMPasses;
+        pTarget->Options.MCOptions.AsmVerbose = false;
+    }
+}
+
+std::string JitManager::GetOutputDir()
+{
+#if defined(_WIN32)
+    DWORD pid = GetCurrentProcessId();
+    char procname[MAX_PATH];
+    GetModuleFileNameA(NULL, procname, MAX_PATH);
+    const char* pBaseName = strrchr(procname, '\\');
+    std::stringstream outDir;
+    outDir << JITTER_OUTPUT_DIR << pBaseName << "_" << pid;
+    CreateDirectoryPath(outDir.str().c_str());
+    return outDir.str();
+#endif
+    return "";
+}
+
+//////////////////////////////////////////////////////////////////////////
+/// @brief Dump function to file.
+void JitManager::DumpToFile(Module *M, const char *fileName)
+{
+    if (KNOB_DUMP_SHADER_IR)
+    {
+        std::string outDir = GetOutputDir();
+
+        std::error_code EC;
+        const char *funcName = M->getName().data();
+        char fName[256];
+#if defined(_WIN32)
+        sprintf(fName, "%s\\%s.%s.ll", outDir.c_str(), funcName, fileName);
+#else
+        sprintf(fName, "%s.%s.ll", funcName, fileName);
+#endif
+        raw_fd_ostream fd(fName, EC, llvm::sys::fs::F_None);
+        M->print(fd, nullptr);
+        fd.flush();
+    }
+}
+
+//////////////////////////////////////////////////////////////////////////
+/// @brief Dump function to file.
+void JitManager::DumpToFile(Function *f, const char *fileName)
+{
+    if (KNOB_DUMP_SHADER_IR)
+    {
+        std::string outDir = GetOutputDir();
+
         std::error_code EC;
         const char *funcName = f->getName().data();
         char fName[256];
 #if defined(_WIN32)
-        sprintf(fName, "%s\\%s.%s.ll", outDir.str().c_str(), funcName, fileName);
+        sprintf(fName, "%s\\%s.%s.ll", outDir.c_str(), funcName, fileName);
 #else
         sprintf(fName, "%s.%s.ll", funcName, fileName);
 #endif
         raw_fd_ostream fd(fName, EC, llvm::sys::fs::F_None);
-        Module* pModule = f->getParent();
-        pModule->print(fd, nullptr);
+        f->print(fd, nullptr);
 
 #if defined(_WIN32)
-        sprintf(fName, "%s\\cfg.%s.%s.dot", outDir.str().c_str(), funcName, fileName);
+        sprintf(fName, "%s\\cfg.%s.%s.dot", outDir.c_str(), funcName, fileName);
 #else
         sprintf(fName, "cfg.%s.%s.dot", funcName, fileName);
 #endif
@@ -300,18 +421,265 @@ void JitManager::DumpToFile(Function *f, const char *fileName)
 
 extern "C"
 {
+    bool g_DllActive = true;
+
     //////////////////////////////////////////////////////////////////////////
     /// @brief Create JIT context.
     /// @param simdWidth - SIMD width to be used in generated program.
-    HANDLE JITCALL JitCreateContext(uint32_t targetSimdWidth, const char* arch)
+    HANDLE JITCALL JitCreateContext(uint32_t targetSimdWidth, const char* arch, const char* core)
     {
-        return new JitManager(targetSimdWidth, arch);
+        return new JitManager(targetSimdWidth, arch, core);
     }
 
     //////////////////////////////////////////////////////////////////////////
     /// @brief Destroy JIT context.
     void JITCALL JitDestroyContext(HANDLE hJitContext)
     {
-        delete reinterpret_cast<JitManager*>(hJitContext);
+        if (g_DllActive)
+        {
+            delete reinterpret_cast<JitManager*>(hJitContext);
+        }
+    }
+}
+
+//////////////////////////////////////////////////////////////////////////
+/// JitCache
+//////////////////////////////////////////////////////////////////////////
+
+//////////////////////////////////////////////////////////////////////////
+/// JitCacheFileHeader
+//////////////////////////////////////////////////////////////////////////
+struct JitCacheFileHeader
+{
+    void Init(
+        uint32_t llCRC,
+        uint32_t objCRC,
+        const std::string& moduleID,
+        const std::string& cpu,
+        uint32_t optLevel,
+        uint64_t objSize)
+    {
+        m_objSize = objSize;
+        m_llCRC = llCRC;
+        m_objCRC = objCRC;
+        strncpy(m_ModuleID, moduleID.c_str(), JC_STR_MAX_LEN - 1);
+        m_ModuleID[JC_STR_MAX_LEN - 1] = 0;
+        strncpy(m_Cpu, cpu.c_str(), JC_STR_MAX_LEN - 1);
+        m_Cpu[JC_STR_MAX_LEN - 1] = 0;
+        m_optLevel = optLevel;
+    }
+
+
+    bool IsValid(uint32_t llCRC, const std::string& moduleID, const std::string& cpu, uint32_t optLevel)
+    {
+        if ((m_MagicNumber != JC_MAGIC_NUMBER) ||
+            (m_llCRC != llCRC) ||
+            (m_platformKey != JC_PLATFORM_KEY) ||
+            (m_optLevel != optLevel))
+        {
+            return false;
+        }
+
+        m_ModuleID[JC_STR_MAX_LEN - 1] = 0;
+        if (strncmp(moduleID.c_str(), m_ModuleID, JC_STR_MAX_LEN - 1))
+        {
+            return false;
+        }
+
+        m_Cpu[JC_STR_MAX_LEN - 1] = 0;
+        if (strncmp(cpu.c_str(), m_Cpu, JC_STR_MAX_LEN - 1))
+        {
+            return false;
+        }
+
+        return true;
+    }
+
+    uint64_t GetObjectSize() const { return m_objSize; }
+    uint64_t GetObjectCRC() const { return m_objCRC; }
+
+private:
+    static const uint64_t   JC_MAGIC_NUMBER = 0xfedcba9876543211ULL + 3;
+    static const size_t     JC_STR_MAX_LEN = 32;
+    static const uint32_t   JC_PLATFORM_KEY =
+        (LLVM_VERSION_MAJOR << 24)  |
+        (LLVM_VERSION_MINOR << 16)  |
+        (LLVM_VERSION_PATCH << 8)   |
+        ((sizeof(void*) > sizeof(uint32_t)) ? 1 : 0);
+
+    uint64_t m_MagicNumber = JC_MAGIC_NUMBER;
+    uint64_t m_objSize = 0;
+    uint32_t m_llCRC = 0;
+    uint32_t m_platformKey = JC_PLATFORM_KEY;
+    uint32_t m_objCRC = 0;
+    uint32_t m_optLevel = 0;
+    char m_ModuleID[JC_STR_MAX_LEN] = {};
+    char m_Cpu[JC_STR_MAX_LEN] = {};
+};
+
+static inline uint32_t ComputeModuleCRC(const llvm::Module* M)
+{
+    std::string bitcodeBuffer;
+    raw_string_ostream bitcodeStream(bitcodeBuffer);
+
+    llvm::WriteBitcodeToFile(M, bitcodeStream);
+    //M->print(bitcodeStream, nullptr, false);
+
+    bitcodeStream.flush();
+
+    return ComputeCRC(0, bitcodeBuffer.data(), bitcodeBuffer.size());
+}
+
+/// constructor
+JitCache::JitCache()
+{
+#if defined(__APPLE__) || defined(FORCE_LINUX) || defined(__linux__) || defined(__gnu_linux__)
+    if (strncmp(KNOB_JIT_CACHE_DIR.c_str(), "~/", 2) == 0) {
+        char *homedir;
+        if (!(homedir = getenv("HOME"))) {
+            homedir = getpwuid(getuid())->pw_dir;
+        }
+        mCacheDir = homedir;
+        mCacheDir += (KNOB_JIT_CACHE_DIR.c_str() + 1);
+    } else
+#endif
+    {
+        mCacheDir = KNOB_JIT_CACHE_DIR;
+    }
+}
+
+int ExecUnhookedProcess(const std::string& CmdLine, std::string* pStdOut, std::string* pStdErr)
+{
+    static const char *g_pEnv = "RASTY_DISABLE_HOOK=1\0";
+
+    return ExecCmd(CmdLine, g_pEnv, pStdOut, pStdErr);
+}
+
+
+/// notifyObjectCompiled - Provides a pointer to compiled code for Module M.
+void JitCache::notifyObjectCompiled(const llvm::Module *M, llvm::MemoryBufferRef Obj)
+{
+    const std::string& moduleID = M->getModuleIdentifier();
+    if (!moduleID.length())
+    {
+        return;
+    }
+
+    if (!llvm::sys::fs::exists(mCacheDir.str()) &&
+        llvm::sys::fs::create_directories(mCacheDir.str()))
+    {
+        SWR_INVALID("Unable to create directory: %s", mCacheDir.c_str());
+        return;
+    }
+
+    JitCacheFileHeader header;
+
+    llvm::SmallString<MAX_PATH> filePath = mCacheDir;
+    llvm::sys::path::append(filePath, moduleID);
+
+    llvm::SmallString<MAX_PATH> objPath = filePath;
+    objPath += JIT_OBJ_EXT;
+
+    {
+        std::error_code err;
+        llvm::raw_fd_ostream fileObj(objPath.c_str(), err, llvm::sys::fs::F_None);
+        fileObj << Obj.getBuffer();
+        fileObj.flush();
+    }
+
+
+    {
+        std::error_code err;
+        llvm::raw_fd_ostream fileObj(filePath.c_str(), err, llvm::sys::fs::F_None);
+
+        uint32_t objcrc = ComputeCRC(0, Obj.getBufferStart(), Obj.getBufferSize());
+
+        header.Init(mCurrentModuleCRC, objcrc, moduleID, mCpu, mOptLevel, Obj.getBufferSize());
+
+        fileObj.write((const char*)&header, sizeof(header));
+        fileObj.flush();
     }
 }
+
+/// Returns a pointer to a newly allocated MemoryBuffer that contains the
+/// object which corresponds with Module M, or 0 if an object is not
+/// available.
+std::unique_ptr<llvm::MemoryBuffer> JitCache::getObject(const llvm::Module* M)
+{
+    const std::string& moduleID = M->getModuleIdentifier();
+    mCurrentModuleCRC = ComputeModuleCRC(M);
+
+    if (!moduleID.length())
+    {
+        return nullptr;
+    }
+
+    if (!llvm::sys::fs::exists(mCacheDir))
+    {
+        return nullptr;
+    }
+
+    llvm::SmallString<MAX_PATH> filePath = mCacheDir;
+    llvm::sys::path::append(filePath, moduleID);
+
+    llvm::SmallString<MAX_PATH> objFilePath = filePath;
+    objFilePath += JIT_OBJ_EXT;
+
+    FILE* fpObjIn = nullptr;
+    FILE* fpIn = fopen(filePath.c_str(), "rb");
+    if (!fpIn)
+    {
+        return nullptr;
+    }
+
+    std::unique_ptr<llvm::MemoryBuffer> pBuf = nullptr;
+    do
+    {
+        JitCacheFileHeader header;
+        if (!fread(&header, sizeof(header), 1, fpIn))
+        {
+            break;
+        }
+
+        if (!header.IsValid(mCurrentModuleCRC, moduleID, mCpu, mOptLevel))
+        {
+            break;
+        }
+
+        fpObjIn = fopen(objFilePath.c_str(), "rb");
+        if (!fpObjIn)
+        {
+            break;
+        }
+
+#if LLVM_VERSION_MAJOR < 6
+        pBuf = llvm::MemoryBuffer::getNewUninitMemBuffer(size_t(header.GetObjectSize()));
+#else
+        pBuf = llvm::WritableMemoryBuffer::getNewUninitMemBuffer(size_t(header.GetObjectSize()));
+#endif
+        if (!fread(const_cast<char*>(pBuf->getBufferStart()), header.GetObjectSize(), 1, fpObjIn))
+        {
+            pBuf = nullptr;
+            break;
+        }
+
+        if (header.GetObjectCRC() != ComputeCRC(0, pBuf->getBufferStart(), pBuf->getBufferSize()))
+        {
+            SWR_TRACE("Invalid object cache file, ignoring: %s", filePath.c_str());
+            pBuf = nullptr;
+            break;
+        }
+
+    }
+    while (0);
+
+    fclose(fpIn);
+
+    if (fpObjIn)
+    {
+        fclose(fpObjIn);
+    }
+
+
+    return pBuf;
+}