From 237d0e3c0416abf9919406bcc92874cfd15f5e0c Mon Sep 17 00:00:00 2001 From: Tim Keith Date: Wed, 6 May 2020 11:43:06 -0700 Subject: [PATCH] [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 --- flang/include/flang/Semantics/symbol.h | 5 +- flang/lib/Semantics/compute-offsets.cpp | 132 +++++++++++++++++++++++++++++--- flang/lib/Semantics/symbol.cpp | 3 + flang/test/Semantics/offsets03.f90 | 39 ++++++++++ 4 files changed, 166 insertions(+), 13 deletions(-) create mode 100644 flang/test/Semantics/offsets03.f90 diff --git a/flang/include/flang/Semantics/symbol.h b/flang/include/flang/Semantics/symbol.h index f19a970..4b22e57 100644 --- a/flang/include/flang/Semantics/symbol.h +++ b/flang/include/flang/Semantics/symbol.h @@ -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 diff --git a/flang/lib/Semantics/compute-offsets.cpp b/flang/lib/Semantics/compute-offsets.cpp index d16d420..ae5481c 100644 --- a/flang/lib/Semantics/compute-offsets.cpp +++ b/flang/lib/Semantics/compute-offsets.cpp @@ -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 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()}; + 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() && !symbol->has()) { + 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()}; + 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 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().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() || symbol.has() || + symbol.has() || symbol.has()) { + 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; } diff --git a/flang/lib/Semantics/symbol.cpp b/flang/lib/Semantics/symbol.cpp index 6caadba..35a3197 100644 --- a/flang/lib/Semantics/symbol.cpp +++ b/flang/lib/Semantics/symbol.cpp @@ -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(); diff --git a/flang/test/Semantics/offsets03.f90 b/flang/test/Semantics/offsets03.f90 new file mode 100644 index 0000000..b10bce7 --- /dev/null +++ b/flang/test/Semantics/offsets03.f90 @@ -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 -- 2.7.4