MLIR-AIE 1.4.5.dev
AIEStructuralRank.cpp
Go to the documentation of this file.
1//===- AIEStructuralRank.cpp ------------------------------------*- C++ -*-===//
2//
3// Copyright (C) 2026 Advanced Micro Devices, Inc.
4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
5//
6//===----------------------------------------------------------------------===//
7
9
10#include "mlir/IR/SymbolTable.h"
11
12#include <map>
13
14using namespace mlir;
15using namespace xilinx;
16using namespace xilinx::AIE;
17
18static bool isSymbolReference(Attribute value) {
19 auto array = dyn_cast<ArrayAttr>(value);
20 return isa<SymbolRefAttr>(value) ||
21 (array && llvm::all_of(array, llvm::IsaPred<SymbolRefAttr>));
22}
23
24/// An op's name and attributes. Symbol names and references are left out:
25/// what a reference names enters through a link instead, and a fifo name
26/// only says whether there is one.
27static void describe(Operation *op, llvm::raw_ostream &os) {
28 os << op->getName();
29 for (NamedAttribute attr : op->getAttrs()) {
30 if (isSymbolReference(attr.getValue()) ||
31 attr.getName() == SymbolTable::getSymbolAttrName())
32 continue;
33 os << ' ' << attr.getName();
34 if (attr.getName() != "fifoName")
35 os << '=' << attr.getValue();
36 }
37}
38
39std::string &StructuralGraph::add(Operation *op) {
40 nodes.push_back(op);
41 llvm::raw_string_ostream os(features[op]);
42 describe(op, os);
43 return features[op];
44}
45
46void StructuralGraph::link(Operation *from, Operation *to, StringRef label) {
47 links[from].push_back({label.str(), to});
48 links[to].push_back({(label + "~").str(), from});
49}
50
51DenseMap<Operation *, unsigned> StructuralGraph::rank() const {
52 DenseMap<Operation *, unsigned> rank;
53 unsigned classes = 0;
54 auto rerank = [&](auto &signature) {
55 SmallVector<Operation *> order(nodes);
56 llvm::stable_sort(order, [&](Operation *a, Operation *b) {
57 return signature.at(a) < signature.at(b);
58 });
59 classes = 0;
60 for (auto [index, op] : llvm::enumerate(order)) {
61 if (index && signature.at(order[index - 1]) < signature.at(op))
62 ++classes;
63 rank[op] = classes;
64 }
65 ++classes;
66 };
67 std::map<Operation *, std::string> initial;
68 for (Operation *op : nodes)
69 initial[op] = features.lookup(op);
70 rerank(initial);
71 for (unsigned previous = 0; classes != previous;) {
72 previous = classes;
73 std::map<Operation *,
74 std::pair<unsigned, SmallVector<std::pair<std::string, unsigned>>>>
75 signature;
76 for (Operation *op : nodes) {
77 auto &[own, around] = signature[op];
78 own = rank[op];
79 for (const auto &[label, other] : links.lookup(op))
80 around.push_back({label, rank[other]});
81 llvm::sort(around);
82 }
83 rerank(signature);
84 }
85 return rank;
86}
87
89 StructuralGraph graph;
90 // Where each nested op sits in its top-level op, for links into it.
91 DenseMap<Operation *, std::pair<Operation *, unsigned>> position;
92 for (Operation &op : device.getBody()->without_terminator()) {
93 if (isa<RuntimeSequenceOp>(op))
94 continue;
95 llvm::raw_string_ostream os(graph.add(&op));
96 unsigned index = 0;
97 op.walk<WalkOrder::PreOrder>([&](Operation *nested) {
98 position[nested] = {&op, index++};
99 if (nested != &op) {
100 os << " {";
101 describe(nested, os);
102 os << '}';
103 }
104 });
105 }
106 for (Operation *node : SmallVector<Operation *>(graph.nodes)) {
107 node->walk<WalkOrder::PreOrder>([&](Operation *nested) {
108 std::string at = std::to_string(position.lookup(nested).second);
109 for (auto [index, operand] : llvm::enumerate(nested->getOperands())) {
110 Operation *def = operand.getDefiningOp();
111 if (def && def->getParentOp() == device && graph.features.count(def))
112 graph.link(node, def, at + ":" + std::to_string(index));
113 }
114 for (NamedAttribute attr : nested->getAttrs()) {
115 if (!isSymbolReference(attr.getValue()))
116 continue;
117 SmallVector<SymbolRefAttr> refs;
118 if (auto ref = dyn_cast<SymbolRefAttr>(attr.getValue()))
119 refs.push_back(ref);
120 else
121 llvm::append_range(
122 refs,
123 cast<ArrayAttr>(attr.getValue()).getAsRange<SymbolRefAttr>());
124 for (auto [index, ref] : llvm::enumerate(refs)) {
125 auto it =
126 position.find(SymbolTable::lookupNearestSymbolFrom(nested, ref));
127 if (it == position.end())
128 continue;
129 auto [target, within] = it->second;
130 graph.link(node, target,
131 at + ":" + attr.getName().str() + ":" +
132 std::to_string(index) + "@" + std::to_string(within));
133 }
134 }
135 });
136 }
137 return graph;
138}
Ops and the labelled links between them, for ranking them by structure where a pass would otherwise f...
void link(mlir::Operation *from, mlir::Operation *to, llvm::StringRef label)
Link from to to under label, and to back to from.
llvm::SmallVector< mlir::Operation * > nodes
std::string & add(mlir::Operation *op)
Add op as a node whose features are its name and attributes, symbol names and references left out.
llvm::DenseMap< mlir::Operation *, unsigned > rank() const
Rank the nodes by their features and then, a round at a time until no class splits,...
llvm::DenseMap< mlir::Operation *, std::string > features
What each node is, before anything it links to is considered.
static StructuralGraph of(DeviceOp device)
The ops directly in device but its runtime sequences, each with the ops nested in it,...
llvm::DenseMap< mlir::Operation *, llvm::SmallVector< std::pair< std::string, mlir::Operation * > > > links