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

1213 lines
49 KiB
C++

// Copyright Advanced Micro Devices, Inc., or its affiliates.
// SPDX-License-Identifier: MIT
#include "GPUContextFixture.hpp"
#include "GenericContextFixture.hpp"
#include <rocRoller/AssemblyKernel.hpp>
#include <rocRoller/ExpressionTransformations.hpp>
#include <rocRoller/KernelGraph/CoordinateGraph/CoordinateGraph.hpp>
#include <rocRoller/KernelGraph/CoordinateGraph/Dimension.hpp>
#include <rocRoller/KernelGraph/CoordinateGraph/Transformer.hpp>
#include <rocRoller/KernelGraph/Utils.hpp>
#include <rocRoller/Serialization/Variant.hpp>
using namespace rocRoller;
using namespace KernelGraph::CoordinateGraph;
namespace rocRollerTest
{
class CoordinateGraphTest : public GenericContextFixture
{
public:
Expression::FastArithmetic fastArith{m_context};
void SetUp() override
{
GenericContextFixture::SetUp();
fastArith = Expression::FastArithmetic(m_context);
}
};
TEST_F(CoordinateGraphTest, SerializationTypePath)
{
CoordinateGraph::Element el = User{};
EXPECT_EQ("Node.User", Serialization::typePath(el));
el = Flatten{};
EXPECT_EQ("Edge.Transform.Flatten", Serialization::typePath(el));
el = Adhoc{"BlockNumber"};
EXPECT_EQ("Node.Adhoc.BlockNumber", Serialization::typePath(el));
{
auto [a, b] = Serialization::splitFirst(".", "Edge.Transform.Flatten");
EXPECT_EQ("Edge", a);
EXPECT_EQ("Transform.Flatten", b);
}
{
auto [a, b] = Serialization::splitFirst(".", "Flatten");
EXPECT_EQ("Flatten", a);
EXPECT_EQ("", b);
}
}
TEST_F(CoordinateGraphTest, EdgeType)
{
auto ct = CoordinateGraph();
auto size = std::make_shared<Expression::Expression>(64u);
auto unit = std::make_shared<Expression::Expression>(1u);
auto x_index = std::make_shared<Expression::Expression>(5u);
auto y_index = std::make_shared<Expression::Expression>(3u);
auto x = ct.addElement(SubDimension(0, size, unit));
auto y = ct.addElement(SubDimension(0, size, unit));
auto m = ct.addElement(SubDimension(0, size * size, unit));
auto flatten_id = ct.addElement(Flatten{}, {x, y}, {m});
EXPECT_EQ(EdgeType::CoordinateTransform, ct.getEdgeType(flatten_id));
EXPECT_EQ(EdgeType::None, ct.getEdgeType(x));
EXPECT_EQ(ct.getNodes().to<std::set>(), std::set<int>({x, y, m}));
}
TEST_F(CoordinateGraphTest, Basic)
{
auto ct = CoordinateGraph();
auto size = std::make_shared<Expression::Expression>(64u);
auto unit = std::make_shared<Expression::Expression>(1u);
auto x_index = std::make_shared<Expression::Expression>(5u);
auto y_index = std::make_shared<Expression::Expression>(3u);
auto x = ct.addElement(SubDimension(0, size, unit));
auto y = ct.addElement(SubDimension(0, size, unit));
auto m = ct.addElement(SubDimension(0, size * size, unit));
auto flatten_id = ct.addElement(Flatten{}, {x, y}, {m});
EXPECT_EQ(ct.getNodes().to<std::set>(), std::set<int>({x, y, m}));
auto exprs = ct.forward({x_index, y_index}, {x, y}, {m});
auto sexpr = Expression::toString(exprs[0]);
EXPECT_EQ(sexpr, "{Flatten: Add(Multiply(5:U32, 64:U32)U32, 3:U32)U32}");
exprs = ct.forward({x_index, y_index}, {x, y}, {m});
sexpr = Expression::toString(fastArith(exprs[0]));
EXPECT_EQ(sexpr, "323:U32");
// Trivial
auto zero = std::make_shared<Expression::Expression>(0u);
exprs = ct.forward({zero, zero}, {x, y}, {m});
EXPECT_EQ(1, exprs.size());
sexpr = Expression::toString(exprs[0]);
EXPECT_EQ(sexpr, "{Flatten: Add(Multiply(0:U32, 64:U32)U32, 0:U32)U32}");
exprs = ct.forward({zero, zero}, {x, y}, {m});
EXPECT_EQ(1, exprs.size());
sexpr = Expression::toString(fastArith(exprs[0]));
EXPECT_EQ(sexpr, "0:U32");
auto result = std::get<unsigned int>(Expression::evaluate(exprs[0]));
EXPECT_EQ(result, 0);
EXPECT_NE(std::vector<int>(),
ct.path<Graph::Direction::Upstream>(std::vector<int>{m}, std::vector<int>{x})
.to<std::vector>());
auto m_index = std::make_shared<Expression::Expression>(67u);
exprs = ct.reverse({m_index}, {x}, {m}); // note 'y' isn't necessary in reverse
result = std::get<unsigned int>(Expression::evaluate(exprs[0]));
EXPECT_EQ(result, 1);
exprs = ct.reverse({m_index}, {y}, {m}); // note 'x' isn't necessary in reverse
result = std::get<unsigned int>(Expression::evaluate(exprs[0]));
EXPECT_EQ(result, 3);
exprs = ct.reverse({zero}, {y}, {m}); // note 'x' isn't necessary in reverse
EXPECT_EQ(1, exprs.size());
result = std::get<unsigned int>(Expression::evaluate(exprs[0]));
EXPECT_EQ(result, 0);
}
TEST_F(CoordinateGraphTest, Basic3D)
{
auto ct = CoordinateGraph();
auto size_x = std::make_shared<Expression::Expression>(64u);
auto stride_x = std::make_shared<Expression::Expression>(1u);
auto size_y = std::make_shared<Expression::Expression>(32u);
auto stride_y = std::make_shared<Expression::Expression>(4u);
auto size_z = std::make_shared<Expression::Expression>(16u);
auto stride_z = std::make_shared<Expression::Expression>(8u);
uint x_index_value = 5;
uint y_index_value = 3;
uint z_index_value = 7;
auto x_index = std::make_shared<Expression::Expression>(x_index_value);
auto y_index = std::make_shared<Expression::Expression>(y_index_value);
auto z_index = std::make_shared<Expression::Expression>(z_index_value);
int x = ct.addElement(SubDimension(0, size_x, stride_x));
int y = ct.addElement(SubDimension(0, size_y, stride_y));
int z = ct.addElement(SubDimension(0, size_z, stride_z));
int m = ct.addElement(SubDimension(0));
EXPECT_EQ(ct.getNodes().to<std::set>(), std::set<int>({x, y, z, m}));
auto flat = ct.addElement(Flatten(), {x, y, z}, {m});
auto exprs = ct.forward({x_index, y_index, z_index}, {x, y, z}, {m});
auto sexpr = Expression::toString(exprs[0]);
EXPECT_EQ(1, exprs.size());
EXPECT_EQ(sexpr,
"{Flatten: Add(Multiply(Add(Multiply(5:U32, 32:U32)U32, 3:U32)U32, 16:U32)U32, "
"7:U32)U32}");
exprs = ct.forward({x_index, y_index, z_index}, {x, y, z}, {m});
sexpr = Expression::toString(fastArith(exprs[0]));
EXPECT_EQ(1, exprs.size());
EXPECT_EQ(sexpr, "2615:U32");
auto result = std::get<unsigned int>(Expression::evaluate(exprs[0]));
EXPECT_EQ(2615, result);
{
auto rev_index = std::make_shared<Expression::Expression>(5);
auto exprs_reverse = ct.reverse({rev_index}, {x, y, z}, {m});
EXPECT_EQ(3, exprs_reverse.size());
EXPECT_EQ(Expression::toString(exprs_reverse[2]),
"{Flatten[2]: Modulo(5:I, 16:U32)U32}");
EXPECT_EQ(Expression::toString(exprs_reverse[1]),
"{Flatten[1]: Modulo(Divide(5:I, 16:U32)U32, 32:U32)U32}");
EXPECT_EQ(Expression::toString(exprs_reverse[0]),
"{Flatten[0]: Divide(Divide(5:I, 16:U32)U32, 32:U32)U32}");
}
{
// Should be able to get back the individual coordinate values.
auto rev_index = std::make_shared<Expression::Expression>(result);
auto exprs_reverse = ct.reverse({rev_index}, {x, y, z}, {m});
EXPECT_EQ(3, exprs_reverse.size());
EXPECT_EQ(x_index_value,
std::get<unsigned int>(Expression::evaluate(exprs_reverse[0])));
EXPECT_EQ(y_index_value,
std::get<unsigned int>(Expression::evaluate(exprs_reverse[1])));
EXPECT_EQ(z_index_value,
std::get<unsigned int>(Expression::evaluate(exprs_reverse[2])));
}
}
TEST_F(CoordinateGraphTest, Basic1D01)
{
auto ct = CoordinateGraph();
auto wavefront_size = Expression::literal(64u);
auto unit = Expression::literal(1u);
auto size = Expression::literal(1024u);
auto stride = Expression::literal(2u);
auto u = ct.addElement(User());
auto i = ct.addElement(SubDimension(0, size, stride));
auto wg = ct.addElement(Workgroup(2));
auto wf = ct.addElement(Wavefront(0, wavefront_size, unit));
// dimension "i" gets tiled into workgroups and wavefronts
ct.addElement(Split(), {u}, {i});
ct.addElement(Tile(), {i}, {wg, wf});
// TODO: uncomment and fix when appropriate methods are in place
// auto uOut = ct.getOutputs(getTag(u), EdgeType::CoordinateTransform);
// ASSERT_EQ(1, uOut.size());
// EXPECT_EQ(getTag(i), getTag(uOut[0]));
// ASSERT_EQ(0, ct.getOutputs(getTag(u), EdgeType::DataFlow).size());
auto block_index = Expression::literal(2);
auto thread_index = Expression::literal(33);
// given indexes for the workgroup and wavefront, compute "i"
auto exprs = ct.reverse({block_index, thread_index}, {u}, {wg, wf});
auto sexpr = Expression::toString(exprs[0]);
EXPECT_EQ(sexpr,
"{Split: Multiply({Tile: Add(Multiply(2:I, 64:U32)U32, 33:I)U32}, 2:U32)U32}");
exprs = ct.reverse({block_index, thread_index}, {u}, {wg, wf});
sexpr = Expression::toString(fastArith(exprs[0]));
EXPECT_EQ(sexpr, "322:U32");
auto thread_index_register = std::make_shared<Register::Value>(
m_context, Register::Type::Vector, DataType::Int32, 1);
thread_index_register->allocateNow();
exprs = ct.reverse({block_index, thread_index_register->expression()}, {u}, {wg, wf});
sexpr = Expression::toString(exprs[0]);
EXPECT_EQ(sexpr,
"{Split: Multiply({Tile: Add(Multiply(2:I, 64:U32)U32, v0:I)U32}, 2:U32)U32}");
exprs = ct.reverse({block_index, thread_index_register->expression()}, {u}, {wg, wf});
sexpr = Expression::toString(fastArith(exprs[0]));
EXPECT_EQ(sexpr, "{TileSplit: AddShiftL(128:U32, v0:I, 1:U32)U32}");
EXPECT_EQ(ct.getNodes().to<std::set>(), std::set<int>({u, i, wg, wf}));
// auto currentEdges = ct.getEdges();
// EXPECT_EQ(currentEdges.size(), 2);
// auto savedEdge = currentEdges[1];
// ct.removeEdge(currentEdges[0]);
// currentEdges = ct.getEdges();
// EXPECT_EQ(currentEdges[0], savedEdge);
}
TEST_F(CoordinateGraphTest, Basic1D01Eval)
{
auto ct = CoordinateGraph();
auto wavefront_size = std::make_shared<Expression::Expression>(64);
auto unit = std::make_shared<Expression::Expression>(1);
auto size = std::make_shared<Expression::Expression>(1024);
auto stride = std::make_shared<Expression::Expression>(2);
auto workgroup_size = std::make_shared<Expression::Expression>(256) / wavefront_size;
auto u = ct.addElement(User());
auto i = ct.addElement(SubDimension(0, size, stride));
auto wg = ct.addElement(Workgroup(2));
auto wf = ct.addElement(Wavefront(0, wavefront_size, unit));
// dimension "i" gets tiled into workgroups and wavefronts
ct.addElement(Split(), {u}, {i});
ct.addElement(Tile(), {i}, {wg, wf});
EXPECT_EQ(ct.getNodes().to<std::set>(), std::set<int>({u, i, wg, wf}));
auto block_index = std::make_shared<Expression::Expression>(2);
auto thread_index = std::make_shared<Expression::Expression>(33);
// given indexes for the workgroup and wavefront, compute "u"
{
auto exprs = ct.reverse({block_index, thread_index}, {u}, {wg, wf});
auto sexpr = Expression::toString(exprs[0]);
auto uVal = std::get<int>(Expression::evaluate(exprs[0]));
EXPECT_EQ(322, uVal) << toString(exprs[0]);
}
// given indexes for the workgroup and wavefront, compute "i"
{
auto exprs = ct.reverse({block_index, thread_index}, {i}, {wg, wf});
auto sexpr = Expression::toString(exprs[0]);
auto iVal = std::get<int>(Expression::evaluate(exprs[0]));
EXPECT_EQ(161, iVal) << toString(exprs[0]);
auto iValExpr = std::make_shared<Expression::Expression>(iVal);
// given "i", compute workgroup and wavefront
auto fwdExprs = ct.forward({iValExpr}, {i}, {wg, wf});
EXPECT_EQ(2, fwdExprs.size());
EXPECT_EQ(2, std::get<int>(evaluate(fwdExprs[0]))) << toString(fwdExprs[0]);
EXPECT_EQ(33, std::get<int>(evaluate(fwdExprs[1]))) << toString(fwdExprs[1]);
}
}
TEST_F(CoordinateGraphTest, Basic1D02)
{
auto ct = CoordinateGraph();
auto wavefront_size = Expression::literal(64);
auto unit = Expression::literal(1);
auto size = Expression::literal(1024);
auto stride = Expression::literal(2);
auto u = ct.addElement(User());
auto i = ct.addElement(SubDimension(0, size, stride));
auto wg = ct.addElement(Workgroup(2));
auto wf = ct.addElement(Wavefront(0, wavefront_size, unit));
auto unroll = ct.addElement(Unroll(4));
// dimension "i" gets tiled into workgroups and wavefronts, and
// each thread in a wavefront operates on 4 elements (unroll)
ct.addElement(Split(), {u}, {i});
ct.addElement(Tile(), {i}, {wg, wf, unroll});
EXPECT_EQ(ct.getNodes().to<std::set>(), std::set<int>({u, i, wg, wf, unroll}));
auto block_index = Expression::literal(2);
auto thread_index = Expression::literal(33);
auto unroll_index = Expression::literal(2);
// given indexes for the workgroup and wavefront, compute "i"
auto exprs = ct.reverse({block_index, thread_index, unroll_index}, {u}, {wg, wf, unroll});
auto sexpr = Expression::toString(exprs[0]);
EXPECT_EQ(sexpr,
"{Split: Multiply({Tile: Add(Multiply(Add(Multiply(2:I, 64:I)I, 33:I)I, "
"4:U32)U32, 2:I)U32}, 2:I)U32}");
exprs = ct.reverse({block_index, thread_index, unroll_index}, {u}, {wg, wf, unroll});
sexpr = Expression::toString(fastArith(exprs[0]));
EXPECT_EQ(sexpr, "1292:U32");
auto thread_index_register = std::make_shared<Register::Value>(
m_context, Register::Type::Vector, DataType::Int32, 1);
thread_index_register->allocateNow();
exprs = ct.reverse({block_index, thread_index_register->expression(), unroll_index},
{u},
{wg, wf, unroll});
sexpr = Expression::toString(exprs[0]);
EXPECT_EQ(sexpr,
"{Split: Multiply({Tile: Add(Multiply(Add(Multiply(2:I, 64:I)I, v0:I)I, "
"4:U32)U32, 2:I)U32}, 2:I)U32}");
exprs = ct.reverse({block_index, thread_index_register->expression(), unroll_index},
{u},
{wg, wf, unroll});
sexpr = Expression::toString(exprs[0]);
EXPECT_EQ(sexpr,
"{Split: Multiply({Tile: Add(Multiply(Add(Multiply(2:I, 64:I)I, v0:I)I, "
"4:U32)U32, 2:I)U32}, 2:I)U32}");
}
TEST_F(CoordinateGraphTest, Basic1D03)
{
auto ct = CoordinateGraph();
auto wavefront_size = Expression::literal(64);
auto unit = Expression::literal(1);
auto size = Expression::literal(1024);
auto stride = Expression::literal(2);
auto u = ct.addElement(User());
auto i = ct.addElement(SubDimension(0, size, stride));
auto wg = ct.addElement(Workgroup(2));
auto wf = ct.addElement(Wavefront(0, Expression::literal(4 * 64), unit));
auto thread = ct.addElement(SubDimension(1, wavefront_size, unit));
auto unroll = ct.addElement(Unroll(4));
// as in Basic1D02, but we add an intermediate dimension "wf" and tile twice
ct.addElement(Split(), {u}, {i});
ct.addElement(Tile(), {i}, {wg, wf});
ct.addElement(Tile(), {wf}, {thread, unroll});
EXPECT_EQ(ct.getNodes().to<std::set>(), std::set<int>({u, i, wg, wf, thread, unroll}));
auto block_index = Expression::literal(2);
auto thread_index = Expression::literal(33);
auto unroll_index = Expression::literal(2);
// given indexes for the workgroup and wavefront, compute "i"
auto exprs
= ct.reverse({block_index, thread_index, unroll_index}, {u}, {wg, thread, unroll});
auto sexpr = Expression::toString(exprs[0]);
EXPECT_EQ(sexpr,
"{Split: Multiply({Tile: Add(Multiply(2:I, 256:I)I, {Tile: Add(Multiply(33:I, "
"4:U32)U32, 2:I)U32})U32}, 2:I)U32}");
exprs = ct.reverse({block_index, thread_index, unroll_index}, {u}, {wg, thread, unroll});
sexpr = Expression::toString(fastArith(exprs[0]));
EXPECT_EQ(sexpr, "1292:U32");
auto thread_index_register = std::make_shared<Register::Value>(
m_context, Register::Type::Vector, DataType::Int32, 1);
thread_index_register->allocateNow();
exprs = ct.reverse({block_index, thread_index_register->expression(), unroll_index},
{u},
{wg, thread, unroll});
sexpr = Expression::toString(exprs[0]);
EXPECT_EQ(sexpr,
"{Split: Multiply({Tile: Add(Multiply(2:I, 256:I)I, {Tile: Add(Multiply(v0:I, "
"4:U32)U32, 2:I)U32})U32}, 2:I)U32}");
exprs = ct.reverse({block_index, thread_index_register->expression(), unroll_index},
{u},
{wg, thread, unroll});
sexpr = Expression::toString(fastArith(exprs[0]));
EXPECT_EQ(
sexpr,
"{Split: ShiftL({Tile: ShiftLAdd(Convert(v0:I)U32, 2:U32, 514:I)U32}, 1:U32)U32}");
}
TEST_F(CoordinateGraphTest, TensorTile2DLoadStore01)
{
// one tile per wavefront
auto ct = CoordinateGraph();
auto wavefront_size = Expression::literal(64);
auto unit = Expression::literal(1);
auto M = Expression::literal(100);
auto N = Expression::literal(200);
auto K = Expression::literal(300);
int m = 16;
int n = 16;
// A matrix; tag 1; M x K; C-ordering
auto A = ct.addElement(User());
auto Ai = ct.addElement(SubDimension(0, M, K));
auto Aj = ct.addElement(SubDimension(1, K, unit));
auto sp0 = ct.addElement(Split(), {A}, {Ai, Aj});
// B matrix; tag 2; K x N; C-ordering
auto B = ct.addElement(User());
auto Bi = ct.addElement(SubDimension(0, K, N));
auto Bj = ct.addElement(SubDimension(1, N, unit));
auto sp1 = ct.addElement(Split(), {B}, {Bi, Bj});
// T tile; tag 3; m x n
auto T = MacroTile({m, n}, MemoryType::VGPR);
auto T_id = ct.addElement(T);
auto i = ct.addElement(T.tileIndex(0));
auto j = ct.addElement(T.tileIndex(1));
auto join = ct.addElement(Join(), {i, j}, {T_id});
// tile each dimension of A matrix; each workgroup gets one tile
auto tile_x = ct.addElement(Workgroup(0));
auto tile_y = ct.addElement(Workgroup(1));
auto t0 = ct.addElement(Tile(), {Ai}, {tile_x, i});
auto t1 = ct.addElement(Tile(), {Aj}, {tile_y, j});
EXPECT_EQ(ct.getNodes().to<std::set>(),
std::set<int>({A, Ai, Aj, B, Bi, Bj, T_id, i, j, tile_x, tile_y}));
EXPECT_EQ(ct.getEdges().to<std::set>(), std::set<int>({sp0, sp1, join, t0, t1}));
auto tile_x_index = Expression::literal(4);
auto tile_y_index = Expression::literal(5);
auto i_index = Expression::literal(33);
auto j_index = Expression::literal(2);
std::vector<Expression::ExpressionPtr> exprs;
std::string sexpr;
{
EXPECT_THROW(ct.reverse({tile_x_index, i_index}, {A}, {tile_x, i}), FatalError);
EXPECT_THROW(ct.reverse({tile_x_index, i_index}, {Aj}, {tile_x, i}), FatalError);
EXPECT_THROW(ct.reverse({tile_x_index, i_index}, {Ai, Aj}, {tile_x, i}), FatalError);
}
exprs = ct.reverse(
{tile_x_index, tile_y_index, i_index, j_index}, {Ai}, {tile_x, tile_y, i, j});
sexpr = Expression::toString(exprs[0]);
EXPECT_EQ(sexpr, "{Tile: Add(Multiply(4:I, 16:U32)U32, 33:I)U32}");
exprs = ct.reverse(
{tile_x_index, tile_y_index, i_index, j_index}, {Ai}, {tile_x, tile_y, i, j});
sexpr = Expression::toString(fastArith(exprs[0]));
EXPECT_EQ(sexpr, "97:U32");
exprs = ct.reverse(
{tile_x_index, tile_y_index, i_index, j_index}, {A}, {tile_x, tile_y, i, j});
sexpr = Expression::toString(exprs[0]);
EXPECT_EQ(sexpr,
"{Split: Add(Multiply({Tile: Add(Multiply(4:I, 16:U32)U32, 33:I)U32}, 300:I)U32, "
"Multiply({Tile: Add(Multiply(5:I, 16:U32)U32, 2:I)U32}, 1:I)U32)U32}");
exprs = ct.reverse(
{tile_x_index, tile_y_index, i_index, j_index}, {A}, {tile_x, tile_y, i, j});
sexpr = Expression::toString(fastArith(exprs[0]));
EXPECT_EQ(sexpr, "29182:U32");
}
TEST_F(CoordinateGraphTest, TensorTile2DLoadStore02)
{
// one tile per wavefront
auto ct = CoordinateGraph();
auto wavefront_size = Expression::literal(64);
auto unit = Expression::literal(1);
auto M = Expression::literal(100);
auto N = Expression::literal(200);
auto K = Expression::literal(300);
int m = 16;
int n = 16;
// A matrix; tag 1; M x K; C-ordering
auto A = ct.addElement(User());
auto Ai = ct.addElement(SubDimension(0, M, K));
auto Aj = ct.addElement(SubDimension(1, K, unit));
auto sp0 = ct.addElement(Split(), {A}, {Ai, Aj});
// T tile; tag 3; m x n
auto T = MacroTile({m, n}, MemoryType::VGPR);
auto T_id = ct.addElement(T);
auto i = ct.addElement(T.tileIndex(0));
auto j = ct.addElement(T.tileIndex(1));
auto join = ct.addElement(Join(), {i, j}, {T_id});
// tile each dimension of A matrix; each workgroup gets one tile
auto tile_x = ct.addElement(Workgroup(0));
auto tile_y = ct.addElement(Workgroup(1));
auto t0 = ct.addElement(Tile(), {Ai}, {tile_x, i});
auto t1 = ct.addElement(Tile(), {Aj}, {tile_y, j});
EXPECT_EQ(ct.getNodes().to<std::set>(), std::set({A, Ai, Aj, T_id, i, j, tile_x, tile_y}));
EXPECT_EQ(ct.getNodes().to<std::set>(), ct.getElements<Dimension>().to<std::set>());
EXPECT_EQ(ct.getEdges().to<std::set>(), std::set({sp0, join, t0, t1}));
// getElements only works on Node, Edge, and direct members of the
// Node and Edge variants, not on any sub-variants of Edge. We can't
// unfortunately (yet?) filter only Tile edges for example.
EXPECT_EQ(ct.getElements<CoordinateTransformEdge>().to<std::set>(),
std::set({sp0, join, t0, t1}));
auto coords = Transformer(&ct, Expression::identity);
auto transducer = [&](auto expr) { return Expression::fastMultiplication(expr); };
std::vector<Expression::ExpressionPtr> exprs;
std::string sexpr;
// set location to thread tile
coords.setCoordinate(tile_x, Expression::literal(4u));
coords.setCoordinate(tile_y, Expression::literal(5u));
coords.setCoordinate(i, Expression::literal(33u));
coords.setCoordinate(j, Expression::literal(2u));
// from thread tile to macro tile row
exprs = coords.reverse({Ai});
sexpr = Expression::toString(exprs[0]);
EXPECT_EQ(sexpr, "{Tile: Add(Multiply(4:U32, 16:U32)U32, 33:U32)U32}");
// from thread tile to user tensor
exprs = coords.reverse({A});
sexpr = Expression::toString(exprs[0]);
EXPECT_EQ(
sexpr,
"{Split: Add(Multiply({Tile: Add(Multiply(4:U32, 16:U32)U32, 33:U32)U32}, 300:I)U32, "
"Multiply({Tile: Add(Multiply(5:U32, 16:U32)U32, 2:U32)U32}, 1:I)U32)U32}");
// check edge case where expression are unchanged
coords.setTransducer(Expression::identity);
exprs = coords.reverse({A});
sexpr = Expression::toString(exprs[0]);
EXPECT_EQ(
sexpr,
"{Split: Add(Multiply({Tile: Add(Multiply(4:U32, 16:U32)U32, 33:U32)U32}, 300:I)U32, "
"Multiply({Tile: Add(Multiply(5:U32, 16:U32)U32, 2:U32)U32}, 1:I)U32)U32}");
coords.setTransducer(nullptr);
// as above, with fast multiplication
coords.setTransducer(Expression::fastMultiplication);
exprs = coords.reverse({A});
sexpr = Expression::toString(transducer(exprs[0]));
EXPECT_EQ(sexpr,
"{Split: Add(Multiply({Tile: Add(ShiftL(4:U32, 4:U32)U32, 33:U32)U32}, "
"300:I)U32, {Tile: Add(ShiftL(5:U32, 4:U32)U32, 2:U32)U32})U32}");
// remove i, try again: should fail
coords.removeCoordinate(i);
EXPECT_THROW(coords.reverse({A}), FatalError);
// remove i and j, so only know workgroup and workitem
coords.removeCoordinate(i);
coords.removeCoordinate(j);
// from user tensor row to thread tile row
coords.setCoordinate(Ai, Expression::literal(17));
exprs = coords.forward({i});
sexpr = Expression::toString(exprs[0]);
EXPECT_EQ(sexpr, "{Tile[1]: Modulo(17:I, 16:U32)U32}");
}
TEST_F(CoordinateGraphTest, TensorTile2DLoadStore03)
{
auto ct = CoordinateGraph();
uint m = 16;
uint n = 16;
auto unit = Expression::literal(1u);
auto M = Expression::literal(100u * m);
auto N = Expression::literal(300u * n);
// A matrix; tag 1; M x K; C-ordering
auto A = ct.addElement(User());
auto Ai = ct.addElement(SubDimension(0, M, N));
auto Aj = ct.addElement(SubDimension(1, N, unit));
ct.addElement(Split(), {A}, {Ai, Aj});
// T tile; tag 3; m x n
auto T = MacroTile({(int)m, (int)n}, MemoryType::VGPR);
auto T_id = ct.addElement(T);
auto i = ct.addElement(T.tileIndex(0));
auto j = ct.addElement(T.tileIndex(1));
ct.addElement(Join(), {i, j}, {T_id});
// tile each dimension of A matrix; each workgroup gets one tile
auto tile_x
= ct.addElement(SubDimension(0, Expression::literal(100u), Expression::literal(m)));
auto tile_y
= ct.addElement(SubDimension(1, Expression::literal(300u), Expression::literal(n)));
ct.addElement(Tile(), {Ai}, {tile_x, i});
ct.addElement(Tile(), {Aj}, {tile_y, j});
auto D = ct.addElement(User());
auto Di = ct.addElement(SubDimension(0, M, N));
auto Dj = ct.addElement(SubDimension(1, N, unit));
ct.addElement(Flatten(), {tile_x, i}, {Di});
ct.addElement(Flatten(), {tile_y, j}, {Dj});
ct.addElement(Join(), {Di, Dj}, {D});
EXPECT_EQ(ct.getNodes().to<std::set>(),
std::set<int>({A, Ai, Aj, T_id, i, j, tile_x, tile_y, D, Di, Dj}));
auto coords = Transformer(&ct);
coords.setCoordinate(tile_x, Expression::literal(4u));
coords.setCoordinate(tile_y, Expression::literal(5u));
coords.setCoordinate(i, Expression::literal(33u));
coords.setCoordinate(j, Expression::literal(2u));
auto exprs = coords.reverseStride(tile_x, Expression::literal(1u), {A});
auto sexpr = Expression::toString(Expression::simplify(exprs[0]));
EXPECT_EQ(sexpr, "76800:U32");
exprs = coords.reverseStride(i, Expression::literal(2u), {A});
sexpr = Expression::toString(Expression::simplify(exprs[0]));
EXPECT_EQ(sexpr, "9600:U32");
exprs = coords.reverseStride(j, Expression::literal(1u), {A});
sexpr = Expression::toString(Expression::simplify(exprs[0]));
EXPECT_EQ(sexpr, "1:U32");
exprs = coords.reverseStride(tile_y, Expression::literal(2u), {A});
sexpr = Expression::toString(Expression::simplify(exprs[0]));
EXPECT_EQ(sexpr, "32:U32");
exprs = coords.forwardStride(tile_y, Expression::literal(2u), {D});
sexpr = Expression::toString(Expression::simplify(exprs[0]));
EXPECT_EQ(sexpr, "32:U32");
}
TEST_F(CoordinateGraphTest, SunderBasicOne)
{
auto zero = Expression::literal(0u);
auto one = Expression::literal(1u);
auto ct = CoordinateGraph();
auto input = ct.addElement(User());
auto linX = ct.addElement(Linear(Expression::literal(100u), one));
auto sw = ct.addElement(Linear(Expression::literal(2u), one));
auto sunder = ct.addElement(Sunder(), {input}, {linX, sw});
auto a = ct.addElement(Linear());
auto b = ct.addElement(Linear(Expression::literal(10u), one));
ct.addElement(Tile(), {linX}, {a, b});
auto aVal = Expression::literal(8u);
auto bVal = Expression::literal(3u);
EXPECT_THROW(only(ct.reverse({aVal, bVal, one}, {input}, {a, b, sw})), FatalError);
auto exprX = only(ct.reverse({aVal, bVal, zero}, {input}, {a, b, sw}));
EXPECT_EQ("{Sunder: Add(Multiply(8:U32, 10:U32)U32, 3:U32)U32}", toString(exprX.value()));
}
TEST_F(CoordinateGraphTest, SunderBasicTwo)
{
auto zero = Expression::literal(0u);
auto one = Expression::literal(1u);
auto two = Expression::literal(2u);
auto ct = CoordinateGraph();
auto input = ct.addElement(User());
auto linX = ct.addElement(Linear(Expression::literal(100u), one));
auto linY = ct.addElement(Linear(Expression::literal(24u), one));
auto sw = ct.addElement(Linear(Expression::literal(2u), one));
auto sunder = ct.addElement(Sunder(), {input}, {linX, linY, sw});
auto a = ct.addElement(Linear());
auto b = ct.addElement(Linear(Expression::literal(10u), one));
ct.addElement(Tile(), {linX}, {a, b});
auto c = ct.addElement(Linear());
auto d = ct.addElement(Linear(Expression::literal(6u), one));
ct.addElement(Tile(), {linY}, {c, d});
auto aVal = Expression::literal(8u);
auto bVal = Expression::literal(3u);
auto cVal = Expression::literal(1u);
auto dVal = Expression::literal(4u);
auto exprX
= only(ct.reverse({aVal, bVal, nullptr, nullptr, zero}, {input}, {a, b, c, d, sw}));
auto exprY
= only(ct.reverse({nullptr, nullptr, cVal, dVal, one}, {input}, {a, b, c, d, sw}));
EXPECT_THROW(
only(ct.reverse({aVal, bVal, nullptr, nullptr, two}, {input}, {a, b, c, d, sw})),
FatalError);
EXPECT_EQ("{Sunder: Add(Multiply(8:U32, 10:U32)U32, 3:U32)U32}", toString(exprX.value()));
EXPECT_EQ("{Sunder: Add({Tile: Add(Multiply(1:U32, 6:U32)U32, 4:U32)U32}, 100:U32)U32}",
toString(exprY.value()));
}
TEST_F(CoordinateGraphTest, SunderBasicThree)
{
auto zero = Expression::literal(0u);
auto one = Expression::literal(1u);
auto two = Expression::literal(2u);
auto three = Expression::literal(3u);
auto ct = CoordinateGraph();
auto input = ct.addElement(User());
auto linX = ct.addElement(Linear(Expression::literal(100u), one));
auto linY = ct.addElement(Linear(Expression::literal(24u), one));
auto linZ = ct.addElement(Linear(Expression::literal(17u), one));
auto sw = ct.addElement(Linear(Expression::literal(2u), one));
auto sunder = ct.addElement(Sunder(), {input}, {linX, linY, linZ, sw});
auto a = ct.addElement(Linear());
auto b = ct.addElement(Linear(Expression::literal(10u), one));
ct.addElement(Tile(), {linX}, {a, b});
auto c = ct.addElement(Linear());
auto d = ct.addElement(Linear(Expression::literal(6u), one));
ct.addElement(Tile(), {linY}, {c, d});
auto e = ct.addElement(Linear());
auto f = ct.addElement(Linear(Expression::literal(9u), one));
ct.addElement(Tile(), {linZ}, {e, f});
auto aVal = Expression::literal(8u);
auto bVal = Expression::literal(3u);
auto cVal = Expression::literal(1u);
auto dVal = Expression::literal(4u);
auto eVal = Expression::literal(0u);
auto fVal = Expression::literal(19u);
auto exprX = only(ct.reverse(
{aVal, bVal, cVal, dVal, eVal, fVal, zero}, {input}, {a, b, c, d, e, f, sw}));
auto exprY = only(
ct.reverse({aVal, bVal, cVal, dVal, eVal, fVal, one}, {input}, {a, b, c, d, e, f, sw}));
auto exprZ = only(
ct.reverse({aVal, bVal, cVal, dVal, eVal, fVal, two}, {input}, {a, b, c, d, e, f, sw}));
EXPECT_THROW(only(ct.reverse({aVal, bVal, cVal, dVal, eVal, fVal, three},
{input},
{a, b, c, d, e, f, sw})),
FatalError);
EXPECT_EQ("{Sunder: Add(Multiply(8:U32, 10:U32)U32, 3:U32)U32}", toString(exprX.value()));
EXPECT_EQ("{Sunder: Add({Tile: Add(Multiply(1:U32, 6:U32)U32, 4:U32)U32}, 100:U32)U32}",
toString(exprY.value()));
EXPECT_EQ("{Sunder: Add({Tile: Add(Multiply(0:U32, 9:U32)U32, 19:U32)U32}, Add(100:U32, "
"24:U32)U32)U32}",
toString(exprZ.value()));
}
TEST_F(CoordinateGraphTest, SunderBasicOneViaTransformer)
{
auto zero = Expression::literal(0u);
auto one = Expression::literal(1u);
auto ct = CoordinateGraph();
auto input = ct.addElement(User());
auto linX = ct.addElement(Linear(Expression::literal(100u), one));
auto sw = ct.addElement(Linear(Expression::literal(2u), one));
auto sunder = ct.addElement(Sunder(), {input}, {linX, sw});
auto a = ct.addElement(Linear());
auto b = ct.addElement(Linear(Expression::literal(10u), one));
ct.addElement(Tile(), {linX}, {a, b});
auto tf = Transformer(&ct, Expression::identity);
tf.setCoordinate(a, Expression::literal(8u));
tf.setCoordinate(b, Expression::literal(3u));
tf.setCoordinate(sw, zero);
auto exprX = only(tf.reverse({input}));
EXPECT_EQ("{Sunder: Add(Multiply(8:U32, 10:U32)U32, 3:U32)U32}", toString(*exprX));
auto strideExprX = only(tf.reverseStride(b, Expression::literal(2u), {input}));
EXPECT_EQ("2:U32", toString(strideExprX.value()));
strideExprX = only(tf.reverseStride(a, Expression::literal(2u), {input}));
EXPECT_EQ("20:U32", toString(strideExprX.value()));
tf.setCoordinate(sw, one);
EXPECT_THROW(tf.reverse({input}), FatalError);
EXPECT_THROW(tf.reverseStride(b, Expression::literal(2u), {input}), FatalError);
EXPECT_THROW(tf.reverseStride(a, Expression::literal(2u), {input}), FatalError);
}
TEST_F(CoordinateGraphTest, SunderBasicTwoViaTransformer)
{
auto zero = Expression::literal(0u);
auto one = Expression::literal(1u);
auto two = Expression::literal(2u);
auto ct = CoordinateGraph();
auto input = ct.addElement(User());
auto linX = ct.addElement(Linear(Expression::literal(100u), one));
auto linY = ct.addElement(Linear(Expression::literal(24u), one));
auto sw = ct.addElement(Linear(Expression::literal(2u), one));
auto sunder = ct.addElement(Sunder(), {input}, {linX, linY, sw});
auto a = ct.addElement(Linear());
auto b = ct.addElement(Linear(Expression::literal(10u), one));
ct.addElement(Tile(), {linX}, {a, b});
auto c = ct.addElement(Linear());
auto d = ct.addElement(Linear(Expression::literal(6u), one));
ct.addElement(Tile(), {linY}, {c, d});
auto tf = Transformer(&ct, Expression::identity);
tf.setCoordinate(a, Expression::literal(8u));
tf.setCoordinate(b, Expression::literal(3u));
tf.setCoordinate(c, Expression::literal(1u));
tf.setCoordinate(d, Expression::literal(4u));
tf.setCoordinate(sw, zero);
auto exprX = only(tf.reverse({input}));
EXPECT_EQ("{Sunder: Add(Multiply(8:U32, 10:U32)U32, 3:U32)U32}", toString(*exprX));
auto strideExprX = only(tf.reverseStride(b, Expression::literal(2u), {input}));
EXPECT_EQ("2:U32", toString(strideExprX.value()));
strideExprX = only(tf.reverseStride(a, Expression::literal(2u), {input}));
EXPECT_EQ("20:U32", toString(strideExprX.value()));
strideExprX = only(tf.reverseStride(d, Expression::literal(3u), {input}));
EXPECT_EQ("0:U32", toString(strideExprX.value()));
strideExprX = only(tf.reverseStride(c, Expression::literal(3u), {input}));
EXPECT_EQ("0:U32", toString(strideExprX.value()));
tf.setCoordinate(sw, one);
auto exprY = only(tf.reverse({input}));
EXPECT_EQ("{Sunder: Add({Tile: Add(Multiply(1:U32, 6:U32)U32, 4:U32)U32}, 100:U32)U32}",
toString(*exprY));
auto strideExprY = only(tf.reverseStride(d, Expression::literal(3u), {input}));
EXPECT_EQ("3:U32", toString(strideExprY.value()));
strideExprY = only(tf.reverseStride(c, Expression::literal(3u), {input}));
EXPECT_EQ("18:U32", toString(strideExprY.value()));
strideExprY = only(tf.reverseStride(b, Expression::literal(2u), {input}));
EXPECT_EQ("0:U32", toString(strideExprY.value()));
strideExprY = only(tf.reverseStride(a, Expression::literal(2u), {input}));
EXPECT_EQ("0:U32", toString(strideExprY.value()));
tf.setCoordinate(sw, two);
EXPECT_THROW(tf.reverse({input}), FatalError);
}
TEST_F(CoordinateGraphTest, SunderBasicThreeViaTransformer)
{
auto zero = Expression::literal(0u);
auto one = Expression::literal(1u);
auto two = Expression::literal(2u);
auto three = Expression::literal(3u);
auto ct = CoordinateGraph();
auto input = ct.addElement(User());
auto linX = ct.addElement(Linear(Expression::literal(100u), one));
auto linY = ct.addElement(Linear(Expression::literal(24u), one));
auto linZ = ct.addElement(Linear(Expression::literal(17u), one));
auto sw = ct.addElement(Linear(Expression::literal(2u), one));
auto sunder = ct.addElement(Sunder(), {input}, {linX, linY, linZ, sw});
auto a = ct.addElement(Linear());
auto b = ct.addElement(Linear(Expression::literal(10u), one));
ct.addElement(Tile(), {linX}, {a, b});
auto c = ct.addElement(Linear());
auto d = ct.addElement(Linear(Expression::literal(6u), one));
ct.addElement(Tile(), {linY}, {c, d});
auto e = ct.addElement(Linear());
auto f = ct.addElement(Linear(Expression::literal(9u), one));
ct.addElement(Tile(), {linZ}, {e, f});
auto tf = Transformer(&ct, nullptr);
tf.setCoordinate(a, Expression::literal(8u));
tf.setCoordinate(b, Expression::literal(3u));
tf.setCoordinate(c, nullptr);
tf.setCoordinate(d, nullptr);
tf.setCoordinate(sw, zero);
tf.setCoordinate(e, nullptr);
tf.setCoordinate(f, nullptr);
auto exprX = only(tf.reverse({input}));
EXPECT_EQ("{Sunder: Add(Multiply(8:U32, 10:U32)U32, 3:U32)U32}", toString(*exprX));
auto strideExprX = only(tf.reverseStride(b, Expression::literal(2u), {input}));
EXPECT_EQ("2:U32", toString(strideExprX.value()));
strideExprX = only(tf.reverseStride(a, Expression::literal(2u), {input}));
EXPECT_EQ("20:U32", toString(strideExprX.value()));
strideExprX = only(tf.reverseStride(d, Expression::literal(3u), {input}));
EXPECT_EQ("0:U32", toString(strideExprX.value()));
strideExprX = only(tf.reverseStride(c, Expression::literal(3u), {input}));
EXPECT_EQ("0:U32", toString(strideExprX.value()));
strideExprX = only(tf.reverseStride(e, Expression::literal(1u), {input}));
EXPECT_EQ("0:U32", toString(strideExprX.value()));
strideExprX = only(tf.reverseStride(f, Expression::literal(3u), {input}));
EXPECT_EQ("0:U32", toString(strideExprX.value()));
tf.setCoordinate(c, Expression::literal(1u));
tf.setCoordinate(d, Expression::literal(4u));
tf.setCoordinate(sw, one);
auto exprY = only(tf.reverse({input}));
EXPECT_EQ("{Sunder: Add({Tile: Add(Multiply(1:U32, 6:U32)U32, 4:U32)U32}, 100:U32)U32}",
toString(*exprY));
auto strideExprY = only(tf.reverseStride(d, Expression::literal(3u), {input}));
EXPECT_EQ("3:U32", toString(strideExprY.value()));
strideExprY = only(tf.reverseStride(c, Expression::literal(3u), {input}));
EXPECT_EQ("18:U32", toString(strideExprY.value()));
strideExprY = only(tf.reverseStride(b, Expression::literal(2u), {input}));
EXPECT_EQ("0:U32", toString(strideExprY.value()));
strideExprY = only(tf.reverseStride(a, Expression::literal(2u), {input}));
EXPECT_EQ("0:U32", toString(strideExprY.value()));
strideExprY = only(tf.reverseStride(e, Expression::literal(1u), {input}));
EXPECT_EQ("0:U32", toString(strideExprY.value()));
strideExprY = only(tf.reverseStride(f, Expression::literal(3u), {input}));
EXPECT_EQ("0:U32", toString(strideExprY.value()));
tf.setCoordinate(e, Expression::literal(0u));
tf.setCoordinate(f, Expression::literal(19u));
tf.setCoordinate(sw, two);
auto exprZ = only(tf.reverse({input}));
EXPECT_EQ("{Sunder: Add({Tile: Add(Multiply(0:U32, 9:U32)U32, 19:U32)U32}, Add(100:U32, "
"24:U32)U32)U32}",
toString(*exprZ));
auto strideExprZ = only(tf.reverseStride(f, Expression::literal(1u), {input}));
EXPECT_EQ("1:U32", toString(strideExprZ.value()));
strideExprZ = only(tf.reverseStride(e, Expression::literal(3u), {input}));
EXPECT_EQ("27:U32", toString(strideExprZ.value()));
strideExprZ = only(tf.reverseStride(b, Expression::literal(2u), {input}));
EXPECT_EQ("0:U32", toString(strideExprZ.value()));
strideExprZ = only(tf.reverseStride(a, Expression::literal(2u), {input}));
EXPECT_EQ("0:U32", toString(strideExprZ.value()));
strideExprZ = only(tf.reverseStride(d, Expression::literal(3u), {input}));
EXPECT_EQ("0:U32", toString(strideExprZ.value()));
strideExprZ = only(tf.reverseStride(c, Expression::literal(3u), {input}));
EXPECT_EQ("0:U32", toString(strideExprZ.value()));
tf.setCoordinate(sw, three);
EXPECT_THROW(tf.reverse({input}), FatalError);
}
TEST_F(CoordinateGraphTest, WaveTileBasic)
{
// one tile per wavefront
auto ct = CoordinateGraph();
auto MacA = MacroTile({32u, 32u, 32u, 32u}, LayoutType::MATRIX_A, {32u, 0u, 2u, 0u});
auto WaveA = WaveTile(MacA);
auto WaveAI = WaveA.tileNumber(0);
auto WaveAJ = WaveA.tileNumber(1);
auto WaveAi = WaveA.tileIndex(0);
auto WaveAj = WaveA.tileIndex(1);
EXPECT_EQ(WaveAI.dim, 0);
EXPECT_EQ(WaveAJ.dim, 1);
EXPECT_EQ(WaveAi.dim, 0);
EXPECT_EQ(WaveAj.dim, 1);
EXPECT_EQ(std::get<uint>(Expression::evaluate(WaveAi.size)), 32u);
EXPECT_EQ(std::get<uint>(Expression::evaluate(WaveAj.size)), 2u);
EXPECT_EQ(std::get<int>(Expression::evaluate(WaveAi.stride)), 2);
EXPECT_EQ(std::get<int>(Expression::evaluate(WaveAj.stride)), 1);
}
TEST_F(CoordinateGraphTest, HasPath)
{
// one tile per wavefront
auto ct = CoordinateGraph();
auto wavefront_size = Expression::literal(64u);
auto unit = Expression::literal(1u);
auto size = Expression::literal(1024u);
auto stride = Expression::literal(2u);
auto u = ct.addElement(User());
auto i = ct.addElement(SubDimension(0, size, stride));
auto j = ct.addElement(SubDimension(1, size, stride));
auto wg = ct.addElement(Workgroup(2));
auto wf = ct.addElement(Wavefront(0, wavefront_size, unit));
ct.addElement(Split(), {u}, {i, j});
ct.addElement(Tile(), {i}, {wg, wf});
EXPECT_TRUE(ct.hasPath<Graph::Direction::Upstream>({u}, {wg, wf, j}));
EXPECT_FALSE(ct.hasPath<Graph::Direction::Upstream>({u}, {wg, j}));
EXPECT_FALSE(ct.hasPath<Graph::Direction::Upstream>({u}, {wg, wf}));
EXPECT_FALSE(ct.hasPath<Graph::Direction::Upstream>({u}, {wf, j}));
EXPECT_TRUE(ct.hasPath<Graph::Direction::Downstream>({u}, {i, j}));
EXPECT_TRUE(ct.hasPath<Graph::Direction::Downstream>({u}, {wg}));
EXPECT_TRUE(ct.hasPath<Graph::Direction::Downstream>({i}, {wg}));
EXPECT_TRUE(ct.hasPath<Graph::Direction::Downstream>({i}, {wf}));
EXPECT_TRUE(ct.hasPath<Graph::Direction::Downstream>({i}, {wg, wf}));
EXPECT_FALSE(ct.hasPath<Graph::Direction::Downstream>({wg, wf}, {j}));
auto coords = Transformer(&ct);
coords.setCoordinate(wg, Expression::literal(0));
coords.setCoordinate(wf, Expression::literal(1));
EXPECT_FALSE(coords.hasPath({u}, false));
coords.setCoordinate(j, Expression::literal(8));
EXPECT_TRUE(coords.hasPath({u}, false));
EXPECT_FALSE(coords.hasPath({i}, true));
}
TEST_F(CoordinateGraphTest, IncludeEdgeNeighbours)
{
auto ct = CoordinateGraph();
auto size = Expression::literal(1024u);
auto sizeI = Expression::literal(512u);
auto sizeJ = Expression::literal(2u);
auto tileNumber = ct.addElement(MacroTileNumber(0, sizeI, nullptr));
auto tileNumberIgnore = ct.addElement(MacroTileNumber(0, sizeJ, nullptr));
auto tileNumberI = ct.addElement(MacroTileNumber(0, sizeI, nullptr));
auto tileNumberJ = ct.addElement(MacroTileNumber(1, sizeJ, nullptr));
auto tileNumberF = ct.addElement(MacroTileNumber(0, size, nullptr));
auto linear = ct.addElement(Linear(size, nullptr));
ct.addElement(PassThrough(), {tileNumber}, {tileNumberI});
ct.addElement(PassThrough(), {tileNumberIgnore}, {tileNumberJ});
ct.addElement(Flatten(), {tileNumberI, tileNumberJ}, {tileNumberF});
ct.addElement(PassThrough(), {tileNumberF}, {linear});
auto path0 = ct.path<Graph::Direction::Upstream>(std::vector<int>{linear},
std::vector<int>{tileNumber})
.to<std::unordered_set>();
// We should never get tileNumberIgnore
EXPECT_FALSE(path0.contains(tileNumberIgnore));
// We should not yet get tileNumberJ
EXPECT_FALSE(path0.contains(tileNumberJ));
auto path1
= rocRoller::KernelGraph::includeEdgeNeighbours(ct, Graph::Direction::Upstream, path0);
// We should now get tileNumberJ
EXPECT_TRUE(path1.contains(tileNumberJ));
EXPECT_TRUE(path0.size() == path1.size() - 1);
}
class ARCH_CoordinateGraphTest : public GPUContextFixture
{
};
TEST_P(ARCH_CoordinateGraphTest, GPU_TensorTile2DLoadStore04)
{
auto ct = CoordinateGraph();
auto wavefront_size = Expression::literal(64);
auto unit = Expression::literal(1);
auto M = Expression::literal(100);
auto N = Expression::literal(200);
auto K = Expression::literal(300);
int m = 16;
int n = 16;
// A matrix; tag 1; M x K; C-ordering
auto A = ct.addElement(User());
auto Ai = ct.addElement(SubDimension(0, M, K));
auto Aj = ct.addElement(SubDimension(1, K, unit));
ct.addElement(Split(), {A}, {Ai, Aj});
// T tile; tag 3; m x n
auto T = MacroTile({m, n}, MemoryType::VGPR);
auto T_id = ct.addElement(T);
auto i = ct.addElement(T.tileIndex(0));
auto j = ct.addElement(T.tileIndex(1));
ct.addElement(Join(), {i, j}, {T_id});
// tile each dimension of A matrix; each workgroup gets one tile
auto tile_x = ct.addElement(Workgroup(0));
auto tile_y = ct.addElement(Workgroup(1));
ct.addElement(Tile(), {Ai}, {tile_x, i});
ct.addElement(Tile(), {Aj}, {tile_y, j});
EXPECT_EQ(ct.getNodes().to<std::set>(),
std::set<int>({A, Ai, Aj, T_id, i, j, tile_x, tile_y}));
// need preamble so work coords are initialised
auto k = m_context->kernel();
k->setKernelName("TensorTile2DLoadStore04");
k->setKernelDimensions(2);
m_context->schedule(k->preamble());
auto coords = Transformer(&ct, nullptr);
coords.fillExecutionCoordinates(m_context);
coords.setCoordinate(i, Expression::literal(33u));
coords.setCoordinate(j, Expression::literal(2u));
auto exprs = coords.reverse({A});
auto sexpr = Expression::toString(exprs[0]);
EXPECT_EQ(sexpr,
"{Split: Add(Multiply({Tile: Add(Multiply({Workgroup Index X: s0:U32}, "
"16:U32)U32, 33:U32)U32}, 300:I)U32, Multiply({Tile: Add(Multiply({Workgroup "
"Index Y: s1:U32}, 16:U32)U32, 2:U32)U32}, 1:I)U32)U32}");
}
INSTANTIATE_TEST_SUITE_P(ARCH_CoordinateGraphTests,
ARCH_CoordinateGraphTest,
supportedISATuples());
}