MLIR-AIE 1.4.6.dev
AIEImportOverlay.cpp
Go to the documentation of this file.
1//===- AIEImportOverlay.cpp ------------------------------------*- C++ -*-===//
2//
3// Copyright (C) 2026 Advanced Micro Devices, Inc.
4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
5//
6//===----------------------------------------------------------------------===//
7
10#include "mlir/IR/IRMapping.h"
11#include "mlir/IR/OperationSupport.h"
12
13using namespace mlir;
14using namespace xilinx::AIE;
15
16namespace xilinx::AIE {
17#define GEN_PASS_DEF_AIEIMPORTOVERLAY
18#include "aie/Dialect/AIE/Transforms/AIEPasses.h.inc"
19} // namespace xilinx::AIE
20
21namespace {
22struct AIEImportOverlayPass
23 : xilinx::AIE::impl::AIEImportOverlayBase<AIEImportOverlayPass> {
24 LogicalResult importDevice(DeviceOp device, DeviceOp overlay) {
25 if (overlay == device || overlay.getOverlayAttr())
26 return device.emitOpError(
27 "requires an overlay without an overlay reference");
28 if (device.getDevice() != overlay.getDevice())
29 return device.emitOpError("and its overlay must use the same target");
30 IRMapping mapping;
31 OpBuilder builder(device.getContext());
32 auto equivalent = [&](Operation *source, Operation *dest) {
33 IRMapping values(mapping);
34 // The overlay supplies header and priority settings for matching flows.
35 OwningOpRef<Operation *> normalized;
36 if (auto flow = dyn_cast<PacketFlowOp>(source)) {
37 if (isa<PacketFlowOp>(dest)) {
38 normalized = dest->clone();
39 auto other = cast<PacketFlowOp>(*normalized);
40 other.setKeepPktHeaderAttr(flow.getKeepPktHeaderAttr());
41 other.setPriorityRouteAttr(flow.getPriorityRouteAttr());
42 dest = *normalized;
43 }
44 }
45 return OperationEquivalence::isEquivalentTo(
46 source, dest,
47 [&](Value a, Value b) {
48 return success(values.lookupOrDefault(a) == b);
49 },
50 [&](Value a, Value b) { values.map(a, b); },
51 OperationEquivalence::IgnoreLocations |
52 OperationEquivalence::IgnoreDiscardableAttrs);
53 };
54 auto inherit = [&](Operation *source, Operation *dest) {
55 mapping.map(source->getResults(), dest->getResults());
56 if (auto flow = dyn_cast<PacketFlowOp>(source)) {
57 auto other = cast<PacketFlowOp>(dest);
58 other.setKeepPktHeaderAttr(flow.getKeepPktHeaderAttr());
59 other.setPriorityRouteAttr(flow.getPriorityRouteAttr());
60 }
61 for (NamedAttribute attr : source->getDiscardableAttrs())
62 if (!dest->hasAttr(attr.getName()))
63 dest->setAttr(attr.getName(), attr.getValue());
64 };
65 for (TileOp tile : overlay.getOps<TileOp>()) {
66 builder.setInsertionPointToStart(device.getBody());
67 TileOp dest = TileOp::getOrCreate(builder, device, tile.colIndex(),
68 tile.rowIndex());
69 mapping.map(tile.getResult(), dest.getResult());
70 if (auto id = tile->getAttr("controller_id")) {
71 if (auto own = dest->getAttr("controller_id"); own && own != id)
72 return dest.emitOpError(
73 "has a different controller ID from its overlay");
74 dest->setAttr("controller_id", id);
75 }
76 }
77 std::function<LogicalResult(Block &, Block &)> importBlock =
78 [&](Block &source, Block &dest) -> LogicalResult {
79 auto slot = dest.begin();
80 for (Operation &op : source.without_terminator()) {
81 if (isa<TileOp>(op))
82 continue;
83 // Rule order fixes both hardware slots and first-match precedence.
84 if (isa<PacketRuleOp>(op) && slot != dest.end() && !isa<EndOp>(*slot)) {
85 if (!equivalent(&op, &*slot))
86 return slot->emitOpError(
87 "occupies a packet-rule slot required by its overlay");
88 inherit(&op, &*slot++);
89 continue;
90 }
91 bool container = isa<SwitchboxOp, ShimMuxOp, PacketRulesOp>(op);
92 Operation *match = nullptr;
93 if (container) {
94 Operation *shape = op.cloneWithoutRegions();
95 for (Operation &candidate : dest.without_terminator()) {
96 if (candidate.getName() != op.getName())
97 continue;
98 Operation *other = candidate.cloneWithoutRegions();
99 bool same = equivalent(shape, other);
100 other->destroy();
101 if (same) {
102 match = &candidate;
103 break;
104 }
105 }
106 shape->destroy();
107 } else {
108 for (Operation &candidate : dest.without_terminator()) {
109 if (equivalent(&op, &candidate)) {
110 match = &candidate;
111 break;
112 }
113 auto name =
114 op.getAttrOfType<StringAttr>(SymbolTable::getSymbolAttrName());
115 if (name && name == candidate.getAttrOfType<StringAttr>(
116 SymbolTable::getSymbolAttrName()))
117 return candidate.emitOpError("conflicts with an overlay symbol ")
118 << name;
119 }
120 }
121 if (match) {
122 if (container) {
123 mapping.map(op.getResults(), match->getResults());
124 if (failed(importBlock(op.getRegion(0).front(),
125 match->getRegion(0).front())))
126 return failure();
127 } else {
128 inherit(&op, match);
129 }
130 } else {
131 builder.setInsertionPoint(dest.getTerminator());
132 builder.clone(op, mapping);
133 }
134 }
135 return success();
136 };
137 if (failed(importBlock(*overlay.getBody(), *device.getBody())))
138 return failure();
139 return success();
140 }
141
142 void runOnOperation() override {
143 ModuleOp module = getOperation();
144 for (DeviceOp device : module.getOps<DeviceOp>()) {
145 auto ref = device.getOverlayAttr();
146 if (!ref)
147 continue;
148 auto overlay = module.lookupSymbol<DeviceOp>(ref.getValue());
149 if (!overlay) {
150 device.emitOpError("references an unknown overlay device ") << ref;
151 return signalPassFailure();
152 }
153 if (failed(importDevice(device, overlay)))
154 return signalPassFailure();
155 }
156 }
157};
158} // namespace
159
160std::unique_ptr<OperationPass<ModuleOp>>
162 return std::make_unique<AIEImportOverlayPass>();
163}
std::unique_ptr< mlir::OperationPass< mlir::ModuleOp > > createAIEImportOverlayPass()