CIRCT 22.0.0git
Loading...
Searching...
No Matches
DeviceDB.cpp
Go to the documentation of this file.
1//===- DeviceDB.cpp - Implement a device database -------------------------===//
2//
3// Part of the LLVM Project, 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//===----------------------------------------------------------------------===//
8
12
13#include "mlir/IR/Builders.h"
14#include "llvm/ADT/TypeSwitch.h"
15
16using namespace circt;
17using namespace msft;
18
19//===----------------------------------------------------------------------===//
20// PrimitiveDB.
21//===----------------------------------------------------------------------===//
22// NOTE: Nothing in this implementation is in any way the most optimal
23// implementation. We put off deciding what the correct data structure is until
24// we have a better handle of the operations it must accelerate. Performance is
25// not an immediate goal.
26//===----------------------------------------------------------------------===//
27
28PrimitiveDB::PrimitiveDB(MLIRContext *ctxt) : ctxt(ctxt) {}
29
30/// Assign an instance to a primitive. Return false if another instance is
31/// already placed at that location.
32LogicalResult PrimitiveDB::addPrimitive(PhysLocationAttr loc) {
33 DenseSet<PrimitiveType> &primsAtLoc = getLeaf(loc);
34 PrimitiveType prim = loc.getPrimitiveType().getValue();
35 if (primsAtLoc.contains(prim))
36 return failure();
37 primsAtLoc.insert(prim);
38 return success();
39}
40
41/// Assign an instance to a primitive. Return false if another instance is
42/// already placed at that location.
43/// Check to see if a primitive exists.
44bool PrimitiveDB::isValidLocation(PhysLocationAttr loc) {
45 DenseSet<PrimitiveType> primsAtLoc = getLeaf(loc);
46 return primsAtLoc.contains(loc.getPrimitiveType().getValue());
47}
48
49PrimitiveDB::DimPrimitiveType &PrimitiveDB::getLeaf(PhysLocationAttr loc) {
50 return placements[loc.getX()][loc.getY()][loc.getNum()];
51}
52
53void PrimitiveDB::foreach (
54 function_ref<void(PhysLocationAttr)> callback) const {
55 for (const auto &x : placements)
56 for (const auto &y : x.second)
57 for (const auto &n : y.second)
58 for (auto p : n.second)
59 callback(PhysLocationAttr::get(ctxt, PrimitiveTypeAttr::get(ctxt, p),
60 x.first, y.first, n.first));
61}
62
63//===----------------------------------------------------------------------===//
64// PlacementDB.
65//===----------------------------------------------------------------------===//
66// NOTE: Nothing in this implementation is in any way the most optimal
67// implementation. We put off deciding what the correct data structure is until
68// we have a better handle of the operations it must accelerate. Performance is
69// not an immediate goal.
70//===----------------------------------------------------------------------===//
71
72PlacementDB::PlacementDB(mlir::ModuleOp topMod)
73 : ctxt(topMod->getContext()), topMod(topMod), seeded(false) {
75}
76PlacementDB::PlacementDB(mlir::ModuleOp topMod, const PrimitiveDB &seed)
77 : ctxt(topMod->getContext()), topMod(topMod), seeded(false) {
78
79 seed.foreach ([this](PhysLocationAttr loc) { (void)getLeaf(loc); });
80 seeded = true;
82}
83
84/// Assign an instance to a primitive. Return null if another instance is
85/// already placed at that location
87 PhysLocationAttr loc, StringRef subPath,
88 Location srcLoc) {
89 StringAttr subPathAttr;
90 if (!subPath.empty())
91 subPathAttr = StringAttr::get(inst->getContext(), subPath);
92 auto builder = OpBuilder(inst.getBody());
93 PDPhysLocationOp locOp = PDPhysLocationOp::create(
94 builder, srcLoc, loc, subPathAttr, FlatSymbolRefAttr());
95 if (succeeded(insertPlacement(locOp, locOp.getLoc())))
96 return locOp;
97 locOp->erase();
98 return {};
99}
100PDRegPhysLocationOp PlacementDB::place(DynamicInstanceOp inst,
101 LocationVectorAttr locs,
102 Location srcLoc) {
103 auto builder = OpBuilder(inst.getBody());
104 PDRegPhysLocationOp locOp =
105 PDRegPhysLocationOp::create(builder, srcLoc, locs, FlatSymbolRefAttr());
106 for (PhysLocationAttr loc : locs.getLocs())
107 if (failed(insertPlacement(locOp, loc))) {
108 locOp->erase();
109 return {};
110 }
111 return locOp;
112}
113
114LogicalResult PlacementDB::insertPlacement(DynInstDataOpInterface op,
115 PhysLocationAttr loc) {
116 if (!loc)
117 return success();
118 PlacementCell *leaf = getLeaf(loc);
119 if (!leaf)
120 return op->emitOpError("Could not apply placement. Invalid location: ")
121 << loc;
122 if (leaf->locOp != nullptr)
123 return op->emitOpError("Could not apply placement ")
124 << loc << ". Position already occupied by "
125 << cast<DynamicInstanceOp>(leaf->locOp->getParentOp()).getPath();
126
127 leaf->locOp = op;
128 return success();
129}
130
131/// Assign an operation to a physical region. Return false on failure.
133 DeclPhysicalRegionOp physregion,
134 StringRef subPath, Location srcLoc) {
135 StringAttr subPathAttr;
136 if (!subPath.empty())
137 subPathAttr = StringAttr::get(inst->getContext(), subPath);
138 auto builder = OpBuilder::atBlockEnd(&inst.getBody().front());
139 PDPhysRegionOp regOp = PDPhysRegionOp::create(
140 builder, srcLoc, FlatSymbolRefAttr::get(physregion), subPathAttr,
141 FlatSymbolRefAttr());
142 regionPlacements.push_back(regOp);
143 return regOp;
144}
145
146/// Using the operation attributes, add the proper placements to the database.
147/// Return the number of placements which weren't added due to conflicts.
149 size_t numFailed = 0;
150 inst->walk([&](Operation *op) {
151 LogicalResult added = TypeSwitch<Operation *, LogicalResult>(op)
152 .Case([&](PDPhysLocationOp op) {
153 return insertPlacement(op, op.getLoc());
154 })
155 .Case([&](PDRegPhysLocationOp op) {
156 ArrayRef<PhysLocationAttr> locs =
157 op.getLocs().getLocs();
158 for (auto loc : locs)
159 if (failed(insertPlacement(op, loc)))
160 return failure();
161 return success();
162 })
163 .Case([&](PDPhysRegionOp op) {
164 regionPlacements.push_back(op);
165 return success();
166 })
167 .Default([](Operation *op) { return failure(); });
168 if (failed(added))
169 ++numFailed;
170 });
171
172 return numFailed;
173}
174
175/// Walk the entire design adding placements.
177 size_t failed = 0;
178 for (auto inst : topMod.getOps<DynamicInstanceOp>())
179 failed += addPlacements(inst);
180 return failed;
181}
182
183/// Remove the placement at a given location. Returns failure if nothing was
184/// placed there.
186 removePlacement(locOp, locOp.getLoc());
187 locOp.erase();
188}
189
190/// Move the placement at a given location to a new location. Returns failure
191/// if nothing was placed at the previous location or something is already
192/// placed at the new location.
194 PhysLocationAttr newLoc) {
195 PhysLocationAttr from = locOp.getLoc();
196 if (failed(movePlacementCheck(locOp, from, newLoc)))
197 return failure();
198 locOp.setLocAttr(newLoc);
199 movePlacement(locOp, from, newLoc);
200 return success();
201}
202
203/// Remove the placement at a given location. Returns failure if nothing was
204/// placed there.
205void PlacementDB::removePlacement(PDRegPhysLocationOp locOp) {
206 for (PhysLocationAttr loc : locOp.getLocs().getLocs())
207 if (loc)
208 removePlacement(locOp, loc);
209 locOp.erase();
210}
211
212/// Move the placement at a given location to a new location. Returns failure
213/// if nothing was placed at the previous location or something is already
214/// placed at the new location.
215LogicalResult PlacementDB::movePlacement(PDRegPhysLocationOp locOp,
216 LocationVectorAttr newLocs) {
217 ArrayRef<PhysLocationAttr> fromLocs = locOp.getLocs().getLocs();
218
219 // Check that each move/insert/delete will succeed before doing any of the
220 // mutations.
221 for (auto [from, to] : llvm::zip(fromLocs, newLocs.getLocs())) {
222 // If 'from' and 'to' are both non-null, this location is being moved.
223 if (from && to && failed(movePlacementCheck(locOp, from, to)))
224 return failure();
225 // If only 'to' is valid, this location is the initial placement.
226 if (to && getInstanceAt(to))
227 return failure();
228 // If 'to' isn't valid, it's a placement removal which will always succeed.
229 }
230
231 // Mutate.
232 for (auto [from, to] : llvm::zip(fromLocs, newLocs.getLocs())) {
233 // If 'from' and 'to' are both non-null, this location is being moved.
234 if (from && to)
235 movePlacement(locOp, from, to);
236 // If 'to' isn't valid, it's a placement removal.
237 else if (from)
238 removePlacement(locOp, from);
239 // If only 'to' is valid, this location is the initial placement. Since we
240 // checked that there isn't anything currently located at 'to' this call
241 // will never fail.
242 else if (to)
243 (void)insertPlacement(locOp, to);
244 }
245
246 locOp.setLocsAttr(newLocs);
247 return success();
248}
249
250void PlacementDB::removePlacement(DynInstDataOpInterface op,
251 PhysLocationAttr loc) {
252 PlacementCell *leaf = getLeaf(loc);
253 assert(leaf && "Could not find op at location specified by op");
254 assert(leaf->locOp == op);
255 leaf->locOp = {};
256}
257
258LogicalResult PlacementDB::movePlacementCheck(DynInstDataOpInterface op,
259 PhysLocationAttr from,
260 PhysLocationAttr to) {
261 if (from == to)
262 return success();
263
264 PlacementCell *oldLeaf = getLeaf(from);
265 PlacementCell *newLeaf = getLeaf(to);
266
267 if (!oldLeaf || !newLeaf)
268 return failure();
269
270 if (oldLeaf->locOp == nullptr)
271 return op.emitError("cannot move from a location not occupied by "
272 "specified op. Currently unoccupied");
273 if (oldLeaf->locOp != op)
274 return op.emitError("cannot move from a location not occupied by "
275 "specified op. Currently occupied by ")
276 << oldLeaf->locOp;
277 if (newLeaf->locOp)
278 return op.emitError(
279 "cannot move to new location since location is occupied by ")
280 << cast<DynamicInstanceOp>(newLeaf->locOp->getParentOp()).getPath();
281 return success();
282}
283
284void PlacementDB::movePlacement(DynInstDataOpInterface op,
285 PhysLocationAttr from, PhysLocationAttr to) {
286 assert(succeeded(movePlacementCheck(op, from, to)) &&
287 "Call `movePlacementCheck` first to ensure that move is legal.");
288 if (from == to)
289 return;
290 PlacementCell *oldLeaf = getLeaf(from);
291 PlacementCell *newLeaf = getLeaf(to);
292 newLeaf->locOp = op;
293 oldLeaf->locOp = {};
294}
295
296/// Lookup the instance at a particular location.
297DynInstDataOpInterface PlacementDB::getInstanceAt(PhysLocationAttr loc) {
298 auto innerMap = placements[loc.getX()][loc.getY()][loc.getNum()];
299 auto instF = innerMap.find(loc.getPrimitiveType().getValue());
300 if (instF == innerMap.end())
301 return {};
302 if (!instF->getSecond().locOp)
303 return {};
304 return instF->getSecond().locOp;
305}
306
307PhysLocationAttr PlacementDB::getNearestFreeInColumn(PrimitiveType prim,
308 uint64_t columnNum,
309 uint64_t nearestToY) {
310 // Simplest possible algorithm.
311 PhysLocationAttr nearest = {};
313 [&nearest, nearestToY](PhysLocationAttr loc, Operation *locOp) {
314 if (locOp)
315 return;
316 if (!nearest) {
317 nearest = loc;
318 return;
319 }
320 int64_t curDist =
321 std::abs((int64_t)nearestToY - (int64_t)nearest.getY());
322 int64_t replDist = std::abs((int64_t)nearestToY - (int64_t)loc.getY());
323 if (replDist < curDist)
324 nearest = loc;
325 },
326 std::make_tuple(columnNum, columnNum, -1, -1), prim);
327 return nearest;
328}
329
331 PrimitiveType primType = loc.getPrimitiveType().getValue();
332
333 DimNumMap &nums = placements[loc.getX()][loc.getY()];
334 if (!seeded)
335 return &nums[loc.getNum()][primType];
336 if (!nums.count(loc.getNum()))
337 return {};
338
339 DimDevType &primitives = nums[loc.getNum()];
340 if (primitives.count(primType) == 0)
341 return {};
342 return &primitives[primType];
343}
344
345/// Walker for placements.
347 function_ref<void(PhysLocationAttr, DynInstDataOpInterface)> callback,
348 std::tuple<int64_t, int64_t, int64_t, int64_t> bounds,
349 std::optional<PrimitiveType> primType, std::optional<WalkOrder> walkOrder) {
350 uint64_t xmin = std::get<0>(bounds) < 0 ? 0 : std::get<0>(bounds);
351 uint64_t xmax = std::get<1>(bounds) < 0 ? std::numeric_limits<uint64_t>::max()
352 : (uint64_t)std::get<1>(bounds);
353 uint64_t ymin = std::get<2>(bounds) < 0 ? 0 : std::get<2>(bounds);
354 uint64_t ymax = std::get<3>(bounds) < 0 ? std::numeric_limits<uint64_t>::max()
355 : (uint64_t)std::get<3>(bounds);
356
357 // TODO: Since the data structures we're using aren't sorted, the best we can
358 // do is iterate and filter. If a specific order is requested, we sort the
359 // keys by that as we go. Once we get to performance, we'll figure out the
360 // right data structure.
361
362 auto maybeSort = [](auto &container, auto direction) {
363 if (!direction.has_value())
364 return;
365 if (*direction == Direction::NONE)
366 return;
367
368 llvm::sort(container, [direction](auto colA, auto colB) {
369 if (*direction == Direction::ASC)
370 return colA.first < colB.first;
371
372 return colA.first > colB.first;
373 });
374 };
375
376 // X loop.
377 SmallVector<std::pair<size_t, DimYMap>> cols(placements.begin(),
378 placements.end());
379 maybeSort(cols, llvm::transformOptional(walkOrder,
380 [](auto wo) { return wo.columns; }));
381 for (const auto &colF : cols) {
382 size_t x = colF.first;
383 if (x < xmin || x > xmax)
384 continue;
385 DimYMap yMap = colF.second;
386
387 // Y loop.
388 SmallVector<std::pair<size_t, DimNumMap>> rows(yMap.begin(), yMap.end());
389 maybeSort(rows, llvm::transformOptional(walkOrder,
390 [](auto wo) { return wo.rows; }));
391 for (const auto &rowF : rows) {
392 size_t y = rowF.first;
393 if (y < ymin || y > ymax)
394 continue;
395 DimNumMap numMap = rowF.second;
396
397 // Num loop.
398 for (auto &numF : numMap) {
399 size_t num = numF.getFirst();
400 DimDevType devMap = numF.getSecond();
401
402 // DevType loop.
403 for (auto &devF : devMap) {
404 PrimitiveType devtype = devF.getFirst();
405 if (primType && devtype != *primType)
406 continue;
407 PlacementCell &inst = devF.getSecond();
408
409 // Marshall and run the callback.
410 PhysLocationAttr loc = PhysLocationAttr::get(
411 ctxt, PrimitiveTypeAttr::get(ctxt, devtype), x, y, num);
412 callback(loc, inst.locOp);
413 }
414 }
415 }
416 }
417}
418
419/// Walk the region placement information.
421 function_ref<void(PDPhysRegionOp)> callback) {
422 for (auto regOp : regionPlacements)
423 callback(regOp);
424}
assert(baseType &&"element must be base type")
PlacementDB(MlirModule top, PrimitiveDB *seed)
bool isValidLocation(MlirAttribute loc)
PrimitiveDB(MlirContext ctxt)
bool addPrimitive(MlirAttribute locAndPrim)
LogicalResult movePlacementCheck(DynInstDataOpInterface op, PhysLocationAttr from, PhysLocationAttr to)
Check to make sure the move is going to succeed.
Definition DeviceDB.cpp:258
void removePlacement(PDPhysLocationOp)
Remove the placement from the DB and IR. Erases the op.
DynInstDataOpInterface getInstanceAt(PhysLocationAttr)
Lookup the instance at a particular location.
size_t addPlacements(DynamicInstanceOp inst)
Load the placements from inst.
Definition DeviceDB.cpp:148
LogicalResult movePlacement(PDPhysLocationOp, PhysLocationAttr)
Move a placement location to a new location.
DenseMap< size_t, DimNumMap > DimYMap
Definition DeviceDB.h:134
PDPhysLocationOp place(DynamicInstanceOp inst, PhysLocationAttr, StringRef subpath, Location srcLoc)
Assign an instance to a primitive.
size_t addDesignPlacements()
Load the database from the IR.
Definition DeviceDB.cpp:176
PlacementCell * getLeaf(PhysLocationAttr)
Get the leaf node.
Definition DeviceDB.cpp:330
DenseMap< PrimitiveType, PlacementCell > DimDevType
Definition DeviceDB.h:132
PlacementDB(mlir::ModuleOp topMod)
Create a placement db containing all the placements in 'topMod'.
Definition DeviceDB.cpp:72
void walkRegionPlacements(function_ref< void(PDPhysRegionOp)>)
Walk the region placement information.
Definition DeviceDB.cpp:420
PhysLocationAttr getNearestFreeInColumn(PrimitiveType prim, uint64_t column, uint64_t nearestToY)
Find the nearest unoccupied primitive location to 'nearestToY' in 'column'.
PDPhysRegionOp placeIn(DynamicInstanceOp inst, DeclPhysicalRegionOp, StringRef subPath, Location srcLoc)
Assign an operation to a physical region. Return false on failure.
Definition DeviceDB.cpp:132
mlir::ModuleOp topMod
Definition DeviceDB.h:130
void walkPlacements(function_ref< void(PhysLocationAttr, DynInstDataOpInterface)>, std::tuple< int64_t, int64_t, int64_t, int64_t > bounds=std::make_tuple(-1, -1, -1, -1), std::optional< PrimitiveType > primType={}, std::optional< WalkOrder >={})
Walk the placement information in some sort of reasonable order.
DenseMap< size_t, DimDevType > DimNumMap
Definition DeviceDB.h:133
LogicalResult insertPlacement(DynInstDataOpInterface op, PhysLocationAttr)
Definition DeviceDB.cpp:114
RegionPlacements regionPlacements
Definition DeviceDB.h:143
A data structure to contain locations of the primitives on the device.
Definition DeviceDB.h:27
void foreach(function_ref< void(PhysLocationAttr)> callback) const
Iterate over all the primitive locations, executing 'callback' on each one.
Definition DeviceDB.cpp:53
DenseSet< PrimitiveType > DimPrimitiveType
Definition DeviceDB.h:42
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition msft.py:1