MLIR-AIE
AIETokenAnalysis.cpp
Go to the documentation of this file.
1//===- AIETokenAnalysis.cpp -------------------------------------*- C++ -*-===//
2//
3// This file is licensed under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7// (c) Copyright 2019 Xilinx Inc.
8//
9//===----------------------------------------------------------------------===//
10
14
15#include "mlir/Pass/Pass.h"
16#include "mlir/Tools/mlir-translate/MlirTranslateMain.h"
17
18using namespace mlir;
19using namespace xilinx;
20using namespace xilinx::AIE;
21using namespace xilinx::AIEX;
22
23static void tokenMapPushBack(
24 std::vector<std::pair<llvm::StringRef, std::vector<mlir::Operation *>>>
25 &map,
26 StringRef tokenName, Operation *op) {
27 for (auto &pair : map) {
28 if (pair.first == tokenName) {
29 pair.second.push_back(op);
30 return;
31 }
32 }
33 map.push_back({tokenName, {op}});
34}
35
37
38 // Collecting token symbols
39 for (auto op : device.getOps<TokenOp>()) {
40 StringRef tokenName =
41 op->getAttrOfType<StringAttr>(::mlir::SymbolTable::getSymbolAttrName())
42 .getValue();
43 int value = op.getTokenValue();
44 tokenSymbols[tokenName] = value;
45 }
46
47 // collect all the UseTokenOps and MemcpyOps
48 std::map<StringRef, SmallVector<UseTokenOp, 4>> visitors;
49 device.getBodyRegion().walk([&](Operation *Op) {
50 if (auto op = dyn_cast<UseTokenOp>(Op)) {
51 StringRef tokenName = op.getTokenName();
52 assert(tokenSymbols.find(tokenName) != tokenSymbols.end() &&
53 "Token not found!");
54 if (op.acquire()) {
55 tokenMapPushBack(tokenAcqMap, tokenName, op.getOperation());
56 visitors[tokenName].push_back(op);
57 } else {
58 tokenMapPushBack(tokenRelMap, tokenName, op.getOperation());
59 if (!visitors[tokenName].empty()) {
60 Operation *Op = visitors[tokenName].pop_back_val();
61 tokenPairs.push_back({Op, op.getOperation()});
62 }
63 }
64 } else if (auto op = dyn_cast<MemcpyOp>(Op)) {
65 StringRef tokenName = op.getTokenName();
66 assert(tokenSymbols.find(tokenName) != tokenSymbols.end() &&
67 "Token not found!");
68 Operation *Op = op.getOperation();
69 tokenMapPushBack(tokenAcqMap, tokenName, op.getOperation());
70 tokenMapPushBack(tokenRelMap, tokenName, op.getOperation());
71 tokenPairs.push_back({Op, Op});
72 }
73 });
74
75 // sanity check: ensure that acquiring a token is followed by releasing a
76 // token
77 for (const auto &map : tokenAcqMap) {
78 StringRef tokenName = map.first;
79 for (auto Op : map.second) {
80 [[maybe_unused]] bool isReleased = false;
81 if (auto op = dyn_cast<MemcpyOp>(Op))
82 isReleased = true;
83
84 for (auto pair : tokenPairs) {
85 if (UseTokenOp aop = dyn_cast<UseTokenOp>(pair.first)) {
86 if (tokenName == aop.getTokenName() && Op == aop.getOperation()) {
87 isReleased = true;
88 break;
89 }
90 }
91 }
92
93 assert(isReleased && "No release found for acquire!"
94 "This might potentially lead to deadlock");
95 }
96 }
97
98 int releaseValue = 0;
99 int acquireValue = 0;
100 // Look for a pair of UseTokenOps (or UseTokenOp and MemcpyOp) such that one
101 // releases and one acquires the same token + value. They form a chain of
102 // releasing and acquiring a token. From the chains of tokens collected, we
103 // can infer the dependency of the parentOps
104 for (const auto &map : tokenRelMap) {
105 StringRef tokenName = map.first;
106 auto tokenRels = map.second;
107 auto tokenAcqs = [&]() {
108 for (auto &pair : tokenAcqMap)
109 if (pair.first == tokenName)
110 return pair.second;
111 return std::vector<Operation *>();
112 }();
113 for (auto ROp : tokenRels) {
114
115 if (auto op = dyn_cast<UseTokenOp>(ROp))
116 releaseValue = op.getTokenValue();
117 else if (auto op = dyn_cast<MemcpyOp>(ROp))
118 releaseValue = op.getReleaseTokenValue();
119
120 for (auto AOp : tokenAcqs) {
121
122 if (auto op = dyn_cast<UseTokenOp>(AOp))
123 acquireValue = op.getTokenValue();
124 else if (auto op = dyn_cast<MemcpyOp>(AOp))
125 acquireValue = op.getAcquireTokenValue();
126 else
127 continue;
128
129 // Release and Acquire form a chain if they set/get the token with the
130 // same value
131 if (releaseValue != acquireValue)
132 continue;
133
134 tokenChains.push_back({ROp, AOp});
135 }
136 }
137 }
138
139 for (auto tile : device.getOps<TileOp>()) {
140 int colIndex = tile.colIndex();
141 int rowIndex = tile.rowIndex();
142 tiles[{colIndex, rowIndex}] = tile;
143 }
144}
145
147
148 if (UseTokenOp op = dyn_cast<UseTokenOp>(Op)) {
149 while (Operation *parentOp = op->getParentOp()) {
150 if (isa<CoreOp>(parentOp) || isa<MemOp>(parentOp) ||
151 isa<ShimDMAOp>(parentOp))
152 return parentOp;
153 }
154 }
155
156 return nullptr;
157}
158
160 int colIndex = 0;
161 int rowIndex = 0;
162
163 if (CoreOp core = dyn_cast<CoreOp>(Op)) {
164 colIndex = core.colIndex();
165 rowIndex = core.rowIndex();
166 } else if (MemOp mem = dyn_cast<MemOp>(Op)) {
167 colIndex = mem.colIndex();
168 rowIndex = mem.rowIndex();
169 } else if (ShimDMAOp shimDma = dyn_cast<ShimDMAOp>(Op)) {
170 colIndex = shimDma.colIndex();
171 rowIndex = shimDma.rowIndex();
172 }
173
174 return {colIndex, rowIndex};
175}
176
178 Operation *Op2) {
179 bool IsOp1Mem = isa<MemOp>(Op1) || isa<ShimDMAOp>(Op1);
180 bool IsOp2Mem = isa<MemOp>(Op2) || isa<ShimDMAOp>(Op2);
181
182 assert((!IsOp1Mem || !IsOp2Mem) &&
183 "Op1 and Op2 cannot be both Mem operation!");
184
185 TileID coord1 = getCoord(Op1);
186 TileID coord2 = getCoord(Op2);
187
188 int col1 = coord1.col;
189 int row1 = coord1.row;
190 int col2 = coord2.col;
191 int row2 = coord2.row;
192
193 const auto &targetModel = xilinx::AIE::getTargetModel(Op1);
194
195 bool IsOp1ShareableMem =
196 IsOp1Mem && targetModel.isLegalMemAffinity(col2, row2, col1, row1);
197 bool IsOp2ShareableMem =
198 IsOp2Mem && targetModel.isLegalMemAffinity(col1, row1, col2, row2);
199
200 if (IsOp1ShareableMem)
201 return tiles[coord1];
202 if (IsOp2ShareableMem)
203 return tiles[coord2];
204
205 // both Op1 and Op2 are core ops
206 if (!IsOp1Mem && !IsOp2Mem) {
207 bool IsS = targetModel.isSouth(col1, row1, col2, row2);
208 bool IsW = targetModel.isWest(col1, row1, col2, row2);
209 bool IsN = targetModel.isNorth(col1, row1, col2, row2);
210 bool IsE = targetModel.isEast(col1, row1, col2, row2);
211 bool IsInternal = targetModel.isInternal(col1, row1, col2, row2);
212 bool IsEvenRow = ((row1 % 2) == 0);
213
214 // FIXME: This logic appears AIE1 specific.
215 if (IsS || IsN || (IsW && !IsEvenRow) || (IsE && IsEvenRow))
216 return tiles[coord2];
217 if ((IsW && IsEvenRow) || (IsE && !IsEvenRow) || IsInternal)
218 return tiles[coord1];
219 }
220
221 return nullptr;
222}
223
225 os << "\n=====tokenPairs: \n";
226 for (auto pair : tokenPairs) {
227 Operation *acquire = pair.first;
228 Operation *release = pair.second;
229 acquire->print(os);
230 os << " -> ";
231 release->print(os);
232 os << "\n";
233 }
234
235 os << "\n=====tokenChains: \n";
236 for (auto pair : tokenChains) {
237 Operation *release = pair.first;
238 Operation *acquire = pair.second;
239 release->print(os);
240 os << " -> ";
241 acquire->print(os);
242 os << "\n";
243 }
244}
mlir::Operation * getShareableTileOp(mlir::Operation *Op1, mlir::Operation *Op2)
xilinx::AIE::TileID getCoord(mlir::Operation *Op)
mlir::Operation * getTokenUserOp(mlir::Operation *Op)
void print(llvm::raw_ostream &os)
Include the generated interface declarations.
TileID { friend std::ostream &operator<<(std::ostream &os, const TileID &s) { os<< "TileID("<< s.col<< ", "<< s.row<< ")" TileID
const AIETargetModel & getTargetModel(mlir::Operation *op)