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 ®istry) {
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