xref: /llvm-project/flang/lib/Semantics/compute-offsets.cpp (revision 79e788d02eefdacb08af365389b9055518f3fad6)
1d1862eb8Speter klausler //===-- lib/Semantics/compute-offsets.cpp -----------------------*- C++ -*-===//
2c353ebbfSTim Keith //
3c353ebbfSTim Keith // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4c353ebbfSTim Keith // See https://llvm.org/LICENSE.txt for license information.
5c353ebbfSTim Keith // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6c353ebbfSTim Keith //
7c353ebbfSTim Keith //===----------------------------------------------------------------------===//
8c353ebbfSTim Keith 
9c353ebbfSTim Keith #include "compute-offsets.h"
1031e6cd28Speter klausler #include "flang/Evaluate/fold-designator.h"
11c353ebbfSTim Keith #include "flang/Evaluate/fold.h"
12c353ebbfSTim Keith #include "flang/Evaluate/shape.h"
13c353ebbfSTim Keith #include "flang/Evaluate/type.h"
14c91ba043SMichael Kruse #include "flang/Runtime/descriptor-consts.h"
15c353ebbfSTim Keith #include "flang/Semantics/scope.h"
16c353ebbfSTim Keith #include "flang/Semantics/semantics.h"
17c353ebbfSTim Keith #include "flang/Semantics/symbol.h"
18c353ebbfSTim Keith #include "flang/Semantics/tools.h"
19c353ebbfSTim Keith #include "flang/Semantics/type.h"
20*79e788d0SKelvin Li #include "llvm/TargetParser/Host.h"
21*79e788d0SKelvin Li #include "llvm/TargetParser/Triple.h"
22c353ebbfSTim Keith #include <algorithm>
23c353ebbfSTim Keith #include <vector>
24c353ebbfSTim Keith 
25c353ebbfSTim Keith namespace Fortran::semantics {
26c353ebbfSTim Keith 
27c353ebbfSTim Keith class ComputeOffsetsHelper {
28c353ebbfSTim Keith public:
29c353ebbfSTim Keith   ComputeOffsetsHelper(SemanticsContext &context) : context_{context} {}
306aa3591eSpeter klausler   void Compute(Scope &);
31c353ebbfSTim Keith 
32c353ebbfSTim Keith private:
3354b35c06STim Keith   struct SizeAndAlignment {
3454b35c06STim Keith     SizeAndAlignment() {}
35d1862eb8Speter klausler     SizeAndAlignment(std::size_t bytes) : size{bytes}, alignment{bytes} {}
36d1862eb8Speter klausler     SizeAndAlignment(std::size_t bytes, std::size_t align)
37d1862eb8Speter klausler         : size{bytes}, alignment{align} {}
38c353ebbfSTim Keith     std::size_t size{0};
3954b35c06STim Keith     std::size_t alignment{0};
40c353ebbfSTim Keith   };
41237d0e3cSTim Keith   struct SymbolAndOffset {
4231e6cd28Speter klausler     SymbolAndOffset(Symbol &s, std::size_t off, const EquivalenceObject &obj)
43d60a0220Speter klausler         : symbol{s}, offset{off}, object{&obj} {}
4431e6cd28Speter klausler     SymbolAndOffset(const SymbolAndOffset &) = default;
45d60a0220Speter klausler     MutableSymbolRef symbol;
4631e6cd28Speter klausler     std::size_t offset;
4731e6cd28Speter klausler     const EquivalenceObject *object;
48237d0e3cSTim Keith   };
49c353ebbfSTim Keith 
50237d0e3cSTim Keith   void DoCommonBlock(Symbol &);
514ac617f4Speter klausler   void DoEquivalenceBlockBase(Symbol &, SizeAndAlignment &);
5231e6cd28Speter klausler   void DoEquivalenceSet(const EquivalenceSet &);
5331e6cd28Speter klausler   SymbolAndOffset Resolve(const SymbolAndOffset &);
54237d0e3cSTim Keith   std::size_t ComputeOffset(const EquivalenceObject &);
55c709f503SPeter Klausler   // Returns amount of padding that was needed for alignment
56*79e788d0SKelvin Li   std::size_t DoSymbol(
57*79e788d0SKelvin Li       Symbol &, std::optional<const size_t> newAlign = std::nullopt);
586aa3591eSpeter klausler   SizeAndAlignment GetSizeAndAlignment(const Symbol &, bool entire);
596aa3591eSpeter klausler   std::size_t Align(std::size_t, std::size_t);
60*79e788d0SKelvin Li   std::optional<size_t> CompAlignment(const Symbol &);
61*79e788d0SKelvin Li   std::optional<size_t> HasSpecialAlign(const Symbol &, Scope &);
62c353ebbfSTim Keith 
63c353ebbfSTim Keith   SemanticsContext &context_;
64c353ebbfSTim Keith   std::size_t offset_{0};
656aa3591eSpeter klausler   std::size_t alignment_{1};
66237d0e3cSTim Keith   // symbol -> symbol+offset that determines its location, from EQUIVALENCE
670d8331c0Speter klausler   std::map<MutableSymbolRef, SymbolAndOffset, SymbolAddressCompare> dependents_;
684ac617f4Speter klausler   // base symbol -> SizeAndAlignment for each distinct EQUIVALENCE block
690d8331c0Speter klausler   std::map<MutableSymbolRef, SizeAndAlignment, SymbolAddressCompare>
700d8331c0Speter klausler       equivalenceBlock_;
71c353ebbfSTim Keith };
72c353ebbfSTim Keith 
73*79e788d0SKelvin Li // This function is only called if the target platform is AIX.
74*79e788d0SKelvin Li static bool isReal8OrLarger(const Fortran::semantics::DeclTypeSpec *type) {
75*79e788d0SKelvin Li   return ((type->IsNumeric(common::TypeCategory::Real) ||
76*79e788d0SKelvin Li               type->IsNumeric(common::TypeCategory::Complex)) &&
77*79e788d0SKelvin Li       evaluate::ToInt64(type->numericTypeSpec().kind()) > 4);
78*79e788d0SKelvin Li }
79*79e788d0SKelvin Li 
80*79e788d0SKelvin Li // This function is only called if the target platform is AIX.
81*79e788d0SKelvin Li // It determines the alignment of a component. If the component is a derived
82*79e788d0SKelvin Li // type, the alignment is computed accordingly.
83*79e788d0SKelvin Li std::optional<size_t> ComputeOffsetsHelper::CompAlignment(const Symbol &sym) {
84*79e788d0SKelvin Li   size_t max_align{0};
85*79e788d0SKelvin Li   constexpr size_t fourByteAlign{4};
86*79e788d0SKelvin Li   bool contain_double{false};
87*79e788d0SKelvin Li   auto derivedTypeSpec{sym.GetType()->AsDerived()};
88*79e788d0SKelvin Li   DirectComponentIterator directs{*derivedTypeSpec};
89*79e788d0SKelvin Li   for (auto it{directs.begin()}; it != directs.end(); ++it) {
90*79e788d0SKelvin Li     auto type{it->GetType()};
91*79e788d0SKelvin Li     auto s{GetSizeAndAlignment(*it, true)};
92*79e788d0SKelvin Li     if (isReal8OrLarger(type)) {
93*79e788d0SKelvin Li       max_align = std::max(max_align, fourByteAlign);
94*79e788d0SKelvin Li       contain_double = true;
95*79e788d0SKelvin Li     } else if (type->AsDerived()) {
96*79e788d0SKelvin Li       if (const auto newAlgin{CompAlignment(*it)}) {
97*79e788d0SKelvin Li         max_align = std::max(max_align, s.alignment);
98*79e788d0SKelvin Li       } else {
99*79e788d0SKelvin Li         return std::nullopt;
100*79e788d0SKelvin Li       }
101*79e788d0SKelvin Li     } else {
102*79e788d0SKelvin Li       max_align = std::max(max_align, s.alignment);
103*79e788d0SKelvin Li     }
104*79e788d0SKelvin Li   }
105*79e788d0SKelvin Li 
106*79e788d0SKelvin Li   if (contain_double) {
107*79e788d0SKelvin Li     return max_align;
108*79e788d0SKelvin Li   } else {
109*79e788d0SKelvin Li     return std::nullopt;
110*79e788d0SKelvin Li   }
111*79e788d0SKelvin Li }
112*79e788d0SKelvin Li 
113*79e788d0SKelvin Li // This function is only called if the target platform is AIX.
114*79e788d0SKelvin Li // Special alignment is needed only if it is a bind(c) derived type
115*79e788d0SKelvin Li // and contain real type components that have larger than 4 bytes.
116*79e788d0SKelvin Li std::optional<size_t> ComputeOffsetsHelper::HasSpecialAlign(
117*79e788d0SKelvin Li     const Symbol &sym, Scope &scope) {
118*79e788d0SKelvin Li   // On AIX, if the component that is not the first component and is
119*79e788d0SKelvin Li   // a float of 8 bytes or larger, it has the 4-byte alignment.
120*79e788d0SKelvin Li   // Only set the special alignment for bind(c) derived type on that platform.
121*79e788d0SKelvin Li   if (const auto type{sym.GetType()}) {
122*79e788d0SKelvin Li     auto &symOwner{sym.owner()};
123*79e788d0SKelvin Li     if (symOwner.symbol() && symOwner.IsDerivedType() &&
124*79e788d0SKelvin Li         symOwner.symbol()->attrs().HasAny({semantics::Attr::BIND_C}) &&
125*79e788d0SKelvin Li         &sym != &(*scope.GetSymbols().front())) {
126*79e788d0SKelvin Li       if (isReal8OrLarger(type)) {
127*79e788d0SKelvin Li         return 4UL;
128*79e788d0SKelvin Li       } else if (type->AsDerived()) {
129*79e788d0SKelvin Li         return CompAlignment(sym);
130*79e788d0SKelvin Li       }
131*79e788d0SKelvin Li     }
132*79e788d0SKelvin Li   }
133*79e788d0SKelvin Li   return std::nullopt;
134*79e788d0SKelvin Li }
135*79e788d0SKelvin Li 
136c353ebbfSTim Keith void ComputeOffsetsHelper::Compute(Scope &scope) {
137c353ebbfSTim Keith   for (Scope &child : scope.children()) {
1386aa3591eSpeter klausler     ComputeOffsets(context_, child);
139c353ebbfSTim Keith   }
140f2da8f5eSJean Perier   if (scope.symbol() && scope.IsDerivedTypeWithKindParameter()) {
141f88a9497SJean Perier     return; // only process instantiations of kind parameterized derived types
142c353ebbfSTim Keith   }
1434fede8bcSpeter klausler   if (scope.alignment().has_value()) {
1444fede8bcSpeter klausler     return; // prevent infinite recursion in error cases
1454fede8bcSpeter klausler   }
1464fede8bcSpeter klausler   scope.SetAlignment(0);
147237d0e3cSTim Keith   // Build dependents_ from equivalences: symbol -> symbol+offset
14831e6cd28Speter klausler   for (const EquivalenceSet &set : scope.equivalenceSets()) {
149237d0e3cSTim Keith     DoEquivalenceSet(set);
150237d0e3cSTim Keith   }
1514ac617f4Speter klausler   // Compute a base symbol and overall block size for each
1524ac617f4Speter klausler   // disjoint EQUIVALENCE storage sequence.
1534ac617f4Speter klausler   for (auto &[symbol, dep] : dependents_) {
1544ac617f4Speter klausler     dep = Resolve(dep);
1554ac617f4Speter klausler     CHECK(symbol->size() == 0);
1566aa3591eSpeter klausler     auto symInfo{GetSizeAndAlignment(*symbol, true)};
1574ac617f4Speter klausler     symbol->set_size(symInfo.size);
1584ac617f4Speter klausler     Symbol &base{*dep.symbol};
1594ac617f4Speter klausler     auto iter{equivalenceBlock_.find(base)};
1604ac617f4Speter klausler     std::size_t minBlockSize{dep.offset + symInfo.size};
1614ac617f4Speter klausler     if (iter == equivalenceBlock_.end()) {
1624ac617f4Speter klausler       equivalenceBlock_.emplace(
1634ac617f4Speter klausler           base, SizeAndAlignment{minBlockSize, symInfo.alignment});
1644ac617f4Speter klausler     } else {
1654ac617f4Speter klausler       SizeAndAlignment &blockInfo{iter->second};
1664ac617f4Speter klausler       blockInfo.size = std::max(blockInfo.size, minBlockSize);
1674ac617f4Speter klausler       blockInfo.alignment = std::max(blockInfo.alignment, symInfo.alignment);
1684ac617f4Speter klausler     }
1694ac617f4Speter klausler   }
1704ac617f4Speter klausler   // Assign offsets for non-COMMON EQUIVALENCE blocks
1714ac617f4Speter klausler   for (auto &[symbol, blockInfo] : equivalenceBlock_) {
17205e62db2SPeter Klausler     if (!FindCommonBlockContaining(*symbol)) {
1734ac617f4Speter klausler       DoSymbol(*symbol);
1744ac617f4Speter klausler       DoEquivalenceBlockBase(*symbol, blockInfo);
1754ac617f4Speter klausler       offset_ = std::max(offset_, symbol->offset() + blockInfo.size);
1764ac617f4Speter klausler     }
1774ac617f4Speter klausler   }
1784ac617f4Speter klausler   // Process remaining non-COMMON symbols; this is all of them if there
1794ac617f4Speter klausler   // was no use of EQUIVALENCE in the scope.
180237d0e3cSTim Keith   for (auto &symbol : scope.GetSymbols()) {
18105e62db2SPeter Klausler     if (!FindCommonBlockContaining(*symbol) &&
1824ac617f4Speter klausler         dependents_.find(symbol) == dependents_.end() &&
1834ac617f4Speter klausler         equivalenceBlock_.find(symbol) == equivalenceBlock_.end()) {
184*79e788d0SKelvin Li 
185*79e788d0SKelvin Li       std::optional<size_t> newAlign{std::nullopt};
186*79e788d0SKelvin Li       // Handle special alignment requirement for AIX
187*79e788d0SKelvin Li       auto triple{llvm::Triple(
188*79e788d0SKelvin Li           llvm::Triple::normalize(llvm::sys::getDefaultTargetTriple()))};
189*79e788d0SKelvin Li       if (triple.getOS() == llvm::Triple::OSType::AIX) {
190*79e788d0SKelvin Li         newAlign = HasSpecialAlign(*symbol, scope);
191*79e788d0SKelvin Li       }
192*79e788d0SKelvin Li       DoSymbol(*symbol, newAlign);
193d418a03eSPeter Klausler       if (auto *generic{symbol->detailsIf<GenericDetails>()}) {
194d418a03eSPeter Klausler         if (Symbol * specific{generic->specific()};
195d418a03eSPeter Klausler             specific && !FindCommonBlockContaining(*specific)) {
196d418a03eSPeter Klausler           // might be a shadowed procedure pointer
197d418a03eSPeter Klausler           DoSymbol(*specific);
198d418a03eSPeter Klausler         }
199d418a03eSPeter Klausler       }
200c353ebbfSTim Keith     }
201c353ebbfSTim Keith   }
20208d6b874SPeter Klausler   // Ensure that the size is a multiple of the alignment
20308d6b874SPeter Klausler   offset_ = Align(offset_, alignment_);
204c353ebbfSTim Keith   scope.set_size(offset_);
2054fede8bcSpeter klausler   scope.SetAlignment(alignment_);
206b2c96d78SEmil Kieri   // Assign offsets in COMMON blocks, unless this scope is a BLOCK construct,
207b2c96d78SEmil Kieri   // where COMMON blocks are illegal (C1107 and C1108).
208b2c96d78SEmil Kieri   if (scope.kind() != Scope::Kind::BlockConstruct) {
2094ac617f4Speter klausler     for (auto &pair : scope.commonBlocks()) {
2104ac617f4Speter klausler       DoCommonBlock(*pair.second);
2114ac617f4Speter klausler     }
212b2c96d78SEmil Kieri   }
2134ac617f4Speter klausler   for (auto &[symbol, dep] : dependents_) {
2144ac617f4Speter klausler     symbol->set_offset(dep.symbol->offset() + dep.offset);
2154ac617f4Speter klausler     if (const auto *block{FindCommonBlockContaining(*dep.symbol)}) {
2164ac617f4Speter klausler       symbol->get<ObjectEntityDetails>().set_commonBlock(*block);
2174ac617f4Speter klausler     }
2184ac617f4Speter klausler   }
219c353ebbfSTim Keith }
220c353ebbfSTim Keith 
22131e6cd28Speter klausler auto ComputeOffsetsHelper::Resolve(const SymbolAndOffset &dep)
22231e6cd28Speter klausler     -> SymbolAndOffset {
223237d0e3cSTim Keith   auto it{dependents_.find(*dep.symbol)};
224237d0e3cSTim Keith   if (it == dependents_.end()) {
22531e6cd28Speter klausler     return dep;
226237d0e3cSTim Keith   } else {
22731e6cd28Speter klausler     SymbolAndOffset result{Resolve(it->second)};
22831e6cd28Speter klausler     result.offset += dep.offset;
22931e6cd28Speter klausler     result.object = dep.object;
23031e6cd28Speter klausler     return result;
231237d0e3cSTim Keith   }
232237d0e3cSTim Keith }
233237d0e3cSTim Keith 
234237d0e3cSTim Keith void ComputeOffsetsHelper::DoCommonBlock(Symbol &commonBlock) {
235237d0e3cSTim Keith   auto &details{commonBlock.get<CommonBlockDetails>()};
236237d0e3cSTim Keith   offset_ = 0;
23754b35c06STim Keith   alignment_ = 0;
2384ac617f4Speter klausler   std::size_t minSize{0};
2394ac617f4Speter klausler   std::size_t minAlignment{0};
24094b4a986SPeter Klausler   UnorderedSymbolSet previous;
24194b4a986SPeter Klausler   for (auto object : details.objects()) {
2424ac617f4Speter klausler     Symbol &symbol{*object};
243c709f503SPeter Klausler     auto errorSite{
244c709f503SPeter Klausler         commonBlock.name().empty() ? symbol.name() : commonBlock.name()};
2454e43a14bSPeixin Qiao     if (std::size_t padding{DoSymbol(symbol.GetUltimate())}) {
2460f973ac7SPeter Klausler       context_.Warn(common::UsageWarning::CommonBlockPadding, errorSite,
247c709f503SPeter Klausler           "COMMON block /%s/ requires %zd bytes of padding before '%s' for alignment"_port_en_US,
248c709f503SPeter Klausler           commonBlock.name(), padding, symbol.name());
249c709f503SPeter Klausler     }
25094b4a986SPeter Klausler     previous.emplace(symbol);
251084d8bebSJean Perier     auto eqIter{equivalenceBlock_.end()};
2524ac617f4Speter klausler     auto iter{dependents_.find(symbol)};
2534ac617f4Speter klausler     if (iter == dependents_.end()) {
254d892d732SJean Perier       eqIter = equivalenceBlock_.find(symbol);
2554ac617f4Speter klausler       if (eqIter != equivalenceBlock_.end()) {
256084d8bebSJean Perier         DoEquivalenceBlockBase(symbol, eqIter->second);
257237d0e3cSTim Keith       }
2584ac617f4Speter klausler     } else {
2594ac617f4Speter klausler       SymbolAndOffset &dep{iter->second};
2604ac617f4Speter klausler       Symbol &base{*dep.symbol};
2614ac617f4Speter klausler       if (const auto *baseBlock{FindCommonBlockContaining(base)}) {
2624ac617f4Speter klausler         if (baseBlock == &commonBlock) {
26394b4a986SPeter Klausler           if (previous.find(SymbolRef{base}) == previous.end() ||
26494b4a986SPeter Klausler               base.offset() != symbol.offset() - dep.offset) {
2654ac617f4Speter klausler             context_.Say(errorSite,
2664ac617f4Speter klausler                 "'%s' is storage associated with '%s' by EQUIVALENCE elsewhere in COMMON block /%s/"_err_en_US,
2674ac617f4Speter klausler                 symbol.name(), base.name(), commonBlock.name());
268166563fdSPeter Klausler           }
26994b4a986SPeter Klausler         } else { // F'2023 8.10.3 p1
2704ac617f4Speter klausler           context_.Say(errorSite,
2714ac617f4Speter klausler               "'%s' in COMMON block /%s/ must not be storage associated with '%s' in COMMON block /%s/ by EQUIVALENCE"_err_en_US,
2724ac617f4Speter klausler               symbol.name(), commonBlock.name(), base.name(),
2734ac617f4Speter klausler               baseBlock->name());
2744ac617f4Speter klausler         }
2754ac617f4Speter klausler       } else if (dep.offset > symbol.offset()) { // 8.10.3(3)
2764ac617f4Speter klausler         context_.Say(errorSite,
2774ac617f4Speter klausler             "'%s' cannot backward-extend COMMON block /%s/ via EQUIVALENCE with '%s'"_err_en_US,
2784ac617f4Speter klausler             symbol.name(), commonBlock.name(), base.name());
2794ac617f4Speter klausler       } else {
280084d8bebSJean Perier         eqIter = equivalenceBlock_.find(base);
2814ac617f4Speter klausler         base.get<ObjectEntityDetails>().set_commonBlock(commonBlock);
2824ac617f4Speter klausler         base.set_offset(symbol.offset() - dep.offset);
28394b4a986SPeter Klausler         previous.emplace(base);
2844ac617f4Speter klausler       }
2854ac617f4Speter klausler     }
286084d8bebSJean Perier     // Get full extent of any EQUIVALENCE block into size of COMMON ( see
287084d8bebSJean Perier     // 8.10.2.2 point 1 (2))
288084d8bebSJean Perier     if (eqIter != equivalenceBlock_.end()) {
289084d8bebSJean Perier       SizeAndAlignment &blockInfo{eqIter->second};
290084d8bebSJean Perier       minSize = std::max(
291084d8bebSJean Perier           minSize, std::max(offset_, eqIter->first->offset() + blockInfo.size));
292084d8bebSJean Perier       minAlignment = std::max(minAlignment, blockInfo.alignment);
293084d8bebSJean Perier     }
2944ac617f4Speter klausler   }
2954ac617f4Speter klausler   commonBlock.set_size(std::max(minSize, offset_));
2964ac617f4Speter klausler   details.set_alignment(std::max(minAlignment, alignment_));
2972c8cb9acSJean Perier   context_.MapCommonBlockAndCheckConflicts(commonBlock);
2984ac617f4Speter klausler }
2994ac617f4Speter klausler 
3004ac617f4Speter klausler void ComputeOffsetsHelper::DoEquivalenceBlockBase(
3014ac617f4Speter klausler     Symbol &symbol, SizeAndAlignment &blockInfo) {
3024ac617f4Speter klausler   if (symbol.size() > blockInfo.size) {
3034ac617f4Speter klausler     blockInfo.size = symbol.size();
3044ac617f4Speter klausler   }
305237d0e3cSTim Keith }
306237d0e3cSTim Keith 
30731e6cd28Speter klausler void ComputeOffsetsHelper::DoEquivalenceSet(const EquivalenceSet &set) {
308237d0e3cSTim Keith   std::vector<SymbolAndOffset> symbolOffsets;
30931e6cd28Speter klausler   std::optional<std::size_t> representative;
31031e6cd28Speter klausler   for (const EquivalenceObject &object : set) {
311237d0e3cSTim Keith     std::size_t offset{ComputeOffset(object)};
31231e6cd28Speter klausler     SymbolAndOffset resolved{
31331e6cd28Speter klausler         Resolve(SymbolAndOffset{object.symbol, offset, object})};
31431e6cd28Speter klausler     symbolOffsets.push_back(resolved);
31531e6cd28Speter klausler     if (!representative ||
31631e6cd28Speter klausler         resolved.offset >= symbolOffsets[*representative].offset) {
31731e6cd28Speter klausler       // The equivalenced object with the largest offset from its resolved
31831e6cd28Speter klausler       // symbol will be the representative of this set, since the offsets
31931e6cd28Speter klausler       // of the other objects will be positive relative to it.
32031e6cd28Speter klausler       representative = symbolOffsets.size() - 1;
321237d0e3cSTim Keith     }
322237d0e3cSTim Keith   }
32331e6cd28Speter klausler   CHECK(representative);
32431e6cd28Speter klausler   const SymbolAndOffset &base{symbolOffsets[*representative]};
32531e6cd28Speter klausler   for (const auto &[symbol, offset, object] : symbolOffsets) {
32631e6cd28Speter klausler     if (symbol == base.symbol) {
32731e6cd28Speter klausler       if (offset != base.offset) {
32831e6cd28Speter klausler         auto x{evaluate::OffsetToDesignator(
32931e6cd28Speter klausler             context_.foldingContext(), *symbol, base.offset, 1)};
33031e6cd28Speter klausler         auto y{evaluate::OffsetToDesignator(
33131e6cd28Speter klausler             context_.foldingContext(), *symbol, offset, 1)};
33231e6cd28Speter klausler         if (x && y) {
33331e6cd28Speter klausler           context_
33431e6cd28Speter klausler               .Say(base.object->source,
33531e6cd28Speter klausler                   "'%s' and '%s' cannot have the same first storage unit"_err_en_US,
33631e6cd28Speter klausler                   x->AsFortran(), y->AsFortran())
33731e6cd28Speter klausler               .Attach(object->source, "Incompatible reference to '%s'"_en_US,
33831e6cd28Speter klausler                   y->AsFortran());
33931e6cd28Speter klausler         } else { // error recovery
34031e6cd28Speter klausler           context_
34131e6cd28Speter klausler               .Say(base.object->source,
34231e6cd28Speter klausler                   "'%s' (offset %zd bytes and %zd bytes) cannot have the same first storage unit"_err_en_US,
34331e6cd28Speter klausler                   symbol->name(), base.offset, offset)
34431e6cd28Speter klausler               .Attach(object->source,
34531e6cd28Speter klausler                   "Incompatible reference to '%s' offset %zd bytes"_en_US,
34631e6cd28Speter klausler                   symbol->name(), offset);
34731e6cd28Speter klausler         }
34831e6cd28Speter klausler       }
34931e6cd28Speter klausler     } else {
35031e6cd28Speter klausler       dependents_.emplace(*symbol,
35131e6cd28Speter klausler           SymbolAndOffset{*base.symbol, base.offset - offset, *object});
352237d0e3cSTim Keith     }
353237d0e3cSTim Keith   }
354237d0e3cSTim Keith }
355237d0e3cSTim Keith 
356237d0e3cSTim Keith // Offset of this equivalence object from the start of its variable.
357237d0e3cSTim Keith std::size_t ComputeOffsetsHelper::ComputeOffset(
358237d0e3cSTim Keith     const EquivalenceObject &object) {
359237d0e3cSTim Keith   std::size_t offset{0};
360237d0e3cSTim Keith   if (!object.subscripts.empty()) {
361d742c2aaSPeter Klausler     if (const auto *details{object.symbol.detailsIf<ObjectEntityDetails>()}) {
362d742c2aaSPeter Klausler       const ArraySpec &shape{details->shape()};
363237d0e3cSTim Keith       auto lbound{[&](std::size_t i) {
364237d0e3cSTim Keith         return *ToInt64(shape[i].lbound().GetExplicit());
365237d0e3cSTim Keith       }};
366237d0e3cSTim Keith       auto ubound{[&](std::size_t i) {
367237d0e3cSTim Keith         return *ToInt64(shape[i].ubound().GetExplicit());
368237d0e3cSTim Keith       }};
369237d0e3cSTim Keith       for (std::size_t i{object.subscripts.size() - 1};;) {
370237d0e3cSTim Keith         offset += object.subscripts[i] - lbound(i);
371237d0e3cSTim Keith         if (i == 0) {
372237d0e3cSTim Keith           break;
373237d0e3cSTim Keith         }
374237d0e3cSTim Keith         --i;
375237d0e3cSTim Keith         offset *= ubound(i) - lbound(i) + 1;
376237d0e3cSTim Keith       }
377237d0e3cSTim Keith     }
378d742c2aaSPeter Klausler   }
3796aa3591eSpeter klausler   auto result{offset * GetSizeAndAlignment(object.symbol, false).size};
38031e6cd28Speter klausler   if (object.substringStart) {
38131e6cd28Speter klausler     int kind{context_.defaultKinds().GetDefaultKind(TypeCategory::Character)};
38231e6cd28Speter klausler     if (const DeclTypeSpec * type{object.symbol.GetType()}) {
38331e6cd28Speter klausler       if (const IntrinsicTypeSpec * intrinsic{type->AsIntrinsic()}) {
38431e6cd28Speter klausler         kind = ToInt64(intrinsic->kind()).value_or(kind);
38531e6cd28Speter klausler       }
38631e6cd28Speter klausler     }
38731e6cd28Speter klausler     result += kind * (*object.substringStart - 1);
38831e6cd28Speter klausler   }
38931e6cd28Speter klausler   return result;
390237d0e3cSTim Keith }
391237d0e3cSTim Keith 
392*79e788d0SKelvin Li std::size_t ComputeOffsetsHelper::DoSymbol(
393*79e788d0SKelvin Li     Symbol &symbol, std::optional<const size_t> newAlign) {
394044a71d8STim Keith   if (!symbol.has<ObjectEntityDetails>() && !symbol.has<ProcEntityDetails>()) {
395c709f503SPeter Klausler     return 0;
396237d0e3cSTim Keith   }
3976aa3591eSpeter klausler   SizeAndAlignment s{GetSizeAndAlignment(symbol, true)};
398c353ebbfSTim Keith   if (s.size == 0) {
399c709f503SPeter Klausler     return 0;
400c353ebbfSTim Keith   }
401c709f503SPeter Klausler   std::size_t previousOffset{offset_};
402*79e788d0SKelvin Li   size_t alignVal{newAlign.value_or(s.alignment)};
403*79e788d0SKelvin Li   offset_ = Align(offset_, alignVal);
404c709f503SPeter Klausler   std::size_t padding{offset_ - previousOffset};
405c353ebbfSTim Keith   symbol.set_size(s.size);
406c353ebbfSTim Keith   symbol.set_offset(offset_);
407c353ebbfSTim Keith   offset_ += s.size;
408*79e788d0SKelvin Li   alignment_ = std::max(alignment_, alignVal);
409c709f503SPeter Klausler   return padding;
410c353ebbfSTim Keith }
411c353ebbfSTim Keith 
4126aa3591eSpeter klausler auto ComputeOffsetsHelper::GetSizeAndAlignment(
4136aa3591eSpeter klausler     const Symbol &symbol, bool entire) -> SizeAndAlignment {
41423c2bedfSPeter Klausler   auto &targetCharacteristics{context_.targetCharacteristics()};
41523c2bedfSPeter Klausler   if (IsDescriptor(symbol)) {
416392173daSPeter Klausler     auto dyType{evaluate::DynamicType::From(symbol)};
417392173daSPeter Klausler     const auto *derived{evaluate::GetDerivedTypeSpec(dyType)};
418a48e4168Speter klausler     int lenParams{derived ? CountLenParameters(*derived) : 0};
419392173daSPeter Klausler     bool needAddendum{derived || (dyType && dyType->IsUnlimitedPolymorphic())};
420c91ba043SMichael Kruse 
421c91ba043SMichael Kruse     // FIXME: Get descriptor size from targetCharacteristics instead
422c91ba043SMichael Kruse     // overapproximation
423c91ba043SMichael Kruse     std::size_t size{runtime::MaxDescriptorSizeInBytes(
424392173daSPeter Klausler         symbol.Rank(), needAddendum, lenParams)};
425c91ba043SMichael Kruse 
42623c2bedfSPeter Klausler     return {size, targetCharacteristics.descriptorAlignment()};
42723c2bedfSPeter Klausler   }
42823c2bedfSPeter Klausler   if (IsProcedurePointer(symbol)) {
42923c2bedfSPeter Klausler     return {targetCharacteristics.procedurePointerByteSize(),
43023c2bedfSPeter Klausler         targetCharacteristics.procedurePointerAlignment()};
431c353ebbfSTim Keith   }
4322c662f3dSTim Keith   if (IsProcedure(symbol)) {
4332c662f3dSTim Keith     return {};
4342c662f3dSTim Keith   }
43523c2bedfSPeter Klausler   auto &foldingContext{context_.foldingContext()};
4366aa3591eSpeter klausler   if (auto chars{evaluate::characteristics::TypeAndShape::Characterize(
4376aa3591eSpeter klausler           symbol, foldingContext)}) {
4386aa3591eSpeter klausler     if (entire) {
4396aa3591eSpeter klausler       if (auto size{ToInt64(chars->MeasureSizeInBytes(foldingContext))}) {
4406aa3591eSpeter klausler         return {static_cast<std::size_t>(*size),
44123c2bedfSPeter Klausler             chars->type().GetAlignment(targetCharacteristics)};
442c353ebbfSTim Keith       }
4436aa3591eSpeter klausler     } else { // element size only
444efc5926cSpeter klausler       if (auto size{ToInt64(chars->MeasureElementSizeInBytes(
4456aa3591eSpeter klausler               foldingContext, true /*aligned*/))}) {
4466aa3591eSpeter klausler         return {static_cast<std::size_t>(*size),
44723c2bedfSPeter Klausler             chars->type().GetAlignment(targetCharacteristics)};
448c353ebbfSTim Keith       }
449c353ebbfSTim Keith     }
450c353ebbfSTim Keith   }
4516aa3591eSpeter klausler   return {};
452c353ebbfSTim Keith }
453c353ebbfSTim Keith 
454c353ebbfSTim Keith // Align a size to its natural alignment, up to maxAlignment.
455c353ebbfSTim Keith std::size_t ComputeOffsetsHelper::Align(std::size_t x, std::size_t alignment) {
45623c2bedfSPeter Klausler   alignment =
45723c2bedfSPeter Klausler       std::min(alignment, context_.targetCharacteristics().maxAlignment());
458c353ebbfSTim Keith   return (x + alignment - 1) & -alignment;
459c353ebbfSTim Keith }
460c353ebbfSTim Keith 
4616aa3591eSpeter klausler void ComputeOffsets(SemanticsContext &context, Scope &scope) {
4626aa3591eSpeter klausler   ComputeOffsetsHelper{context}.Compute(scope);
463c353ebbfSTim Keith }
464c353ebbfSTim Keith 
465c353ebbfSTim Keith } // namespace Fortran::semantics
466