-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[MLIR][Linalg] Fix linalg crash during elementwise op fusion #117667
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
Conversation
@llvm/pr-subscribers-mlir @llvm/pr-subscribers-mlir-core Author: Ian Wood (IanWood1) Changes
Full diff: https://github.com/llvm/llvm-project/pull/117667.diff 4 Files Affected:
diff --git a/mlir/include/mlir/IR/AffineMap.h b/mlir/include/mlir/IR/AffineMap.h
index e30950bbf292d6..4cfc538a2fe72f 100644
--- a/mlir/include/mlir/IR/AffineMap.h
+++ b/mlir/include/mlir/IR/AffineMap.h
@@ -595,7 +595,10 @@ AffineMap inverseAndBroadcastProjectedPermutation(AffineMap map);
/// potentially empty maps. Assumes each of the underlying map has 0 symbols.
/// The resulting map has a number of dims equal to the max of `maps`' dims and
/// the concatenated results as its results.
-/// Returns an empty map if all input `maps` are empty.
+///
+/// This method asserts when `maps` is empty.
+/// TODO: this should return an empty map when `maps` is empty but there is no
+/// way to get the MLIRContext needed to construct it.
///
/// Example:
/// When applied to the following list of 3 affine maps,
diff --git a/mlir/lib/Dialect/Linalg/Transforms/ElementwiseOpFusion.cpp b/mlir/lib/Dialect/Linalg/Transforms/ElementwiseOpFusion.cpp
index a934e47794051c..6ddea53dfb997a 100644
--- a/mlir/lib/Dialect/Linalg/Transforms/ElementwiseOpFusion.cpp
+++ b/mlir/lib/Dialect/Linalg/Transforms/ElementwiseOpFusion.cpp
@@ -93,7 +93,8 @@ static bool isOpOperandCanBeDroppedAfterFusedLinalgs(
// the bounds of the op can be still computed after dropping the selected
// operand. inversePermutation returns an empty AffineMap in case the
// concatanated indexing maps are not invertible.
- return inversePermutation(concatAffineMaps(indexingMaps)) != AffineMap();
+ return !indexingMaps.empty() &&
+ inversePermutation(concatAffineMaps(indexingMaps)) != AffineMap();
}
/// Returns a set of indices of the producer's results which would
diff --git a/mlir/lib/IR/AffineMap.cpp b/mlir/lib/IR/AffineMap.cpp
index ea3c0723b07759..8056058511ebe6 100644
--- a/mlir/lib/IR/AffineMap.cpp
+++ b/mlir/lib/IR/AffineMap.cpp
@@ -834,6 +834,7 @@ AffineMap mlir::inverseAndBroadcastProjectedPermutation(AffineMap map) {
}
AffineMap mlir::concatAffineMaps(ArrayRef<AffineMap> maps) {
+ assert(maps.size());
unsigned numResults = 0, numDims = 0, numSymbols = 0;
for (auto m : maps)
numResults += m.getNumResults();
diff --git a/mlir/test/Dialect/Linalg/fusion-elementwise.mlir b/mlir/test/Dialect/Linalg/fusion-elementwise.mlir
index 8131e4054cc6b0..bd9977f1410b91 100644
--- a/mlir/test/Dialect/Linalg/fusion-elementwise.mlir
+++ b/mlir/test/Dialect/Linalg/fusion-elementwise.mlir
@@ -28,3 +28,34 @@ func.func @drop_unused_producer_result(%arg0 : tensor<?x?xf32>,
// CHECK: %[[FUSED_OP:.+]] = linalg.generic
// CHECK-SAME: ins(%[[ARG0]], %[[ARG1]] :
// CHECK: return %[[FUSED_OP]]
+
+// -----
+
+#map = affine_map<(d0) -> (d0)>
+func.func @handle_unused_operands(%arg0: tensor<8xf32>, %arg1: tensor<8xf32>) -> tensor<8xf32> {
+ %cst_0 = arith.constant 0.000000e+00 : f32
+ %cst_1 = arith.constant 1.000000e+00 : f32
+ %0:2 = linalg.generic {indexing_maps = [#map, #map], iterator_types = ["parallel"]} outs(%arg0, %arg1 : tensor<8xf32>, tensor<8xf32>) {
+ ^bb0(%out: f32, %out_2: f32):
+ %1 = linalg.index 0 : index
+ %2 = arith.index_cast %1 : index to i64
+ %3 = arith.sitofp %2 : i64 to f32
+ %4 = arith.divf %3, %cst_0 : f32
+ linalg.yield %3, %4 : f32, f32
+ } -> (tensor<8xf32>, tensor<8xf32>)
+ linalg.generic {indexing_maps = [#map], iterator_types = ["parallel"]} ins(%0#1 : tensor<8xf32>) {
+ ^bb0(%in: f32):
+ %2 = arith.cmpf one, %in, %cst_1 : f32
+ cf.assert %2, "Side effect op"
+ linalg.yield
+ }
+ func.return %arg1 : tensor<8xf32>
+}
+
+// CHECK-LABEL: func @handle_unused_operands
+// CHECK-SAME: %[[ARG0:[a-zA-Z0-9]+]]: tensor<8xf32>
+// CHECK-SAME: %[[ARG1:[a-zA-Z0-9]+]]: tensor<8xf32>
+// CHECK: %[[EMPTY:.+]] = tensor.empty() : tensor<8xf32>
+// CHECK: %[[FUSED_OP:.+]] = linalg.generic
+// CHECK-SAME: outs(%[[EMPTY]] :
+// CHECK-NOT: linalg.generic
|
Signed-off-by: Ian Wood <[email protected]>
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.
LGTM
Argh, ignore me, sorry 🤦🏻 |
Signed-off-by: Ian Wood <[email protected]>
Co-authored-by: Quinn Dawkins <[email protected]>
isOpOperandCanBeDroppedAfterFusedLinalgs
crashes whenindexingMaps
is empty. This can occur whenproducer
only has DPS init operands andconsumer
only has a single DPS input operand (all operands are ignored and nothing gets added toindexingMaps
). This is becauseconcatAffineMaps
wasn't handling the maps being empty properly.Similar to
canOpOperandsBeDroppedImpl
, I added an early return when the maps are of size zero. Additionally,concatAffineMaps
's declaration comment says it returns an empty map whenmaps
is empty but it has no way to get theMLIRContext
needed to construct the empty affine map when the array is empty. So, I changed this to take the context.NOTE: concatAffineMaps now takes an MLIRContext to be able to construct an empty map in the case where
maps
is empty.