119efe141SMatthias Springer //===- BufferizableOpInterfaceImpl.cpp - Impl. of BufferizableOpInterface -===//
219efe141SMatthias Springer //
319efe141SMatthias Springer // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
419efe141SMatthias Springer // See https://llvm.org/LICENSE.txt for license information.
519efe141SMatthias Springer // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
619efe141SMatthias Springer //
719efe141SMatthias Springer //===----------------------------------------------------------------------===//
819efe141SMatthias Springer 
919efe141SMatthias Springer #include "mlir/Dialect/SCF/BufferizableOpInterfaceImpl.h"
1019efe141SMatthias Springer 
1119efe141SMatthias Springer #include "mlir/Dialect/Bufferization/IR/BufferizableOpInterface.h"
1219efe141SMatthias Springer #include "mlir/Dialect/Bufferization/IR/Bufferization.h"
131e1eeae8SMatthias Springer #include "mlir/Dialect/Bufferization/Transforms/OneShotAnalysis.h"
14eda6f907SRiver Riddle #include "mlir/Dialect/MemRef/IR/MemRef.h"
1519efe141SMatthias Springer #include "mlir/Dialect/SCF/SCF.h"
1672de7588SNicolas Vasilache #include "mlir/Dialect/Tensor/IR/Tensor.h"
1719efe141SMatthias Springer #include "mlir/IR/Dialect.h"
1819efe141SMatthias Springer #include "mlir/IR/Operation.h"
1919efe141SMatthias Springer #include "mlir/IR/PatternMatch.h"
2019efe141SMatthias Springer 
2119efe141SMatthias Springer using namespace mlir;
2219efe141SMatthias Springer using namespace mlir::bufferization;
2319efe141SMatthias Springer using namespace mlir::scf;
2419efe141SMatthias Springer 
2519efe141SMatthias Springer namespace mlir {
2619efe141SMatthias Springer namespace scf {
2719efe141SMatthias Springer namespace {
2819efe141SMatthias Springer 
2919efe141SMatthias Springer // bufferization.to_memref is not allowed to change the rank.
3019efe141SMatthias Springer static void ensureToMemrefOpIsValid(Value tensor, Type memrefType) {
3119efe141SMatthias Springer #ifndef NDEBUG
3219efe141SMatthias Springer   auto rankedTensorType = tensor.getType().dyn_cast<RankedTensorType>();
3319efe141SMatthias Springer   assert((!rankedTensorType || (memrefType.cast<MemRefType>().getRank() ==
3419efe141SMatthias Springer                                 rankedTensorType.getRank())) &&
3519efe141SMatthias Springer          "to_memref would be invalid: mismatching ranks");
3619efe141SMatthias Springer #endif
3719efe141SMatthias Springer }
3819efe141SMatthias Springer 
3919efe141SMatthias Springer /// Bufferization of scf.execute_region. Can be analyzed, but bufferization not
4019efe141SMatthias Springer /// fully implemented at the moment.
4119efe141SMatthias Springer struct ExecuteRegionOpInterface
4219efe141SMatthias Springer     : public BufferizableOpInterface::ExternalModel<ExecuteRegionOpInterface,
4319efe141SMatthias Springer                                                     scf::ExecuteRegionOp> {
4419efe141SMatthias Springer   SmallVector<OpOperand *>
4519efe141SMatthias Springer   getAliasingOpOperand(Operation *op, OpResult opResult,
469597b16aSMatthias Springer                        const AnalysisState &state) const {
4719efe141SMatthias Springer     // ExecuteRegionOps do not have tensor OpOperands. The yielded value can be
4819efe141SMatthias Springer     // any SSA value that is in scope. To allow for use-def chain traversal
4919efe141SMatthias Springer     // through ExecuteRegionOps in the analysis, the corresponding yield value
5019efe141SMatthias Springer     // is considered to be aliasing with the result.
5119efe141SMatthias Springer     auto executeRegionOp = cast<scf::ExecuteRegionOp>(op);
5219efe141SMatthias Springer     size_t resultNum = std::distance(op->getOpResults().begin(),
5319efe141SMatthias Springer                                      llvm::find(op->getOpResults(), opResult));
5419efe141SMatthias Springer     // TODO: Support multiple blocks.
5519efe141SMatthias Springer     assert(executeRegionOp.getRegion().getBlocks().size() == 1 &&
5619efe141SMatthias Springer            "expected exactly 1 block");
5719efe141SMatthias Springer     auto yieldOp = dyn_cast<scf::YieldOp>(
5819efe141SMatthias Springer         executeRegionOp.getRegion().front().getTerminator());
5919efe141SMatthias Springer     assert(yieldOp && "expected scf.yield terminator in scf.execute_region");
6019efe141SMatthias Springer     return {&yieldOp->getOpOperand(resultNum)};
6119efe141SMatthias Springer   }
6219efe141SMatthias Springer 
6319efe141SMatthias Springer   // TODO: For better bufferization results, this could return `true` only if
6419efe141SMatthias Springer   // there is a memory write in the region.
6519efe141SMatthias Springer   bool isMemoryWrite(Operation *op, OpResult opResult,
669597b16aSMatthias Springer                      const AnalysisState &state) const {
6719efe141SMatthias Springer     // Similar to scf.if, results of this op are always considered memory writes
6819efe141SMatthias Springer     // in the analysis. This is a useful pattern for all ops that have tensor
6919efe141SMatthias Springer     // OpResults but no tensor OpOperands. By default, `isMemoryWrite` is
7019efe141SMatthias Springer     // implemented in terms of `bufferizesToMemoryWrite`, which does not work on
7119efe141SMatthias Springer     // ops without OpOperands.
7219efe141SMatthias Springer     return true;
7319efe141SMatthias Springer   }
7419efe141SMatthias Springer 
7519efe141SMatthias Springer   LogicalResult bufferize(Operation *op, RewriterBase &rewriter,
76b55d55ecSMatthias Springer                           const BufferizationOptions &options) const {
7719efe141SMatthias Springer     auto executeRegionOp = cast<scf::ExecuteRegionOp>(op);
7819efe141SMatthias Springer 
7919efe141SMatthias Springer     // Compute new result types.
8019efe141SMatthias Springer     SmallVector<Type> newResultTypes;
8119efe141SMatthias Springer     for (Type type : executeRegionOp->getResultTypes()) {
8219efe141SMatthias Springer       if (auto tensorType = type.dyn_cast<TensorType>()) {
8312e41d92SMatthias Springer         // TODO: Infer the result type instead of computing it.
84b55d55ecSMatthias Springer         newResultTypes.push_back(getMemRefType(tensorType, options));
8519efe141SMatthias Springer       } else {
8619efe141SMatthias Springer         newResultTypes.push_back(type);
8719efe141SMatthias Springer       }
8819efe141SMatthias Springer     }
8919efe141SMatthias Springer 
9019efe141SMatthias Springer     // Create new op and move over region.
9119efe141SMatthias Springer     auto newOp =
9219efe141SMatthias Springer         rewriter.create<scf::ExecuteRegionOp>(op->getLoc(), newResultTypes);
9319efe141SMatthias Springer     newOp.getRegion().takeBody(executeRegionOp.getRegion());
9419efe141SMatthias Springer 
9519efe141SMatthias Springer     // Update terminator.
9619efe141SMatthias Springer     assert(newOp.getRegion().getBlocks().size() == 1 &&
9719efe141SMatthias Springer            "only 1 block supported");
9819efe141SMatthias Springer     Block *newBlock = &newOp.getRegion().front();
9919efe141SMatthias Springer     auto yieldOp = cast<scf::YieldOp>(newBlock->getTerminator());
10019efe141SMatthias Springer     rewriter.setInsertionPoint(yieldOp);
10119efe141SMatthias Springer     SmallVector<Value> newYieldValues;
102bb6119ebSMehdi Amini     for (const auto &it : llvm::enumerate(yieldOp.getResults())) {
10319efe141SMatthias Springer       Value val = it.value();
10419efe141SMatthias Springer       if (val.getType().isa<TensorType>()) {
10519efe141SMatthias Springer         newYieldValues.push_back(rewriter.create<bufferization::ToMemrefOp>(
10619efe141SMatthias Springer             yieldOp.getLoc(), newResultTypes[it.index()], val));
10719efe141SMatthias Springer       } else {
10819efe141SMatthias Springer         newYieldValues.push_back(val);
10919efe141SMatthias Springer       }
11019efe141SMatthias Springer     }
11119efe141SMatthias Springer     rewriter.replaceOpWithNewOp<scf::YieldOp>(yieldOp, newYieldValues);
11219efe141SMatthias Springer 
11319efe141SMatthias Springer     // Update all uses of the old op.
11419efe141SMatthias Springer     rewriter.setInsertionPointAfter(newOp);
11519efe141SMatthias Springer     SmallVector<Value> newResults;
116bb6119ebSMehdi Amini     for (const auto &it : llvm::enumerate(executeRegionOp->getResultTypes())) {
11719efe141SMatthias Springer       if (it.value().isa<TensorType>()) {
11819efe141SMatthias Springer         newResults.push_back(rewriter.create<bufferization::ToTensorOp>(
11919efe141SMatthias Springer             executeRegionOp.getLoc(), newOp->getResult(it.index())));
12019efe141SMatthias Springer       } else {
12119efe141SMatthias Springer         newResults.push_back(newOp->getResult(it.index()));
12219efe141SMatthias Springer       }
12319efe141SMatthias Springer     }
12419efe141SMatthias Springer 
12519efe141SMatthias Springer     // Replace old op.
12619efe141SMatthias Springer     rewriter.replaceOp(executeRegionOp, newResults);
12719efe141SMatthias Springer 
12819efe141SMatthias Springer     return success();
12919efe141SMatthias Springer   }
13019efe141SMatthias Springer 
13119efe141SMatthias Springer   BufferRelation bufferRelation(Operation *op, OpResult opResult,
1329597b16aSMatthias Springer                                 const AnalysisState &state) const {
13319efe141SMatthias Springer     return BufferRelation::Equivalent;
13419efe141SMatthias Springer   }
13519efe141SMatthias Springer };
13619efe141SMatthias Springer 
13719efe141SMatthias Springer /// Bufferization of scf.if. Replace with a new scf.if that yields memrefs.
13819efe141SMatthias Springer struct IfOpInterface
13919efe141SMatthias Springer     : public BufferizableOpInterface::ExternalModel<IfOpInterface, scf::IfOp> {
14019efe141SMatthias Springer   SmallVector<OpOperand *>
14119efe141SMatthias Springer   getAliasingOpOperand(Operation *op, OpResult opResult,
1429597b16aSMatthias Springer                        const AnalysisState &state) const {
14319efe141SMatthias Springer     // IfOps do not have tensor OpOperands. The yielded value can be any SSA
14419efe141SMatthias Springer     // value that is in scope. To allow for use-def chain traversal through
14519efe141SMatthias Springer     // IfOps in the analysis, both corresponding yield values from the then/else
14619efe141SMatthias Springer     // branches are considered to be aliasing with the result.
14719efe141SMatthias Springer     auto ifOp = cast<scf::IfOp>(op);
14819efe141SMatthias Springer     size_t resultNum = std::distance(op->getOpResults().begin(),
14919efe141SMatthias Springer                                      llvm::find(op->getOpResults(), opResult));
15019efe141SMatthias Springer     return {&ifOp.thenYield()->getOpOperand(resultNum),
15119efe141SMatthias Springer             &ifOp.elseYield()->getOpOperand(resultNum)};
15219efe141SMatthias Springer   }
15319efe141SMatthias Springer 
15419efe141SMatthias Springer   // TODO: For better bufferization results, this could return `true` only if
15519efe141SMatthias Springer   // there is a memory write in one (or both) of the branches. Since this is not
15619efe141SMatthias Springer   // allowed at the moment, we should never encounter scf.ifs that yield
15719efe141SMatthias Springer   // unmodified tensors. Such scf.yield ops could just fold away.
15819efe141SMatthias Springer   bool isMemoryWrite(Operation *op, OpResult opResult,
1599597b16aSMatthias Springer                      const AnalysisState &state) const {
16019efe141SMatthias Springer     // IfOp results are always considered memory writes in the analysis. This
16119efe141SMatthias Springer     // design decision simplifies the analysis considerably. E.g., consider the
16219efe141SMatthias Springer     // following test case:
16319efe141SMatthias Springer     //
16419efe141SMatthias Springer     // %0 = "some_writing_op" : tensor<?xf32>
16519efe141SMatthias Springer     // %r = scf.if %c -> (tensor<?xf32>) {
16619efe141SMatthias Springer     //   scf.yield %0
16719efe141SMatthias Springer     // } else {
16819efe141SMatthias Springer     //   %1 = "another_writing_op"(%0) : tensor<?xf32>
16919efe141SMatthias Springer     // }
17019efe141SMatthias Springer     // "some_reading_op"(%r)
17119efe141SMatthias Springer     //
17219efe141SMatthias Springer     // "another_writing_op" in the above example should be able to bufferize
17319efe141SMatthias Springer     // inplace in the absence of another read of %0. However, if the scf.if op
17419efe141SMatthias Springer     // would not be considered a "write", the analysis would detect the
17519efe141SMatthias Springer     // following conflict:
17619efe141SMatthias Springer     //
17719efe141SMatthias Springer     // * read = some_reading_op
17819efe141SMatthias Springer     // * lastWrite = %0  (Note: The last write of %r would be a set: {%0, %1}.)
17919efe141SMatthias Springer     // * conflictingWrite = %1
18019efe141SMatthias Springer     //
18119efe141SMatthias Springer     // For more details, check the "scf.IfOp" section of the design document.
18219efe141SMatthias Springer     return true;
18319efe141SMatthias Springer   }
18419efe141SMatthias Springer 
18519efe141SMatthias Springer   LogicalResult bufferize(Operation *op, RewriterBase &rewriter,
186b55d55ecSMatthias Springer                           const BufferizationOptions &options) const {
18719efe141SMatthias Springer     auto ifOp = cast<scf::IfOp>(op);
18819efe141SMatthias Springer 
18919efe141SMatthias Springer     // Compute new types of the bufferized scf.if op.
19019efe141SMatthias Springer     SmallVector<Type> newTypes;
19119efe141SMatthias Springer     for (Type returnType : ifOp->getResultTypes()) {
19219efe141SMatthias Springer       if (auto tensorType = returnType.dyn_cast<TensorType>()) {
19312e41d92SMatthias Springer         // TODO: Infer the result type instead of computing it.
194b55d55ecSMatthias Springer         newTypes.push_back(getMemRefType(tensorType, options));
19519efe141SMatthias Springer       } else {
19619efe141SMatthias Springer         newTypes.push_back(returnType);
19719efe141SMatthias Springer       }
19819efe141SMatthias Springer     }
19919efe141SMatthias Springer 
20019efe141SMatthias Springer     // Create new op.
20119efe141SMatthias Springer     auto newIfOp =
20219efe141SMatthias Springer         rewriter.create<scf::IfOp>(ifOp.getLoc(), newTypes, ifOp.getCondition(),
20319efe141SMatthias Springer                                    /*withElseRegion=*/true);
20419efe141SMatthias Springer 
20519efe141SMatthias Springer     // Remove terminators.
20619efe141SMatthias Springer     if (!newIfOp.thenBlock()->empty()) {
20719efe141SMatthias Springer       rewriter.eraseOp(newIfOp.thenBlock()->getTerminator());
20819efe141SMatthias Springer       rewriter.eraseOp(newIfOp.elseBlock()->getTerminator());
20919efe141SMatthias Springer     }
21019efe141SMatthias Springer 
21119efe141SMatthias Springer     // Move over then/else blocks.
21219efe141SMatthias Springer     rewriter.mergeBlocks(ifOp.thenBlock(), newIfOp.thenBlock());
21319efe141SMatthias Springer     rewriter.mergeBlocks(ifOp.elseBlock(), newIfOp.elseBlock());
21419efe141SMatthias Springer 
21519efe141SMatthias Springer     // Update scf.yield of new then-block.
21619efe141SMatthias Springer     auto thenYieldOp = cast<scf::YieldOp>(newIfOp.thenBlock()->getTerminator());
21719efe141SMatthias Springer     rewriter.setInsertionPoint(thenYieldOp);
21819efe141SMatthias Springer     SmallVector<Value> thenYieldValues;
21919efe141SMatthias Springer     for (OpOperand &operand : thenYieldOp->getOpOperands()) {
22019efe141SMatthias Springer       if (operand.get().getType().isa<TensorType>()) {
22119efe141SMatthias Springer         ensureToMemrefOpIsValid(operand.get(),
22219efe141SMatthias Springer                                 newTypes[operand.getOperandNumber()]);
22319efe141SMatthias Springer         Value toMemrefOp = rewriter.create<bufferization::ToMemrefOp>(
22419efe141SMatthias Springer             operand.get().getLoc(), newTypes[operand.getOperandNumber()],
22519efe141SMatthias Springer             operand.get());
22619efe141SMatthias Springer         operand.set(toMemrefOp);
22719efe141SMatthias Springer       }
22819efe141SMatthias Springer     }
22919efe141SMatthias Springer 
23019efe141SMatthias Springer     // Update scf.yield of new else-block.
23119efe141SMatthias Springer     auto elseYieldOp = cast<scf::YieldOp>(newIfOp.elseBlock()->getTerminator());
23219efe141SMatthias Springer     rewriter.setInsertionPoint(elseYieldOp);
23319efe141SMatthias Springer     SmallVector<Value> elseYieldValues;
23419efe141SMatthias Springer     for (OpOperand &operand : elseYieldOp->getOpOperands()) {
23519efe141SMatthias Springer       if (operand.get().getType().isa<TensorType>()) {
23619efe141SMatthias Springer         ensureToMemrefOpIsValid(operand.get(),
23719efe141SMatthias Springer                                 newTypes[operand.getOperandNumber()]);
23819efe141SMatthias Springer         Value toMemrefOp = rewriter.create<bufferization::ToMemrefOp>(
23919efe141SMatthias Springer             operand.get().getLoc(), newTypes[operand.getOperandNumber()],
24019efe141SMatthias Springer             operand.get());
24119efe141SMatthias Springer         operand.set(toMemrefOp);
24219efe141SMatthias Springer       }
24319efe141SMatthias Springer     }
24419efe141SMatthias Springer 
24519efe141SMatthias Springer     // Replace op results.
24619efe141SMatthias Springer     replaceOpWithBufferizedValues(rewriter, op, newIfOp->getResults());
24719efe141SMatthias Springer 
24819efe141SMatthias Springer     return success();
24919efe141SMatthias Springer   }
25019efe141SMatthias Springer 
25119efe141SMatthias Springer   BufferRelation bufferRelation(Operation *op, OpResult opResult,
2529597b16aSMatthias Springer                                 const AnalysisState &state) const {
25319efe141SMatthias Springer     // IfOp results are equivalent to their corresponding yield values if both
25419efe141SMatthias Springer     // yield values are equivalent to each other.
25519efe141SMatthias Springer     auto bufferizableOp = cast<BufferizableOpInterface>(op);
25619efe141SMatthias Springer     SmallVector<OpOperand *> yieldValues =
25719efe141SMatthias Springer         bufferizableOp.getAliasingOpOperand(opResult, state);
25819efe141SMatthias Springer     assert(yieldValues.size() == 2 && "expected 2 yield values");
25919efe141SMatthias Springer     bool equivalentYields = state.areEquivalentBufferizedValues(
26019efe141SMatthias Springer         yieldValues[0]->get(), yieldValues[1]->get());
26119efe141SMatthias Springer     return equivalentYields ? BufferRelation::Equivalent : BufferRelation::None;
26219efe141SMatthias Springer   }
26319efe141SMatthias Springer };
26419efe141SMatthias Springer 
265417e1c7dSMatthias Springer /// Helper function for loop bufferization. Return the indices of all values
266417e1c7dSMatthias Springer /// that have a tensor type.
267417e1c7dSMatthias Springer static DenseSet<int64_t> getTensorIndices(ValueRange values) {
268417e1c7dSMatthias Springer   DenseSet<int64_t> result;
269417e1c7dSMatthias Springer   for (const auto &it : llvm::enumerate(values))
270417e1c7dSMatthias Springer     if (it.value().getType().isa<TensorType>())
271417e1c7dSMatthias Springer       result.insert(it.index());
272417e1c7dSMatthias Springer   return result;
273417e1c7dSMatthias Springer }
274417e1c7dSMatthias Springer 
275417e1c7dSMatthias Springer /// Helper function for loop bufferization. Return the indices of all
276417e1c7dSMatthias Springer /// bbArg/yielded value pairs who's buffer relation is "Equivalent".
277a5d09c63SMatthias Springer DenseSet<int64_t> getEquivalentBuffers(Block::BlockArgListType bbArgs,
278417e1c7dSMatthias Springer                                        ValueRange yieldedValues,
279417e1c7dSMatthias Springer                                        const AnalysisState &state) {
280996834e6SMatthias Springer   unsigned int minSize = std::min(bbArgs.size(), yieldedValues.size());
281417e1c7dSMatthias Springer   DenseSet<int64_t> result;
282996834e6SMatthias Springer   for (unsigned int i = 0; i < minSize; ++i) {
283996834e6SMatthias Springer     if (!bbArgs[i].getType().isa<TensorType>() ||
284996834e6SMatthias Springer         !yieldedValues[i].getType().isa<TensorType>())
285417e1c7dSMatthias Springer       continue;
286996834e6SMatthias Springer     if (state.areEquivalentBufferizedValues(bbArgs[i], yieldedValues[i]))
287996834e6SMatthias Springer       result.insert(i);
288417e1c7dSMatthias Springer   }
289417e1c7dSMatthias Springer   return result;
290417e1c7dSMatthias Springer }
291417e1c7dSMatthias Springer 
292417e1c7dSMatthias Springer /// Helper function for loop bufferization. Cast the given buffer to the given
293417e1c7dSMatthias Springer /// memref type.
294417e1c7dSMatthias Springer static Value castBuffer(OpBuilder &b, Value buffer, Type type) {
295417e1c7dSMatthias Springer   assert(type.isa<BaseMemRefType>() && "expected BaseMemRefType");
296417e1c7dSMatthias Springer   assert(buffer.getType().isa<BaseMemRefType>() && "expected BaseMemRefType");
297417e1c7dSMatthias Springer   // If the buffer already has the correct type, no cast is needed.
298417e1c7dSMatthias Springer   if (buffer.getType() == type)
299417e1c7dSMatthias Springer     return buffer;
300417e1c7dSMatthias Springer   // TODO: In case `type` has a layout map that is not the fully dynamic
301417e1c7dSMatthias Springer   // one, we may not be able to cast the buffer. In that case, the loop
302417e1c7dSMatthias Springer   // iter_arg's layout map must be changed (see uses of `castBuffer`).
303417e1c7dSMatthias Springer   assert(memref::CastOp::areCastCompatible(buffer.getType(), type) &&
304417e1c7dSMatthias Springer          "scf.while op bufferization: cast incompatible");
305417e1c7dSMatthias Springer   return b.create<memref::CastOp>(buffer.getLoc(), type, buffer).getResult();
306417e1c7dSMatthias Springer }
307417e1c7dSMatthias Springer 
308417e1c7dSMatthias Springer /// Helper function for loop bufferization. Return the bufferized values of the
309417e1c7dSMatthias Springer /// given OpOperands. If an operand is not a tensor, return the original value.
310417e1c7dSMatthias Springer static SmallVector<Value> getBuffers(RewriterBase &rewriter,
311417e1c7dSMatthias Springer                                      MutableArrayRef<OpOperand> operands,
312b55d55ecSMatthias Springer                                      const BufferizationOptions &options) {
313417e1c7dSMatthias Springer   SmallVector<Value> result;
314417e1c7dSMatthias Springer   for (OpOperand &opOperand : operands) {
315417e1c7dSMatthias Springer     if (opOperand.get().getType().isa<TensorType>()) {
316b55d55ecSMatthias Springer       Value resultBuffer = getBuffer(rewriter, opOperand.get(), options);
317b3ebe3beSMatthias Springer       result.push_back(resultBuffer);
318417e1c7dSMatthias Springer     } else {
319417e1c7dSMatthias Springer       result.push_back(opOperand.get());
320417e1c7dSMatthias Springer     }
321417e1c7dSMatthias Springer   }
322417e1c7dSMatthias Springer   return result;
323417e1c7dSMatthias Springer }
324417e1c7dSMatthias Springer 
325417e1c7dSMatthias Springer /// Helper function for loop bufferization. Compute the buffer that should be
326b3ebe3beSMatthias Springer /// yielded from a loop block (loop body or loop condition).
327417e1c7dSMatthias Springer static Value getYieldedBuffer(RewriterBase &rewriter, Value tensor,
328b55d55ecSMatthias Springer                               BaseMemRefType type,
329b55d55ecSMatthias Springer                               const BufferizationOptions &options) {
330417e1c7dSMatthias Springer   assert(tensor.getType().isa<TensorType>() && "expected tensor");
331417e1c7dSMatthias Springer   ensureToMemrefOpIsValid(tensor, type);
332b55d55ecSMatthias Springer   Value yieldedVal = getBuffer(rewriter, tensor, options);
333417e1c7dSMatthias Springer   return castBuffer(rewriter, yieldedVal, type);
334417e1c7dSMatthias Springer }
335417e1c7dSMatthias Springer 
336417e1c7dSMatthias Springer /// Helper function for loop bufferization. Given a range of values, apply
337417e1c7dSMatthias Springer /// `func` to those marked in `tensorIndices`. Otherwise, store the unmodified
338417e1c7dSMatthias Springer /// value in the result vector.
339417e1c7dSMatthias Springer static SmallVector<Value>
340417e1c7dSMatthias Springer convertTensorValues(ValueRange values, const DenseSet<int64_t> &tensorIndices,
341417e1c7dSMatthias Springer                     llvm::function_ref<Value(Value, int64_t)> func) {
342417e1c7dSMatthias Springer   SmallVector<Value> result;
343417e1c7dSMatthias Springer   for (const auto &it : llvm::enumerate(values)) {
344417e1c7dSMatthias Springer     size_t idx = it.index();
345417e1c7dSMatthias Springer     Value val = it.value();
346417e1c7dSMatthias Springer     result.push_back(tensorIndices.contains(idx) ? func(val, idx) : val);
347417e1c7dSMatthias Springer   }
348417e1c7dSMatthias Springer   return result;
349417e1c7dSMatthias Springer }
350417e1c7dSMatthias Springer 
351417e1c7dSMatthias Springer /// Helper function for loop bufferization. Given a list of pre-bufferization
352417e1c7dSMatthias Springer /// yielded values, compute the list of bufferized yielded values.
353417e1c7dSMatthias Springer SmallVector<Value> getYieldedValues(RewriterBase &rewriter, ValueRange values,
354417e1c7dSMatthias Springer                                     TypeRange bufferizedTypes,
355417e1c7dSMatthias Springer                                     const DenseSet<int64_t> &tensorIndices,
356b55d55ecSMatthias Springer                                     const BufferizationOptions &options) {
357417e1c7dSMatthias Springer   return convertTensorValues(
358417e1c7dSMatthias Springer       values, tensorIndices, [&](Value val, int64_t index) {
359417e1c7dSMatthias Springer         return getYieldedBuffer(rewriter, val,
360417e1c7dSMatthias Springer                                 bufferizedTypes[index].cast<BaseMemRefType>(),
361b55d55ecSMatthias Springer                                 options);
362417e1c7dSMatthias Springer       });
363417e1c7dSMatthias Springer }
364417e1c7dSMatthias Springer 
365a5d09c63SMatthias Springer /// Helper function for loop bufferization. Given a list of bbArgs of the new
366a5d09c63SMatthias Springer /// (bufferized) loop op, wrap the bufferized tensor args (now memrefs) into
367a5d09c63SMatthias Springer /// ToTensorOps, so that the block body can be moved over to the new op.
368a5d09c63SMatthias Springer SmallVector<Value>
369a5d09c63SMatthias Springer getBbArgReplacements(RewriterBase &rewriter, Block::BlockArgListType bbArgs,
370a5d09c63SMatthias Springer                      const DenseSet<int64_t> &tensorIndices) {
371a5d09c63SMatthias Springer   return convertTensorValues(
372a5d09c63SMatthias Springer       bbArgs, tensorIndices, [&](Value val, int64_t index) {
373a5d09c63SMatthias Springer         return rewriter.create<bufferization::ToTensorOp>(val.getLoc(), val);
374a5d09c63SMatthias Springer       });
375a5d09c63SMatthias Springer }
376a5d09c63SMatthias Springer 
37719efe141SMatthias Springer /// Bufferization of scf.for. Replace with a new scf.for that operates on
37819efe141SMatthias Springer /// memrefs.
37919efe141SMatthias Springer struct ForOpInterface
38019efe141SMatthias Springer     : public BufferizableOpInterface::ExternalModel<ForOpInterface,
38119efe141SMatthias Springer                                                     scf::ForOp> {
38219efe141SMatthias Springer   bool bufferizesToMemoryRead(Operation *op, OpOperand &opOperand,
3839597b16aSMatthias Springer                               const AnalysisState &state) const {
38419efe141SMatthias Springer     // scf::ForOp alone doesn't bufferize to a memory read, one of the uses of
38519efe141SMatthias Springer     // its matching bbArg may.
38619efe141SMatthias Springer     auto forOp = cast<scf::ForOp>(op);
38719efe141SMatthias Springer     return state.isValueRead(forOp.getRegionIterArgForOpOperand(opOperand));
38819efe141SMatthias Springer   }
38919efe141SMatthias Springer 
39019efe141SMatthias Springer   bool bufferizesToMemoryWrite(Operation *op, OpOperand &opOperand,
3919597b16aSMatthias Springer                                const AnalysisState &state) const {
3921e1eeae8SMatthias Springer     // Tensor iter_args of scf::ForOps are always considered as a write.
39319efe141SMatthias Springer     return true;
39419efe141SMatthias Springer   }
39519efe141SMatthias Springer 
3969597b16aSMatthias Springer   SmallVector<OpResult> getAliasingOpResult(Operation *op, OpOperand &opOperand,
3979597b16aSMatthias Springer                                             const AnalysisState &state) const {
39819efe141SMatthias Springer     auto forOp = cast<scf::ForOp>(op);
399585a8a32SMatthias Springer     return {forOp.getResultForOpOperand(opOperand)};
40019efe141SMatthias Springer   }
40119efe141SMatthias Springer 
40219efe141SMatthias Springer   BufferRelation bufferRelation(Operation *op, OpResult opResult,
4039597b16aSMatthias Springer                                 const AnalysisState &state) const {
40419efe141SMatthias Springer     // ForOp results are equivalent to their corresponding init_args if the
40519efe141SMatthias Springer     // corresponding iter_args and yield values are equivalent.
40619efe141SMatthias Springer     auto forOp = cast<scf::ForOp>(op);
40719efe141SMatthias Springer     OpOperand &forOperand = forOp.getOpOperandForResult(opResult);
40819efe141SMatthias Springer     auto bbArg = forOp.getRegionIterArgForOpOperand(forOperand);
4091e1eeae8SMatthias Springer     auto yieldOp =
4101e1eeae8SMatthias Springer         cast<scf::YieldOp>(forOp.getLoopBody().front().getTerminator());
41119efe141SMatthias Springer     bool equivalentYield = state.areEquivalentBufferizedValues(
41219efe141SMatthias Springer         bbArg, yieldOp->getOperand(opResult.getResultNumber()));
41319efe141SMatthias Springer     return equivalentYield ? BufferRelation::Equivalent : BufferRelation::None;
41419efe141SMatthias Springer   }
41519efe141SMatthias Springer 
41619efe141SMatthias Springer   bool isWritable(Operation *op, Value value,
4179597b16aSMatthias Springer                   const AnalysisState &state) const {
41819efe141SMatthias Springer     // Interestingly, scf::ForOp's bbArg can **always** be viewed
41919efe141SMatthias Springer     // inplace from the perspective of ops nested under:
42019efe141SMatthias Springer     //   1. Either the matching iter operand is not bufferized inplace and an
42119efe141SMatthias Springer     //      alloc + optional copy makes the bbArg itself inplaceable.
42219efe141SMatthias Springer     //   2. Or the matching iter operand is bufferized inplace and bbArg just
42319efe141SMatthias Springer     //      bufferizes to that too.
42419efe141SMatthias Springer     return true;
42519efe141SMatthias Springer   }
42619efe141SMatthias Springer 
427d361ecbdSMatthias Springer   LogicalResult resolveConflicts(Operation *op, RewriterBase &rewriter,
428d361ecbdSMatthias Springer                                  const AnalysisState &state) const {
429d361ecbdSMatthias Springer     auto bufferizableOp = cast<BufferizableOpInterface>(op);
430d361ecbdSMatthias Springer     if (failed(bufferizableOp.resolveTensorOpOperandConflicts(rewriter, state)))
431d361ecbdSMatthias Springer       return failure();
432d361ecbdSMatthias Springer 
433d361ecbdSMatthias Springer     if (!state.getOptions().enforceAliasingInvariants)
434d361ecbdSMatthias Springer       return success();
435d361ecbdSMatthias Springer 
436d361ecbdSMatthias Springer     // According to the `getAliasing...` implementations, a bufferized OpResult
437d361ecbdSMatthias Springer     // may alias only with the corresponding bufferized init_arg and with no
438d361ecbdSMatthias Springer     // other buffers. I.e., the i-th OpResult may alias with the i-th init_arg;
439d361ecbdSMatthias Springer     // but not with any other OpOperand. If a corresponding OpResult/init_arg
440d361ecbdSMatthias Springer     // pair bufferizes to equivalent buffers, this aliasing requirement is
441d361ecbdSMatthias Springer     // satisfied. Otherwise, we cannot be sure and must yield a new buffer copy.
442d361ecbdSMatthias Springer     // (New buffer copies do not alias with any buffer.)
443d361ecbdSMatthias Springer     auto forOp = cast<scf::ForOp>(op);
444d361ecbdSMatthias Springer     auto yieldOp =
445d361ecbdSMatthias Springer         cast<scf::YieldOp>(forOp.getLoopBody().front().getTerminator());
446d361ecbdSMatthias Springer     OpBuilder::InsertionGuard g(rewriter);
447d361ecbdSMatthias Springer     rewriter.setInsertionPoint(yieldOp);
448d361ecbdSMatthias Springer 
449d361ecbdSMatthias Springer     // Indices of all iter_args that have tensor type. These are the ones that
450d361ecbdSMatthias Springer     // are bufferized.
451d361ecbdSMatthias Springer     DenseSet<int64_t> indices = getTensorIndices(forOp.getInitArgs());
452d361ecbdSMatthias Springer     // For every yielded value, is the value equivalent to its corresponding
453d361ecbdSMatthias Springer     // bbArg?
454d361ecbdSMatthias Springer     DenseSet<int64_t> equivalentYields = getEquivalentBuffers(
455d361ecbdSMatthias Springer         forOp.getRegionIterArgs(), yieldOp.getResults(), state);
456d361ecbdSMatthias Springer     SmallVector<Value> yieldValues;
457d361ecbdSMatthias Springer     for (int64_t idx = 0;
458d361ecbdSMatthias Springer          idx < static_cast<int64_t>(yieldOp.getResults().size()); ++idx) {
459d361ecbdSMatthias Springer       Value value = yieldOp.getResults()[idx];
460d361ecbdSMatthias Springer       if (!indices.contains(idx) || equivalentYields.contains(idx)) {
461d361ecbdSMatthias Springer         yieldValues.push_back(value);
462d361ecbdSMatthias Springer         continue;
463d361ecbdSMatthias Springer       }
464d361ecbdSMatthias Springer       Value alloc = rewriter.create<bufferization::AllocTensorOp>(
465d361ecbdSMatthias Springer           yieldOp.getLoc(), value.getType().cast<RankedTensorType>(),
466d361ecbdSMatthias Springer           /*dynamicSizes=*/ValueRange(), value, /*escape=*/true);
467d361ecbdSMatthias Springer       yieldValues.push_back(alloc);
468d361ecbdSMatthias Springer     }
469d361ecbdSMatthias Springer 
470d361ecbdSMatthias Springer     rewriter.updateRootInPlace(
471d361ecbdSMatthias Springer         yieldOp, [&]() { yieldOp.getResultsMutable().assign(yieldValues); });
472d361ecbdSMatthias Springer     return success();
473d361ecbdSMatthias Springer   }
474d361ecbdSMatthias Springer 
47519efe141SMatthias Springer   LogicalResult bufferize(Operation *op, RewriterBase &rewriter,
476b55d55ecSMatthias Springer                           const BufferizationOptions &options) const {
47719efe141SMatthias Springer     auto forOp = cast<scf::ForOp>(op);
47819efe141SMatthias Springer     Block *oldLoopBody = &forOp.getLoopBody().front();
47919efe141SMatthias Springer 
48019efe141SMatthias Springer     // Indices of all iter_args that have tensor type. These are the ones that
48119efe141SMatthias Springer     // are bufferized.
482417e1c7dSMatthias Springer     DenseSet<int64_t> indices = getTensorIndices(forOp.getInitArgs());
48319efe141SMatthias Springer 
484417e1c7dSMatthias Springer     // The new memref init_args of the loop.
485417e1c7dSMatthias Springer     SmallVector<Value> initArgs =
486b55d55ecSMatthias Springer         getBuffers(rewriter, forOp.getIterOpOperands(), options);
48719efe141SMatthias Springer 
48819efe141SMatthias Springer     // Construct a new scf.for op with memref instead of tensor values.
48919efe141SMatthias Springer     auto newForOp = rewriter.create<scf::ForOp>(
49019efe141SMatthias Springer         forOp.getLoc(), forOp.getLowerBound(), forOp.getUpperBound(),
49119efe141SMatthias Springer         forOp.getStep(), initArgs);
492413fbb04SLei Zhang     newForOp->setAttrs(forOp->getAttrs());
493417e1c7dSMatthias Springer     ValueRange initArgsRange(initArgs);
494417e1c7dSMatthias Springer     TypeRange initArgsTypes(initArgsRange);
49519efe141SMatthias Springer     Block *loopBody = &newForOp.getLoopBody().front();
49619efe141SMatthias Springer 
49719efe141SMatthias Springer     // Set up new iter_args. The loop body uses tensors, so wrap the (memref)
49819efe141SMatthias Springer     // iter_args of the new loop in ToTensorOps.
49919efe141SMatthias Springer     rewriter.setInsertionPointToStart(loopBody);
500a5d09c63SMatthias Springer     SmallVector<Value> iterArgs =
501a5d09c63SMatthias Springer         getBbArgReplacements(rewriter, newForOp.getRegionIterArgs(), indices);
50219efe141SMatthias Springer     iterArgs.insert(iterArgs.begin(), newForOp.getInductionVar());
50319efe141SMatthias Springer 
50419efe141SMatthias Springer     // Erase terminator if present.
50519efe141SMatthias Springer     if (iterArgs.size() == 1)
50619efe141SMatthias Springer       rewriter.eraseOp(loopBody->getTerminator());
50719efe141SMatthias Springer 
50819efe141SMatthias Springer     // Move loop body to new loop.
50919efe141SMatthias Springer     rewriter.mergeBlocks(oldLoopBody, loopBody, iterArgs);
51019efe141SMatthias Springer 
51119efe141SMatthias Springer     // Update scf.yield of new loop.
51219efe141SMatthias Springer     auto yieldOp = cast<scf::YieldOp>(loopBody->getTerminator());
51319efe141SMatthias Springer     rewriter.setInsertionPoint(yieldOp);
514b3ebe3beSMatthias Springer     SmallVector<Value> yieldValues = getYieldedValues(
515b55d55ecSMatthias Springer         rewriter, yieldOp.getResults(), initArgsTypes, indices, options);
51619efe141SMatthias Springer     yieldOp.getResultsMutable().assign(yieldValues);
51719efe141SMatthias Springer 
51819efe141SMatthias Springer     // Replace loop results.
51919efe141SMatthias Springer     replaceOpWithBufferizedValues(rewriter, op, newForOp->getResults());
52019efe141SMatthias Springer 
52119efe141SMatthias Springer     return success();
52219efe141SMatthias Springer   }
5234ec00fb3SMatthias Springer 
5241e1eeae8SMatthias Springer   /// Assert that yielded values of an scf.for op are equivalent to their
525f178c386SMatthias Springer   /// corresponding bbArgs. In that case, the buffer relations of the
526f178c386SMatthias Springer   /// corresponding OpResults are "Equivalent".
527f178c386SMatthias Springer   ///
528f178c386SMatthias Springer   /// If this is not the case, an allocs+copies are inserted and yielded from
529f178c386SMatthias Springer   /// the loop. This could be a performance problem, so it must be explicitly
530f178c386SMatthias Springer   /// activated with `alloc-return-allocs`.
5314ec00fb3SMatthias Springer   LogicalResult verifyAnalysis(Operation *op,
5329597b16aSMatthias Springer                                const AnalysisState &state) const {
5331e1eeae8SMatthias Springer     const auto &options =
5341e1eeae8SMatthias Springer         static_cast<const OneShotBufferizationOptions &>(state.getOptions());
5351e1eeae8SMatthias Springer     if (options.allowReturnAllocs)
5361e1eeae8SMatthias Springer       return success();
5371e1eeae8SMatthias Springer 
5384ec00fb3SMatthias Springer     auto forOp = cast<scf::ForOp>(op);
5394ec00fb3SMatthias Springer     auto yieldOp =
5404ec00fb3SMatthias Springer         cast<scf::YieldOp>(forOp.getLoopBody().front().getTerminator());
541f178c386SMatthias Springer     for (OpResult opResult : op->getOpResults()) {
542f178c386SMatthias Springer       if (!opResult.getType().isa<TensorType>())
5434ec00fb3SMatthias Springer         continue;
5444ec00fb3SMatthias Springer 
5454ec00fb3SMatthias Springer       // Note: This is overly strict. We should check for aliasing bufferized
5464ec00fb3SMatthias Springer       // values. But we don't have a "must-alias" analysis yet.
547f178c386SMatthias Springer       if (bufferRelation(op, opResult, state) != BufferRelation::Equivalent)
5484ec00fb3SMatthias Springer         return yieldOp->emitError()
549f178c386SMatthias Springer                << "Yield operand #" << opResult.getResultNumber()
550e3006825SMatthias Springer                << " is not equivalent to the corresponding iter bbArg";
5514ec00fb3SMatthias Springer     }
552f178c386SMatthias Springer 
5534ec00fb3SMatthias Springer     return success();
5544ec00fb3SMatthias Springer   }
55519efe141SMatthias Springer };
55619efe141SMatthias Springer 
557a5d09c63SMatthias Springer /// Bufferization of scf.while. Replace with a new scf.while that operates on
558a5d09c63SMatthias Springer /// memrefs.
559a5d09c63SMatthias Springer struct WhileOpInterface
560a5d09c63SMatthias Springer     : public BufferizableOpInterface::ExternalModel<WhileOpInterface,
561a5d09c63SMatthias Springer                                                     scf::WhileOp> {
562a5d09c63SMatthias Springer   bool bufferizesToMemoryRead(Operation *op, OpOperand &opOperand,
563a5d09c63SMatthias Springer                               const AnalysisState &state) const {
564a5d09c63SMatthias Springer     // Tensor iter_args of scf::WhileOps are always considered as a read.
565a5d09c63SMatthias Springer     return true;
566a5d09c63SMatthias Springer   }
567a5d09c63SMatthias Springer 
568a5d09c63SMatthias Springer   bool bufferizesToMemoryWrite(Operation *op, OpOperand &opOperand,
569a5d09c63SMatthias Springer                                const AnalysisState &state) const {
570a5d09c63SMatthias Springer     // Tensor iter_args of scf::WhileOps are always considered as a write.
571a5d09c63SMatthias Springer     return true;
572a5d09c63SMatthias Springer   }
573a5d09c63SMatthias Springer 
574a5d09c63SMatthias Springer   SmallVector<OpResult> getAliasingOpResult(Operation *op, OpOperand &opOperand,
575a5d09c63SMatthias Springer                                             const AnalysisState &state) const {
576a5d09c63SMatthias Springer     auto whileOp = cast<scf::WhileOp>(op);
577996834e6SMatthias Springer     unsigned int idx = opOperand.getOperandNumber();
578996834e6SMatthias Springer 
579996834e6SMatthias Springer     // The OpResults and OpOperands may not match. They may not even have the
580996834e6SMatthias Springer     // same type. The number of OpResults and OpOperands can also differ.
581996834e6SMatthias Springer     if (idx >= op->getNumResults() ||
582996834e6SMatthias Springer         opOperand.get().getType() != op->getResult(idx).getType())
583996834e6SMatthias Springer       return {};
584996834e6SMatthias Springer 
585996834e6SMatthias Springer     // The only aliasing OpResult may be the one at the same index.
586996834e6SMatthias Springer     return {whileOp->getResult(idx)};
587a5d09c63SMatthias Springer   }
588a5d09c63SMatthias Springer 
589a5d09c63SMatthias Springer   BufferRelation bufferRelation(Operation *op, OpResult opResult,
590a5d09c63SMatthias Springer                                 const AnalysisState &state) const {
591a5d09c63SMatthias Springer     // WhileOp results are equivalent to their corresponding init_args if the
592a5d09c63SMatthias Springer     // corresponding iter_args and yield values are equivalent (for both the
593a5d09c63SMatthias Springer     // "before" and the "after" block).
594a5d09c63SMatthias Springer     unsigned int resultNumber = opResult.getResultNumber();
595a5d09c63SMatthias Springer     auto whileOp = cast<scf::WhileOp>(op);
596a5d09c63SMatthias Springer 
597996834e6SMatthias Springer     // The "before" region bbArgs and the OpResults may not match.
598996834e6SMatthias Springer     if (resultNumber >= whileOp.getBeforeArguments().size())
599996834e6SMatthias Springer       return BufferRelation::None;
600996834e6SMatthias Springer     if (opResult.getType() !=
601996834e6SMatthias Springer         whileOp.getBeforeArguments()[resultNumber].getType())
602996834e6SMatthias Springer       return BufferRelation::None;
603996834e6SMatthias Springer 
604a5d09c63SMatthias Springer     auto conditionOp = whileOp.getConditionOp();
605a5d09c63SMatthias Springer     BlockArgument conditionBbArg = whileOp.getBeforeArguments()[resultNumber];
606a5d09c63SMatthias Springer     Value conditionOperand = conditionOp.getArgs()[resultNumber];
607a5d09c63SMatthias Springer     bool equivCondition =
608a5d09c63SMatthias Springer         state.areEquivalentBufferizedValues(conditionBbArg, conditionOperand);
609a5d09c63SMatthias Springer 
610a5d09c63SMatthias Springer     auto yieldOp = whileOp.getYieldOp();
611a5d09c63SMatthias Springer     BlockArgument bodyBbArg = whileOp.getAfterArguments()[resultNumber];
612a5d09c63SMatthias Springer     Value yieldOperand = yieldOp.getOperand(resultNumber);
613a5d09c63SMatthias Springer     bool equivYield =
614a5d09c63SMatthias Springer         state.areEquivalentBufferizedValues(bodyBbArg, yieldOperand);
615a5d09c63SMatthias Springer 
616a5d09c63SMatthias Springer     return equivCondition && equivYield ? BufferRelation::Equivalent
617a5d09c63SMatthias Springer                                         : BufferRelation::None;
618a5d09c63SMatthias Springer   }
619a5d09c63SMatthias Springer 
620a5d09c63SMatthias Springer   bool isWritable(Operation *op, Value value,
621a5d09c63SMatthias Springer                   const AnalysisState &state) const {
622a5d09c63SMatthias Springer     // Interestingly, scf::WhileOp's bbArg can **always** be viewed
623a5d09c63SMatthias Springer     // inplace from the perspective of ops nested under:
624a5d09c63SMatthias Springer     //   1. Either the matching iter operand is not bufferized inplace and an
625a5d09c63SMatthias Springer     //      alloc + optional copy makes the bbArg itself inplaceable.
626a5d09c63SMatthias Springer     //   2. Or the matching iter operand is bufferized inplace and bbArg just
627a5d09c63SMatthias Springer     //      bufferizes to that too.
628a5d09c63SMatthias Springer     return true;
629a5d09c63SMatthias Springer   }
630a5d09c63SMatthias Springer 
631d361ecbdSMatthias Springer   LogicalResult resolveConflicts(Operation *op, RewriterBase &rewriter,
632d361ecbdSMatthias Springer                                  const AnalysisState &state) const {
633d361ecbdSMatthias Springer     auto bufferizableOp = cast<BufferizableOpInterface>(op);
634d361ecbdSMatthias Springer     if (failed(bufferizableOp.resolveTensorOpOperandConflicts(rewriter, state)))
635d361ecbdSMatthias Springer       return failure();
636d361ecbdSMatthias Springer 
637d361ecbdSMatthias Springer     if (!state.getOptions().enforceAliasingInvariants)
638d361ecbdSMatthias Springer       return success();
639d361ecbdSMatthias Springer 
640d361ecbdSMatthias Springer     // According to the `getAliasing...` implementations, a bufferized OpResult
641d361ecbdSMatthias Springer     // may alias only with the corresponding bufferized init_arg and with no
642d361ecbdSMatthias Springer     // other buffers. I.e., the i-th OpResult may alias with the i-th init_arg;
643d361ecbdSMatthias Springer     // but not with any other OpOperand. If a corresponding OpResult/init_arg
644d361ecbdSMatthias Springer     // pair bufferizes to equivalent buffers, this aliasing requirement is
645d361ecbdSMatthias Springer     // satisfied. Otherwise, we cannot be sure and must yield a new buffer copy.
646d361ecbdSMatthias Springer     // (New buffer copies do not alias with any buffer.)
647d361ecbdSMatthias Springer     OpBuilder::InsertionGuard g(rewriter);
648d361ecbdSMatthias Springer     auto whileOp = cast<scf::WhileOp>(op);
649d361ecbdSMatthias Springer     auto conditionOp = whileOp.getConditionOp();
650d361ecbdSMatthias Springer     auto yieldOp = whileOp.getYieldOp();
651d361ecbdSMatthias Springer 
652d361ecbdSMatthias Springer     // Indices of all bbArgs that have tensor type. These are the ones that
653d361ecbdSMatthias Springer     // are bufferized. The "before" and "after" regions may have different args.
654d361ecbdSMatthias Springer     DenseSet<int64_t> indicesBefore = getTensorIndices(whileOp.getInits());
655d361ecbdSMatthias Springer     DenseSet<int64_t> indicesAfter =
656d361ecbdSMatthias Springer         getTensorIndices(whileOp.getAfterArguments());
657d361ecbdSMatthias Springer 
658d361ecbdSMatthias Springer     // For every yielded value, is the value equivalent to its corresponding
659d361ecbdSMatthias Springer     // bbArg?
660d361ecbdSMatthias Springer     DenseSet<int64_t> equivalentYieldsBefore = getEquivalentBuffers(
661d361ecbdSMatthias Springer         whileOp.getBeforeArguments(), conditionOp.getArgs(), state);
662d361ecbdSMatthias Springer     DenseSet<int64_t> equivalentYieldsAfter = getEquivalentBuffers(
663d361ecbdSMatthias Springer         whileOp.getAfterArguments(), whileOp.getYieldOp().getResults(), state);
664d361ecbdSMatthias Springer 
665d361ecbdSMatthias Springer     // Update "before" region.
666d361ecbdSMatthias Springer     rewriter.setInsertionPoint(conditionOp);
667d361ecbdSMatthias Springer     SmallVector<Value> beforeYieldValues;
668d361ecbdSMatthias Springer     for (int64_t idx = 0;
669d361ecbdSMatthias Springer          idx < static_cast<int64_t>(conditionOp.getArgs().size()); ++idx) {
670d361ecbdSMatthias Springer       Value value = conditionOp.getArgs()[idx];
671d361ecbdSMatthias Springer       if (!indicesBefore.contains(idx) ||
672d361ecbdSMatthias Springer           equivalentYieldsBefore.contains(idx)) {
673d361ecbdSMatthias Springer         beforeYieldValues.push_back(value);
674d361ecbdSMatthias Springer         continue;
675d361ecbdSMatthias Springer       }
676d361ecbdSMatthias Springer       Value alloc = rewriter.create<bufferization::AllocTensorOp>(
677d361ecbdSMatthias Springer           conditionOp.getLoc(), value.getType().cast<RankedTensorType>(),
678d361ecbdSMatthias Springer           /*dynamicSizes=*/ValueRange(), value, /*escape=*/true);
679d361ecbdSMatthias Springer       beforeYieldValues.push_back(alloc);
680d361ecbdSMatthias Springer     }
681d361ecbdSMatthias Springer     rewriter.updateRootInPlace(conditionOp, [&]() {
682d361ecbdSMatthias Springer       conditionOp.getArgsMutable().assign(beforeYieldValues);
683d361ecbdSMatthias Springer     });
684d361ecbdSMatthias Springer 
685d361ecbdSMatthias Springer     // Update "after" region.
686d361ecbdSMatthias Springer     rewriter.setInsertionPoint(yieldOp);
687d361ecbdSMatthias Springer     SmallVector<Value> afterYieldValues;
688d361ecbdSMatthias Springer     for (int64_t idx = 0;
689d361ecbdSMatthias Springer          idx < static_cast<int64_t>(yieldOp.getResults().size()); ++idx) {
690d361ecbdSMatthias Springer       Value value = yieldOp.getResults()[idx];
691d361ecbdSMatthias Springer       if (!indicesAfter.contains(idx) || equivalentYieldsAfter.contains(idx)) {
692d361ecbdSMatthias Springer         afterYieldValues.push_back(value);
693d361ecbdSMatthias Springer         continue;
694d361ecbdSMatthias Springer       }
695d361ecbdSMatthias Springer       Value alloc = rewriter.create<bufferization::AllocTensorOp>(
696d361ecbdSMatthias Springer           yieldOp.getLoc(), value.getType().cast<RankedTensorType>(),
697d361ecbdSMatthias Springer           /*dynamicSizes=*/ValueRange(), value, /*escape=*/true);
698d361ecbdSMatthias Springer       afterYieldValues.push_back(alloc);
699d361ecbdSMatthias Springer     }
700d361ecbdSMatthias Springer     rewriter.updateRootInPlace(yieldOp, [&]() {
701d361ecbdSMatthias Springer       yieldOp.getResultsMutable().assign(afterYieldValues);
702d361ecbdSMatthias Springer     });
703d361ecbdSMatthias Springer 
704d361ecbdSMatthias Springer     return success();
705d361ecbdSMatthias Springer   }
706d361ecbdSMatthias Springer 
707a5d09c63SMatthias Springer   LogicalResult bufferize(Operation *op, RewriterBase &rewriter,
708b55d55ecSMatthias Springer                           const BufferizationOptions &options) const {
709a5d09c63SMatthias Springer     auto whileOp = cast<scf::WhileOp>(op);
710a5d09c63SMatthias Springer 
711a5d09c63SMatthias Springer     assert(whileOp.getBefore().getBlocks().size() == 1 &&
712a5d09c63SMatthias Springer            "regions with multiple blocks not supported");
713a5d09c63SMatthias Springer     Block *beforeBody = &whileOp.getBefore().front();
714a5d09c63SMatthias Springer     assert(whileOp.getAfter().getBlocks().size() == 1 &&
715a5d09c63SMatthias Springer            "regions with multiple blocks not supported");
716a5d09c63SMatthias Springer     Block *afterBody = &whileOp.getAfter().front();
717a5d09c63SMatthias Springer 
718996834e6SMatthias Springer     // Indices of all bbArgs that have tensor type. These are the ones that
719996834e6SMatthias Springer     // are bufferized. The "before" and "after" regions may have different args.
720996834e6SMatthias Springer     DenseSet<int64_t> indicesBefore = getTensorIndices(whileOp.getInits());
721996834e6SMatthias Springer     DenseSet<int64_t> indicesAfter =
722996834e6SMatthias Springer         getTensorIndices(whileOp.getAfterArguments());
723996834e6SMatthias Springer 
724a5d09c63SMatthias Springer     // The new memref init_args of the loop.
725a5d09c63SMatthias Springer     SmallVector<Value> initArgs =
726b55d55ecSMatthias Springer         getBuffers(rewriter, whileOp->getOpOperands(), options);
727996834e6SMatthias Springer 
728996834e6SMatthias Springer     // The result types of a WhileOp are the same as the "after" bbArg types.
729996834e6SMatthias Springer     SmallVector<Type> argsTypesAfter = llvm::to_vector(
730996834e6SMatthias Springer         llvm::map_range(whileOp.getAfterArguments(), [&](BlockArgument bbArg) {
731b55d55ecSMatthias Springer           return getBufferType(bbArg, options).cast<Type>();
732996834e6SMatthias Springer         }));
733a5d09c63SMatthias Springer 
734a5d09c63SMatthias Springer     // Construct a new scf.while op with memref instead of tensor values.
735996834e6SMatthias Springer     ValueRange argsRangeBefore(initArgs);
736996834e6SMatthias Springer     TypeRange argsTypesBefore(argsRangeBefore);
737996834e6SMatthias Springer     auto newWhileOp = rewriter.create<scf::WhileOp>(whileOp.getLoc(),
738996834e6SMatthias Springer                                                     argsTypesAfter, initArgs);
739996834e6SMatthias Springer 
740a5d09c63SMatthias Springer     // Add before/after regions to the new op.
741996834e6SMatthias Springer     SmallVector<Location> bbArgLocsBefore(initArgs.size(), whileOp.getLoc());
742996834e6SMatthias Springer     SmallVector<Location> bbArgLocsAfter(argsTypesAfter.size(),
743996834e6SMatthias Springer                                          whileOp.getLoc());
744a5d09c63SMatthias Springer     Block *newBeforeBody = &newWhileOp.getBefore().emplaceBlock();
745996834e6SMatthias Springer     newWhileOp.getBefore().addArguments(argsTypesBefore, bbArgLocsBefore);
746a5d09c63SMatthias Springer     Block *newAfterBody = &newWhileOp.getAfter().emplaceBlock();
747996834e6SMatthias Springer     newWhileOp.getAfter().addArguments(argsTypesAfter, bbArgLocsAfter);
748a5d09c63SMatthias Springer 
749a5d09c63SMatthias Springer     // Set up new iter_args and move the loop condition block to the new op.
750a5d09c63SMatthias Springer     // The old block uses tensors, so wrap the (memref) bbArgs of the new block
751a5d09c63SMatthias Springer     // in ToTensorOps.
752a5d09c63SMatthias Springer     rewriter.setInsertionPointToStart(newBeforeBody);
753a5d09c63SMatthias Springer     SmallVector<Value> newBeforeArgs = getBbArgReplacements(
754996834e6SMatthias Springer         rewriter, newWhileOp.getBeforeArguments(), indicesBefore);
755a5d09c63SMatthias Springer     rewriter.mergeBlocks(beforeBody, newBeforeBody, newBeforeArgs);
756a5d09c63SMatthias Springer 
757a5d09c63SMatthias Springer     // Update scf.condition of new loop.
758a5d09c63SMatthias Springer     auto newConditionOp = newWhileOp.getConditionOp();
759a5d09c63SMatthias Springer     rewriter.setInsertionPoint(newConditionOp);
760996834e6SMatthias Springer     // Only equivalent buffers or new buffer allocations may be yielded to the
761996834e6SMatthias Springer     // "after" region.
762996834e6SMatthias Springer     // TODO: This could be relaxed for better bufferization results.
763a5d09c63SMatthias Springer     SmallVector<Value> newConditionArgs =
764996834e6SMatthias Springer         getYieldedValues(rewriter, newConditionOp.getArgs(), argsTypesAfter,
765b55d55ecSMatthias Springer                          indicesAfter, options);
766a5d09c63SMatthias Springer     newConditionOp.getArgsMutable().assign(newConditionArgs);
767a5d09c63SMatthias Springer 
768a5d09c63SMatthias Springer     // Set up new iter_args and move the loop body block to the new op.
769a5d09c63SMatthias Springer     // The old block uses tensors, so wrap the (memref) bbArgs of the new block
770a5d09c63SMatthias Springer     // in ToTensorOps.
771a5d09c63SMatthias Springer     rewriter.setInsertionPointToStart(newAfterBody);
772996834e6SMatthias Springer     SmallVector<Value> newAfterArgs = getBbArgReplacements(
773996834e6SMatthias Springer         rewriter, newWhileOp.getAfterArguments(), indicesAfter);
774a5d09c63SMatthias Springer     rewriter.mergeBlocks(afterBody, newAfterBody, newAfterArgs);
775a5d09c63SMatthias Springer 
776a5d09c63SMatthias Springer     // Update scf.yield of the new loop.
777a5d09c63SMatthias Springer     auto newYieldOp = newWhileOp.getYieldOp();
778a5d09c63SMatthias Springer     rewriter.setInsertionPoint(newYieldOp);
779996834e6SMatthias Springer     // Only equivalent buffers or new buffer allocations may be yielded to the
780996834e6SMatthias Springer     // "before" region.
781996834e6SMatthias Springer     // TODO: This could be relaxed for better bufferization results.
782a5d09c63SMatthias Springer     SmallVector<Value> newYieldValues =
783996834e6SMatthias Springer         getYieldedValues(rewriter, newYieldOp.getResults(), argsTypesBefore,
784b55d55ecSMatthias Springer                          indicesBefore, options);
785a5d09c63SMatthias Springer     newYieldOp.getResultsMutable().assign(newYieldValues);
786a5d09c63SMatthias Springer 
787a5d09c63SMatthias Springer     // Replace loop results.
788a5d09c63SMatthias Springer     replaceOpWithBufferizedValues(rewriter, op, newWhileOp->getResults());
789a5d09c63SMatthias Springer 
790a5d09c63SMatthias Springer     return success();
791a5d09c63SMatthias Springer   }
792a5d09c63SMatthias Springer 
793a5d09c63SMatthias Springer   /// Assert that yielded values of an scf.while op are equivalent to their
794a5d09c63SMatthias Springer   /// corresponding bbArgs. In that case, the buffer relations of the
795a5d09c63SMatthias Springer   /// corresponding OpResults are "Equivalent".
796a5d09c63SMatthias Springer   ///
797a5d09c63SMatthias Springer   /// If this is not the case, allocs+copies are inserted and yielded from
798a5d09c63SMatthias Springer   /// the loop. This could be a performance problem, so it must be explicitly
799a5d09c63SMatthias Springer   /// activated with `alloc-return-allocs`.
800a5d09c63SMatthias Springer   ///
801a5d09c63SMatthias Springer   /// Not: In contrast to scf::ForOp, scf::WhileOp has two regions and the
802a5d09c63SMatthias Springer   /// equivalence condition must be checked for both.
803a5d09c63SMatthias Springer   LogicalResult verifyAnalysis(Operation *op,
804a5d09c63SMatthias Springer                                const AnalysisState &state) const {
805a5d09c63SMatthias Springer     auto whileOp = cast<scf::WhileOp>(op);
806a5d09c63SMatthias Springer     const auto &options =
807a5d09c63SMatthias Springer         static_cast<const OneShotBufferizationOptions &>(state.getOptions());
808a5d09c63SMatthias Springer     if (options.allowReturnAllocs)
809a5d09c63SMatthias Springer       return success();
810a5d09c63SMatthias Springer 
811a5d09c63SMatthias Springer     auto conditionOp = whileOp.getConditionOp();
812a5d09c63SMatthias Springer     for (const auto &it : llvm::enumerate(conditionOp.getArgs())) {
813a5d09c63SMatthias Springer       if (!it.value().getType().isa<TensorType>())
814a5d09c63SMatthias Springer         continue;
815a5d09c63SMatthias Springer       if (!state.areEquivalentBufferizedValues(
816a5d09c63SMatthias Springer               it.value(), conditionOp->getBlock()->getArgument(it.index())))
817a5d09c63SMatthias Springer         return conditionOp->emitError()
818a5d09c63SMatthias Springer                << "Condition arg #" << it.index()
819a5d09c63SMatthias Springer                << " is not equivalent to the corresponding iter bbArg";
820a5d09c63SMatthias Springer     }
821a5d09c63SMatthias Springer 
822a5d09c63SMatthias Springer     auto yieldOp = whileOp.getYieldOp();
823a5d09c63SMatthias Springer     for (const auto &it : llvm::enumerate(yieldOp.getResults())) {
824a5d09c63SMatthias Springer       if (!it.value().getType().isa<TensorType>())
825a5d09c63SMatthias Springer         continue;
826a5d09c63SMatthias Springer       if (!state.areEquivalentBufferizedValues(
827a5d09c63SMatthias Springer               it.value(), yieldOp->getBlock()->getArgument(it.index())))
828a5d09c63SMatthias Springer         return yieldOp->emitError()
829a5d09c63SMatthias Springer                << "Yield operand #" << it.index()
830a5d09c63SMatthias Springer                << " is not equivalent to the corresponding iter bbArg";
831a5d09c63SMatthias Springer     }
832a5d09c63SMatthias Springer 
833a5d09c63SMatthias Springer     return success();
834a5d09c63SMatthias Springer   }
835a5d09c63SMatthias Springer };
836a5d09c63SMatthias Springer 
83719efe141SMatthias Springer /// Bufferization of scf.yield. Bufferized as part of their enclosing ops, so
83819efe141SMatthias Springer /// this is for analysis only.
83919efe141SMatthias Springer struct YieldOpInterface
84019efe141SMatthias Springer     : public BufferizableOpInterface::ExternalModel<YieldOpInterface,
84119efe141SMatthias Springer                                                     scf::YieldOp> {
84219efe141SMatthias Springer   bool bufferizesToMemoryRead(Operation *op, OpOperand &opOperand,
8439597b16aSMatthias Springer                               const AnalysisState &state) const {
84419efe141SMatthias Springer     return true;
84519efe141SMatthias Springer   }
84619efe141SMatthias Springer 
84719efe141SMatthias Springer   bool bufferizesToMemoryWrite(Operation *op, OpOperand &opOperand,
8489597b16aSMatthias Springer                                const AnalysisState &state) const {
84919efe141SMatthias Springer     return false;
85019efe141SMatthias Springer   }
85119efe141SMatthias Springer 
8529597b16aSMatthias Springer   SmallVector<OpResult> getAliasingOpResult(Operation *op, OpOperand &opOperand,
8539597b16aSMatthias Springer                                             const AnalysisState &state) const {
85419efe141SMatthias Springer     if (isa<scf::IfOp>(op->getParentOp()))
855585a8a32SMatthias Springer       return {op->getParentOp()->getResult(opOperand.getOperandNumber())};
85619efe141SMatthias Springer     if (isa<scf::ExecuteRegionOp>(op->getParentOp()))
857585a8a32SMatthias Springer       return {op->getParentOp()->getResult(opOperand.getOperandNumber())};
858585a8a32SMatthias Springer     return {};
85919efe141SMatthias Springer   }
86019efe141SMatthias Springer 
86119efe141SMatthias Springer   bool mustBufferizeInPlace(Operation *op, OpOperand &opOperand,
8629597b16aSMatthias Springer                             const AnalysisState &state) const {
86319efe141SMatthias Springer     // Yield operands always bufferize inplace. Otherwise, an alloc + copy
86419efe141SMatthias Springer     // may be generated inside the block. We should not return/yield allocations
86519efe141SMatthias Springer     // when possible.
86619efe141SMatthias Springer     return true;
86719efe141SMatthias Springer   }
86819efe141SMatthias Springer 
86919efe141SMatthias Springer   LogicalResult bufferize(Operation *op, RewriterBase &rewriter,
870b55d55ecSMatthias Springer                           const BufferizationOptions &options) const {
87119efe141SMatthias Springer     auto yieldOp = cast<scf::YieldOp>(op);
872a5d09c63SMatthias Springer     if (!isa<scf::ExecuteRegionOp, scf::IfOp, scf::ForOp, scf::WhileOp>(
87319efe141SMatthias Springer             yieldOp->getParentOp()))
87419efe141SMatthias Springer       return yieldOp->emitError("unsupported scf::YieldOp parent");
87519efe141SMatthias Springer     return success();
87619efe141SMatthias Springer   }
87719efe141SMatthias Springer };
87819efe141SMatthias Springer 
87972de7588SNicolas Vasilache using tensor::ExtractSliceOp;
88072de7588SNicolas Vasilache 
88172de7588SNicolas Vasilache /// Return the destinations that an ForeachThreadOp is inserting into. One per
88272de7588SNicolas Vasilache /// ParallelInsertSliceOp.
88372de7588SNicolas Vasilache static SmallVector<OpOperand *>
88472de7588SNicolas Vasilache getInsertionDest(ForeachThreadOp foreachThreadOp) {
88572de7588SNicolas Vasilache   PerformConcurrentlyOp terminator = foreachThreadOp.getTerminator();
88672de7588SNicolas Vasilache   SmallVector<OpOperand *> result;
88772de7588SNicolas Vasilache   terminator.walk([&](ParallelInsertSliceOp insertOp) {
88872de7588SNicolas Vasilache     result.push_back(&insertOp->getOpOperand(1) /*dest*/);
88972de7588SNicolas Vasilache   });
89072de7588SNicolas Vasilache   return result;
89172de7588SNicolas Vasilache }
89272de7588SNicolas Vasilache 
89372de7588SNicolas Vasilache /// Bufferization of ForeachThreadOp. This also bufferizes the terminator of the
89472de7588SNicolas Vasilache /// region. There are op interfaces for the terminators (PerformConcurrentlyOp
89572de7588SNicolas Vasilache /// and ParallelInsertSliceOp), but these are only used during analysis. Not
89672de7588SNicolas Vasilache /// for bufferization.
89772de7588SNicolas Vasilache struct ForeachThreadOpInterface
89872de7588SNicolas Vasilache     : public BufferizableOpInterface::ExternalModel<ForeachThreadOpInterface,
89972de7588SNicolas Vasilache                                                     ForeachThreadOp> {
90072de7588SNicolas Vasilache   SmallVector<OpOperand *>
90172de7588SNicolas Vasilache   getAliasingOpOperand(Operation *op, OpResult opResult,
90272de7588SNicolas Vasilache                        const AnalysisState &state) const {
90372de7588SNicolas Vasilache     // Get OpOperand (dest) from corresponding ParallelInsertSliceOp.
90472de7588SNicolas Vasilache     auto foreachThreadOp = cast<ForeachThreadOp>(op);
90572de7588SNicolas Vasilache     return {getInsertionDest(foreachThreadOp)[opResult.getResultNumber()]};
90672de7588SNicolas Vasilache   }
90772de7588SNicolas Vasilache 
90872de7588SNicolas Vasilache   bool isMemoryWrite(Operation *op, OpResult opResult,
90972de7588SNicolas Vasilache                      const AnalysisState &state) const {
91072de7588SNicolas Vasilache     // This op is a memory write. Stop lookup here to avoid finding false
91172de7588SNicolas Vasilache     // conflicts involving this op and one of the ops in the region. This is
91272de7588SNicolas Vasilache     // similar to how scf.if ops are analyzed.
91372de7588SNicolas Vasilache     return true;
91472de7588SNicolas Vasilache   }
91572de7588SNicolas Vasilache 
91672de7588SNicolas Vasilache   BufferRelation bufferRelation(Operation *op, OpResult opResult,
91772de7588SNicolas Vasilache                                 const AnalysisState &state) const {
91872de7588SNicolas Vasilache     return BufferRelation::Equivalent;
91972de7588SNicolas Vasilache   }
92072de7588SNicolas Vasilache 
921d361ecbdSMatthias Springer   LogicalResult resolveConflicts(Operation *op, RewriterBase &rewriter,
922d361ecbdSMatthias Springer                                  const AnalysisState &state) const {
923d361ecbdSMatthias Springer     auto bufferizableOp = cast<BufferizableOpInterface>(op);
924d361ecbdSMatthias Springer     if (failed(bufferizableOp.resolveTensorOpOperandConflicts(rewriter, state)))
925d361ecbdSMatthias Springer       return failure();
926d361ecbdSMatthias Springer 
927d361ecbdSMatthias Springer     OpBuilder::InsertionGuard g(rewriter);
928d361ecbdSMatthias Springer     auto foreachThreadOp = cast<ForeachThreadOp>(op);
929d361ecbdSMatthias Springer     for (OpResult opResult : foreachThreadOp->getOpResults()) {
930d361ecbdSMatthias Springer       SmallVector<OpOperand *> destOperands =
931d361ecbdSMatthias Springer           state.getAliasingOpOperand(opResult);
932d361ecbdSMatthias Springer       assert(destOperands.size() == 1 &&
933d361ecbdSMatthias Springer              "expected exactly one aliasing OpOperand");
934d361ecbdSMatthias Springer       assert(isa<ParallelInsertSliceOp>(destOperands.front()->getOwner()) &&
935d361ecbdSMatthias Springer              "expected ParallelInsertSliceOp");
936d361ecbdSMatthias Springer 
937d361ecbdSMatthias Springer       // Nothing to do if there is no conflict.
938d361ecbdSMatthias Springer       if (state.isInPlace(*destOperands.front()))
939d361ecbdSMatthias Springer         continue;
940d361ecbdSMatthias Springer 
941d361ecbdSMatthias Springer       // Create AllocTensorOp.
942d361ecbdSMatthias Springer       bool isYielded = state.isTensorYielded(opResult);
943d361ecbdSMatthias Springer       auto resultType = opResult.getType().cast<RankedTensorType>();
944d361ecbdSMatthias Springer       Value alloc = rewriter.create<bufferization::AllocTensorOp>(
945d361ecbdSMatthias Springer           op->getLoc(), resultType, /*dynamicDims=*/ValueRange(),
946d361ecbdSMatthias Springer           /*copy=*/destOperands.front()->get(),
947d361ecbdSMatthias Springer           /*escape=*/isYielded);
948d361ecbdSMatthias Springer 
949d361ecbdSMatthias Springer       // Update terminator operand.
950d361ecbdSMatthias Springer       rewriter.updateRootInPlace(destOperands.front()->getOwner(),
951d361ecbdSMatthias Springer                                  [&]() { destOperands.front()->set(alloc); });
952d361ecbdSMatthias Springer     }
953d361ecbdSMatthias Springer 
954d361ecbdSMatthias Springer     return success();
955d361ecbdSMatthias Springer   }
956d361ecbdSMatthias Springer 
95772de7588SNicolas Vasilache   LogicalResult bufferize(Operation *op, RewriterBase &b,
958b55d55ecSMatthias Springer                           const BufferizationOptions &options) const {
95972de7588SNicolas Vasilache     OpBuilder::InsertionGuard g(b);
96072de7588SNicolas Vasilache     auto foreachThreadOp = cast<ForeachThreadOp>(op);
96172de7588SNicolas Vasilache 
96272de7588SNicolas Vasilache     // Gather new results of the ForeachThreadOp.
96372de7588SNicolas Vasilache     SmallVector<Value> newResults;
96472de7588SNicolas Vasilache     for (OpResult opResult : foreachThreadOp->getOpResults()) {
965b3ebe3beSMatthias Springer       OpOperand *insertDest =
966b3ebe3beSMatthias Springer           getInsertionDest(foreachThreadOp)[opResult.getResultNumber()];
96772de7588SNicolas Vasilache       // Insert copies right before the PerformConcurrentlyOp terminator. They
96872de7588SNicolas Vasilache       // should not be inside terminator (which would be the default insertion
96972de7588SNicolas Vasilache       // point).
970b55d55ecSMatthias Springer       Value buffer = getBuffer(b, insertDest->get(), options);
97172de7588SNicolas Vasilache       newResults.push_back(buffer);
97272de7588SNicolas Vasilache     }
97372de7588SNicolas Vasilache 
97472de7588SNicolas Vasilache     // Create new ForeachThreadOp without any results and drop the automatically
97572de7588SNicolas Vasilache     // introduced terminator.
97672de7588SNicolas Vasilache     TypeRange newResultTypes;
97772de7588SNicolas Vasilache     auto newForeachThreadOp =
97872de7588SNicolas Vasilache         b.create<ForeachThreadOp>(foreachThreadOp.getLoc(), newResultTypes,
97972de7588SNicolas Vasilache                                   foreachThreadOp.getNumThreads());
98072de7588SNicolas Vasilache     newForeachThreadOp.getBody()->getTerminator()->erase();
98172de7588SNicolas Vasilache 
98272de7588SNicolas Vasilache     // Move over block contents of the old op.
98372de7588SNicolas Vasilache     b.mergeBlocks(foreachThreadOp.getBody(), newForeachThreadOp.getBody(),
98472de7588SNicolas Vasilache                   {newForeachThreadOp.getBody()->getArguments()});
98572de7588SNicolas Vasilache 
98672de7588SNicolas Vasilache     // Bufferize terminator.
98772de7588SNicolas Vasilache     auto performConcurrentlyOp = cast<PerformConcurrentlyOp>(
98872de7588SNicolas Vasilache         newForeachThreadOp.getBody()->getTerminator());
98972de7588SNicolas Vasilache     b.setInsertionPoint(performConcurrentlyOp);
99072de7588SNicolas Vasilache     unsigned resultCounter = 0;
99172de7588SNicolas Vasilache     WalkResult walkResult =
99272de7588SNicolas Vasilache         performConcurrentlyOp.walk([&](ParallelInsertSliceOp insertOp) {
99372de7588SNicolas Vasilache           Location loc = insertOp.getLoc();
99472de7588SNicolas Vasilache           Type srcType = getMemRefType(
995b55d55ecSMatthias Springer               insertOp.getSource().getType().cast<RankedTensorType>(), options);
99672de7588SNicolas Vasilache           // ParallelInsertSliceOp bufferizes to a copy.
99772de7588SNicolas Vasilache           auto srcMemref = b.create<bufferization::ToMemrefOp>(
99872de7588SNicolas Vasilache               loc, srcType, insertOp.getSource());
99972de7588SNicolas Vasilache           Value destMemref = newResults[resultCounter++];
100072de7588SNicolas Vasilache           Value subview = b.create<memref::SubViewOp>(
100172de7588SNicolas Vasilache               loc, destMemref, insertOp.getMixedOffsets(),
100272de7588SNicolas Vasilache               insertOp.getMixedSizes(), insertOp.getMixedStrides());
100372de7588SNicolas Vasilache           // This memcpy will fold away if everything bufferizes in-place.
1004b55d55ecSMatthias Springer           if (failed(options.createMemCpy(b, insertOp.getLoc(), srcMemref,
1005b55d55ecSMatthias Springer                                           subview)))
100672de7588SNicolas Vasilache             return WalkResult::interrupt();
100772de7588SNicolas Vasilache           b.eraseOp(insertOp);
100872de7588SNicolas Vasilache           return WalkResult::advance();
100972de7588SNicolas Vasilache         });
101072de7588SNicolas Vasilache     if (walkResult.wasInterrupted())
101172de7588SNicolas Vasilache       return failure();
101272de7588SNicolas Vasilache 
101372de7588SNicolas Vasilache     // Replace the op.
101472de7588SNicolas Vasilache     replaceOpWithBufferizedValues(b, op, newResults);
101572de7588SNicolas Vasilache 
101672de7588SNicolas Vasilache     return success();
101772de7588SNicolas Vasilache   }
101872de7588SNicolas Vasilache };
101972de7588SNicolas Vasilache 
102072de7588SNicolas Vasilache /// Nothing to do for PerformConcurrentlyOp.
102172de7588SNicolas Vasilache struct PerformConcurrentlyOpInterface
102272de7588SNicolas Vasilache     : public BufferizableOpInterface::ExternalModel<
102372de7588SNicolas Vasilache           PerformConcurrentlyOpInterface, PerformConcurrentlyOp> {
102472de7588SNicolas Vasilache   LogicalResult bufferize(Operation *op, RewriterBase &b,
1025b55d55ecSMatthias Springer                           const BufferizationOptions &options) const {
1026b3ebe3beSMatthias Springer     llvm_unreachable("op does not have any tensor OpOperands / OpResults");
102772de7588SNicolas Vasilache     return failure();
102872de7588SNicolas Vasilache   }
102972de7588SNicolas Vasilache };
103072de7588SNicolas Vasilache 
103172de7588SNicolas Vasilache /// Return true if the (ExtractSliceOp, ParallelInsertSliceOp) pair match (i.e.
103272de7588SNicolas Vasilache /// equivalent operand / result and same offset/sizes/strides specification).
103372de7588SNicolas Vasilache static bool areEquivalentExtractSliceOps(const AnalysisState &state,
103472de7588SNicolas Vasilache                                          ExtractSliceOp st,
103572de7588SNicolas Vasilache                                          ParallelInsertSliceOp sti) {
103672de7588SNicolas Vasilache   if (!st || !sti)
103772de7588SNicolas Vasilache     return false;
103872de7588SNicolas Vasilache   if (st != sti &&
1039*8df54a6aSJacques Pienaar       !state.areEquivalentBufferizedValues(st.getSource(), sti.getDest()))
104072de7588SNicolas Vasilache     return false;
104172de7588SNicolas Vasilache   if (!sameOffsetsSizesAndStrides(st, sti, isEqualConstantIntOrValue))
104272de7588SNicolas Vasilache     return false;
104372de7588SNicolas Vasilache   return true;
104472de7588SNicolas Vasilache }
104572de7588SNicolas Vasilache 
104672de7588SNicolas Vasilache /// Return true if `value` is originating from an ExtractSliceOp that matches
104772de7588SNicolas Vasilache /// the given InsertSliceOp.
104872de7588SNicolas Vasilache static bool hasMatchingExtractSliceOp(const AnalysisState &state, Value value,
104972de7588SNicolas Vasilache                                       ParallelInsertSliceOp insertOp) {
105072de7588SNicolas Vasilache   auto condition = [&](Value val) {
105172de7588SNicolas Vasilache     if (auto extractOp = val.getDefiningOp<ExtractSliceOp>())
105272de7588SNicolas Vasilache       if (areEquivalentExtractSliceOps(state, extractOp, insertOp))
105372de7588SNicolas Vasilache         return true;
105472de7588SNicolas Vasilache     return false;
105572de7588SNicolas Vasilache   };
105672de7588SNicolas Vasilache 
105772de7588SNicolas Vasilache   return llvm::all_of(state.findValueInReverseUseDefChain(value, condition),
105872de7588SNicolas Vasilache                       condition);
105972de7588SNicolas Vasilache }
106072de7588SNicolas Vasilache 
106172de7588SNicolas Vasilache /// Analysis of ParallelInsertSliceOp.
106272de7588SNicolas Vasilache struct ParallelInsertSliceOpInterface
106372de7588SNicolas Vasilache     : public BufferizableOpInterface::ExternalModel<
106472de7588SNicolas Vasilache           ParallelInsertSliceOpInterface, ParallelInsertSliceOp> {
106572de7588SNicolas Vasilache   SmallVector<OpResult> getAliasingOpResult(Operation *op, OpOperand &opOperand,
106672de7588SNicolas Vasilache                                             const AnalysisState &state) const {
106772de7588SNicolas Vasilache     if (&opOperand != &op->getOpOperand(1) /*dest*/)
106872de7588SNicolas Vasilache       return {};
106972de7588SNicolas Vasilache 
107072de7588SNicolas Vasilache     // ParallelInsertSliceOp itself has no results. Tensors are returned via
107172de7588SNicolas Vasilache     // the parent op.
107272de7588SNicolas Vasilache     auto foreachThreadOp = op->getParentOfType<ForeachThreadOp>();
107372de7588SNicolas Vasilache     assert(foreachThreadOp &&
107472de7588SNicolas Vasilache            "could not find valid owner of parallel_insert_slice");
107572de7588SNicolas Vasilache 
107672de7588SNicolas Vasilache     // The i-th ParallelInsertSliceOp result is returned via the i-th OpResult
107772de7588SNicolas Vasilache     // of the parent ForeachThreadOp.
107872de7588SNicolas Vasilache     Block *block = op->getBlock();
107972de7588SNicolas Vasilache     unsigned int opIdx = 0;
108072de7588SNicolas Vasilache     for (ParallelInsertSliceOp insertOp :
108172de7588SNicolas Vasilache          block->getOps<ParallelInsertSliceOp>()) {
108272de7588SNicolas Vasilache       if (insertOp.getOperation() == op)
108372de7588SNicolas Vasilache         break;
108472de7588SNicolas Vasilache       ++opIdx;
108572de7588SNicolas Vasilache     }
108672de7588SNicolas Vasilache     assert(opIdx < foreachThreadOp->getNumResults() &&
108772de7588SNicolas Vasilache            "could not find op inside terminator op");
108872de7588SNicolas Vasilache 
108972de7588SNicolas Vasilache     return {foreachThreadOp->getResult(opIdx)};
109072de7588SNicolas Vasilache   }
109172de7588SNicolas Vasilache 
109272de7588SNicolas Vasilache   bool bufferizesToMemoryRead(Operation *op, OpOperand &opOperand,
109372de7588SNicolas Vasilache                               const AnalysisState &state) const {
109472de7588SNicolas Vasilache     return true;
109572de7588SNicolas Vasilache   }
109672de7588SNicolas Vasilache 
109772de7588SNicolas Vasilache   bool bufferizesToMemoryWrite(Operation *op, OpOperand &opOperand,
109872de7588SNicolas Vasilache                                const AnalysisState &state) const {
109972de7588SNicolas Vasilache     return &opOperand == &op->getOpOperand(1) /*dest*/;
110072de7588SNicolas Vasilache   }
110172de7588SNicolas Vasilache 
110272de7588SNicolas Vasilache   BufferRelation bufferRelation(Operation *op, OpResult opResult,
110372de7588SNicolas Vasilache                                 const AnalysisState &state) const {
110472de7588SNicolas Vasilache     return BufferRelation::Equivalent;
110572de7588SNicolas Vasilache   }
110672de7588SNicolas Vasilache 
1107d361ecbdSMatthias Springer   LogicalResult resolveConflicts(Operation *op, RewriterBase &rewriter,
1108d361ecbdSMatthias Springer                                  const AnalysisState &state) const {
1109d361ecbdSMatthias Springer     return success();
1110d361ecbdSMatthias Springer   }
1111d361ecbdSMatthias Springer 
111272de7588SNicolas Vasilache   LogicalResult bufferize(Operation *op, RewriterBase &b,
1113b55d55ecSMatthias Springer                           const BufferizationOptions &options) const {
111472de7588SNicolas Vasilache     // Will be bufferized as part of ForeachThreadOp.
111572de7588SNicolas Vasilache     return failure();
111672de7588SNicolas Vasilache   }
111772de7588SNicolas Vasilache 
111872de7588SNicolas Vasilache   // TODO: This is copied from TensorInterfaceImpl.cpp. Find a way to share
111972de7588SNicolas Vasilache   // the code.
112072de7588SNicolas Vasilache   bool isNotConflicting(Operation *op, OpOperand *uRead,
112172de7588SNicolas Vasilache                         OpOperand *uConflictingWrite,
112272de7588SNicolas Vasilache                         const AnalysisState &state) const {
112372de7588SNicolas Vasilache     Operation *readingOp = uRead->getOwner();
112472de7588SNicolas Vasilache     Operation *conflictingWritingOp = uConflictingWrite->getOwner();
112572de7588SNicolas Vasilache 
112672de7588SNicolas Vasilache     // Special rules for matching ExtractSliceOp/InsertSliceOp pairs. If
112772de7588SNicolas Vasilache     // uRead is an InsertSliceOp...
112872de7588SNicolas Vasilache     if (auto insertSliceOp = dyn_cast<ParallelInsertSliceOp>(readingOp)) {
112972de7588SNicolas Vasilache       // As an example, consider the following IR.
113072de7588SNicolas Vasilache       //
113172de7588SNicolas Vasilache       // %0 = tensor.extract_slice %t[%a, %b][%c, %d][1, 1] {inplace = [true] }
113272de7588SNicolas Vasilache       // %1 = linalg.fill %cst, %0 {inplace= [true] }
113372de7588SNicolas Vasilache       // %2 = tensor.insert_slice %1 into %t[%a, %b][%c, %d][1, 1]
113472de7588SNicolas Vasilache       //     {inplace= [true] }
113572de7588SNicolas Vasilache 
113672de7588SNicolas Vasilache       // TODO: Use insertSliceOp.getDestOpOperand etc. when available.
113772de7588SNicolas Vasilache       if (uRead == &insertSliceOp->getOpOperand(1) /*dest*/ &&
113872de7588SNicolas Vasilache           hasMatchingExtractSliceOp(state, uConflictingWrite->get(),
113972de7588SNicolas Vasilache                                     insertSliceOp))
114072de7588SNicolas Vasilache         // Case 1: The main insight is that InsertSliceOp reads only part of
114172de7588SNicolas Vasilache         // the destination tensor. The overwritten area is not read. If
114272de7588SNicolas Vasilache         // uConflictingWrite writes into exactly the memory location that is
114372de7588SNicolas Vasilache         // being read by uRead, this is not a conflict.
114472de7588SNicolas Vasilache         //
114572de7588SNicolas Vasilache         // In the above example:
114672de7588SNicolas Vasilache         // uRead             = OpOperand 1 (%t) of tensor.insert_slice
114772de7588SNicolas Vasilache         // uConflictingWrite = OpOperand 1 (%0) of linalg.fill
114872de7588SNicolas Vasilache         //
114972de7588SNicolas Vasilache         // The read of %t does not conflict with the write of the FillOp
115072de7588SNicolas Vasilache         // (same aliases!) because the area that the FillOp operates on is
115172de7588SNicolas Vasilache         // exactly the one that is *not* read via %t.
115272de7588SNicolas Vasilache         return true;
115372de7588SNicolas Vasilache 
115472de7588SNicolas Vasilache       if (uRead == &insertSliceOp->getOpOperand(0) /*source*/ &&
115572de7588SNicolas Vasilache           uConflictingWrite == &insertSliceOp->getOpOperand(1) /*dest*/ &&
115672de7588SNicolas Vasilache           hasMatchingExtractSliceOp(state, uRead->get(), insertSliceOp))
115772de7588SNicolas Vasilache         // Case 2: The read of the source tensor and the write to the dest
115872de7588SNicolas Vasilache         // tensor via an InsertSliceOp is not a conflict if the read is
115972de7588SNicolas Vasilache         // reading exactly that part of an equivalent tensor that the
116072de7588SNicolas Vasilache         // InsertSliceOp is writing.
116172de7588SNicolas Vasilache         //
116272de7588SNicolas Vasilache         // In the above example:
116372de7588SNicolas Vasilache         // uRead             = OpOperand 0 (%1) of tensor.insert_slice
116472de7588SNicolas Vasilache         // uConflictingWrite = OpOperand 1 (%t) of tensor.insert_slice
116572de7588SNicolas Vasilache         return true;
116672de7588SNicolas Vasilache     }
116772de7588SNicolas Vasilache 
116872de7588SNicolas Vasilache     // If uConflictingWrite is an InsertSliceOp...
116972de7588SNicolas Vasilache     if (auto insertSliceOp =
117072de7588SNicolas Vasilache             dyn_cast<ParallelInsertSliceOp>(conflictingWritingOp))
117172de7588SNicolas Vasilache       // As an example, consider the following IR.
117272de7588SNicolas Vasilache       //
117372de7588SNicolas Vasilache       // %0 = tensor.extract_slice %t[%a, %b][%c, %d][1, 1] {inplace = [true] }
117472de7588SNicolas Vasilache       // %1 = linalg.fill %cst, %0 {inplace= [true] }
117572de7588SNicolas Vasilache       // %2 = tensor.insert_slice %1 into %t[%a, %b][%c, %d][1, 1]
117672de7588SNicolas Vasilache       //     {inplace= [true] }
117772de7588SNicolas Vasilache       // %3 = vector.transfer_read %1, %cst
117872de7588SNicolas Vasilache       //
117972de7588SNicolas Vasilache       // In the above example:
118072de7588SNicolas Vasilache       // uRead             = OpOperand 0 (%1) of vector.transfer_read
118172de7588SNicolas Vasilache       // uConflictingWrite = OpOperand 1 (%t) of tensor.insert_slice
118272de7588SNicolas Vasilache       // lastWrite         = %1
118372de7588SNicolas Vasilache       //
118472de7588SNicolas Vasilache       // This is not a conflict because the InsertSliceOp overwrites the
118572de7588SNicolas Vasilache       // memory segment of %1 with the exact same data. (Effectively, there
118672de7588SNicolas Vasilache       // is no memory write here.)
118772de7588SNicolas Vasilache       if (uConflictingWrite == &insertSliceOp->getOpOperand(1) /*dest*/ &&
118872de7588SNicolas Vasilache           state.areEquivalentBufferizedValues(uRead->get(),
118972de7588SNicolas Vasilache                                               insertSliceOp.getSource()) &&
119072de7588SNicolas Vasilache           hasMatchingExtractSliceOp(state, insertSliceOp.getSource(),
119172de7588SNicolas Vasilache                                     insertSliceOp))
119272de7588SNicolas Vasilache         return true;
119372de7588SNicolas Vasilache 
119472de7588SNicolas Vasilache     return false;
119572de7588SNicolas Vasilache   }
119672de7588SNicolas Vasilache };
119772de7588SNicolas Vasilache 
119819efe141SMatthias Springer } // namespace
119919efe141SMatthias Springer } // namespace scf
120019efe141SMatthias Springer } // namespace mlir
120119efe141SMatthias Springer 
120219efe141SMatthias Springer void mlir::scf::registerBufferizableOpInterfaceExternalModels(
120319efe141SMatthias Springer     DialectRegistry &registry) {
120477eee579SRiver Riddle   registry.addExtension(+[](MLIRContext *ctx, scf::SCFDialect *dialect) {
120577eee579SRiver Riddle     ExecuteRegionOp::attachInterface<ExecuteRegionOpInterface>(*ctx);
120677eee579SRiver Riddle     ForOp::attachInterface<ForOpInterface>(*ctx);
120777eee579SRiver Riddle     IfOp::attachInterface<IfOpInterface>(*ctx);
120872de7588SNicolas Vasilache     ForeachThreadOp::attachInterface<ForeachThreadOpInterface>(*ctx);
120972de7588SNicolas Vasilache     ParallelInsertSliceOp::attachInterface<ParallelInsertSliceOpInterface>(
121072de7588SNicolas Vasilache         *ctx);
121172de7588SNicolas Vasilache     PerformConcurrentlyOp::attachInterface<PerformConcurrentlyOpInterface>(
121272de7588SNicolas Vasilache         *ctx);
1213a5d09c63SMatthias Springer     WhileOp::attachInterface<WhileOpInterface>(*ctx);
121477eee579SRiver Riddle     YieldOp::attachInterface<YieldOpInterface>(*ctx);
121577eee579SRiver Riddle   });
121619efe141SMatthias Springer }
1217