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

456 lines
19 KiB
C++

// Copyright Advanced Micro Devices, Inc., or its affiliates.
// SPDX-License-Identifier: MIT
#pragma once
#include <iostream>
#include <memory>
#include <set>
#include <variant>
#include <rocRoller/Graph/Hypergraph.hpp>
#include <rocRoller/KernelGraph/ControlGraph/ControlGraph.hpp>
#include <rocRoller/KernelGraph/CoordinateGraph/CoordinateEdge_fwd.hpp>
#include <rocRoller/KernelGraph/CoordinateGraph/CoordinateGraph.hpp>
#include <rocRoller/KernelGraph/KernelGraph.hpp>
#include <rocRoller/KernelGraph/Reindexer.hpp>
#include <rocRoller/Expression_fwd.hpp>
#include <rocRoller/AssemblyKernel.hpp>
namespace rocRoller
{
namespace KernelGraph
{
using CTEdge = rocRoller::KernelGraph::CoordinateGraph::Edge;
// clang-format off
template <typename T>
concept CCoordinateEdgeVisitor
= requires(T & vis, int tag, CoordinateGraph::Tile const& edge)
{
{ vis.visitCoordinateEdge(tag, edge) }
-> std::convertible_to<rocRoller::KernelGraph::CoordinateGraph::Edge>;
};
// clang-format on
/***************************
* KernelGraphs rewrite utils
*/
template <typename T>
inline KernelGraph rewriteDimensions(KernelGraph const& k, T& visitor)
{
KernelGraph graph = k;
for(auto const& tag : k.coordinates.getNodes())
{
auto x = k.coordinates.getNode(tag);
auto y
= std::visit([&](auto&& arg) { return visitor.visitDimension(tag, arg); }, x);
graph.coordinates.setElement(tag, y);
}
if constexpr(CCoordinateEdgeVisitor<T>)
{
for(auto const& tag : k.coordinates.getEdges())
{
auto edge = k.coordinates.getEdge(tag);
auto visA = [&](auto&& arg) { return visitor.visitCoordinateEdge(tag, arg); };
auto visB = [&](auto&& arg) { return std::visit(visA, arg); };
auto newEdge = std::visit(visB, edge);
graph.coordinates.setElement(tag, newEdge);
}
}
for(auto const& tag : k.control.getNodes())
{
auto x = k.control.getNode(tag);
auto y
= std::visit([&](auto&& arg) { return visitor.visitOperation(tag, arg); }, x);
graph.control.setElement(tag, y);
}
return graph;
}
#define MAKE_EDGE_VISITOR(CLS) \
virtual void visitEdge(KernelGraph& graph, \
KernelGraph const& original, \
GraphReindexer& reindexer, \
int tag, \
CLS const& edge) \
{ \
copyEdge(graph, original, reindexer, tag); \
}
#define MAKE_OPERATION_VISITOR(CLS) \
virtual void visitOperation(KernelGraph& graph, \
KernelGraph const& original, \
GraphReindexer& reindexer, \
int tag, \
CLS const& dst) \
{ \
copyOperation(graph, original, reindexer, tag); \
if(m_rewriteCoordinates) \
reindexExpressions(graph, reindexer.control.at(tag), reindexer); \
}
/**
* The BaseGraphVisitor implements a "copy" re-write visitor.
*
* To rewrite, for example, only LoadLinear edges, simply
* override the `visitEdge` method.
*/
struct BaseGraphVisitor
{
BaseGraphVisitor(ContextPtr context,
Graph::Direction controlGraphOrder = Graph::Direction::Downstream,
bool rewriteCoordinates = true)
: m_context(context)
, m_controlGraphDirection(controlGraphOrder)
, m_rewriteCoordinates(rewriteCoordinates)
{
}
void copyEdge(KernelGraph& graph,
KernelGraph const& original,
GraphReindexer& reindexer,
int edge)
{
auto location = original.coordinates.getLocation(edge);
std::vector<int> inputs;
for(auto const& input : location.incoming)
{
if(reindexer.coordinates.count(input) == 0)
{
auto newInput
= graph.coordinates.addElement(original.coordinates.getElement(input));
reindexer.coordinates.emplace(input, newInput);
}
inputs.push_back(reindexer.coordinates.at(input));
}
std::vector<int> outputs;
for(auto const& output : location.outgoing)
{
if(reindexer.coordinates.count(output) == 0)
{
auto newOutput
= graph.coordinates.addElement(original.coordinates.getElement(output));
reindexer.coordinates.emplace(output, newOutput);
}
outputs.push_back(reindexer.coordinates.at(output));
}
auto newEdge = graph.coordinates.addElement(
original.coordinates.getElement(edge), inputs, outputs);
reindexer.coordinates.emplace(edge, newEdge);
}
/**
* @brief Reconnect inputs from original operation to new operation.
*/
void reconnectOperation(KernelGraph& graph,
KernelGraph const& original,
GraphReindexer& reindexer,
int new_tag,
int old_tag)
{
auto location = original.control.getLocation(old_tag);
if(m_controlGraphDirection == Graph::Direction::Downstream)
{
for(auto const& input : location.incoming)
{
int parent
= *original.control.getNeighbours<Graph::Direction::Upstream>(input)
.begin();
AssertFatal(reindexer.control.count(parent) > 0,
"Missing control input: ",
ShowValue(old_tag),
ShowValue(input),
ShowValue(parent));
graph.control.addElement(original.control.getElement(input),
{reindexer.control.at(parent)},
{new_tag});
}
}
else
{
for(auto const& output : location.outgoing)
{
int child
= *original.control.getNeighbours<Graph::Direction::Downstream>(output)
.begin();
AssertFatal(reindexer.control.count(child) > 0,
"Missing control output: ",
ShowValue(old_tag),
ShowValue(output),
ShowValue(child));
graph.control.addElement(original.control.getElement(output),
{new_tag},
{reindexer.control.at(child)});
}
}
}
/**
* @brief Reconnect mappings from original operation to new operation.
*/
void reconnectMappings(KernelGraph& graph,
KernelGraph const& original,
GraphReindexer& reindexer,
int new_tag,
int old_tag)
{
if(m_rewriteCoordinates)
{
for(auto const& c : original.mapper.getConnections(old_tag))
{
AssertFatal(reindexer.coordinates.count(c.coordinate) > 0,
"Missing mapped coordinate: ",
ShowValue(old_tag),
ShowValue(c.coordinate));
graph.mapper.connect(
new_tag, reindexer.coordinates.at(c.coordinate), c.connection);
}
}
else
{
for(auto const& c : original.mapper.getConnections(old_tag))
{
graph.mapper.connect(new_tag, c.coordinate, c.connection);
}
}
}
/**
* @brief Copy operation from original graph to new graph.
*
* Inputs and mappings are preserved.
*/
void copyOperation(KernelGraph& graph,
KernelGraph const& original,
GraphReindexer& reindexer,
int tag)
{
auto op = graph.control.addElement(original.control.getElement(tag));
reconnectOperation(graph, original, reindexer, op, tag);
reconnectMappings(graph, original, reindexer, op, tag);
reindexer.control.emplace(tag, op);
}
Expression::ExpressionPtr wavefrontSize() const
{
uint wfs = static_cast<uint>(m_context->kernel()->wavefront_size());
return Expression::literal(wfs);
}
std::array<Expression::ExpressionPtr, 3> workgroupSize() const
{
auto const& wgSize = m_context->kernel()->workgroupSize();
return {
Expression::literal(wgSize[0]),
Expression::literal(wgSize[1]),
Expression::literal(wgSize[2]),
};
}
Expression::ExpressionPtr workgroupCountX() const
{
return m_context->kernel()->workgroupCount(0);
}
Expression::ExpressionPtr workgroupCountY() const
{
return m_context->kernel()->workgroupCount(1);
}
Expression::ExpressionPtr workgroupCountZ() const
{
return m_context->kernel()->workgroupCount(2);
}
Graph::Direction controlGraphDirection() const
{
return m_controlGraphDirection;
}
bool rewriteCoordinates() const
{
return m_rewriteCoordinates;
}
MAKE_EDGE_VISITOR(CoordinateGraph::DataFlow);
MAKE_EDGE_VISITOR(CoordinateGraph::Alias);
MAKE_EDGE_VISITOR(CoordinateGraph::Buffer);
MAKE_EDGE_VISITOR(CoordinateGraph::BaseAddress);
MAKE_EDGE_VISITOR(CoordinateGraph::Offset);
MAKE_EDGE_VISITOR(CoordinateGraph::Stride);
MAKE_EDGE_VISITOR(CoordinateGraph::View);
MAKE_EDGE_VISITOR(CoordinateGraph::TDM);
MAKE_EDGE_VISITOR(CoordinateGraph::ConstructMacroTile);
MAKE_EDGE_VISITOR(CoordinateGraph::DestructMacroTile);
MAKE_EDGE_VISITOR(CoordinateGraph::Flatten);
MAKE_EDGE_VISITOR(CoordinateGraph::Forget);
MAKE_EDGE_VISITOR(CoordinateGraph::Inherit);
MAKE_EDGE_VISITOR(CoordinateGraph::Join);
MAKE_EDGE_VISITOR(CoordinateGraph::MakeOutput);
MAKE_EDGE_VISITOR(CoordinateGraph::PassThrough);
MAKE_EDGE_VISITOR(CoordinateGraph::Split);
MAKE_EDGE_VISITOR(CoordinateGraph::Tile);
MAKE_OPERATION_VISITOR(ControlGraph::AssertOp);
MAKE_OPERATION_VISITOR(ControlGraph::Assign);
MAKE_OPERATION_VISITOR(ControlGraph::Barrier);
MAKE_OPERATION_VISITOR(ControlGraph::Block);
MAKE_OPERATION_VISITOR(ControlGraph::ConditionalOp);
MAKE_OPERATION_VISITOR(ControlGraph::Deallocate);
MAKE_OPERATION_VISITOR(ControlGraph::DoWhileOp);
MAKE_OPERATION_VISITOR(ControlGraph::Exchange);
MAKE_OPERATION_VISITOR(ControlGraph::ForLoopOp);
MAKE_OPERATION_VISITOR(ControlGraph::Kernel);
MAKE_OPERATION_VISITOR(ControlGraph::LoadLDSTile);
MAKE_OPERATION_VISITOR(ControlGraph::LoadLinear);
MAKE_OPERATION_VISITOR(ControlGraph::LoadSGPR);
MAKE_OPERATION_VISITOR(ControlGraph::LoadTileDirect2LDS);
MAKE_OPERATION_VISITOR(ControlGraph::LoadTiledTDMToLDS);
MAKE_OPERATION_VISITOR(ControlGraph::LoadTiled);
MAKE_OPERATION_VISITOR(ControlGraph::LoadVGPR);
MAKE_OPERATION_VISITOR(ControlGraph::Multiply);
MAKE_OPERATION_VISITOR(ControlGraph::NOP);
MAKE_OPERATION_VISITOR(ControlGraph::Scope);
MAKE_OPERATION_VISITOR(ControlGraph::SeedPRNG);
MAKE_OPERATION_VISITOR(ControlGraph::SetCoordinate);
MAKE_OPERATION_VISITOR(ControlGraph::StoreLDSTile);
MAKE_OPERATION_VISITOR(ControlGraph::StoreLinear);
MAKE_OPERATION_VISITOR(ControlGraph::StoreSGPR);
MAKE_OPERATION_VISITOR(ControlGraph::StoreTiled);
MAKE_OPERATION_VISITOR(ControlGraph::StoreVGPR);
MAKE_OPERATION_VISITOR(ControlGraph::TensorContraction);
MAKE_OPERATION_VISITOR(ControlGraph::UnrollOp);
MAKE_OPERATION_VISITOR(ControlGraph::WaitZero);
virtual void visitEdge(KernelGraph& graph,
KernelGraph const& original,
GraphReindexer& reindexer,
int tag,
CoordinateGraph::CoordinateTransformEdge const& edge)
{
std::visit([&](auto&& arg) { visitEdge(graph, original, reindexer, tag, arg); },
edge);
}
virtual void visitEdge(KernelGraph& graph,
KernelGraph const& original,
GraphReindexer& reindexer,
int tag,
CoordinateGraph::DataFlowEdge const& edge)
{
std::visit([&](auto&& arg) { visitEdge(graph, original, reindexer, tag, arg); },
edge);
}
virtual void visitRoot(KernelGraph& graph,
KernelGraph const& original,
GraphReindexer& reindexer,
int tag)
{
copyOperation(graph, original, reindexer, tag);
}
protected:
ContextPtr m_context;
Graph::Direction m_controlGraphDirection;
bool m_rewriteCoordinates;
};
#undef MAKE_OPERATION_VISITOR
#undef MAKE_EDGE_VISITOR
/**
* Apply rewrite visitor to kernel graph.
*
* Edges are visited in topological order.
*
* Internal convenience routine.
*/
template <typename T>
inline KernelGraph rewrite(KernelGraph const& original, T& visitor)
{
KernelGraph graph;
GraphReindexer reindexer;
graph.addConstraints(original.getConstraints());
graph.addAppliedTransforms(original.appliedTransforms());
if(visitor.rewriteCoordinates())
{
// add coordinate roots
for(auto const& index : original.coordinates.roots())
{
reindexer.coordinates.emplace(
index,
graph.coordinates.addElement(original.coordinates.getElement(index)));
}
for(auto const& index : original.coordinates.topologicalSort())
{
auto element = original.coordinates.getElement(index);
if(std::holds_alternative<CTEdge>(element))
{
auto edge = std::get<CTEdge>(element);
std::visit(
[&](auto&& arg) {
visitor.visitEdge(graph, original, reindexer, index, arg);
},
edge);
}
}
}
else
{
graph.coordinates = original.coordinates;
}
// add control flow roots
int kernel = *original.control.roots().begin();
if(visitor.controlGraphDirection() == Graph::Direction::Downstream)
{
visitor.visitRoot(graph, original, reindexer, kernel);
}
for(auto const& index : visitor.controlGraphDirection() == Graph::Direction::Downstream
? original.control.topologicalSort()
: original.control.reverseTopologicalSort())
{
auto element = original.control.getElement(index);
if(std::holds_alternative<ControlGraph::Operation>(element))
{
auto node = std::get<ControlGraph::Operation>(element);
if(std::holds_alternative<ControlGraph::Kernel>(node))
continue;
std::visit(
[&](auto&& arg) {
visitor.visitOperation(graph, original, reindexer, index, arg);
},
node);
}
}
if(visitor.controlGraphDirection() == Graph::Direction::Upstream)
{
visitor.visitRoot(graph, original, reindexer, kernel);
}
return graph;
}
}
}