2
0
Ficheiros
rocm-systems/rocclr/runtime/device/devprogram.cpp
T
foreman 40bfc4cf20 P4 to Git Change 1610640 by gandryey@gera-w8 on 2018/09/25 17:34:09
SWDEV-79445 - OCL generic changes and code clean-up
	Program compilation clean-up. Step#4:
	- Add common method FindGlobalVarSize() and metadata setup to the abstraction layer

Affected files ...

... //depot/stg/opencl/drivers/opencl/runtime/device/devprogram.cpp#6 edit
... //depot/stg/opencl/drivers/opencl/runtime/device/devprogram.hpp#4 edit
... //depot/stg/opencl/drivers/opencl/runtime/device/pal/palprogram.cpp#76 edit
... //depot/stg/opencl/drivers/opencl/runtime/device/pal/palprogram.hpp#32 edit
... //depot/stg/opencl/drivers/opencl/runtime/device/rocm/rocprogram.cpp#89 edit
... //depot/stg/opencl/drivers/opencl/runtime/device/rocm/rocprogram.hpp#38 edit
2018-09-25 17:47:26 -04:00

1467 linhas
48 KiB
C++

//
// Copyright (c) 2008 Advanced Micro Devices, Inc. All rights reserved.
//
#include "platform/runtime.hpp"
#include "platform/program.hpp"
#include "platform/ndrange.hpp"
#include "devprogram.hpp"
#include "devkernel.hpp"
#include "utils/macros.hpp"
#include "utils/options.hpp"
#include "utils/bif_section_labels.hpp"
#include "utils/libUtils.h"
#if defined(WITH_LIGHTNING_COMPILER)
#include "driver/AmdCompiler.h"
#include "opencl1.2-c.amdgcn.inc"
#include "opencl2.0-c.amdgcn.inc"
#endif // !defined(WITH_LIGHTNING_COMPILER)
#include <cstdio>
#include <fstream>
#include <iostream>
#include <string>
#include <sstream>
#include <cstdio>
#if defined(ATI_OS_LINUX)
#include <dlfcn.h>
#include <libgen.h>
#endif // defined(ATI_OS_LINUX)
#include "spirv/spirvUtils.h"
#include "acl.h"
#if defined(WITH_LIGHTNING_COMPILER)
#include "llvm/Support/AMDGPUMetadata.h"
typedef llvm::AMDGPU::HSAMD::Kernel::Arg::Metadata KernelArgMD;
#endif // defined(WITH_LIGHTNING_COMPILER)
namespace device {
// ================================================================================================
Program::Program(amd::Device& device)
: device_(device),
type_(TYPE_NONE),
flags_(0),
clBinary_(nullptr),
llvmBinary_(),
elfSectionType_(amd::OclElf::LLVMIR),
compileOptions_(),
linkOptions_(),
binaryElf_(nullptr),
lastBuildOptionsArg_(),
buildStatus_(CL_BUILD_NONE),
buildError_(CL_SUCCESS),
machineTarget_(nullptr),
globalVariableTotalSize_(0),
programOptions_(nullptr),
metadata_(nullptr)
{
memset(&binOpts_, 0, sizeof(binOpts_));
binOpts_.struct_size = sizeof(binOpts_);
binOpts_.elfclass = LP64_SWITCH(ELFCLASS32, ELFCLASS64);
binOpts_.bitness = ELFDATA2LSB;
binOpts_.alloc = &::malloc;
binOpts_.dealloc = &::free;
}
// ================================================================================================
Program::~Program() {
clear();
delete metadata_;
}
// ================================================================================================
void Program::clear() {
// Destroy all device kernels
for (const auto& it : kernels_) {
delete it.second;
}
kernels_.clear();
}
// ================================================================================================
bool Program::compileImpl(const std::string& sourceCode,
const std::vector<const std::string*>& headers,
const char** headerIncludeNames, amd::option::Options* options) {
if (isLC()) {
return compileImplLC(sourceCode, headers, headerIncludeNames, options);
} else {
return compileImplHSAIL(sourceCode, headers, headerIncludeNames, options);
}
}
// ================================================================================================
#if defined(WITH_LIGHTNING_COMPILER)
static std::string llvmBin_(amd::Os::getEnvironment("LLVM_BIN"));
#if defined(ATI_OS_WIN)
static BOOL CALLBACK checkLLVM_BIN(PINIT_ONCE InitOnce, PVOID Parameter, PVOID* lpContex) {
if (llvmBin_.empty()) {
HMODULE hm = nullptr;
if (GetModuleHandleExA(
GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
(LPCSTR)&amd::Device::init, &hm)) {
char path[1024];
GetModuleFileNameA(hm, path, sizeof(path));
llvmBin_ = path;
size_t pos = llvmBin_.rfind('\\');
if (pos != std::string::npos) {
llvmBin_.resize(pos);
}
}
}
return TRUE;
}
#endif // defined (ATI_OS_WINDOWS)
#if defined(ATI_OS_LINUX)
static pthread_once_t once = PTHREAD_ONCE_INIT;
static void checkLLVM_BIN() {
if (llvmBin_.empty()) {
Dl_info info;
if (dladdr((const void*)&amd::Device::init, &info)) {
char* str = strdup(info.dli_fname);
if (str) {
llvmBin_ = dirname(str);
free(str);
size_t pos = llvmBin_.rfind("lib");
if (pos != std::string::npos) {
llvmBin_.replace(pos, 3, "bin");
}
}
}
}
#if defined(DEBUG)
static const std::string tools[] = { "clang", "llvm-link", "ld.lld" };
for (const std::string tool : tools) {
std::string exePath(llvmBin_ + "/" + tool);
struct stat buf;
if (stat(exePath.c_str(), &buf)) {
std::string msg(exePath + " not found");
LogWarning(msg.c_str());
}
else if ((buf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) == 0) {
std::string msg("Cannot execute " + exePath);
LogWarning(msg.c_str());
}
}
#endif // defined(DEBUG)
}
#endif // defined(ATI_OS_LINUX)
std::unique_ptr<amd::opencl_driver::Compiler> Program::newCompilerInstance() {
#if defined(ATI_OS_WIN)
static INIT_ONCE initOnce;
InitOnceExecuteOnce(&initOnce, checkLLVM_BIN, nullptr, nullptr);
#endif // defined(ATI_OS_WIN)
#if defined(ATI_OS_LINUX)
pthread_once(&once, checkLLVM_BIN);
#endif // defined(ATI_OS_LINUX)
#if defined(DEBUG)
std::string clangExe(llvmBin_ + LINUX_SWITCH("/clang", "\\clang.exe"));
struct stat buf;
if (stat(clangExe.c_str(), &buf)) {
std::string msg("Could not find the Clang binary in " + llvmBin_);
LogWarning(msg.c_str());
}
#endif // defined(DEBUG)
return std::unique_ptr<amd::opencl_driver::Compiler>(
amd::opencl_driver::CompilerFactory().CreateAMDGPUCompiler(llvmBin_));
}
#endif // defined(WITH_LIGHTNING_COMPILER)
// ================================================================================================
bool Program::compileImplLC(const std::string& sourceCode,
const std::vector<const std::string*>& headers,
const char** headerIncludeNames, amd::option::Options* options) {
#if defined(WITH_LIGHTNING_COMPILER)
const char* xLang = options->oVariables->XLang;
if (xLang != nullptr) {
if (strcmp(xLang, "asm") == 0) {
clBinary()->elfOut()->addSection(amd::OclElf::SOURCE, sourceCode.data(), sourceCode.size());
return true;
}
else if (!strcmp(xLang, "cl")) {
buildLog_ += "Unsupported language: \"" + std::string(xLang) + "\".\n";
return false;
}
}
using namespace amd::opencl_driver;
std::unique_ptr<Compiler> C(newCompilerInstance());
std::vector<Data*> inputs;
Data* input = C->NewBufferReference(DT_CL, sourceCode.c_str(), sourceCode.length());
if (input == nullptr) {
buildLog_ += "Error while creating data from source code";
return false;
}
inputs.push_back(input);
amd::opencl_driver::Buffer* output = C->NewBuffer(DT_LLVM_BC);
if (output == nullptr) {
buildLog_ += "Error while creating buffer for the LLVM bitcode";
return false;
}
// Set the options for the compiler
// Some options are set in Clang AMDGPUToolChain (like -m64)
std::ostringstream ostrstr;
std::copy(options->clangOptions.begin(), options->clangOptions.end(),
std::ostream_iterator<std::string>(ostrstr, " "));
std::string driverOptions(ostrstr.str());
// Setting the language
driverOptions.append(" -cl-std=").append(options->oVariables->CLStd);
// Set the -O#
std::ostringstream optLevel;
optLevel << " -O" << options->oVariables->OptLevel;
driverOptions.append(optLevel.str());
// Set the machine target
driverOptions.append(" -mcpu=");
driverOptions.append(machineTarget_);
// Set xnack option if needed
if (xnackEnabled_) {
driverOptions.append(" -mxnack");
}
driverOptions.append(options->llvmOptions);
driverOptions.append(ProcessOptions(options));
// Set whole program mode
driverOptions.append(" -mllvm -amdgpu-early-inline-all -mllvm -amdgpu-prelink");
// Find the temp folder for the OS
std::string tempFolder = amd::Os::getTempPath();
// Iterate through each source code and dump it into tmp
std::fstream f;
std::vector<std::string> headerFileNames(headers.size());
std::vector<std::string> newDirs;
for (size_t i = 0; i < headers.size(); ++i) {
std::string headerPath = tempFolder;
std::string headerIncludeName(headerIncludeNames[i]);
// replace / in path with current os's file separator
if (amd::Os::fileSeparator() != '/') {
for (auto& it : headerIncludeName) {
if (it == '/') it = amd::Os::fileSeparator();
}
}
size_t pos = headerIncludeName.rfind(amd::Os::fileSeparator());
if (pos != std::string::npos) {
headerPath += amd::Os::fileSeparator();
headerPath += headerIncludeName.substr(0, pos);
headerIncludeName = headerIncludeName.substr(pos + 1);
}
if (!amd::Os::pathExists(headerPath)) {
bool ret = amd::Os::createPath(headerPath);
assert(ret && "failed creating path!");
newDirs.push_back(headerPath);
}
std::string headerFullName = headerPath + amd::Os::fileSeparator() + headerIncludeName;
headerFileNames[i] = headerFullName;
f.open(headerFullName.c_str(), std::fstream::out);
// Should we allow asserts
assert(!f.fail() && "failed creating header file!");
f.write(headers[i]->c_str(), headers[i]->length());
f.close();
Data* inc = C->NewFileReference(DT_CL_HEADER, headerFileNames[i]);
if (inc == nullptr) {
buildLog_ += "Error while creating data from headers";
return false;
}
inputs.push_back(inc);
}
// Set the include path for the temp folder that contains the includes
if (!headers.empty()) {
driverOptions.append(" -I");
driverOptions.append(tempFolder);
}
if (options->isDumpFlagSet(amd::option::DUMP_CL)) {
std::ofstream f(options->getDumpFileName(".cl").c_str(), std::ios::trunc);
if (f.is_open()) {
f << "/* Compiler options:\n"
"-c -emit-llvm -target amdgcn-amd-amdhsa -x cl "
<< driverOptions << " -include opencl-c.h "
<< "\n*/\n\n"
<< sourceCode;
f.close();
}
else {
buildLog_ += "Warning: opening the file to dump the OpenCL source failed.\n";
}
}
// FIXME_lmoriche: has the CL option been validated?
uint clcStd =
(options->oVariables->CLStd[2] - '0') * 100 + (options->oVariables->CLStd[4] - '0') * 10;
std::pair<const void*, size_t> hdr;
switch (clcStd) {
case 100:
case 110:
case 120:
hdr = { opencl1_2_c_amdgcn, opencl1_2_c_amdgcn_size };
break;
case 200:
hdr = { opencl2_0_c_amdgcn, opencl2_0_c_amdgcn_size };
break;
default:
buildLog_ += "Unsupported requested OpenCL C version (-cl-std).\n";
return false;
}
File* pch = C->NewTempFile(DT_CL_HEADER);
if (pch == nullptr || !pch->WriteData((const char*)hdr.first, hdr.second)) {
buildLog_ += "Error while opening the opencl-c header ";
return false;
}
driverOptions.append(" -include-pch " + pch->Name());
driverOptions.append(" -Xclang -fno-validate-pch");
// Tokenize the options string into a vector of strings
std::istringstream istrstr(driverOptions);
std::istream_iterator<std::string> sit(istrstr), end;
std::vector<std::string> params(sit, end);
// Compile source to IR
bool ret =
device().cacheCompilation()->compileToLLVMBitcode(C.get(), inputs, output, params, buildLog_);
buildLog_ += C->Output();
if (!ret) {
buildLog_ += "Error: Failed to compile opencl source (from CL to LLVM IR).\n";
return false;
}
llvmBinary_.assign(output->Buf().data(), output->Size());
elfSectionType_ = amd::OclElf::LLVMIR;
if (options->isDumpFlagSet(amd::option::DUMP_BC_ORIGINAL)) {
std::ofstream f(options->getDumpFileName("_original.bc").c_str(),
std::ios::binary | std::ios::trunc);
if (f.is_open()) {
f.write(llvmBinary_.data(), llvmBinary_.size());
f.close();
}
else {
buildLog_ += "Warning: opening the file to dump the compiled IR failed.\n";
}
}
if (clBinary()->saveSOURCE()) {
clBinary()->elfOut()->addSection(amd::OclElf::SOURCE, sourceCode.data(), sourceCode.size());
}
if (clBinary()->saveLLVMIR()) {
clBinary()->elfOut()->addSection(amd::OclElf::LLVMIR, llvmBinary_.data(), llvmBinary_.size(),
false);
// store the original compile options
clBinary()->storeCompileOptions(compileOptions_);
}
#endif // defined(WITH_LIGHTNING_COMPILER)
return true;
}
// ================================================================================================
static void logFunction(const char* msg, size_t size) {
std::cout << "Compiler Log: " << msg << std::endl;
}
// ================================================================================================
bool Program::compileImplHSAIL(const std::string& sourceCode,
const std::vector<const std::string*>& headers,
const char** headerIncludeNames, amd::option::Options* options) {
#if defined(WITH_COMPILER_LIB) || !defined(WITH_LIGHTNING_COMPILER)
acl_error errorCode;
aclTargetInfo target;
std::string arch = LP64_SWITCH("hsail", "hsail64");
target = aclGetTargetInfo(arch.c_str(), machineTarget_, &errorCode);
// end if asic info is ready
// We dump the source code for each program (param: headers)
// into their filenames (headerIncludeNames) into the TEMP
// folder specific to the OS and add the include path while
// compiling
// Find the temp folder for the OS
std::string tempFolder = amd::Os::getTempPath();
// Iterate through each source code and dump it into tmp
std::fstream f;
std::vector<std::string> newDirs;
for (size_t i = 0; i < headers.size(); ++i) {
std::string headerPath = tempFolder;
std::string headerIncludeName(headerIncludeNames[i]);
// replace / in path with current os's file separator
if (amd::Os::fileSeparator() != '/') {
for (auto& it : headerIncludeName) {
if (it == '/') it = amd::Os::fileSeparator();
}
}
size_t pos = headerIncludeName.rfind(amd::Os::fileSeparator());
if (pos != std::string::npos) {
headerPath += amd::Os::fileSeparator();
headerPath += headerIncludeName.substr(0, pos);
headerIncludeName = headerIncludeName.substr(pos + 1);
}
if (!amd::Os::pathExists(headerPath)) {
bool ret = amd::Os::createPath(headerPath);
assert(ret && "failed creating path!");
newDirs.push_back(headerPath);
}
std::string headerFullName = headerPath + amd::Os::fileSeparator() + headerIncludeName;
f.open(headerFullName.c_str(), std::fstream::out);
// Should we allow asserts
assert(!f.fail() && "failed creating header file!");
f.write(headers[i]->c_str(), headers[i]->length());
f.close();
}
// Create Binary
binaryElf_ = aclBinaryInit(sizeof(aclBinary), &target, &binOpts_, &errorCode);
if (errorCode != ACL_SUCCESS) {
buildLog_ += "Error: aclBinary init failure\n";
LogWarning("aclBinaryInit failed");
return false;
}
// Insert opencl into binary
errorCode = aclInsertSection(device().compiler(), binaryElf_, sourceCode.c_str(),
strlen(sourceCode.c_str()), aclSOURCE);
if (errorCode != ACL_SUCCESS) {
buildLog_ += "Error: Inserting openCl Source to binary\n";
}
// Set the options for the compiler
// Set the include path for the temp folder that contains the includes
if (!headers.empty()) {
compileOptions_.append(" -I");
compileOptions_.append(tempFolder);
}
#if !defined(_LP64) && defined(ATI_OS_LINUX)
if (options->origOptionStr.find("-cl-std=CL2.0") != std::string::npos) {
errorCode = ACL_UNSUPPORTED;
LogWarning("aclCompile failed");
return false;
}
#endif
// Compile source to IR
compileOptions_.append(ProcessOptions(options));
errorCode = aclCompile(device().compiler(), binaryElf_, compileOptions_.c_str(), ACL_TYPE_OPENCL,
ACL_TYPE_LLVMIR_BINARY, nullptr /* logFunction */);
buildLog_ += aclGetCompilerLog(device().compiler());
if (errorCode != ACL_SUCCESS) {
LogWarning("aclCompile failed");
buildLog_ += "Error: Compiling CL to IR\n";
return false;
}
clBinary()->storeCompileOptions(compileOptions_);
// Save the binary in the interface class
saveBinaryAndSetType(TYPE_COMPILED);
#endif // defined(WITH_COMPILER_LIB) || !defined(WITH_LIGHTNING_COMPILER)
return true;
}
// ================================================================================================
bool Program::initClBinary() {
if (clBinary_ == nullptr) {
clBinary_ = new ClBinary(device());
if (clBinary_ == nullptr) {
return false;
}
}
return true;
}
// ================================================================================================
void Program::releaseClBinary() {
if (clBinary_ != nullptr) {
delete clBinary_;
clBinary_ = nullptr;
}
}
// ================================================================================================
bool Program::initBuild(amd::option::Options* options) {
programOptions_ = options;
if (options->oVariables->DumpFlags > 0) {
static amd::Atomic<unsigned> build_num = 0;
options->setBuildNo(build_num++);
}
buildLog_.clear();
if (!initClBinary()) {
return false;
}
return true;
}
// ================================================================================================
bool Program::finiBuild(bool isBuildGood) { return true; }
// ================================================================================================
cl_int Program::compile(const std::string& sourceCode,
const std::vector<const std::string*>& headers,
const char** headerIncludeNames, const char* origOptions,
amd::option::Options* options) {
uint64_t start_time = 0;
if (options->oVariables->EnableBuildTiming) {
buildLog_ = "\nStart timing major build components.....\n\n";
start_time = amd::Os::timeNanos();
}
lastBuildOptionsArg_ = origOptions ? origOptions : "";
if (options) {
compileOptions_ = options->origOptionStr;
}
buildStatus_ = CL_BUILD_IN_PROGRESS;
if (!initBuild(options)) {
buildStatus_ = CL_BUILD_ERROR;
if (buildLog_.empty()) {
buildLog_ = "Internal error: Compilation init failed.";
}
}
if (options->oVariables->FP32RoundDivideSqrt &&
!(device().info().singleFPConfig_ & CL_FP_CORRECTLY_ROUNDED_DIVIDE_SQRT)) {
buildStatus_ = CL_BUILD_ERROR;
buildLog_ +=
"Error: -cl-fp32-correctly-rounded-divide-sqrt "
"specified without device support";
}
// Compile the source code if any
if ((buildStatus_ == CL_BUILD_IN_PROGRESS) && !sourceCode.empty() &&
!compileImpl(sourceCode, headers, headerIncludeNames, options)) {
buildStatus_ = CL_BUILD_ERROR;
if (buildLog_.empty()) {
buildLog_ = "Internal error: Compilation failed.";
}
}
setType(TYPE_COMPILED);
if ((buildStatus_ == CL_BUILD_IN_PROGRESS) && !createBinary(options)) {
buildLog_ += "Internal Error: creating OpenCL binary failed!\n";
}
if (!finiBuild(buildStatus_ == CL_BUILD_IN_PROGRESS)) {
buildStatus_ = CL_BUILD_ERROR;
if (buildLog_.empty()) {
buildLog_ = "Internal error: Compilation fini failed.";
}
}
if (buildStatus_ == CL_BUILD_IN_PROGRESS) {
buildStatus_ = CL_BUILD_SUCCESS;
} else {
buildError_ = CL_COMPILE_PROGRAM_FAILURE;
}
if (options->oVariables->EnableBuildTiming) {
std::stringstream tmp_ss;
tmp_ss << "\nTotal Compile Time: " << (amd::Os::timeNanos() - start_time) / 1000ULL << " us\n";
buildLog_ += tmp_ss.str();
}
if (options->oVariables->BuildLog && !buildLog_.empty()) {
if (strcmp(options->oVariables->BuildLog, "stderr") == 0) {
fprintf(stderr, "%s\n", options->optionsLog().c_str());
fprintf(stderr, "%s\n", buildLog_.c_str());
} else if (strcmp(options->oVariables->BuildLog, "stdout") == 0) {
printf("%s\n", options->optionsLog().c_str());
printf("%s\n", buildLog_.c_str());
} else {
std::fstream f;
std::stringstream tmp_ss;
std::string logs = options->optionsLog() + buildLog_;
tmp_ss << options->oVariables->BuildLog << "." << options->getBuildNo();
f.open(tmp_ss.str().c_str(), (std::fstream::out | std::fstream::binary));
f.write(logs.data(), logs.size());
f.close();
}
LogError(buildLog_.c_str());
}
return buildError();
}
// ================================================================================================
cl_int Program::link(const std::vector<Program*>& inputPrograms, const char* origLinkOptions,
amd::option::Options* linkOptions) {
lastBuildOptionsArg_ = origLinkOptions ? origLinkOptions : "";
if (linkOptions) {
linkOptions_ = linkOptions->origOptionStr;
}
buildStatus_ = CL_BUILD_IN_PROGRESS;
amd::option::Options options;
if (!getCompileOptionsAtLinking(inputPrograms, linkOptions)) {
buildStatus_ = CL_BUILD_ERROR;
if (buildLog_.empty()) {
buildLog_ += "Internal error: Get compile options failed.";
}
} else {
if (!amd::option::parseAllOptions(compileOptions_, options)) {
buildStatus_ = CL_BUILD_ERROR;
buildLog_ += options.optionsLog();
LogError("Parsing compile options failed.");
}
}
uint64_t start_time = 0;
if (options.oVariables->EnableBuildTiming) {
buildLog_ = "\nStart timing major build components.....\n\n";
start_time = amd::Os::timeNanos();
}
// initBuild() will clear buildLog_, so store it in a temporary variable
std::string tmpBuildLog = buildLog_;
if ((buildStatus_ == CL_BUILD_IN_PROGRESS) && !initBuild(&options)) {
buildStatus_ = CL_BUILD_ERROR;
if (buildLog_.empty()) {
buildLog_ += "Internal error: Compilation init failed.";
}
}
buildLog_ += tmpBuildLog;
if (options.oVariables->FP32RoundDivideSqrt &&
!(device().info().singleFPConfig_ & CL_FP_CORRECTLY_ROUNDED_DIVIDE_SQRT)) {
buildStatus_ = CL_BUILD_ERROR;
buildLog_ +=
"Error: -cl-fp32-correctly-rounded-divide-sqrt "
"specified without device support";
}
bool createLibrary = linkOptions ? linkOptions->oVariables->clCreateLibrary : false;
if ((buildStatus_ == CL_BUILD_IN_PROGRESS) && !linkImpl(inputPrograms, &options, createLibrary)) {
buildStatus_ = CL_BUILD_ERROR;
if (buildLog_.empty()) {
buildLog_ += "Internal error: Link failed.\n";
buildLog_ += "Make sure the system setup is correct.";
}
}
if (!finiBuild(buildStatus_ == CL_BUILD_IN_PROGRESS)) {
buildStatus_ = CL_BUILD_ERROR;
if (buildLog_.empty()) {
buildLog_ = "Internal error: Compilation fini failed.";
}
}
if (buildStatus_ == CL_BUILD_IN_PROGRESS) {
buildStatus_ = CL_BUILD_SUCCESS;
} else {
buildError_ = CL_LINK_PROGRAM_FAILURE;
}
if (options.oVariables->EnableBuildTiming) {
std::stringstream tmp_ss;
tmp_ss << "\nTotal Link Time: " << (amd::Os::timeNanos() - start_time) / 1000ULL << " us\n";
buildLog_ += tmp_ss.str();
}
if (options.oVariables->BuildLog && !buildLog_.empty()) {
if (strcmp(options.oVariables->BuildLog, "stderr") == 0) {
fprintf(stderr, "%s\n", options.optionsLog().c_str());
fprintf(stderr, "%s\n", buildLog_.c_str());
} else if (strcmp(options.oVariables->BuildLog, "stdout") == 0) {
printf("%s\n", options.optionsLog().c_str());
printf("%s\n", buildLog_.c_str());
} else {
std::fstream f;
std::stringstream tmp_ss;
std::string logs = options.optionsLog() + buildLog_;
tmp_ss << options.oVariables->BuildLog << "." << options.getBuildNo();
f.open(tmp_ss.str().c_str(), (std::fstream::out | std::fstream::binary));
f.write(logs.data(), logs.size());
f.close();
}
}
if (!buildLog_.empty()) {
LogError(buildLog_.c_str());
}
return buildError();
}
// ================================================================================================
cl_int Program::build(const std::string& sourceCode, const char* origOptions,
amd::option::Options* options) {
uint64_t start_time = 0;
if (options->oVariables->EnableBuildTiming) {
buildLog_ = "\nStart timing major build components.....\n\n";
start_time = amd::Os::timeNanos();
}
lastBuildOptionsArg_ = origOptions ? origOptions : "";
if (options) {
compileOptions_ = options->origOptionStr;
}
buildStatus_ = CL_BUILD_IN_PROGRESS;
if (!initBuild(options)) {
buildStatus_ = CL_BUILD_ERROR;
if (buildLog_.empty()) {
buildLog_ = "Internal error: Compilation init failed.";
}
}
if (options->oVariables->FP32RoundDivideSqrt &&
!(device().info().singleFPConfig_ & CL_FP_CORRECTLY_ROUNDED_DIVIDE_SQRT)) {
buildStatus_ = CL_BUILD_ERROR;
buildLog_ +=
"Error: -cl-fp32-correctly-rounded-divide-sqrt "
"specified without device support";
}
// Compile the source code if any
std::vector<const std::string*> headers;
if ((buildStatus_ == CL_BUILD_IN_PROGRESS) && !sourceCode.empty() &&
!compileImpl(sourceCode, headers, nullptr, options)) {
buildStatus_ = CL_BUILD_ERROR;
if (buildLog_.empty()) {
buildLog_ = "Internal error: Compilation failed.";
}
}
if ((buildStatus_ == CL_BUILD_IN_PROGRESS) && !linkImpl(options)) {
buildStatus_ = CL_BUILD_ERROR;
if (buildLog_.empty()) {
buildLog_ += "Internal error: Link failed.\n";
buildLog_ += "Make sure the system setup is correct.";
}
}
if (!finiBuild(buildStatus_ == CL_BUILD_IN_PROGRESS)) {
buildStatus_ = CL_BUILD_ERROR;
if (buildLog_.empty()) {
buildLog_ = "Internal error: Compilation fini failed.";
}
}
if (buildStatus_ == CL_BUILD_IN_PROGRESS) {
buildStatus_ = CL_BUILD_SUCCESS;
} else {
buildError_ = CL_BUILD_PROGRAM_FAILURE;
}
if (options->oVariables->EnableBuildTiming) {
std::stringstream tmp_ss;
tmp_ss << "\nTotal Build Time: " << (amd::Os::timeNanos() - start_time) / 1000ULL << " us\n";
buildLog_ += tmp_ss.str();
}
if (options->oVariables->BuildLog && !buildLog_.empty()) {
if (strcmp(options->oVariables->BuildLog, "stderr") == 0) {
fprintf(stderr, "%s\n", options->optionsLog().c_str());
fprintf(stderr, "%s\n", buildLog_.c_str());
} else if (strcmp(options->oVariables->BuildLog, "stdout") == 0) {
printf("%s\n", options->optionsLog().c_str());
printf("%s\n", buildLog_.c_str());
} else {
std::fstream f;
std::stringstream tmp_ss;
std::string logs = options->optionsLog() + buildLog_;
tmp_ss << options->oVariables->BuildLog << "." << options->getBuildNo();
f.open(tmp_ss.str().c_str(), (std::fstream::out | std::fstream::binary));
f.write(logs.data(), logs.size());
f.close();
}
}
if (!buildLog_.empty()) {
LogError(buildLog_.c_str());
}
return buildError();
}
// ================================================================================================
std::string Program::ProcessOptions(amd::option::Options* options) {
std::string optionsStr;
#ifndef WITH_LIGHTNING_COMPILER
optionsStr.append(" -D__AMD__=1");
optionsStr.append(" -D__").append(machineTarget_).append("__=1");
optionsStr.append(" -D__").append(machineTarget_).append("=1");
#endif
#ifdef WITH_LIGHTNING_COMPILER
int major, minor;
::sscanf(device().info().version_, "OpenCL %d.%d ", &major, &minor);
std::stringstream ss;
ss << " -D__OPENCL_VERSION__=" << (major * 100 + minor * 10);
optionsStr.append(ss.str());
#endif
if (device().info().imageSupport_ && options->oVariables->ImageSupport) {
optionsStr.append(" -D__IMAGE_SUPPORT__=1");
}
#ifndef WITH_LIGHTNING_COMPILER
// Set options for the standard device specific options
// All our devices support these options now
if (device().settings().reportFMAF_) {
optionsStr.append(" -DFP_FAST_FMAF=1");
}
if (device().settings().reportFMA_) {
optionsStr.append(" -DFP_FAST_FMA=1");
}
#endif
uint clcStd =
(options->oVariables->CLStd[2] - '0') * 100 + (options->oVariables->CLStd[4] - '0') * 10;
if (clcStd >= 200) {
std::stringstream opts;
// Add only for CL2.0 and later
opts << " -D"
<< "CL_DEVICE_MAX_GLOBAL_VARIABLE_SIZE=" << device().info().maxGlobalVariableSize_;
optionsStr.append(opts.str());
}
#if !defined(WITH_LIGHTNING_COMPILER)
if (!device().settings().singleFpDenorm_) {
optionsStr.append(" -cl-denorms-are-zero");
}
// Check if the host is 64 bit or 32 bit
LP64_ONLY(optionsStr.append(" -m64"));
#endif // !defined(WITH_LIGHTNING_COMPILER)
// Tokenize the extensions string into a vector of strings
std::istringstream istrstr(device().info().extensions_);
std::istream_iterator<std::string> sit(istrstr), end;
std::vector<std::string> extensions(sit, end);
if (IS_LIGHTNING && !options->oVariables->Legacy) {
// FIXME_lmoriche: opencl-c.h defines 'cl_khr_depth_images', so
// remove it from the command line. Should we fix opencl-c.h?
auto found = std::find(extensions.begin(), extensions.end(), "cl_khr_depth_images");
if (found != extensions.end()) {
extensions.erase(found);
}
if (!extensions.empty()) {
std::ostringstream clext;
clext << " -Xclang -cl-ext=+";
std::copy(extensions.begin(), extensions.end() - 1,
std::ostream_iterator<std::string>(clext, ",+"));
clext << extensions.back();
optionsStr.append(clext.str());
}
} else {
for (auto e : extensions) {
optionsStr.append(" -D").append(e).append("=1");
}
}
return optionsStr;
}
// ================================================================================================
bool Program::getCompileOptionsAtLinking(const std::vector<Program*>& inputPrograms,
const amd::option::Options* linkOptions) {
amd::option::Options compileOptions;
auto it = inputPrograms.cbegin();
const auto itEnd = inputPrograms.cend();
for (size_t i = 0; it != itEnd; ++it, ++i) {
Program* program = *it;
amd::option::Options compileOptions2;
amd::option::Options* thisCompileOptions = i == 0 ? &compileOptions : &compileOptions2;
if (!amd::option::parseAllOptions(program->compileOptions_, *thisCompileOptions)) {
buildLog_ += thisCompileOptions->optionsLog();
LogError("Parsing compile options failed.");
return false;
}
if (i == 0) compileOptions_ = program->compileOptions_;
// if we are linking a program executable, and if "program" is a
// compiled module or a library created with "-enable-link-options",
// we can overwrite "program"'s compile options with linking options
if (!linkOptions_.empty() && !linkOptions->oVariables->clCreateLibrary) {
bool linkOptsCanOverwrite = false;
if (program->type() != TYPE_LIBRARY) {
linkOptsCanOverwrite = true;
} else {
amd::option::Options thisLinkOptions;
if (!amd::option::parseLinkOptions(program->linkOptions_, thisLinkOptions)) {
buildLog_ += thisLinkOptions.optionsLog();
LogError("Parsing link options failed.");
return false;
}
if (thisLinkOptions.oVariables->clEnableLinkOptions) linkOptsCanOverwrite = true;
}
if (linkOptsCanOverwrite) {
if (!thisCompileOptions->setOptionVariablesAs(*linkOptions)) {
buildLog_ += thisCompileOptions->optionsLog();
LogError("Setting link options failed.");
return false;
}
}
if (i == 0) compileOptions_ += " " + linkOptions_;
}
// warn if input modules have inconsistent compile options
if (i > 0) {
if (!compileOptions.equals(*thisCompileOptions, true /*ignore clc options*/)) {
buildLog_ +=
"Warning: Input OpenCL binaries has inconsistent"
" compile options. Using compile options from"
" the first input binary!\n";
}
}
}
return true;
}
// ================================================================================================
bool Program::initClBinary(const char* binaryIn, size_t size) {
if (!initClBinary()) {
return false;
}
// Save the original binary that isn't owned by ClBinary
clBinary()->saveOrigBinary(binaryIn, size);
const char* bin = binaryIn;
size_t sz = size;
// unencrypted
int encryptCode = 0;
char* decryptedBin = nullptr;
#if !defined(WITH_LIGHTNING_COMPILER)
bool isSPIRV = isSPIRVMagic(binaryIn, size);
if (isSPIRV || isBcMagic(binaryIn)) {
acl_error err = ACL_SUCCESS;
aclBinaryOptions binOpts = {0};
binOpts.struct_size = sizeof(binOpts);
binOpts.elfclass =
(info().arch_id == aclX64 || info().arch_id == aclAMDIL64 || info().arch_id == aclHSAIL64)
? ELFCLASS64
: ELFCLASS32;
binOpts.bitness = ELFDATA2LSB;
binOpts.alloc = &::malloc;
binOpts.dealloc = &::free;
aclBinary* aclbin_v30 = aclBinaryInit(sizeof(aclBinary), &info(), &binOpts, &err);
if (err != ACL_SUCCESS) {
LogWarning("aclBinaryInit failed");
aclBinaryFini(aclbin_v30);
return false;
}
err = aclInsertSection(device().compiler(), aclbin_v30, binaryIn, size,
isSPIRV ? aclSPIRV : aclSPIR);
if (ACL_SUCCESS != err) {
LogWarning("aclInsertSection failed");
aclBinaryFini(aclbin_v30);
return false;
}
if (info().arch_id == aclHSAIL || info().arch_id == aclHSAIL64) {
err = aclWriteToMem(aclbin_v30, (void**)const_cast<char**>(&bin), &sz);
if (err != ACL_SUCCESS) {
LogWarning("aclWriteToMem failed");
aclBinaryFini(aclbin_v30);
return false;
}
aclBinaryFini(aclbin_v30);
} else {
aclBinary* aclbin_v21 = aclCreateFromBinary(aclbin_v30, aclBIFVersion21);
err = aclWriteToMem(aclbin_v21, (void**)const_cast<char**>(&bin), &sz);
if (err != ACL_SUCCESS) {
LogWarning("aclWriteToMem failed");
aclBinaryFini(aclbin_v30);
aclBinaryFini(aclbin_v21);
return false;
}
aclBinaryFini(aclbin_v30);
aclBinaryFini(aclbin_v21);
}
} else
#endif // !defined(WITH_LIGHTNING_COMPILER)
{
size_t decryptedSize;
if (!clBinary()->decryptElf(binaryIn, size, &decryptedBin, &decryptedSize, &encryptCode)) {
return false;
}
if (decryptedBin != nullptr) {
// It is decrypted binary.
bin = decryptedBin;
sz = decryptedSize;
}
if (!isElf(bin)) {
// Invalid binary.
if (decryptedBin != nullptr) {
delete[] decryptedBin;
}
return false;
}
}
clBinary()->setFlags(encryptCode);
return clBinary()->setBinary(bin, sz, (decryptedBin != nullptr));
}
// ================================================================================================
bool Program::setBinary(const char* binaryIn, size_t size) {
if (!initClBinary(binaryIn, size)) {
return false;
}
if (!clBinary()->setElfIn()) {
LogError("Setting input OCL binary failed");
return false;
}
uint16_t type;
if (!clBinary()->elfIn()->getType(type)) {
LogError("Bad OCL Binary: error loading ELF type!");
return false;
}
switch (type) {
case ET_NONE: {
setType(TYPE_NONE);
break;
}
case ET_REL: {
if (clBinary()->isSPIR() || clBinary()->isSPIRV()) {
setType(TYPE_INTERMEDIATE);
} else {
setType(TYPE_COMPILED);
}
break;
}
case ET_DYN: {
char* sect = nullptr;
size_t sz = 0;
// FIXME: we should look for the e_machine to detect an HSACO.
if (clBinary()->elfIn()->getSection(amd::OclElf::TEXT, &sect, &sz) && sect && sz > 0) {
setType(TYPE_EXECUTABLE);
} else {
setType(TYPE_LIBRARY);
}
break;
}
case ET_EXEC: {
setType(TYPE_EXECUTABLE);
break;
}
default:
LogError("Bad OCL Binary: bad ELF type!");
return false;
}
clBinary()->loadCompileOptions(compileOptions_);
clBinary()->loadLinkOptions(linkOptions_);
clBinary()->resetElfIn();
return true;
}
// ================================================================================================
aclType Program::getCompilationStagesFromBinary(std::vector<aclType>& completeStages,
bool& needOptionsCheck) {
aclType from = ACL_TYPE_DEFAULT;
if (isLC()) {
#if defined(WITH_LIGHTNING_COMPILER)
completeStages.clear();
needOptionsCheck = true;
//! @todo Should we also check for ACL_TYPE_OPENCL & ACL_TYPE_LLVMIR_TEXT?
// Checking llvmir in .llvmir section
bool containsLlvmirText = (type() == TYPE_COMPILED);
bool containsShaderIsa = (type() == TYPE_EXECUTABLE);
bool containsOpts = !(compileOptions_.empty() && linkOptions_.empty());
if (containsLlvmirText && containsOpts) {
completeStages.push_back(from);
from = ACL_TYPE_LLVMIR_BINARY;
}
if (containsShaderIsa) {
completeStages.push_back(from);
from = ACL_TYPE_ISA;
}
std::string sCurOptions = compileOptions_ + linkOptions_;
amd::option::Options curOptions;
if (!amd::option::parseAllOptions(sCurOptions, curOptions)) {
buildLog_ += curOptions.optionsLog();
LogError("Parsing compile options failed.");
return ACL_TYPE_DEFAULT;
}
switch (from) {
case ACL_TYPE_CG:
case ACL_TYPE_ISA:
// do not check options, if LLVMIR is absent or might be absent or options are absent
if (!curOptions.oVariables->BinLLVMIR || !containsLlvmirText || !containsOpts) {
needOptionsCheck = false;
}
break;
// recompilation might be needed
case ACL_TYPE_LLVMIR_BINARY:
case ACL_TYPE_DEFAULT:
default:
break;
}
#endif // !defined(WITH_LIGHTNING_COMPILER)
} else {
#if defined(WITH_COMPILER_LIB) || !defined(WITH_LIGHTNING_COMPILER)
acl_error errorCode;
size_t secSize = 0;
completeStages.clear();
needOptionsCheck = true;
size_t boolSize = sizeof(bool);
// Checking llvmir in .llvmir section
bool containsSpirv = true;
errorCode = aclQueryInfo(device().compiler(), binaryElf_, RT_CONTAINS_SPIRV, nullptr,
&containsSpirv, &boolSize);
if (errorCode != ACL_SUCCESS) {
containsSpirv = false;
}
if (containsSpirv) {
completeStages.push_back(from);
from = ACL_TYPE_SPIRV_BINARY;
}
bool containsSpirText = true;
errorCode = aclQueryInfo(device().compiler(), binaryElf_, RT_CONTAINS_SPIR, nullptr,
&containsSpirText, &boolSize);
if (errorCode != ACL_SUCCESS) {
containsSpirText = false;
}
if (containsSpirText) {
completeStages.push_back(from);
from = ACL_TYPE_SPIR_BINARY;
}
bool containsLlvmirText = true;
errorCode = aclQueryInfo(device().compiler(), binaryElf_, RT_CONTAINS_LLVMIR, nullptr,
&containsLlvmirText, &boolSize);
if (errorCode != ACL_SUCCESS) {
containsLlvmirText = false;
}
// Checking compile & link options in .comment section
bool containsOpts = true;
errorCode = aclQueryInfo(device().compiler(), binaryElf_, RT_CONTAINS_OPTIONS, nullptr,
&containsOpts, &boolSize);
if (errorCode != ACL_SUCCESS) {
containsOpts = false;
}
if (containsLlvmirText && containsOpts) {
completeStages.push_back(from);
from = ACL_TYPE_LLVMIR_BINARY;
}
// Checking HSAIL in .cg section
bool containsHsailText = true;
errorCode = aclQueryInfo(device().compiler(), binaryElf_, RT_CONTAINS_HSAIL, nullptr,
&containsHsailText, &boolSize);
if (errorCode != ACL_SUCCESS) {
containsHsailText = false;
}
// Checking BRIG sections
bool containsBrig = true;
errorCode = aclQueryInfo(device().compiler(), binaryElf_, RT_CONTAINS_BRIG, nullptr,
&containsBrig, &boolSize);
if (errorCode != ACL_SUCCESS) {
containsBrig = false;
}
if (containsBrig) {
completeStages.push_back(from);
from = ACL_TYPE_HSAIL_BINARY;
}
else if (containsHsailText) {
completeStages.push_back(from);
from = ACL_TYPE_HSAIL_TEXT;
}
// Checking Loader Map symbol from CG section
bool containsLoaderMap = true;
errorCode = aclQueryInfo(device().compiler(), binaryElf_, RT_CONTAINS_LOADER_MAP, nullptr,
&containsLoaderMap, &boolSize);
if (errorCode != ACL_SUCCESS) {
containsLoaderMap = false;
}
if (containsLoaderMap) {
completeStages.push_back(from);
from = ACL_TYPE_CG;
}
// Checking ISA in .text section
bool containsShaderIsa = true;
errorCode = aclQueryInfo(device().compiler(), binaryElf_, RT_CONTAINS_ISA, nullptr,
&containsShaderIsa, &boolSize);
if (errorCode != ACL_SUCCESS) {
containsShaderIsa = false;
}
if (containsShaderIsa) {
completeStages.push_back(from);
from = ACL_TYPE_ISA;
}
std::string sCurOptions = compileOptions_ + linkOptions_;
amd::option::Options curOptions;
if (!amd::option::parseAllOptions(sCurOptions, curOptions)) {
buildLog_ += curOptions.optionsLog();
LogError("Parsing compile options failed.");
return ACL_TYPE_DEFAULT;
}
switch (from) {
// compile from HSAIL text, no matter prev. stages and options
case ACL_TYPE_HSAIL_TEXT:
needOptionsCheck = false;
break;
case ACL_TYPE_HSAIL_BINARY:
// do not check options, if LLVMIR is absent or might be absent or options are absent
if (!curOptions.oVariables->BinLLVMIR || !containsLlvmirText || !containsOpts) {
needOptionsCheck = false;
}
break;
case ACL_TYPE_CG:
case ACL_TYPE_ISA:
// do not check options, if LLVMIR is absent or might be absent or options are absent
if (!curOptions.oVariables->BinLLVMIR || !containsLlvmirText || !containsOpts) {
needOptionsCheck = false;
}
// do not check options, if BRIG is absent or might be absent or LoaderMap is absent
if (!curOptions.oVariables->BinCG || !containsBrig || !containsLoaderMap) {
needOptionsCheck = false;
}
break;
// recompilation might be needed
case ACL_TYPE_LLVMIR_BINARY:
case ACL_TYPE_DEFAULT:
default:
break;
}
#endif // #if defined(WITH_COMPILER_LIB) || !defined(WITH_LIGHTNING_COMPILER)
}
return from;
}
// ================================================================================================
aclType Program::getNextCompilationStageFromBinary(amd::option::Options* options) {
aclType continueCompileFrom = ACL_TYPE_DEFAULT;
binary_t binary = this->binary();
// If the binary already exists
if ((binary.first != nullptr) && (binary.second > 0)) {
#if defined(WITH_COMPILER_LIB) || !defined(WITH_LIGHTNING_COMPILER)
if (aclValidateBinaryImage(binary.first, binary.second, BINARY_TYPE_ELF)) {
acl_error errorCode;
binaryElf_ = aclReadFromMem(binary.first, binary.second, &errorCode);
if (errorCode != ACL_SUCCESS) {
buildLog_ += "Error while BRIG Codegen phase: aclReadFromMem failure \n";
return continueCompileFrom;
}
}
#endif // defined(WITH_COMPILER_LIB) || !defined(WITH_LIGHTNING_COMPILER)
// save the current options
std::string sCurCompileOptions = compileOptions_;
std::string sCurLinkOptions = linkOptions_;
std::string sCurOptions = compileOptions_ + linkOptions_;
// Saving binary in the interface class,
// which also load compile & link options from binary
setBinary(static_cast<const char*>(binary.first), binary.second);
// Calculate the next stage to compile from, based on sections in binaryElf_;
// No any validity checks here
std::vector<aclType> completeStages;
bool needOptionsCheck = true;
continueCompileFrom = getCompilationStagesFromBinary(completeStages, needOptionsCheck);
if (!options || !needOptionsCheck) {
return continueCompileFrom;
}
bool recompile = false;
//! @todo Should we also check for ACL_TYPE_OPENCL & ACL_TYPE_LLVMIR_TEXT?
switch (continueCompileFrom) {
case ACL_TYPE_HSAIL_BINARY:
case ACL_TYPE_CG:
case ACL_TYPE_ISA: {
// Compare options loaded from binary with current ones, recompile if differ;
// If compile options are absent in binary, do not compare and recompile
if (compileOptions_.empty()) break;
std::string sBinOptions;
#if defined(WITH_COMPILER_LIB) || !defined(WITH_LIGHTNING_COMPILER)
if (binaryElf_ != nullptr) {
const oclBIFSymbolStruct* symbol = findBIF30SymStruct(symOpenclCompilerOptions);
assert(symbol && "symbol not found");
std::string symName =
std::string(symbol->str[bif::PRE]) + std::string(symbol->str[bif::POST]);
size_t symSize = 0;
acl_error errorCode;
const void* opts = aclExtractSymbol(device().compiler(), binaryElf_, &symSize,
aclCOMMENT, symName.c_str(), &errorCode);
if (errorCode != ACL_SUCCESS) {
recompile = true;
break;
}
sBinOptions = std::string((char*)opts, symSize);
}
else
#endif // defined(WITH_COMPILER_LIB) || !defined(WITH_LIGHTNING_COMPILER)
{
sBinOptions = sCurOptions;
}
compileOptions_ = sCurCompileOptions;
linkOptions_ = sCurLinkOptions;
amd::option::Options curOptions, binOptions;
if (!amd::option::parseAllOptions(sBinOptions, binOptions)) {
buildLog_ += binOptions.optionsLog();
LogError("Parsing compile options from binary failed.");
return ACL_TYPE_DEFAULT;
}
if (!amd::option::parseAllOptions(sCurOptions, curOptions)) {
buildLog_ += curOptions.optionsLog();
LogError("Parsing compile options failed.");
return ACL_TYPE_DEFAULT;
}
if (!curOptions.equals(binOptions)) {
recompile = true;
}
break;
}
default:
break;
}
if (recompile) {
while (!completeStages.empty()) {
continueCompileFrom = completeStages.back();
if (continueCompileFrom == ACL_TYPE_SPIRV_BINARY ||
continueCompileFrom == ACL_TYPE_LLVMIR_BINARY ||
continueCompileFrom == ACL_TYPE_SPIR_BINARY ||
continueCompileFrom == ACL_TYPE_DEFAULT) {
break;
}
completeStages.pop_back();
}
}
}
else {
const char* xLang = options->oVariables->XLang;
if (xLang != nullptr && strcmp(xLang, "asm") == 0) {
continueCompileFrom = ACL_TYPE_ASM_TEXT;
}
}
return continueCompileFrom;
}
// ================================================================================================
bool Program::FindGlobalVarSize(void* binary, size_t binSize) {
#if defined(WITH_LIGHTNING_COMPILER)
size_t progvarsTotalSize = 0;
size_t dynamicSize = 0;
size_t progvarsWriteSize = 0;
// Begin the Elf image from memory
Elf* e = elf_memory((char*)binary, binSize, nullptr);
if (elf_kind(e) != ELF_K_ELF) {
buildLog_ += "Error while reading the ELF program binary\n";
return false;
}
size_t numpHdrs;
if (elf_getphdrnum(e, &numpHdrs) != 0) {
buildLog_ += "Error while reading the ELF program binary\n";
return false;
}
for (size_t i = 0; i < numpHdrs; ++i) {
GElf_Phdr pHdr;
if (gelf_getphdr(e, i, &pHdr) != &pHdr) {
continue;
}
// Look for the runtime metadata note
if (pHdr.p_type == PT_NOTE && pHdr.p_align >= sizeof(int)) {
// Iterate over the notes in this segment
address ptr = (address)binary + pHdr.p_offset;
address segmentEnd = ptr + pHdr.p_filesz;
while (ptr < segmentEnd) {
Elf_Note* note = (Elf_Note*)ptr;
address name = (address)&note[1];
address desc = name + amd::alignUp(note->n_namesz, sizeof(int));
if (note->n_type == 7 ||
note->n_type == 8) {
buildLog_ += "Error: object code with old metadata is not supported\n";
return false;
}
else if (note->n_type == 10 /* NT_AMD_AMDGPU_HSA_METADATA */ &&
note->n_namesz == sizeof "AMD" &&
!memcmp(name, "AMD", note->n_namesz)) {
std::string metadataStr((const char*)desc, (size_t)note->n_descsz);
metadata_ = new CodeObjectMD();
if (llvm::AMDGPU::HSAMD::fromString(metadataStr, *metadata_)) {
buildLog_ += "Error: failed to process metadata\n";
return false;
}
// We've found and loaded the runtime metadata, exit the
// note record loop now.
break;
}
ptr += sizeof(*note) + amd::alignUp(note->n_namesz, sizeof(int)) +
amd::alignUp(note->n_descsz, sizeof(int));
}
}
// Accumulate the size of R & !X loadable segments
else if (pHdr.p_type == PT_LOAD && !(pHdr.p_flags & PF_X)) {
if (pHdr.p_flags & PF_R) {
progvarsTotalSize += pHdr.p_memsz;
}
if (pHdr.p_flags & PF_W) {
progvarsWriteSize += pHdr.p_memsz;
}
}
else if (pHdr.p_type == PT_DYNAMIC) {
dynamicSize += pHdr.p_memsz;
}
}
elf_end(e);
if (!metadata_) {
buildLog_ +=
"Error: runtime metadata section not present in ELF program binary\n";
return false;
}
progvarsTotalSize -= dynamicSize;
setGlobalVariableTotalSize(progvarsTotalSize);
if (progvarsWriteSize != dynamicSize) {
hasGlobalStores_ = true;
}
#endif // defined(WITH_LIGHTNING_COMPILER)
return true;
}
}