Loading [MathJax]/jax/output/HTML-CSS/config.js
CIRCT 21.0.0git
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
Problems.cpp
Go to the documentation of this file.
1//===- Problems.cpp - Modeling of scheduling problems ---------------------===//
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 implements base classes for scheduling problems.
10//
11//===----------------------------------------------------------------------===//
12
15
16#include "mlir/IR/Operation.h"
17
18using namespace circt;
19using namespace circt::scheduling;
20using namespace circt::scheduling::detail;
21
22//===----------------------------------------------------------------------===//
23// Problem
24//===----------------------------------------------------------------------===//
25
27 Operation *src = dep.getSource();
28 Operation *dst = dep.getDestination();
29
30 // Fail early on invalid dependences (src == dst == null), and def-use
31 // dependences that cannot be added because the source value is not the result
32 // of an operation (e.g., a BlockArgument).
33 if (!src || !dst)
34 return failure();
35
36 // record auxiliary dependences explicitly
37 if (dep.isAuxiliary())
38 auxDependences[dst].insert(src);
39
40 // auto-register the endpoints
41 operations.insert(src);
42 operations.insert(dst);
43
44 return success();
45}
46
48 auto opr = OperatorType::get(containingOp->getContext(), name);
49 operatorTypes.insert(opr);
50 return opr;
51}
52
54 auto rsrc = ResourceType::get(containingOp->getContext(), name);
55 resourceTypes.insert(rsrc);
56 return rsrc;
57}
58
60 return DependenceRange(DependenceIterator(*this, op),
61 DependenceIterator(*this, op, /*end=*/true));
62}
63
66 if (auto linkedOpr = getLinkedOperatorType(op))
67 psv.emplace_back("linkedOpr", (*linkedOpr).str());
68 if (auto startTime = getStartTime(op))
69 psv.emplace_back("startTime", std::to_string(*startTime));
70 return psv;
71}
72
76
79 if (auto latency = getLatency(opr))
80 psv.emplace_back("latency", std::to_string(*latency));
81 return psv;
82}
83
85
89
90LogicalResult Problem::checkLinkedOperatorType(Operation *op) {
91 if (!getLinkedOperatorType(op))
92 return op->emitError("Operation is not linked to an operator type");
94 return op->emitError("Operation uses an unregistered operator type");
95 return success();
96}
97
98LogicalResult Problem::checkLatency(Operation *op) {
99 auto maybeOpr = getLinkedOperatorType(op);
100 if (!maybeOpr)
101 return getContainingOp()->emitError()
102 << "Operation is missing a linked operator type";
103
104 if (!getLatency(*maybeOpr))
105 return getContainingOp()->emitError()
106 << "Operator type '" << maybeOpr->getValue() << "' has no latency";
107
108 return success();
109}
110
111LogicalResult Problem::check() {
112 for (auto *op : getOperations()) {
113 if (failed(checkLinkedOperatorType(op)))
114 return failure();
115
116 if (failed(checkLatency(op)))
117 return failure();
118 }
119
120 return success();
121}
122
123LogicalResult Problem::verifyStartTime(Operation *op) {
124 if (!getStartTime(op))
125 return op->emitError("Operation has no start time");
126 return success();
127}
128
130 Operation *i = dep.getSource();
131 Operation *j = dep.getDestination();
132
133 unsigned stI = *getStartTime(i);
134 unsigned latI = *getLatency(*getLinkedOperatorType(i));
135 unsigned stJ = *getStartTime(j);
136
137 // check if i's result is available before j starts
138 if (!(stI + latI <= stJ))
139 return getContainingOp()->emitError()
140 << "Precedence violated for dependence."
141 << "\n from: " << *i << ", result available in t=" << (stI + latI)
142 << "\n to: " << *j << ", starts in t=" << stJ;
143
144 return success();
145}
146
147LogicalResult Problem::verify() {
148 for (auto *op : getOperations())
149 if (failed(verifyStartTime(op)))
150 return failure();
151
152 for (auto *op : getOperations())
153 for (auto &dep : getDependences(op))
154 if (failed(verifyPrecedence(dep)))
155 return failure();
156
157 return success();
158}
159
160std::optional<unsigned> Problem::getEndTime(Operation *op) {
161 if (auto startTime = getStartTime(op))
162 if (auto opType = getLinkedOperatorType(op))
163 if (auto latency = getLatency(*opType))
164 return startTime.value() + latency.value();
165 return std::nullopt;
166}
167
168//===----------------------------------------------------------------------===//
169// CyclicProblem
170//===----------------------------------------------------------------------===//
171
173 auto psv = Problem::getProperties(dep);
174 if (auto distance = getDistance(dep))
175 psv.emplace_back("distance", std::to_string(*distance));
176 return psv;
177}
178
180 auto psv = Problem::getProperties();
181 if (auto ii = getInitiationInterval())
182 psv.emplace_back("II", std::to_string(*ii));
183 return psv;
184}
185
187 Operation *i = dep.getSource();
188 Operation *j = dep.getDestination();
189
190 unsigned stI = *getStartTime(i);
191 unsigned latI = *getLatency(*getLinkedOperatorType(i));
192 unsigned stJ = *getStartTime(j);
193 unsigned dist = getDistance(dep).value_or(0); // optional property
194 unsigned ii = *getInitiationInterval();
195
196 // check if i's result is available before j starts (dist iterations later)
197 if (!(stI + latI <= stJ + dist * ii))
198 return getContainingOp()->emitError()
199 << "Precedence violated for dependence."
200 << "\n from: " << *i << ", result available in t=" << (stI + latI)
201 << "\n to: " << *j << ", starts in t=" << stJ
202 << "\n dist: " << dist << ", II=" << ii;
203
204 return success();
205}
206
209 return getContainingOp()->emitError("Invalid initiation interval");
210 return success();
211}
212
213LogicalResult CyclicProblem::verify() {
214 if (failed(verifyInitiationInterval()) || failed(Problem::verify()))
215 return failure();
216 return success();
217}
218
219//===----------------------------------------------------------------------===//
220// ChainingProblem
221//===----------------------------------------------------------------------===//
222
224 auto psv = Problem::getProperties(op);
225 if (auto stic = getStartTimeInCycle(op))
226 psv.emplace_back("start time in cycle", std::to_string(*stic));
227 return psv;
228}
229
231 auto psv = Problem::getProperties(opr);
232 if (auto incDelay = getIncomingDelay(opr))
233 psv.emplace_back("incoming delay", std::to_string(*incDelay));
234 if (auto outDelay = getOutgoingDelay(opr))
235 psv.emplace_back("outgoing delay", std::to_string(*outDelay));
236 return psv;
237}
238
242
244 return getContainingOp()->emitError()
245 << "Missing delays for operator type '" << opr.getAttr() << "'";
246
247 float iDel = *incomingDelay;
248 float oDel = *outgoingDelay;
249
250 if (iDel < 0.0f || oDel < 0.0f)
251 return getContainingOp()->emitError()
252 << "Negative delays for operator type '" << opr.getAttr() << "'";
253
254 if (*getLatency(opr) == 0 && iDel != oDel)
255 return getContainingOp()->emitError()
256 << "Incoming & outgoing delay must be equal for zero-latency "
257 "operator type '"
258 << opr.getAttr() << "'";
259
260 return success();
261}
262
263LogicalResult ChainingProblem::verifyStartTimeInCycle(Operation *op) {
265 if (!startTimeInCycle || *startTimeInCycle < 0.0f)
266 return op->emitError("Operation has no non-negative start time in cycle");
267 return success();
268}
269
271 // Auxiliary edges don't transport values.
272 if (dep.isAuxiliary())
273 return success();
274
275 Operation *i = dep.getSource();
276 Operation *j = dep.getDestination();
277
278 unsigned stI = *getStartTime(i);
279 unsigned latI = *getLatency(*getLinkedOperatorType(i));
280 unsigned stJ = *getStartTime(j);
281
282 // If `i` finishes a full time step earlier than `j`, its value is registered
283 // and thereby available at physical time 0.0 in `j`'s start cycle.
284 if (stI + latI < stJ)
285 return success();
286
287 // We have stI + latI == stJ, i.e. `i` ends in the same cycle as `j` starts.
288 // If `i` is combinational, both ops also start in the same cycle, and we must
289 // include `i`'s start time in that cycle in the path delay. Otherwise, `i`
290 // started in an earlier cycle and just contributes its outgoing delay to the
291 // path.
292 float sticI = latI == 0 ? *getStartTimeInCycle(i) : 0.0f;
293 float oDelI = *getOutgoingDelay(*getLinkedOperatorType(i));
294 float sticJ = *getStartTimeInCycle(j);
295
296 if (!(sticI + oDelI <= sticJ))
297 return getContainingOp()->emitError()
298 << "Precedence violated in cycle " << stJ << " for dependence:"
299 << "\n from: " << *i << ", result after z=" << (sticI + oDelI)
300 << "\n to: " << *j << ", starts in z=" << sticJ;
301
302 return success();
303}
304
305LogicalResult ChainingProblem::check() {
306 if (failed(Problem::check()))
307 return failure();
308
309 for (auto opr : getOperatorTypes())
310 if (failed(checkDelays(opr)))
311 return failure();
312
313 return success();
314}
315
316LogicalResult ChainingProblem::verify() {
317 if (failed(Problem::verify()))
318 return failure();
319
320 for (auto *op : getOperations())
321 if (failed(verifyStartTimeInCycle(op)))
322 return failure();
323
324 for (auto *op : getOperations())
325 for (auto dep : getDependences(op))
326 if (failed(verifyPrecedenceInCycle(dep)))
327 return failure();
328
329 return success();
330}
331
332//===----------------------------------------------------------------------===//
333// SharedOperatorsProblem
334//===----------------------------------------------------------------------===//
335
338 auto psv = Problem::getProperties(rsrc);
339 if (auto limit = getLimit(rsrc))
340 psv.emplace_back("limit", std::to_string(*limit));
341 return psv;
342}
343
344LogicalResult SharedOperatorsProblem::checkLatency(Operation *op) {
345 if (failed(Problem::checkLatency(op)))
346 return failure();
347
348 auto maybeRsrcs = getLinkedResourceTypes(op);
349 if (!maybeRsrcs)
350 return success();
351
352 // `linkedOprType` is not null since it must have been checked by the base
353 // class' `checkLatency`.
354 OperatorType linkedOprType = *getLinkedOperatorType(op);
355
356 for (auto rsrc : *maybeRsrcs) {
357 auto limit = getLimit(rsrc);
358 if (limit && *limit > 0 && *getLatency(linkedOprType) == 0)
359 return getContainingOp()->emitError()
360 << "Operator type '" << linkedOprType.getValue()
361 << "' using limited resource '" << rsrc.getValue()
362 << "' has zero latency.";
363 }
364 return success();
365}
366
368 auto limit = getLimit(rsrc);
369 if (!limit)
370 return success();
371
373 for (auto *op : getOperations()) {
374 auto maybeRsrcs = getLinkedResourceTypes(op);
375 if (!maybeRsrcs)
376 continue;
377
378 if (llvm::none_of(*maybeRsrcs, [&](ResourceType linkedRsrc) {
379 return linkedRsrc == rsrc;
380 }))
381 continue;
382
383 ++nOpsPerTimeStep[*getStartTime(op)];
384 }
385
386 for (auto &kv : nOpsPerTimeStep)
387 if (kv.second > *limit)
388 return getContainingOp()->emitError()
389 << "Resource type '" << rsrc.getValue() << "' is oversubscribed."
390 << "\n time step: " << kv.first
391 << "\n #operations: " << kv.second << "\n limit: " << *limit;
392
393 return success();
394}
395
397 if (failed(Problem::verify()))
398 return failure();
399
400 for (auto rsrc : getResourceTypes())
401 if (failed(verifyUtilization(rsrc)))
402 return failure();
403
404 return success();
405}
406
407//===----------------------------------------------------------------------===//
408// ModuloProblem
409//===----------------------------------------------------------------------===//
410
412 auto limit = getLimit(rsrc);
413 if (!limit)
414 return success();
415
416 unsigned ii = *getInitiationInterval();
417 llvm::SmallDenseMap<unsigned, unsigned> nOpsPerCongruenceClass;
418 for (auto *op : getOperations()) {
419 auto maybeRsrcs = getLinkedResourceTypes(op);
420 if (!maybeRsrcs)
421 continue;
422
423 if (llvm::none_of(*maybeRsrcs, [&](ResourceType linkedRsrc) {
424 return linkedRsrc == rsrc;
425 }))
426 continue;
427
428 ++nOpsPerCongruenceClass[*getStartTime(op) % ii];
429 }
430
431 for (auto &kv : nOpsPerCongruenceClass)
432 if (kv.second > *limit)
433 return getContainingOp()->emitError()
434 << "Resource type '" << rsrc.getValue() << "' is oversubscribed."
435 << "\n congruence class: " << kv.first
436 << "\n #operations: " << kv.second << "\n limit: " << *limit;
437
438 return success();
439}
440
441LogicalResult ModuloProblem::verify() {
442 if (failed(CyclicProblem::verify()))
443 return failure();
444
445 // Don't call SharedOperatorsProblem::verify() here to prevent redundant
446 // verification of the base problem.
447 for (auto rsrc : getResourceTypes())
448 if (failed(verifyUtilization(rsrc)))
449 return failure();
450
451 return success();
452}
453
454//===----------------------------------------------------------------------===//
455// ChainingCyclicProblem
456//===----------------------------------------------------------------------===//
457
459 if (!dep.isAuxiliary() && (getDistance(dep).value_or(0) != 0))
460 return getContainingOp()->emitError()
461 << "Def-use dependence cannot have non-zero distance.\n"
462 << "On operation: " << *dep.getDestination() << ".\n";
463 return success();
464}
465
467 for (auto *op : getOperations())
468 for (auto &dep : getDependences(op))
469 if (failed(checkDefUse(dep)))
470 return failure();
471
472 if (ChainingProblem::check().succeeded() &&
473 CyclicProblem::check().succeeded())
474 return success();
475 return failure();
476}
477
479 if (ChainingProblem::verify().succeeded() &&
480 CyclicProblem::verify().succeeded())
481 return success();
482 return failure();
483}
484
485//===----------------------------------------------------------------------===//
486// Dependence
487//===----------------------------------------------------------------------===//
488
489Operation *Dependence::getSource() const {
490 return isDefUse() ? defUse->get().getDefiningOp() : auxSrc;
491}
492
493Operation *Dependence::getDestination() const {
494 return isDefUse() ? defUse->getOwner() : auxDst;
495}
496
497std::optional<unsigned> Dependence::getSourceIndex() const {
498 if (!isDefUse())
499 return std::nullopt;
500
501 assert(isa<OpResult>(defUse->get()) && "source is not an operation");
502 return dyn_cast<OpResult>(defUse->get()).getResultNumber();
503}
504
505std::optional<unsigned> Dependence::getDestinationIndex() const {
506 if (!isDefUse())
507 return std::nullopt;
508 return defUse->getOperandNumber();
509}
510
512 return TupleRepr(getSource(), getDestination(), getSourceIndex(),
513 getDestinationIndex());
514}
515
516bool Dependence::operator==(const Dependence &other) const {
517 return getAsTuple() == other.getAsTuple();
518}
519
520//===----------------------------------------------------------------------===//
521// DependenceIterator
522//===----------------------------------------------------------------------===//
523
525 bool end)
526 : problem(problem), op(op), operandIdx(0), auxPredIdx(0), auxPreds(nullptr),
527 dep() {
528 if (!end) {
529 if (problem.auxDependences.count(op))
531
533 }
534}
535
537 // Yield dependences corresponding to values used by `op`'s operands...
538 while (operandIdx < op->getNumOperands()) {
539 dep = Dependence(&op->getOpOperand(operandIdx++));
540 Operation *src = dep.getSource();
541
542 // ... but only if they are outgoing from operations that are registered in
543 // the scheduling problem.
544 if (src && problem.hasOperation(src))
545 return;
546 }
547
548 // Then, yield auxiliary dependences, if present.
549 if (auxPreds && auxPredIdx < auxPreds->size()) {
551 return;
552 }
553
554 // An invalid dependence signals the end of iteration.
555 dep = Dependence();
556}
assert(baseType &&"element must be base type")
LogicalResult verify() override
Return success if the computed solution is valid.
Definition Problems.cpp:478
LogicalResult checkDefUse(Dependence dep)
Definition Problems.cpp:458
LogicalResult check() override
Return success if the constructed scheduling problem is valid.
Definition Problems.cpp:466
std::optional< float > getOutgoingDelay(OperatorType opr)
The outgoing delay denotes the propagation time from either the operand inputs (combinational operato...
Definition Problems.h:438
OperationProperty< float > startTimeInCycle
Definition Problems.h:422
virtual LogicalResult check() override
Return success if the constructed scheduling problem is valid.
Definition Problems.cpp:305
virtual LogicalResult verifyPrecedenceInCycle(Dependence dep)
If dep is an SSA edge and its source operation finishes in the same time step as the destination oper...
Definition Problems.cpp:270
std::optional< float > getIncomingDelay(OperatorType opr)
The incoming delay denotes the propagation time from the operand inputs to either the result outputs ...
Definition Problems.h:428
OperatorTypeProperty< float > incomingDelay
Definition Problems.h:421
virtual LogicalResult verify() override
Return success if the computed solution is valid.
Definition Problems.cpp:316
OperatorTypeProperty< float > outgoingDelay
Definition Problems.h:421
virtual LogicalResult verifyStartTimeInCycle(Operation *op)
op has a non-negative start time in its cycle.
Definition Problems.cpp:263
virtual LogicalResult checkDelays(OperatorType opr)
Incoming/outgoing delays are set for opr and non-negative.
Definition Problems.cpp:239
std::optional< float > getStartTimeInCycle(Operation *op)
Computed by the scheduler, this start time is relative to the beginning of the cycle that op starts i...
Definition Problems.h:447
virtual LogicalResult verifyInitiationInterval()
This problem has a non-zero II.
Definition Problems.cpp:207
virtual PropertyStringVector getProperties() override
Definition Problems.cpp:179
virtual LogicalResult verifyPrecedence(Dependence dep) override
dep's source operation is available before dep's destination operation starts (dep's distance iterati...
Definition Problems.cpp:186
std::optional< unsigned > getDistance(Dependence dep)
The distance determines whether a dependence has to be satisfied in the same iteration (distance=0 or...
Definition Problems.h:374
virtual LogicalResult verify() override
Return success if the computed solution is valid.
Definition Problems.cpp:213
DependenceProperty< unsigned > distance
Definition Problems.h:368
std::optional< unsigned > getInitiationInterval()
The initiation interval (II) is the number of time steps between subsequent iterations,...
Definition Problems.h:384
virtual LogicalResult verify() override
Return success if the computed solution is valid.
Definition Problems.cpp:441
virtual LogicalResult verifyUtilization(ResourceType rsrc) override
opr is not oversubscribed in any congruence class modulo II.
Definition Problems.cpp:411
This class models the most basic scheduling problem.
Definition Problems.h:75
virtual LogicalResult verify()
Return success if the computed solution is valid.
Definition Problems.cpp:147
virtual LogicalResult check()
Return success if the constructed scheduling problem is valid.
Definition Problems.cpp:111
std::optional< unsigned > getLatency(OperatorType opr)
The latency is the number of cycles opr needs to compute its result.
Definition Problems.h:273
virtual PropertyStringVector getProperties()
Definition Problems.cpp:84
llvm::iterator_range< detail::DependenceIterator > DependenceRange
Definition Problems.h:147
bool hasOperation(Operation *op)
Return true if op is part of this problem.
Definition Problems.h:224
std::optional< SmallVector< ResourceType > > getLinkedResourceTypes(Operation *op)
The linked resource type provides the available resources for op.
Definition Problems.h:265
LogicalResult insertDependence(Dependence dep)
Include dep in the scheduling problem.
Definition Problems.cpp:26
std::optional< unsigned > getEndTime(Operation *op)
Returns the end time for op, as computed by the scheduler.
Definition Problems.cpp:160
OperationProperty< unsigned > startTime
Definition Problems.h:183
std::optional< OperatorType > getLinkedOperatorType(Operation *op)
The linked operator type provides the runtime characteristics for op.
Definition Problems.h:256
ResourceTypeSet resourceTypes
Definition Problems.h:178
OperatorTypeProperty< unsigned > latency
Definition Problems.h:186
const ResourceTypeSet & getResourceTypes()
Return the set of resource types.
Definition Problems.h:250
static constexpr auto name
Definition Problems.h:77
const OperationSet & getOperations()
Return the set of operations.
Definition Problems.h:226
OperatorType getOrInsertOperatorType(StringRef name)
Retrieves the operator type identified by the client-specific name.
Definition Problems.cpp:47
AuxDependenceMap auxDependences
Definition Problems.h:176
std::optional< unsigned > getStartTime(Operation *op)
Return the start time for op, as computed by the scheduler.
Definition Problems.h:281
OperatorTypeSet operatorTypes
Definition Problems.h:177
llvm::SmallVector< std::pair< std::string, std::string >, 2 > PropertyStringVector
Definition Problems.h:324
virtual LogicalResult verifyStartTime(Operation *op)
op has a start time.
Definition Problems.cpp:123
virtual LogicalResult checkLinkedOperatorType(Operation *op)
op is linked to a registered operator type.
Definition Problems.cpp:90
DependenceRange getDependences(Operation *op)
Return a range object to transparently iterate over op's incoming 1) implicit def-use dependences (ba...
Definition Problems.cpp:59
bool hasOperatorType(OperatorType opr)
Return true if opr is part of this problem.
Definition Problems.h:241
virtual LogicalResult verifyPrecedence(Dependence dep)
dep's source operation is available before dep's destination operation starts.
Definition Problems.cpp:129
Operation * getContainingOp()
Return the operation containing this problem, e.g. to emit diagnostics.
Definition Problems.h:219
ResourceType getOrInsertResourceType(StringRef name)
Retrieves the resource type identified by the client-specific name.
Definition Problems.cpp:53
virtual LogicalResult checkLatency(Operation *op)
op has a latency.
Definition Problems.cpp:98
const OperatorTypeSet & getOperatorTypes()
Return the set of operator types.
Definition Problems.h:243
virtual LogicalResult verifyUtilization(ResourceType rsrc)
rsrc is not oversubscribed in any time step.
Definition Problems.cpp:367
virtual LogicalResult verify() override
Return success if the computed solution is valid.
Definition Problems.cpp:396
virtual LogicalResult checkLatency(Operation *op) override
If op is limited, it has a non-zero latency.
Definition Problems.cpp:344
ResourceTypeProperty< unsigned > limit
Definition Problems.h:495
std::optional< unsigned > getLimit(ResourceType rsrc)
The limit is the maximum number of operations using rsrc that are available in the target hardware.
Definition Problems.h:500
An iterator to transparently surface an operation's def-use dependences from the SSA subgraph (induce...
llvm::SmallSetVector< Operation *, 4 > * auxPreds
DependenceIterator(Problem &problem, Operation *op, bool end=false)
Construct an iterator over the op's def-use dependences (i.e.
Definition Problems.cpp:524
A wrapper class to uniformly handle def-use and auxiliary dependence edges.
std::tuple< Operation *, Operation *, std::optional< unsigned >, std::optional< unsigned > > TupleRepr
The "expanded" representation of a dependence, intended as the key for comparisons and hashing.
bool operator==(const Dependence &other) const
Definition Problems.cpp:516
std::optional< unsigned > getDestinationIndex() const
Return the destination operation's operand number, if applicable.
Definition Problems.cpp:505
std::optional< unsigned > getSourceIndex() const
Return the source operation's result number, if applicable.
Definition Problems.cpp:497
Operation * getDestination() const
Return the destination of the dependence.
Definition Problems.cpp:493
bool isAuxiliary() const
Return true if this is a valid auxiliary dependence.
TupleRepr getAsTuple() const
Return the tuple representation of this dependence.
Definition Problems.cpp:511
Operation * getSource() const
Return the source of the dependence.
Definition Problems.cpp:489
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Operator types are distinguished by name (chosen by the client).
Definition Problems.h:98
mlir::StringAttr getAttr() const
Definition Problems.h:108
static OperatorType get(mlir::MLIRContext *ctx, llvm::StringRef name)
Definition Problems.h:104
mlir::StringRef getValue() const
Definition Problems.h:110
Resource types are distinguished by name (chosen by the client).
Definition Problems.h:120
mlir::StringRef getValue() const
Definition Problems.h:132
static ResourceType get(mlir::MLIRContext *ctx, llvm::StringRef name)
Definition Problems.h:126