[Mlir-commits] [llvm] [mlir] [clang-tidy] pro-type-vararg: Allow type-generic builtins in variadic functions #179845 (PR #179896)
Saksham Singhal
llvmlistbot at llvm.org
Thu Feb 5 05:16:56 PST 2026
https://github.com/SakshamSinghal20 updated https://github.com/llvm/llvm-project/pull/179896
>From 0b3f593dfbf4ecdefcf2455dc83783c0c1731cb8 Mon Sep 17 00:00:00 2001
From: SakshamSinghal20 <sakshamsinghal2020 at gmail.com>
Date: Thu, 5 Feb 2026 09:29:19 +0000
Subject: [PATCH 1/2] Fix(llvm): resolve issue #179845 with minimal logic
change
---
implementation_plan.md | 81 +++++++++++++++++++++++
mlir/lib/Dialect/Shape/IR/Shape.cpp | 23 ++++---
mlir/test/Dialect/Shape/canonicalize.mlir | 25 +++++++
3 files changed, 118 insertions(+), 11 deletions(-)
create mode 100644 implementation_plan.md
diff --git a/implementation_plan.md b/implementation_plan.md
new file mode 100644
index 0000000000000..eb1af24bd791b
--- /dev/null
+++ b/implementation_plan.md
@@ -0,0 +1,81 @@
+# Implementation Plan - Fix mlir-opt Crash in Shape Dialect
+
+## Problem Analysis
+The `mlir-opt` tool crashes with an assertion failure in `llvm::cast` during the `--canonicalize` pass. This occurs when `shape.broadcast` or other shape operations consume a `ub.poison` value (or any attribute that is not a `DenseIntElementsAttr`). The folding logic assumes that if an operand is not null, it must be a `DenseIntElementsAttr`, leading to an invalid cast.
+
+**Affected File:** `mlir/lib/Dialect/Shape/IR/Shape.cpp`
+
+**Locations:**
+1. `BroadcastOp::fold` (lines ~647-681)
+2. `hasAtMostSingleNonScalar` helper function (lines ~978-988)
+3. `CstrBroadcastableOp::fold` (lines ~990-1023)
+
+## Proposed Changes
+
+### 1. Update `BroadcastOp::fold`
+Modify the loop that iterates over shapes. Instead of just checking `if (!next)`, check if `next` can be cast to `DenseIntElementsAttr`.
+
+**Current Code:**
+```cpp
+if (!adaptor.getShapes().front())
+ return nullptr;
+// ...
+for (auto next : adaptor.getShapes().drop_front()) {
+ if (!next)
+ return nullptr;
+ // cast ...
+}
+```
+
+**New Code:**
+```cpp
+auto firstAttr = llvm::dyn_cast_if_present<DenseIntElementsAttr>(adaptor.getShapes().front());
+if (!firstAttr)
+ return nullptr;
+// ...
+for (auto next : adaptor.getShapes().drop_front()) {
+ auto nextAttr = llvm::dyn_cast_if_present<DenseIntElementsAttr>(next);
+ if (!nextAttr)
+ return nullptr;
+ // use nextAttr ...
+}
+```
+
+### 2. Update `hasAtMostSingleNonScalar`
+Add a check for `DenseIntElementsAttr` before accessing `getNumElements()`.
+
+**Current Code:**
+```cpp
+if (!a || llvm::cast<DenseIntElementsAttr>(a).getNumElements() != 0) {
+```
+
+**New Code:**
+```cpp
+auto denseAttr = llvm::dyn_cast_if_present<DenseIntElementsAttr>(a);
+if (!denseAttr || denseAttr.getNumElements() != 0) {
+```
+
+### 3. Update `CstrBroadcastableOp::fold`
+Similar to `BroadcastOp`, ensure `dyn_cast` succeeds before accessing values.
+
+**Current Code:**
+```cpp
+if (!operand)
+ return false;
+extents.push_back(llvm::to_vector<6>(
+ llvm::cast<DenseIntElementsAttr>(operand).getValues<int64_t>()));
+```
+
+**New Code:**
+```cpp
+auto denseAttr = llvm::dyn_cast_if_present<DenseIntElementsAttr>(operand);
+if (!denseAttr)
+ return false;
+extents.push_back(llvm::to_vector<6>(
+ denseAttr.getValues<int64_t>()));
+```
+
+## Validation Plan
+1. **Build:** Run `ninja mlir-opt` (or `ninja check-mlir-shape` if available targets).
+2. **Reproduction:** Run the `reproduce_issue.mlir` with `mlir-opt --canonicalize` to ensure it no longer crashes.
+3. **Regression Test:** Add the reproduction case to `mlir/test/Dialect/Shape/canonicalize.mlir` or a new test file.
diff --git a/mlir/lib/Dialect/Shape/IR/Shape.cpp b/mlir/lib/Dialect/Shape/IR/Shape.cpp
index c1210eef4e589..6f7a26374e6f6 100644
--- a/mlir/lib/Dialect/Shape/IR/Shape.cpp
+++ b/mlir/lib/Dialect/Shape/IR/Shape.cpp
@@ -652,18 +652,18 @@ OpFoldResult BroadcastOp::fold(FoldAdaptor adaptor) {
return getShapes().front();
}
- if (!adaptor.getShapes().front())
+ auto firstAttr =
+ llvm::dyn_cast_if_present<DenseIntElementsAttr>(adaptor.getShapes().front());
+ if (!firstAttr)
return nullptr;
- SmallVector<int64_t, 6> resultShape(
- llvm::cast<DenseIntElementsAttr>(adaptor.getShapes().front())
- .getValues<int64_t>());
+ SmallVector<int64_t, 6> resultShape(firstAttr.getValues<int64_t>());
for (auto next : adaptor.getShapes().drop_front()) {
- if (!next)
+ auto nextAttr = llvm::dyn_cast_if_present<DenseIntElementsAttr>(next);
+ if (!nextAttr)
return nullptr;
- auto nextShape = llvm::to_vector<6>(
- llvm::cast<DenseIntElementsAttr>(next).getValues<int64_t>());
+ auto nextShape = llvm::to_vector<6>(nextAttr.getValues<int64_t>());
SmallVector<int64_t, 6> tmpShape;
// If the shapes are not compatible, we can't fold it.
@@ -978,7 +978,8 @@ void CstrBroadcastableOp::getCanonicalizationPatterns(
static bool hasAtMostSingleNonScalar(ArrayRef<Attribute> attributes) {
bool nonScalarSeen = false;
for (Attribute a : attributes) {
- if (!a || llvm::cast<DenseIntElementsAttr>(a).getNumElements() != 0) {
+ auto denseAttr = llvm::dyn_cast_if_present<DenseIntElementsAttr>(a);
+ if (!denseAttr || denseAttr.getNumElements() != 0) {
if (nonScalarSeen)
return false;
nonScalarSeen = true;
@@ -995,10 +996,10 @@ OpFoldResult CstrBroadcastableOp::fold(FoldAdaptor adaptor) {
if ([&] {
SmallVector<SmallVector<int64_t, 6>, 6> extents;
for (const auto &operand : adaptor.getShapes()) {
- if (!operand)
+ auto denseAttr = llvm::dyn_cast_if_present<DenseIntElementsAttr>(operand);
+ if (!denseAttr)
return false;
- extents.push_back(llvm::to_vector<6>(
- llvm::cast<DenseIntElementsAttr>(operand).getValues<int64_t>()));
+ extents.push_back(llvm::to_vector<6>(denseAttr.getValues<int64_t>()));
}
return OpTrait::util::staticallyKnownBroadcastable(extents);
}())
diff --git a/mlir/test/Dialect/Shape/canonicalize.mlir b/mlir/test/Dialect/Shape/canonicalize.mlir
index f3c25b8c8100e..d7a8432cd0469 100644
--- a/mlir/test/Dialect/Shape/canonicalize.mlir
+++ b/mlir/test/Dialect/Shape/canonicalize.mlir
@@ -1626,3 +1626,28 @@ func.func @shape_of_0d(%arg0: tensor<f32>) -> tensor<?xindex> {
%0 = shape.shape_of %arg0 : tensor<f32> -> tensor<?xindex>
return %0 : tensor<?xindex>
}
+
+// -----
+
+// GH#179845: Verify shape.broadcast doesn't crash on poison operands.
+// CHECK-LABEL: func @broadcast_poison
+func.func @broadcast_poison() {
+ // CHECK: ub.poison
+ // CHECK: shape.broadcast
+ %2 = ub.poison : tensor<3xindex>
+ %3 = shape.broadcast %2, %2 : tensor<3xindex>, tensor<3xindex> -> tensor<3xindex>
+ return
+}
+
+// -----
+
+// GH#179845: Verify cstr_broadcastable doesn't crash on poison operands.
+// CHECK-LABEL: func @cstr_broadcastable_poison
+func.func @cstr_broadcastable_poison() {
+ // CHECK: ub.poison
+ // CHECK: shape.cstr_broadcastable
+ %2 = ub.poison : !shape.shape
+ %0 = shape.cstr_broadcastable %2, %2 : !shape.shape, !shape.shape
+ "consume.witness"(%0) : (!shape.witness) -> ()
+ return
+}
>From cccbdef0805270728fe565882c4ddfd0729ed16a Mon Sep 17 00:00:00 2001
From: SakshamSinghal20 <sakshamsinghal2020 at gmail.com>
Date: Thu, 5 Feb 2026 13:15:39 +0000
Subject: [PATCH 2/2] extra file removed
---
implementation_plan.md | 81 ------------------------------------------
1 file changed, 81 deletions(-)
delete mode 100644 implementation_plan.md
diff --git a/implementation_plan.md b/implementation_plan.md
deleted file mode 100644
index eb1af24bd791b..0000000000000
--- a/implementation_plan.md
+++ /dev/null
@@ -1,81 +0,0 @@
-# Implementation Plan - Fix mlir-opt Crash in Shape Dialect
-
-## Problem Analysis
-The `mlir-opt` tool crashes with an assertion failure in `llvm::cast` during the `--canonicalize` pass. This occurs when `shape.broadcast` or other shape operations consume a `ub.poison` value (or any attribute that is not a `DenseIntElementsAttr`). The folding logic assumes that if an operand is not null, it must be a `DenseIntElementsAttr`, leading to an invalid cast.
-
-**Affected File:** `mlir/lib/Dialect/Shape/IR/Shape.cpp`
-
-**Locations:**
-1. `BroadcastOp::fold` (lines ~647-681)
-2. `hasAtMostSingleNonScalar` helper function (lines ~978-988)
-3. `CstrBroadcastableOp::fold` (lines ~990-1023)
-
-## Proposed Changes
-
-### 1. Update `BroadcastOp::fold`
-Modify the loop that iterates over shapes. Instead of just checking `if (!next)`, check if `next` can be cast to `DenseIntElementsAttr`.
-
-**Current Code:**
-```cpp
-if (!adaptor.getShapes().front())
- return nullptr;
-// ...
-for (auto next : adaptor.getShapes().drop_front()) {
- if (!next)
- return nullptr;
- // cast ...
-}
-```
-
-**New Code:**
-```cpp
-auto firstAttr = llvm::dyn_cast_if_present<DenseIntElementsAttr>(adaptor.getShapes().front());
-if (!firstAttr)
- return nullptr;
-// ...
-for (auto next : adaptor.getShapes().drop_front()) {
- auto nextAttr = llvm::dyn_cast_if_present<DenseIntElementsAttr>(next);
- if (!nextAttr)
- return nullptr;
- // use nextAttr ...
-}
-```
-
-### 2. Update `hasAtMostSingleNonScalar`
-Add a check for `DenseIntElementsAttr` before accessing `getNumElements()`.
-
-**Current Code:**
-```cpp
-if (!a || llvm::cast<DenseIntElementsAttr>(a).getNumElements() != 0) {
-```
-
-**New Code:**
-```cpp
-auto denseAttr = llvm::dyn_cast_if_present<DenseIntElementsAttr>(a);
-if (!denseAttr || denseAttr.getNumElements() != 0) {
-```
-
-### 3. Update `CstrBroadcastableOp::fold`
-Similar to `BroadcastOp`, ensure `dyn_cast` succeeds before accessing values.
-
-**Current Code:**
-```cpp
-if (!operand)
- return false;
-extents.push_back(llvm::to_vector<6>(
- llvm::cast<DenseIntElementsAttr>(operand).getValues<int64_t>()));
-```
-
-**New Code:**
-```cpp
-auto denseAttr = llvm::dyn_cast_if_present<DenseIntElementsAttr>(operand);
-if (!denseAttr)
- return false;
-extents.push_back(llvm::to_vector<6>(
- denseAttr.getValues<int64_t>()));
-```
-
-## Validation Plan
-1. **Build:** Run `ninja mlir-opt` (or `ninja check-mlir-shape` if available targets).
-2. **Reproduction:** Run the `reproduce_issue.mlir` with `mlir-opt --canonicalize` to ensure it no longer crashes.
-3. **Regression Test:** Add the reproduction case to `mlir/test/Dialect/Shape/canonicalize.mlir` or a new test file.
More information about the Mlir-commits
mailing list