[Mlir-commits] [mlir] 7e0fc20 - [mlir][linalg] Fix splat fold crash on non-TypedAttr element types (#218012)

llvmlistbot at llvm.org llvmlistbot at llvm.org
Mon Aug 24 07:11:54 PDT 2026


Author: vabridgers
Date: 2026-08-24T09:11:50-05:00
New Revision: 7e0fc20933f48be89b0f82046a1cfb1ab53ee37a

URL: https://github.com/llvm/llvm-project/commit/7e0fc20933f48be89b0f82046a1cfb1ab53ee37a
DIFF: https://github.com/llvm/llvm-project/commit/7e0fc20933f48be89b0f82046a1cfb1ab53ee37a.diff

LOG: [mlir][linalg] Fix splat fold crash on non-TypedAttr element types (#218012)

`getScalarConstantAttrFromDenseSplat` returns
`getSplatValue<TypedAttr>()`, which for a derived attribute type is an
unchecked `llvm::cast`. Complex element types store their splat as an
`ArrayAttr` of two values, and `ArrayAttr` does not implement
`TypedAttr`, so the cast asserts on

    %cst = arith.constant dense<(1.0,2.0)> : tensor<3xcomplex<f32>>
    %0   = linalg.broadcast ins(%cst : tensor<3xcomplex<f32>>)
            outs(%init : tensor<2x3xcomplex<f32>>) dimensions = [0]

under `mlir-opt --canonicalize`. Integer and float splats yield
`IntegerAttr`/`FloatAttr`, which are `TypedAttr`, so only non-scalar
element types are affected.

Guard with a `dyn_cast` and decline the fold when the splat value has no
`TypedAttr` representation. The helper is shared by the broadcast
pattern added in PR 195980 and the transpose patterns added in PR
195991, so both call sites are fixed; a test is added for each, as the
transpose case was not previously covered.

Added: 
    

Modified: 
    mlir/lib/Dialect/Linalg/IR/LinalgOps.cpp
    mlir/test/Dialect/Linalg/canonicalize.mlir

Removed: 
    


################################################################################
diff  --git a/mlir/lib/Dialect/Linalg/IR/LinalgOps.cpp b/mlir/lib/Dialect/Linalg/IR/LinalgOps.cpp
index 170e1edf8a55d..af670b8d4fae7 100644
--- a/mlir/lib/Dialect/Linalg/IR/LinalgOps.cpp
+++ b/mlir/lib/Dialect/Linalg/IR/LinalgOps.cpp
@@ -98,7 +98,13 @@ getScalarConstantAttrFromDenseSplat(Value input) {
   if (!splatAttr || !splatAttr.isSplat())
     return std::nullopt;
 
-  return splatAttr.getSplatValue<TypedAttr>();
+  // Not every element type has a TypedAttr splat value: a complex splat, for
+  // one, is an ArrayAttr. Decline the fold instead of asserting in the cast.
+  auto splatValue = dyn_cast<TypedAttr>(splatAttr.getSplatValue<Attribute>());
+  if (!splatValue)
+    return std::nullopt;
+
+  return splatValue;
 }
 
 //===----------------------------------------------------------------------===//

diff  --git a/mlir/test/Dialect/Linalg/canonicalize.mlir b/mlir/test/Dialect/Linalg/canonicalize.mlir
index bb11ce0d4dfb8..3fe824634b18f 100644
--- a/mlir/test/Dialect/Linalg/canonicalize.mlir
+++ b/mlir/test/Dialect/Linalg/canonicalize.mlir
@@ -1219,6 +1219,23 @@ func.func @broadcast_non_splat_constant(%init: tensor<2x3xf32>) -> tensor<2x3xf3
   return %0 : tensor<2x3xf32>
 }
 
+
+// -----
+
+// A splat of a complex element type yields an ArrayAttr, which is not a
+// TypedAttr, so the fold must decline instead of asserting in the cast.
+// CHECK-LABEL: @broadcast_splat_constant_complex
+//       CHECK:   %[[BROADCAST:.+]] = linalg.broadcast
+//       CHECK:   return %[[BROADCAST]] : tensor<2x3xcomplex<f32>>
+func.func @broadcast_splat_constant_complex(%init: tensor<2x3xcomplex<f32>>)
+    -> tensor<2x3xcomplex<f32>> {
+  %cst = arith.constant dense<(1.000000e+00,2.000000e+00)> : tensor<3xcomplex<f32>>
+  %0 = linalg.broadcast
+      ins(%cst: tensor<3xcomplex<f32>>)
+      outs(%init: tensor<2x3xcomplex<f32>>)
+      dimensions = [0]
+  return %0 : tensor<2x3xcomplex<f32>>
+}
 // -----
 
 // CHECK-LABEL: @broadcast_broadcast_fold
@@ -1350,6 +1367,23 @@ func.func @transpose_non_splat_constant(%init: tensor<3x2xf32>) -> tensor<3x2xf3
   func.return %transpose : tensor<3x2xf32>
 }
 
+
+// -----
+
+// A splat of a complex element type yields an ArrayAttr, which is not a
+// TypedAttr, so the fold must decline instead of asserting in the cast.
+// CHECK-LABEL: @transpose_splat_constant_complex
+//       CHECK:   %[[TRANSPOSE:.+]] = linalg.transpose
+//       CHECK:   return %[[TRANSPOSE]] : tensor<3x2xcomplex<f32>>
+func.func @transpose_splat_constant_complex(%init: tensor<3x2xcomplex<f32>>)
+    -> tensor<3x2xcomplex<f32>> {
+  %cst = arith.constant dense<(1.000000e+00,2.000000e+00)> : tensor<2x3xcomplex<f32>>
+  %transpose = linalg.transpose
+      ins(%cst: tensor<2x3xcomplex<f32>>)
+      outs(%init: tensor<3x2xcomplex<f32>>)
+      permutation = [1, 0]
+  func.return %transpose : tensor<3x2xcomplex<f32>>
+}
 // -----
 
 func.func @transpose_transpose_cancel(%input: tensor<5x4x3xf32>,


        


More information about the Mlir-commits mailing list