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

699 lines
29 KiB
C++

// Copyright Advanced Micro Devices, Inc., or its affiliates.
// SPDX-License-Identifier: MIT
#include <catch2/catch_test_macros.hpp>
#include <rocRoller/KernelGraph/ControlGraph/ControlEdge_fwd.hpp>
#include <rocRoller/KernelGraph/ControlGraph/ControlGraph.hpp>
#include <rocRoller/KernelGraph/ControlGraph/Operation_fwd.hpp>
#include <rocRoller/KernelGraph/CoordinateGraph/Dimension.hpp>
#include <rocRoller/KernelGraph/KernelGraph.hpp>
#include <rocRoller/KernelGraph/Utils.hpp>
#include <common/SourceMatcher.hpp>
using namespace rocRoller;
using namespace KernelGraph::ControlGraph;
TEST_CASE("ControlGraph Basic", "[control-graph]")
{
ControlGraph control = ControlGraph();
int kernel_index = control.addElement(Kernel());
int loadA_index = control.addElement(LoadLinear(DataType::Float));
int loadB_index = control.addElement(LoadLinear(DataType::Float));
int body1_index = control.addElement(Body(), {kernel_index}, {loadA_index});
int body2_index = control.addElement(Body(), {kernel_index}, {loadB_index});
int add_index = control.addElement(Assign());
int sequence1_index = control.addElement(Sequence(), {loadA_index}, {add_index});
int sequence2_index = control.addElement(Sequence(), {loadB_index}, {add_index});
int mul_index = control.addElement(Assign());
int sequence3_index = control.addElement(Sequence(), {add_index}, {mul_index});
int storeC_index = control.addElement(StoreLinear());
control.chain<Sequence>(loadB_index, mul_index, storeC_index);
{
std::vector<int> root = control.roots().to<std::vector>();
REQUIRE(root.size() == 1);
REQUIRE(root[0] == kernel_index);
}
{
auto outputs = control.getOutputNodeIndices<Body>(kernel_index).to<std::vector>();
REQUIRE(outputs.size() == 2);
auto outputs2 = control.getOutputNodeIndices<Sequence>(kernel_index).to<std::vector>();
REQUIRE(outputs2.size() == 0);
}
{
std::vector<int> nodes1 = control.childNodes(kernel_index).to<std::vector>();
REQUIRE(nodes1.size() == 2);
std::vector<int> edges1 = control.getNeighbours<Graph::Direction::Downstream>(kernel_index);
REQUIRE(edges1.size() == 2);
REQUIRE(nodes1.size() == edges1.size());
std::vector<int> nodes2 = control.getNeighbours<Graph::Direction::Upstream>(edges1[0]);
REQUIRE(nodes2.size() == 1);
CHECK(nodes2[0] == kernel_index);
std::vector<int> nodes3 = control.getNeighbours<Graph::Direction::Upstream>(edges1[1]);
REQUIRE(nodes3.size() == 1);
CHECK(nodes3[0] == kernel_index);
}
{
std::vector<int> nodes4 = control.parentNodes(loadA_index).to<std::vector>();
REQUIRE(nodes4.size() == 1);
CHECK(nodes4[0] == kernel_index);
auto inputs = control.getInputNodeIndices<Body>(loadA_index).to<std::vector>();
REQUIRE(inputs.size() == 1);
CHECK(inputs.at(0) == kernel_index);
CHECK(control.getInputNodeIndices<Sequence>(loadA_index).to<std::vector>().empty());
CHECK(control.getInputNodeIndices<Initialize>(loadA_index).to<std::vector>().empty());
CHECK(control.getInputNodeIndices<ForLoopIncrement>(loadA_index).to<std::vector>().empty());
}
{
std::vector<int> edges2 = control.getNeighbours<Graph::Direction::Downstream>(loadA_index);
REQUIRE(edges2.size() == 1);
std::vector<int> nodes5 = control.parentNodes(loadB_index).to<std::vector>();
REQUIRE(nodes5.size() == 1);
CHECK(nodes5[0] == kernel_index);
std::vector<int> edges3 = control.getNeighbours<Graph::Direction::Downstream>(loadB_index);
CHECK(edges3.size() == 2);
}
{
std::vector<int> nodes6 = control.childNodes(loadA_index).to<std::vector>();
REQUIRE(nodes6.size() == 1);
CHECK(nodes6[0] == add_index);
std::vector<int> nodes7 = control.childNodes(add_index).to<std::vector>();
REQUIRE(nodes7.size() == 1);
CHECK(nodes7[0] == mul_index);
}
{
std::vector<int> edges4 = control.getNeighbours<Graph::Direction::Upstream>(storeC_index);
REQUIRE(edges4.size() == 1);
std::vector<int> nodes8 = control.getNeighbours<Graph::Direction::Upstream>(edges4[0]);
REQUIRE(nodes8.size() == 1);
CHECK(nodes8[0] == mul_index);
}
CHECK(NormalizedSource(control.toDOT()) == NormalizedSource(R".(
digraph {
"1"[label="Kernel(1)"];
"2"[label="LoadLinear Value: Float(2)"];
"3"[label="LoadLinear Value: Float(3)"];
"4"[label="Body(4)",shape=box];
"5"[label="Body(5)",shape=box];
"6"[label="Assign Count nullptr(6)"];
"7"[label="Sequence(7)",shape=box];
"8"[label="Sequence(8)",shape=box];
"9"[label="Assign Count nullptr(9)"];
"10"[label="Sequence(10)",shape=box];
"11"[label="StoreLinear(11)"];
"12"[label="Sequence(12)",shape=box];
"13"[label="Sequence(13)",shape=box];
"1" -> "4"
"1" -> "5"
"2" -> "7"
"3" -> "8"
"3" -> "12"
"4" -> "2"
"5" -> "3"
"6" -> "10"
"7" -> "6"
"8" -> "6"
"9" -> "13"
"10" -> "9"
"12" -> "9"
"13" -> "11"
}
)."));
SECTION("compareNodes throws on invalid inputs")
{
auto checkAllPolicies = [&](auto const policy) {
CHECK_THROWS_AS(control.compareNodes(policy, loadA_index, loadA_index), FatalError);
CHECK_THROWS_AS(control.compareNodes(policy, loadA_index, sequence1_index), FatalError);
CHECK_THROWS_AS(control.compareNodes(policy, sequence2_index, loadB_index), FatalError);
CHECK_THROWS(control.compareNodes(policy, loadA_index, 9000));
CHECK_THROWS(control.compareNodes(policy, 9000, loadB_index));
};
checkAllPolicies(rocRoller::UpdateCache);
checkAllPolicies(rocRoller::CacheOnly);
checkAllPolicies(rocRoller::UseCacheIfAvailable);
checkAllPolicies(rocRoller::IgnoreCache);
}
SECTION("nodesInBody / nodesAfter / nodesBefore / nodesContaining")
{
CHECK((std::set{loadA_index, loadB_index, add_index, mul_index, storeC_index})
== control.nodesInBody(kernel_index).to<std::set>());
CHECK((std::set{add_index, mul_index, storeC_index})
== control.nodesAfter(loadA_index).to<std::set>());
CHECK(control.nodesBefore(loadB_index).to<std::set>().empty());
CHECK(std::set{kernel_index} == control.nodesContaining(loadA_index).to<std::set>());
CHECK(std::set{kernel_index} == control.nodesContaining(storeC_index).to<std::set>());
}
SECTION("compareNodes ordering")
{
auto checkAllPolicies = [&](auto const policy) {
CHECK(NodeOrdering::LeftFirst == control.compareNodes(policy, loadA_index, mul_index));
CHECK(NodeOrdering::Undefined
== control.compareNodes(policy, loadA_index, loadB_index));
CHECK(NodeOrdering::RightFirst == control.compareNodes(policy, mul_index, add_index));
CHECK(NodeOrdering::LeftInBodyOfRight
== control.compareNodes(policy, mul_index, kernel_index));
CHECK(NodeOrdering::RightInBodyOfLeft
== control.compareNodes(policy, kernel_index, loadB_index));
};
checkAllPolicies(rocRoller::UpdateCache);
checkAllPolicies(rocRoller::CacheOnly);
checkAllPolicies(rocRoller::UseCacheIfAvailable);
checkAllPolicies(rocRoller::IgnoreCache);
CHECK((std::set{loadA_index, loadB_index, add_index})
== control.nodesBefore(mul_index).to<std::set>());
}
}
TEST_CASE("ControlGraph BeforeAfter", "[control-graph]")
{
ControlGraph control = ControlGraph();
int kernel = control.addElement(Kernel());
int loadA = control.addElement(LoadLinear(DataType::Float));
int loadB = control.addElement(LoadLinear(DataType::Float));
control.addElement(Body(), {kernel}, {loadA});
control.addElement(Body(), {kernel}, {loadB});
int add = control.addElement(Assign());
control.addElement(Sequence(), {loadA}, {add});
control.addElement(Sequence(), {loadB}, {add});
int forOp = control.addElement(ForLoopOp());
control.addElement(Sequence(), {loadA}, {forOp});
int forInit = control.addElement(Assign());
int forInc = control.addElement(Assign());
control.addElement(Initialize(), {forOp}, {forInit});
control.addElement(ForLoopIncrement(), {forOp}, {forInc});
int assign1 = control.addElement(Assign());
control.addElement(Body(), {forOp}, {assign1});
int loadC = control.addElement(LoadLinear(DataType::Float));
control.addElement(Sequence(), {assign1}, {loadC});
int assign2 = control.addElement(Assign());
control.addElement(Body(), {forOp}, {assign2});
int loadD = control.addElement(LoadLinear(DataType::Float));
control.addElement(Sequence(), {assign2}, {loadD});
int assign3 = control.addElement(Assign());
control.addElement(Sequence(), {loadC}, {assign3});
control.addElement(Sequence(), {loadD}, {assign3});
int storeD = control.addElement(StoreLinear());
control.addElement(Sequence(), {assign3}, {storeD});
int scope3 = control.addElement(Scope());
control.addElement(Sequence(), {forOp}, {scope3});
int mul = control.addElement(Assign());
int sequence3 = control.addElement(Body(), {scope3}, {mul});
int storeE = control.addElement(StoreLinear());
int sequence5 = control.addElement(Sequence(), {mul}, {storeE});
CHECK((std::vector{kernel}) == control.roots().to<std::vector>());
CHECK(control.nodesAfter(kernel).to<std::set>().empty());
{
auto expected = control.getNodes().to<std::set>();
expected.erase(kernel);
CHECK(expected == control.nodesInBody(kernel).to<std::set>());
}
CHECK((std::set{scope3, mul, storeE}) == control.nodesAfter(forOp).to<std::set>());
// It doesn't walk up ForLoopIncrement edges yet.
CHECK((std::set{scope3, mul, storeE}) == control.nodesAfter(forInc).to<std::set>());
CHECK((std::set{scope3, kernel}) == control.nodesContaining(storeE).to<std::set>());
SECTION("compareNodes ordering")
{
auto checkAllPolicies = [&](auto const policy) {
CHECK(NodeOrdering::LeftFirst == control.compareNodes(policy, assign2, assign3));
CHECK(NodeOrdering::LeftFirst == control.compareNodes(policy, assign2, mul));
CHECK(NodeOrdering::Undefined == control.compareNodes(policy, assign1, assign2));
CHECK(NodeOrdering::RightFirst == control.compareNodes(policy, forInc, forInit));
CHECK(NodeOrdering::Undefined == control.compareNodes(policy, loadA, loadB));
CHECK(NodeOrdering::RightInBodyOfLeft == control.compareNodes(policy, forOp, assign3));
CHECK(NodeOrdering::LeftInBodyOfRight == control.compareNodes(policy, mul, scope3));
CHECK(NodeOrdering::LeftFirst == control.compareNodes(policy, loadC, storeD));
CHECK(NodeOrdering::LeftFirst == control.compareNodes(policy, loadD, storeD));
};
checkAllPolicies(rocRoller::UpdateCache);
checkAllPolicies(rocRoller::CacheOnly);
checkAllPolicies(rocRoller::UseCacheIfAvailable);
checkAllPolicies(rocRoller::IgnoreCache);
}
SECTION("containingAncestors and controlStack")
{
{
auto parents = KernelGraph::containingAncestors(forInit, control).to<std::vector>();
std::vector<int> parentNodes;
for(auto const& [node, edge] : parents)
parentNodes.push_back(node);
CHECK((std::vector{forOp, kernel}) == parentNodes);
CHECK(std::holds_alternative<Initialize>(parents.at(0).second));
CHECK(std::holds_alternative<Body>(parents.at(1).second));
CHECK((std::deque{kernel, forOp, forInit})
== KernelGraph::controlStack(forInit, control));
}
{
auto parents = KernelGraph::containingAncestors(storeD, control).to<std::vector>();
std::vector<int> parentNodes;
for(auto const& [node, edge] : parents)
parentNodes.push_back(node);
CHECK((std::vector{forOp, kernel}) == parentNodes);
CHECK(std::holds_alternative<Body>(parents.at(0).second));
CHECK(std::holds_alternative<Body>(parents.at(1).second));
CHECK((std::deque{kernel, forOp, storeD})
== KernelGraph::controlStack(storeD, control));
}
{
auto parents = KernelGraph::containingAncestors(storeE, control).to<std::vector>();
std::vector<int> parentNodes;
for(auto const& [node, edge] : parents)
parentNodes.push_back(node);
CHECK((std::vector{scope3, kernel}) == parentNodes);
CHECK(std::holds_alternative<Body>(parents.at(0).second));
CHECK(std::holds_alternative<Body>(parents.at(1).second));
CHECK((std::deque{kernel, scope3, storeE})
== KernelGraph::controlStack(storeE, control));
}
}
SECTION("nodeOrderTableString")
{
CHECK(NormalizedSource(control.nodeOrderTableString()) == NormalizedSource(R".(
\ 1 2 3 6 9 11 12 15 17 19 21 23 26 28 30 32
1| --- RIB RIB RIB RIB RIB RIB RIB RIB RIB RIB RIB RIB RIB RIB RIB | 1
2| LIB --- und LF LF LF LF LF LF LF LF LF LF LF LF LF | 2
3| LIB und --- LF und und und und und und und und und und und und | 3
6| LIB RF RF --- und und und und und und und und und und und und | 6
9| LIB RF und und --- RIB RIB RIB RIB RIB RIB RIB RIB LF LF LF | 9
11| LIB RF und und LIB --- LF LF LF LF LF LF LF LF LF LF | 11
12| LIB RF und und LIB RF --- RF RF RF RF RF RF LF LF LF | 12
15| LIB RF und und LIB RF LF --- LF und und LF LF LF LF LF | 15
17| LIB RF und und LIB RF LF RF --- und und LF LF LF LF LF | 17
19| LIB RF und und LIB RF LF und und --- LF LF LF LF LF LF | 19
21| LIB RF und und LIB RF LF und und RF --- LF LF LF LF LF | 21
23| LIB RF und und LIB RF LF RF RF RF RF --- LF LF LF LF | 23
26| LIB RF und und LIB RF LF RF RF RF RF RF --- LF LF LF | 26
28| LIB RF und und RF RF RF RF RF RF RF RF RF --- RIB RIB | 28
30| LIB RF und und RF RF RF RF RF RF RF RF RF LIB --- LF | 30
32| LIB RF und und RF RF RF RF RF RF RF RF RF LIB RF --- | 32
| 1 2 3 6 9 11 12 15 17 19 21 23 26 28 30 32
)."));
}
SECTION("toDOT")
{
CHECK(NormalizedSource(control.toDOT()) == NormalizedSource(R".(
digraph {
"1"[label="Kernel(1)"];
"2"[label="LoadLinear Value: Float(2)"];
"3"[label="LoadLinear Value: Float(3)"];
"4"[label="Body(4)",shape=box];
"5"[label="Body(5)",shape=box];
"6"[label="Assign Count nullptr(6)"];
"7"[label="Sequence(7)",shape=box];
"8"[label="Sequence(8)",shape=box];
"9"[label="ForLoopOp : nullptr(9)"];
"10"[label="Sequence(10)",shape=box];
"11"[label="Assign Count nullptr(11)"];
"12"[label="Assign Count nullptr(12)"];
"13"[label="Initialize(13)",shape=box];
"14"[label="ForLoopIncrement(14)",shape=box];
"15"[label="Assign Count nullptr(15)"];
"16"[label="Body(16)",shape=box];
"17"[label="LoadLinear Value: Float(17)"];
"18"[label="Sequence(18)",shape=box];
"19"[label="Assign Count nullptr(19)"];
"20"[label="Body(20)",shape=box];
"21"[label="LoadLinear Value: Float(21)"];
"22"[label="Sequence(22)",shape=box];
"23"[label="Assign Count nullptr(23)"];
"24"[label="Sequence(24)",shape=box];
"25"[label="Sequence(25)",shape=box];
"26"[label="StoreLinear(26)"];
"27"[label="Sequence(27)",shape=box];
"28"[label="Scope(28)"];
"29"[label="Sequence(29)",shape=box];
"30"[label="Assign Count nullptr(30)"];
"31"[label="Body(31)",shape=box];
"32"[label="StoreLinear(32)"];
"33"[label="Sequence(33)",shape=box];
"1" -> "4"
"1" -> "5"
"2" -> "7"
"2" -> "10"
"3" -> "8"
"4" -> "2"
"5" -> "3"
"7" -> "6"
"8" -> "6"
"9" -> "13"
"9" -> "14"
"9" -> "16"
"9" -> "20"
"9" -> "29"
"10" -> "9"
"13" -> "11"
"14" -> "12"
"15" -> "18"
"16" -> "15"
"17" -> "24"
"18" -> "17"
"19" -> "22"
"20" -> "19"
"21" -> "25"
"22" -> "21"
"23" -> "27"
"24" -> "23"
"25" -> "23"
"27" -> "26"
"28" -> "31"
"29" -> "28"
"30" -> "33"
"31" -> "30"
"33" -> "32"
}
)."));
}
}
TEST_CASE("ControlGraph Conditional", "[control-graph]")
{
ControlGraph control = ControlGraph();
int kernelIndex = control.addElement(Kernel());
int loadAIndex = control.addElement(LoadLinear(DataType::Float));
int body1Index = control.addElement(Body(), {kernelIndex}, {loadAIndex});
int condOp = control.addElement(ConditionalOp());
control.addElement(Sequence(), {loadAIndex}, {condOp});
int addIndex = control.addElement(Assign());
int mulIndex = control.addElement(Assign());
int trueIndex = control.addElement(Body(), {condOp}, {addIndex});
int falseIndex = control.addElement(Else(), {condOp}, {mulIndex});
int storeCIndex = control.addElement(StoreLinear());
control.addElement(Sequence(), {condOp}, {storeCIndex});
{
std::vector<int> root = control.roots().to<std::vector>();
REQUIRE(root.size() == 1);
CHECK(root[0] == kernelIndex);
}
CHECK(control.getOutputNodeIndices<Body>(condOp).to<std::vector>().size() == 1);
CHECK(control.getOutputNodeIndices<Else>(condOp).to<std::vector>().size() == 1);
CHECK(NormalizedSource(control.toDOT()) == NormalizedSource(R".(
digraph {
"1"[label="Kernel(1)"];
"2"[label="LoadLinear Value: Float(2)"];
"3"[label="Body(3)",shape=box];
"4"[label="ConditionalOp : nullptr(4)"];
"5"[label="Sequence(5)",shape=box];
"6"[label="Assign Count nullptr(6)"];
"7"[label="Assign Count nullptr(7)"];
"8"[label="Body(8)",shape=box];
"9"[label="Else(9)",shape=box];
"10"[label="StoreLinear(10)"];
"11"[label="Sequence(11)",shape=box];
"1" -> "3"
"2" -> "5"
"3" -> "2"
"4" -> "8"
"4" -> "9"
"4" -> "11"
"5" -> "4"
"8" -> "6"
"9" -> "7"
"11" -> "10"
}
)."));
}
TEST_CASE("ControlGraph containingAncestors distinguishes Body vs Else edges", "[control-graph]")
{
ControlGraph control = ControlGraph();
int kernel = control.addElement(Kernel());
int condOp = control.addElement(ConditionalOp());
control.addElement(Body(), {kernel}, {condOp});
int thenAssign = control.addElement(Assign());
int elseAssign = control.addElement(Assign());
control.addElement(Body(), {condOp}, {thenAssign});
control.addElement(Else(), {condOp}, {elseAssign});
// Both assigns have condOp as their immediate containing parent.
auto thenParent = KernelGraph::containingAncestors(thenAssign, control).take(1).only();
auto elseParent = KernelGraph::containingAncestors(elseAssign, control).take(1).only();
REQUIRE(thenParent.has_value());
REQUIRE(elseParent.has_value());
CHECK(thenParent->first == condOp);
CHECK(elseParent->first == condOp);
// The edges must be distinct: Body for then-branch, Else for else-branch.
CHECK(std::holds_alternative<Body>(thenParent->second));
CHECK(std::holds_alternative<Else>(elseParent->second));
}
TEST_CASE("ControlGraph AssertOp", "[control-graph]")
{
using GD = rocRoller::Graph::Direction;
ControlGraph control = ControlGraph();
int kernelIndex = control.addElement(Kernel());
int assertOp = control.addElement(AssertOp());
control.addElement(Body(), {kernelIndex}, {assertOp});
int dummyIndex = control.addElement(Assign());
int passedIndex = control.addElement(Sequence(), {assertOp}, {dummyIndex});
auto assertOps
= control
.findNodes(
kernelIndex,
[&](int tag) -> bool { return isOperation<AssertOp>(control.getElement(tag)); },
GD::Downstream)
.to<std::vector>();
CHECK(assertOps.size() == 1);
}
TEST_CASE("ControlGraph getSetCoordinates", "[control-graph]")
{
KernelGraph::KernelGraph kg;
using namespace KernelGraph;
namespace CT = rocRoller::KernelGraph::CoordinateGraph;
auto five = Expression::literal(5);
auto four = Expression::literal(4);
int topSet1 = kg.control.addElement(SetCoordinate{five});
int topSet2 = kg.control.addElement(SetCoordinate{five});
int notTopSet1 = kg.control.addElement(SetCoordinate{four});
int topSet3 = kg.control.addElement(SetCoordinate{five});
int load1 = kg.control.addElement(LoadLDSTile{DataType::Float});
int load2 = kg.control.addElement(LoadLDSTile{DataType::Float});
int load3 = kg.control.addElement(LoadLDSTile{DataType::Float});
kg.control.addElement(Body{}, {topSet1}, {load1});
CHECK_THROWS_AS(getTopSetCoordinate(kg, load1), FatalError);
CHECK_THROWS_AS(getSetCoordinateForDim(kg, 1, load1), FatalError);
CHECK_THROWS_AS(getUnrollValueForOp(kg, 1, load1), FatalError);
kg.mapper.connect<CT::Unroll>(topSet1, 1);
CHECK(topSet1 == getTopSetCoordinate(kg, load1));
CHECK_THROWS_AS(getSetCoordinateForDim(kg, 2, load1), FatalError);
CHECK_THROWS_AS(getUnrollValueForOp(kg, 2, load1), FatalError);
CHECK(topSet1 == getSetCoordinateForDim(kg, 1, load1));
CHECK(5 == getUnrollValueForOp(kg, 1, load1));
auto loop = kg.control.addElement(ForLoopOp{});
kg.control.chain<Body>(topSet2, notTopSet1, loop, topSet3, load3);
kg.control.chain<Body>(notTopSet1, load2);
CHECK_THROWS_AS(getTopSetCoordinate(kg, load3), FatalError);
kg.mapper.connect<CT::Unroll>(topSet2, 2);
kg.mapper.connect<CT::Unroll>(notTopSet1, 1);
kg.mapper.connect<CT::Unroll>(topSet3, 3);
CHECK(topSet2 == getTopSetCoordinate(kg, load2));
CHECK(topSet3 == getTopSetCoordinate(kg, load3));
CHECK_THROWS_AS(getSetCoordinateForDim(kg, 5, load3), FatalError);
CHECK(topSet2 == getSetCoordinateForDim(kg, 2, load3));
CHECK(notTopSet1 == getSetCoordinateForDim(kg, 1, load3));
CHECK(topSet3 == getSetCoordinateForDim(kg, 3, load3));
CHECK_THROWS_AS(getUnrollValueForOp(kg, 5, load3), FatalError);
CHECK(5u == getUnrollValueForOp(kg, 2, load3));
CHECK(4u == getUnrollValueForOp(kg, 1, load3));
CHECK(5u == getUnrollValueForOp(kg, 3, load3));
CHECK((std::set{topSet1, topSet3}) == getTopSetCoordinates(kg, {load1, load3}));
CHECK((std::set{topSet1, topSet2, topSet3}) == getTopSetCoordinates(kg, {load1, load2, load3}));
}
TEST_CASE("ControlGraph hasExistingSetCoordinate", "[control-graph]")
{
KernelGraph::KernelGraph kg;
using namespace KernelGraph;
namespace CT = rocRoller::KernelGraph::CoordinateGraph;
int load1 = kg.control.addElement(LoadLDSTile{DataType::Float});
int load2 = kg.control.addElement(LoadLDSTile{DataType::Float});
auto coord1 = 1;
auto coord2 = 2;
auto coord3 = 3;
auto unrollDim = 145;
int set1 = kg.control.addElement(SetCoordinate{Expression::literal(coord1)});
kg.mapper.connect<CT::Unroll>(set1, unrollDim);
int set2 = kg.control.addElement(SetCoordinate{Expression::literal(coord2)});
kg.mapper.connect<CT::Unroll>(set2, unrollDim);
auto loop = kg.control.addElement(ForLoopOp{});
kg.control.chain<Body>(loop, set1, set2, load1);
kg.control.chain<Sequence>(set1, load2);
CHECK(hasExistingSetCoordinate(kg, load1, coord1, unrollDim));
CHECK_FALSE(hasExistingSetCoordinate(kg, load1, coord3, unrollDim));
CHECK(hasExistingSetCoordinate(kg, load1, coord2, unrollDim));
CHECK_FALSE(hasExistingSetCoordinate(kg, load2, coord1, unrollDim));
}
TEST_CASE("ControlGraph ModifyOrder", "[control-graph]")
{
// This control graph is from the Basic test with two changes:
// - a new Assign node connected by an Initialize edge
// - a new Assign node connected by a Sequence edge
ControlGraph control = ControlGraph();
int kernel_index = control.addElement(Kernel());
int assign1_index = control.addElement(Assign());
int init_index = control.addElement(Initialize(), {kernel_index}, {assign1_index});
int assign2_index = control.addElement(Assign());
int seq_index = control.addElement(Sequence(), {kernel_index}, {assign2_index});
int loadA_index = control.addElement(LoadLinear(DataType::Float));
int loadB_index = control.addElement(LoadLinear(DataType::Float));
int body1_index = control.addElement(Body(), {kernel_index}, {loadA_index});
int body2_index = control.addElement(Body(), {kernel_index}, {loadB_index});
int add_index = control.addElement(Assign());
int sequence1_index = control.addElement(Sequence(), {loadA_index}, {add_index});
int sequence2_index = control.addElement(Sequence(), {loadB_index}, {add_index});
int mul_index = control.addElement(Assign());
int sequence3_index = control.addElement(Sequence(), {add_index}, {mul_index});
int storeC_index = control.addElement(StoreLinear());
control.chain<Sequence>(loadB_index, mul_index, storeC_index);
auto checkAllPolicies = [&](int nodeA, int nodeB, NodeOrdering expectedOrder) {
CHECK(expectedOrder == control.compareNodes(rocRoller::UpdateCache, nodeA, nodeB));
CHECK(expectedOrder == control.compareNodes(rocRoller::CacheOnly, nodeA, nodeB));
CHECK(expectedOrder == control.compareNodes(rocRoller::UseCacheIfAvailable, nodeA, nodeB));
CHECK(expectedOrder == control.compareNodes(rocRoller::IgnoreCache, nodeA, nodeB));
};
// assign1 is via an Initialize edge, so all other nodes (except kernel) are LeftFirst
checkAllPolicies(assign1_index, loadA_index, NodeOrdering::LeftFirst);
checkAllPolicies(assign1_index, loadB_index, NodeOrdering::LeftFirst);
checkAllPolicies(assign1_index, storeC_index, NodeOrdering::LeftFirst);
checkAllPolicies(assign1_index, assign2_index, NodeOrdering::LeftFirst);
// assign2 is via a Sequence edge, so all other nodes (except kernel) are RightFirst
checkAllPolicies(assign2_index, loadA_index, NodeOrdering::RightFirst);
checkAllPolicies(assign2_index, loadB_index, NodeOrdering::RightFirst);
checkAllPolicies(assign2_index, storeC_index, NodeOrdering::RightFirst);
checkAllPolicies(assign2_index, assign1_index, NodeOrdering::RightFirst);
// kernel is the only ancestor of loadA and loadB, both connected via Body
checkAllPolicies(loadA_index, loadB_index, NodeOrdering::Undefined);
// loadA is a parent of storeC with a path via a Sequence edge
checkAllPolicies(storeC_index, loadA_index, NodeOrdering::RightFirst);
// Delete the Sequence edge between loadA and add: no path between loadA and storeC
control.deleteElement(sequence1_index);
checkAllPolicies(storeC_index, loadA_index, NodeOrdering::Undefined);
// Add the Sequence edge back: order should restore
std::ignore = control.addElement(Sequence(), {loadA_index}, {add_index});
checkAllPolicies(storeC_index, loadA_index, NodeOrdering::RightFirst);
// add is a parent of mul (directly connected via a Sequence edge)
checkAllPolicies(add_index, mul_index, NodeOrdering::LeftFirst);
// Delete the Sequence edge between add and mul: order is Undefined
control.deleteElement(sequence3_index);
checkAllPolicies(add_index, mul_index, NodeOrdering::Undefined);
// loadB is a parent of mul with two paths via two Sequence edges
checkAllPolicies(loadB_index, mul_index, NodeOrdering::LeftFirst);
// Delete one Sequence edge: order should still be the same
control.deleteElement(sequence2_index);
checkAllPolicies(loadB_index, mul_index, NodeOrdering::LeftFirst);
}