Files
TheRock/rocm-libraries/shared/rocroller/lib/include/rocRoller/KernelGraph/ControlGraph/ControlGraph.hpp
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

308 lines
12 KiB
C++

// Copyright Advanced Micro Devices, Inc., or its affiliates.
// SPDX-License-Identifier: MIT
#pragma once
#include <functional>
#include <variant>
#include <rocRoller/KernelGraph/ControlGraph/ControlGraph_fwd.hpp>
#include <rocRoller/Graph/Hypergraph.hpp>
#include <rocRoller/Graph/Hypergraph_fwd.hpp>
#include <rocRoller/KernelGraph/ControlGraph/ControlEdge.hpp>
#include <rocRoller/KernelGraph/ControlGraph/ControlGraph_fwd.hpp>
#include <rocRoller/KernelGraph/ControlGraph/Operation.hpp>
#include <rocRoller/KernelGraph/Policy.hpp>
#include <rocRoller/Utilities/Comparison.hpp>
namespace rocRoller
{
/**
* Control flow routines.
*
* Control flow is represented as a graph. Nodes in the control flow graph represent
* operations (like load/store or a for loop). Edges in the graph encode dependencies
* between nodes.
*
* The graph answers the question:
* What are the series of operations needed to solve the problem?
*
* Each node of the graph, when traversed, will help generate assembly code.
* It relies on coordinate transform graph expressions.
*
* The control graph should begin with a single Kernel node.
*
* There are two main categories of edges: Sequence edges and Body-like edges.
*
* From a given node, each kind of Body-like edge denodes a separate body
* of that node.
* For example, a Conditional node may have Body edges and Else edges.
* The nodes downstream of the Body edges represent the true case, and the
* nodes downstream of the Else edges represent the false case.
*/
namespace KernelGraph::ControlGraph
{
enum class NodeOrdering
{
LeftFirst = 0,
LeftInBodyOfRight,
Undefined,
RightInBodyOfLeft,
RightFirst,
Count
};
enum class CacheStatus
{
Invalid = 0, //< Cache is empty
Partial, //< Cache does not have all the orders between nodes
Valid, //< Cache has all orders of nodes
Count
};
/**
* Return a full representation of 'n'
*/
std::string toString(NodeOrdering n);
std::ostream& operator<<(std::ostream& stream, NodeOrdering n);
/**
* Return a full representation of 'c'
*/
std::string toString(CacheStatus c);
std::ostream& operator<<(std::ostream& stream, CacheStatus c);
/**
* Return a 3-character representation of 'n'.
*/
std::string abbrev(NodeOrdering n);
/**
* If ordering `order` applies to (a, b), return the ordering that applies to (b, a).
*/
NodeOrdering opposite(NodeOrdering order);
struct NodeOrders
{
std::vector<int> after;
std::vector<int> before;
std::vector<int> inBody;
std::vector<int> containing;
void clear()
{
after.clear();
before.clear();
inBody.clear();
containing.clear();
}
};
/**
* Control flow graph.
*
* Nodes in the graph represent operations. Edges describe
* dependencies.
*/
class ControlGraph : public Graph::Hypergraph<Operation, ControlEdge, false>
{
public:
using Base = Graph::Hypergraph<Operation, ControlEdge, false>;
ControlGraph() = default;
/**
* @brief Get a node/edge from the control graph.
*
* If the element specified by tag cannot be converted to
* T, the return value is empty.
*
* @param tag Graph tag/index.
*/
template <typename T>
requires(std::constructible_from<ControlGraph::Element, T>) std::optional<T> get(
int tag)
const;
/**
* Returns the relative ordering of nodeA and nodeB according to the graph rules.
*
* From node X,
*
* - Nodes connected via a Sequence edge (and their descendents) are after X.
* - Nodes connected via other kinds of edges (Initialize, Body, ForLoopIncrement)
* are in the body of X.
* - The descendents of X are ordered by the first kind of edge, in this order:
* Initialize -> Body -> ForLoopIncrement -> Sequence.
*
* Nodes whose relationship is not determined by the above rules could be
* scheduled concurrently.
*/
NodeOrdering compareNodes(UpdateCachePolicy const, int nodeA, int nodeB) const;
NodeOrdering compareNodes(CacheOnlyPolicy const, int nodeA, int nodeB) const;
NodeOrdering compareNodes(UseCacheIfAvailablePolicy const, int nodeA, int nodeB) const;
NodeOrdering compareNodes(IgnoreCachePolicy const, int nodeA, int nodeB) const;
/**
* Yields (in no particular order) all nodes that are definitely after `node`.
*/
Generator<int> nodesAfter(int node) const;
/**
* Yields (in no particular order) all nodes that are definitely before `node`.
*/
Generator<int> nodesBefore(int node) const;
/**
* Yields (in no particular order) all nodes that are inside the body of `node`.
* Note
*/
Generator<int> nodesInBody(int node) const;
/**
* Yields (in no particular order) all nodes that contain `node` in their body.
* Note
*/
Generator<int> nodesContaining(int node) const;
/**
* Returns a string containing a text table describing the relationship between
* all nodes in the graph.
*/
std::string nodeOrderTableString() const;
/**
* Returns a string containing a text table describing the relationship between
* the listed nodes in the graph.
*/
std::string nodeOrderTableString(std::set<int> const& nodes) const;
/**
* Contains a map of every definite ordering between two nodes.
* Note that node pairs whose ordering is undefined will be missing from the cache.
* Also note that the entries are not duplicated for (nodeA, nodeB) and
* (nodeB, nodeA). Only the entry where the first node ID is the lower number
* will be present. The other entry can be obtained from opposite(nodeB, nodeA).
*
* Also, if a reference to the returned value is maintained through any changes
* to the graph, the returned map will be cleared.
*/
std::unordered_map<int, std::unordered_map<int, NodeOrdering>> nodeOrderTable() const;
template <typename T>
requires(std::constructible_from<Operation, T>)
std::set<std::pair<int, int>> ambiguousNodes()
const;
/**
* @brief Given two control stacks, add the necessary sequence edge such that the final nodes in the stack are relatively ordered.
*
* The final nodes in the control stacks are ordered by adding a sequence edge between the first nodes in the stacks that differ.
*
* If ordered is true, the order imposed is a -> b.
* Otherwise, the order is inferred based on the following rules:
* 1. If the differing nodes are translate time evaluatable setcoords, order the smaller one first.
* 2. Otherwise, order the smaller numbered node first.
*
*
* @tparam Range
* @param aControlStack Path in the body-parent tree (computed from the control graph) from root to a.
* @param bControlStack Path in the body-parent tree from root to b.
* @param ordered Inputs are passed in order.
*/
template <CForwardRangeOf<int> Range>
void orderMemoryNodes(Range const& aControlStack,
Range const& bControlStack,
bool ordered = true);
template <typename T>
inline std::predicate<int> auto isElemType() const
{
return [this](int x) -> bool { return get<T>(x).has_value(); };
}
/**
* Connects each argument in order with a separate edge of type `Edge`. Accepts
* any number of arguments. The edges will be default-constructed.
*
* e.g. chain<Sequence>(a, b, c) will create two sequence edges, one from a to b,
* and one from b to c.
*/
template <CControlEdge Edge, std::convertible_to<int>... Nodes>
void chain(int a, int b, Nodes... remaining);
/**
* Check if modifying an element (index) is allowed or not. This
* only comes into effect when the graph is in restricted mode.
*/
virtual bool isModificationAllowed(int index) const override;
/**
* Set the graph to be in restricted mode. Some operations would
* be disallowed when in restricted mode.
*/
void setRestricted()
{
m_changesRestricted = true;
}
private:
/**
* Populates m_orderCache for startingNodes relative to their descendents, and the
* descendents of each relative to each other. Returns the descendents of
* startingNodes.
*/
template <CForwardRangeOf<int> Range>
std::vector<int> populateOrderCache(Range const& startingNodes) const;
/**
* Populates m_orderCache for startingNode relative to its descendents, and the
* descendents relative to each other. Returns the descendents of startingNode.
*/
std::vector<int> populateOrderCache(int startingNode) const;
virtual void clearCache(Graph::GraphModification modification) override;
void populateOrderCache() const;
void sortOrderCache() const;
NodeOrdering lookupOrder(CacheOnlyPolicy const, int nodeA, int nodeB) const;
NodeOrdering lookupOrder(IgnoreCachePolicy const, int nodeA, int nodeB) const;
void writeOrderCache(int nodeA, int nodeB, NodeOrdering order) const;
template <CForwardRangeOf<int> ARange = std::initializer_list<int>,
CForwardRangeOf<int> BRange = std::initializer_list<int>>
void writeOrderCache(ARange const& nodesA,
BRange const& nodesB,
NodeOrdering order) const;
mutable std::unordered_map<int, NodeOrders> m_orderCache;
/**
* If an entry is present, the value will be the IDs of every descendent from the key,
* following every kind of edge.
*/
mutable std::unordered_map<int, std::vector<int>> m_descendentCache;
mutable CacheStatus m_cacheStatus = CacheStatus::Invalid;
mutable bool m_changesRestricted = false;
};
std::string name(ControlGraph::Element const& el);
/**
* @brief Determine if x holds an Operation of type T.
*/
template <typename T>
bool isOperation(auto const& x);
/**
* @brief Determine if x holds a ControlEdge of type T.
*/
template <typename T>
bool isEdge(auto const& x);
}
}
#include <rocRoller/KernelGraph/ControlGraph/ControlGraph_impl.hpp>