CIRCT 24.0.0git
Loading...
Searching...
No Matches
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
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))
530 auxPreds = &problem.auxDependences[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")
Problem::Dependence Dependence
false return failure()
Parse an optional keyword or string and set instance into 'result'.
return success()
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
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
detail::Dependence Dependence
A thin wrapper to allow a uniform handling of def-use and auxiliary dependences.
Definition Problems.h:95
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
llvm::SmallVector< std::pair< std::string, std::string >, 2 > PropertyStringVector
Definition Problems.h:323
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
bool operator==(const Dependence &other) const
Definition Problems.cpp:516
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.
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
bool isDefUse() const
Return true if this is a valid def-use dependence.
Operation * getSource() const
Return the source of the dependence.
Definition Problems.cpp:489
Dependence(OpOperand *defUseDep)
Wrap a def-use dependence, which is uniquely identified in the SSA graph by an OpOperand.
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