xref: /llvm-project/flang/include/flang/Optimizer/Builder/HLFIRTools.h (revision 71ff486bee1b089c78f5b8175fef16f99fcebe19)
1 //===-- HLFIRTools.h -- HLFIR tools       -----------------------*- 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 // Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #ifndef FORTRAN_OPTIMIZER_BUILDER_HLFIRTOOLS_H
14 #define FORTRAN_OPTIMIZER_BUILDER_HLFIRTOOLS_H
15 
16 #include "flang/Optimizer/Builder/BoxValue.h"
17 #include "flang/Optimizer/Dialect/FIROps.h"
18 #include "flang/Optimizer/Dialect/FortranVariableInterface.h"
19 #include "flang/Optimizer/HLFIR/HLFIRDialect.h"
20 #include <optional>
21 
22 namespace fir {
23 class FirOpBuilder;
24 }
25 
26 namespace mlir {
27 class IRMapping;
28 }
29 
30 namespace hlfir {
31 
32 class AssociateOp;
33 class ElementalOp;
34 class ElementalOpInterface;
35 class ElementalAddrOp;
36 class EvaluateInMemoryOp;
37 class YieldElementOp;
38 
39 /// Is this a Fortran variable for which the defining op carrying the Fortran
40 /// attributes is visible?
41 inline bool isFortranVariableWithAttributes(mlir::Value value) {
42   return value.getDefiningOp<fir::FortranVariableOpInterface>();
43 }
44 
45 /// Is this a Fortran expression value, or a Fortran variable for which the
46 /// defining op carrying the Fortran attributes is visible?
47 inline bool isFortranEntityWithAttributes(mlir::Value value) {
48   return isFortranValue(value) || isFortranVariableWithAttributes(value);
49 }
50 
51 class Entity : public mlir::Value {
52 public:
53   explicit Entity(mlir::Value value) : mlir::Value(value) {
54     assert(isFortranEntity(value) &&
55            "must be a value representing a Fortran value or variable like");
56   }
57   Entity(fir::FortranVariableOpInterface variable)
58       : mlir::Value(variable.getBase()) {}
59   bool isValue() const { return isFortranValue(*this); }
60   bool isVariable() const { return !isValue(); }
61   bool isMutableBox() const { return hlfir::isBoxAddressType(getType()); }
62   bool isProcedurePointer() const {
63     return fir::isBoxProcAddressType(getType());
64   }
65   bool isBoxAddressOrValue() const {
66     return hlfir::isBoxAddressOrValueType(getType());
67   }
68 
69   /// Is this entity a procedure designator?
70   bool isProcedure() const { return isFortranProcedureValue(getType()); }
71 
72   /// Is this an array or an assumed ranked entity?
73   bool isArray() const { return getRank() != 0; }
74 
75   /// Is this an assumed ranked entity?
76   bool isAssumedRank() const { return getRank() == -1; }
77 
78   /// Return the rank of this entity or -1 if it is an assumed rank.
79   int getRank() const {
80     mlir::Type type = fir::unwrapPassByRefType(fir::unwrapRefType(getType()));
81     if (auto seqTy = mlir::dyn_cast<fir::SequenceType>(type)) {
82       if (seqTy.hasUnknownShape())
83         return -1;
84       return seqTy.getDimension();
85     }
86     if (auto exprType = mlir::dyn_cast<hlfir::ExprType>(type))
87       return exprType.getRank();
88     return 0;
89   }
90   bool isScalar() const { return !isArray(); }
91 
92   bool isPolymorphic() const { return hlfir::isPolymorphicType(getType()); }
93 
94   mlir::Type getFortranElementType() const {
95     return hlfir::getFortranElementType(getType());
96   }
97   mlir::Type getElementOrSequenceType() const {
98     return hlfir::getFortranElementOrSequenceType(getType());
99   }
100 
101   bool hasLengthParameters() const {
102     mlir::Type eleTy = getFortranElementType();
103     return mlir::isa<fir::CharacterType>(eleTy) ||
104            fir::isRecordWithTypeParameters(eleTy);
105   }
106 
107   bool isCharacter() const {
108     return mlir::isa<fir::CharacterType>(getFortranElementType());
109   }
110 
111   bool hasIntrinsicType() const {
112     mlir::Type eleTy = getFortranElementType();
113     return fir::isa_trivial(eleTy) || mlir::isa<fir::CharacterType>(eleTy);
114   }
115 
116   bool isDerivedWithLengthParameters() const {
117     return fir::isRecordWithTypeParameters(getFortranElementType());
118   }
119 
120   bool mayHaveNonDefaultLowerBounds() const;
121 
122   // Is this entity known to be contiguous at compile time?
123   // Note that when this returns false, the entity may still
124   // turn-out to be contiguous at runtime.
125   bool isSimplyContiguous() const {
126     // If this can be described without a fir.box in FIR, this must
127     // be contiguous.
128     if (!hlfir::isBoxAddressOrValueType(getFirBase().getType()))
129       return true;
130     // Otherwise, if this entity has a visible declaration in FIR,
131     // or is the dereference of an allocatable or contiguous pointer
132     // it is simply contiguous.
133     if (auto varIface = getMaybeDereferencedVariableInterface())
134       return varIface.isAllocatable() || varIface.hasContiguousAttr();
135     return false;
136   }
137 
138   fir::FortranVariableOpInterface getIfVariableInterface() const {
139     return this->getDefiningOp<fir::FortranVariableOpInterface>();
140   }
141 
142   // Return a "declaration" operation for this variable if visible,
143   // or the "declaration" operation of the allocatable/pointer this
144   // variable was dereferenced from (if it is visible).
145   fir::FortranVariableOpInterface
146   getMaybeDereferencedVariableInterface() const {
147     mlir::Value base = *this;
148     if (auto loadOp = base.getDefiningOp<fir::LoadOp>())
149       base = loadOp.getMemref();
150     return base.getDefiningOp<fir::FortranVariableOpInterface>();
151   }
152 
153   bool isOptional() const {
154     auto varIface = getIfVariableInterface();
155     return varIface ? varIface.isOptional() : false;
156   }
157 
158   bool isParameter() const {
159     auto varIface = getIfVariableInterface();
160     return varIface ? varIface.isParameter() : false;
161   }
162 
163   bool isAllocatable() const {
164     auto varIface = getIfVariableInterface();
165     return varIface ? varIface.isAllocatable() : false;
166   }
167 
168   bool isPointer() const {
169     auto varIface = getIfVariableInterface();
170     return varIface ? varIface.isPointer() : false;
171   }
172 
173   // Get the entity as an mlir SSA value containing all the shape, type
174   // parameters and dynamic shape information.
175   mlir::Value getBase() const { return *this; }
176 
177   // Get the entity as a FIR base. This may not carry the shape and type
178   // parameters information, and even when it is a box with shape information.
179   // it will not contain the local lower bounds of the entity. This should
180   // be used with care when generating FIR code that does not need this
181   // information, or has access to it in other ways. Its advantage is that
182   // it will never be a fir.box for explicit shape arrays, leading to simpler
183   // FIR code generation.
184   mlir::Value getFirBase() const;
185 };
186 
187 /// Wrapper over an mlir::Value that can be viewed as a Fortran entity.
188 /// This provides some Fortran specific helpers as well as a guarantee
189 /// in the compiler source that a certain mlir::Value must be a Fortran
190 /// entity, and if it is a variable, its defining operation carrying its
191 /// Fortran attributes must be visible.
192 class EntityWithAttributes : public Entity {
193 public:
194   explicit EntityWithAttributes(mlir::Value value) : Entity(value) {
195     assert(isFortranEntityWithAttributes(value) &&
196            "must be a value representing a Fortran value or variable");
197   }
198   EntityWithAttributes(fir::FortranVariableOpInterface variable)
199       : Entity(variable) {}
200   fir::FortranVariableOpInterface getIfVariable() const {
201     return getIfVariableInterface();
202   }
203 };
204 
205 /// Functions to translate hlfir::EntityWithAttributes to fir::ExtendedValue.
206 /// For Fortran arrays, character, and derived type values, this require
207 /// allocating a storage since these can only be represented in memory in FIR.
208 /// In that case, a cleanup function is provided to generate the finalization
209 /// code after the end of the fir::ExtendedValue use.
210 using CleanupFunction = std::function<void()>;
211 std::pair<fir::ExtendedValue, std::optional<CleanupFunction>>
212 translateToExtendedValue(mlir::Location loc, fir::FirOpBuilder &builder,
213                          Entity entity, bool contiguousHint = false);
214 
215 /// Function to translate FortranVariableOpInterface to fir::ExtendedValue.
216 /// It may generates IR to unbox fir.boxchar, but has otherwise no side effects
217 /// on the IR.
218 fir::ExtendedValue
219 translateToExtendedValue(mlir::Location loc, fir::FirOpBuilder &builder,
220                          fir::FortranVariableOpInterface fortranVariable,
221                          bool forceHlfirBase = false);
222 
223 /// Generate declaration for a fir::ExtendedValue in memory.
224 fir::FortranVariableOpInterface
225 genDeclare(mlir::Location loc, fir::FirOpBuilder &builder,
226            const fir::ExtendedValue &exv, llvm::StringRef name,
227            fir::FortranVariableFlagsAttr flags,
228            mlir::Value dummyScope = nullptr,
229            cuf::DataAttributeAttr dataAttr = {});
230 
231 /// Generate an hlfir.associate to build a variable from an expression value.
232 /// The type of the variable must be provided so that scalar logicals are
233 /// properly typed when placed in memory.
234 hlfir::AssociateOp
235 genAssociateExpr(mlir::Location loc, fir::FirOpBuilder &builder,
236                  hlfir::Entity value, mlir::Type variableType,
237                  llvm::StringRef name,
238                  std::optional<mlir::NamedAttribute> attr = std::nullopt);
239 
240 /// Get the raw address of a variable (simple fir.ref/fir.ptr, or fir.heap
241 /// value). The returned value should be used with care, it does not contain any
242 /// stride, shape, and type parameter information. For pointers and
243 /// allocatables, this returns the address of the target.
244 mlir::Value genVariableRawAddress(mlir::Location loc,
245                                   fir::FirOpBuilder &builder,
246                                   hlfir::Entity var);
247 
248 /// Get a fir.boxchar for character scalar or array variable (the shape is lost
249 /// for arrays).
250 mlir::Value genVariableBoxChar(mlir::Location loc, fir::FirOpBuilder &builder,
251                                hlfir::Entity var);
252 
253 /// Get or create a fir.box or fir.class from a variable.
254 hlfir::Entity genVariableBox(mlir::Location loc, fir::FirOpBuilder &builder,
255                              hlfir::Entity var);
256 
257 /// If the entity is a variable, load its value (dereference pointers and
258 /// allocatables if needed). Do nothing if the entity is already a value, and
259 /// only dereference pointers and allocatables if it is not a scalar entity
260 /// of numerical or logical type.
261 Entity loadTrivialScalar(mlir::Location loc, fir::FirOpBuilder &builder,
262                          Entity entity);
263 
264 /// If \p entity is a POINTER or ALLOCATABLE, dereference it and return the
265 /// target entity. Return \p entity otherwise.
266 hlfir::Entity derefPointersAndAllocatables(mlir::Location loc,
267                                            fir::FirOpBuilder &builder,
268                                            Entity entity);
269 
270 /// Get element entity(oneBasedIndices) if entity is an array, or return entity
271 /// if it is a scalar. The indices are one based. If the entity has non default
272 /// lower bounds, the function will adapt the indices in the indexing operation.
273 hlfir::Entity getElementAt(mlir::Location loc, fir::FirOpBuilder &builder,
274                            Entity entity, mlir::ValueRange oneBasedIndices);
275 /// Compute the lower and upper bounds of an entity.
276 llvm::SmallVector<std::pair<mlir::Value, mlir::Value>>
277 genBounds(mlir::Location loc, fir::FirOpBuilder &builder, Entity entity);
278 /// Compute the lower and upper bounds given a fir.shape or fir.shape_shift
279 /// (fir.shift is not allowed here).
280 llvm::SmallVector<std::pair<mlir::Value, mlir::Value>>
281 genBounds(mlir::Location loc, fir::FirOpBuilder &builder, mlir::Value shape);
282 
283 /// Generate lower bounds from a shape. If \p shape is null or is a fir.shape,
284 /// the returned vector will contain \p rank ones.
285 llvm::SmallVector<mlir::Value> genLowerbounds(mlir::Location loc,
286                                               fir::FirOpBuilder &builder,
287                                               mlir::Value shape, unsigned rank);
288 
289 /// Compute fir.shape<> (no lower bounds) for an entity.
290 mlir::Value genShape(mlir::Location loc, fir::FirOpBuilder &builder,
291                      Entity entity);
292 
293 /// Compute the extent of \p entity in dimension \p dim. Crashes
294 /// if dim is bigger than the entity's rank.
295 mlir::Value genExtent(mlir::Location loc, fir::FirOpBuilder &builder,
296                       hlfir::Entity entity, unsigned dim);
297 
298 /// Compute the lower bound of \p entity in dimension \p dim. Crashes
299 /// if dim is bigger than the entity's rank.
300 mlir::Value genLBound(mlir::Location loc, fir::FirOpBuilder &builder,
301                       hlfir::Entity entity, unsigned dim);
302 
303 /// Generate a vector of extents with index type from a fir.shape
304 /// of fir.shape_shift value.
305 llvm::SmallVector<mlir::Value> getIndexExtents(mlir::Location loc,
306                                                fir::FirOpBuilder &builder,
307                                                mlir::Value shape);
308 
309 /// Return explicit extents. If the base is a fir.box, this won't read it to
310 /// return the extents and will instead return an empty vector.
311 llvm::SmallVector<mlir::Value>
312 getExplicitExtentsFromShape(mlir::Value shape, fir::FirOpBuilder &builder);
313 
314 /// Read length parameters into result if this entity has any.
315 void genLengthParameters(mlir::Location loc, fir::FirOpBuilder &builder,
316                          Entity entity,
317                          llvm::SmallVectorImpl<mlir::Value> &result);
318 
319 /// Get the length of a character entity. Crashes if the entity is not
320 /// a character entity.
321 mlir::Value genCharLength(mlir::Location loc, fir::FirOpBuilder &builder,
322                           Entity entity);
323 
324 mlir::Value genRank(mlir::Location loc, fir::FirOpBuilder &builder,
325                     Entity entity, mlir::Type resultType);
326 
327 /// Return the fir base, shape, and type parameters for a variable. Note that
328 /// type parameters are only added if the entity is not a box and the type
329 /// parameters is not a constant in the base type. This matches the arguments
330 /// expected by fir.embox/fir.array_coor.
331 std::pair<mlir::Value, mlir::Value> genVariableFirBaseShapeAndParams(
332     mlir::Location loc, fir::FirOpBuilder &builder, Entity entity,
333     llvm::SmallVectorImpl<mlir::Value> &typeParams);
334 
335 /// Get the variable type for an element of an array type entity. Returns the
336 /// input entity type if it is scalar. Will crash if the entity is not a
337 /// variable.
338 mlir::Type getVariableElementType(hlfir::Entity variable);
339 /// Get the entity type for an element of an array entity. Returns the
340 /// input type if it is a scalar. If the entity is a variable, this
341 /// is like getVariableElementType, otherwise, this will return a value
342 /// type (that may be an hlfir.expr type).
343 mlir::Type getEntityElementType(hlfir::Entity entity);
344 
345 using ElementalKernelGenerator = std::function<hlfir::Entity(
346     mlir::Location, fir::FirOpBuilder &, mlir::ValueRange)>;
347 /// Generate an hlfir.elementalOp given call back to generate the element
348 /// value at for each iteration.
349 /// If exprType is specified, this will be the return type of the elemental op.
350 /// If exprType is not specified, the resulting expression type is computed
351 /// from the given \p elementType and \p shape, and the type is polymorphic
352 /// if \p polymorphicMold is present.
353 hlfir::ElementalOp genElementalOp(
354     mlir::Location loc, fir::FirOpBuilder &builder, mlir::Type elementType,
355     mlir::Value shape, mlir::ValueRange typeParams,
356     const ElementalKernelGenerator &genKernel, bool isUnordered = false,
357     mlir::Value polymorphicMold = {}, mlir::Type exprType = mlir::Type{});
358 
359 /// Structure to describe a loop nest.
360 struct LoopNest {
361   mlir::Operation *outerOp = nullptr;
362   mlir::Block *body = nullptr;
363   llvm::SmallVector<mlir::Value> oneBasedIndices;
364 };
365 
366 /// Generate a fir.do_loop nest looping from 1 to extents[i].
367 /// \p isUnordered specifies whether the loops in the loop nest
368 /// are unordered.
369 ///
370 /// NOTE: genLoopNestWithReductions() should be used in favor
371 /// of this method, though, it cannot generate OpenMP workshare
372 /// loop constructs currently.
373 LoopNest genLoopNest(mlir::Location loc, fir::FirOpBuilder &builder,
374                      mlir::ValueRange extents, bool isUnordered = false,
375                      bool emitWorkshareLoop = false);
376 inline LoopNest genLoopNest(mlir::Location loc, fir::FirOpBuilder &builder,
377                             mlir::Value shape, bool isUnordered = false,
378                             bool emitWorkshareLoop = false) {
379   return genLoopNest(loc, builder, getIndexExtents(loc, builder, shape),
380                      isUnordered, emitWorkshareLoop);
381 }
382 
383 /// The type of a callback that generates the body of a reduction
384 /// loop nest. It takes a location and a builder, as usual.
385 /// In addition, the first set of values are the values of the loops'
386 /// induction variables. The second set of values are the values
387 /// of the reductions on entry to the innermost loop.
388 /// The callback must return the updated values of the reductions.
389 using ReductionLoopBodyGenerator = std::function<llvm::SmallVector<mlir::Value>(
390     mlir::Location, fir::FirOpBuilder &, mlir::ValueRange, mlir::ValueRange)>;
391 
392 /// Generate a loop nest loopong from 1 to \p extents[i] and reducing
393 /// a set of values.
394 /// \p isUnordered specifies whether the loops in the loop nest
395 /// are unordered.
396 /// \p reductionInits are the initial values of the reductions
397 /// on entry to the outermost loop.
398 /// \p genBody callback is repsonsible for generating the code
399 /// that updates the reduction values in the innermost loop.
400 ///
401 /// NOTE: the implementation of this function may decide
402 /// to perform the reductions on SSA or in memory.
403 /// In the latter case, this function is responsible for
404 /// allocating/loading/storing the reduction variables,
405 /// and making sure they have proper data sharing attributes
406 /// in case any parallel constructs are present around the point
407 /// of the loop nest insertion, or if the function decides
408 /// to use any worksharing loop constructs for the loop nest.
409 llvm::SmallVector<mlir::Value> genLoopNestWithReductions(
410     mlir::Location loc, fir::FirOpBuilder &builder, mlir::ValueRange extents,
411     mlir::ValueRange reductionInits, const ReductionLoopBodyGenerator &genBody,
412     bool isUnordered = false);
413 
414 /// Inline the body of an hlfir.elemental at the current insertion point
415 /// given a list of one based indices. This generates the computation
416 /// of one element of the elemental expression. Return the YieldElementOp
417 /// whose value argument is the element value.
418 /// The original hlfir::ElementalOp is left untouched.
419 hlfir::YieldElementOp inlineElementalOp(mlir::Location loc,
420                                         fir::FirOpBuilder &builder,
421                                         hlfir::ElementalOp elemental,
422                                         mlir::ValueRange oneBasedIndices);
423 
424 /// Inline the body of an hlfir.elemental or hlfir.elemental_addr without
425 /// cloning the resulting hlfir.yield_element/hlfir.yield, and return the cloned
426 /// operand of the hlfir.yield_element/hlfir.yield. The mapper must be provided
427 /// to cover complex cases where the inlined elemental is not defined in the
428 /// current context and uses values that have been cloned already. A callback is
429 /// provided to indicate if an hlfir.apply inside the hlfir.elemental must be
430 /// immediately replaced by the inlining of the applied hlfir.elemental.
431 mlir::Value inlineElementalOp(
432     mlir::Location loc, fir::FirOpBuilder &builder,
433     hlfir::ElementalOpInterface elemental, mlir::ValueRange oneBasedIndices,
434     mlir::IRMapping &mapper,
435     const std::function<bool(hlfir::ElementalOp)> &mustRecursivelyInline);
436 
437 /// Create a new temporary with the shape and parameters of the provided
438 /// hlfir.eval_in_mem operation and clone the body of the hlfir.eval_in_mem
439 /// operating on this new temporary.  returns the temporary and whether the
440 /// temporary is heap or stack allocated.
441 std::pair<hlfir::Entity, bool>
442 computeEvaluateOpInNewTemp(mlir::Location, fir::FirOpBuilder &,
443                            hlfir::EvaluateInMemoryOp evalInMem,
444                            mlir::Value shape, mlir::ValueRange typeParams);
445 
446 // Clone the body of the hlfir.eval_in_mem operating on this the provided
447 // storage.  The provided storage must be a contiguous "raw" memory reference
448 // (not a fir.box) big enough to hold the value computed by hlfir.eval_in_mem.
449 // No runtime check is inserted by this utility to enforce that. It is also
450 // usually invalid to provide some storage that is already addressed directly
451 // or indirectly inside the hlfir.eval_in_mem body.
452 void computeEvaluateOpIn(mlir::Location, fir::FirOpBuilder &,
453                          hlfir::EvaluateInMemoryOp, mlir::Value storage);
454 
455 std::pair<fir::ExtendedValue, std::optional<hlfir::CleanupFunction>>
456 convertToValue(mlir::Location loc, fir::FirOpBuilder &builder,
457                hlfir::Entity entity);
458 
459 std::pair<fir::ExtendedValue, std::optional<hlfir::CleanupFunction>>
460 convertToAddress(mlir::Location loc, fir::FirOpBuilder &builder,
461                  hlfir::Entity entity, mlir::Type targetType);
462 
463 std::pair<fir::ExtendedValue, std::optional<hlfir::CleanupFunction>>
464 convertToBox(mlir::Location loc, fir::FirOpBuilder &builder,
465              hlfir::Entity entity, mlir::Type targetType);
466 
467 /// Clone an hlfir.elemental_addr into an hlfir.elemental value.
468 hlfir::ElementalOp cloneToElementalOp(mlir::Location loc,
469                                       fir::FirOpBuilder &builder,
470                                       hlfir::ElementalAddrOp elementalAddrOp);
471 
472 /// Return true, if \p elemental must produce a temporary array,
473 /// for example, for the purpose of finalization. Note that such
474 /// ElementalOp's must be optimized with caution. For example,
475 /// completely inlining such ElementalOp into another one
476 /// would be incorrect.
477 bool elementalOpMustProduceTemp(hlfir::ElementalOp elemental);
478 
479 std::pair<hlfir::Entity, mlir::Value>
480 createTempFromMold(mlir::Location loc, fir::FirOpBuilder &builder,
481                    hlfir::Entity mold);
482 
483 // TODO: this does not support polymorphic molds
484 hlfir::Entity createStackTempFromMold(mlir::Location loc,
485                                       fir::FirOpBuilder &builder,
486                                       hlfir::Entity mold);
487 
488 hlfir::EntityWithAttributes convertCharacterKind(mlir::Location loc,
489                                                  fir::FirOpBuilder &builder,
490                                                  hlfir::Entity scalarChar,
491                                                  int toKind);
492 
493 /// Materialize an implicit Fortran type conversion from \p source to \p toType.
494 /// This is a no-op if the Fortran category and KIND of \p source are
495 /// the same as the one in \p toType. This is also a no-op if \p toType is an
496 /// unlimited polymorphic. For characters, this implies that a conversion is
497 /// only inserted in case of KIND mismatch (and not in case of length mismatch),
498 /// and that the resulting entity length is the same as the one from \p source.
499 /// It is valid to call this helper if \p source is an array. If a conversion is
500 /// inserted for arrays, a clean-up will be returned. If no conversion is
501 /// needed, the source is returned.
502 /// Beware that the resulting entity mlir type may not be toType: it will be a
503 /// Fortran entity with the same Fortran category and KIND.
504 /// If preserveLowerBounds is set, the returned entity will have the same lower
505 /// bounds as \p source.
506 std::pair<hlfir::Entity, std::optional<hlfir::CleanupFunction>>
507 genTypeAndKindConvert(mlir::Location loc, fir::FirOpBuilder &builder,
508                       hlfir::Entity source, mlir::Type toType,
509                       bool preserveLowerBounds);
510 
511 /// A shortcut for loadTrivialScalar(getElementAt()),
512 /// which designates and loads an element of an array.
513 Entity loadElementAt(mlir::Location loc, fir::FirOpBuilder &builder,
514                      Entity entity, mlir::ValueRange oneBasedIndices);
515 
516 /// Return a vector of extents for the given entity.
517 /// The function creates new operations, but tries to clean-up
518 /// after itself.
519 llvm::SmallVector<mlir::Value, Fortran::common::maxRank>
520 genExtentsVector(mlir::Location loc, fir::FirOpBuilder &builder, Entity entity);
521 
522 } // namespace hlfir
523 
524 #endif // FORTRAN_OPTIMIZER_BUILDER_HLFIRTOOLS_H
525