MLIR  20.0.0git
BufferizableOpInterfaceImpl.cpp
Go to the documentation of this file.
1 //===- BufferizableOpInterfaceImpl.cpp - Impl. of BufferizableOpInterface -===//
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 
22 #include "mlir/IR/Dialect.h"
23 #include "mlir/IR/Operation.h"
24 
25 using namespace mlir;
26 using namespace mlir::bufferization;
27 using namespace mlir::tensor;
28 
29 namespace mlir {
30 namespace tensor {
31 namespace {
32 
33 struct CastOpInterface
34  : public BufferizableOpInterface::ExternalModel<CastOpInterface,
35  tensor::CastOp> {
36  bool bufferizesToMemoryRead(Operation *op, OpOperand &opOperand,
37  const AnalysisState &state) const {
38  return false;
39  }
40 
41  bool bufferizesToMemoryWrite(Operation *op, OpOperand &opOperand,
42  const AnalysisState &state) const {
43  return false;
44  }
45 
46  AliasingValueList getAliasingValues(Operation *op, OpOperand &opOperand,
47  const AnalysisState &state) const {
48  return {{op->getResult(0), BufferRelation::Equivalent}};
49  }
50 
51  FailureOr<BaseMemRefType>
53  SmallVector<Value> &invocationStack) const {
54  auto castOp = cast<tensor::CastOp>(op);
55  auto maybeSrcBufferType = bufferization::getBufferType(
56  castOp.getSource(), options, invocationStack);
57  if (failed(maybeSrcBufferType))
58  return failure();
59  Attribute memorySpace = maybeSrcBufferType->getMemorySpace();
60 
61  // Note: `getMemRefTypeWithFullyDynamicLayout` returns an unranked memref
62  // type in case the input is an unranked tensor type.
63 
64  // Case 1: Casting an unranked tensor
65  if (isa<UnrankedTensorType>(castOp.getSource().getType())) {
66  // When casting to a ranked tensor, we cannot infer any static offset or
67  // strides from the source. Assume fully dynamic.
68  return getMemRefTypeWithFullyDynamicLayout(castOp.getType(), memorySpace);
69  }
70 
71  // Case 2: Casting to an unranked tensor type
72  if (isa<UnrankedTensorType>(castOp.getType())) {
73  return getMemRefTypeWithFullyDynamicLayout(castOp.getType(), memorySpace);
74  }
75 
76  // Case 3: Ranked tensor -> ranked tensor. The offsets and strides do not
77  // change.
78  auto rankedResultType = cast<RankedTensorType>(castOp.getType());
79  return MemRefType::get(
80  rankedResultType.getShape(), rankedResultType.getElementType(),
81  llvm::cast<MemRefType>(*maybeSrcBufferType).getLayout(), memorySpace);
82  }
83 
84  LogicalResult bufferize(Operation *op, RewriterBase &rewriter,
85  const BufferizationOptions &options) const {
86  auto castOp = cast<tensor::CastOp>(op);
87 
88  // The result buffer still has the old (pre-cast) type.
89  FailureOr<Value> resultBuffer =
90  getBuffer(rewriter, castOp.getSource(), options);
91  if (failed(resultBuffer))
92  return failure();
93 
94  // Compute the new type.
95  auto resultMemRefType =
96  bufferization::getBufferType(castOp.getResult(), options);
97  if (failed(resultMemRefType))
98  return failure();
99  if (resultBuffer->getType() == *resultMemRefType) {
100  // This cast is a no-op.
101  replaceOpWithBufferizedValues(rewriter, op, *resultBuffer);
102  return success();
103  }
104 
105  // Replace the op with a memref.cast.
106  assert(memref::CastOp::areCastCompatible(resultBuffer->getType(),
107  *resultMemRefType) &&
108  "CallOp::bufferize: cast incompatible");
109  replaceOpWithNewBufferizedOp<memref::CastOp>(
110  rewriter, op, *resultMemRefType, *resultBuffer);
111 
112  return success();
113  }
114 };
115 
116 /// Bufferization of tensor.collapse_shape. Replace with memref.collapse_shape.
117 struct CollapseShapeOpInterface
118  : public BufferizableOpInterface::ExternalModel<CollapseShapeOpInterface,
119  tensor::CollapseShapeOp> {
120  bool bufferizesToMemoryRead(Operation *op, OpOperand &opOperand,
121  const AnalysisState &state) const {
122  // tensor.collapse_shape may reallocate, at which point the source buffer is
123  // copied. I.e., there will be a memory read side effect on the bufferized
124  // source. This function conservatively returns "true" because whether a
125  // copy will be created or not is not known at this point.
126  return true;
127  }
128 
129  bool bufferizesToMemoryWrite(Operation *op, OpOperand &opOperand,
130  const AnalysisState &state) const {
131  return false;
132  }
133 
134  AliasingValueList getAliasingValues(Operation *op, OpOperand &opOperand,
135  const AnalysisState &state) const {
136  // TODO: CollapseShapeOp may allocate at runtime.
137  return {{op->getOpResult(0), BufferRelation::Equivalent}};
138  }
139 
140  FailureOr<BaseMemRefType>
142  SmallVector<Value> &invocationStack) const {
143  auto collapseShapeOp = cast<tensor::CollapseShapeOp>(op);
144  auto maybeSrcBufferType = bufferization::getBufferType(
145  collapseShapeOp.getSrc(), options, invocationStack);
146  if (failed(maybeSrcBufferType))
147  return failure();
148  auto srcBufferType = llvm::cast<MemRefType>(*maybeSrcBufferType);
149  bool canBeCollapsed = memref::CollapseShapeOp::isGuaranteedCollapsible(
150  srcBufferType, collapseShapeOp.getReassociationIndices());
151 
152  if (!canBeCollapsed) {
153  // If dims cannot be collapsed, this op bufferizes to a new allocation.
154  RankedTensorType tensorResultType = collapseShapeOp.getResultType();
156  tensorResultType, srcBufferType.getMemorySpace());
157  }
158 
159  return memref::CollapseShapeOp::computeCollapsedType(
160  srcBufferType, collapseShapeOp.getReassociationIndices());
161  }
162 
163  LogicalResult bufferize(Operation *op, RewriterBase &rewriter,
164  const BufferizationOptions &options) const {
165  auto collapseShapeOp = cast<tensor::CollapseShapeOp>(op);
166  RankedTensorType tensorResultType = collapseShapeOp.getResultType();
167  FailureOr<Value> maybeBuffer =
168  getBuffer(rewriter, collapseShapeOp.getSrc(), options);
169  if (failed(maybeBuffer))
170  return failure();
171  Value buffer = *maybeBuffer;
172  auto bufferType = cast<MemRefType>(buffer.getType());
173 
174  if (tensorResultType.getRank() == 0) {
175  // 0-d collapses must go through a different op builder.
176  MemRefType resultType;
177 
178  if (bufferType.getLayout().isIdentity()) {
179  // Standard layout: result type has no offset.
180  MemRefLayoutAttrInterface layout;
181  resultType = MemRefType::get({}, tensorResultType.getElementType(),
182  layout, bufferType.getMemorySpace());
183  } else {
184  // Source memref has a layout map: result type has the same offset as
185  // the source type.
186  SmallVector<int64_t> strides;
187  int64_t offset;
188  if (failed(getStridesAndOffset(bufferType, strides, offset)))
189  return failure();
190  resultType = MemRefType::get(
191  {}, tensorResultType.getElementType(),
192  StridedLayoutAttr::get(op->getContext(), offset, {}),
193  bufferType.getMemorySpace());
194  }
195 
196  replaceOpWithNewBufferizedOp<memref::CollapseShapeOp>(
197  rewriter, op, resultType, buffer, collapseShapeOp.getReassociation());
198  return success();
199  }
200 
201  // If the dims are not collapsible (due to an incompatible source layout
202  // map), force an out-of-place bufferization, i.e., a buffer copy. This
203  // newly allocated buffer will have no layout map and thus be collapsible.
204  bool canBeCollapsed = memref::CollapseShapeOp::isGuaranteedCollapsible(
205  bufferType, collapseShapeOp.getReassociationIndices());
206  if (!canBeCollapsed) {
207  // TODO: Create alloc_tensor ops during TensorCopyInsertion.
208  AnalysisState analysisState(options);
209  FailureOr<Value> tensorAlloc = allocateTensorForShapedValue(
210  rewriter, op->getLoc(), collapseShapeOp.getSrc(), options);
211  if (failed(tensorAlloc))
212  return failure();
213  auto memrefType =
214  MemRefType::get(collapseShapeOp.getSrcType().getShape(),
215  collapseShapeOp.getSrcType().getElementType(),
216  AffineMap(), bufferType.getMemorySpace());
217  buffer = rewriter.create<bufferization::ToMemrefOp>(
218  op->getLoc(), memrefType, *tensorAlloc);
219  }
220 
221  // Result type is inferred by the builder.
222  replaceOpWithNewBufferizedOp<memref::CollapseShapeOp>(
223  rewriter, op, buffer, collapseShapeOp.getReassociationIndices());
224  return success();
225  }
226 };
227 
228 /// Bufferization of tensor.dim. Replace with memref.dim.
229 struct DimOpInterface
230  : public BufferizableOpInterface::ExternalModel<DimOpInterface,
231  tensor::DimOp> {
232  bool bufferizesToMemoryRead(Operation *op, OpOperand &opOperand,
233  const AnalysisState &state) const {
234  // The op reads the tensor's metadata but not its contents.
235  return false;
236  }
237 
238  bool bufferizesToMemoryWrite(Operation *op, OpOperand &opOperand,
239  const AnalysisState &state) const {
240  return false;
241  }
242 
243  AliasingValueList getAliasingValues(Operation *op, OpOperand &opOperand,
244  const AnalysisState &state) const {
245  return {};
246  }
247 
248  LogicalResult bufferize(Operation *op, RewriterBase &rewriter,
249  const BufferizationOptions &options) const {
250  auto dimOp = cast<tensor::DimOp>(op);
251  FailureOr<Value> v = getBuffer(rewriter, dimOp.getSource(), options);
252  if (failed(v))
253  return failure();
254  replaceOpWithNewBufferizedOp<memref::DimOp>(rewriter, op, *v,
255  dimOp.getIndex());
256  return success();
257  }
258 };
259 
260 /// Bufferization of "tensor.empty". Replace with "bufferization.alloc_tensor".
261 struct EmptyOpInterface
262  : public BufferizableOpInterface::ExternalModel<EmptyOpInterface,
263  tensor::EmptyOp> {
264  bool bufferizesToAllocation(Operation *op, Value value) const { return true; }
265 
266  bool resultBufferizesToMemoryWrite(Operation *op, OpResult opResult,
267  const AnalysisState &state) const {
268  // The returned tensor does not have specified contents.
269  return false;
270  }
271 
272  LogicalResult bufferize(Operation *op, RewriterBase &rewriter,
273  const BufferizationOptions &options) const {
274  auto emptyOp = cast<tensor::EmptyOp>(op);
275 
276  // Optimization: Fold away the op if it has no uses.
277  if (op->getUses().empty()) {
278  rewriter.eraseOp(op);
279  return success();
280  }
281 
282  // Allocate a tensor. This emits a "bufferization.alloc_tensor" op.
283  FailureOr<Value> allocTensor = allocateTensorForShapedValue(
284  rewriter, op->getLoc(), emptyOp.getResult(), options, /*copy=*/false);
285  if (failed(allocTensor))
286  return failure();
287  rewriter.replaceOp(op, *allocTensor);
288  return success();
289  }
290 };
291 
292 /// Bufferization of tensor.expand_shape. Replace with memref.expand_shape.
293 struct ExpandShapeOpInterface
294  : public BufferizableOpInterface::ExternalModel<ExpandShapeOpInterface,
295  tensor::ExpandShapeOp> {
296  bool bufferizesToMemoryRead(Operation *op, OpOperand &opOperand,
297  const AnalysisState &state) const {
298  // In contrast to tensor.collapse_shape, this op can always be bufferized
299  // without a copy.
300  return false;
301  }
302 
303  bool bufferizesToMemoryWrite(Operation *op, OpOperand &opOperand,
304  const AnalysisState &state) const {
305  return false;
306  }
307 
308  AliasingValueList getAliasingValues(Operation *op, OpOperand &opOperand,
309  const AnalysisState &state) const {
310  return {{op->getOpResult(0), BufferRelation::Equivalent}};
311  }
312 
313  FailureOr<BaseMemRefType>
315  SmallVector<Value> &invocationStack) const {
316  auto expandShapeOp = cast<tensor::ExpandShapeOp>(op);
317  auto maybeSrcBufferType = bufferization::getBufferType(
318  expandShapeOp.getSrc(), options, invocationStack);
319  if (failed(maybeSrcBufferType))
320  return failure();
321  auto srcBufferType = llvm::cast<MemRefType>(*maybeSrcBufferType);
322  auto maybeResultType = memref::ExpandShapeOp::computeExpandedType(
323  srcBufferType, expandShapeOp.getResultType().getShape(),
324  expandShapeOp.getReassociationIndices());
325  if (failed(maybeResultType))
326  return failure();
327  return *maybeResultType;
328  }
329 
330  LogicalResult bufferize(Operation *op, RewriterBase &rewriter,
331  const BufferizationOptions &options) const {
332  auto expandShapeOp = cast<tensor::ExpandShapeOp>(op);
333  auto tensorResultType = expandShapeOp.getResultType();
334  FailureOr<Value> buffer =
335  getBuffer(rewriter, expandShapeOp.getSrc(), options);
336  if (failed(buffer))
337  return failure();
338 
339  // Memref result type is inferred by the builder based on reassociation
340  // indices and result shape.
341  // TODO: Instead of inferring the output shape argument of
342  // memref.expand_shape op, use output_shape argument of tensor.expand_shape
343  // op.
344  replaceOpWithNewBufferizedOp<memref::ExpandShapeOp>(
345  rewriter, op, tensorResultType.getShape(), *buffer,
346  expandShapeOp.getReassociationIndices());
347  return success();
348  }
349 };
350 
351 /// Bufferization of tensor.extract_slice. Replace with memref.subview.
352 struct ExtractSliceOpInterface
353  : public BufferizableOpInterface::ExternalModel<ExtractSliceOpInterface,
354  tensor::ExtractSliceOp> {
355  bool bufferizesToMemoryRead(Operation *op, OpOperand &opOperand,
356  const AnalysisState &state) const {
357  return false;
358  }
359 
360  bool bufferizesToMemoryWrite(Operation *op, OpOperand &opOperand,
361  const AnalysisState &state) const {
362  return false;
363  }
364 
365  AliasingValueList getAliasingValues(Operation *op, OpOperand &opOperand,
366  const AnalysisState &state) const {
367  return {{op->getOpResult(0), BufferRelation::Unknown}};
368  }
369 
370  LogicalResult bufferize(Operation *op, RewriterBase &rewriter,
371  const BufferizationOptions &options) const {
372  auto extractSliceOp = cast<tensor::ExtractSliceOp>(op);
373  SmallVector<OpFoldResult> mixedOffsets = extractSliceOp.getMixedOffsets();
374  SmallVector<OpFoldResult> mixedSizes = extractSliceOp.getMixedSizes();
375  SmallVector<OpFoldResult> mixedStrides = extractSliceOp.getMixedStrides();
376  Location loc = extractSliceOp.getLoc();
377 
378  // Get source buffer.
379  FailureOr<Value> srcMemref =
380  getBuffer(rewriter, extractSliceOp.getSource(), options);
381  if (failed(srcMemref))
382  return failure();
383 
384  // Take a subview of the source buffer.
385  auto resultMemrefType =
386  bufferization::getBufferType(extractSliceOp.getResult(), options);
387  if (failed(resultMemrefType))
388  return failure();
389  Value subView = rewriter.create<memref::SubViewOp>(
390  loc, llvm::cast<MemRefType>(*resultMemrefType), *srcMemref,
391  mixedOffsets, mixedSizes, mixedStrides);
392 
393  replaceOpWithBufferizedValues(rewriter, op, subView);
394  return success();
395  }
396 
397  FailureOr<BaseMemRefType>
399  SmallVector<Value> &invocationStack) const {
400  auto extractSliceOp = cast<tensor::ExtractSliceOp>(op);
401  assert(value == extractSliceOp.getResult() && "invalid value");
402  auto srcMemrefType = bufferization::getBufferType(
403  extractSliceOp.getSource(), options, invocationStack);
404  if (failed(srcMemrefType))
405  return failure();
406  SmallVector<OpFoldResult> mixedOffsets = extractSliceOp.getMixedOffsets();
407  SmallVector<OpFoldResult> mixedSizes = extractSliceOp.getMixedSizes();
408  SmallVector<OpFoldResult> mixedStrides = extractSliceOp.getMixedStrides();
409  return cast<BaseMemRefType>(memref::SubViewOp::inferRankReducedResultType(
410  extractSliceOp.getType().getShape(),
411  llvm::cast<MemRefType>(*srcMemrefType), mixedOffsets, mixedSizes,
412  mixedStrides));
413  }
414 };
415 
416 /// Bufferization of tensor.extract. Replace with memref.load.
417 struct ExtractOpInterface
418  : public BufferizableOpInterface::ExternalModel<ExtractOpInterface,
419  tensor::ExtractOp> {
420  bool bufferizesToMemoryRead(Operation *op, OpOperand &opOperand,
421  const AnalysisState &state) const {
422  return true;
423  }
424 
425  bool bufferizesToMemoryWrite(Operation *op, OpOperand &opOperand,
426  const AnalysisState &state) const {
427  return false;
428  }
429 
430  AliasingValueList getAliasingValues(Operation *op, OpOperand &opOperand,
431  const AnalysisState &state) const {
432  return {};
433  }
434 
435  LogicalResult bufferize(Operation *op, RewriterBase &rewriter,
436  const BufferizationOptions &options) const {
437  auto extractOp = cast<tensor::ExtractOp>(op);
438  FailureOr<Value> srcMemref =
439  getBuffer(rewriter, extractOp.getTensor(), options);
440  if (failed(srcMemref))
441  return failure();
442  replaceOpWithNewBufferizedOp<memref::LoadOp>(rewriter, op, *srcMemref,
443  extractOp.getIndices());
444  return success();
445  }
446 };
447 
448 // Implements backtracking to traverse indices of the output buffer while
449 // iterating over op.elements().
450 static void createStores(RewriterBase &rewriter, Location loc, int dim,
451  Value buffer, ArrayRef<int64_t> shape,
452  ArrayRef<Value> constants,
453  OperandRange::iterator &elementIt,
454  SmallVectorImpl<Value> &indices) {
455  if (dim == static_cast<int>(shape.size()) - 1) {
456  for (int i = 0; i < shape.back(); ++i) {
457  indices.back() = constants[i];
458  rewriter.create<memref::StoreOp>(loc, *elementIt, buffer, indices);
459  ++elementIt;
460  }
461  return;
462  }
463  for (int i = 0; i < shape[dim]; ++i) {
464  indices[dim] = constants[i];
465  createStores(rewriter, loc, dim + 1, buffer, shape, constants, elementIt,
466  indices);
467  }
468 }
469 
470 /// Bufferization of tensor.from_elements.
471 struct FromElementsOpInterface
472  : public BufferizableOpInterface::ExternalModel<FromElementsOpInterface,
473  tensor::FromElementsOp> {
474 
475  bool bufferizesToAllocation(Operation *op, Value value) const { return true; }
476 
477  LogicalResult bufferize(Operation *op, RewriterBase &rewriter,
478  const BufferizationOptions &options) const {
479  auto fromElementsOp = cast<tensor::FromElementsOp>(op);
480  auto tensorType = cast<RankedTensorType>(fromElementsOp.getType());
481 
482  // TODO: Implement memory space for this op.
483  if (options.defaultMemorySpaceFn(tensorType) != Attribute())
484  return op->emitError("memory space not implemented yet");
485 
486  // Allocate a buffer for the result.
487  Location loc = op->getLoc();
488  auto shape = tensorType.getShape();
489  // TODO: Create alloc_tensor ops during TensorCopyInsertion.
490  FailureOr<Value> tensorAlloc = allocateTensorForShapedValue(
491  rewriter, loc, fromElementsOp.getResult(), options,
492  /*copy=*/false);
493  if (failed(tensorAlloc))
494  return failure();
495  auto memrefType =
496  MemRefType::get(tensorType.getShape(), tensorType.getElementType());
497  Value buffer = rewriter.create<bufferization::ToMemrefOp>(
498  op->getLoc(), memrefType, *tensorAlloc);
499 
500  // Case: tensor<0xelem_type>.
501  if (fromElementsOp.getElements().empty()) {
502  replaceOpWithBufferizedValues(rewriter, op, buffer);
503  return success();
504  }
505 
506  // Case: tensor<elem_type>.
507  if (shape.empty()) {
508  rewriter.create<memref::StoreOp>(
509  loc, fromElementsOp.getElements().front(), buffer);
510  replaceOpWithBufferizedValues(rewriter, op, buffer);
511  return success();
512  }
513 
514  // Create constants for the range of possible indices [0, max{shape_i}).
515  auto maxDim = *llvm::max_element(shape);
516  SmallVector<Value, 2> constants;
517  constants.reserve(maxDim);
518  for (int i = 0; i < maxDim; ++i)
519  constants.push_back(rewriter.create<arith::ConstantIndexOp>(loc, i));
520 
521  // Traverse all `elements` and create `memref.store` ops.
522  auto elementIt = fromElementsOp.getElements().begin();
523  SmallVector<Value, 2> indices(tensorType.getRank(), constants[0]);
524  createStores(rewriter, loc, /*dim=*/0, buffer, shape, constants, elementIt,
525  indices);
526 
527  replaceOpWithBufferizedValues(rewriter, op, buffer);
528 
529  return success();
530  }
531 };
532 
533 /// Lower the body of a tensor.generate like op (one index-typed bbArg per dim).
534 /// Such ops are lowered to linalg.map with the given tensor as a destination.
535 ///
536 /// Example:
537 /// ```
538 /// %r = tensor.generate %x, %y {
539 /// ^bb0(%arg0: index, %arg1: index):
540 /// %0 = "some_op"(%arg0, %arg1) : (index, index) -> (index)
541 /// tensor.yield %0 : index
542 /// } : tensor<?x?xindex>
543 /// ```
544 ///
545 /// Is lowered to:
546 /// ```
547 /// linalg.map ins() outs(%dest) {
548 /// %d0 = linalg.index 0 : index
549 /// %d1 = linalg.index 1 : index
550 /// %0 = "some_op"(%d0, %d1) : (index, index) -> (index)
551 /// linalg.yield %0 : index
552 /// }
553 /// ```
554 static Value lowerGenerateLikeOpBody(RewriterBase &rewriter, Location loc,
555  Value tensorDestination,
556  ValueRange dynamicSizes,
557  Region &generateBody) {
558  assert(generateBody.hasOneBlock() && "expected body with single block");
559  auto tensorType = cast<RankedTensorType>(tensorDestination.getType());
560  assert(generateBody.getNumArguments() == tensorType.getRank() &&
561  "rank mismatch");
562 
563  // Create linalg::MapOp.
564  OpBuilder::InsertionGuard g(rewriter);
565  auto linalgOp =
566  rewriter.create<linalg::MapOp>(loc, tensorType, /*inputs=*/ValueRange(),
567  /*init=*/tensorDestination);
568  Block &linalgBody = linalgOp.getMapper().emplaceBlock();
569 
570  // Create linalg::IndexOps.
571  rewriter.setInsertionPointToStart(&linalgBody);
572  SmallVector<Value> indices;
573  for (int64_t dim = 0; dim < tensorType.getRank(); ++dim)
574  indices.push_back(rewriter.create<linalg::IndexOp>(loc, dim));
575 
576  // Move over body.
577  rewriter.mergeBlocks(&generateBody.front(), &linalgBody, indices);
578  auto yieldOp = cast<tensor::YieldOp>(linalgBody.getTerminator());
579  rewriter.replaceOpWithNewOp<linalg::YieldOp>(yieldOp, yieldOp.getValue());
580 
581  return linalgOp.getResult()[0];
582 }
583 
584 /// Bufferization of tensor.generate.
585 struct GenerateOpInterface
586  : public BufferizableOpInterface::ExternalModel<GenerateOpInterface,
587  tensor::GenerateOp> {
588 
589  bool bufferizesToAllocation(Operation *op, Value value) const { return true; }
590 
591  LogicalResult bufferize(Operation *op, RewriterBase &rewriter,
592  const BufferizationOptions &options) const {
593  auto generateOp = cast<tensor::GenerateOp>(op);
594 
595  auto type = generateOp.getResult().getType();
596 
597  // TODO: Implement memory space for this op.
598  if (options.defaultMemorySpaceFn(type) != Attribute())
599  return op->emitError("memory space not implemented yet");
600 
601  // Allocate memory.
602  Location loc = op->getLoc();
603  FailureOr<Value> tensorAlloc = allocateTensorForShapedValue(
604  rewriter, loc, generateOp.getResult(), options,
605  /*copy=*/false);
606  if (failed(tensorAlloc))
607  return failure();
608 
609  Value result = lowerGenerateLikeOpBody(rewriter, loc, *tensorAlloc,
610  generateOp.getDynamicExtents(),
611  generateOp.getBody());
612  rewriter.replaceOp(generateOp, result);
613 
614  return success();
615  }
616 };
617 
618 /// Bufferization of tensor.insert. Replace with memref.store.
619 ///
620 /// Note: DstBufferizableOpInterfaceExternalModel provides many default method
621 /// implementations for DestinationStyle ops.
622 struct InsertOpInterface
623  : public DstBufferizableOpInterfaceExternalModel<InsertOpInterface,
624  tensor::InsertOp> {
625  LogicalResult bufferize(Operation *op, RewriterBase &rewriter,
626  const BufferizationOptions &options) const {
627  auto insertOp = cast<tensor::InsertOp>(op);
628  FailureOr<Value> destMemref =
629  getBuffer(rewriter, insertOp.getDest(), options);
630  if (failed(destMemref))
631  return failure();
632  rewriter.create<memref::StoreOp>(insertOp.getLoc(), insertOp.getScalar(),
633  *destMemref, insertOp.getIndices());
634  replaceOpWithBufferizedValues(rewriter, op, *destMemref);
635  return success();
636  }
637 };
638 
639 /// Bufferization of tensor.insert_slice. Replace with a memory copy. Under
640 /// certain circumstances, this op can also be a no-op.
641 ///
642 /// Note: DstBufferizableOpInterfaceExternalModel provides many default method
643 /// implementations for DestinationStyle ops.
644 struct InsertSliceOpInterface
645  : public DstBufferizableOpInterfaceExternalModel<InsertSliceOpInterface,
646  tensor::InsertSliceOp> {
647  bool bufferizesToMemoryRead(Operation *op, OpOperand &opOperand,
648  const AnalysisState &state) const {
649  auto insertSliceOp = cast<tensor::InsertSliceOp>(op);
650  RankedTensorType destType = insertSliceOp.getDestType();
651 
652  // The source is always read.
653  if (opOperand == insertSliceOp.getSourceMutable())
654  return true;
655 
656  // For the destination, it depends...
657  assert(opOperand == insertSliceOp.getDestMutable() && "expected dest");
658 
659  // Dest is not read if it is entirely overwritten. E.g.:
660  // tensor.insert_slice %a into %t[0][10][1] : ... into tensor<10xf32>
661  bool allOffsetsZero =
662  llvm::all_of(insertSliceOp.getMixedOffsets(), [](OpFoldResult ofr) {
663  return isConstantIntValue(ofr, 0);
664  });
665  bool sizesMatchDestSizes = llvm::all_of(
666  llvm::enumerate(insertSliceOp.getMixedSizes()), [&](const auto &it) {
667  return getConstantIntValue(it.value()) ==
668  destType.getDimSize(it.index());
669  });
670  bool allStridesOne =
671  llvm::all_of(insertSliceOp.getMixedStrides(), [](OpFoldResult ofr) {
672  return isConstantIntValue(ofr, 1);
673  });
674  return !(allOffsetsZero && sizesMatchDestSizes && allStridesOne);
675  }
676 
677  LogicalResult bufferize(Operation *op, RewriterBase &rewriter,
678  const BufferizationOptions &options) const {
679  // insert_slice ops arise from tiling and bufferizing them out-of-place is
680  // generally a deal breaker. When used with loops, this ends up cloning the
681  // whole tensor on every single iteration and is a symptom of a
682  // catastrophically bad scheduling decision.
683  // TODO: be very loud about it or even consider failing the pass.
684  auto insertSliceOp = cast<tensor::InsertSliceOp>(op);
685  SmallVector<OpFoldResult> mixedOffsets = insertSliceOp.getMixedOffsets();
686  SmallVector<OpFoldResult> mixedSizes = insertSliceOp.getMixedSizes();
687  SmallVector<OpFoldResult> mixedStrides = insertSliceOp.getMixedStrides();
688  Location loc = insertSliceOp.getLoc();
689 
690  // Get destination buffer.
691  FailureOr<Value> dstMemref =
692  getBuffer(rewriter, insertSliceOp.getDest(), options);
693  if (failed(dstMemref))
694  return failure();
695 
696  // Take a subview of the destination buffer.
697  auto dstMemrefType = cast<MemRefType>(dstMemref->getType());
698  auto subviewMemRefType =
699  cast<MemRefType>(memref::SubViewOp::inferRankReducedResultType(
700  insertSliceOp.getSourceType().getShape(), dstMemrefType,
701  mixedOffsets, mixedSizes, mixedStrides));
702  Value subView = rewriter.create<memref::SubViewOp>(
703  loc, subviewMemRefType, *dstMemref, mixedOffsets, mixedSizes,
704  mixedStrides);
705 
706  // Copy tensor. If this tensor.insert_slice has a matching
707  // tensor.extract_slice, the copy operation will eventually fold away.
708  FailureOr<Value> srcMemref =
709  getBuffer(rewriter, insertSliceOp.getSource(), options);
710  if (failed(srcMemref))
711  return failure();
712  if (failed(options.createMemCpy(rewriter, loc, *srcMemref, subView)))
713  return failure();
714 
715  replaceOpWithBufferizedValues(rewriter, op, *dstMemref);
716  return success();
717  }
718 };
719 
720 /// Bufferization of tensor.pad. Replace with bufferization.alloc_tensor +
721 /// linalg.map + insert_slice.
722 /// For best performance, vectorize before bufferization (better performance in
723 /// case of padding with a constant).
724 struct PadOpInterface
725  : public BufferizableOpInterface::ExternalModel<PadOpInterface,
726  tensor::PadOp> {
727  bool bufferizesToAllocation(Operation *op, Value value) const { return true; }
728 
729  bool bufferizesToMemoryRead(Operation *op, OpOperand &opOperand,
730  const AnalysisState &state) const {
731  return true;
732  }
733 
734  bool bufferizesToMemoryWrite(Operation *op, OpOperand &opOperand,
735  const AnalysisState &state) const {
736  return false;
737  }
738 
739  AliasingValueList getAliasingValues(Operation *op, OpOperand &opOperand,
740  const AnalysisState &state) const {
741  return {};
742  }
743 
744  FailureOr<BaseMemRefType>
746  SmallVector<Value> &invocationStack) const {
747  // Infer memory space from the source tensor.
748  auto padOp = cast<tensor::PadOp>(op);
749  auto maybeSrcBufferType = bufferization::getBufferType(
750  padOp.getSource(), options, invocationStack);
751  if (failed(maybeSrcBufferType))
752  return failure();
753  MemRefLayoutAttrInterface layout;
754  return MemRefType::get(padOp.getResultType().getShape(),
755  padOp.getResultType().getElementType(), layout,
756  maybeSrcBufferType->getMemorySpace());
757  }
758 
759  LogicalResult bufferize(Operation *op, RewriterBase &rewriter,
760  const BufferizationOptions &options) const {
761  auto padOp = cast<tensor::PadOp>(op);
762  Location loc = padOp.getLoc();
763  RankedTensorType resultType = padOp.getResultType();
764  RankedTensorType srcType = padOp.getSourceType();
765 
766  auto toValue = [&](OpFoldResult ofr) {
767  if (ofr.is<Value>())
768  return ofr.get<Value>();
769  return rewriter
770  .create<arith::ConstantIndexOp>(loc, *getConstantIntValue(ofr))
771  .getResult();
772  };
773 
774  // Compute dynamic result dimensions.
775  SmallVector<OpFoldResult> mixedLowPad = padOp.getMixedLowPad();
776  SmallVector<OpFoldResult> mixedHighPad = padOp.getMixedHighPad();
777  SmallVector<Value> dynamicSizes;
778  for (int64_t i = 0; i < resultType.getRank(); ++i) {
779  if (!resultType.isDynamicDim(i))
780  continue;
781  Value srcDim = rewriter.create<tensor::DimOp>(loc, padOp.getSource(), i);
782  Value lowPad = toValue(mixedLowPad[i]);
783  Value highPad = toValue(mixedHighPad[i]);
784  AffineExpr s0, s1, s2;
785  bindSymbols(op->getContext(), s0, s1, s2);
786  AffineExpr sumExpr = s0 + s1 + s2;
787  Value sum = rewriter.create<affine::AffineApplyOp>(
788  loc, sumExpr, ValueRange{srcDim, lowPad, highPad});
789  dynamicSizes.push_back(sum);
790  }
791 
792  // Allocate a buffer for the padded result.
793  FailureOr<Value> tensorAlloc =
794  allocateTensorForShapedValue(rewriter, loc, padOp.getResult(), options,
795  /*copy=*/false);
796  if (failed(tensorAlloc))
797  return failure();
798 
799  // tensor::PadOp is like tensor::GenerateOp: The only difference is that
800  // only a part of the generated tensor is needed. For simplicity, we reuse
801  // the same functionality here.
802  Value filledBuffer = lowerGenerateLikeOpBody(
803  rewriter, loc, *tensorAlloc, dynamicSizes, padOp.getBodyRegion());
804 
805  // Create tensor::InsertSliceOp.
806  SmallVector<OpFoldResult> sliceSizes =
807  getMixedSizes(rewriter, loc, padOp.getSource());
808  SmallVector<OpFoldResult> sliceStrides(srcType.getRank(),
809  rewriter.getIndexAttr(1));
810  rewriter.replaceOpWithNewOp<tensor::InsertSliceOp>(
811  padOp, padOp.getSource(), filledBuffer,
812  /*offsets=*/padOp.getMixedLowPad(), sliceSizes, sliceStrides);
813 
814  return success();
815  }
816 };
817 
818 /// Bufferization of tensor.rank. Replace with memref.rank.
819 struct RankOpInterface
820  : public BufferizableOpInterface::ExternalModel<RankOpInterface,
821  tensor::RankOp> {
822  bool bufferizesToMemoryRead(Operation *op, OpOperand &opOperand,
823  const AnalysisState &state) const {
824  // The op reads the tensor's metadata but not its contents.
825  return false;
826  }
827 
828  bool bufferizesToMemoryWrite(Operation *op, OpOperand &opOperand,
829  const AnalysisState &state) const {
830  return false;
831  }
832 
833  AliasingValueList getAliasingValues(Operation *op, OpOperand &opOperand,
834  const AnalysisState &state) const {
835  return {};
836  }
837 
838  LogicalResult bufferize(Operation *op, RewriterBase &rewriter,
839  const BufferizationOptions &options) const {
840  auto rankOp = cast<tensor::RankOp>(op);
841  FailureOr<Value> v = getBuffer(rewriter, rankOp.getTensor(), options);
842  if (failed(v))
843  return failure();
844  replaceOpWithNewBufferizedOp<memref::RankOp>(rewriter, op, rankOp.getType(),
845  *v);
846  return success();
847  }
848 };
849 
850 /// Bufferization of tensor.reshape. Replace with memref.reshape.
851 struct ReshapeOpInterface
852  : public BufferizableOpInterface::ExternalModel<ReshapeOpInterface,
853  tensor::ReshapeOp> {
854  bool bufferizesToMemoryRead(Operation *op, OpOperand &opOperand,
855  const AnalysisState &state) const {
856  // Depending on the layout map, the source buffer may have to be copied.
857  auto reshapeOp = cast<tensor::ReshapeOp>(op);
858  return opOperand == reshapeOp.getShapeMutable();
859  }
860 
861  bool bufferizesToMemoryWrite(Operation *op, OpOperand &opOperand,
862  const AnalysisState &state) const {
863  return false;
864  }
865 
866  AliasingValueList getAliasingValues(Operation *op, OpOperand &opOperand,
867  const AnalysisState &state) const {
868  return {{op->getOpResult(0), BufferRelation::Equivalent}};
869  }
870 
871  LogicalResult bufferize(Operation *op, RewriterBase &rewriter,
872  const BufferizationOptions &options) const {
873  auto reshapeOp = cast<tensor::ReshapeOp>(op);
874  FailureOr<Value> srcBuffer =
875  getBuffer(rewriter, reshapeOp.getSource(), options);
876  FailureOr<Value> shapeBuffer =
877  getBuffer(rewriter, reshapeOp.getShape(), options);
878  if (failed(srcBuffer) || failed(shapeBuffer))
879  return failure();
880  auto maybeResultMemRefType =
881  bufferization::getBufferType(reshapeOp.getResult(), options);
882  if (failed(maybeResultMemRefType))
883  return failure();
884 
885  // memref.reshape requires the source buffer to have an identity layout.
886  // If the source memref does not have an identity layout, copy the source
887  // into a new buffer with an identity layout.
888  auto srcType = llvm::dyn_cast<MemRefType>(srcBuffer->getType());
889  if (srcType && !srcType.getLayout().isIdentity()) {
890  FailureOr<Value> tensorAlloc = allocateTensorForShapedValue(
891  rewriter, op->getLoc(), reshapeOp.getSource(), options);
892  if (failed(tensorAlloc))
893  return failure();
894  auto memrefType = MemRefType::get(
895  srcType.getShape(), srcType.getElementType(), AffineMap(),
896  cast<BaseMemRefType>(srcBuffer->getType()).getMemorySpace());
897  srcBuffer = rewriter
898  .create<bufferization::ToMemrefOp>(
899  op->getLoc(), memrefType, *tensorAlloc)
900  .getResult();
901  }
902 
903  replaceOpWithNewBufferizedOp<memref::ReshapeOp>(
904  rewriter, op, maybeResultMemRefType.value(), *srcBuffer, *shapeBuffer);
905  return success();
906  }
907 
908  FailureOr<BaseMemRefType>
910  SmallVector<Value> &invocationStack) const {
911  auto reshapeOp = cast<tensor::ReshapeOp>(op);
912  assert(value == reshapeOp.getResult() && "unexpected value provided");
913  auto maybeSourceBufferType = bufferization::getBufferType(
914  reshapeOp.getSource(), options, invocationStack);
915  if (failed(maybeSourceBufferType))
916  return failure();
918  reshapeOp.getResult().getType(),
919  cast<BaseMemRefType>(maybeSourceBufferType.value()).getMemorySpace());
920  }
921 };
922 
923 /// Analysis of ParallelInsertSliceOp.
924 struct ParallelInsertSliceOpInterface
925  : public BufferizableOpInterface::ExternalModel<
926  ParallelInsertSliceOpInterface, ParallelInsertSliceOp> {
927  AliasingValueList getAliasingValues(Operation *op, OpOperand &opOperand,
928  const AnalysisState &state) const {
929  return {};
930  }
931 
932  bool bufferizesToMemoryRead(Operation *op, OpOperand &opOperand,
933  const AnalysisState &state) const {
934  return true;
935  }
936 
937  bool bufferizesToMemoryWrite(Operation *op, OpOperand &opOperand,
938  const AnalysisState &state) const {
939  auto parallelInsertSliceOp = cast<ParallelInsertSliceOp>(op);
940  return opOperand == parallelInsertSliceOp.getDestMutable();
941  }
942 
943  LogicalResult bufferize(Operation *op, RewriterBase &rewriter,
944  const BufferizationOptions &options) const {
945  OpBuilder::InsertionGuard g(rewriter);
946  auto parallelInsertSliceOp = cast<ParallelInsertSliceOp>(op);
947  ParallelCombiningOpInterface parallelCombiningParent =
948  parallelInsertSliceOp.getParallelCombiningParent();
949 
950  // Bufferize the op outside of the parallel combining terminator.
951  rewriter.setInsertionPoint(parallelCombiningParent);
952 
953  // Get source and destination buffers.
954  FailureOr<Value> destBuffer =
955  getBuffer(rewriter, parallelInsertSliceOp.getDest(), options);
956  if (failed(destBuffer))
957  return failure();
958  FailureOr<Value> srcBuffer =
959  getBuffer(rewriter, parallelInsertSliceOp.getSource(), options);
960  if (failed(srcBuffer))
961  return failure();
962 
963  // Take a subview of the destination buffer.
964  auto destBufferType = cast<MemRefType>(destBuffer->getType());
965  auto subviewMemRefType =
966  cast<MemRefType>(memref::SubViewOp::inferRankReducedResultType(
967  parallelInsertSliceOp.getSourceType().getShape(), destBufferType,
968  parallelInsertSliceOp.getMixedOffsets(),
969  parallelInsertSliceOp.getMixedSizes(),
970  parallelInsertSliceOp.getMixedStrides()));
971  Value subview = rewriter.create<memref::SubViewOp>(
972  parallelInsertSliceOp.getLoc(), subviewMemRefType, *destBuffer,
973  parallelInsertSliceOp.getMixedOffsets(),
974  parallelInsertSliceOp.getMixedSizes(),
975  parallelInsertSliceOp.getMixedStrides());
976 
977  // This memcpy will fold away if everything bufferizes in-place.
978  if (failed(options.createMemCpy(rewriter, parallelInsertSliceOp.getLoc(),
979  *srcBuffer, subview)))
980  return failure();
981 
982  // In case the source was allocated in the same block, make sure that the
983  // deallocation op (if any) appears after the memcpy. By default, deallocs
984  // are placed before the terminator, but this does not work for ForallOp
985  // because the terminator does more than just yielding a value.
986  //
987  // Note: This is not a problem for the destination buffer because these are
988  // assumed to always bufferize in-place.
989  for (Operation *user : srcBuffer->getUsers()) {
990  if (hasEffect<MemoryEffects::Free>(user)) {
991  if (user->getBlock() == parallelCombiningParent->getBlock())
992  rewriter.moveOpBefore(user, user->getBlock()->getTerminator());
993  break;
994  }
995  }
996 
997  // Delete the op.
998  rewriter.eraseOp(op);
999  return success();
1000  }
1001 
1002  /// tensor.parallel_insert_slice op has implicit inplace behavior. We
1003  /// shouldn't create copy to resolve conflict.
1004  LogicalResult resolveConflicts(Operation *op, RewriterBase &rewriter,
1005  const AnalysisState &state) const {
1006  return success();
1007  }
1008 };
1009 
1010 /// Bufferization of tensor.splat. Bufferizes to a new allocation that is filled
1011 /// with a linalg.map. Similar to tensor.generate.
1012 struct SplatOpInterface
1013  : public BufferizableOpInterface::ExternalModel<SplatOpInterface,
1014  tensor::SplatOp> {
1015 
1016  bool bufferizesToAllocation(Operation *op, Value value) const { return true; }
1017 
1018  LogicalResult bufferize(Operation *op, RewriterBase &rewriter,
1019  const BufferizationOptions &options) const {
1020  OpBuilder::InsertionGuard g(rewriter);
1021  auto splatOp = cast<tensor::SplatOp>(op);
1022 
1023  // Allocate memory.
1024  Location loc = op->getLoc();
1025  FailureOr<Value> tensorAlloc = allocateTensorForShapedValue(
1026  rewriter, loc, splatOp.getResult(), options,
1027  /*copy=*/false);
1028  if (failed(tensorAlloc))
1029  return failure();
1030 
1031  // Create linalg::MapOp.
1032  auto tensorType = cast<RankedTensorType>(tensorAlloc->getType());
1033 
1034  // TODO: Implement memory space for this op.
1035  if (options.defaultMemorySpaceFn(tensorType) != Attribute())
1036  return op->emitError("memory space not implemented yet");
1037 
1038  auto linalgOp =
1039  rewriter.create<linalg::MapOp>(loc, tensorType, /*inputs=*/ValueRange(),
1040  /*init=*/*tensorAlloc);
1041  Block &linalgBody = linalgOp.getMapper().emplaceBlock();
1042 
1043  // Create linalg::IndexOps.
1044  rewriter.setInsertionPointToStart(&linalgBody);
1045  rewriter.create<linalg::YieldOp>(loc, splatOp.getInput());
1046  rewriter.replaceOp(splatOp, linalgOp.getResult()[0]);
1047 
1048  return success();
1049  }
1050 };
1051 
1052 } // namespace
1053 } // namespace tensor
1054 } // namespace mlir
1055 
1057  DialectRegistry &registry) {
1058  registry.addExtension(+[](MLIRContext *ctx, tensor::TensorDialect *dialect) {
1059  CastOp::attachInterface<CastOpInterface>(*ctx);
1060  CollapseShapeOp::attachInterface<CollapseShapeOpInterface>(*ctx);
1061  DimOp::attachInterface<DimOpInterface>(*ctx);
1062  EmptyOp::attachInterface<EmptyOpInterface>(*ctx);
1063  ExpandShapeOp::attachInterface<ExpandShapeOpInterface>(*ctx);
1064  ExtractSliceOp::attachInterface<ExtractSliceOpInterface>(*ctx);
1065  ExtractOp::attachInterface<ExtractOpInterface>(*ctx);
1066  FromElementsOp::attachInterface<FromElementsOpInterface>(*ctx);
1067  GenerateOp::attachInterface<GenerateOpInterface>(*ctx);
1068  InsertOp::attachInterface<InsertOpInterface>(*ctx);
1069  InsertSliceOp::attachInterface<InsertSliceOpInterface>(*ctx);
1070  PadOp::attachInterface<PadOpInterface>(*ctx);
1071  ParallelInsertSliceOp::attachInterface<ParallelInsertSliceOpInterface>(
1072  *ctx);
1073  RankOp::attachInterface<RankOpInterface>(*ctx);
1074  ReshapeOp::attachInterface<ReshapeOpInterface>(*ctx);
1075  SplatOp::attachInterface<SplatOpInterface>(*ctx);
1076 
1077  // Load additional dialects of which ops may get created.
1078  ctx->loadDialect<arith::ArithDialect, linalg::LinalgDialect>();
1079  });
1080 
1081  // Bufferization requires SubsetInsertionOpInterface models. Make sure that
1082  // they are registered.
1084 }
static llvm::ManagedStatic< PassManagerOptions > options
Base type for affine expression.
Definition: AffineExpr.h:68
A multi-dimensional affine map Affine map's are immutable like Type's, and they are uniqued.
Definition: AffineMap.h:46
Base class for generic analysis states.
Attributes are known-constant values of operations.
Definition: Attributes.h:25
Block represents an ordered list of Operations.
Definition: Block.h:31
IntegerAttr getIndexAttr(int64_t value)
Definition: Builders.cpp:128
The DialectRegistry maps a dialect namespace to a constructor for the matching dialect.
void addExtension(std::unique_ptr< DialectExtensionBase > extension)
Add the given extension to the registry.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition: Location.h:63
MLIRContext is the top-level object for a collection of MLIR operations.
Definition: MLIRContext.h:60
void loadDialect()
Load a dialect in the context.
Definition: MLIRContext.h:107
RAII guard to reset the insertion point of the builder when destroyed.
Definition: Builders.h:351
void setInsertionPointToStart(Block *block)
Sets the insertion point to the start of the specified block.
Definition: Builders.h:434
void setInsertionPoint(Block *block, Block::iterator insertPoint)
Set the insertion point to the specified location.
Definition: Builders.h:401
Operation * create(const OperationState &state)
Creates an operation given the fields represented as an OperationState.
Definition: Builders.cpp:468
This class represents a single result from folding an operation.
Definition: OpDefinition.h:268
This class represents an operand of an operation.
Definition: Value.h:267
This is a value defined by a result of an operation.
Definition: Value.h:457
Operation is the basic unit of execution within MLIR.
Definition: Operation.h:88
OpResult getOpResult(unsigned idx)
Definition: Operation.h:416
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
Definition: Operation.h:402
MLIRContext * getContext()
Return the context this operation is associated with.
Definition: Operation.h:216
Location getLoc()
The source location the operation was defined or derived from.
Definition: Operation.h:223
InFlightDiagnostic emitError(const Twine &message={})
Emit an error about fatal conditions with this operation, reporting up to any diagnostic handlers tha...
Definition: Operation.cpp:268
user_range getUsers()
Returns a range of all users.
Definition: Operation.h:869
use_range getUses()
Returns a range of all uses, which is useful for iterating over all uses.
Definition: Operation.h:842
This class contains a list of basic blocks and a link to the parent operation it is attached to.
Definition: Region.h:26
unsigned getNumArguments()
Definition: Region.h:123
Block & front()
Definition: Region.h:65
bool hasOneBlock()
Return true if this region has exactly one block.
Definition: Region.h:68
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...
void mergeBlocks(Block *source, Block *dest, ValueRange argValues=std::nullopt)
Inline the operations of block 'source' into the end of block 'dest'.
virtual void eraseOp(Operation *op)
This method erases an operation that is known to have no uses.
void moveOpBefore(Operation *op, Operation *existingOp)
Unlink this operation from its current block and insert it right before existingOp which may be in th...
OpTy replaceOpWithNewOp(Operation *op, Args &&...args)
Replace the results of the given (original) op with a new op that is created without verification (re...
Definition: PatternMatch.h:536
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
Type getType() const
Return the type of this value.
Definition: Value.h:129
void replaceOpWithBufferizedValues(RewriterBase &rewriter, Operation *op, ValueRange values)
Replace an op with replacement values.
BaseMemRefType getMemRefTypeWithStaticIdentityLayout(TensorType tensorType, Attribute memorySpace=nullptr)
Return a MemRef type with a static identity layout (i.e., no layout map).
FailureOr< Value > allocateTensorForShapedValue(OpBuilder &b, Location loc, Value shapedValue, const BufferizationOptions &options, bool copy=true)
Create an AllocTensorOp for the given shaped value (memref or tensor).
FailureOr< BaseMemRefType > getBufferType(Value value, const BufferizationOptions &options)
Return the buffer type for a given Value (tensor) after bufferization without bufferizing any IR.
FailureOr< Value > getBuffer(RewriterBase &rewriter, Value value, const BufferizationOptions &options)
Lookup the buffer for the given value.
BaseMemRefType getMemRefTypeWithFullyDynamicLayout(TensorType tensorType, Attribute memorySpace=nullptr)
Return a MemRef type with fully dynamic layout.
constexpr void enumerate(std::tuple< Tys... > &tuple, CallbackT &&callback)
Definition: Matchers.h:285
SmallVector< OpFoldResult > getMixedSizes(OpBuilder &builder, Location loc, Value value)
Return the dimensions of the given memref value.
Definition: MemRefOps.cpp:77
void registerSubsetOpInterfaceExternalModels(DialectRegistry &registry)
void registerBufferizableOpInterfaceExternalModels(DialectRegistry &registry)
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 getStridesAndOffset(MemRefType t, SmallVectorImpl< int64_t > &strides, int64_t &offset)
Returns the strides of the MemRef if the layout map is in strided form.
void bindSymbols(MLIRContext *ctx, AffineExprTy &...exprs)
Bind a list of AffineExpr references to SymbolExpr at positions: [0 .
Definition: AffineExpr.h:362
auto get(MLIRContext *context, Ts &&...params)
Helper method that injects context only if needed, this helps unify some of the attribute constructio...
Options for BufferizableOpInterface-based bufferization.
Bufferizable ops that implement the DestinationStyleOpInterface can use this external model base clas...