xref: /llvm-project/mlir/lib/Dialect/SparseTensor/Transforms/BufferizableOpInterfaceImpl.cpp (revision 6ed4d15cf49bca27157c6c8c896a7f674ef5df3a)
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 //
9 // These BufferizableOpInterface implementations provide analysis-related
10 // interface methods only. They are getting bufferized by the
11 // SparseTensorConversion pass.
12 
13 #include "mlir/Dialect/SparseTensor/Transforms/BufferizableOpInterfaceImpl.h"
14 
15 #include "mlir/Dialect/Bufferization/IR/BufferizableOpInterface.h"
16 #include "mlir/Dialect/Bufferization/IR/Bufferization.h"
17 #include "mlir/Dialect/SparseTensor/IR/SparseTensor.h"
18 #include "mlir/IR/Dialect.h"
19 #include "mlir/IR/Operation.h"
20 #include "mlir/IR/PatternMatch.h"
21 
22 using namespace mlir::bufferization;
23 using namespace mlir::sparse_tensor;
24 
25 namespace mlir {
26 namespace sparse_tensor {
27 namespace {
28 
29 template <typename ConcreteModel, typename ConcreteOp>
30 struct SparseBufferizableOpInterfaceExternalModel
31     : public BufferizableOpInterface::ExternalModel<ConcreteModel, ConcreteOp> {
bufferizemlir::sparse_tensor::__anonf7ee666a0111::SparseBufferizableOpInterfaceExternalModel32   LogicalResult bufferize(Operation *op, RewriterBase &rewriter,
33                           const BufferizationOptions &options) const {
34     return op->emitError(
35         "sparse_tensor ops must be bufferized with the sparsifier");
36   }
37 };
38 
39 struct ConcatenateOpInterface
40     : SparseBufferizableOpInterfaceExternalModel<ConcatenateOpInterface,
41                                                  sparse_tensor::ConcatenateOp> {
bufferizesToAllocationmlir::sparse_tensor::__anonf7ee666a0111::ConcatenateOpInterface42   bool bufferizesToAllocation(Operation *op, Value value) const { return true; }
43 
bufferizesToMemoryReadmlir::sparse_tensor::__anonf7ee666a0111::ConcatenateOpInterface44   bool bufferizesToMemoryRead(Operation *op, OpOperand &opOperand,
45                               const AnalysisState &state) const {
46     return true;
47   }
48 
bufferizesToMemoryWritemlir::sparse_tensor::__anonf7ee666a0111::ConcatenateOpInterface49   bool bufferizesToMemoryWrite(Operation *op, OpOperand &opOperand,
50                                const AnalysisState &state) const {
51     return false;
52   }
53 
getAliasingValuesmlir::sparse_tensor::__anonf7ee666a0111::ConcatenateOpInterface54   AliasingValueList getAliasingValues(Operation *op, OpOperand &opOperand,
55                                       const AnalysisState &state) const {
56     return {};
57   }
58 
isWritablemlir::sparse_tensor::__anonf7ee666a0111::ConcatenateOpInterface59   bool isWritable(Operation *op, Value value,
60                   const AnalysisState &state) const {
61     return true;
62   }
63 };
64 
65 struct ConvertOpInterface : public SparseBufferizableOpInterfaceExternalModel<
66                                 ConvertOpInterface, sparse_tensor::ConvertOp> {
bufferizesToAllocationmlir::sparse_tensor::__anonf7ee666a0111::ConvertOpInterface67   bool bufferizesToAllocation(Operation *op, Value value) const {
68     // ConvertOps may allocate. (Unless they convert between two identical
69     // types, then they fold away.)
70     return true;
71   }
72 
bufferizesToMemoryReadmlir::sparse_tensor::__anonf7ee666a0111::ConvertOpInterface73   bool bufferizesToMemoryRead(Operation *op, OpOperand &opOperand,
74                               const AnalysisState &state) const {
75     return true;
76   }
77 
bufferizesToMemoryWritemlir::sparse_tensor::__anonf7ee666a0111::ConvertOpInterface78   bool bufferizesToMemoryWrite(Operation *op, OpOperand &opOperand,
79                                const AnalysisState &state) const {
80     return false;
81   }
82 
getAliasingValuesmlir::sparse_tensor::__anonf7ee666a0111::ConvertOpInterface83   AliasingValueList getAliasingValues(Operation *op, OpOperand &opOperand,
84                                       const AnalysisState &state) const {
85     return {};
86   }
87 
isWritablemlir::sparse_tensor::__anonf7ee666a0111::ConvertOpInterface88   bool isWritable(Operation *op, Value value,
89                   const AnalysisState &state) const {
90     return true;
91   }
92 };
93 
94 struct LoadOpInterface
95     : public SparseBufferizableOpInterfaceExternalModel<LoadOpInterface,
96                                                         sparse_tensor::LoadOp> {
bufferizesToMemoryReadmlir::sparse_tensor::__anonf7ee666a0111::LoadOpInterface97   bool bufferizesToMemoryRead(Operation *op, OpOperand &opOperand,
98                               const AnalysisState &state) const {
99     return false;
100   }
101 
bufferizesToMemoryWritemlir::sparse_tensor::__anonf7ee666a0111::LoadOpInterface102   bool bufferizesToMemoryWrite(Operation *op, OpOperand &opOperand,
103                                const AnalysisState &state) const {
104     return false;
105   }
106 
getAliasingValuesmlir::sparse_tensor::__anonf7ee666a0111::LoadOpInterface107   AliasingValueList getAliasingValues(Operation *op, OpOperand &opOperand,
108                                       const AnalysisState &state) const {
109     return {{op->getOpResult(0), BufferRelation::Equivalent}};
110   }
111 };
112 
113 struct NewOpInterface
114     : public SparseBufferizableOpInterfaceExternalModel<NewOpInterface,
115                                                         sparse_tensor::NewOp> {
resultBufferizesToMemoryWritemlir::sparse_tensor::__anonf7ee666a0111::NewOpInterface116   bool resultBufferizesToMemoryWrite(Operation *op, OpResult opResult,
117                                      const AnalysisState &state) const {
118     // NewOps allocate but do not write.
119     return false;
120   }
121 
bufferizesToAllocationmlir::sparse_tensor::__anonf7ee666a0111::NewOpInterface122   bool bufferizesToAllocation(Operation *op, Value value) const { return true; }
123 };
124 
125 struct AssembleOpInterface
126     : public SparseBufferizableOpInterfaceExternalModel<
127           AssembleOpInterface, sparse_tensor::AssembleOp> {
bufferizesToAllocationmlir::sparse_tensor::__anonf7ee666a0111::AssembleOpInterface128   bool bufferizesToAllocation(Operation *op, Value value) const {
129     // AssembleOp reuses all the buffers instead of allocating new ones
130     return false;
131   }
132 
bufferizesToMemoryReadmlir::sparse_tensor::__anonf7ee666a0111::AssembleOpInterface133   bool bufferizesToMemoryRead(Operation *op, OpOperand &opOperand,
134                               const AnalysisState &state) const {
135     return true;
136   }
137 
bufferizesToMemoryWritemlir::sparse_tensor::__anonf7ee666a0111::AssembleOpInterface138   bool bufferizesToMemoryWrite(Operation *op, OpOperand &opOperand,
139                                const AnalysisState &state) const {
140     return false;
141   }
142 
getAliasingValuesmlir::sparse_tensor::__anonf7ee666a0111::AssembleOpInterface143   AliasingValueList getAliasingValues(Operation *op, OpOperand &opOperand,
144                                       const AnalysisState &state) const {
145     assert(op->getNumResults() == 1);
146     // AssembleOp reuses the input tensors as values/coordinates instead of
147     // creating new ones when packing into a COO format.
148     return {{op->getOpResult(0), BufferRelation::Equivalent}};
149   }
150 
bufferRelationmlir::sparse_tensor::__anonf7ee666a0111::AssembleOpInterface151   BufferRelation bufferRelation(Operation *oo, OpResult opResult,
152                                 const AnalysisState &state) const {
153     return BufferRelation::Unknown;
154   }
155 };
156 
157 struct DisassembleOpInterface
158     : public SparseBufferizableOpInterfaceExternalModel<
159           DisassembleOpInterface, sparse_tensor::DisassembleOp> {
bufferizesToAllocationmlir::sparse_tensor::__anonf7ee666a0111::DisassembleOpInterface160   bool bufferizesToAllocation(Operation *op, Value value) const {
161     // The output buffer is pre-allocated by the user.
162     return false;
163   }
164 
bufferizesToMemoryReadmlir::sparse_tensor::__anonf7ee666a0111::DisassembleOpInterface165   bool bufferizesToMemoryRead(Operation *op, OpOperand &opOperand,
166                               const AnalysisState &state) const {
167     // The first operand is the sparse tensor that we are unpacking.
168     return opOperand.getOperandNumber() == 0;
169   }
170 
bufferizesToMemoryWritemlir::sparse_tensor::__anonf7ee666a0111::DisassembleOpInterface171   bool bufferizesToMemoryWrite(Operation *op, OpOperand &opOperand,
172                                const AnalysisState &state) const {
173     // We write into the output operand.
174     assert(2 * (op->getNumOperands() - 1) == op->getNumResults());
175     return opOperand.getOperandNumber() > 0;
176   }
177 
getAliasingValuesmlir::sparse_tensor::__anonf7ee666a0111::DisassembleOpInterface178   AliasingValueList getAliasingValues(Operation *op, OpOperand &opOperand,
179                                       const AnalysisState &state) const {
180     assert(2 * (op->getNumOperands() - 1) == op->getNumResults());
181 
182     if (opOperand.getOperandNumber() == 0)
183       return {};
184     // We write directly into the output tensors and returns them.
185     return {{op->getResult(opOperand.getOperandNumber() - 1),
186              BufferRelation::Equivalent}};
187   }
188 };
189 
190 struct ForeachOpInterface : public SparseBufferizableOpInterfaceExternalModel<
191                                 ForeachOpInterface, sparse_tensor::ForeachOp> {
bufferizesToMemoryReadmlir::sparse_tensor::__anonf7ee666a0111::ForeachOpInterface192   bool bufferizesToMemoryRead(Operation *op, OpOperand &opOperand,
193                               const AnalysisState &state) const {
194     return true;
195   }
196 
bufferizesToMemoryWritemlir::sparse_tensor::__anonf7ee666a0111::ForeachOpInterface197   bool bufferizesToMemoryWrite(Operation *op, OpOperand &opOperand,
198                                const AnalysisState &state) const {
199     return false;
200   }
201 
getAliasingValuesmlir::sparse_tensor::__anonf7ee666a0111::ForeachOpInterface202   AliasingValueList getAliasingValues(Operation *op, OpOperand &opOperand,
203                                       const AnalysisState &state) const {
204     return {};
205   }
206 
verifyAnalysismlir::sparse_tensor::__anonf7ee666a0111::ForeachOpInterface207   LogicalResult verifyAnalysis(Operation *op,
208                                const AnalysisState &state) const {
209     // A more complex analysis (similar to scf.for) is needed if the op returns
210     // a tensor. That tensor would have to be bufferized (not implemented yet).
211     for (OpResult result : op->getResults()) {
212       if (isa<TensorType>(result.getType()))
213         return op->emitOpError("tensor results are not supported yet");
214     }
215     return success();
216   }
217 };
218 
219 struct NumberOfEntriesOpInterface
220     : public SparseBufferizableOpInterfaceExternalModel<
221           NumberOfEntriesOpInterface, sparse_tensor::NumberOfEntriesOp> {
bufferizesToMemoryReadmlir::sparse_tensor::__anonf7ee666a0111::NumberOfEntriesOpInterface222   bool bufferizesToMemoryRead(Operation *op, OpOperand &opOperand,
223                               const AnalysisState &state) const {
224     return true;
225   }
226 
bufferizesToMemoryWritemlir::sparse_tensor::__anonf7ee666a0111::NumberOfEntriesOpInterface227   bool bufferizesToMemoryWrite(Operation *op, OpOperand &opOperand,
228                                const AnalysisState &state) const {
229     return false;
230   }
231 
getAliasingValuesmlir::sparse_tensor::__anonf7ee666a0111::NumberOfEntriesOpInterface232   AliasingValueList getAliasingValues(Operation *op, OpOperand &opOperand,
233                                       const AnalysisState &state) const {
234     return {};
235   }
236 };
237 
238 struct ToCoordinatesBufferOpInterface
239     : public SparseBufferizableOpInterfaceExternalModel<
240           ToCoordinatesBufferOpInterface,
241           sparse_tensor::ToCoordinatesBufferOp> {
bufferizesToMemoryReadmlir::sparse_tensor::__anonf7ee666a0111::ToCoordinatesBufferOpInterface242   bool bufferizesToMemoryRead(Operation *op, OpOperand &opOperand,
243                               const AnalysisState &state) const {
244     return true;
245   }
246 
bufferizesToMemoryWritemlir::sparse_tensor::__anonf7ee666a0111::ToCoordinatesBufferOpInterface247   bool bufferizesToMemoryWrite(Operation *op, OpOperand &opOperand,
248                                const AnalysisState &state) const {
249     // Potential writes into memory through the result of
250     // `sparse_tensor.coordinates` are not considered.
251     return false;
252   }
253 
getAliasingValuesmlir::sparse_tensor::__anonf7ee666a0111::ToCoordinatesBufferOpInterface254   AliasingValueList getAliasingValues(Operation *op, OpOperand &opOperand,
255                                       const AnalysisState &state) const {
256     return {};
257   }
258 };
259 
260 struct ToCoordinatesOpInterface
261     : public SparseBufferizableOpInterfaceExternalModel<
262           ToCoordinatesOpInterface, sparse_tensor::ToCoordinatesOp> {
bufferizesToMemoryReadmlir::sparse_tensor::__anonf7ee666a0111::ToCoordinatesOpInterface263   bool bufferizesToMemoryRead(Operation *op, OpOperand &opOperand,
264                               const AnalysisState &state) const {
265     return true;
266   }
267 
bufferizesToMemoryWritemlir::sparse_tensor::__anonf7ee666a0111::ToCoordinatesOpInterface268   bool bufferizesToMemoryWrite(Operation *op, OpOperand &opOperand,
269                                const AnalysisState &state) const {
270     // Potential writes into memory through the result of
271     // `sparse_tensor.coordinates` are not considered.
272     return false;
273   }
274 
getAliasingValuesmlir::sparse_tensor::__anonf7ee666a0111::ToCoordinatesOpInterface275   AliasingValueList getAliasingValues(Operation *op, OpOperand &opOperand,
276                                       const AnalysisState &state) const {
277     return {};
278   }
279 };
280 
281 struct ToPositionsOpInterface
282     : public SparseBufferizableOpInterfaceExternalModel<
283           ToPositionsOpInterface, sparse_tensor::ToPositionsOp> {
bufferizesToMemoryReadmlir::sparse_tensor::__anonf7ee666a0111::ToPositionsOpInterface284   bool bufferizesToMemoryRead(Operation *op, OpOperand &opOperand,
285                               const AnalysisState &state) const {
286     return true;
287   }
288 
bufferizesToMemoryWritemlir::sparse_tensor::__anonf7ee666a0111::ToPositionsOpInterface289   bool bufferizesToMemoryWrite(Operation *op, OpOperand &opOperand,
290                                const AnalysisState &state) const {
291     // Potential writes into memory through the result of
292     // `sparse_tensor.positions` are not considered.
293     return false;
294   }
295 
getAliasingValuesmlir::sparse_tensor::__anonf7ee666a0111::ToPositionsOpInterface296   AliasingValueList getAliasingValues(Operation *op, OpOperand &opOperand,
297                                       const AnalysisState &state) const {
298     return {};
299   }
300 };
301 
302 struct ToValuesOpInterface
303     : public SparseBufferizableOpInterfaceExternalModel<
304           ToValuesOpInterface, sparse_tensor::ToValuesOp> {
bufferizesToMemoryReadmlir::sparse_tensor::__anonf7ee666a0111::ToValuesOpInterface305   bool bufferizesToMemoryRead(Operation *op, OpOperand &opOperand,
306                               const AnalysisState &state) const {
307     return true;
308   }
309 
bufferizesToMemoryWritemlir::sparse_tensor::__anonf7ee666a0111::ToValuesOpInterface310   bool bufferizesToMemoryWrite(Operation *op, OpOperand &opOperand,
311                                const AnalysisState &state) const {
312     // Potential writes into memory through the result of sparse_tensor.values
313     // are not considered.
314     return false;
315   }
316 
getAliasingValuesmlir::sparse_tensor::__anonf7ee666a0111::ToValuesOpInterface317   AliasingValueList getAliasingValues(Operation *op, OpOperand &opOperand,
318                                       const AnalysisState &state) const {
319     return {};
320   }
321 };
322 
323 } // namespace
324 } // namespace sparse_tensor
325 } // namespace mlir
326 
registerBufferizableOpInterfaceExternalModels(DialectRegistry & registry)327 void mlir::sparse_tensor::registerBufferizableOpInterfaceExternalModels(
328     DialectRegistry &registry) {
329   registry.addExtension(+[](MLIRContext *ctx,
330                             sparse_tensor::SparseTensorDialect *dialect) {
331     sparse_tensor::ConcatenateOp::attachInterface<ConcatenateOpInterface>(*ctx);
332     sparse_tensor::ConvertOp::attachInterface<ConvertOpInterface>(*ctx);
333     sparse_tensor::LoadOp::attachInterface<LoadOpInterface>(*ctx);
334     sparse_tensor::NewOp::attachInterface<NewOpInterface>(*ctx);
335     sparse_tensor::NumberOfEntriesOp::attachInterface<
336         NumberOfEntriesOpInterface>(*ctx);
337     sparse_tensor::AssembleOp::attachInterface<AssembleOpInterface>(*ctx);
338     sparse_tensor::DisassembleOp::attachInterface<DisassembleOpInterface>(*ctx);
339     sparse_tensor::ForeachOp::attachInterface<ForeachOpInterface>(*ctx);
340     sparse_tensor::ToCoordinatesBufferOp::attachInterface<
341         ToCoordinatesBufferOpInterface>(*ctx);
342     sparse_tensor::ToCoordinatesOp::attachInterface<ToCoordinatesOpInterface>(
343         *ctx);
344     sparse_tensor::ToPositionsOp::attachInterface<ToPositionsOpInterface>(*ctx);
345     sparse_tensor::ToValuesOp::attachInterface<ToValuesOpInterface>(*ctx);
346   });
347 }
348