1 //===----------------------------------------------------------------------===//
2 //
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"
10 #include "../../runtime/descriptor.h"
11 #include "flang/Evaluate/fold.h"
12 #include "flang/Evaluate/shape.h"
13 #include "flang/Evaluate/type.h"
14 #include "flang/Semantics/scope.h"
15 #include "flang/Semantics/semantics.h"
16 #include "flang/Semantics/symbol.h"
17 #include "flang/Semantics/tools.h"
18 #include "flang/Semantics/type.h"
19 #include <algorithm>
20 #include <vector>
21 
22 namespace Fortran::semantics {
23 
24 class ComputeOffsetsHelper {
25 public:
26   // TODO: configure based on target
27   static constexpr int descriptorSize{3 * 8};
28   static constexpr int maxAlignment{8};
29 
30   ComputeOffsetsHelper(SemanticsContext &context) : context_{context} {}
31   void Compute() { Compute(context_.globalScope()); }
32 
33 private:
34   struct SizeAndAlign {
35     SizeAndAlign() {}
36     SizeAndAlign(std::size_t size) : size{size}, align{size} {}
37     SizeAndAlign(std::size_t size, std::size_t align)
38         : size{size}, align{align} {}
39     std::size_t size{0};
40     std::size_t align{0};
41   };
42 
43   void Compute(Scope &);
44   void DoScope(Scope &);
45   void DoSymbol(Symbol &);
46   SizeAndAlign GetSizeAndAlign(const Symbol &);
47   std::size_t CountElements(const Symbol &);
48   static std::size_t Align(std::size_t, std::size_t);
49   static SizeAndAlign GetIntrinsicSizeAndAlign(TypeCategory, int);
50 
51   SemanticsContext &context_;
52   evaluate::FoldingContext &foldingContext_{context_.foldingContext()};
53   std::size_t offset_{0};
54   std::size_t align_{0};
55 };
56 
57 void ComputeOffsetsHelper::Compute(Scope &scope) {
58   for (Scope &child : scope.children()) {
59     Compute(child);
60   }
61   DoScope(scope);
62 }
63 
64 void ComputeOffsetsHelper::DoScope(Scope &scope) {
65   if (scope.symbol() && scope.IsParameterizedDerivedType()) {
66     return; // only process instantiations of parameterized derived types
67   }
68   offset_ = 0;
69   align_ = 0;
70   for (auto symbol : scope.GetSymbols()) {
71     if (!symbol->has<TypeParamDetails>() && !symbol->has<SubprogramDetails>()) {
72       DoSymbol(*symbol);
73     }
74   }
75   scope.set_size(offset_);
76   scope.set_align(align_);
77 }
78 
79 void ComputeOffsetsHelper::DoSymbol(Symbol &symbol) {
80   SizeAndAlign s{GetSizeAndAlign(symbol)};
81   if (s.size == 0) {
82     return;
83   }
84   offset_ = Align(offset_, s.align);
85   symbol.set_size(s.size);
86   symbol.set_offset(offset_);
87   offset_ += s.size;
88   if (s.align > align_) {
89     align_ = s.align;
90   }
91 }
92 
93 auto ComputeOffsetsHelper::GetSizeAndAlign(const Symbol &symbol)
94     -> SizeAndAlign {
95   const DeclTypeSpec *type{symbol.GetType()};
96   if (!type) {
97     return {};
98   }
99   if (IsDescriptor(symbol) || IsProcedure(symbol)) {
100     int lenParams{0};
101     if (const DerivedTypeSpec * derived{type->AsDerived()}) {
102       lenParams = derived->NumLengthParameters();
103     }
104     std::size_t size{
105         runtime::Descriptor::SizeInBytes(symbol.Rank(), false, lenParams)};
106     return {size, maxAlignment};
107   }
108   SizeAndAlign result;
109   if (const IntrinsicTypeSpec * intrinsic{type->AsIntrinsic()}) {
110     if (auto kind{ToInt64(intrinsic->kind())}) {
111       result = GetIntrinsicSizeAndAlign(intrinsic->category(), *kind);
112     }
113     if (type->category() == DeclTypeSpec::Character) {
114       ParamValue length{type->characterTypeSpec().length()};
115       CHECK(length.isExplicit()); // else should be descriptor
116       if (MaybeIntExpr lengthExpr{length.GetExplicit()}) {
117         if (auto lengthInt{ToInt64(*lengthExpr)}) {
118           result.size *= *lengthInt;
119         }
120       }
121     }
122   } else if (const DerivedTypeSpec * derived{type->AsDerived()}) {
123     if (derived->scope()) {
124       result.size = derived->scope()->size();
125       result.align = derived->scope()->align();
126     }
127   } else {
128     DIE("not intrinsic or derived");
129   }
130   std::size_t elements{CountElements(symbol)};
131   if (elements > 1) {
132     result.size = Align(result.size, result.align);
133   }
134   result.size *= elements;
135   return result;
136 }
137 
138 std::size_t ComputeOffsetsHelper::CountElements(const Symbol &symbol) {
139   if (auto shape{GetShape(foldingContext_, symbol)}) {
140     if (auto sizeExpr{evaluate::GetSize(std::move(*shape))}) {
141       if (auto size{ToInt64(Fold(foldingContext_, std::move(*sizeExpr)))}) {
142         return *size;
143       }
144     }
145   }
146   return 1;
147 }
148 
149 // Align a size to its natural alignment, up to maxAlignment.
150 std::size_t ComputeOffsetsHelper::Align(std::size_t x, std::size_t alignment) {
151   if (alignment > maxAlignment) {
152     alignment = maxAlignment;
153   }
154   return (x + alignment - 1) & -alignment;
155 }
156 
157 auto ComputeOffsetsHelper::GetIntrinsicSizeAndAlign(
158     TypeCategory category, int kind) -> SizeAndAlign {
159   // TODO: does kind==10 need special handling?
160   std::size_t size{kind == 3 ? 2 : static_cast<std::size_t>(kind)};
161   if (category == TypeCategory::Complex) {
162     return {2 * size, size};
163   } else {
164     return {size};
165   }
166 }
167 
168 void ComputeOffsets(SemanticsContext &context) {
169   ComputeOffsetsHelper{context}.Compute();
170 }
171 
172 } // namespace Fortran::semantics
173