Warning: This file is not a C or C++ file. It does not have highlighting.

1//===-- FirBuilder.h -- FIR operation builder -------------------*- 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// Builder routines for constructing the FIR dialect of MLIR. As FIR is a
10// dialect of MLIR, it makes extensive use of MLIR interfaces and MLIR's coding
11// style (https://mlir.llvm.org/getting_started/DeveloperGuide/) is used in this
12// module.
13//
14//===----------------------------------------------------------------------===//
15
16#ifndef FORTRAN_OPTIMIZER_BUILDER_FIRBUILDER_H
17#define FORTRAN_OPTIMIZER_BUILDER_FIRBUILDER_H
18
19#include "flang/Optimizer/Dialect/FIROps.h"
20#include "flang/Optimizer/Dialect/FIROpsSupport.h"
21#include "flang/Optimizer/Dialect/FIRType.h"
22#include "flang/Optimizer/Dialect/Support/FIRContext.h"
23#include "flang/Optimizer/Dialect/Support/KindMapping.h"
24#include "flang/Support/MathOptionsBase.h"
25#include "mlir/IR/Builders.h"
26#include "mlir/IR/BuiltinOps.h"
27#include "llvm/ADT/DenseMap.h"
28#include <optional>
29#include <utility>
30
31namespace mlir {
32class DataLayout;
33class SymbolTable;
34}
35
36namespace fir {
37class AbstractArrayBox;
38class ExtendedValue;
39class MutableBoxValue;
40class BoxValue;
41
42/// Get the integer type with a pointer size.
43inline mlir::Type getIntPtrType(mlir::OpBuilder &builder) {
44 // TODO: Delay the need of such type until codegen or find a way to use
45 // llvm::DataLayout::getPointerSizeInBits here.
46 return builder.getI64Type();
47}
48
49//===----------------------------------------------------------------------===//
50// FirOpBuilder
51//===----------------------------------------------------------------------===//
52
53/// Extends the MLIR OpBuilder to provide methods for building common FIR
54/// patterns.
55class FirOpBuilder : public mlir::OpBuilder, public mlir::OpBuilder::Listener {
56public:
57 explicit FirOpBuilder(mlir::Operation *op, fir::KindMapping kindMap,
58 mlir::SymbolTable *symbolTable = nullptr)
59 : OpBuilder{op, /*listener=*/this}, kindMap{std::move(kindMap)},
60 symbolTable{symbolTable} {
61 auto fmi = mlir::dyn_cast<mlir::arith::ArithFastMathInterface>(*op);
62 if (fmi) {
63 // Set the builder with FastMathFlags attached to the operation.
64 setFastMathFlags(fmi.getFastMathFlagsAttr().getValue());
65 }
66 }
67 explicit FirOpBuilder(mlir::OpBuilder &builder, fir::KindMapping kindMap,
68 mlir::SymbolTable *symbolTable = nullptr)
69 : OpBuilder(builder), OpBuilder::Listener(), kindMap{std::move(kindMap)},
70 symbolTable{symbolTable} {
71 setListener(this);
72 }
73 explicit FirOpBuilder(mlir::OpBuilder &builder, mlir::ModuleOp mod)
74 : OpBuilder(builder), OpBuilder::Listener(),
75 kindMap{getKindMapping(mod)} {
76 setListener(this);
77 }
78 explicit FirOpBuilder(mlir::OpBuilder &builder, fir::KindMapping kindMap,
79 mlir::Operation *op)
80 : OpBuilder(builder), OpBuilder::Listener(), kindMap{std::move(kindMap)} {
81 setListener(this);
82 auto fmi = mlir::dyn_cast<mlir::arith::ArithFastMathInterface>(*op);
83 if (fmi) {
84 // Set the builder with FastMathFlags attached to the operation.
85 setFastMathFlags(fmi.getFastMathFlagsAttr().getValue());
86 }
87 }
88 FirOpBuilder(mlir::OpBuilder &builder, mlir::Operation *op)
89 : FirOpBuilder(builder, fir::getKindMapping(op), op) {}
90
91 // The listener self-reference has to be updated in case of copy-construction.
92 FirOpBuilder(const FirOpBuilder &other)
93 : OpBuilder(other), OpBuilder::Listener(), kindMap{other.kindMap},
94 fastMathFlags{other.fastMathFlags},
95 integerOverflowFlags{other.integerOverflowFlags},
96 symbolTable{other.symbolTable} {
97 setListener(this);
98 }
99
100 FirOpBuilder(FirOpBuilder &&other)
101 : OpBuilder(other), OpBuilder::Listener(),
102 kindMap{std::move(other.kindMap)}, fastMathFlags{other.fastMathFlags},
103 integerOverflowFlags{other.integerOverflowFlags},
104 symbolTable{other.symbolTable} {
105 setListener(this);
106 }
107
108 /// Get the current Region of the insertion point.
109 mlir::Region &getRegion() { return *getBlock()->getParent(); }
110
111 /// Get the current Module
112 mlir::ModuleOp getModule() {
113 return getRegion().getParentOfType<mlir::ModuleOp>();
114 }
115
116 /// Get the current Function
117 mlir::func::FuncOp getFunction() {
118 return getRegion().getParentOfType<mlir::func::FuncOp>();
119 }
120
121 /// Get a reference to the kind map.
122 const fir::KindMapping &getKindMap() { return kindMap; }
123
124 /// Get func.func/fir.global symbol table attached to this builder if any.
125 mlir::SymbolTable *getMLIRSymbolTable() { return symbolTable; }
126
127 /// Get the default integer type
128 [[maybe_unused]] mlir::IntegerType getDefaultIntegerType() {
129 return getIntegerType(
130 getKindMap().getIntegerBitsize(getKindMap().defaultIntegerKind()));
131 }
132
133 /// The LHS and RHS are not always in agreement in terms of type. In some
134 /// cases, the disagreement is between COMPLEX and other scalar types. In that
135 /// case, the conversion must insert (extract) out of a COMPLEX value to have
136 /// the proper semantics and be strongly typed. E.g., converting an integer
137 /// (real) to a complex, the real part is filled using the integer (real)
138 /// after type conversion and the imaginary part is zero.
139 mlir::Value convertWithSemantics(mlir::Location loc, mlir::Type toTy,
140 mlir::Value val,
141 bool allowCharacterConversion = false,
142 bool allowRebox = false);
143
144 /// Get the entry block of the current Function
145 mlir::Block *getEntryBlock() { return &getFunction().front(); }
146
147 /// Get the block for adding Allocas. If OpenMP is enabled then get the
148 /// the alloca block from an Operation which can be Outlined. Otherwise
149 /// use the entry block of the current Function
150 mlir::Block *getAllocaBlock();
151
152 /// Safely create a reference type to the type `eleTy`.
153 mlir::Type getRefType(mlir::Type eleTy, bool isVolatile = false);
154
155 /// Create a sequence of `eleTy` with `rank` dimensions of unknown size.
156 mlir::Type getVarLenSeqTy(mlir::Type eleTy, unsigned rank = 1);
157
158 /// Get character length type.
159 mlir::Type getCharacterLengthType() { return getIndexType(); }
160
161 /// Get the integer type whose bit width corresponds to the width of pointer
162 /// types, or is bigger.
163 mlir::Type getIntPtrType() { return fir::getIntPtrType(*this); }
164
165 /// Wrap `str` to a SymbolRefAttr.
166 mlir::SymbolRefAttr getSymbolRefAttr(llvm::StringRef str) {
167 return mlir::SymbolRefAttr::get(getContext(), str);
168 }
169
170 /// Get the mlir float type that implements Fortran REAL(kind).
171 mlir::Type getRealType(int kind);
172
173 fir::BoxProcType getBoxProcType(mlir::FunctionType funcTy) {
174 return fir::BoxProcType::get(getContext(), funcTy);
175 }
176
177 /// Create a null constant memory reference of type \p ptrType.
178 /// If \p ptrType is not provided, !fir.ref<none> type will be used.
179 mlir::Value createNullConstant(mlir::Location loc, mlir::Type ptrType = {});
180
181 /// Create an integer constant of type \p type and value \p i.
182 /// Should not be used with negative values with integer types of more
183 /// than 64 bits.
184 mlir::Value createIntegerConstant(mlir::Location loc, mlir::Type integerType,
185 std::int64_t i);
186
187 /// Create an integer of \p integerType where all the bits have been set to
188 /// ones. Safe to use regardless of integerType bitwidth.
189 mlir::Value createAllOnesInteger(mlir::Location loc, mlir::Type integerType);
190
191 /// Create -1 constant of \p integerType. Safe to use regardless of
192 /// integerType bitwidth.
193 mlir::Value createMinusOneInteger(mlir::Location loc,
194 mlir::Type integerType) {
195 return createAllOnesInteger(loc, integerType);
196 }
197
198 /// Create a real constant from an integer value.
199 mlir::Value createRealConstant(mlir::Location loc, mlir::Type realType,
200 llvm::APFloat::integerPart val);
201
202 /// Create a real constant from an APFloat value.
203 mlir::Value createRealConstant(mlir::Location loc, mlir::Type realType,
204 const llvm::APFloat &val);
205
206 /// Create a real constant of type \p realType with a value zero.
207 mlir::Value createRealZeroConstant(mlir::Location loc, mlir::Type realType) {
208 return createRealConstant(loc, realType, 0u);
209 }
210
211 /// Create a slot for a local on the stack. Besides the variable's type and
212 /// shape, it may be given name, pinned, or target attributes.
213 mlir::Value allocateLocal(mlir::Location loc, mlir::Type ty,
214 llvm::StringRef uniqName, llvm::StringRef name,
215 bool pinned, llvm::ArrayRef<mlir::Value> shape,
216 llvm::ArrayRef<mlir::Value> lenParams,
217 bool asTarget = false);
218 mlir::Value allocateLocal(mlir::Location loc, mlir::Type ty,
219 llvm::StringRef uniqName, llvm::StringRef name,
220 llvm::ArrayRef<mlir::Value> shape,
221 llvm::ArrayRef<mlir::Value> lenParams,
222 bool asTarget = false);
223
224 /// Create a two dimensional ArrayAttr containing integer data as
225 /// IntegerAttrs, effectively: ArrayAttr<ArrayAttr<IntegerAttr>>>.
226 mlir::ArrayAttr create2DI64ArrayAttr(
227 llvm::SmallVectorImpl<llvm::SmallVector<int64_t>> &intData);
228
229 /// Create a temporary using `fir.alloca`. This function does not hoist.
230 /// It is the callers responsibility to set the insertion point if
231 /// hoisting is required.
232 mlir::Value createTemporaryAlloc(
233 mlir::Location loc, mlir::Type type, llvm::StringRef name,
234 mlir::ValueRange lenParams = {}, mlir::ValueRange shape = {},
235 llvm::ArrayRef<mlir::NamedAttribute> attrs = {},
236 std::optional<Fortran::common::CUDADataAttr> cudaAttr = std::nullopt);
237
238 /// Create a temporary. A temp is allocated using `fir.alloca` and can be read
239 /// and written using `fir.load` and `fir.store`, resp. The temporary can be
240 /// given a name via a front-end `Symbol` or a `StringRef`.
241 mlir::Value createTemporary(
242 mlir::Location loc, mlir::Type type, llvm::StringRef name = {},
243 mlir::ValueRange shape = {}, mlir::ValueRange lenParams = {},
244 llvm::ArrayRef<mlir::NamedAttribute> attrs = {},
245 std::optional<Fortran::common::CUDADataAttr> cudaAttr = std::nullopt);
246
247 /// Create an unnamed and untracked temporary on the stack.
248 mlir::Value createTemporary(mlir::Location loc, mlir::Type type,
249 mlir::ValueRange shape) {
250 return createTemporary(loc, type, llvm::StringRef{}, shape);
251 }
252
253 mlir::Value createTemporary(mlir::Location loc, mlir::Type type,
254 llvm::ArrayRef<mlir::NamedAttribute> attrs) {
255 return createTemporary(loc, type, llvm::StringRef{}, {}, {}, attrs);
256 }
257
258 mlir::Value createTemporary(mlir::Location loc, mlir::Type type,
259 llvm::StringRef name,
260 llvm::ArrayRef<mlir::NamedAttribute> attrs) {
261 return createTemporary(loc, type, name, {}, {}, attrs);
262 }
263
264 /// Create a temporary on the heap.
265 mlir::Value
266 createHeapTemporary(mlir::Location loc, mlir::Type type,
267 llvm::StringRef name = {}, mlir::ValueRange shape = {},
268 mlir::ValueRange lenParams = {},
269 llvm::ArrayRef<mlir::NamedAttribute> attrs = {});
270
271 /// Sample genDeclare callback for createArrayTemp() below.
272 /// It creates fir.declare operation using the given operands.
273 /// \p memref is the base of the allocated temporary,
274 /// which may be !fir.ref<!fir.array<>> or !fir.box/class<>.
275 static mlir::Value genTempDeclareOp(fir::FirOpBuilder &builder,
276 mlir::Location loc, mlir::Value memref,
277 llvm::StringRef name, mlir::Value shape,
278 llvm::ArrayRef<mlir::Value> typeParams,
279 fir::FortranVariableFlagsAttr attrs);
280
281 /// Create a temporary with the given \p baseType,
282 /// \p shape, \p extents and \p typeParams. An optional
283 /// \p polymorphicMold specifies the entity which dynamic type
284 /// has to be used for the allocation.
285 /// \p genDeclare callback generates a declare operation
286 /// for the created temporary. FIR passes may use genTempDeclareOp()
287 /// function above that creates fir.declare.
288 /// HLFIR passes may provide their own callback that generates
289 /// hlfir.declare. Some passes may provide a callback that
290 /// just passes through the base of the temporary.
291 /// If \p useStack is true, the function will try to do the allocation
292 /// in stack memory (which is not always possible currently).
293 /// The first return value is the base of the temporary object,
294 /// which may be !fir.ref<!fir.array<>> or !fir.box/class<>.
295 /// The second return value is true, if the actual allocation
296 /// was done in heap memory.
297 std::pair<mlir::Value, bool> createAndDeclareTemp(
298 mlir::Location loc, mlir::Type baseType, mlir::Value shape,
299 llvm::ArrayRef<mlir::Value> extents,
300 llvm::ArrayRef<mlir::Value> typeParams,
301 const std::function<decltype(genTempDeclareOp)> &genDeclare,
302 mlir::Value polymorphicMold, bool useStack, llvm::StringRef tmpName);
303 /// Create and declare an array temporary.
304 std::pair<mlir::Value, bool>
305 createArrayTemp(mlir::Location loc, fir::SequenceType arrayType,
306 mlir::Value shape, llvm::ArrayRef<mlir::Value> extents,
307 llvm::ArrayRef<mlir::Value> typeParams,
308 const std::function<decltype(genTempDeclareOp)> &genDeclare,
309 mlir::Value polymorphicMold, bool useStack = false,
310 llvm::StringRef tmpName = ".tmp.array") {
311 return createAndDeclareTemp(loc, arrayType, shape, extents, typeParams,
312 genDeclare, polymorphicMold, useStack, tmpName);
313 }
314
315 /// Create an LLVM stack save intrinsic op. Returns the saved stack pointer.
316 /// The stack address space is fetched from the data layout of the current
317 /// module.
318 mlir::Value genStackSave(mlir::Location loc);
319
320 /// Create an LLVM stack restore intrinsic op. stackPointer should be a value
321 /// previously returned from genStackSave.
322 void genStackRestore(mlir::Location loc, mlir::Value stackPointer);
323
324 /// Create a global value.
325 fir::GlobalOp createGlobal(mlir::Location loc, mlir::Type type,
326 llvm::StringRef name,
327 mlir::StringAttr linkage = {},
328 mlir::Attribute value = {}, bool isConst = false,
329 bool isTarget = false,
330 cuf::DataAttributeAttr dataAttr = {});
331
332 fir::GlobalOp createGlobal(mlir::Location loc, mlir::Type type,
333 llvm::StringRef name, bool isConst, bool isTarget,
334 std::function<void(FirOpBuilder &)> bodyBuilder,
335 mlir::StringAttr linkage = {},
336 cuf::DataAttributeAttr dataAttr = {});
337
338 /// Create a global constant (read-only) value.
339 fir::GlobalOp createGlobalConstant(mlir::Location loc, mlir::Type type,
340 llvm::StringRef name,
341 mlir::StringAttr linkage = {},
342 mlir::Attribute value = {}) {
343 return createGlobal(loc, type, name, linkage, value, /*isConst=*/true,
344 /*isTarget=*/false);
345 }
346
347 fir::GlobalOp
348 createGlobalConstant(mlir::Location loc, mlir::Type type,
349 llvm::StringRef name,
350 std::function<void(FirOpBuilder &)> bodyBuilder,
351 mlir::StringAttr linkage = {}) {
352 return createGlobal(loc, type, name, /*isConst=*/true, /*isTarget=*/false,
353 bodyBuilder, linkage);
354 }
355
356 /// Convert a StringRef string into a fir::StringLitOp.
357 fir::StringLitOp createStringLitOp(mlir::Location loc,
358 llvm::StringRef string);
359
360 std::pair<fir::TypeInfoOp, mlir::OpBuilder::InsertPoint>
361 createTypeInfoOp(mlir::Location loc, fir::RecordType recordType,
362 fir::RecordType parentType);
363
364 //===--------------------------------------------------------------------===//
365 // Linkage helpers (inline). The default linkage is external.
366 //===--------------------------------------------------------------------===//
367
368 mlir::StringAttr createCommonLinkage() { return getStringAttr("common"); }
369
370 mlir::StringAttr createInternalLinkage() { return getStringAttr("internal"); }
371
372 mlir::StringAttr createLinkOnceLinkage() { return getStringAttr("linkonce"); }
373
374 mlir::StringAttr createLinkOnceODRLinkage() {
375 return getStringAttr("linkonce_odr");
376 }
377
378 mlir::StringAttr createWeakLinkage() { return getStringAttr("weak"); }
379
380 /// Get a function by name. If the function exists in the current module, it
381 /// is returned. Otherwise, a null FuncOp is returned.
382 mlir::func::FuncOp getNamedFunction(llvm::StringRef name) {
383 return getNamedFunction(getModule(), getMLIRSymbolTable(), name);
384 }
385 static mlir::func::FuncOp
386 getNamedFunction(mlir::ModuleOp module, const mlir::SymbolTable *symbolTable,
387 llvm::StringRef name);
388
389 /// Get a function by symbol name. The result will be null if there is no
390 /// function with the given symbol in the module.
391 mlir::func::FuncOp getNamedFunction(mlir::SymbolRefAttr symbol) {
392 return getNamedFunction(getModule(), getMLIRSymbolTable(), symbol);
393 }
394 static mlir::func::FuncOp
395 getNamedFunction(mlir::ModuleOp module, const mlir::SymbolTable *symbolTable,
396 mlir::SymbolRefAttr symbol);
397
398 fir::GlobalOp getNamedGlobal(llvm::StringRef name) {
399 return getNamedGlobal(getModule(), getMLIRSymbolTable(), name);
400 }
401
402 static fir::GlobalOp getNamedGlobal(mlir::ModuleOp module,
403 const mlir::SymbolTable *symbolTable,
404 llvm::StringRef name);
405
406 /// Lazy creation of fir.convert op.
407 mlir::Value createConvert(mlir::Location loc, mlir::Type toTy,
408 mlir::Value val);
409
410 /// Create a fir.convert op with a volatile cast if the source value's type
411 /// does not match the target type's volatility.
412 mlir::Value createConvertWithVolatileCast(mlir::Location loc, mlir::Type toTy,
413 mlir::Value val);
414
415 /// Cast \p value to have \p isVolatile volatility.
416 mlir::Value createVolatileCast(mlir::Location loc, bool isVolatile,
417 mlir::Value value);
418
419 /// Create a fir.store of \p val into \p addr. A lazy conversion
420 /// of \p val to the element type of \p addr is created if needed.
421 void createStoreWithConvert(mlir::Location loc, mlir::Value val,
422 mlir::Value addr);
423
424 /// Create a fir.load if \p val is a reference or pointer type. Return the
425 /// result of the load if it was created, otherwise return \p val
426 mlir::Value loadIfRef(mlir::Location loc, mlir::Value val);
427
428 /// Determine if the named function is already in the module. Return the
429 /// instance if found, otherwise add a new named function to the module.
430 mlir::func::FuncOp createFunction(mlir::Location loc, llvm::StringRef name,
431 mlir::FunctionType ty) {
432 return createFunction(loc, getModule(), name, ty, getMLIRSymbolTable());
433 }
434
435 static mlir::func::FuncOp createFunction(mlir::Location loc,
436 mlir::ModuleOp module,
437 llvm::StringRef name,
438 mlir::FunctionType ty,
439 mlir::SymbolTable *);
440
441 /// Returns a named function for a Fortran runtime API, creating
442 /// it, if it does not exist in the module yet.
443 /// If \p isIO is set to true, then the function corresponds
444 /// to one of Fortran runtime IO APIs.
445 mlir::func::FuncOp createRuntimeFunction(mlir::Location loc,
446 llvm::StringRef name,
447 mlir::FunctionType ty,
448 bool isIO = false);
449
450 /// Cast the input value to IndexType.
451 mlir::Value convertToIndexType(mlir::Location loc, mlir::Value val) {
452 return createConvert(loc, getIndexType(), val);
453 }
454
455 /// Construct one of the two forms of shape op from an array box.
456 mlir::Value genShape(mlir::Location loc, const fir::AbstractArrayBox &arr);
457 mlir::Value genShape(mlir::Location loc, llvm::ArrayRef<mlir::Value> shift,
458 llvm::ArrayRef<mlir::Value> exts);
459 mlir::Value genShape(mlir::Location loc, llvm::ArrayRef<mlir::Value> exts);
460 mlir::Value genShift(mlir::Location loc, llvm::ArrayRef<mlir::Value> shift);
461
462 /// Create one of the shape ops given an extended value. For a boxed value,
463 /// this may create a `fir.shift` op.
464 mlir::Value createShape(mlir::Location loc, const fir::ExtendedValue &exv);
465
466 /// Create a slice op extended value. The value to be sliced, `exv`, must be
467 /// an array.
468 mlir::Value createSlice(mlir::Location loc, const fir::ExtendedValue &exv,
469 mlir::ValueRange triples, mlir::ValueRange path);
470
471 /// Create a boxed value (Fortran descriptor) to be passed to the runtime.
472 /// \p exv is an extended value holding a memory reference to the object that
473 /// must be boxed. This function will crash if provided something that is not
474 /// a memory reference type.
475 /// Array entities are boxed with a shape and possibly a shift. Character
476 /// entities are boxed with a LEN parameter.
477 mlir::Value createBox(mlir::Location loc, const fir::ExtendedValue &exv,
478 bool isPolymorphic = false, bool isAssumedType = false);
479
480 mlir::Value createBox(mlir::Location loc, mlir::Type boxType,
481 mlir::Value addr, mlir::Value shape, mlir::Value slice,
482 llvm::ArrayRef<mlir::Value> lengths, mlir::Value tdesc);
483
484 /// Create constant i1 with value 1. if \p b is true or 0. otherwise
485 mlir::Value createBool(mlir::Location loc, bool b) {
486 return createIntegerConstant(loc, getIntegerType(1), b ? 1 : 0);
487 }
488
489 //===--------------------------------------------------------------------===//
490 // If-Then-Else generation helper
491 //===--------------------------------------------------------------------===//
492
493 /// Helper class to create if-then-else in a structured way:
494 /// Usage: genIfOp().genThen([&](){...}).genElse([&](){...}).end();
495 /// Alternatively, getResults() can be used instead of end() to end the ifOp
496 /// and get the ifOp results.
497 class IfBuilder {
498 public:
499 IfBuilder(fir::IfOp ifOp, FirOpBuilder &builder)
500 : ifOp{ifOp}, builder{builder} {}
501 template <typename CC>
502 IfBuilder &genThen(CC func) {
503 builder.setInsertionPointToStart(&ifOp.getThenRegion().front());
504 func();
505 return *this;
506 }
507 template <typename CC>
508 IfBuilder &genElse(CC func) {
509 assert(!ifOp.getElseRegion().empty() && "must have else region");
510 builder.setInsertionPointToStart(&ifOp.getElseRegion().front());
511 func();
512 return *this;
513 }
514 void end() { builder.setInsertionPointAfter(ifOp); }
515
516 /// End the IfOp and return the results if any.
517 mlir::Operation::result_range getResults() {
518 end();
519 return ifOp.getResults();
520 }
521
522 fir::IfOp &getIfOp() { return ifOp; };
523
524 private:
525 fir::IfOp ifOp;
526 FirOpBuilder &builder;
527 };
528
529 /// Create an IfOp and returns an IfBuilder that can generate the else/then
530 /// bodies.
531 IfBuilder genIfOp(mlir::Location loc, mlir::TypeRange results,
532 mlir::Value cdt, bool withElseRegion) {
533 auto op = create<fir::IfOp>(loc, results, cdt, withElseRegion);
534 return IfBuilder(op, *this);
535 }
536
537 /// Create an IfOp with no "else" region, and no result values.
538 /// Usage: genIfThen(loc, cdt).genThen(lambda).end();
539 IfBuilder genIfThen(mlir::Location loc, mlir::Value cdt) {
540 auto op = create<fir::IfOp>(loc, std::nullopt, cdt, false);
541 return IfBuilder(op, *this);
542 }
543
544 /// Create an IfOp with an "else" region, and no result values.
545 /// Usage: genIfThenElse(loc, cdt).genThen(lambda).genElse(lambda).end();
546 IfBuilder genIfThenElse(mlir::Location loc, mlir::Value cdt) {
547 auto op = create<fir::IfOp>(loc, std::nullopt, cdt, true);
548 return IfBuilder(op, *this);
549 }
550
551 mlir::Value genNot(mlir::Location loc, mlir::Value boolean) {
552 return create<mlir::arith::CmpIOp>(loc, mlir::arith::CmpIPredicate::eq,
553 boolean, createBool(loc, false));
554 }
555
556 /// Generate code testing \p addr is not a null address.
557 mlir::Value genIsNotNullAddr(mlir::Location loc, mlir::Value addr);
558
559 /// Generate code testing \p addr is a null address.
560 mlir::Value genIsNullAddr(mlir::Location loc, mlir::Value addr);
561
562 /// Compute the extent of (lb:ub:step) as max((ub-lb+step)/step, 0). See
563 /// Fortran 2018 9.5.3.3.2 section for more details.
564 mlir::Value genExtentFromTriplet(mlir::Location loc, mlir::Value lb,
565 mlir::Value ub, mlir::Value step,
566 mlir::Type type);
567
568 /// Create an AbsentOp of \p argTy type and handle special cases, such as
569 /// Character Procedure Tuple arguments.
570 mlir::Value genAbsentOp(mlir::Location loc, mlir::Type argTy);
571
572 /// Set default FastMathFlags value for all operations
573 /// supporting mlir::arith::FastMathAttr that will be created
574 /// by this builder.
575 void setFastMathFlags(mlir::arith::FastMathFlags flags) {
576 fastMathFlags = flags;
577 }
578
579 /// Set default FastMathFlags value from the passed MathOptionsBase
580 /// config.
581 void setFastMathFlags(Fortran::common::MathOptionsBase options);
582
583 /// Get current FastMathFlags value.
584 mlir::arith::FastMathFlags getFastMathFlags() const { return fastMathFlags; }
585
586 /// Stringify FastMathFlags set in a way
587 /// that the string may be used for mangling a function name.
588 /// If FastMathFlags are set to 'none', then the result is an empty
589 /// string.
590 std::string getFastMathFlagsString() {
591 mlir::arith::FastMathFlags flags = getFastMathFlags();
592 if (flags == mlir::arith::FastMathFlags::none)
593 return {};
594
595 std::string fmfString{mlir::arith::stringifyFastMathFlags(flags)};
596 std::replace(fmfString.begin(), fmfString.end(), ',', '_');
597 return fmfString;
598 }
599
600 /// Set default IntegerOverflowFlags value for all operations
601 /// supporting mlir::arith::IntegerOverflowFlagsAttr that will be created
602 /// by this builder.
603 void setIntegerOverflowFlags(mlir::arith::IntegerOverflowFlags flags) {
604 integerOverflowFlags = flags;
605 }
606
607 /// Get current IntegerOverflowFlags value.
608 mlir::arith::IntegerOverflowFlags getIntegerOverflowFlags() const {
609 return integerOverflowFlags;
610 }
611
612 /// Dump the current function. (debug)
613 LLVM_DUMP_METHOD void dumpFunc();
614
615 /// FirOpBuilder hook for creating new operation.
616 void notifyOperationInserted(mlir::Operation *op,
617 mlir::OpBuilder::InsertPoint previous) override {
618 // We only care about newly created operations.
619 if (previous.isSet())
620 return;
621 setCommonAttributes(op);
622 }
623
624 /// Construct a data layout on demand and return it
625 mlir::DataLayout &getDataLayout();
626
627 /// Convert operands &/or result from/to unsigned so that the operation
628 /// only receives/produces signless operands.
629 template <typename OpTy>
630 mlir::Value createUnsigned(mlir::Location loc, mlir::Type resultType,
631 mlir::Value left, mlir::Value right) {
632 if (!resultType.isIntOrFloat())
633 return create<OpTy>(loc, resultType, left, right);
634 mlir::Type signlessType = mlir::IntegerType::get(
635 getContext(), resultType.getIntOrFloatBitWidth(),
636 mlir::IntegerType::SignednessSemantics::Signless);
637 mlir::Type opResType = resultType;
638 if (left.getType().isUnsignedInteger()) {
639 left = createConvert(loc, signlessType, left);
640 opResType = signlessType;
641 }
642 if (right.getType().isUnsignedInteger()) {
643 right = createConvert(loc, signlessType, right);
644 opResType = signlessType;
645 }
646 mlir::Value result = create<OpTy>(loc, opResType, left, right);
647 if (resultType.isUnsignedInteger())
648 result = createConvert(loc, resultType, result);
649 return result;
650 }
651
652 /// Compare two pointer-like values using the given predicate.
653 mlir::Value genPtrCompare(mlir::Location loc,
654 mlir::arith::CmpIPredicate predicate,
655 mlir::Value ptr1, mlir::Value ptr2) {
656 ptr1 = createConvert(loc, getIndexType(), ptr1);
657 ptr2 = createConvert(loc, getIndexType(), ptr2);
658 return create<mlir::arith::CmpIOp>(loc, predicate, ptr1, ptr2);
659 }
660
661private:
662 /// Set attributes (e.g. FastMathAttr) to \p op operation
663 /// based on the current attributes setting.
664 void setCommonAttributes(mlir::Operation *op) const;
665
666 KindMapping kindMap;
667
668 /// FastMathFlags that need to be set for operations that support
669 /// mlir::arith::FastMathAttr.
670 mlir::arith::FastMathFlags fastMathFlags{};
671
672 /// IntegerOverflowFlags that need to be set for operations that support
673 /// mlir::arith::IntegerOverflowFlagsAttr.
674 mlir::arith::IntegerOverflowFlags integerOverflowFlags{};
675
676 /// fir::GlobalOp and func::FuncOp symbol table to speed-up
677 /// lookups.
678 mlir::SymbolTable *symbolTable = nullptr;
679
680 /// DataLayout constructed on demand. Access via getDataLayout().
681 /// Stored via a unique_ptr rather than an optional so as not to bloat this
682 /// class when most instances won't ever need a data layout.
683 std::unique_ptr<mlir::DataLayout> dataLayout = nullptr;
684};
685
686} // namespace fir
687
688namespace fir::factory {
689
690//===----------------------------------------------------------------------===//
691// ExtendedValue inquiry helpers
692//===----------------------------------------------------------------------===//
693
694/// Read or get character length from \p box that must contain a character
695/// entity. If the length value is contained in the ExtendedValue, this will
696/// not generate any code, otherwise this will generate a read of the fir.box
697/// describing the entity.
698mlir::Value readCharLen(fir::FirOpBuilder &builder, mlir::Location loc,
699 const fir::ExtendedValue &box);
700
701/// Read or get the extent in dimension \p dim of the array described by \p box.
702mlir::Value readExtent(fir::FirOpBuilder &builder, mlir::Location loc,
703 const fir::ExtendedValue &box, unsigned dim);
704
705/// Read or get the lower bound in dimension \p dim of the array described by
706/// \p box. If the lower bound is left default in the ExtendedValue,
707/// \p defaultValue will be returned.
708mlir::Value readLowerBound(fir::FirOpBuilder &builder, mlir::Location loc,
709 const fir::ExtendedValue &box, unsigned dim,
710 mlir::Value defaultValue);
711
712/// Read extents from \p box.
713llvm::SmallVector<mlir::Value> readExtents(fir::FirOpBuilder &builder,
714 mlir::Location loc,
715 const fir::BoxValue &box);
716
717/// Read a fir::BoxValue into an fir::UnboxValue, a fir::ArrayBoxValue or a
718/// fir::CharArrayBoxValue. This should only be called if the fir::BoxValue is
719/// known to be contiguous given the context (or if the resulting address will
720/// not be used). If the value is polymorphic, its dynamic type will be lost.
721/// This must not be used on unlimited polymorphic and assumed rank entities.
722fir::ExtendedValue readBoxValue(fir::FirOpBuilder &builder, mlir::Location loc,
723 const fir::BoxValue &box);
724
725/// Get the lower bounds of \p exv. NB: returns an empty vector if the lower
726/// bounds are all ones, which is the default in Fortran.
727llvm::SmallVector<mlir::Value>
728getNonDefaultLowerBounds(fir::FirOpBuilder &builder, mlir::Location loc,
729 const fir::ExtendedValue &exv);
730
731/// Return LEN parameters associated to \p exv that are not deferred (that are
732/// available without having to read any fir.box values). Empty if \p exv has no
733/// LEN parameters or if they are all deferred.
734llvm::SmallVector<mlir::Value>
735getNonDeferredLenParams(const fir::ExtendedValue &exv);
736
737//===----------------------------------------------------------------------===//
738// String literal helper helpers
739//===----------------------------------------------------------------------===//
740
741/// Create a !fir.char<1> string literal global and returns a fir::CharBoxValue
742/// with its address and length.
743fir::ExtendedValue createStringLiteral(fir::FirOpBuilder &, mlir::Location,
744 llvm::StringRef string);
745
746/// Unique a compiler generated identifier. A short prefix should be provided
747/// to hint at the origin of the identifier.
748std::string uniqueCGIdent(llvm::StringRef prefix, llvm::StringRef name);
749
750/// Lowers the extents from the sequence type to Values.
751/// Any unknown extents are lowered to undefined values.
752llvm::SmallVector<mlir::Value> createExtents(fir::FirOpBuilder &builder,
753 mlir::Location loc,
754 fir::SequenceType seqTy);
755
756//===--------------------------------------------------------------------===//
757// Location helpers
758//===--------------------------------------------------------------------===//
759
760/// Generate a string literal containing the file name and return its address
761mlir::Value locationToFilename(fir::FirOpBuilder &, mlir::Location);
762/// Generate a constant of the given type with the location line number
763mlir::Value locationToLineNo(fir::FirOpBuilder &, mlir::Location, mlir::Type);
764
765//===--------------------------------------------------------------------===//
766// ExtendedValue helpers
767//===--------------------------------------------------------------------===//
768
769/// Return the extended value for a component of a derived type instance given
770/// the address of the component.
771fir::ExtendedValue componentToExtendedValue(fir::FirOpBuilder &builder,
772 mlir::Location loc,
773 mlir::Value component);
774
775/// Given the address of an array element and the ExtendedValue describing the
776/// array, returns the ExtendedValue describing the array element. The purpose
777/// is to propagate the LEN parameters of the array to the element. This can be
778/// used for elements of `array` or `array(i:j:k)`. If \p element belongs to an
779/// array section `array%x` whose base is \p array,
780/// arraySectionElementToExtendedValue must be used instead.
781fir::ExtendedValue arrayElementToExtendedValue(fir::FirOpBuilder &builder,
782 mlir::Location loc,
783 const fir::ExtendedValue &array,
784 mlir::Value element);
785
786/// Build the ExtendedValue for \p element that is an element of an array or
787/// array section with \p array base (`array` or `array(i:j:k)%x%y`).
788/// If it is an array section, \p slice must be provided and be a fir::SliceOp
789/// that describes the section.
790fir::ExtendedValue arraySectionElementToExtendedValue(
791 fir::FirOpBuilder &builder, mlir::Location loc,
792 const fir::ExtendedValue &array, mlir::Value element, mlir::Value slice);
793
794/// Assign \p rhs to \p lhs. Both \p rhs and \p lhs must be scalars. The
795/// assignment follows Fortran intrinsic assignment semantic (10.2.1.3).
796void genScalarAssignment(fir::FirOpBuilder &builder, mlir::Location loc,
797 const fir::ExtendedValue &lhs,
798 const fir::ExtendedValue &rhs,
799 bool needFinalization = false,
800 bool isTemporaryLHS = false);
801
802/// Assign \p rhs to \p lhs. Both \p rhs and \p lhs must be scalar derived
803/// types. The assignment follows Fortran intrinsic assignment semantic for
804/// derived types (10.2.1.3 point 13).
805void genRecordAssignment(fir::FirOpBuilder &builder, mlir::Location loc,
806 const fir::ExtendedValue &lhs,
807 const fir::ExtendedValue &rhs,
808 bool needFinalization = false,
809 bool isTemporaryLHS = false);
810
811/// Builds and returns the type of a ragged array header used to cache mask
812/// evaluations. RaggedArrayHeader is defined in
813/// flang/include/flang/Runtime/ragged.h.
814mlir::TupleType getRaggedArrayHeaderType(fir::FirOpBuilder &builder);
815
816/// Generate the, possibly dynamic, LEN of a CHARACTER. \p arrLoad determines
817/// the base array. After applying \p path, the result must be a reference to a
818/// `!fir.char` type object. \p substring must have 0, 1, or 2 members. The
819/// first member is the starting offset. The second is the ending offset.
820mlir::Value genLenOfCharacter(fir::FirOpBuilder &builder, mlir::Location loc,
821 fir::ArrayLoadOp arrLoad,
822 llvm::ArrayRef<mlir::Value> path,
823 llvm::ArrayRef<mlir::Value> substring);
824mlir::Value genLenOfCharacter(fir::FirOpBuilder &builder, mlir::Location loc,
825 fir::SequenceType seqTy, mlir::Value memref,
826 llvm::ArrayRef<mlir::Value> typeParams,
827 llvm::ArrayRef<mlir::Value> path,
828 llvm::ArrayRef<mlir::Value> substring);
829
830/// Create the zero value of a given the numerical or logical \p type (`false`
831/// for logical types).
832mlir::Value createZeroValue(fir::FirOpBuilder &builder, mlir::Location loc,
833 mlir::Type type);
834
835/// Get the integer constants of triplet and compute the extent.
836std::optional<std::int64_t> getExtentFromTriplet(mlir::Value lb, mlir::Value ub,
837 mlir::Value stride);
838
839/// Compute the extent value given the lower bound \lb and upper bound \ub.
840/// All inputs must have the same SSA integer type.
841mlir::Value computeExtent(fir::FirOpBuilder &builder, mlir::Location loc,
842 mlir::Value lb, mlir::Value ub);
843mlir::Value computeExtent(fir::FirOpBuilder &builder, mlir::Location loc,
844 mlir::Value lb, mlir::Value ub, mlir::Value zero,
845 mlir::Value one);
846
847/// Generate max(\p value, 0) where \p value is a scalar integer.
848mlir::Value genMaxWithZero(fir::FirOpBuilder &builder, mlir::Location loc,
849 mlir::Value value);
850mlir::Value genMaxWithZero(fir::FirOpBuilder &builder, mlir::Location loc,
851 mlir::Value value, mlir::Value zero);
852
853/// The type(C_PTR/C_FUNPTR) is defined as the derived type with only one
854/// component of integer 64, and the component is the C address. Get the C
855/// address.
856mlir::Value genCPtrOrCFunptrAddr(fir::FirOpBuilder &builder, mlir::Location loc,
857 mlir::Value cPtr, mlir::Type ty);
858
859/// The type(C_DEVPTR) is defined as the derived type with only one
860/// component of C_PTR type. Get the C address from the C_PTR component.
861mlir::Value genCDevPtrAddr(fir::FirOpBuilder &builder, mlir::Location loc,
862 mlir::Value cDevPtr, mlir::Type ty);
863
864/// Get the C address value.
865mlir::Value genCPtrOrCFunptrValue(fir::FirOpBuilder &builder,
866 mlir::Location loc, mlir::Value cPtr);
867
868/// Create a fir.box from a fir::ExtendedValue and wrap it in a fir::BoxValue
869/// to keep all the lower bound and explicit parameter information.
870fir::BoxValue createBoxValue(fir::FirOpBuilder &builder, mlir::Location loc,
871 const fir::ExtendedValue &exv);
872
873/// Generate Null BoxProc for procedure pointer null initialization.
874mlir::Value createNullBoxProc(fir::FirOpBuilder &builder, mlir::Location loc,
875 mlir::Type boxType);
876
877/// Convert a value to a new type. Return the value directly if it has the right
878/// type.
879mlir::Value createConvert(mlir::OpBuilder &, mlir::Location, mlir::Type,
880 mlir::Value);
881
882/// Set internal linkage attribute on a function.
883void setInternalLinkage(mlir::func::FuncOp);
884
885llvm::SmallVector<mlir::Value>
886elideExtentsAlreadyInType(mlir::Type type, mlir::ValueRange shape);
887
888llvm::SmallVector<mlir::Value>
889elideLengthsAlreadyInType(mlir::Type type, mlir::ValueRange lenParams);
890
891/// Get the address space which should be used for allocas
892uint64_t getAllocaAddressSpace(const mlir::DataLayout *dataLayout);
893
894/// The two vectors of MLIR values have the following property:
895/// \p extents1[i] must have the same value as \p extents2[i]
896/// The function returns a new vector of MLIR values that preserves
897/// the same property vs \p extents1 and \p extents2, but allows
898/// more optimizations. For example, if extents1[j] is a known constant,
899/// and extents2[j] is not, then result[j] is the MLIR value extents1[j].
900llvm::SmallVector<mlir::Value> deduceOptimalExtents(mlir::ValueRange extents1,
901 mlir::ValueRange extents2);
902
903/// Given array extents generate code that sets them all to zeroes,
904/// if the array is empty, e.g.:
905/// %false = arith.constant false
906/// %c0 = arith.constant 0 : index
907/// %p1 = arith.cmpi eq, %e0, %c0 : index
908/// %p2 = arith.ori %false, %p1 : i1
909/// %p3 = arith.cmpi eq, %e1, %c0 : index
910/// %p4 = arith.ori %p1, %p2 : i1
911/// %result0 = arith.select %p4, %c0, %e0 : index
912/// %result1 = arith.select %p4, %c0, %e1 : index
913llvm::SmallVector<mlir::Value> updateRuntimeExtentsForEmptyArrays(
914 fir::FirOpBuilder &builder, mlir::Location loc, mlir::ValueRange extents);
915
916/// Given \p box of type fir::BaseBoxType representing an array,
917/// the function generates code to fetch the lower bounds,
918/// the extents and the strides from the box. The values are returned via
919/// \p lbounds, \p extents and \p strides.
920void genDimInfoFromBox(fir::FirOpBuilder &builder, mlir::Location loc,
921 mlir::Value box,
922 llvm::SmallVectorImpl<mlir::Value> *lbounds,
923 llvm::SmallVectorImpl<mlir::Value> *extents,
924 llvm::SmallVectorImpl<mlir::Value> *strides);
925
926/// Generate an LLVM dialect lifetime start marker at the current insertion
927/// point given an fir.alloca and its constant size in bytes. Returns the value
928/// to be passed to the lifetime end marker.
929mlir::Value genLifetimeStart(mlir::OpBuilder &builder, mlir::Location loc,
930 fir::AllocaOp alloc, int64_t size,
931 const mlir::DataLayout *dl);
932
933/// Generate an LLVM dialect lifetime end marker at the current insertion point
934/// given an llvm.ptr value and the constant size in bytes of its storage.
935void genLifetimeEnd(mlir::OpBuilder &builder, mlir::Location loc,
936 mlir::Value mem, int64_t size);
937
938} // namespace fir::factory
939
940#endif // FORTRAN_OPTIMIZER_BUILDER_FIRBUILDER_H
941

Warning: This file is not a C or C++ file. It does not have highlighting.

Provided by KDAB

Privacy Policy
Improve your Profiling and Debugging skills
Find out more

source code of flang/include/flang/Optimizer/Builder/FIRBuilder.h