[flang-commits] [flang] [flang][lowering] Implement component-wise initialization for derived types (PR #187465)
via flang-commits
flang-commits at lists.llvm.org
Mon Mar 23 04:16:02 PDT 2026
================
@@ -812,6 +818,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 = fir::FieldIndexOp::create(
+ builder, loc, fir::FieldType::get(recTy.getContext()), name, recTy,
+ mlir::ValueRange{});
+ auto compAddr =
+ fir::CoordinateOp::create(builder, 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());
+ fir::HasValueOp::create(b, loc, initBox);
+ });
+ }
+ 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);
+ }
+ }
+ } 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);
----------------
jeanPerier wrote:
No need to use a global and make a copy, this is just a pointer. You can directly compute procBox and store it into compAddr.
https://github.com/llvm/llvm-project/pull/187465
More information about the flang-commits
mailing list