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
308 lines
12 KiB
C++
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>
|