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 const int64_t corank = fir::getBoxCorank(getType());
108 return fir::BoxType::get(getElementOrSequenceType(), isVolatile, corank);
109 }
110
111 bool hasLengthParameters() const {
112 mlir::Type eleTy = getFortranElementType();
113 return mlir::isa<fir::CharacterType>(eleTy) ||
115 }
116
117 bool isCharacter() const {
118 return mlir::isa<fir::CharacterType>(getFortranElementType());
119 }
120
121 bool hasIntrinsicType() const {
122 mlir::Type eleTy = getFortranElementType();
123 return fir::isa_trivial(eleTy) || mlir::isa<fir::CharacterType>(eleTy);
124 }
125
126 bool isDerivedWithLengthParameters() const {
127 return fir::isRecordWithTypeParameters(getFortranElementType());
128 }
129
130 bool mayHaveNonDefaultLowerBounds() const;
131
132 // Is this entity known to be contiguous at compile time?
133 // Note that when this returns false, the entity may still
134 // turn-out to be contiguous at runtime.
135 bool isSimplyContiguous() const {
136 // If this can be described without a fir.box in FIR, this must
137 // be contiguous.
138 if (!hlfir::isBoxAddressOrValueType(getFirBase().getType()) || isScalar())
139 return true;
140 // Otherwise, if this entity has a visible declaration in FIR,
141 // or is the dereference of an allocatable or contiguous pointer
142 // it is simply contiguous.
143 if (auto varIface = getMaybeDereferencedVariableInterface())
144 return varIface.isAllocatable() || varIface.hasContiguousAttr();
145 return false;
146 }
147
148 fir::FortranVariableOpInterface getIfVariableInterface() const {
149 return this->getDefiningOp<fir::FortranVariableOpInterface>();
150 }
151
152 // Return a "declaration" operation for this variable if visible,
153 // or the "declaration" operation of the allocatable/pointer this
154 // variable was dereferenced from (if it is visible).
155 fir::FortranVariableOpInterface
156 getMaybeDereferencedVariableInterface() const {
157 mlir::Value base = *this;
158 if (auto loadOp = base.getDefiningOp<fir::LoadOp>())
159 base = loadOp.getMemref();
160 return base.getDefiningOp<fir::FortranVariableOpInterface>();
161 }
162
163 bool mayBeOptional() const;
164
165 bool isParameter() const {
166 auto varIface = getIfVariableInterface();
167 return varIface ? varIface.isParameter() : false;
168 }
169
170 bool isAllocatable() const {
171 auto varIface = getIfVariableInterface();
172 return varIface ? varIface.isAllocatable() : false;
173 }
174
175 bool isPointer() const {
176 auto varIface = getIfVariableInterface();
177 return varIface ? varIface.isPointer() : false;
178 }
179
180 // Get the entity as an mlir SSA value containing all the shape, type
181 // parameters and dynamic shape information.
182 mlir::Value getBase() const { return *this; }
183
184 // Get the entity as a FIR base. This may not carry the shape and type
185 // parameters information, and even when it is a box with shape information.
186 // it will not contain the local lower bounds of the entity. This should
187 // be used with care when generating FIR code that does not need this
188 // information, or has access to it in other ways. Its advantage is that
189 // it will never be a fir.box for explicit shape arrays, leading to simpler
190 // FIR code generation.
191 mlir::Value getFirBase() const;
192};
193
199class EntityWithAttributes : public Entity {
200public:
201 explicit EntityWithAttributes(mlir::Value value) : Entity(value) {
202 assert(isFortranEntityWithAttributes(value) &&
203 "must be a value representing a Fortran value or variable");
204 }
205 EntityWithAttributes(fir::FortranVariableOpInterface variable)
206 : Entity(variable) {}
207 fir::FortranVariableOpInterface getIfVariable() const {
208 return getIfVariableInterface();
209 }
210};
211
217using CleanupFunction = std::function<void()>;
218std::pair<fir::ExtendedValue, std::optional<CleanupFunction>>
219translateToExtendedValue(mlir::Location loc, fir::FirOpBuilder &builder,
220 Entity entity, bool contiguousHint = false,
221 bool keepScalarOptionalBoxed = false);
222
227translateToExtendedValue(mlir::Location loc, fir::FirOpBuilder &builder,
228 fir::FortranVariableOpInterface fortranVariable,
229 bool forceHlfirBase = false);
230
232fir::FortranVariableOpInterface
233genDeclare(mlir::Location loc, fir::FirOpBuilder &builder,
234 const fir::ExtendedValue &exv, llvm::StringRef name,
235 fir::FortranVariableFlagsAttr flags,
236 mlir::Value dummyScope = nullptr, mlir::Value storage = nullptr,
237 std::uint64_t storageOffset = 0,
238 cuf::DataAttributeAttr dataAttr = {}, unsigned dummyArgNo = 0);
239
243hlfir::AssociateOp
244genAssociateExpr(mlir::Location loc, fir::FirOpBuilder &builder,
245 hlfir::Entity value, mlir::Type variableType,
246 llvm::StringRef name,
247 std::optional<mlir::NamedAttribute> attr = std::nullopt);
248
253mlir::Value genVariableRawAddress(mlir::Location loc,
254 fir::FirOpBuilder &builder,
255 hlfir::Entity var);
256
259mlir::Value genVariableBoxChar(mlir::Location loc, fir::FirOpBuilder &builder,
260 hlfir::Entity var);
261
265hlfir::Entity genVariableBox(mlir::Location loc, fir::FirOpBuilder &builder,
266 hlfir::Entity var,
267 fir::BaseBoxType forceBoxType = {});
268
273Entity loadTrivialScalar(mlir::Location loc, fir::FirOpBuilder &builder,
274 Entity entity);
275
278hlfir::Entity derefPointersAndAllocatables(mlir::Location loc,
279 fir::FirOpBuilder &builder,
280 Entity entity);
281
285hlfir::Entity getElementAt(mlir::Location loc, fir::FirOpBuilder &builder,
286 Entity entity, mlir::ValueRange oneBasedIndices);
288llvm::SmallVector<std::pair<mlir::Value, mlir::Value>>
289genBounds(mlir::Location loc, fir::FirOpBuilder &builder, Entity entity);
292llvm::SmallVector<std::pair<mlir::Value, mlir::Value>>
293genBounds(mlir::Location loc, fir::FirOpBuilder &builder, mlir::Value shape);
294
297llvm::SmallVector<mlir::Value> genLowerbounds(mlir::Location loc,
298 fir::FirOpBuilder &builder,
299 mlir::Value shape, unsigned rank);
300
302mlir::Value genShape(mlir::Location loc, fir::FirOpBuilder &builder,
303 Entity entity);
304
307mlir::Value genExtent(mlir::Location loc, fir::FirOpBuilder &builder,
308 hlfir::Entity entity, unsigned dim);
309
312mlir::Value genLBound(mlir::Location loc, fir::FirOpBuilder &builder,
313 hlfir::Entity entity, unsigned dim);
314
317llvm::SmallVector<mlir::Value> getIndexExtents(mlir::Location loc,
318 fir::FirOpBuilder &builder,
319 mlir::Value shape);
320
323llvm::SmallVector<mlir::Value>
324getExplicitExtentsFromShape(mlir::Value shape, fir::FirOpBuilder &builder);
325
327void genLengthParameters(mlir::Location loc, fir::FirOpBuilder &builder,
328 Entity entity,
329 llvm::SmallVectorImpl<mlir::Value> &result);
330
333mlir::Value genCharLength(mlir::Location loc, fir::FirOpBuilder &builder,
334 Entity entity);
335
338std::optional<std::int64_t> getCharLengthIfConst(Entity entity);
339
340mlir::Value genRank(mlir::Location loc, fir::FirOpBuilder &builder,
341 Entity entity, mlir::Type resultType);
342
347std::pair<mlir::Value, mlir::Value> genVariableFirBaseShapeAndParams(
348 mlir::Location loc, fir::FirOpBuilder &builder, Entity entity,
349 llvm::SmallVectorImpl<mlir::Value> &typeParams);
350
354mlir::Type getVariableElementType(hlfir::Entity variable);
359mlir::Type getEntityElementType(hlfir::Entity entity);
360
361using ElementalKernelGenerator = std::function<hlfir::Entity(
362 mlir::Location, fir::FirOpBuilder &, mlir::ValueRange)>;
369hlfir::ElementalOp genElementalOp(
370 mlir::Location loc, fir::FirOpBuilder &builder, mlir::Type elementType,
371 mlir::Value shape, mlir::ValueRange typeParams,
372 const ElementalKernelGenerator &genKernel, bool isUnordered = false,
373 mlir::Value polymorphicMold = {}, mlir::Type exprType = mlir::Type{});
374
376struct LoopNest {
377 mlir::Operation *outerOp = nullptr;
378 mlir::Block *body = nullptr;
379 llvm::SmallVector<mlir::Value> oneBasedIndices;
380};
381
389LoopNest genLoopNest(mlir::Location loc, fir::FirOpBuilder &builder,
390 mlir::ValueRange extents, bool isUnordered = false,
391 bool emitWorkshareLoop = false,
392 bool couldVectorize = true);
393inline LoopNest genLoopNest(mlir::Location loc, fir::FirOpBuilder &builder,
394 mlir::Value shape, bool isUnordered = false,
395 bool emitWorkshareLoop = false,
396 bool couldVectorize = true) {
397 return genLoopNest(loc, builder, getIndexExtents(loc, builder, shape),
398 isUnordered, emitWorkshareLoop, couldVectorize);
399}
400
407using ReductionLoopBodyGenerator = std::function<llvm::SmallVector<mlir::Value>(
408 mlir::Location, fir::FirOpBuilder &, mlir::ValueRange, mlir::ValueRange)>;
409
427llvm::SmallVector<mlir::Value> genLoopNestWithReductions(
428 mlir::Location loc, fir::FirOpBuilder &builder, mlir::ValueRange extents,
429 mlir::ValueRange reductionInits, const ReductionLoopBodyGenerator &genBody,
430 bool isUnordered = false);
431
437hlfir::YieldElementOp inlineElementalOp(mlir::Location loc,
438 fir::FirOpBuilder &builder,
439 hlfir::ElementalOp elemental,
440 mlir::ValueRange oneBasedIndices);
441
449mlir::Value inlineElementalOp(
450 mlir::Location loc, fir::FirOpBuilder &builder,
451 hlfir::ElementalOpInterface elemental, mlir::ValueRange oneBasedIndices,
452 mlir::IRMapping &mapper,
453 const std::function<bool(hlfir::ElementalOp)> &mustRecursivelyInline);
454
465void genNoAliasArrayAssignment(
466 mlir::Location loc, fir::FirOpBuilder &builder, hlfir::Entity rhs,
467 hlfir::Entity lhs, bool emitWorkshareLoop = false,
468 bool temporaryLHS = false,
469 std::function<void(mlir::Location, fir::FirOpBuilder &, hlfir::Entity,
470 hlfir::Entity, mlir::ArrayAttr)>
471 *scalarCombineAndAssign = nullptr,
472 mlir::ArrayAttr accessGroups = {});
473
479void genNoAliasAssignment(
480 mlir::Location loc, fir::FirOpBuilder &builder, hlfir::Entity rhs,
481 hlfir::Entity lhs, bool emitWorkshareLoop = false,
482 bool temporaryLHS = false,
483 std::function<void(mlir::Location, fir::FirOpBuilder &, hlfir::Entity,
484 hlfir::Entity, mlir::ArrayAttr accessGroups)>
485 *scalarCombineAndAssign = nullptr,
486 mlir::ArrayAttr accessGroups = {});
487inline void genNoAliasAssignment(
488 mlir::Location loc, fir::FirOpBuilder &builder, hlfir::Entity rhs,
489 hlfir::Entity lhs, bool emitWorkshareLoop, bool temporaryLHS,
490 std::function<void(mlir::Location, fir::FirOpBuilder &, hlfir::Entity,
491 hlfir::Entity, mlir::ArrayAttr)>
492 scalarCombineAndAssign,
493 mlir::ArrayAttr accessGroups = {}) {
494 genNoAliasAssignment(loc, builder, rhs, lhs, emitWorkshareLoop, temporaryLHS,
495 &scalarCombineAndAssign, accessGroups);
496}
497
502std::pair<hlfir::Entity, bool>
503computeEvaluateOpInNewTemp(mlir::Location, fir::FirOpBuilder &,
504 hlfir::EvaluateInMemoryOp evalInMem,
505 mlir::Value shape, mlir::ValueRange typeParams);
506
507// Clone the body of the hlfir.eval_in_mem operating on this the provided
508// storage. The provided storage must be a contiguous "raw" memory reference
509// (not a fir.box) big enough to hold the value computed by hlfir.eval_in_mem.
510// No runtime check is inserted by this utility to enforce that. It is also
511// usually invalid to provide some storage that is already addressed directly
512// or indirectly inside the hlfir.eval_in_mem body.
513void computeEvaluateOpIn(mlir::Location, fir::FirOpBuilder &,
514 hlfir::EvaluateInMemoryOp, mlir::Value storage);
515
516std::pair<fir::ExtendedValue, std::optional<hlfir::CleanupFunction>>
517convertToValue(mlir::Location loc, fir::FirOpBuilder &builder,
518 hlfir::Entity entity);
519
520std::pair<fir::ExtendedValue, std::optional<hlfir::CleanupFunction>>
521convertToAddress(mlir::Location loc, fir::FirOpBuilder &builder,
522 hlfir::Entity entity, mlir::Type targetType);
523
524std::pair<fir::ExtendedValue, std::optional<hlfir::CleanupFunction>>
525convertToBox(mlir::Location loc, fir::FirOpBuilder &builder,
526 hlfir::Entity entity, mlir::Type targetType, unsigned corank = 0);
527
529hlfir::ElementalOp cloneToElementalOp(mlir::Location loc,
530 fir::FirOpBuilder &builder,
531 hlfir::ElementalAddrOp elementalAddrOp);
532
538bool elementalOpMustProduceTemp(hlfir::ElementalOp elemental);
539
549std::pair<hlfir::Entity, bool> createTempFromMold(mlir::Location loc,
550 fir::FirOpBuilder &builder,
551 hlfir::Entity mold);
552
553// TODO: this does not support polymorphic molds
554hlfir::Entity createStackTempFromMold(mlir::Location loc,
555 fir::FirOpBuilder &builder,
556 hlfir::Entity mold);
557
558hlfir::EntityWithAttributes convertCharacterKind(mlir::Location loc,
559 fir::FirOpBuilder &builder,
560 hlfir::Entity scalarChar,
561 int toKind);
562
576std::pair<hlfir::Entity, std::optional<hlfir::CleanupFunction>>
577genTypeAndKindConvert(mlir::Location loc, fir::FirOpBuilder &builder,
578 hlfir::Entity source, mlir::Type toType,
579 bool preserveLowerBounds);
580
583Entity loadElementAt(mlir::Location loc, fir::FirOpBuilder &builder,
584 Entity entity, mlir::ValueRange oneBasedIndices);
585
589llvm::SmallVector<mlir::Value, Fortran::common::maxRank>
590genExtentsVector(mlir::Location loc, fir::FirOpBuilder &builder, Entity entity);
591
599Entity gen1DSection(mlir::Location loc, fir::FirOpBuilder &builder,
600 Entity array, int64_t dim,
601 mlir::ArrayRef<mlir::Value> extents,
602 mlir::ValueRange oneBasedIndices,
603 mlir::ArrayRef<mlir::Value> typeParams);
604
608bool designatePreservesContinuity(hlfir::DesignateOp op);
609
616bool isSimplyContiguous(mlir::Value base, bool checkWhole = true);
617
620bool isInsideHlfirWhereMaskedExpression(mlir::Region &region);
621
622} // namespace hlfir
623
624#endif // FORTRAN_OPTIMIZER_BUILDER_HLFIRTOOLS_H
This class provides a shared interface for box and class types.
Definition FIRType.h:40
Definition BoxValue.h:480
Definition FIRBuilder.h:59
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:766
mlir::Type unwrapPassByRefType(mlir::Type t)
Definition FIRType.h:307
bool isBoxAddress(mlir::Type t)
Is t an address to fir.box or class type?
Definition FIRType.h:528
unsigned getBoxCorank(mlir::Type boxTy)
Get the corank from a !fir.box type.
Definition FIRType.cpp:488
bool isRecordWithTypeParameters(mlir::Type ty)
Return true iff ty is a RecordType with type parameters.
Definition FIRType.h:445
bool isa_trivial(mlir::Type t)
Definition FIRType.h:232
Definition AbstractConverter.h:32
Structure to describe a loop nest.
Definition HLFIRTools.h:376