MLIR  20.0.0git
BufferizableOpInterfaceImpl.cpp
Go to the documentation of this file.
1 //===- BufferizableOpInterfaceImpl.cpp - Impl. of BufferizableOpInterface -===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
16 #include "mlir/IR/Dialect.h"
17 #include "mlir/IR/Operation.h"
19 
20 using namespace mlir;
21 using namespace linalg;
22 using namespace mlir::bufferization;
23 
24 namespace {
25 
26 /// Generic conversion for any DestinationStyleOpInterface on tensors.
27 static LogicalResult
28 bufferizeDestinationStyleOpInterface(RewriterBase &rewriter,
29  DestinationStyleOpInterface op,
31  // Take a guard before anything else.
32  OpBuilder::InsertionGuard g(rewriter);
33  rewriter.setInsertionPoint(op);
34 
35  // Nothing to do. This op is already bufferized.
36  if (op.hasPureBufferSemantics())
37  return success();
38 
39  // Ensure op has only tensors. Allow mixed tensor-buffer mode on a per-need
40  // basis.
41  if (!op.hasPureTensorSemantics())
42  return op->emitError() << "op does not have pure tensor semantics";
43 
44  // New input operands for the cloned op.
45  SmallVector<Value> newInputBuffers;
46  newInputBuffers.reserve(op.getNumDpsInputs());
47  for (OpOperand *opOperand : op.getDpsInputOperands()) {
48  if (op.isScalar(opOperand)) {
49  newInputBuffers.push_back(opOperand->get());
50  continue;
51  }
52  FailureOr<Value> buffer = getBuffer(rewriter, opOperand->get(), options);
53  if (failed(buffer))
54  return failure();
55  newInputBuffers.push_back(*buffer);
56  }
57 
58  // New output operands for the cloned op.
59  SmallVector<Value> newOutputBuffers;
60  for (OpResult opResult : op->getOpResults()) {
61  OpOperand *opOperand = op.getDpsInitOperand(opResult.getResultNumber());
62  FailureOr<Value> resultBuffer =
63  getBuffer(rewriter, opOperand->get(), options);
64  if (failed(resultBuffer))
65  return failure();
66  newOutputBuffers.push_back(*resultBuffer);
67  }
68 
69  // Merge input/output operands.
70  SmallVector<Value> newOperands = newInputBuffers;
71  newOperands.append(newOutputBuffers.begin(), newOutputBuffers.end());
72 
73  // Set insertion point now that potential alloc/dealloc are introduced.
74  rewriter.setInsertionPoint(op);
75  // Clone the op, but use the new operands. Move the existing block into the
76  // new op. Since the new op does not have any tensor results, it does not
77  // return anything.
78  assert(op->getNumRegions() == 1 && "expected that op has 1 region");
79  OperationState state(op->getLoc(), op->getName(), newOperands, TypeRange{},
80  op->getAttrs());
81  state.addRegion();
82  Operation *newOp = Operation::create(state);
83  newOp->getRegion(0).getBlocks().splice(newOp->getRegion(0).begin(),
84  op->getRegion(0).getBlocks());
85 
86  // We don't want the rewriter tracks an incomplete operation, so insert new
87  // operation after op was fully constructed.
88  rewriter.insert(newOp);
89 
90  // Replace the results of the old op with the new output buffers.
91  replaceOpWithBufferizedValues(rewriter, op, newOutputBuffers);
92 
93  return success();
94 }
95 
96 /// Bufferization of linalg.generic. Replace with a new linalg.generic that
97 /// operates entirely on memrefs.
98 template <typename OpTy>
99 struct LinalgOpInterface
100  : public DstBufferizableOpInterfaceExternalModel<LinalgOpInterface<OpTy>,
101  OpTy> {
102  bool bufferizesToMemoryRead(Operation *op, OpOperand &opOperand,
103  const AnalysisState &state) const {
104  // Operand is read if it is used in the computation.
105  auto linalgOp = cast<linalg::LinalgOp>(op);
106  return linalgOp.payloadUsesValueFromOperand(&opOperand);
107  }
108 
109  bool bufferizesToMemoryWrite(Operation *op, OpOperand &opOperand,
110  const AnalysisState &state) const {
111  // Operand is written to if it is not an input/init.
112  auto dpsOp = cast<DestinationStyleOpInterface>(op);
113  return dpsOp.isDpsInit(&opOperand);
114  }
115 
116  bool bufferizesToElementwiseAccess(Operation *op, const AnalysisState &state,
117  ArrayRef<OpOperand *> opOperands) const {
118  auto linalgOp = cast<linalg::LinalgOp>(op);
119 
120  // Accesses into sparse data structures are not necessarily elementwise.
122  return false;
123 
124  // All loops must be parallel.
125  if (linalgOp.getNumLoops() != linalgOp.getNumParallelLoops())
126  return false;
127 
128  // All index maps of tensors must be identity maps.
129  SmallVector<AffineMap> indexingMaps = linalgOp.getIndexingMapsArray();
130  assert(linalgOp->getNumOperands() == indexingMaps.size() &&
131  "unexpected number of indexing maps");
132  for (auto [operand, map] :
133  llvm::zip(linalgOp->getOpOperands(), indexingMaps)) {
134  // Non-tensors do not participate in bufferization, so they can be
135  // ignored.
136  if (!isa<RankedTensorType, MemRefType>(operand.get().getType()))
137  continue;
138  // Only consider operands in `opOperands`.
139  if (!llvm::is_contained(opOperands, &operand))
140  continue;
141  // TODO: This could be generalized to other indexing maps. (All indexing
142  // must be the same.)
143  if (!map.isIdentity())
144  return false;
145  }
146 
147  return true;
148  }
149 
150  LogicalResult bufferize(Operation *op, RewriterBase &rewriter,
151  const BufferizationOptions &options) const {
152  return bufferizeDestinationStyleOpInterface(
153  rewriter, cast<DestinationStyleOpInterface>(op), options);
154  }
155 };
156 
157 /// Helper structure that iterates over all LinalgOps in `OpTys` and registers
158 /// the `BufferizableOpInterface` with each of them.
159 template <typename... Ops>
160 struct LinalgOpInterfaceHelper {
161  static void registerOpInterface(MLIRContext *ctx) {
162  (Ops::template attachInterface<LinalgOpInterface<Ops>>(*ctx), ...);
163  }
164 };
165 
166 struct SoftmaxOpInterface
167  : public DstBufferizableOpInterfaceExternalModel<SoftmaxOpInterface,
168  linalg::SoftmaxOp> {
169  bool bufferizesToMemoryRead(Operation *op, OpOperand &opOperand,
170  const AnalysisState &state) const {
171  // Output operand is not read.
172  auto softmaxOp = cast<linalg::SoftmaxOp>(op);
173  return &opOperand == &softmaxOp.getInputMutable();
174  }
175 
176  LogicalResult bufferize(Operation *op, RewriterBase &rewriter,
177  const BufferizationOptions &options) const {
178  auto softmaxOp = cast<linalg::SoftmaxOp>(op);
179  FailureOr<Value> inputBuffer =
180  getBuffer(rewriter, softmaxOp.getInput(), options);
181  if (failed(inputBuffer))
182  return failure();
183  FailureOr<Value> outputBuffer =
184  getBuffer(rewriter, softmaxOp.getOutput(), options);
185  if (failed(outputBuffer))
186  return failure();
187  rewriter.create<linalg::SoftmaxOp>(softmaxOp.getLoc(),
188  /*result=*/TypeRange(), *inputBuffer,
189  *outputBuffer, softmaxOp.getDimension());
190  replaceOpWithBufferizedValues(rewriter, op, *outputBuffer);
191  return success();
192  }
193 };
194 } // namespace
195 
197  DialectRegistry &registry) {
198  registry.addExtension(+[](MLIRContext *ctx, linalg::LinalgDialect *dialect) {
199  // Register all Linalg structured ops. `LinalgOp` is an interface and it is
200  // not possible to attach an external interface to an existing interface.
201  // Therefore, attach the `BufferizableOpInterface` to all ops one-by-one.
202  LinalgOpInterfaceHelper<
203 #define GET_OP_LIST
204 #include "mlir/Dialect/Linalg/IR/LinalgStructuredOps.cpp.inc"
205  >::registerOpInterface(ctx);
206 
207  SoftmaxOp::attachInterface<SoftmaxOpInterface>(*ctx);
208  });
209 }
static llvm::ManagedStatic< PassManagerOptions > options
Base class for generic analysis states.
The DialectRegistry maps a dialect namespace to a constructor for the matching dialect.
bool addExtension(TypeID extensionID, std::unique_ptr< DialectExtensionBase > extension)
Add the given extension to the registry.
IRValueT get() const
Return the current value being used by this operand.
Definition: UseDefLists.h:160
MLIRContext is the top-level object for a collection of MLIR operations.
Definition: MLIRContext.h:60
RAII guard to reset the insertion point of the builder when destroyed.
Definition: Builders.h:356
void setInsertionPoint(Block *block, Block::iterator insertPoint)
Set the insertion point to the specified location.
Definition: Builders.h:406
Operation * create(const OperationState &state)
Creates an operation given the fields represented as an OperationState.
Definition: Builders.cpp:497
Operation * insert(Operation *op)
Insert the given operation at the current insertion point and return it.
Definition: Builders.cpp:461
This class represents an operand of an operation.
Definition: Value.h:267
This is a value defined by a result of an operation.
Definition: Value.h:457
Operation is the basic unit of execution within MLIR.
Definition: Operation.h:88
static Operation * create(Location location, OperationName name, TypeRange resultTypes, ValueRange operands, NamedAttrList &&attributes, OpaqueProperties properties, BlockRange successors, unsigned numRegions)
Create a new Operation with the specific fields.
Definition: Operation.cpp:67
Region & getRegion(unsigned index)
Returns the region held by this operation at position 'index'.
Definition: Operation.h:682
iterator begin()
Definition: Region.h:55
BlockListType & getBlocks()
Definition: Region.h:45
This class coordinates the application of a rewrite on a set of IR, providing a way for clients to tr...
Definition: PatternMatch.h:400
This class provides an abstraction over the various different ranges of value types.
Definition: TypeRange.h:36
void replaceOpWithBufferizedValues(RewriterBase &rewriter, Operation *op, ValueRange values)
Replace an op with replacement values.
FailureOr< Value > getBuffer(RewriterBase &rewriter, Value value, const BufferizationOptions &options)
Lookup the buffer for the given value.
void registerBufferizableOpInterfaceExternalModels(DialectRegistry &registry)
bool hasAnySparseOperand(Operation *op)
Returns true iff MLIR operand has any sparse operand.
Definition: SparseTensor.h:186
Include the generated interface declarations.
This represents an operation in an abstracted form, suitable for use with the builder APIs.
Options for BufferizableOpInterface-based bufferization.
Bufferizable ops that implement the DestinationStyleOpInterface can use this external model base clas...