Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions projects/clr/hipamd/src/hip_comgr_helper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ THE SOFTWARE.
#include <io.h>
#endif
#include "../src/amd_hsa_elf.hpp"
#include "elf/elf.hpp"

namespace hip {
std::unordered_set<LinkProgram*> LinkProgram::linker_set_;
Expand All @@ -43,8 +44,14 @@ constexpr char const* OFFLOAD_KIND_HIP = "hip";
constexpr char const* OFFLOAD_KIND_HIPV4 = "hipv4";
constexpr char const* OFFLOAD_KIND_HCC = "hcc";
constexpr char const* AMDGCN_TARGET_TRIPLE = "amdgcn-amd-amdhsa-";
constexpr char const* SPIRV_TARGET_TRIPLE = "spirv64-amd-amdhsa-";
constexpr char const* SPIRV_BUNDLE_ENTRY_ID = "hip-spirv64-amd-amdhsa-unknown-amdgcnspirv";

static bool isSpirv(const std::string& isa) {
return isa.find("spirv64") != std::string::npos ||
isa.find("amdgcnspirv") != std::string::npos;
}

static constexpr size_t bundle_magic_string_size =
strLiteralLength(CLANG_OFFLOAD_BUNDLER_MAGIC_STR);

Expand Down Expand Up @@ -312,6 +319,45 @@ bool compileToExecutable(const comgr_helper::ComgrDataSetUniqueHandle& compileIn
const std::string& isa, std::vector<std::string>& compileOptions,
std::vector<std::string>& linkOptions, std::string& buildLog,
std::vector<char>& exe) {
if (isSpirv(isa)) {
amd_comgr_language_t lang = AMD_COMGR_LANGUAGE_HIP;
comgr_helper::ComgrDataSetUniqueHandle bcOutput;
comgr_helper::ComgrDataSetUniqueHandle spirvOutput;
comgr_helper::ComgrActionInfoUniqueHandle compileAction;

// Hip -> Llvm bc
if (!createAction(compileAction, compileOptions, isa, lang))
return false;
if (bcOutput.Create() != AMD_COMGR_STATUS_SUCCESS)
return false;
if (amd::Comgr::do_action(AMD_COMGR_ACTION_COMPILE_SOURCE_WITH_DEVICE_LIBS_TO_BC,
compileAction.get(), compileInputs.get(),
bcOutput.get()) != AMD_COMGR_STATUS_SUCCESS)
return false;

// Llvm bc -> Spirv
comgr_helper::ComgrActionInfoUniqueHandle spirvAction;
std::vector<std::string> emptyOpts;
if (!createAction(spirvAction, emptyOpts, isa, AMD_COMGR_LANGUAGE_NONE))
return false;
if (spirvOutput.Create() != AMD_COMGR_STATUS_SUCCESS)
return false;

if (amd::Comgr::do_action(AMD_COMGR_ACTION_TRANSLATE_BC_TO_SPIRV, spirvAction.get(),
bcOutput.get(), spirvOutput.get()) != AMD_COMGR_STATUS_SUCCESS)
return false;
std::vector<char> spirvBinary;
if (!extractByteCodeBinary(spirvOutput, AMD_COMGR_DATA_KIND_SPIRV, spirvBinary))
return false;

// Output size-prefixed SPIR-V: [4-byte size][SPIR-V module]
uint32_t spirvSize = static_cast<uint32_t>(spirvBinary.size());
exe.resize(sizeof(spirvSize) + spirvBinary.size());
std::memcpy(exe.data(), &spirvSize, sizeof(spirvSize));
std::memcpy(exe.data() + sizeof(spirvSize), spirvBinary.data(), spirvBinary.size());
return true;
}

amd_comgr_language_t lang = AMD_COMGR_LANGUAGE_HIP;
comgr_helper::ComgrDataSetUniqueHandle reloc;
comgr_helper::ComgrDataSetUniqueHandle output;
Expand Down
28 changes: 23 additions & 5 deletions projects/clr/hipamd/src/hip_fatbin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,17 @@ static bool IsCodeObjectCompressed(const void* image) {

static bool IsCodeObjectElf(const void* image) {
const amd::Elf64_Ehdr* ehdr = reinterpret_cast<const amd::Elf64_Ehdr*>(image);
return ehdr->e_machine == EM_AMDGPU && ehdr->e_ident[EI_OSABI] == ELFOSABI_AMDGPU_HSA;
// AMDGPU ELF
if (ehdr->e_machine == EM_AMDGPU && ehdr->e_ident[EI_OSABI] == ELFOSABI_AMDGPU_HSA) {
return true;
}
return false;
}

static bool IsCodeObjectSpirv(const void* image) {
const uint32_t spirv_magic = 0x07230203;
const uint32_t* words = reinterpret_cast<const uint32_t*>(image);
return words[1] == spirv_magic;
}

static bool UncompressAndPopulateCodeObject(
Expand Down Expand Up @@ -425,18 +435,26 @@ hipError_t FatBinaryInfo::ExtractFatBinaryUsingCOMGR(const std::vector<hip::Devi
bool is_compressed = IsCodeObjectCompressed(image_),
is_uncompressed = IsCodeObjectUncompressed(image_);

// It better be elf if its neither compressed nor uncompressed
// Neither compressed nor uncompressed: therefore, ELF or SPIRV.
if (!is_compressed && !is_uncompressed) {
if (IsCodeObjectElf(image_)) {
// Load the binary directly
// Load the ELF binary directly
auto elf_size = amd::Elf::getElfSize(image_);
for (size_t i = 0; i < devices.size(); i++) {
if (hipSuccess != AddDevProgram(devices[i], image_, elf_size, 0))
return hipErrorInvalidImage;
}
return hipSuccess; // We are done since it was already ELF
return hipSuccess;
} else if (IsCodeObjectSpirv(image_)) {
uint32_t spirv_size = *reinterpret_cast<const uint32_t*>(image_);
size_t total_size = sizeof(uint32_t) + spirv_size;
for (size_t i = 0; i < devices.size(); i++) {
if (hipSuccess != AddDevProgram(devices[i], image_, total_size, 0))
return hipErrorInvalidImage;
}
return hipSuccess;
} else {
LogError("The code object has invalid header: compressed, uncompressed or elf");
LogError("The code object has invalid header: compressed, uncompressed, elf, or spirv");
return hipErrorInvalidImage;
}
}
Expand Down
5 changes: 4 additions & 1 deletion projects/clr/hipamd/src/hiprtc/hiprtcInternal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,10 @@ bool RTCCompileProgram::transformOptions(std::vector<std::string>& compile_optio
[](const std::string& str) { return str.find("--offload-arch=") != std::string::npos; });
res != compile_options.end()) {
auto isaName = getValueOf(*res);
isa_ = "amdgcn-amd-amdhsa--" + isaName;
if (isaName == "amdgcnspirv")
isa_ = "spirv64-amd-amdhsa--" + isaName;
else
isa_ = "amdgcn-amd-amdhsa--" + isaName;
settings_.offloadArchProvided = true;
return true;
}
Expand Down
3 changes: 3 additions & 0 deletions projects/clr/rocclr/device/device.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1526,6 +1526,9 @@ bool ClBinary::isSPIRV() const {
char* section = nullptr;
size_t sz = 0;

if (!elfIn_) {
return false;
}
if (elfIn_->getSection(amd::Elf::SPIRV, &section, &sz) && section && sz > 0) {
return true;
}
Expand Down
117 changes: 115 additions & 2 deletions projects/clr/rocclr/device/devprogram.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include "devkernel.hpp"
#include "utils/macros.hpp"
#include "utils/options.hpp"
#include "utils/util.hpp"
#include "comgrctx.hpp"

#include <algorithm>
Expand Down Expand Up @@ -54,6 +55,13 @@ inline static std::vector<std::string> splitSpaceSeparatedString(const char* str
return vec;
}

inline static bool isSpirv(const char* binary, size_t size) {
if (size < 8) return false;
const uint32_t* words = reinterpret_cast<const uint32_t*>(binary);
const uint32_t spirv_magic = 0x07230203;
return words[1] == spirv_magic;
}

// ================================================================================================
Program::Program(amd::Device& device, amd::Program& owner)
: device_(device),
Expand Down Expand Up @@ -802,6 +810,86 @@ bool Program::linkImpl(amd::option::Options* options) {
bLinkLLVMBitcode = false;
break;
}
case FILE_TYPE_SPIRV_BINARY: {
amd::Comgr::destroy_data_set(inputs);

// Get SPIRV binary, compile it to machine code,
// then update clBinary to refer to its Kernel.

const char *spirvSection = llvmBinary_.data();
size_t spirvSize = llvmBinary_.size();

amd_comgr_data_set_t spirvDataSet;
if (amd::Comgr::create_data_set(&spirvDataSet) != AMD_COMGR_STATUS_SUCCESS)
return false;
amd::ScopeGuard spirvDataSetGuard([&]() { amd::Comgr::destroy_data_set(spirvDataSet); });
amd_comgr_data_t spirvData;
if (amd::Comgr::create_data(AMD_COMGR_DATA_KIND_SPIRV, &spirvData) != AMD_COMGR_STATUS_SUCCESS)
return false;
amd::ScopeGuard spirvDataGuard([&]() { amd::Comgr::release_data(spirvData); });
if (amd::Comgr::set_data(spirvData, spirvSize, spirvSection) != AMD_COMGR_STATUS_SUCCESS)
return false;
if (amd::Comgr::set_data_name(spirvData, "offloadhiprtc.spv") != AMD_COMGR_STATUS_SUCCESS)
return false;
if (amd::Comgr::data_set_add(spirvDataSet, spirvData) != AMD_COMGR_STATUS_SUCCESS)
return false;

// Spirv -> Reloc
amd_comgr_action_info_t relocAction;
if (amd::Comgr::create_action_info(&relocAction) != AMD_COMGR_STATUS_SUCCESS)
return false;
amd::ScopeGuard relocActionGuard([&]() { amd::Comgr::destroy_action_info(relocAction); });
std::string isa = device().isa().isaName();
if (amd::Comgr::action_info_set_isa_name(relocAction, isa.c_str()) != AMD_COMGR_STATUS_SUCCESS)
return false;
amd_comgr_data_set_t relocDataSet;
if (amd::Comgr::create_data_set(&relocDataSet) != AMD_COMGR_STATUS_SUCCESS)
return false;
amd::ScopeGuard relocDataSetGuard([&]() { amd::Comgr::destroy_data_set(relocDataSet); });
if (amd::Comgr::action_info_set_device_lib_linking(relocAction, true) != AMD_COMGR_STATUS_SUCCESS)
return false;
if (amd::Comgr::do_action(AMD_COMGR_ACTION_COMPILE_SPIRV_TO_RELOCATABLE, relocAction,
spirvDataSet, relocDataSet) != AMD_COMGR_STATUS_SUCCESS)
return false;

// Reloc -> Linked Exe
amd_comgr_action_info_t exeAction;
if (amd::Comgr::create_action_info(&exeAction) != AMD_COMGR_STATUS_SUCCESS)
return false;
amd::ScopeGuard exeActionGuard([&]() { amd::Comgr::destroy_action_info(exeAction); });
if (amd::Comgr::action_info_set_isa_name(exeAction, isa.c_str()) != AMD_COMGR_STATUS_SUCCESS)
return false;
amd_comgr_data_set_t exeDataSet;
if (amd::Comgr::create_data_set(&exeDataSet) != AMD_COMGR_STATUS_SUCCESS)
return false;
amd::ScopeGuard exeDataSetGuard([&]() { amd::Comgr::destroy_data_set(exeDataSet); });
if (amd::Comgr::do_action(AMD_COMGR_ACTION_LINK_RELOCATABLE_TO_EXECUTABLE, exeAction,
relocDataSet, exeDataSet) != AMD_COMGR_STATUS_SUCCESS)
return false;

// Linked Exe -> Kernel in exeBuffer
amd_comgr_data_t exeData;
if (amd::Comgr::action_data_get_data(exeDataSet, AMD_COMGR_DATA_KIND_EXECUTABLE, 0, &exeData) !=
AMD_COMGR_STATUS_SUCCESS)
return false;
amd::ScopeGuard exeDataGuard([&]() { amd::Comgr::release_data(exeData); });
size_t exeSize = 0;
if (amd::Comgr::get_data(exeData, &exeSize, nullptr) != AMD_COMGR_STATUS_SUCCESS)
return false;
char *exeBuffer = new char[exeSize];
amd::ScopeGuard exeBufferGuard([&]() { delete[] exeBuffer; });
if (amd::Comgr::get_data(exeData, &exeSize, exeBuffer) != AMD_COMGR_STATUS_SUCCESS)
return false;
if (GPU_DUMP_CODE_OBJECT)
dumpCodeObject(std::string{exeBuffer, exeSize});
if (!createKernels(exeBuffer, exeSize, options->oVariables->UniformWorkGroupSize, internal_))
return false;

exeBufferGuard.Dismiss();
clBinary()->setBinary(exeBuffer, exeSize, /*allocated=*/true);
setType(TYPE_EXECUTABLE);
return true;
}
case FILE_TYPE_ISA: {
amd::Comgr::destroy_data_set(inputs);
binary_t isaBinary = binary();
Expand Down Expand Up @@ -1311,7 +1399,7 @@ int32_t Program::build(const std::string& sourceCode, const char* origOptions,
}
}

if (options->oVariables->FP32RoundDivideSqrt &&
if (options->oVariables && options->oVariables->FP32RoundDivideSqrt &&
!(device().info().singleFPConfig_ & CL_FP_CORRECTLY_ROUNDED_DIVIDE_SQRT)) {
buildStatus_ = CL_BUILD_ERROR;
buildLog_ +=
Expand Down Expand Up @@ -1572,6 +1660,19 @@ void Program::addKernel(Kernel* k) {
// ================================================================================================
bool Program::setBinary(const char* binaryIn, size_t size, const device::Program* same_dev_prog,
amd::Os::FileDesc fdesc, size_t foffset, std::string uri) {
if (isSpirv(binaryIn, size)) {
isSpirv_ = true;
uint32_t spirvSize = *reinterpret_cast<const uint32_t*>(binaryIn);
const char *spirvData = binaryIn + sizeof(uint32_t);
llvmBinary_.assign(spirvData, spirvSize);
setType(TYPE_INTERMEDIATE);
if (same_dev_prog != nullptr) {
compileOptions_ = same_dev_prog->compileOptions();
linkOptions_ = same_dev_prog->linkOptions();
}
return true;
}

if (!initClBinary(binaryIn, size, fdesc, foffset, uri)) {
ClPrint(amd::LOG_DETAIL_DEBUG, amd::LOG_KERN, "Init CL Binary failed \n");
return false;
Expand All @@ -1588,7 +1689,12 @@ bool Program::setBinary(const char* binaryIn, size_t size, const device::Program
}
switch (type) {
case ET_NONE: {
setType(TYPE_NONE);
// ET_NONE ELFs can still contain SPIR-V sections (used by HIPRTC SPIRV output)
if (clBinary()->isSPIRV()) {
setType(TYPE_INTERMEDIATE);
} else {
setType(TYPE_NONE);
}
break;
}
case ET_REL: {
Expand Down Expand Up @@ -1640,8 +1746,15 @@ Program::file_type_t Program::getCompilationStagesFromBinary(
// Checking llvmir in .llvmir section
bool containsLlvmirText = (type() == TYPE_COMPILED);
bool containsShaderIsa = (type() == TYPE_EXECUTABLE);
// TYPE_INTERMEDIATE means the binary contains SPIRV
// Note: We can't call isSPIRV() here because elfIn_ may have been reset after setBinary()
bool containsSpirvBinary = (type() == TYPE_INTERMEDIATE);
bool containsOpts = !(compileOptions_.empty() && linkOptions_.empty());

if (containsSpirvBinary) {
completeStages.push_back(from);
from = FILE_TYPE_SPIRV_BINARY;
}
if (containsLlvmirText && containsOpts) {
completeStages.push_back(from);
from = FILE_TYPE_LLVMIR_BINARY;
Expand Down
1 change: 1 addition & 0 deletions projects/clr/rocclr/device/devprogram.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ class Program : public amd::HeapObject {
uint32_t isHIP_ : 1; //!< Determine if the program is for HIP
uint32_t coLoaded_ : 1; //!< Has the code objected been loaded
uint32_t trapHandler_ : 1; //!< It is a trap handler for debugger
uint32_t isSpirv_ : 1; //!< Is SPIRV
};
uint32_t flags_; //!< Program flags
};
Expand Down
11 changes: 10 additions & 1 deletion projects/clr/rocclr/platform/program.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,13 @@

namespace amd {

static bool isSpirv(const void* image, size_t size) {
if (size < 8) return false;
const uint32_t *words = reinterpret_cast<const uint32_t*>(image);
const uint32_t spirv_magic = 0x07230203;
return words[1] == spirv_magic;
}

static void remove_g_option(std::string& option) {
// Remove " -g " option from application.
// People can still add -g in AMD_OCL_BUILD_OPTIONS_APPEND, if it is so desired.
Expand Down Expand Up @@ -93,7 +100,9 @@ const Symbol* Program::findSymbol(const char* kernelName) const {
int32_t Program::addDeviceProgram(Device& device, const void* image, size_t length, bool make_copy,
amd::option::Options* options, const amd::Program* same_prog,
amd::Os::FileDesc fdesc, size_t foffset, std::string uri) {
if (image != NULL && !amd::Elf::isElfMagic((const char*)image)) {
// Accept ELF or SPIRV binaries
if (image != NULL && !amd::Elf::isElfMagic((const char*)image) &&
!isSpirv(image, length)) {
return CL_INVALID_BINARY;
}

Expand Down
Loading