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

Copyright (C) 2017-2026 Intel Corporation

SPDX-License-Identifier: MIT

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

#include "../headers/clang_tb.h"
#include "../headers/common_clang.h"
#include "../headers/RegistryAccess.h"

#include "../headers/resource.h"

#include "common/LLVMWarningsPush.hpp"
#include "llvm/Config/llvm-config.h"
#include "llvm/Bitcode/BitcodeReader.h"
#include "common/LLVMWarningsPop.hpp"
#include "iStdLib/utility.h"

#include "secure_mem.h"
#include "secure_string.h"
#include "common/igc_regkeys.hpp"
#include "common/igc_dump_paths.hpp"

#include "SPIRVExtensionsSupport.h"

#include <sstream>
#include <string>
#include <iomanip>

#include "Probe/Assertion.h"

#ifndef WIN32
#include <dlfcn.h>
#endif

#if defined(_WIN32)
#include <Windows.h>
#include "DriverStore.h"
#endif

using namespace llvm;
using namespace std;

// ElfReader related typedefs
using namespace CLElfLib;

static void ElfReaderDP(CElfReader *pElfReader) {
  if (pElfReader)
    CElfReader::Delete(pElfReader);
}
typedef unique_ptr<CElfReader, decltype(&ElfReaderDP)> CElfReaderPtr;

// ClangFE related typedefs
using namespace Intel::OpenCL::ClangFE;

namespace TC {
constexpr bool is64bit = sizeof(void *) == sizeof(uint64_t);

// Initialize IGC flags from env vars, registry, and -igc_opts on first call.
static void InitRegistryKeysOnce(const char *pOptions, STB_TranslateOutputArgs *pOutputArgs) {
  std::string igcOptsError;
  LoadRegistryKeys(pOptions, &igcOptsError);
  if (!igcOptsError.empty()) {
    pOutputArgs->ErrorString.append(igcOptsError);
  }
}
// Misc utility functions used only in the current module
namespace Utils {
// Replace \0 in input string with \n. This works around an issue in
// Clang where the error message is not generated for inputs that contain
// a non-ending \0
static char *NormalizeString(char *input, uint32_t size) {
  for (uint32_t i = 0; i < size; i++) {
    if (input[i] == '\0') {
      input[i] = '\n';
    }
  }
  input[size - 1] = '\0';
  return input;
}

// Translates the ClangFE results to STB Output results
static void FillOutputArgs(IOCLFEBinaryResult *pFEBinaryResult, STB_TranslateOutputArgs *pOutputArgs,
                           std::string &exceptString) {
  // fill the result structure
  uint32_t ErrorStringSize = (uint32_t)std::string(pFEBinaryResult->GetErrorLog()).length();
  if (ErrorStringSize > 0) {
    TC::CClangTranslationBlock::SetErrorString(pFEBinaryResult->GetErrorLog(), pOutputArgs);
  } else {
    pOutputArgs->ErrorString.clear();
  }
  uint32_t OutputSize = (uint32_t)pFEBinaryResult->GetIRSize();

  // we have to copy the result due to unfortunate design of STB_TranslateOutputArg interface
  // the better design would be for TranslateXXX calls to be responsible to allocate the outputArgs
  // interface entirely, and the client to be responsible to call outputArgs->release() to free it.
  // This way the implementation of TranslateXXX could be free to return inherited from outputArgs
  // class which could glue the outputArgs with other internal interfaces (like the one returned from
  // ::Compile method for example) without any buffer copy
  if (OutputSize > 0) {
    pOutputArgs->Output.resize(OutputSize);
    char *IRBegin = (char *)pFEBinaryResult->GetIR();
    char *IREnd = (char *)pFEBinaryResult->GetIR() + OutputSize;

    pOutputArgs->Output.assign(IRBegin, IREnd);
  }
}
} // namespace Utils

// Input parameters to the 3 function
struct TranslateClangArgs {
  TranslateClangArgs() : pszProgramSource(NULL), pPCHBuffer(NULL), uiPCHBufferSize(0), b32bit(!is64bit) {}

  // A pointer to main program's source (assumed one nullterminated string)
  const char *pszProgramSource;
  // array of additional input headers to be passed in memory
  std::vector<const char *> inputHeaders;
  // array of input headers names corresponding to pInputHeaders
  std::vector<const char *> inputHeadersNames;
  // optional pointer to the pch buffer
  const char *pPCHBuffer;
  // size of the pch buffer
  size_t uiPCHBufferSize;
  // OpenCL application supplied options
  std::string options;
  // optional extra options string usually supplied by runtime
  std::string optionsEx;
  // requested OCL version
  std::string oclVersion;
  // build for 32 bit
  bool b32bit;
};

bool CClangTranslationBlock::Create(const STB_CreateArgs *pCreateArgs, STB_TranslateOutputArgs *pOutputArgs,
                                    CClangTranslationBlock *&pTranslationBlock) {
  pTranslationBlock = new CClangTranslationBlock();
  if (!pTranslationBlock)
    return false;

#if defined(IGC_DEBUG_VARIABLES)
  debugString overrideNameBuffer = {0};
  const char *overrideName = "";
  // Reading igc_opts in ::Create() is not possible without IGC <-> NEO CIF interface modification
  // For now, read IGC env/registry directly.
  ReadIGCRegistry("LibClangOverride", overrideNameBuffer, sizeof(overrideNameBuffer), IGCFlagType_debugString);
  overrideName = overrideNameBuffer;
#endif

  bool success = pTranslationBlock->Initialize(pCreateArgs);
  if (success) {
#ifdef _WIN32
#ifdef COMMON_CLANG_LIB_FULL_NAME
    const char *moduleName = COMMON_CLANG_LIB_FULL_NAME;
#else
#error "Common clang name not defined"
#endif
#endif

    CCModuleStruct &CCModule = pTranslationBlock->m_CCModule;
    const char *moduleDescription = "Opencl-clang library";

#if defined(IGC_DEBUG_VARIABLES)
    if (overrideName[0] != '\0') {
      moduleDescription = "LibClangOverride library";
#ifdef _WIN32
      moduleName = overrideName;
#else
      CCModule.pModule = dlopen(overrideName, RTLD_LAZY | RTLD_LOCAL);
      if (CCModule.pModule) {
        CCModule.pCompile = (CCModuleStruct::PFcnCCCompile)dlsym(CCModule.pModule, "Compile");
        if (!CCModule.pCompile) {
          std::string err =
              std::string("Error: ") + moduleDescription + " '" + overrideName + "' does not export Compile.";
          SetErrorString(err.c_str(), pOutputArgs);
          success = false;
        }
      } else {
        std::string err = std::string("Error: ") + moduleDescription + " '" + overrideName + "' failed to load.";
        SetErrorString(err.c_str(), pOutputArgs);
        success = false;
      }
#endif
    }
#endif // IGC_DEBUG_VARIABLES

#ifdef _WIN32
    if (success && CCModule.pCompile == nullptr) {
      // Load the Common Clang library
      // Both Win32 and Win64
      // load dependency only on RS
      if (GetWinVer() >= OS_WIN_RS) {
        CCModule.pModule = LoadDependency(moduleName);
      } else {
        CCModule.pModule = LoadLibraryA(moduleName);
      }
      if (CCModule.pModule != nullptr) {
        CCModule.pCompile = (CCModuleStruct::PFcnCCCompile)GetProcAddress((HMODULE)CCModule.pModule, "Compile");
        success = CCModule.pCompile != NULL;
        if (!success) {
          std::string err =
              std::string("Error: ") + moduleDescription + " '" + moduleName + "' does not export Compile.";
          SetErrorString(err.c_str(), pOutputArgs);
        }
      } else {
        std::string err = std::string("Error: ") + moduleDescription + " '" + moduleName + "' failed to load.";
        SetErrorString(err.c_str(), pOutputArgs);
        success = false;
      }
    }
#endif
  }

  if (!success) {
    CClangTranslationBlock::Delete(pTranslationBlock);
  }

  return success;
}

void CClangTranslationBlock::Delete(CClangTranslationBlock *&pTranslationBlock) {
  if (pTranslationBlock->m_CCModule.pModule) {
#ifdef _WIN32
    // Unload the Common Clang library
    // Both Win32 and Win64
    FreeLibrary((HMODULE)pTranslationBlock->m_CCModule.pModule);
#else
    dlclose(pTranslationBlock->m_CCModule.pModule);
#endif
  }

  delete pTranslationBlock;
  pTranslationBlock = NULL;
}

// Given an error string, mallocs memory for the string and sets the
// appropriate STB_TranslateOutputArgs fields.
void CClangTranslationBlock::SetErrorString(const char *pErrorString, STB_TranslateOutputArgs *pOutputArgs) {
  IGC_ASSERT(pErrorString != NULL);
  IGC_ASSERT(pOutputArgs != NULL);
  pOutputArgs->ErrorString = pErrorString;
}

// Parses the given internal options and return the OCL Version to be used
// for Clang compilation. If OCL version was not specified in internal options
// returns the default OCL version for the device
std::string CClangTranslationBlock::GetOclApiVersion(const char *pInternalOptions) const {
  static const char *OCL_VERSION_OPT = "-ocl-version=";
  static size_t OCL_VERSION_OPT_SIZE = std::string(OCL_VERSION_OPT).length();

  if (pInternalOptions) {
    const char *pszOpt = strstr(pInternalOptions, OCL_VERSION_OPT);
    if (NULL != pszOpt) {
      // we are in control of internal option - assertion test the validity
      IGC_ASSERT(std::string(pszOpt + OCL_VERSION_OPT_SIZE).length() >= 3);
      return std::string(pszOpt + OCL_VERSION_OPT_SIZE, 3);
    }
  }

  return m_OCL_Ver;
}

// In case the '-force-cl-std' options was specified, check that the user
// requested OCL C version isn't higher than the supported OCL version.
// exception is thrown otherwise
static unsigned int GetOclCVersionFromOptions(const char *pOptions, const char *pInternalOptions,
                                              const std::string &oclVersion /*OCL runtime API version*/,
                                              std::string &exceptString) {
  exceptString.clear();

  if (pOptions == nullptr) {
    return 0; // no options (i.e. no options from client application)
  }

  std::string optName = "-cl-std="; // opt that we are looking for
  unsigned int device_version = std::stoi(oclVersion);

  const char *optSubstring = strstr(pOptions, optName.c_str());
  if (optSubstring == nullptr) {
    return 0; // -cl-std not specified
  }

  bool validate = true;
  if ((pInternalOptions != nullptr) && (strstr(pInternalOptions, "-force-cl-std") != nullptr)) {
    // we're forcing the -cl-std version internally, so no need for validating it
    validate = false;
  }

  const char *optValue = optSubstring + optName.size();
  const char *end = optValue + std::string(optValue).length();

  // parse
  const std::string invalidFormatMessage = "Invalid format of -cl-std option, expected -cl-std=CLMAJOR.MINOR";
  auto isNumeric = [](char v) { return (v >= '0') && (v <= '9'); };
  if (false == ((end - optValue >= 5) && (optValue[0] == 'C') && (optValue[1] == 'L') && isNumeric(optValue[2]) &&
                (optValue[3] == '.') && isNumeric(optValue[4]))) {
    exceptString = invalidFormatMessage;
    return 0;
  }

  unsigned int retVersion = 0;
  // subverions
  if ((end - optValue >= 7) && (optValue[5] != ' ')) {
    if ((optValue[5] == '.') || isNumeric(optValue[6])) {
      retVersion += optValue[6] - '0';
    } else if (isNumeric(optValue[5])) {
      retVersion += optValue[5] - '0';
    } else {
      exceptString = invalidFormatMessage;
      return 0;
    }
  }

  retVersion += 100 * (optValue[2] - '0') + 10 * (optValue[4] - '0');

  if (validate == false) {
    return retVersion;
  }

  if (device_version < retVersion) {
    exceptString = "-cl-std OpenCLC version greater than OpenCL (API) version";
    return 0;
  }

  return retVersion;
}

// Return true if clang should generate 32bit code
static bool IsBuildingFor32bit(const char *pInternalOptions) {
  // Detect pointer size from internal option string. Default to using the
  // architecture type if the string is unavailable.
  if (pInternalOptions != NULL) {
    if (strstr(pInternalOptions, "-m32") != NULL) {
      return true;
    }

    if (strstr(pInternalOptions, "-m64") != NULL) {
      return false;
    }
  }

  return !is64bit;
}

// Returns true if CommonClang used on current OS has VME types defined.
static bool AreVMETypesDefined() {
#ifdef VME_TYPES_DEFINED
#if VME_TYPES_DEFINED
  return true;
#else
  return false;
#endif
#endif
  return true;
}

// Returns true if the input is SYCL source, indicated by the '-x sycl' option.
// For SYCL input the OpenCL-specific options (extension/feature macro defines,
// the custom OpenCL header, etc.) must not be passed into the frontend.
static bool IsBuildingForSYCL(const std::string &options) { return options.find("-x sycl") != std::string::npos; }

// Prepares the arguments for the TranslateClang method for the given text input
void CClangTranslationBlock::GetTranslateClangArgs(char *pInput, uint32_t uiInputSize, const char *pOptions,
                                                   const char *pInternalOptions, TranslateClangArgs *pClangArgs,
                                                   std::string &exceptString) {
  pClangArgs->pszProgramSource = Utils::NormalizeString(pInput, uiInputSize);
  pClangArgs->pPCHBuffer = NULL;
  pClangArgs->uiPCHBufferSize = 0;
  pClangArgs->oclVersion = GetOclApiVersion(pInternalOptions);
  pClangArgs->b32bit = IsBuildingFor32bit(pInternalOptions);

  if (pOptions) {
    pClangArgs->options.assign(pOptions);
  }
#if defined(IGC_DEBUG_VARIABLES)
  const char *extraOpts = IGC_GET_REGKEYSTRING(ExtraOCLOptions);
  if (extraOpts != nullptr && strlen(extraOpts) > 0) {
    if (!pClangArgs->options.empty())
      pClangArgs->options += ' ';
    pClangArgs->options += extraOpts;
  }
#endif

  GetOclCVersionFromOptions(pOptions, pInternalOptions, pClangArgs->oclVersion, exceptString);
  EnsureProperPCH(pClangArgs, pInternalOptions, exceptString);
}

// Parses the given ELF binary to prepare the arguments for the TranslateClang
void CClangTranslationBlock::GetTranslateClangArgs(CElfReader *pElfReader, const char *pOptions,
                                                   const char *pInternalOptions, TranslateClangArgs *pClangArgs,
                                                   std::string &exceptString) {
  IGC_ASSERT_MESSAGE(pElfReader, "pElfReader is invalid");

  const SElfHeader *pHeader = pElfReader->GetElfHeader();
  IGC_ASSERT_MESSAGE(pHeader->Type == EH_TYPE_OPENCL_SOURCE, "OPENCL_SOURCE elf type is expected");

  // First section should be an OpenCL source code
  const SElfSectionHeader *pSectionHeader = pElfReader->GetSectionHeader(1);
  IGC_ASSERT_MESSAGE(NULL != pSectionHeader, "pSectionHeader cannot be NULL");

  if (pSectionHeader->Type == SH_TYPE_OPENCL_SOURCE) {
    char *pData = NULL;
    size_t uiDataSize = 0;
    pElfReader->GetSectionData(1, pData, uiDataSize);

    if (pData != NULL) {
      IGC_ASSERT_MESSAGE(pData[uiDataSize - 1] == '\0', "Program source is not null terminated");
      pClangArgs->pszProgramSource = pData;
    }
  }

  // Other sections could be runtime supplied header files
  for (unsigned i = 2; i < pHeader->NumSectionHeaderEntries; ++i) {
    const SElfSectionHeader *pSectionHeader = pElfReader->GetSectionHeader(i);

    if ((pSectionHeader != NULL) && (pSectionHeader->Type == SH_TYPE_OPENCL_HEADER)) {
      char *pData = NULL;
      size_t uiDataSize = 0;
      pElfReader->GetSectionData(i, pData, uiDataSize);

      if (pData != NULL) {
        IGC_ASSERT_MESSAGE(pData[uiDataSize - 1] == '\0', "Header source is not null terminated");
        pClangArgs->inputHeaders.push_back(pData);
        pClangArgs->inputHeadersNames.push_back(pElfReader->GetSectionName(i));
      }
    }
  }

  if (pOptions) {
    pClangArgs->options.assign(pOptions);
  }

  pClangArgs->oclVersion = GetOclApiVersion(pInternalOptions);
  pClangArgs->b32bit = IsBuildingFor32bit(pInternalOptions);

  EnsureProperPCH(pClangArgs, pInternalOptions, exceptString);
}

static std::string FormatExtensionsString(const std::vector<std::string> &extensions) {
  std::stringstream output;

  if (!extensions.empty()) {
    output << "-cl-ext=-all";
    for (const std::string &extension : extensions)
      output << ",+" << extension;
  }

  output.flush();
  return output.str();
}

// Extracts a substring starting with "prefix" and ending with space.
static std::string GetSubstring(const std::string &str, const std::string &prefix) {
  size_t start_pos = 0, end_pos = 0;
  std::string returnString = "";

  while ((start_pos = str.find(prefix, end_pos)) != std::string::npos) {
    end_pos = str.find(' ', start_pos);
    if (end_pos == std::string::npos) {
      returnString += str.substr(start_pos);
      break;
    }
    returnString += str.substr(start_pos, end_pos - start_pos) + " ";
  }

  if (!returnString.empty() && returnString.back() == ' ') {
    returnString.pop_back();
  }

  return returnString;
}

static std::string GetCDefinesFromInternalOptions(const char *pInternalOptions) {
  if (pInternalOptions == nullptr) {
    return std::string{};
  }

  std::string internalDefines = "";

  const char *beg = strstr(pInternalOptions, "-D");
  const char *end = nullptr;
  while (beg != nullptr) {
    if ((beg == pInternalOptions) || (beg[-1] == ' ')) {
      end = strstr(beg, " ");
      if (end == nullptr) {
        if (beg[2] != '\0') {
          // more than just -D
          internalDefines += std::string(beg);
        }
        break;
      } else {
        if (end - beg > 2) {
          internalDefines += std::string(beg, end);
        }
      }
      internalDefines += " ";
    } else {
      end = beg + 2; // -D
    }

    beg = strstr(end, "-D");
  }

  return internalDefines;
}

// Tracks extensions that require manual feature macro generation for OpenCL C < 3.0.
// These specific extensions were implemented in Clang differently than other extensions -
// they require both the extension define AND corresponding feature macros to be enabled.
// For OpenCL C 3.0, the runtime automatically handles feature macros, but for older versions,
// IGC must manually enable the corresponding feature macros when these extensions are enabled.
struct ExtensionsRequiringManualFeatureMacros {
  // Extensions that generate their own feature macros
  bool integerDotProduct = false; // cl_khr_integer_dot_product
  bool extFloatAtomics = false;   // cl_ext_float_atomics
  bool kernelClock = false;       // cl_khr_kernel_clock

  // Dependency flags - control which macros are enabled
  bool fp16 = false; // cl_khr_fp16 (dependency for atomic macros)
  bool fp64 = false; // cl_khr_fp64 (dependency for atomic macros)
};

static std::string GetFeatureMacrosForExtensions(const ExtensionsRequiringManualFeatureMacros &enabledExtensions,
                                                 unsigned int oclStd) {
  // For OpenCL C versions older than 3.0, manually enable feature macros for specific extensions
  std::string featureMacros;

  IGC_ASSERT_MESSAGE(oclStd < 300, "This function should only be called for OpenCL C versions older than 3.0");

  // Integer dot product macros (OpenCL C 1.2+)
  if (enabledExtensions.integerDotProduct && oclStd >= 120) {
    featureMacros += " -D__opencl_c_integer_dot_product_input_4x8bit_packed";
    featureMacros += " -D__opencl_c_integer_dot_product_input_4x8bit";
  }

  // Float atomics macros (OpenCL C 2.0+)
  if (enabledExtensions.extFloatAtomics && oclStd >= 200) {
    if (enabledExtensions.fp16) {
      featureMacros += " -D__opencl_c_ext_fp16_global_atomic_load_store";
      featureMacros += " -D__opencl_c_ext_fp16_local_atomic_load_store";
      featureMacros += " -D__opencl_c_ext_fp16_global_atomic_add";
      featureMacros += " -D__opencl_c_ext_fp16_local_atomic_add";
      featureMacros += " -D__opencl_c_ext_fp16_global_atomic_min_max";
      featureMacros += " -D__opencl_c_ext_fp16_local_atomic_min_max";
    }
    if (enabledExtensions.fp64) {
      featureMacros += " -D__opencl_c_ext_fp64_global_atomic_add";
      featureMacros += " -D__opencl_c_ext_fp64_local_atomic_add";
      featureMacros += " -D__opencl_c_ext_fp64_global_atomic_min_max";
      featureMacros += " -D__opencl_c_ext_fp64_local_atomic_min_max";
    }
    // FP32 atomics (always available with cl_ext_float_atomics)
    featureMacros += " -D__opencl_c_ext_fp32_global_atomic_add";
    featureMacros += " -D__opencl_c_ext_fp32_local_atomic_add";
    featureMacros += " -D__opencl_c_ext_fp32_global_atomic_min_max";
    featureMacros += " -D__opencl_c_ext_fp32_local_atomic_min_max";
  }

  // Kernel clock macros
  if (enabledExtensions.kernelClock) {
    featureMacros += " -D__opencl_c_kernel_clock_scope_device";
    featureMacros += " -D__opencl_c_kernel_clock_scope_work_group";
    featureMacros += " -D__opencl_c_kernel_clock_scope_sub_group";
  }

  return featureMacros;
}

// The expected extensions input string is in a form:
// -cl-ext=-all,+supported_ext_name,+second_supported_ext_name
// -cl-feature=+__opencl_c_3d_image_writes,+__opencl_c_atomic_order_acq_rel
static std::string GetCDefinesForEnableList(llvm::StringRef enableListStr, unsigned int oclStd,
                                            const StringRef prefix) {

  std::string definesStr;

  // check for the last occurence of prefix, as it invalidates previous occurances.
  size_t pos = enableListStr.rfind(prefix);
  if (pos == llvm::StringRef::npos) {
    // If this string does not exist the input string does not contain valid extension list
    // or it has all extensions disabled (-all without colon afterwards).
    return definesStr;
  }

  enableListStr = enableListStr.substr(pos + prefix.size());

  // string with defines should be similar in size, ",+" will change to "-D".
  definesStr.reserve(enableListStr.size());

  llvm::SmallVector<StringRef, 0> v;
  enableListStr.split(v, ',');

  ExtensionsRequiringManualFeatureMacros enabledExtensions;
  for (auto ext : v) {
    if (ext.consume_front("+")) {
      ext = ext.trim();
      if (ext == "cl_intel_device_side_avc_motion_estimation") {
        // If the user provided -cl-std option we need to add the define only if it's 1.2 and above.
        // This is because clang will not allow declarations of extension's functions which use avc types otherwise.
        if (!(oclStd >= 120 || oclStd == 0))
          continue;
      }

      // Only collect extensions needed for manual feature macro generation
      if (ext == "cl_khr_integer_dot_product") {
        enabledExtensions.integerDotProduct = true;
      } else if (ext == "cl_ext_float_atomics") {
        enabledExtensions.extFloatAtomics = true;
      } else if (ext == "cl_khr_fp16") {
        enabledExtensions.fp16 = true;
      } else if (ext == "cl_khr_fp64") {
        enabledExtensions.fp64 = true;
      } else if (ext == "cl_khr_kernel_clock") {
        enabledExtensions.kernelClock = true;
      }

      definesStr.append(" -D").append(ext.str());
    }
  }

  // For OpenCL C versions older than 3.0, manually enable feature macros for specific extensions.
  // This is required because some extensions (like cl_khr_integer_dot_product) were implemented
  // in Clang to require both the extension define AND corresponding feature macros to be enabled.
  // In OpenCL C 3.0, the runtime automatically handles feature macros, but for older versions,
  // the runtime (NEO UMD) doesn't pass these feature macros, so IGC must enable them manually
  // when the corresponding extension is enabled.
  if (oclStd < 300) {
    definesStr += GetFeatureMacrosForExtensions(enabledExtensions, oclStd);
  }

  return definesStr;
}

// Ensures that the given TranslateClang arguments has the proper CTH and PCH
// headers specified
void CClangTranslationBlock::EnsureProperPCH(TranslateClangArgs *pArgs, const char *pInternalOptions,
                                             std::string &exceptString) {
  // The custom OpenCL header is not applicable to SYCL input.
  if (IsBuildingForSYCL(pArgs->options))
    return;

  unsigned long CTHeaderSize = 0;
  m_cthBuffer = llvm::LoadCharBufferFromResource(IDR_CTH_H, "H", CTHeaderSize);
  IGC_ASSERT_MESSAGE(m_cthBuffer, "Error loading Opencl_cth.h");

  if (m_cthBuffer) {
    // Process the CT Header
    IGC_ASSERT_MESSAGE(CTHeaderSize > 0, "Resource for the CT Header is empty");
    IGC_ASSERT_MESSAGE(m_cthBuffer[CTHeaderSize - 1] == '\0', "Resource for the CT Header is not null terminated");

    pArgs->inputHeaders.push_back(m_cthBuffer);
    pArgs->inputHeadersNames.push_back("CTHeader.h");
    pArgs->optionsEx.append(" -include CTHeader.h");
  }
}

// Takes first parameter from Head
// and assigns the rest of parameters to Tail.
// Head  : input string with list / output with head
// Tail  : output string
// Returns Head or NULL if Head is empty
static char *GetParam(char *Head, char *Tail) {
  static char Delim = ' ';
  static char Slash = '\\';
  char QuoteDouble = 0;
  char QuoteSingle = 0;
  char PrevChar = 0;
  char *Pos = NULL;
  int Length = 0;

  if (Head == NULL) {
    *Tail = 0;
    goto ERROR_HANDLER;
  }

  QuoteDouble = Delim;
  QuoteSingle = Delim;
  Pos = Head;
  Length = (int)std::string(Head).length();

  while (*Pos == Delim) {
    Pos++;
    Length--;
  }
  if (*Pos == 0) {
    *Tail = 0;
    Head = NULL;
    goto ERROR_HANDLER;
  }

  switch (*Pos) {
  case '\"':
    QuoteDouble = *Pos;
    break;
  case '\'':
    QuoteSingle = *Pos;
    break;
  default:
    break;
  }

  do {
    PrevChar = *Pos;
    Pos++;
    Length--;
    switch (*Pos) {
    case '\"':
      if (PrevChar != Slash) {
        if (QuoteDouble == Delim) {
          QuoteDouble = *Pos; // Open quote string
        } else {
          QuoteDouble = Delim; // Close quote string
        }
      }
      break;
    case '\'':
      if (PrevChar != Slash) {
        if (QuoteSingle == Delim) {
          QuoteSingle = *Pos; // Open quote string
        } else {
          QuoteSingle = Delim; // Close quote string
        }
      }
      break;
    default:
      break;
    }
  } while ((*Pos != 0) && ((*Pos != Delim) || ((*Pos == Delim) && ((QuoteDouble != Delim) || (QuoteSingle != Delim)))));
  if (*Pos == Delim) {
    *Pos = 0; // finish Head
    Pos++;    // skip zero
    while (*Pos == Delim) {
      Pos++; // skip multiple delimiters
      Length--;
    }
    if (*Pos != 0) {
      strcpy_s(Tail, Length, Pos); // Assign the rest of params to Tail
    } else {
      *Tail = 0;
    }
  } else {
    *Tail = 0;
  }

  while (*Head == Delim) {
    Head++;
  }

ERROR_HANDLER:
  return Head;
}

// Walks through options and makes sure they're ok
static int BuildOptionsAreValid(const std::string &options, std::string &exceptString) {
  int retVal = 0;

  char *nextTok = NULL;
  char *pParam = NULL;
  char *pBuffer = NULL;
  bool ignoreNextToken = false;
  bool checkBinaryType = false;
  bool isCommonOption = false;

  if (!options.empty()) {
    size_t optionsSize = options.size() + 1;
    // alocate memory for pBuffer and nextTok
    pBuffer = new char[optionsSize];
    nextTok = new char[optionsSize];

    strncpy_s(pBuffer, optionsSize, options.c_str(), optionsSize);
    pParam = GetParam(pBuffer, nextTok);

    if (pParam) {
      do {
        if (checkBinaryType) {
          // If "spir" does not follow the -x option, we must fail
          if ((strcmp(pParam, "spir") && strcmp(pParam, "spir64"))) {
            // Invalid option - break out of the loop and return
            // CL_INVALID_BUILD_OPTIONS
            retVal = -43;
            std::string invalidOption(pParam);

            exceptString += "\nUnrecognized build options: " + invalidOption;
            break;
          }

          // reset
          checkBinaryType = false;
        } else if (ignoreNextToken == false) {
          // Check for common Intel OpenCL CPU/GPU options
          isCommonOption =
              (strcmp(pParam, "-cl-single-precision-constant") == 0) || (strcmp(pParam, "-cl-denorms-are-zero") == 0) ||
              (strcmp(pParam, "-cl-fp32-correctly-rounded-divide-sqrt") == 0) ||
              (strcmp(pParam, "-cl-opt-disable") == 0) || (strcmp(pParam, "-ze-opt-disable") == 0) ||
              (strcmp(pParam, "-cl-strict-aliasing") == 0) || (strcmp(pParam, "-cl-mad-enable") == 0) ||
              (strcmp(pParam, "-cl-no-signed-zeros") == 0) || (strcmp(pParam, "-cl-unsafe-math-optimizations") == 0) ||
              (strcmp(pParam, "-cl-finite-math-only") == 0) || (strcmp(pParam, "-cl-fast-relaxed-math") == 0) ||
              (strcmp(pParam, "-cl-match-sincospi") == 0) || (strcmp(pParam, "-w") == 0) ||
              (strcmp(pParam, "-Werror") == 0) || (strcmp(pParam, "-cl-std=CL1.1") == 0) ||
              (strcmp(pParam, "-cl-std=CL1.2") == 0) || (strcmp(pParam, "-cl-std=CL2.0") == 0) ||
              (strcmp(pParam, "-cl-std=CL2.1") == 0) || (strcmp(pParam, "-cl-std=CL3.0") == 0) ||
              (strcmp(pParam, "-cl-uniform-work-group-size") == 0) || // it's work only for OCL version greater than 1.2
              (strcmp(pParam, "-cl-kernel-arg-info") == 0) || (strncmp(pParam, "-x", 2) == 0) ||
              (strncmp(pParam, "-D", 2) == 0) || (strncmp(pParam, "-I", 2) == 0) ||
              (strncmp(pParam, "-spir-std=", 10) == 0) || (strcmp(pParam, "-gline-tables-only") == 0) ||
              (strcmp(pParam, "-triple") == 0) || // used in NEO
              (strcmp(pParam, "-dwarf-column-info") == 0) ||
              (strcmp(pParam, "-cl-intel-no-prera-scheduling") == 0) || // temporary options
              (strcmp(pParam, "-igc_opts") == 0) ||                     // temporary options
              (strcmp(pParam, "-cl-intel-gtpin-rera") == 0) ||          // temporary options
              (strcmp(pParam, "-cl-intel-128-GRF-per-thread") == 0) ||  // temporary options
              (strcmp(pParam, "-ze-opt-128-GRF-per-thread") == 0) ||    // temporary options
              (strcmp(pParam, "-cl-intel-256-GRF-per-thread") == 0) ||  // temporary options
              (strcmp(pParam, "-ze-opt-256-GRF-per-thread") == 0) ||    // temporary options
              (strcmp(pParam, "-cl-intel-512-GRF-per-thread") == 0) ||  // temporary options
              (strcmp(pParam, "-ze-opt-512-GRF-per-thread") == 0) ||    // temporary options
              (strcmp(pParam, "-ze-exp-register-file-size") == 0) ||    // temporary options
              (strcmp(pParam, "-ze-opt-large-register-file") == 0) ||   // temporary options
              (strcmp(pParam, "-cl-intel-large-grf-kernel") == 0) ||
              (strcmp(pParam, "-ze-opt-large-grf-kernel") == 0) ||
              (strcmp(pParam, "-cl-intel-regular-grf-kernel") == 0) ||
              (strcmp(pParam, "-ze-opt-regular-grf-kernel") == 0) ||
              (strcmp(pParam, "-ze-opt-disable-recompilation") == 0) || // temporary options
              (strcmp(pParam, "-cl-intel-num-thread-per-eu") == 0) ||   // temporary options
              (strncmp(pParam, "-cl-intel-reqd-eu-thread-count", 30) == 0) ||
              (strcmp(pParam, "-cl-replace-global-offsets-by-zero") == 0) ||         // temporary options
              (strcmp(pParam, "-cl-kernel-debug-enable") == 0) ||                    // temporary options
              (strcmp(pParam, "-cl-include-sip-csr") == 0) ||                        // temporary options
              (strcmp(pParam, "-cl-include-sip-kernel-debug") == 0) ||               // temporary options
              (strcmp(pParam, "-cl-include-sip-kernel-local-debug") == 0) ||         // temporary options
              (strcmp(pParam, "-cl-intel-use-32bit-ptr-arith") == 0) ||              // temporary options
              (strcmp(pParam, "-cl-intel-force-disable-4GB-buffer") == 0) ||         // temporary options
              (strcmp(pParam, "-cl-intel-greater-than-4GB-buffer-required") == 0) || // temporary options
              (strcmp(pParam, "-ze-opt-greater-than-4GB-buffer-required") == 0) ||   // temporary options
              (strcmp(pParam, "-cl-intel-has-buffer-offset-arg") == 0) ||            // temporary options
              (strcmp(pParam, "-ze-opt-has-buffer-offset-arg") == 0) ||              // temporary options
              (strcmp(pParam, "-cl-intel-buffer-offset-arg-required") == 0) ||       // temporary options
              (strcmp(pParam, "-ze-opt-buffer-offset-arg-required") == 0) ||         // temporary options
              (strcmp(pParam, "-cl-force-global-mem-allocation") == 0) ||            // temp
              (strcmp(pParam, "-ze-force-global-mem-allocation") == 0) ||            // temp
              (strcmp(pParam, "-cl-no-local-to-generic") == 0) ||                    // temp
              (strcmp(pParam, "-ze-no-local-to-generic") == 0) ||                    // temp
              (strcmp(pParam, "-cl-intel-debug-info") == 0) || (strncmp(pParam, "-dump-opt-llvm", 14) == 0) ||
              (strcmp(pParam, "-cl-no-subgroup-ifp") == 0) ||
              (strcmp(pParam, "-cl-intel-disable-a64WA") == 0) ||     // temporary options
              (strcmp(pParam, "-ze-gtpin-rera") == 0) ||              // used by GTPin
              (strcmp(pParam, "-ze-gtpin-grf-info") == 0) ||          // used by GTPin
              (strcmp(pParam, "-ze-gtpin-scratch-area-size") == 0) || // used by GTPin
              (strcmp(pParam, "-ze-opt-enable-auto-large-GRF-mode") == 0) ||
              (strcmp(pParam, "-cl-intel-enable-auto-large-GRF-mode") == 0) || (strcmp(pParam, "-ze-skip-fde") == 0) ||
              (strcmp(pParam, "-ze-no-fusedCallWA") == 0) || (strcmp(pParam, "-ze-disable-compaction") == 0) ||
              (strcmp(pParam, "-cl-poison-unsupported-fp64-kernels") == 0) || // used by fp64 poisoning
              (strcmp(pParam, "-ze-poison-unsupported-fp64-kernels") == 0) || // used by fp64 poisoning
              (strcmp(pParam, "-cl-fp64-gen-emu") == 0) ||                    // used by fp64 emulation
              (strcmp(pParam, "-ze-fp64-gen-emu") == 0) ||                    // used by fp64 emulation
              (strcmp(pParam, "-cl-fp64-gen-conv-emu") == 0) ||               // used by fp64 conversion emulation
              (strcmp(pParam, "-ze-fp64-gen-conv-emu") == 0) ||               // used by fp64 conversion emulation
              (strcmp(pParam, "-Xfinalizer") == 0) ||                         // used to pass options to visa finalizer
              (strcmp(pParam, "-cl-intel-enable-ieee-float-exception-trap") ==
               0) || // used to enable IEEE float exception trap
              (strcmp(pParam, "-ze-intel-enable-ieee-float-exception-trap") ==
               0) || // used to enable IEEE float exception trap
              (strcmp(pParam, "-cl-intel-disable-sendwarwa") == 0) ||
              (strcmp(pParam, "-ze-opt-disable-sendwarwa") == 0) ||
              (strcmp(pParam, "-cl-intel-static-profile-guided-trimming") ==
               0) ||                                                           // used to enable profile-guided trimming
              (strcmp(pParam, "-ze-opt-static-profile-guided-trimming") == 0); // used to enable profile-guided trimming

          if (isCommonOption) {
            // check to see if they used a space immediately after
            // the define/include. If they did...
            if ((strcmp(pParam, "-D") == 0) || (strcmp(pParam, "-I") == 0) || (strcmp(pParam, "-igc_opts") == 0)) {
              // ignore next token as it is the define/include
              ignoreNextToken = true;
            } else if (strcmp(pParam, "-x") == 0) {
              // we need to check the next parameter for "spir"
              checkBinaryType = true;
            } else if (strcmp(pParam, "-triple") == 0) {
              checkBinaryType = true;
            } else if (strcmp(pParam, "-cl-intel-num-thread-per-eu") == 0 ||
                       strcmp(pParam, "-cl-intel-reqd-eu-thread-count") == 0 ||
                       strcmp(pParam, "-ze-gtpin-scratch-area-size") == 0 ||
                       strcmp(pParam, "-ze-exp-register-file-size") == 0) {
              // Next token is N, so ignore it
              ignoreNextToken = true;
            } else if ((strcmp(pParam, "-cl-intel-large-grf-kernel") == 0) ||
                       (strcmp(pParam, "-ze-opt-large-grf-kernel") == 0) ||
                       (strcmp(pParam, "-cl-intel-regular-grf-kernel") == 0) ||
                       (strcmp(pParam, "-ze-opt-regular-grf-kernel") == 0)) {
              // Next token is kernel name substring, ignore it
              ignoreNextToken = true;
            } else if (strcmp(pParam, "-Xfinalizer") == 0) {
              // if -Xfinalizer is used, ignore next token as it is the option for visa
              ignoreNextToken = true;
            }
          }
          // Check for Intel OpenCL CPU options
          // OCL Kernel Profiler requires "-g" to create debug information for instrumented kernels.
          // Without those information OCL Profiler is unable to associate OpenC code with IL instructions.
          else if ((strcmp(pParam, "-g") == 0) || (strcmp(pParam, "-profiler") == 0) ||
                   (strncmp(pParam, "-s", 2) == 0)) {
            if (strcmp(pParam, "-s") == 0) {
              // ignore next token as it is the source path
              ignoreNextToken = true;
            }
          } else {
            // Invalid option - break out of the loop and return
            // CL_INVALID_BUILD_OPTIONS
            retVal = -43;
            std::string invalidOption(pParam);

            exceptString += "\nUnrecognized build options: " + invalidOption;
            break;
          }
        } else {
          ignoreNextToken = false;
        }
        strncpy_s(pBuffer, optionsSize, nextTok, optionsSize);
      } while ((pParam = GetParam(pBuffer, nextTok)) != NULL);
    }

    delete[] pBuffer;
    delete[] nextTok;
  }
  return retVal;
}

// Builds the OpenCL-specific clang options: the supported extension list, the
// corresponding extension/feature macro defines and the other OpenCL C macros.
// These options only apply to OpenCL C source and must not be passed for SYCL.
std::string CClangTranslationBlock::GetOpenCLOptions(const TranslateClangArgs *pInputArgs, const char *pInternalOptions,
                                                     std::string &exceptString) const {
  std::string optionsEx;
  std::string extensions;
  std::string extensionsFromInternalOptions = GetSubstring(pInternalOptions, "-cl-ext=");

  unsigned int oclStd =
      GetOclCVersionFromOptions(pInputArgs->options.data(), nullptr, pInputArgs->oclVersion, exceptString);

  // if extensions list is passed in via internal options, it will override the default ones.
  if (!extensionsFromInternalOptions.empty()) {
    extensions = extensionsFromInternalOptions;
  } else {
    extensions = FormatExtensionsString(m_Extensions);
  }

  optionsEx.append(extensions);
  optionsEx.append(" -I. -D__ENABLE_GENERIC__=1 -D__ENDIAN_LITTLE__");

  // Undefine Clang's SPIR/SPIRV macros to prevent automatic extension enablement.
  // According to the March 20th OpenCL tooling meeting, Clang's automatic extension
  // handling "is trying to do the right thing on a best effort basis, but should not
  // be treated as a reference solution, and in its current state appears to be broken
  // and doing more harm than good." The recommended solution is to handle all extension
  // and feature macro definitions manually.
  // By undefining __SPIR__ and __SPIRV__, we prevent Clang from automatically enabling extensions,
  // allowing us to explicitly control which extensions are enabled through manual defines.
  optionsEx += " -U__SPIR__ -U__SPIRV__";

  // get additional -D flags from internal options
  optionsEx += " " + GetCDefinesFromInternalOptions(pInternalOptions);
  optionsEx += " " + GetCDefinesForEnableList(extensions, oclStd, "-cl-ext=-all,");

  // Workaround for Clang issue.
  // Clang always defines __IMAGE_SUPPORT__ macro for SPIR target, even if device doesn't support it.
  if (optionsEx.find("__IMAGE_SUPPORT__") == std::string::npos) {
    optionsEx += " -U__IMAGE_SUPPORT__";
  }

  // FIXME: do we still support VME?
  if (AreVMETypesDefined()) {
    optionsEx += " -D__VME_TYPES_DEFINED__";
  }

  optionsEx += " -D__LLVM_VERSION_MAJOR__=" + to_string(LLVM_VERSION_MAJOR);

  return optionsEx;
}

// Builds the '--spirv-ext=' option listing the SPIR-V extensions supported by IGC.
// This allows opencl-clang to enable only relevant extensions when compiling to
// SPIR-V instead of enabling all extensions by default, and prevents generating
// SPIR-V that uses extensions not supported by IGC.
static std::string GetSpirvExtensionsOption() {
  const auto &Extensions = IGC::SPIRVExtensionsSupport::getAllExtensions();
  if (Extensions.empty())
    return {};

  std::string option = " --spirv-ext=";

  auto it = Extensions.begin();
  option += "+";
  option += it->Name;

  for (++it; it != Extensions.end(); ++it) {
    option += ",+";
    option += it->Name;
  }

  return option;
}

// Translates from CL to LL/BC
bool CClangTranslationBlock::TranslateClang(const TranslateClangArgs *pInputArgs, STB_TranslateOutputArgs *pOutputArgs,
                                            std::string &exceptString, const char *pInternalOptions) {
  // additional clang options
  std::string optionsEx = pInputArgs->optionsEx;
  std::string options = pInputArgs->options;
  bool IsSYCL = IsBuildingForSYCL(options);

  switch (m_OutputFormat) {
  case TB_DATA_FORMAT_LLVM_TEXT:
    optionsEx += " -emit-llvm";
    break;
  case TB_DATA_FORMAT_LLVM_BINARY:
    optionsEx += " -emit-llvm-bc";
    break;
  case TB_DATA_FORMAT_SPIR_V:
    optionsEx += " -emit-spirv";
    break;
  default:
    break;
  }

  // TODO: do we really need to override the triple? Do we still support 32-bit
  // host code for compute?
  if (options.find("-triple") == std::string::npos) {
    // if target triple not explicitly set
    if (pInputArgs->b32bit) {
      optionsEx += " -D__32bit__=1";
      options += " -triple spir";
    } else {
      options += " -triple spir64";
    }
  }

#if LLVM_VERSION_MAJOR < 17 && defined(IGC_DEBUG_VARIABLES)
  // Allow to dynamically switch from typed to opaque pointers on
  // LLVM < 17. Disabling opaque pointers dynamically is not possible.
  if (IGC_IS_FLAG_ENABLED(EnableOpaquePointersBackend)) {
    optionsEx += " -opaque-pointers";
  } else {
#endif
    optionsEx += " ";
    optionsEx += __IGC_OPAQUE_POINTERS_DEFAULT_ARG_CLANG;
#if LLVM_VERSION_MAJOR < 17 && defined(IGC_DEBUG_VARIABLES)
  }
#endif

  // SYCL input is compiled without the OpenCL-specific options
  // (extension list, feature/extension macro defines, the custom OpenCL
  // header, etc.), which only apply to OpenCL C source.
  if (!IsSYCL)
    optionsEx += GetOpenCLOptions(pInputArgs, pInternalOptions, exceptString);

  // Pass the list of supported SPIR-V extensions from IGC to opencl-clang.
  optionsEx += GetSpirvExtensionsOption();

  IOCLFEBinaryResult *pResultPtr = NULL;
  int res = 0;
  {
#ifdef _WIN32
    res = m_CCModule.pCompile(
#else
    auto *compileFunc = m_CCModule.pCompile ? m_CCModule.pCompile : &Compile;
    res = compileFunc(
#endif
        pInputArgs->pszProgramSource, (const char **)pInputArgs->inputHeaders.data(),
        (unsigned int)pInputArgs->inputHeaders.size(), (const char **)pInputArgs->inputHeadersNames.data(), NULL, 0,
        options.c_str(), optionsEx.c_str(), pInputArgs->oclVersion.c_str(), &pResultPtr);
  }

  // Don't check the options correctness for SYCL after the compilation failure,
  // because SYCL compilation requires a different set of options.
  if (!IsSYCL && 0 != BuildOptionsAreValid(options, exceptString))
    res = -43;

  Utils::FillOutputArgs(pResultPtr, pOutputArgs, exceptString);
  if (!exceptString.empty()) // str != "" => there was an exception. skip further code and return.
  {
    return false;
  }

  // if -dump-opt-llvm is enabled dump the llvm output to the file
  size_t dumpOptPosition = options.find("-dump-opt-llvm");
  if ((0 == res) && dumpOptPosition != std::string::npos) {
    std::string dumpFileName;
    std::istringstream iss(options.substr(dumpOptPosition));
    iss >> dumpFileName;
    size_t equalSignPosition = dumpFileName.find('=');
    if (equalSignPosition != std::string::npos) {
      dumpFileName = dumpFileName.substr(equalSignPosition + 1);
      // dump the archive
      FILE *file = fopen(dumpFileName.c_str(), "wb");
      if (file != NULL) {
        fwrite(pOutputArgs->Output.data(), pOutputArgs->Output.size(), 1, file);
        fclose(file);
      }
    } else {
      SetErrorString("\nWarning: File name not specified with the -dump-opt-llvm option.\n", pOutputArgs);
    }
  }

  pResultPtr->Release();

  return (0 == res);
}

// Extract the IR from the input arguments and supply it unmodified
// to the output
bool CClangTranslationBlock::ReturnSuppliedIR(const STB_TranslateInputArgs *pInputArgs,
                                              STB_TranslateOutputArgs *pOutputArgs) {
  bool success = true;

  pOutputArgs->ErrorString.clear();
  pOutputArgs->Output.clear();

  CElfReader *pElfReader = CElfReader::Create(pInputArgs->pInput, pInputArgs->InputSize);
  RAIIElf ElfObj(pElfReader);

  if (!pElfReader || !pElfReader->IsValidElf(pInputArgs->pInput, pInputArgs->InputSize)) {
    SetErrorString("Invalid input/output passed to library", pOutputArgs);
    return false;
  }

  const SElfHeader *pHeader = pElfReader->GetElfHeader();
  IGC_ASSERT(pHeader != NULL);

  for (unsigned i = 1; i < pHeader->NumSectionHeaderEntries; i++) {
    const SElfSectionHeader *pSectionHeader = pElfReader->GetSectionHeader(i);
    IGC_ASSERT(pSectionHeader != NULL);
    if (pSectionHeader == NULL) {
      SetErrorString("No section header", pOutputArgs);
      return false;
    }

    if ((pSectionHeader->Type == SH_TYPE_OPENCL_LLVM_ARCHIVE) || (pSectionHeader->Type == SH_TYPE_OPENCL_LLVM_BINARY)) {
      char *pData = NULL;
      size_t dataSize = 0;
      const unsigned char *pBufStart;

      if (!pOutputArgs->Output.empty()) {
        SetErrorString("Multiple inputs passed to library", pOutputArgs);
        success = false;
        break;
      }

      pElfReader->GetSectionData(i, pData, dataSize);
      pBufStart = (const unsigned char *)pData;

      if (llvm::isBitcode(pBufStart, pBufStart + pHeader->ElfHeaderSize)) {
        pOutputArgs->Output.resize(dataSize);
        memcpy_s(pOutputArgs->Output.data(), dataSize, pBufStart, dataSize);
      } else {
        SetErrorString("Invalid input/output passed to library", pOutputArgs);
        success = false;
        break;
      }
    }
  }

  return success;
}

///
// Process the translation of ELF input type
//
bool CClangTranslationBlock::TranslateElf(const STB_TranslateInputArgs *pInputArgs,
                                          STB_TranslateOutputArgs *pOutputArgs, std::string &exceptString) {
  CElfReaderPtr pElfReader(CElfReader::Create(pInputArgs->pInput, pInputArgs->InputSize), ElfReaderDP);

  if (!pElfReader.get()) {
    SetErrorString("CElfReader::Create returned NULL\n", pOutputArgs);
    return false;
  }

  if (!pElfReader->IsValidElf(pInputArgs->pInput, pInputArgs->InputSize)) {
    // throw invalid_input_param ("Wrong ELF format");
    exceptString = "Wrong ELF format";
    return false;
  }
  // Elf is valid, so it is safe to access the header
  E_EH_TYPE ehType = *(const E_EH_TYPE *)&pElfReader->GetElfHeader()->Type;

  //{ { TB_DATA_FORMAT_ELF, TB_DATA_FORMAT_LLVM_BINARY } },
  if (m_OutputFormat == TB_DATA_FORMAT_LLVM_BINARY || m_OutputFormat == TB_DATA_FORMAT_SPIR_V) {
    switch (ehType) {
    case EH_TYPE_OPENCL_SOURCE: {
      TranslateClangArgs args;
      GetTranslateClangArgs(pElfReader.get(), pInputArgs->pOptions, pInputArgs->pInternalOptions, &args, exceptString);
      bool success = TranslateClang(&args, pOutputArgs, exceptString, pInputArgs->pInternalOptions);
      if (exceptString.empty()) {
        return success;
      } else {
        return false;
      }
    }

    case EH_TYPE_OPENCL_OBJECTS:
      if (!pInputArgs->pOptions || strstr(pInputArgs->pOptions, "-x spir") == NULL) {
        exceptString = "Unsupported ELF container";
        return false;
      }

      return ReturnSuppliedIR(pInputArgs, pOutputArgs);

    default:
      exceptString = "Unsupported ELF header type";
      return false;
    }
  } else {
    exceptString = "Unsupported output format";
  }

  return false;
}

bool CClangTranslationBlock::Translate(const STB_TranslateInputArgs *pInputArgs, STB_TranslateOutputArgs *pOutputArgs) {
  InitRegistryKeysOnce(pInputArgs->pOptions ? pInputArgs->pOptions : "", pOutputArgs);

  std::string exceptString;
  switch (m_InputFormat) {
    // Processing the translations from OCL to LLVM
    //{{ TB_DATA_FORMAT_OCL_TEXT,      TB_DATA_FORMAT_LLVM_TEXT }} ,
    //{{ TB_DATA_FORMAT_OCL_TEXT,      TB_DATA_FORMAT_LLVM_BINARY }},
  case TB_DATA_FORMAT_OCL_TEXT: {
    TranslateClangArgs args;
    GetTranslateClangArgs(pInputArgs->pInput, pInputArgs->InputSize, pInputArgs->pOptions, pInputArgs->pInternalOptions,
                          &args, exceptString);
    bool successTC = TranslateClang(&args, pOutputArgs, exceptString, pInputArgs->pInternalOptions);

#if defined(IGC_DEBUG_VARIABLES)
    if (IGC_IS_FLAG_ENABLED(ShaderDumpEnable)) {
      // Works for all OSes. Creates dir if necessary.
      const char *pOutputFolder = FCL::GetShaderOutputFolder();
      stringstream ss;
      const char *pBuffer = pInputArgs->pInput;
      UINT bufferSize = pInputArgs->InputSize;

      if (IGC_IS_FLAG_ENABLED(EnableKernelNamesBasedHash)) {
        // The spirv parser cannot be used here - we would have to include it in the Makefile
        // which is simply not worth it. Without it there's no good and reliable way to get
        // the kernel names for the hash generation, so the warning is to be emitted instead.
        pOutputArgs->ErrorString.append("warning: EnableKernelNamesBasedHash flag doesn't affect .cl dump's hash\n");
      }

      // Use the pre-computed kernel file hash if provided by the caller.
      // Otherwise, fall back to hashing the cclang binary output to generate
      // a unique hash for the .cl dump, ensuring easy matching with .ll/.dat/.asm/... files dumped in igc.
      QWORD hashValue = pInputArgs->SrcHash;

      if (hashValue == 0) {
        hashValue = iSTD::Hash(reinterpret_cast<const DWORD *>(pOutputArgs->Output.data()),
                               (DWORD)(pOutputArgs->Output.size()) / 4);
      }

      std::stringstream hs;
      hs << std::hex << std::setfill('0') << std::setw(sizeof(hashValue) * CHAR_BIT / 4) << hashValue;

      ss << pOutputFolder;
      ss << "OCL_" << "asm" << hs.str() << ".cl";

      FILE *pFile = NULL;
      fopen_s(&pFile, ss.str().c_str(), "wb");
      if (pFile) {
        fwrite(pBuffer, 1, bufferSize, pFile);
        fclose(pFile);
      }
    }
#endif

    if (exceptString.empty()) {
      return successTC;
    }
    // else continue to process "catch"
    break;
  }
  // Processing the translations from ELF to LLVM
  //{ { TB_DATA_FORMAT_ELF, TB_DATA_FORMAT_LLVM_BINARY } },
  case TB_DATA_FORMAT_ELF: {
    bool successTE = TranslateElf(pInputArgs, pOutputArgs, exceptString);
    if (exceptString.empty()) {
      return successTE;
    }
    // else continue to process exception
    break;
  }
  default: {
    exceptString = "Unsupported input format";
    // continue to process exception
  }
  }

  // exceptString != "" => there was an exception. we get here only if there is an exception
  {
    if (exceptString.compare("bad_alloc") == 0) {
      SetErrorString("fcl: Allocation failure", pOutputArgs);
      return false;
    } else {
      SetErrorString(exceptString.c_str(), pOutputArgs);
      return false;
    }
  }
}

CClangTranslationBlock::CClangTranslationBlock(void) : m_GlobalData() {}

CClangTranslationBlock::~CClangTranslationBlock(void) {}


bool CClangTranslationBlock::Initialize(const STB_CreateArgs *pCreateArgs) {
  if (pCreateArgs == NULL) {
    return false;
  }

  unsigned int i;
  for (i = 0; i < std::size(g_cClangTranslationCodes); i++) {
    // Some quick checks to ensure that the input and output types
    // are compatible with this translation blocks
    if ((pCreateArgs->TranslationCode.Type.Input == g_cClangTranslationCodes[i].Type.Input) &&
        (pCreateArgs->TranslationCode.Type.Output == g_cClangTranslationCodes[i].Type.Output)) {
      break;
    }
  }

  if (i >= std::size(g_cClangTranslationCodes)) {
    return false;
  }

  // Find out what GPU platform the driver is running on
  if (pCreateArgs->pCreateData != nullptr) {
  } else {
    // assume m_OCL_Ver, etc. will be set-up later.
    SGlobalData globDataTmp = {0};
    m_GlobalData = globDataTmp;
    m_HWPlatform = IGFX_UNKNOWN;
    m_CoreFamily = IGFX_UNKNOWN_CORE;
    m_OCL_Ver = "120";
  }
  m_InputFormat = pCreateArgs->TranslationCode.Type.Input;
  m_OutputFormat = pCreateArgs->TranslationCode.Type.Output;

  return true;
}
} // namespace TC
