[Mlir-commits] [mlir] [MLIR][LLVMIR] Fixing how ZeroInitializers are converted to undef instead of constant zero type when importing from LLVMIR to llvm.mlir (PR #171107)
llvmlistbot at llvm.org
llvmlistbot at llvm.org
Tue Dec 9 05:12:30 PST 2025
https://github.com/Bhuvan1527 updated https://github.com/llvm/llvm-project/pull/171107
>From 37dced0fb4de5d528847e63f50f2a989482d4eba Mon Sep 17 00:00:00 2001
From: bhuvan1527 <balabhuvanvarma at gmail.com>
Date: Mon, 8 Dec 2025 16:03:56 +0530
Subject: [PATCH 1/2] [MLIR][LLVMIR] Issue when importing LLVM modules which
contain LandingPad clauses
llvm IR with landingPad instructions are not the same when exporting back from llvm mlir dialect to llvm IR.
---
mlir/lib/Target/LLVMIR/ModuleImport.cpp | 18 ++++++-
mlir/test/Target/LLVMIR/Import/constant.ll | 52 +++++++++++++++---
mlir/test/Target/LLVMIR/Import/exception.ll | 60 +++++++++++++++++++--
3 files changed, 118 insertions(+), 12 deletions(-)
diff --git a/mlir/lib/Target/LLVMIR/ModuleImport.cpp b/mlir/lib/Target/LLVMIR/ModuleImport.cpp
index d7d215bf1bd09..803534abccad5 100644
--- a/mlir/lib/Target/LLVMIR/ModuleImport.cpp
+++ b/mlir/lib/Target/LLVMIR/ModuleImport.cpp
@@ -1765,17 +1765,25 @@ FailureOr<Value> ModuleImport::convertConstant(llvm::Constant *constant) {
return lookupValue(inst);
}
+ // Convert zero-initialized aggregates to ZeroOp.
+ if (auto *aggregateZero = dyn_cast<llvm::ConstantAggregateZero>(constant)) {
+ Type type = convertType(aggregateZero->getType());
+ return builder.create<ZeroOp>(loc, type).getResult();
+ }
+
// Convert aggregate constants.
if (isa<llvm::ConstantAggregate>(constant) ||
isa<llvm::ConstantAggregateZero>(constant)) {
// Lookup the aggregate elements that have been converted before.
SmallVector<Value> elementValues;
+ bool isAggregate = false;
if (auto *constAgg = dyn_cast<llvm::ConstantAggregate>(constant)) {
elementValues.reserve(constAgg->getNumOperands());
for (llvm::Value *operand : constAgg->operands())
elementValues.push_back(lookupValue(operand));
}
if (auto *constAgg = dyn_cast<llvm::ConstantAggregateZero>(constant)) {
+ isAggregate = true;
unsigned numElements = constAgg->getElementCount().getFixedValue();
elementValues.reserve(numElements);
for (unsigned i = 0, e = numElements; i != e; ++i)
@@ -1784,12 +1792,18 @@ FailureOr<Value> ModuleImport::convertConstant(llvm::Constant *constant) {
assert(llvm::count(elementValues, nullptr) == 0 &&
"expected all elements have been converted before");
- // Generate an UndefOp as root value and insert the aggregate elements.
+ // Generate a root value and insert the aggregate elements.
+ // For ConstantAggregateZero, use ZeroOp to preserve zero-initialization
+ // semantics. Otherwise use UndefOp as the root.Type rootType = convertType(constant->getType());
Type rootType = convertType(constant->getType());
bool isArrayOrStruct = isa<LLVMArrayType, LLVMStructType>(rootType);
assert((isArrayOrStruct || LLVM::isCompatibleVectorType(rootType)) &&
"unrecognized aggregate type");
- Value root = UndefOp::create(builder, loc, rootType);
+ Value root;
+ if (isAggregate)
+ root = builder.create<ZeroOp>(loc, rootType);
+ else
+ root = builder.create<UndefOp>(loc, rootType);
for (const auto &it : llvm::enumerate(elementValues)) {
if (isArrayOrStruct) {
root =
diff --git a/mlir/test/Target/LLVMIR/Import/constant.ll b/mlir/test/Target/LLVMIR/Import/constant.ll
index 103d0ff001969..9c59aeff2bdf3 100644
--- a/mlir/test/Target/LLVMIR/Import/constant.ll
+++ b/mlir/test/Target/LLVMIR/Import/constant.ll
@@ -198,13 +198,8 @@ define i32 @function_address_after_def() {
%nested_agg_type = type {%simple_agg_type, ptr}
@nested_agg = global %nested_agg_type { %simple_agg_type{i32 1, i8 2, i16 3, i32 4}, ptr null }
-; CHECK-DAG: %[[NULL:.+]] = llvm.mlir.zero : !llvm.ptr
-; CHECK-DAG: %[[ROOT:.+]] = llvm.mlir.undef : vector<2x!llvm.ptr>
-; CHECK-DAG: %[[P0:.+]] = llvm.mlir.constant(0 : i32) : i32
-; CHECK-DAG: %[[CHAIN0:.+]] = llvm.insertelement %[[NULL]], %[[ROOT]][%[[P0]] : i32] : vector<2x!llvm.ptr>
-; CHECK-DAG: %[[P1:.+]] = llvm.mlir.constant(1 : i32) : i32
-; CHECK-DAG: %[[CHAIN1:.+]] = llvm.insertelement %[[NULL]], %[[CHAIN0]][%[[P1]] : i32] : vector<2x!llvm.ptr>
-; CHECK-DAG: llvm.return %[[CHAIN1]] : vector<2x!llvm.ptr>
+; CHECK-DAG: %[[VEC:.+]] = llvm.mlir.zero : vector<2x!llvm.ptr>
+; CHECK-DAG: llvm.return %[[VEC]] : vector<2x!llvm.ptr>
@vector_agg = global <2 x ptr> <ptr null, ptr null>
; // -----
@@ -274,3 +269,46 @@ define void @call_alias_func() {
; CHECK-LABEL: @call_alias_func()
; CHECK: llvm.dso_local_equivalent @alias_func : !llvm.ptr
+
+; // -----
+
+; Test that zeroinitializer for zero-element arrays is correctly translated
+; to llvm.mlir.zero instead of llvm.mlir.undef. This is a regression test for
+; a bug where empty aggregate constants were incorrectly converted to undef.
+
+ at global_zero_array = global [0 x ptr] zeroinitializer
+
+; CHECK: llvm.mlir.global external @global_zero_array() {addr_space = 0 : i32} : !llvm.array<0 x ptr> {
+; CHECK: %[[ZERO:.+]] = llvm.mlir.zero : !llvm.array<0 x ptr>
+; CHECK: llvm.return %[[ZERO]] : !llvm.array<0 x ptr>
+; CHECK: }
+
+; CHECK-LABEL: @load_zero_array
+define [0 x ptr] @load_zero_array() {
+ ; CHECK: %[[ADDR:.+]] = llvm.mlir.addressof @global_zero_array : !llvm.ptr
+ ; CHECK: %[[VAL:.+]] = llvm.load %[[ADDR]] {{.*}}: !llvm.ptr -> !llvm.array<0 x ptr>
+ ; CHECK: llvm.return %[[VAL]] : !llvm.array<0 x ptr>
+ %val = load [0 x ptr], ptr @global_zero_array
+ ret [0 x ptr] %val
+}
+
+; // -----
+
+; Test that zeroinitializer for zero-element structs is correctly handled
+
+ at global_zero_struct = global {} zeroinitializer
+
+; CHECK: llvm.mlir.global external @global_zero_struct() {addr_space = 0 : i32} : !llvm.struct<()> {
+; CHECK: %[[ZERO:.+]] = llvm.mlir.zero : !llvm.struct<()>
+; CHECK: llvm.return %[[ZERO]] : !llvm.struct<()>
+; CHECK: }
+
+; // -----
+
+; Test that zeroinitializer for arrays with elements still works correctly.
+; Note that arrays with primitive types that can be represented as dense
+; attributes may use the attribute form directly.
+
+ at global_array_with_elements = global [3 x i32] zeroinitializer
+
+; CHECK: llvm.mlir.global external @global_array_with_elements({{.*}}) {addr_space = 0 : i32} : !llvm.array<3 x i32>
\ No newline at end of file
diff --git a/mlir/test/Target/LLVMIR/Import/exception.ll b/mlir/test/Target/LLVMIR/Import/exception.ll
index 1451104920623..fd9ec94790661 100644
--- a/mlir/test/Target/LLVMIR/Import/exception.ll
+++ b/mlir/test/Target/LLVMIR/Import/exception.ll
@@ -123,9 +123,7 @@ define void @landingpad_dominance() personality ptr @__gxx_personality_v0 {
entry:
; CHECK: %[[null:.*]] = llvm.mlir.zero : !llvm.ptr
; CHECK: %[[c1:.*]] = llvm.mlir.constant(0 : i32) : i32
- ; CHECK: %[[undef:.*]] = llvm.mlir.undef : !llvm.struct<(ptr, i32)>
- ; CHECK: %[[tmpstruct:.*]] = llvm.insertvalue %[[null]], %[[undef]][0] : !llvm.struct<(ptr, i32)>
- ; CHECK: %[[struct:.*]] = llvm.insertvalue %[[c1]], %[[tmpstruct]][1] : !llvm.struct<(ptr, i32)>
+ ; CHECK: %[[struct:.*]] = llvm.mlir.zero : !llvm.struct<(ptr, i32)>
; CHECK: llvm.call @f0(%[[null]]) : (!llvm.ptr) -> ()
call void @f0(ptr null)
; CHECK: llvm.call @f1(%[[c1]]) : (i32) -> ()
@@ -190,3 +188,59 @@ bb3:
!6 = !DILocation(line: 2, column: 2, scope: !3)
!7 = !DILocation(line: 7, column: 4, scope: !4, inlinedAt: !6)
!8 = !DILocalVariable(scope: !4, name: "size")
+
+; // -----
+
+declare i32 @__gxx_personality_v0(...)
+declare void @foo(ptr)
+
+; Test that landingpad filter clauses with zeroinitializer are correctly
+; translated to llvm.mlir.zero instead of llvm.mlir.undef.
+; This is a regression test for a bug where zero-element arrays used in
+; filter clauses were incorrectly converted to undef.
+
+; CHECK-LABEL: @landingpad_zero_filter
+define void @landingpad_zero_filter() personality ptr @__gxx_personality_v0 {
+entry:
+ ; CHECK: %[[ZERO:.+]] = llvm.mlir.zero : !llvm.array<0 x ptr>
+ invoke void @foo(ptr null) to label %normal unwind label %lpad
+
+normal:
+ ret void
+
+lpad:
+ ; CHECK: %{{[0-9]+}} = llvm.landingpad cleanup (filter %[[ZERO]] : !llvm.array<0 x ptr>) : !llvm.struct<(ptr, i32)>
+ %0 = landingpad { ptr, i32 }
+ cleanup
+ filter [0 x ptr] zeroinitializer
+ ret void
+}
+
+; // -----
+
+declare i32 @__gxx_personality_v0(...)
+declare void @foo(ptr)
+
+; Test that landingpad with multiple filter clauses of different zero-element
+; array types are correctly handled. Note that zero-element arrays of primitive
+; types like i32 may be converted to dense attributes, while ptr arrays use
+; llvm.mlir.zero.
+
+; CHECK-LABEL: @landingpad_mixed_filters
+define void @landingpad_mixed_filters() personality ptr @__gxx_personality_v0 {
+entry:
+ ; CHECK: %[[ZERO1:.+]] = llvm.mlir.zero : !llvm.array<0 x ptr>
+ ; CHECK: %[[ZERO2:.+]] = llvm.mlir.{{(zero|constant)}}{{.*}}: !llvm.array<0 x i32>
+ invoke void @foo(ptr null) to label %normal unwind label %lpad
+
+normal:
+ ret void
+
+lpad:
+ ; CHECK: %{{[0-9]+}} = llvm.landingpad cleanup (filter %[[ZERO1]] : !llvm.array<0 x ptr>) (filter %[[ZERO2]] : !llvm.array<0 x i32>) : !llvm.struct<(ptr, i32)>
+ %0 = landingpad { ptr, i32 }
+ cleanup
+ filter [0 x ptr] zeroinitializer
+ filter [0 x i32] zeroinitializer
+ ret void
+}
\ No newline at end of file
>From 90277c57e59bf3843470eb99a9490975cee5d8b9 Mon Sep 17 00:00:00 2001
From: bhuvan1527 <balabhuvanvarma at gmail.com>
Date: Mon, 8 Dec 2025 20:13:08 +0530
Subject: [PATCH 2/2] [MLIR][LLVMIR] Issue when importing LLVM modules which
contain LandingPad Instructions
Issue:
The problem is how constant zero aggregate structures are imported to llvm.mlir from llvmIR.
---
mlir/lib/Target/LLVMIR/ModuleImport.cpp | 33 +++++--------------
mlir/test/Target/LLVMIR/Import/constant.ll | 8 ++---
mlir/test/Target/LLVMIR/Import/exception.ll | 6 ++--
.../Target/LLVMIR/Import/zeroinitializer.ll | 6 ++--
4 files changed, 15 insertions(+), 38 deletions(-)
diff --git a/mlir/lib/Target/LLVMIR/ModuleImport.cpp b/mlir/lib/Target/LLVMIR/ModuleImport.cpp
index 803534abccad5..c0ff5fdb199b3 100644
--- a/mlir/lib/Target/LLVMIR/ModuleImport.cpp
+++ b/mlir/lib/Target/LLVMIR/ModuleImport.cpp
@@ -1768,42 +1768,27 @@ FailureOr<Value> ModuleImport::convertConstant(llvm::Constant *constant) {
// Convert zero-initialized aggregates to ZeroOp.
if (auto *aggregateZero = dyn_cast<llvm::ConstantAggregateZero>(constant)) {
Type type = convertType(aggregateZero->getType());
- return builder.create<ZeroOp>(loc, type).getResult();
+ return ZeroOp::create(builder, loc, type).getResult();
}
// Convert aggregate constants.
- if (isa<llvm::ConstantAggregate>(constant) ||
- isa<llvm::ConstantAggregateZero>(constant)) {
+ if (auto *constAgg = dyn_cast<llvm::ConstantAggregate>(constant)) {
// Lookup the aggregate elements that have been converted before.
SmallVector<Value> elementValues;
- bool isAggregate = false;
- if (auto *constAgg = dyn_cast<llvm::ConstantAggregate>(constant)) {
- elementValues.reserve(constAgg->getNumOperands());
- for (llvm::Value *operand : constAgg->operands())
- elementValues.push_back(lookupValue(operand));
- }
- if (auto *constAgg = dyn_cast<llvm::ConstantAggregateZero>(constant)) {
- isAggregate = true;
- unsigned numElements = constAgg->getElementCount().getFixedValue();
- elementValues.reserve(numElements);
- for (unsigned i = 0, e = numElements; i != e; ++i)
- elementValues.push_back(lookupValue(constAgg->getElementValue(i)));
- }
+
+ elementValues.reserve(constAgg->getNumOperands());
+ for (llvm::Value *operand : constAgg->operands())
+ elementValues.push_back(lookupValue(operand));
+
assert(llvm::count(elementValues, nullptr) == 0 &&
"expected all elements have been converted before");
- // Generate a root value and insert the aggregate elements.
- // For ConstantAggregateZero, use ZeroOp to preserve zero-initialization
- // semantics. Otherwise use UndefOp as the root.Type rootType = convertType(constant->getType());
+ // Generate an UndefOp as root value and insert the aggregate elements.
Type rootType = convertType(constant->getType());
bool isArrayOrStruct = isa<LLVMArrayType, LLVMStructType>(rootType);
assert((isArrayOrStruct || LLVM::isCompatibleVectorType(rootType)) &&
"unrecognized aggregate type");
- Value root;
- if (isAggregate)
- root = builder.create<ZeroOp>(loc, rootType);
- else
- root = builder.create<UndefOp>(loc, rootType);
+ Value root = UndefOp::create(builder, loc, rootType);
for (const auto &it : llvm::enumerate(elementValues)) {
if (isArrayOrStruct) {
root =
diff --git a/mlir/test/Target/LLVMIR/Import/constant.ll b/mlir/test/Target/LLVMIR/Import/constant.ll
index 9c59aeff2bdf3..ddf29c6d2f380 100644
--- a/mlir/test/Target/LLVMIR/Import/constant.ll
+++ b/mlir/test/Target/LLVMIR/Import/constant.ll
@@ -272,16 +272,13 @@ define void @call_alias_func() {
; // -----
-; Test that zeroinitializer for zero-element arrays is correctly translated
-; to llvm.mlir.zero instead of llvm.mlir.undef. This is a regression test for
-; a bug where empty aggregate constants were incorrectly converted to undef.
+; Test that zeroinitializer for zero-element array is correctly handled
@global_zero_array = global [0 x ptr] zeroinitializer
; CHECK: llvm.mlir.global external @global_zero_array() {addr_space = 0 : i32} : !llvm.array<0 x ptr> {
; CHECK: %[[ZERO:.+]] = llvm.mlir.zero : !llvm.array<0 x ptr>
; CHECK: llvm.return %[[ZERO]] : !llvm.array<0 x ptr>
-; CHECK: }
; CHECK-LABEL: @load_zero_array
define [0 x ptr] @load_zero_array() {
@@ -301,7 +298,6 @@ define [0 x ptr] @load_zero_array() {
; CHECK: llvm.mlir.global external @global_zero_struct() {addr_space = 0 : i32} : !llvm.struct<()> {
; CHECK: %[[ZERO:.+]] = llvm.mlir.zero : !llvm.struct<()>
; CHECK: llvm.return %[[ZERO]] : !llvm.struct<()>
-; CHECK: }
; // -----
@@ -311,4 +307,4 @@ define [0 x ptr] @load_zero_array() {
@global_array_with_elements = global [3 x i32] zeroinitializer
-; CHECK: llvm.mlir.global external @global_array_with_elements({{.*}}) {addr_space = 0 : i32} : !llvm.array<3 x i32>
\ No newline at end of file
+; CHECK: llvm.mlir.global external @global_array_with_elements({{.*}}) {addr_space = 0 : i32} : !llvm.array<3 x i32>
diff --git a/mlir/test/Target/LLVMIR/Import/exception.ll b/mlir/test/Target/LLVMIR/Import/exception.ll
index fd9ec94790661..57c4bce5c6c72 100644
--- a/mlir/test/Target/LLVMIR/Import/exception.ll
+++ b/mlir/test/Target/LLVMIR/Import/exception.ll
@@ -195,9 +195,7 @@ declare i32 @__gxx_personality_v0(...)
declare void @foo(ptr)
; Test that landingpad filter clauses with zeroinitializer are correctly
-; translated to llvm.mlir.zero instead of llvm.mlir.undef.
-; This is a regression test for a bug where zero-element arrays used in
-; filter clauses were incorrectly converted to undef.
+; translated to llvm.mlir.zero
; CHECK-LABEL: @landingpad_zero_filter
define void @landingpad_zero_filter() personality ptr @__gxx_personality_v0 {
@@ -243,4 +241,4 @@ lpad:
filter [0 x ptr] zeroinitializer
filter [0 x i32] zeroinitializer
ret void
-}
\ No newline at end of file
+}
diff --git a/mlir/test/Target/LLVMIR/Import/zeroinitializer.ll b/mlir/test/Target/LLVMIR/Import/zeroinitializer.ll
index da3eae3e9e337..02216dab0cc2f 100644
--- a/mlir/test/Target/LLVMIR/Import/zeroinitializer.ll
+++ b/mlir/test/Target/LLVMIR/Import/zeroinitializer.ll
@@ -5,8 +5,6 @@
; CHECK: llvm.mlir.global external @D()
; CHECK-SAME: !llvm.struct<"Domain", (ptr, ptr)>
; CHECK: %[[E0:.+]] = llvm.mlir.zero : !llvm.ptr
-; CHECK: %[[ROOT:.+]] = llvm.mlir.undef : !llvm.struct<"Domain", (ptr, ptr)>
-; CHECK: %[[CHAIN:.+]] = llvm.insertvalue %[[E0]], %[[ROOT]][0]
-; CHECK: %[[RES:.+]] = llvm.insertvalue %[[E0]], %[[CHAIN]][1]
-; CHECK: llvm.return %[[RES]]
+; CHECK: %[[RES:.+]] = llvm.mlir.zero : !llvm.struct<"Domain", (ptr, ptr)>
+; CHECK: llvm.return %[[RES]] : !llvm.struct<"Domain", (ptr, ptr)>
@D = global %Domain zeroinitializer
More information about the Mlir-commits
mailing list