Files
TheRock/rocm-libraries/shared/rocroller/test/unit/CommandTest.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

364 lines
14 KiB
C++

// Copyright Advanced Micro Devices, Inc., or its affiliates.
// SPDX-License-Identifier: MIT
#include "SimpleFixture.hpp"
#include "SourceMatcher.hpp"
#include <rocRoller/Expression.hpp>
#include <rocRoller/Operations/Command.hpp>
#include <rocRoller/TensorDescriptor.hpp>
using namespace rocRoller;
class CommandTest : public SimpleFixture
{
};
TEST_F(CommandTest, Basic)
{
auto command = std::make_shared<rocRoller::Command>();
EXPECT_EQ(0, command->operations().size());
auto tagTensor = command->addOperation(Operations::Tensor(1, DataType::Int32));
auto load_linear
= std::make_shared<Operations::Operation>(Operations::T_Load_Linear(tagTensor));
auto tagLoad = command->addOperation(load_linear);
Operations::T_Store_Linear tsl(tagLoad, tagTensor);
// tag value is assigned to an operation when it's added to the Command Graph
EXPECT_EQ(-1, static_cast<int32_t>(tsl.getTag()));
auto store_linear = std::make_shared<Operations::Operation>(std::move(tsl));
auto execute
= std::make_shared<Operations::Operation>(Operations::T_Execute(command->getNextTag()));
command->addOperation(store_linear);
command->addOperation(execute);
EXPECT_EQ(load_linear, command->findTag(Operations::OperationTag(1)));
EXPECT_EQ(4, command->operations().size());
}
TEST_F(CommandTest, ToString)
{
auto command = std::make_shared<rocRoller::Command>();
EXPECT_EQ(0, command->operations().size());
command->addOperation(std::make_shared<Operations::Operation>(
Operations::T_Load_Linear(Operations::OperationTag())));
command->addOperation(
std::make_shared<Operations::Operation>(Operations::T_Execute(command->getNextTag())));
EXPECT_EQ(2, command->operations().size());
std::ostringstream msg;
msg << *command;
EXPECT_THAT(msg.str(), ::testing::HasSubstr("T_LOAD_LINEAR"));
EXPECT_THAT(msg.str(), ::testing::HasSubstr("T_EXECUTE"));
EXPECT_EQ(msg.str(), command->toString());
}
TEST_F(CommandTest, ConvertOp)
{
// A command to convert a Float tensor into Half type
auto command = std::make_shared<rocRoller::Command>();
EXPECT_EQ(0, command->operations().size());
auto tagTensor = command->addOperation(Operations::Tensor(1, DataType::Float));
auto load_linear
= std::make_shared<Operations::Operation>(Operations::T_Load_Linear(tagTensor));
auto tagLoad = command->addOperation(load_linear);
auto cvtOp = std::make_shared<Operations::XOp>(Operations::E_Cvt(tagLoad, DataType::Half));
Operations::T_Execute execute(command->getNextTag());
auto tagConvert = execute.addXOp(cvtOp);
command->addOperation(execute);
EXPECT_EQ(execute.getInputs(), std::unordered_set<Operations::OperationTag>({tagLoad}));
EXPECT_EQ(execute.getOutputs(), std::unordered_set<Operations::OperationTag>({tagConvert}));
constexpr auto result = R"(
Tensor.Float.d1 0, (base=&0, sizes={&8 }, strides={&16 })
T_LOAD_LINEAR 1 Source 0
T_EXECUTE 1
E_Cvt 2, 1
)";
EXPECT_EQ(NormalizedSource(command->toString()), NormalizedSource(result));
}
TEST_F(CommandTest, VectorAdd)
{
auto command = std::make_shared<rocRoller::Command>();
auto tagTensorA = command->addOperation(Operations::Tensor(1, DataType::Float));
auto tagLoadA = command->addOperation(Operations::T_Load_Linear(tagTensorA));
auto tagTensorB = command->addOperation(Operations::Tensor(1, DataType::Float));
auto tagLoadB = command->addOperation(Operations::T_Load_Linear(tagTensorB));
Operations::T_Execute execute(command->getNextTag());
auto tagResult = execute.addXOp(Operations::E_Add(tagLoadA, tagLoadB));
command->addOperation(std::move(execute));
auto tagTensorResult = command->addOperation(Operations::Tensor(1, DataType::Float));
command->addOperation(Operations::T_Store_Linear(tagResult, tagTensorResult));
std::string result = R"(
Tensor.Float.d1 0, (base=&0, sizes={&8 }, strides={&16 })
T_LOAD_LINEAR 1 Source 0
Tensor.Float.d1 2, (base=&24, sizes={&32 }, strides={&40 })
T_LOAD_LINEAR 3 Source 2
T_EXECUTE 1 3
E_Add 4, 1, 3
Tensor.Float.d1 6, (base=&48, sizes={&56 }, strides={&64 })
T_STORE_LINEAR 7 Source 4 Dest 6
)";
EXPECT_EQ(NormalizedSource(command->toString()), NormalizedSource(result));
{
std::string expected = R"([
Tensor_0_pointer: PointerGlobal: Float(offset: 0, size: 8, read_write),
Tensor_0_size_0: Value: Int64(offset: 8, size: 8, read_only),
Tensor_0_stride_0: Value: Int64(offset: 16, size: 8, read_only),
Tensor_2_pointer: PointerGlobal: Float(offset: 24, size: 8, read_write),
Tensor_2_size_0: Value: Int64(offset: 32, size: 8, read_only),
Tensor_2_stride_0: Value: Int64(offset: 40, size: 8, read_only),
Tensor_6_pointer: PointerGlobal: Float(offset: 48, size: 8, read_write),
Tensor_6_size_0: Value: Int64(offset: 56, size: 8, read_only),
Tensor_6_stride_0: Value: Int64(offset: 64, size: 8, read_only)
])";
std::ostringstream msg;
msg << command->getArguments();
EXPECT_EQ(NormalizedSource(expected), NormalizedSource(msg.str()));
}
}
TEST_F(CommandTest, DuplicateOp)
{
auto command = std::make_shared<rocRoller::Command>();
auto execute
= std::make_shared<Operations::Operation>(Operations::T_Execute(command->getNextTag()));
command->addOperation(execute);
#ifdef NDEBUG
GTEST_SKIP() << "Skipping assertion check in release mode.";
#endif
EXPECT_THROW({ command->addOperation(execute); }, FatalError);
}
TEST_F(CommandTest, XopInputOutputs)
{
auto command = std::make_shared<rocRoller::Command>();
command->allocateTag();
command->allocateTag();
Operations::OperationTag tagA(0);
Operations::OperationTag tagB(1);
Operations::OperationTag tagC(2);
Operations::OperationTag tagD(3);
Operations::OperationTag tagE(4);
Operations::OperationTag tagF(5);
Operations::T_Execute execute(command->getNextTag());
execute.addXOp(std::make_shared<Operations::XOp>(Operations::E_Sub(tagA, tagB)));
execute.addXOp(std::make_shared<Operations::XOp>(Operations::E_Mul(tagC, tagB)));
EXPECT_EQ(execute.getInputs(), std::unordered_set<Operations::OperationTag>({tagA, tagB}));
EXPECT_EQ(execute.getOutputs(), std::unordered_set<Operations::OperationTag>({tagC, tagD}));
execute.addXOp(std::make_shared<Operations::XOp>(Operations::E_Abs(tagD)));
execute.addXOp(std::make_shared<Operations::XOp>(Operations::E_Neg(tagE)));
EXPECT_EQ(execute.getInputs(), std::unordered_set<Operations::OperationTag>({tagA, tagB}));
EXPECT_EQ(execute.getOutputs(),
std::unordered_set<Operations::OperationTag>({tagC, tagD, tagE, tagF}));
}
TEST_F(CommandTest, BlockScaleInline)
{
auto command = std::make_shared<rocRoller::Command>();
auto dataTensor = command->addOperation(Operations::Tensor(1, DataType::Int32));
auto block_scale = Operations::BlockScale(dataTensor, 3);
auto block_scale_tag
= command->addOperation(std::make_shared<Operations::Operation>(block_scale));
EXPECT_EQ(block_scale.scaleMode(), Operations::ScaleMode::Inline);
EXPECT_EQ(block_scale.strides(), std::vector<size_t>({32, 1, 1}));
EXPECT_EQ(command->getOperation<Operations::BlockScale>(block_scale_tag).getInputs(),
std::unordered_set<Operations::OperationTag>({dataTensor}));
}
TEST_F(CommandTest, BlockScaleSeparate)
{
auto command = std::make_shared<rocRoller::Command>();
auto dataTensor = command->addOperation(Operations::Tensor(1, DataType::Int32));
auto scaleTensor = command->addOperation(Operations::Tensor(1, DataType::Int32));
auto block_scale = Operations::BlockScale(dataTensor, 3, scaleTensor, {4, 32});
auto block_scale_tag
= command->addOperation(std::make_shared<Operations::Operation>(block_scale));
EXPECT_EQ(block_scale.scaleMode(), Operations::ScaleMode::Separate);
EXPECT_EQ(block_scale.strides(), std::vector<size_t>({4, 32, 1}));
EXPECT_EQ(command->getOperation<Operations::BlockScale>(block_scale_tag).getInputs(),
std::unordered_set<Operations::OperationTag>({dataTensor, scaleTensor}));
}
TEST_F(CommandTest, SetCommandArguments)
{
auto command = std::make_shared<rocRoller::Command>();
auto tagTensorA = command->addOperation(Operations::Tensor(1, DataType::Float));
auto tagLoadA = command->addOperation(Operations::T_Load_Linear(tagTensorA));
auto tagScalarB = command->addOperation(Operations::Scalar(DataType::Float));
auto tagLoadB = command->addOperation(Operations::T_Load_Scalar(tagScalarB));
Operations::T_Execute execute(command->getNextTag());
auto tagResult = execute.addXOp(Operations::E_Mul(tagLoadA, tagLoadB));
command->addOperation(std::move(execute));
auto tagTensorResult = command->addOperation(Operations::Tensor(1, DataType::Float));
command->addOperation(Operations::T_Store_Linear(tagResult, tagTensorResult));
CommandArguments commandArgs = command->createArguments();
EXPECT_THROW({ commandArgs.setArgument(tagTensorA, ArgumentType::Size, 10); }, FatalError);
commandArgs.setArgument(tagTensorA, ArgumentType::Size, 0, 10);
EXPECT_THROW({ commandArgs.setArgument(tagTensorA, ArgumentType::Stride, 1); }, FatalError);
commandArgs.setArgument(tagTensorA, ArgumentType::Stride, 0, 1);
commandArgs.setArgument(tagScalarB, ArgumentType::Value, 2);
}
TEST_F(CommandTest, FindCommandArguments)
{
auto command = std::make_shared<rocRoller::Command>();
auto tagScalarA = command->addOperation(Operations::Scalar(DataType::Float));
command->allocateArgument(
DataType::Float, tagScalarA, ArgumentType::Value, DataDirection::ReadOnly, "ScalarA");
// Find an existing arg
auto scalarArgA = findArgumentByName(command, "ScalarA");
EXPECT_NE(scalarArgA, nullptr);
// Find a nonexistent arg
auto scalarArgB = findArgumentByName(command, "ScalarB");
EXPECT_EQ(scalarArgB, nullptr);
}
TEST_F(CommandTest, GPU_TensorDescriptor)
{
auto command = std::make_shared<rocRoller::Command>();
auto tagTensor = command->addOperation(rocRoller::Operations::Tensor(2, DataType::Float));
TensorDescriptor desc(DataType::Float, {512u, 1024u}, "T");
CommandArguments commandArgs = command->createArguments();
auto device = make_shared_device<float>(512u * 1024u);
setCommandTensorArg(commandArgs, tagTensor, desc, device.get());
// Below should NOT error out as arguments have been set in setCommandTensorArg
EXPECT_NO_THROW({ commandArgs.setArgument(tagTensor, ArgumentType::Value, device.get()); });
EXPECT_NO_THROW({ commandArgs.setArgument(tagTensor, ArgumentType::Size, 0, 512u); });
EXPECT_NO_THROW({ commandArgs.setArgument(tagTensor, ArgumentType::Size, 1, 1024u); });
EXPECT_NO_THROW({ commandArgs.setArgument(tagTensor, ArgumentType::Stride, 0, 1024u); });
EXPECT_NO_THROW({ commandArgs.setArgument(tagTensor, ArgumentType::Stride, 1, 1u); });
}
TEST_F(CommandTest, GetRuntimeArguments)
{
std::unordered_map<DataType, size_t> typeSizes
= {{DataType::Float, 4}, {DataType::Double, 8},
{DataType::ComplexFloat, 8}, {DataType::ComplexDouble, 16},
{DataType::Half, 2}, {DataType::Halfx2, 4},
{DataType::FP8, 1}, {DataType::FP8x4, 4},
{DataType::BF8, 1}, {DataType::BF8x4, 4},
{DataType::Int8x4, 4}, {DataType::Int8, 1},
{DataType::Int16, 2}, {DataType::Int32, 4},
{DataType::Int64, 8}, {DataType::BFloat16, 2},
{DataType::Raw32, 4}, {DataType::UInt8x4, 4},
{DataType::UInt8, 1}, {DataType::UInt16, 2},
{DataType::UInt32, 4}, {DataType::UInt64, 8},
{DataType::E8M0, 1}, {DataType::E8M0x4, 4},
{DataType::E5M3, 1}, {DataType::E5M3x4, 4},
{DataType::E4M3, 1}, {DataType::E4M3x4, 4}};
// Check scalar
for(auto const& [type, bytes] : typeSizes)
{
auto command = std::make_shared<rocRoller::Command>();
[[maybe_unused]] auto tagScalar = command->addOperation(Operations::Scalar(type));
CommandArguments commandArgs = command->createArguments();
auto runtimeArguments = commandArgs.runtimeArguments();
EXPECT_EQ(runtimeArguments.size(), bytes);
EXPECT_EQ(runtimeArguments.size_bytes(), bytes);
}
// Check tensor
for(auto const& iter : typeSizes)
{
auto command = std::make_shared<rocRoller::Command>();
[[maybe_unused]] auto tagTensor = command->addOperation(Operations::Tensor(1, iter.first));
CommandArguments commandArgs = command->createArguments();
auto runtimeArguments = commandArgs.runtimeArguments();
// The total number of bytes required by pointer, size and stride
EXPECT_EQ(runtimeArguments.size(), 24);
EXPECT_EQ(runtimeArguments.size_bytes(), 24);
}
}
TEST_F(CommandTest, CommandKernelPredicates)
{
auto command = std::make_shared<Command>();
VariableType intVal{DataType::Int32, PointerType::Value};
auto valTag = command->allocateTag();
auto val_arg = command->allocateArgument(intVal, valTag, ArgumentType::Value);
auto val_exp = std::make_shared<Expression::Expression>(val_arg);
CommandKernel commandKernel(command, "PredicateTestKernel");
auto p1 = val_exp % Expression::literal(2) == Expression::literal(0);
Expression::setComment(p1, "val must be even");
auto p2 = val_exp % Expression::literal(5) == Expression::literal(0);
Expression::setComment(p2, "val must be divisible by 5");
commandKernel.addPredicate(p1);
commandKernel.addPredicate(p2);
CommandArguments commandArgs = command->createArguments();
for(int i = 1; i < 100; i++)
{
commandArgs.setArgument(valTag, ArgumentType::Value, i);
CommandArgumentValue value(i);
commandArgs.setArgument(valTag, ArgumentType::Value, value);
auto runtimeArguments = commandArgs.runtimeArguments();
EXPECT_EQ(commandKernel.matchesPredicates(runtimeArguments), i % 2 == 0 && i % 5 == 0);
if(!(i % 2 == 0 && i % 5 == 0))
{
EXPECT_THROW(commandKernel.launchKernel(runtimeArguments), FatalError);
}
}
}