blob: 82b4eeff390a37ba49388b552cf30287b909ee71 [file] [log] [blame]
peter klauslerd1862eb2020-06-18 20:28:431//===-- lib/Semantics/compute-offsets.cpp -----------------------*- C++ -*-===//
Tim Keithc353ebb2020-04-22 22:39:242//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "compute-offsets.h"
peter klausler31e6cd22020-07-01 18:45:3810#include "flang/Evaluate/fold-designator.h"
Tim Keithc353ebb2020-04-22 22:39:2411#include "flang/Evaluate/fold.h"
12#include "flang/Evaluate/shape.h"
13#include "flang/Evaluate/type.h"
Peter Klausler830c0b92021-09-01 23:00:5314#include "flang/Runtime/descriptor.h"
Tim Keithc353ebb2020-04-22 22:39:2415#include "flang/Semantics/scope.h"
16#include "flang/Semantics/semantics.h"
17#include "flang/Semantics/symbol.h"
18#include "flang/Semantics/tools.h"
19#include "flang/Semantics/type.h"
20#include <algorithm>
21#include <vector>
22
23namespace Fortran::semantics {
24
25class ComputeOffsetsHelper {
26public:
Tim Keithc353ebb2020-04-22 22:39:2427 ComputeOffsetsHelper(SemanticsContext &context) : context_{context} {}
peter klausler6aa35912020-12-15 19:06:4428 void Compute(Scope &);
Tim Keithc353ebb2020-04-22 22:39:2429
30private:
Tim Keith54b35c062020-05-06 22:00:1431 struct SizeAndAlignment {
32 SizeAndAlignment() {}
peter klauslerd1862eb2020-06-18 20:28:4333 SizeAndAlignment(std::size_t bytes) : size{bytes}, alignment{bytes} {}
34 SizeAndAlignment(std::size_t bytes, std::size_t align)
35 : size{bytes}, alignment{align} {}
Tim Keithc353ebb2020-04-22 22:39:2436 std::size_t size{0};
Tim Keith54b35c062020-05-06 22:00:1437 std::size_t alignment{0};
Tim Keithc353ebb2020-04-22 22:39:2438 };
Tim Keith237d0e32020-05-06 18:43:0639 struct SymbolAndOffset {
peter klausler31e6cd22020-07-01 18:45:3840 SymbolAndOffset(Symbol &s, std::size_t off, const EquivalenceObject &obj)
peter klauslerd60a0222021-08-10 17:22:3941 : symbol{s}, offset{off}, object{&obj} {}
peter klausler31e6cd22020-07-01 18:45:3842 SymbolAndOffset(const SymbolAndOffset &) = default;
peter klauslerd60a0222021-08-10 17:22:3943 MutableSymbolRef symbol;
peter klausler31e6cd22020-07-01 18:45:3844 std::size_t offset;
45 const EquivalenceObject *object;
Tim Keith237d0e32020-05-06 18:43:0646 };
Tim Keithc353ebb2020-04-22 22:39:2447
Tim Keith237d0e32020-05-06 18:43:0648 void DoCommonBlock(Symbol &);
peter klausler4ac617f2020-08-07 20:25:1149 void DoEquivalenceBlockBase(Symbol &, SizeAndAlignment &);
peter klausler31e6cd22020-07-01 18:45:3850 void DoEquivalenceSet(const EquivalenceSet &);
51 SymbolAndOffset Resolve(const SymbolAndOffset &);
Tim Keith237d0e32020-05-06 18:43:0652 std::size_t ComputeOffset(const EquivalenceObject &);
Peter Klauslerc709f502022-03-31 19:55:4553 // Returns amount of padding that was needed for alignment
54 std::size_t DoSymbol(Symbol &);
peter klausler6aa35912020-12-15 19:06:4455 SizeAndAlignment GetSizeAndAlignment(const Symbol &, bool entire);
56 std::size_t Align(std::size_t, std::size_t);
Tim Keithc353ebb2020-04-22 22:39:2457
58 SemanticsContext &context_;
Tim Keithc353ebb2020-04-22 22:39:2459 std::size_t offset_{0};
peter klausler6aa35912020-12-15 19:06:4460 std::size_t alignment_{1};
Tim Keith237d0e32020-05-06 18:43:0661 // symbol -> symbol+offset that determines its location, from EQUIVALENCE
peter klausler0d8331c2021-03-18 17:26:2362 std::map<MutableSymbolRef, SymbolAndOffset, SymbolAddressCompare> dependents_;
peter klausler4ac617f2020-08-07 20:25:1163 // base symbol -> SizeAndAlignment for each distinct EQUIVALENCE block
peter klausler0d8331c2021-03-18 17:26:2364 std::map<MutableSymbolRef, SizeAndAlignment, SymbolAddressCompare>
65 equivalenceBlock_;
Tim Keithc353ebb2020-04-22 22:39:2466};
67
68void ComputeOffsetsHelper::Compute(Scope &scope) {
69 for (Scope &child : scope.children()) {
peter klausler6aa35912020-12-15 19:06:4470 ComputeOffsets(context_, child);
Tim Keithc353ebb2020-04-22 22:39:2471 }
Jean Perierf2da8f52022-03-11 18:03:1972 if (scope.symbol() && scope.IsDerivedTypeWithKindParameter()) {
Jean Perierf88a9492022-03-03 09:14:0873 return; // only process instantiations of kind parameterized derived types
Tim Keithc353ebb2020-04-22 22:39:2474 }
peter klausler4fede8b2020-12-07 22:46:2475 if (scope.alignment().has_value()) {
76 return; // prevent infinite recursion in error cases
77 }
78 scope.SetAlignment(0);
Tim Keith237d0e32020-05-06 18:43:0679 // Build dependents_ from equivalences: symbol -> symbol+offset
peter klausler31e6cd22020-07-01 18:45:3880 for (const EquivalenceSet &set : scope.equivalenceSets()) {
Tim Keith237d0e32020-05-06 18:43:0681 DoEquivalenceSet(set);
82 }
peter klausler4ac617f2020-08-07 20:25:1183 // Compute a base symbol and overall block size for each
84 // disjoint EQUIVALENCE storage sequence.
85 for (auto &[symbol, dep] : dependents_) {
86 dep = Resolve(dep);
87 CHECK(symbol->size() == 0);
peter klausler6aa35912020-12-15 19:06:4488 auto symInfo{GetSizeAndAlignment(*symbol, true)};
peter klausler4ac617f2020-08-07 20:25:1189 symbol->set_size(symInfo.size);
90 Symbol &base{*dep.symbol};
91 auto iter{equivalenceBlock_.find(base)};
92 std::size_t minBlockSize{dep.offset + symInfo.size};
93 if (iter == equivalenceBlock_.end()) {
94 equivalenceBlock_.emplace(
95 base, SizeAndAlignment{minBlockSize, symInfo.alignment});
96 } else {
97 SizeAndAlignment &blockInfo{iter->second};
98 blockInfo.size = std::max(blockInfo.size, minBlockSize);
99 blockInfo.alignment = std::max(blockInfo.alignment, symInfo.alignment);
Tim Keithc353ebb2020-04-22 22:39:24100 }
101 }
peter klausler4ac617f2020-08-07 20:25:11102 // Assign offsets for non-COMMON EQUIVALENCE blocks
103 for (auto &[symbol, blockInfo] : equivalenceBlock_) {
104 if (!InCommonBlock(*symbol)) {
105 DoSymbol(*symbol);
106 DoEquivalenceBlockBase(*symbol, blockInfo);
107 offset_ = std::max(offset_, symbol->offset() + blockInfo.size);
108 }
109 }
110 // Process remaining non-COMMON symbols; this is all of them if there
111 // was no use of EQUIVALENCE in the scope.
112 for (auto &symbol : scope.GetSymbols()) {
113 if (!InCommonBlock(*symbol) &&
114 dependents_.find(symbol) == dependents_.end() &&
115 equivalenceBlock_.find(symbol) == equivalenceBlock_.end()) {
116 DoSymbol(*symbol);
Tim Keith237d0e32020-05-06 18:43:06117 }
118 }
Tim Keithc353ebb2020-04-22 22:39:24119 scope.set_size(offset_);
peter klausler4fede8b2020-12-07 22:46:24120 scope.SetAlignment(alignment_);
peter klausler4ac617f2020-08-07 20:25:11121 // Assign offsets in COMMON blocks.
122 for (auto &pair : scope.commonBlocks()) {
123 DoCommonBlock(*pair.second);
124 }
125 for (auto &[symbol, dep] : dependents_) {
126 symbol->set_offset(dep.symbol->offset() + dep.offset);
127 if (const auto *block{FindCommonBlockContaining(*dep.symbol)}) {
128 symbol->get<ObjectEntityDetails>().set_commonBlock(*block);
129 }
130 }
Tim Keithc353ebb2020-04-22 22:39:24131}
132
peter klausler31e6cd22020-07-01 18:45:38133auto ComputeOffsetsHelper::Resolve(const SymbolAndOffset &dep)
134 -> SymbolAndOffset {
Tim Keith237d0e32020-05-06 18:43:06135 auto it{dependents_.find(*dep.symbol)};
136 if (it == dependents_.end()) {
peter klausler31e6cd22020-07-01 18:45:38137 return dep;
Tim Keith237d0e32020-05-06 18:43:06138 } else {
peter klausler31e6cd22020-07-01 18:45:38139 SymbolAndOffset result{Resolve(it->second)};
140 result.offset += dep.offset;
141 result.object = dep.object;
142 return result;
Tim Keith237d0e32020-05-06 18:43:06143 }
144}
145
146void ComputeOffsetsHelper::DoCommonBlock(Symbol &commonBlock) {
147 auto &details{commonBlock.get<CommonBlockDetails>()};
148 offset_ = 0;
Tim Keith54b35c062020-05-06 22:00:14149 alignment_ = 0;
peter klausler4ac617f2020-08-07 20:25:11150 std::size_t minSize{0};
151 std::size_t minAlignment{0};
Tim Keith237d0e32020-05-06 18:43:06152 for (auto &object : details.objects()) {
peter klausler4ac617f2020-08-07 20:25:11153 Symbol &symbol{*object};
Peter Klauslerc709f502022-03-31 19:55:45154 auto errorSite{
155 commonBlock.name().empty() ? symbol.name() : commonBlock.name()};
156 if (std::size_t padding{DoSymbol(symbol)}) {
157 context_.Say(errorSite,
158 "COMMON block /%s/ requires %zd bytes of padding before '%s' for alignment"_port_en_US,
159 commonBlock.name(), padding, symbol.name());
160 }
Jean Perier084d8be2021-09-03 06:12:44161 auto eqIter{equivalenceBlock_.end()};
peter klausler4ac617f2020-08-07 20:25:11162 auto iter{dependents_.find(symbol)};
163 if (iter == dependents_.end()) {
Jean Perierd892d732021-09-09 07:11:49164 eqIter = equivalenceBlock_.find(symbol);
peter klausler4ac617f2020-08-07 20:25:11165 if (eqIter != equivalenceBlock_.end()) {
Jean Perier084d8be2021-09-03 06:12:44166 DoEquivalenceBlockBase(symbol, eqIter->second);
peter klausler4ac617f2020-08-07 20:25:11167 }
168 } else {
169 SymbolAndOffset &dep{iter->second};
170 Symbol &base{*dep.symbol};
peter klausler4ac617f2020-08-07 20:25:11171 if (const auto *baseBlock{FindCommonBlockContaining(base)}) {
172 if (baseBlock == &commonBlock) {
173 context_.Say(errorSite,
174 "'%s' is storage associated with '%s' by EQUIVALENCE elsewhere in COMMON block /%s/"_err_en_US,
175 symbol.name(), base.name(), commonBlock.name());
176 } else { // 8.10.3(1)
177 context_.Say(errorSite,
178 "'%s' in COMMON block /%s/ must not be storage associated with '%s' in COMMON block /%s/ by EQUIVALENCE"_err_en_US,
179 symbol.name(), commonBlock.name(), base.name(),
180 baseBlock->name());
181 }
182 } else if (dep.offset > symbol.offset()) { // 8.10.3(3)
183 context_.Say(errorSite,
184 "'%s' cannot backward-extend COMMON block /%s/ via EQUIVALENCE with '%s'"_err_en_US,
185 symbol.name(), commonBlock.name(), base.name());
186 } else {
Jean Perier084d8be2021-09-03 06:12:44187 eqIter = equivalenceBlock_.find(base);
peter klausler4ac617f2020-08-07 20:25:11188 base.get<ObjectEntityDetails>().set_commonBlock(commonBlock);
189 base.set_offset(symbol.offset() - dep.offset);
190 }
191 }
Jean Perier084d8be2021-09-03 06:12:44192 // Get full extent of any EQUIVALENCE block into size of COMMON ( see
193 // 8.10.2.2 point 1 (2))
194 if (eqIter != equivalenceBlock_.end()) {
195 SizeAndAlignment &blockInfo{eqIter->second};
196 minSize = std::max(
197 minSize, std::max(offset_, eqIter->first->offset() + blockInfo.size));
198 minAlignment = std::max(minAlignment, blockInfo.alignment);
199 }
Tim Keith237d0e32020-05-06 18:43:06200 }
peter klausler4ac617f2020-08-07 20:25:11201 commonBlock.set_size(std::max(minSize, offset_));
202 details.set_alignment(std::max(minAlignment, alignment_));
Jean Perier2c8cb9a2022-04-29 12:52:27203 context_.MapCommonBlockAndCheckConflicts(commonBlock);
peter klausler4ac617f2020-08-07 20:25:11204}
205
206void ComputeOffsetsHelper::DoEquivalenceBlockBase(
207 Symbol &symbol, SizeAndAlignment &blockInfo) {
208 if (symbol.size() > blockInfo.size) {
209 blockInfo.size = symbol.size();
210 }
Tim Keith237d0e32020-05-06 18:43:06211}
212
peter klausler31e6cd22020-07-01 18:45:38213void ComputeOffsetsHelper::DoEquivalenceSet(const EquivalenceSet &set) {
Tim Keith237d0e32020-05-06 18:43:06214 std::vector<SymbolAndOffset> symbolOffsets;
peter klausler31e6cd22020-07-01 18:45:38215 std::optional<std::size_t> representative;
216 for (const EquivalenceObject &object : set) {
Tim Keith237d0e32020-05-06 18:43:06217 std::size_t offset{ComputeOffset(object)};
peter klausler31e6cd22020-07-01 18:45:38218 SymbolAndOffset resolved{
219 Resolve(SymbolAndOffset{object.symbol, offset, object})};
220 symbolOffsets.push_back(resolved);
221 if (!representative ||
222 resolved.offset >= symbolOffsets[*representative].offset) {
223 // The equivalenced object with the largest offset from its resolved
224 // symbol will be the representative of this set, since the offsets
225 // of the other objects will be positive relative to it.
226 representative = symbolOffsets.size() - 1;
Tim Keith237d0e32020-05-06 18:43:06227 }
228 }
peter klausler31e6cd22020-07-01 18:45:38229 CHECK(representative);
230 const SymbolAndOffset &base{symbolOffsets[*representative]};
231 for (const auto &[symbol, offset, object] : symbolOffsets) {
232 if (symbol == base.symbol) {
233 if (offset != base.offset) {
234 auto x{evaluate::OffsetToDesignator(
235 context_.foldingContext(), *symbol, base.offset, 1)};
236 auto y{evaluate::OffsetToDesignator(
237 context_.foldingContext(), *symbol, offset, 1)};
238 if (x && y) {
239 context_
240 .Say(base.object->source,
241 "'%s' and '%s' cannot have the same first storage unit"_err_en_US,
242 x->AsFortran(), y->AsFortran())
243 .Attach(object->source, "Incompatible reference to '%s'"_en_US,
244 y->AsFortran());
245 } else { // error recovery
246 context_
247 .Say(base.object->source,
248 "'%s' (offset %zd bytes and %zd bytes) cannot have the same first storage unit"_err_en_US,
249 symbol->name(), base.offset, offset)
250 .Attach(object->source,
251 "Incompatible reference to '%s' offset %zd bytes"_en_US,
252 symbol->name(), offset);
253 }
254 }
255 } else {
256 dependents_.emplace(*symbol,
257 SymbolAndOffset{*base.symbol, base.offset - offset, *object});
Tim Keith237d0e32020-05-06 18:43:06258 }
259 }
260}
261
262// Offset of this equivalence object from the start of its variable.
263std::size_t ComputeOffsetsHelper::ComputeOffset(
264 const EquivalenceObject &object) {
265 std::size_t offset{0};
Tim Keith237d0e32020-05-06 18:43:06266 if (!object.subscripts.empty()) {
267 const ArraySpec &shape{object.symbol.get<ObjectEntityDetails>().shape()};
268 auto lbound{[&](std::size_t i) {
269 return *ToInt64(shape[i].lbound().GetExplicit());
270 }};
271 auto ubound{[&](std::size_t i) {
272 return *ToInt64(shape[i].ubound().GetExplicit());
273 }};
274 for (std::size_t i{object.subscripts.size() - 1};;) {
275 offset += object.subscripts[i] - lbound(i);
276 if (i == 0) {
277 break;
278 }
279 --i;
280 offset *= ubound(i) - lbound(i) + 1;
281 }
282 }
peter klausler6aa35912020-12-15 19:06:44283 auto result{offset * GetSizeAndAlignment(object.symbol, false).size};
peter klausler31e6cd22020-07-01 18:45:38284 if (object.substringStart) {
285 int kind{context_.defaultKinds().GetDefaultKind(TypeCategory::Character)};
286 if (const DeclTypeSpec * type{object.symbol.GetType()}) {
287 if (const IntrinsicTypeSpec * intrinsic{type->AsIntrinsic()}) {
288 kind = ToInt64(intrinsic->kind()).value_or(kind);
289 }
290 }
291 result += kind * (*object.substringStart - 1);
292 }
293 return result;
Tim Keith237d0e32020-05-06 18:43:06294}
295
Peter Klauslerc709f502022-03-31 19:55:45296std::size_t ComputeOffsetsHelper::DoSymbol(Symbol &symbol) {
Tim Keith044a71d2020-08-25 01:20:22297 if (!symbol.has<ObjectEntityDetails>() && !symbol.has<ProcEntityDetails>()) {
Peter Klauslerc709f502022-03-31 19:55:45298 return 0;
Tim Keith237d0e32020-05-06 18:43:06299 }
peter klausler6aa35912020-12-15 19:06:44300 SizeAndAlignment s{GetSizeAndAlignment(symbol, true)};
Tim Keithc353ebb2020-04-22 22:39:24301 if (s.size == 0) {
Peter Klauslerc709f502022-03-31 19:55:45302 return 0;
Tim Keithc353ebb2020-04-22 22:39:24303 }
Peter Klauslerc709f502022-03-31 19:55:45304 std::size_t previousOffset{offset_};
Tim Keith54b35c062020-05-06 22:00:14305 offset_ = Align(offset_, s.alignment);
Peter Klauslerc709f502022-03-31 19:55:45306 std::size_t padding{offset_ - previousOffset};
Tim Keithc353ebb2020-04-22 22:39:24307 symbol.set_size(s.size);
308 symbol.set_offset(offset_);
309 offset_ += s.size;
Tim Keith54b35c062020-05-06 22:00:14310 alignment_ = std::max(alignment_, s.alignment);
Peter Klauslerc709f502022-03-31 19:55:45311 return padding;
Tim Keithc353ebb2020-04-22 22:39:24312}
313
peter klausler6aa35912020-12-15 19:06:44314auto ComputeOffsetsHelper::GetSizeAndAlignment(
315 const Symbol &symbol, bool entire) -> SizeAndAlignment {
peter klauslerd1862eb2020-06-18 20:28:43316 // TODO: The size of procedure pointers is not yet known
317 // and is independent of rank (and probably also the number
318 // of length type parameters).
peter klausler6aa35912020-12-15 19:06:44319 auto &foldingContext{context_.foldingContext()};
Tim Keith2c662f32020-07-29 14:23:28320 if (IsDescriptor(symbol) || IsProcedurePointer(symbol)) {
peter klauslera48e4162021-07-19 18:53:20321 const auto *derived{
322 evaluate::GetDerivedTypeSpec(evaluate::DynamicType::From(symbol))};
323 int lenParams{derived ? CountLenParameters(*derived) : 0};
324 std::size_t size{runtime::Descriptor::SizeInBytes(
325 symbol.Rank(), derived != nullptr, lenParams)};
peter klausler6aa35912020-12-15 19:06:44326 return {size, foldingContext.maxAlignment()};
Tim Keithc353ebb2020-04-22 22:39:24327 }
Tim Keith2c662f32020-07-29 14:23:28328 if (IsProcedure(symbol)) {
329 return {};
330 }
peter klausler6aa35912020-12-15 19:06:44331 if (auto chars{evaluate::characteristics::TypeAndShape::Characterize(
332 symbol, foldingContext)}) {
333 if (entire) {
334 if (auto size{ToInt64(chars->MeasureSizeInBytes(foldingContext))}) {
335 return {static_cast<std::size_t>(*size),
336 chars->type().GetAlignment(foldingContext)};
Tim Keithc353ebb2020-04-22 22:39:24337 }
peter klausler6aa35912020-12-15 19:06:44338 } else { // element size only
peter klauslerefc59262021-02-02 22:39:05339 if (auto size{ToInt64(chars->MeasureElementSizeInBytes(
peter klausler6aa35912020-12-15 19:06:44340 foldingContext, true /*aligned*/))}) {
341 return {static_cast<std::size_t>(*size),
342 chars->type().GetAlignment(foldingContext)};
Tim Keithc353ebb2020-04-22 22:39:24343 }
344 }
345 }
peter klausler6aa35912020-12-15 19:06:44346 return {};
Tim Keithc353ebb2020-04-22 22:39:24347}
348
349// Align a size to its natural alignment, up to maxAlignment.
350std::size_t ComputeOffsetsHelper::Align(std::size_t x, std::size_t alignment) {
peter klausler6aa35912020-12-15 19:06:44351 alignment = std::min(alignment, context_.foldingContext().maxAlignment());
Tim Keithc353ebb2020-04-22 22:39:24352 return (x + alignment - 1) & -alignment;
353}
354
peter klausler6aa35912020-12-15 19:06:44355void ComputeOffsets(SemanticsContext &context, Scope &scope) {
356 ComputeOffsetsHelper{context}.Compute(scope);
Tim Keithc353ebb2020-04-22 22:39:24357}
358
359} // namespace Fortran::semantics