llvm-project/flang/lib/Semantics/compute-offsets.cpp
Tim Keith c353ebbfa4 [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
2020-04-23 14:54:34 -07:00

173 lines
5.1 KiB
C++

//===----------------------------------------------------------------------===//
//
// 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