1 //===-- Optimizer/Builder/TemporaryStorage.h --------------------*- C++ -*-===// 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 // Utility to create an manipulate vector like temporary storages holding 10 // Fortran values or descriptors in HLFIR. 11 // 12 // This is useful to deal with array constructors, and temporary storage 13 // inside forall and where constructs where it is not known prior to the 14 // construct execution how many values will be stored, or where the values 15 // at each iteration may have different shapes or type parameters. 16 // 17 //===----------------------------------------------------------------------===// 18 19 #ifndef FORTRAN_OPTIMIZER_BUILDER_TEMPORARYSTORAGE_H 20 #define FORTRAN_OPTIMIZER_BUILDER_TEMPORARYSTORAGE_H 21 22 #include "flang/Optimizer/HLFIR/HLFIROps.h" 23 24 namespace fir { 25 class FirOpBuilder; 26 } 27 28 namespace hlfir { 29 class Entity; 30 } 31 32 namespace fir::factory { 33 34 /// Structure to create and manipulate counters in generated code. 35 /// They are used to keep track of the insertion of fetching position in the 36 /// temporary storages. 37 /// By default, this counter is implemented with a value in memory and can be 38 /// incremented inside generated loops or branches. 39 /// The option canCountThroughLoops can be set to false to get a counter that 40 /// is a simple SSA value that is swap by its incremented value (hence, the 41 /// counter cannot count through loops since the SSA value in the loop becomes 42 /// inaccessible after the loop). This form helps reducing compile times for 43 /// huge array constructors without implied-do-loops. 44 struct Counter { 45 /// Create a counter set to the initial value. 46 Counter(mlir::Location loc, fir::FirOpBuilder &builder, 47 mlir::Value initialValue, bool canCountThroughLoops = true); 48 /// Return "counter++". 49 mlir::Value getAndIncrementIndex(mlir::Location loc, 50 fir::FirOpBuilder &builder); 51 /// Set the counter to the initial value. 52 void reset(mlir::Location loc, fir::FirOpBuilder &builder); 53 const bool canCountThroughLoops; 54 55 private: 56 /// Zero for the init/reset. 57 mlir::Value initialValue; 58 /// One for the increment. 59 mlir::Value one; 60 /// Index variable or value holding the counter current value. 61 mlir::Value index; 62 }; 63 64 /// Data structure to stack simple scalars that all have the same type and 65 /// type parameters, and where the total number of elements that will be pushed 66 /// is known or can be maximized. It is implemented inlined and does not require 67 /// runtime. 68 class HomogeneousScalarStack { 69 public: 70 HomogeneousScalarStack(mlir::Location loc, fir::FirOpBuilder &builder, 71 fir::SequenceType declaredType, mlir::Value extent, 72 llvm::ArrayRef<mlir::Value> lengths, 73 bool allocateOnHeap, bool stackThroughLoops, 74 llvm::StringRef name); 75 76 void pushValue(mlir::Location loc, fir::FirOpBuilder &builder, 77 mlir::Value value); 78 void resetFetchPosition(mlir::Location loc, fir::FirOpBuilder &builder); 79 mlir::Value fetch(mlir::Location loc, fir::FirOpBuilder &builder); 80 void destroy(mlir::Location loc, fir::FirOpBuilder &builder); 81 82 /// Move the temporary storage into a rank one array expression value 83 /// (hlfir.expr<?xT>). The temporary should not be used anymore after this 84 /// call. 85 hlfir::Entity moveStackAsArrayExpr(mlir::Location loc, 86 fir::FirOpBuilder &builder); 87 88 /// "fetch" cannot be called right after "pushValue" because the counter is 89 /// both used for pushing and fetching. canBeFetchedAfterPush()90 bool canBeFetchedAfterPush() const { return false; } 91 92 private: 93 /// Allocate the temporary on the heap. 94 const bool allocateOnHeap; 95 /// Counter to keep track of the insertion or fetching position. 96 Counter counter; 97 /// Temporary storage. 98 mlir::Value temp; 99 }; 100 101 /// Structure to hold the value of a single entity. 102 class SimpleCopy { 103 public: 104 SimpleCopy(mlir::Location loc, fir::FirOpBuilder &builder, 105 hlfir::Entity source, llvm::StringRef tempName); 106 pushValue(mlir::Location loc,fir::FirOpBuilder & builder,mlir::Value value)107 void pushValue(mlir::Location loc, fir::FirOpBuilder &builder, 108 mlir::Value value) { 109 assert(false && "must not be called: value already set"); 110 } resetFetchPosition(mlir::Location loc,fir::FirOpBuilder & builder)111 void resetFetchPosition(mlir::Location loc, fir::FirOpBuilder &builder){}; fetch(mlir::Location loc,fir::FirOpBuilder & builder)112 mlir::Value fetch(mlir::Location loc, fir::FirOpBuilder &builder) { 113 return copy.getBase(); 114 } 115 void destroy(mlir::Location loc, fir::FirOpBuilder &builder); canBeFetchedAfterPush()116 bool canBeFetchedAfterPush() const { return true; } 117 118 public: 119 /// Temporary storage for the copy. 120 hlfir::AssociateOp copy; 121 }; 122 123 /// Structure to keep track of a simple mlir::Value. This is useful 124 /// when a value does not need an in memory copy because it is 125 /// already saved in an SSA value that will be accessible at the fetching 126 /// point. 127 class SSARegister { 128 public: SSARegister()129 SSARegister(){}; 130 pushValue(mlir::Location loc,fir::FirOpBuilder & builder,mlir::Value value)131 void pushValue(mlir::Location loc, fir::FirOpBuilder &builder, 132 mlir::Value value) { 133 ssaRegister = value; 134 } resetFetchPosition(mlir::Location loc,fir::FirOpBuilder & builder)135 void resetFetchPosition(mlir::Location loc, fir::FirOpBuilder &builder){}; fetch(mlir::Location loc,fir::FirOpBuilder & builder)136 mlir::Value fetch(mlir::Location loc, fir::FirOpBuilder &builder) { 137 return ssaRegister; 138 } destroy(mlir::Location loc,fir::FirOpBuilder & builder)139 void destroy(mlir::Location loc, fir::FirOpBuilder &builder) {} canBeFetchedAfterPush()140 bool canBeFetchedAfterPush() const { return true; } 141 142 public: 143 /// Temporary storage for the copy. 144 mlir::Value ssaRegister; 145 }; 146 147 /// Data structure to stack any kind of values with the same static type and 148 /// rank. Each value may have different type parameters, bounds, and dynamic 149 /// type. Fetching value N will return a value with the same dynamic type, 150 /// bounds, and type parameters as the Nth value that was pushed. It is 151 /// implemented using runtime. 152 class AnyValueStack { 153 public: 154 AnyValueStack(mlir::Location loc, fir::FirOpBuilder &builder, 155 mlir::Type valueStaticType); 156 157 void pushValue(mlir::Location loc, fir::FirOpBuilder &builder, 158 mlir::Value value); 159 void resetFetchPosition(mlir::Location loc, fir::FirOpBuilder &builder); 160 mlir::Value fetch(mlir::Location loc, fir::FirOpBuilder &builder); 161 void destroy(mlir::Location loc, fir::FirOpBuilder &builder); canBeFetchedAfterPush()162 bool canBeFetchedAfterPush() const { return true; } 163 164 private: 165 /// Keep the original value type. Values may be stored by the runtime 166 /// with a different type (i1 cannot be passed by descriptor). 167 mlir::Type valueStaticType; 168 /// Runtime cookie created by the runtime. It is a pointer to an opaque 169 /// runtime data structure that manages the stack. 170 mlir::Value opaquePtr; 171 /// Counter to keep track of the fetching position. 172 Counter counter; 173 /// Allocatable box passed to the runtime when fetching the values. 174 mlir::Value retValueBox; 175 }; 176 177 /// Data structure to stack any kind of variables with the same static type and 178 /// rank. Each variable may have different type parameters, bounds, and dynamic 179 /// type. Fetching variable N will return a variable with the same address, 180 /// dynamic type, bounds, and type parameters as the Nth variable that was 181 /// pushed. It is implemented using runtime. 182 class AnyVariableStack { 183 public: 184 AnyVariableStack(mlir::Location loc, fir::FirOpBuilder &builder, 185 mlir::Type valueStaticType); 186 187 void pushValue(mlir::Location loc, fir::FirOpBuilder &builder, 188 mlir::Value value); 189 void resetFetchPosition(mlir::Location loc, fir::FirOpBuilder &builder); 190 mlir::Value fetch(mlir::Location loc, fir::FirOpBuilder &builder); 191 void destroy(mlir::Location loc, fir::FirOpBuilder &builder); canBeFetchedAfterPush()192 bool canBeFetchedAfterPush() const { return true; } 193 194 private: 195 /// Keep the original variable type. 196 mlir::Type variableStaticType; 197 /// Runtime cookie created by the runtime. It is a pointer to an opaque 198 /// runtime data structure that manages the stack. 199 mlir::Value opaquePtr; 200 /// Counter to keep track of the fetching position. 201 Counter counter; 202 /// Pointer box passed to the runtime when fetching the values. 203 mlir::Value retValueBox; 204 }; 205 206 class TemporaryStorage; 207 208 /// Data structure to stack vector subscripted entity shape and 209 /// element addresses. AnyVariableStack allows saving vector subscripted 210 /// entities element addresses, but when saving several vector subscripted 211 /// entities on a stack, and if the context does not allow retrieving the 212 /// vector subscript entities shapes, these shapes must be saved too. 213 class AnyVectorSubscriptStack : public AnyVariableStack { 214 public: 215 AnyVectorSubscriptStack(mlir::Location loc, fir::FirOpBuilder &builder, 216 mlir::Type valueStaticType, 217 bool shapeCanBeSavedAsRegister, int rank); 218 void pushShape(mlir::Location loc, fir::FirOpBuilder &builder, 219 mlir::Value shape); 220 void resetFetchPosition(mlir::Location loc, fir::FirOpBuilder &builder); 221 mlir::Value fetchShape(mlir::Location loc, fir::FirOpBuilder &builder); 222 void destroy(mlir::Location loc, fir::FirOpBuilder &builder); canBeFetchedAfterPush()223 bool canBeFetchedAfterPush() const { return true; } 224 225 private: 226 std::unique_ptr<TemporaryStorage> shapeTemp; 227 // If the shape is saved inside a descriptor (as extents), 228 // keep track of the descriptor type. 229 std::optional<mlir::Type> boxType; 230 }; 231 232 /// Generic wrapper over the different sorts of temporary storages. 233 class TemporaryStorage { 234 public: 235 template <typename T> TemporaryStorage(T && impl)236 TemporaryStorage(T &&impl) : impl{std::forward<T>(impl)} {} 237 pushValue(mlir::Location loc,fir::FirOpBuilder & builder,mlir::Value value)238 void pushValue(mlir::Location loc, fir::FirOpBuilder &builder, 239 mlir::Value value) { 240 std::visit([&](auto &temp) { temp.pushValue(loc, builder, value); }, impl); 241 } resetFetchPosition(mlir::Location loc,fir::FirOpBuilder & builder)242 void resetFetchPosition(mlir::Location loc, fir::FirOpBuilder &builder) { 243 std::visit([&](auto &temp) { temp.resetFetchPosition(loc, builder); }, 244 impl); 245 } fetch(mlir::Location loc,fir::FirOpBuilder & builder)246 mlir::Value fetch(mlir::Location loc, fir::FirOpBuilder &builder) { 247 return std::visit([&](auto &temp) { return temp.fetch(loc, builder); }, 248 impl); 249 } destroy(mlir::Location loc,fir::FirOpBuilder & builder)250 void destroy(mlir::Location loc, fir::FirOpBuilder &builder) { 251 std::visit([&](auto &temp) { temp.destroy(loc, builder); }, impl); 252 } 253 /// Can "fetch" be called to get the last value pushed with 254 /// "pushValue"? canBeFetchedAfterPush()255 bool canBeFetchedAfterPush() const { 256 return std::visit([&](auto &temp) { return temp.canBeFetchedAfterPush(); }, 257 impl); 258 } 259 260 template <typename T> cast()261 T &cast() { 262 return std::get<T>(impl); 263 } 264 265 private: 266 std::variant<HomogeneousScalarStack, SimpleCopy, SSARegister, AnyValueStack, 267 AnyVariableStack, AnyVectorSubscriptStack> 268 impl; 269 }; 270 } // namespace fir::factory 271 #endif // FORTRAN_OPTIMIZER_BUILDER_TEMPORARYSTORAGE_H 272