[flang-commits] [flang] [flang][lowering] Implement component-wise initialization for derived types (PR #187465)
via flang-commits
flang-commits at lists.llvm.org
Thu Mar 26 02:49:00 PDT 2026
https://github.com/refuseno updated https://github.com/llvm/llvm-project/pull/187465
>From d1c9fb4cb033bbc5b9e8e5fb95dfa902b7f79496 Mon Sep 17 00:00:00 2001
From: hechenxi <hechenxi at bosc.ac.cn>
Date: Thu, 19 Mar 2026 16:27:03 +0800
Subject: [PATCH 1/4] component-wise-init
Co-authored-by: ict-ql <168183727+ict-ql at users.noreply.github.com>
Co-authored-by: buggfg <wangyingying at bosc.ac.cn>
---
flang/lib/Lower/ConvertVariable.cpp | 369 +++++++++++++++++++----
flang/test/Lower/component-wise-init.f90 | 94 ++++++
2 files changed, 407 insertions(+), 56 deletions(-)
create mode 100644 flang/test/Lower/component-wise-init.f90
diff --git a/flang/lib/Lower/ConvertVariable.cpp b/flang/lib/Lower/ConvertVariable.cpp
index 0ededb364bfea..16509b855ce9e 100644
--- a/flang/lib/Lower/ConvertVariable.cpp
+++ b/flang/lib/Lower/ConvertVariable.cpp
@@ -10,7 +10,6 @@
//
//===----------------------------------------------------------------------===//
-#include "flang/Lower/ConvertVariable.h"
#include "flang/Lower/AbstractConverter.h"
#include "flang/Lower/Allocatable.h"
#include "flang/Lower/BoxAnalyzer.h"
@@ -20,6 +19,7 @@
#include "flang/Lower/ConvertExpr.h"
#include "flang/Lower/ConvertExprToHLFIR.h"
#include "flang/Lower/ConvertProcedureDesignator.h"
+#include "flang/Lower/ConvertVariable.h"
#include "flang/Lower/Mangler.h"
#include "flang/Lower/PFTBuilder.h"
#include "flang/Lower/StatementContext.h"
@@ -32,12 +32,15 @@
#include "flang/Optimizer/Builder/IntrinsicCall.h"
#include "flang/Optimizer/Builder/Runtime/Derived.h"
#include "flang/Optimizer/Builder/Todo.h"
+#include "flang/Optimizer/CodeGen/TypeConverter.h"
#include "flang/Optimizer/Dialect/CUF/CUFOps.h"
#include "flang/Optimizer/Dialect/FIRAttr.h"
#include "flang/Optimizer/Dialect/FIRDialect.h"
#include "flang/Optimizer/Dialect/FIROps.h"
+#include "flang/Optimizer/Dialect/FIRType.h"
#include "flang/Optimizer/Dialect/Support/FIRContext.h"
#include "flang/Optimizer/HLFIR/HLFIROps.h"
+#include "flang/Optimizer/Support/DataLayout.h"
#include "flang/Optimizer/Support/FatalError.h"
#include "flang/Optimizer/Support/InternalNames.h"
#include "flang/Optimizer/Support/Utils.h"
@@ -48,6 +51,11 @@
#include "llvm/Support/Debug.h"
#include <optional>
+static llvm::cl::opt<bool> enablePreciseInit(
+ "enable-precise-init",
+ llvm::cl::desc("Enable precise component initialization for derived types"),
+ llvm::cl::init(false));
+
static llvm::cl::opt<bool>
allowAssumedRank("allow-assumed-rank",
llvm::cl::desc("Enable assumed rank lowering"),
@@ -585,15 +593,14 @@ fir::GlobalOp Fortran::lower::defineGlobal(
if (details && details->init()) {
auto sym{*details->init()};
if (sym) // Has a procedure target.
- createGlobalInitialization(
- builder, global, [&](fir::FirOpBuilder &b) {
- Fortran::lower::StatementContext stmtCtx(
- /*cleanupProhibited=*/true);
- auto box{Fortran::lower::convertProcedureDesignatorInitialTarget(
- converter, loc, *sym)};
- auto castTo{builder.createConvert(loc, symTy, box)};
- fir::HasValueOp::create(b, loc, castTo);
- });
+ createGlobalInitialization(builder, global, [&](fir::FirOpBuilder &b) {
+ Fortran::lower::StatementContext stmtCtx(
+ /*cleanupProhibited=*/true);
+ auto box{Fortran::lower::convertProcedureDesignatorInitialTarget(
+ converter, loc, *sym)};
+ auto castTo{builder.createConvert(loc, symTy, box)};
+ fir::HasValueOp::create(b, loc, castTo);
+ });
else { // Has NULL() target.
createGlobalInitialization(builder, global, [&](fir::FirOpBuilder &b) {
auto box{fir::factory::createNullBoxProc(b, loc, symTy)};
@@ -812,6 +819,244 @@ mustBeDefaultInitializedAtRuntime(const Fortran::lower::pft::Variable &var) {
return Fortran::lower::hasDefaultInitialization(sym);
}
+namespace {
+/// Determines if \p sym represents a complex derived type.
+/// A derived type is considered complex if it contains allocatable, pointer,
+/// or procedure pointer components, or nested complex derived types.
+static bool isComplexDerivedType(const Fortran::semantics::Symbol &sym) {
+ const Fortran::semantics::DeclTypeSpec *declTy = sym.GetType();
+ if (!declTy || !declTy->AsDerived())
+ return false;
+ const Fortran::semantics::DerivedTypeSpec &derivedSpec = *declTy->AsDerived();
+ const Fortran::semantics::Scope *scope = derivedSpec.GetScope();
+ if (!scope)
+ return false;
+ const auto &typeDetails =
+ derivedSpec.typeSymbol().get<Fortran::semantics::DerivedTypeDetails>();
+ for (const auto &compName : typeDetails.componentNames()) {
+ auto iter = scope->find(compName);
+ if (iter == scope->cend())
+ continue;
+ const Fortran::semantics::Symbol &compSym = iter->second.get();
+ const auto *objDetails =
+ compSym.detailsIf<Fortran::semantics::ObjectEntityDetails>();
+ const auto *procDetails =
+ compSym.detailsIf<Fortran::semantics::ProcEntityDetails>();
+ if (Fortran::semantics::IsAllocatableOrPointer(compSym)) {
+ return true;
+ } else if (procDetails && procDetails->init().has_value()) {
+ return true;
+ } else if (objDetails && objDetails->init() && compSym.Rank() > 0) {
+ return true;
+ } else if (isComplexDerivedType(compSym)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/// Performs precise, component-wise initialization for \p derivedSpec,
+/// selectively generating IR only for components that require it.
+static void genDerivedTypeComponentInit(
+ Fortran::lower::AbstractConverter &converter, mlir::Location loc,
+ const Fortran::semantics::DerivedTypeSpec &derivedSpec,
+ mlir::Value baseAddr, fir::RecordType recTy) {
+ fir::FirOpBuilder &builder = converter.getFirOpBuilder();
+ const Fortran::semantics::Scope *scope = derivedSpec.GetScope();
+ if (!scope)
+ return;
+ const auto &typeDetails =
+ derivedSpec.typeSymbol().get<Fortran::semantics::DerivedTypeDetails>();
+ for (const auto &compName : typeDetails.componentNames()) {
+ auto scopeIter = scope->find(compName);
+ if (scopeIter == scope->cend())
+ continue;
+ const Fortran::semantics::Symbol &compSym = scopeIter->second.get();
+ const auto *objDetails =
+ compSym.detailsIf<Fortran::semantics::ObjectEntityDetails>();
+ const auto *procDetails =
+ compSym.detailsIf<Fortran::semantics::ProcEntityDetails>();
+ // Determine if the component requires initialization.
+ // Dynamic descriptors (pointers/allocatables) and explicitly initialized
+ // entities must be processed.
+ bool needsInit = Fortran::semantics::IsAllocatableOrPointer(compSym) ||
+ (objDetails && objDetails->init()) ||
+ (procDetails && procDetails->init().has_value());
+ if (!needsInit && !Fortran::lower::hasDefaultInitialization(compSym))
+ continue;
+ std::string name = converter.getRecordTypeFieldName(compSym);
+ mlir::Type compFirTy = recTy.getType(name);
+ if (!compFirTy)
+ continue;
+ // Compute the memory coordinate of the current component.
+ auto fieldIdx = builder.create<fir::FieldIndexOp>(
+ loc, fir::FieldType::get(recTy.getContext()), name, recTy,
+ mlir::ValueRange{});
+ auto compAddr =
+ builder.create<fir::CoordinateOp>(loc, builder.getRefType(compFirTy),
+ baseAddr, mlir::ValueRange{fieldIdx});
+
+ // Case A: Standard data components (ObjectEntityDetails).
+ if (objDetails) {
+ if (objDetails->init()) {
+ // Subcase A.1: Component has explicit initialization.
+ if (Fortran::semantics::IsPointer(compSym)) {
+ // Subcase A.1.1: Pointer with explicit target.
+ std::string globalName = fir::NameUniquer::doGenerated(
+ (converter.mangleName(derivedSpec) + fir::kNameSeparator + name +
+ fir::kNameSeparator + "init")
+ .str());
+ fir::GlobalOp global = builder.getNamedGlobal(globalName);
+ if (!global) {
+ global = builder.createGlobal(loc, compFirTy, globalName,
+ builder.createInternalLinkage(),
+ mlir::Attribute{}, true);
+ createGlobalInitialization(
+ builder, global, [&](fir::FirOpBuilder &b) {
+ mlir::Value initBox = Fortran::lower::genInitialDataTarget(
+ converter, loc, compFirTy, *objDetails->init());
+ b.create<fir::HasValueOp>(loc, initBox);
+ });
+ }
+ auto srcAddr = builder.create<fir::AddrOfOp>(loc, global.resultType(),
+ global.getSymbol());
+ mlir::Value loadedBox = builder.create<fir::LoadOp>(loc, srcAddr);
+ builder.create<fir::StoreOp>(loc, loadedBox, compAddr);
+ } else {
+ // Subcase A.1.2: Non-pointer components with explicit initialization.
+ Fortran::lower::StatementContext stmtCtx(/*cleanupProhibited=*/true);
+ if (mlir::isa<fir::SequenceType>(compFirTy)) {
+ std::string globalName = fir::NameUniquer::doGenerated(
+ (converter.mangleName(derivedSpec) + fir::kNameSeparator +
+ name + fir::kNameSeparator + "arr_init")
+ .str());
+ fir::GlobalOp global = builder.getNamedGlobal(globalName);
+ if (!global) {
+ const auto &initExpr = objDetails->init().value();
+ cuf::DataAttributeAttr dataAttr = {};
+ global = Fortran::lower::tryCreatingDenseGlobal(
+ builder, loc, compFirTy, globalName,
+ builder.createInternalLinkage(), /*isConst=*/true, initExpr,
+ dataAttr);
+ }
+ if (global) {
+ auto srcAddr = builder.create<fir::AddrOfOp>(
+ loc, global.resultType(), global.getSymbol());
+ builder.create<fir::CopyOp>(loc, srcAddr, compAddr,
+ /*noOverlap=*/true);
+ } else {
+ Fortran::lower::StatementContext inlineCtx;
+ Fortran::lower::SymMap emptyMap;
+ hlfir::Entity lhs{compAddr};
+ hlfir::Entity rhs = Fortran::lower::convertExprToHLFIR(
+ loc, converter, objDetails->init().value(), emptyMap,
+ inlineCtx);
+ builder.create<hlfir::AssignOp>(loc, rhs, lhs);
+ }
+ } else {
+ // Scalar component: Evaluate the expression and store directly.
+ fir::ExtendedValue exInitVal = genInitializerExprValue(
+ converter, loc, objDetails->init().value(), stmtCtx);
+ mlir::Value initVal = fir::getBase(exInitVal);
+ if (fir::isa_ref_type(initVal.getType()) &&
+ !fir::isa_ref_type(compFirTy))
+ initVal = builder.create<fir::LoadOp>(loc, initVal);
+ mlir::Value castVal =
+ builder.createConvert(loc, compFirTy, initVal);
+ builder.create<fir::StoreOp>(loc, castVal, compAddr);
+ }
+ }
+ } else if (Fortran::semantics::IsAllocatableOrPointer(compSym)) {
+ // Subcase A.2: Pointer or allocatable without initialization.
+ // Create deallocated/disassociated value.
+ std::string globalName = fir::NameUniquer::doGenerated(
+ (converter.mangleName(derivedSpec) + fir::kNameSeparator + name +
+ fir::kNameSeparator + "null_box")
+ .str());
+ fir::GlobalOp global = builder.getNamedGlobal(globalName);
+ if (!global) {
+ global = builder.createGlobal(loc, compFirTy, globalName,
+ builder.createInternalLinkage(),
+ mlir::Attribute{}, true);
+ createGlobalInitialization(
+ builder, global, [&](fir::FirOpBuilder &b) {
+ mlir::Value nullBox = fir::factory::createUnallocatedBox(
+ b, loc, compFirTy, mlir::ValueRange{});
+ b.create<fir::HasValueOp>(loc, nullBox);
+ });
+ }
+ auto srcAddr = builder.create<fir::AddrOfOp>(loc, global.resultType(),
+ global.getSymbol());
+ mlir::Value loadedBox = builder.create<fir::LoadOp>(loc, srcAddr);
+ builder.create<fir::StoreOp>(loc, loadedBox, compAddr);
+ } else if (Fortran::lower::hasDefaultInitialization(compSym)) {
+ // Subcase A.3: Nested derived type requires initialization.
+ // Derived type has no explicit initialization but contains
+ // components requiring initialization.
+ if (mlir::isa<fir::SequenceType>(compFirTy) &&
+ isComplexDerivedType(compSym)) {
+ // Complex derived type arrays.
+ mlir::Value box =
+ builder.createBox(loc, fir::ExtendedValue(compAddr));
+ fir::runtime::genDerivedTypeInitialize(builder, loc, box);
+ } else if (isComplexDerivedType(compSym)) {
+ // Complex derived type scalars.
+ const Fortran::semantics::DeclTypeSpec *compDeclTy =
+ compSym.GetType();
+ auto nestedRecTy = mlir::dyn_cast<fir::RecordType>(compFirTy);
+ genDerivedTypeComponentInit(converter, loc, *compDeclTy->AsDerived(),
+ compAddr, nestedRecTy);
+ } else {
+ // Simple derived type scalars.
+ Fortran::lower::StatementContext stmtCtx;
+ mlir::Value initVal = genDefaultInitializerValue(
+ converter, loc, compSym, compFirTy, stmtCtx);
+ mlir::Value castVal = builder.createConvert(loc, compFirTy, initVal);
+ builder.create<fir::StoreOp>(loc, castVal, compAddr);
+ }
+ }
+ }
+ // Case B: Procedure pointers (ProcEntityDetails).
+ else if (procDetails) {
+ if (procDetails->init().has_value()) {
+ // Has a procedure or NULL() target.
+ std::string globalName = fir::NameUniquer::doGenerated(
+ (converter.mangleName(derivedSpec) + fir::kNameSeparator + name +
+ fir::kNameSeparator + "init")
+ .str());
+ fir::GlobalOp global = builder.getNamedGlobal(globalName);
+ if (!global) {
+ global = builder.createGlobal(loc, compFirTy, globalName,
+ builder.createInternalLinkage(),
+ mlir::Attribute{}, true);
+ createGlobalInitialization(
+ builder, global, [&](fir::FirOpBuilder &b) {
+ mlir::Value procBox;
+ auto targetSym = *procDetails->init();
+ if (targetSym) {
+ procBox =
+ Fortran::lower::convertProcedureDesignatorInitialTarget(
+ converter, loc, *targetSym);
+ procBox = b.createConvert(loc, compFirTy, procBox);
+ } else {
+ procBox = fir::factory::createNullBoxProc(b, loc, compFirTy);
+ }
+ b.create<fir::HasValueOp>(loc, procBox);
+ });
+ }
+ auto srcAddr = builder.create<fir::AddrOfOp>(loc, global.resultType(),
+ global.getSymbol());
+ mlir::Value loadedBox = builder.create<fir::LoadOp>(loc, srcAddr);
+ builder.create<fir::StoreOp>(loc, loadedBox, compAddr);
+ } else {
+ // Has no initialization.
+ continue;
+ }
+ }
+ }
+}
+} // namespace
+
/// Call default initialization runtime routine to initialize \p var.
void Fortran::lower::defaultInitializeAtRuntime(
Fortran::lower::AbstractConverter &converter,
@@ -823,8 +1068,8 @@ void Fortran::lower::defaultInitializeAtRuntime(
// 15.5.2.12 point 3, absent optional dummies are not initialized.
// Creating descriptor/passing null descriptor to the runtime would
// create runtime crashes.
- auto isPresent = fir::IsPresentOp::create(builder, loc, builder.getI1Type(),
- fir::getBase(exv));
+ auto isPresent = builder.create<fir::IsPresentOp>(loc, builder.getI1Type(),
+ fir::getBase(exv));
builder.genIfThen(loc, isPresent)
.genThen([&]() {
auto box = builder.createBox(loc, exv);
@@ -839,56 +1084,68 @@ void Fortran::lower::defaultInitializeAtRuntime(
mlir::Type symTy = converter.genType(sym);
const auto *details =
sym.detailsIf<Fortran::semantics::ObjectEntityDetails>();
- if (details && !Fortran::semantics::IsPolymorphic(sym) &&
+ bool useFastInit =
+ details && !Fortran::semantics::IsPolymorphic(sym) &&
declTy->category() ==
Fortran::semantics::DeclTypeSpec::Category::TypeDerived &&
!mlir::isa<fir::SequenceType>(symTy) &&
!sym.test(Fortran::semantics::Symbol::Flag::OmpPrivate) &&
!sym.test(Fortran::semantics::Symbol::Flag::OmpFirstPrivate) &&
- !Fortran::semantics::HasCUDAComponent(sym)) {
- std::string globalName = fir::NameUniquer::doGenerated(
- (converter.mangleName(*declTy->AsDerived()) + fir::kNameSeparator +
- fir::kDerivedTypeInitSuffix)
- .str());
- mlir::Location loc = genLocation(converter, sym);
- mlir::StringAttr linkage = builder.createInternalLinkage();
- fir::GlobalOp global = builder.getNamedGlobal(globalName);
- if (!global && details->init()) {
- global = builder.createGlobal(loc, symTy, globalName, linkage,
- mlir::Attribute{},
- /*isConst=*/true,
- /*isTarget=*/false,
- /*dataAttr=*/{});
- createGlobalInitialization(
- builder, global, [&](fir::FirOpBuilder &builder) {
- Fortran::lower::StatementContext stmtCtx(
- /*cleanupProhibited=*/true);
- fir::ExtendedValue initVal = genInitializerExprValue(
- converter, loc, details->init().value(), stmtCtx);
- mlir::Value castTo =
- builder.createConvert(loc, symTy, fir::getBase(initVal));
- fir::HasValueOp::create(builder, loc, castTo);
- });
- } else if (!global) {
- global = builder.createGlobal(loc, symTy, globalName, linkage,
- mlir::Attribute{},
- /*isConst=*/true,
- /*isTarget=*/false,
- /*dataAttr=*/{});
- createGlobalInitialization(
- builder, global, [&](fir::FirOpBuilder &builder) {
- Fortran::lower::StatementContext stmtCtx(
- /*cleanupProhibited=*/true);
- mlir::Value initVal = genDefaultInitializerValue(
- converter, loc, sym, symTy, stmtCtx);
- mlir::Value castTo = builder.createConvert(loc, symTy, initVal);
- fir::HasValueOp::create(builder, loc, castTo);
- });
+ !Fortran::semantics::HasCUDAComponent(sym);
+ if (useFastInit) {
+ if (enablePreciseInit) {
+ // Precise initialization
+ mlir::Value baseAddr = fir::getBase(exv);
+ const auto &derivedSpec = *sym.GetType()->AsDerived();
+ auto recTy = mlir::cast<fir::RecordType>(
+ fir::unwrapPassByRefType(baseAddr.getType()));
+ genDerivedTypeComponentInit(converter, loc, derivedSpec, baseAddr,
+ recTy);
+ } else {
+ std::string globalName = fir::NameUniquer::doGenerated(
+ (converter.mangleName(*declTy->AsDerived()) + fir::kNameSeparator +
+ fir::kDerivedTypeInitSuffix)
+ .str());
+ mlir::Location loc = genLocation(converter, sym);
+ mlir::StringAttr linkage = builder.createInternalLinkage();
+ fir::GlobalOp global = builder.getNamedGlobal(globalName);
+ if (!global && details->init()) {
+ global = builder.createGlobal(loc, symTy, globalName, linkage,
+ mlir::Attribute{},
+ /*isConst=*/true,
+ /*isTarget=*/false,
+ /*dataAttr=*/{});
+ createGlobalInitialization(
+ builder, global, [&](fir::FirOpBuilder &builder) {
+ Fortran::lower::StatementContext stmtCtx(
+ /*cleanupProhibited=*/true);
+ fir::ExtendedValue initVal = genInitializerExprValue(
+ converter, loc, details->init().value(), stmtCtx);
+ mlir::Value castTo =
+ builder.createConvert(loc, symTy, fir::getBase(initVal));
+ builder.create<fir::HasValueOp>(loc, castTo);
+ });
+ } else if (!global) {
+ global = builder.createGlobal(loc, symTy, globalName, linkage,
+ mlir::Attribute{},
+ /*isConst=*/true,
+ /*isTarget=*/false,
+ /*dataAttr=*/{});
+ createGlobalInitialization(
+ builder, global, [&](fir::FirOpBuilder &builder) {
+ Fortran::lower::StatementContext stmtCtx(
+ /*cleanupProhibited=*/true);
+ mlir::Value initVal = genDefaultInitializerValue(
+ converter, loc, sym, symTy, stmtCtx);
+ mlir::Value castTo = builder.createConvert(loc, symTy, initVal);
+ builder.create<fir::HasValueOp>(loc, castTo);
+ });
+ }
+ auto addrOf = builder.create<fir::AddrOfOp>(loc, global.resultType(),
+ global.getSymbol());
+ builder.create<fir::CopyOp>(loc, addrOf, fir::getBase(exv),
+ /*noOverlap=*/true);
}
- auto addrOf = fir::AddrOfOp::create(builder, loc, global.resultType(),
- global.getSymbol());
- fir::CopyOp::create(builder, loc, addrOf, fir::getBase(exv),
- /*noOverlap=*/true);
} else {
mlir::Value box = builder.createBox(loc, exv);
fir::runtime::genDerivedTypeInitialize(builder, loc, box);
diff --git a/flang/test/Lower/component-wise-init.f90 b/flang/test/Lower/component-wise-init.f90
new file mode 100644
index 0000000000000..2b751e784dc47
--- /dev/null
+++ b/flang/test/Lower/component-wise-init.f90
@@ -0,0 +1,94 @@
+! RUN: bbc -emit-fir -enable-precise-init %s -o - | FileCheck %s
+
+! Test for precise component-wise initialization of derived types
+! to avoid generating large, fully-initialized global templates (sparse initialization).
+
+module my_types
+ type :: InnerType
+ integer :: flag = 42
+ real, pointer :: p
+ real, allocatable :: arr(:)
+ end type InnerType
+
+ type :: OuterType
+ real, pointer :: q
+ integer :: explicit_arr(3) = [10, 20, 30]
+ real(8) :: uninit_buffer(80, 100, 100)
+ type(InnerType) :: nested
+ type(InnerType) :: nested_arr(2)
+ end type OuterType
+end module my_types
+
+subroutine test_complex_init()
+ use my_types
+ type(OuterType) :: my_var
+ call do_something(my_var)
+end subroutine test_complex_init
+
+! ==============================================================================
+! FileCheck Assertions
+! ==============================================================================
+
+! CHECK-LABEL: func.func @_QPtest_complex_init()
+
+! Ensure we allocate the local variable
+! CHECK: %[[MY_VAR:.*]] = fir.alloca !fir.type<_QMmy_typesToutertype{{.*}}>
+
+! ------------------------------------------------------------------------------
+! 1. Check pointer 'q' initialization (First component, offset 0, no coordinate_of needed)
+! ------------------------------------------------------------------------------
+! CHECK: %[[Q_NULL_BOX_ADDR:.*]] = fir.address_of(@_QQ_QMmy_typesToutertype.q.{{(null_box|init)}})
+! CHECK: %[[Q_NULL_BOX:.*]] = fir.load %[[Q_NULL_BOX_ADDR]]
+! CHECK: fir.store %[[Q_NULL_BOX]] to %{{.*}}
+
+! ------------------------------------------------------------------------------
+! 2. Check explicit array initialization
+! ------------------------------------------------------------------------------
+! CHECK: %[[EXPLICIT_ARR_ADDR:.*]] = fir.coordinate_of %{{.*}}, explicit_arr
+! CHECK: %[[EXPLICIT_ARR_INIT:.*]] = fir.address_of(@_QQ_QMmy_typesToutertype.explicit_arr.arr_init)
+! CHECK: fir.copy %[[EXPLICIT_ARR_INIT]] to %[[EXPLICIT_ARR_ADDR]]
+
+! ------------------------------------------------------------------------------
+! 3. Check scalar derived type 'nested' component-wise precise initialization
+! ------------------------------------------------------------------------------
+! CHECK: %[[NESTED_ADDR:.*]] = fir.coordinate_of %{{.*}}, nested
+
+! -> Check 'flag' (integer = 42)
+! CHECK: %[[FLAG_ADDR:.*]] = fir.coordinate_of %[[NESTED_ADDR]], flag
+! CHECK: fir.store %c42{{.*}} to %[[FLAG_ADDR]]
+
+! -> Check 'p' (pointer)
+! CHECK: %[[P_ADDR:.*]] = fir.coordinate_of %[[NESTED_ADDR]], p
+! CHECK: %[[P_INIT_ADDR:.*]] = fir.address_of(@_QQ_QMmy_typesTinnertype.p.{{(null_box|init)}})
+! CHECK: %[[P_INIT_VAL:.*]] = fir.load %[[P_INIT_ADDR]]
+! CHECK: fir.store %[[P_INIT_VAL]] to %[[P_ADDR]]
+
+! -> Check 'arr' (allocatable)
+! CHECK: %[[ARR_ADDR:.*]] = fir.coordinate_of %[[NESTED_ADDR]], arr
+! CHECK: %[[ARR_INIT_ADDR:.*]] = fir.address_of(@_QQ_QMmy_typesTinnertype.arr.null_box)
+! CHECK: %[[ARR_INIT_VAL:.*]] = fir.load %[[ARR_INIT_ADDR]]
+! CHECK: fir.store %[[ARR_INIT_VAL]] to %[[ARR_ADDR]]
+
+! ------------------------------------------------------------------------------
+! 4. Check derived type array 'nested_arr' initialization (Runtime call)
+! ------------------------------------------------------------------------------
+! CHECK: %[[NESTED_ARR_ADDR:.*]] = fir.coordinate_of %{{.*}}, nested_arr
+! CHECK: %[[NESTED_ARR_BOX:.*]] = fir.embox %[[NESTED_ARR_ADDR]]
+! CHECK: %[[NESTED_ARR_PTR:.*]] = fir.convert %[[NESTED_ARR_BOX]]
+! CHECK: fir.call @_FortranAInitialize(%[[NESTED_ARR_PTR]], {{.*}})
+
+
+! ------------------------------------------------------------------------------
+! Global Variables Check
+! ------------------------------------------------------------------------------
+
+! CRITICAL: Ensure NO full derived type initialization templates are generated!
+! We should NOT see fir.global for the entire DerivedInit.
+! CHECK-NOT: fir.global internal @_QQ_QMmy_typesToutertype.DerivedInit
+! CHECK-NOT: fir.global internal @_QQ_QMmy_typesTinnertype.DerivedInit
+
+! Verify the fine-grained component global constants exist
+! CHECK-DAG: fir.global internal @_QQ_QMmy_typesToutertype.explicit_arr.arr_init(dense<[10, 20, 30]> : tensor<3xi32>)
+! CHECK-DAG: fir.global internal @_QQ_QMmy_typesToutertype.q.{{(null_box|init)}}
+! CHECK-DAG: fir.global internal @_QQ_QMmy_typesTinnertype.p.{{(null_box|init)}}
+! CHECK-DAG: fir.global internal @_QQ_QMmy_typesTinnertype.arr.null_box
\ No newline at end of file
>From 2013a119c00f304f8f6dde15129338376e0f59b2 Mon Sep 17 00:00:00 2001
From: hechenxi <hechenxi at bosc.ac.cn>
Date: Fri, 20 Mar 2026 16:08:44 +0800
Subject: [PATCH 2/4] Fix deprecated MLIR builder.create usages
---
flang/lib/Lower/ConvertVariable.cpp | 91 ++++++++++++++---------------
1 file changed, 45 insertions(+), 46 deletions(-)
diff --git a/flang/lib/Lower/ConvertVariable.cpp b/flang/lib/Lower/ConvertVariable.cpp
index 16509b855ce9e..c114643d89e5a 100644
--- a/flang/lib/Lower/ConvertVariable.cpp
+++ b/flang/lib/Lower/ConvertVariable.cpp
@@ -10,6 +10,7 @@
//
//===----------------------------------------------------------------------===//
+#include "flang/Lower/ConvertVariable.h"
#include "flang/Lower/AbstractConverter.h"
#include "flang/Lower/Allocatable.h"
#include "flang/Lower/BoxAnalyzer.h"
@@ -19,7 +20,6 @@
#include "flang/Lower/ConvertExpr.h"
#include "flang/Lower/ConvertExprToHLFIR.h"
#include "flang/Lower/ConvertProcedureDesignator.h"
-#include "flang/Lower/ConvertVariable.h"
#include "flang/Lower/Mangler.h"
#include "flang/Lower/PFTBuilder.h"
#include "flang/Lower/StatementContext.h"
@@ -32,7 +32,6 @@
#include "flang/Optimizer/Builder/IntrinsicCall.h"
#include "flang/Optimizer/Builder/Runtime/Derived.h"
#include "flang/Optimizer/Builder/Todo.h"
-#include "flang/Optimizer/CodeGen/TypeConverter.h"
#include "flang/Optimizer/Dialect/CUF/CUFOps.h"
#include "flang/Optimizer/Dialect/FIRAttr.h"
#include "flang/Optimizer/Dialect/FIRDialect.h"
@@ -40,7 +39,6 @@
#include "flang/Optimizer/Dialect/FIRType.h"
#include "flang/Optimizer/Dialect/Support/FIRContext.h"
#include "flang/Optimizer/HLFIR/HLFIROps.h"
-#include "flang/Optimizer/Support/DataLayout.h"
#include "flang/Optimizer/Support/FatalError.h"
#include "flang/Optimizer/Support/InternalNames.h"
#include "flang/Optimizer/Support/Utils.h"
@@ -593,14 +591,15 @@ fir::GlobalOp Fortran::lower::defineGlobal(
if (details && details->init()) {
auto sym{*details->init()};
if (sym) // Has a procedure target.
- createGlobalInitialization(builder, global, [&](fir::FirOpBuilder &b) {
- Fortran::lower::StatementContext stmtCtx(
- /*cleanupProhibited=*/true);
- auto box{Fortran::lower::convertProcedureDesignatorInitialTarget(
- converter, loc, *sym)};
- auto castTo{builder.createConvert(loc, symTy, box)};
- fir::HasValueOp::create(b, loc, castTo);
- });
+ createGlobalInitialization(
+ builder, global, [&](fir::FirOpBuilder &b) {
+ Fortran::lower::StatementContext stmtCtx(
+ /*cleanupProhibited=*/true);
+ auto box{Fortran::lower::convertProcedureDesignatorInitialTarget(
+ converter, loc, *sym)};
+ auto castTo{builder.createConvert(loc, symTy, box)};
+ fir::HasValueOp::create(b, loc, castTo);
+ });
else { // Has NULL() target.
createGlobalInitialization(builder, global, [&](fir::FirOpBuilder &b) {
auto box{fir::factory::createNullBoxProc(b, loc, symTy)};
@@ -889,12 +888,12 @@ static void genDerivedTypeComponentInit(
if (!compFirTy)
continue;
// Compute the memory coordinate of the current component.
- auto fieldIdx = builder.create<fir::FieldIndexOp>(
- loc, fir::FieldType::get(recTy.getContext()), name, recTy,
+ auto fieldIdx = fir::FieldIndexOp::create(
+ builder, loc, fir::FieldType::get(recTy.getContext()), name, recTy,
mlir::ValueRange{});
auto compAddr =
- builder.create<fir::CoordinateOp>(loc, builder.getRefType(compFirTy),
- baseAddr, mlir::ValueRange{fieldIdx});
+ fir::CoordinateOp::create(builder, loc, builder.getRefType(compFirTy),
+ baseAddr, mlir::ValueRange{fieldIdx});
// Case A: Standard data components (ObjectEntityDetails).
if (objDetails) {
@@ -915,13 +914,13 @@ static void genDerivedTypeComponentInit(
builder, global, [&](fir::FirOpBuilder &b) {
mlir::Value initBox = Fortran::lower::genInitialDataTarget(
converter, loc, compFirTy, *objDetails->init());
- b.create<fir::HasValueOp>(loc, initBox);
+ fir::HasValueOp::create(b, loc, initBox);
});
}
- auto srcAddr = builder.create<fir::AddrOfOp>(loc, global.resultType(),
- global.getSymbol());
- mlir::Value loadedBox = builder.create<fir::LoadOp>(loc, srcAddr);
- builder.create<fir::StoreOp>(loc, loadedBox, compAddr);
+ auto srcAddr = fir::AddrOfOp::create(builder, loc, global.resultType(),
+ global.getSymbol());
+ mlir::Value loadedBox = fir::LoadOp::create(builder, loc, srcAddr);
+ fir::StoreOp::create(builder, loc, loadedBox, compAddr);
} else {
// Subcase A.1.2: Non-pointer components with explicit initialization.
Fortran::lower::StatementContext stmtCtx(/*cleanupProhibited=*/true);
@@ -940,10 +939,10 @@ static void genDerivedTypeComponentInit(
dataAttr);
}
if (global) {
- auto srcAddr = builder.create<fir::AddrOfOp>(
- loc, global.resultType(), global.getSymbol());
- builder.create<fir::CopyOp>(loc, srcAddr, compAddr,
- /*noOverlap=*/true);
+ auto srcAddr = fir::AddrOfOp::create(
+ builder, loc, global.resultType(), global.getSymbol());
+ fir::CopyOp::create(builder, loc, srcAddr, compAddr,
+ /*noOverlap=*/true);
} else {
Fortran::lower::StatementContext inlineCtx;
Fortran::lower::SymMap emptyMap;
@@ -951,7 +950,7 @@ static void genDerivedTypeComponentInit(
hlfir::Entity rhs = Fortran::lower::convertExprToHLFIR(
loc, converter, objDetails->init().value(), emptyMap,
inlineCtx);
- builder.create<hlfir::AssignOp>(loc, rhs, lhs);
+ hlfir::AssignOp::create(builder, loc, rhs, lhs);
}
} else {
// Scalar component: Evaluate the expression and store directly.
@@ -960,10 +959,10 @@ static void genDerivedTypeComponentInit(
mlir::Value initVal = fir::getBase(exInitVal);
if (fir::isa_ref_type(initVal.getType()) &&
!fir::isa_ref_type(compFirTy))
- initVal = builder.create<fir::LoadOp>(loc, initVal);
+ initVal = fir::LoadOp::create(builder, loc, initVal);
mlir::Value castVal =
builder.createConvert(loc, compFirTy, initVal);
- builder.create<fir::StoreOp>(loc, castVal, compAddr);
+ fir::StoreOp::create(builder, loc, castVal, compAddr);
}
}
} else if (Fortran::semantics::IsAllocatableOrPointer(compSym)) {
@@ -982,13 +981,13 @@ static void genDerivedTypeComponentInit(
builder, global, [&](fir::FirOpBuilder &b) {
mlir::Value nullBox = fir::factory::createUnallocatedBox(
b, loc, compFirTy, mlir::ValueRange{});
- b.create<fir::HasValueOp>(loc, nullBox);
+ fir::HasValueOp::create(b, loc, nullBox);
});
}
- auto srcAddr = builder.create<fir::AddrOfOp>(loc, global.resultType(),
- global.getSymbol());
- mlir::Value loadedBox = builder.create<fir::LoadOp>(loc, srcAddr);
- builder.create<fir::StoreOp>(loc, loadedBox, compAddr);
+ auto srcAddr = fir::AddrOfOp::create(builder, loc, global.resultType(),
+ global.getSymbol());
+ mlir::Value loadedBox = fir::LoadOp::create(builder, loc, srcAddr);
+ fir::StoreOp::create(builder, loc, loadedBox, compAddr);
} else if (Fortran::lower::hasDefaultInitialization(compSym)) {
// Subcase A.3: Nested derived type requires initialization.
// Derived type has no explicit initialization but contains
@@ -1012,7 +1011,7 @@ static void genDerivedTypeComponentInit(
mlir::Value initVal = genDefaultInitializerValue(
converter, loc, compSym, compFirTy, stmtCtx);
mlir::Value castVal = builder.createConvert(loc, compFirTy, initVal);
- builder.create<fir::StoreOp>(loc, castVal, compAddr);
+ fir::StoreOp::create(builder, loc, castVal, compAddr);
}
}
}
@@ -1041,13 +1040,13 @@ static void genDerivedTypeComponentInit(
} else {
procBox = fir::factory::createNullBoxProc(b, loc, compFirTy);
}
- b.create<fir::HasValueOp>(loc, procBox);
+ fir::HasValueOp::create(b, loc, procBox);
});
}
- auto srcAddr = builder.create<fir::AddrOfOp>(loc, global.resultType(),
- global.getSymbol());
- mlir::Value loadedBox = builder.create<fir::LoadOp>(loc, srcAddr);
- builder.create<fir::StoreOp>(loc, loadedBox, compAddr);
+ auto srcAddr = fir::AddrOfOp::create(builder, loc, global.resultType(),
+ global.getSymbol());
+ mlir::Value loadedBox = fir::LoadOp::create(builder, loc, srcAddr);
+ fir::StoreOp::create(builder, loc, loadedBox, compAddr);
} else {
// Has no initialization.
continue;
@@ -1068,8 +1067,8 @@ void Fortran::lower::defaultInitializeAtRuntime(
// 15.5.2.12 point 3, absent optional dummies are not initialized.
// Creating descriptor/passing null descriptor to the runtime would
// create runtime crashes.
- auto isPresent = builder.create<fir::IsPresentOp>(loc, builder.getI1Type(),
- fir::getBase(exv));
+ auto isPresent = fir::IsPresentOp::create(builder, loc, builder.getI1Type(),
+ fir::getBase(exv));
builder.genIfThen(loc, isPresent)
.genThen([&]() {
auto box = builder.createBox(loc, exv);
@@ -1123,7 +1122,7 @@ void Fortran::lower::defaultInitializeAtRuntime(
converter, loc, details->init().value(), stmtCtx);
mlir::Value castTo =
builder.createConvert(loc, symTy, fir::getBase(initVal));
- builder.create<fir::HasValueOp>(loc, castTo);
+ fir::HasValueOp::create(builder, loc, castTo);
});
} else if (!global) {
global = builder.createGlobal(loc, symTy, globalName, linkage,
@@ -1138,13 +1137,13 @@ void Fortran::lower::defaultInitializeAtRuntime(
mlir::Value initVal = genDefaultInitializerValue(
converter, loc, sym, symTy, stmtCtx);
mlir::Value castTo = builder.createConvert(loc, symTy, initVal);
- builder.create<fir::HasValueOp>(loc, castTo);
+ fir::HasValueOp::create(builder, loc, castTo);
});
}
- auto addrOf = builder.create<fir::AddrOfOp>(loc, global.resultType(),
- global.getSymbol());
- builder.create<fir::CopyOp>(loc, addrOf, fir::getBase(exv),
- /*noOverlap=*/true);
+ auto addrOf = fir::AddrOfOp::create(builder, loc, global.resultType(),
+ global.getSymbol());
+ fir::CopyOp::create(builder, loc, addrOf, fir::getBase(exv),
+ /*noOverlap=*/true);
}
} else {
mlir::Value box = builder.createBox(loc, exv);
>From a09c761d8498644dd4e95603dd2a451a6acf7b3c Mon Sep 17 00:00:00 2001
From: hechenxi <hechenxi at bosc.ac.cn>
Date: Fri, 20 Mar 2026 20:04:00 +0800
Subject: [PATCH 3/4] Correct the format.
---
flang/lib/Lower/ConvertVariable.cpp | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/flang/lib/Lower/ConvertVariable.cpp b/flang/lib/Lower/ConvertVariable.cpp
index c114643d89e5a..8f2320729cfdd 100644
--- a/flang/lib/Lower/ConvertVariable.cpp
+++ b/flang/lib/Lower/ConvertVariable.cpp
@@ -917,8 +917,8 @@ static void genDerivedTypeComponentInit(
fir::HasValueOp::create(b, loc, initBox);
});
}
- auto srcAddr = fir::AddrOfOp::create(builder, loc, global.resultType(),
- global.getSymbol());
+ auto srcAddr = fir::AddrOfOp::create(
+ builder, loc, global.resultType(), global.getSymbol());
mlir::Value loadedBox = fir::LoadOp::create(builder, loc, srcAddr);
fir::StoreOp::create(builder, loc, loadedBox, compAddr);
} else {
@@ -1141,7 +1141,7 @@ void Fortran::lower::defaultInitializeAtRuntime(
});
}
auto addrOf = fir::AddrOfOp::create(builder, loc, global.resultType(),
- global.getSymbol());
+ global.getSymbol());
fir::CopyOp::create(builder, loc, addrOf, fir::getBase(exv),
/*noOverlap=*/true);
}
>From d9d8c06d4fad7f46af1a89c00b1318ce3309e192 Mon Sep 17 00:00:00 2001
From: hechenxi <hechenxi at bosc.ac.cn>
Date: Thu, 26 Mar 2026 15:47:12 +0800
Subject: [PATCH 4/4] New component-wise initialization logic.
---
flang/lib/Lower/ConvertVariable.cpp | 296 ++++++++---------------
flang/test/Lower/component-wise-init.f90 | 149 +++++++-----
2 files changed, 181 insertions(+), 264 deletions(-)
diff --git a/flang/lib/Lower/ConvertVariable.cpp b/flang/lib/Lower/ConvertVariable.cpp
index 8f2320729cfdd..91b21e80621f3 100644
--- a/flang/lib/Lower/ConvertVariable.cpp
+++ b/flang/lib/Lower/ConvertVariable.cpp
@@ -49,11 +49,6 @@
#include "llvm/Support/Debug.h"
#include <optional>
-static llvm::cl::opt<bool> enablePreciseInit(
- "enable-precise-init",
- llvm::cl::desc("Enable precise component initialization for derived types"),
- llvm::cl::init(false));
-
static llvm::cl::opt<bool>
allowAssumedRank("allow-assumed-rank",
llvm::cl::desc("Enable assumed rank lowering"),
@@ -818,43 +813,39 @@ mustBeDefaultInitializedAtRuntime(const Fortran::lower::pft::Variable &var) {
return Fortran::lower::hasDefaultInitialization(sym);
}
-namespace {
-/// Determines if \p sym represents a complex derived type.
-/// A derived type is considered complex if it contains allocatable, pointer,
-/// or procedure pointer components, or nested complex derived types.
-static bool isComplexDerivedType(const Fortran::semantics::Symbol &sym) {
+/// Determines whether a derived type component requires non-trivial
+/// initialization. A component requires non-trivial initialization if it is an
+/// allocatable or pointer data component, or if it is a nested derived type
+/// that contains such components.
+static bool RequiresNonTrivialComponentInitialization(
+ const Fortran::semantics::Symbol &sym) {
const Fortran::semantics::DeclTypeSpec *declTy = sym.GetType();
if (!declTy || !declTy->AsDerived())
return false;
const Fortran::semantics::DerivedTypeSpec &derivedSpec = *declTy->AsDerived();
const Fortran::semantics::Scope *scope = derivedSpec.GetScope();
- if (!scope)
- return false;
+ assert(scope && "derived type has no scope");
const auto &typeDetails =
derivedSpec.typeSymbol().get<Fortran::semantics::DerivedTypeDetails>();
+
for (const auto &compName : typeDetails.componentNames()) {
auto iter = scope->find(compName);
- if (iter == scope->cend())
- continue;
+ assert(iter != scope->cend() && "component symbol not found in scope");
const Fortran::semantics::Symbol &compSym = iter->second.get();
const auto *objDetails =
compSym.detailsIf<Fortran::semantics::ObjectEntityDetails>();
- const auto *procDetails =
- compSym.detailsIf<Fortran::semantics::ProcEntityDetails>();
- if (Fortran::semantics::IsAllocatableOrPointer(compSym)) {
- return true;
- } else if (procDetails && procDetails->init().has_value()) {
- return true;
- } else if (objDetails && objDetails->init() && compSym.Rank() > 0) {
- return true;
- } else if (isComplexDerivedType(compSym)) {
+ // If data component is an allocatable/pointer,
+ // or a nested derived type that requires initialization,
+ // return true.
+ if (objDetails && (Fortran::semantics::IsAllocatableOrPointer(compSym) ||
+ RequiresNonTrivialComponentInitialization(compSym))) {
return true;
}
}
return false;
}
-/// Performs precise, component-wise initialization for \p derivedSpec,
+/// Performs component-wise initialization for \p derivedSpec,
/// selectively generating IR only for components that require it.
static void genDerivedTypeComponentInit(
Fortran::lower::AbstractConverter &converter, mlir::Location loc,
@@ -862,31 +853,34 @@ static void genDerivedTypeComponentInit(
mlir::Value baseAddr, fir::RecordType recTy) {
fir::FirOpBuilder &builder = converter.getFirOpBuilder();
const Fortran::semantics::Scope *scope = derivedSpec.GetScope();
- if (!scope)
- return;
+ assert(scope && "derived type has no scope");
const auto &typeDetails =
derivedSpec.typeSymbol().get<Fortran::semantics::DerivedTypeDetails>();
+
for (const auto &compName : typeDetails.componentNames()) {
auto scopeIter = scope->find(compName);
- if (scopeIter == scope->cend())
- continue;
+ assert(scopeIter != scope->cend() &&
+ "component name must exist in its scope");
const Fortran::semantics::Symbol &compSym = scopeIter->second.get();
+ // Only process data components; procedure pointers are ignored.
const auto *objDetails =
compSym.detailsIf<Fortran::semantics::ObjectEntityDetails>();
- const auto *procDetails =
- compSym.detailsIf<Fortran::semantics::ProcEntityDetails>();
- // Determine if the component requires initialization.
- // Dynamic descriptors (pointers/allocatables) and explicitly initialized
- // entities must be processed.
- bool needsInit = Fortran::semantics::IsAllocatableOrPointer(compSym) ||
- (objDetails && objDetails->init()) ||
- (procDetails && procDetails->init().has_value());
- if (!needsInit && !Fortran::lower::hasDefaultInitialization(compSym))
+ if (!objDetails)
+ continue;
+
+ bool isPtrOrAlloc = Fortran::semantics::IsAllocatableOrPointer(compSym);
+ const Fortran::semantics::DeclTypeSpec *declTy = compSym.GetType();
+ bool isNestedDerived = declTy && declTy->AsDerived();
+ // Targets are pointer or allocatable components, or nested derived types
+ // containing them.
+ bool isTargetComponent =
+ isPtrOrAlloc ||
+ (isNestedDerived && RequiresNonTrivialComponentInitialization(compSym));
+ if (!isTargetComponent)
continue;
std::string name = converter.getRecordTypeFieldName(compSym);
mlir::Type compFirTy = recTy.getType(name);
- if (!compFirTy)
- continue;
+ assert(compFirTy && "Component field type not found in RecordType");
// Compute the memory coordinate of the current component.
auto fieldIdx = fir::FieldIndexOp::create(
builder, loc, fir::FieldType::get(recTy.getContext()), name, recTy,
@@ -894,167 +888,73 @@ static void genDerivedTypeComponentInit(
auto compAddr =
fir::CoordinateOp::create(builder, loc, builder.getRefType(compFirTy),
baseAddr, mlir::ValueRange{fieldIdx});
+ if (isPtrOrAlloc) {
+ // Target 1: pointer or allocatable component.
+ mlir::Value nullBox = fir::factory::createUnallocatedBox(
+ builder, loc, compFirTy, mlir::ValueRange{});
+ fir::StoreOp::create(builder, loc, nullBox, compAddr);
+ } else if (isNestedDerived) {
+ // Target 2: Nested derived type containing pointer/allocatable
+ // components.
+ auto nestedRecTy = mlir::cast<fir::RecordType>(compFirTy);
+ genDerivedTypeComponentInit(converter, loc, *declTy->AsDerived(),
+ compAddr, nestedRecTy);
+ }
+ }
+}
- // Case A: Standard data components (ObjectEntityDetails).
- if (objDetails) {
- if (objDetails->init()) {
- // Subcase A.1: Component has explicit initialization.
- if (Fortran::semantics::IsPointer(compSym)) {
- // Subcase A.1.1: Pointer with explicit target.
- std::string globalName = fir::NameUniquer::doGenerated(
- (converter.mangleName(derivedSpec) + fir::kNameSeparator + name +
- fir::kNameSeparator + "init")
- .str());
- fir::GlobalOp global = builder.getNamedGlobal(globalName);
- if (!global) {
- global = builder.createGlobal(loc, compFirTy, globalName,
- builder.createInternalLinkage(),
- mlir::Attribute{}, true);
- createGlobalInitialization(
- builder, global, [&](fir::FirOpBuilder &b) {
- mlir::Value initBox = Fortran::lower::genInitialDataTarget(
- converter, loc, compFirTy, *objDetails->init());
- fir::HasValueOp::create(b, loc, initBox);
- });
+/// Checks if a derived type meets the criteria for component-wise
+/// initialization. The type must contain at least one pointer/allocatable
+/// component, and strictly NO explicit initialization anywhere in its
+/// hierarchy.
+static bool isEligibleForComponentWiseInit(
+ const Fortran::semantics::DerivedTypeSpec &derivedSpec) {
+ bool hasPtrOrAlloc = false;
+ auto checkImpl = [&](const Fortran::semantics::DerivedTypeSpec &spec,
+ auto &self) -> bool {
+ const Fortran::semantics::Scope *scope = spec.scope();
+ assert(scope && "derived type has no scope");
+ const auto &typeDetails =
+ spec.typeSymbol().get<Fortran::semantics::DerivedTypeDetails>();
+ for (const auto &compName : typeDetails.componentNames()) {
+ auto scopeIter = scope->find(compName);
+ assert(scopeIter != scope->cend() &&
+ "component name must exist in its scope");
+ const Fortran::semantics::Symbol &comp = scopeIter->second.get();
+ const auto *objDetails =
+ comp.detailsIf<Fortran::semantics::ObjectEntityDetails>();
+ const auto *procDetails =
+ comp.detailsIf<Fortran::semantics::ProcEntityDetails>();
+ // If any data component or procedure pointer has explicit
+ // initialization, abort and fall back to the global memcpy approach.
+ if ((objDetails && objDetails->init()) ||
+ (procDetails && procDetails->init())) {
+ return false;
+ }
+ if (Fortran::semantics::IsAllocatableOrPointer(comp)) {
+ hasPtrOrAlloc = true;
+ continue;
+ }
+ // Recursively traverse nested derived types.
+ if (const Fortran::semantics::DeclTypeSpec *declTy = comp.GetType()) {
+ if (const Fortran::semantics::DerivedTypeSpec *nestedSpec =
+ declTy->AsDerived()) {
+ // If it is an array of derived types that requires initialization,
+ // fall back to memcpy to avoid generating expensive runtime loops.
+ if (comp.Rank() > 0 &&
+ RequiresNonTrivialComponentInitialization(comp)) {
+ return false;
}
- auto srcAddr = fir::AddrOfOp::create(
- builder, loc, global.resultType(), global.getSymbol());
- mlir::Value loadedBox = fir::LoadOp::create(builder, loc, srcAddr);
- fir::StoreOp::create(builder, loc, loadedBox, compAddr);
- } else {
- // Subcase A.1.2: Non-pointer components with explicit initialization.
- Fortran::lower::StatementContext stmtCtx(/*cleanupProhibited=*/true);
- if (mlir::isa<fir::SequenceType>(compFirTy)) {
- std::string globalName = fir::NameUniquer::doGenerated(
- (converter.mangleName(derivedSpec) + fir::kNameSeparator +
- name + fir::kNameSeparator + "arr_init")
- .str());
- fir::GlobalOp global = builder.getNamedGlobal(globalName);
- if (!global) {
- const auto &initExpr = objDetails->init().value();
- cuf::DataAttributeAttr dataAttr = {};
- global = Fortran::lower::tryCreatingDenseGlobal(
- builder, loc, compFirTy, globalName,
- builder.createInternalLinkage(), /*isConst=*/true, initExpr,
- dataAttr);
- }
- if (global) {
- auto srcAddr = fir::AddrOfOp::create(
- builder, loc, global.resultType(), global.getSymbol());
- fir::CopyOp::create(builder, loc, srcAddr, compAddr,
- /*noOverlap=*/true);
- } else {
- Fortran::lower::StatementContext inlineCtx;
- Fortran::lower::SymMap emptyMap;
- hlfir::Entity lhs{compAddr};
- hlfir::Entity rhs = Fortran::lower::convertExprToHLFIR(
- loc, converter, objDetails->init().value(), emptyMap,
- inlineCtx);
- hlfir::AssignOp::create(builder, loc, rhs, lhs);
- }
- } else {
- // Scalar component: Evaluate the expression and store directly.
- fir::ExtendedValue exInitVal = genInitializerExprValue(
- converter, loc, objDetails->init().value(), stmtCtx);
- mlir::Value initVal = fir::getBase(exInitVal);
- if (fir::isa_ref_type(initVal.getType()) &&
- !fir::isa_ref_type(compFirTy))
- initVal = fir::LoadOp::create(builder, loc, initVal);
- mlir::Value castVal =
- builder.createConvert(loc, compFirTy, initVal);
- fir::StoreOp::create(builder, loc, castVal, compAddr);
+ if (!self(*nestedSpec, self)) {
+ return false;
}
}
- } else if (Fortran::semantics::IsAllocatableOrPointer(compSym)) {
- // Subcase A.2: Pointer or allocatable without initialization.
- // Create deallocated/disassociated value.
- std::string globalName = fir::NameUniquer::doGenerated(
- (converter.mangleName(derivedSpec) + fir::kNameSeparator + name +
- fir::kNameSeparator + "null_box")
- .str());
- fir::GlobalOp global = builder.getNamedGlobal(globalName);
- if (!global) {
- global = builder.createGlobal(loc, compFirTy, globalName,
- builder.createInternalLinkage(),
- mlir::Attribute{}, true);
- createGlobalInitialization(
- builder, global, [&](fir::FirOpBuilder &b) {
- mlir::Value nullBox = fir::factory::createUnallocatedBox(
- b, loc, compFirTy, mlir::ValueRange{});
- fir::HasValueOp::create(b, loc, nullBox);
- });
- }
- auto srcAddr = fir::AddrOfOp::create(builder, loc, global.resultType(),
- global.getSymbol());
- mlir::Value loadedBox = fir::LoadOp::create(builder, loc, srcAddr);
- fir::StoreOp::create(builder, loc, loadedBox, compAddr);
- } else if (Fortran::lower::hasDefaultInitialization(compSym)) {
- // Subcase A.3: Nested derived type requires initialization.
- // Derived type has no explicit initialization but contains
- // components requiring initialization.
- if (mlir::isa<fir::SequenceType>(compFirTy) &&
- isComplexDerivedType(compSym)) {
- // Complex derived type arrays.
- mlir::Value box =
- builder.createBox(loc, fir::ExtendedValue(compAddr));
- fir::runtime::genDerivedTypeInitialize(builder, loc, box);
- } else if (isComplexDerivedType(compSym)) {
- // Complex derived type scalars.
- const Fortran::semantics::DeclTypeSpec *compDeclTy =
- compSym.GetType();
- auto nestedRecTy = mlir::dyn_cast<fir::RecordType>(compFirTy);
- genDerivedTypeComponentInit(converter, loc, *compDeclTy->AsDerived(),
- compAddr, nestedRecTy);
- } else {
- // Simple derived type scalars.
- Fortran::lower::StatementContext stmtCtx;
- mlir::Value initVal = genDefaultInitializerValue(
- converter, loc, compSym, compFirTy, stmtCtx);
- mlir::Value castVal = builder.createConvert(loc, compFirTy, initVal);
- fir::StoreOp::create(builder, loc, castVal, compAddr);
- }
- }
- }
- // Case B: Procedure pointers (ProcEntityDetails).
- else if (procDetails) {
- if (procDetails->init().has_value()) {
- // Has a procedure or NULL() target.
- std::string globalName = fir::NameUniquer::doGenerated(
- (converter.mangleName(derivedSpec) + fir::kNameSeparator + name +
- fir::kNameSeparator + "init")
- .str());
- fir::GlobalOp global = builder.getNamedGlobal(globalName);
- if (!global) {
- global = builder.createGlobal(loc, compFirTy, globalName,
- builder.createInternalLinkage(),
- mlir::Attribute{}, true);
- createGlobalInitialization(
- builder, global, [&](fir::FirOpBuilder &b) {
- mlir::Value procBox;
- auto targetSym = *procDetails->init();
- if (targetSym) {
- procBox =
- Fortran::lower::convertProcedureDesignatorInitialTarget(
- converter, loc, *targetSym);
- procBox = b.createConvert(loc, compFirTy, procBox);
- } else {
- procBox = fir::factory::createNullBoxProc(b, loc, compFirTy);
- }
- fir::HasValueOp::create(b, loc, procBox);
- });
- }
- auto srcAddr = fir::AddrOfOp::create(builder, loc, global.resultType(),
- global.getSymbol());
- mlir::Value loadedBox = fir::LoadOp::create(builder, loc, srcAddr);
- fir::StoreOp::create(builder, loc, loadedBox, compAddr);
- } else {
- // Has no initialization.
- continue;
}
}
- }
+ return true;
+ };
+ return checkImpl(derivedSpec, checkImpl) && hasPtrOrAlloc;
}
-} // namespace
/// Call default initialization runtime routine to initialize \p var.
void Fortran::lower::defaultInitializeAtRuntime(
@@ -1083,7 +983,7 @@ void Fortran::lower::defaultInitializeAtRuntime(
mlir::Type symTy = converter.genType(sym);
const auto *details =
sym.detailsIf<Fortran::semantics::ObjectEntityDetails>();
- bool useFastInit =
+ bool isDerivedTypeScalar =
details && !Fortran::semantics::IsPolymorphic(sym) &&
declTy->category() ==
Fortran::semantics::DeclTypeSpec::Category::TypeDerived &&
@@ -1091,11 +991,11 @@ void Fortran::lower::defaultInitializeAtRuntime(
!sym.test(Fortran::semantics::Symbol::Flag::OmpPrivate) &&
!sym.test(Fortran::semantics::Symbol::Flag::OmpFirstPrivate) &&
!Fortran::semantics::HasCUDAComponent(sym);
- if (useFastInit) {
- if (enablePreciseInit) {
- // Precise initialization
+ if (isDerivedTypeScalar) {
+ const auto &derivedSpec = *declTy->AsDerived();
+ if (isEligibleForComponentWiseInit(derivedSpec)) {
+ // Component-wise initialization
mlir::Value baseAddr = fir::getBase(exv);
- const auto &derivedSpec = *sym.GetType()->AsDerived();
auto recTy = mlir::cast<fir::RecordType>(
fir::unwrapPassByRefType(baseAddr.getType()));
genDerivedTypeComponentInit(converter, loc, derivedSpec, baseAddr,
diff --git a/flang/test/Lower/component-wise-init.f90 b/flang/test/Lower/component-wise-init.f90
index 2b751e784dc47..99633062c7619 100644
--- a/flang/test/Lower/component-wise-init.f90
+++ b/flang/test/Lower/component-wise-init.f90
@@ -1,94 +1,111 @@
-! RUN: bbc -emit-fir -enable-precise-init %s -o - | FileCheck %s
+! RUN: bbc -emit-fir %s -o - | FileCheck %s
! Test for precise component-wise initialization of derived types
! to avoid generating large, fully-initialized global templates (sparse initialization).
module my_types
- type :: InnerType
- integer :: flag = 42
+ ! Clean internal type: contains only allocatable and pointer components,
+ ! without any explicit initialization.
+ type :: InnerClean
real, pointer :: p
real, allocatable :: arr(:)
- end type InnerType
+ end type InnerClean
- type :: OuterType
+ ! ============================================================================
+ ! Test Case 1: Type meeting all criteria for precise component-wise init (Target)
+ ! No arrays of derived types, no explicit initialization.
+ ! ============================================================================
+ type :: TargetType
real, pointer :: q
- integer :: explicit_arr(3) = [10, 20, 30]
- real(8) :: uninit_buffer(80, 100, 100)
- type(InnerType) :: nested
- type(InnerType) :: nested_arr(2)
- end type OuterType
+ real(8) :: uninit_buffer(80, 100) ! Large uninitialized array; should be bypassed.
+ type(InnerClean) :: nested_scalar ! Scalar derived type; should be expanded recursively.
+ end type TargetType
+
+ ! ============================================================================
+ ! Test Case 2: Type triggering fallback A (contains an array of derived type)
+ ! ============================================================================
+ type :: FallbackArrayType
+ type(InnerClean) :: nested_arr(10) ! Array of derived type; must trigger fallback.
+ end type FallbackArrayType
+
+ ! ============================================================================
+ ! Test Case 3: Type triggering fallback B (contains explicit initialization)
+ ! ============================================================================
+ type :: FallbackExplicitType
+ real, pointer :: p
+ integer :: flag = 999 ! Explicit initialization; must trigger gatekeeper!
+ end type FallbackExplicitType
end module my_types
-subroutine test_complex_init()
- use my_types
- type(OuterType) :: my_var
- call do_something(my_var)
-end subroutine test_complex_init
-
-! ==============================================================================
-! FileCheck Assertions
-! ==============================================================================
-
-! CHECK-LABEL: func.func @_QPtest_complex_init()
-
-! Ensure we allocate the local variable
-! CHECK: %[[MY_VAR:.*]] = fir.alloca !fir.type<_QMmy_typesToutertype{{.*}}>
! ------------------------------------------------------------------------------
-! 1. Check pointer 'q' initialization (First component, offset 0, no coordinate_of needed)
+! Test 1: FIR generation for precise component-wise initialization
! ------------------------------------------------------------------------------
-! CHECK: %[[Q_NULL_BOX_ADDR:.*]] = fir.address_of(@_QQ_QMmy_typesToutertype.q.{{(null_box|init)}})
-! CHECK: %[[Q_NULL_BOX:.*]] = fir.load %[[Q_NULL_BOX_ADDR]]
-! CHECK: fir.store %[[Q_NULL_BOX]] to %{{.*}}
+subroutine test_target()
+ use my_types
+ type(TargetType) :: my_var
+ call do_something_target(my_var)
+end subroutine test_target
-! ------------------------------------------------------------------------------
-! 2. Check explicit array initialization
-! ------------------------------------------------------------------------------
-! CHECK: %[[EXPLICIT_ARR_ADDR:.*]] = fir.coordinate_of %{{.*}}, explicit_arr
-! CHECK: %[[EXPLICIT_ARR_INIT:.*]] = fir.address_of(@_QQ_QMmy_typesToutertype.explicit_arr.arr_init)
-! CHECK: fir.copy %[[EXPLICIT_ARR_INIT]] to %[[EXPLICIT_ARR_ADDR]]
+! CHECK-LABEL: func.func @_QPtest_target()
+! Match the alloca and the subsequent declare
+! CHECK: %[[ALLOCA:.*]] = fir.alloca !fir.type<_QMmy_typesTtargettype{{.*}}>
+! CHECK: %[[MY_VAR:.*]] = fir.declare %[[ALLOCA]]
-! ------------------------------------------------------------------------------
-! 3. Check scalar derived type 'nested' component-wise precise initialization
-! ------------------------------------------------------------------------------
-! CHECK: %[[NESTED_ADDR:.*]] = fir.coordinate_of %{{.*}}, nested
+! 1. Check initialization of the outermost pointer 'q'
+! CHECK: %[[Q_ADDR:.*]] = fir.coordinate_of %[[MY_VAR]], q
+! CHECK: fir.store %{{.*}} to %[[Q_ADDR]]
-! -> Check 'flag' (integer = 42)
-! CHECK: %[[FLAG_ADDR:.*]] = fir.coordinate_of %[[NESTED_ADDR]], flag
-! CHECK: fir.store %c42{{.*}} to %[[FLAG_ADDR]]
+! 2. Check recursive precise initialization of the scalar derived type 'nested_scalar'
+! CHECK: %[[NESTED_ADDR:.*]] = fir.coordinate_of %[[MY_VAR]], nested_scalar
-! -> Check 'p' (pointer)
+! -> Check internal pointer 'p'
! CHECK: %[[P_ADDR:.*]] = fir.coordinate_of %[[NESTED_ADDR]], p
-! CHECK: %[[P_INIT_ADDR:.*]] = fir.address_of(@_QQ_QMmy_typesTinnertype.p.{{(null_box|init)}})
-! CHECK: %[[P_INIT_VAL:.*]] = fir.load %[[P_INIT_ADDR]]
-! CHECK: fir.store %[[P_INIT_VAL]] to %[[P_ADDR]]
+! CHECK: fir.store %{{.*}} to %[[P_ADDR]]
-! -> Check 'arr' (allocatable)
+! -> Check internal allocatable array 'arr'
! CHECK: %[[ARR_ADDR:.*]] = fir.coordinate_of %[[NESTED_ADDR]], arr
-! CHECK: %[[ARR_INIT_ADDR:.*]] = fir.address_of(@_QQ_QMmy_typesTinnertype.arr.null_box)
-! CHECK: %[[ARR_INIT_VAL:.*]] = fir.load %[[ARR_INIT_ADDR]]
-! CHECK: fir.store %[[ARR_INIT_VAL]] to %[[ARR_ADDR]]
+! CHECK: fir.store %{{.*}} to %[[ARR_ADDR]]
-! ------------------------------------------------------------------------------
-! 4. Check derived type array 'nested_arr' initialization (Runtime call)
-! ------------------------------------------------------------------------------
-! CHECK: %[[NESTED_ARR_ADDR:.*]] = fir.coordinate_of %{{.*}}, nested_arr
-! CHECK: %[[NESTED_ARR_BOX:.*]] = fir.embox %[[NESTED_ARR_ADDR]]
-! CHECK: %[[NESTED_ARR_PTR:.*]] = fir.convert %[[NESTED_ARR_BOX]]
-! CHECK: fir.call @_FortranAInitialize(%[[NESTED_ARR_PTR]], {{.*}})
+! CRITICAL 1: Ensure NO expensive runtime calls are generated.
+! CHECK-NOT: fir.call @_FortranAInitialize
+
+! CRITICAL 2: Ensure NO large global initialization templates are generated for TargetType.
+! CHECK-NOT: fir.global internal @_QQ_QMmy_typesTtargettype.DerivedInit
! ------------------------------------------------------------------------------
-! Global Variables Check
+! Test 2: Fallback mechanism for arrays of derived types
! ------------------------------------------------------------------------------
+subroutine test_fallback_array()
+ use my_types
+ type(FallbackArrayType) :: var_array
+ call do_something_array(var_array)
+end subroutine test_fallback_array
-! CRITICAL: Ensure NO full derived type initialization templates are generated!
-! We should NOT see fir.global for the entire DerivedInit.
-! CHECK-NOT: fir.global internal @_QQ_QMmy_typesToutertype.DerivedInit
-! CHECK-NOT: fir.global internal @_QQ_QMmy_typesTinnertype.DerivedInit
+! CHECK-LABEL: func.func @_QPtest_fallback_array()
+! CHECK: %[[ALLOCA_ARR:.*]] = fir.alloca !fir.type<_QMmy_typesTfallbackarraytype{{.*}}>
+! CHECK: %[[VAR_ARRAY:.*]] = fir.declare %[[ALLOCA_ARR]]
+!
+! Ensure FallbackArrayType falls back to the highly optimized memcpy from a global template,
+! entirely avoiding the runtime loop initialization.
+! CHECK: %[[GLOBAL_INIT_ARR:.*]] = fir.address_of(@_QQ_QMmy_typesTfallbackarraytype.DerivedInit)
+! CHECK: fir.copy %[[GLOBAL_INIT_ARR]] to %[[VAR_ARRAY]]
-! Verify the fine-grained component global constants exist
-! CHECK-DAG: fir.global internal @_QQ_QMmy_typesToutertype.explicit_arr.arr_init(dense<[10, 20, 30]> : tensor<3xi32>)
-! CHECK-DAG: fir.global internal @_QQ_QMmy_typesToutertype.q.{{(null_box|init)}}
-! CHECK-DAG: fir.global internal @_QQ_QMmy_typesTinnertype.p.{{(null_box|init)}}
-! CHECK-DAG: fir.global internal @_QQ_QMmy_typesTinnertype.arr.null_box
\ No newline at end of file
+
+! ------------------------------------------------------------------------------
+! Test 3: Fallback mechanism for explicit initialization
+! ------------------------------------------------------------------------------
+subroutine test_fallback_explicit()
+ use my_types
+ type(FallbackExplicitType) :: var_explicit
+ call do_something_explicit(var_explicit)
+end subroutine test_fallback_explicit
+
+! CHECK-LABEL: func.func @_QPtest_fallback_explicit()
+! CHECK: %[[ALLOCA_EXP:.*]] = fir.alloca !fir.type<_QMmy_typesTfallbackexplicittype{{.*}}>
+! CHECK: %[[VAR_EXPLICIT:.*]] = fir.declare %[[ALLOCA_EXP]]
+!
+! Ensure types with explicit initialization trigger the gatekeeper and fall back to the old logic.
+! CHECK: %[[GLOBAL_INIT_EXP:.*]] = fir.address_of(@_QQ_QMmy_typesTfallbackexplicittype.DerivedInit)
+! CHECK: fir.copy %[[GLOBAL_INIT_EXP]] to %[[VAR_EXPLICIT]]
\ No newline at end of file
More information about the flang-commits
mailing list