Files
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

446 lines
14 KiB
C++

// Copyright Advanced Micro Devices, Inc., or its affiliates.
// SPDX-License-Identifier: MIT
#ifdef ROCROLLER_USE_HIP
#include <hip/hip_ext.h>
#include <hip/hip_runtime.h>
#endif /* ROCROLLER_USE_HIP */
#include <rocRoller/AssemblyKernel.hpp>
#include <rocRoller/CodeGen/ArgumentLoader.hpp>
#include <rocRoller/CodeGen/CopyGenerator.hpp>
#include <rocRoller/CodeGen/MemoryInstructions.hpp>
#include <rocRoller/CommandSolution.hpp>
#include <rocRoller/ExecutableKernel.hpp>
#include <rocRoller/GPUArchitecture/GPUArchitectureLibrary.hpp>
#include <rocRoller/KernelArguments.hpp>
#include <rocRoller/Operations/Command.hpp>
#include <rocRoller/Utilities/Generator.hpp>
#include "GPUContextFixture.hpp"
#include "GenericContextFixture.hpp"
#include "SourceMatcher.hpp"
#include "Utilities.hpp"
using namespace rocRoller;
namespace rocRollerTest
{
class KernelTest : public GenericContextFixture
{
void SetUp() override
{
GenericContextFixture::SetUp();
Settings::getInstance()->set(Settings::SerializeKernelGraph, true);
}
};
class ARCH_KernelTest : public GPUContextFixture
{
};
TEST_F(KernelTest, Preamble)
{
AssemblyKernel k(m_context, "hello_world");
m_context->schedule(k.preamble());
std::string expected = R"(
.amdgcn_target "amdgcn-amd-amdhsa--gfx908"
.set .amdgcn.next_free_vgpr, 0
.set .amdgcn.next_free_sgpr, 0
.text
.globl hello_world
.p2align 8
.type hello_world,@function
hello_world:
)";
EXPECT_THAT(output(), MatchesSource(expected));
EXPECT_EQ(NormalizedSource(expected), NormalizedSource(output()));
}
TEST_F(KernelTest, AddArgument)
{
auto kernel = m_context->kernel();
kernel->addArgument({"foo", {DataType::Float}});
EXPECT_EQ(0, kernel->arguments()[0].getOffset());
EXPECT_EQ(4, kernel->arguments()[0].getSize());
kernel->addArgument(
{"bar", {DataType::Float, PointerType::PointerGlobal}, DataDirection::ReadOnly});
EXPECT_EQ(8, kernel->arguments()[1].getOffset());
EXPECT_EQ(8, kernel->arguments()[1].getSize());
kernel->addArgument({"baz", {DataType::Double}});
EXPECT_EQ(16, kernel->arguments()[2].getOffset());
EXPECT_EQ(8, kernel->arguments()[2].getSize());
}
TEST_F(KernelTest, Postamble)
{
AssemblyKernel k(m_context, "hello_world");
m_context->schedule(k.postamble());
std::string expected = R"(
s_endpgm
.Lhello_world_end:
.size hello_world, .Lhello_world_end-hello_world
.rodata
.p2align 6
.amdhsa_kernel hello_world
.amdhsa_next_free_vgpr 0
.amdhsa_next_free_sgpr .amdgcn.next_free_sgpr
.amdhsa_system_sgpr_workgroup_id_x 1
.amdhsa_system_sgpr_workgroup_id_y 1
.amdhsa_system_sgpr_workgroup_id_z 1
.amdhsa_system_sgpr_workgroup_info 0
.amdhsa_system_vgpr_workitem_id 2
.end_amdhsa_kernel
)";
EXPECT_EQ(NormalizedSource(output()), NormalizedSource(expected));
}
class MaxRegisterKernelTest : public KernelTest
{
protected:
GPUArchitectureTarget targetArchitecture() override
{
return {GPUArchitectureGFX::GFX90A};
}
void SetUp() override
{
m_kernelOptions->maxVGPRs = 234;
m_kernelOptions->setNextFreeVGPRToMax = true;
GenericContextFixture::SetUp();
}
};
TEST_F(MaxRegisterKernelTest, MaxVGPRsInPostamble)
{
AssemblyKernel k(m_context, "hello_world");
m_context->schedule(k.postamble());
EXPECT_EQ(countSubstring(output(), ".amdhsa_next_free_vgpr 234"), 1);
}
TEST_F(KernelTest, Metadata)
{
#ifdef ROCROLLER_TESTS_USE_YAML_CPP
AssemblyKernel k(m_context, "hello_world");
m_context->schedule(k.amdgpu_metadata());
std::string expected = R"(
.amdgpu_metadata
---
amdhsa.version:
- 1
- 2
amdhsa.kernels:
- .name: hello_world
.symbol: hello_world.kd
.kernarg_segment_size: 0
.group_segment_fixed_size: 0
.private_segment_fixed_size: 0
.kernarg_segment_align: 8
.sgpr_count: 0
.vgpr_count: 0
.agpr_count: 0
.max_flat_workgroup_size: 1
.kernel_dimensions: 3
.workgroup_size: [1, 1, 1]
.wavefront_size: 64
.workitem_count: [{is-null: true}, {is-null: true}, {is-null: true}]
.dynamic_sharedmemory_bytes:
is-null: true
.args:
[]
.kernel_graph:
is-null: true
.command:
is-null: true
...
.end_amdgpu_metadata
)";
EXPECT_EQ(NormalizedSource(output()), NormalizedSource(expected));
#else
GTEST_SKIP() << "Skipping NormalizedYAML test (Only implemented for yaml-cpp)";
#endif
}
TEST_F(KernelTest, ArgumentsAndRegisters)
{
#ifdef ROCROLLER_TESTS_USE_YAML_CPP
AssemblyKernel k(m_context, "hello_world");
k.addArgument({"foo", {DataType::Float}});
k.setWorkgroupSize({16, 8, 2});
auto alloc0 = std::make_shared<Register::Allocation>(
m_context, Register::Type::Scalar, DataType::Float);
m_context->allocator(Register::Type::Scalar)->allocate(alloc0);
auto alloc1 = std::make_shared<Register::Allocation>(
m_context, Register::Type::Vector, DataType::Double);
m_context->allocator(Register::Type::Vector)->allocate(alloc1);
m_context->schedule(k.amdgpu_metadata());
std::string expected = R"(
.amdgpu_metadata
---
amdhsa.version:
- 1
- 2
amdhsa.kernels:
- .name: hello_world
.symbol: hello_world.kd
.kernarg_segment_size: 4
.group_segment_fixed_size: 0
.private_segment_fixed_size: 0
.kernarg_segment_align: 8
.sgpr_count: 1
.vgpr_count: 2
.agpr_count: 0
.max_flat_workgroup_size: 256
.kernel_dimensions: 3
.workgroup_size: [16, 8, 2]
.wavefront_size: 64
.workitem_count: [{is-null: true}, {is-null: true}, {is-null: true}]
.dynamic_sharedmemory_bytes:
is-null: true
.args:
- .name: foo
.size: 4
.offset: 0
.expression:
is-null: true
.variableType: {dataType: Float, pointerType: Value}
.value_kind: by_value
.kernel_graph:
is-null: true
.command:
is-null: true
...
.end_amdgpu_metadata)";
EXPECT_EQ(NormalizedSource(output()), NormalizedSource(expected));
#else
GTEST_SKIP() << "Skipping NormalizedYAML test (Only implemented for yaml-cpp)";
#endif
}
TEST_P(ARCH_KernelTest, GPU_WholeKernel)
{
auto k = m_context->kernel();
k->setKernelName("hello_world");
k->setKernelDimensions(1);
k->addArgument(
{"ptr", {DataType::Float, PointerType::PointerGlobal}, DataDirection::WriteOnly});
k->addArgument({"val", {DataType::Float}});
m_context->schedule(k->preamble());
m_context->schedule(k->prolog());
auto kb = [&]() -> Generator<Instruction> {
Register::ValuePtr s_ptr, s_value;
co_yield m_context->argLoader()->getValue("ptr", s_ptr);
co_yield m_context->argLoader()->getValue("val", s_value);
auto v_ptr = Register::Value::Placeholder(m_context,
Register::Type::Vector,
{DataType::Float, PointerType::PointerGlobal},
1);
auto v_value = Register::Value::Placeholder(
m_context, Register::Type::Vector, DataType::Float, 1);
co_yield v_ptr->allocate();
co_yield m_context->copier()->copy(v_ptr, s_ptr, "Move pointer");
co_yield v_value->allocate();
co_yield m_context->copier()->copy(v_value, s_value, "Move value");
co_yield m_context->mem()->storeGlobal(v_ptr, v_value, 0, 4);
};
m_context->schedule(kb());
m_context->schedule(k->postamble());
m_context->schedule(k->amdgpu_metadata());
// Only execute the kernels if running on the architecture that the kernel was built for,
// otherwise just assemble the instructions.
if(isLocalDevice())
{
std::shared_ptr<rocRoller::ExecutableKernel> executableKernel
= m_context->instructions()->getExecutableKernel();
auto ptr = make_shared_device<float>();
ASSERT_THAT(hipMemset(ptr.get(), 0, sizeof(float)), HasHipSuccess(0));
KernelArguments kargs;
kargs.append("ptr", ptr.get());
kargs.append("val", 6.0f);
KernelInvocation invocation;
executableKernel->executeKernel(kargs, invocation);
float resultValue = 0.0f;
ASSERT_THAT(hipMemcpy(&resultValue, ptr.get(), sizeof(float), hipMemcpyDefault),
HasHipSuccess(0));
EXPECT_EQ(resultValue, 6.0f);
// Call the kernel a second time with different input.
KernelArguments kargs2;
kargs2.append("ptr", ptr.get());
kargs2.append("val", 7.5f);
executableKernel->executeKernel(kargs2, invocation);
ASSERT_THAT(hipMemcpy(&resultValue, ptr.get(), sizeof(float), hipMemcpyDefault),
HasHipSuccess(0));
EXPECT_EQ(resultValue, 7.5f);
}
else
{
std::vector<char> assembledKernel = m_context->instructions()->assemble();
EXPECT_GT(assembledKernel.size(), 0);
}
// kernel.s can be assembled with:
// /opt/rocm/bin/amdclang -x assembler -target amdgcn-amd-amdhsa -mcpu=gfx90a:xnack+ build/kernel.s -o build/kernel.o
}
INSTANTIATE_TEST_SUITE_P(ARCH_KernelTests, ARCH_KernelTest, supportedISATuples());
class GPU_KernelTest : public CurrentGPUContextFixture,
public ::testing::WithParamInterface<AssemblerType>
{
};
TEST_P(GPU_KernelTest, GPU_WholeKernel)
{
//FIXME fix the arch check with InProcess assembler for gfx94X and gfx95X
if(GetParam() == AssemblerType::InProcess
&& m_context->targetArchitecture().HasCapability(GPUCapability::HasMFMA_fp8))
{
GTEST_SKIP() << "Skipping InProcess Assembler on "
<< m_context->targetArchitecture().target() << std::endl;
}
Settings::getInstance()->set(Settings::KernelAssembler, GetParam());
ASSERT_EQ(true, isLocalDevice());
auto command = std::make_shared<Command>();
VariableType floatPtr{DataType::Float, PointerType::PointerGlobal};
VariableType floatVal{DataType::Float, PointerType::Value};
VariableType uintVal{DataType::UInt32, PointerType::Value};
auto ptrTag = command->allocateTag();
auto ptr_arg = command->allocateArgument(floatPtr, ptrTag, ArgumentType::Value);
auto valTag = command->allocateTag();
auto val_arg = command->allocateArgument(floatVal, valTag, ArgumentType::Value);
auto sizeTag = command->allocateTag();
auto size_arg = command->allocateArgument(uintVal, sizeTag, ArgumentType::Size);
auto ptr_exp = std::make_shared<Expression::Expression>(ptr_arg);
auto val_exp = std::make_shared<Expression::Expression>(val_arg);
auto size_exp = std::make_shared<Expression::Expression>(size_arg);
auto one = std::make_shared<Expression::Expression>(1u);
auto zero = std::make_shared<Expression::Expression>(0u);
auto k = m_context->kernel();
k->setKernelDimensions(1);
k->addArgument({"ptr",
{DataType::Float, PointerType::PointerGlobal},
DataDirection::WriteOnly,
ptr_exp});
k->addArgument({"val", {DataType::Float}, DataDirection::ReadOnly, val_exp});
k->setWorkgroupSize({1, 1, 1});
k->setWorkitemCount({size_exp, one, one});
k->setDynamicSharedMemBytes(zero);
m_context->schedule(k->preamble());
m_context->schedule(k->prolog());
auto kb = [&]() -> Generator<Instruction> {
Register::ValuePtr s_ptr, s_value;
co_yield m_context->argLoader()->getValue("ptr", s_ptr);
co_yield m_context->argLoader()->getValue("val", s_value);
auto v_ptr = Register::Value::Placeholder(m_context,
Register::Type::Vector,
{DataType::Float, PointerType::PointerGlobal},
1);
auto v_value = Register::Value::Placeholder(
m_context, Register::Type::Vector, DataType::Float, 1);
co_yield v_ptr->allocate();
co_yield m_context->copier()->copy(v_ptr, s_ptr, "Move pointer");
co_yield v_value->allocate();
co_yield m_context->copier()->copy(v_value, s_value, "Move value");
co_yield m_context->mem()->storeGlobal(v_ptr, v_value, 0, 4);
};
m_context->schedule(kb());
m_context->schedule(k->postamble());
m_context->schedule(k->amdgpu_metadata());
CommandKernel commandKernel;
commandKernel.setContext(m_context);
commandKernel.generateKernel();
auto ptr = make_shared_device<float>();
float val = 6.0f;
unsigned int size = 1;
ASSERT_THAT(hipMemset(ptr.get(), 0, sizeof(float)), HasHipSuccess(0));
CommandArguments commandArgs = command->createArguments();
commandArgs.setArgument(ptrTag, ArgumentType::Value, ptr.get());
commandArgs.setArgument(valTag, ArgumentType::Value, val);
commandArgs.setArgument(sizeTag, ArgumentType::Size, size);
commandKernel.launchKernel(commandArgs.runtimeArguments());
float resultValue = 0.0f;
ASSERT_THAT(hipMemcpy(&resultValue, ptr.get(), sizeof(float), hipMemcpyDefault),
HasHipSuccess(0));
EXPECT_EQ(resultValue, 6.0f);
}
INSTANTIATE_TEST_SUITE_P(GPU_KernelTests,
GPU_KernelTest,
::testing::Values(AssemblerType::InProcess,
AssemblerType::Subprocess));
}