| //===-- lib/Semantics/compute-offsets.cpp -----------------------*- 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 "flang/Evaluate/fold-designator.h" | |
| #include "flang/Evaluate/fold.h" | |
| #include "flang/Evaluate/shape.h" | |
| #include "flang/Evaluate/type.h" | |
| #include "flang/Runtime/descriptor-consts.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 "llvm/TargetParser/Host.h" | |
| #include "llvm/TargetParser/Triple.h" | |
| #include <algorithm> | |
| #include <cstdint> | |
| #include <limits> | |
| #include <vector> | |
| namespace Fortran::semantics { | |
| // Generated IR represents storage sizes and offsets as signed 64-bit integers. | |
| static_assert(sizeof(std::size_t) >= sizeof(std::int64_t), | |
| "byte sizes and offsets are accumulated in std::size_t and must not be " | |
| "narrowed"); | |
| static constexpr std::size_t maxStorageSizeInBytes{ | |
| static_cast<std::size_t>(common::numeric_limits<std::int64_t>::max())}; | |
| static bool IsTooBig(std::size_t bytes) { | |
| return bytes > maxStorageSizeInBytes; | |
| } | |
| // Add sizes while tracking whether the signed 64-bit limit was exceeded. | |
| static std::size_t AddSizes(std::size_t x, std::size_t y, bool &tooBig) { | |
| tooBig |= IsTooBig(x) || IsTooBig(y) || x > maxStorageSizeInBytes - y; | |
| return x + y; | |
| } | |
| // A folded extent is (ub-lb+1) evaluated with signed 64-bit arithmetic, so it | |
| // comes out nonpositive both for an empty dimension and for one that wrapped | |
| // around, as in a(0:huge(0_8)). Tell those apart with the declared bounds. | |
| static bool IsEmptyDimension(const Symbol &symbol, int dimension) { | |
| if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()}) { | |
| const ArraySpec &shape{object->shape()}; | |
| if (dimension < shape.Rank()) { | |
| auto lb{evaluate::ToInt64(shape[dimension].lbound().GetExplicit())}; | |
| auto ub{evaluate::ToInt64(shape[dimension].ubound().GetExplicit())}; | |
| if (lb && ub) { | |
| return *ub < *lb; | |
| } | |
| } | |
| } | |
| return true; // no constant bounds to contradict the folded extent | |
| } | |
| class ComputeOffsetsHelper { | |
| public: | |
| ComputeOffsetsHelper(SemanticsContext &context) : context_{context} {} | |
| void Compute(Scope &); | |
| private: | |
| struct SizeAndAlignment { | |
| SizeAndAlignment() {} | |
| SizeAndAlignment(std::size_t bytes) : size{bytes}, alignment{bytes} {} | |
| SizeAndAlignment(std::size_t bytes, std::size_t align) | |
| : size{bytes}, alignment{align} {} | |
| SizeAndAlignment(std::size_t bytes, std::size_t align, const Symbol *tooBig) | |
| : size{bytes}, alignment{align}, oversized{tooBig} {} | |
| std::size_t size{0}; | |
| std::size_t alignment{0}; | |
| // Null unless the size exceeds maxStorageSizeInBytes, in which case the | |
| // size is clamped to it and this is the symbol to blame in a diagnostic: | |
| // for an EQUIVALENCE storage sequence, the first member found not to fit. | |
| const Symbol *oversized{nullptr}; | |
| }; | |
| struct SymbolAndOffset { | |
| SymbolAndOffset(Symbol &s, std::size_t off, const EquivalenceObject &obj) | |
| : symbol{s}, offset{off}, object{&obj} {} | |
| SymbolAndOffset(const SymbolAndOffset &) = default; | |
| MutableSymbolRef symbol; | |
| std::size_t offset; | |
| const EquivalenceObject *object; | |
| bool offsetOverflow{false}; | |
| }; | |
| void DoCommonBlock(Symbol &); | |
| void DoEquivalenceBlockBase(Symbol &, SizeAndAlignment &); | |
| void DoEquivalenceSet(const EquivalenceSet &); | |
| SymbolAndOffset Resolve(const SymbolAndOffset &); | |
| std::size_t ComputeOffset(const EquivalenceObject &); | |
| // Returns amount of padding that was needed for alignment | |
| std::size_t DoSymbol( | |
| Symbol &, std::optional<const size_t> newAlign = std::nullopt); | |
| SizeAndAlignment GetSizeAndAlignment(const Symbol &, bool entire); | |
| std::size_t Align(std::size_t, std::size_t); | |
| std::optional<size_t> CompAlignment(const Symbol &); | |
| std::optional<size_t> HasSpecialAlign(const Symbol &, Scope &); | |
| SemanticsContext &context_; | |
| std::size_t offset_{0}; | |
| std::size_t alignment_{1}; | |
| bool sizeOverflow_{false}; | |
| // symbol -> symbol+offset that determines its location, from EQUIVALENCE | |
| std::map<MutableSymbolRef, SymbolAndOffset, SymbolAddressCompare> dependents_; | |
| // base symbol -> SizeAndAlignment for each distinct EQUIVALENCE block | |
| std::map<MutableSymbolRef, SizeAndAlignment, SymbolAddressCompare> | |
| equivalenceBlock_; | |
| }; | |
| // This function is only called if the target platform is AIX. | |
| static bool isReal8OrLarger(const Fortran::semantics::DeclTypeSpec *type) { | |
| return ((type->IsNumeric(common::TypeCategory::Real) || | |
| type->IsNumeric(common::TypeCategory::Complex)) && | |
| evaluate::ToInt64(type->numericTypeSpec().kind()) > 4); | |
| } | |
| // This function is only called if the target platform is AIX. | |
| // It determines the alignment of a component. If the component is a derived | |
| // type, the alignment is computed accordingly. | |
| std::optional<size_t> ComputeOffsetsHelper::CompAlignment(const Symbol &sym) { | |
| size_t max_align{0}; | |
| constexpr size_t fourByteAlign{4}; | |
| bool contain_double{false}; | |
| auto derivedTypeSpec{sym.GetType()->AsDerived()}; | |
| DirectComponentIterator directs{*derivedTypeSpec}; | |
| for (auto it{directs.begin()}; it != directs.end(); ++it) { | |
| auto type{it->GetType()}; | |
| auto s{GetSizeAndAlignment(*it, true)}; | |
| if (isReal8OrLarger(type)) { | |
| max_align = std::max(max_align, fourByteAlign); | |
| contain_double = true; | |
| } else if (type->AsDerived()) { | |
| if (const auto newAlgin{CompAlignment(*it)}) { | |
| max_align = std::max(max_align, s.alignment); | |
| } else { | |
| return std::nullopt; | |
| } | |
| } else { | |
| max_align = std::max(max_align, s.alignment); | |
| } | |
| } | |
| if (contain_double) { | |
| return max_align; | |
| } else { | |
| return std::nullopt; | |
| } | |
| } | |
| // This function is only called if the target platform is AIX. | |
| // Special alignment is needed only if it is a bind(c) derived type | |
| // and contain real type components that have larger than 4 bytes. | |
| std::optional<size_t> ComputeOffsetsHelper::HasSpecialAlign( | |
| const Symbol &sym, Scope &scope) { | |
| // On AIX, if the component that is not the first component and is | |
| // a float of 8 bytes or larger, it has the 4-byte alignment. | |
| // Only set the special alignment for bind(c) derived type on that platform. | |
| if (const auto type{sym.GetType()}) { | |
| auto &symOwner{sym.owner()}; | |
| if (symOwner.symbol() && symOwner.IsDerivedType() && | |
| symOwner.symbol()->attrs().HasAny({semantics::Attr::BIND_C}) && | |
| &sym != &(*scope.GetSymbols().front())) { | |
| if (isReal8OrLarger(type)) { | |
| return 4UL; | |
| } else if (type->AsDerived()) { | |
| return CompAlignment(sym); | |
| } | |
| } | |
| } | |
| return std::nullopt; | |
| } | |
| void ComputeOffsetsHelper::Compute(Scope &scope) { | |
| for (Scope &child : scope.children()) { | |
| ComputeOffsets(context_, child); | |
| } | |
| if (scope.symbol() && scope.IsDerivedTypeWithKindParameter()) { | |
| return; // only process instantiations of kind parameterized derived types | |
| } | |
| if (scope.alignment().has_value()) { | |
| return; // prevent infinite recursion in error cases | |
| } | |
| scope.SetAlignment(0); | |
| // Build dependents_ from equivalences: symbol -> symbol+offset | |
| for (const EquivalenceSet &set : scope.equivalenceSets()) { | |
| DoEquivalenceSet(set); | |
| } | |
| // Compute a base symbol and overall block size for each | |
| // disjoint EQUIVALENCE storage sequence. | |
| for (auto &[symbol, dep] : dependents_) { | |
| dep = Resolve(dep); | |
| CHECK(symbol->size() == 0); | |
| auto symInfo{GetSizeAndAlignment(*symbol, true)}; | |
| symbol->set_size(symInfo.size); | |
| Symbol &base{*dep.symbol}; | |
| auto iter{equivalenceBlock_.find(base)}; | |
| bool blockOverflow{dep.offsetOverflow || symInfo.oversized}; | |
| std::size_t minBlockSize{AddSizes(dep.offset, symInfo.size, blockOverflow)}; | |
| const Symbol *oversized{ | |
| blockOverflow || IsTooBig(minBlockSize) ? &*symbol : nullptr}; | |
| if (iter == equivalenceBlock_.end()) { | |
| equivalenceBlock_.emplace( | |
| base, SizeAndAlignment{minBlockSize, symInfo.alignment, oversized}); | |
| } else { | |
| SizeAndAlignment &blockInfo{iter->second}; | |
| blockInfo.size = std::max(blockInfo.size, minBlockSize); | |
| blockInfo.alignment = std::max(blockInfo.alignment, symInfo.alignment); | |
| if (!blockInfo.oversized) { | |
| blockInfo.oversized = oversized; | |
| } | |
| } | |
| } | |
| // Complete each EQUIVALENCE block with its base object, and assign offsets | |
| // for non-COMMON blocks. | |
| for (auto &[symbol, blockInfo] : equivalenceBlock_) { | |
| // The base does not appear in dependents_. | |
| SizeAndAlignment baseInfo{GetSizeAndAlignment(*symbol, true)}; | |
| blockInfo.size = std::max(blockInfo.size, baseInfo.size); | |
| blockInfo.alignment = std::max(blockInfo.alignment, baseInfo.alignment); | |
| if (!blockInfo.oversized && | |
| (baseInfo.oversized || IsTooBig(baseInfo.size))) { | |
| blockInfo.oversized = &*symbol; | |
| } | |
| if (!FindCommonBlockContaining(*symbol)) { | |
| DoSymbol(*symbol); | |
| DoEquivalenceBlockBase(*symbol, blockInfo); | |
| // Each EQUIVALENCE block is lowered as one aggregate. Blame a member | |
| // that does not fit rather than the base object, whose selection in | |
| // DoEquivalenceSet is a layout decision that is invisible to the user. | |
| if (blockInfo.oversized || IsTooBig(blockInfo.size)) { | |
| const Symbol &blamed{ | |
| blockInfo.oversized ? *blockInfo.oversized : *symbol}; | |
| context_.Say(blamed.name(), | |
| "The size of the storage sequence created by EQUIVALENCE with '%s' exceeds the maximum supported size of %zu bytes"_err_en_US, | |
| blamed.name(), maxStorageSizeInBytes); | |
| } | |
| offset_ = std::max(offset_, symbol->offset() + blockInfo.size); | |
| } | |
| } | |
| // Process remaining non-COMMON symbols; this is all of them if there | |
| // was no use of EQUIVALENCE in the scope. | |
| for (auto &symbol : scope.GetSymbols()) { | |
| if (!FindCommonBlockContaining(*symbol) && | |
| dependents_.find(symbol) == dependents_.end() && | |
| equivalenceBlock_.find(symbol) == equivalenceBlock_.end()) { | |
| std::optional<size_t> newAlign{std::nullopt}; | |
| // Handle special alignment requirement for AIX | |
| auto triple{llvm::Triple( | |
| llvm::Triple::normalize(llvm::sys::getDefaultTargetTriple()))}; | |
| if (triple.getOS() == llvm::Triple::OSType::AIX) { | |
| newAlign = HasSpecialAlign(*symbol, scope); | |
| } | |
| DoSymbol(*symbol, newAlign); | |
| if (auto *generic{symbol->detailsIf<GenericDetails>()}) { | |
| if (Symbol * specific{generic->specific()}; | |
| specific && !FindCommonBlockContaining(*specific)) { | |
| // might be a shadowed procedure pointer | |
| DoSymbol(*specific); | |
| } | |
| } | |
| } | |
| } | |
| // Ensure that the size is a multiple of the alignment | |
| offset_ = Align(offset_, alignment_); | |
| sizeOverflow_ |= IsTooBig(offset_); | |
| // Only derived-type scope sizes are materialized in generated IR. | |
| if (sizeOverflow_ && scope.IsDerivedType() && scope.symbol()) { | |
| context_.Say(scope.symbol()->name(), | |
| "The size of derived type '%s' exceeds the maximum supported size of %zu bytes"_err_en_US, | |
| scope.symbol()->name(), maxStorageSizeInBytes); | |
| } | |
| scope.set_size(offset_); | |
| scope.SetAlignment(alignment_); | |
| // Assign offsets in COMMON blocks, unless this scope is a BLOCK construct, | |
| // where COMMON blocks are illegal (C1107 and C1108). | |
| if (scope.kind() != Scope::Kind::BlockConstruct) { | |
| for (auto &pair : scope.commonBlocks()) { | |
| DoCommonBlock(*pair.second); | |
| } | |
| } | |
| for (auto &[symbol, dep] : dependents_) { | |
| symbol->set_offset(dep.symbol->offset() + dep.offset); | |
| if (const auto *block{FindCommonBlockContaining(*dep.symbol)}) { | |
| if (auto *object{symbol->detailsIf<ObjectEntityDetails>()}) { | |
| object->set_commonBlock(*block); | |
| } | |
| } | |
| } | |
| } | |
| auto ComputeOffsetsHelper::Resolve(const SymbolAndOffset &dep) | |
| -> SymbolAndOffset { | |
| auto it{dependents_.find(*dep.symbol)}; | |
| if (it == dependents_.end()) { | |
| return dep; | |
| } else { | |
| SymbolAndOffset result{Resolve(it->second)}; | |
| // Preserve overflow while resolving EQUIVALENCE chains, both the overflow | |
| // already recorded for the offset being resolved and any that appears when | |
| // accumulating it. | |
| result.offsetOverflow |= dep.offsetOverflow; | |
| result.offset = AddSizes(result.offset, dep.offset, result.offsetOverflow); | |
| result.object = dep.object; | |
| return result; | |
| } | |
| } | |
| void ComputeOffsetsHelper::DoCommonBlock(Symbol &commonBlock) { | |
| auto &details{commonBlock.get<CommonBlockDetails>()}; | |
| offset_ = 0; | |
| alignment_ = 0; | |
| sizeOverflow_ = false; | |
| std::size_t minSize{0}; | |
| std::size_t minAlignment{0}; | |
| UnorderedSymbolSet previous; | |
| for (auto object : details.objects()) { | |
| // Allow for host association when the common block is | |
| // OpenMP firstprivate. | |
| Symbol &symbol{object->GetUltimate()}; | |
| auto errorSite{ | |
| commonBlock.name().empty() ? symbol.name() : commonBlock.name()}; | |
| if (std::size_t padding{DoSymbol(symbol.GetUltimate())}) { | |
| context_.Warn(common::UsageWarning::CommonBlockPadding, errorSite, | |
| "COMMON block /%s/ requires %zd bytes of padding before '%s' for alignment"_port_en_US, | |
| commonBlock.name(), padding, symbol.name()); | |
| } | |
| previous.emplace(symbol); | |
| auto eqIter{equivalenceBlock_.end()}; | |
| auto iter{dependents_.find(symbol)}; | |
| if (iter == dependents_.end()) { | |
| eqIter = equivalenceBlock_.find(symbol); | |
| if (eqIter != equivalenceBlock_.end()) { | |
| DoEquivalenceBlockBase(symbol, eqIter->second); | |
| } | |
| } else { | |
| SymbolAndOffset &dep{iter->second}; | |
| Symbol &base{*dep.symbol}; | |
| if (const auto *baseBlock{FindCommonBlockContaining(base)}) { | |
| if (baseBlock == &commonBlock) { | |
| if (previous.find(SymbolRef{base}) == previous.end() || | |
| base.offset() != symbol.offset() - dep.offset) { | |
| context_.Say(errorSite, | |
| "'%s' is storage associated with '%s' by EQUIVALENCE elsewhere in COMMON block /%s/"_err_en_US, | |
| symbol.name(), base.name(), commonBlock.name()); | |
| } | |
| } else { // F'2023 8.10.3 p1 | |
| context_.Say(errorSite, | |
| "'%s' in COMMON block /%s/ must not be storage associated with '%s' in COMMON block /%s/ by EQUIVALENCE"_err_en_US, | |
| symbol.name(), commonBlock.name(), base.name(), | |
| baseBlock->name()); | |
| } | |
| } else if (dep.offset > symbol.offset()) { // 8.10.3(3) | |
| context_.Say(errorSite, | |
| "'%s' cannot backward-extend COMMON block /%s/ via EQUIVALENCE with '%s'"_err_en_US, | |
| symbol.name(), commonBlock.name(), base.name()); | |
| } else { | |
| eqIter = equivalenceBlock_.find(base); | |
| base.get<ObjectEntityDetails>().set_commonBlock(commonBlock); | |
| base.set_offset(symbol.offset() - dep.offset); | |
| previous.emplace(base); | |
| } | |
| } | |
| // Get full extent of any EQUIVALENCE block into size of COMMON ( see | |
| // 8.10.2.2 point 1 (2)) | |
| if (eqIter != equivalenceBlock_.end()) { | |
| SizeAndAlignment &blockInfo{eqIter->second}; | |
| sizeOverflow_ |= blockInfo.oversized != nullptr; | |
| std::size_t blockEnd{ | |
| AddSizes(eqIter->first->offset(), blockInfo.size, sizeOverflow_)}; | |
| minSize = std::max(minSize, std::max(offset_, blockEnd)); | |
| minAlignment = std::max(minAlignment, blockInfo.alignment); | |
| } | |
| } | |
| std::size_t size{std::max(minSize, offset_)}; | |
| if (sizeOverflow_) { | |
| context_.Say(details.sourceLocation(), | |
| "The size of COMMON block /%s/ exceeds the maximum supported size of %zu bytes"_err_en_US, | |
| commonBlock.name(), maxStorageSizeInBytes); | |
| } | |
| commonBlock.set_size(size); | |
| details.set_alignment(std::max(minAlignment, alignment_)); | |
| context_.MapCommonBlockAndCheckConflicts(commonBlock); | |
| } | |
| void ComputeOffsetsHelper::DoEquivalenceBlockBase( | |
| Symbol &symbol, SizeAndAlignment &blockInfo) { | |
| if (symbol.size() > blockInfo.size) { | |
| blockInfo.size = symbol.size(); | |
| } | |
| } | |
| void ComputeOffsetsHelper::DoEquivalenceSet(const EquivalenceSet &set) { | |
| std::vector<SymbolAndOffset> symbolOffsets; | |
| std::optional<std::size_t> representative; | |
| for (const EquivalenceObject &object : set) { | |
| std::size_t offset{ComputeOffset(object)}; | |
| SymbolAndOffset resolved{ | |
| Resolve(SymbolAndOffset{object.symbol, offset, object})}; | |
| symbolOffsets.push_back(resolved); | |
| if (!representative || | |
| resolved.offset >= symbolOffsets[*representative].offset) { | |
| // The equivalenced object with the largest offset from its resolved | |
| // symbol will be the representative of this set, since the offsets | |
| // of the other objects will be positive relative to it. | |
| representative = symbolOffsets.size() - 1; | |
| } | |
| } | |
| CHECK(representative); | |
| const SymbolAndOffset &base{symbolOffsets[*representative]}; | |
| for (const auto &[symbol, offset, object, offsetOverflow] : symbolOffsets) { | |
| if (symbol == base.symbol) { | |
| if (offset != base.offset) { | |
| auto x{evaluate::OffsetToDesignator( | |
| context_.foldingContext(), *symbol, base.offset, 1)}; | |
| auto y{evaluate::OffsetToDesignator( | |
| context_.foldingContext(), *symbol, offset, 1)}; | |
| if (x && y) { | |
| context_ | |
| .Say(base.object->source, | |
| "'%s' and '%s' cannot have the same first storage unit"_err_en_US, | |
| x->AsFortran(), y->AsFortran()) | |
| .Attach(object->source, "Incompatible reference to '%s'"_en_US, | |
| y->AsFortran()); | |
| } else { // error recovery | |
| context_ | |
| .Say(base.object->source, | |
| "'%s' (offset %zd bytes and %zd bytes) cannot have the same first storage unit"_err_en_US, | |
| symbol->name(), base.offset, offset) | |
| .Attach(object->source, | |
| "Incompatible reference to '%s' offset %zd bytes"_en_US, | |
| symbol->name(), offset); | |
| } | |
| } | |
| } else { | |
| SymbolAndOffset dependent{*base.symbol, base.offset - offset, *object}; | |
| dependent.offsetOverflow = base.offsetOverflow || offsetOverflow; | |
| dependents_.emplace(*symbol, dependent); | |
| } | |
| } | |
| } | |
| // Offset of this equivalence object from the start of its variable. | |
| std::size_t ComputeOffsetsHelper::ComputeOffset( | |
| const EquivalenceObject &object) { | |
| std::size_t offset{0}; | |
| if (!object.subscripts.empty()) { | |
| if (const auto *details{object.symbol.detailsIf<ObjectEntityDetails>()}) { | |
| const ArraySpec &shape{details->shape()}; | |
| auto lbound{[&](std::size_t i) { | |
| return *ToInt64(shape[i].lbound().GetExplicit()); | |
| }}; | |
| auto ubound{[&](std::size_t i) { | |
| return *ToInt64(shape[i].ubound().GetExplicit()); | |
| }}; | |
| for (std::size_t i{object.subscripts.size() - 1};;) { | |
| offset += object.subscripts[i] - lbound(i); | |
| if (i == 0) { | |
| break; | |
| } | |
| --i; | |
| offset *= ubound(i) - lbound(i) + 1; | |
| } | |
| } | |
| } | |
| auto result{offset * GetSizeAndAlignment(object.symbol, false).size}; | |
| if (object.substringStart) { | |
| int kind{context_.defaultKinds().GetDefaultKind(TypeCategory::Character)}; | |
| if (const DeclTypeSpec * type{object.symbol.GetType()}) { | |
| if (const IntrinsicTypeSpec * intrinsic{type->AsIntrinsic()}) { | |
| kind = ToInt64(intrinsic->kind()).value_or(kind); | |
| } | |
| } | |
| result += kind * (*object.substringStart - 1); | |
| } | |
| return result; | |
| } | |
| std::size_t ComputeOffsetsHelper::DoSymbol( | |
| Symbol &symbol, std::optional<const size_t> newAlign) { | |
| if (!symbol.has<ObjectEntityDetails>() && !symbol.has<ProcEntityDetails>()) { | |
| return 0; | |
| } | |
| SizeAndAlignment s{GetSizeAndAlignment(symbol, true)}; | |
| // Oversized standalone objects are left to object emission. | |
| sizeOverflow_ |= s.oversized != nullptr; | |
| if (s.size == 0) { | |
| // Zero-size symbols (e.g. CHARACTER*0) still occupy their sequential | |
| // position in a COMMON block or derived-type sequence. Record the current | |
| // offset so that LOC() and storage-association checks see the correct | |
| // address rather than always returning the block base (offset 0). | |
| symbol.set_size(0); | |
| symbol.set_offset(offset_); | |
| return 0; | |
| } | |
| std::size_t previousOffset{offset_}; | |
| size_t alignVal{newAlign.value_or(s.alignment)}; | |
| offset_ = Align(offset_, alignVal); | |
| sizeOverflow_ |= IsTooBig(offset_); | |
| std::size_t padding{offset_ - previousOffset}; | |
| symbol.set_size(s.size); | |
| symbol.set_offset(offset_); | |
| offset_ = AddSizes(offset_, s.size, sizeOverflow_); | |
| alignment_ = std::max(alignment_, alignVal); | |
| return padding; | |
| } | |
| auto ComputeOffsetsHelper::GetSizeAndAlignment( | |
| const Symbol &symbol, bool entire) -> SizeAndAlignment { | |
| auto &targetCharacteristics{context_.targetCharacteristics()}; | |
| if (IsDescriptor(symbol)) { | |
| auto dyType{evaluate::DynamicType::From(symbol)}; | |
| const auto *derived{evaluate::GetDerivedTypeSpec(dyType)}; | |
| int lenParams{derived ? CountLenParameters(*derived) : 0}; | |
| bool needAddendum{derived || (dyType && dyType->IsUnlimitedPolymorphic())}; | |
| // FIXME: Get descriptor size from targetCharacteristics instead | |
| // overapproximation | |
| std::size_t size{runtime::MaxDescriptorSizeInBytes( | |
| symbol.Rank(), needAddendum, lenParams)}; | |
| return {size, targetCharacteristics.descriptorAlignment()}; | |
| } | |
| if (IsProcedurePointer(symbol)) { | |
| return {targetCharacteristics.procedurePointerByteSize(), | |
| targetCharacteristics.procedurePointerAlignment()}; | |
| } | |
| if (IsProcedure(symbol)) { | |
| return {}; | |
| } | |
| auto &foldingContext{context_.foldingContext()}; | |
| if (auto chars{evaluate::characteristics::TypeAndShape::Characterize( | |
| symbol, foldingContext)}) { | |
| std::size_t alignment{chars->type().GetAlignment(targetCharacteristics)}; | |
| // Avoid folded products, which can wrap in signed 64-bit arithmetic. | |
| bool aligned{!entire || chars->Rank() > 0}; | |
| std::size_t size; | |
| if (chars->type().category() == TypeCategory::Character && chars->LEN()) { | |
| auto length{ToInt64(*chars->LEN())}; | |
| if (!length) { | |
| return {}; | |
| } | |
| auto bytesPerCharacter{ | |
| static_cast<std::size_t>(targetCharacteristics.GetByteSize( | |
| TypeCategory::Character, chars->type().kind()))}; | |
| if (*length < 0 || | |
| static_cast<std::size_t>(*length) > | |
| maxStorageSizeInBytes / bytesPerCharacter) { | |
| return {maxStorageSizeInBytes, alignment, &symbol}; | |
| } | |
| size = static_cast<std::size_t>(*length) * bytesPerCharacter; | |
| } else { | |
| auto elementSize{ | |
| ToInt64(chars->MeasureElementSizeInBytes(foldingContext, aligned))}; | |
| if (!elementSize) { | |
| return {}; | |
| } | |
| if (*elementSize < 0) { | |
| return {maxStorageSizeInBytes, alignment, &symbol}; | |
| } | |
| size = static_cast<std::size_t>(*elementSize); | |
| } | |
| if (!entire) { // element size only | |
| return {size, alignment}; | |
| } | |
| if (auto extents{ | |
| evaluate::AsConstantExtents(foldingContext, chars->shape())}) { | |
| if (size == 0) { // zero-sized elements occupy no storage | |
| return {0, alignment}; | |
| } | |
| for (int dimension{0}; dimension < static_cast<int>(extents->size()); | |
| ++dimension) { | |
| if ((*extents)[dimension] <= 0) { | |
| if (!IsEmptyDimension(symbol, dimension)) { | |
| return {maxStorageSizeInBytes, alignment, &symbol}; | |
| } | |
| return {0, alignment}; // a zero-sized array occupies no storage | |
| } | |
| } | |
| for (ConstantSubscript extent : *extents) { | |
| auto n{static_cast<std::size_t>(extent)}; | |
| if (size > maxStorageSizeInBytes / n) { | |
| return {maxStorageSizeInBytes, alignment, &symbol}; | |
| } | |
| size *= n; | |
| } | |
| return {size, alignment}; | |
| } | |
| } | |
| return {}; | |
| } | |
| // Align a size to its natural alignment, up to maxAlignment. | |
| std::size_t ComputeOffsetsHelper::Align(std::size_t x, std::size_t alignment) { | |
| alignment = | |
| std::min(alignment, context_.targetCharacteristics().maxAlignment()); | |
| return (x + alignment - 1) & -alignment; | |
| } | |
| void ComputeOffsets(SemanticsContext &context, Scope &scope) { | |
| ComputeOffsetsHelper{context}.Compute(scope); | |
| } | |
| } // namespace Fortran::semantics |