[flang] Handle EQUIVALENCE and COMMON in size and offset computations

Objects in common blocks have offsets relative to the start of the
common block, independent of the enclosing scope, so they are processed
first. Add alignment to CommonBlockDetails to record the required
alignment of the common block.

For equivalence sets, each object depends on the one that is forced to
occur first in memory. The rest are recorded in the dependents_ map and
have offsets assigned after the other symbols are done.

Differential Revision: https://reviews.llvm.org/D79347
This commit is contained in:
Tim Keith 2020-05-06 11:43:06 -07:00
parent 26f93d9f37
commit 237d0e3c04
4 changed files with 166 additions and 13 deletions

View file

@ -303,10 +303,13 @@ public:
void add_object(const Symbol &object) { objects_.emplace_back(object); }
MaybeExpr bindName() const { return bindName_; }
void set_bindName(MaybeExpr &&expr) { bindName_ = std::move(expr); }
std::size_t align() const { return align_; }
void set_align(std::size_t align) { align_ = align; }
private:
SymbolVector objects_;
MaybeExpr bindName_;
std::size_t align_{0}; // required alignment in bytes
};
class FinalProcDetails {}; // TODO
@ -670,7 +673,7 @@ private:
Flags flags_;
Scope *scope_{nullptr};
std::size_t size_{0}; // size in bytes
std::size_t offset_{0}; // byte offset in enclosing scope
std::size_t offset_{0}; // byte offset in scope or common block
Details details_;
Symbol() {} // only created in class Symbols

View file

@ -24,7 +24,6 @@ namespace Fortran::semantics {
class ComputeOffsetsHelper {
public:
// TODO: configure based on target
static constexpr int descriptorSize{3 * 8};
static constexpr int maxAlignment{8};
ComputeOffsetsHelper(SemanticsContext &context) : context_{context} {}
@ -39,11 +38,20 @@ private:
std::size_t size{0};
std::size_t align{0};
};
struct SymbolAndOffset {
Symbol *symbol{nullptr};
std::size_t offset{0};
};
void Compute(Scope &);
void DoScope(Scope &);
void DoCommonBlock(Symbol &);
void DoEquivalenceSet(EquivalenceSet &);
std::size_t GetOffset(SymbolAndOffset &);
std::size_t ComputeOffset(const EquivalenceObject &);
void DoSymbol(Symbol &);
SizeAndAlign GetSizeAndAlign(const Symbol &);
SizeAndAlign GetElementSize(const Symbol &, bool isSubstring = false);
std::size_t CountElements(const Symbol &);
static std::size_t Align(std::size_t, std::size_t);
static SizeAndAlign GetIntrinsicSizeAndAlign(TypeCategory, int);
@ -52,6 +60,8 @@ private:
evaluate::FoldingContext &foldingContext_{context_.foldingContext()};
std::size_t offset_{0};
std::size_t align_{0};
// symbol -> symbol+offset that determines its location, from EQUIVALENCE
std::map<MutableSymbolRef, SymbolAndOffset> dependents_;
};
void ComputeOffsetsHelper::Compute(Scope &scope) {
@ -61,22 +71,116 @@ void ComputeOffsetsHelper::Compute(Scope &scope) {
DoScope(scope);
}
static bool InCommonBlock(const Symbol &symbol) {
const auto *details{symbol.detailsIf<ObjectEntityDetails>()};
return details && details->commonBlock();
}
void ComputeOffsetsHelper::DoScope(Scope &scope) {
if (scope.symbol() && scope.IsParameterizedDerivedType()) {
return; // only process instantiations of parameterized derived types
}
// Symbols in common block get offsets from the beginning of the block
for (auto &pair : scope.commonBlocks()) {
DoCommonBlock(*pair.second);
}
// Build dependents_ from equivalences: symbol -> symbol+offset
for (EquivalenceSet &set : scope.equivalenceSets()) {
DoEquivalenceSet(set);
}
offset_ = 0;
align_ = 0;
for (auto symbol : scope.GetSymbols()) {
if (!symbol->has<TypeParamDetails>() && !symbol->has<SubprogramDetails>()) {
for (auto &symbol : scope.GetSymbols()) {
if (!InCommonBlock(*symbol) &&
dependents_.find(symbol) == dependents_.end()) {
DoSymbol(*symbol);
}
}
for (auto &[symbol, dep] : dependents_) {
if (symbol->size() == 0) {
SizeAndAlign s{GetSizeAndAlign(*symbol)};
symbol->set_size(s.size);
symbol->set_offset(GetOffset(dep));
offset_ = std::max(offset_, symbol->offset() + symbol->size());
}
}
scope.set_size(offset_);
scope.set_align(align_);
}
std::size_t ComputeOffsetsHelper::GetOffset(SymbolAndOffset &dep) {
auto it{dependents_.find(*dep.symbol)};
if (it == dependents_.end()) {
return dep.symbol->offset() + dep.offset;
} else {
return GetOffset(it->second) + dep.offset;
}
}
void ComputeOffsetsHelper::DoCommonBlock(Symbol &commonBlock) {
auto &details{commonBlock.get<CommonBlockDetails>()};
offset_ = 0;
align_ = 0;
for (auto &object : details.objects()) {
DoSymbol(*object);
}
commonBlock.set_size(offset_);
details.set_align(align_);
}
void ComputeOffsetsHelper::DoEquivalenceSet(EquivalenceSet &set) {
std::vector<SymbolAndOffset> symbolOffsets;
SymbolAndOffset max;
for (EquivalenceObject &object : set) {
std::size_t offset{ComputeOffset(object)};
symbolOffsets.push_back({&object.symbol, offset});
if (offset >= max.offset) {
max.offset = offset;
max.symbol = &object.symbol;
}
}
CHECK(max.symbol);
for (auto &[symbol, offset] : symbolOffsets) {
if (symbol != max.symbol) {
dependents_.emplace(
*symbol, SymbolAndOffset{max.symbol, max.offset - offset});
}
}
}
// Offset of this equivalence object from the start of its variable.
std::size_t ComputeOffsetsHelper::ComputeOffset(
const EquivalenceObject &object) {
std::size_t offset{0};
if (object.substringStart) {
offset = *object.substringStart - 1;
}
if (!object.subscripts.empty()) {
const ArraySpec &shape{object.symbol.get<ObjectEntityDetails>().shape()};
auto lbound{[&](std::size_t i) {
return *ToInt64(shape[i].lbound().GetExplicit());
}};
auto ubound{[&](std::size_t i) {
return *ToInt64(shape[i].ubound().GetExplicit());
}};
for (std::size_t i{object.subscripts.size() - 1};;) {
offset += object.subscripts[i] - lbound(i);
if (i == 0) {
break;
}
--i;
offset *= ubound(i) - lbound(i) + 1;
}
}
return offset *
GetElementSize(object.symbol, object.substringStart.has_value()).size;
}
void ComputeOffsetsHelper::DoSymbol(Symbol &symbol) {
if (symbol.has<TypeParamDetails>() || symbol.has<SubprogramDetails>() ||
symbol.has<UseDetails>() || symbol.has<ProcBindingDetails>()) {
return; // these have type but no size
}
SizeAndAlign s{GetSizeAndAlign(symbol)};
if (s.size == 0) {
return;
@ -85,13 +189,22 @@ void ComputeOffsetsHelper::DoSymbol(Symbol &symbol) {
symbol.set_size(s.size);
symbol.set_offset(offset_);
offset_ += s.size;
if (s.align > align_) {
align_ = s.align;
}
align_ = std::max(align_, s.align);
}
auto ComputeOffsetsHelper::GetSizeAndAlign(const Symbol &symbol)
-> SizeAndAlign {
SizeAndAlign result{GetElementSize(symbol)};
std::size_t elements{CountElements(symbol)};
if (elements > 1) {
result.size = Align(result.size, result.align);
}
result.size *= elements;
return result;
}
auto ComputeOffsetsHelper::GetElementSize(
const Symbol &symbol, bool isSubstring) -> SizeAndAlign {
const DeclTypeSpec *type{symbol.GetType()};
if (!type) {
return {};
@ -110,7 +223,7 @@ auto ComputeOffsetsHelper::GetSizeAndAlign(const Symbol &symbol)
if (auto kind{ToInt64(intrinsic->kind())}) {
result = GetIntrinsicSizeAndAlign(intrinsic->category(), *kind);
}
if (type->category() == DeclTypeSpec::Character) {
if (!isSubstring && type->category() == DeclTypeSpec::Character) {
ParamValue length{type->characterTypeSpec().length()};
CHECK(length.isExplicit()); // else should be descriptor
if (MaybeIntExpr lengthExpr{length.GetExplicit()}) {
@ -127,11 +240,6 @@ auto ComputeOffsetsHelper::GetSizeAndAlign(const Symbol &symbol)
} else {
DIE("not intrinsic or derived");
}
std::size_t elements{CountElements(symbol)};
if (elements > 1) {
result.size = Align(result.size, result.align);
}
result.size *= elements;
return result;
}

View file

@ -434,6 +434,9 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const Details &details) {
DumpSymbolVector(os, x.objects());
},
[&](const CommonBlockDetails &x) {
if (x.align()) {
os << " align=" << x.align();
}
os << ':';
for (const Symbol &object : x.objects()) {
os << ' ' << object.name();

View file

@ -0,0 +1,39 @@
!RUN: %f18 -fdebug-dump-symbols -fparse-only %s | FileCheck %s
! Size and alignment with EQUIVALENCE and COMMON
! a1 depends on a2 depends on a3
module ma
real :: a1(10), a2(10), a3(10)
equivalence(a1, a2(3)) !CHECK: a1, PUBLIC size=40 offset=20:
equivalence(a2, a3(4)) !CHECK: a2, PUBLIC size=40 offset=12:
!CHECK: a3, PUBLIC size=40 offset=0:
end
! equivalence and 2-dimensional array
module mb
real :: b1(4), b2, b3, b4
real :: b(-1:1,2:6) !CHECK: b, PUBLIC size=60 offset=0:
equivalence(b(1,6), b1) !CHECK: b1, PUBLIC size=16 offset=56:
equivalence(b(1,5), b2) !CHECK: b2, PUBLIC size=4 offset=44:
equivalence(b(0,6), b3) !CHECK: b3, PUBLIC size=4 offset=52:
equivalence(b(0,4), b4) !CHECK: b4, PUBLIC size=4 offset=28:
end
! equivalence and substring
subroutine mc !CHECK: Subprogram scope: mc size=12 align=1
character(10) :: c1 !CHECK: c1 size=10 offset=0:
character(5) :: c2 !CHECK: c2 size=5 offset=7:
equivalence(c1(9:), c2(2:4))
end
! Common block: objects are in order from COMMON statement and not part of module
module md !CHECK: Module scope: md size=1 align=1
integer(1) :: i
integer(2) :: d1 !CHECK: d1, PUBLIC size=2 offset=8:
integer(4) :: d2 !CHECK: d2, PUBLIC size=4 offset=4:
integer(1) :: d3 !CHECK: d3, PUBLIC size=1 offset=0:
real(2) :: d4 !CHECK: d4, PUBLIC size=2 offset=0:
common /common1/ d3,d2,d1 !CHECK: common1 size=10 offset=0: CommonBlockDetails align=4:
common /common2/ d4 !CHECK: common2 size=2 offset=0: CommonBlockDetails align=2:
end