Skip to content

Commit dde28cd

Browse files
committed
Fix module variables definition vs declaration
The initializer of module variables (if any) must only be emitted when lowering the compilation unit that defines the module, otherwise, this lead to conflict at link time. Current lowering was already accounting for this with the `var.isDeclaration()` helper to tell if a variable was owned by another module. However, it was based on a core assumption that turned out to be wrong. The core assumption was that any module symbols that was instantiated and had not `semantics::UseAssoc` details was necessarily being lowered in the compilation unit defining the module. It turned out to be wrong for at least the members of a module namelist used in a another compilation unit, as well as for the type descriptor symbol of a derived type from another compilation unit used to defined a new derived type in a module. Both cases have valid reasons to not come as use associated symbols (in both cases, the user never really used them). Part of the need for isDeclaration() came from a "create fir::GlobalOp once per compilation unit and do not touch it anymore" approach that was tough because global op initializer can depend on each other in circular ways. Instead of relying on this assumption that might be broken in other cases too, change the approach to define module globals: when using a module variable (in a when instantiating variables to lower a function, or the initializer or another variable), do not try to define it, only create the globalOp with the right linkage if it does not exist already. Only create initializer body for module globals when actually lowering the module. If the fir.global already exist (its address was required before the it was itself lowered), just create the initializer body at that point. This removes some assumptions and allows simplifying the PFT symbol analysis that was trying to keep track of the "declaration vs definition" aspects for aggregates.
1 parent 1d2f9cc commit dde28cd

File tree

5 files changed

+190
-101
lines changed

5 files changed

+190
-101
lines changed

flang/include/flang/Lower/PFTBuilder.h

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "flang/Lower/PFTDefs.h"
2424
#include "flang/Parser/parse-tree.h"
2525
#include "flang/Semantics/attr.h"
26+
#include "flang/Semantics/scope.h"
2627
#include "flang/Semantics/symbol.h"
2728
#include "llvm/Support/ErrorHandling.h"
2829
#include "llvm/Support/raw_ostream.h"
@@ -391,9 +392,6 @@ struct Variable {
391392
const semantics::Symbol *symbol{};
392393

393394
bool isGlobal() const { return global; }
394-
bool isDeclaration() const {
395-
return !symbol || symbol != &symbol->GetUltimate();
396-
}
397395

398396
int depth{};
399397
bool global{};
@@ -413,18 +411,16 @@ struct Variable {
413411
struct AggregateStore {
414412
AggregateStore(Interval &&interval,
415413
const Fortran::semantics::Symbol &namingSym,
416-
bool isDeclaration = false, bool isGlobal = false)
414+
bool isGlobal = false)
417415
: interval{std::move(interval)}, namingSymbol{&namingSym},
418-
isDecl{isDeclaration}, isGlobalAggregate{isGlobal} {}
416+
isGlobalAggregate{isGlobal} {}
419417
AggregateStore(const semantics::Symbol &initialValueSym,
420-
const semantics::Symbol &namingSym,
421-
bool isDeclaration = false, bool isGlobal = false)
418+
const semantics::Symbol &namingSym, bool isGlobal = false)
422419
: interval{initialValueSym.offset(), initialValueSym.size()},
423420
namingSymbol{&namingSym}, initialValueSymbol{&initialValueSym},
424-
isDecl{isDeclaration}, isGlobalAggregate{isGlobal} {};
421+
isGlobalAggregate{isGlobal} {};
425422

426423
bool isGlobal() const { return isGlobalAggregate; }
427-
bool isDeclaration() const { return isDecl; }
428424
/// Get offset of the aggregate inside its scope.
429425
std::size_t getOffset() const { return std::get<0>(interval); }
430426
/// Returns symbols holding the aggregate initial value if any.
@@ -443,8 +439,6 @@ struct Variable {
443439
const semantics::Symbol *namingSymbol;
444440
/// Compiler generated symbol with the aggregate initial value if any.
445441
const semantics::Symbol *initialValueSymbol = nullptr;
446-
/// Is this a declaration of a storage defined in another scope ?
447-
bool isDecl;
448442
/// Is this a global aggregate ?
449443
bool isGlobalAggregate;
450444
};
@@ -485,9 +479,10 @@ struct Variable {
485479
return std::visit([](const auto &x) { return x.isGlobal(); }, var);
486480
}
487481

488-
/// Is this a declaration of a variable owned by another scope ?
489-
bool isDeclaration() const {
490-
return std::visit([](const auto &x) { return x.isDeclaration(); }, var);
482+
/// Is this a module variable ?
483+
bool isModuleVariable() const {
484+
const semantics::Scope *scope = getOwningScope();
485+
return scope && scope->IsModule();
491486
}
492487

493488
const Fortran::semantics::Scope *getOwningScope() const {
@@ -700,6 +695,9 @@ struct ModuleLikeUnit : public ProgramUnit {
700695
/// Get the starting source location for this module like unit.
701696
parser::CharBlock getStartingSourceLoc() const;
702697

698+
/// Get the module scope.
699+
const Fortran::semantics::Scope &getScope() const;
700+
703701
ModuleStatement beginStmt;
704702
ModuleStatement endStmt;
705703
std::list<FunctionLikeUnit> nestedFunctions;

flang/lib/Lower/Bridge.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2699,8 +2699,13 @@ class FirConverter : public Fortran::lower::AbstractConverter {
26992699
fir::NameUniquer::doGenerated("ModuleSham"),
27002700
mlir::FunctionType::get(context, llvm::None, llvm::None));
27012701
builder = new fir::FirOpBuilder(func, bridge.getKindMap());
2702-
for (const Fortran::lower::pft::Variable &var : mod.getOrderedSymbolTable())
2703-
Fortran::lower::defineModuleVariable(*this, var);
2702+
for (const Fortran::lower::pft::Variable &var :
2703+
mod.getOrderedSymbolTable()) {
2704+
// Only define the variables owned by this module.
2705+
const Fortran::semantics::Scope *owningScope = var.getOwningScope();
2706+
if (!owningScope || mod.getScope() == *owningScope)
2707+
Fortran::lower::defineModuleVariable(*this, var);
2708+
}
27042709
if (mlir::Region *region = func.getCallableRegion())
27052710
region->dropAllReferences();
27062711
func.erase();

flang/lib/Lower/ConvertVariable.cpp

Lines changed: 92 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,8 @@ static fir::GlobalOp declareGlobal(Fortran::lower::AbstractConverter &converter,
125125
llvm::StringRef globalName,
126126
mlir::StringAttr linkage) {
127127
fir::FirOpBuilder &builder = converter.getFirOpBuilder();
128+
if (fir::GlobalOp global = builder.getNamedGlobal(globalName))
129+
return global;
128130
const Fortran::semantics::Symbol &sym = var.getSymbol();
129131
mlir::Location loc = converter.genLocation(sym.name());
130132
// Resolve potential host and module association before checking that this
@@ -356,6 +358,24 @@ static mlir::Value genDefaultInitializerValue(
356358
return initialValue;
357359
}
358360

361+
/// Does this global already have an initializer ?
362+
static bool globalIsInitialized(fir::GlobalOp global) {
363+
return !global.getRegion().empty() || global.initVal();
364+
}
365+
366+
/// Call \p genInit to generate code inside \p global initializer region.
367+
static void
368+
createGlobalInitialization(fir::FirOpBuilder &builder, fir::GlobalOp global,
369+
std::function<void(fir::FirOpBuilder &)> genInit) {
370+
mlir::Region &region = global.getRegion();
371+
region.push_back(new mlir::Block);
372+
mlir::Block &block = region.back();
373+
auto insertPt = builder.saveInsertionPoint();
374+
builder.setInsertionPointToStart(&block);
375+
genInit(builder);
376+
builder.restoreInsertionPoint(insertPt);
377+
}
378+
359379
/// Create the global op and its init if it has one
360380
static fir::GlobalOp defineGlobal(Fortran::lower::AbstractConverter &converter,
361381
const Fortran::lower::pft::Variable &var,
@@ -365,96 +385,96 @@ static fir::GlobalOp defineGlobal(Fortran::lower::AbstractConverter &converter,
365385
const Fortran::semantics::Symbol &sym = var.getSymbol();
366386
mlir::Location loc = converter.genLocation(sym.name());
367387
bool isConst = sym.attrs().test(Fortran::semantics::Attr::PARAMETER);
368-
fir::GlobalOp global;
388+
fir::GlobalOp global = builder.getNamedGlobal(globalName);
389+
if (global && globalIsInitialized(global))
390+
return global;
391+
mlir::Type symTy = converter.genType(var);
392+
if (!global)
393+
global = builder.createGlobal(loc, symTy, globalName, linkage,
394+
mlir::Attribute{}, isConst);
369395
if (Fortran::semantics::IsAllocatableOrPointer(sym)) {
370-
mlir::Type symTy = converter.genType(var);
371396
const auto *details =
372397
sym.detailsIf<Fortran::semantics::ObjectEntityDetails>();
373398
if (details && details->init()) {
374399
auto expr = *details->init();
375-
auto init = [&](fir::FirOpBuilder &b) {
400+
createGlobalInitialization(builder, global, [&](fir::FirOpBuilder &b) {
376401
mlir::Value box =
377402
Fortran::lower::genInitialDataTarget(converter, loc, symTy, expr);
378403
b.create<fir::HasValueOp>(loc, box);
379-
};
380-
global =
381-
builder.createGlobal(loc, symTy, globalName, isConst, init, linkage);
404+
});
382405
} else {
383406
// Create unallocated/disassociated descriptor if no explicit init
384-
auto init = [&](fir::FirOpBuilder &b) {
407+
createGlobalInitialization(builder, global, [&](fir::FirOpBuilder &b) {
385408
mlir::Value box =
386409
fir::factory::createUnallocatedBox(b, loc, symTy, llvm::None);
387410
b.create<fir::HasValueOp>(loc, box);
388-
};
389-
global =
390-
builder.createGlobal(loc, symTy, globalName, isConst, init, linkage);
411+
});
391412
}
392413

393414
} else if (const auto *details =
394415
sym.detailsIf<Fortran::semantics::ObjectEntityDetails>()) {
395416
if (details->init()) {
396-
mlir::Type symTy = converter.genType(var);
397417
if (fir::isa_char(symTy)) {
398418
// CHARACTER literal
399419
if (auto chLit = getCharacterLiteralCopy(details->init().value())) {
400420
mlir::StringAttr init =
401421
builder.getStringAttr(std::get<std::string>(*chLit));
402-
global = builder.createGlobal(loc, symTy, globalName, linkage, init,
403-
isConst);
422+
global->setAttr(global.getInitValAttrName(), init);
404423
} else {
405424
fir::emitFatalError(loc, "CHARACTER has unexpected initial value");
406425
}
407426
} else {
408-
global = builder.createGlobal(
409-
loc, symTy, globalName, isConst,
410-
[&](fir::FirOpBuilder &builder) {
427+
createGlobalInitialization(
428+
builder, global, [&](fir::FirOpBuilder &builder) {
411429
Fortran::lower::StatementContext stmtCtx(/*prohibited=*/true);
412430
fir::ExtendedValue initVal = genInitializerExprValue(
413431
converter, loc, details->init().value(), stmtCtx);
414432
mlir::Value castTo =
415433
builder.createConvert(loc, symTy, fir::getBase(initVal));
416434
stmtCtx.finalize();
417435
builder.create<fir::HasValueOp>(loc, castTo);
418-
},
419-
linkage);
436+
});
420437
}
421438
} else if (hasDefaultInitialization(sym)) {
422-
mlir::Type symTy = converter.genType(var);
423-
global = builder.createGlobal(
424-
loc, symTy, globalName, isConst,
425-
[&](fir::FirOpBuilder &builder) {
439+
createGlobalInitialization(
440+
builder, global, [&](fir::FirOpBuilder &builder) {
426441
Fortran::lower::StatementContext stmtCtx(/*prohibited=*/true);
427442
mlir::Value initVal =
428443
genDefaultInitializerValue(converter, loc, sym, symTy, stmtCtx);
429-
mlir::Value castTo =
430-
builder.createConvert(loc, symTy, fir::getBase(initVal));
444+
mlir::Value castTo = builder.createConvert(loc, symTy, initVal);
431445
stmtCtx.finalize();
432446
builder.create<fir::HasValueOp>(loc, castTo);
433-
},
434-
linkage);
447+
});
435448
}
436449
} else if (sym.has<Fortran::semantics::CommonBlockDetails>()) {
437450
mlir::emitError(loc, "COMMON symbol processed elsewhere");
438451
} else {
439452
TODO(loc, "global"); // Procedure pointer or something else
440453
}
441-
// Creates undefined initializer for globals without initialziers
442-
if (!global) {
443-
mlir::Type symTy = converter.genType(var);
444-
global = builder.createGlobal(
445-
loc, symTy, globalName, isConst,
446-
[&](fir::FirOpBuilder &builder) {
454+
// Creates undefined initializer for globals without initializers
455+
if (!globalIsInitialized(global))
456+
createGlobalInitialization(
457+
builder, global, [&](fir::FirOpBuilder &builder) {
447458
builder.create<fir::HasValueOp>(
448459
loc, builder.create<fir::UndefOp>(loc, symTy));
449-
},
450-
linkage);
451-
}
460+
});
452461
// Set public visibility to prevent global definition to be optimized out
453462
// even if they have no initializer and are unused in this compilation unit.
454463
global.setVisibility(mlir::SymbolTable::Visibility::Public);
455464
return global;
456465
}
457466

467+
/// Return linkage attribute for \p var.
468+
static mlir::StringAttr
469+
getLinkageAttribute(fir::FirOpBuilder &builder,
470+
const Fortran::lower::pft::Variable &var) {
471+
if (var.isModuleVariable())
472+
return {}; // external linkage
473+
// Otherwise, the variable is owned by a procedure and must not be visible in
474+
// other compilation units.
475+
return builder.createInternalLinkage();
476+
}
477+
458478
/// Instantiate a global variable. If it hasn't already been processed, add
459479
/// the global to the ModuleOp as a new uniqued symbol and initialize it with
460480
/// the correct value. It will be referenced on demand using `fir.addr_of`.
@@ -467,18 +487,13 @@ static void instantiateGlobal(Fortran::lower::AbstractConverter &converter,
467487
std::string globalName = Fortran::lower::mangle::mangleName(sym);
468488
mlir::Location loc = converter.genLocation(sym.name());
469489
fir::GlobalOp global = builder.getNamedGlobal(globalName);
470-
if (!global) {
471-
if (var.isDeclaration()) {
472-
// Using a global from a module not defined in this compilation unit.
473-
mlir::StringAttr externalLinkage;
474-
global = declareGlobal(converter, var, globalName, externalLinkage);
475-
} else {
476-
mlir::StringAttr linkage; // external if remains empty.
477-
if (!sym.owner().IsModule() &&
478-
!Fortran::semantics::FindCommonBlockContaining(sym))
479-
linkage = builder.createInternalLinkage();
480-
global = defineGlobal(converter, var, globalName, linkage);
481-
}
490+
mlir::StringAttr linkage = getLinkageAttribute(builder, var);
491+
if (var.isModuleVariable()) {
492+
// A module global was or will be defined when lowering the module. Emit
493+
// only a declaration if the global does not exist at that point.
494+
global = declareGlobal(converter, var, globalName, linkage);
495+
} else {
496+
global = defineGlobal(converter, var, globalName, linkage);
482497
}
483498
auto addrOf = builder.create<fir::AddrOfOp>(loc, global.resultType(),
484499
global.getSymbol());
@@ -624,23 +639,27 @@ static fir::GlobalOp defineGlobalAggregateStore(
624639
StringRef aggName, mlir::StringAttr linkage) {
625640
assert(aggregate.isGlobal() && "not a global interval");
626641
fir::FirOpBuilder &builder = converter.getFirOpBuilder();
642+
fir::GlobalOp global = builder.getNamedGlobal(aggName);
643+
if (global && globalIsInitialized(global))
644+
return global;
627645
mlir::Location loc = converter.getCurrentLocation();
628646
mlir::Type aggTy = getAggregateType(converter, aggregate);
647+
if (!global)
648+
global = builder.createGlobal(loc, aggTy, aggName, linkage);
649+
629650
if (const Fortran::semantics::Symbol *initSym =
630651
aggregate.getInitialValueSymbol())
631652
if (const auto *objectDetails =
632653
initSym->detailsIf<Fortran::semantics::ObjectEntityDetails>())
633-
if (objectDetails->init()) {
634-
auto initFunc = [&](fir::FirOpBuilder &builder) {
635-
Fortran::lower::StatementContext stmtCtx;
636-
mlir::Value initVal = fir::getBase(genInitializerExprValue(
637-
converter, loc, objectDetails->init().value(), stmtCtx));
638-
builder.create<fir::HasValueOp>(loc, initVal);
639-
};
640-
return builder.createGlobal(loc, aggTy, aggName,
641-
/*isConstant=*/false, initFunc, linkage);
642-
}
643-
return builder.createGlobal(loc, aggTy, aggName, linkage);
654+
if (objectDetails->init())
655+
createGlobalInitialization(
656+
builder, global, [&](fir::FirOpBuilder &builder) {
657+
Fortran::lower::StatementContext stmtCtx;
658+
mlir::Value initVal = fir::getBase(genInitializerExprValue(
659+
converter, loc, objectDetails->init().value(), stmtCtx));
660+
builder.create<fir::HasValueOp>(loc, initVal);
661+
});
662+
return global;
644663
}
645664

646665
/// Declare a GlobalOp for the storage of a global equivalence described
@@ -656,8 +675,11 @@ static fir::GlobalOp declareGlobalAggregateStore(
656675
const Fortran::lower::pft::Variable::AggregateStore &aggregate,
657676
StringRef aggName, mlir::StringAttr linkage) {
658677
assert(aggregate.isGlobal() && "not a global interval");
678+
fir::FirOpBuilder &builder = converter.getFirOpBuilder();
679+
if (fir::GlobalOp global = builder.getNamedGlobal(aggName))
680+
return global;
659681
mlir::Type aggTy = getAggregateType(converter, aggregate);
660-
return converter.getFirOpBuilder().createGlobal(loc, aggTy, aggName, linkage);
682+
return builder.createGlobal(loc, aggTy, aggName, linkage);
661683
}
662684

663685
/// This is an aggregate store for a set of EQUIVALENCED variables. Create the
@@ -672,23 +694,17 @@ instantiateAggregateStore(Fortran::lower::AbstractConverter &converter,
672694
mlir::Location loc = converter.getCurrentLocation();
673695
std::string aggName = mangleGlobalAggregateStore(var.getAggregateStore());
674696
if (var.isGlobal()) {
675-
// The scope of this aggregate is this procedure.
676-
fir::GlobalOp global = builder.getNamedGlobal(aggName);
677-
if (!global) {
678-
auto &aggregate = var.getAggregateStore();
679-
if (var.isDeclaration()) {
680-
// Using aggregate from a module not defined in the current
681-
// compilation unit.
682-
mlir::StringAttr externalLinkage;
683-
global = declareGlobalAggregateStore(converter, loc, aggregate, aggName,
684-
externalLinkage);
685-
} else {
686-
// The aggregate is owned by a procedure and must not be
687-
// visible in other compilation units.
688-
mlir::StringAttr internalLinkage = builder.createInternalLinkage();
689-
global = defineGlobalAggregateStore(converter, aggregate, aggName,
690-
internalLinkage);
691-
}
697+
fir::GlobalOp global;
698+
auto &aggregate = var.getAggregateStore();
699+
mlir::StringAttr linkage = getLinkageAttribute(builder, var);
700+
if (var.isModuleVariable()) {
701+
// A module global was or will be defined when lowering the module. Emit
702+
// only a declaration if the global does not exist at that point.
703+
global = declareGlobalAggregateStore(converter, loc, aggregate, aggName,
704+
linkage);
705+
} else {
706+
global =
707+
defineGlobalAggregateStore(converter, aggregate, aggName, linkage);
692708
}
693709
auto addr = builder.create<fir::AddrOfOp>(loc, global.resultType(),
694710
global.getSymbol());
@@ -1709,9 +1725,6 @@ void Fortran::lower::defineModuleVariable(
17091725
// Use empty linkage for module variables, which makes them available
17101726
// for use in another unit.
17111727
mlir::StringAttr externalLinkage;
1712-
// Only define variable owned by this module
1713-
if (var.isDeclaration())
1714-
return;
17151728
if (!var.isGlobal())
17161729
fir::emitFatalError(converter.genLocation(),
17171730
"attempting to lower module variable as local");

0 commit comments

Comments
 (0)