[flang] Compute sizes and offsets for symbols

Summary:
Add size and offset properties to symbols, representing their byte size
and offset within their enclosing scope.

Add size and align properties to scopes so that they are available for
scopes representing derived types.

Add ComputeOffsets pass over the symbol table to fill in those fields.

Compute descriptor size based on rank and length parameters. Extract
DerivedTypeSpec::NumLengthParameters from DynamicType::RequiresDescriptor
to share the code.

Add Scope::GetSymbols to get symbols in canonical order.
compute-offsets.cpp and mod-file.cpp both need to process symbols in the
order in which they are declared. Move the collecting of those symbols
into Scope so that it can be shared.

Add symbol size and offset to output of `-fdebug-dump-symbols` and use
that in some tests.

Still to do:
- make size and alignment rules configurable based on target
- use offsets to check EQUIVALENCE statements

Differential Revision: https://reviews.llvm.org/D78680
This commit is contained in:
Tim Keith
2020-04-22 15:39:24 -07:00
parent fcbc613ad8
commit c353ebbfa4
14 changed files with 375 additions and 46 deletions

View File

@@ -108,12 +108,15 @@ public:
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<common::Reference<Symbol>> 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 @@ public:
// 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<SourceName> importNames() const { return importNames_; }
@@ -217,6 +225,8 @@ public:
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<Scope> children_;

View File

@@ -499,6 +499,10 @@ public:
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 @@ private:
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 ProcEntityDetails::HasExplicitInterface() const {
}
inline bool operator<(SymbolRef x, SymbolRef y) { return *x < *y; }
inline bool operator<(
common::Reference<Symbol> x, common::Reference<Symbol> y) {
return *x < *y;
}
using SymbolSet = std::set<SymbolRef>;
} // namespace Fortran::semantics

View File

@@ -250,6 +250,7 @@ public:
void ReplaceScope(const Scope &);
RawParameters &rawParameters() { return rawParameters_; }
const ParameterMapType &parameters() const { return parameters_; }
int NumLengthParameters() const;
bool MightBeParameterized() const;
bool IsForwardReferenced() const;

View File

@@ -431,28 +431,8 @@ DynamicType DynamicType::ResultTypeForMultiply(const DynamicType &that) const {
}
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<semantics::DerivedTypeDetails>()}) {
for (const Symbol &param : details->paramDecls()) {
if (const auto *details{
param.detailsIf<semantics::TypeParamDetails>()}) {
if (details->attr() == common::TypeParamAttr::Len) {
return true;
}
}
}
}
}
}
}
return false;
return IsPolymorphic() || IsUnknownLengthCharacter() ||
(derived_ && derived_->NumLengthParameters() > 0);
}
bool DynamicType::HasDeferredTypeParameter() const {

View File

@@ -21,6 +21,7 @@ add_flang_library(FortranSemantics
check-purity.cpp
check-return.cpp
check-stop.cpp
compute-offsets.cpp
expression.cpp
mod-file.cpp
pointer-assignment.cpp

View File

@@ -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 <algorithm>
#include <vector>
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<TypeParamDetails>() && !symbol->has<SubprogramDetails>()) {
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<std::size_t>(kind)};
if (category == TypeCategory::Complex) {
return {2 * size, size};
} else {
return {size};
}
}
void ComputeOffsets(SemanticsContext &context) {
ComputeOffsetsHelper{context}.Compute();
}
} // namespace Fortran::semantics

View File

@@ -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

View File

@@ -414,36 +414,25 @@ void ModFileWriter::PutUseExtraAttr(
// 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<NamelistDetails>()) {
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<NamelistDetails>()) {
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;
}

View File

@@ -60,6 +60,25 @@ Scope &Scope::MakeScope(Kind kind, Symbol *symbol) {
return children_.emplace_back(*this, kind, symbol);
}
template <typename T>
static std::vector<common::Reference<T>> GetSortedSymbols(
std::map<SourceName, common::Reference<Symbol>> symbols) {
std::vector<common::Reference<T>> result;
result.reserve(symbols.size());
for (auto &pair : symbols) {
result.push_back(*pair.second);
}
std::sort(result.begin(), result.end());
return result;
}
std::vector<common::Reference<Symbol>> Scope::GetSymbols() {
return GetSortedSymbols<Symbol>(symbols_);
}
SymbolVector Scope::GetSymbols() const {
return GetSortedSymbols<const Symbol>(symbols_);
}
Scope::iterator Scope::find(const SourceName &name) {
return symbols_.find(name);
}
@@ -292,6 +311,9 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const Scope &scope) {
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};

View File

@@ -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 @@ static bool PerformStatementSemantics(
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 @@ void DoDumpSymbols(llvm::raw_ostream &os, const Scope &scope, int indent) {
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) {

View File

@@ -482,6 +482,9 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const Symbol &symbol) {
if (!symbol.flags().empty()) {
os << " (" << symbol.flags() << ')';
}
if (symbol.size_) {
os << " size=" << symbol.size_ << " offset=" << symbol.offset_;
}
os << ": " << symbol.details_;
return os;
}

View File

@@ -160,6 +160,16 @@ void DerivedTypeSpec::AddParamValue(SourceName name, ParamValue &&value) {
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();
}

View File

@@ -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

View File

@@ -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