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