// Copyright Advanced Micro Devices, Inc., or its affiliates. // SPDX-License-Identifier: MIT #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ExpressionMatchers.hpp" #include "TestContext.hpp" TEST_CASE("Test getForLoopName", "[kernel-graph][unroll]") { using namespace rocRoller; namespace kg = rocRoller::KernelGraph; kg::KernelGraph kgraph; auto [forDim, forOp] = kg::rangeFor(kgraph, Expression::literal(4), KLOOP); auto [forDim2, forOp2] = kg::rangeFor(kgraph, Expression::literal(16), "Another loop"); CHECK(kg::getForLoopName(kgraph, forOp) == KLOOP); CHECK(kg::getForLoopName(kgraph, forOp2) == "Another loop"); } TEST_CASE("Test getUnrollAmount", "[kernel-graph][unroll]") { using namespace rocRoller; namespace kg = rocRoller::KernelGraph; kg::KernelGraph kgraph; auto params = std::make_shared(); params->unrollK = 5; auto [forDim, forOp] = kg::rangeFor(kgraph, Expression::literal(4), KLOOP); auto [forDim2, forOp2] = kg::rangeFor(kgraph, Expression::literal(16), "Another loop"); auto [forDim3, forOp3] = kg::rangeFor(kgraph, Expression::literal(4), XLOOP); CHECK(kg::getUnrollAmount(kgraph, forOp, params) == 5); CHECK(kg::getUnrollAmount(kgraph, forOp2, params) == 1); // X loop is always fully unrolled when length is a constant CHECK(kg::getUnrollAmount(kgraph, forOp3, params) == 4); params->unrollK = 7; CHECK(kg::getUnrollAmount(kgraph, forOp, params) == 7); } TEST_CASE("UnrollLoops simple test", "[kernel-graph][unroll][graph-transforms]") { using namespace rocRoller; namespace kg = rocRoller::KernelGraph; Settings::getInstance()->set(Settings::LogConsole, true); Settings::getInstance()->set(Settings::LogLvl, LogLevel::Debug); Settings::getInstance()->set(Settings::LogGraphs, false); auto ctx = TestContext::ForDefaultTarget(); auto command = std::make_shared(); kg::KernelGraph kgraph; auto [user, wg, wi, vgpr] = kgraph.coordinates.addElements( // kg::CoordinateGraph::User{}, kg::CoordinateGraph::Workgroup{0, 0}, kg::CoordinateGraph::Workitem{0, Expression::literal(64)}, kg::CoordinateGraph::VGPR{}); auto argTag = command->allocateTag(); auto arg = command->allocateArgument(DataType::Int32, argTag, ArgumentType::Size); auto [forDim, forOp] = kg::rangeFor(kgraph, arg->expression(), rocRoller::KLOOP); kgraph.coordinates.addElement(kg::CoordinateGraph::Tile{}, {user}, {wg, wi, forDim}); kgraph.coordinates.addElement(kg::CoordinateGraph::Forget{}, {wg, wi, forDim}, {vgpr}); auto x = std::make_shared( Expression::DataFlowTag{forDim, Register::Type::Vector, DataType::Int32}); auto [kernel, calculate, store] = kgraph.control.addElements( // kg::ControlGraph::Kernel{}, kg::ControlGraph::Assign{Register::Type::Vector, Expression::literal(5) * x}, kg::ControlGraph::StoreLinear{}); kgraph.mapper.connect(calculate, vgpr); kgraph.control.chain(kernel, forOp, calculate); kgraph.control.chain(forOp, store); auto params = std::make_shared(); params->unrollK = 4; SECTION("Only one for loop initially.") { auto ops = kgraph.control.findElements(kgraph.control.isElemType()) .to(); REQUIRE(ops == std::set{forOp}); } SECTION("Create tail loop manually.") { int forLoopDimension = kgraph.coordinates.addElement(kg::CoordinateGraph::ForLoop()); int unrollDimension = kgraph.coordinates.addElement(kg::CoordinateGraph::Unroll(4)); kg::UnrollLoops unroll(params, ctx.get()); auto tail = unroll.createTailLoop(kgraph, forOp, 4, unrollDimension, forLoopDimension); CHECK(tail != std::nullopt); kgraph = kgraph.transform(std::make_shared()); CHECK(kgraph.control.compareNodes(rocRoller::UpdateCache, forOp, *tail) == rocRoller::KernelGraph::ControlGraph::NodeOrdering::LeftFirst); CHECK(kgraph.control.compareNodes(rocRoller::CacheOnly, forOp, *tail) == rocRoller::KernelGraph::ControlGraph::NodeOrdering::LeftFirst); CHECK(kgraph.control.compareNodes(rocRoller::UseCacheIfAvailable, forOp, *tail) == rocRoller::KernelGraph::ControlGraph::NodeOrdering::LeftFirst); CHECK(kgraph.control.compareNodes(rocRoller::IgnoreCache, forOp, *tail) == rocRoller::KernelGraph::ControlGraph::NodeOrdering::LeftFirst); auto ops = kgraph.control.findElements(kgraph.control.isElemType()) .to(); REQUIRE(ops == std::set{forOp, *tail}); auto tailOp = kgraph.control.getNode(*tail); auto [_, tailOpSize] = split(tailOp.condition); CHECK_THAT(tailOpSize, IdenticalTo(arg->expression())); auto unrollInitTag = kgraph.control.getOutputNodeIndices(*tail) .only() .value(); auto unrollInit = kgraph.control.getNode(unrollInitTag); auto origOp = kgraph.control.getNode(forOp); auto [__, origOpSize] = split(origOp.condition); CHECK_THAT(origOpSize, IdenticalTo(unrollInit.expression)); } SECTION("Create tail loop automatically in UnrollLoops xform.") { kgraph = kgraph.transform(std::make_shared(params, ctx.get())); kgraph = kgraph.transform(std::make_shared()); auto ops = kgraph.control.findElements(kgraph.control.isElemType()) .to(); REQUIRE(ops.size() == 2); REQUIRE_THAT(ops, Catch::Matchers::Contains(forOp)); // remove the first loop, the other one will be the new tail loop. ops.erase(forOp); auto tailOpTag = *ops.begin(); auto tailOp = kgraph.control.getNode(tailOpTag); auto [_, tailOpSize] = split(tailOp.condition); CHECK_THAT(tailOpSize, IdenticalTo(arg->expression())); auto unrollInitTag = kgraph.control.getOutputNodeIndices(tailOpTag) .only() .value(); auto unrollInit = kgraph.control.getNode(unrollInitTag); auto origOp = kgraph.control.getNode(forOp); auto [__, origOpSize] = split(origOp.condition); CHECK_THAT(origOpSize, IdenticalTo(unrollInit.expression)); } }