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

442 lines
17 KiB
C++

// Copyright Advanced Micro Devices, Inc., or its affiliates.
// SPDX-License-Identifier: MIT
#include <rocRoller/AssemblyKernel.hpp>
#include <rocRoller/CodeGen/ArgumentLoader.hpp>
#include <rocRoller/CodeGen/MemoryInstructions.hpp>
#include <rocRoller/CommandSolution.hpp>
#include <rocRoller/Expression.hpp>
#include <rocRoller/ExpressionTransformations.hpp>
#include <rocRoller/KernelGraph/KernelGraph.hpp>
#include <rocRoller/KernelGraph/Utils.hpp>
#include <rocRoller/Operations/Command.hpp>
#include "GPUContextFixture.hpp"
#include "GenericContextFixture.hpp"
#include "Utilities.hpp"
#include <common/TestValues.hpp>
#include <limits>
#include <typeinfo>
#include <libdivide.h>
using namespace rocRoller;
namespace GPUFastDivisionTest
{
template <bool Modulo,
bool Defer,
typename Result,
typename AType = Result,
typename BType = AType>
struct Options
{
inline static const bool IsModulo = Modulo;
inline static const bool DeferExpression = Defer;
using R = Result;
using A = AType;
using B = BType;
};
template <typename Options>
class FastDivisionTestCurrentGPU : public CurrentGPUContextFixture
{
};
#define BoolCombinations(...) \
Options<false, false, __VA_ARGS__>, Options<false, true, __VA_ARGS__>, \
Options<true, false, __VA_ARGS__>, Options<true, true, __VA_ARGS__>
using TestedOptions = ::testing::Types<BoolCombinations(int32_t),
BoolCombinations(uint32_t),
BoolCombinations(int64_t, int32_t, int64_t),
BoolCombinations(int64_t, int64_t, int32_t),
BoolCombinations(int64_t)>;
#undef BoolCombinations
TYPED_TEST_SUITE(FastDivisionTestCurrentGPU, TestedOptions);
TYPED_TEST(FastDivisionTestCurrentGPU, GPU_FastDivision)
{
bool IsModulo = TypeParam::IsModulo;
bool DeferExpression = TypeParam::DeferExpression;
using R = typename TypeParam::R;
using A = typename TypeParam::A;
using B = typename TypeParam::B;
auto command = std::make_shared<Command>();
auto dataTypeA = TypeInfo<A>::Var.dataType;
auto dataTypeB = TypeInfo<B>::Var.dataType;
auto dataTypeResult = TypeInfo<R>::Var.dataType;
auto infoResult = DataTypeInfo::Get(dataTypeResult);
auto resultTag = command->allocateTag();
auto result_arg = command->allocateArgument(
{dataTypeResult, PointerType::PointerGlobal}, resultTag, ArgumentType::Value);
auto aTag = command->allocateTag();
auto a_arg
= command->allocateArgument({dataTypeA, PointerType::Value}, aTag, ArgumentType::Value);
auto bTag = command->allocateTag();
auto b_arg
= command->allocateArgument({dataTypeB, PointerType::Value}, bTag, ArgumentType::Value);
auto result_exp = std::make_shared<Expression::Expression>(result_arg);
auto k = this->m_context->kernel();
k->addArgument({result_arg->name(),
{dataTypeResult, PointerType::PointerGlobal},
DataDirection::WriteOnly,
result_exp});
auto a_exp = k->addCommandArgument(a_arg);
auto b_exp = k->addCommandArgument(b_arg);
auto one = std::make_shared<Expression::Expression>(1u);
auto zero = std::make_shared<Expression::Expression>(0u);
auto a_reg = Register::Value::Placeholder(
this->m_context, Register::Type::Scalar, dataTypeResult, 1);
if(dataTypeA != dataTypeResult)
a_exp = convert(dataTypeResult, a_exp);
if(dataTypeB != dataTypeResult)
b_exp = convert(dataTypeResult, b_exp);
std::shared_ptr<Expression::Expression> expr;
if(IsModulo)
expr = a_reg->expression() % b_exp;
else
expr = a_reg->expression() / b_exp;
if(DeferExpression)
{
enableDivideBy(b_exp, this->m_context);
}
else
{
expr = fastDivision(expr, this->m_context);
AssertFatal(!std::holds_alternative<Expression::Divide>(*expr), toString(expr));
AssertFatal(!std::holds_alternative<Expression::Modulo>(*expr), toString(expr));
AssertFatal(!contains<Expression::Divide>(expr), toString(expr));
AssertFatal(!contains<Expression::Modulo>(expr), toString(expr));
}
k->setWorkgroupSize({1, 1, 1});
k->setWorkitemCount({one, one, one});
k->setDynamicSharedMemBytes(zero);
this->m_context->schedule(k->preamble());
this->m_context->schedule(k->prolog());
auto kb = [&]() -> Generator<Instruction> {
Register::ValuePtr s_result, s_a, s_b;
co_yield this->m_context->argLoader()->getValue(result_arg->name(), s_result);
co_yield this->m_context->argLoader()->getValue(a_arg->name(), s_a);
co_yield this->m_context->argLoader()->getValue(b_arg->name(), s_b);
co_yield generate(a_reg, a_exp, this->m_context);
auto v_result
= Register::Value::Placeholder(this->m_context,
Register::Type::Vector,
{dataTypeResult, PointerType::PointerGlobal},
1);
auto v_c = Register::Value::Placeholder(
this->m_context, Register::Type::Vector, dataTypeResult, 1);
co_yield v_result->allocate();
co_yield this->m_context->copier()->copy(v_result, s_result, "Move pointer");
if(DeferExpression)
{
expr = fastDivision(expr, this->m_context);
}
AssertFatal(!std::holds_alternative<Expression::Divide>(*expr), toString(expr));
AssertFatal(!std::holds_alternative<Expression::Modulo>(*expr), toString(expr));
AssertFatal(!contains<Expression::Divide>(expr), toString(expr));
AssertFatal(!contains<Expression::Modulo>(expr), toString(expr));
Register::ValuePtr s_c;
co_yield Expression::generate(s_c, expr, this->m_context);
AssertFatal(s_c->variableType() == dataTypeResult,
ShowValue(s_c->variableType()),
ShowValue(dataTypeResult),
ShowValue(expr));
co_yield this->m_context->copier()->copy(v_c, s_c, "Move result to vgpr to store.");
co_yield this->m_context->mem()->storeGlobal(
v_result, v_c, 0, CeilDivide(infoResult.elementBits, 8u));
};
this->m_context->schedule(kb());
this->m_context->schedule(k->postamble());
this->m_context->schedule(k->amdgpu_metadata());
std::shared_ptr<rocRoller::ExecutableKernel> executableKernel
= this->m_context->instructions()->getExecutableKernel();
auto d_result = make_shared_device<R>();
CommandKernel commandKernel;
commandKernel.setContext(this->m_context);
commandKernel.generateKernel();
auto numerators = TestValues::ByType<A>::values;
auto denominators = TestValues::ByType<B>::values;
for(A a : numerators)
{
for(B b : denominators)
{
if(b == 0)
continue;
CommandArguments commandArgs = command->createArguments();
commandArgs.setArgument(resultTag, ArgumentType::Value, d_result.get());
commandArgs.setArgument(aTag, ArgumentType::Value, a);
commandArgs.setArgument(bTag, ArgumentType::Value, b);
commandKernel.launchKernel(commandArgs.runtimeArguments());
R result;
ASSERT_THAT(hipMemcpy(&result,
d_result.get(),
CeilDivide(infoResult.elementBits, 8u),
hipMemcpyDefault),
HasHipSuccess(0));
if(IsModulo)
{
EXPECT_EQ(result, a % b) << ShowValue(a) << ShowValue(dataTypeA) << ShowValue(b)
<< ShowValue(dataTypeB);
}
else
{
auto bLibDivide = libdivide::libdivide_s64_branchfree_gen(b);
EXPECT_EQ(result, libdivide::libdivide_s64_branchfree_do(a, &bLibDivide))
<< ShowValue(a) << ShowValue(dataTypeA) << ShowValue(b)
<< ShowValue(dataTypeB);
// Sanity check
EXPECT_EQ(a / b, libdivide::libdivide_s64_branchfree_do(a, &bLibDivide));
}
}
}
}
class FastDivisionTestByConstantCurrentGPU : public CurrentGPUContextFixture
{
public:
template <typename A, typename B>
void executeFastDivisionByConstant(std::vector<A> numerators, B divisor, bool isModulo)
{
if(divisor == 0)
{
return;
}
DataType dataTypeA = rocRoller::TypeInfo<A>::Var.dataType;
auto command = std::make_shared<Command>();
auto resultTag = command->allocateTag();
auto result_arg = command->allocateArgument(
{dataTypeA, PointerType::PointerGlobal}, resultTag, ArgumentType::Value);
auto aTag = command->allocateTag();
auto a_arg = command->allocateArgument(
{dataTypeA, PointerType::Value}, aTag, ArgumentType::Value);
auto result_exp = std::make_shared<Expression::Expression>(result_arg);
auto a_exp = std::make_shared<Expression::Expression>(a_arg);
auto k = m_context->kernel();
k->addArgument({"result",
{dataTypeA, PointerType::PointerGlobal},
DataDirection::WriteOnly,
result_exp});
k->addArgument({"a", dataTypeA, DataDirection::ReadOnly, a_exp});
auto one = std::make_shared<Expression::Expression>(1u);
auto zero = std::make_shared<Expression::Expression>(0u);
k->setWorkgroupSize({1, 1, 1});
k->setWorkitemCount({one, one, one});
k->setDynamicSharedMemBytes(zero);
m_context->schedule(k->preamble());
m_context->schedule(k->prolog());
auto kb = [&]() -> Generator<Instruction> {
Register::ValuePtr s_result, s_a;
co_yield m_context->argLoader()->getValue("result", s_result);
co_yield m_context->argLoader()->getValue("a", s_a);
auto v_result
= Register::Value::Placeholder(m_context,
Register::Type::Vector,
DataType::Raw32,
2,
Register::AllocationOptions::FullyContiguous());
auto v_c
= Register::Value::Placeholder(m_context, Register::Type::Vector, dataTypeA, 1);
co_yield v_result->allocate();
co_yield m_context->copier()->copy(v_result, s_result, "Move pointer");
auto a = s_a->expression();
auto denom = Expression::literal(divisor);
std::shared_ptr<Expression::Expression> expr = isModulo ? (a % denom) : (a / denom);
Register::ValuePtr s_c;
co_yield Expression::generate(s_c, expr, m_context);
co_yield m_context->copier()->copy(v_c, s_c, "Move result to vgpr to store.");
co_yield m_context->mem()->storeGlobal(
v_result, v_c, 0, v_c->variableType().getElementSize());
};
m_context->schedule(kb());
m_context->schedule(k->postamble());
m_context->schedule(k->amdgpu_metadata());
std::shared_ptr<rocRoller::ExecutableKernel> executableKernel
= m_context->instructions()->getExecutableKernel();
auto d_result = make_shared_device<A>();
CommandKernel commandKernel;
commandKernel.setContext(m_context);
commandKernel.generateKernel();
for(A a : numerators)
{
CommandArguments commandArgs = command->createArguments();
commandArgs.setArgument(resultTag, ArgumentType::Value, d_result.get());
commandArgs.setArgument(aTag, ArgumentType::Value, a);
commandKernel.launchKernel(commandArgs.runtimeArguments());
A result;
ASSERT_THAT(hipMemcpy(&result, d_result.get(), sizeof(A), hipMemcpyDefault),
HasHipSuccess(0));
if(isModulo)
{
EXPECT_EQ(result, a % divisor) << ShowValue(a) << ShowValue(divisor);
}
else
{
EXPECT_EQ(result, a / divisor) << ShowValue(a) << ShowValue(divisor);
}
}
}
};
class FastDivisionTestByConstantSignedCurrentGPU : public FastDivisionTestByConstantCurrentGPU,
public ::testing::WithParamInterface<int>
{
};
TEST_P(FastDivisionTestByConstantSignedCurrentGPU, GPU_FastDivisionByConstant32)
{
executeFastDivisionByConstant(TestValues::int32Values, GetParam(), false);
}
TEST_P(FastDivisionTestByConstantSignedCurrentGPU, GPU_FastModuloByConstant32)
{
executeFastDivisionByConstant(TestValues::int32Values, GetParam(), true);
}
// FIXME: Types not supported yet
// TEST_P(FastDivisionTestByConstantSignedCurrentGPU, GPU_FastDivisionByConstantU32)
// {
// executeFastDivisionByConstant(TestValues::uint32Values, GetParam(), false);
// }
// FIXME: Types not supported yet
// TEST_P(FastDivisionTestByConstantSignedCurrentGPU, GPU_FastModuloByConstantU32)
// {
// executeFastDivisionByConstant(TestValues::uint32Values, GetParam(), true);
// }
// FIXME: Types not supported yet
// TEST_P(FastDivisionTestByConstantSignedCurrentGPU, GPU_FastDivisionByConstant64)
// {
// executeFastDivisionByConstant(TestValues::int64Values, GetParam(), false);
// }
// FIXME: Types not supported yet
// TEST_P(FastDivisionTestByConstantSignedCurrentGPU, GPU_FastModuloByConstant64)
// {
// executeFastDivisionByConstant(TestValues::int64Values, GetParam(), true);
// }
INSTANTIATE_TEST_SUITE_P(FastDivisionTestByConstantSignedCurrentGPUs,
FastDivisionTestByConstantSignedCurrentGPU,
::testing::ValuesIn(TestValues::int32Values));
class FastDivisionTestByConstantUnsignedCurrentGPU
: public FastDivisionTestByConstantCurrentGPU,
public ::testing::WithParamInterface<unsigned int>
{
};
// FIXME: Types not supported yet
// TEST_P(FastDivisionTestByConstantUnsignedCurrentGPU, GPU_FastDivisionByConstant32)
// {
// executeFastDivisionByConstant(TestValues::int32Values, GetParam(), false);
// }
// FIXME: Types not supported yet
// TEST_P(FastDivisionTestByConstantUnsignedCurrentGPU, GPU_FastModuloByConstant32)
// {
// executeFastDivisionByConstant(TestValues::int32Values, GetParam(), true);
// }
TEST_P(FastDivisionTestByConstantUnsignedCurrentGPU, GPU_FastDivisionByConstantU32)
{
executeFastDivisionByConstant(TestValues::uint32Values, GetParam(), false);
}
TEST_P(FastDivisionTestByConstantUnsignedCurrentGPU, GPU_FastModuloByConstantU32)
{
executeFastDivisionByConstant(TestValues::uint32Values, GetParam(), true);
}
// FIXME: Types not supported yet
// TEST_P(FastDivisionTestByConstantUnsignedCurrentGPU, GPU_FastDivisionByConstant64)
// {
// executeFastDivisionByConstant(TestValues::int64Values, GetParam(), false);
// }
// FIXME: Types not supported yet
// TEST_P(FastDivisionTestByConstantUnsignedCurrentGPU, GPU_FastModuloByConstant64)
// {
// executeFastDivisionByConstant(TestValues::int64Values, GetParam(), true);
// }
INSTANTIATE_TEST_SUITE_P(FastDivisionTestByConstantUnsignedCurrentGPUs,
FastDivisionTestByConstantUnsignedCurrentGPU,
::testing::ValuesIn(TestValues::uint32Values));
}