FLANG
HLFIRTools.h
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 "flang/Optimizer/HLFIR/HLFIROps.h"
21#include <optional>
22
23namespace fir {
24class FirOpBuilder;
25}
26
27namespace mlir {
28class IRMapping;
29}
30
31namespace hlfir {
32
33class AssociateOp;
34class ElementalOp;
35class ElementalOpInterface;
36class ElementalAddrOp;
37class EvaluateInMemoryOp;
38class YieldElementOp;
39
42inline bool isFortranVariableWithAttributes(mlir::Value value) {
43 return value.getDefiningOp<fir::FortranVariableOpInterface>();
44}
45
48inline bool isFortranEntityWithAttributes(mlir::Value value) {
49 return isFortranValue(value) || isFortranVariableWithAttributes(value);
50}
51
52class Entity : public mlir::Value {
53public:
54 explicit Entity(mlir::Value value) : mlir::Value(value) {
55 assert(isFortranEntity(value) &&
56 "must be a value representing a Fortran value or variable like");
57 }
58 Entity(fir::FortranVariableOpInterface variable)
59 : mlir::Value(variable.getBase()) {}
60 bool isValue() const { return isFortranValue(*this); }
61 bool isVariable() const { return !isValue(); }
62 bool isMutableBox() const { return hlfir::isBoxAddressType(getType()); }
63 bool isProcedurePointer() const {
64 return hlfir::isFortranProcedurePointerType(getType());
65 }
66 bool isBoxAddressOrValue() const {
67 return hlfir::isBoxAddressOrValueType(getType());
68 }
69 bool isBoxAddress() const { return fir::isBoxAddress(getType()); }
70
72 bool isProcedure() const { return isFortranProcedureValue(getType()); }
73
75 bool isArray() const { return getRank() != 0; }
76
78 bool isAssumedRank() const { return getRank() == -1; }
79
81 int getRank() const {
82 mlir::Type type = fir::unwrapPassByRefType(fir::unwrapRefType(getType()));
83 if (auto seqTy = mlir::dyn_cast<fir::SequenceType>(type)) {
84 if (seqTy.hasUnknownShape())
85 return -1;
86 return seqTy.getDimension();
87 }
88 if (auto exprType = mlir::dyn_cast<hlfir::ExprType>(type))
89 return exprType.getRank();
90 return 0;
91 }
92 bool isScalar() const { return !isArray(); }
93
94 bool isPolymorphic() const { return hlfir::isPolymorphicType(getType()); }
95
96 mlir::Type getFortranElementType() const {
97 return hlfir::getFortranElementType(getType());
98 }
99 mlir::Type getElementOrSequenceType() const {
100 return hlfir::getFortranElementOrSequenceType(getType());
101 }
104 if (isBoxAddressOrValue())
105 return llvm::cast<fir::BaseBoxType>(fir::unwrapRefType(getType()));
106 const bool isVolatile = fir::isa_volatile_type(getType());
107 return fir::BoxType::get(getElementOrSequenceType(), isVolatile);
108 }
109
110 bool hasLengthParameters() const {
111 mlir::Type eleTy = getFortranElementType();
112 return mlir::isa<fir::CharacterType>(eleTy) ||
114 }
115
116 bool isCharacter() const {
117 return mlir::isa<fir::CharacterType>(getFortranElementType());
118 }
119
120 bool hasIntrinsicType() const {
121 mlir::Type eleTy = getFortranElementType();
122 return fir::isa_trivial(eleTy) || mlir::isa<fir::CharacterType>(eleTy);
123 }
124
125 bool isDerivedWithLengthParameters() const {
126 return fir::isRecordWithTypeParameters(getFortranElementType());
127 }
128
129 bool mayHaveNonDefaultLowerBounds() const;
130
131 // Is this entity known to be contiguous at compile time?
132 // Note that when this returns false, the entity may still
133 // turn-out to be contiguous at runtime.
134 bool isSimplyContiguous() const {
135 // If this can be described without a fir.box in FIR, this must
136 // be contiguous.
137 if (!hlfir::isBoxAddressOrValueType(getFirBase().getType()) || isScalar())
138 return true;
139 // Otherwise, if this entity has a visible declaration in FIR,
140 // or is the dereference of an allocatable or contiguous pointer
141 // it is simply contiguous.
142 if (auto varIface = getMaybeDereferencedVariableInterface())
143 return varIface.isAllocatable() || varIface.hasContiguousAttr();
144 return false;
145 }
146
147 fir::FortranVariableOpInterface getIfVariableInterface() const {
148 return this->getDefiningOp<fir::FortranVariableOpInterface>();
149 }
150
151 // Return a "declaration" operation for this variable if visible,
152 // or the "declaration" operation of the allocatable/pointer this
153 // variable was dereferenced from (if it is visible).
154 fir::FortranVariableOpInterface
155 getMaybeDereferencedVariableInterface() const {
156 mlir::Value base = *this;
157 if (auto loadOp = base.getDefiningOp<fir::LoadOp>())
158 base = loadOp.getMemref();
159 return base.getDefiningOp<fir::FortranVariableOpInterface>();
160 }
161
162 bool mayBeOptional() const;
163
164 bool isParameter() const {
165 auto varIface = getIfVariableInterface();
166 return varIface ? varIface.isParameter() : false;
167 }
168
169 bool isAllocatable() const {
170 auto varIface = getIfVariableInterface();
171 return varIface ? varIface.isAllocatable() : false;
172 }
173
174 bool isPointer() const {
175 auto varIface = getIfVariableInterface();
176 return varIface ? varIface.isPointer() : false;
177 }
178
179 // Get the entity as an mlir SSA value containing all the shape, type
180 // parameters and dynamic shape information.
181 mlir::Value getBase() const { return *this; }
182
183 // Get the entity as a FIR base. This may not carry the shape and type
184 // parameters information, and even when it is a box with shape information.
185 // it will not contain the local lower bounds of the entity. This should
186 // be used with care when generating FIR code that does not need this
187 // information, or has access to it in other ways. Its advantage is that
188 // it will never be a fir.box for explicit shape arrays, leading to simpler
189 // FIR code generation.
190 mlir::Value getFirBase() const;
191};
192
198class EntityWithAttributes : public Entity {
199public:
200 explicit EntityWithAttributes(mlir::Value value) : Entity(value) {
201 assert(isFortranEntityWithAttributes(value) &&
202 "must be a value representing a Fortran value or variable");
203 }
204 EntityWithAttributes(fir::FortranVariableOpInterface variable)
205 : Entity(variable) {}
206 fir::FortranVariableOpInterface getIfVariable() const {
207 return getIfVariableInterface();
208 }
209};
210
216using CleanupFunction = std::function<void()>;
217std::pair<fir::ExtendedValue, std::optional<CleanupFunction>>
218translateToExtendedValue(mlir::Location loc, fir::FirOpBuilder &builder,
219 Entity entity, bool contiguousHint = false,
220 bool keepScalarOptionalBoxed = false);
221
226translateToExtendedValue(mlir::Location loc, fir::FirOpBuilder &builder,
227 fir::FortranVariableOpInterface fortranVariable,
228 bool forceHlfirBase = false);
229
231fir::FortranVariableOpInterface
232genDeclare(mlir::Location loc, fir::FirOpBuilder &builder,
233 const fir::ExtendedValue &exv, llvm::StringRef name,
234 fir::FortranVariableFlagsAttr flags,
235 mlir::Value dummyScope = nullptr, mlir::Value storage = nullptr,
236 std::uint64_t storageOffset = 0,
237 cuf::DataAttributeAttr dataAttr = {}, unsigned dummyArgNo = 0);
238
242hlfir::AssociateOp
243genAssociateExpr(mlir::Location loc, fir::FirOpBuilder &builder,
244 hlfir::Entity value, mlir::Type variableType,
245 llvm::StringRef name,
246 std::optional<mlir::NamedAttribute> attr = std::nullopt);
247
252mlir::Value genVariableRawAddress(mlir::Location loc,
253 fir::FirOpBuilder &builder,
254 hlfir::Entity var);
255
258mlir::Value genVariableBoxChar(mlir::Location loc, fir::FirOpBuilder &builder,
259 hlfir::Entity var);
260
264hlfir::Entity genVariableBox(mlir::Location loc, fir::FirOpBuilder &builder,
265 hlfir::Entity var,
266 fir::BaseBoxType forceBoxType = {});
267
272Entity loadTrivialScalar(mlir::Location loc, fir::FirOpBuilder &builder,
273 Entity entity);
274
277hlfir::Entity derefPointersAndAllocatables(mlir::Location loc,
278 fir::FirOpBuilder &builder,
279 Entity entity);
280
284hlfir::Entity getElementAt(mlir::Location loc, fir::FirOpBuilder &builder,
285 Entity entity, mlir::ValueRange oneBasedIndices);
287llvm::SmallVector<std::pair<mlir::Value, mlir::Value>>
288genBounds(mlir::Location loc, fir::FirOpBuilder &builder, Entity entity);
291llvm::SmallVector<std::pair<mlir::Value, mlir::Value>>
292genBounds(mlir::Location loc, fir::FirOpBuilder &builder, mlir::Value shape);
293
296llvm::SmallVector<mlir::Value> genLowerbounds(mlir::Location loc,
297 fir::FirOpBuilder &builder,
298 mlir::Value shape, unsigned rank);
299
301mlir::Value genShape(mlir::Location loc, fir::FirOpBuilder &builder,
302 Entity entity);
303
306mlir::Value genExtent(mlir::Location loc, fir::FirOpBuilder &builder,
307 hlfir::Entity entity, unsigned dim);
308
311mlir::Value genLBound(mlir::Location loc, fir::FirOpBuilder &builder,
312 hlfir::Entity entity, unsigned dim);
313
316llvm::SmallVector<mlir::Value> getIndexExtents(mlir::Location loc,
317 fir::FirOpBuilder &builder,
318 mlir::Value shape);
319
322llvm::SmallVector<mlir::Value>
323getExplicitExtentsFromShape(mlir::Value shape, fir::FirOpBuilder &builder);
324
326void genLengthParameters(mlir::Location loc, fir::FirOpBuilder &builder,
327 Entity entity,
328 llvm::SmallVectorImpl<mlir::Value> &result);
329
332mlir::Value genCharLength(mlir::Location loc, fir::FirOpBuilder &builder,
333 Entity entity);
334
337std::optional<std::int64_t> getCharLengthIfConst(Entity entity);
338
339mlir::Value genRank(mlir::Location loc, fir::FirOpBuilder &builder,
340 Entity entity, mlir::Type resultType);
341
346std::pair<mlir::Value, mlir::Value> genVariableFirBaseShapeAndParams(
347 mlir::Location loc, fir::FirOpBuilder &builder, Entity entity,
348 llvm::SmallVectorImpl<mlir::Value> &typeParams);
349
353mlir::Type getVariableElementType(hlfir::Entity variable);
358mlir::Type getEntityElementType(hlfir::Entity entity);
359
360using ElementalKernelGenerator = std::function<hlfir::Entity(
361 mlir::Location, fir::FirOpBuilder &, mlir::ValueRange)>;
368hlfir::ElementalOp genElementalOp(
369 mlir::Location loc, fir::FirOpBuilder &builder, mlir::Type elementType,
370 mlir::Value shape, mlir::ValueRange typeParams,
371 const ElementalKernelGenerator &genKernel, bool isUnordered = false,
372 mlir::Value polymorphicMold = {}, mlir::Type exprType = mlir::Type{});
373
375struct LoopNest {
376 mlir::Operation *outerOp = nullptr;
377 mlir::Block *body = nullptr;
378 llvm::SmallVector<mlir::Value> oneBasedIndices;
379};
380
388LoopNest genLoopNest(mlir::Location loc, fir::FirOpBuilder &builder,
389 mlir::ValueRange extents, bool isUnordered = false,
390 bool emitWorkshareLoop = false,
391 bool couldVectorize = true);
392inline LoopNest genLoopNest(mlir::Location loc, fir::FirOpBuilder &builder,
393 mlir::Value shape, bool isUnordered = false,
394 bool emitWorkshareLoop = false,
395 bool couldVectorize = true) {
396 return genLoopNest(loc, builder, getIndexExtents(loc, builder, shape),
397 isUnordered, emitWorkshareLoop, couldVectorize);
398}
399
406using ReductionLoopBodyGenerator = std::function<llvm::SmallVector<mlir::Value>(
407 mlir::Location, fir::FirOpBuilder &, mlir::ValueRange, mlir::ValueRange)>;
408
426llvm::SmallVector<mlir::Value> genLoopNestWithReductions(
427 mlir::Location loc, fir::FirOpBuilder &builder, mlir::ValueRange extents,
428 mlir::ValueRange reductionInits, const ReductionLoopBodyGenerator &genBody,
429 bool isUnordered = false);
430
436hlfir::YieldElementOp inlineElementalOp(mlir::Location loc,
437 fir::FirOpBuilder &builder,
438 hlfir::ElementalOp elemental,
439 mlir::ValueRange oneBasedIndices);
440
448mlir::Value inlineElementalOp(
449 mlir::Location loc, fir::FirOpBuilder &builder,
450 hlfir::ElementalOpInterface elemental, mlir::ValueRange oneBasedIndices,
451 mlir::IRMapping &mapper,
452 const std::function<bool(hlfir::ElementalOp)> &mustRecursivelyInline);
453
459void genNoAliasArrayAssignment(
460 mlir::Location loc, fir::FirOpBuilder &builder, hlfir::Entity rhs,
461 hlfir::Entity lhs, bool emitWorkshareLoop = false,
462 bool temporaryLHS = false,
463 std::function<hlfir::Entity(mlir::Location, fir::FirOpBuilder &,
464 hlfir::Entity, hlfir::Entity)> *combiner =
465 nullptr,
466 mlir::ArrayAttr accessGroups = {});
467
473void genNoAliasAssignment(
474 mlir::Location loc, fir::FirOpBuilder &builder, hlfir::Entity rhs,
475 hlfir::Entity lhs, bool emitWorkshareLoop = false,
476 bool temporaryLHS = false,
477 std::function<hlfir::Entity(mlir::Location, fir::FirOpBuilder &,
478 hlfir::Entity, hlfir::Entity)> *combiner =
479 nullptr,
480 mlir::ArrayAttr accessGroups = {});
481inline void genNoAliasAssignment(
482 mlir::Location loc, fir::FirOpBuilder &builder, hlfir::Entity rhs,
483 hlfir::Entity lhs, bool emitWorkshareLoop, bool temporaryLHS,
484 std::function<hlfir::Entity(mlir::Location, fir::FirOpBuilder &,
485 hlfir::Entity, hlfir::Entity)>
486 combiner,
487 mlir::ArrayAttr accessGroups = {}) {
488 genNoAliasAssignment(loc, builder, rhs, lhs, emitWorkshareLoop, temporaryLHS,
489 &combiner, accessGroups);
490}
491
496std::pair<hlfir::Entity, bool>
497computeEvaluateOpInNewTemp(mlir::Location, fir::FirOpBuilder &,
498 hlfir::EvaluateInMemoryOp evalInMem,
499 mlir::Value shape, mlir::ValueRange typeParams);
500
501// Clone the body of the hlfir.eval_in_mem operating on this the provided
502// storage. The provided storage must be a contiguous "raw" memory reference
503// (not a fir.box) big enough to hold the value computed by hlfir.eval_in_mem.
504// No runtime check is inserted by this utility to enforce that. It is also
505// usually invalid to provide some storage that is already addressed directly
506// or indirectly inside the hlfir.eval_in_mem body.
507void computeEvaluateOpIn(mlir::Location, fir::FirOpBuilder &,
508 hlfir::EvaluateInMemoryOp, mlir::Value storage);
509
510std::pair<fir::ExtendedValue, std::optional<hlfir::CleanupFunction>>
511convertToValue(mlir::Location loc, fir::FirOpBuilder &builder,
512 hlfir::Entity entity);
513
514std::pair<fir::ExtendedValue, std::optional<hlfir::CleanupFunction>>
515convertToAddress(mlir::Location loc, fir::FirOpBuilder &builder,
516 hlfir::Entity entity, mlir::Type targetType);
517
518std::pair<fir::ExtendedValue, std::optional<hlfir::CleanupFunction>>
519convertToBox(mlir::Location loc, fir::FirOpBuilder &builder,
520 hlfir::Entity entity, mlir::Type targetType);
521
523hlfir::ElementalOp cloneToElementalOp(mlir::Location loc,
524 fir::FirOpBuilder &builder,
525 hlfir::ElementalAddrOp elementalAddrOp);
526
532bool elementalOpMustProduceTemp(hlfir::ElementalOp elemental);
533
543std::pair<hlfir::Entity, bool> createTempFromMold(mlir::Location loc,
544 fir::FirOpBuilder &builder,
545 hlfir::Entity mold);
546
547// TODO: this does not support polymorphic molds
548hlfir::Entity createStackTempFromMold(mlir::Location loc,
549 fir::FirOpBuilder &builder,
550 hlfir::Entity mold);
551
552hlfir::EntityWithAttributes convertCharacterKind(mlir::Location loc,
553 fir::FirOpBuilder &builder,
554 hlfir::Entity scalarChar,
555 int toKind);
556
570std::pair<hlfir::Entity, std::optional<hlfir::CleanupFunction>>
571genTypeAndKindConvert(mlir::Location loc, fir::FirOpBuilder &builder,
572 hlfir::Entity source, mlir::Type toType,
573 bool preserveLowerBounds);
574
577Entity loadElementAt(mlir::Location loc, fir::FirOpBuilder &builder,
578 Entity entity, mlir::ValueRange oneBasedIndices);
579
583llvm::SmallVector<mlir::Value, Fortran::common::maxRank>
584genExtentsVector(mlir::Location loc, fir::FirOpBuilder &builder, Entity entity);
585
593Entity gen1DSection(mlir::Location loc, fir::FirOpBuilder &builder,
594 Entity array, int64_t dim,
595 mlir::ArrayRef<mlir::Value> lbounds,
596 mlir::ArrayRef<mlir::Value> extents,
597 mlir::ValueRange oneBasedIndices,
598 mlir::ArrayRef<mlir::Value> typeParams);
599
603bool designatePreservesContinuity(hlfir::DesignateOp op);
604
611bool isSimplyContiguous(mlir::Value base, bool checkWhole = true);
612
613} // namespace hlfir
614
615#endif // FORTRAN_OPTIMIZER_BUILDER_HLFIRTOOLS_H
This class provides a shared interface for box and class types.
Definition FIRType.h:40
Definition BoxValue.h:478
Definition FIRBuilder.h:55
Definition HLFIRTools.h:52
bool isAssumedRank() const
Is this an assumed ranked entity?
Definition HLFIRTools.h:78
int getRank() const
Return the rank of this entity or -1 if it is an assumed rank.
Definition HLFIRTools.h:81
bool isArray() const
Is this an array or an assumed ranked entity?
Definition HLFIRTools.h:75
bool isProcedure() const
Is this entity a procedure designator?
Definition HLFIRTools.h:72
fir::BaseBoxType getBoxType() const
Return the fir.class or fir.box type needed to describe this entity.
Definition HLFIRTools.h:103
Definition OpenACC.h:20
Definition AbstractConverter.h:37
bool isa_volatile_type(mlir::Type t)
Definition FIRType.cpp:747
mlir::Type unwrapPassByRefType(mlir::Type t)
Definition FIRType.h:292
bool isBoxAddress(mlir::Type t)
Is t an address to fir.box or class type?
Definition FIRType.h:512
bool isRecordWithTypeParameters(mlir::Type ty)
Return true iff ty is a RecordType with type parameters.
Definition FIRType.h:430
bool isa_trivial(mlir::Type t)
Definition FIRType.h:217
Definition AbstractConverter.h:32
Structure to describe a loop nest.
Definition HLFIRTools.h:375