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

664 lines
25 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/CopyGenerator.hpp>
#include <rocRoller/CodeGen/MemoryInstructions.hpp>
#include <rocRoller/CommandSolution.hpp>
#include <rocRoller/Context.hpp>
#include <rocRoller/DataTypes/DataTypes_BF6.hpp>
#include <rocRoller/DataTypes/DataTypes_FP6.hpp>
#include <rocRoller/Operations/Command.hpp>
#include "GPUContextFixture.hpp"
#include "GenericContextFixture.hpp"
#include "SourceMatcher.hpp"
#include "Utilities.hpp"
using namespace rocRoller;
namespace rocRollerTest
{
template <typename F6Type>
struct FloatReference
{
};
template <>
struct FloatReference<rocRoller::FP6> // E2M3
{
// clang-format off
static constexpr auto Values = std::to_array<float>({
0.000, 0.125, 0.25, 0.375, 0.50, 0.625, 0.75, 0.875,
1.000, 1.125, 1.25, 1.375, 1.50, 1.625, 1.75, 1.875,
2.000, 2.250, 2.50, 2.750, 3.00, 3.250, 3.50, 3.750,
4.000, 4.500, 5.00, 5.500, 6.00, 6.500, 7.00, 7.500,
-0.000, -0.125, -0.25, -0.375, -0.50, -0.625, -0.75, -0.875,
-1.000, -1.125, -1.25, -1.375, -1.50, -1.625, -1.75, -1.875,
-2.000, -2.250, -2.50, -2.750, -3.00, -3.250, -3.50, -3.750,
-4.000, -4.500, -5.00, -5.500, -6.00, -6.500, -7.00, -7.500
});
// clang-format on
};
template <>
struct FloatReference<rocRoller::BF6> // E3M2
{
// clang-format off
static constexpr auto Values = std::to_array<float>({
0.00, 0.0625, 0.125, 0.1875,
0.25, 0.3125, 0.375, 0.4375,
0.50, 0.6250, 0.750, 0.8750,
1.00, 1.2500, 1.500, 1.7500,
2.00, 2.5000, 3.000, 3.5000,
4.00, 5.0000, 6.000, 7.0000,
8.00, 10.0000, 12.000, 14.0000,
16.00, 20.0000, 24.000, 28.0000,
-0.00, -0.0625, -0.125, -0.1875,
-0.25, -0.3125, -0.375, -0.4375,
-0.50, -0.6250, -0.750, -0.8750,
-1.00, -1.2500, -1.500, -1.7500,
-2.00, -2.5000, -3.000, -3.5000,
-4.00, -5.0000, -6.000, -7.0000,
-8.00, -10.0000, -12.000, -14.0000,
-16.00, -20.0000, -24.000, -28.0000
});
// clang-format on
};
const size_t numF6PerF6x16 = 16;
const size_t numBytesPerF6x16 = 12;
class F6Test : public GPUContextFixtureParam<rocRoller::DataType>
{
public:
/*
* Packs F6 to F6x16 on CPU, buffer_load that into F6x16 to
* GPU, buffer_store to CPU
*/
void genF6x16BufferLoadAndStore(int num_f6, DataType F6x16Type)
{
int N = (num_f6 / numF6PerF6x16) * numBytesPerF6x16;
auto k = m_context->kernel();
k->setKernelName("BufferLoadAndStoreF6x16");
k->setKernelDimensions(1);
auto command = std::make_shared<Command>();
auto resultTag = command->allocateTag();
auto resultExpr = std::make_shared<Expression::Expression>(command->allocateArgument(
{DataType::UInt32, PointerType::PointerGlobal}, resultTag, ArgumentType::Value));
auto aTag = command->allocateTag();
auto aExpr = std::make_shared<Expression::Expression>(command->allocateArgument(
{DataType::UInt32, PointerType::PointerGlobal}, aTag, ArgumentType::Value));
k->addArgument({"result",
{DataType::UInt32, PointerType::PointerGlobal},
DataDirection::WriteOnly,
resultExpr});
k->addArgument({"a",
{DataType::UInt32, PointerType::PointerGlobal},
DataDirection::ReadOnly,
aExpr});
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 vgprSerial = m_context->kernel()->workitemIndex()[0];
int size = num_f6 / numF6PerF6x16;
auto v_a
= Register::Value::Placeholder(m_context,
Register::Type::Vector,
F6x16Type,
size,
Register::AllocationOptions::FullyContiguous());
co_yield v_a->allocate();
Expression::ExpressionPtr bufferExpr = Expression::literal(Buffer{0, 0, 0, 0});
bufferExpr = BufferDescriptor::SetDefaults(bufferExpr, m_context);
bufferExpr
= BufferDescriptor::SetBasePointer(bufferExpr, s_a->expression(), m_context);
bufferExpr
= BufferDescriptor::SetSize(bufferExpr, Expression::literal(N), m_context);
bufferExpr = BufferDescriptor::SetOptions(bufferExpr,
Expression::literal(131072)); //0x00020000
auto bufferRegs = Register::Value::Placeholder(
m_context, Register::Type::Scalar, {DataType::None, PointerType::Buffer}, 1);
co_yield Expression::generate(bufferRegs, bufferExpr, m_context);
bufferExpr = bufferRegs->expression();
auto bufInstOpts = rocRoller::BufferInstructionOptions();
co_yield m_context->mem()->loadBuffer(
v_a, vgprSerial, 0, bufferRegs, bufInstOpts, N);
bufferExpr = BufferDescriptor::SetBasePointer(
bufferExpr, s_result->expression(), m_context);
co_yield Expression::generate(bufferRegs, bufferExpr, m_context);
co_yield m_context->mem()->storeBuffer(
v_a, vgprSerial, 0, bufferRegs, bufInstOpts, N);
};
m_context->schedule(kb());
m_context->schedule(k->postamble());
m_context->schedule(k->amdgpu_metadata());
}
/**
* Packs F6 to F6x16 on CPU, global_load that into F6x16 to GPU,
* global_store to CPU
*/
void genF6x16GlobalLoadAndStore(int num_f6, DataType F6x16Type)
{
int N = (num_f6 / numF6PerF6x16) * numBytesPerF6x16;
auto k = m_context->kernel();
k->setKernelName("GlobalLoadAndStoreF6x16");
k->setKernelDimensions(1);
auto command = std::make_shared<Command>();
auto resultTag = command->allocateTag();
auto resultExpr = std::make_shared<Expression::Expression>(command->allocateArgument(
{DataType::UInt32, PointerType::PointerGlobal}, resultTag, ArgumentType::Value));
auto aTag = command->allocateTag();
auto aExpr = std::make_shared<Expression::Expression>(command->allocateArgument(
{DataType::UInt32, PointerType::PointerGlobal}, aTag, ArgumentType::Value));
k->addArgument({"result",
{DataType::UInt32, PointerType::PointerGlobal},
DataDirection::WriteOnly,
resultExpr});
k->addArgument({"a",
{DataType::UInt32, PointerType::PointerGlobal},
DataDirection::ReadOnly,
aExpr});
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::UInt32, PointerType::PointerGlobal},
1);
auto v_ptr
= Register::Value::Placeholder(m_context,
Register::Type::Vector,
{DataType::UInt32, PointerType::PointerGlobal},
1);
int size = num_f6 / numF6PerF6x16;
auto v_a
= Register::Value::Placeholder(m_context,
Register::Type::Vector,
F6x16Type,
size,
Register::AllocationOptions::FullyContiguous());
co_yield v_a->allocate();
co_yield v_ptr->allocate();
co_yield v_result->allocate();
co_yield m_context->copier()->copy(v_result, s_result, "Move pointer.");
co_yield m_context->copier()->copy(v_ptr, s_a, "Move pointer.");
co_yield m_context->mem()->loadGlobal(v_a, v_ptr, 0, N);
co_yield m_context->mem()->storeGlobal(v_result, v_a, 0, N);
};
m_context->schedule(kb());
m_context->schedule(k->postamble());
m_context->schedule(k->amdgpu_metadata());
}
void executeF6x16LoadAndStore(int num_f6, DataType F6x16Type, int isGlobal)
{
std::vector<uint8_t> data(num_f6);
for(uint32_t i = 0; i < num_f6; i++)
{
data[i] = (i + 10) % 64;
}
auto f6x16_data = packF6x16(data);
std::vector<uint32_t> result(f6x16_data.size());
if(isGlobal)
genF6x16GlobalLoadAndStore(num_f6, F6x16Type);
else
genF6x16BufferLoadAndStore(num_f6, F6x16Type);
CommandKernel commandKernel;
commandKernel.setContext(m_context);
auto d_a = make_shared_device(f6x16_data);
auto d_result = make_shared_device<uint32_t>(result.size());
KernelArguments runtimeArgs;
runtimeArgs.append<void*>("result", d_result.get());
runtimeArgs.append<void*>("a", d_a.get());
commandKernel.launchKernel(runtimeArgs.runtimeArguments());
ASSERT_THAT(hipMemcpy(result.data(),
d_result.get(),
sizeof(uint32_t) * result.size(),
hipMemcpyDefault),
HasHipSuccess(0));
auto actual_f6 = unpackF6x16(result);
for(int i = 0; i < data.size(); i++)
{
EXPECT_EQ(actual_f6[i], data[i]);
}
}
void loadStoreTileF6(std::shared_ptr<CommandKernel>& commandKernel,
DataType F6Type,
bool launch,
size_t nx, // tensor size x
size_t ny, // tensor size y
int m, // macro tile size x
int n, // macro tile size y
int t_m = 1, // thread tile size x
int t_n = 1) // thread tile size y
{
REQUIRE_ARCH_CAP(GPUCapability::HasExplicitVectorCO);
AssertFatal(nx % numF6PerF6x16 == 0, "Invalid F6 Dimensions");
int numF6 = nx * ny;
int numF6x16 = numF6 / numF6PerF6x16;
unsigned int workgroup_size_x = m / t_m;
unsigned int workgroup_size_y = n / t_n;
// each workgroup will get one tile; since workgroup_size matches m * n
auto NX = std::make_shared<Expression::Expression>(nx / t_m); // number of work items x
auto NY = std::make_shared<Expression::Expression>(ny / t_n); // number of work items y
auto NZ = std::make_shared<Expression::Expression>(1u); // number of work items z
// generate fp6 values
std::vector<uint8_t> data(numF6);
for(uint32_t i = 0; i < numF6; i++)
{
data[i] = (i + 10) % 64;
}
auto a = packF6x16(data);
auto d_a = make_shared_device(a);
auto d_b = make_shared_device<uint32_t>(a.size());
auto command = std::make_shared<Command>();
auto tagTensorA = command->addOperation(
rocRoller::Operations::Tensor(2, F6Type, {}, {0, 1})); // Load A
auto tagLoadA = command->addOperation(rocRoller::Operations::T_Load_Tiled(tagTensorA));
auto tagTensorB = command->addOperation(
rocRoller::Operations::Tensor(2, F6Type, {}, {0, 1})); // Store B
command->addOperation(rocRoller::Operations::T_Store_Tiled(tagLoadA, tagTensorB));
auto commandArgs = command->createArguments();
commandArgs.setArgument(tagTensorA, ArgumentType::Value, d_a.get());
commandArgs.setArgument(tagTensorA, ArgumentType::Size, 0, (size_t)nx);
commandArgs.setArgument(tagTensorA, ArgumentType::Size, 1, (size_t)ny);
commandArgs.setArgument(tagTensorA, ArgumentType::Stride, 0, (size_t)ny);
commandArgs.setArgument(tagTensorA, ArgumentType::Stride, 1, (size_t)1);
commandArgs.setArgument(tagTensorB, ArgumentType::Value, d_b.get());
commandArgs.setArgument(tagTensorB, ArgumentType::Size, 0, (size_t)nx);
commandArgs.setArgument(tagTensorB, ArgumentType::Size, 1, (size_t)ny);
commandArgs.setArgument(tagTensorB, ArgumentType::Stride, 0, (size_t)ny);
commandArgs.setArgument(tagTensorB, ArgumentType::Stride, 1, (size_t)1);
auto params = std::make_shared<CommandParameters>();
params->setManualKernelDimension(2);
params->setManualWorkgroupSize({workgroup_size_x, workgroup_size_y, 1});
auto launchParams = std::make_shared<CommandLaunchParameters>();
launchParams->setManualWorkitemCount({NX, NY, NZ});
auto macTileVGPR
= KernelGraph::CoordinateGraph::MacroTile({m, n}, MemoryType::VGPR, {t_m, t_n});
params->setDimensionInfo(tagLoadA, macTileVGPR);
params->transposeMemoryAccess.set(LayoutType::None, true);
commandKernel = std::make_shared<CommandKernel>(command, "loadStoreTileF6");
commandKernel->setContext(m_context);
commandKernel->setCommandParameters(params);
commandKernel->setLaunchParameters(launchParams);
commandKernel->generateKernel();
if(launch)
{
commandKernel->launchKernel(commandArgs.runtimeArguments());
auto r = std::vector<uint32_t>(a.size());
ASSERT_THAT(
hipMemcpy(r.data(), d_b.get(), numF6x16 * sizeof(FP6x16), hipMemcpyDefault),
HasHipSuccess(0));
for(size_t i = 0; i < a.size(); ++i)
{
EXPECT_EQ(r[i], a[i]);
}
}
}
};
TEST_P(F6Test, GPU_F6x16BufferLoadAndStore)
{
auto const& arch = m_context->targetArchitecture().target();
if(!arch.isCDNAGPU())
{
GTEST_SKIP() << "Test not yet supported on "
<< m_context->targetArchitecture().target().toString() << std::endl;
}
int num_f6 = 16;
auto F6x16Type = std::get<rocRoller::DataType>(GetParam()) == DataType::FP6
? DataType::FP6x16
: DataType::BF6x16;
if(isLocalDevice())
{
executeF6x16LoadAndStore(num_f6, F6x16Type, false);
}
else
{
genF6x16BufferLoadAndStore(num_f6, F6x16Type);
std::vector<char> assembledKernel = m_context->instructions()->assemble();
EXPECT_GT(assembledKernel.size(), 0);
}
}
TEST_P(F6Test, GPU_F6x16GlobalLoadAndStore)
{
auto const& arch = m_context->targetArchitecture().target();
if(!arch.isCDNAGPU())
{
GTEST_SKIP() << "Test not yet supported on "
<< m_context->targetArchitecture().target().toString() << std::endl;
}
int num_f6 = 16;
auto F6x16Type = std::get<rocRoller::DataType>(GetParam()) == DataType::FP6
? DataType::FP6x16
: DataType::BF6x16;
if(isLocalDevice())
{
executeF6x16LoadAndStore(num_f6, F6x16Type, true);
}
else
{
genF6x16GlobalLoadAndStore(num_f6, F6x16Type);
auto assembledKernel = m_context->instructions()->assemble();
EXPECT_GT(assembledKernel.size(), 0);
}
auto instructions = NormalizedSourceLines(m_context->instructions()->toString(), false);
int numGlobalLoad = 0;
int numGlobalLoadx3 = 0;
for(auto const& instruction : instructions)
{
if(instruction.starts_with("global_load"))
numGlobalLoad++;
if(instruction.starts_with("global_load_dwordx3"))
numGlobalLoadx3++;
}
EXPECT_EQ(numGlobalLoad, 1);
EXPECT_EQ(numGlobalLoadx3, 1);
}
TEST_P(F6Test, GPU_F6TiledLoadStore)
{
int workitemsPerWorkgroup = 64;
int elementsPerWorkitem = 16;
int macM = workitemsPerWorkgroup * elementsPerWorkitem;
int macN = 16;
int M = 4 * macM;
int N = 4 * macN;
std::shared_ptr<CommandKernel> commandKernel;
loadStoreTileF6(commandKernel,
std::get<rocRoller::DataType>(GetParam()),
isLocalDevice(),
M,
N,
macM,
macN,
1,
elementsPerWorkitem);
if(!commandKernel)
return;
auto instructions = NormalizedSourceLines(commandKernel->getInstructions(), false);
int numBufferLoad = 0;
int numBufferLoadx3 = 0;
int numBufferStore = 0;
int numBufferStorex3 = 0;
for(auto instruction : instructions)
{
if(instruction.starts_with("buffer_load"))
numBufferLoad++;
if(instruction.starts_with("buffer_load_dwordx3"))
numBufferLoadx3++;
if(instruction.starts_with("buffer_store"))
numBufferStore++;
if(instruction.starts_with("buffer_store_dwordx3"))
numBufferStorex3++;
}
EXPECT_EQ(numBufferLoad, 1);
EXPECT_EQ(numBufferLoadx3, 1);
EXPECT_EQ(numBufferStore, 1);
EXPECT_EQ(numBufferStorex3, 1);
}
INSTANTIATE_TEST_SUITE_P(F6Test,
F6Test,
::testing::Combine(supportedISAValues(),
::testing::Values(rocRoller::DataType::FP6,
rocRoller::DataType::BF6)));
class CPUF6Test : public GenericContextFixture
{
};
template <typename F6Type>
void numberConversion(double fp64)
{
// F6 to FP32
F6Type f6(fp64);
float fp32(f6);
EXPECT_FLOAT_EQ(fp32, fp64);
// FP32 to F6
f6 = F6Type(fp32);
EXPECT_FLOAT_EQ((double)f6, fp64);
}
TEST_F(CPUF6Test, CPUConversions)
{
auto const& FP6Values = FloatReference<rocRoller::FP6>::Values;
std::for_each(FP6Values.begin(), FP6Values.end(), numberConversion<rocRoller::FP6>);
auto const& BF6Values = FloatReference<rocRoller::BF6>::Values;
std::for_each(BF6Values.begin(), BF6Values.end(), numberConversion<rocRoller::BF6>);
}
TEST_F(CPUF6Test, CPUPack)
{
std::vector<uint8_t> f6bytes(64);
for(int i = 0; i < f6bytes.size(); i++)
{
f6bytes[i] = (i + 10) % 64;
}
auto f6x16 = packF6x16(f6bytes);
auto result = unpackF6x16(f6x16);
for(int i = 0; i < f6bytes.size(); i++)
{
EXPECT_EQ(result[i], f6bytes[i]);
}
// clang-format off
std::array fp6 = {0.125f, /* 000001 */
0.25f, /* 000010 */
0.375f, /* 000011 */
0.5f, /* 000100 */
0.625f, /* 000101 */
0.75f, /* 000110 */
0.875f, /* 000111 */
1.f, /* 001000 */
1.125f, /* 001001 */
1.25f, /* 001010 */
1.375f, /* 001011 */
1.5f, /* 001100 */
1.625f, /* 001101 */
1.75f, /* 001110 */
1.875f, /* 001111 */
2.f}; /* 010000 */
std::array bf6 = {0.0625f, /* 000001 */
0.125f, /* 000010 */
0.1875f, /* 000011 */
0.25f, /* 000100 */
0.3125f, /* 000101 */
0.375f, /* 000110 */
0.4375f, /* 000111 */
0.50f, /* 001000 */
0.625f, /* 001001 */
0.75f, /* 001010 */
0.875f, /* 001011 */
1.f, /* 001100 */
1.25f, /* 001101 */
1.5f, /* 001110 */
1.75f, /* 001111 */
2.f}; /* 010000 */
// clang-format on
auto testPacking = [&](auto& floats, auto fmt) {
ASSERT_EQ(floats.size(), 16);
f6bytes.clear();
for(auto v : floats)
f6bytes.push_back(cast_to_f6(v, fmt));
f6x16 = packF6x16(f6bytes);
EXPECT_EQ(f6x16.size(), 3);
EXPECT_EQ(f6x16[0], 0b10000101000100000011000010000001);
EXPECT_EQ(f6x16[1], 0b10110010100010010010000001110001);
EXPECT_EQ(f6x16[2], 0b01000000111100111000110100110000);
};
testPacking(fp6, DataTypes::FP6_FMT);
testPacking(bf6, DataTypes::BF6_FMT);
}
TEST_F(CPUF6Test, OutOfBoundsConversions)
{
rocRoller::FP6 nF6;
nF6 = 7.6;
EXPECT_FLOAT_EQ(nF6, 7.5);
nF6 = 8.0;
EXPECT_FLOAT_EQ(nF6, 7.5);
nF6 = 128.0;
EXPECT_FLOAT_EQ(nF6, 7.5);
nF6 = -7.6;
EXPECT_FLOAT_EQ(nF6, -7.5);
nF6 = -8.0;
EXPECT_FLOAT_EQ(nF6, -7.5);
nF6 = -128.0;
EXPECT_FLOAT_EQ(nF6, -7.5);
rocRoller::BF6 nBF6;
nBF6 = 28.1;
EXPECT_FLOAT_EQ(nBF6, 28.0);
nBF6 = 29.0;
EXPECT_FLOAT_EQ(nBF6, 28.0);
nBF6 = 128.0;
EXPECT_FLOAT_EQ(nBF6, 28.0);
nBF6 = -28.1;
EXPECT_FLOAT_EQ(nBF6, -28.0);
nBF6 = -29.0;
EXPECT_FLOAT_EQ(nBF6, -28.0);
nBF6 = -128.0;
EXPECT_FLOAT_EQ(nBF6, -28.0);
}
template <typename F6Type>
void checkSpecialValues(float& f32_inf, float& f32_nan, float& f32_zero)
{
F6Type f6_zero(f32_zero);
F6Type f6_pos_inf(f32_inf);
F6Type f6_neg_inf(-f32_inf);
F6Type f6_nan(f32_nan);
EXPECT_TRUE(std::iszero(f6_zero));
EXPECT_FALSE(std::isnan(f6_pos_inf));
EXPECT_FALSE(std::isinf(f6_pos_inf));
EXPECT_FALSE(std::isnan(f6_neg_inf));
EXPECT_FALSE(std::isinf(f6_neg_inf));
EXPECT_FALSE(std::isnan(f6_nan));
EXPECT_FALSE(std::isinf(f6_nan));
constexpr float max_f6_val = std::is_same_v<F6Type, BF6> ? 28.0f : 7.5f;
EXPECT_FLOAT_EQ(f6_pos_inf, max_f6_val);
EXPECT_FLOAT_EQ(f6_neg_inf, -max_f6_val);
EXPECT_FLOAT_EQ(f6_nan, max_f6_val);
}
TEST_F(CPUF6Test, SpecialValues)
{
union
{
uint32_t bits;
float val;
} f32_inf, f32_nan, f32_zero;
// For single-precision, if all exponent bits are 1 and
// - if mantissa is zero => Inf
// - if mantissa is non-zero => NaN
f32_inf.bits = 0x7F800000;
f32_nan.bits = 0x7F800001;
f32_zero.bits = 0x0;
EXPECT_TRUE(std::isinf(f32_inf.val));
EXPECT_TRUE(std::isnan(f32_nan.val));
checkSpecialValues<rocRoller::FP6>(f32_inf.val, f32_nan.val, f32_zero.val);
checkSpecialValues<rocRoller::BF6>(f32_inf.val, f32_nan.val, f32_zero.val);
}
}