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