// 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 namespace rocRoller::KernelGraph { using namespace ControlGraph; using namespace CoordinateGraph; ModelAddresses::ModelAddresses(ContextPtr context) : m_context(context) { setup(); } void ModelAddresses::setup() { namespace CT = rocRoller::KernelGraph::CoordinateGraph; namespace Expression = rocRoller::Expression; using namespace ControlGraph; using namespace CoordinateGraph; using namespace Expression; for(int i = 0; i < 3; ++i) { workgroupOffset[i] = arguments.size(); auto wg_name = concatenate("WG", i); auto wg_carg = CommandArgument( nullptr, DataType::UInt32, workgroupOffset[i], DataDirection::ReadOnly, wg_name); auto wg = std::make_shared(wg_carg); arguments.appendUnbound(wg_name); workitemOffset[i] = arguments.size(); auto wi_name = concatenate("WI", i); auto wi_carg = CommandArgument( nullptr, DataType::UInt32, workitemOffset[i], DataDirection::ReadOnly, wi_name); auto wi = std::make_shared(wi_carg); arguments.appendUnbound(wi_name); kernelWorkgroupIndexes[i] = std::make_shared(wg); kernelWorkitemIndexes[i] = std::make_shared(wi); } rawArguments = arguments.dataVector(); runtimeArguments = RuntimeArguments(rawArguments.data(), rawArguments.size()); } void ModelAddresses::setWorkgroup(uint dim, uint value) { std::memcpy(rawArguments.data() + workgroupOffset[dim], &value, sizeof(value)); } void ModelAddresses::setWorkitem(uint dim, uint value) { std::memcpy(rawArguments.data() + workitemOffset[dim], &value, sizeof(value)); } std::vector ModelAddresses::getLDSAddressesImpl(KernelGraph& graph, int tag, LoadStoreTileGenerator::LoadStoreTileInfo const& info, LDSDirection direction) { namespace CT = rocRoller::KernelGraph::CoordinateGraph; namespace Expression = rocRoller::Expression; using namespace CoordinateGraph; using namespace Expression; auto [ldsTag, lds] = graph.getDimension(tag); auto [tileTag, tile] = graph.getDimension(tag); auto maybeParentLDS = only(graph.coordinates.getOutputNodeIndices(ldsTag, CT::isEdge)); if(maybeParentLDS) ldsTag = *maybeParentLDS; const auto varInfo = DataTypeInfo::Get(info.varType); const auto segInfo = DataTypeInfo::Get(varInfo.segmentVariableType); const auto packedCount = std::max(1u, varInfo.packing); const uint64_t numElements = info.m * info.n * packedCount; const uint64_t segmentBits = static_cast(segInfo.elementBits); AssertFatal(numElements > 0, "Invalid LDS tile element count.", ShowValue(numElements)); auto coords = graph.buildTransformer(tag); // Follows code in loadStoreTileGenerator if(tile.memoryType == MemoryType::WAVE || tile.memoryType == MemoryType::WAVE_SWIZZLE || tile.memoryType == MemoryType::WAVE_FROM_GLOBAL) { auto maybeVGPRBlockSets = GetVGPRBlockSetDimSize(graph, tag); if(maybeVGPRBlockSets.has_value()) { const uint numVGPRBlockSets = maybeVGPRBlockSets.value(); auto [vgprBlockSetTag, _] = graph.getDimension(tag); coords.setCoordinate(vgprBlockSetTag, Expression::literal(numVGPRBlockSets)); } auto [vgprTag, vgpr] = graph.getDimension(tag); coords.setCoordinate(vgprTag, Expression::literal(0)); } else if(tile.memoryType == MemoryType::VGPR || tile.memoryType == MemoryType::WAVE_SPLIT || tile.memoryType == MemoryType::LDS) { auto [elemXTag, elemX] = graph.getDimension(tag, 0); auto [elemYTag, elemY] = graph.getDimension(tag, 1); coords.setCoordinate(elemXTag, Expression::literal(0)); coords.setCoordinate(elemYTag, Expression::literal(0)); } coords.fillExecutionCoordinates(kernelWorkgroupIndexes, kernelWorkitemIndexes); auto index = (direction == LDSDirection::Load) ? coords.reverse({ldsTag})[0] : coords.forward({ldsTag})[0]; Log::debug("{}: tag {}, segmentBits {}, numElements {}", (direction == LDSDirection::Load) ? "LoadLDSTile" : "StoreLDSTile", tag, segmentBits, numElements); const auto byteIndex = index * Expression::literal(segmentBits) / Expression::literal(8u); Log::debug("Offset expression: {}", toString(byteIndex)); setWorkgroup(0, 0); std::vector addresses; for(uint wi = 0; wi < product(m_context->kernel()->workgroupSize()); ++wi) { setWorkitem(0, wi); const auto offsetValue = Expression::evaluate(byteIndex, runtimeArguments); const auto offset = std::visit( [](auto&& x) { using T = std::decay_t; if constexpr(std::is_same_v) { Throw("Cannot extract LDS address from " "rocRoller::Buffer"); return size_t{0}; } else if constexpr(std::is_same_v) { Throw("Cannot extract LDS address from " "rocRoller::TDM"); return size_t{0}; } else { return (size_t)x; } }, offsetValue); addresses.push_back(offset); } return addresses; } template std::vector ModelAddresses::getLDSAddresses(KernelGraph& graph, int tag, Op const& op) { constexpr bool isLoad = std::is_same_v; constexpr auto direction = isLoad ? LDSDirection::Load : LDSDirection::Store; // Use nullptr context to avoid modifying the real tag manager during modeling. auto graphPtr = std::make_shared(graph); LoadStoreTileGenerator tileGenerator( graphPtr, nullptr, m_context->kernel()->max_flat_workgroup_size()); LoadStoreTileGenerator::LoadStoreTileInfo info; if constexpr(isLoad) { info = tileGenerator.getLoadLDSTileInfo(tag, op); } else { info = tileGenerator.getStoreLDSTileInfo(tag, op); } return getLDSAddressesImpl(graph, tag, info, direction); } template std::vector ModelAddresses::getLDSAddresses(KernelGraph&, int, LoadLDSTile const&); template std::vector ModelAddresses::getLDSAddresses(KernelGraph&, int, StoreLDSTile const&); KernelGraph ModelAddresses::apply(KernelGraph const& original) { KernelGraph graph = original; auto root = graph.control.roots().only(); AssertFatal(root.has_value()); auto allNodes = graph.control.depthFirstVisit(*root).filter(graph.control.isElemType()); for(const auto node : allNodes) { auto modelAddresses = [&](std::vector addresses) { if(addresses.empty()) return; std::vector normalizedAddresses; auto minAddress = *std::min_element(addresses.begin(), addresses.end()); for(auto addr : addresses) { normalizedAddresses.push_back(addr - minAddress); } graph.modelledAddresses[node] = std::move(normalizedAddresses); }; auto visitor = rocRoller::overloaded{[&](CIsAnyOf auto op) { modelAddresses(getLDSAddresses(graph, node, op)); }, [&](auto op) {}}; std::visit(visitor, graph.control.getNode(node)); } return graph; } std::string ModelAddresses::name() const { return "ModelAddresses"; } }