[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