xref: /llvm-project/flang/include/flang/Optimizer/Builder/TemporaryStorage.h (revision 0446bfcc5ca206701b511796ed1c8316daa2d169)
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