Skip to content

Commit de404fd

Browse files
committed
Update lowering of equivalences after semantics change.
Semantics analysis is now doing an analysis of equivalence default and explicit initializations to detect conflicting initializations. This allows lowering to re-use semantics tools/results and: 1. Remove the interval analysis and use GetStorageAssociations to get a group of symbol that belongs to the same aggregate. 2. Simplify the static equivalence initialization lowering (simply use the generated integer array expression with the equivalence initial value, that already combines all the equivalence members default and explicit initialization). Side consequences of this change: 1. The aggregate storage names are changed, they now use the first member name in alphabetical order. The rational is to cover the cases where we need to ensure that the same name is built in two different translation unit for a same equivalence. The previous naming was relying on a mix of offset and scope order. There is no guarantee the scope order is the same in module files. 2. The aggregate storage types are now fir.array<sizexiw> where size*w/8 is the equivalence storage size and w is the biggest possible storage units given the equivalence member types and Fortran storage unit rules.
1 parent 2852010 commit de404fd

File tree

9 files changed

+178
-394
lines changed

9 files changed

+178
-394
lines changed

flang/include/flang/Lower/PFTBuilder.h

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -392,31 +392,49 @@ struct Variable {
392392
std::size_t aliasOffset{};
393393
};
394394

395+
/// <offset, size> pair
395396
using Interval = std::tuple<std::size_t, std::size_t>;
396397

397398
/// An interval of storage is a contiguous block of memory to be allocated or
398399
/// mapped onto another variable. Aliasing variables will be pointers into
399400
/// interval stores and may overlap each other.
400401
struct AggregateStore {
401-
AggregateStore(Interval &&interval, const Fortran::semantics::Scope &scope,
402-
bool isDeclaration = false)
403-
: interval{std::move(interval)}, scope{&scope}, isDecl{isDeclaration} {}
404-
AggregateStore(
405-
Interval &&interval, const Fortran::semantics::Scope &scope,
406-
const llvm::SmallVector<const semantics::Symbol *> &unorderedVars,
407-
bool isDeclaration = false);
408-
409-
bool isGlobal() const { return vars.size() > 0; }
402+
AggregateStore(Interval &&interval,
403+
const Fortran::semantics::Symbol &namingSym,
404+
bool isDeclaration = false, bool isGlobal = false)
405+
: interval{std::move(interval)}, namingSymbol{&namingSym},
406+
isDecl{isDeclaration}, isGlobalAggregate{isGlobal} {}
407+
AggregateStore(const semantics::Symbol &initialValueSym,
408+
const semantics::Symbol &namingSym,
409+
bool isDeclaration = false, bool isGlobal = false)
410+
: interval{initialValueSym.offset(), initialValueSym.size()},
411+
namingSymbol{&namingSym}, initialValueSymbol{&initialValueSym},
412+
isDecl{isDeclaration}, isGlobalAggregate{isGlobal} {};
413+
414+
bool isGlobal() const { return isGlobalAggregate; }
410415
bool isDeclaration() const { return isDecl; }
411416
/// Get offset of the aggregate inside its scope.
412417
std::size_t getOffset() const { return std::get<0>(interval); }
413-
418+
/// Returns symbols holding the aggregate initial value if any.
419+
const semantics::Symbol *getInitialValueSymbol() const {
420+
return initialValueSymbol;
421+
}
422+
/// Returns the symbol that gives its name to the aggregate.
423+
const semantics::Symbol &getNamingSymbol() const { return *namingSymbol; }
424+
/// Scope to which the aggregates belongs to.
425+
const semantics::Scope &getOwningScope() const {
426+
return getNamingSymbol().owner();
427+
}
428+
/// <offset, size> of the aggregate in its scope.
414429
Interval interval{};
415-
/// scope in which the interval is.
416-
const Fortran::semantics::Scope *scope;
417-
llvm::SmallVector<const semantics::Symbol *> vars{};
430+
/// Symbol that gives its name to the aggregate. Always set by constructor.
431+
const semantics::Symbol *namingSymbol;
432+
/// Compiler generated symbol with the aggregate initial value if any.
433+
const semantics::Symbol *initialValueSymbol = nullptr;
418434
/// Is this a declaration of a storage defined in another scope ?
419435
bool isDecl;
436+
/// Is this a global aggregate ?
437+
bool isGlobalAggregate;
420438
};
421439

422440
explicit Variable(const Fortran::semantics::Symbol &sym, bool global = false,
@@ -464,7 +482,7 @@ struct Variable {
464482
return std::visit(
465483
common::visitors{
466484
[](const Nominal &x) { return &x.symbol->GetUltimate().owner(); },
467-
[](const AggregateStore &agg) { return agg.scope; }},
485+
[](const AggregateStore &agg) { return &agg.getOwningScope(); }},
468486
var);
469487
}
470488

flang/lib/Lower/ConvertVariable.cpp

Lines changed: 35 additions & 209 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,10 @@
2424
#include "flang/Lower/Todo.h"
2525
#include "flang/Optimizer/Builder/Character.h"
2626
#include "flang/Optimizer/Builder/FIRBuilder.h"
27+
#include "flang/Optimizer/Builder/Runtime/Derived.h"
2728
#include "flang/Optimizer/Dialect/FIRAttr.h"
2829
#include "flang/Optimizer/Dialect/FIRDialect.h"
2930
#include "flang/Optimizer/Dialect/FIROps.h"
30-
#include "flang/Optimizer/Builder/Runtime/Derived.h"
3131
#include "flang/Optimizer/Support/FIRContext.h"
3232
#include "flang/Optimizer/Support/FatalError.h"
3333
#include "flang/Semantics/tools.h"
@@ -588,147 +588,19 @@ getAggregateStore(Fortran::lower::AggregateStoreMap &storeMap,
588588
/// Build the name for the storage of a global equivalence.
589589
static std::string mangleGlobalAggregateStore(
590590
const Fortran::lower::pft::Variable::AggregateStore &st) {
591-
assert(st.isGlobal() && "cannot name local aggregate");
592-
return Fortran::lower::mangle::mangleName(*st.vars[0]);
593-
}
594-
595-
// In equivalences, 19.5.3.4 point 10 gives rules regarding explicit and default
596-
// initialization interaction. The interpretation of this point is not
597-
// ubiquitous among compilers, but the majority (nag, ifort, nvfortran) accept
598-
// full or partial overlap of explicit init over default init. In case of
599-
// partial overlap, these compiler still apply the default init for the
600-
// components for which the storage is not explicitly initialized.
601-
//
602-
// In the example below, the equivalence storage must be initialized to [3 , 2].
603-
// 3 comes from the explicit init of k, and 2 from part of the deafult init of
604-
// a.
605-
//
606-
// type t
607-
// i = 1
608-
// j = 2
609-
// end type
610-
// type(t), save :: a
611-
// integer, save :: k = 3
612-
// equivalence (a, k)
613-
//
614-
615-
/// Helper to analyze overlaps between default and explicit initialization in
616-
/// equivalences. \p offset is the current equivalence offset, memIter is the
617-
/// current symbol iterator over the equivalence members. It must point to a
618-
/// member that has default initialization. \p endIter must be the end of the
619-
/// equivalence members iterators. This will tell if the equivalence member \p
620-
/// memIter is fully, partially, or not overlapped by members with explicit
621-
/// initialization.
622-
enum class OverlapWithExplicitInit { None, Partial, Full };
623-
template <typename SymIter>
624-
static OverlapWithExplicitInit analyzeDefaultInitOverlap(std::size_t offset,
625-
SymIter memIter,
626-
SymIter endIter) {
627-
const auto *mem = *memIter;
628-
auto isFullyEquivalencedByExplicitInit = false;
629-
auto overlapsWithExplicitInit = false;
630-
if (mem->offset() < offset)
631-
// Explicit initialization was already performed for part of the storage
632-
// that also have default initialization.
633-
overlapsWithExplicitInit = true;
634-
auto memEnd = mem->offset() + mem->size();
635-
if (memEnd > offset) {
636-
// The default initialization overlaps storage that has no initialization
637-
// yet. It may have to be taken into account if no further explicit
638-
// initialization overrides it.
639-
auto fullExplicitInitUntil = offset;
640-
for (auto peek = memIter; peek < endIter; ++peek) {
641-
const auto *nextSym = *peek;
642-
if (nextSym->offset() > memEnd)
643-
// Reached the end of the storage that may be default initialized.
644-
break;
645-
if (const auto *nextDet = nextSym->template detailsIf<
646-
Fortran::semantics::ObjectEntityDetails>())
647-
if (nextDet->init()) {
648-
// Test there is no gap between the previous explicit init and this
649-
// one.
650-
overlapsWithExplicitInit = true;
651-
if (nextSym->offset() == fullExplicitInitUntil)
652-
fullExplicitInitUntil = nextSym->offset() + nextSym->size();
653-
}
654-
}
655-
isFullyEquivalencedByExplicitInit = fullExplicitInitUntil >= memEnd;
656-
} else {
657-
isFullyEquivalencedByExplicitInit = overlapsWithExplicitInit;
658-
}
659-
if (!overlapsWithExplicitInit)
660-
return OverlapWithExplicitInit::None;
661-
if (isFullyEquivalencedByExplicitInit)
662-
return OverlapWithExplicitInit::Full;
663-
return OverlapWithExplicitInit::Partial;
591+
return Fortran::lower::mangle::mangleName(st.getNamingSymbol());
664592
}
665593

666594
/// Build the type for the storage of an equivalence.
667-
static mlir::TupleType
595+
static mlir::Type
668596
getAggregateType(Fortran::lower::AbstractConverter &converter,
669597
const Fortran::lower::pft::Variable::AggregateStore &st) {
670-
auto &builder = converter.getFirOpBuilder();
671-
auto i8Ty = builder.getIntegerType(8);
672-
llvm::SmallVector<mlir::Type> members;
673-
std::size_t counter = std::get<0>(st.interval);
674-
675-
for (auto iter = st.vars.begin(); iter != st.vars.end(); ++iter) {
676-
const auto *mem = *iter;
677-
if (const auto *memDet =
678-
mem->detailsIf<Fortran::semantics::ObjectEntityDetails>()) {
679-
if (mem->offset() > counter) {
680-
fir::SequenceType::Shape len = {
681-
static_cast<fir::SequenceType::Extent>(mem->offset() - counter)};
682-
auto byteTy = builder.getIntegerType(8);
683-
auto memTy = fir::SequenceType::get(len, byteTy);
684-
members.push_back(memTy);
685-
counter = mem->offset();
686-
}
687-
if (memDet->init()) {
688-
auto memTy = converter.genType(*mem);
689-
members.push_back(memTy);
690-
counter = mem->offset() + mem->size();
691-
} else if (hasDefaultInitialization(*mem)) {
692-
auto overlap = analyzeDefaultInitOverlap(counter, iter, st.vars.end());
693-
if (overlap == OverlapWithExplicitInit::None) {
694-
if (mem->offset() == counter) {
695-
// No overlap with explicit init on the storage and default init
696-
// not yet performed for this storage. Apply default initialization.
697-
auto memTy = converter.genType(*mem);
698-
members.push_back(memTy);
699-
counter = mem->offset() + mem->size();
700-
} else {
701-
// It is possible the storage was already default initialized by an
702-
// entity of the same type. The standard mandates in 19.5.3.4 point
703-
// 10 that only one type must provide default initialization for a
704-
// storage. Simply ensure the already default initialized storage
705-
// has the same size.
706-
assert(counter == mem->offset() + mem->size() &&
707-
"bad default init overlap");
708-
}
709-
} else if (overlap == OverlapWithExplicitInit::Partial) {
710-
// Must interleave pieces of default initialization with explicit init
711-
// to achieve ifort, nvfortran and nag semantics.
712-
TODO(converter.genLocation(mem->name()),
713-
"overlapping default and explicit initialization in equivalence "
714-
"storage");
715-
} else {
716-
// The storage if fully covered with explicit initialization, simply
717-
// ignore the default initialization and let explicit initialization
718-
// happen.
719-
assert(overlap == OverlapWithExplicitInit::Full);
720-
}
721-
}
722-
}
723-
}
724-
if (counter < std::get<0>(st.interval) + std::get<1>(st.interval)) {
725-
fir::SequenceType::Shape len = {static_cast<fir::SequenceType::Extent>(
726-
std::get<0>(st.interval) + std::get<1>(st.interval) - counter)};
727-
auto memTy = fir::SequenceType::get(len, i8Ty);
728-
members.push_back(memTy);
729-
}
730-
return mlir::TupleType::get(builder.getContext(), members);
598+
if (const auto *initSym = st.getInitialValueSymbol())
599+
return converter.genType(*initSym);
600+
auto byteTy = converter.getFirOpBuilder().getIntegerType(8);
601+
return fir::SequenceType::get(std::get<1>(st.interval), byteTy);
731602
}
603+
732604
/// Define a GlobalOp for the storage of a global equivalence described
733605
/// by \p aggregate. The global is named \p aggName and is created with
734606
/// the provided \p linkage.
@@ -745,70 +617,21 @@ static fir::GlobalOp defineGlobalAggregateStore(
745617
assert(aggregate.isGlobal() && "not a global interval");
746618
auto &builder = converter.getFirOpBuilder();
747619
auto loc = converter.getCurrentLocation();
748-
auto idxTy = builder.getIndexType();
749-
mlir::TupleType aggTy = getAggregateType(converter, aggregate);
750-
auto initFunc = [&](fir::FirOpBuilder &builder) {
751-
mlir::Value cb = builder.create<fir::UndefOp>(loc, aggTy);
752-
unsigned tupIdx = 0;
753-
std::size_t offset = std::get<0>(aggregate.interval);
754-
LLVM_DEBUG(llvm::dbgs() << "equivalence {\n");
755-
for (auto iter = aggregate.vars.begin(); iter != aggregate.vars.end();
756-
++iter) {
757-
const auto *mem = *iter;
758-
if (const auto *memDet =
759-
mem->detailsIf<Fortran::semantics::ObjectEntityDetails>()) {
760-
if (mem->offset() > offset) {
761-
++tupIdx;
762-
offset = mem->offset();
763-
}
764-
bool applyDefaultInit = false;
765-
if (hasDefaultInitialization(*mem)) {
766-
auto overlap =
767-
analyzeDefaultInitOverlap(offset, iter, aggregate.vars.end());
768-
if (overlap == OverlapWithExplicitInit::None) {
769-
if (mem->offset() == offset)
770-
// No explicit init. Not yet default initialized.
771-
applyDefaultInit = true;
772-
else
773-
// Already default initialized.
774-
assert(offset == mem->offset() + mem->size() &&
775-
"bad default init overlap");
776-
} else if (overlap == OverlapWithExplicitInit::Partial) {
777-
TODO(converter.genLocation(mem->name()),
778-
"overlapping default and explicit initialization in "
779-
"equivalence storage");
780-
} else {
781-
// Explicit inits completely override default init.
782-
assert(overlap == OverlapWithExplicitInit::Full);
783-
}
784-
}
785-
if (memDet->init() || applyDefaultInit) {
786-
LLVM_DEBUG(llvm::dbgs()
787-
<< "offset: " << mem->offset() << " is " << *mem << '\n');
620+
auto aggTy = getAggregateType(converter, aggregate);
621+
if (const auto *initSym = aggregate.getInitialValueSymbol())
622+
if (const auto *objectDetails =
623+
initSym->detailsIf<Fortran::semantics::ObjectEntityDetails>())
624+
if (objectDetails->init()) {
625+
auto initFunc = [&](fir::FirOpBuilder &builder) {
788626
Fortran::lower::StatementContext stmtCtx;
789-
mlir::Value initVal;
790-
if (memDet->init())
791-
// Explicit initialization.
792-
initVal = fir::getBase(genInitializerExprValue(
793-
converter, loc, memDet->init().value(), stmtCtx));
794-
else
795-
initVal = genDefaultInitializerValue(
796-
converter, loc, *mem, converter.genType(*mem), stmtCtx);
797-
auto offVal = builder.createIntegerConstant(loc, idxTy, tupIdx);
798-
auto castVal =
799-
builder.createConvert(loc, aggTy.getType(tupIdx), initVal);
800-
cb = builder.create<fir::InsertValueOp>(loc, aggTy, cb, castVal,
801-
offVal);
802-
++tupIdx;
803-
offset = mem->offset() + mem->size();
804-
}
627+
auto initVal = fir::getBase(genInitializerExprValue(
628+
converter, loc, objectDetails->init().value(), stmtCtx));
629+
builder.create<fir::HasValueOp>(loc, initVal);
630+
};
631+
return builder.createGlobal(loc, aggTy, aggName,
632+
/*isConstant=*/false, initFunc, linkage);
805633
}
806-
}
807-
LLVM_DEBUG(llvm::dbgs() << "}\n");
808-
builder.create<fir::HasValueOp>(loc, cb);
809-
};
810-
return builder.createGlobal(loc, aggTy, aggName,
811-
/*isConstant=*/false, initFunc, linkage);
634+
return builder.createGlobal(loc, aggTy, aggName, linkage);
812635
}
813636

814637
/// Declare a GlobalOp for the storage of a global equivalence described
@@ -893,8 +716,8 @@ static void instantiateAlias(Fortran::lower::AbstractConverter &converter,
893716
auto store = getAggregateStore(storeMap, var);
894717
auto i8Ty = builder.getIntegerType(8);
895718
auto i8Ptr = builder.getRefType(i8Ty);
896-
auto offset =
897-
builder.createIntegerConstant(loc, idxTy, sym.offset() - aliasOffset);
719+
auto offset = builder.createIntegerConstant(
720+
loc, idxTy, sym.GetUltimate().offset() - aliasOffset);
898721
auto ptr = builder.create<fir::CoordinateOp>(loc, i8Ptr, store,
899722
mlir::ValueRange{offset});
900723
auto preAlloc = builder.createConvert(
@@ -983,15 +806,18 @@ getCommonMembersWithInitAliases(const Fortran::semantics::Symbol &common) {
983806
// common members * common members
984807
for (const auto &set : common.owner().equivalenceSets())
985808
for (const auto &obj : set) {
986-
if (const auto &details =
987-
obj.symbol.detailsIf<Fortran::semantics::ObjectEntityDetails>()) {
988-
const auto *com = FindCommonBlockContaining(obj.symbol);
989-
if (!details->init() || com != &common)
990-
continue;
991-
// This is an alias with an init that belongs to the list
992-
if (std::find(members.begin(), members.end(), obj.symbol) ==
993-
members.end())
994-
members.emplace_back(obj.symbol);
809+
if (!obj.symbol.test(Fortran::semantics::Symbol::Flag::CompilerCreated)) {
810+
if (const auto &details =
811+
obj.symbol
812+
.detailsIf<Fortran::semantics::ObjectEntityDetails>()) {
813+
const auto *com = FindCommonBlockContaining(obj.symbol);
814+
if (!details->init() || com != &common)
815+
continue;
816+
// This is an alias with an init that belongs to the list
817+
if (std::find(members.begin(), members.end(), obj.symbol) ==
818+
members.end())
819+
members.emplace_back(obj.symbol);
820+
}
995821
}
996822
}
997823
return members;

0 commit comments

Comments
 (0)