MLIR-AIE
AIEAssignRuntimeSequenceBDIDs.cpp
Go to the documentation of this file.
1//===- AIEAssignRuntimeSequenceBDIDs.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 2024 Advanced Micro Devices, Inc.
8//
9//===----------------------------------------------------------------------===//
10
15
16#include "mlir/Pass/Pass.h"
17#include "llvm/ADT/TypeSwitch.h"
18
19using namespace mlir;
20using namespace xilinx;
21using namespace xilinx::AIEX;
22
24 : AIEAssignRuntimeSequenceBDIDsBase<AIEAssignRuntimeSequenceBDIDsPass> {
25
27 getGeneratorForTile(AIE::TileOp tile,
28 std::map<AIE::TileOp, BdIdGenerator> &gens) {
29 const AIETargetModel &targetModel =
30 tile->getParentOfType<AIE::DeviceOp>().getTargetModel();
31 auto genIt = gens.find(tile);
32 if (genIt == gens.end()) {
33 gens.insert(std::pair(
34 tile, BdIdGenerator(tile.getCol(), tile.getRow(), targetModel)));
35 }
36 BdIdGenerator &gen = std::get<1>(*gens.find(tile));
37 return gen;
38 }
39
40 LogicalResult runOnConfigureBDs(DMAConfigureTaskOp op,
41 std::map<AIE::TileOp, BdIdGenerator> &gens) {
42 AIE::TileOp tile = op.getTileOp();
43 BdIdGenerator &gen = getGeneratorForTile(tile, gens);
44
45 // First, honor all the user-specified BD IDs
46 WalkResult result = op.walk<WalkOrder::PreOrder>([&](AIE::DMABDOp bd_op) {
47 if (bd_op.getBdId().has_value()) {
48 if (gen.bdIdAlreadyAssigned(bd_op.getBdId().value())) {
49 op.emitOpError("Specified buffer descriptor ID ")
50 << bd_op.getBdId().value()
51 << " is already in use. Emit an aiex.dma_free_task operation to "
52 "reuse BDs.";
53 return WalkResult::interrupt();
54 }
55 gen.assignBdId(bd_op.getBdId().value());
56 }
57 return WalkResult::advance();
58 });
59 if (result.wasInterrupted()) {
60 return failure();
61 }
62
63 // Now, allocate BD IDs for all unspecified BDs
64 result =
65 op.walk<WalkOrder::PreOrder>([&](AIE::DMABDOp bd_op) {
66 if (bd_op.getBdId().has_value()) {
67 return WalkResult::advance();
68 }
69 // FIXME: use correct channelIndex here
70 std::optional<int32_t> next_id = gen.nextBdId(/*channelIndex=*/0);
71 if (!next_id) {
72 op.emitOpError()
73 << "Allocator exhausted available buffer descriptor IDs.";
74 return WalkResult::interrupt();
75 }
76 bd_op.setBdId(*next_id);
77 return WalkResult::advance();
78 });
79 if (result.wasInterrupted()) {
80 return failure();
81 }
82
83 return success();
84 }
85
86 LogicalResult runOnFreeBDs(DMAFreeTaskOp op,
87 std::map<AIE::TileOp, BdIdGenerator> &gens) {
88 DMAConfigureTaskOp task_op = op.getTaskOp();
89 if (!task_op) {
90 auto err = op.emitOpError(
91 "does not reference a valid configure_task operation.");
92 Operation *task_op = op.getTask().getDefiningOp();
93 if (llvm::isa<DMAStartBdChainOp>(task_op)) {
94 err.attachNote(task_op->getLoc())
95 << "Lower this operation first using the "
96 "--aie-materialize-bd-chains pass.";
97 }
98 if (llvm::isa<DMAConfigureTaskForOp>(task_op)) {
99 err.attachNote(task_op->getLoc())
100 << "Lower this operation first using the "
101 "--aie-substitute-shim-dma-allocations pass.";
102 }
103 return err;
104 }
105
106 AIE::TileOp tile = task_op.getTileOp();
107 BdIdGenerator &gen = getGeneratorForTile(tile, gens);
108
109 // First, honor all the hard-coded BD IDs
110 WalkResult result =
111 task_op.walk<WalkOrder::PreOrder>([&](AIE::DMABDOp bd_op) {
112 if (!bd_op.getBdId().has_value()) {
113 bd_op.emitOpError("Free called on BD chain with unassigned IDs.");
114 return WalkResult::interrupt();
115 }
116 if (!gen.bdIdAlreadyAssigned(bd_op.getBdId().value())) {
117 // Ignore double-frees.
118 return WalkResult::advance();
119 }
120 gen.freeBdId(bd_op.getBdId().value());
121 return WalkResult::advance();
122 });
123 if (result.wasInterrupted()) {
124 return failure();
125 }
126
127 op.erase();
128
129 return success();
130 }
131
132 void runOnOperation() override {
133
134 // This pass currently assigns BD IDs with a simple linear pass. IDs are
135 // assigned in sequence, and issuing an aiex.free_bds or aiex.await_bds op
136 // kills the correspondings IDs use. If in the future we support
137 // branching/jumping in the sequence function, a proper liveness analysis
138 // will become necessary here.
139
140 AIE::DeviceOp device = getOperation();
141 std::map<AIE::TileOp, BdIdGenerator> gens;
142
143 // Insert a free_bds operation for each await_bds
144 // After waiting for BD IDs, they can definitely safely be reused
145 device.walk([&](DMAAwaitTaskOp op) {
146 OpBuilder builder(op);
147 builder.setInsertionPointAfter(op);
148 builder.create<DMAFreeTaskOp>(op.getLoc(), op.getTask());
149 });
150
151 // TODO: Only walk the sequence function
152 device.walk([&](Operation *op) {
153 LogicalResult result =
154 llvm::TypeSwitch<Operation *, LogicalResult>(op)
155 .Case<DMAConfigureTaskOp>([&](DMAConfigureTaskOp op) {
156 return runOnConfigureBDs(op, gens);
157 })
158 .Case<DMAFreeTaskOp>(
159 [&](DMAFreeTaskOp op) { return runOnFreeBDs(op, gens); })
160 .Default([](Operation *op) { return success(); });
161 if (failed(result)) {
162 return signalPassFailure();
163 }
164 });
165 }
166};
167
168std::unique_ptr<OperationPass<AIE::DeviceOp>>
170 return std::make_unique<AIEAssignRuntimeSequenceBDIDsPass>();
171}
std::unique_ptr< mlir::OperationPass< AIE::DeviceOp > > createAIEAssignRuntimeSequenceBDIDsPass()
const AIETargetModel & getTargetModel(mlir::Operation *op)
LogicalResult runOnFreeBDs(DMAFreeTaskOp op, std::map< AIE::TileOp, BdIdGenerator > &gens)
LogicalResult runOnConfigureBDs(DMAConfigureTaskOp op, std::map< AIE::TileOp, BdIdGenerator > &gens)
BdIdGenerator & getGeneratorForTile(AIE::TileOp tile, std::map< AIE::TileOp, BdIdGenerator > &gens)
bool bdIdAlreadyAssigned(uint32_t bdId)
void assignBdId(uint32_t bdId)
void freeBdId(uint32_t bdId)
std::optional< uint32_t > nextBdId(int channelIndex)