[flang-commits] [flang] [flang][CodeGen] Fix element type of folded insert_on_range initializers (PR #218587)

Christian Ulmann via flang-commits flang-commits at lists.llvm.org
Tue Aug 25 02:14:51 PDT 2026


https://github.com/Dinistro updated https://github.com/llvm/llvm-project/pull/218587

>From 62a903ea2478c46f4cd5926c8bbdc58a023bf47f Mon Sep 17 00:00:00 2001
From: Christian Ulmann <christian.ulmann at nextsilicon.com>
Date: Tue, 25 Aug 2026 07:55:28 +0200
Subject: [PATCH 1/3] [flang][CodeGen] Fix element type of folded
 insert_on_range initializers

`GlobalOpConversion` folds a full-range `fir.insert_on_range` into a dense
constant. When the inserted value comes from a `fir.convert`, the fold reached
through the conversion and built the dense attribute from the type of the
*source* constant. For a `logical(4)` array initialized to `.true.` this
produced

  llvm.mlir.constant(dense<true> : vector<32768xi1>) : !llvm.array<32768 x i32>

where the attribute element type `i1` disagrees with the result element type
`i32`. Translation to LLVM IR ignores the attribute type and uses the result
type, so the emitted global is still correct today, but the IR is malformed and
any consumer that trusts the attribute type sees the wrong element width.

Build the dense attribute from the converted element type instead. A logical
conversion normalizes its operand to a canonical 0/1 (see `ConvertOpConversion`),
so apply the same normalization here; any other mismatching conversion is left
to the regular lowering.

Co-Authored-By: Claude Opus 5 (1M context) <noreply at anthropic.com>
---
 flang/lib/Optimizer/CodeGen/CodeGen.cpp  | 30 ++++++++++++++++++++----
 flang/test/Fir/global-initialization.fir |  2 +-
 2 files changed, 26 insertions(+), 6 deletions(-)

diff --git a/flang/lib/Optimizer/CodeGen/CodeGen.cpp b/flang/lib/Optimizer/CodeGen/CodeGen.cpp
index 7696f5f900c5e..7274fecc5e056 100644
--- a/flang/lib/Optimizer/CodeGen/CodeGen.cpp
+++ b/flang/lib/Optimizer/CodeGen/CodeGen.cpp
@@ -3757,19 +3757,39 @@ struct GlobalOpConversion : public fir::FIROpConversion<fir::GlobalOp> {
       for (auto insertOp : insertOnRangeOps) {
         if (insertOp.isFullRange()) {
           auto seqTyAttr = convertType(insertOp.getType());
+          // The dense attribute must use the converted element type of the
+          // array, not the type of whatever constant feeds the insertion.
+          mlir::Type elementType = convertType(insertOp.getType().getEleTy());
           auto *op = insertOp.getVal().getDefiningOp();
           auto constant = mlir::dyn_cast<mlir::arith::ConstantOp>(op);
+          fir::ConvertOp convertOp;
           if (!constant) {
-            auto convertOp = mlir::dyn_cast<fir::ConvertOp>(op);
+            convertOp = mlir::dyn_cast<fir::ConvertOp>(op);
             if (!convertOp)
               continue;
-            constant = mlir::cast<mlir::arith::ConstantOp>(
+            constant = mlir::dyn_cast<mlir::arith::ConstantOp>(
                 convertOp.getValue().getDefiningOp());
+            if (!constant)
+              continue;
+          }
+          mlir::TypedAttr valueAttr = constant.getValue();
+          if (valueAttr.getType() != elementType) {
+            // Reaching through a `fir.convert` leaves the constant with the
+            // source type. Only a logical conversion is folded here, and it
+            // normalizes the value to a canonical 0/1, see ConvertOpConversion.
+            auto intAttr = mlir::dyn_cast<mlir::IntegerAttr>(valueAttr);
+            auto intType = mlir::dyn_cast<mlir::IntegerType>(elementType);
+            if (!intAttr || !intType || !convertOp ||
+                (!mlir::isa<fir::LogicalType>(convertOp.getType()) &&
+                 !mlir::isa<fir::LogicalType>(convertOp.getValue().getType())))
+              continue;
+            valueAttr = mlir::IntegerAttr::get(
+                intType, intAttr.getValue().isZero() ? 0 : 1);
           }
-          mlir::Type vecType = mlir::VectorType::get(
-              insertOp.getType().getShape(), constant.getType());
+          mlir::Type vecType =
+              mlir::VectorType::get(insertOp.getType().getShape(), elementType);
           auto denseAttr = mlir::DenseElementsAttr::get(
-              mlir::cast<mlir::ShapedType>(vecType), constant.getValue());
+              mlir::cast<mlir::ShapedType>(vecType), valueAttr);
           rewriter.setInsertionPointAfter(insertOp);
           rewriter.replaceOpWithNewOp<mlir::arith::ConstantOp>(
               insertOp, seqTyAttr, denseAttr);
diff --git a/flang/test/Fir/global-initialization.fir b/flang/test/Fir/global-initialization.fir
index 98c842c4b7551..740429f320ee0 100644
--- a/flang/test/Fir/global-initialization.fir
+++ b/flang/test/Fir/global-initialization.fir
@@ -41,7 +41,7 @@ fir.global internal @_QEmasklogical : !fir.array<32768x!fir.logical<4>> {
 // CHECK:   [[VAL0:%.*]] = llvm.mlir.constant(true) : i1
 // CHECK:   [[VAL1:%.*]] = llvm.mlir.undef : !llvm.array<32768 x i32>
 // CHECK:   [[VAL2:%.*]] = llvm.mlir.constant(1 : i32) : i32
-// CHECK:   [[VAL3:%.*]] = llvm.mlir.constant(dense<true> : vector<32768xi1>) : !llvm.array<32768 x i32>
+// CHECK:   [[VAL3:%.*]] = llvm.mlir.constant(dense<1> : vector<32768xi32>) : !llvm.array<32768 x i32>
 // CHECK:   llvm.return [[VAL3]] : !llvm.array<32768 x i32>
 // CHECK: }
 

>From 94fa34b39273bb9985e34bc5f300c544d2ee065d Mon Sep 17 00:00:00 2001
From: Christian Ulmann <christian.ulmann at nextsilicon.com>
Date: Tue, 25 Aug 2026 08:09:29 +0200
Subject: [PATCH 2/3] simplification

---
 flang/lib/Optimizer/CodeGen/CodeGen.cpp | 67 +++++++++++--------------
 1 file changed, 29 insertions(+), 38 deletions(-)

diff --git a/flang/lib/Optimizer/CodeGen/CodeGen.cpp b/flang/lib/Optimizer/CodeGen/CodeGen.cpp
index 7274fecc5e056..83d8b36201ecf 100644
--- a/flang/lib/Optimizer/CodeGen/CodeGen.cpp
+++ b/flang/lib/Optimizer/CodeGen/CodeGen.cpp
@@ -3755,45 +3755,36 @@ struct GlobalOpConversion : public fir::FIROpConversion<fir::GlobalOp> {
       // initialization is on the full range.
       auto insertOnRangeOps = gr.front().getOps<fir::InsertOnRangeOp>();
       for (auto insertOp : insertOnRangeOps) {
-        if (insertOp.isFullRange()) {
-          auto seqTyAttr = convertType(insertOp.getType());
-          // The dense attribute must use the converted element type of the
-          // array, not the type of whatever constant feeds the insertion.
-          mlir::Type elementType = convertType(insertOp.getType().getEleTy());
-          auto *op = insertOp.getVal().getDefiningOp();
-          auto constant = mlir::dyn_cast<mlir::arith::ConstantOp>(op);
-          fir::ConvertOp convertOp;
-          if (!constant) {
-            convertOp = mlir::dyn_cast<fir::ConvertOp>(op);
-            if (!convertOp)
-              continue;
-            constant = mlir::dyn_cast<mlir::arith::ConstantOp>(
-                convertOp.getValue().getDefiningOp());
-            if (!constant)
-              continue;
-          }
-          mlir::TypedAttr valueAttr = constant.getValue();
-          if (valueAttr.getType() != elementType) {
-            // Reaching through a `fir.convert` leaves the constant with the
-            // source type. Only a logical conversion is folded here, and it
-            // normalizes the value to a canonical 0/1, see ConvertOpConversion.
-            auto intAttr = mlir::dyn_cast<mlir::IntegerAttr>(valueAttr);
-            auto intType = mlir::dyn_cast<mlir::IntegerType>(elementType);
-            if (!intAttr || !intType || !convertOp ||
-                (!mlir::isa<fir::LogicalType>(convertOp.getType()) &&
-                 !mlir::isa<fir::LogicalType>(convertOp.getValue().getType())))
-              continue;
-            valueAttr = mlir::IntegerAttr::get(
-                intType, intAttr.getValue().isZero() ? 0 : 1);
-          }
-          mlir::Type vecType =
-              mlir::VectorType::get(insertOp.getType().getShape(), elementType);
-          auto denseAttr = mlir::DenseElementsAttr::get(
-              mlir::cast<mlir::ShapedType>(vecType), valueAttr);
-          rewriter.setInsertionPointAfter(insertOp);
-          rewriter.replaceOpWithNewOp<mlir::arith::ConstantOp>(
-              insertOp, seqTyAttr, denseAttr);
+        if (!insertOp.isFullRange())
+          continue;
+        // The dense attribute must use the converted element type of the
+        // array, not the type of whatever constant feeds the insertion.
+        mlir::Type elementType = convertType(insertOp.getType().getEleTy());
+        mlir::Value val = insertOp.getVal();
+        // Logical constants reach the insertion through a `fir.convert`.
+        if (auto convertOp = val.getDefiningOp<fir::ConvertOp>())
+          val = convertOp.getValue();
+        auto constant = val.getDefiningOp<mlir::arith::ConstantOp>();
+        if (!constant)
+          continue;
+        mlir::TypedAttr valueAttr = constant.getValue();
+        if (valueAttr.getType() != elementType) {
+          // Looking through the `fir.convert` leaves the constant with the
+          // source type. Only fold a boolean feeding an integer element, which
+          // the conversion normalizes to a canonical 0/1, see
+          // ConvertOpConversion.
+          auto boolAttr = mlir::dyn_cast<mlir::BoolAttr>(valueAttr);
+          auto intType = mlir::dyn_cast<mlir::IntegerType>(elementType);
+          if (!boolAttr || !intType)
+            continue;
+          valueAttr = mlir::IntegerAttr::get(intType, boolAttr.getValue());
         }
+        auto vecType =
+            mlir::VectorType::get(insertOp.getType().getShape(), elementType);
+        auto denseAttr = mlir::DenseElementsAttr::get(vecType, valueAttr);
+        rewriter.setInsertionPointAfter(insertOp);
+        rewriter.replaceOpWithNewOp<mlir::arith::ConstantOp>(
+            insertOp, convertType(insertOp.getType()), denseAttr);
       }
     }
 

>From a142691c4b9accd57f664e467e92252679520742 Mon Sep 17 00:00:00 2001
From: Christian Ulmann <christian.ulmann at nextsilicon.com>
Date: Tue, 25 Aug 2026 11:14:36 +0200
Subject: [PATCH 3/3] fix folding interaction

---
 flang/lib/Optimizer/CodeGen/CodeGen.cpp  | 19 ++++++++++++-------
 flang/test/Fir/global-initialization.fir | 15 +++++++++++++++
 2 files changed, 27 insertions(+), 7 deletions(-)

diff --git a/flang/lib/Optimizer/CodeGen/CodeGen.cpp b/flang/lib/Optimizer/CodeGen/CodeGen.cpp
index 83d8b36201ecf..cbe52bcddcb03 100644
--- a/flang/lib/Optimizer/CodeGen/CodeGen.cpp
+++ b/flang/lib/Optimizer/CodeGen/CodeGen.cpp
@@ -3762,7 +3762,8 @@ struct GlobalOpConversion : public fir::FIROpConversion<fir::GlobalOp> {
         mlir::Type elementType = convertType(insertOp.getType().getEleTy());
         mlir::Value val = insertOp.getVal();
         // Logical constants reach the insertion through a `fir.convert`.
-        if (auto convertOp = val.getDefiningOp<fir::ConvertOp>())
+        auto convertOp = val.getDefiningOp<fir::ConvertOp>();
+        if (convertOp)
           val = convertOp.getValue();
         auto constant = val.getDefiningOp<mlir::arith::ConstantOp>();
         if (!constant)
@@ -3770,14 +3771,18 @@ struct GlobalOpConversion : public fir::FIROpConversion<fir::GlobalOp> {
         mlir::TypedAttr valueAttr = constant.getValue();
         if (valueAttr.getType() != elementType) {
           // Looking through the `fir.convert` leaves the constant with the
-          // source type. Only fold a boolean feeding an integer element, which
-          // the conversion normalizes to a canonical 0/1, see
-          // ConvertOpConversion.
-          auto boolAttr = mlir::dyn_cast<mlir::BoolAttr>(valueAttr);
+          // source type. Only an integer<->logical conversion is folded here:
+          // it normalizes any integer operand to a canonical 0/1, see
+          // ConvertOpConversion. Any other mismatching conversion is left to
+          // the regular lowering.
+          auto intAttr = mlir::dyn_cast<mlir::IntegerAttr>(valueAttr);
           auto intType = mlir::dyn_cast<mlir::IntegerType>(elementType);
-          if (!boolAttr || !intType)
+          if (!intAttr || !intType || !convertOp ||
+              (!mlir::isa<fir::LogicalType>(convertOp.getType()) &&
+               !mlir::isa<fir::LogicalType>(convertOp.getValue().getType())))
             continue;
-          valueAttr = mlir::IntegerAttr::get(intType, boolAttr.getValue());
+          valueAttr = mlir::IntegerAttr::get(
+              intType, intAttr.getValue().isZero() ? 0 : 1);
         }
         auto vecType =
             mlir::VectorType::get(insertOp.getType().getShape(), elementType);
diff --git a/flang/test/Fir/global-initialization.fir b/flang/test/Fir/global-initialization.fir
index 740429f320ee0..a683b46de2541 100644
--- a/flang/test/Fir/global-initialization.fir
+++ b/flang/test/Fir/global-initialization.fir
@@ -45,6 +45,21 @@ fir.global internal @_QEmasklogical : !fir.array<32768x!fir.logical<4>> {
 // CHECK:   llvm.return [[VAL3]] : !llvm.array<32768 x i32>
 // CHECK: }
 
+// A logical conversion normalizes any integer operand to a canonical 0/1, not
+// just an `i1` one, so the full-range fold must apply here as well.
+fir.global internal @_QEmasklogicalkind : !fir.array<8x!fir.logical<8>> {
+  %c1_i32 = arith.constant 1 : i32
+  %0 = fir.undefined !fir.array<8x!fir.logical<8>>
+  %1 = fir.convert %c1_i32 : (i32) -> !fir.logical<8>
+  %2 = fir.insert_on_range %0, %1 from (0) to (7) : (!fir.array<8x!fir.logical<8>>, !fir.logical<8>) -> !fir.array<8x!fir.logical<8>>
+  fir.has_value %2 : !fir.array<8x!fir.logical<8>>
+}
+
+// CHECK: llvm.mlir.global internal @_QEmasklogicalkind() {addr_space = 0 : i32} : !llvm.array<8 x i64> {
+// CHECK:   [[VAL2:%.*]] = llvm.mlir.constant(dense<1> : vector<8xi64>) : !llvm.array<8 x i64>
+// CHECK:   llvm.return [[VAL2]] : !llvm.array<8 x i64>
+// CHECK: }
+
 fir.global internal @_QElookforme : !fir.type<_QTt{i:!fir.array<500xi32>,j:!fir.array<500xi32>}> {
   %c2_i32 = arith.constant 2 : i32
   %c52_i32 = arith.constant 52 : i32



More information about the flang-commits mailing list