Files
TheRock/rocm-systems/projects/clr/rocclr/device/devkernel.cpp
T
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

1138 lines
39 KiB
C++

/*
* Copyright (c) Advanced Micro Devices, Inc., or its affiliates.
*
* SPDX-License-Identifier: MIT
*/
#include "platform/runtime.hpp"
#include "platform/program.hpp"
#include "platform/ndrange.hpp"
#include "platform/kernel_init.hpp"
#include "devkernel.hpp"
#include "utils/macros.hpp"
#include "utils/options.hpp"
#include "comgrctx.hpp"
#include <array>
#include <map>
#include <string>
#include <sstream>
namespace amd::device {
// ================================================================================================
static constexpr clk_value_type_t ClkValueMapType[6][6] = {
{T_CHAR, T_CHAR2, T_CHAR3, T_CHAR4, T_CHAR8, T_CHAR16},
{T_SHORT, T_SHORT2, T_SHORT3, T_SHORT4, T_SHORT8, T_SHORT16},
{T_INT, T_INT2, T_INT3, T_INT4, T_INT8, T_INT16},
{T_LONG, T_LONG2, T_LONG3, T_LONG4, T_LONG8, T_LONG16},
{T_FLOAT, T_FLOAT2, T_FLOAT3, T_FLOAT4, T_FLOAT8, T_FLOAT16},
{T_DOUBLE, T_DOUBLE2, T_DOUBLE3, T_DOUBLE4, T_DOUBLE8, T_DOUBLE16},
};
// ================================================================================================
amd_comgr_status_t getMetaBuf(const amd_comgr_metadata_node_t meta,
std::string* str) {
size_t size = 0;
amd_comgr_status_t status = amd::Comgr::get_metadata_string(meta, &size, NULL);
if (status == AMD_COMGR_STATUS_SUCCESS) {
str->resize(size-1); // minus one to discount the null character
status = amd::Comgr::get_metadata_string(meta, &size, &((*str)[0]));
}
return status;
}
// ================================================================================================
bool getValueFromIsaMeta(const std::string& isa, const char* key, std::string& retValue) {
amd_comgr_metadata_node_t isaMeta;
amd_comgr_status_t status = amd::Comgr::get_isa_metadata(isa.c_str(), &isaMeta);
if (status != AMD_COMGR_STATUS_SUCCESS) {
ClPrint(amd::LOG_ERROR, amd::LOG_INIT, "getIsaMeta(%s) failed!", isa.c_str());
return false;
}
amd_comgr_metadata_node_t valMeta;
size_t size = 0;
status = amd::Comgr::metadata_lookup(isaMeta, key, &valMeta);
if (status == AMD_COMGR_STATUS_SUCCESS) {
status = amd::Comgr::get_metadata_string(valMeta, &size, NULL);
}
if (status == AMD_COMGR_STATUS_SUCCESS) {
retValue.resize(size - 1);
status = amd::Comgr::get_metadata_string(valMeta, &size, &(retValue[0]));
}
if (status == AMD_COMGR_STATUS_SUCCESS) {
status = amd::Comgr::destroy_metadata(valMeta);
}
amd::Comgr::destroy_metadata(isaMeta);
return (status == AMD_COMGR_STATUS_SUCCESS) ? true : false;
}
// ================================================================================================
static amd_comgr_status_t populateArgs(const amd_comgr_metadata_node_t key,
const amd_comgr_metadata_node_t value, void* data) {
amd_comgr_status_t status;
amd_comgr_metadata_kind_t kind;
std::string buf;
// get the key of the argument field
size_t size = 0;
status = amd::Comgr::get_metadata_kind(key, &kind);
if (kind == AMD_COMGR_METADATA_KIND_STRING && status == AMD_COMGR_STATUS_SUCCESS) {
status = getMetaBuf(key, &buf);
}
if (status != AMD_COMGR_STATUS_SUCCESS) {
return AMD_COMGR_STATUS_ERROR;
}
ArgField itArgField = amd::Kernel::FindValue<ArgField>(amd::Kernel::kArgFieldMap, buf);
if (itArgField == ArgField::MaxSize) {
return AMD_COMGR_STATUS_ERROR;
}
// get the value of the argument field
status = getMetaBuf(value, &buf);
amd::KernelParameterDescriptor* lcArg = static_cast<amd::KernelParameterDescriptor*>(data);
switch (itArgField) {
case ArgField::Name:
lcArg->name_ = buf;
break;
case ArgField::TypeName:
lcArg->typeName_ = buf;
break;
case ArgField::Size:
lcArg->size_ = atoi(buf.c_str());
break;
case ArgField::Align:
lcArg->alignment_ = atoi(buf.c_str());
break;
case ArgField::ValueKind: {
amd::KernelParameterDescriptor::Desc itValueKind =
amd::Kernel::FindValue<amd::KernelParameterDescriptor::Desc>(amd::Kernel::kArgValueKind,
buf);
if (itValueKind == amd::KernelParameterDescriptor::Desc::MaxSize) {
lcArg->info_.hidden_ = true;
return AMD_COMGR_STATUS_ERROR;
}
lcArg->info_.oclObject_ = itValueKind;
switch (lcArg->info_.oclObject_) {
case amd::KernelParameterDescriptor::MemoryObject:
if (buf.compare("DynamicSharedPointer") == 0) {
lcArg->info_.shared_ = true;
}
break;
case amd::KernelParameterDescriptor::HiddenGlobalOffsetX:
case amd::KernelParameterDescriptor::HiddenGlobalOffsetY:
case amd::KernelParameterDescriptor::HiddenGlobalOffsetZ:
case amd::KernelParameterDescriptor::HiddenPrintfBuffer:
case amd::KernelParameterDescriptor::HiddenHostcallBuffer:
case amd::KernelParameterDescriptor::HiddenDefaultQueue:
case amd::KernelParameterDescriptor::HiddenCompletionAction:
case amd::KernelParameterDescriptor::HiddenMultiGridSync:
case amd::KernelParameterDescriptor::HiddenDynamicLdsSize:
case amd::KernelParameterDescriptor::HiddenNone:
lcArg->info_.hidden_ = true;
break;
}
} break;
case ArgField::PointeeAlign:
lcArg->info_.arrayIndex_ = atoi(buf.c_str());
break;
case ArgField::AddrSpaceQual: {
cl_int itAddrSpaceQual = amd::Kernel::FindValue(amd::Kernel::kArgAddrSpaceQual, buf);
if (itAddrSpaceQual == static_cast<cl_int>(0)) {
return AMD_COMGR_STATUS_ERROR;
}
lcArg->addressQualifier_ = itAddrSpaceQual;
} break;
case ArgField::AccQual: {
cl_int itAccQual = amd::Kernel::FindValue(amd::Kernel::kArgAccQual, buf);
if (itAccQual == static_cast<cl_int>(0)) {
return AMD_COMGR_STATUS_ERROR;
}
lcArg->accessQualifier_ = itAccQual;
lcArg->info_.readOnly_ =
(lcArg->accessQualifier_ == CL_KERNEL_ARG_ACCESS_READ_ONLY) ? true : false;
} break;
case ArgField::ActualAccQual: {
cl_int itAccQual = amd::Kernel::FindValue(amd::Kernel::kArgAccQual, buf);
if (itAccQual == static_cast<cl_int>(0)) {
return AMD_COMGR_STATUS_ERROR;
}
// lcArg->mActualAccQual = itAccQual->second;
} break;
case ArgField::IsConst:
lcArg->typeQualifier_ |= (buf.compare("true") == 0) ? CL_KERNEL_ARG_TYPE_CONST : 0;
break;
case ArgField::IsRestrict:
lcArg->typeQualifier_ |= (buf.compare("true") == 0) ? CL_KERNEL_ARG_TYPE_RESTRICT : 0;
break;
case ArgField::IsVolatile:
lcArg->typeQualifier_ |= (buf.compare("true") == 0) ? CL_KERNEL_ARG_TYPE_VOLATILE : 0;
break;
case ArgField::IsPipe:
lcArg->typeQualifier_ |= (buf.compare("true") == 0) ? CL_KERNEL_ARG_TYPE_PIPE : 0;
break;
default:
return AMD_COMGR_STATUS_ERROR;
}
return AMD_COMGR_STATUS_SUCCESS;
}
static amd_comgr_status_t populateAttrs(const amd_comgr_metadata_node_t key,
const amd_comgr_metadata_node_t value, void* data) {
amd_comgr_status_t status;
amd_comgr_metadata_kind_t kind;
size_t size = 0;
std::string buf;
// get the key of the argument field
status = amd::Comgr::get_metadata_kind(key, &kind);
if (kind == AMD_COMGR_METADATA_KIND_STRING && status == AMD_COMGR_STATUS_SUCCESS) {
status = getMetaBuf(key, &buf);
}
if (status != AMD_COMGR_STATUS_SUCCESS) {
return AMD_COMGR_STATUS_ERROR;
}
AttrField itAttrField = amd::Kernel::FindValue<AttrField>(amd::Kernel::kAttrFieldMap, buf);
if (itAttrField == AttrField::MaxSize) {
return AMD_COMGR_STATUS_ERROR;
}
device::Kernel* kernel = static_cast<device::Kernel*>(data);
switch (itAttrField) {
case AttrField::ReqdWorkGroupSize: {
status = amd::Comgr::get_metadata_list_size(value, &size);
if (size == 3 && status == AMD_COMGR_STATUS_SUCCESS) {
std::array<size_t, 3> wrkSize;
for (size_t i = 0; i < size && status == AMD_COMGR_STATUS_SUCCESS; i++) {
amd_comgr_metadata_node_t workgroupSize;
status = amd::Comgr::index_list_metadata(value, i, &workgroupSize);
if (status == AMD_COMGR_STATUS_SUCCESS &&
getMetaBuf(workgroupSize, &buf) == AMD_COMGR_STATUS_SUCCESS) {
wrkSize[i] = atoi(buf.c_str());
}
amd::Comgr::destroy_metadata(workgroupSize);
}
if (!wrkSize.empty()) {
kernel->setReqdWorkGroupSize(wrkSize[0], wrkSize[1], wrkSize[2]);
}
}
} break;
case AttrField::WorkGroupSizeHint: {
status = amd::Comgr::get_metadata_list_size(value, &size);
if (status == AMD_COMGR_STATUS_SUCCESS && size == 3) {
std::array<size_t, 3> hintSize;
for (size_t i = 0; i < size && status == AMD_COMGR_STATUS_SUCCESS; i++) {
amd_comgr_metadata_node_t workgroupSizeHint;
status = amd::Comgr::index_list_metadata(value, i, &workgroupSizeHint);
if (status == AMD_COMGR_STATUS_SUCCESS &&
getMetaBuf(workgroupSizeHint, &buf) == AMD_COMGR_STATUS_SUCCESS) {
hintSize[i] = atoi(buf.c_str());
}
amd::Comgr::destroy_metadata(workgroupSizeHint);
}
if (!hintSize.empty()) {
kernel->setWorkGroupSizeHint(hintSize[0], hintSize[1], hintSize[2]);
}
}
} break;
case AttrField::VecTypeHint:
if (getMetaBuf(value, &buf) == AMD_COMGR_STATUS_SUCCESS) {
kernel->setVecTypeHint(buf);
}
break;
case AttrField::RuntimeHandle:
if (getMetaBuf(value, &buf) == AMD_COMGR_STATUS_SUCCESS) {
kernel->setRuntimeHandle(buf);
}
break;
default:
return AMD_COMGR_STATUS_ERROR;
}
return status;
}
static amd_comgr_status_t populateCodeProps(const amd_comgr_metadata_node_t key,
const amd_comgr_metadata_node_t value, void* data) {
amd_comgr_status_t status;
amd_comgr_metadata_kind_t kind;
std::string buf;
// get the key of the argument field
status = amd::Comgr::get_metadata_kind(key, &kind);
if (kind == AMD_COMGR_METADATA_KIND_STRING && status == AMD_COMGR_STATUS_SUCCESS) {
status = getMetaBuf(key, &buf);
}
if (status != AMD_COMGR_STATUS_SUCCESS) {
return AMD_COMGR_STATUS_ERROR;
}
CodePropField itCodePropField =
amd::Kernel::FindValue<CodePropField>(amd::Kernel::kCodePropFieldMap, buf);
if (itCodePropField == CodePropField::MaxSize) {
return AMD_COMGR_STATUS_ERROR;
}
// get the value of the argument field
if (status == AMD_COMGR_STATUS_SUCCESS) {
status = getMetaBuf(value, &buf);
}
device::Kernel* kernel = static_cast<device::Kernel*>(data);
switch (itCodePropField) {
case CodePropField::KernargSegmentSize:
kernel->SetKernargSegmentByteSize(atoi(buf.c_str()));
break;
case CodePropField::GroupSegmentFixedSize:
kernel->SetWorkgroupGroupSegmentByteSize(atoi(buf.c_str()));
break;
case CodePropField::PrivateSegmentFixedSize:
kernel->SetWorkitemPrivateSegmentByteSize(atoi(buf.c_str()));
break;
case CodePropField::KernargSegmentAlign:
kernel->SetKernargSegmentAlignment(atoi(buf.c_str()));
break;
case CodePropField::WavefrontSize:
kernel->workGroupInfo()->wavefrontSize_ = atoi(buf.c_str());
break;
case CodePropField::NumSGPRs:
kernel->workGroupInfo()->usedSGPRs_ = atoi(buf.c_str());
break;
case CodePropField::NumVGPRs:
kernel->workGroupInfo()->usedVGPRs_ = atoi(buf.c_str());
break;
case CodePropField::MaxFlatWorkGroupSize:
kernel->workGroupInfo()->size_ = atoi(buf.c_str());
break;
case CodePropField::IsDynamicCallStack: {
size_t mIsDynamicCallStack = (buf.compare("true") == 0);
} break;
case CodePropField::IsXNACKEnabled: {
size_t mIsXNACKEnabled = (buf.compare("true") == 0);
} break;
case CodePropField::NumSpilledSGPRs: {
size_t mNumSpilledSGPRs = atoi(buf.c_str());
} break;
case CodePropField::NumSpilledVGPRs: {
size_t mNumSpilledVGPRs = atoi(buf.c_str());
} break;
default:
return AMD_COMGR_STATUS_ERROR;
}
return AMD_COMGR_STATUS_SUCCESS;
}
static amd_comgr_status_t populateArgsV3(const amd_comgr_metadata_node_t key,
const amd_comgr_metadata_node_t value, void* data) {
amd_comgr_status_t status;
amd_comgr_metadata_kind_t kind;
std::string buf;
// get the key of the argument field
size_t size = 0;
status = amd::Comgr::get_metadata_kind(key, &kind);
if (kind == AMD_COMGR_METADATA_KIND_STRING && status == AMD_COMGR_STATUS_SUCCESS) {
status = getMetaBuf(key, &buf);
}
if (status != AMD_COMGR_STATUS_SUCCESS) {
return AMD_COMGR_STATUS_ERROR;
}
ArgField itArgField = amd::Kernel::FindValue<ArgField>(amd::Kernel::kArgFieldMapV3, buf);
if (itArgField == ArgField::MaxSize) {
return AMD_COMGR_STATUS_ERROR;
}
// get the value of the argument field
status = getMetaBuf(value, &buf);
amd::KernelParameterDescriptor* lcArg = static_cast<amd::KernelParameterDescriptor*>(data);
switch (itArgField) {
case ArgField::Name:
lcArg->name_ = buf;
break;
case ArgField::TypeName:
lcArg->typeName_ = buf;
break;
case ArgField::Size:
lcArg->size_ = atoi(buf.c_str());
break;
case ArgField::Offset:
lcArg->offset_ = atoi(buf.c_str());
break;
case ArgField::ValueKind: {
amd::KernelParameterDescriptor::Desc itArgValue =
amd::Kernel::FindValue<amd::KernelParameterDescriptor::Desc>(amd::Kernel::kArgValueKindV3,
buf);
if (itArgValue == amd::KernelParameterDescriptor::MaxSize) {
LogPrintfError("Unknown Kernel arg metadata: %s", buf.c_str());
LogError("This may be due to running HIP app that requires a new HIP runtime version");
LogError("Please update the display driver");
return AMD_COMGR_STATUS_ERROR;
}
lcArg->info_.oclObject_ = itArgValue;
if (lcArg->info_.oclObject_ == amd::KernelParameterDescriptor::MemoryObject) {
if (buf.compare("dynamic_shared_pointer") == 0) {
lcArg->info_.shared_ = true;
}
} else if ((lcArg->info_.oclObject_ >= amd::KernelParameterDescriptor::HiddenNone) &&
(lcArg->info_.oclObject_ < amd::KernelParameterDescriptor::HiddenLast)) {
lcArg->info_.hidden_ = true;
}
} break;
case ArgField::PointeeAlign:
lcArg->info_.arrayIndex_ = atoi(buf.c_str());
break;
case ArgField::AddrSpaceQual: {
cl_int itAddrSpaceQual = amd::Kernel::FindValue(amd::Kernel::kArgAddrSpaceQualV3, buf);
if (itAddrSpaceQual == static_cast<cl_int>(0)) {
return AMD_COMGR_STATUS_ERROR;
}
lcArg->addressQualifier_ = itAddrSpaceQual;
} break;
case ArgField::AccQual: {
cl_int itAccQual = amd::Kernel::FindValue(amd::Kernel::kArgAccQualV3, buf);
if (itAccQual == static_cast<cl_int>(0)) {
return AMD_COMGR_STATUS_ERROR;
}
lcArg->accessQualifier_ = itAccQual;
if (!lcArg->info_.isReadOnlyByCompiler) {
lcArg->info_.readOnly_ =
(lcArg->accessQualifier_ == CL_KERNEL_ARG_ACCESS_READ_ONLY) ? true : false;
}
} break;
case ArgField::ActualAccQual: {
cl_int itAccQual = amd::Kernel::FindValue(amd::Kernel::kArgAccQualV3, buf);
if (itAccQual == static_cast<cl_int>(0)) {
return AMD_COMGR_STATUS_ERROR;
}
lcArg->info_.isReadOnlyByCompiler = true;
lcArg->info_.readOnly_ = (itAccQual == CL_KERNEL_ARG_ACCESS_READ_ONLY) ? true : false;
} break;
case ArgField::IsConst:
lcArg->typeQualifier_ |= (buf.compare("1") == 0) ? CL_KERNEL_ARG_TYPE_CONST : 0;
break;
case ArgField::IsRestrict:
lcArg->typeQualifier_ |= (buf.compare("1") == 0) ? CL_KERNEL_ARG_TYPE_RESTRICT : 0;
break;
case ArgField::IsVolatile:
lcArg->typeQualifier_ |= (buf.compare("1") == 0) ? CL_KERNEL_ARG_TYPE_VOLATILE : 0;
break;
case ArgField::IsPipe:
lcArg->typeQualifier_ |= (buf.compare("1") == 0) ? CL_KERNEL_ARG_TYPE_PIPE : 0;
break;
default:
return AMD_COMGR_STATUS_ERROR;
}
return AMD_COMGR_STATUS_SUCCESS;
}
static amd_comgr_status_t populateKernelMetaV3(const amd_comgr_metadata_node_t key,
const amd_comgr_metadata_node_t value, void* data) {
amd_comgr_status_t status;
amd_comgr_metadata_kind_t kind;
size_t size = 0;
std::string buf;
// get the key of the argument field
status = amd::Comgr::get_metadata_kind(key, &kind);
if (kind == AMD_COMGR_METADATA_KIND_STRING && status == AMD_COMGR_STATUS_SUCCESS) {
status = getMetaBuf(key, &buf);
}
if (status != AMD_COMGR_STATUS_SUCCESS) {
return AMD_COMGR_STATUS_ERROR;
}
KernelField itKernelField =
amd::Kernel::FindValue<KernelField>(amd::Kernel::kKernelFieldMapV3, buf);
if (itKernelField == KernelField::MaxSize) {
return AMD_COMGR_STATUS_ERROR;
}
if (itKernelField != KernelField::ReqdWorkGroupSize &&
itKernelField != KernelField::WorkGroupSizeHint &&
itKernelField != KernelField::ClusterDims) {
status = getMetaBuf(value, &buf);
}
if (status != AMD_COMGR_STATUS_SUCCESS) {
return AMD_COMGR_STATUS_ERROR;
}
device::Kernel* kernel = static_cast<device::Kernel*>(data);
switch (itKernelField) {
case KernelField::ReqdWorkGroupSize:
status = amd::Comgr::get_metadata_list_size(value, &size);
if (size == 3 && status == AMD_COMGR_STATUS_SUCCESS) {
std::array<size_t, 3> wrkSize;
for (size_t i = 0; i < size && status == AMD_COMGR_STATUS_SUCCESS; i++) {
amd_comgr_metadata_node_t workgroupSize;
status = amd::Comgr::index_list_metadata(value, i, &workgroupSize);
if (status == AMD_COMGR_STATUS_SUCCESS &&
getMetaBuf(workgroupSize, &buf) == AMD_COMGR_STATUS_SUCCESS) {
wrkSize[i] = atoi(buf.c_str());
}
amd::Comgr::destroy_metadata(workgroupSize);
}
if (!wrkSize.empty()) {
kernel->setReqdWorkGroupSize(wrkSize[0], wrkSize[1], wrkSize[2]);
}
}
break;
case KernelField::WorkGroupSizeHint:
status = amd::Comgr::get_metadata_list_size(value, &size);
if (status == AMD_COMGR_STATUS_SUCCESS && size == 3) {
std::array<size_t, 3> hintSize;
for (size_t i = 0; i < size && status == AMD_COMGR_STATUS_SUCCESS; i++) {
amd_comgr_metadata_node_t workgroupSizeHint;
status = amd::Comgr::index_list_metadata(value, i, &workgroupSizeHint);
if (status == AMD_COMGR_STATUS_SUCCESS &&
getMetaBuf(workgroupSizeHint, &buf) == AMD_COMGR_STATUS_SUCCESS) {
hintSize[i] = atoi(buf.c_str());
}
amd::Comgr::destroy_metadata(workgroupSizeHint);
}
if (!hintSize.empty()) {
kernel->setWorkGroupSizeHint(hintSize[0], hintSize[1], hintSize[2]);
}
}
break;
case KernelField::ClusterDims:
status = amd::Comgr::get_metadata_list_size(value, &size);
if (size == 3 && status == AMD_COMGR_STATUS_SUCCESS) {
std::vector<size_t> clusterSize;
for (size_t i = 0; i < size && status == AMD_COMGR_STATUS_SUCCESS; i++) {
amd_comgr_metadata_node_t clusterSizeNode;
status = amd::Comgr::index_list_metadata(value, i, &clusterSizeNode);
if (status == AMD_COMGR_STATUS_SUCCESS &&
getMetaBuf(clusterSizeNode, &buf) == AMD_COMGR_STATUS_SUCCESS) {
clusterSize.push_back(atoi(buf.c_str()));
}
amd::Comgr::destroy_metadata(clusterSizeNode);
}
if (!clusterSize.empty()) {
kernel->setClusterSize(clusterSize[0], clusterSize[1], clusterSize[2]);
}
}
break;
case KernelField::VecTypeHint:
kernel->setVecTypeHint(buf);
break;
case KernelField::DeviceEnqueueSymbol:
kernel->setRuntimeHandle(buf);
break;
case KernelField::KernargSegmentSize:
kernel->SetKernargSegmentByteSize(atoi(buf.c_str()));
break;
case KernelField::GroupSegmentFixedSize:
kernel->SetWorkgroupGroupSegmentByteSize(atoi(buf.c_str()));
break;
case KernelField::PrivateSegmentFixedSize:
kernel->SetWorkitemPrivateSegmentByteSize(atoi(buf.c_str()));
break;
case KernelField::KernargSegmentAlign:
kernel->SetKernargSegmentAlignment(atoi(buf.c_str()));
break;
case KernelField::WavefrontSize:
kernel->workGroupInfo()->wavefrontSize_ = atoi(buf.c_str());
break;
case KernelField::NumSGPRs:
kernel->workGroupInfo()->usedSGPRs_ = atoi(buf.c_str());
break;
case KernelField::NumVGPRs:
kernel->workGroupInfo()->usedVGPRs_ = atoi(buf.c_str());
break;
case KernelField::MaxFlatWorkGroupSize:
kernel->workGroupInfo()->size_ = atoi(buf.c_str());
break;
case KernelField::NumSpilledSGPRs: {
size_t mNumSpilledSGPRs = atoi(buf.c_str());
} break;
case KernelField::NumSpilledVGPRs: {
size_t mNumSpilledVGPRs = atoi(buf.c_str());
} break;
case KernelField::SymbolName:
kernel->SetSymbolName(buf);
break;
case KernelField::Kind:
kernel->SetKernelKind(buf);
break;
case KernelField::WgpMode:
// The compiler currently serializes this boolean field as "0"/"1" instead
// of "false"/"true"; consider both "true" and "1" truthy values.
kernel->SetWGPMode(buf.compare("true") == 0 || buf.compare("1") == 0);
break;
case KernelField::UniformWrokGroupSize:
kernel->setUniformWorkGroupSize(buf.compare("1") == 0);
break;
default:
return AMD_COMGR_STATUS_ERROR;
}
return status;
}
// ================================================================================================
Kernel::Kernel(const amd::Device& dev, const std::string& name, const Program& prog)
: dev_(dev), name_(name), prog_(prog), signature_(nullptr) {
// Instead of memset(&workGroupInfo_, '\0', sizeof(workGroupInfo_));
// Due to std::string not being able to be memset to 0
workGroupInfo_.size_ = 0;
workGroupInfo_.compileSize_[0] = 0;
workGroupInfo_.compileSize_[1] = 0;
workGroupInfo_.compileSize_[2] = 0;
workGroupInfo_.clusterSize_[0] = 1;
workGroupInfo_.clusterSize_[1] = 1;
workGroupInfo_.clusterSize_[2] = 1;
workGroupInfo_.localMemSize_ = 0;
workGroupInfo_.preferredSizeMultiple_ = 0;
workGroupInfo_.privateMemSize_ = 0;
workGroupInfo_.scratchRegs_ = 0;
workGroupInfo_.wavefrontPerSIMD_ = 0;
workGroupInfo_.wavefrontSize_ = 0;
workGroupInfo_.availableGPRs_ = 0;
workGroupInfo_.usedGPRs_ = 0;
workGroupInfo_.availableSGPRs_ = 0;
workGroupInfo_.usedSGPRs_ = 0;
workGroupInfo_.availableVGPRs_ = dev.info().availableVGPRs_;
workGroupInfo_.usedVGPRs_ = 0;
workGroupInfo_.availableLDSSize_ = 0;
workGroupInfo_.usedLDSSize_ = 0;
workGroupInfo_.availableStackSize_ = 0;
workGroupInfo_.usedStackSize_ = 0;
workGroupInfo_.compileSizeHint_[0] = 0;
workGroupInfo_.compileSizeHint_[1] = 0;
workGroupInfo_.compileSizeHint_[2] = 0;
workGroupInfo_.compileVecTypeHint_ = "";
workGroupInfo_.isWGPMode_ = false;
workGroupInfo_.uniformWorkGroupSize_ = false;
workGroupInfo_.wavesPerSimdHint_ = 0;
workGroupInfo_.constMemSize_ = 0;
workGroupInfo_.maxDynamicSharedSizeBytes_ = 0;
workGroupInfo_.hasClusterAttr_ = false;
}
// ================================================================================================
bool Kernel::createSignature(const parameters_t& params, uint32_t numParameters, uint32_t version) {
std::stringstream attribs;
if (workGroupInfo_.compileSize_[0] != 0) {
attribs << "reqd_work_group_size(";
for (size_t i = 0; i < 3; ++i) {
if (i != 0) {
attribs << ",";
}
attribs << workGroupInfo_.compileSize_[i];
}
attribs << ")";
}
if (workGroupInfo_.clusterSize_[0] != 0) {
attribs << "cluster_dims(";
for (size_t i = 0; i < 3; ++i) {
if (i != 0) {
attribs << ",";
}
attribs << workGroupInfo_.clusterSize_[i];
}
attribs << ")";
}
if (workGroupInfo_.compileSizeHint_[0] != 0) {
attribs << " work_group_size_hint(";
for (size_t i = 0; i < 3; ++i) {
if (i != 0) {
attribs << ",";
}
attribs << workGroupInfo_.compileSizeHint_[i];
}
attribs << ")";
}
if (!workGroupInfo_.compileVecTypeHint_.empty()) {
attribs << " vec_type_hint(" << workGroupInfo_.compileVecTypeHint_ << ")";
}
// Destroy old signature if it was allocated before
// (offline devices path)
delete signature_;
signature_ = new amd::KernelSignature(params, attribs.str(), numParameters, version);
if (NULL != signature_) {
return true;
}
return false;
}
// ================================================================================================
Kernel::~Kernel() { delete signature_; }
// ================================================================================================
void Kernel::FindLocalWorkSize(size_t workDim, const amd::NDRange& gblWorkSize,
amd::NDRange& lclWorkSize) const {
// Initialize the default workgoup info
// Check if the kernel has the compiled sizes
if (workGroupInfo()->compileSize_[0] == 0) {
// Find the default local workgroup size, if it wasn't specified
if (lclWorkSize[0] == 0) {
// Find threads per group
size_t thrPerGrp = workGroupInfo()->size_;
// Check if kernel uses images
if (flags_.imageEna_ &&
// and thread group is a multiple value of wavefronts
((thrPerGrp % workGroupInfo()->wavefrontSize_) == 0) &&
// and it's 2 or 3-dimensional workload
(workDim > 1) && (((gblWorkSize[0] % 16) == 0) && ((gblWorkSize[1] % 16) == 0))) {
// Use 8x8 workgroup size if kernel has image writes
if (flags_.imageWriteEna_ || (thrPerGrp != device().info().preferredWorkGroupSize_)) {
lclWorkSize[0] = 8;
lclWorkSize[1] = 8;
} else {
lclWorkSize[0] = 16;
lclWorkSize[1] = 16;
}
if (workDim == 3) {
lclWorkSize[2] = 1;
}
} else {
size_t tmp = thrPerGrp;
// Split the local workgroup into the most efficient way
for (uint d = 0; d < workDim; ++d) {
size_t div = tmp;
for (; (gblWorkSize[d] % div) != 0; div--);
lclWorkSize[d] = div;
tmp /= div;
}
if (!workGroupInfo()->uniformWorkGroupSize_) {
// Assuming DWORD access
const uint cacheLineMatch = device().info().globalMemCacheLineSize_ >> 2;
// Check if we couldn't find optimal workload
if (((lclWorkSize.product() % workGroupInfo()->wavefrontSize_) != 0) ||
// or size is too small for the cache line
(lclWorkSize[0] < cacheLineMatch)) {
size_t maxSize = 0;
size_t maxDim = 0;
for (uint d = 0; d < workDim; ++d) {
if (maxSize < gblWorkSize[d]) {
maxSize = gblWorkSize[d];
maxDim = d;
}
}
// Use X dimension as high priority. Runtime will assume that
// X dimension is more important for the address calculation
if ((maxDim != 0) && (gblWorkSize[0] >= (cacheLineMatch / 2))) {
lclWorkSize[0] = cacheLineMatch;
thrPerGrp /= cacheLineMatch;
lclWorkSize[maxDim] = thrPerGrp;
for (uint d = 1; d < workDim; ++d) {
if (d != maxDim) {
lclWorkSize[d] = 1;
}
}
} else {
// Check if a local workgroup has the most optimal size
if (thrPerGrp > maxSize) {
thrPerGrp = maxSize;
}
lclWorkSize[maxDim] = thrPerGrp;
for (uint d = 0; d < workDim; ++d) {
if (d != maxDim) {
lclWorkSize[d] = 1;
}
}
}
}
}
}
}
} else {
for (uint d = 0; d < workDim; ++d) {
lclWorkSize[d] = workGroupInfo()->compileSize_[d];
}
}
}
// ================================================================================================
bool Kernel::GetAttrCodePropMetadata() {
amd_comgr_metadata_node_t kernelMetaNode;
if (!prog().getKernelMetadata(name(), &kernelMetaNode)) {
ClPrint(amd::LOG_DETAIL_DEBUG, amd::LOG_KERN,
"Cannot get program kernel metadata for %s \n", name().c_str());
return false;
}
// Set the workgroup information for the kernel
workGroupInfo_.availableLDSSize_ = device().info().localMemSizePerCU_;
workGroupInfo_.availableSGPRs_ = 104;
workGroupInfo_.availableVGPRs_ = 256;
// extract the attribute metadata if there is any
amd_comgr_status_t status = AMD_COMGR_STATUS_SUCCESS;
switch (codeObjectVer()) {
case 2: {
amd_comgr_metadata_node_t symbolName;
status = amd::Comgr::metadata_lookup(kernelMetaNode, "SymbolName", &symbolName);
if (status == AMD_COMGR_STATUS_SUCCESS) {
std::string name;
status = getMetaBuf(symbolName, &name);
amd::Comgr::destroy_metadata(symbolName);
SetSymbolName(name);
}
amd_comgr_metadata_node_t attrMeta;
if (status == AMD_COMGR_STATUS_SUCCESS) {
if (amd::Comgr::metadata_lookup(kernelMetaNode, "Attrs", &attrMeta) ==
AMD_COMGR_STATUS_SUCCESS) {
status =
amd::Comgr::iterate_map_metadata(attrMeta, populateAttrs, static_cast<void*>(this));
amd::Comgr::destroy_metadata(attrMeta);
}
}
// extract the code properties metadata
amd_comgr_metadata_node_t codePropsMeta;
if (status == AMD_COMGR_STATUS_SUCCESS) {
status = amd::Comgr::metadata_lookup(kernelMetaNode, "CodeProps", &codePropsMeta);
}
if (status == AMD_COMGR_STATUS_SUCCESS) {
status = amd::Comgr::iterate_map_metadata(codePropsMeta, populateCodeProps,
static_cast<void*>(this));
amd::Comgr::destroy_metadata(codePropsMeta);
}
} break;
default:
status = amd::Comgr::iterate_map_metadata(kernelMetaNode, populateKernelMetaV3,
static_cast<void*>(this));
}
if (status != AMD_COMGR_STATUS_SUCCESS) {
LogError("Comgr Api failed with Status: \n");
return false;
}
InitParameters(kernelMetaNode);
return true;
}
bool Kernel::GetPrintfStr(std::vector<std::string>* printfStr) {
const amd_comgr_metadata_node_t programMD = prog().metadata();
amd_comgr_metadata_node_t printfMeta;
amd_comgr_status_t status = amd::Comgr::metadata_lookup(
programMD, codeObjectVer() == 2 ? "Printf" : "amdhsa.printf", &printfMeta);
if (status != AMD_COMGR_STATUS_SUCCESS) {
return true; // printf string metadata is not provided so just exit
}
// handle the printf string
size_t printfSize = 0;
status = amd::Comgr::get_metadata_list_size(printfMeta, &printfSize);
if (status == AMD_COMGR_STATUS_SUCCESS) {
size_t originalSize = printfStr->size();
printfStr->reserve(originalSize + printfSize);
for (size_t i = 0; i < printfSize; ++i) {
amd_comgr_metadata_node_t str;
status = amd::Comgr::index_list_metadata(printfMeta, i, &str);
std::string buf;
if (status == AMD_COMGR_STATUS_SUCCESS) {
status = getMetaBuf(str, &buf);
amd::Comgr::destroy_metadata(str);
}
if (status != AMD_COMGR_STATUS_SUCCESS) {
ClPrint(amd::LOG_DETAIL_DEBUG, amd::LOG_COMGR,
"Comgr API failed with status: %d \n", status);
amd::Comgr::destroy_metadata(printfMeta);
printfStr->resize(originalSize); // restore the original size of the vector
return false;
}
printfStr->push_back(std::move(buf));
}
}
amd::Comgr::destroy_metadata(printfMeta);
return (status == AMD_COMGR_STATUS_SUCCESS);
}
void Kernel::InitParameters(const amd_comgr_metadata_node_t kernelMD) {
// Iterate through the arguments and insert into parameterList
device::Kernel::parameters_t params;
device::Kernel::parameters_t hiddenParams;
hiddenParams.reserve(amd::KernelParameterDescriptor::Desc::MaxSize);
size_t offset = 0;
amd_comgr_metadata_node_t argsMeta;
bool hsaArgsMeta = false;
size_t argsSize = 0;
amd_comgr_status_t status =
amd::Comgr::metadata_lookup(kernelMD, (codeObjectVer() == 2) ? "Args" : ".args", &argsMeta);
// Assume no arguments if lookup fails.
if (status == AMD_COMGR_STATUS_SUCCESS) {
hsaArgsMeta = true;
status = amd::Comgr::get_metadata_list_size(argsMeta, &argsSize);
}
params.reserve(argsSize);
for (size_t i = 0; i < argsSize; ++i) {
amd::KernelParameterDescriptor desc = {};
amd_comgr_metadata_node_t argsNode;
amd_comgr_metadata_kind_t kind = AMD_COMGR_METADATA_KIND_NULL;
bool hsaArgsNode = false;
status = amd::Comgr::index_list_metadata(argsMeta, i, &argsNode);
if (status == AMD_COMGR_STATUS_SUCCESS) {
hsaArgsNode = true;
status = amd::Comgr::get_metadata_kind(argsNode, &kind);
}
if (kind != AMD_COMGR_METADATA_KIND_MAP) {
status = AMD_COMGR_STATUS_ERROR;
}
if (status == AMD_COMGR_STATUS_SUCCESS) {
void* data = static_cast<void*>(&desc);
if (codeObjectVer() == 2) {
status = amd::Comgr::iterate_map_metadata(argsNode, populateArgs, data);
} else if (codeObjectVer() >= 3) {
status = amd::Comgr::iterate_map_metadata(argsNode, populateArgsV3, data);
}
}
if (hsaArgsNode) {
amd::Comgr::destroy_metadata(argsNode);
}
if (status != AMD_COMGR_STATUS_SUCCESS) {
if (hsaArgsMeta) {
amd::Comgr::destroy_metadata(argsMeta);
}
return;
}
// COMGR has unclear/undefined order of the fields filling.
// Correct the types for the abstraciton layer after all fields are available
if (desc.info_.oclObject_ != amd::KernelParameterDescriptor::ValueObject) {
switch (desc.info_.oclObject_) {
case amd::KernelParameterDescriptor::MemoryObject:
case amd::KernelParameterDescriptor::ImageObject:
desc.type_ = T_POINTER;
if (desc.info_.shared_) {
if (desc.info_.arrayIndex_ == 0) {
LogWarning("Missing DynamicSharedPointer alignment");
desc.info_.arrayIndex_ = 128; /* worst case alignment */
}
} else {
desc.info_.arrayIndex_ = 1;
}
break;
case amd::KernelParameterDescriptor::SamplerObject:
desc.type_ = T_SAMPLER;
desc.addressQualifier_ = CL_KERNEL_ARG_ADDRESS_PRIVATE;
break;
case amd::KernelParameterDescriptor::QueueObject:
desc.type_ = T_QUEUE;
break;
default:
desc.type_ = T_VOID;
break;
}
}
if ((desc.info_.oclObject_ == amd::KernelParameterDescriptor::ImageObject) ||
(desc.typeQualifier_ & CL_KERNEL_ARG_TYPE_PIPE)) {
// LC doesn't report correct address qualifier for images and pipes,
// hence overwrite it
// We will remove this when newer LC is ready
desc.addressQualifier_ = CL_KERNEL_ARG_ADDRESS_GLOBAL;
} else {
// According to CL spec, otherwise must be CL_KERNEL_ARG_ACCESS_NONE,
desc.accessQualifier_ = CL_KERNEL_ARG_ACCESS_NONE;
}
size_t size = desc.size_;
// Allocate the hidden arguments, but abstraction layer will skip them
if (desc.info_.hidden_) {
if (desc.info_.oclObject_ == amd::KernelParameterDescriptor::HiddenCompletionAction &&
!amd::IS_HIP) {
setDynamicParallelFlag(true);
}
if (codeObjectVer() == 2) {
desc.offset_ = amd::alignUp(offset, desc.alignment_);
offset += size;
}
hiddenParams.push_back(desc);
continue;
}
// These objects have forced data size to uint64_t
if (codeObjectVer() == 2) {
if ((desc.info_.oclObject_ == amd::KernelParameterDescriptor::ImageObject) ||
(desc.info_.oclObject_ == amd::KernelParameterDescriptor::SamplerObject) ||
(desc.info_.oclObject_ == amd::KernelParameterDescriptor::QueueObject)) {
offset = amd::alignUp(offset, sizeof(uint64_t));
desc.offset_ = offset;
offset += sizeof(uint64_t);
} else {
offset = amd::alignUp(offset, desc.alignment_);
desc.offset_ = offset;
offset += size;
}
}
params.push_back(desc);
if (desc.info_.oclObject_ == amd::KernelParameterDescriptor::ImageObject) {
flags_.imageEna_ = true;
if (desc.accessQualifier_ != CL_KERNEL_ARG_ACCESS_READ_ONLY) {
flags_.imageWriteEna_ = true;
}
}
}
if (hsaArgsMeta) {
amd::Comgr::destroy_metadata(argsMeta);
}
// Save the number of OCL arguments
uint32_t numParams = params.size();
// Append the hidden arguments to the OCL arguments
params.insert(params.end(), hiddenParams.begin(), hiddenParams.end());
createSignature(params, numParams, amd::KernelSignature::ABIVersion_LC);
}
// ================================================================================================
void Kernel::InitPrintf(const std::vector<std::string>& printfInfoStrings) {
size_t HIPPrintfInfoID = 0;
for (auto str : printfInfoStrings) {
std::vector<std::string> tokens;
size_t end, pos = 0;
do {
end = str.find_first_of(':', pos);
tokens.push_back(str.substr(pos, end - pos));
pos = end + 1;
} while (end != std::string::npos);
if (tokens.size() < 2) {
LogPrintfError("Invalid PrintInfo string: \"%s\"", str.c_str());
continue;
}
pos = 0;
size_t printfInfoID;
if (amd::IS_HIP) {
printfInfoID = HIPPrintfInfoID++;
printf_.resize(HIPPrintfInfoID);
pos++;
} else {
printfInfoID = std::stoi(tokens[pos++]);
if (printf_.size() <= printfInfoID) {
printf_.resize(printfInfoID + 1);
}
}
PrintfInfo& info = printf_[printfInfoID];
size_t numSizes = std::stoi(tokens[pos++]);
end = pos + numSizes;
// ensure that we have the correct number of tokens
if (tokens.size() < end + 1 /*last token is the fmtString*/) {
LogPrintfError("Invalid PrintInfo string: \"%s\"", str.c_str());
continue;
}
// push the argument sizes
while (pos < end) {
info.arguments_.push_back(std::stoi(tokens[pos++]));
}
// FIXME: We should not need this! [
std::string fmt;
// Format string itself might contain ':' characters
for (int i = 0; pos < tokens.size(); i++) {
if (i) fmt += ':';
fmt += tokens[pos++];
}
bool need_nl = true;
for (pos = 0; pos < fmt.size(); ++pos) {
char symbol = fmt[pos];
need_nl = true;
if (symbol == '\\') {
switch (fmt[pos + 1]) {
case 'a':
pos++;
symbol = '\a';
break;
case 'b':
pos++;
symbol = '\b';
break;
case 'f':
pos++;
symbol = '\f';
break;
case 'n':
pos++;
symbol = '\n';
need_nl = false;
break;
case 'r':
pos++;
symbol = '\r';
break;
case 'v':
pos++;
symbol = '\v';
break;
case '7':
if (fmt[pos + 2] == '2') {
pos += 2;
symbol = '\72';
}
break;
default:
break;
}
}
info.fmtString_.push_back(symbol);
}
if (need_nl && !amd::IS_HIP) {
info.fmtString_ += "\n";
}
// ]
}
}
} // namespace amd::device