-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[mlir][tensor] Refine the semantics of createPadHighOp
#109667
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[mlir][tensor] Refine the semantics of createPadHighOp
#109667
Conversation
@llvm/pr-subscribers-mlir Author: Andrzej Warzyński (banach-space) ChangesRefine > // If The actual padding width should be set to: This PR is an attempt to clarify the semantics surrounding dynamic (*) Scalable as in Arm's Scalable Vector Extension (SVE) Full diff: https://github.com/llvm/llvm-project/pull/109667.diff 2 Files Affected:
diff --git a/mlir/include/mlir/Dialect/Tensor/Utils/Utils.h b/mlir/include/mlir/Dialect/Tensor/Utils/Utils.h
index 84d06d456bb689..e63749eb384316 100644
--- a/mlir/include/mlir/Dialect/Tensor/Utils/Utils.h
+++ b/mlir/include/mlir/Dialect/Tensor/Utils/Utils.h
@@ -14,10 +14,10 @@
namespace mlir {
namespace tensor {
-// Return a PadOp that pads `source` to `type` size where the static
-// sizes are assumed to be greater than the dynamic sizes. If `type` has dynamic
-// dimensions the padding width is set to zero. The op performs "high" padding
-// (i.e. it adds trailing padding values until the desired size is met).
+// Return a PadOp that pads `source` to `type` size. Output sizes (from `type`)
+// are assumed to be static and greater than the potentially dynamic input sizes
+// (from `source). The op performs "high" padding (i.e. it adds trailing padding
+// values until the desired size is met).
PadOp createPadHighOp(RankedTensorType type, Value source, Value pad,
bool nofold, Location loc, OpBuilder &builder);
diff --git a/mlir/lib/Dialect/Tensor/Utils/Utils.cpp b/mlir/lib/Dialect/Tensor/Utils/Utils.cpp
index a0d8a08fc6ba47..c8e0c05bfb2b87 100644
--- a/mlir/lib/Dialect/Tensor/Utils/Utils.cpp
+++ b/mlir/lib/Dialect/Tensor/Utils/Utils.cpp
@@ -24,12 +24,16 @@ using namespace mlir::tensor;
PadOp mlir::tensor::createPadHighOp(RankedTensorType type, Value source,
Value pad, bool nofold, Location loc,
OpBuilder &b) {
+
+ assert(!ShapedType::isDynamicShape(type.getShape()) &&
+ "The output type is dynamic - that's not supported ATM.");
+
+ // Init "low" and "high" padding values ("low" is kept as is, "high" is
+ // computed below).
SmallVector<OpFoldResult> low(type.getRank(), b.getIndexAttr(0));
SmallVector<OpFoldResult> high(type.getRank(), b.getIndexAttr(0));
+
for (const auto &en : enumerate(type.getShape())) {
- // Pad only the static dimensions of the result tensor type.
- if (ShapedType::isDynamic(en.value()))
- continue;
// Compute the padding width.
AffineExpr d0;
bindDims(b.getContext(), d0);
|
@llvm/pr-subscribers-mlir-tensor Author: Andrzej Warzyński (banach-space) ChangesRefine > // If The actual padding width should be set to: This PR is an attempt to clarify the semantics surrounding dynamic (*) Scalable as in Arm's Scalable Vector Extension (SVE) Full diff: https://github.com/llvm/llvm-project/pull/109667.diff 2 Files Affected:
diff --git a/mlir/include/mlir/Dialect/Tensor/Utils/Utils.h b/mlir/include/mlir/Dialect/Tensor/Utils/Utils.h
index 84d06d456bb689..e63749eb384316 100644
--- a/mlir/include/mlir/Dialect/Tensor/Utils/Utils.h
+++ b/mlir/include/mlir/Dialect/Tensor/Utils/Utils.h
@@ -14,10 +14,10 @@
namespace mlir {
namespace tensor {
-// Return a PadOp that pads `source` to `type` size where the static
-// sizes are assumed to be greater than the dynamic sizes. If `type` has dynamic
-// dimensions the padding width is set to zero. The op performs "high" padding
-// (i.e. it adds trailing padding values until the desired size is met).
+// Return a PadOp that pads `source` to `type` size. Output sizes (from `type`)
+// are assumed to be static and greater than the potentially dynamic input sizes
+// (from `source). The op performs "high" padding (i.e. it adds trailing padding
+// values until the desired size is met).
PadOp createPadHighOp(RankedTensorType type, Value source, Value pad,
bool nofold, Location loc, OpBuilder &builder);
diff --git a/mlir/lib/Dialect/Tensor/Utils/Utils.cpp b/mlir/lib/Dialect/Tensor/Utils/Utils.cpp
index a0d8a08fc6ba47..c8e0c05bfb2b87 100644
--- a/mlir/lib/Dialect/Tensor/Utils/Utils.cpp
+++ b/mlir/lib/Dialect/Tensor/Utils/Utils.cpp
@@ -24,12 +24,16 @@ using namespace mlir::tensor;
PadOp mlir::tensor::createPadHighOp(RankedTensorType type, Value source,
Value pad, bool nofold, Location loc,
OpBuilder &b) {
+
+ assert(!ShapedType::isDynamicShape(type.getShape()) &&
+ "The output type is dynamic - that's not supported ATM.");
+
+ // Init "low" and "high" padding values ("low" is kept as is, "high" is
+ // computed below).
SmallVector<OpFoldResult> low(type.getRank(), b.getIndexAttr(0));
SmallVector<OpFoldResult> high(type.getRank(), b.getIndexAttr(0));
+
for (const auto &en : enumerate(type.getShape())) {
- // Pad only the static dimensions of the result tensor type.
- if (ShapedType::isDynamic(en.value()))
- continue;
// Compute the padding width.
AffineExpr d0;
bindDims(b.getContext(), d0);
|
@@ -24,12 +24,16 @@ using namespace mlir::tensor; | |||
PadOp mlir::tensor::createPadHighOp(RankedTensorType type, Value source, | |||
Value pad, bool nofold, Location loc, | |||
OpBuilder &b) { | |||
|
|||
assert(!ShapedType::isDynamicShape(type.getShape()) && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead cant we make the semantics here that the dynamic shapes of the output are same as the dynamic shapes of the input?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead cant we make the semantics here that the dynamic shapes of the output are same as the dynamic shapes of the input?
How would we verify that these are indeed identical?
Also, what should happen when the input is static, and the output is dynamic? This is one specific example that I have in mind right now:
func.func @simple_pad_and_pack_scalable(%input: tensor<5x1xf32>, %output: tensor<1x1x?x2xf32>, %pad: f32) -> tensor<1x1x?x2xf32> {
%c8 = arith.constant 8 : index
%vscale = vector.vscale
%c8_vscale = arith.muli %vscale, %c8 : index
%0 = tensor.pack %input padding_value(%pad : f32) inner_dims_pos = [0, 1] inner_tiles = [%c8_vscale, 2] into %output : tensor<5x1xf32> -> tensor<1x1x?x2xf32>
return %0 : tensor<1x1x?x2xf32>
}
That's based on the example from #109815. Overall, there's quite a few cases to consider (and to test):
- input static, output static (easy)
- input dynamic, output static (tricky)
- input static, output dynamic (tricky)
- input dynamic, output dynamic (tricky)
I'd rather restrict the semantics to begin with, and then gradually relax them when there's need (e.g. for #109815). No strong opinion though, whatever works and is correct :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead cant we make the semantics here that the dynamic shapes of the output are same as the dynamic shapes of the input?
How would we verify that these are indeed identical?
I think its a matter of defining it. You can say that the createPadHighOp
will effectively do low and high for dynamic shape to be 0. So for dynamic dims the input dim extent and output dim extent would be the same.
Also, what should happen when the input is static, and the output is dynamic? This is one specific example that I have in mind right now:
func.func @simple_pad_and_pack_scalable(%input: tensor<5x1xf32>, %output: tensor<1x1x?x2xf32>, %pad: f32) -> tensor<1x1x?x2xf32> { %c8 = arith.constant 8 : index %vscale = vector.vscale %c8_vscale = arith.muli %vscale, %c8 : index %0 = tensor.pack %input padding_value(%pad : f32) inner_dims_pos = [0, 1] inner_tiles = [%c8_vscale, 2] into %output : tensor<5x1xf32> -> tensor<1x1x?x2xf32> return %0 : tensor<1x1x?x2xf32> }
Thats a pack operation? I am not following your point.
That's based on the example from #109815. Overall, there's quite a few cases to consider (and to test):
- input static, output static (easy)
+1- input dynamic, output static (tricky)
You can compute the high padding needed as output dim - input dim, and the high padding can be dynamic.- input static, output dynamic (tricky)
- input dynamic, output dynamic (tricky)
I'd rather restrict the semantics to begin with, and then gradually relax them when there's need (e.g. for #109815). No strong opinion though, whatever works and is correct :)
Ok, that makes sense. Let me re-review.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe dont assert and instead return an error?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adding an error complicates things for the clients of this API, and I will be relaxing this shortly. Let me leave a TODO for myself and I'll follow-up shortly.
Refine `createPadHighOp` so that the output tensor is required to be statically shaped. This is to prevent the current behaviour, which is incorrect: > // If `type` has dynamic dimensions the padding width is set to zero. The actual padding width should be set to: `%new_dim - %old_dim`, where %new_dim` and `%old_dim` are defined via e.g. `tensor.dim` Op applied to output and input tensors, respectively. This PR is an attempt to clarify the semantics surrounding dynamic shapes in preparation for adding support for scalable vectors to the pack/unpack logic in Tensor/Linalg (dynamic shapes is what we use to model scalable (*) sizes at the Tensor/MemRef level). (*) Scalable as in Arm's Scalable Vector Extension (SVE)
84fcd1a
to
7a5a875
Compare
@@ -24,12 +24,16 @@ using namespace mlir::tensor; | |||
PadOp mlir::tensor::createPadHighOp(RankedTensorType type, Value source, | |||
Value pad, bool nofold, Location loc, | |||
OpBuilder &b) { | |||
|
|||
assert(!ShapedType::isDynamicShape(type.getShape()) && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe dont assert and instead return an error?
This breaks the behavior of linalg::padAndHoistLinalgOp, which calls makeComposedPadHighOp here. The implementation of makeComposedPadHighOp relies on the behavior to create valid pad ops. In the transform script, it asks for padding Can we revert it until it is fixed? To repro: module {
func.func @batch_matmul_f16(%arg0: tensor<1x?x1281xf16>, %arg1: tensor<1x1281x?xf16>, %arg2: tensor<1x?x?xf16>) -> tensor<1x?x?xf16> {
%cst = arith.constant 0.000000e+00 : f16
%c0 = arith.constant 0 : index
%0 = linalg.fill ins(%cst : f16) outs(%arg2 : tensor<1x?x?xf16>) -> tensor<1x?x?xf16>
%1 = linalg.batch_matmul ins(%arg0, %arg1 : tensor<1x?x1281xf16>, tensor<1x1281x?xf16>) outs(%0 : tensor<1x?x?xf16>) -> tensor<1x?x?xf16>
return %1 : tensor<1x?x?xf16>
}
module attributes {transform.with_named_sequence} {
transform.named_sequence @__transform_main(%arg0: !transform.any_op {transform.readonly}) {
%0 = transform.structured.match ops{["linalg.batch_matmul"]} in %arg0 : (!transform.any_op) -> !transform.any_op
%padded, %pad, %copy = transform.structured.pad %0 {pack_paddings = [1, 1], padding_dimensions = [3], padding_values = [0.000000e+00 : f16, 0.000000e+00 : f16, 0.000000e+00 : f16]} : (!transform.any_op) -> (!transform.any_op, !transform.any_op, !transform.op<"bufferization.materialize_in_destination">)
%1 = transform.num_associations %copy : (!transform.op<"bufferization.materialize_in_destination">) -> !transform.param<i64>
transform.debug.emit_param_as_remark %1 : !transform.param<i64>
transform.yield
}
}
} |
Refine `createPadHighOp` so that the output tensor is required to be statically shaped. This is to prevent the current behaviour, which is incorrect: > // If `type` has dynamic dimensions the padding width is set to zero. The actual padding width should be set to: `%new_dim - %old_dim`, where %new_dim` and `%old_dim` are defined via e.g. `tensor.dim` Op applied to output and input tensors, respectively. This PR is an attempt to clarify the semantics surrounding dynamic shapes in preparation for adding support for scalable vectors to the pack/unpack logic in Tensor/Linalg (dynamic shapes is what we use to model scalable (*) sizes at the Tensor/MemRef level). (*) Scalable as in Arm's Scalable Vector Extension (SVE)
Allow multiple dynamic dims and remove the dependency on llvm#109667
Allow multiple dynamic dims and remove the dependency on llvm#109667
Refine
createPadHighOp
so that the output tensor is required to bestatically shaped. This is to prevent the current behaviour, which is
incorrect:
The actual padding width should be set to:
%new_dim - %old_dim
, where%new_dim
and
%old_dimare defined via e.g.
tensor.dim` Op applied tooutput and input tensors, respectively.
This PR is an attempt to clarify the semantics surrounding dynamic
shapes in preparation for adding support for scalable vectors to the
pack/unpack logic in Tensor/Linalg (dynamic shapes is what we use to
model scalable (*) sizes at the Tensor/MemRef level).
(*) Scalable as in Arm's Scalable Vector Extension (SVE)