MLIR 24.0.0git
TransformOps.cpp
Go to the documentation of this file.
1//===- TransformOps.cpp - Transform dialect operations --------------------===//
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
10
19#include "mlir/IR/Diagnostics.h"
20#include "mlir/IR/Dominance.h"
24#include "mlir/IR/Verifier.h"
30#include "mlir/Transforms/CSE.h"
35#include "llvm/ADT/DenseSet.h"
36#include "llvm/ADT/STLExtras.h"
37#include "llvm/ADT/ScopeExit.h"
38#include "llvm/ADT/SmallPtrSet.h"
39#include "llvm/ADT/SmallVectorExtras.h"
40#include "llvm/ADT/TypeSwitch.h"
41#include "llvm/Support/Debug.h"
42#include "llvm/Support/DebugLog.h"
43#include "llvm/Support/ErrorHandling.h"
44#include "llvm/Support/InterleavedRange.h"
45#include <optional>
46
47#define DEBUG_TYPE "transform-dialect"
48#define DEBUG_TYPE_MATCHER "transform-matcher"
49
50using namespace mlir;
51
52static ParseResult parseApplyRegisteredPassOptions(
53 OpAsmParser &parser, DictionaryAttr &options,
56 Operation *op,
57 DictionaryAttr options,
58 ValueRange dynamicOptions);
59static ParseResult parseSequenceOpOperands(
60 OpAsmParser &parser, std::optional<OpAsmParser::UnresolvedOperand> &root,
61 Type &rootType,
63 SmallVectorImpl<Type> &extraBindingTypes);
64static void printSequenceOpOperands(OpAsmPrinter &printer, Operation *op,
65 Value root, Type rootType,
66 ValueRange extraBindings,
67 TypeRange extraBindingTypes);
68static void printForeachMatchSymbols(OpAsmPrinter &printer, Operation *op,
70static ParseResult parseForeachMatchSymbols(OpAsmParser &parser,
72 ArrayAttr &actions);
73
74/// Helper function to check if the given transform op is contained in (or
75/// equal to) the given payload target op. In that case, an error is returned.
76/// Transforming transform IR that is currently executing is generally unsafe.
78ensurePayloadIsSeparateFromTransform(transform::TransformOpInterface transform,
79 Operation *payload) {
80 Operation *transformAncestor = transform.getOperation();
81 while (transformAncestor) {
82 if (transformAncestor == payload) {
84 transform.emitDefiniteFailure()
85 << "cannot apply transform to itself (or one of its ancestors)";
86 diag.attachNote(payload->getLoc()) << "target payload op";
87 return diag;
88 }
89 transformAncestor = transformAncestor->getParentOp();
90 }
92}
93
94#define GET_OP_CLASSES
95#include "mlir/Dialect/Transform/IR/TransformOps.cpp.inc"
96
97//===----------------------------------------------------------------------===//
98// AlternativesOp
99//===----------------------------------------------------------------------===//
100
101OperandRange transform::AlternativesOp::getEntrySuccessorOperands(
102 RegionSuccessor successor) {
103 if (!successor.isOperation() && getOperation()->getNumOperands() == 1)
104 return getOperation()->getOperands();
105 return OperandRange(getOperation()->operand_end(),
106 getOperation()->operand_end());
107}
108
109void transform::AlternativesOp::getSuccessorRegions(
111 for (Region &alternative : llvm::drop_begin(
112 getAlternatives(), point.isParent()
113 ? 0
115 ->getParentRegion()
116 ->getRegionNumber() +
117 1)) {
118 regions.emplace_back(&alternative);
119 }
120 if (!point.isParent())
121 regions.push_back(RegionSuccessor(getOperation()));
122}
123
125transform::AlternativesOp::getSuccessorInputs(RegionSuccessor successor) {
126 if (successor.isOperation())
127 return getOperation()->getResults();
128 return successor.getSuccessor()->getArguments();
129}
130
131void transform::AlternativesOp::getRegionInvocationBounds(
133 (void)operands;
134 // The region corresponding to the first alternative is always executed, the
135 // remaining may or may not be executed.
136 bounds.reserve(getNumRegions());
137 bounds.emplace_back(1, 1);
138 bounds.resize(getNumRegions(), InvocationBounds(0, 1));
139}
140
143 for (const auto &res : block->getParentOp()->getOpResults())
144 results.set(res, {});
145}
146
148transform::AlternativesOp::apply(transform::TransformRewriter &rewriter,
151 SmallVector<Operation *> originals;
152 if (Value scopeHandle = getScope())
153 llvm::append_range(originals, state.getPayloadOps(scopeHandle));
154 else
155 originals.push_back(state.getTopLevel());
156
157 for (Operation *original : originals) {
158 if (original->isAncestor(getOperation())) {
160 << "scope must not contain the transforms being applied";
161 diag.attachNote(original->getLoc()) << "scope";
162 return diag;
163 }
164 if (!original->hasTrait<OpTrait::IsIsolatedFromAbove>()) {
166 << "only isolated-from-above ops can be alternative scopes";
167 diag.attachNote(original->getLoc()) << "scope";
168 return diag;
169 }
170 }
171
172 for (Region &reg : getAlternatives()) {
173 // Clone the scope operations and make the transforms in this alternative
174 // region apply to them by virtue of mapping the block argument (the only
175 // visible handle) to the cloned scope operations. This effectively prevents
176 // the transformation from accessing any IR outside the scope.
177 auto scope = state.make_region_scope(reg);
178 auto clones = llvm::map_to_vector(
179 originals, [](Operation *op) { return op->clone(); });
180 llvm::scope_exit deleteClones([&] {
181 for (Operation *clone : clones)
182 clone->erase();
183 });
184 if (failed(state.mapBlockArguments(reg.front().getArgument(0), clones)))
186
187 bool failed = false;
188 for (Operation &transform : reg.front().without_terminator()) {
190 state.applyTransform(cast<TransformOpInterface>(transform));
191 if (result.isSilenceableFailure()) {
192 LDBG() << "alternative failed: " << result.getMessage();
193 failed = true;
194 break;
195 }
196
197 if (::mlir::failed(result.silence()))
199 }
200
201 // If all operations in the given alternative succeeded, no need to consider
202 // the rest. Replace the original scoping operation with the clone on which
203 // the transformations were performed.
204 if (!failed) {
205 // We will be using the clones, so cancel their scheduled deletion.
206 deleteClones.release();
207 TrackingListener listener(state, *this);
208 IRRewriter rewriter(getContext(), &listener);
209 for (const auto &kvp : llvm::zip(originals, clones)) {
210 Operation *original = std::get<0>(kvp);
211 Operation *clone = std::get<1>(kvp);
212 original->getBlock()->getOperations().insert(original->getIterator(),
213 clone);
214 rewriter.replaceOp(original, clone->getResults());
215 }
216 detail::forwardTerminatorOperands(&reg.front(), state, results);
218 }
219 }
220 return emitSilenceableError() << "all alternatives failed";
221}
222
223void transform::AlternativesOp::getEffects(
225 consumesHandle(getOperation()->getOpOperands(), effects);
226 producesHandle(getOperation()->getOpResults(), effects);
227 for (Region *region : getRegions()) {
228 if (!region->empty())
229 producesHandle(region->front().getArguments(), effects);
230 }
231 modifiesPayload(effects);
232}
233
234LogicalResult transform::AlternativesOp::verify() {
235 for (Region &alternative : getAlternatives()) {
236 Block &block = alternative.front();
237 Operation *terminator = block.getTerminator();
238 if (terminator->getOperands().getTypes() != getResults().getTypes()) {
240 << "expects terminator operands to have the "
241 "same type as results of the operation";
242 diag.attachNote(terminator->getLoc()) << "terminator";
243 return diag;
244 }
245 }
246
247 return success();
248}
249
250//===----------------------------------------------------------------------===//
251// AnnotateOp
252//===----------------------------------------------------------------------===//
253
255transform::AnnotateOp::apply(transform::TransformRewriter &rewriter,
259 llvm::to_vector(state.getPayloadOps(getTarget()));
260
261 Attribute attr = UnitAttr::get(getContext());
262 if (auto paramH = getParam()) {
263 ArrayRef<Attribute> params = state.getParams(paramH);
264 if (params.size() != 1) {
265 if (targets.size() != params.size()) {
266 return emitSilenceableError()
267 << "parameter and target have different payload lengths ("
268 << params.size() << " vs " << targets.size() << ")";
269 }
270 for (auto &&[target, attr] : llvm::zip_equal(targets, params))
271 target->setAttr(getName(), attr);
273 }
274 attr = params[0];
275 }
276 for (auto *target : targets)
277 target->setAttr(getName(), attr);
279}
280
281void transform::AnnotateOp::getEffects(
283 onlyReadsHandle(getTargetMutable(), effects);
284 onlyReadsHandle(getParamMutable(), effects);
285 modifiesPayload(effects);
286}
287
288//===----------------------------------------------------------------------===//
289// ApplyCommonSubexpressionEliminationOp
290//===----------------------------------------------------------------------===//
291
293transform::ApplyCommonSubexpressionEliminationOp::applyToOne(
295 ApplyToEachResultList &results, transform::TransformState &state) {
296 // Make sure that this transform is not applied to itself. Modifying the
297 // transform IR while it is being interpreted is generally dangerous.
298 DiagnosedSilenceableFailure payloadCheck =
300 if (!payloadCheck.succeeded())
301 return payloadCheck;
302
303 DominanceInfo domInfo;
306}
307
308void transform::ApplyCommonSubexpressionEliminationOp::getEffects(
310 transform::onlyReadsHandle(getTargetMutable(), effects);
312}
313
314//===----------------------------------------------------------------------===//
315// ApplyDeadCodeEliminationOp
316//===----------------------------------------------------------------------===//
317
318DiagnosedSilenceableFailure transform::ApplyDeadCodeEliminationOp::applyToOne(
320 ApplyToEachResultList &results, transform::TransformState &state) {
321 // Make sure that this transform is not applied to itself. Modifying the
322 // transform IR while it is being interpreted is generally dangerous.
323 DiagnosedSilenceableFailure payloadCheck =
325 if (!payloadCheck.succeeded())
326 return payloadCheck;
327
328 for (Region &region : target->getRegions())
329 eliminateTriviallyDeadOps(rewriter, region);
330
332}
333
334void transform::ApplyDeadCodeEliminationOp::getEffects(
336 transform::onlyReadsHandle(getTargetMutable(), effects);
338}
339
340//===----------------------------------------------------------------------===//
341// ApplyPatternsOp
342//===----------------------------------------------------------------------===//
343
344DiagnosedSilenceableFailure transform::ApplyPatternsOp::applyToOne(
346 ApplyToEachResultList &results, transform::TransformState &state) {
347 // Make sure that this transform is not applied to itself. Modifying the
348 // transform IR while it is being interpreted is generally dangerous. Even
349 // more so for the ApplyPatternsOp because the GreedyPatternRewriteDriver
350 // performs many additional simplifications such as dead code elimination.
351 DiagnosedSilenceableFailure payloadCheck =
353 if (!payloadCheck.succeeded())
354 return payloadCheck;
355
356 // Gather all specified patterns.
357 MLIRContext *ctx = target->getContext();
358 RewritePatternSet patterns(ctx);
359 if (!getRegion().empty()) {
360 for (Operation &op : getRegion().front()) {
361 cast<transform::PatternDescriptorOpInterface>(&op)
362 .populatePatternsWithState(patterns, state);
363 }
364 }
365
366 // Configure the GreedyPatternRewriteDriver.
367 GreedyRewriteConfig config;
368 config.setListener(
369 static_cast<RewriterBase::Listener *>(rewriter.getListener()));
370 FrozenRewritePatternSet frozenPatterns(std::move(patterns));
371
372 config.setMaxIterations(getMaxIterations() == static_cast<uint64_t>(-1)
374 : getMaxIterations());
375 config.setMaxNumRewrites(getMaxNumRewrites() == static_cast<uint64_t>(-1)
377 : getMaxNumRewrites());
378
379 if (target->hasTrait<OpTrait::IsIsolatedFromAbove>()) {
380 // Op is isolated from above. The greedy driver iterates to a fixpoint
381 // internally and optionally runs full CSE between iterations.
382 config.enableCSEBetweenIterations(getApplyCse());
383 if (failed(applyPatternsGreedily(target, frozenPatterns, config))) {
385 << "greedy pattern application failed";
386 }
388 }
389
390 // Non-isolated case: gather the ops manually because the op-list
391 // GreedyPatternRewriteDriver overload only performs a single iteration and
392 // does not simplify regions. CSE is driven externally to reach a fixpoint.
393
394 // One or two iterations should be sufficient. Stop iterating after a certain
395 // threshold to make debugging easier.
396 static const int64_t kNumMaxIterations = 50;
397 int64_t iteration = 0;
398 bool cseChanged = false;
399 do {
401 target->walk([&](Operation *nestedOp) {
402 if (target != nestedOp)
403 ops.push_back(nestedOp);
404 });
405
406 if (failed(applyOpPatternsGreedily(ops, frozenPatterns, config))) {
408 << "greedy pattern application failed";
409 }
410
411 if (getApplyCse()) {
412 DominanceInfo domInfo;
414 &cseChanged);
415 }
416 } while (cseChanged && ++iteration < kNumMaxIterations);
417
418 if (iteration == kNumMaxIterations)
419 return emitDefiniteFailure() << "fixpoint iteration did not converge";
420
422}
423
424LogicalResult transform::ApplyPatternsOp::verify() {
425 if (!getRegion().empty()) {
426 for (Operation &op : getRegion().front()) {
427 if (!isa<transform::PatternDescriptorOpInterface>(&op)) {
429 << "expected children ops to implement "
430 "PatternDescriptorOpInterface";
431 diag.attachNote(op.getLoc()) << "op without interface";
432 return diag;
433 }
434 }
435 }
436 return success();
437}
438
439void transform::ApplyPatternsOp::getEffects(
441 transform::onlyReadsHandle(getTargetMutable(), effects);
443}
444
445void transform::ApplyPatternsOp::build(
447 function_ref<void(OpBuilder &, Location)> bodyBuilder) {
448 result.addOperands(target);
449
450 OpBuilder::InsertionGuard g(builder);
451 Region *region = result.addRegion();
452 builder.createBlock(region);
453 if (bodyBuilder)
454 bodyBuilder(builder, result.location);
455}
456
457//===----------------------------------------------------------------------===//
458// ApplyCanonicalizationPatternsOp
459//===----------------------------------------------------------------------===//
460
461void transform::ApplyCanonicalizationPatternsOp::populatePatterns(
462 RewritePatternSet &patterns) {
463 MLIRContext *ctx = patterns.getContext();
464 for (Dialect *dialect : ctx->getLoadedDialects())
465 dialect->getCanonicalizationPatterns(patterns);
467 op.getCanonicalizationPatterns(patterns, ctx);
468}
469
470//===----------------------------------------------------------------------===//
471// ApplyConversionPatternsOp
472//===----------------------------------------------------------------------===//
473
474DiagnosedSilenceableFailure transform::ApplyConversionPatternsOp::apply(
477 MLIRContext *ctx = getContext();
478
479 // Instantiate the default type converter if a type converter builder is
480 // specified.
481 std::unique_ptr<TypeConverter> defaultTypeConverter;
482 transform::TypeConverterBuilderOpInterface typeConverterBuilder =
483 getDefaultTypeConverter();
484 if (typeConverterBuilder)
485 defaultTypeConverter = typeConverterBuilder.getTypeConverter();
486
487 // Configure conversion target.
488 ConversionTarget conversionTarget(*getContext());
489 if (getLegalOps())
490 for (Attribute attr : cast<ArrayAttr>(*getLegalOps()))
491 conversionTarget.addLegalOp(
492 OperationName(cast<StringAttr>(attr).getValue(), ctx));
493 if (getIllegalOps())
494 for (Attribute attr : cast<ArrayAttr>(*getIllegalOps()))
495 conversionTarget.addIllegalOp(
496 OperationName(cast<StringAttr>(attr).getValue(), ctx));
497 if (getLegalDialects())
498 for (Attribute attr : cast<ArrayAttr>(*getLegalDialects()))
499 conversionTarget.addLegalDialect(cast<StringAttr>(attr).getValue());
500 if (getIllegalDialects())
501 for (Attribute attr : cast<ArrayAttr>(*getIllegalDialects()))
502 conversionTarget.addIllegalDialect(cast<StringAttr>(attr).getValue());
503
504 // Gather all specified patterns.
505 RewritePatternSet patterns(ctx);
506 // Need to keep the converters alive until after pattern application because
507 // the patterns take a reference to an object that would otherwise get out of
508 // scope.
510 if (!getPatterns().empty()) {
511 for (Operation &op : getPatterns().front()) {
512 auto descriptor =
513 cast<transform::ConversionPatternDescriptorOpInterface>(&op);
514
515 // Check if this pattern set specifies a type converter.
516 std::unique_ptr<TypeConverter> typeConverter =
517 descriptor.getTypeConverter();
518 TypeConverter *converter = nullptr;
519 if (typeConverter) {
520 keepAliveConverters.emplace_back(std::move(typeConverter));
521 converter = keepAliveConverters.back().get();
522 } else {
523 // No type converter specified: Use the default type converter.
524 if (!defaultTypeConverter) {
526 << "pattern descriptor does not specify type "
527 "converter and apply_conversion_patterns op has "
528 "no default type converter";
529 diag.attachNote(op.getLoc()) << "pattern descriptor op";
530 return diag;
531 }
532 converter = defaultTypeConverter.get();
533 }
534
535 // Add descriptor-specific updates to the conversion target, which may
536 // depend on the final type converter. In structural converters, the
537 // legality of types dictates the dynamic legality of an operation.
538 descriptor.populateConversionTargetRules(*converter, conversionTarget);
539
540 descriptor.populatePatterns(*converter, patterns);
541 }
542 }
543
544 // Attach a tracking listener if handles should be preserved. We configure the
545 // listener to allow op replacements with different names, as conversion
546 // patterns typically replace ops with replacement ops that have a different
547 // name.
548 TrackingListenerConfig trackingConfig;
549 trackingConfig.requireMatchingReplacementOpName = false;
550 ErrorCheckingTrackingListener trackingListener(state, *this, trackingConfig);
551 ConversionConfig conversionConfig;
552 if (getPreserveHandles())
553 conversionConfig.listener = &trackingListener;
554
555 FrozenRewritePatternSet frozenPatterns(std::move(patterns));
556 for (Operation *target : state.getPayloadOps(getTarget())) {
557 // Make sure that this transform is not applied to itself. Modifying the
558 // transform IR while it is being interpreted is generally dangerous.
559 DiagnosedSilenceableFailure payloadCheck =
561 if (!payloadCheck.succeeded())
562 return payloadCheck;
563
564 LogicalResult status = failure();
565 if (getPartialConversion()) {
566 status = applyPartialConversion(target, conversionTarget, frozenPatterns,
567 conversionConfig);
568 } else {
569 status = applyFullConversion(target, conversionTarget, frozenPatterns,
570 conversionConfig);
571 }
572
573 // Check dialect conversion state.
575 if (failed(status)) {
576 diag = emitSilenceableError() << "dialect conversion failed";
577 diag.attachNote(target->getLoc()) << "target op";
578 }
579
580 // Check tracking listener error state.
581 DiagnosedSilenceableFailure trackingFailure =
582 trackingListener.checkAndResetError();
583 if (!trackingFailure.succeeded()) {
584 if (diag.succeeded()) {
585 // Tracking failure is the only failure.
586 return trackingFailure;
587 }
588 diag.attachNote() << "tracking listener also failed: "
589 << trackingFailure.getMessage();
590 (void)trackingFailure.silence();
591 }
592
593 if (!diag.succeeded())
594 return diag;
595 }
596
598}
599
600LogicalResult transform::ApplyConversionPatternsOp::verify() {
601 if (getNumRegions() != 1 && getNumRegions() != 2)
602 return emitOpError() << "expected 1 or 2 regions";
603 if (!getPatterns().empty()) {
604 for (Operation &op : getPatterns().front()) {
605 if (!isa<transform::ConversionPatternDescriptorOpInterface>(&op)) {
607 emitOpError() << "expected pattern children ops to implement "
608 "ConversionPatternDescriptorOpInterface";
609 diag.attachNote(op.getLoc()) << "op without interface";
610 return diag;
611 }
612 }
613 }
614 if (getNumRegions() == 2) {
615 Region &typeConverterRegion = getRegion(1);
616 if (!llvm::hasSingleElement(typeConverterRegion.front()))
617 return emitOpError()
618 << "expected exactly one op in default type converter region";
619 Operation *maybeTypeConverter = &typeConverterRegion.front().front();
620 auto typeConverterOp = dyn_cast<transform::TypeConverterBuilderOpInterface>(
621 maybeTypeConverter);
622 if (!typeConverterOp) {
624 << "expected default converter child op to "
625 "implement TypeConverterBuilderOpInterface";
626 diag.attachNote(maybeTypeConverter->getLoc()) << "op without interface";
627 return diag;
628 }
629 // Check default type converter type.
630 if (!getPatterns().empty()) {
631 for (Operation &op : getPatterns().front()) {
632 auto descriptor =
633 cast<transform::ConversionPatternDescriptorOpInterface>(&op);
634 if (failed(descriptor.verifyTypeConverter(typeConverterOp)))
635 return failure();
636 }
637 }
638 }
639 return success();
640}
641
642void transform::ApplyConversionPatternsOp::getEffects(
644 if (!getPreserveHandles()) {
645 transform::consumesHandle(getTargetMutable(), effects);
646 } else {
647 transform::onlyReadsHandle(getTargetMutable(), effects);
648 }
650}
651
652void transform::ApplyConversionPatternsOp::build(
654 function_ref<void(OpBuilder &, Location)> patternsBodyBuilder,
655 function_ref<void(OpBuilder &, Location)> typeConverterBodyBuilder) {
656 result.addOperands(target);
657
658 {
659 OpBuilder::InsertionGuard g(builder);
660 Region *region1 = result.addRegion();
661 builder.createBlock(region1);
662 if (patternsBodyBuilder)
663 patternsBodyBuilder(builder, result.location);
664 }
665 {
666 OpBuilder::InsertionGuard g(builder);
667 Region *region2 = result.addRegion();
668 builder.createBlock(region2);
669 if (typeConverterBodyBuilder)
670 typeConverterBodyBuilder(builder, result.location);
671 }
672}
673
674//===----------------------------------------------------------------------===//
675// ApplyToLLVMConversionPatternsOp
676//===----------------------------------------------------------------------===//
677
678void transform::ApplyToLLVMConversionPatternsOp::populatePatterns(
679 TypeConverter &typeConverter, RewritePatternSet &patterns) {
680 Dialect *dialect = getContext()->getLoadedDialect(getDialectName());
681 assert(dialect && "expected that dialect is loaded");
682 auto *iface = cast<ConvertToLLVMPatternInterface>(dialect);
683 // ConversionTarget is currently ignored because the enclosing
684 // apply_conversion_patterns op sets up its own ConversionTarget.
686 iface->populateConvertToLLVMConversionPatterns(
687 target, static_cast<LLVMTypeConverter &>(typeConverter), patterns);
688}
689
690LogicalResult transform::ApplyToLLVMConversionPatternsOp::verifyTypeConverter(
691 transform::TypeConverterBuilderOpInterface builder) {
692 if (builder.getTypeConverterType() != "LLVMTypeConverter")
693 return emitOpError("expected LLVMTypeConverter");
694 return success();
695}
696
697LogicalResult transform::ApplyToLLVMConversionPatternsOp::verify() {
698 Dialect *dialect = getContext()->getLoadedDialect(getDialectName());
699 if (!dialect)
700 return emitOpError("unknown dialect or dialect not loaded: ")
701 << getDialectName();
702 auto *iface = dyn_cast<ConvertToLLVMPatternInterface>(dialect);
703 if (!iface)
704 return emitOpError(
705 "dialect does not implement ConvertToLLVMPatternInterface or "
706 "extension was not loaded: ")
707 << getDialectName();
708 return success();
709}
710
711//===----------------------------------------------------------------------===//
712// ApplyLoopInvariantCodeMotionOp
713//===----------------------------------------------------------------------===//
714
716transform::ApplyLoopInvariantCodeMotionOp::applyToOne(
717 transform::TransformRewriter &rewriter, LoopLikeOpInterface target,
720 // Currently, LICM does not remove operations, so we don't need tracking.
721 // If this ever changes, add a LICM entry point that takes a rewriter.
724}
725
726void transform::ApplyLoopInvariantCodeMotionOp::getEffects(
728 transform::onlyReadsHandle(getTargetMutable(), effects);
730}
731
732//===----------------------------------------------------------------------===//
733// ApplyRegisteredPassOp
734//===----------------------------------------------------------------------===//
735
736void transform::ApplyRegisteredPassOp::getEffects(
738 consumesHandle(getTargetMutable(), effects);
739 onlyReadsHandle(getDynamicOptionsMutable(), effects);
740 producesHandle(getOperation()->getOpResults(), effects);
741 modifiesPayload(effects);
742}
743
745transform::ApplyRegisteredPassOp::apply(transform::TransformRewriter &rewriter,
748 // Obtain a single options-string to pass to the pass(-pipeline) from options
749 // passed in as a dictionary of keys mapping to values which are either
750 // attributes or param-operands pointing to attributes.
751 OperandRange dynamicOptions = getDynamicOptions();
752
753 std::string options;
754 llvm::raw_string_ostream optionsStream(options); // For "printing" attrs.
755
756 // A helper to convert an option's attribute value into a corresponding
757 // string representation, with the ability to obtain the attr(s) from a param.
758 std::function<void(Attribute)> appendValueAttr = [&](Attribute valueAttr) {
759 if (auto paramOperand = dyn_cast<transform::ParamOperandAttr>(valueAttr)) {
760 // The corresponding value attribute(s) is/are passed in via a param.
761 // Obtain the param-operand via its specified index.
762 int64_t dynamicOptionIdx = paramOperand.getIndex().getInt();
763 assert(dynamicOptionIdx < static_cast<int64_t>(dynamicOptions.size()) &&
764 "the number of ParamOperandAttrs in the options DictionaryAttr"
765 "should be the same as the number of options passed as params");
766 ArrayRef<Attribute> attrsAssociatedToParam =
767 state.getParams(dynamicOptions[dynamicOptionIdx]);
768 // Recursive so as to append all attrs associated to the param.
769 llvm::interleave(attrsAssociatedToParam, optionsStream, appendValueAttr,
770 ",");
771 } else if (auto arrayAttr = dyn_cast<ArrayAttr>(valueAttr)) {
772 // Recursive so as to append all nested attrs of the array.
773 llvm::interleave(arrayAttr, optionsStream, appendValueAttr, ",");
774 } else if (auto strAttr = dyn_cast<StringAttr>(valueAttr)) {
775 // Convert to unquoted string.
776 optionsStream << strAttr.getValue().str();
777 } else {
778 // For all other attributes, ask the attr to print itself (without type).
779 valueAttr.print(optionsStream, /*elideType=*/true);
780 }
781 };
782
783 // Convert the options DictionaryAttr into a single string.
784 llvm::interleave(
785 getOptions(), optionsStream,
786 [&](auto namedAttribute) {
787 optionsStream << namedAttribute.getName().str(); // Append the key.
788 optionsStream << "="; // And the key-value separator.
789 appendValueAttr(namedAttribute.getValue()); // And the attr's str repr.
790 },
791 " ");
792 optionsStream.flush();
793
794 // Get pass or pass pipeline from registry.
795 const PassRegistryEntry *info = PassPipelineInfo::lookup(getPassName());
796 if (!info)
797 info = PassInfo::lookup(getPassName());
798 if (!info)
799 return emitDefiniteFailure()
800 << "unknown pass or pass pipeline: " << getPassName();
801
802 // Create pass manager and add the pass or pass pipeline.
804 if (failed(info->addToPipeline(pm, options, [&](const Twine &msg) {
805 emitError(msg);
806 return failure();
807 }))) {
808 return emitDefiniteFailure()
809 << "failed to add pass or pass pipeline to pipeline: "
810 << getPassName();
811 }
812
813 auto targets = SmallVector<Operation *>(state.getPayloadOps(getTarget()));
814 for (Operation *target : targets) {
815 // Make sure that this transform is not applied to itself. Modifying the
816 // transform IR while it is being interpreted is generally dangerous. Even
817 // more so when applying passes because they may perform a wide range of IR
818 // modifications.
819 DiagnosedSilenceableFailure payloadCheck =
821 if (!payloadCheck.succeeded())
822 return payloadCheck;
823
824 // Run the pass or pass pipeline on the current target operation.
825 if (failed(pm.run(target))) {
826 auto diag = emitSilenceableError() << "pass pipeline failed";
827 diag.attachNote(target->getLoc()) << "target op";
828 return diag;
829 }
830 }
831
832 // The applied pass will have directly modified the payload IR(s).
833 results.set(llvm::cast<OpResult>(getResult()), targets);
835}
836
838 OpAsmParser &parser, DictionaryAttr &options,
840 // Construct the options DictionaryAttr per a `{ key = value, ... }` syntax.
841 SmallVector<NamedAttribute> keyValuePairs;
842 size_t dynamicOptionsIdx = 0;
843
844 // Helper for allowing parsing of option values which can be of the form:
845 // - a normal attribute
846 // - an operand (which would be converted to an attr referring to the operand)
847 // - ArrayAttrs containing the foregoing (in correspondence with ListOptions)
848 std::function<ParseResult(Attribute &)> parseValue =
849 [&](Attribute &valueAttr) -> ParseResult {
850 // Allow for array syntax, e.g. `[0 : i64, %param, true, %other_param]`:
851 if (succeeded(parser.parseOptionalLSquare())) {
853
854 // Recursively parse the array's elements, which might be operands.
855 if (parser.parseCommaSeparatedList(
857 [&]() -> ParseResult { return parseValue(attrs.emplace_back()); },
858 " in options dictionary") ||
859 parser.parseRSquare())
860 return failure(); // NB: Attempted parse should've output error message.
861
862 valueAttr = ArrayAttr::get(parser.getContext(), attrs);
863
864 return success();
865 }
866
867 // Parse the value, which can be either an attribute or an operand.
868 OptionalParseResult parsedValueAttr =
869 parser.parseOptionalAttribute(valueAttr);
870 if (!parsedValueAttr.has_value()) {
872 ParseResult parsedOperand = parser.parseOperand(operand);
873 if (failed(parsedOperand))
874 return failure(); // NB: Attempted parse should've output error message.
875 // To make use of the operand, we need to store it in the options dict.
876 // As SSA-values cannot occur in attributes, what we do instead is store
877 // an attribute in its place that contains the index of the param-operand,
878 // so that an attr-value associated to the param can be resolved later on.
879 dynamicOptions.push_back(operand);
880 auto wrappedIndex = IntegerAttr::get(
881 IntegerType::get(parser.getContext(), 64), dynamicOptionsIdx++);
882 valueAttr =
883 transform::ParamOperandAttr::get(parser.getContext(), wrappedIndex);
884 } else if (failed(parsedValueAttr.value())) {
885 return failure(); // NB: Attempted parse should have output error message.
886 } else if (isa<transform::ParamOperandAttr>(valueAttr)) {
887 return parser.emitError(parser.getCurrentLocation())
888 << "the param_operand attribute is a marker reserved for "
889 << "indicating a value will be passed via params and is only used "
890 << "in the generic print format";
891 }
892
893 return success();
894 };
895
896 // Helper for `key = value`-pair parsing where `key` is a bare identifier or a
897 // string and `value` looks like either an attribute or an operand-in-an-attr.
898 std::function<ParseResult()> parseKeyValuePair = [&]() -> ParseResult {
899 std::string key;
900 Attribute valueAttr;
901
902 if (failed(parser.parseOptionalKeywordOrString(&key)) || key.empty())
903 return parser.emitError(parser.getCurrentLocation())
904 << "expected key to either be an identifier or a string";
905
906 if (failed(parser.parseEqual()))
907 return parser.emitError(parser.getCurrentLocation())
908 << "expected '=' after key in key-value pair";
909
910 if (failed(parseValue(valueAttr)))
911 return parser.emitError(parser.getCurrentLocation())
912 << "expected a valid attribute or operand as value associated "
913 << "to key '" << key << "'";
914
915 keyValuePairs.push_back(NamedAttribute(key, valueAttr));
916
917 return success();
918 };
919
922 " in options dictionary"))
923 return failure(); // NB: Attempted parse should have output error message.
924
925 if (DictionaryAttr::findDuplicate(
926 keyValuePairs, /*isSorted=*/false) // Also sorts the keyValuePairs.
927 .has_value())
928 return parser.emitError(parser.getCurrentLocation())
929 << "duplicate keys found in options dictionary";
930
931 options = DictionaryAttr::getWithSorted(parser.getContext(), keyValuePairs);
932
933 return success();
934}
935
937 Operation *op,
938 DictionaryAttr options,
939 ValueRange dynamicOptions) {
940 if (options.empty())
941 return;
942
943 std::function<void(Attribute)> printOptionValue = [&](Attribute valueAttr) {
944 if (auto paramOperandAttr =
945 dyn_cast<transform::ParamOperandAttr>(valueAttr)) {
946 // Resolve index of param-operand to its actual SSA-value and print that.
947 printer.printOperand(
948 dynamicOptions[paramOperandAttr.getIndex().getInt()]);
949 } else if (auto arrayAttr = dyn_cast<ArrayAttr>(valueAttr)) {
950 // This case is so that ArrayAttr-contained operands are pretty-printed.
951 printer << "[";
952 llvm::interleaveComma(arrayAttr, printer, printOptionValue);
953 printer << "]";
954 } else {
955 printer.printAttribute(valueAttr);
956 }
957 };
958
959 printer << "{";
960 llvm::interleaveComma(options, printer, [&](NamedAttribute namedAttribute) {
961 printer << namedAttribute.getName();
962 printer << " = ";
963 printOptionValue(namedAttribute.getValue());
964 });
965 printer << "}";
966}
967
968LogicalResult transform::ApplyRegisteredPassOp::verify() {
969 // Check that there is a one-to-one correspondence between param operands
970 // and references to dynamic options in the options dictionary.
971
972 auto dynamicOptions = SmallVector<Value>(getDynamicOptions());
973
974 // Helper for option values to mark seen operands as having been seen (once).
975 std::function<LogicalResult(Attribute)> checkOptionValue =
976 [&](Attribute valueAttr) -> LogicalResult {
977 if (auto paramOperand = dyn_cast<transform::ParamOperandAttr>(valueAttr)) {
978 int64_t dynamicOptionIdx = paramOperand.getIndex().getInt();
979 if (dynamicOptionIdx < 0 ||
980 dynamicOptionIdx >= static_cast<int64_t>(dynamicOptions.size()))
981 return emitOpError()
982 << "dynamic option index " << dynamicOptionIdx
983 << " is out of bounds for the number of dynamic options: "
984 << dynamicOptions.size();
985 if (dynamicOptions[dynamicOptionIdx] == nullptr)
986 return emitOpError() << "dynamic option index " << dynamicOptionIdx
987 << " is already used in options";
988 dynamicOptions[dynamicOptionIdx] = nullptr; // Mark this option as used.
989 } else if (auto arrayAttr = dyn_cast<ArrayAttr>(valueAttr)) {
990 // Recurse into ArrayAttrs as they may contain references to operands.
991 for (auto eltAttr : arrayAttr)
992 if (failed(checkOptionValue(eltAttr)))
993 return failure();
994 }
995 return success();
996 };
997
998 for (NamedAttribute namedAttr : getOptions())
999 if (failed(checkOptionValue(namedAttr.getValue())))
1000 return failure();
1001
1002 // All dynamicOptions-params seen in the dict will have been set to null.
1003 for (Value dynamicOption : dynamicOptions)
1004 if (dynamicOption)
1005 return emitOpError() << "a param operand does not have a corresponding "
1006 << "param_operand attr in the options dict";
1007
1008 return success();
1009}
1010
1011//===----------------------------------------------------------------------===//
1012// CastOp
1013//===----------------------------------------------------------------------===//
1014
1016transform::CastOp::applyToOne(transform::TransformRewriter &rewriter,
1017 Operation *target, ApplyToEachResultList &results,
1019 results.push_back(target);
1021}
1022
1023void transform::CastOp::getEffects(
1025 onlyReadsPayload(effects);
1026 onlyReadsHandle(getInputMutable(), effects);
1027 producesHandle(getOperation()->getOpResults(), effects);
1028}
1029
1030bool transform::CastOp::areCastCompatible(TypeRange inputs, TypeRange outputs) {
1031 assert(inputs.size() == 1 && "expected one input");
1032 assert(outputs.size() == 1 && "expected one output");
1033 return llvm::all_of(
1034 std::initializer_list<Type>{inputs.front(), outputs.front()},
1035 llvm::IsaPred<transform::TransformHandleTypeInterface>);
1036}
1037
1038//===----------------------------------------------------------------------===//
1039// CollectMatchingOp
1040//===----------------------------------------------------------------------===//
1041
1042/// Applies matcher operations from the given `block` using
1043/// `blockArgumentMapping` to initialize block arguments. Updates `state`
1044/// accordingly. If any of the matcher produces a silenceable failure, discards
1045/// it (printing the content to the debug output stream) and returns failure. If
1046/// any of the matchers produces a definite failure, reports it and returns
1047/// failure. If all matchers in the block succeed, populates `mappings` with the
1048/// payload entities associated with the block terminator operands. Note that
1049/// `mappings` will be cleared before that.
1052 ArrayRef<SmallVector<transform::MappedValue>> blockArgumentMapping,
1055 assert(block.getParent() && "cannot match using a detached block");
1056 auto matchScope = state.make_region_scope(*block.getParent());
1057 if (failed(
1058 state.mapBlockArguments(block.getArguments(), blockArgumentMapping)))
1060
1061 for (Operation &match : block.without_terminator()) {
1062 if (!isa<transform::MatchOpInterface>(match)) {
1063 return emitDefiniteFailure(match.getLoc())
1064 << "expected operations in the match part to "
1065 "implement MatchOpInterface";
1066 }
1068 state.applyTransform(cast<transform::TransformOpInterface>(match));
1069 if (diag.succeeded())
1070 continue;
1071
1072 return diag;
1073 }
1074
1075 // Remember the values mapped to the terminator operands so we can
1076 // forward them to the action.
1077 ValueRange yieldedValues = block.getTerminator()->getOperands();
1078 // Our contract with the caller is that the mappings will contain only the
1079 // newly mapped values, clear the rest.
1080 mappings.clear();
1081 transform::detail::prepareValueMappings(mappings, yieldedValues, state);
1083}
1084
1085/// Returns `true` if both types implement one of the interfaces provided as
1086/// template parameters.
1087template <typename... Tys>
1088static bool implementSameInterface(Type t1, Type t2) {
1089 return ((isa<Tys>(t1) && isa<Tys>(t2)) || ... || false);
1090}
1091
1092/// Returns `true` if both types implement one of the transform dialect
1093/// interfaces.
1095 return implementSameInterface<transform::TransformHandleTypeInterface,
1096 transform::TransformParamTypeInterface,
1097 transform::TransformValueHandleTypeInterface>(
1098 t1, t2);
1099}
1100
1101//===----------------------------------------------------------------------===//
1102// CollectMatchingOp
1103//===----------------------------------------------------------------------===//
1104
1106transform::CollectMatchingOp::apply(transform::TransformRewriter &rewriter,
1110 getOperation(), getMatcher());
1111 if (matcher.isExternal()) {
1112 return emitDefiniteFailure()
1113 << "unresolved external symbol " << getMatcher();
1114 }
1115
1117 rawResults.resize(getOperation()->getNumResults());
1118 std::optional<DiagnosedSilenceableFailure> maybeFailure;
1119 for (Operation *root : state.getPayloadOps(getRoot())) {
1120 WalkResult walkResult = root->walk([&](Operation *op) {
1121 LDBG(DEBUG_TYPE_MATCHER, 1)
1122 << "matching "
1123 << OpWithFlags(op, OpPrintingFlags().assumeVerified().skipRegions())
1124 << " @" << op;
1125
1126 // Try matching.
1128 SmallVector<transform::MappedValue> inputMapping({op});
1130 matcher.getFunctionBody().front(),
1131 ArrayRef<SmallVector<transform::MappedValue>>(inputMapping), state,
1132 mappings);
1133 if (diag.isDefiniteFailure())
1134 return WalkResult::interrupt();
1135 if (diag.isSilenceableFailure()) {
1136 LDBG(DEBUG_TYPE_MATCHER, 1) << "matcher " << matcher.getName()
1137 << " failed: " << diag.getMessage();
1138 return WalkResult::advance();
1139 }
1140
1141 // If succeeded, collect results.
1142 for (auto &&[i, mapping] : llvm::enumerate(mappings)) {
1143 if (mapping.size() != 1) {
1144 maybeFailure.emplace(emitSilenceableError()
1145 << "result #" << i << ", associated with "
1146 << mapping.size()
1147 << " payload objects, expected 1");
1148 return WalkResult::interrupt();
1149 }
1150 rawResults[i].push_back(mapping[0]);
1151 }
1152 return WalkResult::advance();
1153 });
1154 if (walkResult.wasInterrupted())
1155 return std::move(*maybeFailure);
1156 assert(!maybeFailure && "failure set but the walk was not interrupted");
1157
1158 for (auto &&[opResult, rawResult] :
1159 llvm::zip_equal(getOperation()->getResults(), rawResults)) {
1160 results.setMappedValues(opResult, rawResult);
1161 }
1162 }
1164}
1165
1166void transform::CollectMatchingOp::getEffects(
1168 onlyReadsHandle(getRootMutable(), effects);
1169 producesHandle(getOperation()->getOpResults(), effects);
1170 onlyReadsPayload(effects);
1171}
1172
1173LogicalResult transform::CollectMatchingOp::verifySymbolUses(
1174 SymbolTableCollection &symbolTable) {
1175 auto matcherSymbol = dyn_cast_or_null<FunctionOpInterface>(
1176 symbolTable.lookupNearestSymbolFrom(getOperation(), getMatcher()));
1177 if (!matcherSymbol ||
1178 !isa<TransformOpInterface>(matcherSymbol.getOperation()))
1179 return emitError() << "unresolved matcher symbol " << getMatcher();
1180
1181 ArrayRef<Type> argumentTypes = matcherSymbol.getArgumentTypes();
1182 if (argumentTypes.size() != 1 ||
1183 !isa<TransformHandleTypeInterface>(argumentTypes[0])) {
1184 return emitError()
1185 << "expected the matcher to take one operation handle argument";
1186 }
1187 if (!matcherSymbol.getArgAttr(
1188 0, transform::TransformDialect::kArgReadOnlyAttrName)) {
1189 return emitError() << "expected the matcher argument to be marked readonly";
1190 }
1191
1192 ArrayRef<Type> resultTypes = matcherSymbol.getResultTypes();
1193 if (resultTypes.size() != getOperation()->getNumResults()) {
1194 return emitError()
1195 << "expected the matcher to yield as many values as op has results ("
1196 << getOperation()->getNumResults() << "), got "
1197 << resultTypes.size();
1198 }
1199
1200 for (auto &&[i, matcherType, resultType] :
1201 llvm::enumerate(resultTypes, getOperation()->getResultTypes())) {
1202 if (implementSameTransformInterface(matcherType, resultType))
1203 continue;
1204
1205 return emitError()
1206 << "mismatching type interfaces for matcher result and op result #"
1207 << i;
1208 }
1209
1210 return success();
1211}
1212
1213//===----------------------------------------------------------------------===//
1214// ForeachMatchOp
1215//===----------------------------------------------------------------------===//
1216
1217// This is fine because nothing is actually consumed by this op.
1218bool transform::ForeachMatchOp::allowsRepeatedHandleOperands() { return true; }
1219
1221transform::ForeachMatchOp::apply(transform::TransformRewriter &rewriter,
1225 matchActionPairs;
1226 matchActionPairs.reserve(getMatchers().size());
1227 SymbolTableCollection symbolTable;
1228 for (auto &&[matcher, action] :
1229 llvm::zip_equal(getMatchers(), getActions())) {
1230 auto matcherSymbol =
1231 symbolTable.lookupNearestSymbolFrom<FunctionOpInterface>(
1232 getOperation(), cast<SymbolRefAttr>(matcher));
1233 auto actionSymbol =
1234 symbolTable.lookupNearestSymbolFrom<FunctionOpInterface>(
1235 getOperation(), cast<SymbolRefAttr>(action));
1236 assert(matcherSymbol && actionSymbol &&
1237 "unresolved symbols not caught by the verifier");
1238
1239 if (matcherSymbol.isExternal())
1240 return emitDefiniteFailure() << "unresolved external symbol " << matcher;
1241 if (actionSymbol.isExternal())
1242 return emitDefiniteFailure() << "unresolved external symbol " << action;
1243
1244 matchActionPairs.emplace_back(matcherSymbol, actionSymbol);
1245 }
1246
1247 DiagnosedSilenceableFailure overallDiag =
1249
1250 SmallVector<SmallVector<MappedValue>> matchInputMapping;
1251 SmallVector<SmallVector<MappedValue>> matchOutputMapping;
1252 SmallVector<SmallVector<MappedValue>> actionResultMapping;
1253 // Explicitly add the mapping for the first block argument (the op being
1254 // matched).
1255 matchInputMapping.emplace_back();
1257 getForwardedInputs(), state);
1258 SmallVector<MappedValue> &firstMatchArgument = matchInputMapping.front();
1259 actionResultMapping.resize(getForwardedOutputs().size());
1260
1261 for (Operation *root : state.getPayloadOps(getRoot())) {
1262 WalkResult walkResult = root->walk([&](Operation *op) {
1263 // If getRestrictRoot is not present, skip over the root op itself so we
1264 // don't invalidate it.
1265 if (!getRestrictRoot() && op == root)
1266 return WalkResult::advance();
1267
1268 LDBG(DEBUG_TYPE_MATCHER, 1)
1269 << "matching "
1270 << OpWithFlags(op, OpPrintingFlags().assumeVerified().skipRegions())
1271 << " @" << op;
1272
1273 firstMatchArgument.clear();
1274 firstMatchArgument.push_back(op);
1275
1276 // Try all the match/action pairs until the first successful match.
1277 for (auto [matcher, action] : matchActionPairs) {
1279 matchBlock(matcher.getFunctionBody().front(), matchInputMapping,
1280 state, matchOutputMapping);
1281 if (diag.isDefiniteFailure())
1282 return WalkResult::interrupt();
1283 if (diag.isSilenceableFailure()) {
1284 LDBG(DEBUG_TYPE_MATCHER, 1) << "matcher " << matcher.getName()
1285 << " failed: " << diag.getMessage();
1286 continue;
1287 }
1288
1289 auto scope = state.make_region_scope(action.getFunctionBody());
1290 if (failed(state.mapBlockArguments(
1291 action.getFunctionBody().front().getArguments(),
1292 matchOutputMapping))) {
1293 return WalkResult::interrupt();
1294 }
1295
1296 for (Operation &transform :
1297 action.getFunctionBody().front().without_terminator()) {
1299 state.applyTransform(cast<TransformOpInterface>(transform));
1300 if (result.isDefiniteFailure())
1301 return WalkResult::interrupt();
1302 if (result.isSilenceableFailure()) {
1303 if (overallDiag.succeeded()) {
1304 overallDiag = emitSilenceableError() << "actions failed";
1305 }
1306 overallDiag.attachNote(action->getLoc())
1307 << "failed action: " << result.getMessage();
1308 overallDiag.attachNote(op->getLoc())
1309 << "when applied to this matching payload";
1310 (void)result.silence();
1311 continue;
1312 }
1313 }
1314 if (failed(detail::appendValueMappings(
1315 MutableArrayRef<SmallVector<MappedValue>>(actionResultMapping),
1316 action.getFunctionBody().front().getTerminator()->getOperands(),
1317 state, getFlattenResults()))) {
1319 << "action @" << action.getName()
1320 << " has results associated with multiple payload entities, "
1321 "but flattening was not requested";
1322 return WalkResult::interrupt();
1323 }
1324 break;
1325 }
1326 return WalkResult::advance();
1327 });
1328 if (walkResult.wasInterrupted())
1330 }
1331
1332 // The root operation should not have been affected, so we can just reassign
1333 // the payload to the result. Note that we need to consume the root handle to
1334 // make sure any handles to operations inside, that could have been affected
1335 // by actions, are invalidated.
1336 results.set(llvm::cast<OpResult>(getUpdated()),
1337 state.getPayloadOps(getRoot()));
1338 for (auto &&[result, mapping] :
1339 llvm::zip_equal(getForwardedOutputs(), actionResultMapping)) {
1340 results.setMappedValues(result, mapping);
1341 }
1342 return overallDiag;
1343}
1344
1345void transform::ForeachMatchOp::getAsmResultNames(
1346 OpAsmSetValueNameFn setNameFn) {
1347 setNameFn(getUpdated(), "updated_root");
1348 for (Value v : getForwardedOutputs()) {
1349 setNameFn(v, "yielded");
1350 }
1351}
1352
1353void transform::ForeachMatchOp::getEffects(
1355 // Bail if invalid.
1356 if (getOperation()->getNumOperands() < 1 ||
1357 getOperation()->getNumResults() < 1) {
1358 return modifiesPayload(effects);
1359 }
1360
1361 consumesHandle(getRootMutable(), effects);
1362 onlyReadsHandle(getForwardedInputsMutable(), effects);
1363 producesHandle(getOperation()->getOpResults(), effects);
1364 modifiesPayload(effects);
1365}
1366
1367/// Parses the comma-separated list of symbol reference pairs of the format
1368/// `@matcher -> @action`.
1369static ParseResult parseForeachMatchSymbols(OpAsmParser &parser,
1371 ArrayAttr &actions) {
1372 StringAttr matcher;
1373 StringAttr action;
1374 SmallVector<Attribute> matcherList;
1375 SmallVector<Attribute> actionList;
1376 do {
1377 if (parser.parseSymbolName(matcher) || parser.parseArrow() ||
1378 parser.parseSymbolName(action)) {
1379 return failure();
1380 }
1381 matcherList.push_back(SymbolRefAttr::get(matcher));
1382 actionList.push_back(SymbolRefAttr::get(action));
1383 } while (parser.parseOptionalComma().succeeded());
1384
1385 matchers = parser.getBuilder().getArrayAttr(matcherList);
1386 actions = parser.getBuilder().getArrayAttr(actionList);
1387 return success();
1388}
1389
1390/// Prints the comma-separated list of symbol reference pairs of the format
1391/// `@matcher -> @action`.
1393 ArrayAttr matchers, ArrayAttr actions) {
1394 printer.increaseIndent();
1395 printer.increaseIndent();
1396 for (auto &&[matcher, action, idx] : llvm::zip_equal(
1397 matchers, actions, llvm::seq<unsigned>(0, matchers.size()))) {
1398 printer.printNewline();
1399 printer << cast<SymbolRefAttr>(matcher) << " -> "
1400 << cast<SymbolRefAttr>(action);
1401 if (idx != matchers.size() - 1)
1402 printer << ", ";
1403 }
1404 printer.decreaseIndent();
1405 printer.decreaseIndent();
1406}
1407
1408LogicalResult transform::ForeachMatchOp::verify() {
1409 if (getMatchers().size() != getActions().size())
1410 return emitOpError() << "expected the same number of matchers and actions";
1411 if (getMatchers().empty())
1412 return emitOpError() << "expected at least one match/action pair";
1413
1415 for (Attribute name : getMatchers()) {
1416 if (matcherNames.insert(name).second)
1417 continue;
1418 emitWarning() << "matcher " << name
1419 << " is used more than once, only the first match will apply";
1420 }
1421
1422 return success();
1423}
1424
1425/// Checks that the attributes of the function-like operation have correct
1426/// consumption effect annotations. If `alsoVerifyInternal`, checks for
1427/// annotations being present even if they can be inferred from the body.
1429verifyFunctionLikeConsumeAnnotations(FunctionOpInterface op, bool emitWarnings,
1430 bool alsoVerifyInternal = false) {
1431 auto transformOp = cast<transform::TransformOpInterface>(op.getOperation());
1432 llvm::SmallDenseSet<unsigned> consumedArguments;
1433 if (!op.isExternal()) {
1434 transform::getConsumedBlockArguments(op.getFunctionBody().front(),
1435 consumedArguments);
1436 }
1437 for (unsigned i = 0, e = op.getNumArguments(); i < e; ++i) {
1438 bool isConsumed =
1439 op.getArgAttr(i, transform::TransformDialect::kArgConsumedAttrName) !=
1440 nullptr;
1441 bool isReadOnly =
1442 op.getArgAttr(i, transform::TransformDialect::kArgReadOnlyAttrName) !=
1443 nullptr;
1444 if (isConsumed && isReadOnly) {
1445 return transformOp.emitSilenceableError()
1446 << "argument #" << i << " cannot be both readonly and consumed";
1447 }
1448 if ((op.isExternal() || alsoVerifyInternal) && !isConsumed && !isReadOnly) {
1449 return transformOp.emitSilenceableError()
1450 << "must provide consumed/readonly status for arguments of "
1451 "external or called ops";
1452 }
1453 if (op.isExternal())
1454 continue;
1455
1456 if (consumedArguments.contains(i) && !isConsumed && isReadOnly) {
1457 return transformOp.emitSilenceableError()
1458 << "argument #" << i
1459 << " is consumed in the body but is not marked as such";
1460 }
1461 if (emitWarnings && !consumedArguments.contains(i) && isConsumed) {
1462 // Cannot use op.emitWarning() here as it would attempt to verify the op
1463 // before printing, resulting in infinite recursion.
1464 emitWarning(op->getLoc())
1465 << "op argument #" << i
1466 << " is not consumed in the body but is marked as consumed";
1467 }
1468 }
1470}
1471
1472LogicalResult transform::ForeachMatchOp::verifySymbolUses(
1473 SymbolTableCollection &symbolTable) {
1474 assert(getMatchers().size() == getActions().size());
1475 auto consumedAttr =
1476 StringAttr::get(getContext(), TransformDialect::kArgConsumedAttrName);
1477 for (auto &&[matcher, action] :
1478 llvm::zip_equal(getMatchers(), getActions())) {
1479 // Presence and typing.
1480 auto matcherSymbol = dyn_cast_or_null<FunctionOpInterface>(
1481 symbolTable.lookupNearestSymbolFrom(getOperation(),
1482 cast<SymbolRefAttr>(matcher)));
1483 auto actionSymbol = dyn_cast_or_null<FunctionOpInterface>(
1484 symbolTable.lookupNearestSymbolFrom(getOperation(),
1485 cast<SymbolRefAttr>(action)));
1486 if (!matcherSymbol ||
1487 !isa<TransformOpInterface>(matcherSymbol.getOperation()))
1488 return emitError() << "unresolved matcher symbol " << matcher;
1489 if (!actionSymbol ||
1490 !isa<TransformOpInterface>(actionSymbol.getOperation()))
1491 return emitError() << "unresolved action symbol " << action;
1492
1494 /*emitWarnings=*/false,
1495 /*alsoVerifyInternal=*/true)
1496 .checkAndReport())) {
1497 return failure();
1498 }
1500 /*emitWarnings=*/false,
1501 /*alsoVerifyInternal=*/true)
1502 .checkAndReport())) {
1503 return failure();
1504 }
1505
1506 // Input -> matcher forwarding.
1507 TypeRange operandTypes = getOperandTypes();
1508 TypeRange matcherArguments = matcherSymbol.getArgumentTypes();
1509 if (operandTypes.size() != matcherArguments.size()) {
1511 emitError() << "the number of operands (" << operandTypes.size()
1512 << ") doesn't match the number of matcher arguments ("
1513 << matcherArguments.size() << ") for " << matcher;
1514 diag.attachNote(matcherSymbol->getLoc()) << "symbol declaration";
1515 return diag;
1516 }
1517 for (auto &&[i, operand, argument] :
1518 llvm::enumerate(operandTypes, matcherArguments)) {
1519 if (matcherSymbol.getArgAttr(i, consumedAttr)) {
1521 emitOpError()
1522 << "does not expect matcher symbol to consume its operand #" << i;
1523 diag.attachNote(matcherSymbol->getLoc()) << "symbol declaration";
1524 return diag;
1525 }
1526
1527 if (implementSameTransformInterface(operand, argument))
1528 continue;
1529
1531 emitError()
1532 << "mismatching type interfaces for operand and matcher argument #"
1533 << i << " of matcher " << matcher;
1534 diag.attachNote(matcherSymbol->getLoc()) << "symbol declaration";
1535 return diag;
1536 }
1537
1538 // Matcher -> action forwarding.
1539 TypeRange matcherResults = matcherSymbol.getResultTypes();
1540 TypeRange actionArguments = actionSymbol.getArgumentTypes();
1541 if (matcherResults.size() != actionArguments.size()) {
1542 return emitError() << "mismatching number of matcher results and "
1543 "action arguments between "
1544 << matcher << " (" << matcherResults.size() << ") and "
1545 << action << " (" << actionArguments.size() << ")";
1546 }
1547 for (auto &&[i, matcherType, actionType] :
1548 llvm::enumerate(matcherResults, actionArguments)) {
1549 if (implementSameTransformInterface(matcherType, actionType))
1550 continue;
1551
1552 return emitError() << "mismatching type interfaces for matcher result "
1553 "and action argument #"
1554 << i << "of matcher " << matcher << " and action "
1555 << action;
1556 }
1557
1558 // Action -> result forwarding.
1559 TypeRange actionResults = actionSymbol.getResultTypes();
1560 auto resultTypes = TypeRange(getResultTypes()).drop_front();
1561 if (actionResults.size() != resultTypes.size()) {
1563 emitError() << "the number of action results ("
1564 << actionResults.size() << ") for " << action
1565 << " doesn't match the number of extra op results ("
1566 << resultTypes.size() << ")";
1567 diag.attachNote(actionSymbol->getLoc()) << "symbol declaration";
1568 return diag;
1569 }
1570 for (auto &&[i, resultType, actionType] :
1571 llvm::enumerate(resultTypes, actionResults)) {
1572 if (implementSameTransformInterface(resultType, actionType))
1573 continue;
1574
1576 emitError() << "mismatching type interfaces for action result #" << i
1577 << " of action " << action << " and op result";
1578 diag.attachNote(actionSymbol->getLoc()) << "symbol declaration";
1579 return diag;
1580 }
1581 }
1582 return success();
1583}
1584
1585//===----------------------------------------------------------------------===//
1586// ForeachOp
1587//===----------------------------------------------------------------------===//
1588
1590transform::ForeachOp::apply(transform::TransformRewriter &rewriter,
1593 // We store the payloads before executing the body as ops may be removed from
1594 // the mapping by the TrackingRewriter while iteration is in progress.
1596 detail::prepareValueMappings(payloads, getTargets(), state);
1597 size_t numIterations = payloads.empty() ? 0 : payloads.front().size();
1598 bool withZipShortest = getWithZipShortest();
1599
1600 // In case of `zip_shortest`, set the number of iterations to the
1601 // smallest payload in the targets.
1602 if (withZipShortest) {
1603 numIterations =
1604 llvm::min_element(payloads, [&](const SmallVector<MappedValue> &a,
1605 const SmallVector<MappedValue> &b) {
1606 return a.size() < b.size();
1607 })->size();
1608
1609 for (auto &payload : payloads)
1610 payload.resize(numIterations);
1611 }
1612
1613 // As we will be "zipping" over them, check all payloads have the same size.
1614 // `zip_shortest` adjusts all payloads to the same size, so skip this check
1615 // when true.
1616 for (size_t argIdx = 1; !withZipShortest && argIdx < payloads.size();
1617 argIdx++) {
1618 if (payloads[argIdx].size() != numIterations) {
1619 return emitSilenceableError()
1620 << "prior targets' payload size (" << numIterations
1621 << ") differs from payload size (" << payloads[argIdx].size()
1622 << ") of target " << getTargets()[argIdx];
1623 }
1624 }
1625
1626 // Start iterating, indexing into payloads to obtain the right arguments to
1627 // call the body with - each slice of payloads at the same argument index
1628 // corresponding to a tuple to use as the body's block arguments.
1629 ArrayRef<BlockArgument> blockArguments = getBody().front().getArguments();
1630 SmallVector<SmallVector<MappedValue>> zippedResults(getNumResults(), {});
1631 for (size_t iterIdx = 0; iterIdx < numIterations; iterIdx++) {
1632 auto scope = state.make_region_scope(getBody());
1633 // Set up arguments to the region's block.
1634 for (auto &&[argIdx, blockArg] : llvm::enumerate(blockArguments)) {
1635 MappedValue argument = payloads[argIdx][iterIdx];
1636 // Note that each blockArg's handle gets associated with just a single
1637 // element from the corresponding target's payload.
1638 if (failed(state.mapBlockArgument(blockArg, {argument})))
1640 }
1641
1642 // Execute loop body.
1643 for (Operation &transform : getBody().front().without_terminator()) {
1645 llvm::cast<transform::TransformOpInterface>(transform));
1646 if (!result.succeeded())
1647 return result;
1648 }
1649
1650 // Append yielded payloads to corresponding results from prior iterations.
1651 OperandRange yieldOperands = getYieldOp().getOperands();
1652 for (auto &&[result, yieldOperand, resTuple] :
1653 llvm::zip_equal(getResults(), yieldOperands, zippedResults))
1654 // NB: each iteration we add any number of ops/vals/params to a result.
1655 if (isa<TransformHandleTypeInterface>(result.getType()))
1656 llvm::append_range(resTuple, state.getPayloadOps(yieldOperand));
1657 else if (isa<TransformValueHandleTypeInterface>(result.getType()))
1658 llvm::append_range(resTuple, state.getPayloadValues(yieldOperand));
1659 else if (isa<TransformParamTypeInterface>(result.getType()))
1660 llvm::append_range(resTuple, state.getParams(yieldOperand));
1661 else
1662 assert(false && "unhandled handle type");
1663 }
1664
1665 // Associate the accumulated result payloads to the op's actual results.
1666 for (auto &&[result, resPayload] : zip_equal(getResults(), zippedResults))
1667 results.setMappedValues(llvm::cast<OpResult>(result), resPayload);
1668
1670}
1671
1672void transform::ForeachOp::getEffects(
1674 // NB: this `zip` should be `zip_equal` - while this op's verifier catches
1675 // arity errors, this method might get called before/in absence of `verify()`.
1676 for (auto &&[target, blockArg] :
1677 llvm::zip(getTargetsMutable(), getBody().front().getArguments())) {
1678 BlockArgument blockArgument = blockArg;
1679 if (any_of(getBody().front().without_terminator(), [&](Operation &op) {
1680 return isHandleConsumed(blockArgument,
1681 cast<TransformOpInterface>(&op));
1682 })) {
1683 consumesHandle(target, effects);
1684 } else {
1685 onlyReadsHandle(target, effects);
1686 }
1687 }
1688
1689 if (any_of(getBody().front().without_terminator(), [&](Operation &op) {
1690 return doesModifyPayload(cast<TransformOpInterface>(&op));
1691 })) {
1692 modifiesPayload(effects);
1693 } else if (any_of(getBody().front().without_terminator(), [&](Operation &op) {
1694 return doesReadPayload(cast<TransformOpInterface>(&op));
1695 })) {
1696 onlyReadsPayload(effects);
1697 }
1698
1699 producesHandle(getOperation()->getOpResults(), effects);
1700}
1701
1702void transform::ForeachOp::getSuccessorRegions(
1704 Region *bodyRegion = &getBody();
1705 if (point.isParent()) {
1706 regions.emplace_back(bodyRegion);
1707 return;
1708 }
1709
1710 // Branch back to the region or the parent.
1711 assert(point.getTerminatorPredecessorOrNull()->getParentRegion() ==
1712 &getBody() &&
1713 "unexpected region index");
1714 regions.emplace_back(bodyRegion);
1715 regions.push_back(RegionSuccessor(getOperation()));
1716}
1717
1718ValueRange transform::ForeachOp::getSuccessorInputs(RegionSuccessor successor) {
1719 return successor.isOperation() ? ValueRange(getResults())
1720 : ValueRange(getBody().getArguments());
1721}
1722
1724transform::ForeachOp::getEntrySuccessorOperands(RegionSuccessor successor) {
1725 // Each block argument handle is mapped to a subset (one op to be precise)
1726 // of the payload of the corresponding `targets` operand of ForeachOp.
1727 assert(successor.getSuccessor() == &getBody() && "unexpected region index");
1728 return getOperation()->getOperands();
1729}
1730
1731transform::YieldOp transform::ForeachOp::getYieldOp() {
1732 return cast<transform::YieldOp>(getBody().front().getTerminator());
1733}
1734
1735LogicalResult transform::ForeachOp::verify() {
1736 for (auto [targetOpt, bodyArgOpt] :
1737 llvm::zip_longest(getTargets(), getBody().front().getArguments())) {
1738 if (!targetOpt || !bodyArgOpt)
1739 return emitOpError() << "expects the same number of targets as the body "
1740 "has block arguments";
1741 if (targetOpt.value().getType() != bodyArgOpt.value().getType())
1742 return emitOpError(
1743 "expects co-indexed targets and the body's "
1744 "block arguments to have the same op/value/param type");
1745 }
1746
1747 for (auto [resultOpt, yieldOperandOpt] :
1748 llvm::zip_longest(getResults(), getYieldOp().getOperands())) {
1749 if (!resultOpt || !yieldOperandOpt)
1750 return emitOpError() << "expects the same number of results as the "
1751 "yield terminator has operands";
1752 if (resultOpt.value().getType() != yieldOperandOpt.value().getType())
1753 return emitOpError("expects co-indexed results and yield "
1754 "operands to have the same op/value/param type");
1755 }
1756
1757 return success();
1758}
1759
1760//===----------------------------------------------------------------------===//
1761// GetParentOp
1762//===----------------------------------------------------------------------===//
1763
1765transform::GetParentOp::apply(transform::TransformRewriter &rewriter,
1769 DenseSet<Operation *> resultSet;
1770 for (Operation *target : state.getPayloadOps(getTarget())) {
1771 Operation *parent = target;
1772 for (int64_t i = 0, e = getNthParent(); i < e; ++i) {
1773 parent = parent->getParentOp();
1774 while (parent) {
1775 bool checkIsolatedFromAbove =
1776 !getIsolatedFromAbove() ||
1778 bool checkOpName = !getOpName().has_value() ||
1779 parent->getName().getStringRef() == *getOpName();
1780 if (checkIsolatedFromAbove && checkOpName)
1781 break;
1782 parent = parent->getParentOp();
1783 }
1784 if (!parent) {
1785 if (getAllowEmptyResults()) {
1786 results.set(llvm::cast<OpResult>(getResult()), parents);
1788 }
1790 emitSilenceableError()
1791 << "could not find a parent op that matches all requirements";
1792 diag.attachNote(target->getLoc()) << "target op";
1793 return diag;
1794 }
1795 }
1796 if (getDeduplicate()) {
1797 if (resultSet.insert(parent).second)
1798 parents.push_back(parent);
1799 } else {
1800 parents.push_back(parent);
1801 }
1802 }
1803 results.set(llvm::cast<OpResult>(getResult()), parents);
1805}
1806
1807//===----------------------------------------------------------------------===//
1808// GetConsumersOfResult
1809//===----------------------------------------------------------------------===//
1810
1812transform::GetConsumersOfResult::apply(transform::TransformRewriter &rewriter,
1815 int64_t resultNumber = getResultNumber();
1816 auto payloadOps = state.getPayloadOps(getTarget());
1817 if (std::empty(payloadOps)) {
1818 results.set(cast<OpResult>(getResult()), {});
1820 }
1821 if (!llvm::hasSingleElement(payloadOps))
1822 return emitDefiniteFailure()
1823 << "handle must be mapped to exactly one payload op";
1824
1825 Operation *target = *payloadOps.begin();
1826 if (target->getNumResults() <= resultNumber)
1827 return emitDefiniteFailure() << "result number overflow";
1828 results.set(llvm::cast<OpResult>(getResult()),
1829 llvm::to_vector(target->getResult(resultNumber).getUsers()));
1831}
1832
1833//===----------------------------------------------------------------------===//
1834// GetDefiningOp
1835//===----------------------------------------------------------------------===//
1836
1838transform::GetDefiningOp::apply(transform::TransformRewriter &rewriter,
1841 SmallVector<Operation *> definingOps;
1842 for (Value v : state.getPayloadValues(getTarget())) {
1843 if (llvm::isa<BlockArgument>(v)) {
1845 emitSilenceableError() << "cannot get defining op of block argument";
1846 diag.attachNote(v.getLoc()) << "target value";
1847 return diag;
1848 }
1849 definingOps.push_back(v.getDefiningOp());
1850 }
1851 results.set(llvm::cast<OpResult>(getResult()), definingOps);
1853}
1854
1855//===----------------------------------------------------------------------===//
1856// GetProducerOfOperand
1857//===----------------------------------------------------------------------===//
1858
1860transform::GetProducerOfOperand::apply(transform::TransformRewriter &rewriter,
1863 int64_t operandNumber = getOperandNumber();
1864 SmallVector<Operation *> producers;
1865 for (Operation *target : state.getPayloadOps(getTarget())) {
1866 Operation *producer =
1867 target->getNumOperands() <= operandNumber
1868 ? nullptr
1869 : target->getOperand(operandNumber).getDefiningOp();
1870 if (!producer) {
1872 emitSilenceableError()
1873 << "could not find a producer for operand number: " << operandNumber
1874 << " of " << *target;
1875 diag.attachNote(target->getLoc()) << "target op";
1876 return diag;
1877 }
1878 producers.push_back(producer);
1879 }
1880 results.set(llvm::cast<OpResult>(getResult()), producers);
1882}
1883
1884//===----------------------------------------------------------------------===//
1885// GetOperandOp
1886//===----------------------------------------------------------------------===//
1887
1889transform::GetOperandOp::apply(transform::TransformRewriter &rewriter,
1892 SmallVector<Value> operands;
1893 for (Operation *target : state.getPayloadOps(getTarget())) {
1894 SmallVector<int64_t> operandPositions;
1896 getLoc(), getIsAll(), getIsInverted(), getRawPositionList(),
1897 target->getNumOperands(), operandPositions);
1898 if (diag.isSilenceableFailure()) {
1899 diag.attachNote(target->getLoc())
1900 << "while considering positions of this payload operation";
1901 return diag;
1902 }
1903 llvm::append_range(operands,
1904 llvm::map_range(operandPositions, [&](int64_t pos) {
1905 return target->getOperand(pos);
1906 }));
1907 }
1908 results.setValues(cast<OpResult>(getResult()), operands);
1910}
1911
1912LogicalResult transform::GetOperandOp::verify() {
1913 return verifyTransformMatchDimsOp(getOperation(), getRawPositionList(),
1914 getIsInverted(), getIsAll());
1915}
1916
1917//===----------------------------------------------------------------------===//
1918// GetResultOp
1919//===----------------------------------------------------------------------===//
1920
1922transform::GetResultOp::apply(transform::TransformRewriter &rewriter,
1925 SmallVector<Value> opResults;
1926 for (Operation *target : state.getPayloadOps(getTarget())) {
1927 SmallVector<int64_t> resultPositions;
1929 getLoc(), getIsAll(), getIsInverted(), getRawPositionList(),
1930 target->getNumResults(), resultPositions);
1931 if (diag.isSilenceableFailure()) {
1932 diag.attachNote(target->getLoc())
1933 << "while considering positions of this payload operation";
1934 return diag;
1935 }
1936 llvm::append_range(opResults,
1937 llvm::map_range(resultPositions, [&](int64_t pos) {
1938 return target->getResult(pos);
1939 }));
1940 }
1941 results.setValues(cast<OpResult>(getResult()), opResults);
1943}
1944
1945LogicalResult transform::GetResultOp::verify() {
1946 return verifyTransformMatchDimsOp(getOperation(), getRawPositionList(),
1947 getIsInverted(), getIsAll());
1948}
1949
1950//===----------------------------------------------------------------------===//
1951// GetTypeOp
1952//===----------------------------------------------------------------------===//
1953
1954void transform::GetTypeOp::getEffects(
1956 onlyReadsHandle(getValueMutable(), effects);
1957 producesHandle(getOperation()->getOpResults(), effects);
1958 onlyReadsPayload(effects);
1959}
1960
1962transform::GetTypeOp::apply(transform::TransformRewriter &rewriter,
1966 for (Value value : state.getPayloadValues(getValue())) {
1967 Type type = value.getType();
1968 if (getElemental()) {
1969 if (auto shaped = dyn_cast<ShapedType>(type)) {
1970 type = shaped.getElementType();
1971 }
1972 }
1973 params.push_back(TypeAttr::get(type));
1974 }
1975 results.setParams(cast<OpResult>(getResult()), params);
1977}
1978
1979//===----------------------------------------------------------------------===//
1980// IncludeOp
1981//===----------------------------------------------------------------------===//
1982
1983/// Applies the transform ops contained in `block`. Maps `results` to the same
1984/// values as the operands of the block terminator.
1986applySequenceBlock(Block &block, transform::FailurePropagationMode mode,
1988 transform::TransformResults &results) {
1989 // Apply the sequenced ops one by one.
1990 for (Operation &transform : block.without_terminator()) {
1992 state.applyTransform(cast<transform::TransformOpInterface>(transform));
1993 if (result.isDefiniteFailure())
1994 return result;
1995
1996 if (result.isSilenceableFailure()) {
1997 if (mode == transform::FailurePropagationMode::Propagate) {
1998 // Propagate empty results in case of early exit.
1999 forwardEmptyOperands(&block, state, results);
2000 return result;
2001 }
2002 (void)result.silence();
2003 }
2004 }
2005
2006 // Forward the operation mapping for values yielded from the sequence to the
2007 // values produced by the sequence op.
2008 transform::detail::forwardTerminatorOperands(&block, state, results);
2010}
2011
2013transform::IncludeOp::apply(transform::TransformRewriter &rewriter,
2017 getOperation(), getTarget());
2018 assert(callee && "unverified reference to unknown symbol");
2019
2020 if (callee.isExternal())
2021 return emitDefiniteFailure() << "unresolved external named sequence";
2022
2023 // Map operands to block arguments.
2025 detail::prepareValueMappings(mappings, getOperands(), state);
2026 auto scope = state.make_region_scope(callee.getBody());
2027 for (auto &&[arg, map] :
2028 llvm::zip_equal(callee.getBody().front().getArguments(), mappings)) {
2029 if (failed(state.mapBlockArgument(arg, map)))
2031 }
2032
2034 callee.getBody().front(), getFailurePropagationMode(), state, results);
2035
2036 if (!result.succeeded())
2037 return result;
2038
2039 mappings.clear();
2040 detail::prepareValueMappings(
2041 mappings, callee.getBody().front().getTerminator()->getOperands(), state);
2042 for (auto &&[result, mapping] : llvm::zip_equal(getResults(), mappings))
2043 results.setMappedValues(result, mapping);
2044 return result;
2045}
2046
2048verifyNamedSequenceOp(transform::NamedSequenceOp op, bool emitWarnings);
2049
2050void transform::IncludeOp::getEffects(
2052 // Always mark as modifying the payload.
2053 // TODO: a mechanism to annotate effects on payload. Even when all handles are
2054 // only read, the payload may still be modified, so we currently stay on the
2055 // conservative side and always indicate modification. This may prevent some
2056 // code reordering.
2057 modifiesPayload(effects);
2058
2059 // Results are always produced.
2060 producesHandle(getOperation()->getOpResults(), effects);
2061
2062 // Adds default effects to operands and results. This will be added if
2063 // preconditions fail so the trait verifier doesn't complain about missing
2064 // effects and the real precondition failure is reported later on.
2065 auto defaultEffects = [&] {
2066 onlyReadsHandle(getOperation()->getOpOperands(), effects);
2067 };
2068
2069 // Bail if the callee is unknown. This may run as part of the verification
2070 // process before we verified the validity of the callee or of this op.
2071 auto target =
2072 getOperation()->getAttrOfType<SymbolRefAttr>(getTargetAttrName());
2073 if (!target)
2074 return defaultEffects();
2076 getOperation(), getTarget());
2077 if (!callee)
2078 return defaultEffects();
2079
2080 for (unsigned i = 0, e = getNumOperands(); i < e; ++i) {
2081 if (callee.getArgAttr(i, TransformDialect::kArgConsumedAttrName))
2082 consumesHandle(getOperation()->getOpOperand(i), effects);
2083 else if (callee.getArgAttr(i, TransformDialect::kArgReadOnlyAttrName))
2084 onlyReadsHandle(getOperation()->getOpOperand(i), effects);
2085 }
2086}
2087
2088LogicalResult
2089transform::IncludeOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
2090 // Access through indirection and do additional checking because this may be
2091 // running before the main op verifier.
2092 auto targetAttr = getOperation()->getAttrOfType<SymbolRefAttr>("target");
2093 if (!targetAttr)
2094 return emitOpError() << "expects a 'target' symbol reference attribute";
2095
2096 auto target = symbolTable.lookupNearestSymbolFrom<transform::NamedSequenceOp>(
2097 *this, targetAttr);
2098 if (!target)
2099 return emitOpError() << "does not reference a named transform sequence";
2100
2101 FunctionType fnType = target.getFunctionType();
2102 if (fnType.getNumInputs() != getNumOperands())
2103 return emitError("incorrect number of operands for callee");
2104
2105 for (unsigned i = 0, e = fnType.getNumInputs(); i != e; ++i) {
2106 if (getOperand(i).getType() != fnType.getInput(i)) {
2107 return emitOpError("operand type mismatch: expected operand type ")
2108 << fnType.getInput(i) << ", but provided "
2109 << getOperand(i).getType() << " for operand number " << i;
2110 }
2111 }
2112
2113 if (fnType.getNumResults() != getNumResults())
2114 return emitError("incorrect number of results for callee");
2115
2116 for (unsigned i = 0, e = fnType.getNumResults(); i != e; ++i) {
2117 Type resultType = getResult(i).getType();
2118 Type funcType = fnType.getResult(i);
2119 if (!implementSameTransformInterface(resultType, funcType)) {
2120 return emitOpError() << "type of result #" << i
2121 << " must implement the same transform dialect "
2122 "interface as the corresponding callee result";
2123 }
2124 }
2125
2127 cast<FunctionOpInterface>(*target), /*emitWarnings=*/false,
2128 /*alsoVerifyInternal=*/true)
2129 .checkAndReport();
2130}
2131
2132//===----------------------------------------------------------------------===//
2133// MatchOperationEmptyOp
2134//===----------------------------------------------------------------------===//
2135
2136DiagnosedSilenceableFailure transform::MatchOperationEmptyOp::matchOperation(
2137 ::std::optional<::mlir::Operation *> maybeCurrent,
2139 if (!maybeCurrent.has_value()) {
2140 LDBG(DEBUG_TYPE_MATCHER, 1) << "MatchOperationEmptyOp success";
2142 }
2143 LDBG(DEBUG_TYPE_MATCHER, 1) << "MatchOperationEmptyOp failure";
2144 return emitSilenceableError() << "operation is not empty";
2145}
2146
2147//===----------------------------------------------------------------------===//
2148// MatchOperationNameOp
2149//===----------------------------------------------------------------------===//
2150
2151DiagnosedSilenceableFailure transform::MatchOperationNameOp::matchOperation(
2152 Operation *current, transform::TransformResults &results,
2154 StringRef currentOpName = current->getName().getStringRef();
2155 for (auto acceptedAttr : getOpNames().getAsRange<StringAttr>()) {
2156 if (acceptedAttr.getValue() == currentOpName)
2158 }
2159 return emitSilenceableError() << "wrong operation name";
2160}
2161
2162//===----------------------------------------------------------------------===//
2163// MatchParamCmpIOp
2164//===----------------------------------------------------------------------===//
2165
2167transform::MatchParamCmpIOp::apply(transform::TransformRewriter &rewriter,
2170 auto signedAPIntAsString = [&](const APInt &value) {
2171 std::string str;
2172 llvm::raw_string_ostream os(str);
2173 value.print(os, /*isSigned=*/true);
2174 return str;
2175 };
2176
2177 ArrayRef<Attribute> params = state.getParams(getParam());
2178 ArrayRef<Attribute> references = state.getParams(getReference());
2179
2180 if (params.size() != references.size()) {
2181 return emitSilenceableError()
2182 << "parameters have different payload lengths (" << params.size()
2183 << " vs " << references.size() << ")";
2184 }
2185
2186 for (auto &&[i, param, reference] : llvm::enumerate(params, references)) {
2187 auto intAttr = llvm::dyn_cast<IntegerAttr>(param);
2188 auto refAttr = llvm::dyn_cast<IntegerAttr>(reference);
2189 if (!intAttr || !refAttr) {
2190 return emitDefiniteFailure()
2191 << "non-integer parameter value not expected";
2192 }
2193 if (intAttr.getType() != refAttr.getType()) {
2194 return emitDefiniteFailure()
2195 << "mismatching integer attribute types in parameter #" << i;
2196 }
2197 APInt value = intAttr.getValue();
2198 APInt refValue = refAttr.getValue();
2199
2200 // TODO: this copy will not be necessary in C++20.
2201 int64_t position = i;
2202 auto reportError = [&](StringRef direction) {
2204 emitSilenceableError() << "expected parameter to be " << direction
2205 << " " << signedAPIntAsString(refValue)
2206 << ", got " << signedAPIntAsString(value);
2207 diag.attachNote(getParam().getLoc())
2208 << "value # " << position
2209 << " associated with the parameter defined here";
2210 return diag;
2211 };
2212
2213 switch (getPredicate()) {
2214 case MatchCmpIPredicate::eq:
2215 if (value.eq(refValue))
2216 break;
2217 return reportError("equal to");
2218 case MatchCmpIPredicate::ne:
2219 if (value.ne(refValue))
2220 break;
2221 return reportError("not equal to");
2222 case MatchCmpIPredicate::lt:
2223 if (value.slt(refValue))
2224 break;
2225 return reportError("less than");
2226 case MatchCmpIPredicate::le:
2227 if (value.sle(refValue))
2228 break;
2229 return reportError("less than or equal to");
2230 case MatchCmpIPredicate::gt:
2231 if (value.sgt(refValue))
2232 break;
2233 return reportError("greater than");
2234 case MatchCmpIPredicate::ge:
2235 if (value.sge(refValue))
2236 break;
2237 return reportError("greater than or equal to");
2238 }
2239 }
2241}
2242
2243void transform::MatchParamCmpIOp::getEffects(
2245 onlyReadsHandle(getParamMutable(), effects);
2246 onlyReadsHandle(getReferenceMutable(), effects);
2247}
2248
2249//===----------------------------------------------------------------------===//
2250// ParamConstantOp
2251//===----------------------------------------------------------------------===//
2252
2254transform::ParamConstantOp::apply(transform::TransformRewriter &rewriter,
2257 results.setParams(cast<OpResult>(getParam()), {getValue()});
2259}
2260
2261//===----------------------------------------------------------------------===//
2262// MergeHandlesOp
2263//===----------------------------------------------------------------------===//
2264
2266transform::MergeHandlesOp::apply(transform::TransformRewriter &rewriter,
2269 ValueRange handles = getHandles();
2270 if (isa<TransformHandleTypeInterface>(handles.front().getType())) {
2271 SmallVector<Operation *> operations;
2272 for (Value operand : handles)
2273 llvm::append_range(operations, state.getPayloadOps(operand));
2274 if (!getDeduplicate()) {
2275 results.set(llvm::cast<OpResult>(getResult()), operations);
2277 }
2278
2279 SetVector<Operation *> uniqued(llvm::from_range, operations);
2280 results.set(llvm::cast<OpResult>(getResult()), uniqued.getArrayRef());
2282 }
2283
2284 if (llvm::isa<TransformParamTypeInterface>(handles.front().getType())) {
2286 for (Value attribute : handles)
2287 llvm::append_range(attrs, state.getParams(attribute));
2288 if (!getDeduplicate()) {
2289 results.setParams(cast<OpResult>(getResult()), attrs);
2291 }
2292
2293 SetVector<Attribute> uniqued(llvm::from_range, attrs);
2294 results.setParams(cast<OpResult>(getResult()), uniqued.getArrayRef());
2296 }
2297
2298 assert(
2299 llvm::isa<TransformValueHandleTypeInterface>(handles.front().getType()) &&
2300 "expected value handle type");
2301 SmallVector<Value> payloadValues;
2302 for (Value value : handles)
2303 llvm::append_range(payloadValues, state.getPayloadValues(value));
2304 if (!getDeduplicate()) {
2305 results.setValues(cast<OpResult>(getResult()), payloadValues);
2307 }
2308
2309 SetVector<Value> uniqued(llvm::from_range, payloadValues);
2310 results.setValues(cast<OpResult>(getResult()), uniqued.getArrayRef());
2312}
2313
2314bool transform::MergeHandlesOp::allowsRepeatedHandleOperands() {
2315 // Handles may be the same if deduplicating is enabled.
2316 return getDeduplicate();
2317}
2318
2319void transform::MergeHandlesOp::getEffects(
2321 onlyReadsHandle(getHandlesMutable(), effects);
2322 producesHandle(getOperation()->getOpResults(), effects);
2323
2324 // There are no effects on the Payload IR as this is only a handle
2325 // manipulation.
2326}
2327
2328OpFoldResult transform::MergeHandlesOp::fold(FoldAdaptor adaptor) {
2329 if (getDeduplicate() || getHandles().size() != 1)
2330 return {};
2331
2332 // If deduplication is not required and there is only one operand, it can be
2333 // used directly instead of merging.
2334 return getHandles().front();
2335}
2336
2337//===----------------------------------------------------------------------===//
2338// NamedSequenceOp
2339//===----------------------------------------------------------------------===//
2340
2342transform::NamedSequenceOp::apply(transform::TransformRewriter &rewriter,
2345 if (isExternal())
2346 return emitDefiniteFailure() << "unresolved external named sequence";
2347
2348 // Map the entry block argument to the list of operations.
2349 // Note: this is the same implementation as PossibleTopLevelTransformOp but
2350 // without attaching the interface / trait since that is tailored to a
2351 // dangling top-level op that does not get "called".
2352 auto scope = state.make_region_scope(getBody());
2353 if (failed(detail::mapPossibleTopLevelTransformOpBlockArguments(
2354 state, this->getOperation(), getBody())))
2356
2357 return applySequenceBlock(getBody().front(),
2358 FailurePropagationMode::Propagate, state, results);
2359}
2360
2361void transform::NamedSequenceOp::getEffects(
2363
2364ParseResult transform::NamedSequenceOp::parse(OpAsmParser &parser,
2367 parser, result, /*allowVariadic=*/false,
2368 getFunctionTypeAttrName(result.name),
2369 [](Builder &builder, ArrayRef<Type> inputs, ArrayRef<Type> results,
2371 std::string &) { return builder.getFunctionType(inputs, results); },
2372 getArgAttrsAttrName(result.name), getResAttrsAttrName(result.name));
2373}
2374
2375void transform::NamedSequenceOp::print(OpAsmPrinter &printer) {
2377 printer, cast<FunctionOpInterface>(getOperation()), /*isVariadic=*/false,
2378 getFunctionTypeAttrName().getValue(), getArgAttrsAttrName(),
2379 getResAttrsAttrName());
2380}
2381
2382/// Verifies that a symbol function-like transform dialect operation has the
2383/// signature and the terminator that have conforming types, i.e., types
2384/// implementing the same transform dialect type interface. If `allowExternal`
2385/// is set, allow external symbols (declarations) and don't check the terminator
2386/// as it may not exist.
2388verifyYieldingSingleBlockOp(FunctionOpInterface op, bool allowExternal) {
2389 if (auto parent = op->getParentOfType<transform::TransformOpInterface>()) {
2392 << "cannot be defined inside another transform op";
2393 diag.attachNote(parent.getLoc()) << "ancestor transform op";
2394 return diag;
2395 }
2396
2397 if (op.isExternal() || op.getFunctionBody().empty()) {
2398 if (allowExternal)
2400
2401 return emitSilenceableFailure(op) << "cannot be external";
2402 }
2403
2404 if (op.getFunctionBody().front().empty())
2405 return emitSilenceableFailure(op) << "expected a non-empty body block";
2406
2407 Operation *terminator = &op.getFunctionBody().front().back();
2408 if (!isa<transform::YieldOp>(terminator)) {
2410 << "expected '"
2411 << transform::YieldOp::getOperationName()
2412 << "' as terminator";
2413 diag.attachNote(terminator->getLoc()) << "terminator";
2414 return diag;
2415 }
2416
2417 if (terminator->getNumOperands() != op.getResultTypes().size()) {
2418 return emitSilenceableFailure(terminator)
2419 << "expected terminator to have as many operands as the parent op "
2420 "has results";
2421 }
2422 for (auto [i, operandType, resultType] : llvm::zip_equal(
2423 llvm::seq<unsigned>(0, terminator->getNumOperands()),
2424 terminator->getOperands().getType(), op.getResultTypes())) {
2425 if (operandType == resultType)
2426 continue;
2427 return emitSilenceableFailure(terminator)
2428 << "the type of the terminator operand #" << i
2429 << " must match the type of the corresponding parent op result ("
2430 << operandType << " vs " << resultType << ")";
2431 }
2432
2434}
2435
2436/// Verification of a NamedSequenceOp. This does not report the error
2437/// immediately, so it can be used to check for op's well-formedness before the
2438/// verifier runs, e.g., during trait verification.
2440verifyNamedSequenceOp(transform::NamedSequenceOp op, bool emitWarnings) {
2441 if (Operation *parent = op->getParentWithTrait<OpTrait::SymbolTable>()) {
2442 if (!parent->getAttr(
2443 transform::TransformDialect::kWithNamedSequenceAttrName)) {
2446 << "expects the parent symbol table to have the '"
2447 << transform::TransformDialect::kWithNamedSequenceAttrName
2448 << "' attribute";
2449 diag.attachNote(parent->getLoc()) << "symbol table operation";
2450 return diag;
2451 }
2452 }
2453
2454 if (auto parent = op->getParentOfType<transform::TransformOpInterface>()) {
2457 << "cannot be defined inside another transform op";
2458 diag.attachNote(parent.getLoc()) << "ancestor transform op";
2459 return diag;
2460 }
2461
2462 if (op.isExternal() || op.getBody().empty())
2463 return verifyFunctionLikeConsumeAnnotations(cast<FunctionOpInterface>(*op),
2464 emitWarnings);
2465
2466 if (op.getBody().front().empty())
2467 return emitSilenceableFailure(op) << "expected a non-empty body block";
2468
2469 // Check that all operations in the body implement TransformOpInterface
2470 for (Operation &child : op.getBody().front().without_terminator()) {
2471 if (!isa<transform::TransformOpInterface>(child)) {
2474 << "expected children ops to implement TransformOpInterface";
2475 diag.attachNote(child.getLoc()) << "op without interface";
2476 return diag;
2477 }
2478 }
2479
2480 Operation *terminator = &op.getBody().front().back();
2481 if (!isa<transform::YieldOp>(terminator)) {
2483 << "expected '"
2484 << transform::YieldOp::getOperationName()
2485 << "' as terminator";
2486 diag.attachNote(terminator->getLoc()) << "terminator";
2487 return diag;
2488 }
2489
2490 if (terminator->getNumOperands() != op.getFunctionType().getNumResults()) {
2491 return emitSilenceableFailure(terminator)
2492 << "expected terminator to have as many operands as the parent op "
2493 "has results";
2494 }
2495 for (auto [i, operandType, resultType] :
2496 llvm::zip_equal(llvm::seq<unsigned>(0, terminator->getNumOperands()),
2497 terminator->getOperands().getType(),
2498 op.getFunctionType().getResults())) {
2499 if (operandType == resultType)
2500 continue;
2501 return emitSilenceableFailure(terminator)
2502 << "the type of the terminator operand #" << i
2503 << " must match the type of the corresponding parent op result ("
2504 << operandType << " vs " << resultType << ")";
2505 }
2506
2507 auto funcOp = cast<FunctionOpInterface>(*op);
2509 verifyFunctionLikeConsumeAnnotations(funcOp, emitWarnings);
2510 if (!diag.succeeded())
2511 return diag;
2512
2513 return verifyYieldingSingleBlockOp(funcOp,
2514 /*allowExternal=*/true);
2515}
2516
2517LogicalResult transform::NamedSequenceOp::verify() {
2518 // Actual verification happens in a separate function for reusability.
2519 return verifyNamedSequenceOp(*this, /*emitWarnings=*/true).checkAndReport();
2520}
2521
2522template <typename FnTy>
2523static void buildSequenceBody(OpBuilder &builder, OperationState &state,
2524 Type bbArgType, TypeRange extraBindingTypes,
2525 FnTy bodyBuilder) {
2526 SmallVector<Type> types;
2527 types.reserve(1 + extraBindingTypes.size());
2528 types.push_back(bbArgType);
2529 llvm::append_range(types, extraBindingTypes);
2530
2531 OpBuilder::InsertionGuard guard(builder);
2532 Region *region = state.regions.back().get();
2533 Block *bodyBlock =
2534 builder.createBlock(region, region->begin(), types,
2535 SmallVector<Location>(types.size(), state.location));
2536
2537 // Populate body.
2538 builder.setInsertionPointToStart(bodyBlock);
2539 if constexpr (llvm::function_traits<FnTy>::num_args == 3) {
2540 bodyBuilder(builder, state.location, bodyBlock->getArgument(0));
2541 } else {
2542 bodyBuilder(builder, state.location, bodyBlock->getArgument(0),
2543 bodyBlock->getArguments().drop_front());
2544 }
2545}
2546
2547void transform::NamedSequenceOp::build(OpBuilder &builder,
2548 OperationState &state, StringRef symName,
2549 Type rootType, TypeRange resultTypes,
2550 SequenceBodyBuilderFn bodyBuilder,
2552 ArrayRef<DictionaryAttr> argAttrs) {
2554 builder.getStringAttr(symName));
2555 state.addAttribute(getFunctionTypeAttrName(state.name),
2556 TypeAttr::get(FunctionType::get(builder.getContext(),
2557 rootType, resultTypes)));
2558 state.attributes.append(attrs.begin(), attrs.end());
2559 state.addRegion();
2560
2561 buildSequenceBody(builder, state, rootType,
2562 /*extraBindingTypes=*/TypeRange(), bodyBuilder);
2563}
2564
2565//===----------------------------------------------------------------------===//
2566// NumAssociationsOp
2567//===----------------------------------------------------------------------===//
2568
2570transform::NumAssociationsOp::apply(transform::TransformRewriter &rewriter,
2573 size_t numAssociations =
2575 .Case([&](TransformHandleTypeInterface opHandle) {
2576 return llvm::range_size(state.getPayloadOps(getHandle()));
2577 })
2578 .Case([&](TransformValueHandleTypeInterface valueHandle) {
2579 return llvm::range_size(state.getPayloadValues(getHandle()));
2580 })
2581 .Case([&](TransformParamTypeInterface param) {
2582 return llvm::range_size(state.getParams(getHandle()));
2583 })
2584 .DefaultUnreachable("unknown kind of transform dialect type");
2585 results.setParams(cast<OpResult>(getNum()),
2586 rewriter.getI64IntegerAttr(numAssociations));
2588}
2589
2590LogicalResult transform::NumAssociationsOp::verify() {
2591 // Verify that the result type accepts an i64 attribute as payload.
2592 auto resultType = cast<TransformParamTypeInterface>(getNum().getType());
2593 return resultType
2594 .checkPayload(getLoc(), {Builder(getContext()).getI64IntegerAttr(0)})
2595 .checkAndReport();
2596}
2597
2598//===----------------------------------------------------------------------===//
2599// SelectOp
2600//===----------------------------------------------------------------------===//
2601
2603transform::SelectOp::apply(transform::TransformRewriter &rewriter,
2607 auto payloadOps = state.getPayloadOps(getTarget());
2608 for (Operation *op : payloadOps) {
2609 if (op->getName().getStringRef() == getOpName())
2610 result.push_back(op);
2611 }
2612 results.set(cast<OpResult>(getResult()), result);
2614}
2615
2616//===----------------------------------------------------------------------===//
2617// SplitHandleOp
2618//===----------------------------------------------------------------------===//
2619
2620void transform::SplitHandleOp::build(OpBuilder &builder, OperationState &result,
2621 Value target, int64_t numResultHandles) {
2622 result.addOperands(target);
2623 result.addTypes(SmallVector<Type>(numResultHandles, target.getType()));
2624}
2625
2627transform::SplitHandleOp::apply(transform::TransformRewriter &rewriter,
2630 int64_t numPayloads =
2632 .Case([&](TransformHandleTypeInterface x) {
2633 return llvm::range_size(state.getPayloadOps(getHandle()));
2634 })
2635 .Case([&](TransformValueHandleTypeInterface x) {
2636 return llvm::range_size(state.getPayloadValues(getHandle()));
2637 })
2638 .Case([&](TransformParamTypeInterface x) {
2639 return llvm::range_size(state.getParams(getHandle()));
2640 })
2641 .DefaultUnreachable("unknown transform dialect type interface");
2642
2643 auto produceNumOpsError = [&]() {
2644 return emitSilenceableError()
2645 << getHandle() << " expected to contain " << this->getNumResults()
2646 << " payloads but it contains " << numPayloads << " payloads";
2647 };
2648
2649 // Fail if there are more payload ops than results and no overflow result was
2650 // specified.
2651 if (numPayloads > getNumResults() && !getOverflowResult().has_value())
2652 return produceNumOpsError();
2653
2654 // Fail if there are more results than payload ops. Unless:
2655 // - "fail_on_payload_too_small" is set to "false", or
2656 // - "pass_through_empty_handle" is set to "true" and there are 0 payload ops.
2657 if (numPayloads < getNumResults() && getFailOnPayloadTooSmall() &&
2658 (numPayloads != 0 || !getPassThroughEmptyHandle()))
2659 return produceNumOpsError();
2660
2661 // Distribute payloads.
2662 SmallVector<SmallVector<MappedValue, 1>> resultHandles(getNumResults(), {});
2663 if (getOverflowResult())
2664 resultHandles[*getOverflowResult()].reserve(numPayloads - getNumResults());
2665
2666 auto container = [&]() {
2667 if (isa<TransformHandleTypeInterface>(getHandle().getType())) {
2668 return llvm::map_to_vector(
2669 state.getPayloadOps(getHandle()),
2670 [](Operation *op) -> MappedValue { return op; });
2671 }
2672 if (isa<TransformValueHandleTypeInterface>(getHandle().getType())) {
2673 return llvm::map_to_vector(state.getPayloadValues(getHandle()),
2674 [](Value v) -> MappedValue { return v; });
2675 }
2676 assert(isa<TransformParamTypeInterface>(getHandle().getType()) &&
2677 "unsupported kind of transform dialect type");
2678 return llvm::map_to_vector(state.getParams(getHandle()),
2679 [](Attribute a) -> MappedValue { return a; });
2680 }();
2681
2682 for (auto &&en : llvm::enumerate(container)) {
2683 int64_t resultNum = en.index();
2684 if (resultNum >= getNumResults())
2685 resultNum = *getOverflowResult();
2686 resultHandles[resultNum].push_back(en.value());
2687 }
2688
2689 // Set transform op results.
2690 for (auto &&it : llvm::enumerate(resultHandles))
2691 results.setMappedValues(llvm::cast<OpResult>(getResult(it.index())),
2692 it.value());
2693
2695}
2696
2697void transform::SplitHandleOp::getEffects(
2699 onlyReadsHandle(getHandleMutable(), effects);
2700 producesHandle(getOperation()->getOpResults(), effects);
2701 // There are no effects on the Payload IR as this is only a handle
2702 // manipulation.
2703}
2704
2705LogicalResult transform::SplitHandleOp::verify() {
2706 if (getOverflowResult().has_value() &&
2707 !(*getOverflowResult() < getNumResults()))
2708 return emitOpError("overflow_result is not a valid result index");
2709
2710 for (Type resultType : getResultTypes()) {
2711 if (implementSameTransformInterface(getHandle().getType(), resultType))
2712 continue;
2713
2714 return emitOpError("expects result types to implement the same transform "
2715 "interface as the operand type");
2716 }
2717
2718 return success();
2719}
2720
2721//===----------------------------------------------------------------------===//
2722// PayloadOp
2723//===----------------------------------------------------------------------===//
2724
2725void transform::PayloadOp::getCheckedNormalForms(
2727 llvm::append_range(normalForms,
2728 getNormalForms().getAsRange<NormalFormAttrInterface>());
2729}
2730
2731//===----------------------------------------------------------------------===//
2732// ReplicateOp
2733//===----------------------------------------------------------------------===//
2734
2736transform::ReplicateOp::apply(transform::TransformRewriter &rewriter,
2739 unsigned numRepetitions = llvm::range_size(state.getPayloadOps(getPattern()));
2740 for (const auto &en : llvm::enumerate(getHandles())) {
2741 Value handle = en.value();
2742 if (isa<TransformHandleTypeInterface>(handle.getType())) {
2743 SmallVector<Operation *> current =
2744 llvm::to_vector(state.getPayloadOps(handle));
2746 payload.reserve(numRepetitions * current.size());
2747 for (unsigned i = 0; i < numRepetitions; ++i)
2748 llvm::append_range(payload, current);
2749 results.set(llvm::cast<OpResult>(getReplicated()[en.index()]), payload);
2750 } else {
2751 assert(llvm::isa<TransformParamTypeInterface>(handle.getType()) &&
2752 "expected param type");
2753 ArrayRef<Attribute> current = state.getParams(handle);
2755 params.reserve(numRepetitions * current.size());
2756 for (unsigned i = 0; i < numRepetitions; ++i)
2757 llvm::append_range(params, current);
2758 results.setParams(llvm::cast<OpResult>(getReplicated()[en.index()]),
2759 params);
2760 }
2761 }
2763}
2764
2765void transform::ReplicateOp::getEffects(
2767 onlyReadsHandle(getPatternMutable(), effects);
2768 onlyReadsHandle(getHandlesMutable(), effects);
2769 producesHandle(getOperation()->getOpResults(), effects);
2770}
2771
2772//===----------------------------------------------------------------------===//
2773// SequenceOp
2774//===----------------------------------------------------------------------===//
2775
2777transform::SequenceOp::apply(transform::TransformRewriter &rewriter,
2780 // Map the entry block argument to the list of operations.
2781 auto scope = state.make_region_scope(*getBodyBlock()->getParent());
2782 if (failed(mapBlockArguments(state)))
2784
2785 return applySequenceBlock(*getBodyBlock(), getFailurePropagationMode(), state,
2786 results);
2787}
2788
2789static ParseResult parseSequenceOpOperands(
2790 OpAsmParser &parser, std::optional<OpAsmParser::UnresolvedOperand> &root,
2791 Type &rootType,
2793 SmallVectorImpl<Type> &extraBindingTypes) {
2795 OptionalParseResult hasRoot = parser.parseOptionalOperand(rootOperand);
2796 if (!hasRoot.has_value()) {
2797 root = std::nullopt;
2798 return success();
2799 }
2800 if (failed(hasRoot.value()))
2801 return failure();
2802 root = rootOperand;
2803
2804 if (succeeded(parser.parseOptionalComma())) {
2805 if (failed(parser.parseOperandList(extraBindings)))
2806 return failure();
2807 }
2808 if (failed(parser.parseColon()))
2809 return failure();
2810
2811 // The paren is truly optional.
2812 (void)parser.parseOptionalLParen();
2813
2814 if (failed(parser.parseType(rootType))) {
2815 return failure();
2816 }
2817
2818 if (!extraBindings.empty()) {
2819 if (parser.parseComma() || parser.parseTypeList(extraBindingTypes))
2820 return failure();
2821 }
2822
2823 if (extraBindingTypes.size() != extraBindings.size()) {
2824 return parser.emitError(parser.getNameLoc(),
2825 "expected types to be provided for all operands");
2826 }
2827
2828 // The paren is truly optional.
2829 (void)parser.parseOptionalRParen();
2830 return success();
2831}
2832
2834 Value root, Type rootType,
2835 ValueRange extraBindings,
2836 TypeRange extraBindingTypes) {
2837 if (!root)
2838 return;
2839
2840 printer << root;
2841 bool hasExtras = !extraBindings.empty();
2842 if (hasExtras) {
2843 printer << ", ";
2844 printer.printOperands(extraBindings);
2845 }
2846
2847 printer << " : ";
2848 if (hasExtras)
2849 printer << "(";
2850
2851 printer << rootType;
2852 if (hasExtras)
2853 printer << ", " << llvm::interleaved(extraBindingTypes) << ')';
2854}
2855
2856/// Returns `true` if the given op operand may be consuming the handle value in
2857/// the Transform IR. That is, if it may have a Free effect on it.
2859 // Conservatively assume the effect being present in absence of the interface.
2860 auto iface = dyn_cast<transform::TransformOpInterface>(use.getOwner());
2861 if (!iface)
2862 return true;
2863
2864 return isHandleConsumed(use.get(), iface);
2865}
2866
2867static LogicalResult
2869 function_ref<InFlightDiagnostic()> reportError) {
2870 OpOperand *potentialConsumer = nullptr;
2871 for (OpOperand &use : value.getUses()) {
2873 continue;
2874
2875 if (!potentialConsumer) {
2876 potentialConsumer = &use;
2877 continue;
2878 }
2879
2880 InFlightDiagnostic diag = reportError()
2881 << " has more than one potential consumer";
2882 diag.attachNote(potentialConsumer->getOwner()->getLoc())
2883 << "used here as operand #" << potentialConsumer->getOperandNumber();
2884 diag.attachNote(use.getOwner()->getLoc())
2885 << "used here as operand #" << use.getOperandNumber();
2886 return diag;
2887 }
2888
2889 return success();
2890}
2891
2892LogicalResult transform::SequenceOp::verify() {
2893 assert(getBodyBlock()->getNumArguments() >= 1 &&
2894 "the number of arguments must have been verified to be more than 1 by "
2895 "PossibleTopLevelTransformOpTrait");
2896
2897 if (!getRoot() && !getExtraBindings().empty()) {
2898 return emitOpError()
2899 << "does not expect extra operands when used as top-level";
2900 }
2901
2902 // Check if a block argument has more than one consuming use.
2903 for (BlockArgument arg : getBodyBlock()->getArguments()) {
2904 if (failed(checkDoubleConsume(arg, [this, arg]() {
2905 return (emitOpError() << "block argument #" << arg.getArgNumber());
2906 }))) {
2907 return failure();
2908 }
2909 }
2910
2911 // Check properties of the nested operations they cannot check themselves.
2912 for (Operation &child : *getBodyBlock()) {
2913 if (!isa<TransformOpInterface>(child) &&
2914 &child != &getBodyBlock()->back()) {
2916 emitOpError()
2917 << "expected children ops to implement TransformOpInterface";
2918 diag.attachNote(child.getLoc()) << "op without interface";
2919 return diag;
2920 }
2921
2922 for (OpResult result : child.getResults()) {
2923 auto report = [&]() {
2924 return (child.emitError() << "result #" << result.getResultNumber());
2925 };
2926 if (failed(checkDoubleConsume(result, report)))
2927 return failure();
2928 }
2929 }
2930
2931 if (!getBodyBlock()->mightHaveTerminator())
2932 return emitOpError() << "expects to have a terminator in the body";
2933
2934 if (getBodyBlock()->getTerminator()->getOperandTypes() !=
2935 getOperation()->getResultTypes()) {
2937 << "expects the types of the terminator operands "
2938 "to match the types of the result";
2939 diag.attachNote(getBodyBlock()->getTerminator()->getLoc()) << "terminator";
2940 return diag;
2941 }
2942 return success();
2943}
2944
2945void transform::SequenceOp::getEffects(
2948}
2949
2951transform::SequenceOp::getEntrySuccessorOperands(RegionSuccessor successor) {
2952 assert(successor.getSuccessor() == &getBody() && "unexpected region index");
2953 if (getOperation()->getNumOperands() > 0)
2954 return getOperation()->getOperands();
2955 return OperandRange(getOperation()->operand_end(),
2956 getOperation()->operand_end());
2957}
2958
2959void transform::SequenceOp::getSuccessorRegions(
2961 if (point.isParent()) {
2962 Region *bodyRegion = &getBody();
2963 regions.emplace_back(bodyRegion);
2964 return;
2965 }
2966
2967 assert(point.getTerminatorPredecessorOrNull()->getParentRegion() ==
2968 &getBody() &&
2969 "unexpected region index");
2970 regions.push_back(RegionSuccessor(getOperation()));
2971}
2972
2974transform::SequenceOp::getSuccessorInputs(RegionSuccessor successor) {
2975 if (getNumOperands() == 0)
2976 return ValueRange();
2977 if (successor.isOperation())
2978 return getResults();
2979 return getBody().getArguments();
2980}
2981
2982void transform::SequenceOp::getRegionInvocationBounds(
2984 (void)operands;
2985 bounds.emplace_back(1, 1);
2986}
2987
2988void transform::SequenceOp::build(OpBuilder &builder, OperationState &state,
2989 TypeRange resultTypes,
2990 FailurePropagationMode failurePropagationMode,
2991 Value root,
2992 SequenceBodyBuilderFn bodyBuilder) {
2993 build(builder, state, resultTypes, failurePropagationMode, root,
2994 /*extra_bindings=*/ValueRange());
2995 Type bbArgType = root.getType();
2996 buildSequenceBody(builder, state, bbArgType,
2997 /*extraBindingTypes=*/TypeRange(), bodyBuilder);
2998}
2999
3000void transform::SequenceOp::build(OpBuilder &builder, OperationState &state,
3001 TypeRange resultTypes,
3002 FailurePropagationMode failurePropagationMode,
3003 Value root, ValueRange extraBindings,
3004 SequenceBodyBuilderArgsFn bodyBuilder) {
3005 build(builder, state, resultTypes, failurePropagationMode, root,
3006 extraBindings);
3007 buildSequenceBody(builder, state, root.getType(), extraBindings.getTypes(),
3008 bodyBuilder);
3009}
3010
3011void transform::SequenceOp::build(OpBuilder &builder, OperationState &state,
3012 TypeRange resultTypes,
3013 FailurePropagationMode failurePropagationMode,
3014 Type bbArgType,
3015 SequenceBodyBuilderFn bodyBuilder) {
3016 build(builder, state, resultTypes, failurePropagationMode, /*root=*/Value(),
3017 /*extra_bindings=*/ValueRange());
3018 buildSequenceBody(builder, state, bbArgType,
3019 /*extraBindingTypes=*/TypeRange(), bodyBuilder);
3020}
3021
3022void transform::SequenceOp::build(OpBuilder &builder, OperationState &state,
3023 TypeRange resultTypes,
3024 FailurePropagationMode failurePropagationMode,
3025 Type bbArgType, TypeRange extraBindingTypes,
3026 SequenceBodyBuilderArgsFn bodyBuilder) {
3027 build(builder, state, resultTypes, failurePropagationMode, /*root=*/Value(),
3028 /*extra_bindings=*/ValueRange());
3029 buildSequenceBody(builder, state, bbArgType, extraBindingTypes, bodyBuilder);
3030}
3031
3032//===----------------------------------------------------------------------===//
3033// PrintOp
3034//===----------------------------------------------------------------------===//
3035
3036void transform::PrintOp::build(OpBuilder &builder, OperationState &result,
3037 StringRef name) {
3038 if (!name.empty())
3039 result.getOrAddProperties<Properties>().name = builder.getStringAttr(name);
3040}
3041
3042void transform::PrintOp::build(OpBuilder &builder, OperationState &result,
3043 Value target, StringRef name) {
3044 result.addOperands({target});
3045 build(builder, result, name);
3046}
3047
3049transform::PrintOp::apply(transform::TransformRewriter &rewriter,
3052 llvm::outs() << "[[[ IR printer: ";
3053 if (getName().has_value())
3054 llvm::outs() << *getName() << " ";
3055
3056 OpPrintingFlags printFlags;
3057 if (getAssumeVerified().value_or(false))
3058 printFlags.assumeVerified();
3059 if (getUseLocalScope().value_or(false))
3060 printFlags.useLocalScope();
3061 if (getSkipRegions().value_or(false))
3062 printFlags.skipRegions();
3063
3064 if (!getTarget()) {
3065 llvm::outs() << "top-level ]]]\n";
3066 state.getTopLevel()->print(llvm::outs(), printFlags);
3067 llvm::outs() << "\n";
3068 llvm::outs().flush();
3070 }
3071
3072 llvm::outs() << "]]]\n";
3073 for (Operation *target : state.getPayloadOps(getTarget())) {
3074 target->print(llvm::outs(), printFlags);
3075 llvm::outs() << "\n";
3076 }
3077
3078 llvm::outs().flush();
3080}
3081
3082void transform::PrintOp::getEffects(
3084 // We don't really care about mutability here, but `getTarget` now
3085 // unconditionally casts to a specific type before verification could run
3086 // here.
3087 if (!getTargetMutable().empty())
3088 onlyReadsHandle(getTargetMutable()[0], effects);
3089 onlyReadsPayload(effects);
3090
3091 // There is no resource for stderr file descriptor, so just declare print
3092 // writes into the default resource.
3093 effects.emplace_back(MemoryEffects::Write::get());
3094}
3095
3096//===----------------------------------------------------------------------===//
3097// VerifyOp
3098//===----------------------------------------------------------------------===//
3099
3101transform::VerifyOp::applyToOne(transform::TransformRewriter &rewriter,
3107 << "failed to verify payload op";
3108 diag.attachNote(target->getLoc()) << "payload op";
3109 return diag;
3110 }
3112}
3113
3114void transform::VerifyOp::getEffects(
3116 transform::onlyReadsHandle(getTargetMutable(), effects);
3117}
3118
3119//===----------------------------------------------------------------------===//
3120// YieldOp
3121//===----------------------------------------------------------------------===//
3122
3123void transform::YieldOp::getEffects(
3125 onlyReadsHandle(getOperandsMutable(), effects);
3126}
return success()
p<< " : "<< getMemRefType()<< ", "<< getType();}static LogicalResult verifyVectorMemoryOp(Operation *op, MemRefType memrefType, VectorType vectorType) { if(memrefType.getElementType() !=vectorType.getElementType()) return op-> emitOpError("requires memref and vector types of the same elemental type")
Given a list of lists of parsed operands, populates uniqueOperands with unique operands.
static ParseResult parseKeyValuePair(AsmParser &parser, DataLayoutEntryInterface &entry, bool tryType=false)
Parse an entry which can either be of the form key = value or a dlti.dl_entry attribute.
Definition DLTI.cpp:38
b
Return true if permutation is a valid permutation of the outer_dims_perm (case OuterOrInnerPerm::Oute...
ArrayAttr()
b getContext())
static std::string diag(const llvm::Value &value)
static llvm::ManagedStatic< PassManagerOptions > options
static void printForeachMatchSymbols(OpAsmPrinter &printer, Operation *op, ArrayAttr matchers, ArrayAttr actions)
Prints the comma-separated list of symbol reference pairs of the format @matcher -> @action.
static LogicalResult checkDoubleConsume(Value value, function_ref< InFlightDiagnostic()> reportError)
static ParseResult parseApplyRegisteredPassOptions(OpAsmParser &parser, DictionaryAttr &options, SmallVectorImpl< OpAsmParser::UnresolvedOperand > &dynamicOptions)
static DiagnosedSilenceableFailure verifyYieldingSingleBlockOp(FunctionOpInterface op, bool allowExternal)
Verifies that a symbol function-like transform dialect operation has the signature and the terminator...
#define DEBUG_TYPE_MATCHER
static DiagnosedSilenceableFailure matchBlock(Block &block, ArrayRef< SmallVector< transform::MappedValue > > blockArgumentMapping, transform::TransformState &state, SmallVectorImpl< SmallVector< transform::MappedValue > > &mappings)
Applies matcher operations from the given block using blockArgumentMapping to initialize block argume...
static void buildSequenceBody(OpBuilder &builder, OperationState &state, Type bbArgType, TypeRange extraBindingTypes, FnTy bodyBuilder)
static void forwardEmptyOperands(Block *block, transform::TransformState &state, transform::TransformResults &results)
static bool implementSameInterface(Type t1, Type t2)
Returns true if both types implement one of the interfaces provided as template parameters.
static void printSequenceOpOperands(OpAsmPrinter &printer, Operation *op, Value root, Type rootType, ValueRange extraBindings, TypeRange extraBindingTypes)
static bool isValueUsePotentialConsumer(OpOperand &use)
Returns true if the given op operand may be consuming the handle value in the Transform IR.
static ParseResult parseSequenceOpOperands(OpAsmParser &parser, std::optional< OpAsmParser::UnresolvedOperand > &root, Type &rootType, SmallVectorImpl< OpAsmParser::UnresolvedOperand > &extraBindings, SmallVectorImpl< Type > &extraBindingTypes)
static DiagnosedSilenceableFailure applySequenceBlock(Block &block, transform::FailurePropagationMode mode, transform::TransformState &state, transform::TransformResults &results)
Applies the transform ops contained in block.
static DiagnosedSilenceableFailure verifyNamedSequenceOp(transform::NamedSequenceOp op, bool emitWarnings)
Verification of a NamedSequenceOp.
static DiagnosedSilenceableFailure verifyFunctionLikeConsumeAnnotations(FunctionOpInterface op, bool emitWarnings, bool alsoVerifyInternal=false)
Checks that the attributes of the function-like operation have correct consumption effect annotations...
static ParseResult parseForeachMatchSymbols(OpAsmParser &parser, ArrayAttr &matchers, ArrayAttr &actions)
Parses the comma-separated list of symbol reference pairs of the format @matcher -> @action.
static void printApplyRegisteredPassOptions(OpAsmPrinter &printer, Operation *op, DictionaryAttr options, ValueRange dynamicOptions)
static bool implementSameTransformInterface(Type t1, Type t2)
Returns true if both types implement one of the transform dialect interfaces.
static DiagnosedSilenceableFailure ensurePayloadIsSeparateFromTransform(transform::TransformOpInterface transform, Operation *payload)
Helper function to check if the given transform op is contained in (or equal to) the given payload ta...
ParseResult parseSymbolName(StringAttr &result)
Parse an -identifier and store it (without the '@' symbol) in a string attribute.
@ None
Zero or more operands with no delimiters.
@ Braces
{} brackets surrounding zero or more operands.
virtual ParseResult parseOptionalKeywordOrString(std::string *result)=0
Parse an optional keyword or string.
virtual Builder & getBuilder() const =0
Return a builder which provides useful access to MLIRContext, global objects like types and attribute...
virtual ParseResult parseCommaSeparatedList(Delimiter delimiter, function_ref< ParseResult()> parseElementFn, StringRef contextMessage=StringRef())=0
Parse a list of comma-separated items with an optional delimiter.
MLIRContext * getContext() const
virtual InFlightDiagnostic emitError(SMLoc loc, const Twine &message={})=0
Emit a diagnostic at the specified location and return failure.
virtual ParseResult parseRSquare()=0
Parse a ] token.
virtual ParseResult parseOptionalRParen()=0
Parse a ) token if present.
virtual ParseResult parseEqual()=0
Parse a = token.
virtual OptionalParseResult parseOptionalAttribute(Attribute &result, Type type={})=0
Parse an arbitrary optional attribute of a given type and return it in result.
virtual SMLoc getCurrentLocation()=0
Get the location of the next token and store it into the argument.
virtual ParseResult parseOptionalComma()=0
Parse a , token if present.
virtual ParseResult parseColon()=0
Parse a : token.
virtual SMLoc getNameLoc() const =0
Return the location of the original name token.
virtual ParseResult parseArrow()=0
Parse a '->' token.
virtual ParseResult parseType(Type &result)=0
Parse a type.
virtual ParseResult parseComma()=0
Parse a , token.
virtual ParseResult parseOptionalLParen()=0
Parse a ( token if present.
ParseResult parseTypeList(SmallVectorImpl< Type > &result)
Parse a type list.
virtual ParseResult parseOptionalLSquare()=0
Parse a [ token if present.
virtual void decreaseIndent()
Decrease indentation.
virtual void increaseIndent()
Increase indentation.
virtual void printAttribute(Attribute attr)
virtual void printNewline()
Print a newline and indent the printer to the start of the current operation/attribute/type.
Attributes are known-constant values of operations.
Definition Attributes.h:25
This class represents an argument of a Block.
Definition Value.h:306
Block represents an ordered list of Operations.
Definition Block.h:33
BlockArgument getArgument(unsigned i)
Definition Block.h:153
Region * getParent() const
Provide a 'getParent' method for ilist_node_with_parent methods.
Definition Block.cpp:27
OpListType & getOperations()
Definition Block.h:161
Operation & front()
Definition Block.h:177
Operation * getTerminator()
Get the terminator operation of this block.
Definition Block.cpp:249
BlockArgListType getArguments()
Definition Block.h:111
iterator_range< iterator > without_terminator()
Return an iterator range over the operation within this block excluding the terminator operation at t...
Definition Block.h:236
Operation * getParentOp()
Returns the closest surrounding operation that contains this block.
Definition Block.cpp:31
This class is a general helper class for creating context-global objects like types,...
Definition Builders.h:51
IntegerAttr getI64IntegerAttr(int64_t value)
Definition Builders.cpp:120
StringAttr getStringAttr(const Twine &bytes)
Definition Builders.cpp:271
ArrayAttr getArrayAttr(ArrayRef< Attribute > value)
Definition Builders.cpp:275
MLIRContext * getContext() const
Definition Builders.h:56
A compatibility class connecting InFlightDiagnostic to DiagnosedSilenceableFailure while providing an...
The result of a transform IR operation application.
LogicalResult silence()
Converts silenceable failure into LogicalResult success without reporting the diagnostic,...
static DiagnosedSilenceableFailure success()
Constructs a DiagnosedSilenceableFailure in the success state.
std::string getMessage() const
Returns the diagnostic message without emitting it.
Diagnostic & attachNote(std::optional< Location > loc=std::nullopt)
Attaches a note to the last diagnostic.
LogicalResult checkAndReport()
Converts all kinds of failure into a LogicalResult failure, emitting the diagnostic if necessary.
bool succeeded() const
Returns true if this is a success.
static DiagnosedSilenceableFailure definiteFailure()
Constructs a DiagnosedSilenceableFailure in the failure state.
Dialects are groups of MLIR operations, types and attributes, as well as behavior associated with the...
Definition Dialect.h:38
A class for computing basic dominance information.
Definition Dominance.h:143
This class represents a frozen set of patterns that can be processed by a pattern applicator.
This class allows control over how the GreedyPatternRewriteDriver works.
static constexpr int64_t kNoLimit
GreedyRewriteConfig & setListener(RewriterBase::Listener *listener)
GreedyRewriteConfig & enableCSEBetweenIterations(bool enable=true)
GreedyRewriteConfig & setMaxIterations(int64_t iterations)
GreedyRewriteConfig & setMaxNumRewrites(int64_t limit)
IRValueT get() const
Return the current value being used by this operand.
This class coordinates rewriting a piece of IR outside of a pattern rewrite, providing a way to keep ...
This class represents a diagnostic that is inflight and set to be reported.
This class represents upper and lower bounds on the number of times a region of a RegionBranchOpInter...
Conversion from types to the LLVM IR dialect.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition Location.h:76
MLIRContext is the top-level object for a collection of MLIR operations.
Definition MLIRContext.h:63
std::vector< Dialect * > getLoadedDialects()
Return information about all IR dialects loaded in the context.
ArrayRef< RegisteredOperationName > getRegisteredOperations()
Return a sorted array containing the information about all registered operations.
void append(StringRef name, Attribute attr)
Add an attribute with the specified name.
NamedAttribute represents a combination of a name and an Attribute value.
Definition Attributes.h:164
StringAttr getName() const
Return the name of the attribute.
Attribute getValue() const
Return the value of the attribute.
Definition Attributes.h:179
The OpAsmParser has methods for interacting with the asm parser: parsing things from it,...
virtual OptionalParseResult parseOptionalOperand(UnresolvedOperand &result, bool allowResultNumber=true)=0
Parse a single operand if present.
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 ParseResult parseOperandList(SmallVectorImpl< UnresolvedOperand > &result, Delimiter delimiter=Delimiter::None, bool allowResultNumber=true, int requiredOperandCount=-1)=0
Parse zero or more SSA comma-separated operand references with a specified surrounding delimiter,...
This is a pure-virtual base class that exposes the asmprinter hooks necessary to implement a custom p...
void printOperands(const ContainerType &container)
Print a comma separated list of operands.
virtual void printOperand(Value value)=0
Print implementations for various things an operation contains.
RAII guard to reset the insertion point of the builder when destroyed.
Definition Builders.h:351
This class helps build Operations.
Definition Builders.h:210
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
void setInsertionPointToStart(Block *block)
Sets the insertion point to the start of the specified block.
Definition Builders.h:434
Listener * getListener() const
Returns the current listener of this builder, or nullptr if this builder doesn't have a listener.
Definition Builders.h:323
This class represents a single result from folding an operation.
This class represents an operand of an operation.
Definition Value.h:254
unsigned getOperandNumber() const
Return which operand this is in the OpOperand list of the Operation.
Definition Value.cpp:226
Set of flags used to control the behavior of the various IR print methods (e.g.
OpPrintingFlags & useLocalScope(bool enable=true)
Use local scope when printing the operation.
OpPrintingFlags & assumeVerified(bool enable=true)
Do not verify the operation when using custom operation printers.
OpPrintingFlags & skipRegions(bool skip=true)
Skip printing regions.
This is a value defined by a result of an operation.
Definition Value.h:454
This class provides the API for ops that are known to be isolated from above.
A trait used to provide symbol table functionalities to a region operation.
A wrapper class that allows for printing an operation with a set of flags, useful to act as a "stream...
Definition Operation.h:1142
This class implements the operand iterators for the Operation class.
Definition ValueRange.h:44
type_range getType() const
type_range getTypes() const
StringRef getStringRef() const
Return the name of this operation. This always succeeds.
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
bool hasTrait()
Returns true if the operation was registered with a particular trait, e.g.
Definition Operation.h:774
Attribute getAttr(StringAttr name)
Return the specified attribute if present, null otherwise.
Definition Operation.h:559
Block * getBlock()
Returns the operation block that contains this operation.
Definition Operation.h:230
Operation * getParentWithTrait()
Returns the closest surrounding parent operation with trait Trait.
Definition Operation.h:273
Location getLoc()
The source location the operation was defined or derived from.
Definition Operation.h:240
Operation * getParentOp()
Returns the closest surrounding operation that contains this operation or nullptr if this is a top-le...
Definition Operation.h:251
unsigned getNumOperands()
Definition Operation.h:371
OpTy getParentOfType()
Return the closest surrounding parent operation that is of type 'OpTy'.
Definition Operation.h:255
OperationName getName()
The name of an operation is the key identifier for it.
Definition Operation.h:115
void print(raw_ostream &os, const OpPrintingFlags &flags={})
operand_range getOperands()
Returns an iterator on the underlying Value's.
Definition Operation.h:403
result_range getOpResults()
Definition Operation.h:445
Operation * clone(IRMapping &mapper, const CloneOptions &options=CloneOptions::all())
Create a deep copy of this operation, remapping any operands that use values outside of the operation...
This class implements Optional functionality for ParseResult.
ParseResult value() const
Access the internal ParseResult value.
bool has_value() const
Returns true if we contain a valid ParseResult value.
static const PassInfo * lookup(StringRef passArg)
Returns the pass info for the specified pass class or null if unknown.
The main pass manager and pipeline builder.
static const PassPipelineInfo * lookup(StringRef pipelineArg)
Returns the pass pipeline info for the specified pass pipeline or null if unknown.
Structure to group information about a passes and pass pipelines (argument to invoke via mlir-opt,...
LogicalResult addToPipeline(OpPassManager &pm, StringRef options, function_ref< LogicalResult(const Twine &)> errorHandler) const
Adds this pass registry entry to the given pass manager.
This class represents a point being branched from in the methods of the RegionBranchOpInterface.
bool isParent() const
Returns true if branching from the parent op.
RegionBranchTerminatorOpInterface getTerminatorPredecessorOrNull() const
Returns the terminator if branching from a region.
This class represents a successor of a region.
Region * getSuccessor() const
Return the given region successor.
bool isOperation() const
Return true if the successor is an operation.
This class contains a list of basic blocks and a link to the parent operation it is attached to.
Definition Region.h:26
Block & front()
Definition Region.h:65
BlockArgListType getArguments()
Definition Region.h:94
iterator begin()
Definition Region.h:55
This is a "type erased" representation of a registered operation.
MLIRContext * getContext() const
virtual void replaceOp(Operation *op, ValueRange newValues)
Replace the results of the given (original) operation with the specified list of values (replacements...
This class represents a collection of SymbolTables.
virtual Operation * lookupNearestSymbolFrom(Operation *from, StringAttr symbol)
Returns the operation registered with the given symbol name within the closest parent operation of,...
static StringRef getSymbolAttrName()
Return the name of the attribute used for symbol names.
Definition SymbolTable.h:76
static Operation * lookupNearestSymbolFrom(Operation *from, StringAttr symbol)
Returns the operation registered with the given symbol name within the closest parent operation of,...
This class provides an abstraction over the various different ranges of value types.
Definition TypeRange.h:40
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition Types.h:74
This class provides an abstraction over the different types of ranges over Values.
Definition ValueRange.h:389
type_range getType() const
type_range getTypes() const
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition Value.h:96
Type getType() const
Return the type of this value.
Definition Value.h:105
use_range getUses() const
Returns a range of all uses, which is useful for iterating over all uses.
Definition Value.h:188
A utility result that is used to signal how to proceed with an ongoing walk:
Definition WalkResult.h:29
static WalkResult advance()
Definition WalkResult.h:47
bool wasInterrupted() const
Returns true if the walk was interrupted.
Definition WalkResult.h:51
static WalkResult interrupt()
Definition WalkResult.h:46
Operation * getOwner() const
Return the owner of this operand.
Definition UseDefLists.h:38
A named class for passing around the variadic flag.
A list of results of applying a transform op with ApplyEachOpTrait to a single payload operation,...
Local mapping between values defined by a specific op implementing the TransformOpInterface and the p...
void setValues(OpResult handle, Range &&values)
Indicates that the result of the transform IR op at the given position corresponds to the given range...
void setParams(OpResult value, ArrayRef< TransformState::Param > params)
Indicates that the result of the transform IR op at the given position corresponds to the given list ...
void set(OpResult value, Range &&ops)
Indicates that the result of the transform IR op at the given position corresponds to the given list ...
void setMappedValues(OpResult handle, ArrayRef< MappedValue > values)
Indicates that the result of the transform IR op at the given position corresponds to the given range...
This is a special rewriter to be used in transform op implementations, providing additional helper fu...
The state maintained across applications of various ops implementing the TransformOpInterface.
auto getPayloadOps(Value value) const
Returns an iterator that enumerates all ops that the given transform IR value corresponds to.
auto getPayloadValues(Value handleValue) const
Returns an iterator that enumerates all payload IR values that the given transform IR value correspon...
LogicalResult mapBlockArguments(BlockArgument argument, ArrayRef< Operation * > operations)
Records the mapping between a block argument in the transform IR and a list of operations in the payl...
DiagnosedSilenceableFailure applyTransform(TransformOpInterface transform)
Applies the transformation specified by the given transform op and updates the state accordingly.
RegionScope make_region_scope(Region &region)
Creates a new region scope for the given region.
ArrayRef< Attribute > getParams(Value value) const
Returns the list of parameters that the given transform IR value corresponds to.
LogicalResult mapBlockArgument(BlockArgument argument, ArrayRef< MappedValue > values)
Operation * getTopLevel() const
Returns the op at which the transformation state is rooted.
void printFunctionOp(OpAsmPrinter &p, FunctionOpInterface op, bool isVariadic, StringRef typeAttrName, StringAttr argAttrsName, StringAttr resAttrsName)
Printer implementation for function-like operations.
ParseResult parseFunctionOp(OpAsmParser &parser, OperationState &result, bool allowVariadic, StringAttr typeAttrName, FuncTypeBuilder funcTypeBuilder, StringAttr argAttrsName, StringAttr resAttrsName)
Parser implementation for function-like operations.
detail::InFlightRemark failed(Location loc, RemarkOpts opts)
Report an optimization remark that failed.
Definition Remarks.h:717
void forwardTerminatorOperands(Block *block, transform::TransformState &state, transform::TransformResults &results)
Populates results with payload associations that match exactly those of the operands to block's termi...
void prepareValueMappings(SmallVectorImpl< SmallVector< transform::MappedValue > > &mappings, ValueRange values, const transform::TransformState &state)
Populates mappings with mapped values associated with the given transform IR values in the given stat...
void getPotentialTopLevelEffects(Operation *operation, Value root, Block &body, SmallVectorImpl< MemoryEffects::EffectInstance > &effects)
Populates effects with side effects implied by PossibleTopLevelTransformOpTrait for the given operati...
LogicalResult verifyTransformMatchDimsOp(Operation *op, ArrayRef< int64_t > raw, bool inverted, bool all)
Checks if the positional specification defined is valid and reports errors otherwise.
void onlyReadsPayload(SmallVectorImpl< MemoryEffects::EffectInstance > &effects)
bool isHandleConsumed(Value handle, transform::TransformOpInterface transform)
Checks whether the transform op consumes the given handle.
llvm::PointerUnion< Operation *, Param, Value > MappedValue
DiagnosedSilenceableFailure expandTargetSpecification(Location loc, bool isAll, bool isInverted, ArrayRef< int64_t > rawList, int64_t maxNumber, SmallVectorImpl< int64_t > &result)
Populates result with the positional identifiers relative to maxNumber.
void getConsumedBlockArguments(Block &block, llvm::SmallDenseSet< unsigned > &consumedArguments)
Populates consumedArguments with positions of block arguments that are consumed by the operations in ...
bool doesModifyPayload(transform::TransformOpInterface transform)
Checks whether the transform op modifies the payload.
void producesHandle(ResultRange handles, SmallVectorImpl< MemoryEffects::EffectInstance > &effects)
bool doesReadPayload(transform::TransformOpInterface transform)
Checks whether the transform op reads the payload.
void consumesHandle(MutableArrayRef< OpOperand > handles, SmallVectorImpl< MemoryEffects::EffectInstance > &effects)
Populates effects with the memory effects indicating the operation on the given handle value:
void onlyReadsHandle(MutableArrayRef< OpOperand > handles, SmallVectorImpl< MemoryEffects::EffectInstance > &effects)
void modifiesPayload(SmallVectorImpl< MemoryEffects::EffectInstance > &effects)
Populates effects with the memory effects indicating the access to payload IR resource.
Include the generated interface declarations.
InFlightDiagnostic emitWarning(Location loc)
Utility method to emit a warning message using this location.
void eliminateCommonSubExpressions(RewriterBase &rewriter, DominanceInfo &domInfo, Operation *op, bool *changed=nullptr, int64_t *numCSE=nullptr, int64_t *numDCE=nullptr)
Eliminate common subexpressions within the given operation.
Definition CSE.cpp:415
function_ref< void(Value, StringRef)> OpAsmSetValueNameFn
A functor used to set the name of the start of a result group of an operation.
Type getType(OpFoldResult ofr)
Returns the int type of the integer in ofr.
Definition Utils.cpp:307
LogicalResult applyPatternsGreedily(Region &region, const FrozenRewritePatternSet &patterns, GreedyRewriteConfig config=GreedyRewriteConfig(), bool *changed=nullptr)
Rewrite ops in the given region, which must be isolated from above, by repeatedly applying the highes...
DiagnosedSilenceableFailure emitSilenceableFailure(Location loc, const Twine &message={})
Emits a silenceable failure with the given message.
llvm::DenseSet< ValueT, ValueInfoT > DenseSet
Definition LLVM.h:122
InFlightDiagnostic emitError(Location loc)
Utility method to emit an error message using this location.
LogicalResult applyOpPatternsGreedily(ArrayRef< Operation * > ops, const FrozenRewritePatternSet &patterns, GreedyRewriteConfig config=GreedyRewriteConfig(), bool *changed=nullptr, bool *allErased=nullptr)
Rewrite the specified ops by repeatedly applying the highest benefit patterns in a greedy worklist dr...
llvm::SetVector< T, Vector, Set, N > SetVector
Definition LLVM.h:125
DiagnosedDefiniteFailure emitDefiniteFailure(Location loc, const Twine &message={})
Emits a definite failure with the given message.
bool eliminateTriviallyDeadOps(RewriterBase &rewriter, Region &region, bool includeNestedRegions=true)
Remove trivially dead operations from region.
Operation * clone(OpBuilder &b, Operation *op, TypeRange newResultTypes, ValueRange newOperands)
LogicalResult verify(Operation *op, bool verifyRecursively=true)
Perform (potentially expensive) checks of invariants, used to detect compiler bugs,...
Definition Verifier.cpp:566
llvm::function_ref< Fn > function_ref
Definition LLVM.h:147
size_t moveLoopInvariantCode(ArrayRef< Region * > regions, function_ref< bool(Value, Region *)> isDefinedOutsideRegion, function_ref< bool(Operation *, Region *)> shouldMoveOutOfRegion, function_ref< void(Operation *, Region *)> moveOutOfRegion)
Given a list of regions, perform loop-invariant code motion.
This is the representation of an operand reference.
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.
SmallVector< std::unique_ptr< Region >, 1 > regions
Regions that the op will hold.
Region * addRegion()
Create a region that should be attached to the operation.