Skip to content

Commit 2c8cb9a

Browse files
committed
[flang] Handle common block with different sizes in same file
Semantics is not preventing a named common block to appear with different size in a same file (named common block should always have the same storage size (see Fortran 2018 8.10.2.5), but it is a common extension to accept different sizes). Lowering was not coping with this well, since it just use the first common block appearance, starting with BLOCK DATAs to define common blocks (this also was an issue with the blank common block, which can legally appear with different size in different scoping units). Semantics is also not preventing named common from being initialized outside of a BLOCK DATA, and lowering was dealing badly with this, since it only gave an initial value to common blocks Globals if the first common block appearance, starting with BLOCK DATAs had an initial value. Semantics is also allowing blank common to be initialized, while lowering was assuming this would never happen, and was never creating an initial value for it. Lastly, semantics was not complaining if a COMMON block was initialized in several scoping unit in a same file, while lowering can only generate one of these initial value. To fix this, add a structure to keep track of COMMON block properties (biggest size, and initial value if any) at the Program level. Once the size of a common block appearance is know, the common block appearance is checked against this information. It allows semantics to emit an error in case of multiple initialization in different scopes of a same common block, and to warn in case named common blocks appears with different sizes. Lastly, this allows lowering to use the Program level info about common blocks to emit the right GlobalOp for a Common Block, regardless of the COMMON Block appearances order: It emits a GlobalOp with the biggest size, whose lowest bytes are initialized with the initial value if any is given in a scope where the common block appears. Lowering is updated to go emit the common blocks before anything else so that the related GlobalOps are available when lowering the scopes where common block appear. It is also updated to not assume that blank common are never initialized. Differential Revision: https://reviews.llvm.org/D124622
1 parent 1881711 commit 2c8cb9a

17 files changed

+417
-114
lines changed

flang/docs/Extensions.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@ end
206206
* External unit 0 is predefined and connected to the standard error output,
207207
and defined as `ERROR_UNIT` in the intrinsic `ISO_FORTRAN_ENV` module.
208208
* Objects in blank COMMON may be initialized.
209+
* Initialization of COMMON blocks outside of BLOCK DATA subprograms.
209210
* Multiple specifications of the SAVE attribute on the same object
210211
are allowed, with a warning.
211212
* Specific intrinsic functions BABS, IIABS, JIABS, KIABS, ZABS, and CDABS.

flang/include/flang/Lower/ConvertVariable.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,14 @@ void instantiateVariable(AbstractConverter &, const pft::Variable &var,
5454
/// called.
5555
void defineModuleVariable(AbstractConverter &, const pft::Variable &var);
5656

57+
/// Create fir::GlobalOp for all common blocks, including their initial values
58+
/// if they have one. This should be called before lowering any scopes so that
59+
/// common block globals are available when a common appear in a scope.
60+
void defineCommonBlocks(
61+
AbstractConverter &,
62+
const std::vector<std::pair<semantics::SymbolRef, std::size_t>>
63+
&commonBlocks);
64+
5765
/// Lower a symbol attributes given an optional storage \p and add it to the
5866
/// provided symbol map. If \preAlloc is not provided, a temporary storage will
5967
/// be allocated. This is a low level function that should only be used if

flang/include/flang/Lower/PFTBuilder.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "flang/Parser/parse-tree.h"
2525
#include "flang/Semantics/attr.h"
2626
#include "flang/Semantics/scope.h"
27+
#include "flang/Semantics/semantics.h"
2728
#include "flang/Semantics/symbol.h"
2829
#include "llvm/Support/ErrorHandling.h"
2930
#include "llvm/Support/raw_ostream.h"
@@ -737,18 +738,23 @@ struct Program {
737738
using Units = std::variant<FunctionLikeUnit, ModuleLikeUnit, BlockDataUnit,
738739
CompilerDirectiveUnit>;
739740

740-
Program() = default;
741+
Program(semantics::CommonBlockList &&commonBlocks)
742+
: commonBlocks{std::move(commonBlocks)} {}
741743
Program(Program &&) = default;
742744
Program(const Program &) = delete;
743745

744746
const std::list<Units> &getUnits() const { return units; }
745747
std::list<Units> &getUnits() { return units; }
748+
const semantics::CommonBlockList &getCommonBlocks() const {
749+
return commonBlocks;
750+
}
746751

747752
/// LLVM dump method on a Program.
748753
LLVM_DUMP_METHOD void dump() const;
749754

750755
private:
751756
std::list<Units> units;
757+
semantics::CommonBlockList commonBlocks;
752758
};
753759

754760
/// Return the list of variables that appears in the specification expressions

flang/include/flang/Semantics/semantics.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ struct WhereConstruct;
4949
namespace Fortran::semantics {
5050

5151
class Symbol;
52+
class CommonBlockMap;
53+
using CommonBlockList = std::vector<std::pair<SymbolRef, std::size_t>>;
5254

5355
using ConstructNode = std::variant<const parser::AssociateConstruct *,
5456
const parser::BlockConstruct *, const parser::CaseConstruct *,
@@ -199,6 +201,30 @@ class SemanticsContext {
199201
// during semantics.
200202
parser::Program &SaveParseTree(parser::Program &&);
201203

204+
// Ensures a common block definition does not conflict with previous
205+
// appearances in the program and consolidate information about
206+
// common blocks at the program level for later checks and lowering.
207+
// This can obviously not check any conflicts between different compilation
208+
// units (in case such conflicts exist, the behavior will depend on the
209+
// linker).
210+
void MapCommonBlockAndCheckConflicts(const Symbol &);
211+
212+
// Get the list of common blocks appearing in the program. If a common block
213+
// appears in several subprograms, only one of its appearance is returned in
214+
// the list alongside the biggest byte size of all its appearances.
215+
// If a common block is initialized in any of its appearances, the list will
216+
// contain the appearance with the initialization, otherwise the appearance
217+
// with the biggest size is returned. The extra byte size information allows
218+
// handling the case where the common block initialization is not the
219+
// appearance with the biggest size: the common block will have the biggest
220+
// size with the first bytes initialized with the initial value. This is not
221+
// standard, if the initialization and biggest size appearances are in
222+
// different compilation units, the behavior will depend on the linker. The
223+
// linker may have the behavior described before, but it may also keep the
224+
// initialized common symbol without extending its size, or have some other
225+
// behavior.
226+
CommonBlockList GetCommonBlocks() const;
227+
202228
private:
203229
void CheckIndexVarRedefine(
204230
const parser::CharBlock &, const Symbol &, parser::MessageFixedText &&);
@@ -231,6 +257,7 @@ class SemanticsContext {
231257
std::set<std::string> tempNames_;
232258
const Scope *builtinsScope_{nullptr}; // module __Fortran_builtins
233259
std::list<parser::Program> modFileParseTrees_;
260+
std::unique_ptr<CommonBlockMap> commonBlockMap_;
234261
};
235262

236263
class Semantics {

flang/lib/Lower/Bridge.cpp

Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -178,29 +178,35 @@ class FirConverter : public Fortran::lower::AbstractConverter {
178178
/// Convert the PFT to FIR.
179179
void run(Fortran::lower::pft::Program &pft) {
180180
// Preliminary translation pass.
181+
182+
// - Lower common blocks from the PFT common block list that contains a
183+
// consolidated list of the common blocks (with the initialization if any in
184+
// the Program, and with the common block biggest size in all its
185+
// appearance). This is done before lowering any scope declarations because
186+
// it is not know at the local scope level what MLIR type common blocks
187+
// should have to suit all its usage in the compilation unit.
188+
lowerCommonBlocks(pft.getCommonBlocks());
189+
181190
// - Declare all functions that have definitions so that definition
182191
// signatures prevail over call site signatures.
183192
// - Define module variables and OpenMP/OpenACC declarative construct so
184193
// that they are available before lowering any function that may use
185194
// them.
186-
// - Translate block data programs so that common block definitions with
187-
// data initializations take precedence over other definitions.
188195
for (Fortran::lower::pft::Program::Units &u : pft.getUnits()) {
189-
std::visit(
190-
Fortran::common::visitors{
191-
[&](Fortran::lower::pft::FunctionLikeUnit &f) {
192-
declareFunction(f);
193-
},
194-
[&](Fortran::lower::pft::ModuleLikeUnit &m) {
195-
lowerModuleDeclScope(m);
196-
for (Fortran::lower::pft::FunctionLikeUnit &f :
197-
m.nestedFunctions)
198-
declareFunction(f);
199-
},
200-
[&](Fortran::lower::pft::BlockDataUnit &b) { lowerBlockData(b); },
201-
[&](Fortran::lower::pft::CompilerDirectiveUnit &d) {},
202-
},
203-
u);
196+
std::visit(Fortran::common::visitors{
197+
[&](Fortran::lower::pft::FunctionLikeUnit &f) {
198+
declareFunction(f);
199+
},
200+
[&](Fortran::lower::pft::ModuleLikeUnit &m) {
201+
lowerModuleDeclScope(m);
202+
for (Fortran::lower::pft::FunctionLikeUnit &f :
203+
m.nestedFunctions)
204+
declareFunction(f);
205+
},
206+
[&](Fortran::lower::pft::BlockDataUnit &b) {},
207+
[&](Fortran::lower::pft::CompilerDirectiveUnit &d) {},
208+
},
209+
u);
204210
}
205211

206212
// Primary translation pass.
@@ -2562,6 +2568,13 @@ class FirConverter : public Fortran::lower::AbstractConverter {
25622568
});
25632569
}
25642570

2571+
/// Create fir::Global for all the common blocks that appear in the program.
2572+
void
2573+
lowerCommonBlocks(const Fortran::semantics::CommonBlockList &commonBlocks) {
2574+
createGlobalOutsideOfFunctionLowering(
2575+
[&]() { Fortran::lower::defineCommonBlocks(*this, commonBlocks); });
2576+
}
2577+
25652578
/// Lower a procedure (nest).
25662579
void lowerFunc(Fortran::lower::pft::FunctionLikeUnit &funit) {
25672580
if (!funit.isMainProgram()) {

flang/lib/Lower/ConvertVariable.cpp

Lines changed: 76 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -882,47 +882,82 @@ getCommonMembersWithInitAliases(const Fortran::semantics::Symbol &common) {
882882
return members;
883883
}
884884

885-
/// Define a global for a common block if it does not already exist in the
886-
/// mlir module.
887-
/// There is no "declare" version since there is not a
888-
/// scope that owns common blocks more that the others. All scopes using
889-
/// a common block attempts to define it with common linkage.
885+
/// Return the fir::GlobalOp that was created of COMMON block \p common.
886+
/// It is an error if the fir::GlobalOp was not created before this is
887+
/// called (it cannot be created on the flight because it is not known here
888+
/// what mlir type the GlobalOp should have to satisfy all the
889+
/// appearances in the program).
890890
static fir::GlobalOp
891-
defineCommonBlock(Fortran::lower::AbstractConverter &converter,
892-
const Fortran::semantics::Symbol &common) {
891+
getCommonBlockGlobal(Fortran::lower::AbstractConverter &converter,
892+
const Fortran::semantics::Symbol &common) {
893+
fir::FirOpBuilder &builder = converter.getFirOpBuilder();
894+
std::string commonName = Fortran::lower::mangle::mangleName(common);
895+
fir::GlobalOp global = builder.getNamedGlobal(commonName);
896+
// Common blocks are lowered before any subprograms to deal with common
897+
// whose size may not be the same in every subprograms.
898+
if (!global)
899+
fir::emitFatalError(converter.genLocation(common.name()),
900+
"COMMON block was not lowered before its usage");
901+
return global;
902+
}
903+
904+
/// Create the fir::GlobalOp for COMMON block \p common. If \p common has an
905+
/// initial value, it is not created yet. Instead, the common block list
906+
/// members is returned to later create the initial value in
907+
/// finalizeCommonBlockDefinition.
908+
static std::optional<std::tuple<
909+
fir::GlobalOp, Fortran::semantics::MutableSymbolVector, mlir::Location>>
910+
declareCommonBlock(Fortran::lower::AbstractConverter &converter,
911+
const Fortran::semantics::Symbol &common,
912+
std::size_t commonSize) {
893913
fir::FirOpBuilder &builder = converter.getFirOpBuilder();
894914
std::string commonName = Fortran::lower::mangle::mangleName(common);
895915
fir::GlobalOp global = builder.getNamedGlobal(commonName);
896916
if (global)
897-
return global;
917+
return std::nullopt;
898918
Fortran::semantics::MutableSymbolVector cmnBlkMems =
899919
getCommonMembersWithInitAliases(common);
900920
mlir::Location loc = converter.genLocation(common.name());
901-
mlir::IndexType idxTy = builder.getIndexType();
902921
mlir::StringAttr linkage = builder.createCommonLinkage();
903-
if (!common.name().size() || !commonBlockHasInit(cmnBlkMems)) {
904-
// A blank (anonymous) COMMON block must always be initialized to zero.
905-
// A named COMMON block sans initializers is also initialized to zero.
922+
if (!commonBlockHasInit(cmnBlkMems)) {
923+
// A COMMON block sans initializers is initialized to zero.
906924
// mlir::Vector types must have a strictly positive size, so at least
907925
// temporarily, force a zero size COMMON block to have one byte.
908-
const auto sz = static_cast<fir::SequenceType::Extent>(
909-
common.size() > 0 ? common.size() : 1);
926+
const auto sz =
927+
static_cast<fir::SequenceType::Extent>(commonSize > 0 ? commonSize : 1);
910928
fir::SequenceType::Shape shape = {sz};
911929
mlir::IntegerType i8Ty = builder.getIntegerType(8);
912930
auto commonTy = fir::SequenceType::get(shape, i8Ty);
913931
auto vecTy = mlir::VectorType::get(sz, i8Ty);
914932
mlir::Attribute zero = builder.getIntegerAttr(i8Ty, 0);
915933
auto init = mlir::DenseElementsAttr::get(vecTy, llvm::makeArrayRef(zero));
916-
return builder.createGlobal(loc, commonTy, commonName, linkage, init);
934+
builder.createGlobal(loc, commonTy, commonName, linkage, init);
935+
// No need to add any initial value later.
936+
return std::nullopt;
917937
}
918-
919-
// Named common with initializer, sort members by offset before generating
920-
// the type and initializer.
938+
// COMMON block with initializer (note that initialized blank common are
939+
// accepted as an extension by semantics). Sort members by offset before
940+
// generating the type and initializer.
921941
std::sort(cmnBlkMems.begin(), cmnBlkMems.end(),
922942
[](auto &s1, auto &s2) { return s1->offset() < s2->offset(); });
923943
mlir::TupleType commonTy =
924-
getTypeOfCommonWithInit(converter, cmnBlkMems, common.size());
944+
getTypeOfCommonWithInit(converter, cmnBlkMems, commonSize);
945+
// Create the global object, the initial value will be added later.
946+
global = builder.createGlobal(loc, commonTy, commonName);
947+
return std::make_tuple(global, std::move(cmnBlkMems), loc);
948+
}
949+
950+
/// Add initial value to a COMMON block fir::GlobalOp \p global given the list
951+
/// \p cmnBlkMems of the common block member symbols that contains symbols with
952+
/// an initial value.
953+
static void finalizeCommonBlockDefinition(
954+
mlir::Location loc, Fortran::lower::AbstractConverter &converter,
955+
fir::GlobalOp global,
956+
const Fortran::semantics::MutableSymbolVector &cmnBlkMems) {
957+
fir::FirOpBuilder &builder = converter.getFirOpBuilder();
958+
mlir::TupleType commonTy = global.getType().cast<mlir::TupleType>();
925959
auto initFunc = [&](fir::FirOpBuilder &builder) {
960+
mlir::IndexType idxTy = builder.getIndexType();
926961
mlir::Value cb = builder.create<fir::UndefOp>(loc, commonTy);
927962
unsigned tupIdx = 0;
928963
std::size_t offset = 0;
@@ -957,10 +992,25 @@ defineCommonBlock(Fortran::lower::AbstractConverter &converter,
957992
LLVM_DEBUG(llvm::dbgs() << "}\n");
958993
builder.create<fir::HasValueOp>(loc, cb);
959994
};
960-
// create the global object
961-
return builder.createGlobal(loc, commonTy, commonName,
962-
/*isConstant=*/false, initFunc);
995+
createGlobalInitialization(builder, global, initFunc);
963996
}
997+
998+
void Fortran::lower::defineCommonBlocks(
999+
Fortran::lower::AbstractConverter &converter,
1000+
const Fortran::semantics::CommonBlockList &commonBlocks) {
1001+
// Common blocks may depend on another common block address (if they contain
1002+
// pointers with initial targets). To cover this case, create all common block
1003+
// fir::Global before creating the initial values (if any).
1004+
std::vector<std::tuple<fir::GlobalOp, Fortran::semantics::MutableSymbolVector,
1005+
mlir::Location>>
1006+
delayedInitializations;
1007+
for (const auto [common, size] : commonBlocks)
1008+
if (auto delayedInit = declareCommonBlock(converter, common, size))
1009+
delayedInitializations.emplace_back(std::move(*delayedInit));
1010+
for (auto &[global, cmnBlkMems, loc] : delayedInitializations)
1011+
finalizeCommonBlockDefinition(loc, converter, global, cmnBlkMems);
1012+
}
1013+
9641014
/// The COMMON block is a global structure. `var` will be at some offset
9651015
/// within the COMMON block. Adds the address of `var` (COMMON + offset) to
9661016
/// the symbol map.
@@ -977,7 +1027,7 @@ static void instantiateCommon(Fortran::lower::AbstractConverter &converter,
9771027
commonAddr = symBox.getAddr();
9781028
if (!commonAddr) {
9791029
// introduce a local AddrOf and add it to the map
980-
fir::GlobalOp global = defineCommonBlock(converter, common);
1030+
fir::GlobalOp global = getCommonBlockGlobal(converter, common);
9811031
commonAddr = builder.create<fir::AddrOfOp>(loc, global.resultType(),
9821032
global.getSymbol());
9831033

@@ -1761,8 +1811,9 @@ void Fortran::lower::defineModuleVariable(
17611811
const Fortran::semantics::Symbol &sym = var.getSymbol();
17621812
if (const Fortran::semantics::Symbol *common =
17631813
Fortran::semantics::FindCommonBlockContaining(var.getSymbol())) {
1764-
// Define common block containing the variable.
1765-
defineCommonBlock(converter, *common);
1814+
// Nothing to do, common block are generated before everything. Ensure
1815+
// this was done by calling getCommonBlockGlobal.
1816+
getCommonBlockGlobal(converter, *common);
17661817
} else if (var.isAlias()) {
17671818
// Do nothing. Mapping will be done on user side.
17681819
} else {

flang/lib/Lower/PFTBuilder.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,9 @@ struct UnwrapStmt<parser::UnlabeledStatement<A>> {
7676
class PFTBuilder {
7777
public:
7878
PFTBuilder(const semantics::SemanticsContext &semanticsContext)
79-
: pgm{std::make_unique<lower::pft::Program>()}, semanticsContext{
80-
semanticsContext} {
79+
: pgm{std::make_unique<lower::pft::Program>(
80+
semanticsContext.GetCommonBlocks())},
81+
semanticsContext{semanticsContext} {
8182
lower::pft::PftNode pftRoot{*pgm.get()};
8283
pftParentStack.push_back(pftRoot);
8384
}

flang/lib/Semantics/compute-offsets.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ void ComputeOffsetsHelper::DoCommonBlock(Symbol &commonBlock) {
200200
}
201201
commonBlock.set_size(std::max(minSize, offset_));
202202
details.set_alignment(std::max(minAlignment, alignment_));
203+
context_.MapCommonBlockAndCheckConflicts(commonBlock);
203204
}
204205

205206
void ComputeOffsetsHelper::DoEquivalenceBlockBase(

0 commit comments

Comments
 (0)