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