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  rewriter.setInsertionPoint(target);
73 
74  if (!target.getOutputs().empty()) {
75  return emitSilenceableError()
76  << "unsupported shared outputs (didn't bufferize?)";
77  }
78 
79  SmallVector<OpFoldResult> lbs = target.getMixedLowerBound();
80  SmallVector<OpFoldResult> ubs = target.getMixedUpperBound();
81  SmallVector<OpFoldResult> steps = target.getMixedStep();
82 
83  if (getNumResults() != lbs.size()) {
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  auto loc = target.getLoc();
94  for (auto &&[lb, ub, step] : llvm::zip(lbs, ubs, steps)) {
95  Value lbValue = getValueOrCreateConstantIndexOp(rewriter, loc, lb);
96  Value ubValue = getValueOrCreateConstantIndexOp(rewriter, loc, ub);
97  Value stepValue = getValueOrCreateConstantIndexOp(rewriter, loc, step);
98  auto loop = rewriter.create<scf::ForOp>(
99  loc, lbValue, ubValue, stepValue, ValueRange(),
100  [](OpBuilder &, Location, Value, ValueRange) {});
101  ivs.push_back(loop.getInductionVar());
102  rewriter.setInsertionPointToStart(loop.getBody());
103  rewriter.create<scf::YieldOp>(loc);
104  rewriter.setInsertionPointToStart(loop.getBody());
105  }
106  rewriter.eraseOp(target.getBody()->getTerminator());
107  rewriter.inlineBlockBefore(target.getBody(), &*rewriter.getInsertionPoint(),
108  ivs);
109  rewriter.eraseOp(target);
110 
111  for (auto &&[i, iv] : llvm::enumerate(ivs)) {
112  results.set(cast<OpResult>(getTransformed()[i]),
113  {iv.getParentBlock()->getParentOp()});
114  }
116 }
117 
118 //===----------------------------------------------------------------------===//
119 // LoopOutlineOp
120 //===----------------------------------------------------------------------===//
121 
122 /// Wraps the given operation `op` into an `scf.execute_region` operation. Uses
123 /// the provided rewriter for all operations to remain compatible with the
124 /// rewriting infra, as opposed to just splicing the op in place.
125 static scf::ExecuteRegionOp wrapInExecuteRegion(RewriterBase &b,
126  Operation *op) {
127  if (op->getNumRegions() != 1)
128  return nullptr;
130  b.setInsertionPoint(op);
131  scf::ExecuteRegionOp executeRegionOp =
132  b.create<scf::ExecuteRegionOp>(op->getLoc(), op->getResultTypes());
133  {
135  b.setInsertionPointToStart(&executeRegionOp.getRegion().emplaceBlock());
136  Operation *clonedOp = b.cloneWithoutRegions(*op);
137  Region &clonedRegion = clonedOp->getRegions().front();
138  assert(clonedRegion.empty() && "expected empty region");
139  b.inlineRegionBefore(op->getRegions().front(), clonedRegion,
140  clonedRegion.end());
141  b.create<scf::YieldOp>(op->getLoc(), clonedOp->getResults());
142  }
143  b.replaceOp(op, executeRegionOp.getResults());
144  return executeRegionOp;
145 }
146 
148 transform::LoopOutlineOp::apply(transform::TransformRewriter &rewriter,
150  transform::TransformState &state) {
151  SmallVector<Operation *> functions;
154  for (Operation *target : state.getPayloadOps(getTarget())) {
155  Location location = target->getLoc();
156  Operation *symbolTableOp = SymbolTable::getNearestSymbolTable(target);
157  scf::ExecuteRegionOp exec = wrapInExecuteRegion(rewriter, target);
158  if (!exec) {
159  DiagnosedSilenceableFailure diag = emitSilenceableError()
160  << "failed to outline";
161  diag.attachNote(target->getLoc()) << "target op";
162  return diag;
163  }
164  func::CallOp call;
166  rewriter, location, exec.getRegion(), getFuncName(), &call);
167 
168  if (failed(outlined))
169  return emitDefaultDefiniteFailure(target);
170 
171  if (symbolTableOp) {
172  SymbolTable &symbolTable =
173  symbolTables.try_emplace(symbolTableOp, symbolTableOp)
174  .first->getSecond();
175  symbolTable.insert(*outlined);
176  call.setCalleeAttr(FlatSymbolRefAttr::get(*outlined));
177  }
178  functions.push_back(*outlined);
179  calls.push_back(call);
180  }
181  results.set(cast<OpResult>(getFunction()), functions);
182  results.set(cast<OpResult>(getCall()), calls);
184 }
185 
186 //===----------------------------------------------------------------------===//
187 // LoopPeelOp
188 //===----------------------------------------------------------------------===//
189 
191 transform::LoopPeelOp::applyToOne(transform::TransformRewriter &rewriter,
192  scf::ForOp target,
194  transform::TransformState &state) {
195  scf::ForOp result;
196  if (getPeelFront()) {
197  LogicalResult status =
198  scf::peelForLoopFirstIteration(rewriter, target, result);
199  if (failed(status)) {
201  emitSilenceableError() << "failed to peel the first iteration";
202  return diag;
203  }
204  } else {
205  LogicalResult status =
206  scf::peelForLoopAndSimplifyBounds(rewriter, target, result);
207  if (failed(status)) {
208  DiagnosedSilenceableFailure diag = emitSilenceableError()
209  << "failed to peel the last iteration";
210  return diag;
211  }
212  }
213 
214  results.push_back(target);
215  results.push_back(result);
216 
218 }
219 
220 //===----------------------------------------------------------------------===//
221 // LoopPipelineOp
222 //===----------------------------------------------------------------------===//
223 
224 /// Callback for PipeliningOption. Populates `schedule` with the mapping from an
225 /// operation to its logical time position given the iteration interval and the
226 /// read latency. The latter is only relevant for vector transfers.
227 static void
228 loopScheduling(scf::ForOp forOp,
229  std::vector<std::pair<Operation *, unsigned>> &schedule,
230  unsigned iterationInterval, unsigned readLatency) {
231  auto getLatency = [&](Operation *op) -> unsigned {
232  if (isa<vector::TransferReadOp>(op))
233  return readLatency;
234  return 1;
235  };
236 
238  std::map<unsigned, std::vector<Operation *>> wrappedSchedule;
239  for (Operation &op : forOp.getBody()->getOperations()) {
240  if (isa<scf::YieldOp>(op))
241  continue;
242  unsigned earlyCycle = 0;
243  for (Value operand : op.getOperands()) {
244  Operation *def = operand.getDefiningOp();
245  if (!def)
246  continue;
247  earlyCycle = std::max(earlyCycle, opCycles[def] + getLatency(def));
248  }
249  opCycles[&op] = earlyCycle;
250  wrappedSchedule[earlyCycle % iterationInterval].push_back(&op);
251  }
252  for (const auto &it : wrappedSchedule) {
253  for (Operation *op : it.second) {
254  unsigned cycle = opCycles[op];
255  schedule.emplace_back(op, cycle / iterationInterval);
256  }
257  }
258 }
259 
261 transform::LoopPipelineOp::applyToOne(transform::TransformRewriter &rewriter,
262  scf::ForOp target,
264  transform::TransformState &state) {
266  options.getScheduleFn =
267  [this](scf::ForOp forOp,
268  std::vector<std::pair<Operation *, unsigned>> &schedule) mutable {
269  loopScheduling(forOp, schedule, getIterationInterval(),
270  getReadLatency());
271  };
272  scf::ForLoopPipeliningPattern pattern(options, target->getContext());
273  rewriter.setInsertionPoint(target);
274  FailureOr<scf::ForOp> patternResult =
275  scf::pipelineForLoop(rewriter, target, options);
276  if (succeeded(patternResult)) {
277  results.push_back(*patternResult);
279  }
280  return emitDefaultSilenceableFailure(target);
281 }
282 
283 //===----------------------------------------------------------------------===//
284 // LoopPromoteIfOneIterationOp
285 //===----------------------------------------------------------------------===//
286 
287 DiagnosedSilenceableFailure transform::LoopPromoteIfOneIterationOp::applyToOne(
288  transform::TransformRewriter &rewriter, LoopLikeOpInterface target,
290  transform::TransformState &state) {
291  (void)target.promoteIfSingleIteration(rewriter);
293 }
294 
295 void transform::LoopPromoteIfOneIterationOp::getEffects(
297  consumesHandle(getTarget(), effects);
298  modifiesPayload(effects);
299 }
300 
301 //===----------------------------------------------------------------------===//
302 // LoopUnrollOp
303 //===----------------------------------------------------------------------===//
304 
306 transform::LoopUnrollOp::applyToOne(transform::TransformRewriter &rewriter,
307  Operation *op,
309  transform::TransformState &state) {
310  LogicalResult result(failure());
311  if (scf::ForOp scfFor = dyn_cast<scf::ForOp>(op))
312  result = loopUnrollByFactor(scfFor, getFactor());
313  else if (AffineForOp affineFor = dyn_cast<AffineForOp>(op))
314  result = loopUnrollByFactor(affineFor, getFactor());
315 
316  if (failed(result)) {
317  DiagnosedSilenceableFailure diag = emitSilenceableError()
318  << "failed to unroll";
319  return diag;
320  }
322 }
323 
324 //===----------------------------------------------------------------------===//
325 // LoopCoalesceOp
326 //===----------------------------------------------------------------------===//
327 
329 transform::LoopCoalesceOp::applyToOne(transform::TransformRewriter &rewriter,
330  Operation *op,
332  transform::TransformState &state) {
333  LogicalResult result(failure());
334  if (scf::ForOp scfForOp = dyn_cast<scf::ForOp>(op))
335  result = coalescePerfectlyNestedLoops(scfForOp);
336  else if (AffineForOp affineForOp = dyn_cast<AffineForOp>(op))
337  result = coalescePerfectlyNestedLoops(affineForOp);
338 
339  results.push_back(op);
340  if (failed(result)) {
341  DiagnosedSilenceableFailure diag = emitSilenceableError()
342  << "failed to coalesce";
343  return diag;
344  }
346 }
347 
348 //===----------------------------------------------------------------------===//
349 // TakeAssumedBranchOp
350 //===----------------------------------------------------------------------===//
351 /// Replaces the given op with the contents of the given single-block region,
352 /// using the operands of the block terminator to replace operation results.
353 static void replaceOpWithRegion(RewriterBase &rewriter, Operation *op,
354  Region &region) {
355  assert(llvm::hasSingleElement(region) && "expected single-region block");
356  Block *block = &region.front();
357  Operation *terminator = block->getTerminator();
358  ValueRange results = terminator->getOperands();
359  rewriter.inlineBlockBefore(block, op, /*blockArgs=*/{});
360  rewriter.replaceOp(op, results);
361  rewriter.eraseOp(terminator);
362 }
363 
364 DiagnosedSilenceableFailure transform::TakeAssumedBranchOp::applyToOne(
365  transform::TransformRewriter &rewriter, scf::IfOp ifOp,
367  transform::TransformState &state) {
368  rewriter.setInsertionPoint(ifOp);
369  Region &region =
370  getTakeElseBranch() ? ifOp.getElseRegion() : ifOp.getThenRegion();
371  if (!llvm::hasSingleElement(region)) {
372  return emitDefiniteFailure()
373  << "requires an scf.if op with a single-block "
374  << ((getTakeElseBranch()) ? "`else`" : "`then`") << " region";
375  }
376  replaceOpWithRegion(rewriter, ifOp, region);
378 }
379 
380 void transform::TakeAssumedBranchOp::getEffects(
382  onlyReadsHandle(getTarget(), effects);
383  modifiesPayload(effects);
384 }
385 
386 //===----------------------------------------------------------------------===//
387 // LoopFuseSiblingOp
388 //===----------------------------------------------------------------------===//
389 
390 /// Check if `target` and `source` are siblings, in the context that `target`
391 /// is being fused into `source`.
392 ///
393 /// This is a simple check that just checks if both operations are in the same
394 /// block and some checks to ensure that the fused IR does not violate
395 /// dominance.
397  Operation *source) {
398  // Check if both operations are same.
399  if (target == source)
400  return emitSilenceableFailure(source)
401  << "target and source need to be different loops";
402 
403  // Check if both operations are in the same block.
404  if (target->getBlock() != source->getBlock())
405  return emitSilenceableFailure(source)
406  << "target and source are not in the same block";
407 
408  // Check if fusion will violate dominance.
409  DominanceInfo domInfo(source);
410  if (target->isBeforeInBlock(source)) {
411  // Since `target` is before `source`, all users of results of `target`
412  // need to be dominated by `source`.
413  for (Operation *user : target->getUsers()) {
414  if (!domInfo.properlyDominates(source, user, /*enclosingOpOk=*/false)) {
415  return emitSilenceableFailure(target)
416  << "user of results of target should be properly dominated by "
417  "source";
418  }
419  }
420  } else {
421  // Since `target` is after `source`, all values used by `target` need
422  // to dominate `source`.
423 
424  // Check if operands of `target` are dominated by `source`.
425  for (Value operand : target->getOperands()) {
426  Operation *operandOp = operand.getDefiningOp();
427  // Operands without defining operations are block arguments. When `target`
428  // and `source` occur in the same block, these operands dominate `source`.
429  if (!operandOp)
430  continue;
431 
432  // Operand's defining operation should properly dominate `source`.
433  if (!domInfo.properlyDominates(operandOp, source,
434  /*enclosingOpOk=*/false))
435  return emitSilenceableFailure(target)
436  << "operands of target should be properly dominated by source";
437  }
438 
439  // Check if values used by `target` are dominated by `source`.
440  bool failed = false;
441  OpOperand *failedValue = nullptr;
442  visitUsedValuesDefinedAbove(target->getRegions(), [&](OpOperand *operand) {
443  Operation *operandOp = operand->get().getDefiningOp();
444  if (operandOp && !domInfo.properlyDominates(operandOp, source,
445  /*enclosingOpOk=*/false)) {
446  // `operand` is not an argument of an enclosing block and the defining
447  // op of `operand` is outside `target` but does not dominate `source`.
448  failed = true;
449  failedValue = operand;
450  }
451  });
452 
453  if (failed)
454  return emitSilenceableFailure(failedValue->getOwner())
455  << "values used inside regions of target should be properly "
456  "dominated by source";
457  }
458 
460 }
461 
462 /// Check if `target` scf.forall can be fused into `source` scf.forall.
463 ///
464 /// This simply checks if both loops have the same bounds, steps and mapping.
465 /// No attempt is made at checking that the side effects of `target` and
466 /// `source` are independent of each other.
468  Operation *source) {
469  auto targetOp = dyn_cast<scf::ForallOp>(target);
470  auto sourceOp = dyn_cast<scf::ForallOp>(source);
471  if (!targetOp || !sourceOp)
472  return false;
473 
474  return targetOp.getMixedLowerBound() == sourceOp.getMixedLowerBound() &&
475  targetOp.getMixedUpperBound() == sourceOp.getMixedUpperBound() &&
476  targetOp.getMixedStep() == sourceOp.getMixedStep() &&
477  targetOp.getMapping() == sourceOp.getMapping();
478 }
479 
480 /// Check if `target` scf.for can be fused into `source` scf.for.
481 ///
482 /// This simply checks if both loops have the same bounds and steps. No attempt
483 /// is made at checking that the side effects of `target` and `source` are
484 /// independent of each other.
486  Operation *source) {
487  auto targetOp = dyn_cast<scf::ForOp>(target);
488  auto sourceOp = dyn_cast<scf::ForOp>(source);
489  if (!targetOp || !sourceOp)
490  return false;
491 
492  return targetOp.getLowerBound() == sourceOp.getLowerBound() &&
493  targetOp.getUpperBound() == sourceOp.getUpperBound() &&
494  targetOp.getStep() == sourceOp.getStep();
495 }
496 
498 transform::LoopFuseSiblingOp::apply(transform::TransformRewriter &rewriter,
500  transform::TransformState &state) {
501  auto targetOps = state.getPayloadOps(getTarget());
502  auto sourceOps = state.getPayloadOps(getSource());
503 
504  if (!llvm::hasSingleElement(targetOps) ||
505  !llvm::hasSingleElement(sourceOps)) {
506  return emitDefiniteFailure()
507  << "requires exactly one target handle (got "
508  << llvm::range_size(targetOps) << ") and exactly one "
509  << "source handle (got " << llvm::range_size(sourceOps) << ")";
510  }
511 
512  Operation *target = *targetOps.begin();
513  Operation *source = *sourceOps.begin();
514 
515  // Check if the target and source are siblings.
516  DiagnosedSilenceableFailure diag = isOpSibling(target, source);
517  if (!diag.succeeded())
518  return diag;
519 
520  Operation *fusedLoop;
521  /// TODO: Support fusion for loop-like ops besides scf.for and scf.forall.
522  if (isForWithIdenticalConfiguration(target, source)) {
523  fusedLoop = fuseIndependentSiblingForLoops(
524  cast<scf::ForOp>(target), cast<scf::ForOp>(source), rewriter);
525  } else if (isForallWithIdenticalConfiguration(target, source)) {
527  cast<scf::ForallOp>(target), cast<scf::ForallOp>(source), rewriter);
528  } else
529  return emitSilenceableFailure(target->getLoc())
530  << "operations cannot be fused";
531 
532  assert(fusedLoop && "failed to fuse operations");
533 
534  results.set(cast<OpResult>(getFusedLoop()), {fusedLoop});
536 }
537 
538 //===----------------------------------------------------------------------===//
539 // Transform op registration
540 //===----------------------------------------------------------------------===//
541 
542 namespace {
543 class SCFTransformDialectExtension
545  SCFTransformDialectExtension> {
546 public:
547  using Base::Base;
548 
549  void init() {
550  declareGeneratedDialect<affine::AffineDialect>();
551  declareGeneratedDialect<func::FuncDialect>();
552 
553  registerTransformOps<
554 #define GET_OP_LIST
555 #include "mlir/Dialect/SCF/TransformOps/SCFTransformOps.cpp.inc"
556  >();
557  }
558 };
559 } // namespace
560 
561 #define GET_OP_CLASSES
562 #include "mlir/Dialect/SCF/TransformOps/SCFTransformOps.cpp.inc"
563 
565  registry.addExtensions<SCFTransformDialectExtension>();
566 }
static std::string diag(const llvm::Value &value)
static llvm::ManagedStatic< PassManagerOptions > options
static Value max(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:30
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
This class provides support for representing a failure result, or a valid value of type T.
Definition: LogicalResult.h:78
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
This class helps build Operations.
Definition: Builders.h:209
Block::iterator getInsertionPoint() const
Returns the current insertion point of the builder.
Definition: Builders.h:447
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:578
This class represents an operand of an operation.
Definition: Value.h:263
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:399
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:1016
LogicalResult coalescePerfectlyNestedLoops(LoopOpTy op)
Walk either an scf.for or an affine.for to find a band to coalesce.
Definition: LoopUtils.h:304
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 peelForLoopFirstIteration(RewriterBase &rewriter, ForOp forOp, scf::ForOp &partialIteration)
Peel the first iteration out of the scf.for 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 onlyReadsHandle(ValueRange handles, SmallVectorImpl< MemoryEffects::EffectInstance > &effects)
void consumesHandle(ValueRange handles, SmallVectorImpl< MemoryEffects::EffectInstance > &effects)
Populates effects with the memory effects indicating the operation on the given handle value:
void modifiesPayload(SmallVectorImpl< MemoryEffects::EffectInstance > &effects)
Populates effects with the memory effects indicating the access to payload IR resource.
Include the generated interface declarations.
LogicalResult failure(bool isFailure=true)
Utility function to generate a LogicalResult.
Definition: LogicalResult.h:62
DiagnosedSilenceableFailure emitSilenceableFailure(Location loc, const Twine &message={})
Emits a silenceable failure with the given message.
bool succeeded(LogicalResult result)
Utility function that returns true if the provided LogicalResult corresponds to a success value.
Definition: LogicalResult.h:68
DiagnosedDefiniteFailure emitDefiniteFailure(Location loc, const Twine &message={})
Emits a definite failure with the given message.
Value getValueOrCreateConstantIndexOp(OpBuilder &b, Location loc, OpFoldResult ofr)
Converts an OpFoldResult to a Value.
Definition: Utils.cpp:41
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:120
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:907
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:959
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:36
bool failed(LogicalResult result)
Utility function that returns true if the provided LogicalResult corresponds to a failure value.
Definition: LogicalResult.h:72
This class represents an efficient way to signal success or failure.
Definition: LogicalResult.h:26
Options to dictate how loops should be pipelined.
Definition: Transforms.h:113