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