MLIR  19.0.0git
SCFTransformOps.cpp
Go to the documentation of this file.
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-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
25 #include "mlir/IR/Dominance.h"
26 #include "mlir/IR/OpDefinition.h"
27 
28 using namespace mlir;
29 using namespace mlir::affine;
30 
31 //===----------------------------------------------------------------------===//
32 // Apply...PatternsOp
33 //===----------------------------------------------------------------------===//
34 
35 void transform::ApplyForLoopCanonicalizationPatternsOp::populatePatterns(
36  RewritePatternSet &patterns) {
38 }
39 
40 void transform::ApplySCFStructuralConversionPatternsOp::populatePatterns(
41  TypeConverter &typeConverter, RewritePatternSet &patterns) {
42  scf::populateSCFStructuralTypeConversions(typeConverter, patterns);
43 }
44 
45 void transform::ApplySCFStructuralConversionPatternsOp::
46  populateConversionTargetRules(const TypeConverter &typeConverter,
47  ConversionTarget &conversionTarget) {
49  conversionTarget);
50 }
51 
52 //===----------------------------------------------------------------------===//
53 // ForallToForOp
54 //===----------------------------------------------------------------------===//
55 
57 transform::ForallToForOp::apply(transform::TransformRewriter &rewriter,
60  auto payload = state.getPayloadOps(getTarget());
61  if (!llvm::hasSingleElement(payload))
62  return emitSilenceableError() << "expected a single payload op";
63 
64  auto target = dyn_cast<scf::ForallOp>(*payload.begin());
65  if (!target) {
67  emitSilenceableError() << "expected the payload to be scf.forall";
68  diag.attachNote((*payload.begin())->getLoc()) << "payload op";
69  return diag;
70  }
71 
72  if (!target.getOutputs().empty()) {
73  return emitSilenceableError()
74  << "unsupported shared outputs (didn't bufferize?)";
75  }
76 
77  SmallVector<OpFoldResult> lbs = target.getMixedLowerBound();
78 
79  if (getNumResults() != lbs.size()) {
81  emitSilenceableError()
82  << "op expects as many results (" << getNumResults()
83  << ") as payload has induction variables (" << lbs.size() << ")";
84  diag.attachNote(target.getLoc()) << "payload op";
85  return diag;
86  }
87 
88  SmallVector<Operation *> opResults;
89  if (failed(scf::forallToForLoop(rewriter, target, &opResults))) {
90  DiagnosedSilenceableFailure diag = emitSilenceableError()
91  << "failed to convert forall into for";
92  return diag;
93  }
94 
95  for (auto &&[i, res] : llvm::enumerate(opResults)) {
96  results.set(cast<OpResult>(getTransformed()[i]), {res});
97  }
99 }
100 
101 //===----------------------------------------------------------------------===//
102 // ForallToForOp
103 //===----------------------------------------------------------------------===//
104 
106 transform::ForallToParallelOp::apply(transform::TransformRewriter &rewriter,
108  transform::TransformState &state) {
109  auto payload = state.getPayloadOps(getTarget());
110  if (!llvm::hasSingleElement(payload))
111  return emitSilenceableError() << "expected a single payload op";
112 
113  auto target = dyn_cast<scf::ForallOp>(*payload.begin());
114  if (!target) {
116  emitSilenceableError() << "expected the payload to be scf.forall";
117  diag.attachNote((*payload.begin())->getLoc()) << "payload op";
118  return diag;
119  }
120 
121  if (!target.getOutputs().empty()) {
122  return emitSilenceableError()
123  << "unsupported shared outputs (didn't bufferize?)";
124  }
125 
126  if (getNumResults() != 1) {
127  DiagnosedSilenceableFailure diag = emitSilenceableError()
128  << "op expects one result, given "
129  << getNumResults();
130  diag.attachNote(target.getLoc()) << "payload op";
131  return diag;
132  }
133 
134  scf::ParallelOp opResult;
135  if (failed(scf::forallToParallelLoop(rewriter, target, &opResult))) {
137  emitSilenceableError() << "failed to convert forall into parallel";
138  return diag;
139  }
140 
141  results.set(cast<OpResult>(getTransformed()[0]), {opResult});
143 }
144 
145 //===----------------------------------------------------------------------===//
146 // LoopOutlineOp
147 //===----------------------------------------------------------------------===//
148 
149 /// Wraps the given operation `op` into an `scf.execute_region` operation. Uses
150 /// the provided rewriter for all operations to remain compatible with the
151 /// rewriting infra, as opposed to just splicing the op in place.
152 static scf::ExecuteRegionOp wrapInExecuteRegion(RewriterBase &b,
153  Operation *op) {
154  if (op->getNumRegions() != 1)
155  return nullptr;
157  b.setInsertionPoint(op);
158  scf::ExecuteRegionOp executeRegionOp =
159  b.create<scf::ExecuteRegionOp>(op->getLoc(), op->getResultTypes());
160  {
162  b.setInsertionPointToStart(&executeRegionOp.getRegion().emplaceBlock());
163  Operation *clonedOp = b.cloneWithoutRegions(*op);
164  Region &clonedRegion = clonedOp->getRegions().front();
165  assert(clonedRegion.empty() && "expected empty region");
166  b.inlineRegionBefore(op->getRegions().front(), clonedRegion,
167  clonedRegion.end());
168  b.create<scf::YieldOp>(op->getLoc(), clonedOp->getResults());
169  }
170  b.replaceOp(op, executeRegionOp.getResults());
171  return executeRegionOp;
172 }
173 
175 transform::LoopOutlineOp::apply(transform::TransformRewriter &rewriter,
177  transform::TransformState &state) {
178  SmallVector<Operation *> functions;
181  for (Operation *target : state.getPayloadOps(getTarget())) {
182  Location location = target->getLoc();
183  Operation *symbolTableOp = SymbolTable::getNearestSymbolTable(target);
184  scf::ExecuteRegionOp exec = wrapInExecuteRegion(rewriter, target);
185  if (!exec) {
186  DiagnosedSilenceableFailure diag = emitSilenceableError()
187  << "failed to outline";
188  diag.attachNote(target->getLoc()) << "target op";
189  return diag;
190  }
191  func::CallOp call;
192  FailureOr<func::FuncOp> outlined = outlineSingleBlockRegion(
193  rewriter, location, exec.getRegion(), getFuncName(), &call);
194 
195  if (failed(outlined))
196  return emitDefaultDefiniteFailure(target);
197 
198  if (symbolTableOp) {
199  SymbolTable &symbolTable =
200  symbolTables.try_emplace(symbolTableOp, symbolTableOp)
201  .first->getSecond();
202  symbolTable.insert(*outlined);
203  call.setCalleeAttr(FlatSymbolRefAttr::get(*outlined));
204  }
205  functions.push_back(*outlined);
206  calls.push_back(call);
207  }
208  results.set(cast<OpResult>(getFunction()), functions);
209  results.set(cast<OpResult>(getCall()), calls);
211 }
212 
213 //===----------------------------------------------------------------------===//
214 // LoopPeelOp
215 //===----------------------------------------------------------------------===//
216 
218 transform::LoopPeelOp::applyToOne(transform::TransformRewriter &rewriter,
219  scf::ForOp target,
221  transform::TransformState &state) {
222  scf::ForOp result;
223  if (getPeelFront()) {
224  LogicalResult status =
225  scf::peelForLoopFirstIteration(rewriter, target, result);
226  if (failed(status)) {
228  emitSilenceableError() << "failed to peel the first iteration";
229  return diag;
230  }
231  } else {
232  LogicalResult status =
233  scf::peelForLoopAndSimplifyBounds(rewriter, target, result);
234  if (failed(status)) {
235  DiagnosedSilenceableFailure diag = emitSilenceableError()
236  << "failed to peel the last iteration";
237  return diag;
238  }
239  }
240 
241  results.push_back(target);
242  results.push_back(result);
243 
245 }
246 
247 //===----------------------------------------------------------------------===//
248 // LoopPipelineOp
249 //===----------------------------------------------------------------------===//
250 
251 /// Callback for PipeliningOption. Populates `schedule` with the mapping from an
252 /// operation to its logical time position given the iteration interval and the
253 /// read latency. The latter is only relevant for vector transfers.
254 static void
255 loopScheduling(scf::ForOp forOp,
256  std::vector<std::pair<Operation *, unsigned>> &schedule,
257  unsigned iterationInterval, unsigned readLatency) {
258  auto getLatency = [&](Operation *op) -> unsigned {
259  if (isa<vector::TransferReadOp>(op))
260  return readLatency;
261  return 1;
262  };
263 
264  std::optional<int64_t> ubConstant = getConstantIntValue(forOp.getUpperBound());
265  std::optional<int64_t> lbConstant = getConstantIntValue(forOp.getLowerBound());
267  std::map<unsigned, std::vector<Operation *>> wrappedSchedule;
268  for (Operation &op : forOp.getBody()->getOperations()) {
269  if (isa<scf::YieldOp>(op))
270  continue;
271  unsigned earlyCycle = 0;
272  for (Value operand : op.getOperands()) {
273  Operation *def = operand.getDefiningOp();
274  if (!def)
275  continue;
276  if (ubConstant && lbConstant) {
277  unsigned ubInt = ubConstant.value();
278  unsigned lbInt = lbConstant.value();
279  auto minLatency = std::min(ubInt - lbInt - 1, getLatency(def));
280  earlyCycle = std::max(earlyCycle, opCycles[def] + minLatency);
281  } else {
282  earlyCycle = std::max(earlyCycle, opCycles[def] + getLatency(def));
283  }
284  }
285  opCycles[&op] = earlyCycle;
286  wrappedSchedule[earlyCycle % iterationInterval].push_back(&op);
287  }
288  for (const auto &it : wrappedSchedule) {
289  for (Operation *op : it.second) {
290  unsigned cycle = opCycles[op];
291  schedule.emplace_back(op, cycle / iterationInterval);
292  }
293  }
294 }
295 
297 transform::LoopPipelineOp::applyToOne(transform::TransformRewriter &rewriter,
298  scf::ForOp target,
300  transform::TransformState &state) {
302  options.getScheduleFn =
303  [this](scf::ForOp forOp,
304  std::vector<std::pair<Operation *, unsigned>> &schedule) mutable {
305  loopScheduling(forOp, schedule, getIterationInterval(),
306  getReadLatency());
307  };
308  scf::ForLoopPipeliningPattern pattern(options, target->getContext());
309  rewriter.setInsertionPoint(target);
310  FailureOr<scf::ForOp> patternResult =
311  scf::pipelineForLoop(rewriter, target, options);
312  if (succeeded(patternResult)) {
313  results.push_back(*patternResult);
315  }
316  return emitDefaultSilenceableFailure(target);
317 }
318 
319 //===----------------------------------------------------------------------===//
320 // LoopPromoteIfOneIterationOp
321 //===----------------------------------------------------------------------===//
322 
323 DiagnosedSilenceableFailure transform::LoopPromoteIfOneIterationOp::applyToOne(
324  transform::TransformRewriter &rewriter, LoopLikeOpInterface target,
326  transform::TransformState &state) {
327  (void)target.promoteIfSingleIteration(rewriter);
329 }
330 
331 void transform::LoopPromoteIfOneIterationOp::getEffects(
333  consumesHandle(getTargetMutable(), effects);
334  modifiesPayload(effects);
335 }
336 
337 //===----------------------------------------------------------------------===//
338 // LoopUnrollOp
339 //===----------------------------------------------------------------------===//
340 
342 transform::LoopUnrollOp::applyToOne(transform::TransformRewriter &rewriter,
343  Operation *op,
345  transform::TransformState &state) {
346  LogicalResult result(failure());
347  if (scf::ForOp scfFor = dyn_cast<scf::ForOp>(op))
348  result = loopUnrollByFactor(scfFor, getFactor());
349  else if (AffineForOp affineFor = dyn_cast<AffineForOp>(op))
350  result = loopUnrollByFactor(affineFor, getFactor());
351  else
352  return emitSilenceableError()
353  << "failed to unroll, incorrect type of payload";
354 
355  if (failed(result))
356  return emitSilenceableError() << "failed to unroll";
357 
359 }
360 
361 //===----------------------------------------------------------------------===//
362 // LoopUnrollAndJamOp
363 //===----------------------------------------------------------------------===//
364 
365 DiagnosedSilenceableFailure transform::LoopUnrollAndJamOp::applyToOne(
368  transform::TransformState &state) {
369  LogicalResult result(failure());
370  if (scf::ForOp scfFor = dyn_cast<scf::ForOp>(op))
371  result = loopUnrollJamByFactor(scfFor, getFactor());
372  else if (AffineForOp affineFor = dyn_cast<AffineForOp>(op))
373  result = loopUnrollJamByFactor(affineFor, getFactor());
374  else
375  return emitSilenceableError()
376  << "failed to unroll and jam, incorrect type of payload";
377 
378  if (failed(result))
379  return emitSilenceableError() << "failed to unroll and jam";
380 
382 }
383 
384 //===----------------------------------------------------------------------===//
385 // LoopCoalesceOp
386 //===----------------------------------------------------------------------===//
387 
389 transform::LoopCoalesceOp::applyToOne(transform::TransformRewriter &rewriter,
390  Operation *op,
392  transform::TransformState &state) {
393  LogicalResult result(failure());
394  if (scf::ForOp scfForOp = dyn_cast<scf::ForOp>(op))
395  result = coalescePerfectlyNestedSCFForLoops(scfForOp);
396  else if (AffineForOp affineForOp = dyn_cast<AffineForOp>(op))
397  result = coalescePerfectlyNestedAffineLoops(affineForOp);
398 
399  results.push_back(op);
400  if (failed(result)) {
401  DiagnosedSilenceableFailure diag = emitSilenceableError()
402  << "failed to coalesce";
403  return diag;
404  }
406 }
407 
408 //===----------------------------------------------------------------------===//
409 // TakeAssumedBranchOp
410 //===----------------------------------------------------------------------===//
411 /// Replaces the given op with the contents of the given single-block region,
412 /// using the operands of the block terminator to replace operation results.
413 static void replaceOpWithRegion(RewriterBase &rewriter, Operation *op,
414  Region &region) {
415  assert(llvm::hasSingleElement(region) && "expected single-region block");
416  Block *block = &region.front();
417  Operation *terminator = block->getTerminator();
418  ValueRange results = terminator->getOperands();
419  rewriter.inlineBlockBefore(block, op, /*blockArgs=*/{});
420  rewriter.replaceOp(op, results);
421  rewriter.eraseOp(terminator);
422 }
423 
424 DiagnosedSilenceableFailure transform::TakeAssumedBranchOp::applyToOne(
425  transform::TransformRewriter &rewriter, scf::IfOp ifOp,
427  transform::TransformState &state) {
428  rewriter.setInsertionPoint(ifOp);
429  Region &region =
430  getTakeElseBranch() ? ifOp.getElseRegion() : ifOp.getThenRegion();
431  if (!llvm::hasSingleElement(region)) {
432  return emitDefiniteFailure()
433  << "requires an scf.if op with a single-block "
434  << ((getTakeElseBranch()) ? "`else`" : "`then`") << " region";
435  }
436  replaceOpWithRegion(rewriter, ifOp, region);
438 }
439 
440 void transform::TakeAssumedBranchOp::getEffects(
442  onlyReadsHandle(getTargetMutable(), effects);
443  modifiesPayload(effects);
444 }
445 
446 //===----------------------------------------------------------------------===//
447 // LoopFuseSiblingOp
448 //===----------------------------------------------------------------------===//
449 
450 /// Check if `target` and `source` are siblings, in the context that `target`
451 /// is being fused into `source`.
452 ///
453 /// This is a simple check that just checks if both operations are in the same
454 /// block and some checks to ensure that the fused IR does not violate
455 /// dominance.
457  Operation *source) {
458  // Check if both operations are same.
459  if (target == source)
460  return emitSilenceableFailure(source)
461  << "target and source need to be different loops";
462 
463  // Check if both operations are in the same block.
464  if (target->getBlock() != source->getBlock())
465  return emitSilenceableFailure(source)
466  << "target and source are not in the same block";
467 
468  // Check if fusion will violate dominance.
469  DominanceInfo domInfo(source);
470  if (target->isBeforeInBlock(source)) {
471  // Since `target` is before `source`, all users of results of `target`
472  // need to be dominated by `source`.
473  for (Operation *user : target->getUsers()) {
474  if (!domInfo.properlyDominates(source, user, /*enclosingOpOk=*/false)) {
475  return emitSilenceableFailure(target)
476  << "user of results of target should be properly dominated by "
477  "source";
478  }
479  }
480  } else {
481  // Since `target` is after `source`, all values used by `target` need
482  // to dominate `source`.
483 
484  // Check if operands of `target` are dominated by `source`.
485  for (Value operand : target->getOperands()) {
486  Operation *operandOp = operand.getDefiningOp();
487  // Operands without defining operations are block arguments. When `target`
488  // and `source` occur in the same block, these operands dominate `source`.
489  if (!operandOp)
490  continue;
491 
492  // Operand's defining operation should properly dominate `source`.
493  if (!domInfo.properlyDominates(operandOp, source,
494  /*enclosingOpOk=*/false))
495  return emitSilenceableFailure(target)
496  << "operands of target should be properly dominated by source";
497  }
498 
499  // Check if values used by `target` are dominated by `source`.
500  bool failed = false;
501  OpOperand *failedValue = nullptr;
502  visitUsedValuesDefinedAbove(target->getRegions(), [&](OpOperand *operand) {
503  Operation *operandOp = operand->get().getDefiningOp();
504  if (operandOp && !domInfo.properlyDominates(operandOp, source,
505  /*enclosingOpOk=*/false)) {
506  // `operand` is not an argument of an enclosing block and the defining
507  // op of `operand` is outside `target` but does not dominate `source`.
508  failed = true;
509  failedValue = operand;
510  }
511  });
512 
513  if (failed)
514  return emitSilenceableFailure(failedValue->getOwner())
515  << "values used inside regions of target should be properly "
516  "dominated by source";
517  }
518 
520 }
521 
522 /// Check if `target` scf.forall can be fused into `source` scf.forall.
523 ///
524 /// This simply checks if both loops have the same bounds, steps and mapping.
525 /// No attempt is made at checking that the side effects of `target` and
526 /// `source` are independent of each other.
528  Operation *source) {
529  auto targetOp = dyn_cast<scf::ForallOp>(target);
530  auto sourceOp = dyn_cast<scf::ForallOp>(source);
531  if (!targetOp || !sourceOp)
532  return false;
533 
534  return targetOp.getMixedLowerBound() == sourceOp.getMixedLowerBound() &&
535  targetOp.getMixedUpperBound() == sourceOp.getMixedUpperBound() &&
536  targetOp.getMixedStep() == sourceOp.getMixedStep() &&
537  targetOp.getMapping() == sourceOp.getMapping();
538 }
539 
540 /// Check if `target` scf.for can be fused into `source` scf.for.
541 ///
542 /// This simply checks if both loops have the same bounds and steps. No attempt
543 /// is made at checking that the side effects of `target` and `source` are
544 /// independent of each other.
546  Operation *source) {
547  auto targetOp = dyn_cast<scf::ForOp>(target);
548  auto sourceOp = dyn_cast<scf::ForOp>(source);
549  if (!targetOp || !sourceOp)
550  return false;
551 
552  return targetOp.getLowerBound() == sourceOp.getLowerBound() &&
553  targetOp.getUpperBound() == sourceOp.getUpperBound() &&
554  targetOp.getStep() == sourceOp.getStep();
555 }
556 
558 transform::LoopFuseSiblingOp::apply(transform::TransformRewriter &rewriter,
560  transform::TransformState &state) {
561  auto targetOps = state.getPayloadOps(getTarget());
562  auto sourceOps = state.getPayloadOps(getSource());
563 
564  if (!llvm::hasSingleElement(targetOps) ||
565  !llvm::hasSingleElement(sourceOps)) {
566  return emitDefiniteFailure()
567  << "requires exactly one target handle (got "
568  << llvm::range_size(targetOps) << ") and exactly one "
569  << "source handle (got " << llvm::range_size(sourceOps) << ")";
570  }
571 
572  Operation *target = *targetOps.begin();
573  Operation *source = *sourceOps.begin();
574 
575  // Check if the target and source are siblings.
576  DiagnosedSilenceableFailure diag = isOpSibling(target, source);
577  if (!diag.succeeded())
578  return diag;
579 
580  Operation *fusedLoop;
581  /// TODO: Support fusion for loop-like ops besides scf.for and scf.forall.
582  if (isForWithIdenticalConfiguration(target, source)) {
583  fusedLoop = fuseIndependentSiblingForLoops(
584  cast<scf::ForOp>(target), cast<scf::ForOp>(source), rewriter);
585  } else if (isForallWithIdenticalConfiguration(target, source)) {
587  cast<scf::ForallOp>(target), cast<scf::ForallOp>(source), rewriter);
588  } else
589  return emitSilenceableFailure(target->getLoc())
590  << "operations cannot be fused";
591 
592  assert(fusedLoop && "failed to fuse operations");
593 
594  results.set(cast<OpResult>(getFusedLoop()), {fusedLoop});
596 }
597 
598 //===----------------------------------------------------------------------===//
599 // Transform op registration
600 //===----------------------------------------------------------------------===//
601 
602 namespace {
603 class SCFTransformDialectExtension
605  SCFTransformDialectExtension> {
606 public:
607  using Base::Base;
608 
609  void init() {
610  declareGeneratedDialect<affine::AffineDialect>();
611  declareGeneratedDialect<func::FuncDialect>();
612 
613  registerTransformOps<
614 #define GET_OP_LIST
615 #include "mlir/Dialect/SCF/TransformOps/SCFTransformOps.cpp.inc"
616  >();
617  }
618 };
619 } // namespace
620 
621 #define GET_OP_CLASSES
622 #include "mlir/Dialect/SCF/TransformOps/SCFTransformOps.cpp.inc"
623 
625  registry.addExtensions<SCFTransformDialectExtension>();
626 }
static std::string diag(const llvm::Value &value)
static llvm::ManagedStatic< PassManagerOptions > options
static Value max(ImplicitLocOpBuilder &builder, Value value, Value bound)
static Value min(ImplicitLocOpBuilder &builder, Value value, Value bound)
static bool isForWithIdenticalConfiguration(Operation *target, Operation *source)
Check if target scf.for can be fused into source scf.for.
static DiagnosedSilenceableFailure isOpSibling(Operation *target, Operation *source)
Check if target and source are siblings, in the context that target is being fused into source.
static void loopScheduling(scf::ForOp forOp, std::vector< std::pair< Operation *, unsigned >> &schedule, unsigned iterationInterval, unsigned readLatency)
Callback for PipeliningOption.
static bool isForallWithIdenticalConfiguration(Operation *target, Operation *source)
Check if target scf.forall can be fused into source scf.forall.
static void replaceOpWithRegion(RewriterBase &rewriter, Operation *op, Region &region)
Replaces the given op with the contents of the given single-block region, using the operands of the b...
static scf::ExecuteRegionOp wrapInExecuteRegion(RewriterBase &b, Operation *op)
Wraps the given operation op into an scf.execute_region operation.
Block represents an ordered list of Operations.
Definition: Block.h:31
Operation * getTerminator()
Get the terminator operation of this block.
Definition: Block.cpp:243
This class describes a specific conversion target.
The result of a transform IR operation application.
static DiagnosedSilenceableFailure success()
Constructs a DiagnosedSilenceableFailure in the success state.
The DialectRegistry maps a dialect namespace to a constructor for the matching dialect.
void addExtensions()
Add the given extensions to the registry.
A class for computing basic dominance information.
Definition: Dominance.h:136
bool properlyDominates(Operation *a, Operation *b, bool enclosingOpOk=true) const
Return true if operation A properly dominates operation B, i.e.
Definition: Dominance.h:149
static FlatSymbolRefAttr get(StringAttr value)
Construct a symbol reference for the given value name.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition: Location.h:63
RAII guard to reset the insertion point of the builder when destroyed.
Definition: Builders.h:350
void setInsertionPointToStart(Block *block)
Sets the insertion point to the start of the specified block.
Definition: Builders.h:433
void setInsertionPoint(Block *block, Block::iterator insertPoint)
Set the insertion point to the specified location.
Definition: Builders.h:400
Operation * create(const OperationState &state)
Creates an operation given the fields represented as an OperationState.
Definition: Builders.cpp:464
Operation * cloneWithoutRegions(Operation &op, IRMapping &mapper)
Creates a deep copy of this operation but keep the operation regions empty.
Definition: Builders.h:586
This class represents an operand of an operation.
Definition: Value.h:267
Operation is the basic unit of execution within MLIR.
Definition: Operation.h:88
bool isBeforeInBlock(Operation *other)
Given an operation 'other' that is within the same parent block, return whether the current operation...
Definition: Operation.cpp:386
unsigned getNumRegions()
Returns the number of regions held by this operation.
Definition: Operation.h:669
Location getLoc()
The source location the operation was defined or derived from.
Definition: Operation.h:223
Block * getBlock()
Returns the operation block that contains this operation.
Definition: Operation.h:213
MutableArrayRef< Region > getRegions()
Returns the regions held by this operation.
Definition: Operation.h:672
result_type_range getResultTypes()
Definition: Operation.h:423
operand_range getOperands()
Returns an iterator on the underlying Value's.
Definition: Operation.h:373
user_range getUsers()
Returns a range of all users.
Definition: Operation.h:869
result_range getResults()
Definition: Operation.h:410
This class contains a list of basic blocks and a link to the parent operation it is attached to.
Definition: Region.h:26
bool empty()
Definition: Region.h:60
iterator end()
Definition: Region.h:56
Block & front()
Definition: Region.h:65
This class coordinates the application of a rewrite on a set of IR, providing a way for clients to tr...
Definition: PatternMatch.h:400
virtual void replaceOp(Operation *op, ValueRange newValues)
Replace the results of the given (original) operation with the specified list of values (replacements...
virtual void eraseOp(Operation *op)
This method erases an operation that is known to have no uses.
void inlineRegionBefore(Region &region, Region &parent, Region::iterator before)
Move the blocks that belong to "region" before the given position in another region "parent".
virtual void inlineBlockBefore(Block *source, Block *dest, Block::iterator before, ValueRange argValues=std::nullopt)
Inline the operations of block 'source' into block 'dest' before the given position.
This class allows for representing and managing the symbol table used by operations with the 'SymbolT...
Definition: SymbolTable.h:24
StringAttr insert(Operation *symbol, Block::iterator insertPt={})
Insert a new symbol into the table, and rename it as necessary to avoid collisions.
static Operation * getNearestSymbolTable(Operation *from)
Returns the nearest symbol table from a given operation from.
Type conversion class.
This class provides an abstraction over the different types of ranges over Values.
Definition: ValueRange.h:381
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition: Value.h:96
A list of results of applying a transform op with ApplyEachOpTrait to a single payload operation,...
void push_back(Operation *op)
Appends an element to the list.
Base class for extensions of the Transform dialect that supports injecting operations into the Transf...
Local mapping between values defined by a specific op implementing the TransformOpInterface and the p...
void set(OpResult value, Range &&ops)
Indicates that the result of the transform IR op at the given position corresponds to the given list ...
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.
LogicalResult loopUnrollByFactor(AffineForOp forOp, uint64_t unrollFactor, function_ref< void(unsigned, Operation *, OpBuilder)> annotateFn=nullptr, bool cleanUpUnroll=false)
Unrolls this for operation by the specified unroll factor.
Definition: LoopUtils.cpp:1005
LogicalResult loopUnrollJamByFactor(AffineForOp forOp, uint64_t unrollJamFactor)
Unrolls and jams this loop by the specified factor.
Definition: LoopUtils.cpp:1095
LogicalResult coalescePerfectlyNestedAffineLoops(AffineForOp op)
Walk an affine.for to find a band to coalesce.
Definition: LoopUtils.cpp:2730
constexpr void enumerate(std::tuple< Tys... > &tuple, CallbackT &&callback)
Definition: Matchers.h:285
void registerTransformDialectExtension(DialectRegistry &registry)
LogicalResult peelForLoopAndSimplifyBounds(RewriterBase &rewriter, ForOp forOp, scf::ForOp &partialIteration)
Rewrite a for loop with bounds/step that potentially do not divide evenly into a for loop where the s...
LogicalResult forallToForLoop(RewriterBase &rewriter, ForallOp forallOp, SmallVectorImpl< Operation * > *results=nullptr)
Try converting scf.forall into a set of nested scf.for loops.
LogicalResult peelForLoopFirstIteration(RewriterBase &rewriter, ForOp forOp, scf::ForOp &partialIteration)
Peel the first iteration out of the scf.for loop.
LogicalResult forallToParallelLoop(RewriterBase &rewriter, ForallOp forallOp, ParallelOp *result=nullptr)
Try converting scf.forall into an scf.parallel loop.
void populateSCFForLoopCanonicalizationPatterns(RewritePatternSet &patterns)
Populate patterns for canonicalizing operations inside SCF loop bodies.
FailureOr< ForOp > pipelineForLoop(RewriterBase &rewriter, ForOp forOp, const PipeliningOption &options, bool *modifiedIR=nullptr)
Generate a pipelined version of the scf.for loop based on the schedule given as option.
void populateSCFStructuralTypeConversions(TypeConverter &typeConverter, RewritePatternSet &patterns)
Similar to populateSCFStructuralTypeConversionsAndLegality but does not populate the conversion targe...
void populateSCFStructuralTypeConversionTarget(const TypeConverter &typeConverter, ConversionTarget &target)
Updates the ConversionTarget with dynamic legality of SCF operations based on the provided type conve...
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.
std::optional< int64_t > getConstantIntValue(OpFoldResult ofr)
If ofr is a constant integer or an IntegerAttr, return the integer.
LogicalResult coalescePerfectlyNestedSCFForLoops(scf::ForOp op)
Walk an affine.for to find a band to coalesce.
Definition: Utils.cpp:887
DiagnosedSilenceableFailure emitSilenceableFailure(Location loc, const Twine &message={})
Emits a silenceable failure with the given message.
DiagnosedDefiniteFailure emitDefiniteFailure(Location loc, const Twine &message={})
Emits a definite failure with the given message.
FailureOr< func::FuncOp > outlineSingleBlockRegion(RewriterBase &rewriter, Location loc, Region &region, StringRef funcName, func::CallOp *callOp=nullptr)
Outline a region with a single block into a new FuncOp.
Definition: Utils.cpp:118
scf::ForallOp fuseIndependentSiblingForallLoops(scf::ForallOp target, scf::ForallOp source, RewriterBase &rewriter)
Given two scf.forall loops, target and source, fuses target into source.
Definition: Utils.cpp:1265
scf::ForOp fuseIndependentSiblingForLoops(scf::ForOp target, scf::ForOp source, RewriterBase &rewriter)
Given two scf.for loops, target and source, fuses target into source.
Definition: Utils.cpp:1317
void visitUsedValuesDefinedAbove(Region &region, Region &limit, function_ref< void(OpOperand *)> callback)
Calls callback for each use of a value within region or its descendants that was defined at the ances...
Definition: RegionUtils.cpp:35
Options to dictate how loops should be pipelined.
Definition: Transforms.h:123