Files
TheRock/rocm-libraries/shared/rocroller/lib/source/KernelGraph/Transformations/AddLDS.cpp
T
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

258 lines
10 KiB
C++

// Copyright Advanced Micro Devices, Inc., or its affiliates.
// SPDX-License-Identifier: MIT
/**
@class AddLDS
@brief Add load/store through LDS to the graph; and prefetching.
# Overview
Load/store operations inside loops, and tagged with MemoryType LDS
or WAVE_LDS, are transformed.
An entire tile is loaded once per loop iteration (which may be
unrolled) into LDS. Subsequent loads in the loop read from LDS.
*/
#include "rocRoller/DataTypes/DataTypes.hpp"
#include <rocRoller/CommandSolution.hpp>
#include <rocRoller/Expression.hpp>
#include <rocRoller/KernelGraph/ControlGraph/ControlGraph.hpp>
#include <rocRoller/KernelGraph/ControlGraph/LastRWTracer.hpp>
#include <rocRoller/KernelGraph/ControlGraph/Operation.hpp>
#include <rocRoller/KernelGraph/ControlToCoordinateMapper.hpp>
#include <rocRoller/KernelGraph/CoordinateGraph/Dimension.hpp>
#include <rocRoller/KernelGraph/Transforms/AddLDS.hpp>
#include <rocRoller/KernelGraph/Transforms/Simplify.hpp>
#include <rocRoller/KernelGraph/Utils.hpp>
#include <rocRoller/KernelGraph/Visitors.hpp>
#include <rocRoller/Operations/Command.hpp>
#include <rocRoller/Operations/Operations.hpp>
namespace rocRoller
{
namespace KernelGraph
{
using GD = rocRoller::Graph::Direction;
using namespace ControlGraph;
using namespace CoordinateGraph;
using namespace Expression;
using namespace Register;
template <Graph::Direction Dir>
void insertInstead(rocRoller::KernelGraph::CoordinateGraph::CoordinateGraph& coordinates,
int newTag,
int oldTag)
{
auto edgeTags = coordinates.getNeighbours<Dir>(oldTag);
for(auto edgeTag : edgeTags)
{
auto edge = coordinates.getElement(edgeTag);
auto upDirTags = coordinates.getNeighbours<Graph::opposite(Dir)>(edgeTag);
auto downDirTags = coordinates.getNeighbours<Dir>(edgeTag);
std::replace(upDirTags.begin(), upDirTags.end(), oldTag, newTag);
coordinates.deleteElement(edgeTag);
if constexpr(Dir == Graph::Direction::Upstream)
{
coordinates.addElement(edge, downDirTags, upDirTags);
}
else
{
coordinates.addElement(edge, upDirTags, downDirTags);
}
}
}
/**
* Add LDS transformer.
*
* Splits LoadTiled operations into:
* - LoadTiled
* - StoreLDSTile
* - LoadLDSTile
*
* Similarly for StoreTiled operations.
*/
struct AddLDSVisitor
{
AddLDSVisitor(CommandParametersPtr params, ContextPtr context)
: m_params(params)
, m_context(context)
{
}
void stage(KernelGraph const&, int);
void commit(KernelGraph&);
private:
std::set<int> m_operations;
CommandParametersPtr m_params;
ContextPtr m_context;
};
KernelGraph AddLDS::apply(KernelGraph const& original)
{
auto graph = original;
auto visitor = AddLDSVisitor(m_params, m_context);
for(auto const& loadTag : graph.control.getNodes<LoadTiled>())
visitor.stage(graph, loadTag);
for(auto const& storeTag : graph.control.getNodes<StoreTiled>())
visitor.stage(graph, storeTag);
visitor.commit(graph);
return graph;
}
ConstraintStatus NoLDSTiles(const KernelGraph& graph)
{
TIMER(t, "Constraint::NoLDSTiles");
ConstraintStatus retval;
for(auto tag : graph.coordinates.getNodes<MacroTile>())
{
auto tile = *graph.coordinates.get<MacroTile>(tag);
if(tile.memoryType == MemoryType::LDS || tile.memoryType == MemoryType::WAVE_LDS
|| tile.memoryType == MemoryType::WAVE_LDS_FROM_GLOBAL)
{
retval.combine(false, concatenate("Tile has LDS memory type: ", tag));
}
}
return retval;
}
std::vector<GraphConstraint> AddLDS::postConstraints() const
{
return {NoLDSTiles};
}
void AddLDSVisitor::stage(KernelGraph const& k, int opTag)
{
auto [userTag, user] = k.getDimension<User>(opTag);
auto [tileTag, tile] = k.getDimension<MacroTile>(opTag);
if(not(tile.memoryType == MemoryType::WAVE_LDS || tile.memoryType == MemoryType::LDS
|| tile.memoryType == MemoryType::WAVE_Direct2LDS
|| tile.memoryType == MemoryType::WAVE_LDS_FROM_GLOBAL
|| tile.memoryType == MemoryType::WAVE_TDMToLDS))
return;
rocRoller::Log::getLogger()->debug(
"KernelGraph::AddLDS()::stage({}): User {}, MacroTile {}", opTag, userTag, tileTag);
m_operations.insert(opTag);
}
void AddLDSVisitor::commit(KernelGraph& k)
{
//
// Commit: Create operations and DataFlow
//
for(auto opTag : m_operations)
{
Log::debug("KernelGraph::AddLDS()::commit({})", opTag);
auto isLoad = k.control.get<LoadTiled>(opTag).has_value();
auto tileTag = k.mapper.get<MacroTile>(opTag);
auto userTag = k.mapper.get<User>(opTag);
auto varType = getVariableType(k, opTag);
auto tile = k.coordinates.getNode<MacroTile>(tileTag);
// TODO: enable SwizzleScale when store D via LDS
auto isStoreD = tile.layoutType == LayoutType::MATRIX_ACCUMULATOR;
if(!isLoad && isStoreD)
AssertFatal(!m_params->swizzleScale,
"Store D via LDS is not supported by SwizzleScale");
// Create new coordinates
auto ldsTag = k.coordinates.addElement(LDS());
std::vector<uint> jammedTiles = {1, 1};
bool splitStore = false;
if(!isLoad)
{
// For StoreTiled operations, the waves are
// "serialized" in OrderEpilogueBlocks, so we can
// use a smaller internal tile.
jammedTiles = m_params->getWaveTilesPerWavefront();
splitStore = m_params->getSplitStoreTileIntoWaveBlocks();
}
auto internalTag = createInternalTile(
k, varType, tileTag, jammedTiles, splitStore, m_params, m_context);
// Connect coordinates with DataFlow edges
insertInstead<Graph::Direction::Downstream>(k.coordinates, internalTag, tileTag);
k.coordinates.addElement(DataFlow(), {internalTag}, {ldsTag});
k.coordinates.addElement(DataFlow(), {ldsTag}, {tileTag});
// Create new operations and update old operation
auto loadLDSOp = k.control.addElement(LoadLDSTile(varType));
auto storeLDSOp = k.control.addElement(StoreLDSTile(varType.dataType));
const auto isDirect2LDS = tile.memoryType == MemoryType::WAVE_Direct2LDS;
// Update tile
if(tile.memoryType == MemoryType::WAVE_Direct2LDS
|| tile.memoryType == MemoryType::WAVE_TDMToLDS)
tile.memoryType = MemoryType::WAVE;
if(tile.memoryType == MemoryType::WAVE_LDS)
tile.memoryType = MemoryType::WAVE;
if(tile.memoryType == MemoryType::LDS)
tile.memoryType = MemoryType::VGPR;
if(tile.memoryType == MemoryType::WAVE_LDS_FROM_GLOBAL)
tile.memoryType = MemoryType::WAVE_FROM_GLOBAL;
k.coordinates.setElement(tileTag, tile);
if(!isLoad)
{
// For StoreTiled operations, the waves are
// "serialized" in OrderEpilogueBlocks, so we can
// use a smaller tile size.
tile.sizes[0] /= m_params->getWaveTilesPerWavefront()[0];
tile.sizes[1] /= m_params->getWaveTilesPerWavefront()[1];
k.coordinates.setElement(tileTag, tile);
}
// Update connections and connect operations
k.control.addElement(Sequence(), {storeLDSOp}, {loadLDSOp});
k.mapper.purge(opTag);
k.mapper.connect<User>(opTag, userTag);
k.mapper.connect<User>(storeLDSOp, userTag);
k.mapper.connect<LDS>(storeLDSOp, ldsTag);
k.mapper.connect<User>(loadLDSOp, userTag);
k.mapper.connect<LDS>(loadLDSOp, ldsTag);
// Connection to LDS is required as LoadTileDirect2LDS acts as both
// LoadTile & StoreLDSTile and this connection is used create pre-operation
// barriers to syncronize LDS accesses when dependencies are loop-carried.
if(isDirect2LDS)
k.mapper.connect<LDS>(opTag, ldsTag, 0);
if(isLoad)
{
insertAfter(k, opTag, storeLDSOp, loadLDSOp);
k.mapper.connect<MacroTile>(opTag, internalTag);
k.mapper.connect<MacroTile>(storeLDSOp, internalTag);
k.mapper.connect<MacroTile>(loadLDSOp, tileTag);
}
else
{
insertBefore(k, opTag, storeLDSOp, loadLDSOp);
k.mapper.connect<MacroTile>(storeLDSOp, tileTag);
k.mapper.connect<MacroTile>(loadLDSOp, internalTag);
k.mapper.connect<MacroTile>(opTag, internalTag);
}
}
}
}
}