diff --git a/flang/include/flang/Semantics/scope.h b/flang/include/flang/Semantics/scope.h --- a/flang/include/flang/Semantics/scope.h +++ b/flang/include/flang/Semantics/scope.h @@ -108,12 +108,15 @@ const_iterator cbegin() const { return symbols_.cbegin(); } const_iterator cend() const { return symbols_.cend(); } + // Return symbols in declaration order (the iterators above are in name order) + SymbolVector GetSymbols() const; + std::vector> GetSymbols(); + iterator find(const SourceName &name); const_iterator find(const SourceName &name) const { return symbols_.find(name); } size_type erase(const SourceName &); - size_type size() const { return symbols_.size(); } bool empty() const { return symbols_.empty(); } // Look for symbol by name in this scope and host (depending on imports). @@ -182,6 +185,11 @@ // that are referenced by SourceName objects. void set_chars(parser::CookedSource &); + std::size_t size() const { return size_; } + void set_size(std::size_t size) { size_ = size; } + std::size_t align() const { return align_; } + void set_align(std::size_t align) { align_ = align; } + ImportKind GetImportKind() const; // Names appearing in IMPORT statements in this scope std::set importNames() const { return importNames_; } @@ -217,6 +225,8 @@ private: Scope &parent_; // this is enclosing scope, not extended derived type base const Kind kind_; + std::size_t size_{0}; // size in bytes + std::size_t align_{0}; // required alignment in bytes parser::CharBlock sourceRange_; Symbol *const symbol_; // if not null, symbol_->scope() == this std::list children_; diff --git a/flang/include/flang/Semantics/symbol.h b/flang/include/flang/Semantics/symbol.h --- a/flang/include/flang/Semantics/symbol.h +++ b/flang/include/flang/Semantics/symbol.h @@ -499,6 +499,10 @@ Scope *scope() { return scope_; } const Scope *scope() const { return scope_; } void set_scope(Scope *scope) { scope_ = scope; } + std::size_t size() const { return size_; } + void set_size(std::size_t size) { size_ = size; } + std::size_t offset() const { return offset_; } + void set_offset(std::size_t offset) { offset_ = offset; } // Give the symbol a name with a different source location but same chars. void ReplaceName(const SourceName &); @@ -665,6 +669,8 @@ Attrs attrs_; Flags flags_; Scope *scope_{nullptr}; + std::size_t size_{0}; // size in bytes + std::size_t offset_{0}; // byte offset in enclosing scope Details details_; Symbol() {} // only created in class Symbols @@ -730,6 +736,10 @@ } inline bool operator<(SymbolRef x, SymbolRef y) { return *x < *y; } +inline bool operator<( + common::Reference x, common::Reference y) { + return *x < *y; +} using SymbolSet = std::set; } // namespace Fortran::semantics diff --git a/flang/include/flang/Semantics/type.h b/flang/include/flang/Semantics/type.h --- a/flang/include/flang/Semantics/type.h +++ b/flang/include/flang/Semantics/type.h @@ -250,6 +250,7 @@ void ReplaceScope(const Scope &); RawParameters &rawParameters() { return rawParameters_; } const ParameterMapType ¶meters() const { return parameters_; } + int NumLengthParameters() const; bool MightBeParameterized() const; bool IsForwardReferenced() const; diff --git a/flang/lib/Evaluate/type.cpp b/flang/lib/Evaluate/type.cpp --- a/flang/lib/Evaluate/type.cpp +++ b/flang/lib/Evaluate/type.cpp @@ -431,28 +431,8 @@ } bool DynamicType::RequiresDescriptor() const { - if (IsPolymorphic() || IsUnknownLengthCharacter()) { - return true; - } - if (derived_) { - // Any length type parameter? - if (const auto *scope{derived_->scope()}) { - if (const auto *symbol{scope->symbol()}) { - if (const auto *details{ - symbol->detailsIf()}) { - for (const Symbol ¶m : details->paramDecls()) { - if (const auto *details{ - param.detailsIf()}) { - if (details->attr() == common::TypeParamAttr::Len) { - return true; - } - } - } - } - } - } - } - return false; + return IsPolymorphic() || IsUnknownLengthCharacter() || + (derived_ && derived_->NumLengthParameters() > 0); } bool DynamicType::HasDeferredTypeParameter() const { diff --git a/flang/lib/Semantics/CMakeLists.txt b/flang/lib/Semantics/CMakeLists.txt --- a/flang/lib/Semantics/CMakeLists.txt +++ b/flang/lib/Semantics/CMakeLists.txt @@ -21,6 +21,7 @@ check-purity.cpp check-return.cpp check-stop.cpp + compute-offsets.cpp expression.cpp mod-file.cpp pointer-assignment.cpp diff --git a/flang/lib/Semantics/compute-offsets.h b/flang/lib/Semantics/compute-offsets.h new file mode 100644 --- /dev/null +++ b/flang/lib/Semantics/compute-offsets.h @@ -0,0 +1,17 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef FORTRAN_SEMANTICS_COMPUTE_OFFSETS_H_ +#define FORTRAN_SEMANTICS_COMPUTE_OFFSETS_H_ +namespace Fortran::semantics { + +class SemanticsContext; +void ComputeOffsets(SemanticsContext &); + +} // namespace Fortran::semantics +#endif diff --git a/flang/lib/Semantics/compute-offsets.cpp b/flang/lib/Semantics/compute-offsets.cpp new file mode 100644 --- /dev/null +++ b/flang/lib/Semantics/compute-offsets.cpp @@ -0,0 +1,172 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "compute-offsets.h" +#include "../../runtime/descriptor.h" +#include "flang/Evaluate/fold.h" +#include "flang/Evaluate/shape.h" +#include "flang/Evaluate/type.h" +#include "flang/Semantics/scope.h" +#include "flang/Semantics/semantics.h" +#include "flang/Semantics/symbol.h" +#include "flang/Semantics/tools.h" +#include "flang/Semantics/type.h" +#include +#include + +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} {} + void Compute() { Compute(context_.globalScope()); } + +private: + struct SizeAndAlign { + SizeAndAlign() {} + SizeAndAlign(std::size_t size) : size{size}, align{size} {} + SizeAndAlign(std::size_t size, std::size_t align) + : size{size}, align{align} {} + std::size_t size{0}; + std::size_t align{0}; + }; + + void Compute(Scope &); + void DoScope(Scope &); + void DoSymbol(Symbol &); + SizeAndAlign GetSizeAndAlign(const Symbol &); + std::size_t CountElements(const Symbol &); + static std::size_t Align(std::size_t, std::size_t); + static SizeAndAlign GetIntrinsicSizeAndAlign(TypeCategory, int); + + SemanticsContext &context_; + evaluate::FoldingContext &foldingContext_{context_.foldingContext()}; + std::size_t offset_{0}; + std::size_t align_{0}; +}; + +void ComputeOffsetsHelper::Compute(Scope &scope) { + for (Scope &child : scope.children()) { + Compute(child); + } + DoScope(scope); +} + +void ComputeOffsetsHelper::DoScope(Scope &scope) { + if (scope.symbol() && scope.IsParameterizedDerivedType()) { + return; // only process instantiations of parameterized derived types + } + offset_ = 0; + align_ = 0; + for (auto symbol : scope.GetSymbols()) { + if (!symbol->has() && !symbol->has()) { + DoSymbol(*symbol); + } + } + scope.set_size(offset_); + scope.set_align(align_); +} + +void ComputeOffsetsHelper::DoSymbol(Symbol &symbol) { + SizeAndAlign s{GetSizeAndAlign(symbol)}; + if (s.size == 0) { + return; + } + offset_ = Align(offset_, s.align); + symbol.set_size(s.size); + symbol.set_offset(offset_); + offset_ += s.size; + if (s.align > align_) { + align_ = s.align; + } +} + +auto ComputeOffsetsHelper::GetSizeAndAlign(const Symbol &symbol) + -> SizeAndAlign { + const DeclTypeSpec *type{symbol.GetType()}; + if (!type) { + return {}; + } + if (IsDescriptor(symbol) || IsProcedure(symbol)) { + int lenParams{0}; + if (const DerivedTypeSpec * derived{type->AsDerived()}) { + lenParams = derived->NumLengthParameters(); + } + std::size_t size{ + runtime::Descriptor::SizeInBytes(symbol.Rank(), false, lenParams)}; + return {size, maxAlignment}; + } + SizeAndAlign result; + if (const IntrinsicTypeSpec * intrinsic{type->AsIntrinsic()}) { + if (auto kind{ToInt64(intrinsic->kind())}) { + result = GetIntrinsicSizeAndAlign(intrinsic->category(), *kind); + } + if (type->category() == DeclTypeSpec::Character) { + ParamValue length{type->characterTypeSpec().length()}; + CHECK(length.isExplicit()); // else should be descriptor + if (MaybeIntExpr lengthExpr{length.GetExplicit()}) { + if (auto lengthInt{ToInt64(*lengthExpr)}) { + result.size *= *lengthInt; + } + } + } + } else if (const DerivedTypeSpec * derived{type->AsDerived()}) { + if (derived->scope()) { + result.size = derived->scope()->size(); + result.align = derived->scope()->align(); + } + } 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; +} + +std::size_t ComputeOffsetsHelper::CountElements(const Symbol &symbol) { + if (auto shape{GetShape(foldingContext_, symbol)}) { + if (auto sizeExpr{evaluate::GetSize(std::move(*shape))}) { + if (auto size{ToInt64(Fold(foldingContext_, std::move(*sizeExpr)))}) { + return *size; + } + } + } + return 1; +} + +// Align a size to its natural alignment, up to maxAlignment. +std::size_t ComputeOffsetsHelper::Align(std::size_t x, std::size_t alignment) { + if (alignment > maxAlignment) { + alignment = maxAlignment; + } + return (x + alignment - 1) & -alignment; +} + +auto ComputeOffsetsHelper::GetIntrinsicSizeAndAlign( + TypeCategory category, int kind) -> SizeAndAlign { + // TODO: does kind==10 need special handling? + std::size_t size{kind == 3 ? 2 : static_cast(kind)}; + if (category == TypeCategory::Complex) { + return {2 * size, size}; + } else { + return {size}; + } +} + +void ComputeOffsets(SemanticsContext &context) { + ComputeOffsetsHelper{context}.Compute(); +} + +} // namespace Fortran::semantics diff --git a/flang/lib/Semantics/mod-file.cpp b/flang/lib/Semantics/mod-file.cpp --- a/flang/lib/Semantics/mod-file.cpp +++ b/flang/lib/Semantics/mod-file.cpp @@ -414,36 +414,25 @@ // Collect the symbols of this scope sorted by their original order, not name. // Namelists are an exception: they are sorted after other symbols. SymbolVector CollectSymbols(const Scope &scope) { - SymbolSet symbols; // to prevent duplicates SymbolVector sorted; SymbolVector namelist; - SymbolVector common; - sorted.reserve(scope.size() + scope.commonBlocks().size()); - for (const auto &pair : scope) { - const Symbol &symbol{*pair.second}; - if (!symbol.test(Symbol::Flag::ParentComp)) { - if (symbols.insert(symbol).second) { - if (symbol.has()) { - namelist.push_back(symbol); - } else { - sorted.push_back(symbol); - } + std::size_t commonSize{scope.commonBlocks().size()}; + auto symbols{scope.GetSymbols()}; + sorted.reserve(symbols.size() + commonSize); + for (SymbolRef symbol : symbols) { + if (!symbol->test(Symbol::Flag::ParentComp)) { + if (symbol->has()) { + namelist.push_back(symbol); + } else { + sorted.push_back(symbol); } } } + sorted.insert(sorted.end(), namelist.begin(), namelist.end()); for (const auto &pair : scope.commonBlocks()) { - const Symbol &symbol{*pair.second}; - if (symbols.insert(symbol).second) { - common.push_back(symbol); - } + sorted.push_back(*pair.second); } - // sort normal symbols, then namelists, then common blocks: - auto cursor{sorted.begin()}; - std::sort(cursor, sorted.end()); - cursor = sorted.insert(sorted.end(), namelist.begin(), namelist.end()); - std::sort(cursor, sorted.end()); - cursor = sorted.insert(sorted.end(), common.begin(), common.end()); - std::sort(cursor, sorted.end()); + std::sort(sorted.end() - commonSize, sorted.end()); return sorted; } diff --git a/flang/lib/Semantics/scope.cpp b/flang/lib/Semantics/scope.cpp --- a/flang/lib/Semantics/scope.cpp +++ b/flang/lib/Semantics/scope.cpp @@ -60,6 +60,25 @@ return children_.emplace_back(*this, kind, symbol); } +template +static std::vector> GetSortedSymbols( + std::map> symbols) { + std::vector> result; + result.reserve(symbols.size()); + for (auto &pair : symbols) { + result.push_back(*pair.second); + } + std::sort(result.begin(), result.end()); + return result; +} + +std::vector> Scope::GetSymbols() { + return GetSortedSymbols(symbols_); +} +SymbolVector Scope::GetSymbols() const { + return GetSortedSymbols(symbols_); +} + Scope::iterator Scope::find(const SourceName &name) { return symbols_.find(name); } @@ -292,6 +311,9 @@ if (auto *symbol{scope.symbol()}) { os << *symbol << ' '; } + if (scope.derivedTypeSpec_) { + os << "instantiation of " << *scope.derivedTypeSpec_ << ' '; + } os << scope.children_.size() << " children\n"; for (const auto &pair : scope.symbols_) { const Symbol &symbol{*pair.second}; diff --git a/flang/lib/Semantics/semantics.cpp b/flang/lib/Semantics/semantics.cpp --- a/flang/lib/Semantics/semantics.cpp +++ b/flang/lib/Semantics/semantics.cpp @@ -26,6 +26,7 @@ #include "check-purity.h" #include "check-return.h" #include "check-stop.h" +#include "compute-offsets.h" #include "mod-file.h" #include "resolve-labels.h" #include "resolve-names.h" @@ -161,6 +162,7 @@ SemanticsContext &context, parser::Program &program) { ResolveNames(context, program); RewriteParseTree(context, program); + ComputeOffsets(context); CheckDeclarations(context); StatementSemanticsPass1{context}.Walk(program); StatementSemanticsPass2{context}.Walk(program); @@ -351,6 +353,12 @@ if (const auto *symbol{scope.symbol()}) { os << ' ' << symbol->name(); } + if (scope.size()) { + os << " size=" << scope.size() << " align=" << scope.align(); + } + if (scope.derivedTypeSpec()) { + os << " instantiation of " << *scope.derivedTypeSpec(); + } os << '\n'; ++indent; for (const auto &pair : scope) { diff --git a/flang/lib/Semantics/symbol.cpp b/flang/lib/Semantics/symbol.cpp --- a/flang/lib/Semantics/symbol.cpp +++ b/flang/lib/Semantics/symbol.cpp @@ -482,6 +482,9 @@ if (!symbol.flags().empty()) { os << " (" << symbol.flags() << ')'; } + if (symbol.size_) { + os << " size=" << symbol.size_ << " offset=" << symbol.offset_; + } os << ": " << symbol.details_; return os; } diff --git a/flang/lib/Semantics/type.cpp b/flang/lib/Semantics/type.cpp --- a/flang/lib/Semantics/type.cpp +++ b/flang/lib/Semantics/type.cpp @@ -160,6 +160,16 @@ CHECK(pair.second); // name was not already present } +int DerivedTypeSpec::NumLengthParameters() const { + int result{0}; + for (const auto &pair : parameters_) { + if (pair.second.isLen()) { + ++result; + } + } + return result; +} + bool DerivedTypeSpec::MightBeParameterized() const { return !cooked_ || !parameters_.empty(); } diff --git a/flang/test/Semantics/offsets01.f90 b/flang/test/Semantics/offsets01.f90 new file mode 100644 --- /dev/null +++ b/flang/test/Semantics/offsets01.f90 @@ -0,0 +1,52 @@ +!RUN: %f18 -fdebug-dump-symbols -fparse-only %s | FileCheck %s + +! Size and alignment of intrinsic types +subroutine s1 + integer(1) :: a_i1 !CHECK: a_i1 size=1 offset=0: + integer(8) :: b_i8 !CHECK: b_i8 size=8 offset=8: + real(2) :: c_r2 !CHECK: c_r2 size=2 offset=16: + real(2) :: d_r2 !CHECK: d_r2 size=2 offset=18: + real(8) :: e_r8 !CHECK: e_r8 size=8 offset=24: + real(4) :: f_r4 !CHECK: f_r4 size=4 offset=32: + complex(8) :: g_c8 !CHECK: g_c8 size=16 offset=40: + complex(4) :: h_c4 !CHECK: h_c4 size=8 offset=56: + logical :: i_l4 !CHECK: i_l4 size=4 offset=64: +end + +! Character +subroutine s2 + character(10) :: c1 !CHECK: c1 size=10 offset=0: + character(1) :: c2 !CHECK: c2 size=1 offset=10: + character(10,kind=2) :: c3 !CHECK: c3 size=20 offset=12: +end + +! Descriptors +subroutine s3(n) + integer :: n + real, pointer :: x !CHECK: x, POINTER size=24 offset=8: + character(n) :: y !CHECK: y size=24 offset=32: +end + +! Descriptors for arrays +subroutine s4 + integer, allocatable :: z0 !CHECK: z0, ALLOCATABLE size=24 offset= + integer, allocatable :: z1(:) !CHECK: z1, ALLOCATABLE size=48 offset= + integer, allocatable :: z2(:,:) !CHECK: z2, ALLOCATABLE size=72 offset= + integer, allocatable :: z3(:,:,:) !CHECK: z3, ALLOCATABLE size=96 offset= +end + +! Descriptors with length parameters +subroutine s5(n) + integer :: n + type :: t1(l) + integer, len :: l + real :: a(l) + end type + type :: t2(l1, l2) + integer, len :: l1 + integer, len :: l2 + real :: b(l1, l2) + end type + type(t1(n)) :: x1 !CHECK: x1 size=48 offset= + type(t2(n,n)) :: x2 !CHECK: x2 size=56 offset= +end diff --git a/flang/test/Semantics/offsets02.f90 b/flang/test/Semantics/offsets02.f90 new file mode 100644 --- /dev/null +++ b/flang/test/Semantics/offsets02.f90 @@ -0,0 +1,54 @@ +!RUN: %f18 -fdebug-dump-symbols -fparse-only %s | FileCheck %s + +! Size and alignment of derived types + +! Array of derived type with 64-bit alignment +subroutine s1 + type t1 + real(8) :: a + real(4) :: b + end type + !CHECK: x1 size=12 offset=0: + !CHECK: y1 size=12 offset=16: + type(t1) :: x1, y1 + !CHECK: z1 size=160 offset=32: + type(t1) :: z1(10) +end + +! Like t1 but t2 does not need to be aligned on 64-bit boundary +subroutine s2 + type t2 + real(4) :: a + real(4) :: b + real(4) :: c + end type + !CHECK: x2 size=12 offset=0: + !CHECK: y2 size=12 offset=12: + type(t2) :: x2, y2 + !CHECK: z2 size=120 offset=24: + type(t2) :: z2(10) +end + +! Parameterized derived types +subroutine s3 + type :: t(k, l) + integer, kind :: k + integer, len :: l + real(k) :: a3 + integer(kind=k) :: b3 + character(kind=k, len=8) :: c3 + character(kind=k, len=l) :: d3 + end type + !CHECK: DerivedType scope: size=48 align=8 instantiation of t(k=2_4,l=10_4) + !CHECK: a3 size=2 offset=0: + !CHECK: b3 size=2 offset=2: + !CHECK: c3 size=16 offset=4: + !CHECK: d3 size=24 offset=24: + type(t(2, 10)) :: x3 + !CHECK: DerivedType scope: size=64 align=8 instantiation of t(k=4_4,l=20_4) + !CHECK: a3 size=4 offset=0: + !CHECK: b3 size=4 offset=4: + !CHECK: c3 size=32 offset=8: + !CHECK: d3 size=24 offset=40: + type(t(4, 20)) :: x4 +end