[flang-commits] [flang] [flang][CodeGen] Fold partial insert_on_range on 1D arrays to a constant (PR #215836)

John Otken via flang-commits flang-commits at lists.llvm.org
Wed Aug 12 09:06:49 PDT 2026


https://github.com/jotken created https://github.com/llvm/llvm-project/pull/215836

When a one dimensional array (e.g., a large array in a COMMON block) is only partially initialized by a DATA statement, lowering the fir.insert_on_range to an llvm.insertvalue chain produces O(N) operations that are O(N^2) to fold into a constant when generating LLVM IR.

Handle this case in InsertOnRangeOpConversion by folding both the sequence being updated and the inserted value to attributes and building the resulting constant array directly as a DenseElementsAttr (when all elements are scalar integer or floating point constants).

To support this, extend fir::tryFoldingLLVMInsertChain to also fold insert chains whose aggregate type is an LLVMArrayType, not just LLVMStructType.

Add a convert-to-llvm.fir test covering a global with a few leading elements set followed by a partial insert_on_range.

Co-authored-by: John Otken john.otken at hpe.com
Assisted-by: Copilot and Claude Opus 4.8.

>From a7c54994aa6de015fd55183d7f70964e81b4b2b5 Mon Sep 17 00:00:00 2001
From: John Otken <john at otken.com>
Date: Wed, 12 Aug 2026 11:00:58 -0500
Subject: [PATCH] [flang][CodeGen] Fold partial insert_on_range on 1D arrays to
 a constant

When a one dimensional array (e.g., a large array in a COMMON block)
is only partially initialized by a DATA statement, lowering the
fir.insert_on_range to an llvm.insertvalue chain produces O(N) operations
that are O(N^2) to fold into a constant when generating LLVM IR.

Handle this case in InsertOnRangeOpConversion by folding both the sequence
being updated and the inserted value to attributes and building the
resulting constant array directly as a DenseElementsAttr (when all elements
are scalar integer or floating point constants).

To support this, extend fir::tryFoldingLLVMInsertChain to also fold insert
chains whose aggregate type is an LLVMArrayType, not just LLVMStructType.

Add a convert-to-llvm.fir test covering a global with a few leading elements
set followed by a partial insert_on_range.

Co-authored-by: John Otken john.otken at hpe.com
Assisted-by: Copilot and Claude Opus 4.8.
---
 flang/lib/Optimizer/CodeGen/CodeGen.cpp       | 46 +++++++++++++++++++
 .../CodeGen/LLVMInsertChainFolder.cpp         |  7 +--
 flang/test/Fir/convert-to-llvm.fir            | 25 ++++++++++
 3 files changed, 75 insertions(+), 3 deletions(-)

diff --git a/flang/lib/Optimizer/CodeGen/CodeGen.cpp b/flang/lib/Optimizer/CodeGen/CodeGen.cpp
index 4b7891bd46df1..179b7d87f0f96 100644
--- a/flang/lib/Optimizer/CodeGen/CodeGen.cpp
+++ b/flang/lib/Optimizer/CodeGen/CodeGen.cpp
@@ -2968,6 +2968,52 @@ struct InsertOnRangeOpConversion
       }
     }
 
+    // Partial range on a one-dimensional array (e.g. a large array in a COMMON
+    // block that is only partially initialized by a DATA statement). Building
+    // an llvm.insertvalue chain here would be O(N) operations that are O(N^2)
+    // to fold into a constant when generating LLVM IR. Instead, fold both the
+    // sequence being updated and the inserted value to attributes and build the
+    // resulting constant array directly.
+    if (dims.size() == 1) {
+      llvm::FailureOr<mlir::Attribute> valCst =
+          fir::tryFoldingLLVMInsertChain(adaptor.getVal(), rewriter);
+      llvm::FailureOr<mlir::Attribute> seqCst =
+          fir::tryFoldingLLVMInsertChain(adaptor.getSeq(), rewriter);
+      if (llvm::succeeded(valCst) && llvm::succeeded(seqCst)) {
+        auto seqArray = mlir::dyn_cast<mlir::ArrayAttr>(*seqCst);
+        // coor holds the inclusive [lower, upper] bound for the single dim.
+        auto bounds = range.getCoor().getValues<int64_t>();
+        int64_t lo = bounds[0];
+        int64_t hi = bounds[1];
+        if (seqArray && lo >= 0 && hi < static_cast<int64_t>(seqArray.size())) {
+          llvm::SmallVector<mlir::Attribute> elements(seqArray.begin(),
+                                                      seqArray.end());
+          for (int64_t i = lo; i <= hi; ++i)
+            elements[i] = *valCst;
+          // A constant array of scalars is represented with a
+          // DenseElementsAttr (an ArrayAttr is only valid for arrays of
+          // aggregates). This is only possible if every element is a scalar
+          // integer or floating point constant.
+          mlir::Type eleTy =
+              mlir::cast<mlir::LLVM::LLVMArrayType>(arrayType).getElementType();
+          bool allScalarCst = llvm::all_of(elements, [&](mlir::Attribute a) {
+            return llvm::isa<mlir::IntegerAttr, mlir::FloatAttr>(a) &&
+                   mlir::cast<mlir::TypedAttr>(a).getType() == eleTy;
+          });
+          if (allScalarCst) {
+            // Represent the scalar array constant with a DenseElementsAttr over
+            // a tensor shape: this is translated to an LLVM array constant
+            // (a vector shape would instead produce a vector constant).
+            auto tensorTy = mlir::RankedTensorType::get(dims[0], eleTy);
+            rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>(
+                range, arrayType,
+                mlir::DenseElementsAttr::get(tensorTy, elements));
+            return mlir::success();
+          }
+        }
+      }
+    }
+
     // The inserted value cannot be folded to an attribute, turn the
     // insert_range into an llvm.insertvalue chain.
     llvm::SmallVector<std::int64_t> lBounds;
diff --git a/flang/lib/Optimizer/CodeGen/LLVMInsertChainFolder.cpp b/flang/lib/Optimizer/CodeGen/LLVMInsertChainFolder.cpp
index 5b522f2647916..804f5a1895e02 100644
--- a/flang/lib/Optimizer/CodeGen/LLVMInsertChainFolder.cpp
+++ b/flang/lib/Optimizer/CodeGen/LLVMInsertChainFolder.cpp
@@ -174,12 +174,13 @@ fir::tryFoldingLLVMInsertChain(mlir::Value val, mlir::OpBuilder &rewriter) {
     return cst.getValue();
   if (auto insert = val.getDefiningOp<mlir::LLVM::InsertValueOp>()) {
     LLVM_DEBUG(llvm::dbgs() << "trying to fold insert chain:" << val << "\n");
-    if (auto structTy =
-            llvm::dyn_cast<mlir::LLVM::LLVMStructType>(insert.getType())) {
+    mlir::Type aggTy = insert.getType();
+    if (llvm::isa<mlir::LLVM::LLVMStructType, mlir::LLVM::LLVMArrayType>(
+            aggTy)) {
       mlir::LLVM::InsertValueOp currentInsert = insert;
       mlir::LLVM::InsertValueOp lastInsert;
       std::deque<InsertChainBackwardFolder> folderStorage;
-      InsertChainBackwardFolder inFlightList(structTy, &folderStorage);
+      InsertChainBackwardFolder inFlightList(aggTy, &folderStorage);
       while (currentInsert) {
         mlir::Attribute attr =
             getAttrIfConstant(currentInsert.getValue(), rewriter);
diff --git a/flang/test/Fir/convert-to-llvm.fir b/flang/test/Fir/convert-to-llvm.fir
index fe803ea2e65af..7beb4353b451c 100644
--- a/flang/test/Fir/convert-to-llvm.fir
+++ b/flang/test/Fir/convert-to-llvm.fir
@@ -142,6 +142,31 @@ fir.global internal @_QEmultiarray : !fir.array<32xi32> {
 
 // -----
 
+// Test global where a few leading elements are set and the rest are set by a
+// partial insert_on_range. It must be folded to a constant array instead of a
+// long (and very expensive to fold) insertvalue chain.
+
+fir.global internal @_QEpartial_init : !fir.array<8xi32> {
+  %c7_i32 = arith.constant 7 : i32
+  %c77_i32 = arith.constant 77 : i32
+  %c0_i32 = arith.constant 0 : i32
+  %0 = fir.undefined !fir.array<8xi32>
+  %1 = fir.insert_value %0, %c7_i32, [0 : index] : (!fir.array<8xi32>, i32) -> !fir.array<8xi32>
+  %2 = fir.insert_value %1, %c77_i32, [1 : index] : (!fir.array<8xi32>, i32) -> !fir.array<8xi32>
+  %3 = fir.insert_on_range %2, %c0_i32 from (2) to (7) : (!fir.array<8xi32>, i32) -> !fir.array<8xi32>
+  fir.has_value %3 : !fir.array<8xi32>
+}
+
+// CHECK:          llvm.mlir.global internal @_QEpartial_init()
+// GENERIC-SAME: {addr_space = 0 : i32}
+// AMDGPU-SAME: {addr_space = 1 : i32}
+// CHECK-SAME: : !llvm.array<8 x i32> {
+// CHECK:            %[[CST:.*]] = llvm.mlir.constant(dense<[7, 77, 0, 0, 0, 0, 0, 0]> : tensor<8xi32>) : !llvm.array<8 x i32>
+// CHECK:            llvm.return %[[CST]] : !llvm.array<8 x i32>
+// CHECK:          }
+
+// -----
+
 // Test global with box
 
 fir.global internal @_QFEx : !fir.box<!fir.ptr<i32>> {



More information about the flang-commits mailing list