675 lines · cpp
1//===- SCFTransformOps.cpp - Implementation of SCF transformation ops -----===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "mlir/Dialect/SCF/TransformOps/SCFTransformOps.h"10 11#include "mlir/Conversion/SCFToControlFlow/SCFToControlFlow.h"12#include "mlir/Dialect/Affine/IR/AffineOps.h"13#include "mlir/Dialect/Affine/LoopUtils.h"14#include "mlir/Dialect/Func/IR/FuncOps.h"15#include "mlir/Dialect/SCF/IR/SCF.h"16#include "mlir/Dialect/SCF/Transforms/Patterns.h"17#include "mlir/Dialect/SCF/Transforms/Transforms.h"18#include "mlir/Dialect/SCF/Utils/Utils.h"19#include "mlir/Dialect/Transform/IR/TransformDialect.h"20#include "mlir/Dialect/Transform/Interfaces/TransformInterfaces.h"21#include "mlir/Dialect/Utils/StaticValueUtils.h"22#include "mlir/Dialect/Vector/IR/VectorOps.h"23#include "mlir/IR/BuiltinAttributes.h"24#include "mlir/IR/Dominance.h"25#include "mlir/IR/OpDefinition.h"26 27using namespace mlir;28using namespace mlir::affine;29 30//===----------------------------------------------------------------------===//31// Apply...PatternsOp32//===----------------------------------------------------------------------===//33 34void transform::ApplyForLoopCanonicalizationPatternsOp::populatePatterns(35 RewritePatternSet &patterns) {36 scf::populateSCFForLoopCanonicalizationPatterns(patterns);37}38 39void transform::ApplySCFStructuralConversionPatternsOp::populatePatterns(40 TypeConverter &typeConverter, RewritePatternSet &patterns) {41 scf::populateSCFStructuralTypeConversions(typeConverter, patterns);42}43 44void transform::ApplySCFStructuralConversionPatternsOp::45 populateConversionTargetRules(const TypeConverter &typeConverter,46 ConversionTarget &conversionTarget) {47 scf::populateSCFStructuralTypeConversionTarget(typeConverter,48 conversionTarget);49}50 51void transform::ApplySCFToControlFlowPatternsOp::populatePatterns(52 TypeConverter &typeConverter, RewritePatternSet &patterns) {53 populateSCFToControlFlowConversionPatterns(patterns);54}55 56//===----------------------------------------------------------------------===//57// ForallToForOp58//===----------------------------------------------------------------------===//59 60DiagnosedSilenceableFailure61transform::ForallToForOp::apply(transform::TransformRewriter &rewriter,62 transform::TransformResults &results,63 transform::TransformState &state) {64 auto payload = state.getPayloadOps(getTarget());65 if (!llvm::hasSingleElement(payload))66 return emitSilenceableError() << "expected a single payload op";67 68 auto target = dyn_cast<scf::ForallOp>(*payload.begin());69 if (!target) {70 DiagnosedSilenceableFailure diag =71 emitSilenceableError() << "expected the payload to be scf.forall";72 diag.attachNote((*payload.begin())->getLoc()) << "payload op";73 return diag;74 }75 76 if (!target.getOutputs().empty()) {77 return emitSilenceableError()78 << "unsupported shared outputs (didn't bufferize?)";79 }80 81 SmallVector<OpFoldResult> lbs = target.getMixedLowerBound();82 83 if (getNumResults() != lbs.size()) {84 DiagnosedSilenceableFailure diag =85 emitSilenceableError()86 << "op expects as many results (" << getNumResults()87 << ") as payload has induction variables (" << lbs.size() << ")";88 diag.attachNote(target.getLoc()) << "payload op";89 return diag;90 }91 92 SmallVector<Operation *> opResults;93 if (failed(scf::forallToForLoop(rewriter, target, &opResults))) {94 DiagnosedSilenceableFailure diag = emitSilenceableError()95 << "failed to convert forall into for";96 return diag;97 }98 99 for (auto &&[i, res] : llvm::enumerate(opResults)) {100 results.set(cast<OpResult>(getTransformed()[i]), {res});101 }102 return DiagnosedSilenceableFailure::success();103}104 105//===----------------------------------------------------------------------===//106// ForallToForOp107//===----------------------------------------------------------------------===//108 109DiagnosedSilenceableFailure110transform::ForallToParallelOp::apply(transform::TransformRewriter &rewriter,111 transform::TransformResults &results,112 transform::TransformState &state) {113 auto payload = state.getPayloadOps(getTarget());114 if (!llvm::hasSingleElement(payload))115 return emitSilenceableError() << "expected a single payload op";116 117 auto target = dyn_cast<scf::ForallOp>(*payload.begin());118 if (!target) {119 DiagnosedSilenceableFailure diag =120 emitSilenceableError() << "expected the payload to be scf.forall";121 diag.attachNote((*payload.begin())->getLoc()) << "payload op";122 return diag;123 }124 125 if (!target.getOutputs().empty()) {126 return emitSilenceableError()127 << "unsupported shared outputs (didn't bufferize?)";128 }129 130 if (getNumResults() != 1) {131 DiagnosedSilenceableFailure diag = emitSilenceableError()132 << "op expects one result, given "133 << getNumResults();134 diag.attachNote(target.getLoc()) << "payload op";135 return diag;136 }137 138 scf::ParallelOp opResult;139 if (failed(scf::forallToParallelLoop(rewriter, target, &opResult))) {140 DiagnosedSilenceableFailure diag =141 emitSilenceableError() << "failed to convert forall into parallel";142 return diag;143 }144 145 results.set(cast<OpResult>(getTransformed()[0]), {opResult});146 return DiagnosedSilenceableFailure::success();147}148 149//===----------------------------------------------------------------------===//150// ParallelForToNestedForOps151//===----------------------------------------------------------------------===//152 153DiagnosedSilenceableFailure transform::ParallelForToNestedForOps::apply(154 transform::TransformRewriter &rewriter,155 transform::TransformResults &results, transform::TransformState &state) {156 auto payload = state.getPayloadOps(getTarget());157 if (!llvm::hasSingleElement(payload))158 return emitSilenceableError() << "expected a single payload op";159 160 auto target = dyn_cast<scf::ParallelOp>(*payload.begin());161 if (!target) {162 DiagnosedSilenceableFailure diag =163 emitSilenceableError() << "expected the payload to be scf.parallel";164 diag.attachNote((*payload.begin())->getLoc()) << "payload op";165 return diag;166 }167 168 if (getNumResults() != 1) {169 DiagnosedSilenceableFailure diag = emitSilenceableError()170 << "op expects one result, given "171 << getNumResults();172 diag.attachNote(target.getLoc()) << "payload op";173 return diag;174 }175 176 FailureOr<scf::LoopNest> loopNest =177 scf::parallelForToNestedFors(rewriter, target);178 if (failed(loopNest)) {179 DiagnosedSilenceableFailure diag =180 emitSilenceableError() << "failed to convert parallel into nested fors";181 return diag;182 }183 184 results.set(cast<OpResult>(getTransformed()[0]), {loopNest->loops.front()});185 return DiagnosedSilenceableFailure::success();186}187 188//===----------------------------------------------------------------------===//189// LoopOutlineOp190//===----------------------------------------------------------------------===//191 192/// Wraps the given operation `op` into an `scf.execute_region` operation. Uses193/// the provided rewriter for all operations to remain compatible with the194/// rewriting infra, as opposed to just splicing the op in place.195static scf::ExecuteRegionOp wrapInExecuteRegion(RewriterBase &b,196 Operation *op) {197 if (op->getNumRegions() != 1)198 return nullptr;199 OpBuilder::InsertionGuard g(b);200 b.setInsertionPoint(op);201 scf::ExecuteRegionOp executeRegionOp =202 scf::ExecuteRegionOp::create(b, op->getLoc(), op->getResultTypes());203 {204 OpBuilder::InsertionGuard g(b);205 b.setInsertionPointToStart(&executeRegionOp.getRegion().emplaceBlock());206 Operation *clonedOp = b.cloneWithoutRegions(*op);207 Region &clonedRegion = clonedOp->getRegions().front();208 assert(clonedRegion.empty() && "expected empty region");209 b.inlineRegionBefore(op->getRegions().front(), clonedRegion,210 clonedRegion.end());211 scf::YieldOp::create(b, op->getLoc(), clonedOp->getResults());212 }213 b.replaceOp(op, executeRegionOp.getResults());214 return executeRegionOp;215}216 217DiagnosedSilenceableFailure218transform::LoopOutlineOp::apply(transform::TransformRewriter &rewriter,219 transform::TransformResults &results,220 transform::TransformState &state) {221 SmallVector<Operation *> functions;222 SmallVector<Operation *> calls;223 DenseMap<Operation *, SymbolTable> symbolTables;224 for (Operation *target : state.getPayloadOps(getTarget())) {225 Location location = target->getLoc();226 Operation *symbolTableOp = SymbolTable::getNearestSymbolTable(target);227 scf::ExecuteRegionOp exec = wrapInExecuteRegion(rewriter, target);228 if (!exec) {229 DiagnosedSilenceableFailure diag = emitSilenceableError()230 << "failed to outline";231 diag.attachNote(target->getLoc()) << "target op";232 return diag;233 }234 func::CallOp call;235 FailureOr<func::FuncOp> outlined = outlineSingleBlockRegion(236 rewriter, location, exec.getRegion(), getFuncName(), &call);237 238 if (failed(outlined))239 return emitDefaultDefiniteFailure(target);240 241 if (symbolTableOp) {242 SymbolTable &symbolTable =243 symbolTables.try_emplace(symbolTableOp, symbolTableOp)244 .first->getSecond();245 symbolTable.insert(*outlined);246 call.setCalleeAttr(FlatSymbolRefAttr::get(*outlined));247 }248 functions.push_back(*outlined);249 calls.push_back(call);250 }251 results.set(cast<OpResult>(getFunction()), functions);252 results.set(cast<OpResult>(getCall()), calls);253 return DiagnosedSilenceableFailure::success();254}255 256//===----------------------------------------------------------------------===//257// LoopPeelOp258//===----------------------------------------------------------------------===//259 260DiagnosedSilenceableFailure261transform::LoopPeelOp::applyToOne(transform::TransformRewriter &rewriter,262 scf::ForOp target,263 transform::ApplyToEachResultList &results,264 transform::TransformState &state) {265 scf::ForOp result;266 if (getPeelFront()) {267 LogicalResult status =268 scf::peelForLoopFirstIteration(rewriter, target, result);269 if (failed(status)) {270 DiagnosedSilenceableFailure diag =271 emitSilenceableError() << "failed to peel the first iteration";272 return diag;273 }274 } else {275 LogicalResult status =276 scf::peelForLoopAndSimplifyBounds(rewriter, target, result);277 if (failed(status)) {278 DiagnosedSilenceableFailure diag = emitSilenceableError()279 << "failed to peel the last iteration";280 return diag;281 }282 }283 284 results.push_back(target);285 results.push_back(result);286 287 return DiagnosedSilenceableFailure::success();288}289 290//===----------------------------------------------------------------------===//291// LoopPipelineOp292//===----------------------------------------------------------------------===//293 294/// Callback for PipeliningOption. Populates `schedule` with the mapping from an295/// operation to its logical time position given the iteration interval and the296/// read latency. The latter is only relevant for vector transfers.297static void298loopScheduling(scf::ForOp forOp,299 std::vector<std::pair<Operation *, unsigned>> &schedule,300 unsigned iterationInterval, unsigned readLatency) {301 auto getLatency = [&](Operation *op) -> unsigned {302 if (isa<vector::TransferReadOp>(op))303 return readLatency;304 return 1;305 };306 307 std::optional<int64_t> ubConstant =308 getConstantIntValue(forOp.getUpperBound());309 std::optional<int64_t> lbConstant =310 getConstantIntValue(forOp.getLowerBound());311 DenseMap<Operation *, unsigned> opCycles;312 std::map<unsigned, std::vector<Operation *>> wrappedSchedule;313 for (Operation &op : forOp.getBody()->getOperations()) {314 if (isa<scf::YieldOp>(op))315 continue;316 unsigned earlyCycle = 0;317 for (Value operand : op.getOperands()) {318 Operation *def = operand.getDefiningOp();319 if (!def)320 continue;321 if (ubConstant && lbConstant) {322 unsigned ubInt = ubConstant.value();323 unsigned lbInt = lbConstant.value();324 auto minLatency = std::min(ubInt - lbInt - 1, getLatency(def));325 earlyCycle = std::max(earlyCycle, opCycles[def] + minLatency);326 } else {327 earlyCycle = std::max(earlyCycle, opCycles[def] + getLatency(def));328 }329 }330 opCycles[&op] = earlyCycle;331 wrappedSchedule[earlyCycle % iterationInterval].push_back(&op);332 }333 for (const auto &it : wrappedSchedule) {334 for (Operation *op : it.second) {335 unsigned cycle = opCycles[op];336 schedule.emplace_back(op, cycle / iterationInterval);337 }338 }339}340 341DiagnosedSilenceableFailure342transform::LoopPipelineOp::applyToOne(transform::TransformRewriter &rewriter,343 scf::ForOp target,344 transform::ApplyToEachResultList &results,345 transform::TransformState &state) {346 scf::PipeliningOption options;347 options.getScheduleFn =348 [this](scf::ForOp forOp,349 std::vector<std::pair<Operation *, unsigned>> &schedule) mutable {350 loopScheduling(forOp, schedule, getIterationInterval(),351 getReadLatency());352 };353 scf::ForLoopPipeliningPattern pattern(options, target->getContext());354 rewriter.setInsertionPoint(target);355 FailureOr<scf::ForOp> patternResult =356 scf::pipelineForLoop(rewriter, target, options);357 if (succeeded(patternResult)) {358 results.push_back(*patternResult);359 return DiagnosedSilenceableFailure::success();360 }361 return emitDefaultSilenceableFailure(target);362}363 364//===----------------------------------------------------------------------===//365// LoopPromoteIfOneIterationOp366//===----------------------------------------------------------------------===//367 368DiagnosedSilenceableFailure transform::LoopPromoteIfOneIterationOp::applyToOne(369 transform::TransformRewriter &rewriter, LoopLikeOpInterface target,370 transform::ApplyToEachResultList &results,371 transform::TransformState &state) {372 (void)target.promoteIfSingleIteration(rewriter);373 return DiagnosedSilenceableFailure::success();374}375 376void transform::LoopPromoteIfOneIterationOp::getEffects(377 SmallVectorImpl<MemoryEffects::EffectInstance> &effects) {378 consumesHandle(getTargetMutable(), effects);379 modifiesPayload(effects);380}381 382//===----------------------------------------------------------------------===//383// LoopUnrollOp384//===----------------------------------------------------------------------===//385 386DiagnosedSilenceableFailure387transform::LoopUnrollOp::applyToOne(transform::TransformRewriter &rewriter,388 Operation *op,389 transform::ApplyToEachResultList &results,390 transform::TransformState &state) {391 LogicalResult result(failure());392 if (scf::ForOp scfFor = dyn_cast<scf::ForOp>(op))393 result = loopUnrollByFactor(scfFor, getFactor());394 else if (AffineForOp affineFor = dyn_cast<AffineForOp>(op))395 result = loopUnrollByFactor(affineFor, getFactor());396 else397 return emitSilenceableError()398 << "failed to unroll, incorrect type of payload";399 400 if (failed(result))401 return emitSilenceableError() << "failed to unroll";402 403 return DiagnosedSilenceableFailure::success();404}405 406//===----------------------------------------------------------------------===//407// LoopUnrollAndJamOp408//===----------------------------------------------------------------------===//409 410DiagnosedSilenceableFailure transform::LoopUnrollAndJamOp::applyToOne(411 transform::TransformRewriter &rewriter, Operation *op,412 transform::ApplyToEachResultList &results,413 transform::TransformState &state) {414 LogicalResult result(failure());415 if (scf::ForOp scfFor = dyn_cast<scf::ForOp>(op))416 result = loopUnrollJamByFactor(scfFor, getFactor());417 else if (AffineForOp affineFor = dyn_cast<AffineForOp>(op))418 result = loopUnrollJamByFactor(affineFor, getFactor());419 else420 return emitSilenceableError()421 << "failed to unroll and jam, incorrect type of payload";422 423 if (failed(result))424 return emitSilenceableError() << "failed to unroll and jam";425 426 return DiagnosedSilenceableFailure::success();427}428 429//===----------------------------------------------------------------------===//430// LoopCoalesceOp431//===----------------------------------------------------------------------===//432 433DiagnosedSilenceableFailure434transform::LoopCoalesceOp::applyToOne(transform::TransformRewriter &rewriter,435 Operation *op,436 transform::ApplyToEachResultList &results,437 transform::TransformState &state) {438 LogicalResult result(failure());439 if (scf::ForOp scfForOp = dyn_cast<scf::ForOp>(op))440 result = coalescePerfectlyNestedSCFForLoops(scfForOp);441 else if (AffineForOp affineForOp = dyn_cast<AffineForOp>(op))442 result = coalescePerfectlyNestedAffineLoops(affineForOp);443 444 results.push_back(op);445 if (failed(result)) {446 DiagnosedSilenceableFailure diag = emitSilenceableError()447 << "failed to coalesce";448 return diag;449 }450 return DiagnosedSilenceableFailure::success();451}452 453//===----------------------------------------------------------------------===//454// TakeAssumedBranchOp455//===----------------------------------------------------------------------===//456/// Replaces the given op with the contents of the given single-block region,457/// using the operands of the block terminator to replace operation results.458static void replaceOpWithRegion(RewriterBase &rewriter, Operation *op,459 Region ®ion) {460 assert(region.hasOneBlock() && "expected single-block region");461 Block *block = ®ion.front();462 Operation *terminator = block->getTerminator();463 ValueRange results = terminator->getOperands();464 rewriter.inlineBlockBefore(block, op, /*blockArgs=*/{});465 rewriter.replaceOp(op, results);466 rewriter.eraseOp(terminator);467}468 469DiagnosedSilenceableFailure transform::TakeAssumedBranchOp::applyToOne(470 transform::TransformRewriter &rewriter, scf::IfOp ifOp,471 transform::ApplyToEachResultList &results,472 transform::TransformState &state) {473 rewriter.setInsertionPoint(ifOp);474 Region ®ion =475 getTakeElseBranch() ? ifOp.getElseRegion() : ifOp.getThenRegion();476 if (!region.hasOneBlock()) {477 return emitDefiniteFailure()478 << "requires an scf.if op with a single-block "479 << ((getTakeElseBranch()) ? "`else`" : "`then`") << " region";480 }481 replaceOpWithRegion(rewriter, ifOp, region);482 return DiagnosedSilenceableFailure::success();483}484 485void transform::TakeAssumedBranchOp::getEffects(486 SmallVectorImpl<MemoryEffects::EffectInstance> &effects) {487 onlyReadsHandle(getTargetMutable(), effects);488 modifiesPayload(effects);489}490 491//===----------------------------------------------------------------------===//492// LoopFuseSiblingOp493//===----------------------------------------------------------------------===//494 495/// Check if `target` and `source` are siblings, in the context that `target`496/// is being fused into `source`.497///498/// This is a simple check that just checks if both operations are in the same499/// block and some checks to ensure that the fused IR does not violate500/// dominance.501static DiagnosedSilenceableFailure isOpSibling(Operation *target,502 Operation *source) {503 // Check if both operations are same.504 if (target == source)505 return emitSilenceableFailure(source)506 << "target and source need to be different loops";507 508 // Check if both operations are in the same block.509 if (target->getBlock() != source->getBlock())510 return emitSilenceableFailure(source)511 << "target and source are not in the same block";512 513 // Check if fusion will violate dominance.514 DominanceInfo domInfo(source);515 if (target->isBeforeInBlock(source)) {516 // Since `target` is before `source`, all users of results of `target`517 // need to be dominated by `source`.518 for (Operation *user : target->getUsers()) {519 if (!domInfo.properlyDominates(source, user, /*enclosingOpOk=*/false)) {520 return emitSilenceableFailure(target)521 << "user of results of target should be properly dominated by "522 "source";523 }524 }525 } else {526 // Since `target` is after `source`, all values used by `target` need527 // to dominate `source`.528 529 // Check if operands of `target` are dominated by `source`.530 for (Value operand : target->getOperands()) {531 Operation *operandOp = operand.getDefiningOp();532 // Operands without defining operations are block arguments. When `target`533 // and `source` occur in the same block, these operands dominate `source`.534 if (!operandOp)535 continue;536 537 // Operand's defining operation should properly dominate `source`.538 if (!domInfo.properlyDominates(operandOp, source,539 /*enclosingOpOk=*/false))540 return emitSilenceableFailure(target)541 << "operands of target should be properly dominated by source";542 }543 544 // Check if values used by `target` are dominated by `source`.545 bool failed = false;546 OpOperand *failedValue = nullptr;547 visitUsedValuesDefinedAbove(target->getRegions(), [&](OpOperand *operand) {548 Operation *operandOp = operand->get().getDefiningOp();549 if (operandOp && !domInfo.properlyDominates(operandOp, source,550 /*enclosingOpOk=*/false)) {551 // `operand` is not an argument of an enclosing block and the defining552 // op of `operand` is outside `target` but does not dominate `source`.553 failed = true;554 failedValue = operand;555 }556 });557 558 if (failed)559 return emitSilenceableFailure(failedValue->getOwner())560 << "values used inside regions of target should be properly "561 "dominated by source";562 }563 564 return DiagnosedSilenceableFailure::success();565}566 567/// Check if `target` scf.forall can be fused into `source` scf.forall.568///569/// This simply checks if both loops have the same bounds, steps and mapping.570/// No attempt is made at checking that the side effects of `target` and571/// `source` are independent of each other.572static bool isForallWithIdenticalConfiguration(Operation *target,573 Operation *source) {574 auto targetOp = dyn_cast<scf::ForallOp>(target);575 auto sourceOp = dyn_cast<scf::ForallOp>(source);576 if (!targetOp || !sourceOp)577 return false;578 579 return targetOp.getMixedLowerBound() == sourceOp.getMixedLowerBound() &&580 targetOp.getMixedUpperBound() == sourceOp.getMixedUpperBound() &&581 targetOp.getMixedStep() == sourceOp.getMixedStep() &&582 targetOp.getMapping() == sourceOp.getMapping();583}584 585/// Check if `target` scf.for can be fused into `source` scf.for.586///587/// This simply checks if both loops have the same bounds and steps. No attempt588/// is made at checking that the side effects of `target` and `source` are589/// independent of each other.590static bool isForWithIdenticalConfiguration(Operation *target,591 Operation *source) {592 auto targetOp = dyn_cast<scf::ForOp>(target);593 auto sourceOp = dyn_cast<scf::ForOp>(source);594 if (!targetOp || !sourceOp)595 return false;596 597 return targetOp.getLowerBound() == sourceOp.getLowerBound() &&598 targetOp.getUpperBound() == sourceOp.getUpperBound() &&599 targetOp.getStep() == sourceOp.getStep();600}601 602DiagnosedSilenceableFailure603transform::LoopFuseSiblingOp::apply(transform::TransformRewriter &rewriter,604 transform::TransformResults &results,605 transform::TransformState &state) {606 auto targetOps = state.getPayloadOps(getTarget());607 auto sourceOps = state.getPayloadOps(getSource());608 609 if (!llvm::hasSingleElement(targetOps) ||610 !llvm::hasSingleElement(sourceOps)) {611 return emitDefiniteFailure()612 << "requires exactly one target handle (got "613 << llvm::range_size(targetOps) << ") and exactly one "614 << "source handle (got " << llvm::range_size(sourceOps) << ")";615 }616 617 Operation *target = *targetOps.begin();618 Operation *source = *sourceOps.begin();619 620 // Check if the target and source are siblings.621 DiagnosedSilenceableFailure diag = isOpSibling(target, source);622 if (!diag.succeeded())623 return diag;624 625 Operation *fusedLoop;626 /// TODO: Support fusion for loop-like ops besides scf.for and scf.forall.627 if (isForWithIdenticalConfiguration(target, source)) {628 fusedLoop = fuseIndependentSiblingForLoops(629 cast<scf::ForOp>(target), cast<scf::ForOp>(source), rewriter);630 } else if (isForallWithIdenticalConfiguration(target, source)) {631 fusedLoop = fuseIndependentSiblingForallLoops(632 cast<scf::ForallOp>(target), cast<scf::ForallOp>(source), rewriter);633 } else {634 return emitSilenceableFailure(target->getLoc())635 << "operations cannot be fused";636 }637 638 assert(fusedLoop && "failed to fuse operations");639 640 results.set(cast<OpResult>(getFusedLoop()), {fusedLoop});641 return DiagnosedSilenceableFailure::success();642}643 644//===----------------------------------------------------------------------===//645// Transform op registration646//===----------------------------------------------------------------------===//647 648namespace {649class SCFTransformDialectExtension650 : public transform::TransformDialectExtension<651 SCFTransformDialectExtension> {652public:653 MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(SCFTransformDialectExtension)654 655 using Base::Base;656 657 void init() {658 declareGeneratedDialect<affine::AffineDialect>();659 declareGeneratedDialect<func::FuncDialect>();660 661 registerTransformOps<662#define GET_OP_LIST663#include "mlir/Dialect/SCF/TransformOps/SCFTransformOps.cpp.inc"664 >();665 }666};667} // namespace668 669#define GET_OP_CLASSES670#include "mlir/Dialect/SCF/TransformOps/SCFTransformOps.cpp.inc"671 672void mlir::scf::registerTransformDialectExtension(DialectRegistry ®istry) {673 registry.addExtensions<SCFTransformDialectExtension>();674}675