MLIR 23.0.0git
ConvertToDestinationStyle.cpp
Go to the documentation of this file.
1//===- ConvertToDestinationStyle.cpp - Convert non-DPS to DPS 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//
9// This file contains patterns to convert non-DPS ops to DPS ops. New
10// tensor.empty ops are inserted as a destination. Such tensor.empty can be
11// eliminated with "empty tensor elimination", allowing them to bufferize
12// without an allocation (assuming there are no further conflicts).
13//
14//===----------------------------------------------------------------------===//
15//
24#include "mlir/IR/Matchers.h"
26#include "llvm/ADT/STLExtras.h"
27
28using namespace mlir;
29using namespace mlir::tensor;
30
31// Implements backtracking to traverse indices of the output buffer while
32// iterating over op.elements().
33static Value createInserts(RewriterBase &rewriter, Location loc, int dim,
34 Value destination, ArrayRef<int64_t> shape,
35 ArrayRef<Value> constants,
36 OperandRange::iterator &elementIt,
38 if (dim == static_cast<int>(shape.size()) - 1) {
39 for (int i = 0; i < shape.back(); ++i) {
40 indices.back() = constants[i];
41 destination = tensor::InsertOp::create(rewriter, loc, *elementIt,
42 destination, indices);
43 ++elementIt;
44 }
45 return destination;
46 }
47 for (int i = 0; i < shape[dim]; ++i) {
48 indices[dim] = constants[i];
49 destination = createInserts(rewriter, loc, dim + 1, destination, shape,
50 constants, elementIt, indices);
51 }
52 return destination;
53}
54
55/// Create a memcpy from the given source tensor to the given destination
56/// memref. The copy op type can be specified in the `options`.
57static void createMemcpy(OpBuilder &b, Location loc, Value tensorSource,
58 Value memrefDest,
60 auto tensorType = dyn_cast<RankedTensorType>(tensorSource.getType());
61 assert(tensorType && "expected ranked tensor");
62 assert(isa<MemRefType>(memrefDest.getType()) && "expected ranked memref");
63
64 switch (options.memcpyOp) {
65 case linalg::BufferizeToAllocationOptions::MemcpyOp::
66 MaterializeInDestination: {
67 // Note: This is the preferred way of memcpy'ing because no layout map
68 // and/or memory space must be specified for the source.
69 auto materializeOp = bufferization::MaterializeInDestinationOp::create(
70 b, loc, tensorSource, memrefDest);
71 materializeOp.setWritable(true);
72 } break;
74 // TODO: Support custom memory space on source.
75 // We do not know the layout map of the source yet, so use a fully dynamic
76 // layout for best compatibility.
77 Value toBuffer = bufferization::ToBufferOp::create(
78 b, loc, bufferization::getMemRefTypeWithFullyDynamicLayout(tensorType),
79 tensorSource, /*read_only=*/true);
80 memref::CopyOp::create(b, loc, toBuffer, memrefDest);
81 } break;
83 // TODO: Support custom memory space on source.
84 // We do not know the layout map of the source yet, so use a fully dynamic
85 // layout for best compatibility.
86 Value toBuffer = bufferization::ToBufferOp::create(
87 b, loc, bufferization::getMemRefTypeWithFullyDynamicLayout(tensorType),
88 tensorSource, /*read_only=*/true);
89 linalg::CopyOp::create(b, loc, toBuffer, memrefDest);
90 } break;
91 };
92}
93
95 Location loc, PadOp padOp,
96 Value dest) {
97 OpBuilder::InsertionGuard g(rewriter);
98 RankedTensorType resultType = padOp.getResultType();
99
100 // Collect user/dialect attributes from the pad op to preserve on the newly
101 // created ops.
102 SmallVector<NamedAttribute> preservedAttrs =
103 getPrunedAttributeList(padOp, PadOp::getAttributeNames());
104
105 // Examine the yielded value to decide if a linalg.generic is needed or a
106 // linalg.fill is sufficient.
107 Value yieldedValue =
108 cast<tensor::YieldOp>(padOp.getBody()->getTerminator()).getValue();
109 Attribute constYieldedValue;
110 // Is the yielded value a bbArg defined outside of the PadOp?
111 bool outsideBbArg =
112 isa<BlockArgument>(yieldedValue) &&
113 cast<BlockArgument>(yieldedValue).getOwner()->getParentOp() !=
114 padOp.getOperation();
115 // Is the yielded value an OpResult defined outside of the PadOp?
116 bool outsideOpResult =
117 isa<OpResult>(yieldedValue) &&
118 yieldedValue.getDefiningOp()->getParentOp() != padOp.getOperation();
119 bool invariantYieldedValue = outsideBbArg || outsideOpResult;
120 if (matchPattern(yieldedValue, m_Constant(&constYieldedValue))) {
121 // Padding with a constant: Create linalg.fill.
122 Dialect *arithDialect =
123 rewriter.getContext()->getLoadedDialect<arith::ArithDialect>();
124 Value fillValue =
125 arithDialect
126 ->materializeConstant(rewriter, constYieldedValue,
127 yieldedValue.getType(), yieldedValue.getLoc())
128 ->getResult(0);
129 auto fillOp = linalg::FillOp::create(rewriter, loc, ValueRange(fillValue),
130 ValueRange(dest));
131 fillOp->setDiscardableAttrs(preservedAttrs);
132 return fillOp;
133 }
134
135 if (invariantYieldedValue) {
136 // Padding with an invariant value.
137 auto fillOp = linalg::FillOp::create(
138 rewriter, loc, ValueRange(yieldedValue), ValueRange(dest));
139 fillOp->setDiscardableAttrs(preservedAttrs);
140 return fillOp;
141 }
142
143 // Create linalg.generic.
144 SmallVector<utils::IteratorType> iteratorTypes(resultType.getRank(),
145 utils::IteratorType::parallel);
146 SmallVector<AffineMap> indexingMaps(
147 1, rewriter.getMultiDimIdentityMap(resultType.getRank()));
148 auto genericOp = linalg::GenericOp::create(
149 rewriter, loc, resultType, /*inputs=*/ValueRange(),
150 /*outputs=*/ValueRange{dest}, /*indexingMaps=*/
151 indexingMaps, iteratorTypes);
152 genericOp->setDiscardableAttrs(preservedAttrs);
153 Block *body = rewriter.createBlock(&genericOp->getRegion(0), {},
154 resultType.getElementType(), loc);
155 rewriter.setInsertionPointToStart(body);
156 SmallVector<Value> bbArgReplacements;
157 for (int64_t i = 0; i < resultType.getRank(); ++i)
158 bbArgReplacements.push_back(linalg::IndexOp::create(rewriter, loc, i));
159 rewriter.mergeBlocks(padOp.getBody(), body, bbArgReplacements);
160
161 // Update terminator.
162 auto yieldOp = cast<tensor::YieldOp>(body->getTerminator());
163 rewriter.replaceOpWithNewOp<linalg::YieldOp>(yieldOp, yieldOp.getValue());
164 return genericOp;
165}
166
168 Value value) {
169 auto tensorType = cast<RankedTensorType>(value.getType());
170 if (tensorType.hasStaticShape())
171 return {};
172
173 // Try to reify dynamic sizes.
174 ReifiedRankedShapedTypeDims reifiedShape;
175 if (isa<OpResult>(value) &&
176 succeeded(reifyResultShapes(b, value.getDefiningOp(), reifiedShape))) {
177 SmallVector<Value> dynSizes;
178 for (int64_t i = 0; i < tensorType.getRank(); ++i) {
179 if (tensorType.isDynamicDim(i))
180 dynSizes.push_back(cast<Value>(
181 reifiedShape[cast<OpResult>(value).getResultNumber()][i]));
182 }
183 return dynSizes;
184 }
185
186 // Create tensor.dim ops.
187 SmallVector<Value> dynSizes;
188 for (int64_t i = 0; i < tensorType.getRank(); ++i) {
189 if (tensorType.isDynamicDim(i))
190 dynSizes.push_back(
191 DimOp::create(b, value.getLoc(), value,
193 }
194 return dynSizes;
195}
196
197static Value
200 Attribute memorySpace = {}) {
201 OpBuilder::InsertionGuard g(rewriter);
202 auto tensorType = cast<RankedTensorType>(value.getType());
203
204 // Create buffer allocation.
205 auto memrefType =
206 cast<MemRefType>(bufferization::getMemRefTypeWithStaticIdentityLayout(
207 tensorType, memorySpace));
208 SmallVector<Value> dynamicSizes = reifyOrComputeDynamicSizes(rewriter, value);
209
210 Value alloc;
211 if (options.allocOp ==
213 alloc = memref::AllocOp::create(rewriter, loc, memrefType, dynamicSizes);
214 if (options.emitDealloc) {
215 // Place deallocation at the end of the block.
216 rewriter.setInsertionPoint(rewriter.getInsertionBlock()->getTerminator());
217 memref::DeallocOp::create(rewriter, loc, alloc);
218 }
219 } else if (options.allocOp ==
221 alloc = memref::AllocaOp::create(rewriter, loc, memrefType, dynamicSizes);
222 // No dealloc is needed.
223 }
224
225 return alloc;
226}
227
230 PadOp padOp, Attribute memorySpace, Operation *insertionPoint) {
231 // tensor.pad does not have a destination operand.
232 assert(!options.bufferizeDestinationOnly && "invalid options");
233
234 OpBuilder::InsertionGuard g(rewriter);
235 rewriter.setInsertionPoint(insertionPoint ? insertionPoint : padOp);
236 Location loc = padOp.getLoc();
237
238 // Create buffer allocation.
239 Value alloc = createAllocationForTensor(rewriter, loc, padOp.getResult(),
240 options, memorySpace);
241 rewriter.setInsertionPoint(padOp);
242
243 if (!padOp.hasZeroLowPad() || !padOp.hasZeroHighPad()) {
244 // Create linalg.fill or linalg.generic. Not needed if there is no padding.
245 Operation *fillOp =
246 movePaddingToFillOrGenericOp(rewriter, loc, padOp, alloc);
247 rewriter.setInsertionPointAfter(fillOp);
248 }
249
250 // Create memcpy.
252 getMixedSizes(rewriter, loc, padOp.getSource());
253 SmallVector<OpFoldResult> strides(padOp.getResultType().getRank(),
254 rewriter.getIndexAttr(1));
255 Value subview = memref::SubViewOp::create(
256 rewriter, loc, alloc, /*offsets=*/padOp.getMixedLowPad(), sizes, strides);
257 createMemcpy(rewriter, loc, padOp.getSource(), subview, options);
258
259 // Create bufferization.to_tensor with "restrict" and "writable". The returned
260 // tensor is a new buffer allocation, so it does not alias with any buffer.
261 Value toTensorOp = bufferization::ToTensorOp::create(
262 rewriter, loc, padOp.getResult().getType(), alloc, /*restrict=*/true,
263 /*writable=*/true);
264 rewriter.replaceOp(padOp, toTensorOp);
265 return alloc;
266}
267
270 vector::MaskOp maskOp, Attribute memorySpace, Operation *insertionPoint) {
271 assert(llvm::range_size(maskOp.getMaskBlock()->without_terminator()) == 1 &&
272 "expected single masked op");
273 OpBuilder::InsertionGuard g(rewriter);
274
275 // Should the bufferization options and state be function arguments?
276 bufferization::BufferizationOptions bufferizationOptions;
277 bufferization::BufferizationState bufferizationState;
278
279 Operation *yieldOp = maskOp.getMaskRegion().front().getTerminator();
280 assert(isa<vector::YieldOp>(yieldOp) && "expected yield op terminator");
281
282 // Bufferize maskable op. By default, place the buffer allocation right before
283 // the mask op.
285 rewriter, options, maskOp.getMaskableOp(), memorySpace,
286 /*insertionPoint=*/insertionPoint ? insertionPoint : maskOp);
287
288 if (options.bufferizeDestinationOnly)
289 return alloc;
290
291 // Bufferize terminator.
292 rewriter.setInsertionPoint(yieldOp);
293 if (failed(cast<bufferization::BufferizableOpInterface>(yieldOp).bufferize(
294 rewriter, bufferizationOptions, bufferizationState)))
295 return nullptr;
296
297 // Erase dead to_tensor ops inside of the mask op. This is necessary because
298 // there only be one op (apart from the terminator) inside the mask op.
299 // TODO: Remove dead to_tensor ops more aggressively during bufferization.
300 SmallVector<Operation *> toTensorOps;
301 maskOp.walk([&](bufferization::ToTensorOp toTensorOp) {
302 if (toTensorOp->getUses().empty())
303 toTensorOps.push_back(toTensorOp.getOperation());
304 });
305 for (Operation *op : toTensorOps)
306 rewriter.eraseOp(op);
307
308 // Bufferize mask op.
309 SmallVector<OpOperand *> resultUses;
310 for (Value result : maskOp.getResults())
311 if (isa<TensorType>(result.getType()))
312 for (OpOperand &use : result.getUses())
313 resultUses.push_back(&use);
314 rewriter.setInsertionPoint(maskOp);
315 if (failed(
316 cast<bufferization::BufferizableOpInterface>(maskOp.getOperation())
317 .bufferize(rewriter, bufferizationOptions, bufferizationState)))
318 return nullptr;
319
320 // Set "restrict" attribute, indicating that no other tensor aliases with
321 // this tensor. That is because we just allocated a new buffer for the tensor.
322 for (OpOperand *resultUse : resultUses) {
323 auto toTensorOp =
324 resultUse->get().getDefiningOp<bufferization::ToTensorOp>();
325 assert(toTensorOp && "expected to_tensor op");
326 rewriter.modifyOpInPlace(toTensorOp, [&]() {
327 toTensorOp.setRestrict(true);
328 toTensorOp.setWritable(true);
329 });
330 }
331
332 return alloc;
333}
334
337 bufferization::AllocTensorOp allocTensorOp, Attribute memorySpace,
338 Operation *insertionPoint) {
339 Location loc = allocTensorOp.getLoc();
340 OpBuilder::InsertionGuard g(rewriter);
341 rewriter.setInsertionPoint(insertionPoint ? insertionPoint : allocTensorOp);
342 bufferization::BufferizationOptions bufferizationOptions;
343
344 // Create buffer allocation.
346 rewriter, loc, allocTensorOp.getResult(), options, memorySpace);
347
348 // Create bufferization.to_tensor with "restrict" and "writable". The returned
349 // tensor is a new buffer allocation, so it does not alias with any buffer.
350 Value toTensorOp = bufferization::ToTensorOp::create(
351 rewriter, loc, allocTensorOp.getResult().getType(), alloc,
352 /*restrict=*/true,
353 /*writable=*/true);
354 rewriter.replaceOp(allocTensorOp, toTensorOp);
355 return alloc;
356}
357
358/// Lower tensor.from_elements to a sequence of chained tensor.insert.
360 RewriterBase &rewriter, tensor::FromElementsOp fromElementsOp) {
361 Location loc = fromElementsOp.getLoc();
362 RankedTensorType tensorType =
363 cast<RankedTensorType>(fromElementsOp.getType());
364 auto shape = tensorType.getShape();
365
366 // Create tensor.empty.
367 auto emptyOp = EmptyOp::create(rewriter, loc, tensorType, ValueRange());
368
369 // Case: tensor<elem_type>.
370 if (shape.empty()) {
371 Operation *res = rewriter.replaceOpWithNewOp<tensor::InsertOp>(
372 fromElementsOp, fromElementsOp.getElements().front(),
373 emptyOp.getResult(), ValueRange());
374 return res;
375 }
376
377 // Create constants for the range of possible indices [0, max{shape_i}).
378 auto maxDim = *llvm::max_element(shape);
379 SmallVector<Value, 2> constants;
380 constants.reserve(maxDim);
381 for (int i = 0; i < maxDim; ++i)
382 constants.push_back(arith::ConstantIndexOp::create(rewriter, loc, i));
383
384 // Traverse all elements and create tensor.insert ops.
385 auto elementIt = fromElementsOp.getElements().begin();
386 SmallVector<Value, 2> indices(tensorType.getRank(), constants[0]);
387 Value result = createInserts(rewriter, loc, /*dim=*/0, emptyOp.getResult(),
388 shape, constants, elementIt, indices);
389
390 // Replace tensor.from_elements.
391 rewriter.replaceOp(fromElementsOp, result);
392 return result.getDefiningOp();
393}
394
395/// Lower tensor.generate to linalg.generic.
396FailureOr<Operation *>
398 tensor::GenerateOp generateOp) {
399 // Only ops with exactly one block are supported.
400 if (!generateOp.getBody().hasOneBlock())
401 return failure();
402
403 Location loc = generateOp.getLoc();
404 RankedTensorType tensorType = cast<RankedTensorType>(generateOp.getType());
405
406 // Create tensor.empty.
407 auto emptyOp = EmptyOp::create(rewriter, loc, tensorType,
408 generateOp.getDynamicExtents());
409
410 // Create linalg.generic.
411 SmallVector<utils::IteratorType> iteratorTypes(tensorType.getRank(),
412 utils::IteratorType::parallel);
413 SmallVector<AffineMap> indexingMaps(
414 1, rewriter.getMultiDimIdentityMap(tensorType.getRank()));
415 auto genericOp = linalg::GenericOp::create(
416 rewriter, loc, tensorType, /*inputs=*/ValueRange(),
417 /*outputs=*/ValueRange{emptyOp.getResult()}, /*indexingMaps=*/
418 indexingMaps, iteratorTypes);
419 Block *body = rewriter.createBlock(&genericOp->getRegion(0), {},
420 tensorType.getElementType(), loc);
421 rewriter.setInsertionPointToStart(body);
422 SmallVector<Value> bbArgReplacements;
423 for (int64_t i = 0; i < tensorType.getRank(); ++i)
424 bbArgReplacements.push_back(linalg::IndexOp::create(rewriter, loc, i));
425 rewriter.mergeBlocks(&generateOp.getBody().front(), body, bbArgReplacements);
426
427 // Update terminator.
428 auto yieldOp = cast<tensor::YieldOp>(body->getTerminator());
429 rewriter.replaceOpWithNewOp<linalg::YieldOp>(yieldOp, yieldOp.getValue());
430
431 // Replace tensor.generate.
432 rewriter.replaceOp(generateOp, genericOp->getResult(0));
433 return genericOp.getOperation();
434}
435
436/// Lower tensor.pad to linalg.generic + tensor.insert_slice.
437FailureOr<Operation *>
439 tensor::PadOp padOp) {
440 // Only ops with exactly one block are supported.
441 if (!padOp.getBodyRegion().hasOneBlock())
442 return failure();
443
444 // Create tensor.empty.
445 Location loc = padOp.getLoc();
446 RankedTensorType resultType = padOp.getResultType();
447 ReifiedRankedShapedTypeDims reifiedShape;
448 if (failed(reifyResultShapes(rewriter, padOp, reifiedShape)))
449 return rewriter.notifyMatchFailure(
450 padOp, "failed to reify tensor.pad op result shape");
451 SmallVector<Value> dynamicSizes;
452 for (int64_t i = 0; i < resultType.getRank(); ++i)
453 if (resultType.isDynamicDim(i))
454 dynamicSizes.push_back(cast<Value>(reifiedShape[0][i]));
455
456 // If the `padOp` has a nofold attribute and all paddings are known to be 0,
457 // explicitly insert a `linalg.copy`.
458 if (padOp.getNofoldAttr() &&
459 llvm::all_of(padOp.getMixedLowPad(), isZeroInteger) &&
460 llvm::all_of(padOp.getMixedHighPad(), isZeroInteger)) {
461 using bufferization::AllocTensorOp;
462 Value allocated =
463 AllocTensorOp::create(rewriter, loc, resultType, dynamicSizes);
464 auto copyOp = rewriter.replaceOpWithNewOp<linalg::CopyOp>(
465 padOp, padOp.getSource(), allocated);
466 return copyOp.getOperation();
467 }
468
469 Value empty = EmptyOp::create(rewriter, loc, resultType, dynamicSizes);
470 // Create linalg.fill or linalg.generic.
471 Operation *fillOp = movePaddingToFillOrGenericOp(rewriter, loc, padOp, empty);
472 rewriter.setInsertionPointAfter(fillOp);
473
474 // Create tensor::InsertSliceOp.
475 SmallVector<OpFoldResult> sliceSizes =
476 getMixedSizes(rewriter, loc, padOp.getSource());
477 SmallVector<OpFoldResult> sliceStrides(resultType.getRank(),
478 rewriter.getIndexAttr(1));
479 auto insertSliceOp = rewriter.replaceOpWithNewOp<tensor::InsertSliceOp>(
480 padOp, padOp.getSource(), fillOp->getResult(0),
481 /*offsets=*/padOp.getMixedLowPad(), sliceSizes, sliceStrides);
482 return insertSliceOp.getOperation();
483}
484
487 Operation *op, Attribute memorySpace, Operation *insertionPoint) {
488 using namespace bufferization;
489
490 // Call specialized overload for certain ops.
491 if (auto padOp = dyn_cast<tensor::PadOp>(op))
492 return bufferizeToAllocation(rewriter, options, padOp, memorySpace);
493 if (auto maskOp = dyn_cast<vector::MaskOp>(op))
494 return bufferizeToAllocation(rewriter, options, maskOp, memorySpace);
495 if (auto allocTensorOp = dyn_cast<bufferization::AllocTensorOp>(op))
496 return bufferizeToAllocation(rewriter, options, allocTensorOp, memorySpace);
497
498 // Only bufferizable ops are supported.
499 auto bufferizableOp = dyn_cast<BufferizableOpInterface>(op);
500 if (!bufferizableOp)
501 return nullptr;
502
503 // Should the bufferization options and states be function arguments?
504 BufferizationOptions bufferizationOptions;
505 AnalysisState analysisState(bufferizationOptions);
506 BufferizationState bufferizationState;
507
508#ifndef NDEBUG
509 if (!options.bufferizeDestinationOnly) {
510 // Ops with nested tensor ops are not supported yet. At the moment, this
511 // function just bufferizes the given op itself, but not its body.
512 op->walk([&](Operation *nestedOp) {
513 if (op == nestedOp)
514 return;
515 if (llvm::any_of(nestedOp->getOperands(),
516 [](Value v) { return isa<TensorType>(v.getType()); }))
517 llvm_unreachable("ops with nested tensor ops are not supported yet");
518 if (llvm::any_of(nestedOp->getResults(),
519 [](Value v) { return isa<TensorType>(v.getType()); }))
520 llvm_unreachable("ops with nested tensor ops are not supported yet");
521 });
522 }
523#endif // NDEBUG
524
525 // Gather tensor results.
526 SmallVector<OpResult> tensorResults;
527 for (OpResult result : op->getResults()) {
528 if (!isa<TensorType>(result.getType()))
529 continue;
530 // Unranked tensors are not supported
531 if (!isa<RankedTensorType>(result.getType()))
532 return nullptr;
533 // Ops that bufferize to an allocation are not supported.
534 if (bufferizableOp.bufferizesToAllocation(result))
535 return nullptr;
536 tensorResults.push_back(result);
537 }
538
539 // Gather all operands that should bufferize to a new allocation. I.e.,
540 // bufferize out-of-place.
541 SmallVector<OpOperand *> outOfPlaceOperands, resultUses;
542 auto addOutOfPlaceOperand = [&](OpOperand *operand) {
543 if (!llvm::is_contained(outOfPlaceOperands, operand))
544 outOfPlaceOperands.push_back(operand);
545 };
546 for (OpResult result : tensorResults) {
547 AliasingOpOperandList aliasingOperands =
548 analysisState.getAliasingOpOperands(result);
549 for (const AliasingOpOperand &operand : aliasingOperands) {
550 addOutOfPlaceOperand(operand.opOperand);
551 for (OpOperand &resultUse : result.getUses())
552 resultUses.push_back(&resultUse);
553 }
554 }
555 for (OpOperand &operand : op->getOpOperands()) {
556 if (!analysisState.bufferizesToMemoryWrite(operand))
557 continue;
558 if (!isa<RankedTensorType>(operand.get().getType()))
559 continue;
560 addOutOfPlaceOperand(&operand);
561 }
562 // TODO: Support multiple buffers.
563 if (outOfPlaceOperands.size() != 1)
564 return nullptr;
565
566 // Allocate buffers.
567 OpBuilder::InsertionGuard g(rewriter);
568 rewriter.setInsertionPoint(insertionPoint ? insertionPoint : op);
569 SmallVector<Value> allocs;
570 for (OpOperand *operand : outOfPlaceOperands) {
572 rewriter, op->getLoc(), operand->get(), options, memorySpace);
573 allocs.push_back(alloc);
574 if (!analysisState.findDefinitions(operand).empty()) {
575 // Initialize buffer with a copy of the operand data. Not needed if the
576 // tensor is uninitialized.
577 createMemcpy(rewriter, op->getLoc(), operand->get(), alloc, options);
578 }
579 rewriter.modifyOpInPlace(op, [&]() {
580 auto toTensorOp = ToTensorOp::create(rewriter, op->getLoc(),
581 operand->get().getType(), alloc);
582 operand->set(toTensorOp);
583 if (options.bufferizeDestinationOnly) {
584 rewriter.modifyOpInPlace(toTensorOp, [&]() {
585 toTensorOp.setRestrict(true);
586 toTensorOp.setWritable(true);
587 });
588 }
589 });
590 }
591
592 if (options.bufferizeDestinationOnly)
593 return allocs.front();
594
595 // Bufferize the op.
596 rewriter.setInsertionPoint(op);
597 if (failed(bufferizableOp.bufferize(rewriter, bufferizationOptions,
598 bufferizationState)))
599 return nullptr;
600
601 // Set "restrict" attribute, indicating that no other tensor aliases with
602 // this tensor. That is because we just allocated a new buffer for the tensor.
603 for (OpOperand *resultUse : resultUses) {
604 auto toTensorOp = resultUse->get().getDefiningOp<ToTensorOp>();
605 assert(toTensorOp && "expected to_tensor op");
606 rewriter.modifyOpInPlace(toTensorOp, [&]() {
607 toTensorOp.setRestrict(true);
608 toTensorOp.setWritable(true);
609 });
610 }
611 return allocs.front();
612}
613
614namespace {
615
616template <typename OpTy>
617LogicalResult rewriteOpInDestinationPassingStyle(OpTy op,
618 PatternRewriter &rewriter) {
619 return linalg::rewriteInDestinationPassingStyle(rewriter, op);
620}
621
622} // namespace
623
625 RewritePatternSet &patterns) {
626 patterns.add(rewriteOpInDestinationPassingStyle<tensor::FromElementsOp>);
627 patterns.add(rewriteOpInDestinationPassingStyle<tensor::GenerateOp>);
628 patterns.add(rewriteOpInDestinationPassingStyle<tensor::PadOp>);
629}
static Value createAllocationForTensor(RewriterBase &rewriter, Location loc, Value value, const linalg::BufferizeToAllocationOptions &options, Attribute memorySpace={})
static SmallVector< Value > reifyOrComputeDynamicSizes(OpBuilder &b, Value value)
static void createMemcpy(OpBuilder &b, Location loc, Value tensorSource, Value memrefDest, const linalg::BufferizeToAllocationOptions &options)
Create a memcpy from the given source tensor to the given destination memref.
static Operation * movePaddingToFillOrGenericOp(RewriterBase &rewriter, Location loc, PadOp padOp, Value dest)
static Value createInserts(RewriterBase &rewriter, Location loc, int dim, Value destination, ArrayRef< int64_t > shape, ArrayRef< Value > constants, OperandRange::iterator &elementIt, SmallVectorImpl< Value > &indices)
b
Return true if permutation is a valid permutation of the outer_dims_perm (case OuterOrInnerPerm::Oute...
static llvm::ManagedStatic< PassManagerOptions > options
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:33
Operation * getTerminator()
Get the terminator operation of this block.
Definition Block.cpp:249
IntegerAttr getIndexAttr(int64_t value)
Definition Builders.cpp:112
AffineMap getMultiDimIdentityMap(unsigned rank)
Definition Builders.cpp:391
MLIRContext * getContext() const
Definition Builders.h:56
Dialects are groups of MLIR operations, types and attributes, as well as behavior associated with the...
Definition Dialect.h:38
virtual Operation * materializeConstant(OpBuilder &builder, Attribute value, Type type, Location loc)
Registered hook to materialize a single constant operation from a given attribute value with the desi...
Definition Dialect.h:83
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition Location.h:76
Dialect * getLoadedDialect(StringRef name)
Get a registered IR dialect with the given namespace.
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 * createBlock(Region *parent, Region::iterator insertPt={}, TypeRange argTypes={}, ArrayRef< Location > locs={})
Add new block with 'argTypes' arguments and set the insertion point to the end of it.
Definition Builders.cpp:434
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
Block * getInsertionBlock() const
Return the block the current insertion point belongs to.
Definition Builders.h:444
void setInsertionPointAfter(Operation *op)
Sets the insertion point to the node after the specified operation, which will cause subsequent inser...
Definition Builders.h:414
This class represents an operand of an operation.
Definition Value.h:257
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 getResult(unsigned idx)
Get the 'idx'th result of this operation.
Definition Operation.h:436
Location getLoc()
The source location the operation was defined or derived from.
Definition Operation.h:244
Operation * getParentOp()
Returns the closest surrounding operation that contains this operation or nullptr if this is a top-le...
Definition Operation.h:255
MutableArrayRef< OpOperand > getOpOperands()
Definition Operation.h:412
operand_range getOperands()
Returns an iterator on the underlying Value's.
Definition Operation.h:407
std::enable_if_t< llvm::function_traits< std::decay_t< FnT > >::num_args==1, RetT > walk(FnT &&callback)
Walk the operation by calling the callback for each nested operation (including this one),...
Definition Operation.h:826
result_range getResults()
Definition Operation.h:444
A special type of RewriterBase that coordinates the application of a rewrite pattern on the current I...
RewritePatternSet & add(ConstructorArg &&arg, ConstructorArgs &&...args)
Add an instance of each of the pattern types 'Ts' to the pattern list with the given arguments.
This class coordinates the application of a rewrite on a set of IR, providing a way for clients to tr...
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 mergeBlocks(Block *source, Block *dest, ValueRange argValues={})
Inline the operations of block 'source' into the end of block 'dest'.
std::enable_if_t<!std::is_convertible< CallbackT, Twine >::value, LogicalResult > notifyMatchFailure(Location loc, CallbackT &&reasonCallback)
Used to notify the listener that the IR failed to be rewritten because of a match failure,...
void modifyOpInPlace(Operation *root, CallableT &&callable)
This method is a utility wrapper around an in-place modification of an operation.
OpTy replaceOpWithNewOp(Operation *op, Args &&...args)
Replace the results of the given (original) op with a new op that is created without verification (re...
This class provides an abstraction over the different types of ranges over Values.
Definition ValueRange.h:387
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition Value.h:96
Type getType() const
Return the type of this value.
Definition Value.h:105
Location getLoc() const
Return the location of this value.
Definition Value.cpp:24
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
Definition Value.cpp:18
static ConstantIndexOp create(OpBuilder &builder, Location location, int64_t value)
Definition ArithOps.cpp:363
Value bufferizeToAllocation(RewriterBase &rewriter, const BufferizeToAllocationOptions &options, tensor::PadOp padOp, Attribute memorySpace={}, Operation *insertionPoint=nullptr)
Materialize a buffer allocation for the given tensor.pad op and lower the op to linalg....
FailureOr< Operation * > rewriteInDestinationPassingStyle(RewriterBase &rewriter, tensor::FromElementsOp fromElementsOp)
Rewrite tensor.from_elements to linalg.generic.
void populateConvertToDestinationStylePatterns(RewritePatternSet &patterns)
Populate patterns that convert non-destination-style ops to destination style ops.
SmallVector< OpFoldResult > getMixedSizes(OpBuilder &builder, Location loc, Value value)
Return the dimensions of the given tensor value.
Definition TensorOps.cpp:68
Include the generated interface declarations.
bool matchPattern(Value value, const Pattern &pattern)
Entry point for matching a pattern over a Value.
Definition Matchers.h:490
LogicalResult reifyResultShapes(OpBuilder &b, Operation *op, ReifiedRankedShapedTypeDims &reifiedReturnShapes)
Reify the shape of the result of an operation (typically in terms of the shape of its operands).
SmallVector< SmallVector< OpFoldResult > > ReifiedRankedShapedTypeDims
bool isZeroInteger(OpFoldResult v)
Return "true" if v is an integer value/attribute with constant value 0.
detail::constant_op_matcher m_Constant()
Matches a constant foldable operation.
Definition Matchers.h:369
SmallVector< NamedAttribute > getPrunedAttributeList(Operation *op, ArrayRef< StringRef > elidedAttrs)