CIRCT 20.0.0git
Loading...
Searching...
No Matches
Utilities.h
Go to the documentation of this file.
1//===- Utilities.h - SSP <-> circt::scheduling infra conversion -*- C++ -*-===//
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//
9// This file provides utilities for the conversion between SSP IR and the
10// extensible problem model in the scheduling infrastructure.
11//
12//===----------------------------------------------------------------------===//
13
14#ifndef CIRCT_DIALECT_SSP_SSPUTILITIES_H
15#define CIRCT_DIALECT_SSP_SSPUTILITIES_H
16
21
22#include "mlir/IR/ImplicitLocOpBuilder.h"
23#include "mlir/IR/SymbolTable.h"
24
25#include "llvm/ADT/DenseMap.h"
26#include "llvm/ADT/TypeSwitch.h"
27
28#include <functional>
29
30namespace circt {
31namespace ssp {
32
35
36//===----------------------------------------------------------------------===//
37// ssp.InstanceOp -> circt::scheduling::Problem (or subclasses)
38//===----------------------------------------------------------------------===//
39
40template <typename ProblemT>
41void loadOperationProperties(ProblemT &, Operation *, ArrayAttr) {}
42template <typename ProblemT, typename OperationPropertyT,
43 typename... OperationPropertyTs>
44void loadOperationProperties(ProblemT &prob, Operation *op, ArrayAttr props) {
45 if (!props)
46 return;
47 for (auto prop : props) {
48 TypeSwitch<Attribute>(prop)
49 .Case<OperationPropertyT, OperationPropertyTs...>(
50 [&](auto p) { p.setInProblem(prob, op); });
51 }
52}
53
54template <typename ProblemT>
55void loadOperatorTypeProperties(ProblemT &, OperatorType, ArrayAttr) {}
56template <typename ProblemT, typename OperatorTypePropertyT,
57 typename... OperatorTypePropertyTs>
59 ArrayAttr props) {
60 if (!props)
61 return;
62 for (auto prop : props) {
63 TypeSwitch<Attribute>(prop)
64 .Case<OperatorTypePropertyT, OperatorTypePropertyTs...>(
65 [&](auto p) { p.setInProblem(prob, opr); });
66 }
67}
68
69template <typename ProblemT>
70void loadDependenceProperties(ProblemT &, Dependence, ArrayAttr) {}
71template <typename ProblemT, typename DependencePropertyT,
72 typename... DependencePropertyTs>
73void loadDependenceProperties(ProblemT &prob, Dependence dep, ArrayAttr props) {
74 if (!props)
75 return;
76 for (auto prop : props) {
77 TypeSwitch<Attribute>(prop)
78 .Case<DependencePropertyT, DependencePropertyTs...>(
79 [&](auto p) { p.setInProblem(prob, dep); });
80 }
81}
82
83template <typename ProblemT>
84void loadInstanceProperties(ProblemT &, ArrayAttr) {}
85template <typename ProblemT, typename InstancePropertyT,
86 typename... InstancePropertyTs>
87void loadInstanceProperties(ProblemT &prob, ArrayAttr props) {
88 if (!props)
89 return;
90 for (auto prop : props) {
91 TypeSwitch<Attribute>(prop).Case<InstancePropertyT, InstancePropertyTs...>(
92 [&](auto p) { p.setInProblem(prob); });
93 }
94}
95
96/// Load the operator type represented by \p oprOp into \p prob under a unique
97/// name informed by \p oprIds, and attempt to set its properties from the
98/// given attribute classes. The registered name is returned. The template
99/// instantiation fails if properties are incompatible with \p ProblemT.
100template <typename ProblemT, typename... OperatorTypePropertyTs>
101OperatorType loadOperatorType(ProblemT &prob, OperatorTypeOp oprOp,
103 OperatorType opr = oprOp.getNameAttr();
104 unsigned &id = oprIds[opr];
105 if (id > 0)
106 opr = StringAttr::get(opr.getContext(),
107 opr.getValue() + Twine('_') + Twine(id));
108 ++id;
109 assert(!prob.hasOperatorType(opr));
110 prob.insertOperatorType(opr);
111 loadOperatorTypeProperties<ProblemT, OperatorTypePropertyTs...>(
112 prob, opr, oprOp.getSspPropertiesAttr());
113 return opr;
114}
115
116/// Construct an instance of \p ProblemT from \p instOp, and attempt to set
117/// properties from the given attribute classes. The attribute tuples are used
118/// solely for grouping/inferring the template parameter packs. The tuple
119/// elements may therefore be unitialized objects. The template instantiation
120/// fails if properties are incompatible with \p ProblemT.
121///
122/// Operations may link to operator types in other libraries, but the origin of
123/// an operator type will not be preserved in the problem instance. As this
124/// could lead to conflicts, operator types will be automatically renamed in the
125/// returned instance.
126///
127/// Example: To load an instance of the `circt::scheduling::CyclicProblem` with
128/// all its input and solution properties, call this as follows:
129///
130/// ```
131/// loadProblem<CyclicProblem>(instOp,
132/// std::make_tuple(LinkedOperatorTypeAttr(), StartTimeAttr()),
133/// std::make_tuple(LatencyAttr()),
134/// std::make_tuple(DistanceAttr()),
135/// std::make_tuple(InitiationIntervalAttr()));
136/// ```
137template <typename ProblemT, typename... OperationPropertyTs,
138 typename... OperatorTypePropertyTs, typename... DependencePropertyTs,
139 typename... InstancePropertyTs>
140ProblemT loadProblem(InstanceOp instOp,
141 std::tuple<OperationPropertyTs...> opProps,
142 std::tuple<OperatorTypePropertyTs...> oprProps,
143 std::tuple<DependencePropertyTs...> depProps,
144 std::tuple<InstancePropertyTs...> instProps) {
145 ProblemT prob(instOp);
146
147 loadInstanceProperties<ProblemT, InstancePropertyTs...>(
148 prob, instOp.getSspPropertiesAttr());
149 if (auto instName = instOp.getSymNameAttr())
150 prob.setInstanceName(instName);
151
152 // Use IDs to disambiguate operator types with the same name defined in
153 // different libraries.
155 // Map `OperatorTypeOp`s to their (possibly uniqued) name in the problem
156 // instance.
158
159 // Register all operator types in the instance's library.
160 auto libraryOp = instOp.getOperatorLibrary();
161 libraryOp.walk([&](OperatorTypeOp oprOp) {
162 operatorTypes[oprOp] =
163 loadOperatorType<ProblemT, OperatorTypePropertyTs...>(prob, oprOp,
164 operatorTypeIds);
165 });
166 if (auto libName = libraryOp.getSymNameAttr())
167 prob.setLibraryName(libName);
168
169 // Register all operations first, in order to retain their original order.
170 auto graphOp = instOp.getDependenceGraph();
171 graphOp.walk([&](OperationOp opOp) {
172 prob.insertOperation(opOp);
173 loadOperationProperties<ProblemT, OperationPropertyTs...>(
174 prob, opOp, opOp.getSspPropertiesAttr());
175 if (auto opName = opOp.getSymNameAttr())
176 prob.setOperationName(opOp, opName);
177
178 // Nothing else to check if no linked operator type is set for `opOp`,
179 // because the operation doesn't carry a `LinkedOperatorTypeAttr`, or that
180 // class is not part of the `OperationPropertyTs` to load.
181 if (!prob.getLinkedOperatorType(opOp).has_value())
182 return;
183
184 // Otherwise, inspect the corresponding attribute to make sure the operator
185 // type is available.
186 SymbolRefAttr oprRef = opOp.getLinkedOperatorTypeAttr().getValue();
187
188 Operation *oprOp;
189 // 1) Look in the instance's library.
190 oprOp = SymbolTable::lookupSymbolIn(libraryOp, oprRef);
191 // 2) Try to resolve a nested reference to the instance's library.
192 if (!oprOp)
193 oprOp = SymbolTable::lookupSymbolIn(instOp, oprRef);
194 // 3) Look outside of the instance.
195 if (!oprOp)
196 oprOp =
197 SymbolTable::lookupNearestSymbolFrom(instOp->getParentOp(), oprRef);
198
199 assert(oprOp && isa<OperatorTypeOp>(oprOp)); // checked by verifier
200
201 // Load the operator type from `oprOp` if needed.
202 auto &opr = operatorTypes[oprOp];
203 if (!opr)
204 opr = loadOperatorType<ProblemT, OperatorTypePropertyTs...>(
205 prob, cast<OperatorTypeOp>(oprOp), operatorTypeIds);
206
207 // Update `opOp`'s property (may be a no-op if `opr` wasn't renamed).
208 prob.setLinkedOperatorType(opOp, opr);
209 });
210
211 // Then walk them again, and load auxiliary dependences as well as any
212 // dependence properties.
213 graphOp.walk([&](OperationOp opOp) {
214 ArrayAttr depsAttr = opOp.getDependencesAttr();
215 if (!depsAttr)
216 return;
217
218 for (auto depAttr : depsAttr.getAsRange<DependenceAttr>()) {
219 Dependence dep;
220 if (FlatSymbolRefAttr sourceRef = depAttr.getSourceRef()) {
221 Operation *sourceOp = SymbolTable::lookupSymbolIn(graphOp, sourceRef);
222 assert(sourceOp);
223 dep = Dependence(sourceOp, opOp);
224 LogicalResult res = prob.insertDependence(dep);
225 assert(succeeded(res));
226 (void)res;
227 } else
228 dep = Dependence(&opOp->getOpOperand(depAttr.getOperandIdx()));
229
230 loadDependenceProperties<ProblemT, DependencePropertyTs...>(
231 prob, dep, depAttr.getProperties());
232 }
233 });
234
235 return prob;
236}
237
238//===----------------------------------------------------------------------===//
239// circt::scheduling::Problem (or subclasses) -> ssp.InstanceOp
240//===----------------------------------------------------------------------===//
241
242template <typename ProblemT, typename... OperationPropertyTs>
243ArrayAttr saveOperationProperties(ProblemT &prob, Operation *op,
244 ImplicitLocOpBuilder &b) {
245 SmallVector<Attribute> props;
246 Attribute prop;
247 // Fold expression: Expands to a `getFromProblem` and a conditional
248 // `push_back` call for each of the `OperationPropertyTs`.
249 ((prop = OperationPropertyTs::getFromProblem(prob, op, b.getContext()),
250 prop ? props.push_back(prop) : (void)prop),
251 ...);
252 return props.empty() ? ArrayAttr() : b.getArrayAttr(props);
253}
254
255template <typename ProblemT, typename... OperatorTypePropertyTs>
256ArrayAttr saveOperatorTypeProperties(ProblemT &prob, OperatorType opr,
257 ImplicitLocOpBuilder &b) {
258 SmallVector<Attribute> props;
259 Attribute prop;
260 // Fold expression: Expands to a `getFromProblem` and a conditional
261 // `push_back` call for each of the `OperatorTypePropertyTs`.
262 ((prop = OperatorTypePropertyTs::getFromProblem(prob, opr, b.getContext()),
263 prop ? props.push_back(prop) : (void)prop),
264 ...);
265 return props.empty() ? ArrayAttr() : b.getArrayAttr(props);
266}
267
268template <typename ProblemT, typename... DependencePropertyTs>
269ArrayAttr saveDependenceProperties(ProblemT &prob, Dependence dep,
270 ImplicitLocOpBuilder &b) {
271 SmallVector<Attribute> props;
272 Attribute prop;
273 // Fold expression: Expands to a `getFromProblem` and a conditional
274 // `push_back` call for each of the `DependencePropertyTs`.
275 ((prop = DependencePropertyTs::getFromProblem(prob, dep, b.getContext()),
276 prop ? props.push_back(prop) : (void)prop),
277 ...);
278 return props.empty() ? ArrayAttr() : b.getArrayAttr(props);
279}
280
281template <typename ProblemT, typename... InstancePropertyTs>
282ArrayAttr saveInstanceProperties(ProblemT &prob, ImplicitLocOpBuilder &b) {
283 SmallVector<Attribute> props;
284 Attribute prop;
285 // Fold expression: Expands to a `getFromProblem` and a conditional
286 // `push_back` call for each of the `InstancePropertyTs`.
287 ((prop = InstancePropertyTs::getFromProblem(prob, b.getContext()),
288 prop ? props.push_back(prop) : (void)prop),
289 ...);
290 return props.empty() ? ArrayAttr() : b.getArrayAttr(props);
291}
292
293/// Construct an `InstanceOp` from a given \p ProblemT instance, and
294/// create/attach attributes of the given classes for the corresponding
295/// properties on the scheduling problem. The returned `InstanceOp` uses the
296/// given \p instanceName and \p problemName. `OperationOp`s are created
297/// unnamed, unless they represent the source operation in an auxiliary
298/// dependence, or the \p operationNameFn callback returns a non-null
299/// `StringAttr` with the desired name. The attribute tuples are used
300/// solely for grouping/inferring the template parameter packs. The tuple
301/// elements may therefore be unitialized objects. The template instantiation
302/// fails if properties are incompatible with \p ProblemT.
303///
304/// Example: To save an instance of the `circt::scheduling::CyclicProblem` with
305/// all its input and solution properties, and reyling on default operation
306/// names, call this as follows:
307///
308/// ```
309/// saveProblem<CyclicProblem>(prob,
310/// std::make_tuple(LinkedOperatorTypeAttr(), StartTimeAttr()),
311/// std::make_tuple(LatencyAttr()),
312/// std::make_tuple(DistanceAttr()),
313/// std::make_tuple(InitiationIntervalAttr()),
314/// builder);
315/// ```
316template <typename ProblemT, typename... OperationPropertyTs,
317 typename... OperatorTypePropertyTs, typename... DependencePropertyTs,
318 typename... InstancePropertyTs>
319InstanceOp
320saveProblem(ProblemT &prob, std::tuple<OperationPropertyTs...> opProps,
321 std::tuple<OperatorTypePropertyTs...> oprProps,
322 std::tuple<DependencePropertyTs...> depProps,
323 std::tuple<InstancePropertyTs...> instProps, OpBuilder &builder) {
324 ImplicitLocOpBuilder b(builder.getUnknownLoc(), builder);
325
326 // Set up instance.
327 auto instOp = b.create<InstanceOp>(
328 builder.getStringAttr(ProblemT::name),
329 saveInstanceProperties<ProblemT, InstancePropertyTs...>(prob, b));
330 if (auto instName = prob.getInstanceName())
331 instOp.setSymNameAttr(instName);
332
333 // Emit operator types.
334 b.setInsertionPointToEnd(instOp.getBodyBlock());
335 auto libraryOp = b.create<OperatorLibraryOp>();
336 if (auto libName = prob.getLibraryName())
337 libraryOp.setSymNameAttr(libName);
338 b.setInsertionPointToStart(libraryOp.getBodyBlock());
339
340 for (auto opr : prob.getOperatorTypes())
341 b.create<OperatorTypeOp>(
342 opr, saveOperatorTypeProperties<ProblemT, OperatorTypePropertyTs...>(
343 prob, opr, b));
344
345 // Determine which operations act as source ops for auxiliary dependences, and
346 // therefore need a name. Also, honor names provided by the client.
347 DenseMap<Operation *, StringAttr> opNames;
348 for (auto *op : prob.getOperations()) {
349 if (auto opName = prob.getOperationName(op))
350 opNames[op] = opName;
351
352 for (auto &dep : prob.getDependences(op)) {
353 Operation *src = dep.getSource();
354 if (!dep.isAuxiliary() || opNames.count(src))
355 continue;
356 if (auto srcOpName = prob.getOperationName(src)) {
357 opNames[src] = srcOpName;
358 continue;
359 }
360 opNames[src] = b.getStringAttr(Twine("Op") + Twine(opNames.size()));
361 }
362 }
363
364 // Construct operations and model their dependences.
365 b.setInsertionPointToEnd(instOp.getBodyBlock());
366 auto graphOp = b.create<DependenceGraphOp>();
367 b.setInsertionPointToStart(graphOp.getBodyBlock());
368
369 BackedgeBuilder backedgeBuilder(b, b.getLoc());
370 ValueMapper v(&backedgeBuilder);
371 for (auto *op : prob.getOperations()) {
372 // Construct the `dependences attribute`. It contains `DependenceAttr` for
373 // def-use deps _with_ properties, and all aux deps.
374 ArrayAttr dependences;
375 SmallVector<Attribute> depAttrs;
376 unsigned auxOperandIdx = op->getNumOperands();
377 for (auto &dep : prob.getDependences(op)) {
378 ArrayAttr depProps =
379 saveDependenceProperties<ProblemT, DependencePropertyTs...>(prob, dep,
380 b);
381 if (dep.isDefUse() && depProps) {
382 auto depAttr = b.getAttr<DependenceAttr>(*dep.getDestinationIndex(),
383 FlatSymbolRefAttr(), depProps);
384 depAttrs.push_back(depAttr);
385 continue;
386 }
387
388 if (!dep.isAuxiliary())
389 continue;
390
391 auto sourceOpName = opNames.lookup(dep.getSource());
392 assert(sourceOpName);
393 auto sourceRef = b.getAttr<FlatSymbolRefAttr>(sourceOpName);
394 auto depAttr =
395 b.getAttr<DependenceAttr>(auxOperandIdx, sourceRef, depProps);
396 depAttrs.push_back(depAttr);
397 ++auxOperandIdx;
398 }
399 if (!depAttrs.empty())
400 dependences = b.getArrayAttr(depAttrs);
401
402 // Delegate to helper to construct the `properties` attribute.
403 ArrayAttr properties =
404 saveOperationProperties<ProblemT, OperationPropertyTs...>(prob, op, b);
405
406 // Finally, create the `OperationOp` and inform the value mapper.
407 // NB: sym_name, dependences and properties are optional attributes, so
408 // passing potentially unitialized String/ArrayAttrs is intentional here.
409 auto opOp =
410 b.create<OperationOp>(op->getNumResults(), v.get(op->getOperands()),
411 opNames.lookup(op), dependences, properties);
412 v.set(op->getResults(), opOp->getResults());
413 }
414
415 return instOp;
416}
417
418/// Dummy struct to query a problem's default properties (i.e. all input and
419/// solution properties). Specializations shall provide the following
420/// definitions:
421///
422/// ```
423/// static constexpr auto operationProperties = std::make_tuple(...);
424/// static constexpr auto operatorTypeProperties = std::make_tuple(...);
425/// static constexpr auto dependenceProperties = std::make_tuple(...);
426/// static constexpr auto instanceProperties = std::make_tuple(...);
427/// ```
428template <typename ProblemT>
429struct Default {};
430
431/// Construct an instance of \p ProblemT from \p instOp, and attempt to set all
432/// of the problem class' properties.
433///
434/// Relies on the specialization of template `circt::ssp::Default` for \p
435/// ProblemT.
436template <typename ProblemT>
443
444/// Construct an `InstanceOp` from a given \p ProblemT instance, and
445/// create/attach attributes for all of the problem class' properties.
446///
447/// Relies on the specialization of template `circt::ssp::Default` for \p
448/// ProblemT.
449template <typename ProblemT>
450InstanceOp saveProblem(ProblemT &prob, OpBuilder &builder) {
451 return saveProblem<ProblemT>(prob, Default<ProblemT>::operationProperties,
455}
456
457//===----------------------------------------------------------------------===//
458// Default property tuples for the built-in problems
459//===----------------------------------------------------------------------===//
460
461template <>
462struct Default<scheduling::Problem> {
463 static constexpr auto operationProperties =
464 std::make_tuple(LinkedOperatorTypeAttr(), StartTimeAttr());
465 static constexpr auto operatorTypeProperties = std::make_tuple(LatencyAttr());
466 static constexpr auto dependenceProperties = std::make_tuple();
467 static constexpr auto instanceProperties = std::make_tuple();
468};
469
470template <>
471struct Default<scheduling::CyclicProblem> {
472 static constexpr auto operationProperties =
474 static constexpr auto operatorTypeProperties =
476 static constexpr auto dependenceProperties =
478 std::make_tuple(DistanceAttr()));
479 static constexpr auto instanceProperties =
481 std::make_tuple(InitiationIntervalAttr()));
482};
483
484template <>
485struct Default<scheduling::ChainingProblem> {
486 static constexpr auto operationProperties =
488 std::make_tuple(StartTimeInCycleAttr()));
489 static constexpr auto operatorTypeProperties =
491 std::make_tuple(IncomingDelayAttr(), OutgoingDelayAttr()));
492 static constexpr auto dependenceProperties =
494 static constexpr auto instanceProperties =
496};
497
498template <>
499struct Default<scheduling::SharedOperatorsProblem> {
500 static constexpr auto operationProperties =
502 static constexpr auto operatorTypeProperties =
504 std::make_tuple(LimitAttr()));
505 static constexpr auto dependenceProperties =
507 static constexpr auto instanceProperties =
509};
510
511template <>
512struct Default<scheduling::ModuloProblem> {
513 static constexpr auto operationProperties =
515 static constexpr auto operatorTypeProperties =
517 static constexpr auto dependenceProperties =
519 static constexpr auto instanceProperties =
521};
522
523template <>
524struct Default<scheduling::ChainingCyclicProblem> {
525 static constexpr auto operationProperties =
527 static constexpr auto operatorTypeProperties =
529 static constexpr auto dependenceProperties =
531 static constexpr auto instanceProperties =
533};
534
535} // namespace ssp
536} // namespace circt
537
538#endif // CIRCT_DIALECT_SSP_SSPUTILITIES_H
assert(baseType &&"element must be base type")
Instantiate one of these and use it to build typed backedges.
The ValueMapper class facilitates the definition and connection of SSA def-use chains between two loc...
Definition ValueMapper.h:35
void set(mlir::Value from, mlir::Value to, bool replace=false)
mlir::Value get(mlir::Value from, TypeTransformer typeTransformer=ValueMapper::identity)
detail::Dependence Dependence
A thin wrapper to allow a uniform handling of def-use and auxiliary dependences.
Definition Problems.h:95
mlir::StringAttr OperatorType
Operator types are distinguished by name (chosen by the client).
Definition Problems.h:98
A wrapper class to uniformly handle def-use and auxiliary dependence edges.
scheduling::Problem::Dependence Dependence
Definition Utilities.h:34
void loadInstanceProperties(ProblemT &, ArrayAttr)
Definition Utilities.h:84
void loadOperationProperties(ProblemT &, Operation *, ArrayAttr)
Definition Utilities.h:41
ArrayAttr saveDependenceProperties(ProblemT &prob, Dependence dep, ImplicitLocOpBuilder &b)
Definition Utilities.h:269
void loadOperatorTypeProperties(ProblemT &, OperatorType, ArrayAttr)
Definition Utilities.h:55
ArrayAttr saveOperatorTypeProperties(ProblemT &prob, OperatorType opr, ImplicitLocOpBuilder &b)
Definition Utilities.h:256
ProblemT loadProblem(InstanceOp instOp, std::tuple< OperationPropertyTs... > opProps, std::tuple< OperatorTypePropertyTs... > oprProps, std::tuple< DependencePropertyTs... > depProps, std::tuple< InstancePropertyTs... > instProps)
Construct an instance of ProblemT from instOp, and attempt to set properties from the given attribute...
Definition Utilities.h:140
void loadDependenceProperties(ProblemT &, Dependence, ArrayAttr)
Definition Utilities.h:70
ArrayAttr saveInstanceProperties(ProblemT &prob, ImplicitLocOpBuilder &b)
Definition Utilities.h:282
scheduling::Problem::OperatorType OperatorType
Definition Utilities.h:33
ArrayAttr saveOperationProperties(ProblemT &prob, Operation *op, ImplicitLocOpBuilder &b)
Definition Utilities.h:243
InstanceOp saveProblem(ProblemT &prob, std::tuple< OperationPropertyTs... > opProps, std::tuple< OperatorTypePropertyTs... > oprProps, std::tuple< DependencePropertyTs... > depProps, std::tuple< InstancePropertyTs... > instProps, OpBuilder &builder)
Construct an InstanceOp from a given ProblemT instance, and create/attach attributes of the given cla...
Definition Utilities.h:320
OperatorType loadOperatorType(ProblemT &prob, OperatorTypeOp oprOp, SmallDenseMap< StringAttr, unsigned > &oprIds)
Load the operator type represented by oprOp into prob under a unique name informed by oprIds,...
Definition Utilities.h:101
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Dummy struct to query a problem's default properties (i.e.
Definition Utilities.h:429