/// bufferized or not.
bool bufferizeFunctionBoundaries = false;
+ /// The default memory space that should be used when it cannot be inferred
+ /// from the context. If no default memory space is specified, bufferization
+ /// fails when the memory space cannot be inferred at any point.
+ Optional<unsigned> defaultMemorySpace = 0;
+
/// Certain ops have aliasing OpOperand/OpResult invariants (e.g., scf.for).
/// If this flag is set to `false`, those invariants are no longer enforced
/// with buffer copies.
conflicts that would have been introduced by the in-place bufferization of
another op.
+ The optional `memory_space` attribute specifies the memory space when
+ bufferizing this op. If `memory_space` is not specified, the default memory
+ space is used during bufferization.
+
Both dense and sparse tensor types are supported. The result of a
`bufferization.alloc_tensor` is a tensor value that can be used like any
other tensor value. In practice, it is often used as the "out" operand of
}];
let arguments = (ins Variadic<Index>:$dynamic_sizes,
- Optional<AnyTensor>:$copy);
+ Optional<AnyTensor>:$copy,
+ OptionalAttr<UI64Attr>:$memory_space);
let results = (outs AnyTensor:$result);
}];
let builders = [
- // Build an op without `copy` operand.
+ // Build an op without `copy` or `memory_space`.
OpBuilder<(ins "RankedTensorType":$type, "ValueRange":$dynamicSizes)>,
+
+ // Build an op without `memory_space`.
+ OpBuilder<(ins "RankedTensorType":$type, "ValueRange":$dynamicSizes,
+ "Value":$copy)>,
];
let hasCanonicalizer = 1;
"function-boundary-type-conversion", "std::string",
/*default=*/"\"infer-layout-map\"",
"Controls layout maps when bufferizing function signatures.">,
+ Option<"mustInferMemorySpace", "must-infer-memory-space", "bool",
+ /*default=*/"false",
+ "The memory space of an memref types must always be inferred. If "
+ "unset, a default memory space of 0 is used otherwise.">,
Option<"testAnalysisOnly", "test-analysis-only", "bool",
/*default=*/"false",
"Test only: Only run inplaceability analysis and annotate IR">,
return success();
}
- // Create buffer allocation.
+ // Get "copy" buffer.
Value copyBuffer;
if (getCopy())
copyBuffer = getBuffer(rewriter, getCopy(), options);
+
+ // Compute memory space of this allocation.
+ unsigned memorySpace;
+ if (getMemorySpace().hasValue()) {
+ memorySpace = *getMemorySpace();
+ } else if (options.defaultMemorySpace.hasValue()) {
+ memorySpace = *options.defaultMemorySpace;
+ } else {
+ return op->emitError("could not infer memory space");
+ }
+
+ // Create memory allocation.
auto allocType =
- MemRefType::get(getType().getShape(), getType().getElementType());
+ MemRefType::get(getType().getShape(), getType().getElementType(),
+ AffineMap(), memorySpace);
SmallVector<Value> dynamicDims = getDynamicSizes();
if (getCopy()) {
assert(dynamicDims.empty() && "expected either `copy` or `dynamicDims`");
void AllocTensorOp::build(OpBuilder &builder, OperationState &result,
RankedTensorType type, ValueRange dynamicSizes) {
- build(builder, result, type, dynamicSizes, /*copy=*/Value());
+ build(builder, result, type, dynamicSizes, /*copy=*/Value(),
+ /*memory_space=*/BoolAttr());
+}
+
+void AllocTensorOp::build(OpBuilder &builder, OperationState &result,
+ RankedTensorType type, ValueRange dynamicSizes,
+ Value copy) {
+ build(builder, result, type, dynamicSizes, copy, /*memory_space=*/BoolAttr());
}
namespace {
opt.createDeallocs = createDeallocs;
opt.functionBoundaryTypeConversion =
parseLayoutMapOption(functionBoundaryTypeConversion);
+ if (mustInferMemorySpace)
+ opt.defaultMemorySpace = None;
opt.printConflicts = printConflicts;
opt.testAnalysisOnly = testAnalysisOnly;
opt.bufferizeFunctionBoundaries = bufferizeFunctionBoundaries;
--- /dev/null
+// RUN: mlir-opt %s -allow-unregistered-dialect -one-shot-bufferize="must-infer-memory-space" -split-input-file -verify-diagnostics
+
+func.func @alloc_tensor_without_memory_space() -> tensor<10xf32> {
+ // expected-error @+2 {{could not infer memory space}}
+ // expected-error @+1 {{failed to bufferize op}}
+ %0 = bufferization.alloc_tensor() : tensor<10xf32>
+ return %0 : tensor<10xf32>
+}
return %0 : tensor<5xf32>
}
+// -----
+
+// CHECK-LABEL: func @alloc_tensor_with_memory_space()
+func.func @alloc_tensor_with_memory_space() -> tensor<5xf32> {
+ // CHECK: %[[alloc:.*]] = memref.alloc() {{.*}} : memref<5xf32, 1>
+ %0 = bufferization.alloc_tensor() {memory_space = 1 : ui64} : tensor<5xf32>
+ // CHECK: %[[r:.*]] = bufferization.to_tensor %[[alloc]]
+ // CHECK: memref.dealloc %[[alloc]]
+ // CHECK: return %[[r]]
+ return %0 : tensor<5xf32>
+}
return
}
+// -----
+
+func.func @alloc_tensor_invalid_memory_space_attr(%sz: index) {
+ // expected-error @+1{{'bufferization.alloc_tensor' op attribute 'memory_space' failed to satisfy constraint: 64-bit unsigned integer attribute}}
+ %0 = bufferization.alloc_tensor(%sz) {memory_space = "foo"} : tensor<?xf32>
+ return
+}
+