MLIR-AIE 1.4.5.dev
AIEAssignLockIDs.cpp
Go to the documentation of this file.
1//===- AIEAssignLockIDs.cpp -------------------------------------*- C++ -*-===//
2//
3// Copyright (C) 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
9// This pass aims to assign lockIDs to AIE.lock operations. The lockID is
10// numbered from the most recent AIE.lock within the same tile. If the lockID
11// exceeds the number of locks on the tile, the pass generates an error and
12// terminates. AIE.lock operations for different tiles are numbered
13// independently. If there are existing lock IDs, this pass is idempotent
14// and only assigns lock IDs to locks without an ID.
15
18
19#include "mlir/Pass/Pass.h"
20#include "llvm/ADT/DenseMap.h"
21
22namespace xilinx::AIE {
23#define GEN_PASS_DEF_AIEASSIGNLOCKIDS
24#include "aie/Dialect/AIE/Transforms/AIEPasses.h.inc"
25} // namespace xilinx::AIE
26
27#define DEBUG_TYPE "aie-assign-lock-ids"
28
29using namespace mlir;
30using namespace xilinx;
31using namespace xilinx::AIE;
32
34 : xilinx::AIE::impl::AIEAssignLockIDsBase<AIEAssignLockIDsPass> {
35 void getDependentDialects(DialectRegistry &registry) const override {
36 registry.insert<func::FuncDialect>();
37 registry.insert<AIEDialect>();
38 }
39
40 void runOnOperation() override {
41 DeviceOp device = getOperation();
42 OpBuilder rewriter = OpBuilder::atBlockTerminator(device.getBody());
43
44 // All of the lock ops on a tile, separated into ops which have been
45 // assigned to a lock, and ops which have not.
46 struct TileLockOps {
47 DenseSet<int> assigned;
48 SmallVector<LockOp> unassigned;
49 };
50
51 DenseMap<TileOp, TileLockOps> tileToLocks;
52
53 // Construct data structure storing locks by tile.
54 device.walk<WalkOrder::PreOrder>([&](LockOp lockOp) {
55 TileOp tileOp = lockOp.getTileOp();
56 if (lockOp.getLockID().has_value()) {
57 auto lockID = lockOp.getLockID().value();
58 auto iter = tileToLocks.find(tileOp);
59 if (iter == tileToLocks.end())
60 tileToLocks.insert({tileOp, {{lockID}, /* unassigned = */ {}}});
61 else {
62 if (iter->second.assigned.find(lockID) !=
63 iter->second.assigned.end()) {
64 auto diag = lockOp->emitOpError("is assigned to the same lock (")
65 << lockID << ") as another op.";
66 diag.attachNote(tileOp.getLoc())
67 << "tile has lock ops assigned to same lock.";
68 return signalPassFailure();
69 }
70 iter->second.assigned.insert(lockID);
71 }
72 } else {
73 auto iter = tileToLocks.find(tileOp);
74 if (iter == tileToLocks.end())
75 tileToLocks.insert({tileOp, {/* assigned = */ {}, {lockOp}}});
76 else
77 iter->second.unassigned.push_back(lockOp);
78 }
79 });
80
81 // IR mutation: assign locks to all unassigned lock ops.
82 for (auto [tileOp, locks] : tileToLocks) {
83 const auto locksPerTile =
84 getTargetModel(tileOp).getNumLocks(tileOp.getCol(), tileOp.getRow());
85 uint32_t nextID = 0;
86 for (auto lockOp : locks.unassigned) {
87 while (nextID < locksPerTile &&
88 (locks.assigned.find(nextID) != locks.assigned.end())) {
89 ++nextID;
90 }
91 if (nextID == locksPerTile) {
92 mlir::InFlightDiagnostic diag =
93 lockOp->emitOpError("not allocated a lock.");
94 diag.attachNote(tileOp.getLoc()) << "because only " << locksPerTile
95 << " locks available in this tile.";
96 return signalPassFailure();
97 }
98 lockOp.setLockIDAttr(rewriter.getI32IntegerAttr(nextID));
99 ++nextID;
100 }
101 }
102 }
103};
104
105std::unique_ptr<OperationPass<DeviceOp>> AIE::createAIEAssignLockIDsPass() {
106 return std::make_unique<AIEAssignLockIDsPass>();
107}
virtual uint32_t getNumLocks(AIETileType tileType) const =0
Return the number of lock objects for a given tile type.
std::unique_ptr< mlir::OperationPass< DeviceOp > > createAIEAssignLockIDsPass()
const AIETargetModel & getTargetModel(mlir::Operation *op)
void getDependentDialects(DialectRegistry &registry) const override
void runOnOperation() override