MLIR-AIE
AIECreateLocks.cpp
Go to the documentation of this file.
1//===- AIECreateLocks.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
15
16#include "mlir/IR/Attributes.h"
17#include "mlir/IR/PatternMatch.h"
18#include "mlir/Pass/Pass.h"
19#include "mlir/Tools/mlir-translate/MlirTranslateMain.h"
20#include "mlir/Transforms/DialectConversion.h"
21
22namespace xilinx::AIEX {
23#define GEN_PASS_DEF_AIECREATELOCKS
24#include "aie/Dialect/AIEX/Transforms/AIEXPasses.h.inc"
25} // namespace xilinx::AIEX
26
27#define DEBUG_TYPE "aie-create-locks"
28
29using namespace mlir;
30using namespace xilinx;
31using namespace xilinx::AIE;
32using namespace xilinx::AIEX;
33
34struct Token2LockLowering : public OpConversionPattern<UseTokenOp> {
36 DenseMap<Operation *, std::vector<std::pair<Value, int>>> &acqLocks;
37 DenseMap<Operation *, std::vector<std::pair<Value, int>>> &relLocks;
38 DenseMap<std::pair<Operation *, Operation *>, std::pair<Value, int>>
40
42 MLIRContext *context,
43 DenseMap<Operation *, std::vector<std::pair<Value, int>>> &acqLocks,
44 DenseMap<Operation *, std::vector<std::pair<Value, int>>> &relLocks,
45 DenseMap<std::pair<Operation *, Operation *>, std::pair<Value, int>>
47 PatternBenefit benefit = 1)
48 : OpConversionPattern<UseTokenOp>(context, benefit), acqLocks(acqLocks),
50
51 LogicalResult
52 matchAndRewrite(UseTokenOp op, OpAdaptor adaptor,
53 ConversionPatternRewriter &rewriter) const override {
54 Operation *Op = op.getOperation();
55 Operation *parentOp = op->getParentOp();
56
57 if (CoreOp core = dyn_cast<CoreOp>(parentOp)) {
58 } else if (MemOp mem = dyn_cast<MemOp>(parentOp)) {
59 } else if (auto shimDma = dyn_cast<ShimDMAOp>(parentOp)) {
60 } else {
61 llvm_unreachable("A parent operation of UseTokenOp must be either CoreOp "
62 "or MemOp or ShimDMAOp");
63 }
64
65 if (op.acquire()) {
66 // Acquire lock from pair
67 LLVM_DEBUG(llvm::dbgs() << "Replacing Acquire: " << op << "\n");
68 for (auto acqLock : acqLocks[op]) {
69 Value lockFromPair = acqLock.first;
70 int lockValueFromPair = acqLock.second;
71 UseLockOp::create(rewriter, op.getLoc(), lockFromPair,
72 LockAction::Acquire, lockValueFromPair);
73 LLVM_DEBUG(llvm::dbgs() << "Acquire from pair " << lockFromPair
74 << " with " << lockValueFromPair << "\n");
75 }
76
77 // Acquire lock from chain
78 for (auto map : lockChains) {
79 Value lockFromChain = map.second.first;
80 int lockValueFromChain = map.second.second;
81 Operation *acqOp = map.first.second;
82 if (acqOp != Op)
83 continue;
84
85 UseLockOp::create(rewriter, op.getLoc(), lockFromChain,
86 LockAction::Acquire, lockValueFromChain);
87 LLVM_DEBUG(llvm::dbgs() << "Acquire from chain " << lockFromChain
88 << " with " << lockValueFromChain << "\n");
89 }
90 } else if (op.release()) {
91 // Release lock from pair
92 LLVM_DEBUG(llvm::dbgs() << "Replacing Release: " << op << "\n");
93 for (auto relLock : relLocks[op]) {
94 Value lockFromPair = relLock.first;
95 int lockValueFromPair = relLock.second;
96 UseLockOp::create(rewriter, op.getLoc(), lockFromPair,
97 LockAction::Release, lockValueFromPair);
98 LLVM_DEBUG(llvm::dbgs() << "Release from pair " << lockFromPair
99 << " with " << lockValueFromPair << "\n");
100 }
101
102 // Release lock from chain
103 for (auto map : lockChains) {
104 Value lockFromChain = map.second.first;
105 int lockValueFromChain = map.second.second;
106 Operation *relOp = map.first.first;
107 if (relOp != Op)
108 continue;
109
110 UseLockOp::create(rewriter, op.getLoc(), lockFromChain,
111 LockAction::Release, lockValueFromChain);
112 LLVM_DEBUG(llvm::dbgs() << "Release from chain " << lockFromChain
113 << " with " << lockValueFromChain << "\n");
114 }
115 }
116
117 rewriter.eraseOp(Op);
118
119 return success();
120 }
121};
122
123static int getLockID(DenseMap<std::pair<Operation *, int>, int> &locks,
124 Operation *op) {
125 auto tileOp = cast<TileOp>(op);
126 const auto &targetModel = xilinx::AIE::getTargetModel(op);
127 for (unsigned i = 0;
128 i < targetModel.getNumLocks(tileOp.getCol(), tileOp.getRow()); i++) {
129 int usageCnt = locks[std::make_pair(tileOp, i)];
130 if (usageCnt == 0) {
131 locks[std::make_pair(tileOp, i)] = 1;
132 return i;
133 }
134 }
135
136 return -1;
137}
138
140 : public xilinx::AIEX::impl::AIECreateLocksBase<AIECreateLocksPass> {
141 void runOnOperation() override {
142
143 DeviceOp device = getOperation();
144 OpBuilder builder = OpBuilder::atBlockTerminator(device.getBody());
145
146 TokenAnalysis TA(device);
147 TA.runAnalysis();
148 LLVM_DEBUG(TA.print(llvm::dbgs()));
149 SmallVector<std::pair<Operation *, Operation *>, 4> tokenChains(
150 TA.getTokenChains());
151 SmallVector<std::pair<Operation *, Operation *>, 4> tokenPairs(
152 TA.getTokenPairs());
153 DenseMap<TileID, Operation *> tiles(TA.getTiles());
154
155 DenseMap<std::pair<Operation *, int>, int> locks;
156 DenseMap<std::pair<Operation *, Operation *>, std::pair<Value, int>>
157 lockChains;
158 DenseMap<Operation *, std::vector<std::pair<Value, int>>> acqLocks;
159 DenseMap<Operation *, std::vector<std::pair<Value, int>>> relLocks;
160
161 for (auto map : tokenChains) {
162 Operation *release = map.first;
163 Operation *acquire = map.second;
164
165 Operation *relUser = TA.getTokenUserOp(release);
166 Operation *acqUser = TA.getTokenUserOp(acquire);
167 bool IsRelUserCore = isa<CoreOp>(relUser);
168 bool IsAcqUserCore = isa<CoreOp>(acqUser);
169
170 Operation *tileOp = TA.getShareableTileOp(relUser, acqUser);
171
172 LLVM_DEBUG(llvm::dbgs() << "\n=== CHECKING TOKEN CHAIN ===\n";
173 release->print(llvm::dbgs());
174 if (IsRelUserCore) llvm::dbgs() << " @Core";
175 else llvm::dbgs() << " @DMA";
176 llvm::dbgs() << " (" << TA.getCoord(relUser).col << ", "
177 << TA.getCoord(relUser).row << ")" << '\n';
178 acquire->print(llvm::dbgs());
179 if (IsAcqUserCore) llvm::dbgs() << " @Core";
180 else llvm::dbgs() << " @DMA";
181 llvm::dbgs() << " (" << TA.getCoord(acqUser).col << ", "
182 << TA.getCoord(acqUser).row << ")" << '\n';);
183
184 // ignore chain that involves a MemOp (DMA) user and CoreOp user and they
185 // don't have a shareable tile. This might be caused by MemcpyOp lowering
186 // -- there are two MemOps that use the same token and the same lock
187 // action + value. Therefore, TokenAnalysis accidentally chains one MemOp
188 // to a Core (from the MemcpyOp relationship) that does not have memory
189 // affinity with it
190 // TODO: verify if it is actually safe to ignore this case
191 if (!tileOp && ((!IsRelUserCore && IsAcqUserCore) ||
192 (!IsAcqUserCore && IsRelUserCore)))
193 continue;
194 assert(tileOp &&
195 "Sorry, the lock users of this chain do not have a common lock");
196
197 TileOp tile = dyn_cast<TileOp>(tileOp);
198 int lockID = getLockID(locks, tileOp);
199 assert(lockID >= 0 && "No more locks to allocate!");
200 LLVM_DEBUG(llvm::dbgs() << "Shared tile \n"; tileOp->print(llvm::dbgs()));
201 LLVM_DEBUG(llvm::dbgs() << " LockID: " << lockID << '\n');
202 builder.setInsertionPointAfter(tileOp);
203 LockOp lock =
204 LockOp::create(builder, builder.getUnknownLoc(), tile, lockID, 0);
205
206 lockChains[std::make_pair(release, acquire)] = std::make_pair(lock, 1);
207
208 for (auto pair : tokenPairs) {
209 Operation *acqFromPair = pair.first;
210 Operation *relFromPair = pair.second;
211
212 if (relFromPair == release)
213 acqLocks[acqFromPair].push_back(std::make_pair(lock, 0));
214
215 if (acqFromPair == acquire)
216 relLocks[relFromPair].push_back(std::make_pair(lock, 0));
217 }
218 }
219
220 ConversionTarget target(getContext());
221 target.addLegalOp<UseLockOp>();
222
223 RewritePatternSet patterns(&getContext());
224 patterns.insert<Token2LockLowering>(device.getContext(), acqLocks, relLocks,
225 lockChains);
226
227 if (failed(applyPartialConversion(device, target, std::move(patterns))))
228 signalPassFailure();
229 }
230};
231
232std::unique_ptr<OperationPass<DeviceOp>>
234 return std::make_unique<AIECreateLocksPass>();
235}
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)
std::unique_ptr< mlir::OperationPass< AIE::DeviceOp > > createAIECreateLocksPass()
Include the generated interface declarations.
const AIETargetModel & getTargetModel(mlir::Operation *op)
void runOnOperation() override
DenseMap< Operation *, std::vector< std::pair< Value, int > > > & acqLocks
DenseMap< std::pair< Operation *, Operation * >, std::pair< Value, int > > & lockChains
Token2LockLowering(MLIRContext *context, DenseMap< Operation *, std::vector< std::pair< Value, int > > > &acqLocks, DenseMap< Operation *, std::vector< std::pair< Value, int > > > &relLocks, DenseMap< std::pair< Operation *, Operation * >, std::pair< Value, int > > &lockChains, PatternBenefit benefit=1)
LogicalResult matchAndRewrite(UseTokenOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
DenseMap< Operation *, std::vector< std::pair< Value, int > > > & relLocks