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

289 lines
13 KiB
C++

// Copyright Advanced Micro Devices, Inc., or its affiliates.
// SPDX-License-Identifier: MIT
#include <rocRoller/AssemblyKernel.hpp>
#include <rocRoller/CommandSolution.hpp>
#include <rocRoller/Expression.hpp>
#include <rocRoller/KernelGraph/KernelGraph.hpp>
#include <rocRoller/KernelGraph/Transforms/RemapOutputTiles.hpp>
#include <rocRoller/KernelGraph/Transforms/RemapOutputTiles_detail.hpp>
#include <rocRoller/KernelGraph/Utils.hpp>
namespace rocRoller
{
namespace KernelGraph
{
using namespace CoordinateGraph;
using GD = rocRoller::Graph::Direction;
namespace RemapOutputTilesDetail
{
void TileSizeInfo::recordSize(int dim, int tileNumTag, auto direction, auto expr)
{
// TODO: Is there a way to make this safer?
//
// The result of, for example:
//
// matrixSize(M) / tileSize(M)
//
// should fit within a Int32
AssertFatal(0 <= dim && dim < 3, ShowValue(dim));
if(expr != nullptr)
{
if(sizes[dim] == nullptr)
{
sizes[dim] = convert(DataType::Int32, expr);
}
else
{
// They aren't alway identical, but they
// should be equivalent after resolving
// command arguments.
//
// For example: A_size_1 is K; and so is B_size_0
//
// TODO: Emit a command predicate to enforce this
}
}
danglers[{dim, direction}].insert(tileNumTag);
}
TileSizeInfo getTileSizeInfo(KernelGraph const& kgraph)
{
TileSizeInfo info;
auto tileNumTags = kgraph.coordinates.getNodes<MacroTileNumber>().to<std::vector>();
for(auto const& tileNumTag : tileNumTags)
{
if(std::empty(kgraph.coordinates.getNeighbours<GD::Downstream>(tileNumTag)))
{
// If we have no downstream neighbours, we
// will create a new workgroup below this, and
// look upstream
auto tileNum = *kgraph.coordinates.get<MacroTileNumber>(tileNumTag);
info.recordSize(tileNum.dim, tileNumTag, GD::Upstream, tileNum.size);
}
if(std::empty(kgraph.coordinates.getNeighbours<GD::Upstream>(tileNumTag)))
{
auto tileNum = *kgraph.coordinates.get<MacroTileNumber>(tileNumTag);
info.recordSize(tileNum.dim, tileNumTag, GD::Downstream, tileNum.size);
}
}
return info;
}
int workgroupDimensions(TileSizeInfo const& info)
{
if(info.sizes[0] != nullptr && info.sizes[1] != nullptr && info.sizes[2] != nullptr)
return 3;
if(info.sizes[0] != nullptr && info.sizes[1] != nullptr)
return 2;
if(info.sizes[0] != nullptr)
return 1;
Throw<FatalError>("Invalid number of dimensions.");
}
Expression::ExpressionPtr totalNumberOfWorkgroups(TileSizeInfo const& info)
{
AssertFatal(info.sizes[0] != nullptr);
auto rv = info.sizes[0];
for(int i = 1; i < 3; ++i)
if(info.sizes[i] != nullptr)
rv = rv * info.sizes[i];
return rv;
}
void connectWorkgroupsWithMapping(TileSizeInfo const& info,
rocRoller::KernelGraph::KernelGraph& graph,
int dimension,
Expression::ExpressionPtr size)
{
auto totalSize = totalNumberOfWorkgroups(info);
auto numDims = workgroupDimensions(info);
AssertFatal(numDims == 2, ShowValue(numDims));
AssertFatal(dimension == 0 || dimension == 1, ShowValue(dimension));
for(auto direction : {GD::Downstream, GD::Upstream})
{
// Downstream: Starting at wgTop looking down
// Upstream: Starting at wgBot looking up
auto remappedDims = workgroupMapping(info, graph, direction, dimension, size);
std::array<int, 2> tileNumTags
= {remappedDims.parallelDim, remappedDims.perpendicularDim};
for(auto dim = 0; dim < numDims; ++dim)
{
for(auto tileNumTag : info.danglers.at({dim, direction}))
{
if(direction == GD::Upstream)
{
Log::debug("KernelGraph::RemapOutputTiles: Adding PassThrough "
"from tile {} to mapped-tile {} (size {})",
tileNumTag,
tileNumTags[dim],
toString(info.sizes[dim]));
graph.coordinates.addElement(
PassThrough(), {tileNumTag}, {tileNumTags[dim]});
}
else
{
Log::debug("KernelGraph::RemapOutputTiles: Adding PassThrough "
"from mapped-tile {} (size {}) to tile {}",
tileNumTags[dim],
toString(info.sizes[dim]),
tileNumTag);
graph.coordinates.addElement(
PassThrough(), {tileNumTags[dim]}, {tileNumTag});
}
}
}
}
}
RemappedDimensions workgroupMapping(TileSizeInfo const& info,
rocRoller::KernelGraph::KernelGraph& graph,
GD direction,
uint dimension,
Expression::ExpressionPtr size)
{
AssertFatal(dimension == 0 || dimension == 1);
AssertFatal(workgroupDimensions(info) == 2);
auto totalSize = totalNumberOfWorkgroups(info);
// This MacroTileNumber will be connected to a workgroup in
// later transformation or be used in AddStreamK with dimension
// K to create a flattened tile space.
auto workgroup
= graph.coordinates.addElement(MacroTileNumber(0, totalSize, nullptr));
// Downstream: Starting at workgroup looking down (forward transform)
// Upstream: Starting at workgroup looking up (reverse transform)
using ExpressionPtr = Expression::ExpressionPtr;
using ExpressionPtrPair = std::pair<ExpressionPtr, ExpressionPtr>;
using ExpressionPtrVectorPair
= std::pair<std::vector<ExpressionPtr>, std::vector<ExpressionPtr>>;
auto one = Expression::literal(1);
auto zero = Expression::literal(0);
auto parallelSize = info.sizes[dimension];
auto perpendicularSize = info.sizes[1 - dimension];
auto blockSize = convert(DataType::Int32, size * perpendicularSize);
setComment(blockSize, "WGM block size");
auto mainBlockSize = convert(DataType::Int32, totalSize / blockSize) * blockSize;
setComment(mainBlockSize, "WGM main block size");
auto tailBlockSize = convert(DataType::Int32, parallelSize % size);
setComment(tailBlockSize, "WGM tail block size");
auto groupNumber = graph.coordinates.addElement(Linear());
auto groupIndex = graph.coordinates.addElement(Linear(size, nullptr));
auto blockNumber = graph.coordinates.addElement(Linear());
auto blockIndex = graph.coordinates.addElement(Linear(perpendicularSize, nullptr));
auto mainBlockNumber = graph.coordinates.addElement(Linear());
auto mainBlockIndex = graph.coordinates.addElement(Linear(mainBlockSize, nullptr));
auto tailBlockNumber = graph.coordinates.addElement(Linear());
auto tailBlockIndex = graph.coordinates.addElement(Linear(tailBlockSize, nullptr));
auto parallel = graph.coordinates.addElement(Linear(parallelSize, nullptr));
auto perpendicular
= graph.coordinates.addElement(Linear(perpendicularSize, nullptr));
// 0 argument is mainBlockNumber
auto condition
= Expression::positionalArgument(0, Register::Type::Scalar, DataType::UInt32)
== Expression::literal(0);
ExpressionPtrVectorPair stridesParallel{{zero, size, one, zero},
{zero, zero, zero, one}};
ExpressionPtrPair initialValuesParallel{
nullptr, convert(DataType::Int32, parallelSize / size) * size};
ExpressionPtrVectorPair stridesPerpendicular{{zero, one, zero}, {zero, zero, one}};
ExpressionPtrPair initialValuesPerpendicular{nullptr, nullptr};
if(direction == GD::Upstream)
{
graph.coordinates.addElement(PiecewiseAffineJoin(condition,
stridesPerpendicular,
initialValuesPerpendicular),
{perpendicular},
{mainBlockNumber, blockIndex, tailBlockNumber});
graph.coordinates.addElement(
PiecewiseAffineJoin(condition, stridesParallel, initialValuesParallel),
{parallel},
{mainBlockNumber, blockNumber, groupIndex, tailBlockIndex});
graph.coordinates.addElement(
Flatten(), {tailBlockNumber, tailBlockIndex}, {mainBlockIndex});
graph.coordinates.addElement(
Flatten(), {blockNumber, blockIndex}, {groupNumber});
graph.coordinates.addElement(Flatten(), {groupNumber, groupIndex}, {workgroup});
graph.coordinates.addElement(
Flatten(), {mainBlockNumber, mainBlockIndex}, {workgroup});
}
else
{
graph.coordinates.addElement(
Tile(), {workgroup}, {mainBlockNumber, mainBlockIndex});
graph.coordinates.addElement(Tile(), {workgroup}, {groupNumber, groupIndex});
graph.coordinates.addElement(Tile(), {groupNumber}, {blockNumber, blockIndex});
graph.coordinates.addElement(
Tile(), {mainBlockIndex}, {tailBlockNumber, tailBlockIndex});
graph.coordinates.addElement(
PiecewiseAffineJoin(condition, stridesParallel, initialValuesParallel),
{mainBlockNumber, blockNumber, groupIndex, tailBlockIndex},
{parallel});
graph.coordinates.addElement(PiecewiseAffineJoin(condition,
stridesPerpendicular,
initialValuesPerpendicular),
{mainBlockNumber, blockIndex, tailBlockNumber},
{perpendicular});
}
if(dimension == 0)
return {workgroup, parallel, perpendicular};
return {workgroup, perpendicular, parallel};
}
}
RemapOutputTiles::RemapOutputTiles(std::optional<int> workgroupMappingDim,
Expression::ExpressionPtr workgroupMappingValue)
: m_workgroupMappingDim(workgroupMappingDim)
, m_workgroupMappingValue(workgroupMappingValue)
{
}
KernelGraph RemapOutputTiles::apply(KernelGraph const& original)
{
using namespace RemapOutputTilesDetail;
auto kgraph = original;
if(m_workgroupMappingDim.has_value())
{
auto info = getTileSizeInfo(original);
connectWorkgroupsWithMapping(
info, kgraph, m_workgroupMappingDim.value(), m_workgroupMappingValue);
}
return kgraph;
}
}
}