Skip to content

Commit e45f6e9

Browse files
authored
[flang][hlfir] Make the parent type the first component (llvm#69348)
Type extension is currently handled in FIR by inlining the parents components as the first member of the record type. This is not correct from a memory layout point of view since the storage size of the parent type may be bigger than the sum of the size of its component (due to alignment requirement). To avoid making FIR types target dependent and fix this issue, make the parent component a single component with the parent type at the beginning of the record type. This also simplifies addressing since parent component is now a "normal" component that can be designated with hlfir.designate. StructureComponent lowering however is a bit more complex since the symbols in the structure component may refer to subcomponents of parent types. Notes: 1. The fix is only done in HLFIR for now, a similar fix should be done in ConvertExpr.cpp to fix the path without HLFIR (I will likely still do it in a new patch since it would be an annoying bug to investigate for people testing flang without HLFIR). 2. The private component extra mangling is useless after this patch. I will remove it after 1. 3. The "parent component" TODO in constant CTOR is free to implement for HLFIR after this patch, but I would rather remove it and test it in a different patch.
1 parent 1abc28f commit e45f6e9

15 files changed

+452
-286
lines changed

flang/include/flang/Lower/ConvertType.h

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ struct SomeType;
4848
namespace semantics {
4949
class Symbol;
5050
class DerivedTypeSpec;
51+
class DerivedTypeDetails;
52+
class Scope;
5153
} // namespace semantics
5254

5355
namespace lower {
@@ -97,6 +99,34 @@ class TypeBuilder {
9799
using namespace evaluate;
98100
FOR_EACH_SPECIFIC_TYPE(extern template class TypeBuilder, )
99101

102+
/// A helper class to reverse iterate through the component names of a derived
103+
/// type, including the parent component and the component of the parents. This
104+
/// is useful to deal with StructureConstructor lowering.
105+
class ComponentReverseIterator {
106+
public:
107+
ComponentReverseIterator(const Fortran::semantics::DerivedTypeSpec &derived) {
108+
setCurrentType(derived);
109+
}
110+
/// Does the current type has a component with \name (does not look-up the
111+
/// components of the parent if any)? If there is a match, the iterator
112+
/// is advanced to the search result.
113+
bool lookup(const Fortran::parser::CharBlock &name) {
114+
componentIt = std::find(componentIt, componentItEnd, name);
115+
return componentIt != componentItEnd;
116+
};
117+
118+
/// Advance iterator to the last components of the current type parent.
119+
const Fortran::semantics::DerivedTypeSpec &advanceToParentType();
120+
121+
private:
122+
void setCurrentType(const Fortran::semantics::DerivedTypeSpec &derived);
123+
const Fortran::semantics::DerivedTypeSpec *currentParentType = nullptr;
124+
const Fortran::semantics::DerivedTypeDetails *currentTypeDetails = nullptr;
125+
using name_iterator =
126+
std::list<Fortran::parser::CharBlock>::const_reverse_iterator;
127+
name_iterator componentIt{};
128+
name_iterator componentItEnd{};
129+
};
100130
} // namespace lower
101131
} // namespace Fortran
102132

flang/include/flang/Semantics/tools.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -509,7 +509,7 @@ template <ComponentKind componentKind> class ComponentIterator {
509509
explicit ComponentPathNode(const DerivedTypeSpec &derived)
510510
: derived_{derived} {
511511
if constexpr (componentKind == ComponentKind::Scope) {
512-
const Scope &scope{DEREF(derived.scope())};
512+
const Scope &scope{DEREF(derived.GetScope())};
513513
nameIterator_ = scope.cbegin();
514514
nameEnd_ = scope.cend();
515515
} else {

flang/include/flang/Semantics/type.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,8 @@ class DerivedTypeSpec {
261261
const SourceName &name() const { return name_; }
262262
const Symbol &typeSymbol() const { return typeSymbol_; }
263263
const Scope *scope() const { return scope_; }
264+
// Return scope_ if it is set, or the typeSymbol_ scope otherwise.
265+
const Scope *GetScope() const;
264266
void set_scope(const Scope &);
265267
void ReplaceScope(const Scope &);
266268
const RawParameters &rawParameters() const { return rawParameters_; }

flang/lib/Lower/ConvertConstant.cpp

Lines changed: 139 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -347,91 +347,160 @@ genConstantValue(Fortran::lower::AbstractConverter &converter,
347347
mlir::Location loc,
348348
const Fortran::lower::SomeExpr &constantExpr);
349349

350+
static mlir::Value genStructureComponentInit(
351+
Fortran::lower::AbstractConverter &converter, mlir::Location loc,
352+
const Fortran::semantics::Symbol &sym, const Fortran::lower::SomeExpr &expr,
353+
mlir::Value res) {
354+
fir::FirOpBuilder &builder = converter.getFirOpBuilder();
355+
fir::RecordType recTy = mlir::cast<fir::RecordType>(res.getType());
356+
std::string name = converter.getRecordTypeFieldName(sym);
357+
mlir::Type componentTy = recTy.getType(name);
358+
auto fieldTy = fir::FieldType::get(recTy.getContext());
359+
assert(componentTy && "failed to retrieve component");
360+
// FIXME: type parameters must come from the derived-type-spec
361+
auto field = builder.create<fir::FieldIndexOp>(
362+
loc, fieldTy, name, recTy,
363+
/*typeParams=*/mlir::ValueRange{} /*TODO*/);
364+
365+
if (Fortran::semantics::IsAllocatable(sym))
366+
TODO(loc, "allocatable component in structure constructor");
367+
368+
if (Fortran::semantics::IsPointer(sym)) {
369+
mlir::Value initialTarget =
370+
Fortran::lower::genInitialDataTarget(converter, loc, componentTy, expr);
371+
res = builder.create<fir::InsertValueOp>(
372+
loc, recTy, res, initialTarget,
373+
builder.getArrayAttr(field.getAttributes()));
374+
return res;
375+
}
376+
377+
if (Fortran::lower::isDerivedTypeWithLenParameters(sym))
378+
TODO(loc, "component with length parameters in structure constructor");
379+
380+
// Special handling for scalar c_ptr/c_funptr constants. The array constant
381+
// must fall through to genConstantValue() below.
382+
if (Fortran::semantics::IsBuiltinCPtr(sym) && sym.Rank() == 0 &&
383+
(Fortran::evaluate::GetLastSymbol(expr) ||
384+
Fortran::evaluate::IsNullPointer(expr))) {
385+
// Builtin c_ptr and c_funptr have special handling because designators
386+
// and NULL() are handled as initial values for them as an extension
387+
// (otherwise only c_ptr_null/c_funptr_null are allowed and these are
388+
// replaced by structure constructors by semantics, so GetLastSymbol
389+
// returns nothing).
390+
391+
// The Ev::Expr is an initializer that is a pointer target (e.g., 'x' or
392+
// NULL()) that must be inserted into an intermediate cptr record value's
393+
// address field, which ought to be an intptr_t on the target.
394+
mlir::Value addr = fir::getBase(
395+
Fortran::lower::genExtAddrInInitializer(converter, loc, expr));
396+
if (addr.getType().isa<fir::BoxProcType>())
397+
addr = builder.create<fir::BoxAddrOp>(loc, addr);
398+
assert((fir::isa_ref_type(addr.getType()) ||
399+
addr.getType().isa<mlir::FunctionType>()) &&
400+
"expect reference type for address field");
401+
assert(fir::isa_derived(componentTy) &&
402+
"expect C_PTR, C_FUNPTR to be a record");
403+
auto cPtrRecTy = componentTy.cast<fir::RecordType>();
404+
llvm::StringRef addrFieldName = Fortran::lower::builtin::cptrFieldName;
405+
mlir::Type addrFieldTy = cPtrRecTy.getType(addrFieldName);
406+
auto addrField = builder.create<fir::FieldIndexOp>(
407+
loc, fieldTy, addrFieldName, componentTy,
408+
/*typeParams=*/mlir::ValueRange{});
409+
mlir::Value castAddr = builder.createConvert(loc, addrFieldTy, addr);
410+
auto undef = builder.create<fir::UndefOp>(loc, componentTy);
411+
addr = builder.create<fir::InsertValueOp>(
412+
loc, componentTy, undef, castAddr,
413+
builder.getArrayAttr(addrField.getAttributes()));
414+
res = builder.create<fir::InsertValueOp>(
415+
loc, recTy, res, addr, builder.getArrayAttr(field.getAttributes()));
416+
return res;
417+
}
418+
419+
mlir::Value val = fir::getBase(genConstantValue(converter, loc, expr));
420+
assert(!fir::isa_ref_type(val.getType()) && "expecting a constant value");
421+
mlir::Value castVal = builder.createConvert(loc, componentTy, val);
422+
res = builder.create<fir::InsertValueOp>(
423+
loc, recTy, res, castVal, builder.getArrayAttr(field.getAttributes()));
424+
return res;
425+
}
426+
350427
// Generate a StructureConstructor inlined (returns raw fir.type<T> value,
351428
// not the address of a global constant).
352429
static mlir::Value genInlinedStructureCtorLitImpl(
353430
Fortran::lower::AbstractConverter &converter, mlir::Location loc,
354431
const Fortran::evaluate::StructureConstructor &ctor, mlir::Type type) {
355432
fir::FirOpBuilder &builder = converter.getFirOpBuilder();
356433
auto recTy = type.cast<fir::RecordType>();
357-
auto fieldTy = fir::FieldType::get(type.getContext());
358-
mlir::Value res = builder.create<fir::UndefOp>(loc, recTy);
359-
360-
for (const auto &[sym, expr] : ctor.values()) {
361-
// Parent components need more work because they do not appear in the
362-
// fir.rec type.
363-
if (sym->test(Fortran::semantics::Symbol::Flag::ParentComp))
364-
TODO(loc, "parent component in structure constructor");
365-
366-
std::string name = converter.getRecordTypeFieldName(sym);
367-
mlir::Type componentTy = recTy.getType(name);
368-
assert(componentTy && "failed to retrieve component");
369-
// FIXME: type parameters must come from the derived-type-spec
370-
auto field = builder.create<fir::FieldIndexOp>(
371-
loc, fieldTy, name, type,
372-
/*typeParams=*/mlir::ValueRange{} /*TODO*/);
373434

374-
if (Fortran::semantics::IsAllocatable(sym))
375-
TODO(loc, "allocatable component in structure constructor");
435+
if (!converter.getLoweringOptions().getLowerToHighLevelFIR()) {
436+
mlir::Value res = builder.create<fir::UndefOp>(loc, recTy);
437+
for (const auto &[sym, expr] : ctor.values()) {
438+
// Parent components need more work because they do not appear in the
439+
// fir.rec type.
440+
if (sym->test(Fortran::semantics::Symbol::Flag::ParentComp))
441+
TODO(loc, "parent component in structure constructor");
442+
res = genStructureComponentInit(converter, loc, sym, expr.value(), res);
443+
}
444+
return res;
445+
}
376446

377-
if (Fortran::semantics::IsPointer(sym)) {
378-
mlir::Value initialTarget = Fortran::lower::genInitialDataTarget(
379-
converter, loc, componentTy, expr.value());
447+
auto fieldTy = fir::FieldType::get(recTy.getContext());
448+
mlir::Value res{};
449+
// When the first structure component values belong to some parent type PT
450+
// and the next values belong to a type extension ET, a new undef for ET must
451+
// be created and the previous PT value inserted into it. There may
452+
// be empty parent types in between ET and PT, hence the list and while loop.
453+
auto insertParentValueIntoExtension = [&](mlir::Type typeExtension) {
454+
assert(res && "res must be set");
455+
llvm::SmallVector<mlir::Type> parentTypes = {typeExtension};
456+
while (true) {
457+
fir::RecordType last = mlir::cast<fir::RecordType>(parentTypes.back());
458+
mlir::Type next =
459+
last.getType(0); // parent components are first in HLFIR.
460+
if (next != res.getType())
461+
parentTypes.push_back(next);
462+
else
463+
break;
464+
}
465+
for (mlir::Type parentType : llvm::reverse(parentTypes)) {
466+
auto undef = builder.create<fir::UndefOp>(loc, parentType);
467+
fir::RecordType parentRecTy = mlir::cast<fir::RecordType>(parentType);
468+
auto field = builder.create<fir::FieldIndexOp>(
469+
loc, fieldTy, parentRecTy.getTypeList()[0].first, parentType,
470+
/*typeParams=*/mlir::ValueRange{} /*TODO*/);
380471
res = builder.create<fir::InsertValueOp>(
381-
loc, recTy, res, initialTarget,
472+
loc, parentRecTy, undef, res,
382473
builder.getArrayAttr(field.getAttributes()));
383-
continue;
384474
}
475+
};
385476

386-
if (Fortran::lower::isDerivedTypeWithLenParameters(sym))
387-
TODO(loc, "component with length parameters in structure constructor");
388-
389-
// Special handling for scalar c_ptr/c_funptr constants. The array constant
390-
// must fall through to genConstantValue() below.
391-
if (Fortran::semantics::IsBuiltinCPtr(sym) && sym->Rank() == 0 &&
392-
(Fortran::evaluate::GetLastSymbol(expr.value()) ||
393-
Fortran::evaluate::IsNullPointer(expr.value()))) {
394-
// Builtin c_ptr and c_funptr have special handling because designators
395-
// and NULL() are handled as initial values for them as an extension
396-
// (otherwise only c_ptr_null/c_funptr_null are allowed and these are
397-
// replaced by structure constructors by semantics, so GetLastSymbol
398-
// returns nothing).
399-
400-
// The Ev::Expr is an initializer that is a pointer target (e.g., 'x' or
401-
// NULL()) that must be inserted into an intermediate cptr record value's
402-
// address field, which ought to be an intptr_t on the target.
403-
mlir::Value addr = fir::getBase(Fortran::lower::genExtAddrInInitializer(
404-
converter, loc, expr.value()));
405-
if (addr.getType().isa<fir::BoxProcType>())
406-
addr = builder.create<fir::BoxAddrOp>(loc, addr);
407-
assert((fir::isa_ref_type(addr.getType()) ||
408-
addr.getType().isa<mlir::FunctionType>()) &&
409-
"expect reference type for address field");
410-
assert(fir::isa_derived(componentTy) &&
411-
"expect C_PTR, C_FUNPTR to be a record");
412-
auto cPtrRecTy = componentTy.cast<fir::RecordType>();
413-
llvm::StringRef addrFieldName = Fortran::lower::builtin::cptrFieldName;
414-
mlir::Type addrFieldTy = cPtrRecTy.getType(addrFieldName);
415-
auto addrField = builder.create<fir::FieldIndexOp>(
416-
loc, fieldTy, addrFieldName, componentTy,
417-
/*typeParams=*/mlir::ValueRange{});
418-
mlir::Value castAddr = builder.createConvert(loc, addrFieldTy, addr);
419-
auto undef = builder.create<fir::UndefOp>(loc, componentTy);
420-
addr = builder.create<fir::InsertValueOp>(
421-
loc, componentTy, undef, castAddr,
422-
builder.getArrayAttr(addrField.getAttributes()));
423-
res = builder.create<fir::InsertValueOp>(
424-
loc, recTy, res, addr, builder.getArrayAttr(field.getAttributes()));
425-
continue;
477+
const Fortran::semantics::DerivedTypeSpec *curentType = nullptr;
478+
for (const auto &[sym, expr] : ctor.values()) {
479+
// This TODO is not needed here anymore, but should be removed in a separate
480+
// patch.
481+
if (sym->test(Fortran::semantics::Symbol::Flag::ParentComp))
482+
TODO(loc, "parent component in structure constructor");
483+
const Fortran::semantics::DerivedTypeSpec *componentParentType =
484+
sym->owner().derivedTypeSpec();
485+
assert(componentParentType && "failed to retrieve component parent type");
486+
if (!res) {
487+
mlir::Type parentType = converter.genType(*componentParentType);
488+
curentType = componentParentType;
489+
res = builder.create<fir::UndefOp>(loc, parentType);
490+
} else if (*componentParentType != *curentType) {
491+
mlir::Type parentType = converter.genType(*componentParentType);
492+
insertParentValueIntoExtension(parentType);
493+
curentType = componentParentType;
426494
}
427-
428-
mlir::Value val =
429-
fir::getBase(genConstantValue(converter, loc, expr.value()));
430-
assert(!fir::isa_ref_type(val.getType()) && "expecting a constant value");
431-
mlir::Value castVal = builder.createConvert(loc, componentTy, val);
432-
res = builder.create<fir::InsertValueOp>(
433-
loc, recTy, res, castVal, builder.getArrayAttr(field.getAttributes()));
495+
res = genStructureComponentInit(converter, loc, sym, expr.value(), res);
434496
}
497+
498+
if (!res) // structure constructor for empty type.
499+
return builder.create<fir::UndefOp>(loc, recTy);
500+
501+
// The last component may belong to a parent type.
502+
if (res.getType() != recTy)
503+
insertParentValueIntoExtension(recTy);
435504
return res;
436505
}
437506

0 commit comments

Comments
 (0)