blob: 99f81940bbbee29e77d9b95f7a0723e1ee171271 [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 &);
Tim Keithc353ebb2020-04-22 22:39:2453 void DoSymbol(Symbol &);
peter klausler6aa35912020-12-15 19:06:4454 SizeAndAlignment GetSizeAndAlignment(const Symbol &, bool entire);
55 std::size_t Align(std::size_t, std::size_t);
Tim Keithc353ebb2020-04-22 22:39:2456
57 SemanticsContext &context_;
Tim Keithc353ebb2020-04-22 22:39:2458 std::size_t offset_{0};
peter klausler6aa35912020-12-15 19:06:4459 std::size_t alignment_{1};
Tim Keith237d0e32020-05-06 18:43:0660 // symbol -> symbol+offset that determines its location, from EQUIVALENCE
peter klausler0d8331c2021-03-18 17:26:2361 std::map<MutableSymbolRef, SymbolAndOffset, SymbolAddressCompare> dependents_;
peter klausler4ac617f2020-08-07 20:25:1162 // base symbol -> SizeAndAlignment for each distinct EQUIVALENCE block
peter klausler0d8331c2021-03-18 17:26:2363 std::map<MutableSymbolRef, SizeAndAlignment, SymbolAddressCompare>
64 equivalenceBlock_;
Tim Keithc353ebb2020-04-22 22:39:2465};
66
67void ComputeOffsetsHelper::Compute(Scope &scope) {
68 for (Scope &child : scope.children()) {
peter klausler6aa35912020-12-15 19:06:4469 ComputeOffsets(context_, child);
Tim Keithc353ebb2020-04-22 22:39:2470 }
Jean Perierf88a9492022-03-03 09:14:0871 if (scope.symbol() && scope.IsKindParameterizedDerivedType()) {
72 return; // only process instantiations of kind parameterized derived types
Tim Keithc353ebb2020-04-22 22:39:2473 }
peter klausler4fede8b2020-12-07 22:46:2474 if (scope.alignment().has_value()) {
75 return; // prevent infinite recursion in error cases
76 }
77 scope.SetAlignment(0);
Tim Keith237d0e32020-05-06 18:43:0678 // Build dependents_ from equivalences: symbol -> symbol+offset
peter klausler31e6cd22020-07-01 18:45:3879 for (const EquivalenceSet &set : scope.equivalenceSets()) {
Tim Keith237d0e32020-05-06 18:43:0680 DoEquivalenceSet(set);
81 }
peter klausler4ac617f2020-08-07 20:25:1182 // Compute a base symbol and overall block size for each
83 // disjoint EQUIVALENCE storage sequence.
84 for (auto &[symbol, dep] : dependents_) {
85 dep = Resolve(dep);
86 CHECK(symbol->size() == 0);
peter klausler6aa35912020-12-15 19:06:4487 auto symInfo{GetSizeAndAlignment(*symbol, true)};
peter klausler4ac617f2020-08-07 20:25:1188 symbol->set_size(symInfo.size);
89 Symbol &base{*dep.symbol};
90 auto iter{equivalenceBlock_.find(base)};
91 std::size_t minBlockSize{dep.offset + symInfo.size};
92 if (iter == equivalenceBlock_.end()) {
93 equivalenceBlock_.emplace(
94 base, SizeAndAlignment{minBlockSize, symInfo.alignment});
95 } else {
96 SizeAndAlignment &blockInfo{iter->second};
97 blockInfo.size = std::max(blockInfo.size, minBlockSize);
98 blockInfo.alignment = std::max(blockInfo.alignment, symInfo.alignment);
Tim Keithc353ebb2020-04-22 22:39:2499 }
100 }
peter klausler4ac617f2020-08-07 20:25:11101 // Assign offsets for non-COMMON EQUIVALENCE blocks
102 for (auto &[symbol, blockInfo] : equivalenceBlock_) {
103 if (!InCommonBlock(*symbol)) {
104 DoSymbol(*symbol);
105 DoEquivalenceBlockBase(*symbol, blockInfo);
106 offset_ = std::max(offset_, symbol->offset() + blockInfo.size);
107 }
108 }
109 // Process remaining non-COMMON symbols; this is all of them if there
110 // was no use of EQUIVALENCE in the scope.
111 for (auto &symbol : scope.GetSymbols()) {
112 if (!InCommonBlock(*symbol) &&
113 dependents_.find(symbol) == dependents_.end() &&
114 equivalenceBlock_.find(symbol) == equivalenceBlock_.end()) {
115 DoSymbol(*symbol);
Tim Keith237d0e32020-05-06 18:43:06116 }
117 }
Tim Keithc353ebb2020-04-22 22:39:24118 scope.set_size(offset_);
peter klausler4fede8b2020-12-07 22:46:24119 scope.SetAlignment(alignment_);
peter klausler4ac617f2020-08-07 20:25:11120 // Assign offsets in COMMON blocks.
121 for (auto &pair : scope.commonBlocks()) {
122 DoCommonBlock(*pair.second);
123 }
124 for (auto &[symbol, dep] : dependents_) {
125 symbol->set_offset(dep.symbol->offset() + dep.offset);
126 if (const auto *block{FindCommonBlockContaining(*dep.symbol)}) {
127 symbol->get<ObjectEntityDetails>().set_commonBlock(*block);
128 }
129 }
Tim Keithc353ebb2020-04-22 22:39:24130}
131
peter klausler31e6cd22020-07-01 18:45:38132auto ComputeOffsetsHelper::Resolve(const SymbolAndOffset &dep)
133 -> SymbolAndOffset {
Tim Keith237d0e32020-05-06 18:43:06134 auto it{dependents_.find(*dep.symbol)};
135 if (it == dependents_.end()) {
peter klausler31e6cd22020-07-01 18:45:38136 return dep;
Tim Keith237d0e32020-05-06 18:43:06137 } else {
peter klausler31e6cd22020-07-01 18:45:38138 SymbolAndOffset result{Resolve(it->second)};
139 result.offset += dep.offset;
140 result.object = dep.object;
141 return result;
Tim Keith237d0e32020-05-06 18:43:06142 }
143}
144
145void ComputeOffsetsHelper::DoCommonBlock(Symbol &commonBlock) {
146 auto &details{commonBlock.get<CommonBlockDetails>()};
147 offset_ = 0;
Tim Keith54b35c062020-05-06 22:00:14148 alignment_ = 0;
peter klausler4ac617f2020-08-07 20:25:11149 std::size_t minSize{0};
150 std::size_t minAlignment{0};
Tim Keith237d0e32020-05-06 18:43:06151 for (auto &object : details.objects()) {
peter klausler4ac617f2020-08-07 20:25:11152 Symbol &symbol{*object};
153 DoSymbol(symbol);
Jean Perier084d8be2021-09-03 06:12:44154 auto eqIter{equivalenceBlock_.end()};
peter klausler4ac617f2020-08-07 20:25:11155 auto iter{dependents_.find(symbol)};
156 if (iter == dependents_.end()) {
Jean Perierd892d732021-09-09 07:11:49157 eqIter = equivalenceBlock_.find(symbol);
peter klausler4ac617f2020-08-07 20:25:11158 if (eqIter != equivalenceBlock_.end()) {
Jean Perier084d8be2021-09-03 06:12:44159 DoEquivalenceBlockBase(symbol, eqIter->second);
peter klausler4ac617f2020-08-07 20:25:11160 }
161 } else {
162 SymbolAndOffset &dep{iter->second};
163 Symbol &base{*dep.symbol};
164 auto errorSite{
165 commonBlock.name().empty() ? symbol.name() : commonBlock.name()};
166 if (const auto *baseBlock{FindCommonBlockContaining(base)}) {
167 if (baseBlock == &commonBlock) {
168 context_.Say(errorSite,
169 "'%s' is storage associated with '%s' by EQUIVALENCE elsewhere in COMMON block /%s/"_err_en_US,
170 symbol.name(), base.name(), commonBlock.name());
171 } else { // 8.10.3(1)
172 context_.Say(errorSite,
173 "'%s' in COMMON block /%s/ must not be storage associated with '%s' in COMMON block /%s/ by EQUIVALENCE"_err_en_US,
174 symbol.name(), commonBlock.name(), base.name(),
175 baseBlock->name());
176 }
177 } else if (dep.offset > symbol.offset()) { // 8.10.3(3)
178 context_.Say(errorSite,
179 "'%s' cannot backward-extend COMMON block /%s/ via EQUIVALENCE with '%s'"_err_en_US,
180 symbol.name(), commonBlock.name(), base.name());
181 } else {
Jean Perier084d8be2021-09-03 06:12:44182 eqIter = equivalenceBlock_.find(base);
peter klausler4ac617f2020-08-07 20:25:11183 base.get<ObjectEntityDetails>().set_commonBlock(commonBlock);
184 base.set_offset(symbol.offset() - dep.offset);
185 }
186 }
Jean Perier084d8be2021-09-03 06:12:44187 // Get full extent of any EQUIVALENCE block into size of COMMON ( see
188 // 8.10.2.2 point 1 (2))
189 if (eqIter != equivalenceBlock_.end()) {
190 SizeAndAlignment &blockInfo{eqIter->second};
191 minSize = std::max(
192 minSize, std::max(offset_, eqIter->first->offset() + blockInfo.size));
193 minAlignment = std::max(minAlignment, blockInfo.alignment);
194 }
Tim Keith237d0e32020-05-06 18:43:06195 }
peter klausler4ac617f2020-08-07 20:25:11196 commonBlock.set_size(std::max(minSize, offset_));
197 details.set_alignment(std::max(minAlignment, alignment_));
198}
199
200void ComputeOffsetsHelper::DoEquivalenceBlockBase(
201 Symbol &symbol, SizeAndAlignment &blockInfo) {
202 if (symbol.size() > blockInfo.size) {
203 blockInfo.size = symbol.size();
204 }
Tim Keith237d0e32020-05-06 18:43:06205}
206
peter klausler31e6cd22020-07-01 18:45:38207void ComputeOffsetsHelper::DoEquivalenceSet(const EquivalenceSet &set) {
Tim Keith237d0e32020-05-06 18:43:06208 std::vector<SymbolAndOffset> symbolOffsets;
peter klausler31e6cd22020-07-01 18:45:38209 std::optional<std::size_t> representative;
210 for (const EquivalenceObject &object : set) {
Tim Keith237d0e32020-05-06 18:43:06211 std::size_t offset{ComputeOffset(object)};
peter klausler31e6cd22020-07-01 18:45:38212 SymbolAndOffset resolved{
213 Resolve(SymbolAndOffset{object.symbol, offset, object})};
214 symbolOffsets.push_back(resolved);
215 if (!representative ||
216 resolved.offset >= symbolOffsets[*representative].offset) {
217 // The equivalenced object with the largest offset from its resolved
218 // symbol will be the representative of this set, since the offsets
219 // of the other objects will be positive relative to it.
220 representative = symbolOffsets.size() - 1;
Tim Keith237d0e32020-05-06 18:43:06221 }
222 }
peter klausler31e6cd22020-07-01 18:45:38223 CHECK(representative);
224 const SymbolAndOffset &base{symbolOffsets[*representative]};
225 for (const auto &[symbol, offset, object] : symbolOffsets) {
226 if (symbol == base.symbol) {
227 if (offset != base.offset) {
228 auto x{evaluate::OffsetToDesignator(
229 context_.foldingContext(), *symbol, base.offset, 1)};
230 auto y{evaluate::OffsetToDesignator(
231 context_.foldingContext(), *symbol, offset, 1)};
232 if (x && y) {
233 context_
234 .Say(base.object->source,
235 "'%s' and '%s' cannot have the same first storage unit"_err_en_US,
236 x->AsFortran(), y->AsFortran())
237 .Attach(object->source, "Incompatible reference to '%s'"_en_US,
238 y->AsFortran());
239 } else { // error recovery
240 context_
241 .Say(base.object->source,
242 "'%s' (offset %zd bytes and %zd bytes) cannot have the same first storage unit"_err_en_US,
243 symbol->name(), base.offset, offset)
244 .Attach(object->source,
245 "Incompatible reference to '%s' offset %zd bytes"_en_US,
246 symbol->name(), offset);
247 }
248 }
249 } else {
250 dependents_.emplace(*symbol,
251 SymbolAndOffset{*base.symbol, base.offset - offset, *object});
Tim Keith237d0e32020-05-06 18:43:06252 }
253 }
254}
255
256// Offset of this equivalence object from the start of its variable.
257std::size_t ComputeOffsetsHelper::ComputeOffset(
258 const EquivalenceObject &object) {
259 std::size_t offset{0};
Tim Keith237d0e32020-05-06 18:43:06260 if (!object.subscripts.empty()) {
261 const ArraySpec &shape{object.symbol.get<ObjectEntityDetails>().shape()};
262 auto lbound{[&](std::size_t i) {
263 return *ToInt64(shape[i].lbound().GetExplicit());
264 }};
265 auto ubound{[&](std::size_t i) {
266 return *ToInt64(shape[i].ubound().GetExplicit());
267 }};
268 for (std::size_t i{object.subscripts.size() - 1};;) {
269 offset += object.subscripts[i] - lbound(i);
270 if (i == 0) {
271 break;
272 }
273 --i;
274 offset *= ubound(i) - lbound(i) + 1;
275 }
276 }
peter klausler6aa35912020-12-15 19:06:44277 auto result{offset * GetSizeAndAlignment(object.symbol, false).size};
peter klausler31e6cd22020-07-01 18:45:38278 if (object.substringStart) {
279 int kind{context_.defaultKinds().GetDefaultKind(TypeCategory::Character)};
280 if (const DeclTypeSpec * type{object.symbol.GetType()}) {
281 if (const IntrinsicTypeSpec * intrinsic{type->AsIntrinsic()}) {
282 kind = ToInt64(intrinsic->kind()).value_or(kind);
283 }
284 }
285 result += kind * (*object.substringStart - 1);
286 }
287 return result;
Tim Keith237d0e32020-05-06 18:43:06288}
289
Tim Keithc353ebb2020-04-22 22:39:24290void ComputeOffsetsHelper::DoSymbol(Symbol &symbol) {
Tim Keith044a71d2020-08-25 01:20:22291 if (!symbol.has<ObjectEntityDetails>() && !symbol.has<ProcEntityDetails>()) {
292 return;
Tim Keith237d0e32020-05-06 18:43:06293 }
peter klausler6aa35912020-12-15 19:06:44294 SizeAndAlignment s{GetSizeAndAlignment(symbol, true)};
Tim Keithc353ebb2020-04-22 22:39:24295 if (s.size == 0) {
296 return;
297 }
Tim Keith54b35c062020-05-06 22:00:14298 offset_ = Align(offset_, s.alignment);
Tim Keithc353ebb2020-04-22 22:39:24299 symbol.set_size(s.size);
300 symbol.set_offset(offset_);
301 offset_ += s.size;
Tim Keith54b35c062020-05-06 22:00:14302 alignment_ = std::max(alignment_, s.alignment);
Tim Keithc353ebb2020-04-22 22:39:24303}
304
peter klausler6aa35912020-12-15 19:06:44305auto ComputeOffsetsHelper::GetSizeAndAlignment(
306 const Symbol &symbol, bool entire) -> SizeAndAlignment {
peter klauslerd1862eb2020-06-18 20:28:43307 // TODO: The size of procedure pointers is not yet known
308 // and is independent of rank (and probably also the number
309 // of length type parameters).
peter klausler6aa35912020-12-15 19:06:44310 auto &foldingContext{context_.foldingContext()};
Tim Keith2c662f32020-07-29 14:23:28311 if (IsDescriptor(symbol) || IsProcedurePointer(symbol)) {
peter klauslera48e4162021-07-19 18:53:20312 const auto *derived{
313 evaluate::GetDerivedTypeSpec(evaluate::DynamicType::From(symbol))};
314 int lenParams{derived ? CountLenParameters(*derived) : 0};
315 std::size_t size{runtime::Descriptor::SizeInBytes(
316 symbol.Rank(), derived != nullptr, lenParams)};
peter klausler6aa35912020-12-15 19:06:44317 return {size, foldingContext.maxAlignment()};
Tim Keithc353ebb2020-04-22 22:39:24318 }
Tim Keith2c662f32020-07-29 14:23:28319 if (IsProcedure(symbol)) {
320 return {};
321 }
peter klausler6aa35912020-12-15 19:06:44322 if (auto chars{evaluate::characteristics::TypeAndShape::Characterize(
323 symbol, foldingContext)}) {
324 if (entire) {
325 if (auto size{ToInt64(chars->MeasureSizeInBytes(foldingContext))}) {
326 return {static_cast<std::size_t>(*size),
327 chars->type().GetAlignment(foldingContext)};
Tim Keithc353ebb2020-04-22 22:39:24328 }
peter klausler6aa35912020-12-15 19:06:44329 } else { // element size only
peter klauslerefc59262021-02-02 22:39:05330 if (auto size{ToInt64(chars->MeasureElementSizeInBytes(
peter klausler6aa35912020-12-15 19:06:44331 foldingContext, true /*aligned*/))}) {
332 return {static_cast<std::size_t>(*size),
333 chars->type().GetAlignment(foldingContext)};
Tim Keithc353ebb2020-04-22 22:39:24334 }
335 }
336 }
peter klausler6aa35912020-12-15 19:06:44337 return {};
Tim Keithc353ebb2020-04-22 22:39:24338}
339
340// Align a size to its natural alignment, up to maxAlignment.
341std::size_t ComputeOffsetsHelper::Align(std::size_t x, std::size_t alignment) {
peter klausler6aa35912020-12-15 19:06:44342 alignment = std::min(alignment, context_.foldingContext().maxAlignment());
Tim Keithc353ebb2020-04-22 22:39:24343 return (x + alignment - 1) & -alignment;
344}
345
peter klausler6aa35912020-12-15 19:06:44346void ComputeOffsets(SemanticsContext &context, Scope &scope) {
347 ComputeOffsetsHelper{context}.Compute(scope);
Tim Keithc353ebb2020-04-22 22:39:24348}
349
350} // namespace Fortran::semantics