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
173 lines
5.1 KiB
C++
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
|