/*========================== begin_copyright_notice ============================

Copyright (C) 2017-2024 Intel Corporation

SPDX-License-Identifier: MIT

============================= end_copyright_notice ===========================*/

#include "AdaptorOCL/OCL/LoadBuffer.h"

#include "common/LLVMWarningsPush.hpp"
#include "llvm/Config/llvm-config.h"
#include "llvm/Support/MemoryBuffer.h"
#include "common/LLVMWarningsPop.hpp"

#include <cstdlib>

using namespace llvm;
#ifdef LLVM_ON_UNIX

#include "Probe/Assertion.h"
#include <dlfcn.h>
#include <stdio.h>

MemoryBuffer *llvm::LoadBufferFromResource(const char *pResName,
        const char *pResType)
{
    // Symbol Name is <type>_<number>
    char name[73];      // 64 + 9 for prefix
    char size_name[78]; // 64 + 9 for prefix + 5 for suffix
    void *module;
    void *symbol;
    uint32_t size;

    snprintf(name,      sizeof(name),      "_igc_bif_%s_%s",      pResType, &pResName[1]);
    snprintf(size_name, sizeof(size_name), "_igc_bif_%s_%s_size", pResType, &pResName[1]);

    module = RTLD_DEFAULT;

    symbol = dlsym(module, size_name);
    if (!symbol)
    {
        IGC_ASSERT_EXIT_MESSAGE(0, "LoadBufferFromResource: [%s]\n", dlerror());
        return NULL;
    }
    size = *(uint32_t *)symbol;

    symbol = dlsym(module, name);
    if (!symbol)
    {
        IGC_ASSERT_EXIT_MESSAGE(0, "LoadBufferFromResource: [%s]\n", dlerror());
        return NULL;
    }

    // Create a copy of the buffer for the caller. This copy is managed
    return MemoryBuffer::getMemBufferCopy(StringRef((char *)symbol, size)).release();
}

#endif

#ifdef WIN32
#include <Windows.h>
// Windows.h defines MemoryFence as _mm_mfence, but this conflicts with llvm::sys::MemoryFence
#undef MemoryFence

HRSRC FindResourceCompat(HMODULE hMod, const char * pResName, const char * pResType)
{
    std::wstring resNameW;
    std::wstring resTypeW;
    if (pResName != nullptr)
    {
        resNameW.assign(pResName, pResName + strlen(pResName));
    }
    if (pResType != nullptr)
    {
        resTypeW.assign(pResType, pResType + strlen(pResType));
    }

    return FindResourceExW(hMod,
                           pResType ? resTypeW.c_str() : nullptr,
                           pResName ? resNameW.c_str() : nullptr,
                           MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL));
}

MemoryBuffer *llvm::LoadBufferFromResource(const char *pResName,
  const char *pResType)
{
    HMODULE hMod = NULL;

    // Get the handle to the current module
    GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
        GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
        (LPCSTR)llvm::LoadBufferFromResource,
        &hMod);
    if (hMod == NULL)
    {
        return NULL;
    }

    // Locate the resource
    HRSRC hRes = FindResourceCompat(hMod, pResName, pResType);

    if (hRes == NULL)
    {
        return NULL;
    }

    // Load the resource
    HGLOBAL hBytes = LoadResource(hMod, hRes);
    if (hBytes == NULL)
    {
        return NULL;
    }

    // Get the base address to the resource. This call doesn't really lock it
    const char *pData = (char *)LockResource(hBytes);
    if (pData == NULL)
    {
        return NULL;
    }

    // Get the buffer size
    size_t dResSize = SizeofResource(hMod, hRes);
    if (dResSize == 0)
    {
        return NULL;
    }

    // this memory never needs to be freeded since it is not dynamically allocated
    return MemoryBuffer::getMemBuffer(StringRef(pData, dResSize), "", false).release();
}
#endif // LLVM_ON_WIN32

MemoryBuffer* llvm::LoadBufferFromFile( const std::string &FileName )
{
    std::string FullFileName(FileName);

    // If buffer is not on the current working directory try to load it from
    // a predetermined path
    char *pEnv = getenv("INTEL_OPENCL_GPU_DIR");
    FullFileName.insert(0, "Inc\\");
    if (pEnv) {
        FullFileName.insert(0, "\\");
        FullFileName.insert(0, pEnv);
    }

    ErrorOr<std::unique_ptr<MemoryBuffer>> FileOrErr =
        MemoryBuffer::getFileOrSTDIN(FullFileName);

    return FileOrErr.get().release();
}

