MLIR-AIE 1.4.5.dev
AIECreateLocks.cpp
Go to the documentation of this file.
1//===- AIECreateLocks.cpp ---------------------------------------*- C++ -*-===//
2//
3// Copyright (C) 2019-2022 Xilinx, Inc.
4// Copyright (C) 2022-2026 Advanced Micro Devices, Inc.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
13
14#include "mlir/Dialect/Arith/IR/Arith.h"
15#include "mlir/IR/Attributes.h"
16#include "mlir/IR/PatternMatch.h"
17#include "mlir/Pass/Pass.h"
18#include "mlir/Tools/mlir-translate/MlirTranslateMain.h"
19#include "mlir/Transforms/DialectConversion.h"
20
21namespace xilinx::AIEX {
22#define GEN_PASS_DEF_AIECREATELOCKS
23#include "aie/Dialect/AIEX/Transforms/AIEXPasses.h.inc"
24} // namespace xilinx::AIEX
25
26#define DEBUG_TYPE "aie-create-locks"
27
28using namespace mlir;
29using namespace xilinx;
30using namespace xilinx::AIE;
31using namespace xilinx::AIEX;
32
33struct Token2LockLowering : public OpConversionPattern<UseTokenOp> {
34 using OpConversionPattern<UseTokenOp>::OpConversionPattern;
35 DenseMap<Operation *, std::vector<std::pair<Value, int>>> &acqLocks;
36 DenseMap<Operation *, std::vector<std::pair<Value, int>>> &relLocks;
37 DenseMap<std::pair<Operation *, Operation *>, std::pair<Value, int>>
39
41 MLIRContext *context,
42 DenseMap<Operation *, std::vector<std::pair<Value, int>>> &acqLocks,
43 DenseMap<Operation *, std::vector<std::pair<Value, int>>> &relLocks,
44 DenseMap<std::pair<Operation *, Operation *>, std::pair<Value, int>>
46 PatternBenefit benefit = 1)
47 : OpConversionPattern<UseTokenOp>(context, benefit), acqLocks(acqLocks),
49
50 LogicalResult
51 matchAndRewrite(UseTokenOp op, OpAdaptor adaptor,
52 ConversionPatternRewriter &rewriter) const override {
53 Operation *Op = op.getOperation();
54 Operation *parentOp = op->getParentOp();
55
56 if (!isa<CoreOp, MemOp, ShimDMAOp>(parentOp))
57 llvm_unreachable("A parent operation of UseTokenOp must be either CoreOp "
58 "or MemOp or ShimDMAOp");
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 UseLockOp::create(rewriter, 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 UseLockOp::create(rewriter, 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 UseLockOp::create(rewriter, 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 UseLockOp::create(rewriter, 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
135 : public xilinx::AIEX::impl::AIECreateLocksBase<AIECreateLocksPass> {
136 void getDependentDialects(DialectRegistry &registry) const override {
137 registry.insert<arith::ArithDialect>();
138 }
139 void runOnOperation() override {
140
141 DeviceOp device = getOperation();
142 OpBuilder builder = OpBuilder::atBlockTerminator(device.getBody());
143
144 TokenAnalysis TA(device);
145 TA.runAnalysis();
146 LLVM_DEBUG(TA.print(llvm::dbgs()));
147 SmallVector<std::pair<Operation *, Operation *>, 4> tokenChains(
148 TA.getTokenChains());
149 SmallVector<std::pair<Operation *, Operation *>, 4> tokenPairs(
150 TA.getTokenPairs());
151 DenseMap<TileID, Operation *> tiles(TA.getTiles());
152
153 DenseMap<std::pair<Operation *, int>, int> locks;
154 DenseMap<std::pair<Operation *, Operation *>, std::pair<Value, int>>
155 lockChains;
156 DenseMap<Operation *, std::vector<std::pair<Value, int>>> acqLocks;
157 DenseMap<Operation *, std::vector<std::pair<Value, int>>> relLocks;
158
159 for (auto map : tokenChains) {
160 Operation *release = map.first;
161 Operation *acquire = map.second;
162
163 Operation *relUser = TA.getTokenUserOp(release);
164 Operation *acqUser = TA.getTokenUserOp(acquire);
165 bool IsRelUserCore = isa<CoreOp>(relUser);
166 bool IsAcqUserCore = isa<CoreOp>(acqUser);
167
168 Operation *tileOp = TA.getShareableTileOp(relUser, acqUser);
169
170 LLVM_DEBUG(llvm::dbgs() << "\n=== CHECKING TOKEN CHAIN ===\n";
171 release->print(llvm::dbgs());
172 if (IsRelUserCore) llvm::dbgs() << " @Core";
173 else llvm::dbgs() << " @DMA";
174 llvm::dbgs() << " (" << TA.getCoord(relUser).col << ", "
175 << TA.getCoord(relUser).row << ")" << '\n';
176 acquire->print(llvm::dbgs());
177 if (IsAcqUserCore) llvm::dbgs() << " @Core";
178 else llvm::dbgs() << " @DMA";
179 llvm::dbgs() << " (" << TA.getCoord(acqUser).col << ", "
180 << TA.getCoord(acqUser).row << ")" << '\n';);
181
182 // ignore chain that involves a MemOp (DMA) user and CoreOp user and they
183 // don't have a shareable tile. This might be caused by MemcpyOp lowering
184 // -- there are two MemOps that use the same token and the same lock
185 // action + value. Therefore, TokenAnalysis accidentally chains one MemOp
186 // to a Core (from the MemcpyOp relationship) that does not have memory
187 // affinity with it
188 // TODO: verify if it is actually safe to ignore this case
189 if (!tileOp && ((!IsRelUserCore && IsAcqUserCore) ||
190 (!IsAcqUserCore && IsRelUserCore)))
191 continue;
192 assert(tileOp &&
193 "the lock users of this chain do not have a common lock");
194
195 TileOp tile = cast<TileOp>(tileOp);
196 int lockID = getLockID(locks, tileOp);
197 assert(lockID >= 0 && "No more locks to allocate!");
198 LLVM_DEBUG(llvm::dbgs() << "Shared tile \n"; tileOp->print(llvm::dbgs()));
199 LLVM_DEBUG(llvm::dbgs() << " LockID: " << lockID << '\n');
200 builder.setInsertionPointAfter(tileOp);
201 LockOp lock = LockOp::create(builder, tile.getLoc(), tile, lockID, 0);
202
203 lockChains[std::make_pair(release, acquire)] = std::make_pair(lock, 1);
204
205 for (auto pair : tokenPairs) {
206 Operation *acqFromPair = pair.first;
207 Operation *relFromPair = pair.second;
208
209 if (relFromPair == release)
210 acqLocks[acqFromPair].emplace_back(lock, 0);
211
212 if (acqFromPair == acquire)
213 relLocks[relFromPair].emplace_back(lock, 0);
214 }
215 }
216
217 ConversionTarget target(getContext());
218 target.addLegalOp<UseLockOp>();
219
220 RewritePatternSet patterns(&getContext());
221 patterns.insert<Token2LockLowering>(device.getContext(), acqLocks, relLocks,
222 lockChains);
223
224 if (failed(applyPartialConversion(device, target, std::move(patterns))))
225 signalPassFailure();
226 }
227};
228
229std::unique_ptr<OperationPass<DeviceOp>>
231 return std::make_unique<AIECreateLocksPass>();
232}
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()
const AIETargetModel & getTargetModel(mlir::Operation *op)
void getDependentDialects(DialectRegistry &registry) const override
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