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

989 lines
36 KiB
C++

// Copyright Advanced Micro Devices, Inc., or its affiliates.
// SPDX-License-Identifier: MIT
#include <cmath>
#include <memory>
#include "CustomMatchers.hpp"
#include "CustomSections.hpp"
#include "TestContext.hpp"
#include "TestKernels.hpp"
#include <common/SourceMatcher.hpp>
#include <common/TestValues.hpp>
#include <rocRoller/AssemblyKernel.hpp>
#include <rocRoller/CodeGen/ArgumentLoader.hpp>
#include <rocRoller/CodeGen/CopyGenerator.hpp>
#include <rocRoller/CodeGen/Instruction.hpp>
#include <rocRoller/CodeGen/MemoryInstructions.hpp>
#include <rocRoller/Expression.hpp>
#include <rocRoller/ExpressionTransformations.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/matchers/catch_matchers.hpp>
#include <catch2/matchers/catch_matchers_floating_point.hpp>
#include <catch2/matchers/catch_matchers_string.hpp>
using namespace rocRoller;
namespace ExpressionTest
{
enum class DestRegisterStatus
{
NullPointer = 0,
Placeholder,
Allocated,
Count
};
std::ostream& operator<<(std::ostream& stream, DestRegisterStatus status)
{
switch(status)
{
case DestRegisterStatus::NullPointer:
return stream << "NullPointer";
case DestRegisterStatus::Placeholder:
return stream << "Placeholder";
case DestRegisterStatus::Allocated:
return stream << "Allocated";
case DestRegisterStatus::Count:
return stream << "Count";
}
Throw<rocRoller::FatalError>("Bad value!");
}
struct TernaryExpressionKernel : public AssemblyTestKernel
{
using ExpressionFunc = std::function<Expression::ExpressionPtr(
Expression::ExpressionPtr, Expression::ExpressionPtr, Expression::ExpressionPtr)>;
TernaryExpressionKernel(ContextPtr context,
ExpressionFunc func,
DataType resultType,
DataType aType,
DataType bType,
DataType cType,
Register::Type regType = Register::Type::Vector,
DestRegisterStatus destRegMode = DestRegisterStatus::Placeholder)
: AssemblyTestKernel(context)
, m_func(func)
, m_resultType(resultType)
, m_aType(aType)
, m_bType(bType)
, m_cType(cType)
, m_regType(regType)
, m_destRegMode(destRegMode)
{
}
void generate() override
{
auto k = m_context->kernel();
k->addArgument(
{"result", {m_resultType, PointerType::PointerGlobal}, DataDirection::WriteOnly});
k->addArgument({"a", m_aType});
k->addArgument({"b", m_bType});
k->addArgument({"c", m_cType});
m_context->schedule(k->preamble());
m_context->schedule(k->prolog());
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,
{m_resultType, PointerType::PointerGlobal},
1);
auto v_a = s_a->placeholder(m_regType, {});
auto v_b = s_b->placeholder(m_regType, {});
auto v_c = s_c->placeholder(m_regType, {});
auto a = v_a->expression();
auto b = v_b->expression();
auto c = v_c->expression();
auto expr = m_func(a, b, c);
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 m_context->copier()->copy(v_b, s_b, "Move value");
co_yield m_context->copier()->copy(v_c, s_c, "Move value");
Register::ValuePtr resultValue, expressionDest;
if(m_destRegMode == DestRegisterStatus::Placeholder)
{
// resultValue and expressionDest are both pointing to the same object, which is an unallocated register.
expressionDest
= Register::Value::Placeholder(m_context, m_regType, m_resultType, 1);
resultValue = expressionDest;
}
else if(m_destRegMode == DestRegisterStatus::Allocated)
{
// resultValue and expressionDest pointing to the different objects, which are allocated and aliased to the same register.
expressionDest
= Register::Value::Placeholder(m_context, m_regType, m_resultType, 1);
expressionDest->allocateNow();
resultValue = expressionDest->element({0});
REQUIRE(expressionDest != resultValue);
}
co_yield Expression::generate(expressionDest, expr, m_context);
if(m_destRegMode == DestRegisterStatus::NullPointer)
{
// both pointers were null, but expressionDest isn't any more.
REQUIRE(expressionDest != nullptr);
REQUIRE(resultValue == nullptr);
resultValue = expressionDest;
}
else if(m_destRegMode == DestRegisterStatus::Placeholder)
{
// the pointers should still equal each other.
REQUIRE(expressionDest == resultValue);
}
else if(m_destRegMode == DestRegisterStatus::Allocated)
{
// the pointers should still not equal each other.
REQUIRE(expressionDest != resultValue);
}
Register::ValuePtr v_resultValue;
if(m_regType == Register::Type::Vector)
v_resultValue = resultValue;
else
{
v_resultValue = resultValue->placeholder(Register::Type::Vector, {});
co_yield Expression::generate(
v_resultValue, resultValue->expression(), m_context);
}
co_yield m_context->mem()->storeGlobal(
v_result, v_resultValue, 0, DataTypeInfo::Get(m_resultType).elementBytes);
};
m_context->schedule(kb());
m_context->schedule(k->postamble());
m_context->schedule(k->amdgpu_metadata());
}
protected:
ExpressionFunc m_func;
DataType m_resultType, m_aType, m_bType, m_cType;
Register::Type m_regType;
DestRegisterStatus m_destRegMode;
};
TEST_CASE("Run ternary expression kernel 1", "[expression][ternary][fma][gpu]")
{
auto context = TestContext::ForTestDevice();
auto expr = [](Expression::ExpressionPtr a,
Expression::ExpressionPtr b,
Expression::ExpressionPtr c) { //
return a * b + c;
};
TernaryExpressionKernel kernel(context.get(),
expr,
DataType::Float,
DataType::Float,
DataType::Float,
DataType::Float);
auto d_result = make_shared_device<float>();
for(float a : TestValues::floatValues)
{
for(float b : TestValues::floatValues)
{
for(float c : TestValues::floatValues)
{
float r = std::fma(a, b, c);
CAPTURE(a, b, c, r);
kernel({}, d_result.get(), a, b, c);
REQUIRE_THAT(d_result, HasDeviceScalar(Catch::Matchers::WithinULP(r, 1)));
}
}
}
}
TEST_CASE("Assemble ternary expression kernel 1", "[expression][codegen]")
{
SUPPORTED_ARCH_SECTION(arch)
{
auto destMode = GENERATE(DestRegisterStatus::NullPointer,
DestRegisterStatus::Placeholder,
DestRegisterStatus::Allocated);
DYNAMIC_SECTION(destMode)
{
auto context = TestContext::ForTarget(arch);
auto expr = [](Expression::ExpressionPtr a,
Expression::ExpressionPtr b,
Expression::ExpressionPtr c) { //
return a * b + c;
};
TernaryExpressionKernel kernel(context.get(),
expr,
DataType::Float,
DataType::Float,
DataType::Float,
DataType::Float);
CHECK(kernel.getAssembledKernel().size() > 0);
using namespace Catch::Matchers;
CHECK_THAT(context.output(), ContainsSubstring("v_fma_f32"));
}
}
}
TEST_CASE("Run ternary conversion expression kernel 1", "[expression][conversions][gpu]")
{
auto regType = GENERATE(Register::Type::Scalar, Register::Type::Vector);
DYNAMIC_SECTION(regType)
{
auto destMode = GENERATE(DestRegisterStatus::NullPointer,
DestRegisterStatus::Placeholder,
DestRegisterStatus::Allocated);
DYNAMIC_SECTION(destMode)
{
auto context = TestContext::ForTestDevice({}, regType, destMode);
auto expr = [](Expression::ExpressionPtr a,
Expression::ExpressionPtr b,
Expression::ExpressionPtr c) {
namespace Ex = Expression;
return convert<DataType::UInt64>(convert<DataType::UInt64>(
((b << Ex::literal(3)) * a) >> Ex::literal(4)))
+ convert(DataType::Int64, c);
};
TernaryExpressionKernel kernel(context.get(),
expr,
DataType::UInt64,
DataType::Int32,
DataType::Int64,
DataType::UInt64,
regType,
destMode);
auto d_result = make_shared_device<uint64_t>();
for(auto a : TestValues::int32Values)
{
for(auto b : TestValues::int64Values)
{
for(auto c : TestValues::uint64Values)
{
auto r = static_cast<uint64_t>(((b << 3) * a) >> 4)
+ static_cast<int64_t>(c);
CAPTURE(a, b, c, r);
kernel({}, d_result.get(), a, b, c);
REQUIRE_THAT(d_result, HasDeviceScalarEqualTo(r));
}
}
}
}
}
}
TEST_CASE("Assemble ternary conversion expression kernel 1",
"[expression][conversions][codegen]")
{
SUPPORTED_ARCH_SECTION(arch)
{
auto destMode = GENERATE(DestRegisterStatus::NullPointer,
DestRegisterStatus::Placeholder,
DestRegisterStatus::Allocated);
DYNAMIC_SECTION(destMode)
{
auto context = TestContext::ForTarget(arch);
auto expr = [](Expression::ExpressionPtr a,
Expression::ExpressionPtr b,
Expression::ExpressionPtr c) {
namespace Ex = Expression;
return convert<DataType::UInt64>(((b << Ex::literal(3)) * a) >> Ex::literal(4))
+ convert(DataType::Int64, c);
};
TernaryExpressionKernel kernel(context.get(),
expr,
DataType::UInt64,
DataType::Int32,
DataType::Int64,
DataType::UInt64);
CHECK(kernel.getAssembledKernel().size() > 0);
using namespace Catch::Matchers;
CHECK_THAT(context.output(), !ContainsSubstring("v_fma_f32"));
}
}
}
TEST_CASE("Run ternary conversion expression kernel 2", "[expression][conversions][gpu]")
{
auto context = TestContext::ForTestDevice();
auto expr = [](Expression::ExpressionPtr a,
Expression::ExpressionPtr b,
Expression::ExpressionPtr c) {
namespace Ex = Expression;
return ((convert<DataType::UInt64>(a) * Ex::literal(23)) + b)
- convert(DataType::UInt64, c);
};
TernaryExpressionKernel kernel(context.get(),
expr,
DataType::UInt64,
DataType::UInt32,
DataType::UInt32,
DataType::Int64);
auto d_result = make_shared_device<uint64_t>();
for(auto a : TestValues::uint32Values)
{
for(auto b : TestValues::uint32Values)
{
for(auto c : TestValues::int64Values)
{
auto r = ((static_cast<uint64_t>(a) * 23) + b) - static_cast<uint64_t>(c);
CAPTURE(a, b, c, r);
kernel({}, d_result.get(), a, b, c);
REQUIRE_THAT(d_result, HasDeviceScalarEqualTo(r));
}
}
}
}
TEST_CASE("Assemble ternary conversion expression kernel 2",
"[expression][conversions][codegen]")
{
SUPPORTED_ARCH_SECTION(arch)
{
auto context = TestContext::ForTarget(arch);
auto expr = [](Expression::ExpressionPtr a,
Expression::ExpressionPtr b,
Expression::ExpressionPtr c) {
namespace Ex = Expression;
return ((convert<DataType::UInt64>(a) * Ex::literal(23)) + b)
- convert(DataType::UInt64, c);
};
TernaryExpressionKernel kernel(context.get(),
expr,
DataType::UInt64,
DataType::UInt32,
DataType::UInt32,
DataType::Int64);
CHECK(kernel.getAssembledKernel().size() > 0);
using namespace Catch::Matchers;
CHECK_THAT(context.output(), !ContainsSubstring("v_fma_f32"));
}
}
TEST_CASE("Run ternary conversion expression kernel 3", "[expression][conversions][gpu]")
{
auto context = TestContext::ForTestDevice();
auto expr = [](Expression::ExpressionPtr a,
Expression::ExpressionPtr b,
Expression::ExpressionPtr c) {
namespace Ex = Expression;
return ((convert(DataType::Int32, b) + a) >> Ex::literal(4))
+ convert(DataType::Int32, c);
};
TernaryExpressionKernel kernel(context.get(),
expr,
DataType::Int32,
DataType::Int32,
DataType::Int64,
DataType::UInt32);
auto d_result = make_shared_device<int32_t>();
for(auto a : TestValues::int32Values)
{
for(auto b : TestValues::int64Values)
{
for(auto c : TestValues::uint32Values)
{
auto r = ((static_cast<int32_t>(b) + a) >> 4) + static_cast<int32_t>(c);
CAPTURE(a, b, c, r);
kernel({}, d_result.get(), a, b, c);
REQUIRE_THAT(d_result, HasDeviceScalarEqualTo(r));
}
}
}
}
TEST_CASE("Assemble ternary conversion expression kernel 3",
"[expression][conversions][codegen]")
{
SUPPORTED_ARCH_SECTION(arch)
{
auto context = TestContext::ForTarget(arch);
auto expr = [](Expression::ExpressionPtr a,
Expression::ExpressionPtr b,
Expression::ExpressionPtr c) {
namespace Ex = Expression;
return ((convert(DataType::Int32, b) + a) >> Ex::literal(4))
+ convert(DataType::Int32, c);
};
TernaryExpressionKernel kernel(context.get(),
expr,
DataType::Int32,
DataType::Int32,
DataType::Int64,
DataType::UInt32);
CHECK(kernel.getAssembledKernel().size() > 0);
using namespace Catch::Matchers;
CHECK_THAT(context.output(), !ContainsSubstring("v_fma_f32"));
}
}
TEST_CASE("Run ternary conversion expression kernel 4", "[expression][conversions][gpu]")
{
auto context = TestContext::ForTestDevice();
auto expr = [](Expression::ExpressionPtr a,
Expression::ExpressionPtr b,
Expression::ExpressionPtr c) {
namespace Ex = Expression;
return logicalShiftR(convert(DataType::UInt32, b) + a, Ex::literal(4))
+ convert(DataType::UInt32, c);
};
TernaryExpressionKernel kernel(context.get(),
expr,
DataType::UInt32,
DataType::UInt32,
DataType::UInt64,
DataType::Int32);
auto d_result = make_shared_device<uint32_t>();
for(auto a : TestValues::uint32Values)
{
for(auto b : TestValues::uint64Values)
{
for(auto c : TestValues::int32Values)
{
auto r = ((static_cast<uint32_t>(b) + a) >> 4) + static_cast<uint32_t>(c);
CAPTURE(a, b, c, r);
kernel({}, d_result.get(), a, b, c);
REQUIRE_THAT(d_result, HasDeviceScalarEqualTo(r));
}
}
}
}
TEST_CASE("Assemble ternary conversion expression kernel 4",
"[expression][conversions][codegen]")
{
SUPPORTED_ARCH_SECTION(arch)
{
auto context = TestContext::ForTarget(arch);
auto expr = [](Expression::ExpressionPtr a,
Expression::ExpressionPtr b,
Expression::ExpressionPtr c) {
namespace Ex = Expression;
return logicalShiftR(convert(DataType::UInt32, b) + a, Ex::literal(4))
+ convert(DataType::UInt32, c);
};
TernaryExpressionKernel kernel(context.get(),
expr,
DataType::UInt32,
DataType::UInt32,
DataType::UInt64,
DataType::Int32);
CHECK(kernel.getAssembledKernel().size() > 0);
using namespace Catch::Matchers;
CHECK_THAT(context.output(), !ContainsSubstring("v_fma_f32"));
}
}
TEST_CASE("Run ternary conversion expression kernel 5", "[expression][conversions][gpu]")
{
auto regType = GENERATE(Register::Type::Scalar, Register::Type::Vector);
DYNAMIC_SECTION(regType)
{
auto destMode = GENERATE(DestRegisterStatus::NullPointer,
DestRegisterStatus::Placeholder,
DestRegisterStatus::Allocated);
DYNAMIC_SECTION(destMode)
{
auto context = TestContext::ForTestDevice({}, regType, destMode);
auto expr = [](Expression::ExpressionPtr a,
Expression::ExpressionPtr b,
Expression::ExpressionPtr c) {
namespace Ex = Expression;
return convert<DataType::UInt64>(a);
};
TernaryExpressionKernel kernel(context.get(),
expr,
DataType::UInt64,
DataType::UInt64,
DataType::UInt64,
DataType::UInt64,
regType,
destMode);
auto d_result = make_shared_device<uint64_t>();
for(auto a : TestValues::uint64Values)
{
for(auto b : TestValues::uint64Values)
{
for(auto c : TestValues::uint64Values)
{
auto r = a;
CAPTURE(a, b, c, r);
kernel({}, d_result.get(), a, b, c);
REQUIRE_THAT(d_result, HasDeviceScalarEqualTo(r));
}
}
}
}
}
}
TEST_CASE("Expression implicitly converts ShiftL 1", "[expression][conversions][gpu]")
{
auto context = TestContext::ForTestDevice();
auto expr = [](Expression::ExpressionPtr a,
Expression::ExpressionPtr b,
Expression::ExpressionPtr c) {
namespace Ex = Expression;
return (a + b) << c;
};
TernaryExpressionKernel kernel(context.get(),
expr,
DataType::UInt64,
DataType::UInt32,
DataType::UInt32,
DataType::Int32);
auto d_result = make_shared_device<uint64_t>();
for(auto a : TestValues::uint32Values)
{
for(auto b : TestValues::uint32Values)
{
for(auto c : TestValues::int32Values)
{
uint64_t r = static_cast<uint64_t>(a + b) << c;
CAPTURE(a, b, c, r);
kernel({}, d_result.get(), a, b, c);
REQUIRE_THAT(d_result, HasDeviceScalarEqualTo(r));
}
}
}
}
// This function must be defined in a different compilation unit or the compiler will
// realize that it does nothing.
void potentiallyMutate(int32_t& x);
TEST_CASE("Expression implicitly converts ShiftL 2", "[expression][conversions][gpu]")
{
auto context = TestContext::ForTestDevice();
auto expr = [](Expression::ExpressionPtr a,
Expression::ExpressionPtr b,
Expression::ExpressionPtr c) {
namespace Ex = Expression;
return (a + b) << c;
};
TernaryExpressionKernel kernel(context.get(),
expr,
DataType::Int64,
DataType::Int32,
DataType::Int32,
DataType::Int32);
auto d_result = make_shared_device<int64_t>();
for(auto a : TestValues::int32Values)
{
for(auto b : TestValues::int32Values)
{
for(int32_t c : TestValues::int32Values)
{
int32_t tmp = a + b;
// If this is not placed here, the compiler will directly calculate a + b
// as int64_t, and we will not have the same overflow behaviour that we
// have on the GPU.
potentiallyMutate(tmp);
int64_t r = static_cast<int64_t>(tmp) << c;
CAPTURE(a, b, c, r, tmp);
kernel({}, d_result.get(), a, b, c);
REQUIRE_THAT(d_result, HasDeviceScalarEqualTo(r));
}
}
}
}
TEST_CASE("Expression implicitly converts ShiftR 1", "[expression][conversions][gpu]")
{
auto context = TestContext::ForTestDevice();
auto expr = [](Expression::ExpressionPtr a,
Expression::ExpressionPtr b,
Expression::ExpressionPtr c) {
namespace Ex = Expression;
return Ex::logicalShiftR((a + b), c);
};
TernaryExpressionKernel kernel(context.get(),
expr,
DataType::UInt64,
DataType::UInt32,
DataType::UInt32,
DataType::Int32);
auto d_result = make_shared_device<uint64_t>();
for(auto a : TestValues::uint32Values)
{
for(auto b : TestValues::uint32Values)
{
for(auto c : TestValues::int32Values)
{
uint64_t r = static_cast<uint64_t>(a + b) >> c;
CAPTURE(a, b, c, r);
kernel({}, d_result.get(), a, b, c);
REQUIRE_THAT(d_result, HasDeviceScalarEqualTo(r));
}
}
}
}
TEST_CASE("Run expression implicitly converts ShiftR 2", "[expression][conversions][gpu]")
{
auto context = TestContext::ForTestDevice();
auto expr = [](Expression::ExpressionPtr a,
Expression::ExpressionPtr b,
Expression::ExpressionPtr c) {
namespace Ex = Expression;
return (a + b) >> c;
};
TernaryExpressionKernel kernel(context.get(),
expr,
DataType::UInt64,
DataType::UInt32,
DataType::UInt32,
DataType::Int32);
auto d_result = make_shared_device<uint64_t>();
for(auto a : TestValues::uint32Values)
{
for(auto b : TestValues::uint32Values)
{
for(auto c : TestValues::int32Values)
{
uint64_t r = static_cast<uint64_t>(a + b) >> c;
CAPTURE(a, b, c, r);
kernel({}, d_result.get(), a, b, c);
REQUIRE_THAT(d_result, HasDeviceScalarEqualTo(r));
}
}
}
}
TEST_CASE("Run expression implicitly converts ShiftR 3", "[expression][conversions][gpu]")
{
auto context = TestContext::ForTestDevice();
auto expr = [](Expression::ExpressionPtr a,
Expression::ExpressionPtr b,
Expression::ExpressionPtr c) {
namespace Ex = Expression;
return (a + b) >> c;
};
TernaryExpressionKernel kernel(context.get(),
expr,
DataType::Int64,
DataType::Int32,
DataType::Int32,
DataType::Int32);
auto d_result = make_shared_device<int64_t>();
for(int32_t a : TestValues::int32Values)
{
for(int32_t b : TestValues::int32Values)
{
for(int32_t c : TestValues::int32Values)
{
int32_t tmp1 = a + b;
potentiallyMutate(tmp1);
int64_t tmp2 = tmp1;
int64_t r = tmp2 >> c;
// if(a == 1141374976 && b == 1141374976)
// {
// // 1141374976 has a 1 in the second-from-MSB.
// // 1141374976 + 1141374976 will overflow int32.
// // In release mode if tmp1 is never used, the compiler will
// // do 64-bit addition, which won't overflow and the result
// // will be different from the GPU code which doesn't have
// // this optimization.
// // TODO: Find change compiler flags in this file to prevent
// // this optimization?
// CHECK(tmp1 < 0);
// CHECK(tmp2 < 0);
// CHECK(r < 0);
// }
// int64_t r = static_cast<int64_t>(a + b) >> c;
CAPTURE(a, b, c, r);
kernel({}, d_result.get(), a, b, c);
REQUIRE_THAT(d_result, HasDeviceScalarEqualTo(r));
}
}
}
}
TEST_CASE("Run ConvertPropagation kernel ternary",
"[expression][expression-transformation][gpu]")
{
using InputType = int64_t;
using ResultType = int32_t;
using ShiftType = int;
constexpr auto inputType = TypeInfo<InputType>::Var.dataType;
constexpr auto resultType = TypeInfo<ResultType>::Var.dataType;
constexpr auto shiftType = TypeInfo<ShiftType>::Var.dataType;
using CPUExpressionFunc = std::function<ResultType(InputType, InputType, ShiftType)>;
auto [gpu_expr, cpu_expr] = GENERATE(
as<std::pair<TernaryExpressionKernel::ExpressionFunc, CPUExpressionFunc>>{},
std::make_pair(
[](auto a, auto b, auto c) {
return Expression::convert(resultType, (a + b) << c);
},
[](auto a, auto b, auto c) { return static_cast<ResultType>((a + b) << c); }));
TernaryExpressionKernel kernel(TestContext::ForTestDevice().get(),
gpu_expr,
resultType,
inputType,
inputType,
shiftType);
auto result = make_shared_device<int32_t>();
for(auto a : TestValues::int64Values)
{
for(auto b : TestValues::int64Values)
{
for(auto c : TestValues::shiftValues)
{
CAPTURE(a, b, c);
int32_t r = cpu_expr(a, b, c);
kernel({}, result.get(), a, b, c);
CHECK_THAT(result, HasDeviceScalarEqualTo(r));
}
}
}
}
struct Less
{
static constexpr auto name = "<";
static constexpr auto compare(auto a, auto b)
{
return a < b;
}
};
struct LessEqual
{
static constexpr auto name = "<=";
static constexpr auto compare(auto a, auto b)
{
return a <= b;
}
};
struct Equal
{
static constexpr auto name = "==";
static constexpr auto compare(auto a, auto b)
{
return a == b;
}
};
struct GreaterEqual
{
static constexpr auto name = ">=";
static constexpr auto compare(auto a, auto b)
{
return a >= b;
}
};
struct Greater
{
static constexpr auto name = ">";
static constexpr auto compare(auto a, auto b)
{
return a > b;
}
};
TEST_CASE("Run expression Conditional", "[expression][conditional][gpu]")
{
auto regType = GENERATE(Register::Type::Scalar, Register::Type::Vector);
DYNAMIC_SECTION(regType)
{
auto testFunc = [&](auto oper, auto dtype) {
using Operator = std::decay_t<decltype(oper)>;
using DType = std::decay_t<decltype(dtype)>;
auto dataType = rocRoller::TypeInfo<DType>::Var.dataType;
DYNAMIC_SECTION(Operator::name << ", " << dataType)
{
auto context
= TestContext::ForTestDevice({}, regType, Operator::name, dataType);
auto expr = [](Expression::ExpressionPtr a,
Expression::ExpressionPtr b,
Expression::ExpressionPtr c) {
namespace Ex = Expression;
auto cond = Operator::compare(a, b);
// auto d = a > b;
return Ex::conditional(cond, b, c);
};
TernaryExpressionKernel kernel(
context.get(), expr, dataType, dataType, dataType, dataType, regType);
auto d_result = make_shared_device<DType>();
for(auto a : TestValues::ByType<DType>::values)
{
for(auto b : TestValues::ByType<DType>::values)
{
for(auto c : TestValues::ByType<DType>::values)
{
auto cond = Operator::compare(a, b);
auto r = cond ? b : c;
CAPTURE(a, b, c, r);
kernel({}, d_result.get(), a, b, c);
REQUIRE_THAT(d_result, HasDeviceScalarEqualTo(r));
}
}
}
}
};
testFunc(Less{}, uint32_t{});
testFunc(LessEqual{}, uint32_t{});
testFunc(Equal{}, uint32_t{});
testFunc(GreaterEqual{}, uint32_t{});
testFunc(Greater{}, uint32_t{});
testFunc(Less{}, int32_t{});
testFunc(LessEqual{}, int32_t{});
testFunc(Equal{}, int32_t{});
testFunc(GreaterEqual{}, int32_t{});
testFunc(Greater{}, int32_t{});
testFunc(Less{}, uint64_t{});
testFunc(LessEqual{}, uint64_t{});
testFunc(Equal{}, uint64_t{});
testFunc(GreaterEqual{}, uint64_t{});
testFunc(Greater{}, uint64_t{});
testFunc(Less{}, int64_t{});
testFunc(LessEqual{}, int64_t{});
testFunc(Equal{}, int64_t{});
testFunc(GreaterEqual{}, int64_t{});
testFunc(Greater{}, int64_t{});
if(regType == Register::Type::Vector)
{
testFunc(Less{}, float{});
testFunc(LessEqual{}, float{});
testFunc(Equal{}, float{});
testFunc(GreaterEqual{}, float{});
testFunc(Greater{}, float{});
testFunc(Less{}, double{});
testFunc(LessEqual{}, double{});
testFunc(Equal{}, double{});
testFunc(GreaterEqual{}, double{});
testFunc(Greater{}, double{});
}
}
}
}