/* * Copyright (c) Advanced Micro Devices, Inc., or its affiliates. * * SPDX-License-Identifier: MIT */ #include "rockernel.hpp" #include namespace amd::roc { bool Kernel::init() { return GetAttrCodePropMetadata(); } bool Kernel::postLoad() { // Set the kernel symbol name and size/alignment based on the kernel metadata // NOTE: kernel name is used to get the kernel code handle in V2, // but kernel symbol name is used in V3 if (codeObjectVer() == 2) { symbolName_ = name(); } kernargSegmentAlignment_ = amd::alignUp(std::max(kernargSegmentAlignment_, 128u), device().info().globalMemCacheLineSize_); // Set the workgroup information for the kernel workGroupInfo_.availableLDSSize_ = device().info().localMemSizePerCU_; assert(workGroupInfo_.availableLDSSize_ > 0); // Get the kernel code handle hsa_status_t hsaStatus; hsa_executable_symbol_t symbol; hsa_agent_t agent = program()->rocDevice().getBackendDevice(); hsaStatus = Hsa::executable_get_symbol_by_name(program()->hsaExecutable(), symbolName().c_str(), &agent, &symbol); if (hsaStatus != HSA_STATUS_SUCCESS) { ClPrint(amd::LOG_DETAIL_DEBUG, amd::LOG_KERN, "Cannot Get Symbol : %s, failed with hsa_status: %d \n", symbolName().c_str(), hsaStatus); return false; } hsaStatus = Hsa::executable_symbol_get_info(symbol, HSA_EXECUTABLE_SYMBOL_INFO_KERNEL_OBJECT, &kernelCodeHandle_); if (hsaStatus != HSA_STATUS_SUCCESS) { ClPrint(amd::LOG_DETAIL_DEBUG, amd::LOG_KERN, " Cannot Get Symbol Info: %s, failed with hsa_status: %d \n ", symbolName().c_str(), hsaStatus); return false; } // Resolve the metadata kernel descriptor for prefetching at load time, // so dispatch doesn't need to call loaderQueryHostAddress per packet. const void* host_address = nullptr; Device::loaderQueryHostAddress(reinterpret_cast(kernelCodeHandle_), &host_address); if (host_address != nullptr) { constexpr size_t KERNEL_CODE_ENTRY_BYTE_OFFSET_OFFSET = 16; auto* descriptor = reinterpret_cast( reinterpret_cast(host_address) + KERNEL_CODE_ENTRY_BYTE_OFFSET_OFFSET); metadataKernelDescriptor_ = descriptor; metadata_preload_length_ = descriptor->kernarg_preload.length; metadata_preload_offset_ = descriptor->kernarg_preload.offset; } hsaStatus = Hsa::executable_symbol_get_info( symbol, HSA_EXECUTABLE_SYMBOL_INFO_KERNEL_DYNAMIC_CALLSTACK, &kernelHasDynamicCallStack_); if (hsaStatus != HSA_STATUS_SUCCESS) { ClPrint(amd::LOG_DETAIL_DEBUG, amd::LOG_KERN, " Cannot Get Dynamic callstack info, failed with hsa_status: %d \n ", hsaStatus); return false; } if (!RuntimeHandle().empty()) { hsa_executable_symbol_t kernelSymbol; int variable_size; uint64_t variable_address; // Only kernels that could be enqueued by another kernel has the RuntimeHandle metadata. The // RuntimeHandle metadata is a string that represents a variable from which the library code can // retrieve the kernel code object handle of such a kernel. The address of the variable and the // kernel code object handle are known only after the hsa executable is loaded. The below code // copies the kernel code object handle to the address of the variable. hsaStatus = Hsa::executable_get_symbol_by_name(program()->hsaExecutable(), RuntimeHandle().c_str(), &agent, &kernelSymbol); if (hsaStatus != HSA_STATUS_SUCCESS) { ClPrint(amd::LOG_DETAIL_DEBUG, amd::LOG_KERN, "Cannot get Kernel Symbol by name: %s, failed with hsa_status: %d \n", RuntimeHandle().c_str(), hsaStatus); return false; } hsaStatus = Hsa::executable_symbol_get_info( kernelSymbol, HSA_EXECUTABLE_SYMBOL_INFO_VARIABLE_SIZE, &variable_size); if (hsaStatus != HSA_STATUS_SUCCESS) { ClPrint(amd::LOG_DETAIL_DEBUG, amd::LOG_KERN, "[ROC][Kernel] Cannot get Kernel Symbol Info, failed with hsa_status: %d \n", hsaStatus); return false; } hsaStatus = Hsa::executable_symbol_get_info( kernelSymbol, HSA_EXECUTABLE_SYMBOL_INFO_VARIABLE_ADDRESS, &variable_address); if (hsaStatus != HSA_STATUS_SUCCESS) { ClPrint(amd::LOG_DETAIL_DEBUG, amd::LOG_KERN, "[ROC][Kernel] Cannot get Kernel Address, failed with hsa_status: %d \n", hsaStatus); return false; } const struct RuntimeHandle runtime_handle = { kernelCodeHandle_, WorkitemPrivateSegmentByteSize(), WorkgroupGroupSegmentByteSize()}; hsaStatus = Hsa::memory_copy(reinterpret_cast(variable_address), &runtime_handle, variable_size); if (hsaStatus != HSA_STATUS_SUCCESS) { ClPrint(amd::LOG_DETAIL_DEBUG, amd::LOG_KERN, "[ROC][Kernel] HSA Memory copy failed, failed with hsa_status: %d \n", hsaStatus); return false; } } // This can be set in code object and the value might be different than what HSA reports // For example on Navi GPUs someone using -mwavefrontsize64 // We set the value to HSA if the value is uninitialized uint32_t wavefront_size = workGroupInfo_.wavefrontPerSIMD_; if (wavefront_size == 0 && Hsa::agent_get_info(program()->rocDevice().getBackendDevice(), HSA_AGENT_INFO_WAVEFRONT_SIZE, &wavefront_size) != HSA_STATUS_SUCCESS) { ClPrint(amd::LOG_DETAIL_DEBUG, amd::LOG_KERN, "[ROC][Kernel] Cannot get Wavefront Size, failed with hsa_status: %d \n", hsaStatus); return false; } assert(wavefront_size > 0); workGroupInfo_.availableVGPRs_ = device().info().availableVGPRs_; workGroupInfo_.availableSGPRs_ = device().info().availableSGPRs_; workGroupInfo_.privateMemSize_ = workitemPrivateSegmentByteSize_; workGroupInfo_.localMemSize_ = workgroupGroupSegmentByteSize_; workGroupInfo_.usedLDSSize_ = workgroupGroupSegmentByteSize_; workGroupInfo_.preferredSizeMultiple_ = wavefront_size; workGroupInfo_.usedStackSize_ = kernelHasDynamicCallStack_; workGroupInfo_.wavefrontPerSIMD_ = program()->rocDevice().info().maxWorkItemSizes_[0] / wavefront_size; workGroupInfo_.constMemSize_ = 0; workGroupInfo_.maxDynamicSharedSizeBytes_ = static_cast(workGroupInfo_.availableLDSSize_ - workGroupInfo_.localMemSize_); if (workGroupInfo_.size_ == 0) { return false; } // handle the printf metadata if any std::vector printfStr; if (!GetPrintfStr(&printfStr)) { return false; } if (!printfStr.empty()) { InitPrintf(printfStr); } // Add kernel to the map of all kernels on the device program()->rocDevice().AddKernel(*this); return true; } } // namespace amd::roc