[flang-commits] [flang] 5eaa5ca - [flang] Speed up large CHARACTER DATA initializers (#218813)

via flang-commits flang-commits at lists.llvm.org
Thu Aug 27 19:28:10 PDT 2026


Author: Valentin Clement (バレンタイン クレメン)
Date: 2026-08-27T19:28:05-07:00
New Revision: 5eaa5ca54df58913f467a4d7318d91958d903345

URL: https://github.com/llvm/llvm-project/commit/5eaa5ca54df58913f467a4d7318d91958d903345
DIFF: https://github.com/llvm/llvm-project/commit/5eaa5ca54df58913f467a4d7318d91958d903345.diff

LOG: [flang] Speed up large CHARACTER DATA initializers (#218813)

[flang] Speed up large CHARACTER DATA initializers

Repeated CHARACTER(KIND=1) array constants were lowered as one
fir.insert_value per element. Converting those chains to LLVM IR is
quadratic and can make compilation take tens of minutes.

Lower consecutive equal KIND=1 character elements with
fir.insert_on_range
and emit full-range initializers as a single flattened [N x i8] LLVM
global
string, keeping Fortran blank padding.

A 160000-element character DATA statement now compiles in well under a
second and before was more than 10 minutes.

Added: 
    flang/test/Lower/character-array-constant.f90

Modified: 
    flang/lib/Lower/ConvertConstant.cpp
    flang/lib/Optimizer/CodeGen/CodeGen.cpp

Removed: 
    


################################################################################
diff  --git a/flang/lib/Lower/ConvertConstant.cpp b/flang/lib/Lower/ConvertConstant.cpp
index 70dc4c77ab869..6525ce50f9fdc 100644
--- a/flang/lib/Lower/ConvertConstant.cpp
+++ b/flang/lib/Lower/ConvertConstant.cpp
@@ -627,22 +627,24 @@ genInlinedArrayLit(Fortran::lower::AbstractConverter &converter,
   mlir::Value array = fir::UndefOp::create(builder, loc, arrayTy);
   if (Fortran::evaluate::GetSize(con.shape()) == 0)
     return array;
-  if constexpr (T::category == Fortran::common::TypeCategory::Character) {
+  if constexpr (T::category == Fortran::common::TypeCategory::Derived) {
     do {
+      mlir::Type eleTy =
+          mlir::cast<fir::SequenceType>(arrayTy).getElementType();
       mlir::Value elementVal =
-          genScalarLit<T::kind>(builder, loc, con.At(subscripts), con.LEN(),
-                                /*outlineInReadOnlyMemory=*/false);
+          genScalarLit(converter, loc, con.At(subscripts), eleTy,
+                       /*outlineInReadOnlyMemory=*/false);
       array =
           fir::InsertValueOp::create(builder, loc, arrayTy, array, elementVal,
                                      builder.getArrayAttr(createIdx()));
     } while (con.IncrementSubscripts(subscripts));
-  } else if constexpr (T::category == Fortran::common::TypeCategory::Derived) {
+  } else if constexpr (T::category ==
+                           Fortran::common::TypeCategory::Character &&
+                       T::kind != 1) {
     do {
-      mlir::Type eleTy =
-          mlir::cast<fir::SequenceType>(arrayTy).getElementType();
       mlir::Value elementVal =
-          genScalarLit(converter, loc, con.At(subscripts), eleTy,
-                       /*outlineInReadOnlyMemory=*/false);
+          genScalarLit<T::kind>(builder, loc, con.At(subscripts), con.LEN(),
+                                /*outlineInReadOnlyMemory=*/false);
       array =
           fir::InsertValueOp::create(builder, loc, arrayTy, array, elementVal,
                                      builder.getArrayAttr(createIdx()));
@@ -653,9 +655,14 @@ genInlinedArrayLit(Fortran::lower::AbstractConverter &converter,
     mlir::Type eleTy = mlir::cast<fir::SequenceType>(arrayTy).getElementType();
     do {
       auto getElementVal = [&]() {
-        return builder.createConvert(loc, eleTy,
-                                     genScalarLit<T::category, T::kind>(
-                                         builder, loc, con.At(subscripts)));
+        if constexpr (T::category == Fortran::common::TypeCategory::Character)
+          return genScalarLit<T::kind>(builder, loc, con.At(subscripts),
+                                       con.LEN(),
+                                       /*outlineInReadOnlyMemory=*/false);
+        else
+          return builder.createConvert(loc, eleTy,
+                                       genScalarLit<T::category, T::kind>(
+                                           builder, loc, con.At(subscripts)));
       };
       Fortran::evaluate::ConstantSubscripts nextSubscripts = subscripts;
       bool nextIsSame = con.IncrementSubscripts(nextSubscripts) &&

diff  --git a/flang/lib/Optimizer/CodeGen/CodeGen.cpp b/flang/lib/Optimizer/CodeGen/CodeGen.cpp
index c5360d468bdd7..fb98ca5fb5dcd 100644
--- a/flang/lib/Optimizer/CodeGen/CodeGen.cpp
+++ b/flang/lib/Optimizer/CodeGen/CodeGen.cpp
@@ -2981,13 +2981,24 @@ struct InsertOnRangeOpConversion
 
     auto arrayType = adaptor.getSeq().getType();
 
-    // Iteratively extract the array dimensions from the type.
+    // Extract the dimensions of the array being initialized. Only those are
+    // used to expand a fallback insert chain. Any remaining LLVM array
+    // dimension belongs to an aggregate element type: a CHARACTER element is
+    // itself an array of characters.
     llvm::SmallVector<std::int64_t> dims;
     mlir::Type type = arrayType;
-    while (auto t = mlir::dyn_cast<mlir::LLVM::LLVMArrayType>(type)) {
+    for (std::size_t i = 0, rank = range.getType().getShape().size(); i < rank;
+         ++i) {
+      auto t = mlir::cast<mlir::LLVM::LLVMArrayType>(type);
       dims.push_back(t.getNumElements());
       type = t.getElementType();
     }
+    llvm::SmallVector<std::int64_t> elementDims;
+    mlir::Type scalarType = type;
+    while (auto t = mlir::dyn_cast<mlir::LLVM::LLVMArrayType>(scalarType)) {
+      elementDims.push_back(t.getNumElements());
+      scalarType = t.getElementType();
+    }
 
     // Avoid generating long insert chain that are very slow to fold back
     // (which is required in globals when later generating LLVM IR). Attempt to
@@ -2997,16 +3008,45 @@ struct InsertOnRangeOpConversion
       llvm::FailureOr<mlir::Attribute> cst =
           fir::tryFoldingLLVMInsertChain(adaptor.getVal(), rewriter);
       if (llvm::succeeded(cst)) {
-        mlir::Attribute dimVal = *cst;
-        for (auto dim : llvm::reverse(dims)) {
-          // Use std::vector in case the number of elements is big.
-          std::vector<mlir::Attribute> elements(dim, dimVal);
-          dimVal = mlir::ArrayAttr::get(range.getContext(), elements);
+        if (elementDims.empty()) {
+          mlir::Attribute dimVal = *cst;
+          for (auto dim : llvm::reverse(dims)) {
+            // Use std::vector in case the number of elements is big.
+            std::vector<mlir::Attribute> elements(dim, dimVal);
+            dimVal = mlir::ArrayAttr::get(range.getContext(), elements);
+          }
+          // Replace insert chain with constant.
+          rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>(range, arrayType,
+                                                              dimVal);
+          return mlir::success();
+        }
+        // An array with an aggregate element type cannot be described by a
+        // nested ArrayAttr. A CHARACTER array is a dense array of characters,
+        // so replicate the element data into a dense attribute.
+        if (auto strAttr = mlir::dyn_cast<mlir::StringAttr>(*cst)) {
+          llvm::StringRef element = strAttr.getValue();
+          std::int64_t elementSize = 1;
+          for (std::int64_t dim : elementDims)
+            elementSize *= dim;
+          if (scalarType.isInteger(8) &&
+              element.size() == static_cast<std::size_t>(elementSize)) {
+            std::int64_t count = 1;
+            for (std::int64_t dim : dims)
+              count *= dim;
+            std::string data;
+            data.reserve(count * element.size());
+            for (std::int64_t i = 0; i < count; ++i)
+              data.append(element.data(), element.size());
+            llvm::SmallVector<std::int64_t> shape(dims);
+            shape.append(elementDims);
+            auto denseAttr = mlir::DenseElementsAttr::getFromRawBuffer(
+                mlir::RankedTensorType::get(shape, scalarType),
+                llvm::ArrayRef(data.data(), data.size()));
+            rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>(
+                range, arrayType, denseAttr);
+            return mlir::success();
+          }
         }
-        // Replace insert chain with constant.
-        rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>(range, arrayType,
-                                                            dimVal);
-        return mlir::success();
       }
     }
 

diff  --git a/flang/test/Lower/character-array-constant.f90 b/flang/test/Lower/character-array-constant.f90
new file mode 100644
index 0000000000000..274d68b1adf62
--- /dev/null
+++ b/flang/test/Lower/character-array-constant.f90
@@ -0,0 +1,40 @@
+! RUN: bbc -emit-hlfir %s -o - | FileCheck %s
+! RUN: %flang_fc1 -emit-llvm %s -o - | FileCheck %s --check-prefix=LLVM
+
+program character_array_constant
+  character(4) :: values(10000)
+  character(4) :: words(3)
+  data values / 10000 * ' ' /
+  data words / 3 * 'ab' /
+end program
+
+! CHECK-LABEL: fir.global internal @_QFEvalues
+! CHECK: %[[UNDEF:.*]] = fir.undefined !fir.array<10000x!fir.char<1,4>>
+! CHECK-NEXT: %[[SPACE:.*]] = fir.string_lit "    "(4) : !fir.char<1,4>
+! CHECK-NEXT: %[[INIT:.*]] = fir.insert_on_range %[[UNDEF]], %[[SPACE]] from (0) to (9999) : (!fir.array<10000x!fir.char<1,4>>, !fir.char<1,4>) -> !fir.array<10000x!fir.char<1,4>>
+! CHECK: fir.has_value %[[INIT]] : !fir.array<10000x!fir.char<1,4>>
+
+! CHECK-LABEL: fir.global internal @_QFEwords
+! CHECK: %[[UNDEF:.*]] = fir.undefined !fir.array<3x!fir.char<1,4>>
+! CHECK-NEXT: %[[AB:.*]] = fir.string_lit "ab "(4) : !fir.char<1,4>
+! CHECK-NEXT: %[[INIT:.*]] = fir.insert_on_range %[[UNDEF]], %[[AB]] from (0) to (2) : (!fir.array<3x!fir.char<1,4>>, !fir.char<1,4>) -> !fir.array<3x!fir.char<1,4>>
+! CHECK: fir.has_value %[[INIT]] : !fir.array<3x!fir.char<1,4>>
+
+! LLVM: @_QFEvalues = internal global [10000 x [4 x i8]]
+! LLVM: @_QFEwords = internal global [3 x [4 x i8]] [{{.*}}c"ab  ", {{.*}}c"ab  ", {{.*}}c"ab  "]
+
+! A CHARACTER array component is an array of characters nested inside a
+! structure, so its initial value cannot be described by an array attribute.
+subroutine component()
+  type t
+    character(2) :: c(2)
+  end type
+  type(t), save :: x = t(c=["ab", "ab"])
+  call use_x(x)
+end subroutine
+
+! CHECK-LABEL: fir.global internal @_QFcomponentEx
+! CHECK: %[[AB:.*]] = fir.string_lit "ab"(2) : !fir.char<1,2>
+! CHECK-NEXT: fir.insert_on_range %{{.*}}, %[[AB]] from (0) to (1) : (!fir.array<2x!fir.char<1,2>>, !fir.char<1,2>) -> !fir.array<2x!fir.char<1,2>>
+
+! LLVM: @_QFcomponentEx = internal global %_QFcomponentTt { [2 x [2 x i8]] [{{.*}}c"ab", {{.*}}c"ab"] }


        


More information about the flang-commits mailing list