[flang-commits] [flang] [flang][lowering] Implement component-wise initialization for derived types (PR #187465)
via flang-commits
flang-commits at lists.llvm.org
Tue Apr 21 09:02:23 PDT 2026
================
@@ -855,6 +858,167 @@ mustBeDefaultInitializedAtRuntime(const Fortran::lower::pft::Variable &var) {
return Fortran::lower::hasDefaultInitialization(sym);
}
+/// 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,
+ const Fortran::semantics::DerivedTypeSpec &derivedSpec,
+ mlir::Value baseAddr, fir::RecordType recTy) {
+ fir::FirOpBuilder &builder = converter.getFirOpBuilder();
+ // Flattened DFS traversal to visit only the leaf components.
+ Fortran::semantics::UltimateComponentIterator ultimateIter{derivedSpec};
+ for (auto it = ultimateIter.begin(); it != ultimateIter.end(); ++it) {
+ const Fortran::semantics::Symbol &comp = *it;
+ const auto *objDetails =
+ comp.detailsIf<Fortran::semantics::ObjectEntityDetails>();
+ const auto *procDetails =
+ comp.detailsIf<Fortran::semantics::ProcEntityDetails>();
+ if ((!objDetails && !procDetails) ||
+ !Fortran::semantics::IsAllocatableOrPointer(comp))
+ continue;
+ // Retrieve the nested symbol path from the root type to this ultimate
+ // component.
+ auto path = it.GetComponentPath();
+ mlir::Value currentAddr = baseAddr;
+ fir::RecordType currentRecTy = recTy;
+ // Traverse the path and calculate memory coordinates.
+ for (const Fortran::semantics::Symbol &pathSym : path) {
+ const Fortran::semantics::Symbol *symPtr = &pathSym;
+ // Generate coordinate for this nesting level.
+ std::string name = converter.getRecordTypeFieldName(*symPtr);
+ mlir::Type compFirTy = currentRecTy.getType(name);
+ assert(compFirTy && "Component field type not found in RecordType");
+ auto fieldIdx = fir::FieldIndexOp::create(
+ builder, loc, fir::FieldType::get(currentRecTy.getContext()), name,
+ currentRecTy, mlir::ValueRange{});
+ currentAddr =
+ fir::CoordinateOp::create(builder, loc, builder.getRefType(compFirTy),
+ currentAddr, mlir::ValueRange{fieldIdx});
+ currentRecTy = mlir::dyn_cast<fir::RecordType>(compFirTy);
+ }
+ mlir::Type finalCompFirTy = fir::unwrapPassByRefType(currentAddr.getType());
+ mlir::Value initVal;
+ if (objDetails) {
+ initVal = fir::factory::createUnallocatedBox(
+ builder, loc, finalCompFirTy, mlir::ValueRange{});
+ } else {
+ initVal =
+ fir::factory::createNullBoxProc(builder, loc, finalCompFirTy);
+ }
+ fir::StoreOp::create(builder, loc, initVal, currentAddr);
+ }
+}
+
+/// Initializes a derived type via a bulk memory copy (memcpy).
+/// This method generates a global constant containing the default
+/// initialized state of the type, and copies it directly into the
+/// target memory location.
+static void
+genInlinedInitWithMemcpy(Fortran::lower::AbstractConverter &converter,
+ const Fortran::semantics::Symbol &sym,
+ mlir::Type symTy, const fir::ExtendedValue &exv,
+ const Fortran::semantics::ObjectEntityDetails *details,
+ const Fortran::semantics::DeclTypeSpec *declTy) {
+ fir::FirOpBuilder &builder = converter.getFirOpBuilder();
+ mlir::Location symLoc = genLocation(converter, sym);
+ std::string globalName = fir::NameUniquer::doGenerated(
+ (converter.mangleName(*declTy->AsDerived()) + fir::kNameSeparator +
+ fir::kDerivedTypeInitSuffix)
+ .str());
+ mlir::StringAttr linkage = builder.createInternalLinkage();
+ fir::GlobalOp global = builder.getNamedGlobal(globalName);
+ if (!global && details->init()) {
+ global = builder.createGlobal(symLoc, 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, symLoc, details->init().value(), stmtCtx);
+ mlir::Value castTo =
+ builder.createConvert(symLoc, symTy, fir::getBase(initVal));
+ fir::HasValueOp::create(builder, symLoc, castTo);
+ });
+ } else if (!global) {
+ global = builder.createGlobal(symLoc, 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, symLoc,
+ sym, symTy, stmtCtx);
+ mlir::Value castTo = builder.createConvert(symLoc, symTy, initVal);
+ fir::HasValueOp::create(builder, symLoc, castTo);
+ });
+ }
+ auto addrOf = fir::AddrOfOp::create(builder, symLoc, global.resultType(),
+ global.getSymbol());
+ fir::CopyOp::create(builder, symLoc, addrOf, fir::getBase(exv),
+ /*noOverlap=*/true);
+}
+
+/// Checks if a derived type is eligible for component-wise initialization.
+/// This is preferred over a bulk memcpy when the type contains at least
+/// one pointer/allocatable component, but no components with explicit
+/// default initializers.
+static bool isEligibleForComponentWiseInit(
+ const Fortran::semantics::DerivedTypeSpec &derivedSpec) {
+ bool hasPtrOrAlloc = false;
+ // Worklist for iterative traversal of the derived type tree.
+ llvm::SmallVector<const Fortran::semantics::DerivedTypeSpec *> worklist;
+ worklist.push_back(&derivedSpec);
+ while (!worklist.empty()) {
+ const Fortran::semantics::DerivedTypeSpec *currentSpec =
+ worklist.pop_back_val();
+ const Fortran::semantics::Scope *scope = currentSpec->scope();
+ assert(scope && "derived type has no scope");
+ const auto &typeDetails =
+ currentSpec->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();
+ // Return false if any component has an explicit default initializer.
+ const auto *objDetails =
+ comp.detailsIf<Fortran::semantics::ObjectEntityDetails>();
+ const auto *procDetails =
+ comp.detailsIf<Fortran::semantics::ProcEntityDetails>();
+ if ((objDetails && objDetails->init()) ||
+ (procDetails && procDetails->init())) {
+ return false;
+ }
+ if (Fortran::semantics::IsAllocatableOrPointer(comp)) {
+ hasPtrOrAlloc = true;
+ continue;
+ }
+ // Traverse nested derived types.
+ if (const Fortran::semantics::DeclTypeSpec *declTy = comp.GetType()) {
+ if (const Fortran::semantics::DerivedTypeSpec *nestedSpec =
+ declTy->AsDerived()) {
+ if (Fortran::lower::hasDefaultInitialization(comp)) {
+ // Return false for arrays of derived types requiring
+ // initialization.
+ if (comp.Rank() > 0) {
+ return false;
+ }
----------------
jeanPerier wrote:
nit: no braces around single line if.
https://github.com/llvm/llvm-project/pull/187465
More information about the flang-commits
mailing list