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

1386 lines
64 KiB
C++

// Copyright Advanced Micro Devices, Inc., or its affiliates.
// SPDX-License-Identifier: MIT
#include <cmath>
#include <memory>
#include <rocRoller/CodeGen/Arithmetic/ArithmeticGenerator.hpp>
#include <rocRoller/AssemblyKernel.hpp>
#include <rocRoller/CodeGen/ArgumentLoader.hpp>
#include <rocRoller/CodeGen/MemoryInstructions.hpp>
#include <rocRoller/CommandSolution.hpp>
#include <rocRoller/GPUArchitecture/GPUArchitectureLibrary.hpp>
#include <rocRoller/KernelArguments.hpp>
#include <rocRoller/KernelOptions_detail.hpp>
#include <rocRoller/Operations/Command.hpp>
#include <rocRoller/Utilities/Generator.hpp>
#include <rocRoller/Utilities/Utils.hpp>
#include "GPUContextFixture.hpp"
#include "GenericContextFixture.hpp"
#include "SourceMatcher.hpp"
#include "Utilities.hpp"
#include <common/TestValues.hpp>
namespace ArithmeticTest
{
template <typename T>
struct identity
{
using type = T;
};
template <typename T>
using try_make_unsigned =
typename std::conditional_t<std::is_integral_v<T>, std::make_unsigned<T>, identity<T>>;
template <typename T>
using try_make_signed =
typename std::conditional_t<std::is_integral_v<T>, std::make_signed<T>, identity<T>>;
using namespace rocRoller;
const int LITERAL_TEST = 227;
struct IntegralArithmeticTest : public GPUContextFixtureParam<Register::Type>
{
template <typename T>
void testBody()
{
setKernelOptions({{.enableFullDivision = true}});
auto dataType = TypeInfo<T>::Var.dataType;
auto regType = std::get<1>(GetParam());
auto k = m_context->kernel();
auto numBoolRegs = k->wavefront_size() / 32;
k->setKernelDimensions(1);
auto command = std::make_shared<Command>();
auto resultTag = command->allocateTag();
auto resultExpr = std::make_shared<Expression::Expression>(command->allocateArgument(
{dataType, PointerType::PointerGlobal}, resultTag, ArgumentType::Value));
auto comparisonResultTag = command->allocateTag();
auto comparisonResultExpr = std::make_shared<Expression::Expression>(
command->allocateArgument({DataType::UInt32, PointerType::PointerGlobal},
comparisonResultTag,
ArgumentType::Value));
auto aTag = command->allocateTag();
auto aExpr = std::make_shared<Expression::Expression>(command->allocateArgument(
{dataType, PointerType::Value}, aTag, ArgumentType::Value));
auto bTag = command->allocateTag();
auto bExpr = std::make_shared<Expression::Expression>(command->allocateArgument(
{dataType, PointerType::Value}, bTag, ArgumentType::Value));
auto shTag = command->allocateTag();
auto shExpr = std::make_shared<Expression::Expression>(command->allocateArgument(
{DataType::UInt32, PointerType::Value}, shTag, ArgumentType::Value));
auto one = std::make_shared<Expression::Expression>(1u);
auto zero = std::make_shared<Expression::Expression>(0u);
k->addArgument({"result",
{dataType, PointerType::PointerGlobal},
DataDirection::WriteOnly,
resultExpr});
k->addArgument({"comparisonResult",
{DataType::UInt32, PointerType::PointerGlobal},
DataDirection::WriteOnly,
comparisonResultExpr});
k->addArgument({"a", dataType, DataDirection::ReadOnly, aExpr});
k->addArgument({"b", dataType, DataDirection::ReadOnly, bExpr});
k->addArgument({"shift", DataType::UInt32, DataDirection::ReadOnly, shExpr});
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 resultArg, comparisonResultArg, aArg, bArg, shiftArg;
co_yield m_context->argLoader()->getValue("result", resultArg);
co_yield m_context->argLoader()->getValue("comparisonResult", comparisonResultArg);
co_yield m_context->argLoader()->getValue("a", aArg);
co_yield m_context->argLoader()->getValue("b", bArg);
co_yield m_context->argLoader()->getValue("shift", shiftArg);
auto resultPtr = Register::Value::Placeholder(
m_context, Register::Type::Vector, {dataType, PointerType::PointerGlobal}, 1);
auto resultReg
= Register::Value::Placeholder(m_context, Register::Type::Vector, dataType, 1);
auto a = Register::Value::Placeholder(m_context, regType, dataType, 1);
auto b = Register::Value::Placeholder(m_context, regType, dataType, 1);
auto c = Register::Value::Placeholder(m_context, regType, dataType, 1);
auto shift = Register::Value::Placeholder(m_context, regType, DataType::UInt32, 1);
auto boolean = (regType == Register::Type::Vector)
? Register::Value::WavefrontPlaceholder(m_context)
: Register::Value::Placeholder(
m_context, Register::Type::Scalar, DataType::Bool, 1);
co_yield a->allocate();
co_yield b->allocate();
co_yield c->allocate();
co_yield shift->allocate();
co_yield boolean->allocate();
co_yield resultPtr->allocate();
co_yield resultReg->allocate();
co_yield m_context->copier()->copy(resultPtr, resultArg, "Move pointer");
co_yield m_context->copier()->copy(a, aArg, "Move value");
co_yield m_context->copier()->copy(b, bArg, "Move value");
co_yield m_context->copier()->copy(shift, shiftArg, "Move value");
auto store = [&](size_t idx, bool logical = false) -> Generator<Instruction> {
co_yield m_context->copier()->copy(
resultReg, c, "Move result to VGPR to store.");
if(logical && regType == Register::Type::Scalar)
{
co_yield m_context->mem()->store(
MemoryInstructions::MemoryKind::Global,
resultPtr,
resultReg,
Register::Value::Literal(idx * sizeof(uint32_t)),
sizeof(uint32_t));
}
else if(logical && regType == Register::Type::Vector)
{
co_yield m_context->mem()->store(
MemoryInstructions::MemoryKind::Global,
resultPtr,
resultReg,
Register::Value::Literal(idx * sizeof(uint32_t) * numBoolRegs),
sizeof(uint32_t) * numBoolRegs);
}
else
{
co_yield m_context->mem()->store(MemoryInstructions::MemoryKind::Global,
resultPtr,
resultReg,
Register::Value::Literal(idx * sizeof(T)),
sizeof(T));
}
};
co_yield generateOp<Expression::Add>(c, a, b);
co_yield store(0);
co_yield generateOp<Expression::Subtract>(c, a, b);
co_yield store(1);
co_yield generateOp<Expression::Multiply>(c, a, b);
co_yield store(2);
co_yield generateOp<Expression::Divide>(c, a, b);
co_yield store(3);
co_yield generateOp<Expression::Modulo>(c, a, b);
co_yield store(4);
co_yield generateOp<Expression::ShiftL>(c, a, b);
co_yield store(5);
co_yield generateOp<Expression::LogicalShiftR>(c, a, b);
co_yield store(6);
co_yield generateOp<Expression::ArithmeticShiftR>(c, a, b);
co_yield store(7);
co_yield generateOp<Expression::BitwiseAnd>(c, a, b);
co_yield store(8);
co_yield generateOp<Expression::MultiplyHigh>(c, a, b);
co_yield store(9);
co_yield generateOp<Expression::Negate>(c, a);
co_yield store(10);
co_yield generateOp<Expression::BitwiseXor>(c, a, b);
co_yield store(11);
co_yield generateOp<Expression::AddShiftL>(c, a, b, shift);
co_yield store(12);
co_yield generateOp<Expression::ShiftLAdd>(c, a, shift, b);
co_yield store(13);
co_yield generateOp<Expression::BitwiseOr>(c, a, b);
co_yield store(14);
co_yield generateOp<Expression::Divide>(
c, a, Register::Value::Literal(LITERAL_TEST));
co_yield store(15);
co_yield generateOp<Expression::Divide>(
c, Register::Value::Literal(LITERAL_TEST), b);
co_yield store(16);
co_yield generateOp<Expression::Modulo>(
c, a, Register::Value::Literal(LITERAL_TEST));
co_yield store(17);
co_yield generateOp<Expression::Modulo>(
c, Register::Value::Literal(LITERAL_TEST), b);
co_yield store(18);
co_yield generateOp<Expression::BitwiseNegate>(c, a);
co_yield store(19);
co_yield generateOp<Expression::GreaterThanEqual>(boolean, a, b);
co_yield generateOp<Expression::Conditional>(c, boolean, a, b);
co_yield store(20);
co_yield generateOp<Expression::BitFieldExtract>(
c,
a,
Expression::BitFieldExtract{
.outputDataType = dataType, .offset = 8, .width = 8});
co_yield store(21);
co_yield generateOp<Expression::BitwiseAnd>(
c, Register::Value::Literal(static_cast<T>(LITERAL_TEST)), b);
co_yield store(22);
co_yield generateOp<Expression::BitwiseAnd>(
c, a, Register::Value::Literal(static_cast<T>(LITERAL_TEST)));
co_yield store(23);
co_yield generateOp<Expression::BitwiseAnd>(
c, Register::Value::Literal(LITERAL_TEST), b);
co_yield store(24);
co_yield generateOp<Expression::BitwiseAnd>(
c, a, Register::Value::Literal(LITERAL_TEST));
co_yield store(25);
//
// Logical / boolean
//
// Change result and c
//
resultPtr
= Register::Value::Placeholder(m_context,
Register::Type::Vector,
{DataType::Raw32, PointerType::PointerGlobal},
1);
co_yield resultPtr->allocate();
co_yield m_context->copier()->copy(resultPtr, comparisonResultArg, "Move pointer");
resultReg = boolean->placeholder(Register::Type::Vector,
Register::AllocationOptions::FullyContiguous());
co_yield resultReg->allocate();
c = boolean;
co_yield generateOp<Expression::GreaterThan>(c, a, b);
co_yield store(0, true);
co_yield generateOp<Expression::GreaterThanEqual>(c, a, b);
co_yield store(1, true);
co_yield generateOp<Expression::LessThan>(c, a, b);
co_yield store(2, true);
co_yield generateOp<Expression::LessThanEqual>(c, a, b);
co_yield store(3, true);
co_yield generateOp<Expression::Equal>(c, a, b);
co_yield store(4, true);
co_yield generateOp<Expression::NotEqual>(c, a, b);
co_yield store(5, true);
auto aExpr = a->expression();
auto bExpr = b->expression();
co_yield generate(c,
(aExpr < Expression::literal(0, dataType))
&& (bExpr < Expression::literal(0, dataType)),
m_context);
co_yield store(6, true);
co_yield generate(c,
(aExpr < Expression::literal(0, dataType))
|| (bExpr < Expression::literal(0, dataType)),
m_context);
co_yield store(7, true);
co_yield generate(c, Expression::logicalNot(aExpr < bExpr), m_context);
co_yield store(8, true);
co_yield generate(
c, Expression::logicalNot(Expression::logicalNot(aExpr > bExpr)), m_context);
co_yield store(9, true);
};
m_context->schedule(kb());
m_context->schedule(k->postamble());
m_context->schedule(k->amdgpu_metadata());
if(isLocalDevice())
{
CommandKernel commandKernel;
commandKernel.setContext(m_context);
commandKernel.generateKernel();
size_t const resultSize = 26;
size_t const comparisonResultSize = 10;
auto d_result = make_shared_device<T>(resultSize);
auto d_comparisonResult
= make_shared_device<uint32_t>(comparisonResultSize * numBoolRegs);
for(T a : TestValues::ByType<T>::values)
{
for(T b : TestValues::ByType<T>::values)
{
using T_unsigned = typename try_make_unsigned<T>::type;
using T_signed = typename try_make_signed<T>::type;
// Signed division/modulo is used for unsigned
auto withinDivisionDomain = b != 0
&& a <= std::numeric_limits<T_signed>::max()
&& b <= std::numeric_limits<T_signed>::max();
for(uint32_t shift : TestValues::shiftValues)
{
CommandArguments commandArgs = command->createArguments();
commandArgs.setArgument(resultTag, ArgumentType::Value, d_result.get());
commandArgs.setArgument(
comparisonResultTag, ArgumentType::Value, d_comparisonResult.get());
commandArgs.setArgument(aTag, ArgumentType::Value, a);
commandArgs.setArgument(bTag, ArgumentType::Value, b);
commandArgs.setArgument(shTag, ArgumentType::Value, shift);
commandKernel.launchKernel(commandArgs.runtimeArguments());
std::vector<T> result(resultSize);
ASSERT_THAT(hipMemcpy(result.data(),
d_result.get(),
result.size() * sizeof(T),
hipMemcpyDefault),
HasHipSuccess(0));
std::vector<uint32_t> comparisonResult(comparisonResultSize
* numBoolRegs);
ASSERT_THAT(hipMemcpy(comparisonResult.data(),
d_comparisonResult.get(),
comparisonResult.size() * sizeof(uint32_t),
hipMemcpyDefault),
HasHipSuccess(0));
EXPECT_EQ(result[0], a + b);
EXPECT_EQ(result[1], a - b);
EXPECT_EQ(result[2], a * b);
if(withinDivisionDomain)
{
EXPECT_EQ(result[3], a / b) << "a: " << a << " b: " << b;
EXPECT_EQ(result[4], a % b);
}
if(b < 32 && b >= 0)
{
EXPECT_EQ(result[5], a << b);
EXPECT_EQ(result[6], static_cast<T_unsigned>(a) >> b);
EXPECT_EQ(result[7], a >> b);
}
EXPECT_EQ(result[8], a & b);
if constexpr(std::is_same_v<T_signed, int32_t>)
{
EXPECT_EQ(result[9],
(a * (int64_t)b)
>> std::numeric_limits<T_unsigned>::digits);
}
else if constexpr(std::is_same_v<T, int64_t>)
{
EXPECT_EQ(result[9],
(int64_t)(((__int128_t)a * (__int128_t)b)
>> std::numeric_limits<T_unsigned>::digits));
}
EXPECT_EQ(result[10], -a);
EXPECT_EQ(result[11], a ^ b);
EXPECT_EQ(result[12], (a + b) << shift);
EXPECT_EQ(result[13], (a << shift) + b);
EXPECT_EQ(result[14], a | b);
if(withinDivisionDomain)
{
EXPECT_EQ(result[15], a / LITERAL_TEST);
EXPECT_EQ(result[16], LITERAL_TEST / b);
EXPECT_EQ(result[17], a % LITERAL_TEST);
EXPECT_EQ(result[18], LITERAL_TEST % b);
}
EXPECT_EQ(result[19], ~a);
EXPECT_EQ(result[20], (a >= b) ? a : b);
EXPECT_EQ(result[21],
std::bit_cast<T>((std::bit_cast<similar_integral_type<T>>(a)
<< (sizeof(T) * 8 - 16))
>> (sizeof(T) * 8 - 8)));
EXPECT_EQ(result[22], LITERAL_TEST & b);
EXPECT_EQ(result[23], a & LITERAL_TEST);
int wm = regType == Register::Type::Vector ? numBoolRegs : 1;
EXPECT_EQ(comparisonResult[0 * wm], (a > b ? 1 : 0));
EXPECT_EQ(comparisonResult[1 * wm], (a >= b ? 1 : 0));
EXPECT_EQ(comparisonResult[2 * wm], (a < b ? 1 : 0));
EXPECT_EQ(comparisonResult[3 * wm], (a <= b ? 1 : 0));
EXPECT_EQ(comparisonResult[4 * wm], (a == b ? 1 : 0));
EXPECT_EQ(comparisonResult[5 * wm], (a != b ? 1 : 0));
EXPECT_EQ(comparisonResult[6 * wm], ((a < 0) && (b < 0)) ? 1 : 0);
EXPECT_EQ(comparisonResult[7 * wm], ((a < 0) || (b < 0)) ? 1 : 0);
EXPECT_EQ(comparisonResult[8 * wm], !(a < b) ? 1 : 0);
EXPECT_EQ(comparisonResult[9 * wm], !!(a > b) ? 1 : 0);
}
}
}
}
else
{
std::vector<char> assembledKernel = m_context->instructions()->assemble();
EXPECT_GT(assembledKernel.size(), 0);
}
}
};
TEST_P(IntegralArithmeticTest, GPU_Int32)
{
testBody<int32_t>();
}
TEST_P(IntegralArithmeticTest, GPU_UInt32)
{
testBody<uint32_t>();
}
TEST_P(IntegralArithmeticTest, GPU_Int64)
{
testBody<int64_t>();
}
TEST_P(IntegralArithmeticTest, GPU_UInt64)
{
testBody<uint64_t>();
}
INSTANTIATE_TEST_SUITE_P(NewArithmeticTests,
IntegralArithmeticTest,
::testing::Combine(supportedISAValues(),
::testing::Values(Register::Type::Vector,
Register::Type::Scalar)));
class FPArithmeticTest : public GPUContextFixture
{
public:
FPArithmeticTest() {}
};
TEST_P(FPArithmeticTest, GPU_ArithFloat)
{
auto k = m_context->kernel();
k->setKernelDimensions(1);
auto command = std::make_shared<Command>();
auto resultTag = command->allocateTag();
auto resultExpr = std::make_shared<Expression::Expression>(command->allocateArgument(
{DataType::Float, PointerType::PointerGlobal}, resultTag, ArgumentType::Value));
auto condResultTag = command->allocateTag();
auto cond_resultExpr = std::make_shared<Expression::Expression>(command->allocateArgument(
{DataType::Int32, PointerType::PointerGlobal}, condResultTag, ArgumentType::Value));
auto aTag = command->allocateTag();
auto aExpr = std::make_shared<Expression::Expression>(command->allocateArgument(
{DataType::Float, PointerType::Value}, aTag, ArgumentType::Value));
auto bTag = command->allocateTag();
auto bExpr = std::make_shared<Expression::Expression>(command->allocateArgument(
{DataType::Float, PointerType::Value}, bTag, ArgumentType::Value));
auto cTag = command->allocateTag();
auto cExpr = std::make_shared<Expression::Expression>(command->allocateArgument(
{DataType::Float, PointerType::Value}, cTag, ArgumentType::Value));
auto one = std::make_shared<Expression::Expression>(1u);
auto zero = std::make_shared<Expression::Expression>(0u);
k->addArgument({"result",
{DataType::Float, PointerType::PointerGlobal},
DataDirection::WriteOnly,
resultExpr});
k->addArgument({"cond_result",
{DataType::Int32, PointerType::PointerGlobal},
DataDirection::WriteOnly,
cond_resultExpr});
k->addArgument({"a", DataType::Float, DataDirection::ReadOnly, aExpr});
k->addArgument({"b", DataType::Float, DataDirection::ReadOnly, bExpr});
k->addArgument({"c", DataType::Float, DataDirection::ReadOnly, cExpr});
k->setWorkgroupSize({1, 1, 1});
k->setWorkitemCount({one, one, one});
k->setDynamicSharedMemBytes(zero);
m_context->schedule(k->preamble());
m_context->schedule(k->prolog());
auto arch = m_context->targetArchitecture();
auto gpu = arch.target();
auto const wavefrontSize = arch.GetCapability(GPUCapability::DefaultWavefrontSize);
auto kb = [&]() -> Generator<Instruction> {
Register::ValuePtr s_result, s_cond_result, s_a, s_b, s_c;
co_yield m_context->argLoader()->getValue("result", s_result);
co_yield m_context->argLoader()->getValue("cond_result", s_cond_result);
co_yield m_context->argLoader()->getValue("a", s_a);
co_yield m_context->argLoader()->getValue("b", s_b);
co_yield m_context->argLoader()->getValue("c", s_c);
auto v_result
= Register::Value::Placeholder(m_context,
Register::Type::Vector,
{DataType::Float, PointerType::PointerGlobal},
1);
auto v_cond_result
= Register::Value::Placeholder(m_context,
Register::Type::Vector,
{DataType::Float, PointerType::PointerGlobal},
1);
auto v_a = Register::Value::Placeholder(
m_context, Register::Type::Vector, DataType::Float, 1);
auto v_b = Register::Value::Placeholder(
m_context, Register::Type::Vector, DataType::Float, 1);
auto v_c = Register::Value::Placeholder(
m_context, Register::Type::Vector, DataType::Float, 1);
auto v_r = Register::Value::Placeholder(
m_context, Register::Type::Vector, DataType::Float, 1);
auto v_tmp = Register::Value::Placeholder(
m_context, Register::Type::Vector, DataType::UInt8, 1);
auto s_r = Register::Value::Placeholder(m_context,
Register::Type::Scalar,
{DataType::Float},
wavefrontSize == 64 ? 2 : 1,
Register::AllocationOptions::FullyContiguous());
co_yield v_a->allocate();
co_yield v_b->allocate();
co_yield v_c->allocate();
co_yield v_r->allocate();
co_yield s_r->allocate();
co_yield v_result->allocate();
co_yield v_cond_result->allocate();
co_yield m_context->copier()->copy(v_result, s_result, "Move pointer");
co_yield m_context->copier()->copy(v_cond_result, s_cond_result, "Move pointer");
co_yield m_context->copier()->copy(v_a, s_a, "Move value");
co_yield m_context->copier()->copy(v_b, s_b, "Move value");
co_yield m_context->copier()->copy(v_c, s_c, "Move value");
co_yield generateOp<Expression::Add>(v_r, v_a, v_b);
co_yield m_context->mem()->storeGlobal(v_result, v_r, 0, 4);
co_yield generateOp<Expression::Subtract>(v_r, v_a, v_b);
co_yield m_context->mem()->store(MemoryInstructions::MemoryKind::Global,
v_result,
v_r,
Register::Value::Literal(4),
4);
co_yield generateOp<Expression::Multiply>(v_r, v_a, v_b);
co_yield m_context->mem()->store(MemoryInstructions::MemoryKind::Global,
v_result,
v_r,
Register::Value::Literal(8),
4);
co_yield generateOp<Expression::Negate>(v_r, v_a);
co_yield m_context->mem()->store(MemoryInstructions::MemoryKind::Global,
v_result,
v_r,
Register::Value::Literal(12),
4);
co_yield generateOp<Expression::MultiplyAdd>(v_r, v_a, v_b, v_c);
co_yield m_context->mem()->store(MemoryInstructions::MemoryKind::Global,
v_result,
v_r,
Register::Value::Literal(16),
4);
auto vcc = m_context->getVCC();
co_yield generate(vcc, v_c->expression() >= v_a->expression(), m_context);
co_yield generateOp<Expression::Conditional>(v_r, vcc, v_a, v_b);
co_yield m_context->mem()->store(MemoryInstructions::MemoryKind::Global,
v_result,
v_r,
Register::Value::Literal(20),
4);
co_yield generateOp<Expression::BitFieldExtract>(
v_tmp,
v_a,
Expression::BitFieldExtract{
.outputDataType = DataType::UInt8, .offset = 23, .width = 8});
co_yield_(Instruction("v_cvt_f32_ubyte0", {v_r}, {v_tmp}, {}, ""));
co_yield m_context->mem()->store(MemoryInstructions::MemoryKind::Global,
v_result,
v_r,
Register::Value::Literal(24),
4);
co_yield generateOp<Expression::GreaterThan>(s_r, v_a, v_b);
co_yield m_context->copier()->copy(
v_r, s_r->subset({0}), "Move result to vgpr to store.");
co_yield m_context->mem()->storeGlobal(v_cond_result, v_r->subset({0}), 0, 4);
co_yield generateOp<Expression::GreaterThanEqual>(s_r, v_a, v_b);
co_yield m_context->copier()->copy(
v_r, s_r->subset({0}), "Move result to vgpr to store.");
co_yield m_context->mem()->storeGlobal(v_cond_result, v_r->subset({0}), 4, 4);
co_yield generateOp<Expression::LessThan>(s_r, v_a, v_b);
co_yield m_context->copier()->copy(
v_r, s_r->subset({0}), "Move result to vgpr to store.");
co_yield m_context->mem()->storeGlobal(v_cond_result, v_r->subset({0}), 8, 4);
co_yield generateOp<Expression::LessThanEqual>(s_r, v_a, v_b);
co_yield m_context->copier()->copy(
v_r, s_r->subset({0}), "Move result to vgpr to store.");
co_yield m_context->mem()->storeGlobal(v_cond_result, v_r->subset({0}), 12, 4);
co_yield generateOp<Expression::Equal>(s_r, v_a, v_b);
co_yield m_context->copier()->copy(
v_r, s_r->subset({0}), "Move result to vgpr to store.");
co_yield m_context->mem()->storeGlobal(v_cond_result, v_r->subset({0}), 16, 4);
co_yield generateOp<Expression::NotEqual>(s_r, v_a, v_b);
co_yield m_context->copier()->copy(
v_r, s_r->subset({0}), "Move result to vgpr to store.");
co_yield m_context->mem()->storeGlobal(v_cond_result, v_r->subset({0}), 20, 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())
{
CommandKernel commandKernel;
commandKernel.setContext(m_context);
commandKernel.generateKernel();
auto d_result = make_shared_device<float>(7);
auto d_cond_result = make_shared_device<int>(6);
for(float a : TestValues::floatValues)
{
for(float b : TestValues::floatValues)
{
for(float c : TestValues::floatValues)
{
CommandArguments commandArgs = command->createArguments();
commandArgs.setArgument(resultTag, ArgumentType::Value, d_result.get());
commandArgs.setArgument(
condResultTag, ArgumentType::Value, d_cond_result.get());
commandArgs.setArgument(aTag, ArgumentType::Value, a);
commandArgs.setArgument(bTag, ArgumentType::Value, b);
commandArgs.setArgument(cTag, ArgumentType::Value, c);
commandKernel.launchKernel(commandArgs.runtimeArguments());
std::vector<float> result(7);
ASSERT_THAT(hipMemcpy(result.data(),
d_result.get(),
result.size() * sizeof(float),
hipMemcpyDefault),
HasHipSuccess(0));
std::vector<int> cond_result(6);
ASSERT_THAT(hipMemcpy(cond_result.data(),
d_cond_result.get(),
cond_result.size() * sizeof(int),
hipMemcpyDefault),
HasHipSuccess(0));
EXPECT_EQ(result[0], a + b);
EXPECT_EQ(result[1], a - b);
EXPECT_EQ(result[2], a * b);
EXPECT_EQ(result[3], -a);
float fma = std::fma(a, b, c);
if(fma != 0.0)
{
EXPECT_LT((result[4] - fma) / fma, 5.e-7)
<< "a: " << a << ", b: " << b << ", c: " << c;
;
}
EXPECT_EQ(result[5], c >= a ? a : b)
<< "a: " << a << ", b: " << b << ", c: " << c;
EXPECT_EQ(result[6], a == 0 ? 0 : std::ilogb(a) + 127);
;
EXPECT_EQ(cond_result[0], (a > b ? 1 : 0));
EXPECT_EQ(cond_result[1], (a >= b ? 1 : 0));
EXPECT_EQ(cond_result[2], (a < b ? 1 : 0));
EXPECT_EQ(cond_result[3], (a <= b ? 1 : 0));
EXPECT_EQ(cond_result[4], (a == b ? 1 : 0));
EXPECT_EQ(cond_result[5], (a != b ? 1 : 0));
}
}
}
}
else
{
std::vector<char> assembledKernel = m_context->instructions()->assemble();
EXPECT_GT(assembledKernel.size(), 0);
}
}
TEST_P(FPArithmeticTest, GPU_ArithUnaryFloat)
{
auto k = m_context->kernel();
k->setKernelDimensions(1);
auto command = std::make_shared<Command>();
auto resultTag = command->allocateTag();
auto resultExpr = std::make_shared<Expression::Expression>(command->allocateArgument(
{DataType::Float, PointerType::PointerGlobal}, resultTag, ArgumentType::Value));
auto aTag = command->allocateTag();
auto aExpr = std::make_shared<Expression::Expression>(command->allocateArgument(
{DataType::Float, PointerType::Value}, aTag, ArgumentType::Value));
auto one = std::make_shared<Expression::Expression>(1u);
auto zero = std::make_shared<Expression::Expression>(0u);
k->addArgument({"result",
{DataType::Float, PointerType::PointerGlobal},
DataDirection::WriteOnly,
resultExpr});
k->addArgument({"a", DataType::Float, DataDirection::ReadOnly, aExpr});
k->setWorkgroupSize({1, 1, 1});
k->setWorkitemCount({one, one, one});
k->setDynamicSharedMemBytes(zero);
m_context->schedule(k->preamble());
m_context->schedule(k->prolog());
auto const& gpu = m_context->targetArchitecture().target();
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::Float, PointerType::PointerGlobal},
1);
auto v_a = Register::Value::Placeholder(
m_context, Register::Type::Vector, DataType::Float, 1);
auto v_r = Register::Value::Placeholder(
m_context, Register::Type::Vector, DataType::Float, 1);
co_yield v_a->allocate();
co_yield v_r->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_a, s_a, "Move value");
co_yield generateOp<Expression::Exponential2>(v_r, v_a);
co_yield m_context->mem()->store(
MemoryInstructions::MemoryKind::Global, v_result, v_r, 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())
{
CommandKernel commandKernel;
commandKernel.setContext(m_context);
commandKernel.generateKernel();
auto d_result = make_shared_device<float>(1);
for(float a : TestValues::floatValues)
{
CommandArguments commandArgs = command->createArguments();
commandArgs.setArgument(resultTag, ArgumentType::Value, d_result.get());
commandArgs.setArgument(aTag, ArgumentType::Value, a);
commandKernel.launchKernel(commandArgs.runtimeArguments());
std::vector<float> result(1);
ASSERT_THAT(hipMemcpy(result.data(),
d_result.get(),
result.size() * sizeof(float),
hipMemcpyDefault),
HasHipSuccess(0));
EXPECT_EQ(result[0], (a > -126.0) ? std::exp2(a) : 0) << "a: " << a;
;
}
}
else
{
std::vector<char> assembledKernel = m_context->instructions()->assemble();
EXPECT_GT(assembledKernel.size(), 0);
}
}
TEST_P(FPArithmeticTest, GPU_ArithFMAMixed)
{
auto k = m_context->kernel();
k->setKernelDimensions(1);
auto command = std::make_shared<Command>();
auto resultTag = command->allocateTag();
auto resultExpr = std::make_shared<Expression::Expression>(command->allocateArgument(
{DataType::Float, PointerType::PointerGlobal}, resultTag, ArgumentType::Value));
auto aTag = command->allocateTag();
auto aExpr = std::make_shared<Expression::Expression>(command->allocateArgument(
{DataType::Float, PointerType::Value}, aTag, ArgumentType::Value));
auto bTag = command->allocateTag();
auto bExpr = std::make_shared<Expression::Expression>(command->allocateArgument(
{DataType::Half, PointerType::PointerGlobal}, bTag, ArgumentType::Value));
auto cTag = command->allocateTag();
auto cExpr = std::make_shared<Expression::Expression>(command->allocateArgument(
{DataType::Float, PointerType::PointerGlobal}, cTag, ArgumentType::Value));
auto one = std::make_shared<Expression::Expression>(1u);
auto zero = std::make_shared<Expression::Expression>(0u);
k->addArgument({"result",
{DataType::Float, PointerType::PointerGlobal},
DataDirection::WriteOnly,
resultExpr});
k->addArgument({"a", DataType::Float, DataDirection::ReadOnly, aExpr});
k->addArgument(
{"b", {DataType::Half, PointerType::PointerGlobal}, DataDirection::ReadOnly, bExpr});
k->addArgument(
{"c", {DataType::Float, PointerType::PointerGlobal}, DataDirection::ReadOnly, cExpr});
k->setWorkgroupSize({1, 1, 1});
k->setWorkitemCount({one, one, one});
k->setDynamicSharedMemBytes(zero);
m_context->schedule(k->preamble());
m_context->schedule(k->prolog());
auto const& gpu = m_context->targetArchitecture().target();
auto kb = [&]() -> Generator<Instruction> {
Register::ValuePtr s_result, s_a, s_b, s_c;
co_yield m_context->argLoader()->getValue("result", s_result);
co_yield m_context->argLoader()->getValue("a", s_a);
co_yield m_context->argLoader()->getValue("b", s_b);
co_yield m_context->argLoader()->getValue("c", s_c);
auto v_result
= Register::Value::Placeholder(m_context,
Register::Type::Vector,
{DataType::Int64, PointerType::PointerGlobal},
1);
auto vbPtr = Register::Value::Placeholder(m_context,
Register::Type::Vector,
{DataType::Int64, PointerType::PointerGlobal},
1);
auto vcPtr = Register::Value::Placeholder(m_context,
Register::Type::Vector,
{DataType::Int64, PointerType::PointerGlobal},
1);
auto v_b = Register::Value::Placeholder(
m_context, Register::Type::Vector, DataType::Halfx2, 1);
auto v_c = Register::Value::Placeholder(m_context,
Register::Type::Vector,
DataType::Float,
2,
Register::AllocationOptions::FullyContiguous());
auto v_r = Register::Value::Placeholder(m_context,
Register::Type::Vector,
DataType::Float,
2,
Register::AllocationOptions::FullyContiguous());
co_yield vbPtr->allocate();
co_yield vcPtr->allocate();
co_yield v_b->allocate();
co_yield v_c->allocate();
co_yield v_r->allocate();
co_yield v_result->allocate();
co_yield m_context->copier()->copy(v_result, s_result, "Move pointer");
co_yield m_context->copier()->copy(vbPtr, s_b, "Move Pointer");
co_yield m_context->copier()->copy(vcPtr, s_c, "Move Pointer");
co_yield m_context->mem()->loadGlobal(v_b, vbPtr, 0, 4);
co_yield m_context->mem()->loadGlobal(v_c, vcPtr, 0, 8);
// fp32 = fp32 * fp16 + fp32
co_yield generateOp<Expression::MultiplyAdd>(v_r, s_a, v_b, v_c);
co_yield m_context->mem()->store(
MemoryInstructions::MemoryKind::Global, v_result, v_r, 0, 8);
// fp32 = fp16 * fp16 + fp32
co_yield generateOp<Expression::MultiplyAdd>(v_r, v_b, v_b, v_c);
co_yield m_context->mem()->store(MemoryInstructions::MemoryKind::Global,
v_result,
v_r,
Register::Value::Literal(8),
8);
// fp32 = fp16 * fp32 + fp16
co_yield generateOp<Expression::MultiplyAdd>(v_r, v_b, v_c, v_b);
co_yield m_context->mem()->store(MemoryInstructions::MemoryKind::Global,
v_result,
v_r,
Register::Value::Literal(16),
8);
// fp32 = fp32 * fp16 + fp16
co_yield generateOp<Expression::MultiplyAdd>(v_r, s_a, v_b, v_b);
co_yield m_context->mem()->store(MemoryInstructions::MemoryKind::Global,
v_result,
v_r,
Register::Value::Literal(24),
8);
// fp32 = fp32 * fp32 + fp16
co_yield generateOp<Expression::MultiplyAdd>(v_r, s_a, v_c, v_b);
co_yield m_context->mem()->store(MemoryInstructions::MemoryKind::Global,
v_result,
v_r,
Register::Value::Literal(32),
8);
// fp32 = fp16 * fp32 + fp32
co_yield generateOp<Expression::MultiplyAdd>(v_r, v_b, v_c, v_c);
co_yield m_context->mem()->store(MemoryInstructions::MemoryKind::Global,
v_result,
v_r,
Register::Value::Literal(40),
8);
};
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())
{
CommandKernel commandKernel;
commandKernel.setContext(m_context);
commandKernel.generateKernel();
float a = 2.f;
std::vector<__half> b = {static_cast<__half>(1.), static_cast<__half>(2.)};
std::vector<float> c = {1.f, 2.f};
auto bDevice = make_shared_device<__half>(b);
auto cDevice = make_shared_device<float>(c);
auto dResult = make_shared_device<float>(12);
CommandArguments commandArgs = command->createArguments();
commandArgs.setArgument(resultTag, ArgumentType::Value, dResult.get());
commandArgs.setArgument(aTag, ArgumentType::Value, a);
commandArgs.setArgument(bTag, ArgumentType::Value, bDevice.get());
commandArgs.setArgument(cTag, ArgumentType::Value, cDevice.get());
commandKernel.launchKernel(commandArgs.runtimeArguments());
//6 different options
std::vector<float> result(12);
ASSERT_THAT(
hipMemcpy(
result.data(), dResult.get(), result.size() * sizeof(float), hipMemcpyDefault),
HasHipSuccess(0));
// fp32 * fp16 + f32
float fma1 = a * static_cast<float>(b[0]) + c[0];
float fma2 = a * static_cast<float>(b[1]) + c[1];
// fp16 * fp16 + fp32
float fma3 = static_cast<float>(b[0]) * static_cast<float>(b[0]) + c[0];
float fma4 = static_cast<float>(b[1]) * static_cast<float>(b[1]) + c[1];
// fp16 * fp32 + fp16
float fma5 = static_cast<float>(b[0]) * c[0] + static_cast<float>(b[0]);
float fma6 = static_cast<float>(b[1]) * c[1] + static_cast<float>(b[1]);
// fp32 * fp16 + fp16
float fma7 = a * static_cast<float>(b[0]) + static_cast<float>(b[0]);
float fma8 = a * static_cast<float>(b[1]) + static_cast<float>(b[1]);
// fp32 * fp32 + fp16
float fma9 = a * c[0] + static_cast<float>(b[0]);
float fma10 = a * c[1] + static_cast<float>(b[1]);
// fp16 * fp32 + fp32
float fma11 = static_cast<float>(b[0]) * c[0] + c[0];
float fma12 = static_cast<float>(b[1]) * c[1] + c[1];
EXPECT_LT(std::abs(result[0] - fma1) / fma1, 5.e-7);
EXPECT_LT(std::abs(result[1] - fma2) / fma2, 5.e-7);
EXPECT_LT(std::abs(result[2] - fma3) / fma3, 5.e-7);
EXPECT_LT(std::abs(result[3] - fma4) / fma4, 5.e-7);
EXPECT_LT(std::abs(result[4] - fma5) / fma5, 5.e-7);
EXPECT_LT(std::abs(result[5] - fma6) / fma6, 5.e-7);
EXPECT_LT(std::abs(result[6] - fma7) / fma7, 5.e-7);
EXPECT_LT(std::abs(result[7] - fma8) / fma8, 5.e-7);
EXPECT_LT(std::abs(result[8] - fma9) / fma9, 5.e-7);
EXPECT_LT(std::abs(result[9] - fma10) / fma10, 5.e-7);
EXPECT_LT(std::abs(result[10] - fma11) / fma11, 5.e-7);
EXPECT_LT(std::abs(result[11] - fma12) / fma12, 5.e-7);
}
else
{
std::vector<char> assembledKernel = m_context->instructions()->assemble();
EXPECT_GT(assembledKernel.size(), 0);
}
}
TEST_P(FPArithmeticTest, GPU_ArithDouble)
{
auto k = m_context->kernel();
k->setKernelDimensions(1);
auto command = std::make_shared<Command>();
auto resultTag = command->allocateTag();
auto resultExpr = std::make_shared<Expression::Expression>(command->allocateArgument(
{DataType::Double, PointerType::PointerGlobal}, resultTag, ArgumentType::Value));
auto condResultTag = command->allocateTag();
auto cond_resultExpr = std::make_shared<Expression::Expression>(command->allocateArgument(
{DataType::Int32, PointerType::PointerGlobal}, condResultTag, ArgumentType::Value));
auto aTag = command->allocateTag();
auto aExpr = std::make_shared<Expression::Expression>(command->allocateArgument(
{DataType::Double, PointerType::Value}, aTag, ArgumentType::Value));
auto bTag = command->allocateTag();
auto bExpr = std::make_shared<Expression::Expression>(command->allocateArgument(
{DataType::Double, PointerType::Value}, bTag, ArgumentType::Value));
auto one = std::make_shared<Expression::Expression>(1u);
auto zero = std::make_shared<Expression::Expression>(0u);
k->addArgument({"result",
{DataType::Double, PointerType::PointerGlobal},
DataDirection::WriteOnly,
resultExpr});
k->addArgument({"cond_result",
{DataType::Int32, PointerType::PointerGlobal},
DataDirection::WriteOnly,
cond_resultExpr});
k->addArgument({"a", DataType::Double, DataDirection::ReadOnly, aExpr});
k->addArgument({"b", DataType::Double, DataDirection::ReadOnly, bExpr});
k->setWorkgroupSize({1, 1, 1});
k->setWorkitemCount({one, one, one});
k->setDynamicSharedMemBytes(zero);
m_context->schedule(k->preamble());
m_context->schedule(k->prolog());
auto arch = m_context->targetArchitecture();
auto gpu = arch.target();
auto const wavefrontSize = arch.GetCapability(GPUCapability::DefaultWavefrontSize);
auto kb = [&]() -> Generator<Instruction> {
Register::ValuePtr s_result, s_cond_result, s_a, s_b;
co_yield m_context->argLoader()->getValue("result", s_result);
co_yield m_context->argLoader()->getValue("cond_result", s_cond_result);
co_yield m_context->argLoader()->getValue("a", s_a);
co_yield m_context->argLoader()->getValue("b", s_b);
auto v_result
= Register::Value::Placeholder(m_context,
Register::Type::Vector,
{DataType::Double, PointerType::PointerGlobal},
1);
auto v_cond_result
= Register::Value::Placeholder(m_context,
Register::Type::Vector,
{DataType::Double, PointerType::PointerGlobal},
1);
auto v_a = Register::Value::Placeholder(
m_context, Register::Type::Vector, DataType::Double, 1);
auto v_b = Register::Value::Placeholder(
m_context, Register::Type::Vector, DataType::Double, 1);
auto v_c = Register::Value::Placeholder(
m_context, Register::Type::Vector, DataType::Double, 1);
auto v_tmp = Register::Value::Placeholder(
m_context, Register::Type::Vector, DataType::UInt16, 1);
auto v_cond
= Register::Value::Placeholder(m_context,
Register::Type::Vector,
DataType::UInt32,
(wavefrontSize == 64) ? 2 : 1,
Register::AllocationOptions::FullyContiguous());
auto s_c = Register::Value::Placeholder(m_context,
Register::Type::Scalar,
DataType::UInt32,
(wavefrontSize == 64) ? 2 : 1,
Register::AllocationOptions::FullyContiguous());
co_yield v_a->allocate();
co_yield v_b->allocate();
co_yield v_c->allocate();
co_yield v_cond->allocate();
co_yield s_c->allocate();
co_yield v_result->allocate();
co_yield v_cond_result->allocate();
co_yield m_context->copier()->copy(v_result, s_result, "Move pointer");
co_yield m_context->copier()->copy(v_cond_result, s_cond_result, "Move pointer");
co_yield m_context->copier()->copy(v_a, s_a, "Move value");
co_yield m_context->copier()->copy(v_b, s_b, "Move value");
co_yield generateOp<Expression::Add>(v_c, v_a, v_b);
co_yield m_context->mem()->storeGlobal(v_result, v_c, 0, 8);
co_yield generateOp<Expression::Subtract>(v_c, v_a, v_b);
co_yield m_context->mem()->store(MemoryInstructions::MemoryKind::Global,
v_result,
v_c,
Register::Value::Literal(8),
8);
co_yield generateOp<Expression::Multiply>(v_c, v_a, v_b);
co_yield m_context->mem()->store(MemoryInstructions::MemoryKind::Global,
v_result,
v_c,
Register::Value::Literal(16),
8);
co_yield generateOp<Expression::Negate>(v_c, v_a);
co_yield m_context->mem()->store(MemoryInstructions::MemoryKind::Global,
v_result,
v_c,
Register::Value::Literal(24),
8);
auto vcc = m_context->getVCC();
co_yield generate(vcc, v_a->expression() >= v_b->expression(), m_context);
co_yield generateOp<Expression::Conditional>(v_c, vcc, v_a, v_b);
co_yield m_context->mem()->store(MemoryInstructions::MemoryKind::Global,
v_result,
v_c,
Register::Value::Literal(32),
8);
co_yield generateOp<Expression::BitFieldExtract>(
v_tmp,
v_a,
Expression::BitFieldExtract{
.outputDataType = DataType::UInt16, .offset = 52, .width = 11});
co_yield_(Instruction("v_cvt_f64_u32", {v_c}, {v_tmp->subset({0})}, {}, ""));
co_yield m_context->mem()->store(MemoryInstructions::MemoryKind::Global,
v_result,
v_c,
Register::Value::Literal(40),
8);
co_yield generateOp<Expression::GreaterThan>(s_c, v_a, v_b);
co_yield m_context->copier()->copy(v_cond, s_c, "Move result to vgpr to store.");
co_yield m_context->mem()->store(MemoryInstructions::MemoryKind::Global,
v_cond_result,
v_cond->subset({0}),
Register::Value::Literal(0),
4);
co_yield generateOp<Expression::GreaterThanEqual>(s_c, v_a, v_b);
co_yield m_context->copier()->copy(v_cond, s_c, "Move result to vgpr to store.");
co_yield m_context->mem()->store(MemoryInstructions::MemoryKind::Global,
v_cond_result,
v_cond->subset({0}),
Register::Value::Literal(4),
4);
co_yield generateOp<Expression::LessThan>(s_c, v_a, v_b);
co_yield m_context->copier()->copy(v_cond, s_c, "Move result to vgpr to store.");
co_yield m_context->mem()->store(MemoryInstructions::MemoryKind::Global,
v_cond_result,
v_cond->subset({0}),
Register::Value::Literal(8),
4);
co_yield generateOp<Expression::LessThanEqual>(s_c, v_a, v_b);
co_yield m_context->copier()->copy(v_cond, s_c, "Move result to vgpr to store.");
co_yield m_context->mem()->store(MemoryInstructions::MemoryKind::Global,
v_cond_result,
v_cond->subset({0}),
Register::Value::Literal(12),
4);
co_yield generateOp<Expression::Equal>(s_c, v_a, v_b);
co_yield m_context->copier()->copy(v_cond, s_c, "Move result to vgpr to store.");
co_yield m_context->mem()->store(MemoryInstructions::MemoryKind::Global,
v_cond_result,
v_cond->subset({0}),
Register::Value::Literal(16),
4);
co_yield generateOp<Expression::NotEqual>(s_c, v_a, v_b);
co_yield m_context->copier()->copy(v_cond, s_c, "Move result to vgpr to store.");
co_yield m_context->mem()->store(MemoryInstructions::MemoryKind::Global,
v_cond_result,
v_cond->subset({0}),
Register::Value::Literal(20),
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())
{
CommandKernel commandKernel;
commandKernel.setContext(m_context);
commandKernel.generateKernel();
auto d_result = make_shared_device<double>(6);
auto d_cond_result = make_shared_device<int>(6);
for(double a : TestValues::doubleValues)
{
for(double b : TestValues::doubleValues)
{
CommandArguments commandArgs = command->createArguments();
commandArgs.setArgument(resultTag, ArgumentType::Value, d_result.get());
commandArgs.setArgument(
condResultTag, ArgumentType::Value, d_cond_result.get());
commandArgs.setArgument(aTag, ArgumentType::Value, a);
commandArgs.setArgument(bTag, ArgumentType::Value, b);
commandKernel.launchKernel(commandArgs.runtimeArguments());
std::vector<double> result(6);
ASSERT_THAT(hipMemcpy(result.data(),
d_result.get(),
result.size() * sizeof(double),
hipMemcpyDefault),
HasHipSuccess(0));
std::vector<int> cond_result(6);
ASSERT_THAT(hipMemcpy(cond_result.data(),
d_cond_result.get(),
cond_result.size() * sizeof(int),
hipMemcpyDefault),
HasHipSuccess(0));
EXPECT_EQ(result[0], a + b);
EXPECT_EQ(result[1], a - b);
EXPECT_EQ(result[2], a * b);
EXPECT_EQ(result[3], -a);
EXPECT_EQ(result[4], a >= b ? a : b);
EXPECT_EQ(result[5], a == 0 ? 0 : std::ilogb(a) + 1023);
EXPECT_EQ(cond_result[0], (a > b ? 1 : 0));
EXPECT_EQ(cond_result[1], (a >= b ? 1 : 0));
EXPECT_EQ(cond_result[2], (a < b ? 1 : 0));
EXPECT_EQ(cond_result[3], (a <= b ? 1 : 0));
EXPECT_EQ(cond_result[4], (a == b ? 1 : 0));
EXPECT_EQ(cond_result[5], (a != b ? 1 : 0));
}
}
}
else
{
std::vector<char> assembledKernel = m_context->instructions()->assemble();
EXPECT_GT(assembledKernel.size(), 0);
}
}
TEST_P(FPArithmeticTest, GPU_NullChecks)
{
(void)(::testing::GTEST_FLAG(death_test_style) = "threadsafe");
auto kb_null = [&]() -> Generator<Instruction> {
co_yield generateOp<Expression::Subtract>(nullptr, nullptr, nullptr);
};
ASSERT_THROW(m_context->schedule(kb_null()), FatalError);
auto kb32 = [&]() -> Generator<Instruction> {
auto v_c = Register::Value::Placeholder(
m_context, Register::Type::Vector, DataType::Int32, 1);
co_yield v_c->allocate();
co_yield generateOp<Expression::Subtract>(v_c, nullptr, nullptr);
};
ASSERT_THROW(m_context->schedule(kb32()), FatalError);
auto kb64 = [&]() -> Generator<Instruction> {
auto v_c = Register::Value::Placeholder(
m_context, Register::Type::Vector, DataType::Int64, 1);
co_yield v_c->allocate();
co_yield generateOp<Expression::Subtract>(v_c, nullptr, nullptr);
};
ASSERT_THROW(m_context->schedule(kb64()), FatalError);
auto kb_dst = [&]() -> Generator<Instruction> {
auto v_a = Register::Value::Placeholder(
m_context, Register::Type::Vector, DataType::Int64, 1);
auto v_b = Register::Value::Placeholder(
m_context, Register::Type::Vector, DataType::Int64, 1);
co_yield v_a->allocate();
co_yield v_b->allocate();
co_yield generateOp<Expression::Subtract>(nullptr, v_a, v_b);
};
ASSERT_THROW(m_context->schedule(kb_dst()), FatalError);
}
INSTANTIATE_TEST_SUITE_P(ArithmeticTests, FPArithmeticTest, supportedISATuples());
}