MLIR 24.0.0git
ControlFlowOps.cpp
Go to the documentation of this file.
1//===- ControlFlowOps.cpp - MLIR SPIR-V Control Flow Ops -----------------===//
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// Defines the control flow operations in the SPIR-V dialect.
10//
11//===----------------------------------------------------------------------===//
12
17
18#include "llvm/Support/InterleavedRange.h"
19
20#include "SPIRVOpUtils.h"
21#include "SPIRVParsingUtils.h"
22
23using namespace mlir::spirv::AttrNames;
24
25namespace mlir::spirv {
26
27/// Parses Function, Selection and Loop control attributes. If no control is
28/// specified, "None" is used as a default.
29template <typename EnumAttrClass, typename EnumClass>
30static ParseResult
32 StringRef attrName = spirv::attributeName<EnumClass>()) {
33 if (succeeded(parser.parseOptionalKeyword(kControl))) {
34 EnumClass control;
35 if (parser.parseLParen() ||
36 spirv::parseEnumKeywordAttr<EnumAttrClass>(control, parser, state) ||
37 parser.parseRParen())
38 return failure();
39 return success();
40 }
41 // Set control to "None" otherwise.
42 Builder builder = parser.getBuilder();
43 state.addAttribute(attrName,
44 builder.getAttr<EnumAttrClass>(static_cast<EnumClass>(0)));
45 return success();
46}
47
48//===----------------------------------------------------------------------===//
49// spirv.BranchOp
50//===----------------------------------------------------------------------===//
51
52SuccessorOperands BranchOp::getSuccessorOperands(unsigned index) {
53 assert(index == 0 && "invalid successor index");
54 return SuccessorOperands(0, getTargetOperandsMutable());
55}
56
57//===----------------------------------------------------------------------===//
58// spirv.BranchConditionalOp
59//===----------------------------------------------------------------------===//
60
61SuccessorOperands BranchConditionalOp::getSuccessorOperands(unsigned index) {
62 assert(index < 2 && "invalid successor index");
63 return SuccessorOperands(index == kTrueIndex
64 ? getTrueTargetOperandsMutable()
65 : getFalseTargetOperandsMutable());
66}
67
68ParseResult BranchConditionalOp::parse(OpAsmParser &parser,
69 OperationState &result) {
70 auto &builder = parser.getBuilder();
71 OpAsmParser::UnresolvedOperand condInfo;
72 Block *dest;
73
74 // Parse the condition.
75 Type boolTy = builder.getI1Type();
76 if (parser.parseOperand(condInfo) ||
77 parser.resolveOperand(condInfo, boolTy, result.operands))
78 return failure();
79
80 // Parse the optional branch weights.
81 if (succeeded(parser.parseOptionalLSquare())) {
82 IntegerAttr trueWeight, falseWeight;
83 NamedAttrList weights;
84
85 auto i32Type = builder.getIntegerType(32);
86 if (parser.parseAttribute(trueWeight, i32Type, "weight", weights) ||
87 parser.parseComma() ||
88 parser.parseAttribute(falseWeight, i32Type, "weight", weights) ||
89 parser.parseRSquare())
90 return failure();
91
92 StringAttr branchWeightsAttrName =
93 BranchConditionalOp::getBranchWeightsAttrName(result.name);
94 result.addAttribute(branchWeightsAttrName,
95 builder.getArrayAttr({trueWeight, falseWeight}));
96 }
97
98 // Parse the true branch.
99 SmallVector<Value, 4> trueOperands;
100 if (parser.parseComma() ||
101 parser.parseSuccessorAndUseList(dest, trueOperands))
102 return failure();
103 result.addSuccessors(dest);
104 result.addOperands(trueOperands);
105
106 // Parse the false branch.
107 SmallVector<Value, 4> falseOperands;
108 if (parser.parseComma() ||
109 parser.parseSuccessorAndUseList(dest, falseOperands))
110 return failure();
111 result.addSuccessors(dest);
112 result.addOperands(falseOperands);
113 result.addAttribute(spirv::BranchConditionalOp::getOperandSegmentSizeAttr(),
114 builder.getDenseI32ArrayAttr(
115 {1, static_cast<int32_t>(trueOperands.size()),
116 static_cast<int32_t>(falseOperands.size())}));
117
118 return success();
119}
120
121void BranchConditionalOp::print(OpAsmPrinter &printer) {
122 printer << ' ' << getCondition();
123
124 if (std::optional<ArrayAttr> weights = getBranchWeights()) {
125 printer << ' '
126 << llvm::interleaved_array(weights->getAsValueRange<IntegerAttr>());
127 }
128
129 printer << ", ";
130 printer.printSuccessorAndUseList(getTrueBlock(), getTrueBlockArguments());
131 printer << ", ";
132 printer.printSuccessorAndUseList(getFalseBlock(), getFalseBlockArguments());
133}
134
135LogicalResult BranchConditionalOp::verify() {
136 if (auto weights = getBranchWeights()) {
137 if (weights->getValue().size() != 2) {
138 return emitOpError("must have exactly two branch weights");
139 }
140 if (llvm::all_of(*weights, [](Attribute attr) {
141 return cast<IntegerAttr>(attr).getValue().isZero();
142 }))
143 return emitOpError("branch weights cannot both be zero");
144 }
145
146 return success();
147}
148
149//===----------------------------------------------------------------------===//
150// spirv.FunctionCall
151//===----------------------------------------------------------------------===//
152
153LogicalResult FunctionCallOp::verify() {
154 if (getNumResults() > 1) {
155 return emitOpError(
156 "expected callee function to have 0 or 1 result, but provided ")
157 << getNumResults();
158 }
159 return success();
160}
161
162LogicalResult
163FunctionCallOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
164 auto fnName = getCalleeAttr();
165
166 auto funcOp =
167 symbolTable.lookupNearestSymbolFrom<spirv::FuncOp>(*this, fnName);
168 if (!funcOp) {
169 return emitOpError("callee function '")
170 << fnName.getValue() << "' not found in nearest symbol table";
171 }
172
173 auto functionType = funcOp.getFunctionType();
174
175 if (functionType.getNumInputs() != getNumOperands()) {
176 return emitOpError("has incorrect number of operands for callee: expected ")
177 << functionType.getNumInputs() << ", but provided "
178 << getNumOperands();
179 }
180
181 for (uint32_t i = 0, e = functionType.getNumInputs(); i != e; ++i) {
182 if (getOperand(i).getType() != functionType.getInput(i)) {
183 return emitOpError("operand type mismatch: expected operand type ")
184 << functionType.getInput(i) << ", but provided "
185 << getOperand(i).getType() << " for operand number " << i;
186 }
187 }
188
189 if (functionType.getNumResults() != getNumResults()) {
190 return emitOpError(
191 "has incorrect number of results has for callee: expected ")
192 << functionType.getNumResults() << ", but provided "
193 << getNumResults();
194 }
195
196 if (getNumResults() &&
197 (getResult(0).getType() != functionType.getResult(0))) {
198 return emitOpError("result type mismatch: expected ")
199 << functionType.getResult(0) << ", but provided "
200 << getResult(0).getType();
201 }
202
203 return success();
204}
205
206CallInterfaceCallable FunctionCallOp::getCallableForCallee() {
207 return getCalleeAttr();
208}
209
210void FunctionCallOp::setCalleeFromCallable(CallInterfaceCallable callee) {
211 setCalleeAttr(cast<FlatSymbolRefAttr>(cast<SymbolRefAttr>(callee)));
212}
213
214Operation::operand_range FunctionCallOp::getArgOperands() {
215 return getArguments();
216}
217
218MutableOperandRange FunctionCallOp::getArgOperandsMutable() {
219 return getArgumentsMutable();
220}
221
222//===----------------------------------------------------------------------===//
223// spirv.Switch
224//===----------------------------------------------------------------------===//
225
226void SwitchOp::build(OpBuilder &builder, OperationState &result, Value selector,
227 Block *defaultTarget, ValueRange defaultOperands,
228 DenseIntElementsAttr literals, BlockRange targets,
229 ArrayRef<ValueRange> targetOperands) {
230 build(builder, result, selector, defaultOperands, targetOperands, literals,
231 defaultTarget, targets);
232}
233
234void SwitchOp::build(OpBuilder &builder, OperationState &result, Value selector,
235 Block *defaultTarget, ValueRange defaultOperands,
236 ArrayRef<APInt> literals, BlockRange targets,
237 ArrayRef<ValueRange> targetOperands) {
238 DenseIntElementsAttr literalsAttr;
239 if (!literals.empty()) {
240 ShapedType literalType = VectorType::get(
241 static_cast<int64_t>(literals.size()), selector.getType());
242 literalsAttr = DenseIntElementsAttr::get(literalType, literals);
243 }
244 build(builder, result, selector, defaultTarget, defaultOperands, literalsAttr,
245 targets, targetOperands);
246}
247
248void SwitchOp::build(OpBuilder &builder, OperationState &result, Value selector,
249 Block *defaultTarget, ValueRange defaultOperands,
250 ArrayRef<int32_t> literals, BlockRange targets,
251 ArrayRef<ValueRange> targetOperands) {
252 DenseIntElementsAttr literalsAttr;
253 if (!literals.empty()) {
254 ShapedType literalType = VectorType::get(
255 static_cast<int64_t>(literals.size()), selector.getType());
256 literalsAttr = DenseIntElementsAttr::get(literalType, literals);
257 }
258 build(builder, result, selector, defaultTarget, defaultOperands, literalsAttr,
259 targets, targetOperands);
260}
261
262LogicalResult SwitchOp::verify() {
263 std::optional<DenseIntElementsAttr> literals = getLiterals();
264 BlockRange targets = getTargets();
265
266 if (!literals && targets.empty())
267 return success();
268
269 if (literals) {
270 Type selectorType = getSelector().getType();
271 Type literalType = literals->getType().getElementType();
272 if (literalType != selectorType)
273 return emitOpError() << "'selector' type (" << selectorType
274 << ") should match literals type (" << literalType
275 << ")";
276 }
277
278 if (literals && literals->size() != static_cast<int64_t>(targets.size()))
279 return emitOpError() << "number of literals (" << literals->size()
280 << ") should match number of targets ("
281 << targets.size() << ")";
282 return success();
283}
284
285SuccessorOperands SwitchOp::getSuccessorOperands(unsigned index) {
286 assert(index < getNumSuccessors() && "invalid successor index");
287 return SuccessorOperands(index == 0 ? getDefaultOperandsMutable()
288 : getTargetOperandsMutable(index - 1));
289}
290
291Block *SwitchOp::getSuccessorForOperands(ArrayRef<Attribute> operands) {
292 std::optional<DenseIntElementsAttr> literals = getLiterals();
293
294 if (!literals)
295 return getDefaultTarget();
296
297 SuccessorRange targets = getTargets();
298 if (auto value = dyn_cast_or_null<IntegerAttr>(operands.front())) {
299 for (auto [index, literal] : llvm::enumerate(literals->getValues<APInt>()))
300 if (literal == value.getValue())
301 return targets[index];
302 return getDefaultTarget();
303 }
304 return nullptr;
305}
306
307//===----------------------------------------------------------------------===//
308// spirv.LifetimeStart / spirv.LifetimeStop
309//===----------------------------------------------------------------------===//
310
311template <typename LifetimeOpTy>
312static LogicalResult verifyLifetimeOp(LifetimeOpTy op) {
313 auto ptrType = cast<spirv::PointerType>(op.getPointer().getType());
314 if (ptrType.getStorageClass() != spirv::StorageClass::Function)
315 return op.emitOpError("pointer must have the Function storage class");
316
317 return success();
318}
319
320LogicalResult LifetimeStartOp::verify() { return verifyLifetimeOp(*this); }
321
322LogicalResult LifetimeStopOp::verify() { return verifyLifetimeOp(*this); }
323
324//===----------------------------------------------------------------------===//
325// spirv.mlir.loop
326//===----------------------------------------------------------------------===//
327
328void LoopOp::build(OpBuilder &builder, OperationState &state) {
329 state.addAttribute("loop_control", builder.getAttr<spirv::LoopControlAttr>(
330 spirv::LoopControl::None));
331 state.addRegion();
332}
333
334ParseResult LoopOp::parse(OpAsmParser &parser, OperationState &result) {
336 result))
337 return failure();
338
339 if (succeeded(parser.parseOptionalArrow()))
340 if (parser.parseTypeList(result.types))
341 return failure();
342
343 return parser.parseRegion(*result.addRegion(), /*arguments=*/{});
344}
345
346void LoopOp::print(OpAsmPrinter &printer) {
347 auto control = getLoopControl();
348 if (control != spirv::LoopControl::None)
349 printer << " control(" << spirv::stringifyLoopControl(control) << ")";
350 if (getNumResults() > 0) {
351 printer << " -> ";
352 printer << getResultTypes();
353 }
354 printer << ' ';
355 printer.printRegion(getRegion(), /*printEntryBlockArgs=*/false,
356 /*printBlockTerminators=*/true);
357}
358
359/// Returns true if the given `srcBlock` contains only one `spirv.Branch` to the
360/// given `dstBlock`.
361static bool hasOneBranchOpTo(Block &srcBlock, Block &dstBlock) {
362 // Check that there is only one op in the `srcBlock`.
363 if (!llvm::hasSingleElement(srcBlock))
364 return false;
365
366 auto branchOp = dyn_cast<spirv::BranchOp>(srcBlock.back());
367 return branchOp && branchOp.getSuccessor() == &dstBlock;
368}
369
370/// Returns true if the given `block` only contains one `spirv.mlir.merge` op.
371static bool isMergeBlock(Block &block) {
372 return llvm::hasSingleElement(block) && isa<spirv::MergeOp>(block.front());
373}
374
375/// Returns true if a `spirv.mlir.merge` op outside the merge block.
376static bool hasOtherMerge(Region &region) {
377 return !region.empty() && llvm::any_of(region.getOps(), [&](Operation &op) {
378 return isa<spirv::MergeOp>(op) && op.getBlock() != &region.back();
379 });
380}
381
382/// Returns true if types yielded by `spirv.mlir.merge` in the region match
383/// those returned by the `op`.
384static bool returnTypesMatch(Region &region, Operation *op) {
385 auto mergeOps = region.getOps<spirv::MergeOp>();
386 Operation *mergeOp = llvm::getSingleElement(mergeOps);
387 return llvm::equal(mergeOp->getOperandTypes(), op->getResultTypes());
388}
389
390LogicalResult LoopOp::verifyRegions() {
391 auto *op = getOperation();
392
393 // We need to verify that the blocks follow the following layout:
394 //
395 // +-------------+
396 // | entry block |
397 // +-------------+
398 // |
399 // v
400 // +-------------+
401 // | loop header | <-----+
402 // +-------------+ |
403 // |
404 // ... |
405 // \ | / |
406 // v |
407 // +---------------+ |
408 // | loop continue | -----+
409 // +---------------+
410 //
411 // ...
412 // \ | /
413 // v
414 // +-------------+
415 // | merge block |
416 // +-------------+
417
418 auto &region = op->getRegion(0);
419 // Allow empty region as a degenerated case, which can come from
420 // optimizations.
421 if (region.empty())
422 return success();
423
424 // The last block is the merge block.
425 Block &merge = region.back();
426 if (!isMergeBlock(merge))
427 return emitOpError("last block must be the merge block with only one "
428 "'spirv.mlir.merge' op");
429 if (hasOtherMerge(region))
430 return emitOpError(
431 "should not have 'spirv.mlir.merge' op outside the merge block");
432
433 if (region.hasOneBlock())
434 return emitOpError(
435 "must have an entry block branching to the loop header block");
436 // The first block is the entry block.
437 Block &entry = region.front();
438
439 if (std::next(region.begin(), 2) == region.end())
440 return emitOpError(
441 "must have a loop header block branched from the entry block");
442 // The second block is the loop header block.
443 Block &header = *std::next(region.begin(), 1);
444
445 if (!hasOneBranchOpTo(entry, header))
446 return emitOpError(
447 "entry block must only have one 'spirv.Branch' op to the second block");
448
449 if (std::next(region.begin(), 3) == region.end())
450 return emitOpError(
451 "requires a loop continue block branching to the loop header block");
452 // The second to last block is the loop continue block.
453 Block &cont = *std::prev(region.end(), 2);
454
455 // Make sure that we have a branch from the loop continue block to the loop
456 // header block.
457 if (llvm::none_of(
458 llvm::seq<unsigned>(0, cont.getNumSuccessors()),
459 [&](unsigned index) { return cont.getSuccessor(index) == &header; }))
460 return emitOpError("second to last block must be the loop continue "
461 "block that branches to the loop header block");
462
463 // Make sure that no other blocks (except the entry and loop continue block)
464 // branches to the loop header block.
465 for (auto &block : llvm::make_range(std::next(region.begin(), 2),
466 std::prev(region.end(), 2))) {
467 for (auto i : llvm::seq<unsigned>(0, block.getNumSuccessors())) {
468 if (block.getSuccessor(i) == &header) {
469 return emitOpError("can only have the entry and loop continue "
470 "block branching to the loop header block");
471 }
472 }
473 }
474
475 if (!returnTypesMatch(region, op))
476 return emitOpError(
477 "result types do not match types yielded with `spirv.mlir.merge`");
478
479 return success();
480}
481
482Block *LoopOp::getEntryBlock() {
483 assert(!getBody().empty() && "op region should not be empty!");
484 return &getBody().front();
485}
486
487Block *LoopOp::getHeaderBlock() {
488 assert(!getBody().empty() && "op region should not be empty!");
489 // The second block is the loop header block.
490 return &*std::next(getBody().begin());
491}
492
493Block *LoopOp::getContinueBlock() {
494 assert(!getBody().empty() && "op region should not be empty!");
495 // The second to last block is the loop continue block.
496 return &*std::prev(getBody().end(), 2);
497}
498
499Block *LoopOp::getMergeBlock() {
500 assert(!getBody().empty() && "op region should not be empty!");
501 // The last block is the loop merge block.
502 return &getBody().back();
503}
504
505void LoopOp::addEntryAndMergeBlock(OpBuilder &builder) {
506 assert(getBody().empty() && "entry and merge block already exist");
507 OpBuilder::InsertionGuard g(builder);
508 builder.createBlock(&getBody());
509 builder.createBlock(&getBody());
510
511 // Add a spirv.mlir.merge op into the merge block.
512 spirv::MergeOp::create(builder, getLoc());
513}
514
515//===----------------------------------------------------------------------===//
516// spirv.Return
517//===----------------------------------------------------------------------===//
518
519LogicalResult ReturnOp::verify() {
520 // Verification is performed in spirv.func op.
521 return success();
522}
523
524//===----------------------------------------------------------------------===//
525// spirv.ReturnValue
526//===----------------------------------------------------------------------===//
527
528LogicalResult ReturnValueOp::verify() {
529 // Verification is performed in spirv.func op.
530 return success();
531}
532
533//===----------------------------------------------------------------------===//
534// spirv.Select
535//===----------------------------------------------------------------------===//
536
537LogicalResult SelectOp::verify() {
538 if (auto conditionTy = dyn_cast<VectorType>(getCondition().getType())) {
539 auto resultVectorTy = dyn_cast<VectorType>(getResult().getType());
540 if (!resultVectorTy) {
541 return emitOpError("result expected to be of vector type when "
542 "condition is of vector type");
543 }
544 if (resultVectorTy.getNumElements() != conditionTy.getNumElements()) {
545 return emitOpError("result should have the same number of elements as "
546 "the condition when condition is of vector type");
547 }
548 }
549 return success();
550}
551
552// Custom availability implementation is needed for spirv.Select given the
553// syntax changes starting v1.4.
554SmallVector<ArrayRef<spirv::Extension>, 1> SelectOp::getExtensions() {
555 return {};
556}
557SmallVector<ArrayRef<spirv::Capability>, 1> SelectOp::getCapabilities() {
558 return {};
559}
560std::optional<spirv::Version> SelectOp::getMinVersion() {
561 // Per the spec, "Before version 1.4, results are only computed per
562 // component."
563 if (isa<spirv::ScalarType>(getCondition().getType()) &&
564 isa<spirv::CompositeType>(getType()))
565 return Version::V_1_4;
566
567 return Version::V_1_0;
568}
569std::optional<spirv::Version> SelectOp::getMaxVersion() {
570 return Version::V_1_6;
571}
572
573//===----------------------------------------------------------------------===//
574// spirv.mlir.selection
575//===----------------------------------------------------------------------===//
576
577ParseResult SelectionOp::parse(OpAsmParser &parser, OperationState &result) {
578 if (parseControlAttribute<spirv::SelectionControlAttr,
579 spirv::SelectionControl>(parser, result))
580 return failure();
581
582 if (succeeded(parser.parseOptionalArrow()))
583 if (parser.parseTypeList(result.types))
584 return failure();
585
586 return parser.parseRegion(*result.addRegion(), /*arguments=*/{});
587}
588
589void SelectionOp::print(OpAsmPrinter &printer) {
590 auto control = getSelectionControl();
591 if (control != spirv::SelectionControl::None)
592 printer << " control(" << spirv::stringifySelectionControl(control) << ")";
593 if (getNumResults() > 0) {
594 printer << " -> ";
595 printer << getResultTypes();
596 }
597 printer << ' ';
598 printer.printRegion(getRegion(), /*printEntryBlockArgs=*/false,
599 /*printBlockTerminators=*/true);
600}
601
602LogicalResult SelectionOp::verifyRegions() {
603 auto *op = getOperation();
604
605 // We need to verify that the blocks follow the following layout:
606 //
607 // +--------------+
608 // | header block |
609 // +--------------+
610 // / | \
611 // ...
612 //
613 //
614 // +---------+ +---------+ +---------+
615 // | case #0 | | case #1 | | case #2 | ...
616 // +---------+ +---------+ +---------+
617 //
618 //
619 // ...
620 // \ | /
621 // v
622 // +-------------+
623 // | merge block |
624 // +-------------+
625
626 auto &region = op->getRegion(0);
627 // Allow empty region as a degenerated case, which can come from
628 // optimizations.
629 if (region.empty())
630 return success();
631
632 // The last block is the merge block.
633 if (!isMergeBlock(region.back()))
634 return emitOpError("last block must be the merge block with only one "
635 "'spirv.mlir.merge' op");
636 if (hasOtherMerge(region))
637 return emitOpError(
638 "should not have 'spirv.mlir.merge' op outside the merge block");
639
640 if (region.hasOneBlock())
641 return emitOpError("must have a selection header block");
642
643 if (!returnTypesMatch(region, op))
644 return emitOpError(
645 "result types do not match types yielded with `spirv.mlir.merge`");
646
647 return success();
648}
649
650Block *SelectionOp::getHeaderBlock() {
651 assert(!getBody().empty() && "op region should not be empty!");
652 // The first block is the loop header block.
653 return &getBody().front();
654}
655
656Block *SelectionOp::getMergeBlock() {
657 assert(!getBody().empty() && "op region should not be empty!");
658 // The last block is the loop merge block.
659 return &getBody().back();
660}
661
662void SelectionOp::addMergeBlock(OpBuilder &builder) {
663 assert(getBody().empty() && "entry and merge block already exist");
664 OpBuilder::InsertionGuard guard(builder);
665 builder.createBlock(&getBody());
666
667 // Add a spirv.mlir.merge op into the merge block.
668 spirv::MergeOp::create(builder, getLoc());
669}
670
671SelectionOp
672SelectionOp::createIfThen(Location loc, Value condition,
673 function_ref<void(OpBuilder &builder)> thenBody,
674 OpBuilder &builder) {
675 auto selectionOp =
676 spirv::SelectionOp::create(builder, loc, spirv::SelectionControl::None);
677
678 selectionOp.addMergeBlock(builder);
679 Block *mergeBlock = selectionOp.getMergeBlock();
680 Block *thenBlock = nullptr;
681
682 // Build the "then" block.
683 {
684 OpBuilder::InsertionGuard guard(builder);
685 thenBlock = builder.createBlock(mergeBlock);
686 thenBody(builder);
687 spirv::BranchOp::create(builder, loc, mergeBlock);
688 }
689
690 // Build the header block.
691 {
692 OpBuilder::InsertionGuard guard(builder);
693 builder.createBlock(thenBlock);
694 spirv::BranchConditionalOp::create(builder, loc, condition, thenBlock,
695 /*trueArguments=*/ArrayRef<Value>(),
696 mergeBlock,
697 /*falseArguments=*/ArrayRef<Value>());
698 }
699
700 return selectionOp;
701}
702
703//===----------------------------------------------------------------------===//
704// spirv.Unreachable
705//===----------------------------------------------------------------------===//
706
707LogicalResult spirv::UnreachableOp::verify() {
708 auto *block = (*this)->getBlock();
709 // Fast track: if this is in entry block, its invalid. Otherwise, if no
710 // predecessors, it's valid.
711 if (block->isEntryBlock())
712 return emitOpError("cannot be used in reachable block");
713 if (block->hasNoPredecessors())
714 return success();
715
716 // TODO: further verification needs to analyze reachability from
717 // the entry block.
718
719 return success();
720}
721
722} // namespace mlir::spirv
return success()
getNumOperands() - 1))) return failure()
Given a list of lists of parsed operands, populates uniqueOperands with unique operands.
virtual Builder & getBuilder() const =0
Return a builder which provides useful access to MLIRContext, global objects like types and attribute...
virtual ParseResult parseOptionalKeyword(StringRef keyword)=0
Parse the given keyword if present.
virtual ParseResult parseRParen()=0
Parse a ) token.
virtual ParseResult parseRSquare()=0
Parse a ] token.
virtual ParseResult parseOptionalArrow()=0
Parse a '->' token if present.
virtual ParseResult parseLParen()=0
Parse a ( token.
virtual ParseResult parseComma()=0
Parse a , token.
ParseResult parseTypeList(SmallVectorImpl< Type > &result)
Parse a type list.
virtual ParseResult parseOptionalLSquare()=0
Parse a [ token if present.
virtual ParseResult parseAttribute(Attribute &result, Type type={})=0
Parse an arbitrary attribute of a given type and return it in result.
Block represents an ordered list of Operations.
Definition Block.h:34
unsigned getNumSuccessors()
Definition Block.cpp:270
Operation & front()
Definition Block.h:178
Operation & back()
Definition Block.h:177
This class is a general helper class for creating context-global objects like types,...
Definition Builders.h:51
IntegerType getI1Type()
Definition Builders.cpp:61
Attr getAttr(Args &&...args)
Get or construct an instance of the attribute Attr with provided arguments.
Definition Builders.h:101
static DenseIntElementsAttr get(const ShapedType &type, Arg &&arg)
Get an instance of a DenseIntElementsAttr with the given arguments.
The OpAsmParser has methods for interacting with the asm parser: parsing things from it,...
virtual ParseResult parseRegion(Region &region, ArrayRef< Argument > arguments={}, bool enableNameShadowing=false)=0
Parses a region.
virtual ParseResult resolveOperand(const UnresolvedOperand &operand, Type type, SmallVectorImpl< Value > &result)=0
Resolve an operand to an SSA value, emitting an error on failure.
virtual ParseResult parseSuccessorAndUseList(Block *&dest, SmallVectorImpl< Value > &operands)=0
Parse a single operation successor and its operand list.
virtual ParseResult parseOperand(UnresolvedOperand &result, bool allowResultNumber=true)=0
Parse a single SSA value operand name along with a result number if allowResultNumber is true.
virtual void printSuccessorAndUseList(Block *successor, ValueRange succOperands)=0
Print the successor and its operands.
virtual void printRegion(Region &blocks, bool printEntryBlockArgs=true, bool printBlockTerminators=true, bool printEmptyBlock=false)=0
Prints a region.
Block * createBlock(Region *parent, Region::iterator insertPt={}, TypeRange argTypes={}, ArrayRef< Location > locs={})
Add new block with 'argTypes' arguments and set the insertion point to the end of it.
Definition Builders.cpp:439
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
OperandRange operand_range
Definition Operation.h:396
result_type_range getResultTypes()
Definition Operation.h:453
This class contains a list of basic blocks and a link to the parent operation it is attached to.
Definition Region.h:26
iterator_range< OpIterator > getOps()
Definition Region.h:180
bool empty()
Definition Region.h:60
This class models how operands are forwarded to block arguments in control flow.
virtual Operation * lookupNearestSymbolFrom(Operation *from, StringAttr symbol)
Returns the operation registered with the given symbol name within the closest parent operation of,...
Type getType() const
Return the type of this value.
Definition Value.h:105
constexpr char kControl[]
static LogicalResult verifyLifetimeOp(LifetimeOpTy op)
ParseResult parseEnumKeywordAttr(EnumClass &value, ParserType &parser, StringRef attrName=spirv::attributeName< EnumClass >())
Parses the next keyword in parser as an enumerant of the given EnumClass.
static bool hasOtherMerge(Region &region)
Returns true if a spirv.mlir.merge op outside the merge block.
static bool returnTypesMatch(Region &region, Operation *op)
Returns true if types yielded by spirv.mlir.merge in the region match those returned by the op.
static bool hasOneBranchOpTo(Block &srcBlock, Block &dstBlock)
Returns true if the given srcBlock contains only one spirv.Branch to the given dstBlock.
constexpr StringRef attributeName()
static ParseResult parseControlAttribute(OpAsmParser &parser, OperationState &state, StringRef attrName=spirv::attributeName< EnumClass >())
Parses Function, Selection and Loop control attributes.
static bool isMergeBlock(Block &block)
Returns true if the given block only contains one spirv.mlir.merge op.
Type getType(OpFoldResult ofr)
Returns the int type of the integer in ofr.
Definition Utils.cpp:311
llvm::function_ref< Fn > function_ref
Definition LLVM.h:155
This represents an operation in an abstracted form, suitable for use with the builder APIs.
void addAttribute(StringRef name, Attribute attr)
Add an attribute with the specified name.
Region * addRegion()
Create a region that should be attached to the operation.