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