1 //===-- IntrinsicCall.cpp -------------------------------------------------===//
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 // Helper 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 #include "flang/Lower/IntrinsicCall.h"
17 #include "flang/Common/static-multimap-view.h"
18 #include "flang/Lower/Mangler.h"
19 #include "flang/Lower/Runtime.h"
20 #include "flang/Lower/StatementContext.h"
21 #include "flang/Lower/SymbolMap.h"
22 #include "flang/Optimizer/Builder/Character.h"
23 #include "flang/Optimizer/Builder/Complex.h"
24 #include "flang/Optimizer/Builder/FIRBuilder.h"
25 #include "flang/Optimizer/Builder/MutableBox.h"
26 #include "flang/Optimizer/Builder/Runtime/Character.h"
27 #include "flang/Optimizer/Builder/Runtime/Command.h"
28 #include "flang/Optimizer/Builder/Runtime/Inquiry.h"
29 #include "flang/Optimizer/Builder/Runtime/Numeric.h"
30 #include "flang/Optimizer/Builder/Runtime/RTBuilder.h"
31 #include "flang/Optimizer/Builder/Runtime/Reduction.h"
32 #include "flang/Optimizer/Builder/Runtime/Stop.h"
33 #include "flang/Optimizer/Builder/Runtime/Transformational.h"
34 #include "flang/Optimizer/Builder/Todo.h"
35 #include "flang/Optimizer/Dialect/FIROpsSupport.h"
36 #include "flang/Optimizer/Support/FatalError.h"
37 #include "mlir/Dialect/LLVMIR/LLVMDialect.h"
38 #include "mlir/Dialect/Math/IR/Math.h"
39 #include "llvm/Support/CommandLine.h"
40 #include "llvm/Support/Debug.h"
41 
42 #define DEBUG_TYPE "flang-lower-intrinsic"
43 
44 #define PGMATH_DECLARE
45 #include "flang/Evaluate/pgmath.h.inc"
46 
47 /// This file implements lowering of Fortran intrinsic procedures and Fortran
48 /// intrinsic module procedures.  A call may be inlined with a mix of FIR and
49 /// MLIR operations, or as a call to a runtime function or LLVM intrinsic.
50 
51 /// Lowering of intrinsic procedure calls is based on a map that associates
52 /// Fortran intrinsic generic names to FIR generator functions.
53 /// All generator functions are member functions of the IntrinsicLibrary class
54 /// and have the same interface.
55 /// If no generator is given for an intrinsic name, a math runtime library
56 /// is searched for an implementation and, if a runtime function is found,
57 /// a call is generated for it. LLVM intrinsics are handled as a math
58 /// runtime library here.
59 
60 /// Enums used to templatize and share lowering of MIN and MAX.
61 enum class Extremum { Min, Max };
62 
63 // There are different ways to deal with NaNs in MIN and MAX.
64 // Known existing behaviors are listed below and can be selected for
65 // f18 MIN/MAX implementation.
66 enum class ExtremumBehavior {
67   // Note: the Signaling/quiet aspect of NaNs in the behaviors below are
68   // not described because there is no way to control/observe such aspect in
69   // MLIR/LLVM yet. The IEEE behaviors come with requirements regarding this
70   // aspect that are therefore currently not enforced. In the descriptions
71   // below, NaNs can be signaling or quite. Returned NaNs may be signaling
72   // if one of the input NaN was signaling but it cannot be guaranteed either.
73   // Existing compilers using an IEEE behavior (gfortran) also do not fulfill
74   // signaling/quiet requirements.
75   IeeeMinMaximumNumber,
76   // IEEE minimumNumber/maximumNumber behavior (754-2019, section 9.6):
77   // If one of the argument is and number and the other is NaN, return the
78   // number. If both arguements are NaN, return NaN.
79   // Compilers: gfortran.
80   IeeeMinMaximum,
81   // IEEE minimum/maximum behavior (754-2019, section 9.6):
82   // If one of the argument is NaN, return NaN.
83   MinMaxss,
84   // x86 minss/maxss behavior:
85   // If the second argument is a number and the other is NaN, return the number.
86   // In all other cases where at least one operand is NaN, return NaN.
87   // Compilers: xlf (only for MAX), ifort, pgfortran -nollvm, and nagfor.
88   PgfortranLlvm,
89   // "Opposite of" x86 minss/maxss behavior:
90   // If the first argument is a number and the other is NaN, return the
91   // number.
92   // In all other cases where at least one operand is NaN, return NaN.
93   // Compilers: xlf (only for MIN), and pgfortran (with llvm).
94   IeeeMinMaxNum
95   // IEEE minNum/maxNum behavior (754-2008, section 5.3.1):
96   // TODO: Not implemented.
97   // It is the only behavior where the signaling/quiet aspect of a NaN argument
98   // impacts if the result should be NaN or the argument that is a number.
99   // LLVM/MLIR do not provide ways to observe this aspect, so it is not
100   // possible to implement it without some target dependent runtime.
101 };
102 
103 fir::ExtendedValue Fortran::lower::getAbsentIntrinsicArgument() {
104   return fir::UnboxedValue{};
105 }
106 
107 /// Test if an ExtendedValue is absent. This is used to test if an intrinsic
108 /// argument are absent at compile time.
109 static bool isStaticallyAbsent(const fir::ExtendedValue &exv) {
110   return !fir::getBase(exv);
111 }
112 static bool isStaticallyAbsent(llvm::ArrayRef<fir::ExtendedValue> args,
113                                size_t argIndex) {
114   return args.size() <= argIndex || isStaticallyAbsent(args[argIndex]);
115 }
116 static bool isStaticallyAbsent(llvm::ArrayRef<mlir::Value> args,
117                                size_t argIndex) {
118   return args.size() <= argIndex || !args[argIndex];
119 }
120 
121 /// Test if an ExtendedValue is present. This is used to test if an intrinsic
122 /// argument is present at compile time. This does not imply that the related
123 /// value may not be an absent dummy optional, disassociated pointer, or a
124 /// deallocated allocatable. See `handleDynamicOptional` to deal with these
125 /// cases when it makes sense.
126 static bool isStaticallyPresent(const fir::ExtendedValue &exv) {
127   return !isStaticallyAbsent(exv);
128 }
129 
130 /// Process calls to Maxval, Minval, Product, Sum intrinsic functions that
131 /// take a DIM argument.
132 template <typename FD>
133 static fir::ExtendedValue
134 genFuncDim(FD funcDim, mlir::Type resultType, fir::FirOpBuilder &builder,
135            mlir::Location loc, Fortran::lower::StatementContext *stmtCtx,
136            llvm::StringRef errMsg, mlir::Value array, fir::ExtendedValue dimArg,
137            mlir::Value mask, int rank) {
138 
139   // Create mutable fir.box to be passed to the runtime for the result.
140   mlir::Type resultArrayType = builder.getVarLenSeqTy(resultType, rank - 1);
141   fir::MutableBoxValue resultMutableBox =
142       fir::factory::createTempMutableBox(builder, loc, resultArrayType);
143   mlir::Value resultIrBox =
144       fir::factory::getMutableIRBox(builder, loc, resultMutableBox);
145 
146   mlir::Value dim =
147       isStaticallyAbsent(dimArg)
148           ? builder.createIntegerConstant(loc, builder.getIndexType(), 0)
149           : fir::getBase(dimArg);
150   funcDim(builder, loc, resultIrBox, array, dim, mask);
151 
152   fir::ExtendedValue res =
153       fir::factory::genMutableBoxRead(builder, loc, resultMutableBox);
154   return res.match(
155       [&](const fir::ArrayBoxValue &box) -> fir::ExtendedValue {
156         // Add cleanup code
157         assert(stmtCtx);
158         fir::FirOpBuilder *bldr = &builder;
159         mlir::Value temp = box.getAddr();
160         stmtCtx->attachCleanup(
161             [=]() { bldr->create<fir::FreeMemOp>(loc, temp); });
162         return box;
163       },
164       [&](const fir::CharArrayBoxValue &box) -> fir::ExtendedValue {
165         // Add cleanup code
166         assert(stmtCtx);
167         fir::FirOpBuilder *bldr = &builder;
168         mlir::Value temp = box.getAddr();
169         stmtCtx->attachCleanup(
170             [=]() { bldr->create<fir::FreeMemOp>(loc, temp); });
171         return box;
172       },
173       [&](const auto &) -> fir::ExtendedValue {
174         fir::emitFatalError(loc, errMsg);
175       });
176 }
177 
178 /// Process calls to Product, Sum intrinsic functions
179 template <typename FN, typename FD>
180 static fir::ExtendedValue
181 genProdOrSum(FN func, FD funcDim, mlir::Type resultType,
182              fir::FirOpBuilder &builder, mlir::Location loc,
183              Fortran::lower::StatementContext *stmtCtx, llvm::StringRef errMsg,
184              llvm::ArrayRef<fir::ExtendedValue> args) {
185 
186   assert(args.size() == 3);
187 
188   // Handle required array argument
189   fir::BoxValue arryTmp = builder.createBox(loc, args[0]);
190   mlir::Value array = fir::getBase(arryTmp);
191   int rank = arryTmp.rank();
192   assert(rank >= 1);
193 
194   // Handle optional mask argument
195   auto mask = isStaticallyAbsent(args[2])
196                   ? builder.create<fir::AbsentOp>(
197                         loc, fir::BoxType::get(builder.getI1Type()))
198                   : builder.createBox(loc, args[2]);
199 
200   bool absentDim = isStaticallyAbsent(args[1]);
201 
202   // We call the type specific versions because the result is scalar
203   // in the case below.
204   if (absentDim || rank == 1) {
205     mlir::Type ty = array.getType();
206     mlir::Type arrTy = fir::dyn_cast_ptrOrBoxEleTy(ty);
207     auto eleTy = arrTy.cast<fir::SequenceType>().getEleTy();
208     if (fir::isa_complex(eleTy)) {
209       mlir::Value result = builder.createTemporary(loc, eleTy);
210       func(builder, loc, array, mask, result);
211       return builder.create<fir::LoadOp>(loc, result);
212     }
213     auto resultBox = builder.create<fir::AbsentOp>(
214         loc, fir::BoxType::get(builder.getI1Type()));
215     return func(builder, loc, array, mask, resultBox);
216   }
217   // Handle Product/Sum cases that have an array result.
218   return genFuncDim(funcDim, resultType, builder, loc, stmtCtx, errMsg, array,
219                     args[1], mask, rank);
220 }
221 
222 /// Process calls to DotProduct
223 template <typename FN>
224 static fir::ExtendedValue
225 genDotProd(FN func, mlir::Type resultType, fir::FirOpBuilder &builder,
226            mlir::Location loc, Fortran::lower::StatementContext *stmtCtx,
227            llvm::ArrayRef<fir::ExtendedValue> args) {
228 
229   assert(args.size() == 2);
230 
231   // Handle required vector arguments
232   mlir::Value vectorA = fir::getBase(args[0]);
233   mlir::Value vectorB = fir::getBase(args[1]);
234 
235   mlir::Type eleTy = fir::dyn_cast_ptrOrBoxEleTy(vectorA.getType())
236                          .cast<fir::SequenceType>()
237                          .getEleTy();
238   if (fir::isa_complex(eleTy)) {
239     mlir::Value result = builder.createTemporary(loc, eleTy);
240     func(builder, loc, vectorA, vectorB, result);
241     return builder.create<fir::LoadOp>(loc, result);
242   }
243 
244   auto resultBox = builder.create<fir::AbsentOp>(
245       loc, fir::BoxType::get(builder.getI1Type()));
246   return func(builder, loc, vectorA, vectorB, resultBox);
247 }
248 
249 /// Process calls to Maxval, Minval, Product, Sum intrinsic functions
250 template <typename FN, typename FD, typename FC>
251 static fir::ExtendedValue
252 genExtremumVal(FN func, FD funcDim, FC funcChar, mlir::Type resultType,
253                fir::FirOpBuilder &builder, mlir::Location loc,
254                Fortran::lower::StatementContext *stmtCtx,
255                llvm::StringRef errMsg,
256                llvm::ArrayRef<fir::ExtendedValue> args) {
257 
258   assert(args.size() == 3);
259 
260   // Handle required array argument
261   fir::BoxValue arryTmp = builder.createBox(loc, args[0]);
262   mlir::Value array = fir::getBase(arryTmp);
263   int rank = arryTmp.rank();
264   assert(rank >= 1);
265   bool hasCharacterResult = arryTmp.isCharacter();
266 
267   // Handle optional mask argument
268   auto mask = isStaticallyAbsent(args[2])
269                   ? builder.create<fir::AbsentOp>(
270                         loc, fir::BoxType::get(builder.getI1Type()))
271                   : builder.createBox(loc, args[2]);
272 
273   bool absentDim = isStaticallyAbsent(args[1]);
274 
275   // For Maxval/MinVal, we call the type specific versions of
276   // Maxval/Minval because the result is scalar in the case below.
277   if (!hasCharacterResult && (absentDim || rank == 1))
278     return func(builder, loc, array, mask);
279 
280   if (hasCharacterResult && (absentDim || rank == 1)) {
281     // Create mutable fir.box to be passed to the runtime for the result.
282     fir::MutableBoxValue resultMutableBox =
283         fir::factory::createTempMutableBox(builder, loc, resultType);
284     mlir::Value resultIrBox =
285         fir::factory::getMutableIRBox(builder, loc, resultMutableBox);
286 
287     funcChar(builder, loc, resultIrBox, array, mask);
288 
289     // Handle cleanup of allocatable result descriptor and return
290     fir::ExtendedValue res =
291         fir::factory::genMutableBoxRead(builder, loc, resultMutableBox);
292     return res.match(
293         [&](const fir::CharBoxValue &box) -> fir::ExtendedValue {
294           // Add cleanup code
295           assert(stmtCtx);
296           fir::FirOpBuilder *bldr = &builder;
297           mlir::Value temp = box.getAddr();
298           stmtCtx->attachCleanup(
299               [=]() { bldr->create<fir::FreeMemOp>(loc, temp); });
300           return box;
301         },
302         [&](const auto &) -> fir::ExtendedValue {
303           fir::emitFatalError(loc, errMsg);
304         });
305   }
306 
307   // Handle Min/Maxval cases that have an array result.
308   return genFuncDim(funcDim, resultType, builder, loc, stmtCtx, errMsg, array,
309                     args[1], mask, rank);
310 }
311 
312 /// Process calls to Minloc, Maxloc intrinsic functions
313 template <typename FN, typename FD>
314 static fir::ExtendedValue genExtremumloc(
315     FN func, FD funcDim, mlir::Type resultType, fir::FirOpBuilder &builder,
316     mlir::Location loc, Fortran::lower::StatementContext *stmtCtx,
317     llvm::StringRef errMsg, llvm::ArrayRef<fir::ExtendedValue> args) {
318 
319   assert(args.size() == 5);
320 
321   // Handle required array argument
322   mlir::Value array = builder.createBox(loc, args[0]);
323   unsigned rank = fir::BoxValue(array).rank();
324   assert(rank >= 1);
325 
326   // Handle optional mask argument
327   auto mask = isStaticallyAbsent(args[2])
328                   ? builder.create<fir::AbsentOp>(
329                         loc, fir::BoxType::get(builder.getI1Type()))
330                   : builder.createBox(loc, args[2]);
331 
332   // Handle optional kind argument
333   auto kind = isStaticallyAbsent(args[3])
334                   ? builder.createIntegerConstant(
335                         loc, builder.getIndexType(),
336                         builder.getKindMap().defaultIntegerKind())
337                   : fir::getBase(args[3]);
338 
339   // Handle optional back argument
340   auto back = isStaticallyAbsent(args[4]) ? builder.createBool(loc, false)
341                                           : fir::getBase(args[4]);
342 
343   bool absentDim = isStaticallyAbsent(args[1]);
344 
345   if (!absentDim && rank == 1) {
346     // If dim argument is present and the array is rank 1, then the result is
347     // a scalar (since the the result is rank-1 or 0).
348     // Therefore, we use a scalar result descriptor with Min/MaxlocDim().
349     mlir::Value dim = fir::getBase(args[1]);
350     // Create mutable fir.box to be passed to the runtime for the result.
351     fir::MutableBoxValue resultMutableBox =
352         fir::factory::createTempMutableBox(builder, loc, resultType);
353     mlir::Value resultIrBox =
354         fir::factory::getMutableIRBox(builder, loc, resultMutableBox);
355 
356     funcDim(builder, loc, resultIrBox, array, dim, mask, kind, back);
357 
358     // Handle cleanup of allocatable result descriptor and return
359     fir::ExtendedValue res =
360         fir::factory::genMutableBoxRead(builder, loc, resultMutableBox);
361     return res.match(
362         [&](const mlir::Value &tempAddr) -> fir::ExtendedValue {
363           // Add cleanup code
364           assert(stmtCtx);
365           fir::FirOpBuilder *bldr = &builder;
366           stmtCtx->attachCleanup(
367               [=]() { bldr->create<fir::FreeMemOp>(loc, tempAddr); });
368           return builder.create<fir::LoadOp>(loc, resultType, tempAddr);
369         },
370         [&](const auto &) -> fir::ExtendedValue {
371           fir::emitFatalError(loc, errMsg);
372         });
373   }
374 
375   // Note: The Min/Maxloc/val cases below have an array result.
376 
377   // Create mutable fir.box to be passed to the runtime for the result.
378   mlir::Type resultArrayType =
379       builder.getVarLenSeqTy(resultType, absentDim ? 1 : rank - 1);
380   fir::MutableBoxValue resultMutableBox =
381       fir::factory::createTempMutableBox(builder, loc, resultArrayType);
382   mlir::Value resultIrBox =
383       fir::factory::getMutableIRBox(builder, loc, resultMutableBox);
384 
385   if (absentDim) {
386     // Handle min/maxloc/val case where there is no dim argument
387     // (calls Min/Maxloc()/MinMaxval() runtime routine)
388     func(builder, loc, resultIrBox, array, mask, kind, back);
389   } else {
390     // else handle min/maxloc case with dim argument (calls
391     // Min/Max/loc/val/Dim() runtime routine).
392     mlir::Value dim = fir::getBase(args[1]);
393     funcDim(builder, loc, resultIrBox, array, dim, mask, kind, back);
394   }
395 
396   return fir::factory::genMutableBoxRead(builder, loc, resultMutableBox)
397       .match(
398           [&](const fir::ArrayBoxValue &box) -> fir::ExtendedValue {
399             // Add cleanup code
400             assert(stmtCtx);
401             fir::FirOpBuilder *bldr = &builder;
402             mlir::Value temp = box.getAddr();
403             stmtCtx->attachCleanup(
404                 [=]() { bldr->create<fir::FreeMemOp>(loc, temp); });
405             return box;
406           },
407           [&](const auto &) -> fir::ExtendedValue {
408             fir::emitFatalError(loc, errMsg);
409           });
410 }
411 
412 // TODO error handling -> return a code or directly emit messages ?
413 struct IntrinsicLibrary {
414 
415   // Constructors.
416   explicit IntrinsicLibrary(fir::FirOpBuilder &builder, mlir::Location loc,
417                             Fortran::lower::StatementContext *stmtCtx = nullptr)
418       : builder{builder}, loc{loc}, stmtCtx{stmtCtx} {}
419   IntrinsicLibrary() = delete;
420   IntrinsicLibrary(const IntrinsicLibrary &) = delete;
421 
422   /// Generate FIR for call to Fortran intrinsic \p name with arguments \p arg
423   /// and expected result type \p resultType.
424   fir::ExtendedValue genIntrinsicCall(llvm::StringRef name,
425                                       llvm::Optional<mlir::Type> resultType,
426                                       llvm::ArrayRef<fir::ExtendedValue> arg);
427 
428   /// Search a runtime function that is associated to the generic intrinsic name
429   /// and whose signature matches the intrinsic arguments and result types.
430   /// If no such runtime function is found but a runtime function associated
431   /// with the Fortran generic exists and has the same number of arguments,
432   /// conversions will be inserted before and/or after the call. This is to
433   /// mainly to allow 16 bits float support even-though little or no math
434   /// runtime is currently available for it.
435   mlir::Value genRuntimeCall(llvm::StringRef name, mlir::Type,
436                              llvm::ArrayRef<mlir::Value>);
437 
438   using RuntimeCallGenerator = std::function<mlir::Value(
439       fir::FirOpBuilder &, mlir::Location, llvm::ArrayRef<mlir::Value>)>;
440   RuntimeCallGenerator
441   getRuntimeCallGenerator(llvm::StringRef name,
442                           mlir::FunctionType soughtFuncType);
443 
444   /// Lowering for the ABS intrinsic. The ABS intrinsic expects one argument in
445   /// the llvm::ArrayRef. The ABS intrinsic is lowered into MLIR/FIR operation
446   /// if the argument is an integer, into llvm intrinsics if the argument is
447   /// real and to the `hypot` math routine if the argument is of complex type.
448   mlir::Value genAbs(mlir::Type, llvm::ArrayRef<mlir::Value>);
449   template <void (*CallRuntime)(fir::FirOpBuilder &, mlir::Location loc,
450                                 mlir::Value, mlir::Value)>
451   fir::ExtendedValue genAdjustRtCall(mlir::Type,
452                                      llvm::ArrayRef<fir::ExtendedValue>);
453   mlir::Value genAimag(mlir::Type, llvm::ArrayRef<mlir::Value>);
454   mlir::Value genAint(mlir::Type, llvm::ArrayRef<mlir::Value>);
455   fir::ExtendedValue genAll(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
456   fir::ExtendedValue genAllocated(mlir::Type,
457                                   llvm::ArrayRef<fir::ExtendedValue>);
458   mlir::Value genAnint(mlir::Type, llvm::ArrayRef<mlir::Value>);
459   fir::ExtendedValue genAny(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
460   fir::ExtendedValue
461       genCommandArgumentCount(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
462   fir::ExtendedValue genAssociated(mlir::Type,
463                                    llvm::ArrayRef<fir::ExtendedValue>);
464   mlir::Value genBtest(mlir::Type, llvm::ArrayRef<mlir::Value>);
465   mlir::Value genCeiling(mlir::Type, llvm::ArrayRef<mlir::Value>);
466   fir::ExtendedValue genChar(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
467   template <mlir::arith::CmpIPredicate pred>
468   fir::ExtendedValue genCharacterCompare(mlir::Type,
469                                          llvm::ArrayRef<fir::ExtendedValue>);
470   mlir::Value genCmplx(mlir::Type, llvm::ArrayRef<mlir::Value>);
471   mlir::Value genConjg(mlir::Type, llvm::ArrayRef<mlir::Value>);
472   fir::ExtendedValue genCount(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
473   void genCpuTime(llvm::ArrayRef<fir::ExtendedValue>);
474   fir::ExtendedValue genCshift(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
475   void genDateAndTime(llvm::ArrayRef<fir::ExtendedValue>);
476   mlir::Value genDim(mlir::Type, llvm::ArrayRef<mlir::Value>);
477   fir::ExtendedValue genDotProduct(mlir::Type,
478                                    llvm::ArrayRef<fir::ExtendedValue>);
479   mlir::Value genDprod(mlir::Type, llvm::ArrayRef<mlir::Value>);
480   fir::ExtendedValue genEoshift(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
481   void genExit(llvm::ArrayRef<fir::ExtendedValue>);
482   mlir::Value genExponent(mlir::Type, llvm::ArrayRef<mlir::Value>);
483   template <Extremum, ExtremumBehavior>
484   mlir::Value genExtremum(mlir::Type, llvm::ArrayRef<mlir::Value>);
485   mlir::Value genFloor(mlir::Type, llvm::ArrayRef<mlir::Value>);
486   mlir::Value genFraction(mlir::Type resultType,
487                           mlir::ArrayRef<mlir::Value> args);
488   void genGetCommandArgument(mlir::ArrayRef<fir::ExtendedValue> args);
489   void genGetEnvironmentVariable(llvm::ArrayRef<fir::ExtendedValue>);
490   /// Lowering for the IAND intrinsic. The IAND intrinsic expects two arguments
491   /// in the llvm::ArrayRef.
492   mlir::Value genIand(mlir::Type, llvm::ArrayRef<mlir::Value>);
493   mlir::Value genIbclr(mlir::Type, llvm::ArrayRef<mlir::Value>);
494   mlir::Value genIbits(mlir::Type, llvm::ArrayRef<mlir::Value>);
495   mlir::Value genIbset(mlir::Type, llvm::ArrayRef<mlir::Value>);
496   fir::ExtendedValue genIchar(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
497   mlir::Value genIeeeIsFinite(mlir::Type, llvm::ArrayRef<mlir::Value>);
498   template <mlir::arith::CmpIPredicate pred>
499   fir::ExtendedValue genIeeeTypeCompare(mlir::Type,
500                                         llvm::ArrayRef<fir::ExtendedValue>);
501   mlir::Value genIeor(mlir::Type, llvm::ArrayRef<mlir::Value>);
502   fir::ExtendedValue genIndex(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
503   mlir::Value genIor(mlir::Type, llvm::ArrayRef<mlir::Value>);
504   mlir::Value genIshft(mlir::Type, llvm::ArrayRef<mlir::Value>);
505   mlir::Value genIshftc(mlir::Type, llvm::ArrayRef<mlir::Value>);
506   fir::ExtendedValue genLbound(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
507   fir::ExtendedValue genLen(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
508   fir::ExtendedValue genLenTrim(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
509   fir::ExtendedValue genMatmul(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
510   fir::ExtendedValue genMaxloc(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
511   fir::ExtendedValue genMaxval(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
512   fir::ExtendedValue genMerge(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
513   fir::ExtendedValue genMinloc(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
514   fir::ExtendedValue genMinval(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
515   mlir::Value genMod(mlir::Type, llvm::ArrayRef<mlir::Value>);
516   mlir::Value genModulo(mlir::Type, llvm::ArrayRef<mlir::Value>);
517   void genMvbits(llvm::ArrayRef<fir::ExtendedValue>);
518   mlir::Value genNearest(mlir::Type, llvm::ArrayRef<mlir::Value>);
519   mlir::Value genNint(mlir::Type, llvm::ArrayRef<mlir::Value>);
520   mlir::Value genNot(mlir::Type, llvm::ArrayRef<mlir::Value>);
521   fir::ExtendedValue genNull(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
522   fir::ExtendedValue genPack(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
523   fir::ExtendedValue genPresent(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
524   fir::ExtendedValue genProduct(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
525   void genRandomInit(llvm::ArrayRef<fir::ExtendedValue>);
526   void genRandomNumber(llvm::ArrayRef<fir::ExtendedValue>);
527   void genRandomSeed(llvm::ArrayRef<fir::ExtendedValue>);
528   fir::ExtendedValue genRepeat(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
529   fir::ExtendedValue genReshape(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
530   mlir::Value genRRSpacing(mlir::Type resultType,
531                            llvm::ArrayRef<mlir::Value> args);
532   mlir::Value genScale(mlir::Type, llvm::ArrayRef<mlir::Value>);
533   fir::ExtendedValue genScan(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
534   mlir::Value genSetExponent(mlir::Type resultType,
535                              llvm::ArrayRef<mlir::Value> args);
536   mlir::Value genSign(mlir::Type, llvm::ArrayRef<mlir::Value>);
537   fir::ExtendedValue genSize(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
538   mlir::Value genSpacing(mlir::Type resultType,
539                          llvm::ArrayRef<mlir::Value> args);
540   fir::ExtendedValue genSpread(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
541   fir::ExtendedValue genSum(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
542   void genSystemClock(llvm::ArrayRef<fir::ExtendedValue>);
543   fir::ExtendedValue genTransfer(mlir::Type,
544                                  llvm::ArrayRef<fir::ExtendedValue>);
545   fir::ExtendedValue genTranspose(mlir::Type,
546                                   llvm::ArrayRef<fir::ExtendedValue>);
547   fir::ExtendedValue genTrim(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
548   fir::ExtendedValue genUbound(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
549   fir::ExtendedValue genUnpack(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
550   fir::ExtendedValue genVerify(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
551   /// Implement all conversion functions like DBLE, the first argument is
552   /// the value to convert. There may be an additional KIND arguments that
553   /// is ignored because this is already reflected in the result type.
554   mlir::Value genConversion(mlir::Type, llvm::ArrayRef<mlir::Value>);
555 
556   /// Define the different FIR generators that can be mapped to intrinsic to
557   /// generate the related code.
558   using ElementalGenerator = decltype(&IntrinsicLibrary::genAbs);
559   using ExtendedGenerator = decltype(&IntrinsicLibrary::genLenTrim);
560   using SubroutineGenerator = decltype(&IntrinsicLibrary::genDateAndTime);
561   using Generator =
562       std::variant<ElementalGenerator, ExtendedGenerator, SubroutineGenerator>;
563 
564   /// All generators can be outlined. This will build a function named
565   /// "fir."+ <generic name> + "." + <result type code> and generate the
566   /// intrinsic implementation inside instead of at the intrinsic call sites.
567   /// This can be used to keep the FIR more readable. Only one function will
568   /// be generated for all the similar calls in a program.
569   /// If the Generator is nullptr, the wrapper uses genRuntimeCall.
570   template <typename GeneratorType>
571   mlir::Value outlineInWrapper(GeneratorType, llvm::StringRef name,
572                                mlir::Type resultType,
573                                llvm::ArrayRef<mlir::Value> args);
574   template <typename GeneratorType>
575   fir::ExtendedValue
576   outlineInExtendedWrapper(GeneratorType, llvm::StringRef name,
577                            llvm::Optional<mlir::Type> resultType,
578                            llvm::ArrayRef<fir::ExtendedValue> args);
579 
580   template <typename GeneratorType>
581   mlir::func::FuncOp getWrapper(GeneratorType, llvm::StringRef name,
582                                 mlir::FunctionType,
583                                 bool loadRefArguments = false);
584 
585   /// Generate calls to ElementalGenerator, handling the elemental aspects
586   template <typename GeneratorType>
587   fir::ExtendedValue
588   genElementalCall(GeneratorType, llvm::StringRef name, mlir::Type resultType,
589                    llvm::ArrayRef<fir::ExtendedValue> args, bool outline);
590 
591   /// Helper to invoke code generator for the intrinsics given arguments.
592   mlir::Value invokeGenerator(ElementalGenerator generator,
593                               mlir::Type resultType,
594                               llvm::ArrayRef<mlir::Value> args);
595   mlir::Value invokeGenerator(RuntimeCallGenerator generator,
596                               mlir::Type resultType,
597                               llvm::ArrayRef<mlir::Value> args);
598   mlir::Value invokeGenerator(ExtendedGenerator generator,
599                               mlir::Type resultType,
600                               llvm::ArrayRef<mlir::Value> args);
601   mlir::Value invokeGenerator(SubroutineGenerator generator,
602                               llvm::ArrayRef<mlir::Value> args);
603 
604   /// Get pointer to unrestricted intrinsic. Generate the related unrestricted
605   /// intrinsic if it is not defined yet.
606   mlir::SymbolRefAttr
607   getUnrestrictedIntrinsicSymbolRefAttr(llvm::StringRef name,
608                                         mlir::FunctionType signature);
609 
610   /// Add clean-up for \p temp to the current statement context;
611   void addCleanUpForTemp(mlir::Location loc, mlir::Value temp);
612   /// Helper function for generating code clean-up for result descriptors
613   fir::ExtendedValue readAndAddCleanUp(fir::MutableBoxValue resultMutableBox,
614                                        mlir::Type resultType,
615                                        llvm::StringRef errMsg);
616 
617   fir::FirOpBuilder &builder;
618   mlir::Location loc;
619   Fortran::lower::StatementContext *stmtCtx;
620 };
621 
622 struct IntrinsicDummyArgument {
623   const char *name = nullptr;
624   Fortran::lower::LowerIntrinsicArgAs lowerAs =
625       Fortran::lower::LowerIntrinsicArgAs::Value;
626   bool handleDynamicOptional = false;
627 };
628 
629 struct Fortran::lower::IntrinsicArgumentLoweringRules {
630   /// There is no more than 7 non repeated arguments in Fortran intrinsics.
631   IntrinsicDummyArgument args[7];
632   constexpr bool hasDefaultRules() const { return args[0].name == nullptr; }
633 };
634 
635 /// Structure describing what needs to be done to lower intrinsic "name".
636 struct IntrinsicHandler {
637   const char *name;
638   IntrinsicLibrary::Generator generator;
639   // The following may be omitted in the table below.
640   Fortran::lower::IntrinsicArgumentLoweringRules argLoweringRules = {};
641   bool isElemental = true;
642   /// Code heavy intrinsic can be outlined to make FIR
643   /// more readable.
644   bool outline = false;
645 };
646 
647 constexpr auto asValue = Fortran::lower::LowerIntrinsicArgAs::Value;
648 constexpr auto asAddr = Fortran::lower::LowerIntrinsicArgAs::Addr;
649 constexpr auto asBox = Fortran::lower::LowerIntrinsicArgAs::Box;
650 constexpr auto asInquired = Fortran::lower::LowerIntrinsicArgAs::Inquired;
651 using I = IntrinsicLibrary;
652 
653 /// Flag to indicate that an intrinsic argument has to be handled as
654 /// being dynamically optional (e.g. special handling when actual
655 /// argument is an optional variable in the current scope).
656 static constexpr bool handleDynamicOptional = true;
657 
658 /// Table that drives the fir generation depending on the intrinsic.
659 /// one to one mapping with Fortran arguments. If no mapping is
660 /// defined here for a generic intrinsic, genRuntimeCall will be called
661 /// to look for a match in the runtime a emit a call. Note that the argument
662 /// lowering rules for an intrinsic need to be provided only if at least one
663 /// argument must not be lowered by value. In which case, the lowering rules
664 /// should be provided for all the intrinsic arguments for completeness.
665 static constexpr IntrinsicHandler handlers[]{
666     {"abs", &I::genAbs},
667     {"achar", &I::genChar},
668     {"adjustl",
669      &I::genAdjustRtCall<fir::runtime::genAdjustL>,
670      {{{"string", asAddr}}},
671      /*isElemental=*/true},
672     {"adjustr",
673      &I::genAdjustRtCall<fir::runtime::genAdjustR>,
674      {{{"string", asAddr}}},
675      /*isElemental=*/true},
676     {"aimag", &I::genAimag},
677     {"aint", &I::genAint},
678     {"all",
679      &I::genAll,
680      {{{"mask", asAddr}, {"dim", asValue}}},
681      /*isElemental=*/false},
682     {"allocated",
683      &I::genAllocated,
684      {{{"array", asInquired}, {"scalar", asInquired}}},
685      /*isElemental=*/false},
686     {"anint", &I::genAnint},
687     {"any",
688      &I::genAny,
689      {{{"mask", asAddr}, {"dim", asValue}}},
690      /*isElemental=*/false},
691     {"associated",
692      &I::genAssociated,
693      {{{"pointer", asInquired}, {"target", asInquired}}},
694      /*isElemental=*/false},
695     {"btest", &I::genBtest},
696     {"ceiling", &I::genCeiling},
697     {"char", &I::genChar},
698     {"cmplx",
699      &I::genCmplx,
700      {{{"x", asValue}, {"y", asValue, handleDynamicOptional}}}},
701     {"command_argument_count", &I::genCommandArgumentCount},
702     {"conjg", &I::genConjg},
703     {"count",
704      &I::genCount,
705      {{{"mask", asAddr}, {"dim", asValue}, {"kind", asValue}}},
706      /*isElemental=*/false},
707     {"cpu_time",
708      &I::genCpuTime,
709      {{{"time", asAddr}}},
710      /*isElemental=*/false},
711     {"cshift",
712      &I::genCshift,
713      {{{"array", asAddr}, {"shift", asAddr}, {"dim", asValue}}},
714      /*isElemental=*/false},
715     {"date_and_time",
716      &I::genDateAndTime,
717      {{{"date", asAddr, handleDynamicOptional},
718        {"time", asAddr, handleDynamicOptional},
719        {"zone", asAddr, handleDynamicOptional},
720        {"values", asBox, handleDynamicOptional}}},
721      /*isElemental=*/false},
722     {"dble", &I::genConversion},
723     {"dim", &I::genDim},
724     {"dot_product",
725      &I::genDotProduct,
726      {{{"vector_a", asBox}, {"vector_b", asBox}}},
727      /*isElemental=*/false},
728     {"dprod", &I::genDprod},
729     {"eoshift",
730      &I::genEoshift,
731      {{{"array", asBox},
732        {"shift", asAddr},
733        {"boundary", asBox, handleDynamicOptional},
734        {"dim", asValue}}},
735      /*isElemental=*/false},
736     {"exit",
737      &I::genExit,
738      {{{"status", asValue, handleDynamicOptional}}},
739      /*isElemental=*/false},
740     {"exponent", &I::genExponent},
741     {"floor", &I::genFloor},
742     {"fraction", &I::genFraction},
743     {"get_command_argument",
744      &I::genGetCommandArgument,
745      {{{"number", asValue},
746        {"value", asBox, handleDynamicOptional},
747        {"length", asAddr},
748        {"status", asAddr},
749        {"errmsg", asBox, handleDynamicOptional}}},
750      /*isElemental=*/false},
751     {"get_environment_variable",
752      &I::genGetEnvironmentVariable,
753      {{{"name", asBox},
754        {"value", asBox, handleDynamicOptional},
755        {"length", asAddr},
756        {"status", asAddr},
757        {"trim_name", asAddr},
758        {"errmsg", asBox, handleDynamicOptional}}},
759      /*isElemental=*/false},
760     {"iachar", &I::genIchar},
761     {"iand", &I::genIand},
762     {"ibclr", &I::genIbclr},
763     {"ibits", &I::genIbits},
764     {"ibset", &I::genIbset},
765     {"ichar", &I::genIchar},
766     {"ieee_class_eq", &I::genIeeeTypeCompare<mlir::arith::CmpIPredicate::eq>},
767     {"ieee_class_ne", &I::genIeeeTypeCompare<mlir::arith::CmpIPredicate::ne>},
768     {"ieee_is_finite", &I::genIeeeIsFinite},
769     {"ieee_round_eq", &I::genIeeeTypeCompare<mlir::arith::CmpIPredicate::eq>},
770     {"ieee_round_ne", &I::genIeeeTypeCompare<mlir::arith::CmpIPredicate::ne>},
771     {"ieor", &I::genIeor},
772     {"index",
773      &I::genIndex,
774      {{{"string", asAddr},
775        {"substring", asAddr},
776        {"back", asValue, handleDynamicOptional},
777        {"kind", asValue}}}},
778     {"ior", &I::genIor},
779     {"ishft", &I::genIshft},
780     {"ishftc", &I::genIshftc},
781     {"lbound",
782      &I::genLbound,
783      {{{"array", asInquired}, {"dim", asValue}, {"kind", asValue}}},
784      /*isElemental=*/false},
785     {"len",
786      &I::genLen,
787      {{{"string", asInquired}, {"kind", asValue}}},
788      /*isElemental=*/false},
789     {"len_trim", &I::genLenTrim},
790     {"lge", &I::genCharacterCompare<mlir::arith::CmpIPredicate::sge>},
791     {"lgt", &I::genCharacterCompare<mlir::arith::CmpIPredicate::sgt>},
792     {"lle", &I::genCharacterCompare<mlir::arith::CmpIPredicate::sle>},
793     {"llt", &I::genCharacterCompare<mlir::arith::CmpIPredicate::slt>},
794     {"matmul",
795      &I::genMatmul,
796      {{{"matrix_a", asAddr}, {"matrix_b", asAddr}}},
797      /*isElemental=*/false},
798     {"max", &I::genExtremum<Extremum::Max, ExtremumBehavior::MinMaxss>},
799     {"maxloc",
800      &I::genMaxloc,
801      {{{"array", asBox},
802        {"dim", asValue},
803        {"mask", asBox, handleDynamicOptional},
804        {"kind", asValue},
805        {"back", asValue, handleDynamicOptional}}},
806      /*isElemental=*/false},
807     {"maxval",
808      &I::genMaxval,
809      {{{"array", asBox},
810        {"dim", asValue},
811        {"mask", asBox, handleDynamicOptional}}},
812      /*isElemental=*/false},
813     {"merge", &I::genMerge},
814     {"min", &I::genExtremum<Extremum::Min, ExtremumBehavior::MinMaxss>},
815     {"minloc",
816      &I::genMinloc,
817      {{{"array", asBox},
818        {"dim", asValue},
819        {"mask", asBox, handleDynamicOptional},
820        {"kind", asValue},
821        {"back", asValue, handleDynamicOptional}}},
822      /*isElemental=*/false},
823     {"minval",
824      &I::genMinval,
825      {{{"array", asBox},
826        {"dim", asValue},
827        {"mask", asBox, handleDynamicOptional}}},
828      /*isElemental=*/false},
829     {"mod", &I::genMod},
830     {"modulo", &I::genModulo},
831     {"mvbits",
832      &I::genMvbits,
833      {{{"from", asValue},
834        {"frompos", asValue},
835        {"len", asValue},
836        {"to", asAddr},
837        {"topos", asValue}}}},
838     {"nearest", &I::genNearest},
839     {"nint", &I::genNint},
840     {"not", &I::genNot},
841     {"null", &I::genNull, {{{"mold", asInquired}}}, /*isElemental=*/false},
842     {"pack",
843      &I::genPack,
844      {{{"array", asBox},
845        {"mask", asBox},
846        {"vector", asBox, handleDynamicOptional}}},
847      /*isElemental=*/false},
848     {"present",
849      &I::genPresent,
850      {{{"a", asInquired}}},
851      /*isElemental=*/false},
852     {"product",
853      &I::genProduct,
854      {{{"array", asBox},
855        {"dim", asValue},
856        {"mask", asBox, handleDynamicOptional}}},
857      /*isElemental=*/false},
858     {"random_init",
859      &I::genRandomInit,
860      {{{"repeatable", asValue}, {"image_distinct", asValue}}},
861      /*isElemental=*/false},
862     {"random_number",
863      &I::genRandomNumber,
864      {{{"harvest", asBox}}},
865      /*isElemental=*/false},
866     {"random_seed",
867      &I::genRandomSeed,
868      {{{"size", asBox}, {"put", asBox}, {"get", asBox}}},
869      /*isElemental=*/false},
870     {"repeat",
871      &I::genRepeat,
872      {{{"string", asAddr}, {"ncopies", asValue}}},
873      /*isElemental=*/false},
874     {"reshape",
875      &I::genReshape,
876      {{{"source", asBox},
877        {"shape", asBox},
878        {"pad", asBox, handleDynamicOptional},
879        {"order", asBox, handleDynamicOptional}}},
880      /*isElemental=*/false},
881     {"rrspacing", &I::genRRSpacing},
882     {"scale",
883      &I::genScale,
884      {{{"x", asValue}, {"i", asValue}}},
885      /*isElemental=*/true},
886     {"scan",
887      &I::genScan,
888      {{{"string", asAddr},
889        {"set", asAddr},
890        {"back", asValue, handleDynamicOptional},
891        {"kind", asValue}}},
892      /*isElemental=*/true},
893     {"set_exponent", &I::genSetExponent},
894     {"sign", &I::genSign},
895     {"size",
896      &I::genSize,
897      {{{"array", asBox},
898        {"dim", asAddr, handleDynamicOptional},
899        {"kind", asValue}}},
900      /*isElemental=*/false},
901     {"spacing", &I::genSpacing},
902     {"spread",
903      &I::genSpread,
904      {{{"source", asAddr}, {"dim", asValue}, {"ncopies", asValue}}},
905      /*isElemental=*/false},
906     {"sum",
907      &I::genSum,
908      {{{"array", asBox},
909        {"dim", asValue},
910        {"mask", asBox, handleDynamicOptional}}},
911      /*isElemental=*/false},
912     {"system_clock",
913      &I::genSystemClock,
914      {{{"count", asAddr}, {"count_rate", asAddr}, {"count_max", asAddr}}},
915      /*isElemental=*/false},
916     {"transfer",
917      &I::genTransfer,
918      {{{"source", asAddr}, {"mold", asAddr}, {"size", asValue}}},
919      /*isElemental=*/false},
920     {"transpose",
921      &I::genTranspose,
922      {{{"matrix", asAddr}}},
923      /*isElemental=*/false},
924     {"trim", &I::genTrim, {{{"string", asAddr}}}, /*isElemental=*/false},
925     {"ubound",
926      &I::genUbound,
927      {{{"array", asBox}, {"dim", asValue}, {"kind", asValue}}},
928      /*isElemental=*/false},
929     {"unpack",
930      &I::genUnpack,
931      {{{"vector", asBox}, {"mask", asBox}, {"field", asBox}}},
932      /*isElemental=*/false},
933     {"verify",
934      &I::genVerify,
935      {{{"string", asAddr},
936        {"set", asAddr},
937        {"back", asValue, handleDynamicOptional},
938        {"kind", asValue}}},
939      /*isElemental=*/true},
940 };
941 
942 static const IntrinsicHandler *findIntrinsicHandler(llvm::StringRef name) {
943   auto compare = [](const IntrinsicHandler &handler, llvm::StringRef name) {
944     return name.compare(handler.name) > 0;
945   };
946   auto result =
947       std::lower_bound(std::begin(handlers), std::end(handlers), name, compare);
948   return result != std::end(handlers) && result->name == name ? result
949                                                               : nullptr;
950 }
951 
952 /// To make fir output more readable for debug, one can outline all intrinsic
953 /// implementation in wrappers (overrides the IntrinsicHandler::outline flag).
954 static llvm::cl::opt<bool> outlineAllIntrinsics(
955     "outline-intrinsics",
956     llvm::cl::desc(
957         "Lower all intrinsic procedure implementation in their own functions"),
958     llvm::cl::init(false));
959 
960 //===----------------------------------------------------------------------===//
961 // Math runtime description and matching utility
962 //===----------------------------------------------------------------------===//
963 
964 /// Command line option to control how math operations are lowered
965 /// into MLIR.
966 /// Going forward, most of the math operations have to be lowered
967 /// to some MLIR dialect operations or libm calls, if the corresponding
968 /// MLIR operation is not available or not reasonable to create
969 /// (e.g. there are no known optimization opportunities for the math
970 /// operation in MLIR).
971 ///
972 /// In general, exposing MLIR operations early can potentially enable more
973 /// MLIR optimizations.
974 llvm::cl::opt<bool> lowerEarlyToLibCall(
975     "lower-math-early",
976     llvm::cl::desc("Controls when to lower Math intrinsics to library calls"),
977     llvm::cl::init(true));
978 
979 /// Command line option to modify math runtime behavior used to implement
980 /// intrinsics. This option applies both to early and late math-lowering modes.
981 enum MathRuntimeVersion {
982   fastVersion,
983   relaxedVersion,
984   preciseVersion,
985   llvmOnly
986 };
987 llvm::cl::opt<MathRuntimeVersion> mathRuntimeVersion(
988     "math-runtime", llvm::cl::desc("Select math operations' runtime behavior:"),
989     llvm::cl::values(
990         clEnumValN(fastVersion, "fast", "use fast runtime behavior"),
991         clEnumValN(relaxedVersion, "relaxed", "use relaxed runtime behavior"),
992         clEnumValN(preciseVersion, "precise", "use precise runtime behavior"),
993         clEnumValN(llvmOnly, "llvm",
994                    "only use LLVM intrinsics (may be incomplete)")),
995     llvm::cl::init(fastVersion));
996 
997 struct RuntimeFunction {
998   // llvm::StringRef comparison operator are not constexpr, so use string_view.
999   using Key = std::string_view;
1000   // Needed for implicit compare with keys.
1001   constexpr operator Key() const { return key; }
1002   Key key; // intrinsic name
1003 
1004   // Name of a runtime function that implements the operation.
1005   llvm::StringRef symbol;
1006   fir::runtime::FuncTypeBuilderFunc typeGenerator;
1007 };
1008 
1009 #define RUNTIME_STATIC_DESCRIPTION(name, func)                                 \
1010   {#name, #func, fir::runtime::RuntimeTableKey<decltype(func)>::getTypeModel()},
1011 static constexpr RuntimeFunction pgmathFast[] = {
1012 #define PGMATH_FAST
1013 #define PGMATH_USE_ALL_TYPES(name, func) RUNTIME_STATIC_DESCRIPTION(name, func)
1014 #include "flang/Evaluate/pgmath.h.inc"
1015 };
1016 static constexpr RuntimeFunction pgmathRelaxed[] = {
1017 #define PGMATH_RELAXED
1018 #define PGMATH_USE_ALL_TYPES(name, func) RUNTIME_STATIC_DESCRIPTION(name, func)
1019 #include "flang/Evaluate/pgmath.h.inc"
1020 };
1021 static constexpr RuntimeFunction pgmathPrecise[] = {
1022 #define PGMATH_PRECISE
1023 #define PGMATH_USE_ALL_TYPES(name, func) RUNTIME_STATIC_DESCRIPTION(name, func)
1024 #include "flang/Evaluate/pgmath.h.inc"
1025 };
1026 
1027 static mlir::FunctionType genF32F32FuncType(mlir::MLIRContext *context) {
1028   mlir::Type t = mlir::FloatType::getF32(context);
1029   return mlir::FunctionType::get(context, {t}, {t});
1030 }
1031 
1032 static mlir::FunctionType genF64F64FuncType(mlir::MLIRContext *context) {
1033   mlir::Type t = mlir::FloatType::getF64(context);
1034   return mlir::FunctionType::get(context, {t}, {t});
1035 }
1036 
1037 static mlir::FunctionType genF32F32F32FuncType(mlir::MLIRContext *context) {
1038   auto t = mlir::FloatType::getF32(context);
1039   return mlir::FunctionType::get(context, {t, t}, {t});
1040 }
1041 
1042 static mlir::FunctionType genF64F64F64FuncType(mlir::MLIRContext *context) {
1043   auto t = mlir::FloatType::getF64(context);
1044   return mlir::FunctionType::get(context, {t, t}, {t});
1045 }
1046 
1047 static mlir::FunctionType genF80F80F80FuncType(mlir::MLIRContext *context) {
1048   auto t = mlir::FloatType::getF80(context);
1049   return mlir::FunctionType::get(context, {t, t}, {t});
1050 }
1051 
1052 static mlir::FunctionType genF128F128F128FuncType(mlir::MLIRContext *context) {
1053   auto t = mlir::FloatType::getF128(context);
1054   return mlir::FunctionType::get(context, {t, t}, {t});
1055 }
1056 
1057 template <int Bits>
1058 static mlir::FunctionType genIntF64FuncType(mlir::MLIRContext *context) {
1059   auto t = mlir::FloatType::getF64(context);
1060   auto r = mlir::IntegerType::get(context, Bits);
1061   return mlir::FunctionType::get(context, {t}, {r});
1062 }
1063 
1064 template <int Bits>
1065 static mlir::FunctionType genIntF32FuncType(mlir::MLIRContext *context) {
1066   auto t = mlir::FloatType::getF32(context);
1067   auto r = mlir::IntegerType::get(context, Bits);
1068   return mlir::FunctionType::get(context, {t}, {r});
1069 }
1070 
1071 template <int Bits>
1072 static mlir::FunctionType genF64F64IntFuncType(mlir::MLIRContext *context) {
1073   auto ftype = mlir::FloatType::getF64(context);
1074   auto itype = mlir::IntegerType::get(context, Bits);
1075   return mlir::FunctionType::get(context, {ftype, itype}, {ftype});
1076 }
1077 
1078 template <int Bits>
1079 static mlir::FunctionType genF32F32IntFuncType(mlir::MLIRContext *context) {
1080   auto ftype = mlir::FloatType::getF32(context);
1081   auto itype = mlir::IntegerType::get(context, Bits);
1082   return mlir::FunctionType::get(context, {ftype, itype}, {ftype});
1083 }
1084 
1085 /// Callback type for generating lowering for a math operation.
1086 using MathGeneratorTy = mlir::Value (*)(fir::FirOpBuilder &, mlir::Location,
1087                                         llvm::StringRef, mlir::FunctionType,
1088                                         llvm::ArrayRef<mlir::Value>);
1089 
1090 struct MathOperation {
1091   // llvm::StringRef comparison operator are not constexpr, so use string_view.
1092   using Key = std::string_view;
1093   // Needed for implicit compare with keys.
1094   constexpr operator Key() const { return key; }
1095   // Intrinsic name.
1096   Key key;
1097 
1098   // Name of a runtime function that implements the operation.
1099   llvm::StringRef runtimeFunc;
1100   fir::runtime::FuncTypeBuilderFunc typeGenerator;
1101 
1102   // A callback to generate FIR for the intrinsic defined by 'key'.
1103   // A callback may generate either dedicated MLIR operation(s) or
1104   // a function call to a runtime function with name defined by
1105   // 'runtimeFunc'.
1106   MathGeneratorTy funcGenerator;
1107 };
1108 
1109 static mlir::Value genLibCall(fir::FirOpBuilder &builder, mlir::Location loc,
1110                               llvm::StringRef libFuncName,
1111                               mlir::FunctionType libFuncType,
1112                               llvm::ArrayRef<mlir::Value> args) {
1113   LLVM_DEBUG(llvm::dbgs() << "Generating '" << libFuncName
1114                           << "' call with type ";
1115              libFuncType.dump(); llvm::dbgs() << "\n");
1116   mlir::func::FuncOp funcOp =
1117       builder.addNamedFunction(loc, libFuncName, libFuncType);
1118   // TODO: ensure 'strictfp' setting on the call for "precise/strict"
1119   //       FP mode. Set appropriate Fast-Math Flags otherwise.
1120   // TODO: we should also mark as many libm function as possible
1121   //       with 'pure' attribute (of course, not in strict FP mode).
1122   auto libCall = builder.create<fir::CallOp>(loc, funcOp, args);
1123   LLVM_DEBUG(libCall.dump(); llvm::dbgs() << "\n");
1124   return libCall.getResult(0);
1125 }
1126 
1127 template <typename T>
1128 static mlir::Value genMathOp(fir::FirOpBuilder &builder, mlir::Location loc,
1129                              llvm::StringRef mathLibFuncName,
1130                              mlir::FunctionType mathLibFuncType,
1131                              llvm::ArrayRef<mlir::Value> args) {
1132   // TODO: we have to annotate the math operations with flags
1133   //       that will allow to define FP accuracy/exception
1134   //       behavior per operation, so that after early multi-module
1135   //       MLIR inlining we can distiguish operation that were
1136   //       compiled with different settings.
1137   //       Suggestion:
1138   //         * For "relaxed" FP mode set all Fast-Math Flags
1139   //           (see "[RFC] FastMath flags support in MLIR (arith dialect)"
1140   //           topic at discourse.llvm.org).
1141   //         * For "fast" FP mode set all Fast-Math Flags except 'afn'.
1142   //         * For "precise/strict" FP mode generate fir.calls to libm
1143   //           entries and annotate them with an attribute that will
1144   //           end up transformed into 'strictfp' LLVM attribute (TBD).
1145   //           Elsewhere, "precise/strict" FP mode should also set
1146   //           'strictfp' for all user functions and calls so that
1147   //           LLVM backend does the right job.
1148   //         * Operations that cannot be reasonably optimized in MLIR
1149   //           can be also lowered to libm calls for "fast" and "relaxed"
1150   //           modes.
1151   mlir::Value result;
1152   if (mathRuntimeVersion == preciseVersion) {
1153     result = genLibCall(builder, loc, mathLibFuncName, mathLibFuncType, args);
1154   } else {
1155     LLVM_DEBUG(llvm::dbgs() << "Generating '" << mathLibFuncName
1156                             << "' operation with type ";
1157                mathLibFuncType.dump(); llvm::dbgs() << "\n");
1158     result = builder.create<T>(loc, args);
1159   }
1160   LLVM_DEBUG(result.dump(); llvm::dbgs() << "\n");
1161   return result;
1162 }
1163 
1164 /// Mapping between mathematical intrinsic operations and MLIR operations
1165 /// of some appropriate dialect (math, complex, etc.) or libm calls.
1166 /// TODO: support remaining Fortran math intrinsics.
1167 ///       See https://gcc.gnu.org/onlinedocs/gcc-12.1.0/gfortran/\
1168 ///       Intrinsic-Procedures.html for a reference.
1169 static constexpr MathOperation mathOperations[] = {
1170     {"abs", "fabsf", genF32F32FuncType, genMathOp<mlir::math::AbsOp>},
1171     {"abs", "fabs", genF64F64FuncType, genMathOp<mlir::math::AbsOp>},
1172     // llvm.trunc behaves the same way as libm's trunc.
1173     {"aint", "llvm.trunc.f32", genF32F32FuncType, genLibCall},
1174     {"aint", "llvm.trunc.f64", genF64F64FuncType, genLibCall},
1175     // llvm.round behaves the same way as libm's round.
1176     {"anint", "llvm.round.f32", genF32F32FuncType,
1177      genMathOp<mlir::LLVM::RoundOp>},
1178     {"anint", "llvm.round.f64", genF64F64FuncType,
1179      genMathOp<mlir::LLVM::RoundOp>},
1180     {"atan", "atanf", genF32F32FuncType, genMathOp<mlir::math::AtanOp>},
1181     {"atan", "atan", genF64F64FuncType, genMathOp<mlir::math::AtanOp>},
1182     {"atan2", "atan2f", genF32F32F32FuncType, genMathOp<mlir::math::Atan2Op>},
1183     {"atan2", "atan2", genF64F64F64FuncType, genMathOp<mlir::math::Atan2Op>},
1184     // math::CeilOp returns a real, while Fortran CEILING returns integer.
1185     {"ceil", "ceilf", genF32F32FuncType, genMathOp<mlir::math::CeilOp>},
1186     {"ceil", "ceil", genF64F64FuncType, genMathOp<mlir::math::CeilOp>},
1187     {"cos", "cosf", genF32F32FuncType, genMathOp<mlir::math::CosOp>},
1188     {"cos", "cos", genF64F64FuncType, genMathOp<mlir::math::CosOp>},
1189     {"erf", "erff", genF32F32FuncType, genMathOp<mlir::math::ErfOp>},
1190     {"erf", "erf", genF64F64FuncType, genMathOp<mlir::math::ErfOp>},
1191     {"exp", "expf", genF32F32FuncType, genMathOp<mlir::math::ExpOp>},
1192     {"exp", "exp", genF64F64FuncType, genMathOp<mlir::math::ExpOp>},
1193     // math::FloorOp returns a real, while Fortran FLOOR returns integer.
1194     {"floor", "floorf", genF32F32FuncType, genMathOp<mlir::math::FloorOp>},
1195     {"floor", "floor", genF64F64FuncType, genMathOp<mlir::math::FloorOp>},
1196     {"hypot", "hypotf", genF32F32F32FuncType, genLibCall},
1197     {"hypot", "hypot", genF64F64F64FuncType, genLibCall},
1198     {"log", "logf", genF32F32FuncType, genMathOp<mlir::math::LogOp>},
1199     {"log", "log", genF64F64FuncType, genMathOp<mlir::math::LogOp>},
1200     {"log10", "log10f", genF32F32FuncType, genMathOp<mlir::math::Log10Op>},
1201     {"log10", "log10", genF64F64FuncType, genMathOp<mlir::math::Log10Op>},
1202     // llvm.lround behaves the same way as libm's lround.
1203     {"nint", "llvm.lround.i64.f64", genIntF64FuncType<64>, genLibCall},
1204     {"nint", "llvm.lround.i64.f32", genIntF32FuncType<64>, genLibCall},
1205     {"nint", "llvm.lround.i32.f64", genIntF64FuncType<32>, genLibCall},
1206     {"nint", "llvm.lround.i32.f32", genIntF32FuncType<32>, genLibCall},
1207     {"pow", "powf", genF32F32F32FuncType, genMathOp<mlir::math::PowFOp>},
1208     {"pow", "pow", genF64F64F64FuncType, genMathOp<mlir::math::PowFOp>},
1209     // TODO: add PowIOp in math and complex dialects.
1210     {"pow", "llvm.powi.f32.i32", genF32F32IntFuncType<32>, genLibCall},
1211     {"pow", "llvm.powi.f64.i32", genF64F64IntFuncType<32>, genLibCall},
1212     {"sign", "copysignf", genF32F32F32FuncType,
1213      genMathOp<mlir::math::CopySignOp>},
1214     {"sign", "copysign", genF64F64F64FuncType,
1215      genMathOp<mlir::math::CopySignOp>},
1216     {"sin", "sinf", genF32F32FuncType, genMathOp<mlir::math::SinOp>},
1217     {"sin", "sin", genF64F64FuncType, genMathOp<mlir::math::SinOp>},
1218     {"sqrt", "sqrtf", genF32F32FuncType, genMathOp<mlir::math::SqrtOp>},
1219     {"sqrt", "sqrt", genF64F64FuncType, genMathOp<mlir::math::SqrtOp>},
1220     {"tanh", "tanhf", genF32F32FuncType, genMathOp<mlir::math::TanhOp>},
1221     {"tanh", "tanh", genF64F64FuncType, genMathOp<mlir::math::TanhOp>},
1222 };
1223 
1224 // Note: These are also defined as operations in LLVM dialect. See if this
1225 // can be use and has advantages.
1226 // TODO: remove this table, since the late math lowering should
1227 //       replace it and generate proper MLIR operations rather
1228 //       than llvm intrinsic calls, which still look like generic
1229 //       calls to MLIR and do not enable many optimizations.
1230 //       When late math lowering is able to handle all math operations
1231 //       described in pgmath.h.inc and in the table below, we can
1232 //       switch to it by default.
1233 static constexpr RuntimeFunction llvmIntrinsics[] = {
1234     {"abs", "llvm.fabs.f32", genF32F32FuncType},
1235     {"abs", "llvm.fabs.f64", genF64F64FuncType},
1236     {"aint", "llvm.trunc.f32", genF32F32FuncType},
1237     {"aint", "llvm.trunc.f64", genF64F64FuncType},
1238     {"anint", "llvm.round.f32", genF32F32FuncType},
1239     {"anint", "llvm.round.f64", genF64F64FuncType},
1240     {"atan", "atanf", genF32F32FuncType},
1241     {"atan", "atan", genF64F64FuncType},
1242     // ceil is used for CEILING but is different, it returns a real.
1243     {"ceil", "llvm.ceil.f32", genF32F32FuncType},
1244     {"ceil", "llvm.ceil.f64", genF64F64FuncType},
1245     {"cos", "llvm.cos.f32", genF32F32FuncType},
1246     {"cos", "llvm.cos.f64", genF64F64FuncType},
1247     {"cosh", "coshf", genF32F32FuncType},
1248     {"cosh", "cosh", genF64F64FuncType},
1249     {"exp", "llvm.exp.f32", genF32F32FuncType},
1250     {"exp", "llvm.exp.f64", genF64F64FuncType},
1251     // llvm.floor is used for FLOOR, but returns real.
1252     {"floor", "llvm.floor.f32", genF32F32FuncType},
1253     {"floor", "llvm.floor.f64", genF64F64FuncType},
1254     {"log", "llvm.log.f32", genF32F32FuncType},
1255     {"log", "llvm.log.f64", genF64F64FuncType},
1256     {"log10", "llvm.log10.f32", genF32F32FuncType},
1257     {"log10", "llvm.log10.f64", genF64F64FuncType},
1258     {"nint", "llvm.lround.i64.f64", genIntF64FuncType<64>},
1259     {"nint", "llvm.lround.i64.f32", genIntF32FuncType<64>},
1260     {"nint", "llvm.lround.i32.f64", genIntF64FuncType<32>},
1261     {"nint", "llvm.lround.i32.f32", genIntF32FuncType<32>},
1262     {"pow", "llvm.pow.f32", genF32F32F32FuncType},
1263     {"pow", "llvm.pow.f64", genF64F64F64FuncType},
1264     {"sign", "llvm.copysign.f32", genF32F32F32FuncType},
1265     {"sign", "llvm.copysign.f64", genF64F64F64FuncType},
1266     {"sign", "llvm.copysign.f80", genF80F80F80FuncType},
1267     {"sign", "llvm.copysign.f128", genF128F128F128FuncType},
1268     {"sin", "llvm.sin.f32", genF32F32FuncType},
1269     {"sin", "llvm.sin.f64", genF64F64FuncType},
1270     {"sinh", "sinhf", genF32F32FuncType},
1271     {"sinh", "sinh", genF64F64FuncType},
1272     {"sqrt", "llvm.sqrt.f32", genF32F32FuncType},
1273     {"sqrt", "llvm.sqrt.f64", genF64F64FuncType},
1274 };
1275 
1276 // This helper class computes a "distance" between two function types.
1277 // The distance measures how many narrowing conversions of actual arguments
1278 // and result of "from" must be made in order to use "to" instead of "from".
1279 // For instance, the distance between ACOS(REAL(10)) and ACOS(REAL(8)) is
1280 // greater than the one between ACOS(REAL(10)) and ACOS(REAL(16)). This means
1281 // if no implementation of ACOS(REAL(10)) is available, it is better to use
1282 // ACOS(REAL(16)) with casts rather than ACOS(REAL(8)).
1283 // Note that this is not a symmetric distance and the order of "from" and "to"
1284 // arguments matters, d(foo, bar) may not be the same as d(bar, foo) because it
1285 // may be safe to replace foo by bar, but not the opposite.
1286 class FunctionDistance {
1287 public:
1288   FunctionDistance() : infinite{true} {}
1289 
1290   FunctionDistance(mlir::FunctionType from, mlir::FunctionType to) {
1291     unsigned nInputs = from.getNumInputs();
1292     unsigned nResults = from.getNumResults();
1293     if (nResults != to.getNumResults() || nInputs != to.getNumInputs()) {
1294       infinite = true;
1295     } else {
1296       for (decltype(nInputs) i = 0; i < nInputs && !infinite; ++i)
1297         addArgumentDistance(from.getInput(i), to.getInput(i));
1298       for (decltype(nResults) i = 0; i < nResults && !infinite; ++i)
1299         addResultDistance(to.getResult(i), from.getResult(i));
1300     }
1301   }
1302 
1303   /// Beware both d1.isSmallerThan(d2) *and* d2.isSmallerThan(d1) may be
1304   /// false if both d1 and d2 are infinite. This implies that
1305   ///  d1.isSmallerThan(d2) is not equivalent to !d2.isSmallerThan(d1)
1306   bool isSmallerThan(const FunctionDistance &d) const {
1307     return !infinite &&
1308            (d.infinite || std::lexicographical_compare(
1309                               conversions.begin(), conversions.end(),
1310                               d.conversions.begin(), d.conversions.end()));
1311   }
1312 
1313   bool isLosingPrecision() const {
1314     return conversions[narrowingArg] != 0 || conversions[extendingResult] != 0;
1315   }
1316 
1317   bool isInfinite() const { return infinite; }
1318 
1319 private:
1320   enum class Conversion { Forbidden, None, Narrow, Extend };
1321 
1322   void addArgumentDistance(mlir::Type from, mlir::Type to) {
1323     switch (conversionBetweenTypes(from, to)) {
1324     case Conversion::Forbidden:
1325       infinite = true;
1326       break;
1327     case Conversion::None:
1328       break;
1329     case Conversion::Narrow:
1330       conversions[narrowingArg]++;
1331       break;
1332     case Conversion::Extend:
1333       conversions[nonNarrowingArg]++;
1334       break;
1335     }
1336   }
1337 
1338   void addResultDistance(mlir::Type from, mlir::Type to) {
1339     switch (conversionBetweenTypes(from, to)) {
1340     case Conversion::Forbidden:
1341       infinite = true;
1342       break;
1343     case Conversion::None:
1344       break;
1345     case Conversion::Narrow:
1346       conversions[nonExtendingResult]++;
1347       break;
1348     case Conversion::Extend:
1349       conversions[extendingResult]++;
1350       break;
1351     }
1352   }
1353 
1354   // Floating point can be mlir::FloatType or fir::real
1355   static unsigned getFloatingPointWidth(mlir::Type t) {
1356     if (auto f{t.dyn_cast<mlir::FloatType>()})
1357       return f.getWidth();
1358     // FIXME: Get width another way for fir.real/complex
1359     // - use fir/KindMapping.h and llvm::Type
1360     // - or use evaluate/type.h
1361     if (auto r{t.dyn_cast<fir::RealType>()})
1362       return r.getFKind() * 4;
1363     if (auto cplx{t.dyn_cast<fir::ComplexType>()})
1364       return cplx.getFKind() * 4;
1365     llvm_unreachable("not a floating-point type");
1366   }
1367 
1368   static Conversion conversionBetweenTypes(mlir::Type from, mlir::Type to) {
1369     if (from == to)
1370       return Conversion::None;
1371 
1372     if (auto fromIntTy{from.dyn_cast<mlir::IntegerType>()}) {
1373       if (auto toIntTy{to.dyn_cast<mlir::IntegerType>()}) {
1374         return fromIntTy.getWidth() > toIntTy.getWidth() ? Conversion::Narrow
1375                                                          : Conversion::Extend;
1376       }
1377     }
1378 
1379     if (fir::isa_real(from) && fir::isa_real(to)) {
1380       return getFloatingPointWidth(from) > getFloatingPointWidth(to)
1381                  ? Conversion::Narrow
1382                  : Conversion::Extend;
1383     }
1384 
1385     if (auto fromCplxTy{from.dyn_cast<fir::ComplexType>()}) {
1386       if (auto toCplxTy{to.dyn_cast<fir::ComplexType>()}) {
1387         return getFloatingPointWidth(fromCplxTy) >
1388                        getFloatingPointWidth(toCplxTy)
1389                    ? Conversion::Narrow
1390                    : Conversion::Extend;
1391       }
1392     }
1393     // Notes:
1394     // - No conversion between character types, specialization of runtime
1395     // functions should be made instead.
1396     // - It is not clear there is a use case for automatic conversions
1397     // around Logical and it may damage hidden information in the physical
1398     // storage so do not do it.
1399     return Conversion::Forbidden;
1400   }
1401 
1402   // Below are indexes to access data in conversions.
1403   // The order in data does matter for lexicographical_compare
1404   enum {
1405     narrowingArg = 0,   // usually bad
1406     extendingResult,    // usually bad
1407     nonExtendingResult, // usually ok
1408     nonNarrowingArg,    // usually ok
1409     dataSize
1410   };
1411 
1412   std::array<int, dataSize> conversions = {};
1413   bool infinite = false; // When forbidden conversion or wrong argument number
1414 };
1415 
1416 /// Build mlir::func::FuncOp from runtime symbol description and add
1417 /// fir.runtime attribute.
1418 static mlir::func::FuncOp getFuncOp(mlir::Location loc,
1419                                     fir::FirOpBuilder &builder,
1420                                     const RuntimeFunction &runtime) {
1421   mlir::func::FuncOp function = builder.addNamedFunction(
1422       loc, runtime.symbol, runtime.typeGenerator(builder.getContext()));
1423   function->setAttr("fir.runtime", builder.getUnitAttr());
1424   return function;
1425 }
1426 
1427 /// Select runtime function that has the smallest distance to the intrinsic
1428 /// function type and that will not imply narrowing arguments or extending the
1429 /// result.
1430 /// If nothing is found, the mlir::func::FuncOp will contain a nullptr.
1431 static mlir::func::FuncOp searchFunctionInLibrary(
1432     mlir::Location loc, fir::FirOpBuilder &builder,
1433     const Fortran::common::StaticMultimapView<RuntimeFunction> &lib,
1434     llvm::StringRef name, mlir::FunctionType funcType,
1435     const RuntimeFunction **bestNearMatch,
1436     FunctionDistance &bestMatchDistance) {
1437   std::pair<const RuntimeFunction *, const RuntimeFunction *> range =
1438       lib.equal_range(name);
1439   for (auto iter = range.first; iter != range.second && iter; ++iter) {
1440     const RuntimeFunction &impl = *iter;
1441     mlir::FunctionType implType = impl.typeGenerator(builder.getContext());
1442     if (funcType == implType)
1443       return getFuncOp(loc, builder, impl); // exact match
1444 
1445     FunctionDistance distance(funcType, implType);
1446     if (distance.isSmallerThan(bestMatchDistance)) {
1447       *bestNearMatch = &impl;
1448       bestMatchDistance = std::move(distance);
1449     }
1450   }
1451   return {};
1452 }
1453 
1454 using RtMap = Fortran::common::StaticMultimapView<MathOperation>;
1455 static constexpr RtMap mathOps(mathOperations);
1456 static_assert(mathOps.Verify() && "map must be sorted");
1457 
1458 /// Look for a MathOperation entry specifying how to lower a mathematical
1459 /// operation defined by \p name with its result' and operands' types
1460 /// specified in the form of a FunctionType \p funcType.
1461 /// If exact match for the given types is found, then the function
1462 /// returns a pointer to the corresponding MathOperation.
1463 /// Otherwise, the function returns nullptr.
1464 /// If there is a MathOperation that can be used with additional
1465 /// type casts for the operands or/and result (non-exact match),
1466 /// then it is returned via \p bestNearMatch argument, and
1467 /// \p bestMatchDistance specifies the FunctionDistance between
1468 /// the requested operation and the non-exact match.
1469 static const MathOperation *
1470 searchMathOperation(fir::FirOpBuilder &builder, llvm::StringRef name,
1471                     mlir::FunctionType funcType,
1472                     const MathOperation **bestNearMatch,
1473                     FunctionDistance &bestMatchDistance) {
1474   auto range = mathOps.equal_range(name);
1475   for (auto iter = range.first; iter != range.second && iter; ++iter) {
1476     const auto &impl = *iter;
1477     auto implType = impl.typeGenerator(builder.getContext());
1478     if (funcType == implType)
1479       return &impl; // exact match
1480 
1481     FunctionDistance distance(funcType, implType);
1482     if (distance.isSmallerThan(bestMatchDistance)) {
1483       *bestNearMatch = &impl;
1484       bestMatchDistance = std::move(distance);
1485     }
1486   }
1487   return nullptr;
1488 }
1489 
1490 /// Implementation of the operation defined by \p name with type
1491 /// \p funcType is not precise, and the actual available implementation
1492 /// is \p distance away from the requested. If using the available
1493 /// implementation results in a precision loss, emit an error message
1494 /// with the given code location \p loc.
1495 static void checkPrecisionLoss(llvm::StringRef name,
1496                                mlir::FunctionType funcType,
1497                                const FunctionDistance &distance,
1498                                mlir::Location loc) {
1499   if (!distance.isLosingPrecision())
1500     return;
1501 
1502   // Using this runtime version requires narrowing the arguments
1503   // or extending the result. It is not numerically safe. There
1504   // is currently no quad math library that was described in
1505   // lowering and could be used here. Emit an error and continue
1506   // generating the code with the narrowing cast so that the user
1507   // can get a complete list of the problematic intrinsic calls.
1508   std::string message("not yet implemented: no math runtime available for '");
1509   llvm::raw_string_ostream sstream(message);
1510   if (name == "pow") {
1511     assert(funcType.getNumInputs() == 2 && "power operator has two arguments");
1512     sstream << funcType.getInput(0) << " ** " << funcType.getInput(1);
1513   } else {
1514     sstream << name << "(";
1515     if (funcType.getNumInputs() > 0)
1516       sstream << funcType.getInput(0);
1517     for (mlir::Type argType : funcType.getInputs().drop_front())
1518       sstream << ", " << argType;
1519     sstream << ")";
1520   }
1521   sstream << "'";
1522   mlir::emitError(loc, message);
1523 }
1524 
1525 /// Search runtime for the best runtime function given an intrinsic name
1526 /// and interface. The interface may not be a perfect match in which case
1527 /// the caller is responsible to insert argument and return value conversions.
1528 /// If nothing is found, the mlir::func::FuncOp will contain a nullptr.
1529 static mlir::func::FuncOp getRuntimeFunction(mlir::Location loc,
1530                                              fir::FirOpBuilder &builder,
1531                                              llvm::StringRef name,
1532                                              mlir::FunctionType funcType) {
1533   const RuntimeFunction *bestNearMatch = nullptr;
1534   FunctionDistance bestMatchDistance;
1535   mlir::func::FuncOp match;
1536   using RtMap = Fortran::common::StaticMultimapView<RuntimeFunction>;
1537   static constexpr RtMap pgmathF(pgmathFast);
1538   static_assert(pgmathF.Verify() && "map must be sorted");
1539   static constexpr RtMap pgmathR(pgmathRelaxed);
1540   static_assert(pgmathR.Verify() && "map must be sorted");
1541   static constexpr RtMap pgmathP(pgmathPrecise);
1542   static_assert(pgmathP.Verify() && "map must be sorted");
1543 
1544   if (mathRuntimeVersion == fastVersion) {
1545     match = searchFunctionInLibrary(loc, builder, pgmathF, name, funcType,
1546                                     &bestNearMatch, bestMatchDistance);
1547   } else if (mathRuntimeVersion == relaxedVersion) {
1548     match = searchFunctionInLibrary(loc, builder, pgmathR, name, funcType,
1549                                     &bestNearMatch, bestMatchDistance);
1550   } else if (mathRuntimeVersion == preciseVersion) {
1551     match = searchFunctionInLibrary(loc, builder, pgmathP, name, funcType,
1552                                     &bestNearMatch, bestMatchDistance);
1553   } else {
1554     assert(mathRuntimeVersion == llvmOnly && "unknown math runtime");
1555   }
1556   if (match)
1557     return match;
1558 
1559   // Go through llvm intrinsics if not exact match in libpgmath or if
1560   // mathRuntimeVersion == llvmOnly
1561   static constexpr RtMap llvmIntr(llvmIntrinsics);
1562   static_assert(llvmIntr.Verify() && "map must be sorted");
1563   if (mlir::func::FuncOp exactMatch =
1564           searchFunctionInLibrary(loc, builder, llvmIntr, name, funcType,
1565                                   &bestNearMatch, bestMatchDistance))
1566     return exactMatch;
1567 
1568   if (bestNearMatch != nullptr) {
1569     checkPrecisionLoss(name, funcType, bestMatchDistance, loc);
1570     return getFuncOp(loc, builder, *bestNearMatch);
1571   }
1572   return {};
1573 }
1574 
1575 /// Helpers to get function type from arguments and result type.
1576 static mlir::FunctionType getFunctionType(llvm::Optional<mlir::Type> resultType,
1577                                           llvm::ArrayRef<mlir::Value> arguments,
1578                                           fir::FirOpBuilder &builder) {
1579   llvm::SmallVector<mlir::Type> argTypes;
1580   for (mlir::Value arg : arguments)
1581     argTypes.push_back(arg.getType());
1582   llvm::SmallVector<mlir::Type> resTypes;
1583   if (resultType)
1584     resTypes.push_back(*resultType);
1585   return mlir::FunctionType::get(builder.getModule().getContext(), argTypes,
1586                                  resTypes);
1587 }
1588 
1589 /// fir::ExtendedValue to mlir::Value translation layer
1590 
1591 fir::ExtendedValue toExtendedValue(mlir::Value val, fir::FirOpBuilder &builder,
1592                                    mlir::Location loc) {
1593   assert(val && "optional unhandled here");
1594   mlir::Type type = val.getType();
1595   mlir::Value base = val;
1596   mlir::IndexType indexType = builder.getIndexType();
1597   llvm::SmallVector<mlir::Value> extents;
1598 
1599   fir::factory::CharacterExprHelper charHelper{builder, loc};
1600   // FIXME: we may want to allow non character scalar here.
1601   if (charHelper.isCharacterScalar(type))
1602     return charHelper.toExtendedValue(val);
1603 
1604   if (auto refType = type.dyn_cast<fir::ReferenceType>())
1605     type = refType.getEleTy();
1606 
1607   if (auto arrayType = type.dyn_cast<fir::SequenceType>()) {
1608     type = arrayType.getEleTy();
1609     for (fir::SequenceType::Extent extent : arrayType.getShape()) {
1610       if (extent == fir::SequenceType::getUnknownExtent())
1611         break;
1612       extents.emplace_back(
1613           builder.createIntegerConstant(loc, indexType, extent));
1614     }
1615     // Last extent might be missing in case of assumed-size. If more extents
1616     // could not be deduced from type, that's an error (a fir.box should
1617     // have been used in the interface).
1618     if (extents.size() + 1 < arrayType.getShape().size())
1619       mlir::emitError(loc, "cannot retrieve array extents from type");
1620   } else if (type.isa<fir::BoxType>() || type.isa<fir::RecordType>()) {
1621     fir::emitFatalError(loc, "not yet implemented: descriptor or derived type");
1622   }
1623 
1624   if (!extents.empty())
1625     return fir::ArrayBoxValue{base, extents};
1626   return base;
1627 }
1628 
1629 mlir::Value toValue(const fir::ExtendedValue &val, fir::FirOpBuilder &builder,
1630                     mlir::Location loc) {
1631   if (const fir::CharBoxValue *charBox = val.getCharBox()) {
1632     mlir::Value buffer = charBox->getBuffer();
1633     auto buffTy = buffer.getType();
1634     if (buffTy.isa<mlir::FunctionType>())
1635       fir::emitFatalError(
1636           loc, "A character's buffer type cannot be a function type.");
1637     if (buffTy.isa<fir::BoxCharType>())
1638       return buffer;
1639     return fir::factory::CharacterExprHelper{builder, loc}.createEmboxChar(
1640         buffer, charBox->getLen());
1641   }
1642 
1643   // FIXME: need to access other ExtendedValue variants and handle them
1644   // properly.
1645   return fir::getBase(val);
1646 }
1647 
1648 //===----------------------------------------------------------------------===//
1649 // IntrinsicLibrary
1650 //===----------------------------------------------------------------------===//
1651 
1652 static bool isIntrinsicModuleProcedure(llvm::StringRef name) {
1653   return name.startswith("c_") || name.startswith("compiler_") ||
1654          name.startswith("ieee_");
1655 }
1656 
1657 /// Return the generic name of an intrinsic module procedure specific name.
1658 /// Remove any "__builtin_" prefix, and any specific suffix of the form
1659 /// {_[ail]?[0-9]+}*, such as _1 or _a4.
1660 llvm::StringRef genericName(llvm::StringRef specificName) {
1661   const std::string builtin = "__builtin_";
1662   llvm::StringRef name = specificName.startswith(builtin)
1663                              ? specificName.drop_front(builtin.size())
1664                              : specificName;
1665   size_t size = name.size();
1666   if (isIntrinsicModuleProcedure(name))
1667     while (isdigit(name[size - 1]))
1668       while (name[--size] != '_')
1669         ;
1670   return name.drop_back(name.size() - size);
1671 }
1672 
1673 /// Generate a TODO error message for an as yet unimplemented intrinsic.
1674 void crashOnMissingIntrinsic(mlir::Location loc, llvm::StringRef name) {
1675   if (isIntrinsicModuleProcedure(name))
1676     TODO(loc, "intrinsic module procedure: " + llvm::Twine(name));
1677   else
1678     TODO(loc, "intrinsic: " + llvm::Twine(name));
1679 }
1680 
1681 template <typename GeneratorType>
1682 fir::ExtendedValue IntrinsicLibrary::genElementalCall(
1683     GeneratorType generator, llvm::StringRef name, mlir::Type resultType,
1684     llvm::ArrayRef<fir::ExtendedValue> args, bool outline) {
1685   llvm::SmallVector<mlir::Value> scalarArgs;
1686   for (const fir::ExtendedValue &arg : args)
1687     if (arg.getUnboxed() || arg.getCharBox())
1688       scalarArgs.emplace_back(fir::getBase(arg));
1689     else
1690       fir::emitFatalError(loc, "nonscalar intrinsic argument");
1691   if (outline)
1692     return outlineInWrapper(generator, name, resultType, scalarArgs);
1693   return invokeGenerator(generator, resultType, scalarArgs);
1694 }
1695 
1696 template <>
1697 fir::ExtendedValue
1698 IntrinsicLibrary::genElementalCall<IntrinsicLibrary::ExtendedGenerator>(
1699     ExtendedGenerator generator, llvm::StringRef name, mlir::Type resultType,
1700     llvm::ArrayRef<fir::ExtendedValue> args, bool outline) {
1701   for (const fir::ExtendedValue &arg : args)
1702     if (!arg.getUnboxed() && !arg.getCharBox())
1703       fir::emitFatalError(loc, "nonscalar intrinsic argument");
1704   if (outline)
1705     return outlineInExtendedWrapper(generator, name, resultType, args);
1706   return std::invoke(generator, *this, resultType, args);
1707 }
1708 
1709 template <>
1710 fir::ExtendedValue
1711 IntrinsicLibrary::genElementalCall<IntrinsicLibrary::SubroutineGenerator>(
1712     SubroutineGenerator generator, llvm::StringRef name, mlir::Type resultType,
1713     llvm::ArrayRef<fir::ExtendedValue> args, bool outline) {
1714   for (const fir::ExtendedValue &arg : args)
1715     if (!arg.getUnboxed() && !arg.getCharBox())
1716       // fir::emitFatalError(loc, "nonscalar intrinsic argument");
1717       crashOnMissingIntrinsic(loc, name);
1718   if (outline)
1719     return outlineInExtendedWrapper(generator, name, resultType, args);
1720   std::invoke(generator, *this, args);
1721   return mlir::Value();
1722 }
1723 
1724 static fir::ExtendedValue
1725 invokeHandler(IntrinsicLibrary::ElementalGenerator generator,
1726               const IntrinsicHandler &handler,
1727               llvm::Optional<mlir::Type> resultType,
1728               llvm::ArrayRef<fir::ExtendedValue> args, bool outline,
1729               IntrinsicLibrary &lib) {
1730   assert(resultType && "expect elemental intrinsic to be functions");
1731   return lib.genElementalCall(generator, handler.name, *resultType, args,
1732                               outline);
1733 }
1734 
1735 static fir::ExtendedValue
1736 invokeHandler(IntrinsicLibrary::ExtendedGenerator generator,
1737               const IntrinsicHandler &handler,
1738               llvm::Optional<mlir::Type> resultType,
1739               llvm::ArrayRef<fir::ExtendedValue> args, bool outline,
1740               IntrinsicLibrary &lib) {
1741   assert(resultType && "expect intrinsic function");
1742   if (handler.isElemental)
1743     return lib.genElementalCall(generator, handler.name, *resultType, args,
1744                                 outline);
1745   if (outline)
1746     return lib.outlineInExtendedWrapper(generator, handler.name, *resultType,
1747                                         args);
1748   return std::invoke(generator, lib, *resultType, args);
1749 }
1750 
1751 static fir::ExtendedValue
1752 invokeHandler(IntrinsicLibrary::SubroutineGenerator generator,
1753               const IntrinsicHandler &handler,
1754               llvm::Optional<mlir::Type> resultType,
1755               llvm::ArrayRef<fir::ExtendedValue> args, bool outline,
1756               IntrinsicLibrary &lib) {
1757   if (handler.isElemental)
1758     return lib.genElementalCall(generator, handler.name, mlir::Type{}, args,
1759                                 outline);
1760   if (outline)
1761     return lib.outlineInExtendedWrapper(generator, handler.name, resultType,
1762                                         args);
1763   std::invoke(generator, lib, args);
1764   return mlir::Value{};
1765 }
1766 
1767 fir::ExtendedValue
1768 IntrinsicLibrary::genIntrinsicCall(llvm::StringRef specificName,
1769                                    llvm::Optional<mlir::Type> resultType,
1770                                    llvm::ArrayRef<fir::ExtendedValue> args) {
1771   llvm::StringRef name = genericName(specificName);
1772   if (const IntrinsicHandler *handler = findIntrinsicHandler(name)) {
1773     bool outline = handler->outline || outlineAllIntrinsics;
1774     return std::visit(
1775         [&](auto &generator) -> fir::ExtendedValue {
1776           return invokeHandler(generator, *handler, resultType, args, outline,
1777                                *this);
1778         },
1779         handler->generator);
1780   }
1781 
1782   if (!resultType)
1783     // Subroutine should have a handler, they are likely missing for now.
1784     crashOnMissingIntrinsic(loc, name);
1785 
1786   // Try the runtime if no special handler was defined for the
1787   // intrinsic being called. Maths runtime only has numerical elemental.
1788   // No optional arguments are expected at this point, the code will
1789   // crash if it gets absent optional.
1790 
1791   // FIXME: using toValue to get the type won't work with array arguments.
1792   llvm::SmallVector<mlir::Value> mlirArgs;
1793   for (const fir::ExtendedValue &extendedVal : args) {
1794     mlir::Value val = toValue(extendedVal, builder, loc);
1795     if (!val)
1796       // If an absent optional gets there, most likely its handler has just
1797       // not yet been defined.
1798       crashOnMissingIntrinsic(loc, name);
1799     mlirArgs.emplace_back(val);
1800   }
1801   mlir::FunctionType soughtFuncType =
1802       getFunctionType(*resultType, mlirArgs, builder);
1803 
1804   IntrinsicLibrary::RuntimeCallGenerator runtimeCallGenerator =
1805       getRuntimeCallGenerator(name, soughtFuncType);
1806   return genElementalCall(runtimeCallGenerator, name, *resultType, args,
1807                           /*outline=*/outlineAllIntrinsics);
1808 }
1809 
1810 mlir::Value
1811 IntrinsicLibrary::invokeGenerator(ElementalGenerator generator,
1812                                   mlir::Type resultType,
1813                                   llvm::ArrayRef<mlir::Value> args) {
1814   return std::invoke(generator, *this, resultType, args);
1815 }
1816 
1817 mlir::Value
1818 IntrinsicLibrary::invokeGenerator(RuntimeCallGenerator generator,
1819                                   mlir::Type resultType,
1820                                   llvm::ArrayRef<mlir::Value> args) {
1821   return generator(builder, loc, args);
1822 }
1823 
1824 mlir::Value
1825 IntrinsicLibrary::invokeGenerator(ExtendedGenerator generator,
1826                                   mlir::Type resultType,
1827                                   llvm::ArrayRef<mlir::Value> args) {
1828   llvm::SmallVector<fir::ExtendedValue> extendedArgs;
1829   for (mlir::Value arg : args)
1830     extendedArgs.emplace_back(toExtendedValue(arg, builder, loc));
1831   auto extendedResult = std::invoke(generator, *this, resultType, extendedArgs);
1832   return toValue(extendedResult, builder, loc);
1833 }
1834 
1835 mlir::Value
1836 IntrinsicLibrary::invokeGenerator(SubroutineGenerator generator,
1837                                   llvm::ArrayRef<mlir::Value> args) {
1838   llvm::SmallVector<fir::ExtendedValue> extendedArgs;
1839   for (mlir::Value arg : args)
1840     extendedArgs.emplace_back(toExtendedValue(arg, builder, loc));
1841   std::invoke(generator, *this, extendedArgs);
1842   return {};
1843 }
1844 
1845 template <typename GeneratorType>
1846 mlir::func::FuncOp IntrinsicLibrary::getWrapper(GeneratorType generator,
1847                                                 llvm::StringRef name,
1848                                                 mlir::FunctionType funcType,
1849                                                 bool loadRefArguments) {
1850   std::string wrapperName = fir::mangleIntrinsicProcedure(name, funcType);
1851   mlir::func::FuncOp function = builder.getNamedFunction(wrapperName);
1852   if (!function) {
1853     // First time this wrapper is needed, build it.
1854     function = builder.createFunction(loc, wrapperName, funcType);
1855     function->setAttr("fir.intrinsic", builder.getUnitAttr());
1856     auto internalLinkage = mlir::LLVM::linkage::Linkage::Internal;
1857     auto linkage =
1858         mlir::LLVM::LinkageAttr::get(builder.getContext(), internalLinkage);
1859     function->setAttr("llvm.linkage", linkage);
1860     function.addEntryBlock();
1861 
1862     // Create local context to emit code into the newly created function
1863     // This new function is not linked to a source file location, only
1864     // its calls will be.
1865     auto localBuilder =
1866         std::make_unique<fir::FirOpBuilder>(function, builder.getKindMap());
1867     localBuilder->setInsertionPointToStart(&function.front());
1868     // Location of code inside wrapper of the wrapper is independent from
1869     // the location of the intrinsic call.
1870     mlir::Location localLoc = localBuilder->getUnknownLoc();
1871     llvm::SmallVector<mlir::Value> localArguments;
1872     for (mlir::BlockArgument bArg : function.front().getArguments()) {
1873       auto refType = bArg.getType().dyn_cast<fir::ReferenceType>();
1874       if (loadRefArguments && refType) {
1875         auto loaded = localBuilder->create<fir::LoadOp>(localLoc, bArg);
1876         localArguments.push_back(loaded);
1877       } else {
1878         localArguments.push_back(bArg);
1879       }
1880     }
1881 
1882     IntrinsicLibrary localLib{*localBuilder, localLoc};
1883 
1884     if constexpr (std::is_same_v<GeneratorType, SubroutineGenerator>) {
1885       localLib.invokeGenerator(generator, localArguments);
1886       localBuilder->create<mlir::func::ReturnOp>(localLoc);
1887     } else {
1888       assert(funcType.getNumResults() == 1 &&
1889              "expect one result for intrinsic function wrapper type");
1890       mlir::Type resultType = funcType.getResult(0);
1891       auto result =
1892           localLib.invokeGenerator(generator, resultType, localArguments);
1893       localBuilder->create<mlir::func::ReturnOp>(localLoc, result);
1894     }
1895   } else {
1896     // Wrapper was already built, ensure it has the sought type
1897     assert(function.getFunctionType() == funcType &&
1898            "conflict between intrinsic wrapper types");
1899   }
1900   return function;
1901 }
1902 
1903 /// Helpers to detect absent optional (not yet supported in outlining).
1904 bool static hasAbsentOptional(llvm::ArrayRef<mlir::Value> args) {
1905   for (const mlir::Value &arg : args)
1906     if (!arg)
1907       return true;
1908   return false;
1909 }
1910 bool static hasAbsentOptional(llvm::ArrayRef<fir::ExtendedValue> args) {
1911   for (const fir::ExtendedValue &arg : args)
1912     if (!fir::getBase(arg))
1913       return true;
1914   return false;
1915 }
1916 
1917 template <typename GeneratorType>
1918 mlir::Value
1919 IntrinsicLibrary::outlineInWrapper(GeneratorType generator,
1920                                    llvm::StringRef name, mlir::Type resultType,
1921                                    llvm::ArrayRef<mlir::Value> args) {
1922   if (hasAbsentOptional(args)) {
1923     // TODO: absent optional in outlining is an issue: we cannot just ignore
1924     // them. Needs a better interface here. The issue is that we cannot easily
1925     // tell that a value is optional or not here if it is presents. And if it is
1926     // absent, we cannot tell what it type should be.
1927     TODO(loc, "cannot outline call to intrinsic " + llvm::Twine(name) +
1928                   " with absent optional argument");
1929   }
1930 
1931   mlir::FunctionType funcType = getFunctionType(resultType, args, builder);
1932   mlir::func::FuncOp wrapper = getWrapper(generator, name, funcType);
1933   return builder.create<fir::CallOp>(loc, wrapper, args).getResult(0);
1934 }
1935 
1936 template <typename GeneratorType>
1937 fir::ExtendedValue IntrinsicLibrary::outlineInExtendedWrapper(
1938     GeneratorType generator, llvm::StringRef name,
1939     llvm::Optional<mlir::Type> resultType,
1940     llvm::ArrayRef<fir::ExtendedValue> args) {
1941   if (hasAbsentOptional(args))
1942     TODO(loc, "cannot outline call to intrinsic " + llvm::Twine(name) +
1943                   " with absent optional argument");
1944   llvm::SmallVector<mlir::Value> mlirArgs;
1945   for (const auto &extendedVal : args)
1946     mlirArgs.emplace_back(toValue(extendedVal, builder, loc));
1947   mlir::FunctionType funcType = getFunctionType(resultType, mlirArgs, builder);
1948   mlir::func::FuncOp wrapper = getWrapper(generator, name, funcType);
1949   auto call = builder.create<fir::CallOp>(loc, wrapper, mlirArgs);
1950   if (resultType)
1951     return toExtendedValue(call.getResult(0), builder, loc);
1952   // Subroutine calls
1953   return mlir::Value{};
1954 }
1955 
1956 IntrinsicLibrary::RuntimeCallGenerator
1957 IntrinsicLibrary::getRuntimeCallGenerator(llvm::StringRef name,
1958                                           mlir::FunctionType soughtFuncType) {
1959   mlir::func::FuncOp funcOp;
1960   mlir::FunctionType actualFuncType;
1961   const MathOperation *mathOp = nullptr;
1962   if (!lowerEarlyToLibCall) {
1963     // Look for a dedicated math operation generator, which
1964     // normally produces a single MLIR operation implementing
1965     // the math operation.
1966     // If not found fall back to a runtime function lookup.
1967     const MathOperation *bestNearMatch = nullptr;
1968     FunctionDistance bestMatchDistance;
1969     mathOp = searchMathOperation(builder, name, soughtFuncType, &bestNearMatch,
1970                                  bestMatchDistance);
1971     if (!mathOp && bestNearMatch) {
1972       // Use the best near match, optionally issuing an error,
1973       // if types conversions cause precision loss.
1974       checkPrecisionLoss(name, soughtFuncType, bestMatchDistance, loc);
1975       mathOp = bestNearMatch;
1976     }
1977     if (mathOp)
1978       actualFuncType = mathOp->typeGenerator(builder.getContext());
1979   }
1980   if (!mathOp)
1981     if ((funcOp = getRuntimeFunction(loc, builder, name, soughtFuncType)))
1982       actualFuncType = funcOp.getFunctionType();
1983 
1984   if (!mathOp && !funcOp) {
1985     std::string nameAndType;
1986     llvm::raw_string_ostream sstream(nameAndType);
1987     sstream << name << "\nrequested type: " << soughtFuncType;
1988     crashOnMissingIntrinsic(loc, nameAndType);
1989   }
1990 
1991   assert(actualFuncType.getNumResults() == soughtFuncType.getNumResults() &&
1992          actualFuncType.getNumInputs() == soughtFuncType.getNumInputs() &&
1993          actualFuncType.getNumResults() == 1 && "Bad intrinsic match");
1994 
1995   return [funcOp, actualFuncType, mathOp,
1996           soughtFuncType](fir::FirOpBuilder &builder, mlir::Location loc,
1997                           llvm::ArrayRef<mlir::Value> args) {
1998     llvm::SmallVector<mlir::Value> convertedArguments;
1999     for (auto [fst, snd] : llvm::zip(actualFuncType.getInputs(), args))
2000       convertedArguments.push_back(builder.createConvert(loc, fst, snd));
2001     mlir::Value result;
2002     // Use math operation generator, if available.
2003     if (mathOp)
2004       result = mathOp->funcGenerator(builder, loc, mathOp->runtimeFunc,
2005                                      actualFuncType, convertedArguments);
2006     else
2007       result = builder.create<fir::CallOp>(loc, funcOp, convertedArguments)
2008                    .getResult(0);
2009     mlir::Type soughtType = soughtFuncType.getResult(0);
2010     return builder.createConvert(loc, soughtType, result);
2011   };
2012 }
2013 
2014 mlir::SymbolRefAttr IntrinsicLibrary::getUnrestrictedIntrinsicSymbolRefAttr(
2015     llvm::StringRef name, mlir::FunctionType signature) {
2016   // Unrestricted intrinsics signature follows implicit rules: argument
2017   // are passed by references. But the runtime versions expect values.
2018   // So instead of duplicating the runtime, just have the wrappers loading
2019   // this before calling the code generators.
2020   bool loadRefArguments = true;
2021   mlir::func::FuncOp funcOp;
2022   if (const IntrinsicHandler *handler = findIntrinsicHandler(name))
2023     funcOp = std::visit(
2024         [&](auto generator) {
2025           return getWrapper(generator, name, signature, loadRefArguments);
2026         },
2027         handler->generator);
2028 
2029   if (!funcOp) {
2030     llvm::SmallVector<mlir::Type> argTypes;
2031     for (mlir::Type type : signature.getInputs()) {
2032       if (auto refType = type.dyn_cast<fir::ReferenceType>())
2033         argTypes.push_back(refType.getEleTy());
2034       else
2035         argTypes.push_back(type);
2036     }
2037     mlir::FunctionType soughtFuncType =
2038         builder.getFunctionType(argTypes, signature.getResults());
2039     IntrinsicLibrary::RuntimeCallGenerator rtCallGenerator =
2040         getRuntimeCallGenerator(name, soughtFuncType);
2041     funcOp = getWrapper(rtCallGenerator, name, signature, loadRefArguments);
2042   }
2043 
2044   return mlir::SymbolRefAttr::get(funcOp);
2045 }
2046 
2047 void IntrinsicLibrary::addCleanUpForTemp(mlir::Location loc, mlir::Value temp) {
2048   assert(stmtCtx);
2049   fir::FirOpBuilder *bldr = &builder;
2050   stmtCtx->attachCleanup([=]() { bldr->create<fir::FreeMemOp>(loc, temp); });
2051 }
2052 
2053 fir::ExtendedValue
2054 IntrinsicLibrary::readAndAddCleanUp(fir::MutableBoxValue resultMutableBox,
2055                                     mlir::Type resultType,
2056                                     llvm::StringRef intrinsicName) {
2057   fir::ExtendedValue res =
2058       fir::factory::genMutableBoxRead(builder, loc, resultMutableBox);
2059   return res.match(
2060       [&](const fir::ArrayBoxValue &box) -> fir::ExtendedValue {
2061         // Add cleanup code
2062         addCleanUpForTemp(loc, box.getAddr());
2063         return box;
2064       },
2065       [&](const fir::BoxValue &box) -> fir::ExtendedValue {
2066         // Add cleanup code
2067         auto addr =
2068             builder.create<fir::BoxAddrOp>(loc, box.getMemTy(), box.getAddr());
2069         addCleanUpForTemp(loc, addr);
2070         return box;
2071       },
2072       [&](const fir::CharArrayBoxValue &box) -> fir::ExtendedValue {
2073         // Add cleanup code
2074         addCleanUpForTemp(loc, box.getAddr());
2075         return box;
2076       },
2077       [&](const mlir::Value &tempAddr) -> fir::ExtendedValue {
2078         // Add cleanup code
2079         addCleanUpForTemp(loc, tempAddr);
2080         return builder.create<fir::LoadOp>(loc, resultType, tempAddr);
2081       },
2082       [&](const fir::CharBoxValue &box) -> fir::ExtendedValue {
2083         // Add cleanup code
2084         addCleanUpForTemp(loc, box.getAddr());
2085         return box;
2086       },
2087       [&](const auto &) -> fir::ExtendedValue {
2088         fir::emitFatalError(loc, "unexpected result for " + intrinsicName);
2089       });
2090 }
2091 
2092 //===----------------------------------------------------------------------===//
2093 // Code generators for the intrinsic
2094 //===----------------------------------------------------------------------===//
2095 
2096 mlir::Value IntrinsicLibrary::genRuntimeCall(llvm::StringRef name,
2097                                              mlir::Type resultType,
2098                                              llvm::ArrayRef<mlir::Value> args) {
2099   mlir::FunctionType soughtFuncType =
2100       getFunctionType(resultType, args, builder);
2101   return getRuntimeCallGenerator(name, soughtFuncType)(builder, loc, args);
2102 }
2103 
2104 mlir::Value IntrinsicLibrary::genConversion(mlir::Type resultType,
2105                                             llvm::ArrayRef<mlir::Value> args) {
2106   // There can be an optional kind in second argument.
2107   assert(args.size() >= 1);
2108   return builder.convertWithSemantics(loc, resultType, args[0]);
2109 }
2110 
2111 // ABS
2112 mlir::Value IntrinsicLibrary::genAbs(mlir::Type resultType,
2113                                      llvm::ArrayRef<mlir::Value> args) {
2114   assert(args.size() == 1);
2115   mlir::Value arg = args[0];
2116   mlir::Type type = arg.getType();
2117   if (fir::isa_real(type)) {
2118     // Runtime call to fp abs. An alternative would be to use mlir
2119     // math::AbsFOp but it does not support all fir floating point types.
2120     return genRuntimeCall("abs", resultType, args);
2121   }
2122   if (auto intType = type.dyn_cast<mlir::IntegerType>()) {
2123     // At the time of this implementation there is no abs op in mlir.
2124     // So, implement abs here without branching.
2125     mlir::Value shift =
2126         builder.createIntegerConstant(loc, intType, intType.getWidth() - 1);
2127     auto mask = builder.create<mlir::arith::ShRSIOp>(loc, arg, shift);
2128     auto xored = builder.create<mlir::arith::XOrIOp>(loc, arg, mask);
2129     return builder.create<mlir::arith::SubIOp>(loc, xored, mask);
2130   }
2131   if (fir::isa_complex(type)) {
2132     // Use HYPOT to fulfill the no underflow/overflow requirement.
2133     auto parts = fir::factory::Complex{builder, loc}.extractParts(arg);
2134     llvm::SmallVector<mlir::Value> args = {parts.first, parts.second};
2135     return genRuntimeCall("hypot", resultType, args);
2136   }
2137   llvm_unreachable("unexpected type in ABS argument");
2138 }
2139 
2140 // ADJUSTL & ADJUSTR
2141 template <void (*CallRuntime)(fir::FirOpBuilder &, mlir::Location loc,
2142                               mlir::Value, mlir::Value)>
2143 fir::ExtendedValue
2144 IntrinsicLibrary::genAdjustRtCall(mlir::Type resultType,
2145                                   llvm::ArrayRef<fir::ExtendedValue> args) {
2146   assert(args.size() == 1);
2147   mlir::Value string = builder.createBox(loc, args[0]);
2148   // Create a mutable fir.box to be passed to the runtime for the result.
2149   fir::MutableBoxValue resultMutableBox =
2150       fir::factory::createTempMutableBox(builder, loc, resultType);
2151   mlir::Value resultIrBox =
2152       fir::factory::getMutableIRBox(builder, loc, resultMutableBox);
2153 
2154   // Call the runtime -- the runtime will allocate the result.
2155   CallRuntime(builder, loc, resultIrBox, string);
2156 
2157   // Read result from mutable fir.box and add it to the list of temps to be
2158   // finalized by the StatementContext.
2159   fir::ExtendedValue res =
2160       fir::factory::genMutableBoxRead(builder, loc, resultMutableBox);
2161   return res.match(
2162       [&](const fir::CharBoxValue &box) -> fir::ExtendedValue {
2163         addCleanUpForTemp(loc, fir::getBase(box));
2164         return box;
2165       },
2166       [&](const auto &) -> fir::ExtendedValue {
2167         fir::emitFatalError(loc, "result of ADJUSTL is not a scalar character");
2168       });
2169 }
2170 
2171 // AIMAG
2172 mlir::Value IntrinsicLibrary::genAimag(mlir::Type resultType,
2173                                        llvm::ArrayRef<mlir::Value> args) {
2174   assert(args.size() == 1);
2175   return fir::factory::Complex{builder, loc}.extractComplexPart(
2176       args[0], /*isImagPart=*/true);
2177 }
2178 
2179 // AINT
2180 mlir::Value IntrinsicLibrary::genAint(mlir::Type resultType,
2181                                       llvm::ArrayRef<mlir::Value> args) {
2182   assert(args.size() >= 1 && args.size() <= 2);
2183   // Skip optional kind argument to search the runtime; it is already reflected
2184   // in result type.
2185   return genRuntimeCall("aint", resultType, {args[0]});
2186 }
2187 
2188 // ALL
2189 fir::ExtendedValue
2190 IntrinsicLibrary::genAll(mlir::Type resultType,
2191                          llvm::ArrayRef<fir::ExtendedValue> args) {
2192 
2193   assert(args.size() == 2);
2194   // Handle required mask argument
2195   mlir::Value mask = builder.createBox(loc, args[0]);
2196 
2197   fir::BoxValue maskArry = builder.createBox(loc, args[0]);
2198   int rank = maskArry.rank();
2199   assert(rank >= 1);
2200 
2201   // Handle optional dim argument
2202   bool absentDim = isStaticallyAbsent(args[1]);
2203   mlir::Value dim =
2204       absentDim ? builder.createIntegerConstant(loc, builder.getIndexType(), 1)
2205                 : fir::getBase(args[1]);
2206 
2207   if (rank == 1 || absentDim)
2208     return builder.createConvert(loc, resultType,
2209                                  fir::runtime::genAll(builder, loc, mask, dim));
2210 
2211   // else use the result descriptor AllDim() intrinsic
2212 
2213   // Create mutable fir.box to be passed to the runtime for the result.
2214 
2215   mlir::Type resultArrayType = builder.getVarLenSeqTy(resultType, rank - 1);
2216   fir::MutableBoxValue resultMutableBox =
2217       fir::factory::createTempMutableBox(builder, loc, resultArrayType);
2218   mlir::Value resultIrBox =
2219       fir::factory::getMutableIRBox(builder, loc, resultMutableBox);
2220 
2221   // Call runtime. The runtime is allocating the result.
2222   fir::runtime::genAllDescriptor(builder, loc, resultIrBox, mask, dim);
2223   return fir::factory::genMutableBoxRead(builder, loc, resultMutableBox)
2224       .match(
2225           [&](const fir::ArrayBoxValue &box) -> fir::ExtendedValue {
2226             addCleanUpForTemp(loc, box.getAddr());
2227             return box;
2228           },
2229           [&](const auto &) -> fir::ExtendedValue {
2230             fir::emitFatalError(loc, "Invalid result for ALL");
2231           });
2232 }
2233 
2234 // ALLOCATED
2235 fir::ExtendedValue
2236 IntrinsicLibrary::genAllocated(mlir::Type resultType,
2237                                llvm::ArrayRef<fir::ExtendedValue> args) {
2238   assert(args.size() == 1);
2239   return args[0].match(
2240       [&](const fir::MutableBoxValue &x) -> fir::ExtendedValue {
2241         return fir::factory::genIsAllocatedOrAssociatedTest(builder, loc, x);
2242       },
2243       [&](const auto &) -> fir::ExtendedValue {
2244         fir::emitFatalError(loc,
2245                             "allocated arg not lowered to MutableBoxValue");
2246       });
2247 }
2248 
2249 // ANINT
2250 mlir::Value IntrinsicLibrary::genAnint(mlir::Type resultType,
2251                                        llvm::ArrayRef<mlir::Value> args) {
2252   assert(args.size() >= 1 && args.size() <= 2);
2253   // Skip optional kind argument to search the runtime; it is already reflected
2254   // in result type.
2255   return genRuntimeCall("anint", resultType, {args[0]});
2256 }
2257 
2258 // ANY
2259 fir::ExtendedValue
2260 IntrinsicLibrary::genAny(mlir::Type resultType,
2261                          llvm::ArrayRef<fir::ExtendedValue> args) {
2262 
2263   assert(args.size() == 2);
2264   // Handle required mask argument
2265   mlir::Value mask = builder.createBox(loc, args[0]);
2266 
2267   fir::BoxValue maskArry = builder.createBox(loc, args[0]);
2268   int rank = maskArry.rank();
2269   assert(rank >= 1);
2270 
2271   // Handle optional dim argument
2272   bool absentDim = isStaticallyAbsent(args[1]);
2273   mlir::Value dim =
2274       absentDim ? builder.createIntegerConstant(loc, builder.getIndexType(), 1)
2275                 : fir::getBase(args[1]);
2276 
2277   if (rank == 1 || absentDim)
2278     return builder.createConvert(loc, resultType,
2279                                  fir::runtime::genAny(builder, loc, mask, dim));
2280 
2281   // else use the result descriptor AnyDim() intrinsic
2282 
2283   // Create mutable fir.box to be passed to the runtime for the result.
2284 
2285   mlir::Type resultArrayType = builder.getVarLenSeqTy(resultType, rank - 1);
2286   fir::MutableBoxValue resultMutableBox =
2287       fir::factory::createTempMutableBox(builder, loc, resultArrayType);
2288   mlir::Value resultIrBox =
2289       fir::factory::getMutableIRBox(builder, loc, resultMutableBox);
2290 
2291   // Call runtime. The runtime is allocating the result.
2292   fir::runtime::genAnyDescriptor(builder, loc, resultIrBox, mask, dim);
2293   return fir::factory::genMutableBoxRead(builder, loc, resultMutableBox)
2294       .match(
2295           [&](const fir::ArrayBoxValue &box) -> fir::ExtendedValue {
2296             addCleanUpForTemp(loc, box.getAddr());
2297             return box;
2298           },
2299           [&](const auto &) -> fir::ExtendedValue {
2300             fir::emitFatalError(loc, "Invalid result for ANY");
2301           });
2302 }
2303 
2304 // ASSOCIATED
2305 fir::ExtendedValue
2306 IntrinsicLibrary::genAssociated(mlir::Type resultType,
2307                                 llvm::ArrayRef<fir::ExtendedValue> args) {
2308   assert(args.size() == 2);
2309   auto *pointer =
2310       args[0].match([&](const fir::MutableBoxValue &x) { return &x; },
2311                     [&](const auto &) -> const fir::MutableBoxValue * {
2312                       fir::emitFatalError(loc, "pointer not a MutableBoxValue");
2313                     });
2314   const fir::ExtendedValue &target = args[1];
2315   if (isStaticallyAbsent(target))
2316     return fir::factory::genIsAllocatedOrAssociatedTest(builder, loc, *pointer);
2317 
2318   mlir::Value targetBox = builder.createBox(loc, target);
2319   if (fir::valueHasFirAttribute(fir::getBase(target),
2320                                 fir::getOptionalAttrName())) {
2321     // Subtle: contrary to other intrinsic optional arguments, disassociated
2322     // POINTER and unallocated ALLOCATABLE actual argument are not considered
2323     // absent here. This is because ASSOCIATED has special requirements for
2324     // TARGET actual arguments that are POINTERs. There is no precise
2325     // requirements for ALLOCATABLEs, but all existing Fortran compilers treat
2326     // them similarly to POINTERs. That is: unallocated TARGETs cause ASSOCIATED
2327     // to rerun false.  The runtime deals with the disassociated/unallocated
2328     // case. Simply ensures that TARGET that are OPTIONAL get conditionally
2329     // emboxed here to convey the optional aspect to the runtime.
2330     auto isPresent = builder.create<fir::IsPresentOp>(loc, builder.getI1Type(),
2331                                                       fir::getBase(target));
2332     auto absentBox = builder.create<fir::AbsentOp>(loc, targetBox.getType());
2333     targetBox = builder.create<mlir::arith::SelectOp>(loc, isPresent, targetBox,
2334                                                       absentBox);
2335   }
2336   mlir::Value pointerBoxRef =
2337       fir::factory::getMutableIRBox(builder, loc, *pointer);
2338   auto pointerBox = builder.create<fir::LoadOp>(loc, pointerBoxRef);
2339   return Fortran::lower::genAssociated(builder, loc, pointerBox, targetBox);
2340 }
2341 
2342 // BTEST
2343 mlir::Value IntrinsicLibrary::genBtest(mlir::Type resultType,
2344                                        llvm::ArrayRef<mlir::Value> args) {
2345   // A conformant BTEST(I,POS) call satisfies:
2346   //     POS >= 0
2347   //     POS < BIT_SIZE(I)
2348   // Return:  (I >> POS) & 1
2349   assert(args.size() == 2);
2350   mlir::Type argType = args[0].getType();
2351   mlir::Value pos = builder.createConvert(loc, argType, args[1]);
2352   auto shift = builder.create<mlir::arith::ShRUIOp>(loc, args[0], pos);
2353   mlir::Value one = builder.createIntegerConstant(loc, argType, 1);
2354   auto res = builder.create<mlir::arith::AndIOp>(loc, shift, one);
2355   return builder.createConvert(loc, resultType, res);
2356 }
2357 
2358 // CEILING
2359 mlir::Value IntrinsicLibrary::genCeiling(mlir::Type resultType,
2360                                          llvm::ArrayRef<mlir::Value> args) {
2361   // Optional KIND argument.
2362   assert(args.size() >= 1);
2363   mlir::Value arg = args[0];
2364   // Use ceil that is not an actual Fortran intrinsic but that is
2365   // an llvm intrinsic that does the same, but return a floating
2366   // point.
2367   mlir::Value ceil = genRuntimeCall("ceil", arg.getType(), {arg});
2368   return builder.createConvert(loc, resultType, ceil);
2369 }
2370 
2371 // CHAR
2372 fir::ExtendedValue
2373 IntrinsicLibrary::genChar(mlir::Type type,
2374                           llvm::ArrayRef<fir::ExtendedValue> args) {
2375   // Optional KIND argument.
2376   assert(args.size() >= 1);
2377   const mlir::Value *arg = args[0].getUnboxed();
2378   // expect argument to be a scalar integer
2379   if (!arg)
2380     mlir::emitError(loc, "CHAR intrinsic argument not unboxed");
2381   fir::factory::CharacterExprHelper helper{builder, loc};
2382   fir::CharacterType::KindTy kind = helper.getCharacterType(type).getFKind();
2383   mlir::Value cast = helper.createSingletonFromCode(*arg, kind);
2384   mlir::Value len =
2385       builder.createIntegerConstant(loc, builder.getCharacterLengthType(), 1);
2386   return fir::CharBoxValue{cast, len};
2387 }
2388 
2389 // CMPLX
2390 mlir::Value IntrinsicLibrary::genCmplx(mlir::Type resultType,
2391                                        llvm::ArrayRef<mlir::Value> args) {
2392   assert(args.size() >= 1);
2393   fir::factory::Complex complexHelper(builder, loc);
2394   mlir::Type partType = complexHelper.getComplexPartType(resultType);
2395   mlir::Value real = builder.createConvert(loc, partType, args[0]);
2396   mlir::Value imag = isStaticallyAbsent(args, 1)
2397                          ? builder.createRealZeroConstant(loc, partType)
2398                          : builder.createConvert(loc, partType, args[1]);
2399   return fir::factory::Complex{builder, loc}.createComplex(resultType, real,
2400                                                            imag);
2401 }
2402 
2403 // COMMAND_ARGUMENT_COUNT
2404 fir::ExtendedValue IntrinsicLibrary::genCommandArgumentCount(
2405     mlir::Type resultType, llvm::ArrayRef<fir::ExtendedValue> args) {
2406   assert(args.size() == 0);
2407   assert(resultType == builder.getDefaultIntegerType() &&
2408          "result type is not default integer kind type");
2409   return builder.createConvert(
2410       loc, resultType, fir::runtime::genCommandArgumentCount(builder, loc));
2411   ;
2412 }
2413 
2414 // CONJG
2415 mlir::Value IntrinsicLibrary::genConjg(mlir::Type resultType,
2416                                        llvm::ArrayRef<mlir::Value> args) {
2417   assert(args.size() == 1);
2418   if (resultType != args[0].getType())
2419     llvm_unreachable("argument type mismatch");
2420 
2421   mlir::Value cplx = args[0];
2422   auto imag = fir::factory::Complex{builder, loc}.extractComplexPart(
2423       cplx, /*isImagPart=*/true);
2424   auto negImag = builder.create<mlir::arith::NegFOp>(loc, imag);
2425   return fir::factory::Complex{builder, loc}.insertComplexPart(
2426       cplx, negImag, /*isImagPart=*/true);
2427 }
2428 
2429 // COUNT
2430 fir::ExtendedValue
2431 IntrinsicLibrary::genCount(mlir::Type resultType,
2432                            llvm::ArrayRef<fir::ExtendedValue> args) {
2433   assert(args.size() == 3);
2434 
2435   // Handle mask argument
2436   fir::BoxValue mask = builder.createBox(loc, args[0]);
2437   unsigned maskRank = mask.rank();
2438 
2439   assert(maskRank > 0);
2440 
2441   // Handle optional dim argument
2442   bool absentDim = isStaticallyAbsent(args[1]);
2443   mlir::Value dim =
2444       absentDim ? builder.createIntegerConstant(loc, builder.getIndexType(), 0)
2445                 : fir::getBase(args[1]);
2446 
2447   if (absentDim || maskRank == 1) {
2448     // Result is scalar if no dim argument or mask is rank 1.
2449     // So, call specialized Count runtime routine.
2450     return builder.createConvert(
2451         loc, resultType,
2452         fir::runtime::genCount(builder, loc, fir::getBase(mask), dim));
2453   }
2454 
2455   // Call general CountDim runtime routine.
2456 
2457   // Handle optional kind argument
2458   bool absentKind = isStaticallyAbsent(args[2]);
2459   mlir::Value kind = absentKind ? builder.createIntegerConstant(
2460                                       loc, builder.getIndexType(),
2461                                       builder.getKindMap().defaultIntegerKind())
2462                                 : fir::getBase(args[2]);
2463 
2464   // Create mutable fir.box to be passed to the runtime for the result.
2465   mlir::Type type = builder.getVarLenSeqTy(resultType, maskRank - 1);
2466   fir::MutableBoxValue resultMutableBox =
2467       fir::factory::createTempMutableBox(builder, loc, type);
2468 
2469   mlir::Value resultIrBox =
2470       fir::factory::getMutableIRBox(builder, loc, resultMutableBox);
2471 
2472   fir::runtime::genCountDim(builder, loc, resultIrBox, fir::getBase(mask), dim,
2473                             kind);
2474 
2475   // Handle cleanup of allocatable result descriptor and return
2476   fir::ExtendedValue res =
2477       fir::factory::genMutableBoxRead(builder, loc, resultMutableBox);
2478   return res.match(
2479       [&](const fir::ArrayBoxValue &box) -> fir::ExtendedValue {
2480         // Add cleanup code
2481         addCleanUpForTemp(loc, box.getAddr());
2482         return box;
2483       },
2484       [&](const auto &) -> fir::ExtendedValue {
2485         fir::emitFatalError(loc, "unexpected result for COUNT");
2486       });
2487 }
2488 
2489 // CPU_TIME
2490 void IntrinsicLibrary::genCpuTime(llvm::ArrayRef<fir::ExtendedValue> args) {
2491   assert(args.size() == 1);
2492   const mlir::Value *arg = args[0].getUnboxed();
2493   assert(arg && "nonscalar cpu_time argument");
2494   mlir::Value res1 = Fortran::lower::genCpuTime(builder, loc);
2495   mlir::Value res2 =
2496       builder.createConvert(loc, fir::dyn_cast_ptrEleTy(arg->getType()), res1);
2497   builder.create<fir::StoreOp>(loc, res2, *arg);
2498 }
2499 
2500 // CSHIFT
2501 fir::ExtendedValue
2502 IntrinsicLibrary::genCshift(mlir::Type resultType,
2503                             llvm::ArrayRef<fir::ExtendedValue> args) {
2504   assert(args.size() == 3);
2505 
2506   // Handle required ARRAY argument
2507   fir::BoxValue arrayBox = builder.createBox(loc, args[0]);
2508   mlir::Value array = fir::getBase(arrayBox);
2509   unsigned arrayRank = arrayBox.rank();
2510 
2511   // Create mutable fir.box to be passed to the runtime for the result.
2512   mlir::Type resultArrayType = builder.getVarLenSeqTy(resultType, arrayRank);
2513   fir::MutableBoxValue resultMutableBox =
2514       fir::factory::createTempMutableBox(builder, loc, resultArrayType);
2515   mlir::Value resultIrBox =
2516       fir::factory::getMutableIRBox(builder, loc, resultMutableBox);
2517 
2518   if (arrayRank == 1) {
2519     // Vector case
2520     // Handle required SHIFT argument as a scalar
2521     const mlir::Value *shiftAddr = args[1].getUnboxed();
2522     assert(shiftAddr && "nonscalar CSHIFT argument");
2523     auto shift = builder.create<fir::LoadOp>(loc, *shiftAddr);
2524 
2525     fir::runtime::genCshiftVector(builder, loc, resultIrBox, array, shift);
2526   } else {
2527     // Non-vector case
2528     // Handle required SHIFT argument as an array
2529     mlir::Value shift = builder.createBox(loc, args[1]);
2530 
2531     // Handle optional DIM argument
2532     mlir::Value dim =
2533         isStaticallyAbsent(args[2])
2534             ? builder.createIntegerConstant(loc, builder.getIndexType(), 1)
2535             : fir::getBase(args[2]);
2536     fir::runtime::genCshift(builder, loc, resultIrBox, array, shift, dim);
2537   }
2538   return readAndAddCleanUp(resultMutableBox, resultType, "CSHIFT");
2539 }
2540 
2541 // DATE_AND_TIME
2542 void IntrinsicLibrary::genDateAndTime(llvm::ArrayRef<fir::ExtendedValue> args) {
2543   assert(args.size() == 4 && "date_and_time has 4 args");
2544   llvm::SmallVector<llvm::Optional<fir::CharBoxValue>> charArgs(3);
2545   for (unsigned i = 0; i < 3; ++i)
2546     if (const fir::CharBoxValue *charBox = args[i].getCharBox())
2547       charArgs[i] = *charBox;
2548 
2549   mlir::Value values = fir::getBase(args[3]);
2550   if (!values)
2551     values = builder.create<fir::AbsentOp>(
2552         loc, fir::BoxType::get(builder.getNoneType()));
2553 
2554   Fortran::lower::genDateAndTime(builder, loc, charArgs[0], charArgs[1],
2555                                  charArgs[2], values);
2556 }
2557 
2558 // DIM
2559 mlir::Value IntrinsicLibrary::genDim(mlir::Type resultType,
2560                                      llvm::ArrayRef<mlir::Value> args) {
2561   assert(args.size() == 2);
2562   if (resultType.isa<mlir::IntegerType>()) {
2563     mlir::Value zero = builder.createIntegerConstant(loc, resultType, 0);
2564     auto diff = builder.create<mlir::arith::SubIOp>(loc, args[0], args[1]);
2565     auto cmp = builder.create<mlir::arith::CmpIOp>(
2566         loc, mlir::arith::CmpIPredicate::sgt, diff, zero);
2567     return builder.create<mlir::arith::SelectOp>(loc, cmp, diff, zero);
2568   }
2569   assert(fir::isa_real(resultType) && "Only expects real and integer in DIM");
2570   mlir::Value zero = builder.createRealZeroConstant(loc, resultType);
2571   auto diff = builder.create<mlir::arith::SubFOp>(loc, args[0], args[1]);
2572   auto cmp = builder.create<mlir::arith::CmpFOp>(
2573       loc, mlir::arith::CmpFPredicate::OGT, diff, zero);
2574   return builder.create<mlir::arith::SelectOp>(loc, cmp, diff, zero);
2575 }
2576 
2577 // DOT_PRODUCT
2578 fir::ExtendedValue
2579 IntrinsicLibrary::genDotProduct(mlir::Type resultType,
2580                                 llvm::ArrayRef<fir::ExtendedValue> args) {
2581   return genDotProd(fir::runtime::genDotProduct, resultType, builder, loc,
2582                     stmtCtx, args);
2583 }
2584 
2585 // DPROD
2586 mlir::Value IntrinsicLibrary::genDprod(mlir::Type resultType,
2587                                        llvm::ArrayRef<mlir::Value> args) {
2588   assert(args.size() == 2);
2589   assert(fir::isa_real(resultType) &&
2590          "Result must be double precision in DPROD");
2591   mlir::Value a = builder.createConvert(loc, resultType, args[0]);
2592   mlir::Value b = builder.createConvert(loc, resultType, args[1]);
2593   return builder.create<mlir::arith::MulFOp>(loc, a, b);
2594 }
2595 
2596 // EOSHIFT
2597 fir::ExtendedValue
2598 IntrinsicLibrary::genEoshift(mlir::Type resultType,
2599                              llvm::ArrayRef<fir::ExtendedValue> args) {
2600   assert(args.size() == 4);
2601 
2602   // Handle required ARRAY argument
2603   fir::BoxValue arrayBox = builder.createBox(loc, args[0]);
2604   mlir::Value array = fir::getBase(arrayBox);
2605   unsigned arrayRank = arrayBox.rank();
2606 
2607   // Create mutable fir.box to be passed to the runtime for the result.
2608   mlir::Type resultArrayType = builder.getVarLenSeqTy(resultType, arrayRank);
2609   fir::MutableBoxValue resultMutableBox =
2610       fir::factory::createTempMutableBox(builder, loc, resultArrayType);
2611   mlir::Value resultIrBox =
2612       fir::factory::getMutableIRBox(builder, loc, resultMutableBox);
2613 
2614   // Handle optional BOUNDARY argument
2615   mlir::Value boundary =
2616       isStaticallyAbsent(args[2])
2617           ? builder.create<fir::AbsentOp>(
2618                 loc, fir::BoxType::get(builder.getNoneType()))
2619           : builder.createBox(loc, args[2]);
2620 
2621   if (arrayRank == 1) {
2622     // Vector case
2623     // Handle required SHIFT argument as a scalar
2624     const mlir::Value *shiftAddr = args[1].getUnboxed();
2625     assert(shiftAddr && "nonscalar EOSHIFT SHIFT argument");
2626     auto shift = builder.create<fir::LoadOp>(loc, *shiftAddr);
2627     fir::runtime::genEoshiftVector(builder, loc, resultIrBox, array, shift,
2628                                    boundary);
2629   } else {
2630     // Non-vector case
2631     // Handle required SHIFT argument as an array
2632     mlir::Value shift = builder.createBox(loc, args[1]);
2633 
2634     // Handle optional DIM argument
2635     mlir::Value dim =
2636         isStaticallyAbsent(args[3])
2637             ? builder.createIntegerConstant(loc, builder.getIndexType(), 1)
2638             : fir::getBase(args[3]);
2639     fir::runtime::genEoshift(builder, loc, resultIrBox, array, shift, boundary,
2640                              dim);
2641   }
2642   return readAndAddCleanUp(resultMutableBox, resultType,
2643                            "unexpected result for EOSHIFT");
2644 }
2645 
2646 // EXIT
2647 void IntrinsicLibrary::genExit(llvm::ArrayRef<fir::ExtendedValue> args) {
2648   assert(args.size() == 1);
2649 
2650   mlir::Value status =
2651       isStaticallyAbsent(args[0])
2652           ? builder.createIntegerConstant(loc, builder.getDefaultIntegerType(),
2653                                           EXIT_SUCCESS)
2654           : fir::getBase(args[0]);
2655 
2656   assert(status.getType() == builder.getDefaultIntegerType() &&
2657          "STATUS parameter must be an INTEGER of default kind");
2658 
2659   fir::runtime::genExit(builder, loc, status);
2660 }
2661 
2662 // EXPONENT
2663 mlir::Value IntrinsicLibrary::genExponent(mlir::Type resultType,
2664                                           llvm::ArrayRef<mlir::Value> args) {
2665   assert(args.size() == 1);
2666 
2667   return builder.createConvert(
2668       loc, resultType,
2669       fir::runtime::genExponent(builder, loc, resultType,
2670                                 fir::getBase(args[0])));
2671 }
2672 
2673 // FLOOR
2674 mlir::Value IntrinsicLibrary::genFloor(mlir::Type resultType,
2675                                        llvm::ArrayRef<mlir::Value> args) {
2676   // Optional KIND argument.
2677   assert(args.size() >= 1);
2678   mlir::Value arg = args[0];
2679   // Use LLVM floor that returns real.
2680   mlir::Value floor = genRuntimeCall("floor", arg.getType(), {arg});
2681   return builder.createConvert(loc, resultType, floor);
2682 }
2683 
2684 // FRACTION
2685 mlir::Value IntrinsicLibrary::genFraction(mlir::Type resultType,
2686                                           llvm::ArrayRef<mlir::Value> args) {
2687   assert(args.size() == 1);
2688 
2689   return builder.createConvert(
2690       loc, resultType,
2691       fir::runtime::genFraction(builder, loc, fir::getBase(args[0])));
2692 }
2693 
2694 // GET_COMMAND_ARGUMENT
2695 void IntrinsicLibrary::genGetCommandArgument(
2696     llvm::ArrayRef<fir::ExtendedValue> args) {
2697   assert(args.size() == 5);
2698   mlir::Value number = fir::getBase(args[0]);
2699   const fir::ExtendedValue &value = args[1];
2700   const fir::ExtendedValue &length = args[2];
2701   const fir::ExtendedValue &status = args[3];
2702   const fir::ExtendedValue &errmsg = args[4];
2703 
2704   if (!number)
2705     fir::emitFatalError(loc, "expected NUMBER parameter");
2706 
2707   if (isStaticallyPresent(value) || isStaticallyPresent(status) ||
2708       isStaticallyPresent(errmsg)) {
2709     mlir::Type boxNoneTy = fir::BoxType::get(builder.getNoneType());
2710     mlir::Value valBox =
2711         isStaticallyPresent(value)
2712             ? fir::getBase(value)
2713             : builder.create<fir::AbsentOp>(loc, boxNoneTy).getResult();
2714     mlir::Value errBox =
2715         isStaticallyPresent(errmsg)
2716             ? fir::getBase(errmsg)
2717             : builder.create<fir::AbsentOp>(loc, boxNoneTy).getResult();
2718     mlir::Value stat =
2719         fir::runtime::genArgumentValue(builder, loc, number, valBox, errBox);
2720     if (isStaticallyPresent(status)) {
2721       mlir::Value statAddr = fir::getBase(status);
2722       mlir::Value statIsPresentAtRuntime =
2723           builder.genIsNotNullAddr(loc, statAddr);
2724       builder.genIfThen(loc, statIsPresentAtRuntime)
2725           .genThen(
2726               [&]() { builder.createStoreWithConvert(loc, stat, statAddr); })
2727           .end();
2728     }
2729   }
2730   if (isStaticallyPresent(length)) {
2731     mlir::Value lenAddr = fir::getBase(length);
2732     mlir::Value lenIsPresentAtRuntime = builder.genIsNotNullAddr(loc, lenAddr);
2733     builder.genIfThen(loc, lenIsPresentAtRuntime)
2734         .genThen([&]() {
2735           mlir::Value len =
2736               fir::runtime::genArgumentLength(builder, loc, number);
2737           builder.createStoreWithConvert(loc, len, lenAddr);
2738         })
2739         .end();
2740   }
2741 }
2742 
2743 // GET_ENVIRONMENT_VARIABLE
2744 void IntrinsicLibrary::genGetEnvironmentVariable(
2745     llvm::ArrayRef<fir::ExtendedValue> args) {
2746   assert(args.size() == 6);
2747   mlir::Value name = fir::getBase(args[0]);
2748   const fir::ExtendedValue &value = args[1];
2749   const fir::ExtendedValue &length = args[2];
2750   const fir::ExtendedValue &status = args[3];
2751   const fir::ExtendedValue &trimName = args[4];
2752   const fir::ExtendedValue &errmsg = args[5];
2753 
2754   // Handle optional TRIM_NAME argument
2755   mlir::Value trim;
2756   if (isStaticallyAbsent(trimName)) {
2757     trim = builder.createBool(loc, true);
2758   } else {
2759     mlir::Type i1Ty = builder.getI1Type();
2760     mlir::Value trimNameAddr = fir::getBase(trimName);
2761     mlir::Value trimNameIsPresentAtRuntime =
2762         builder.genIsNotNullAddr(loc, trimNameAddr);
2763     trim = builder
2764                .genIfOp(loc, {i1Ty}, trimNameIsPresentAtRuntime,
2765                         /*withElseRegion=*/true)
2766                .genThen([&]() {
2767                  auto trimLoad = builder.create<fir::LoadOp>(loc, trimNameAddr);
2768                  mlir::Value cast = builder.createConvert(loc, i1Ty, trimLoad);
2769                  builder.create<fir::ResultOp>(loc, cast);
2770                })
2771                .genElse([&]() {
2772                  mlir::Value trueVal = builder.createBool(loc, true);
2773                  builder.create<fir::ResultOp>(loc, trueVal);
2774                })
2775                .getResults()[0];
2776   }
2777 
2778   if (isStaticallyPresent(value) || isStaticallyPresent(status) ||
2779       isStaticallyPresent(errmsg)) {
2780     mlir::Type boxNoneTy = fir::BoxType::get(builder.getNoneType());
2781     mlir::Value valBox =
2782         isStaticallyPresent(value)
2783             ? fir::getBase(value)
2784             : builder.create<fir::AbsentOp>(loc, boxNoneTy).getResult();
2785     mlir::Value errBox =
2786         isStaticallyPresent(errmsg)
2787             ? fir::getBase(errmsg)
2788             : builder.create<fir::AbsentOp>(loc, boxNoneTy).getResult();
2789     mlir::Value stat = fir::runtime::genEnvVariableValue(builder, loc, name,
2790                                                          valBox, trim, errBox);
2791     if (isStaticallyPresent(status)) {
2792       mlir::Value statAddr = fir::getBase(status);
2793       mlir::Value statIsPresentAtRuntime =
2794           builder.genIsNotNullAddr(loc, statAddr);
2795       builder.genIfThen(loc, statIsPresentAtRuntime)
2796           .genThen(
2797               [&]() { builder.createStoreWithConvert(loc, stat, statAddr); })
2798           .end();
2799     }
2800   }
2801 
2802   if (isStaticallyPresent(length)) {
2803     mlir::Value lenAddr = fir::getBase(length);
2804     mlir::Value lenIsPresentAtRuntime = builder.genIsNotNullAddr(loc, lenAddr);
2805     builder.genIfThen(loc, lenIsPresentAtRuntime)
2806         .genThen([&]() {
2807           mlir::Value len =
2808               fir::runtime::genEnvVariableLength(builder, loc, name, trim);
2809           builder.createStoreWithConvert(loc, len, lenAddr);
2810         })
2811         .end();
2812   }
2813 }
2814 
2815 // IAND
2816 mlir::Value IntrinsicLibrary::genIand(mlir::Type resultType,
2817                                       llvm::ArrayRef<mlir::Value> args) {
2818   assert(args.size() == 2);
2819   auto arg0 = builder.createConvert(loc, resultType, args[0]);
2820   auto arg1 = builder.createConvert(loc, resultType, args[1]);
2821   return builder.create<mlir::arith::AndIOp>(loc, arg0, arg1);
2822 }
2823 
2824 // IBCLR
2825 mlir::Value IntrinsicLibrary::genIbclr(mlir::Type resultType,
2826                                        llvm::ArrayRef<mlir::Value> args) {
2827   // A conformant IBCLR(I,POS) call satisfies:
2828   //     POS >= 0
2829   //     POS < BIT_SIZE(I)
2830   // Return:  I & (!(1 << POS))
2831   assert(args.size() == 2);
2832   mlir::Value pos = builder.createConvert(loc, resultType, args[1]);
2833   mlir::Value one = builder.createIntegerConstant(loc, resultType, 1);
2834   mlir::Value ones = builder.createIntegerConstant(loc, resultType, -1);
2835   auto mask = builder.create<mlir::arith::ShLIOp>(loc, one, pos);
2836   auto res = builder.create<mlir::arith::XOrIOp>(loc, ones, mask);
2837   return builder.create<mlir::arith::AndIOp>(loc, args[0], res);
2838 }
2839 
2840 // IBITS
2841 mlir::Value IntrinsicLibrary::genIbits(mlir::Type resultType,
2842                                        llvm::ArrayRef<mlir::Value> args) {
2843   // A conformant IBITS(I,POS,LEN) call satisfies:
2844   //     POS >= 0
2845   //     LEN >= 0
2846   //     POS + LEN <= BIT_SIZE(I)
2847   // Return:  LEN == 0 ? 0 : (I >> POS) & (-1 >> (BIT_SIZE(I) - LEN))
2848   // For a conformant call, implementing (I >> POS) with a signed or an
2849   // unsigned shift produces the same result.  For a nonconformant call,
2850   // the two choices may produce different results.
2851   assert(args.size() == 3);
2852   mlir::Value pos = builder.createConvert(loc, resultType, args[1]);
2853   mlir::Value len = builder.createConvert(loc, resultType, args[2]);
2854   mlir::Value bitSize = builder.createIntegerConstant(
2855       loc, resultType, resultType.cast<mlir::IntegerType>().getWidth());
2856   auto shiftCount = builder.create<mlir::arith::SubIOp>(loc, bitSize, len);
2857   mlir::Value zero = builder.createIntegerConstant(loc, resultType, 0);
2858   mlir::Value ones = builder.createIntegerConstant(loc, resultType, -1);
2859   auto mask = builder.create<mlir::arith::ShRUIOp>(loc, ones, shiftCount);
2860   auto res1 = builder.create<mlir::arith::ShRSIOp>(loc, args[0], pos);
2861   auto res2 = builder.create<mlir::arith::AndIOp>(loc, res1, mask);
2862   auto lenIsZero = builder.create<mlir::arith::CmpIOp>(
2863       loc, mlir::arith::CmpIPredicate::eq, len, zero);
2864   return builder.create<mlir::arith::SelectOp>(loc, lenIsZero, zero, res2);
2865 }
2866 
2867 // IBSET
2868 mlir::Value IntrinsicLibrary::genIbset(mlir::Type resultType,
2869                                        llvm::ArrayRef<mlir::Value> args) {
2870   // A conformant IBSET(I,POS) call satisfies:
2871   //     POS >= 0
2872   //     POS < BIT_SIZE(I)
2873   // Return:  I | (1 << POS)
2874   assert(args.size() == 2);
2875   mlir::Value pos = builder.createConvert(loc, resultType, args[1]);
2876   mlir::Value one = builder.createIntegerConstant(loc, resultType, 1);
2877   auto mask = builder.create<mlir::arith::ShLIOp>(loc, one, pos);
2878   return builder.create<mlir::arith::OrIOp>(loc, args[0], mask);
2879 }
2880 
2881 // ICHAR
2882 fir::ExtendedValue
2883 IntrinsicLibrary::genIchar(mlir::Type resultType,
2884                            llvm::ArrayRef<fir::ExtendedValue> args) {
2885   // There can be an optional kind in second argument.
2886   assert(args.size() == 2);
2887   const fir::CharBoxValue *charBox = args[0].getCharBox();
2888   if (!charBox)
2889     llvm::report_fatal_error("expected character scalar");
2890 
2891   fir::factory::CharacterExprHelper helper{builder, loc};
2892   mlir::Value buffer = charBox->getBuffer();
2893   mlir::Type bufferTy = buffer.getType();
2894   mlir::Value charVal;
2895   if (auto charTy = bufferTy.dyn_cast<fir::CharacterType>()) {
2896     assert(charTy.singleton());
2897     charVal = buffer;
2898   } else {
2899     // Character is in memory, cast to fir.ref<char> and load.
2900     mlir::Type ty = fir::dyn_cast_ptrEleTy(bufferTy);
2901     if (!ty)
2902       llvm::report_fatal_error("expected memory type");
2903     // The length of in the character type may be unknown. Casting
2904     // to a singleton ref is required before loading.
2905     fir::CharacterType eleType = helper.getCharacterType(ty);
2906     fir::CharacterType charType =
2907         fir::CharacterType::get(builder.getContext(), eleType.getFKind(), 1);
2908     mlir::Type toTy = builder.getRefType(charType);
2909     mlir::Value cast = builder.createConvert(loc, toTy, buffer);
2910     charVal = builder.create<fir::LoadOp>(loc, cast);
2911   }
2912   LLVM_DEBUG(llvm::dbgs() << "ichar(" << charVal << ")\n");
2913   auto code = helper.extractCodeFromSingleton(charVal);
2914   if (code.getType() == resultType)
2915     return code;
2916   return builder.create<mlir::arith::ExtUIOp>(loc, resultType, code);
2917 }
2918 
2919 // IEEE_CLASS_TYPE OPERATOR(==), OPERATOR(/=)
2920 // IEEE_ROUND_TYPE OPERATOR(==), OPERATOR(/=)
2921 template <mlir::arith::CmpIPredicate pred>
2922 fir::ExtendedValue
2923 IntrinsicLibrary::genIeeeTypeCompare(mlir::Type resultType,
2924                                      llvm::ArrayRef<fir::ExtendedValue> args) {
2925   assert(args.size() == 2);
2926   mlir::Value arg0 = fir::getBase(args[0]);
2927   mlir::Value arg1 = fir::getBase(args[1]);
2928   auto recType =
2929       fir::unwrapPassByRefType(arg0.getType()).dyn_cast<fir::RecordType>();
2930   assert(recType.getTypeList().size() == 1 && "expected exactly one component");
2931   auto [fieldName, fieldType] = recType.getTypeList().front();
2932   mlir::Type fieldIndexType = fir::FieldType::get(recType.getContext());
2933   mlir::Value field = builder.create<fir::FieldIndexOp>(
2934       loc, fieldIndexType, fieldName, recType, fir::getTypeParams(arg0));
2935   mlir::Value left = builder.create<fir::LoadOp>(
2936       loc, fieldType,
2937       builder.create<fir::CoordinateOp>(loc, builder.getRefType(fieldType),
2938                                         arg0, field));
2939   mlir::Value right = builder.create<fir::LoadOp>(
2940       loc, fieldType,
2941       builder.create<fir::CoordinateOp>(loc, builder.getRefType(fieldType),
2942                                         arg1, field));
2943   return builder.create<mlir::arith::CmpIOp>(loc, pred, left, right);
2944 }
2945 
2946 // IEEE_IS_FINITE
2947 mlir::Value
2948 IntrinsicLibrary::genIeeeIsFinite(mlir::Type resultType,
2949                                   llvm::ArrayRef<mlir::Value> args) {
2950   // IEEE_IS_FINITE(X) is true iff exponent(X) is the max exponent of kind(X).
2951   assert(args.size() == 1);
2952   mlir::Value floatVal = fir::getBase(args[0]);
2953   mlir::FloatType floatType = floatVal.getType().dyn_cast<mlir::FloatType>();
2954   int floatBits = floatType.getWidth();
2955   mlir::Type intType = builder.getIntegerType(
2956       floatType.isa<mlir::Float80Type>() ? 128 : floatBits);
2957   mlir::Value intVal =
2958       builder.create<mlir::arith::BitcastOp>(loc, intType, floatVal);
2959   int significandBits;
2960   if (floatType.isa<mlir::Float32Type>())
2961     significandBits = 23;
2962   else if (floatType.isa<mlir::Float64Type>())
2963     significandBits = 52;
2964   else // problems elsewhere for other kinds
2965     TODO(loc, "intrinsic module procedure: ieee_is_finite");
2966   mlir::Value significand =
2967       builder.createIntegerConstant(loc, intType, significandBits);
2968   int exponentBits = floatBits - 1 - significandBits;
2969   mlir::Value maxExponent =
2970       builder.createIntegerConstant(loc, intType, (1 << exponentBits) - 1);
2971   mlir::Value exponent = genIbits(
2972       intType, {intVal, significand,
2973                 builder.createIntegerConstant(loc, intType, exponentBits)});
2974   return builder.createConvert(
2975       loc, resultType,
2976       builder.create<mlir::arith::CmpIOp>(loc, mlir::arith::CmpIPredicate::ne,
2977                                           exponent, maxExponent));
2978 }
2979 
2980 // IEOR
2981 mlir::Value IntrinsicLibrary::genIeor(mlir::Type resultType,
2982                                       llvm::ArrayRef<mlir::Value> args) {
2983   assert(args.size() == 2);
2984   return builder.create<mlir::arith::XOrIOp>(loc, args[0], args[1]);
2985 }
2986 
2987 // INDEX
2988 fir::ExtendedValue
2989 IntrinsicLibrary::genIndex(mlir::Type resultType,
2990                            llvm::ArrayRef<fir::ExtendedValue> args) {
2991   assert(args.size() >= 2 && args.size() <= 4);
2992 
2993   mlir::Value stringBase = fir::getBase(args[0]);
2994   fir::KindTy kind =
2995       fir::factory::CharacterExprHelper{builder, loc}.getCharacterKind(
2996           stringBase.getType());
2997   mlir::Value stringLen = fir::getLen(args[0]);
2998   mlir::Value substringBase = fir::getBase(args[1]);
2999   mlir::Value substringLen = fir::getLen(args[1]);
3000   mlir::Value back =
3001       isStaticallyAbsent(args, 2)
3002           ? builder.createIntegerConstant(loc, builder.getI1Type(), 0)
3003           : fir::getBase(args[2]);
3004   if (isStaticallyAbsent(args, 3))
3005     return builder.createConvert(
3006         loc, resultType,
3007         fir::runtime::genIndex(builder, loc, kind, stringBase, stringLen,
3008                                substringBase, substringLen, back));
3009 
3010   // Call the descriptor-based Index implementation
3011   mlir::Value string = builder.createBox(loc, args[0]);
3012   mlir::Value substring = builder.createBox(loc, args[1]);
3013   auto makeRefThenEmbox = [&](mlir::Value b) {
3014     fir::LogicalType logTy = fir::LogicalType::get(
3015         builder.getContext(), builder.getKindMap().defaultLogicalKind());
3016     mlir::Value temp = builder.createTemporary(loc, logTy);
3017     mlir::Value castb = builder.createConvert(loc, logTy, b);
3018     builder.create<fir::StoreOp>(loc, castb, temp);
3019     return builder.createBox(loc, temp);
3020   };
3021   mlir::Value backOpt = isStaticallyAbsent(args, 2)
3022                             ? builder.create<fir::AbsentOp>(
3023                                   loc, fir::BoxType::get(builder.getI1Type()))
3024                             : makeRefThenEmbox(fir::getBase(args[2]));
3025   mlir::Value kindVal = isStaticallyAbsent(args, 3)
3026                             ? builder.createIntegerConstant(
3027                                   loc, builder.getIndexType(),
3028                                   builder.getKindMap().defaultIntegerKind())
3029                             : fir::getBase(args[3]);
3030   // Create mutable fir.box to be passed to the runtime for the result.
3031   fir::MutableBoxValue mutBox =
3032       fir::factory::createTempMutableBox(builder, loc, resultType);
3033   mlir::Value resBox = fir::factory::getMutableIRBox(builder, loc, mutBox);
3034   // Call runtime. The runtime is allocating the result.
3035   fir::runtime::genIndexDescriptor(builder, loc, resBox, string, substring,
3036                                    backOpt, kindVal);
3037   // Read back the result from the mutable box.
3038   return readAndAddCleanUp(mutBox, resultType, "INDEX");
3039 }
3040 
3041 // IOR
3042 mlir::Value IntrinsicLibrary::genIor(mlir::Type resultType,
3043                                      llvm::ArrayRef<mlir::Value> args) {
3044   assert(args.size() == 2);
3045   return builder.create<mlir::arith::OrIOp>(loc, args[0], args[1]);
3046 }
3047 
3048 // ISHFT
3049 mlir::Value IntrinsicLibrary::genIshft(mlir::Type resultType,
3050                                        llvm::ArrayRef<mlir::Value> args) {
3051   // A conformant ISHFT(I,SHIFT) call satisfies:
3052   //     abs(SHIFT) <= BIT_SIZE(I)
3053   // Return:  abs(SHIFT) >= BIT_SIZE(I)
3054   //              ? 0
3055   //              : SHIFT < 0
3056   //                    ? I >> abs(SHIFT)
3057   //                    : I << abs(SHIFT)
3058   assert(args.size() == 2);
3059   mlir::Value bitSize = builder.createIntegerConstant(
3060       loc, resultType, resultType.cast<mlir::IntegerType>().getWidth());
3061   mlir::Value zero = builder.createIntegerConstant(loc, resultType, 0);
3062   mlir::Value shift = builder.createConvert(loc, resultType, args[1]);
3063   mlir::Value absShift = genAbs(resultType, {shift});
3064   auto left = builder.create<mlir::arith::ShLIOp>(loc, args[0], absShift);
3065   auto right = builder.create<mlir::arith::ShRUIOp>(loc, args[0], absShift);
3066   auto shiftIsLarge = builder.create<mlir::arith::CmpIOp>(
3067       loc, mlir::arith::CmpIPredicate::sge, absShift, bitSize);
3068   auto shiftIsNegative = builder.create<mlir::arith::CmpIOp>(
3069       loc, mlir::arith::CmpIPredicate::slt, shift, zero);
3070   auto sel =
3071       builder.create<mlir::arith::SelectOp>(loc, shiftIsNegative, right, left);
3072   return builder.create<mlir::arith::SelectOp>(loc, shiftIsLarge, zero, sel);
3073 }
3074 
3075 // ISHFTC
3076 mlir::Value IntrinsicLibrary::genIshftc(mlir::Type resultType,
3077                                         llvm::ArrayRef<mlir::Value> args) {
3078   // A conformant ISHFTC(I,SHIFT,SIZE) call satisfies:
3079   //     SIZE > 0
3080   //     SIZE <= BIT_SIZE(I)
3081   //     abs(SHIFT) <= SIZE
3082   // if SHIFT > 0
3083   //     leftSize = abs(SHIFT)
3084   //     rightSize = SIZE - abs(SHIFT)
3085   // else [if SHIFT < 0]
3086   //     leftSize = SIZE - abs(SHIFT)
3087   //     rightSize = abs(SHIFT)
3088   // unchanged = SIZE == BIT_SIZE(I) ? 0 : (I >> SIZE) << SIZE
3089   // leftMaskShift = BIT_SIZE(I) - leftSize
3090   // rightMaskShift = BIT_SIZE(I) - rightSize
3091   // left = (I >> rightSize) & (-1 >> leftMaskShift)
3092   // right = (I & (-1 >> rightMaskShift)) << leftSize
3093   // Return:  SHIFT == 0 || SIZE == abs(SHIFT) ? I : (unchanged | left | right)
3094   assert(args.size() == 3);
3095   mlir::Value bitSize = builder.createIntegerConstant(
3096       loc, resultType, resultType.cast<mlir::IntegerType>().getWidth());
3097   mlir::Value I = args[0];
3098   mlir::Value shift = builder.createConvert(loc, resultType, args[1]);
3099   mlir::Value size =
3100       args[2] ? builder.createConvert(loc, resultType, args[2]) : bitSize;
3101   mlir::Value zero = builder.createIntegerConstant(loc, resultType, 0);
3102   mlir::Value ones = builder.createIntegerConstant(loc, resultType, -1);
3103   mlir::Value absShift = genAbs(resultType, {shift});
3104   auto elseSize = builder.create<mlir::arith::SubIOp>(loc, size, absShift);
3105   auto shiftIsZero = builder.create<mlir::arith::CmpIOp>(
3106       loc, mlir::arith::CmpIPredicate::eq, shift, zero);
3107   auto shiftEqualsSize = builder.create<mlir::arith::CmpIOp>(
3108       loc, mlir::arith::CmpIPredicate::eq, absShift, size);
3109   auto shiftIsNop =
3110       builder.create<mlir::arith::OrIOp>(loc, shiftIsZero, shiftEqualsSize);
3111   auto shiftIsPositive = builder.create<mlir::arith::CmpIOp>(
3112       loc, mlir::arith::CmpIPredicate::sgt, shift, zero);
3113   auto leftSize = builder.create<mlir::arith::SelectOp>(loc, shiftIsPositive,
3114                                                         absShift, elseSize);
3115   auto rightSize = builder.create<mlir::arith::SelectOp>(loc, shiftIsPositive,
3116                                                          elseSize, absShift);
3117   auto hasUnchanged = builder.create<mlir::arith::CmpIOp>(
3118       loc, mlir::arith::CmpIPredicate::ne, size, bitSize);
3119   auto unchangedTmp1 = builder.create<mlir::arith::ShRUIOp>(loc, I, size);
3120   auto unchangedTmp2 =
3121       builder.create<mlir::arith::ShLIOp>(loc, unchangedTmp1, size);
3122   auto unchanged = builder.create<mlir::arith::SelectOp>(loc, hasUnchanged,
3123                                                          unchangedTmp2, zero);
3124   auto leftMaskShift =
3125       builder.create<mlir::arith::SubIOp>(loc, bitSize, leftSize);
3126   auto leftMask =
3127       builder.create<mlir::arith::ShRUIOp>(loc, ones, leftMaskShift);
3128   auto leftTmp = builder.create<mlir::arith::ShRUIOp>(loc, I, rightSize);
3129   auto left = builder.create<mlir::arith::AndIOp>(loc, leftTmp, leftMask);
3130   auto rightMaskShift =
3131       builder.create<mlir::arith::SubIOp>(loc, bitSize, rightSize);
3132   auto rightMask =
3133       builder.create<mlir::arith::ShRUIOp>(loc, ones, rightMaskShift);
3134   auto rightTmp = builder.create<mlir::arith::AndIOp>(loc, I, rightMask);
3135   auto right = builder.create<mlir::arith::ShLIOp>(loc, rightTmp, leftSize);
3136   auto resTmp = builder.create<mlir::arith::OrIOp>(loc, unchanged, left);
3137   auto res = builder.create<mlir::arith::OrIOp>(loc, resTmp, right);
3138   return builder.create<mlir::arith::SelectOp>(loc, shiftIsNop, I, res);
3139 }
3140 
3141 // LEN
3142 // Note that this is only used for an unrestricted intrinsic LEN call.
3143 // Other uses of LEN are rewritten as descriptor inquiries by the front-end.
3144 fir::ExtendedValue
3145 IntrinsicLibrary::genLen(mlir::Type resultType,
3146                          llvm::ArrayRef<fir::ExtendedValue> args) {
3147   // Optional KIND argument reflected in result type and otherwise ignored.
3148   assert(args.size() == 1 || args.size() == 2);
3149   mlir::Value len = fir::factory::readCharLen(builder, loc, args[0]);
3150   return builder.createConvert(loc, resultType, len);
3151 }
3152 
3153 // LEN_TRIM
3154 fir::ExtendedValue
3155 IntrinsicLibrary::genLenTrim(mlir::Type resultType,
3156                              llvm::ArrayRef<fir::ExtendedValue> args) {
3157   // Optional KIND argument reflected in result type and otherwise ignored.
3158   assert(args.size() == 1 || args.size() == 2);
3159   const fir::CharBoxValue *charBox = args[0].getCharBox();
3160   if (!charBox)
3161     TODO(loc, "character array len_trim");
3162   auto len =
3163       fir::factory::CharacterExprHelper(builder, loc).createLenTrim(*charBox);
3164   return builder.createConvert(loc, resultType, len);
3165 }
3166 
3167 // LGE, LGT, LLE, LLT
3168 template <mlir::arith::CmpIPredicate pred>
3169 fir::ExtendedValue
3170 IntrinsicLibrary::genCharacterCompare(mlir::Type resultType,
3171                                       llvm::ArrayRef<fir::ExtendedValue> args) {
3172   assert(args.size() == 2);
3173   return fir::runtime::genCharCompare(
3174       builder, loc, pred, fir::getBase(args[0]), fir::getLen(args[0]),
3175       fir::getBase(args[1]), fir::getLen(args[1]));
3176 }
3177 
3178 // MATMUL
3179 fir::ExtendedValue
3180 IntrinsicLibrary::genMatmul(mlir::Type resultType,
3181                             llvm::ArrayRef<fir::ExtendedValue> args) {
3182   assert(args.size() == 2);
3183 
3184   // Handle required matmul arguments
3185   fir::BoxValue matrixTmpA = builder.createBox(loc, args[0]);
3186   mlir::Value matrixA = fir::getBase(matrixTmpA);
3187   fir::BoxValue matrixTmpB = builder.createBox(loc, args[1]);
3188   mlir::Value matrixB = fir::getBase(matrixTmpB);
3189   unsigned resultRank =
3190       (matrixTmpA.rank() == 1 || matrixTmpB.rank() == 1) ? 1 : 2;
3191 
3192   // Create mutable fir.box to be passed to the runtime for the result.
3193   mlir::Type resultArrayType = builder.getVarLenSeqTy(resultType, resultRank);
3194   fir::MutableBoxValue resultMutableBox =
3195       fir::factory::createTempMutableBox(builder, loc, resultArrayType);
3196   mlir::Value resultIrBox =
3197       fir::factory::getMutableIRBox(builder, loc, resultMutableBox);
3198   // Call runtime. The runtime is allocating the result.
3199   fir::runtime::genMatmul(builder, loc, resultIrBox, matrixA, matrixB);
3200   // Read result from mutable fir.box and add it to the list of temps to be
3201   // finalized by the StatementContext.
3202   return readAndAddCleanUp(resultMutableBox, resultType,
3203                            "unexpected result for MATMUL");
3204 }
3205 
3206 // MERGE
3207 fir::ExtendedValue
3208 IntrinsicLibrary::genMerge(mlir::Type,
3209                            llvm::ArrayRef<fir::ExtendedValue> args) {
3210   assert(args.size() == 3);
3211   mlir::Value tsource = fir::getBase(args[0]);
3212   mlir::Value fsource = fir::getBase(args[1]);
3213   mlir::Value rawMask = fir::getBase(args[2]);
3214   mlir::Type type0 = fir::unwrapRefType(tsource.getType());
3215   bool isCharRslt = fir::isa_char(type0); // result is same as first argument
3216   mlir::Value mask = builder.createConvert(loc, builder.getI1Type(), rawMask);
3217   // FSOURCE has the same type as TSOURCE, but they may not have the same MLIR
3218   // types (one can have dynamic length while the other has constant lengths,
3219   // or one may be a fir.logical<> while the other is an i1). Insert a cast to
3220   // fulfill mlir::SelectOp constraint that the MLIR types must be the same.
3221   mlir::Value fsourceCast =
3222       builder.createConvert(loc, tsource.getType(), fsource);
3223   auto rslt =
3224       builder.create<mlir::arith::SelectOp>(loc, mask, tsource, fsourceCast);
3225   if (isCharRslt) {
3226     // Need a CharBoxValue for character results
3227     const fir::CharBoxValue *charBox = args[0].getCharBox();
3228     fir::CharBoxValue charRslt(rslt, charBox->getLen());
3229     return charRslt;
3230   }
3231   return rslt;
3232 }
3233 
3234 // MOD
3235 mlir::Value IntrinsicLibrary::genMod(mlir::Type resultType,
3236                                      llvm::ArrayRef<mlir::Value> args) {
3237   assert(args.size() == 2);
3238   if (resultType.isa<mlir::IntegerType>())
3239     return builder.create<mlir::arith::RemSIOp>(loc, args[0], args[1]);
3240 
3241   // Use runtime. Note that mlir::arith::RemFOp implements floating point
3242   // remainder, but it does not work with fir::Real type.
3243   // TODO: consider using mlir::arith::RemFOp when possible, that may help
3244   // folding and  optimizations.
3245   return genRuntimeCall("mod", resultType, args);
3246 }
3247 
3248 // MODULO
3249 mlir::Value IntrinsicLibrary::genModulo(mlir::Type resultType,
3250                                         llvm::ArrayRef<mlir::Value> args) {
3251   assert(args.size() == 2);
3252   // No floored modulo op in LLVM/MLIR yet. TODO: add one to MLIR.
3253   // In the meantime, use a simple inlined implementation based on truncated
3254   // modulo (MOD(A, P) implemented by RemIOp, RemFOp). This avoids making manual
3255   // division and multiplication from MODULO formula.
3256   //  - If A/P > 0 or MOD(A,P)=0, then INT(A/P) = FLOOR(A/P), and MODULO = MOD.
3257   //  - Otherwise, when A/P < 0 and MOD(A,P) !=0, then MODULO(A, P) =
3258   //    A-FLOOR(A/P)*P = A-(INT(A/P)-1)*P = A-INT(A/P)*P+P = MOD(A,P)+P
3259   // Note that A/P < 0 if and only if A and P signs are different.
3260   if (resultType.isa<mlir::IntegerType>()) {
3261     auto remainder =
3262         builder.create<mlir::arith::RemSIOp>(loc, args[0], args[1]);
3263     auto argXor = builder.create<mlir::arith::XOrIOp>(loc, args[0], args[1]);
3264     mlir::Value zero = builder.createIntegerConstant(loc, argXor.getType(), 0);
3265     auto argSignDifferent = builder.create<mlir::arith::CmpIOp>(
3266         loc, mlir::arith::CmpIPredicate::slt, argXor, zero);
3267     auto remainderIsNotZero = builder.create<mlir::arith::CmpIOp>(
3268         loc, mlir::arith::CmpIPredicate::ne, remainder, zero);
3269     auto mustAddP = builder.create<mlir::arith::AndIOp>(loc, remainderIsNotZero,
3270                                                         argSignDifferent);
3271     auto remPlusP =
3272         builder.create<mlir::arith::AddIOp>(loc, remainder, args[1]);
3273     return builder.create<mlir::arith::SelectOp>(loc, mustAddP, remPlusP,
3274                                                  remainder);
3275   }
3276   // Real case
3277   auto remainder = builder.create<mlir::arith::RemFOp>(loc, args[0], args[1]);
3278   mlir::Value zero = builder.createRealZeroConstant(loc, remainder.getType());
3279   auto remainderIsNotZero = builder.create<mlir::arith::CmpFOp>(
3280       loc, mlir::arith::CmpFPredicate::UNE, remainder, zero);
3281   auto aLessThanZero = builder.create<mlir::arith::CmpFOp>(
3282       loc, mlir::arith::CmpFPredicate::OLT, args[0], zero);
3283   auto pLessThanZero = builder.create<mlir::arith::CmpFOp>(
3284       loc, mlir::arith::CmpFPredicate::OLT, args[1], zero);
3285   auto argSignDifferent =
3286       builder.create<mlir::arith::XOrIOp>(loc, aLessThanZero, pLessThanZero);
3287   auto mustAddP = builder.create<mlir::arith::AndIOp>(loc, remainderIsNotZero,
3288                                                       argSignDifferent);
3289   auto remPlusP = builder.create<mlir::arith::AddFOp>(loc, remainder, args[1]);
3290   return builder.create<mlir::arith::SelectOp>(loc, mustAddP, remPlusP,
3291                                                remainder);
3292 }
3293 
3294 // MVBITS
3295 void IntrinsicLibrary::genMvbits(llvm::ArrayRef<fir::ExtendedValue> args) {
3296   // A conformant MVBITS(FROM,FROMPOS,LEN,TO,TOPOS) call satisfies:
3297   //     FROMPOS >= 0
3298   //     LEN >= 0
3299   //     TOPOS >= 0
3300   //     FROMPOS + LEN <= BIT_SIZE(FROM)
3301   //     TOPOS + LEN <= BIT_SIZE(TO)
3302   // MASK = -1 >> (BIT_SIZE(FROM) - LEN)
3303   // TO = LEN == 0 ? TO : ((!(MASK << TOPOS)) & TO) |
3304   //                      (((FROM >> FROMPOS) & MASK) << TOPOS)
3305   assert(args.size() == 5);
3306   auto unbox = [&](fir::ExtendedValue exv) {
3307     const mlir::Value *arg = exv.getUnboxed();
3308     assert(arg && "nonscalar mvbits argument");
3309     return *arg;
3310   };
3311   mlir::Value from = unbox(args[0]);
3312   mlir::Type resultType = from.getType();
3313   mlir::Value frompos = builder.createConvert(loc, resultType, unbox(args[1]));
3314   mlir::Value len = builder.createConvert(loc, resultType, unbox(args[2]));
3315   mlir::Value toAddr = unbox(args[3]);
3316   assert(fir::dyn_cast_ptrEleTy(toAddr.getType()) == resultType &&
3317          "mismatched mvbits types");
3318   auto to = builder.create<fir::LoadOp>(loc, resultType, toAddr);
3319   mlir::Value topos = builder.createConvert(loc, resultType, unbox(args[4]));
3320   mlir::Value zero = builder.createIntegerConstant(loc, resultType, 0);
3321   mlir::Value ones = builder.createIntegerConstant(loc, resultType, -1);
3322   mlir::Value bitSize = builder.createIntegerConstant(
3323       loc, resultType, resultType.cast<mlir::IntegerType>().getWidth());
3324   auto shiftCount = builder.create<mlir::arith::SubIOp>(loc, bitSize, len);
3325   auto mask = builder.create<mlir::arith::ShRUIOp>(loc, ones, shiftCount);
3326   auto unchangedTmp1 = builder.create<mlir::arith::ShLIOp>(loc, mask, topos);
3327   auto unchangedTmp2 =
3328       builder.create<mlir::arith::XOrIOp>(loc, unchangedTmp1, ones);
3329   auto unchanged = builder.create<mlir::arith::AndIOp>(loc, unchangedTmp2, to);
3330   auto frombitsTmp1 = builder.create<mlir::arith::ShRUIOp>(loc, from, frompos);
3331   auto frombitsTmp2 =
3332       builder.create<mlir::arith::AndIOp>(loc, frombitsTmp1, mask);
3333   auto frombits = builder.create<mlir::arith::ShLIOp>(loc, frombitsTmp2, topos);
3334   auto resTmp = builder.create<mlir::arith::OrIOp>(loc, unchanged, frombits);
3335   auto lenIsZero = builder.create<mlir::arith::CmpIOp>(
3336       loc, mlir::arith::CmpIPredicate::eq, len, zero);
3337   auto res = builder.create<mlir::arith::SelectOp>(loc, lenIsZero, to, resTmp);
3338   builder.create<fir::StoreOp>(loc, res, toAddr);
3339 }
3340 
3341 // NEAREST
3342 mlir::Value IntrinsicLibrary::genNearest(mlir::Type resultType,
3343                                          llvm::ArrayRef<mlir::Value> args) {
3344   assert(args.size() == 2);
3345 
3346   mlir::Value realX = fir::getBase(args[0]);
3347   mlir::Value realS = fir::getBase(args[1]);
3348 
3349   return builder.createConvert(
3350       loc, resultType, fir::runtime::genNearest(builder, loc, realX, realS));
3351 }
3352 
3353 // NINT
3354 mlir::Value IntrinsicLibrary::genNint(mlir::Type resultType,
3355                                       llvm::ArrayRef<mlir::Value> args) {
3356   assert(args.size() >= 1);
3357   // Skip optional kind argument to search the runtime; it is already reflected
3358   // in result type.
3359   return genRuntimeCall("nint", resultType, {args[0]});
3360 }
3361 
3362 // NOT
3363 mlir::Value IntrinsicLibrary::genNot(mlir::Type resultType,
3364                                      llvm::ArrayRef<mlir::Value> args) {
3365   assert(args.size() == 1);
3366   mlir::Value allOnes = builder.createIntegerConstant(loc, resultType, -1);
3367   return builder.create<mlir::arith::XOrIOp>(loc, args[0], allOnes);
3368 }
3369 
3370 // NULL
3371 fir::ExtendedValue
3372 IntrinsicLibrary::genNull(mlir::Type, llvm::ArrayRef<fir::ExtendedValue> args) {
3373   // NULL() without MOLD must be handled in the contexts where it can appear
3374   // (see table 16.5 of Fortran 2018 standard).
3375   assert(args.size() == 1 && isStaticallyPresent(args[0]) &&
3376          "MOLD argument required to lower NULL outside of any context");
3377   const auto *mold = args[0].getBoxOf<fir::MutableBoxValue>();
3378   assert(mold && "MOLD must be a pointer or allocatable");
3379   fir::BoxType boxType = mold->getBoxTy();
3380   mlir::Value boxStorage = builder.createTemporary(loc, boxType);
3381   mlir::Value box = fir::factory::createUnallocatedBox(
3382       builder, loc, boxType, mold->nonDeferredLenParams());
3383   builder.create<fir::StoreOp>(loc, box, boxStorage);
3384   return fir::MutableBoxValue(boxStorage, mold->nonDeferredLenParams(), {});
3385 }
3386 
3387 // PACK
3388 fir::ExtendedValue
3389 IntrinsicLibrary::genPack(mlir::Type resultType,
3390                           llvm::ArrayRef<fir::ExtendedValue> args) {
3391   [[maybe_unused]] auto numArgs = args.size();
3392   assert(numArgs == 2 || numArgs == 3);
3393 
3394   // Handle required array argument
3395   mlir::Value array = builder.createBox(loc, args[0]);
3396 
3397   // Handle required mask argument
3398   mlir::Value mask = builder.createBox(loc, args[1]);
3399 
3400   // Handle optional vector argument
3401   mlir::Value vector = isStaticallyAbsent(args, 2)
3402                            ? builder.create<fir::AbsentOp>(
3403                                  loc, fir::BoxType::get(builder.getI1Type()))
3404                            : builder.createBox(loc, args[2]);
3405 
3406   // Create mutable fir.box to be passed to the runtime for the result.
3407   mlir::Type resultArrayType = builder.getVarLenSeqTy(resultType, 1);
3408   fir::MutableBoxValue resultMutableBox =
3409       fir::factory::createTempMutableBox(builder, loc, resultArrayType);
3410   mlir::Value resultIrBox =
3411       fir::factory::getMutableIRBox(builder, loc, resultMutableBox);
3412 
3413   fir::runtime::genPack(builder, loc, resultIrBox, array, mask, vector);
3414 
3415   return readAndAddCleanUp(resultMutableBox, resultType,
3416                            "unexpected result for PACK");
3417 }
3418 
3419 // PRESENT
3420 fir::ExtendedValue
3421 IntrinsicLibrary::genPresent(mlir::Type,
3422                              llvm::ArrayRef<fir::ExtendedValue> args) {
3423   assert(args.size() == 1);
3424   return builder.create<fir::IsPresentOp>(loc, builder.getI1Type(),
3425                                           fir::getBase(args[0]));
3426 }
3427 
3428 // PRODUCT
3429 fir::ExtendedValue
3430 IntrinsicLibrary::genProduct(mlir::Type resultType,
3431                              llvm::ArrayRef<fir::ExtendedValue> args) {
3432   return genProdOrSum(fir::runtime::genProduct, fir::runtime::genProductDim,
3433                       resultType, builder, loc, stmtCtx,
3434                       "unexpected result for Product", args);
3435 }
3436 
3437 // RANDOM_INIT
3438 void IntrinsicLibrary::genRandomInit(llvm::ArrayRef<fir::ExtendedValue> args) {
3439   assert(args.size() == 2);
3440   Fortran::lower::genRandomInit(builder, loc, fir::getBase(args[0]),
3441                                 fir::getBase(args[1]));
3442 }
3443 
3444 // RANDOM_NUMBER
3445 void IntrinsicLibrary::genRandomNumber(
3446     llvm::ArrayRef<fir::ExtendedValue> args) {
3447   assert(args.size() == 1);
3448   Fortran::lower::genRandomNumber(builder, loc, fir::getBase(args[0]));
3449 }
3450 
3451 // RANDOM_SEED
3452 void IntrinsicLibrary::genRandomSeed(llvm::ArrayRef<fir::ExtendedValue> args) {
3453   assert(args.size() == 3);
3454   for (int i = 0; i < 3; ++i)
3455     if (isStaticallyPresent(args[i])) {
3456       Fortran::lower::genRandomSeed(builder, loc, i, fir::getBase(args[i]));
3457       return;
3458     }
3459   Fortran::lower::genRandomSeed(builder, loc, -1, mlir::Value{});
3460 }
3461 
3462 // REPEAT
3463 fir::ExtendedValue
3464 IntrinsicLibrary::genRepeat(mlir::Type resultType,
3465                             llvm::ArrayRef<fir::ExtendedValue> args) {
3466   assert(args.size() == 2);
3467   mlir::Value string = builder.createBox(loc, args[0]);
3468   mlir::Value ncopies = fir::getBase(args[1]);
3469   // Create mutable fir.box to be passed to the runtime for the result.
3470   fir::MutableBoxValue resultMutableBox =
3471       fir::factory::createTempMutableBox(builder, loc, resultType);
3472   mlir::Value resultIrBox =
3473       fir::factory::getMutableIRBox(builder, loc, resultMutableBox);
3474   // Call runtime. The runtime is allocating the result.
3475   fir::runtime::genRepeat(builder, loc, resultIrBox, string, ncopies);
3476   // Read result from mutable fir.box and add it to the list of temps to be
3477   // finalized by the StatementContext.
3478   return readAndAddCleanUp(resultMutableBox, resultType, "REPEAT");
3479 }
3480 
3481 // RESHAPE
3482 fir::ExtendedValue
3483 IntrinsicLibrary::genReshape(mlir::Type resultType,
3484                              llvm::ArrayRef<fir::ExtendedValue> args) {
3485   assert(args.size() == 4);
3486 
3487   // Handle source argument
3488   mlir::Value source = builder.createBox(loc, args[0]);
3489 
3490   // Handle shape argument
3491   mlir::Value shape = builder.createBox(loc, args[1]);
3492   assert(fir::BoxValue(shape).rank() == 1);
3493   mlir::Type shapeTy = shape.getType();
3494   mlir::Type shapeArrTy = fir::dyn_cast_ptrOrBoxEleTy(shapeTy);
3495   auto resultRank = shapeArrTy.cast<fir::SequenceType>().getShape()[0];
3496 
3497   if (resultRank == fir::SequenceType::getUnknownExtent())
3498     TODO(loc, "RESHAPE intrinsic requires computing rank of result");
3499 
3500   // Handle optional pad argument
3501   mlir::Value pad = isStaticallyAbsent(args[2])
3502                         ? builder.create<fir::AbsentOp>(
3503                               loc, fir::BoxType::get(builder.getI1Type()))
3504                         : builder.createBox(loc, args[2]);
3505 
3506   // Handle optional order argument
3507   mlir::Value order = isStaticallyAbsent(args[3])
3508                           ? builder.create<fir::AbsentOp>(
3509                                 loc, fir::BoxType::get(builder.getI1Type()))
3510                           : builder.createBox(loc, args[3]);
3511 
3512   // Create mutable fir.box to be passed to the runtime for the result.
3513   mlir::Type type = builder.getVarLenSeqTy(resultType, resultRank);
3514   fir::MutableBoxValue resultMutableBox =
3515       fir::factory::createTempMutableBox(builder, loc, type);
3516 
3517   mlir::Value resultIrBox =
3518       fir::factory::getMutableIRBox(builder, loc, resultMutableBox);
3519 
3520   fir::runtime::genReshape(builder, loc, resultIrBox, source, shape, pad,
3521                            order);
3522 
3523   return readAndAddCleanUp(resultMutableBox, resultType,
3524                            "unexpected result for RESHAPE");
3525 }
3526 
3527 // RRSPACING
3528 mlir::Value IntrinsicLibrary::genRRSpacing(mlir::Type resultType,
3529                                            llvm::ArrayRef<mlir::Value> args) {
3530   assert(args.size() == 1);
3531 
3532   return builder.createConvert(
3533       loc, resultType,
3534       fir::runtime::genRRSpacing(builder, loc, fir::getBase(args[0])));
3535 }
3536 
3537 // SCALE
3538 mlir::Value IntrinsicLibrary::genScale(mlir::Type resultType,
3539                                        llvm::ArrayRef<mlir::Value> args) {
3540   assert(args.size() == 2);
3541 
3542   mlir::Value realX = fir::getBase(args[0]);
3543   mlir::Value intI = fir::getBase(args[1]);
3544 
3545   return builder.createConvert(
3546       loc, resultType, fir::runtime::genScale(builder, loc, realX, intI));
3547 }
3548 
3549 // SCAN
3550 fir::ExtendedValue
3551 IntrinsicLibrary::genScan(mlir::Type resultType,
3552                           llvm::ArrayRef<fir::ExtendedValue> args) {
3553 
3554   assert(args.size() == 4);
3555 
3556   if (isStaticallyAbsent(args[3])) {
3557     // Kind not specified, so call scan/verify runtime routine that is
3558     // specialized on the kind of characters in string.
3559 
3560     // Handle required string base arg
3561     mlir::Value stringBase = fir::getBase(args[0]);
3562 
3563     // Handle required set string base arg
3564     mlir::Value setBase = fir::getBase(args[1]);
3565 
3566     // Handle kind argument; it is the kind of character in this case
3567     fir::KindTy kind =
3568         fir::factory::CharacterExprHelper{builder, loc}.getCharacterKind(
3569             stringBase.getType());
3570 
3571     // Get string length argument
3572     mlir::Value stringLen = fir::getLen(args[0]);
3573 
3574     // Get set string length argument
3575     mlir::Value setLen = fir::getLen(args[1]);
3576 
3577     // Handle optional back argument
3578     mlir::Value back =
3579         isStaticallyAbsent(args[2])
3580             ? builder.createIntegerConstant(loc, builder.getI1Type(), 0)
3581             : fir::getBase(args[2]);
3582 
3583     return builder.createConvert(loc, resultType,
3584                                  fir::runtime::genScan(builder, loc, kind,
3585                                                        stringBase, stringLen,
3586                                                        setBase, setLen, back));
3587   }
3588   // else use the runtime descriptor version of scan/verify
3589 
3590   // Handle optional argument, back
3591   auto makeRefThenEmbox = [&](mlir::Value b) {
3592     fir::LogicalType logTy = fir::LogicalType::get(
3593         builder.getContext(), builder.getKindMap().defaultLogicalKind());
3594     mlir::Value temp = builder.createTemporary(loc, logTy);
3595     mlir::Value castb = builder.createConvert(loc, logTy, b);
3596     builder.create<fir::StoreOp>(loc, castb, temp);
3597     return builder.createBox(loc, temp);
3598   };
3599   mlir::Value back = fir::isUnboxedValue(args[2])
3600                          ? makeRefThenEmbox(*args[2].getUnboxed())
3601                          : builder.create<fir::AbsentOp>(
3602                                loc, fir::BoxType::get(builder.getI1Type()));
3603 
3604   // Handle required string argument
3605   mlir::Value string = builder.createBox(loc, args[0]);
3606 
3607   // Handle required set argument
3608   mlir::Value set = builder.createBox(loc, args[1]);
3609 
3610   // Handle kind argument
3611   mlir::Value kind = fir::getBase(args[3]);
3612 
3613   // Create result descriptor
3614   fir::MutableBoxValue resultMutableBox =
3615       fir::factory::createTempMutableBox(builder, loc, resultType);
3616   mlir::Value resultIrBox =
3617       fir::factory::getMutableIRBox(builder, loc, resultMutableBox);
3618 
3619   fir::runtime::genScanDescriptor(builder, loc, resultIrBox, string, set, back,
3620                                   kind);
3621 
3622   // Handle cleanup of allocatable result descriptor and return
3623   return readAndAddCleanUp(resultMutableBox, resultType, "SCAN");
3624 }
3625 
3626 // SET_EXPONENT
3627 mlir::Value IntrinsicLibrary::genSetExponent(mlir::Type resultType,
3628                                              llvm::ArrayRef<mlir::Value> args) {
3629   assert(args.size() == 2);
3630 
3631   return builder.createConvert(
3632       loc, resultType,
3633       fir::runtime::genSetExponent(builder, loc, fir::getBase(args[0]),
3634                                    fir::getBase(args[1])));
3635 }
3636 
3637 // SIGN
3638 mlir::Value IntrinsicLibrary::genSign(mlir::Type resultType,
3639                                       llvm::ArrayRef<mlir::Value> args) {
3640   assert(args.size() == 2);
3641   if (resultType.isa<mlir::IntegerType>()) {
3642     mlir::Value abs = genAbs(resultType, {args[0]});
3643     mlir::Value zero = builder.createIntegerConstant(loc, resultType, 0);
3644     auto neg = builder.create<mlir::arith::SubIOp>(loc, zero, abs);
3645     auto cmp = builder.create<mlir::arith::CmpIOp>(
3646         loc, mlir::arith::CmpIPredicate::slt, args[1], zero);
3647     return builder.create<mlir::arith::SelectOp>(loc, cmp, neg, abs);
3648   }
3649   return genRuntimeCall("sign", resultType, args);
3650 }
3651 
3652 // SIZE
3653 fir::ExtendedValue
3654 IntrinsicLibrary::genSize(mlir::Type resultType,
3655                           llvm::ArrayRef<fir::ExtendedValue> args) {
3656   // Note that the value of the KIND argument is already reflected in the
3657   // resultType
3658   assert(args.size() == 3);
3659   if (const auto *boxValue = args[0].getBoxOf<fir::BoxValue>())
3660     if (boxValue->hasAssumedRank())
3661       TODO(loc, "SIZE intrinsic with assumed rank argument");
3662 
3663   // Get the ARRAY argument
3664   mlir::Value array = builder.createBox(loc, args[0]);
3665 
3666   // The front-end rewrites SIZE without the DIM argument to
3667   // an array of SIZE with DIM in most cases, but it may not be
3668   // possible in some cases like when in SIZE(function_call()).
3669   if (isStaticallyAbsent(args, 1))
3670     return builder.createConvert(loc, resultType,
3671                                  fir::runtime::genSize(builder, loc, array));
3672 
3673   // Get the DIM argument.
3674   mlir::Value dim = fir::getBase(args[1]);
3675   if (!fir::isa_ref_type(dim.getType()))
3676     return builder.createConvert(
3677         loc, resultType, fir::runtime::genSizeDim(builder, loc, array, dim));
3678 
3679   mlir::Value isDynamicallyAbsent = builder.genIsNullAddr(loc, dim);
3680   return builder
3681       .genIfOp(loc, {resultType}, isDynamicallyAbsent,
3682                /*withElseRegion=*/true)
3683       .genThen([&]() {
3684         mlir::Value size = builder.createConvert(
3685             loc, resultType, fir::runtime::genSize(builder, loc, array));
3686         builder.create<fir::ResultOp>(loc, size);
3687       })
3688       .genElse([&]() {
3689         mlir::Value dimValue = builder.create<fir::LoadOp>(loc, dim);
3690         mlir::Value size = builder.createConvert(
3691             loc, resultType,
3692             fir::runtime::genSizeDim(builder, loc, array, dimValue));
3693         builder.create<fir::ResultOp>(loc, size);
3694       })
3695       .getResults()[0];
3696 }
3697 
3698 static bool hasDefaultLowerBound(const fir::ExtendedValue &exv) {
3699   return exv.match(
3700       [](const fir::ArrayBoxValue &arr) { return arr.getLBounds().empty(); },
3701       [](const fir::CharArrayBoxValue &arr) {
3702         return arr.getLBounds().empty();
3703       },
3704       [](const fir::BoxValue &arr) { return arr.getLBounds().empty(); },
3705       [](const auto &) { return false; });
3706 }
3707 
3708 /// Compute the lower bound in dimension \p dim (zero based) of \p array
3709 /// taking care of returning one when the related extent is zero.
3710 static mlir::Value computeLBOUND(fir::FirOpBuilder &builder, mlir::Location loc,
3711                                  const fir::ExtendedValue &array, unsigned dim,
3712                                  mlir::Value zero, mlir::Value one) {
3713   assert(dim < array.rank() && "invalid dimension");
3714   if (hasDefaultLowerBound(array))
3715     return one;
3716   mlir::Value lb = fir::factory::readLowerBound(builder, loc, array, dim, one);
3717   if (dim + 1 == array.rank() && array.isAssumedSize())
3718     return lb;
3719   mlir::Value extent = fir::factory::readExtent(builder, loc, array, dim);
3720   zero = builder.createConvert(loc, extent.getType(), zero);
3721   auto dimIsEmpty = builder.create<mlir::arith::CmpIOp>(
3722       loc, mlir::arith::CmpIPredicate::eq, extent, zero);
3723   one = builder.createConvert(loc, lb.getType(), one);
3724   return builder.create<mlir::arith::SelectOp>(loc, dimIsEmpty, one, lb);
3725 }
3726 
3727 /// Create a fir.box to be passed to the LBOUND runtime.
3728 /// This ensure that local lower bounds of assumed shape are propagated and that
3729 /// a fir.box with equivalent LBOUNDs but an explicit shape is created for
3730 /// assumed size arrays to avoid undefined behaviors in codegen or the runtime.
3731 static mlir::Value createBoxForLBOUND(mlir::Location loc,
3732                                       fir::FirOpBuilder &builder,
3733                                       const fir::ExtendedValue &array) {
3734   if (!array.isAssumedSize())
3735     return array.match(
3736         [&](const fir::BoxValue &boxValue) -> mlir::Value {
3737           // This entity is mapped to a fir.box that may not contain the local
3738           // lower bound information if it is a dummy. Rebox it with the local
3739           // shape information.
3740           mlir::Value localShape = builder.createShape(loc, array);
3741           mlir::Value oldBox = boxValue.getAddr();
3742           return builder.create<fir::ReboxOp>(loc, oldBox.getType(), oldBox,
3743                                               localShape,
3744                                               /*slice=*/mlir::Value{});
3745         },
3746         [&](const auto &) -> mlir::Value {
3747           // This a pointer/allocatable, or an entity not yet tracked with a
3748           // fir.box. For pointer/allocatable, createBox will forward the
3749           // descriptor that contains the correct lower bound information. For
3750           // other entities, a new fir.box will be made with the local lower
3751           // bounds.
3752           return builder.createBox(loc, array);
3753         });
3754   // Assumed sized are not meant to be emboxed. This could cause the undefined
3755   // extent cannot safely be understood by the runtime/codegen that will
3756   // consider that the dimension is empty and that the related LBOUND value must
3757   // be one. Pretend that the related extent is one to get the correct LBOUND
3758   // value.
3759   llvm::SmallVector<mlir::Value> shape =
3760       fir::factory::getExtents(loc, builder, array);
3761   assert(!shape.empty() && "assumed size must have at least one dimension");
3762   shape.back() = builder.createIntegerConstant(loc, builder.getIndexType(), 1);
3763   auto safeToEmbox = array.match(
3764       [&](const fir::CharArrayBoxValue &x) -> fir::ExtendedValue {
3765         return fir::CharArrayBoxValue{x.getAddr(), x.getLen(), shape,
3766                                       x.getLBounds()};
3767       },
3768       [&](const fir::ArrayBoxValue &x) -> fir::ExtendedValue {
3769         return fir::ArrayBoxValue{x.getAddr(), shape, x.getLBounds()};
3770       },
3771       [&](const auto &) -> fir::ExtendedValue {
3772         fir::emitFatalError(loc, "not an assumed size array");
3773       });
3774   return builder.createBox(loc, safeToEmbox);
3775 }
3776 
3777 // LBOUND
3778 fir::ExtendedValue
3779 IntrinsicLibrary::genLbound(mlir::Type resultType,
3780                             llvm::ArrayRef<fir::ExtendedValue> args) {
3781   assert(args.size() == 2 || args.size() == 3);
3782   const fir::ExtendedValue &array = args[0];
3783   if (const auto *boxValue = array.getBoxOf<fir::BoxValue>())
3784     if (boxValue->hasAssumedRank())
3785       TODO(loc, "LBOUND intrinsic with assumed rank argument");
3786 
3787   //===----------------------------------------------------------------------===//
3788   mlir::Type indexType = builder.getIndexType();
3789 
3790   // Semantics builds signatures for LBOUND calls as either
3791   // LBOUND(array, dim, [kind]) or LBOUND(array, [kind]).
3792   if (args.size() == 2 || isStaticallyAbsent(args, 1)) {
3793     // DIM is absent.
3794     mlir::Type lbType = fir::unwrapSequenceType(resultType);
3795     unsigned rank = array.rank();
3796     mlir::Type lbArrayType = fir::SequenceType::get(
3797         {static_cast<fir::SequenceType::Extent>(array.rank())}, lbType);
3798     mlir::Value lbArray = builder.createTemporary(loc, lbArrayType);
3799     mlir::Type lbAddrType = builder.getRefType(lbType);
3800     mlir::Value one = builder.createIntegerConstant(loc, lbType, 1);
3801     mlir::Value zero = builder.createIntegerConstant(loc, indexType, 0);
3802     for (unsigned dim = 0; dim < rank; ++dim) {
3803       mlir::Value lb = computeLBOUND(builder, loc, array, dim, zero, one);
3804       lb = builder.createConvert(loc, lbType, lb);
3805       auto index = builder.createIntegerConstant(loc, indexType, dim);
3806       auto lbAddr =
3807           builder.create<fir::CoordinateOp>(loc, lbAddrType, lbArray, index);
3808       builder.create<fir::StoreOp>(loc, lb, lbAddr);
3809     }
3810     mlir::Value lbArrayExtent =
3811         builder.createIntegerConstant(loc, indexType, rank);
3812     llvm::SmallVector<mlir::Value> extents{lbArrayExtent};
3813     return fir::ArrayBoxValue{lbArray, extents};
3814   }
3815   // DIM is present.
3816   mlir::Value dim = fir::getBase(args[1]);
3817 
3818   // If it is a compile time constant, skip the runtime call.
3819   if (llvm::Optional<std::int64_t> cstDim =
3820           fir::factory::getIntIfConstant(dim)) {
3821     mlir::Value one = builder.createIntegerConstant(loc, resultType, 1);
3822     mlir::Value zero = builder.createIntegerConstant(loc, indexType, 0);
3823     mlir::Value lb = computeLBOUND(builder, loc, array, *cstDim - 1, zero, one);
3824     return builder.createConvert(loc, resultType, lb);
3825   }
3826 
3827   fir::ExtendedValue box = createBoxForLBOUND(loc, builder, array);
3828   return builder.createConvert(
3829       loc, resultType,
3830       fir::runtime::genLboundDim(builder, loc, fir::getBase(box), dim));
3831 }
3832 
3833 // UBOUND
3834 fir::ExtendedValue
3835 IntrinsicLibrary::genUbound(mlir::Type resultType,
3836                             llvm::ArrayRef<fir::ExtendedValue> args) {
3837   assert(args.size() == 3 || args.size() == 2);
3838   if (args.size() == 3) {
3839     // Handle calls to UBOUND with the DIM argument, which return a scalar
3840     mlir::Value extent = fir::getBase(genSize(resultType, args));
3841     mlir::Value lbound = fir::getBase(genLbound(resultType, args));
3842 
3843     mlir::Value one = builder.createIntegerConstant(loc, resultType, 1);
3844     mlir::Value ubound = builder.create<mlir::arith::SubIOp>(loc, lbound, one);
3845     return builder.create<mlir::arith::AddIOp>(loc, ubound, extent);
3846   } else {
3847     // Handle calls to UBOUND without the DIM argument, which return an array
3848     mlir::Value kind = isStaticallyAbsent(args[1])
3849                            ? builder.createIntegerConstant(
3850                                  loc, builder.getIndexType(),
3851                                  builder.getKindMap().defaultIntegerKind())
3852                            : fir::getBase(args[1]);
3853 
3854     // Create mutable fir.box to be passed to the runtime for the result.
3855     mlir::Type type = builder.getVarLenSeqTy(resultType, /*rank=*/1);
3856     fir::MutableBoxValue resultMutableBox =
3857         fir::factory::createTempMutableBox(builder, loc, type);
3858     mlir::Value resultIrBox =
3859         fir::factory::getMutableIRBox(builder, loc, resultMutableBox);
3860 
3861     fir::runtime::genUbound(builder, loc, resultIrBox, fir::getBase(args[0]),
3862                             kind);
3863 
3864     return readAndAddCleanUp(resultMutableBox, resultType, "UBOUND");
3865   }
3866   return mlir::Value();
3867 }
3868 
3869 // SPACING
3870 mlir::Value IntrinsicLibrary::genSpacing(mlir::Type resultType,
3871                                          llvm::ArrayRef<mlir::Value> args) {
3872   assert(args.size() == 1);
3873 
3874   return builder.createConvert(
3875       loc, resultType,
3876       fir::runtime::genSpacing(builder, loc, fir::getBase(args[0])));
3877 }
3878 
3879 // SPREAD
3880 fir::ExtendedValue
3881 IntrinsicLibrary::genSpread(mlir::Type resultType,
3882                             llvm::ArrayRef<fir::ExtendedValue> args) {
3883 
3884   assert(args.size() == 3);
3885 
3886   // Handle source argument
3887   mlir::Value source = builder.createBox(loc, args[0]);
3888   fir::BoxValue sourceTmp = source;
3889   unsigned sourceRank = sourceTmp.rank();
3890 
3891   // Handle Dim argument
3892   mlir::Value dim = fir::getBase(args[1]);
3893 
3894   // Handle ncopies argument
3895   mlir::Value ncopies = fir::getBase(args[2]);
3896 
3897   // Generate result descriptor
3898   mlir::Type resultArrayType =
3899       builder.getVarLenSeqTy(resultType, sourceRank + 1);
3900   fir::MutableBoxValue resultMutableBox =
3901       fir::factory::createTempMutableBox(builder, loc, resultArrayType);
3902   mlir::Value resultIrBox =
3903       fir::factory::getMutableIRBox(builder, loc, resultMutableBox);
3904 
3905   fir::runtime::genSpread(builder, loc, resultIrBox, source, dim, ncopies);
3906 
3907   return readAndAddCleanUp(resultMutableBox, resultType,
3908                            "unexpected result for SPREAD");
3909 }
3910 
3911 // SUM
3912 fir::ExtendedValue
3913 IntrinsicLibrary::genSum(mlir::Type resultType,
3914                          llvm::ArrayRef<fir::ExtendedValue> args) {
3915   return genProdOrSum(fir::runtime::genSum, fir::runtime::genSumDim, resultType,
3916                       builder, loc, stmtCtx, "unexpected result for Sum", args);
3917 }
3918 
3919 // SYSTEM_CLOCK
3920 void IntrinsicLibrary::genSystemClock(llvm::ArrayRef<fir::ExtendedValue> args) {
3921   assert(args.size() == 3);
3922   Fortran::lower::genSystemClock(builder, loc, fir::getBase(args[0]),
3923                                  fir::getBase(args[1]), fir::getBase(args[2]));
3924 }
3925 
3926 // TRANSFER
3927 fir::ExtendedValue
3928 IntrinsicLibrary::genTransfer(mlir::Type resultType,
3929                               llvm::ArrayRef<fir::ExtendedValue> args) {
3930 
3931   assert(args.size() >= 2); // args.size() == 2 when size argument is omitted.
3932 
3933   // Handle source argument
3934   mlir::Value source = builder.createBox(loc, args[0]);
3935 
3936   // Handle mold argument
3937   mlir::Value mold = builder.createBox(loc, args[1]);
3938   fir::BoxValue moldTmp = mold;
3939   unsigned moldRank = moldTmp.rank();
3940 
3941   bool absentSize = (args.size() == 2);
3942 
3943   // Create mutable fir.box to be passed to the runtime for the result.
3944   mlir::Type type = (moldRank == 0 && absentSize)
3945                         ? resultType
3946                         : builder.getVarLenSeqTy(resultType, 1);
3947   fir::MutableBoxValue resultMutableBox =
3948       fir::factory::createTempMutableBox(builder, loc, type);
3949 
3950   if (moldRank == 0 && absentSize) {
3951     // This result is a scalar in this case.
3952     mlir::Value resultIrBox =
3953         fir::factory::getMutableIRBox(builder, loc, resultMutableBox);
3954 
3955     Fortran::lower::genTransfer(builder, loc, resultIrBox, source, mold);
3956   } else {
3957     // The result is a rank one array in this case.
3958     mlir::Value resultIrBox =
3959         fir::factory::getMutableIRBox(builder, loc, resultMutableBox);
3960 
3961     if (absentSize) {
3962       Fortran::lower::genTransfer(builder, loc, resultIrBox, source, mold);
3963     } else {
3964       mlir::Value sizeArg = fir::getBase(args[2]);
3965       Fortran::lower::genTransferSize(builder, loc, resultIrBox, source, mold,
3966                                       sizeArg);
3967     }
3968   }
3969   return readAndAddCleanUp(resultMutableBox, resultType,
3970                            "unexpected result for TRANSFER");
3971 }
3972 
3973 // TRANSPOSE
3974 fir::ExtendedValue
3975 IntrinsicLibrary::genTranspose(mlir::Type resultType,
3976                                llvm::ArrayRef<fir::ExtendedValue> args) {
3977 
3978   assert(args.size() == 1);
3979 
3980   // Handle source argument
3981   mlir::Value source = builder.createBox(loc, args[0]);
3982 
3983   // Create mutable fir.box to be passed to the runtime for the result.
3984   mlir::Type resultArrayType = builder.getVarLenSeqTy(resultType, 2);
3985   fir::MutableBoxValue resultMutableBox =
3986       fir::factory::createTempMutableBox(builder, loc, resultArrayType);
3987   mlir::Value resultIrBox =
3988       fir::factory::getMutableIRBox(builder, loc, resultMutableBox);
3989   // Call runtime. The runtime is allocating the result.
3990   fir::runtime::genTranspose(builder, loc, resultIrBox, source);
3991   // Read result from mutable fir.box and add it to the list of temps to be
3992   // finalized by the StatementContext.
3993   return readAndAddCleanUp(resultMutableBox, resultType,
3994                            "unexpected result for TRANSPOSE");
3995 }
3996 
3997 // TRIM
3998 fir::ExtendedValue
3999 IntrinsicLibrary::genTrim(mlir::Type resultType,
4000                           llvm::ArrayRef<fir::ExtendedValue> args) {
4001   assert(args.size() == 1);
4002   mlir::Value string = builder.createBox(loc, args[0]);
4003   // Create mutable fir.box to be passed to the runtime for the result.
4004   fir::MutableBoxValue resultMutableBox =
4005       fir::factory::createTempMutableBox(builder, loc, resultType);
4006   mlir::Value resultIrBox =
4007       fir::factory::getMutableIRBox(builder, loc, resultMutableBox);
4008   // Call runtime. The runtime is allocating the result.
4009   fir::runtime::genTrim(builder, loc, resultIrBox, string);
4010   // Read result from mutable fir.box and add it to the list of temps to be
4011   // finalized by the StatementContext.
4012   return readAndAddCleanUp(resultMutableBox, resultType, "TRIM");
4013 }
4014 
4015 // Compare two FIR values and return boolean result as i1.
4016 template <Extremum extremum, ExtremumBehavior behavior>
4017 static mlir::Value createExtremumCompare(mlir::Location loc,
4018                                          fir::FirOpBuilder &builder,
4019                                          mlir::Value left, mlir::Value right) {
4020   static constexpr mlir::arith::CmpIPredicate integerPredicate =
4021       extremum == Extremum::Max ? mlir::arith::CmpIPredicate::sgt
4022                                 : mlir::arith::CmpIPredicate::slt;
4023   static constexpr mlir::arith::CmpFPredicate orderedCmp =
4024       extremum == Extremum::Max ? mlir::arith::CmpFPredicate::OGT
4025                                 : mlir::arith::CmpFPredicate::OLT;
4026   mlir::Type type = left.getType();
4027   mlir::Value result;
4028   if (fir::isa_real(type)) {
4029     // Note: the signaling/quit aspect of the result required by IEEE
4030     // cannot currently be obtained with LLVM without ad-hoc runtime.
4031     if constexpr (behavior == ExtremumBehavior::IeeeMinMaximumNumber) {
4032       // Return the number if one of the inputs is NaN and the other is
4033       // a number.
4034       auto leftIsResult =
4035           builder.create<mlir::arith::CmpFOp>(loc, orderedCmp, left, right);
4036       auto rightIsNan = builder.create<mlir::arith::CmpFOp>(
4037           loc, mlir::arith::CmpFPredicate::UNE, right, right);
4038       result =
4039           builder.create<mlir::arith::OrIOp>(loc, leftIsResult, rightIsNan);
4040     } else if constexpr (behavior == ExtremumBehavior::IeeeMinMaximum) {
4041       // Always return NaNs if one the input is NaNs
4042       auto leftIsResult =
4043           builder.create<mlir::arith::CmpFOp>(loc, orderedCmp, left, right);
4044       auto leftIsNan = builder.create<mlir::arith::CmpFOp>(
4045           loc, mlir::arith::CmpFPredicate::UNE, left, left);
4046       result = builder.create<mlir::arith::OrIOp>(loc, leftIsResult, leftIsNan);
4047     } else if constexpr (behavior == ExtremumBehavior::MinMaxss) {
4048       // If the left is a NaN, return the right whatever it is.
4049       result =
4050           builder.create<mlir::arith::CmpFOp>(loc, orderedCmp, left, right);
4051     } else if constexpr (behavior == ExtremumBehavior::PgfortranLlvm) {
4052       // If one of the operand is a NaN, return left whatever it is.
4053       static constexpr auto unorderedCmp =
4054           extremum == Extremum::Max ? mlir::arith::CmpFPredicate::UGT
4055                                     : mlir::arith::CmpFPredicate::ULT;
4056       result =
4057           builder.create<mlir::arith::CmpFOp>(loc, unorderedCmp, left, right);
4058     } else {
4059       // TODO: ieeeMinNum/ieeeMaxNum
4060       static_assert(behavior == ExtremumBehavior::IeeeMinMaxNum,
4061                     "ieeeMinNum/ieeeMaxNum behavior not implemented");
4062     }
4063   } else if (fir::isa_integer(type)) {
4064     result =
4065         builder.create<mlir::arith::CmpIOp>(loc, integerPredicate, left, right);
4066   } else if (fir::isa_char(type) || fir::isa_char(fir::unwrapRefType(type))) {
4067     // TODO: ! character min and max is tricky because the result
4068     // length is the length of the longest argument!
4069     // So we may need a temp.
4070     TODO(loc, "CHARACTER min and max");
4071   }
4072   assert(result && "result must be defined");
4073   return result;
4074 }
4075 
4076 // UNPACK
4077 fir::ExtendedValue
4078 IntrinsicLibrary::genUnpack(mlir::Type resultType,
4079                             llvm::ArrayRef<fir::ExtendedValue> args) {
4080   assert(args.size() == 3);
4081 
4082   // Handle required vector argument
4083   mlir::Value vector = builder.createBox(loc, args[0]);
4084 
4085   // Handle required mask argument
4086   fir::BoxValue maskBox = builder.createBox(loc, args[1]);
4087   mlir::Value mask = fir::getBase(maskBox);
4088   unsigned maskRank = maskBox.rank();
4089 
4090   // Handle required field argument
4091   mlir::Value field = builder.createBox(loc, args[2]);
4092 
4093   // Create mutable fir.box to be passed to the runtime for the result.
4094   mlir::Type resultArrayType = builder.getVarLenSeqTy(resultType, maskRank);
4095   fir::MutableBoxValue resultMutableBox =
4096       fir::factory::createTempMutableBox(builder, loc, resultArrayType);
4097   mlir::Value resultIrBox =
4098       fir::factory::getMutableIRBox(builder, loc, resultMutableBox);
4099 
4100   fir::runtime::genUnpack(builder, loc, resultIrBox, vector, mask, field);
4101 
4102   return readAndAddCleanUp(resultMutableBox, resultType,
4103                            "unexpected result for UNPACK");
4104 }
4105 
4106 // VERIFY
4107 fir::ExtendedValue
4108 IntrinsicLibrary::genVerify(mlir::Type resultType,
4109                             llvm::ArrayRef<fir::ExtendedValue> args) {
4110 
4111   assert(args.size() == 4);
4112 
4113   if (isStaticallyAbsent(args[3])) {
4114     // Kind not specified, so call scan/verify runtime routine that is
4115     // specialized on the kind of characters in string.
4116 
4117     // Handle required string base arg
4118     mlir::Value stringBase = fir::getBase(args[0]);
4119 
4120     // Handle required set string base arg
4121     mlir::Value setBase = fir::getBase(args[1]);
4122 
4123     // Handle kind argument; it is the kind of character in this case
4124     fir::KindTy kind =
4125         fir::factory::CharacterExprHelper{builder, loc}.getCharacterKind(
4126             stringBase.getType());
4127 
4128     // Get string length argument
4129     mlir::Value stringLen = fir::getLen(args[0]);
4130 
4131     // Get set string length argument
4132     mlir::Value setLen = fir::getLen(args[1]);
4133 
4134     // Handle optional back argument
4135     mlir::Value back =
4136         isStaticallyAbsent(args[2])
4137             ? builder.createIntegerConstant(loc, builder.getI1Type(), 0)
4138             : fir::getBase(args[2]);
4139 
4140     return builder.createConvert(
4141         loc, resultType,
4142         fir::runtime::genVerify(builder, loc, kind, stringBase, stringLen,
4143                                 setBase, setLen, back));
4144   }
4145   // else use the runtime descriptor version of scan/verify
4146 
4147   // Handle optional argument, back
4148   auto makeRefThenEmbox = [&](mlir::Value b) {
4149     fir::LogicalType logTy = fir::LogicalType::get(
4150         builder.getContext(), builder.getKindMap().defaultLogicalKind());
4151     mlir::Value temp = builder.createTemporary(loc, logTy);
4152     mlir::Value castb = builder.createConvert(loc, logTy, b);
4153     builder.create<fir::StoreOp>(loc, castb, temp);
4154     return builder.createBox(loc, temp);
4155   };
4156   mlir::Value back = fir::isUnboxedValue(args[2])
4157                          ? makeRefThenEmbox(*args[2].getUnboxed())
4158                          : builder.create<fir::AbsentOp>(
4159                                loc, fir::BoxType::get(builder.getI1Type()));
4160 
4161   // Handle required string argument
4162   mlir::Value string = builder.createBox(loc, args[0]);
4163 
4164   // Handle required set argument
4165   mlir::Value set = builder.createBox(loc, args[1]);
4166 
4167   // Handle kind argument
4168   mlir::Value kind = fir::getBase(args[3]);
4169 
4170   // Create result descriptor
4171   fir::MutableBoxValue resultMutableBox =
4172       fir::factory::createTempMutableBox(builder, loc, resultType);
4173   mlir::Value resultIrBox =
4174       fir::factory::getMutableIRBox(builder, loc, resultMutableBox);
4175 
4176   fir::runtime::genVerifyDescriptor(builder, loc, resultIrBox, string, set,
4177                                     back, kind);
4178 
4179   // Handle cleanup of allocatable result descriptor and return
4180   return readAndAddCleanUp(resultMutableBox, resultType, "VERIFY");
4181 }
4182 
4183 // MAXLOC
4184 fir::ExtendedValue
4185 IntrinsicLibrary::genMaxloc(mlir::Type resultType,
4186                             llvm::ArrayRef<fir::ExtendedValue> args) {
4187   return genExtremumloc(fir::runtime::genMaxloc, fir::runtime::genMaxlocDim,
4188                         resultType, builder, loc, stmtCtx,
4189                         "unexpected result for Maxloc", args);
4190 }
4191 
4192 // MAXVAL
4193 fir::ExtendedValue
4194 IntrinsicLibrary::genMaxval(mlir::Type resultType,
4195                             llvm::ArrayRef<fir::ExtendedValue> args) {
4196   return genExtremumVal(fir::runtime::genMaxval, fir::runtime::genMaxvalDim,
4197                         fir::runtime::genMaxvalChar, resultType, builder, loc,
4198                         stmtCtx, "unexpected result for Maxval", args);
4199 }
4200 
4201 // MINLOC
4202 fir::ExtendedValue
4203 IntrinsicLibrary::genMinloc(mlir::Type resultType,
4204                             llvm::ArrayRef<fir::ExtendedValue> args) {
4205   return genExtremumloc(fir::runtime::genMinloc, fir::runtime::genMinlocDim,
4206                         resultType, builder, loc, stmtCtx,
4207                         "unexpected result for Minloc", args);
4208 }
4209 
4210 // MINVAL
4211 fir::ExtendedValue
4212 IntrinsicLibrary::genMinval(mlir::Type resultType,
4213                             llvm::ArrayRef<fir::ExtendedValue> args) {
4214   return genExtremumVal(fir::runtime::genMinval, fir::runtime::genMinvalDim,
4215                         fir::runtime::genMinvalChar, resultType, builder, loc,
4216                         stmtCtx, "unexpected result for Minval", args);
4217 }
4218 
4219 // MIN and MAX
4220 template <Extremum extremum, ExtremumBehavior behavior>
4221 mlir::Value IntrinsicLibrary::genExtremum(mlir::Type,
4222                                           llvm::ArrayRef<mlir::Value> args) {
4223   assert(args.size() >= 1);
4224   mlir::Value result = args[0];
4225   for (auto arg : args.drop_front()) {
4226     mlir::Value mask =
4227         createExtremumCompare<extremum, behavior>(loc, builder, result, arg);
4228     result = builder.create<mlir::arith::SelectOp>(loc, mask, result, arg);
4229   }
4230   return result;
4231 }
4232 
4233 //===----------------------------------------------------------------------===//
4234 // Argument lowering rules interface
4235 //===----------------------------------------------------------------------===//
4236 
4237 const Fortran::lower::IntrinsicArgumentLoweringRules *
4238 Fortran::lower::getIntrinsicArgumentLowering(llvm::StringRef intrinsicName) {
4239   if (const IntrinsicHandler *handler = findIntrinsicHandler(intrinsicName))
4240     if (!handler->argLoweringRules.hasDefaultRules())
4241       return &handler->argLoweringRules;
4242   return nullptr;
4243 }
4244 
4245 /// Return how argument \p argName should be lowered given the rules for the
4246 /// intrinsic function.
4247 Fortran::lower::ArgLoweringRule Fortran::lower::lowerIntrinsicArgumentAs(
4248     const IntrinsicArgumentLoweringRules &rules, unsigned position) {
4249   assert(position < sizeof(rules.args) / sizeof(decltype(*rules.args)) &&
4250          "invalid argument");
4251   return {rules.args[position].lowerAs,
4252           rules.args[position].handleDynamicOptional};
4253 }
4254 
4255 //===----------------------------------------------------------------------===//
4256 // Public intrinsic call helpers
4257 //===----------------------------------------------------------------------===//
4258 
4259 fir::ExtendedValue
4260 Fortran::lower::genIntrinsicCall(fir::FirOpBuilder &builder, mlir::Location loc,
4261                                  llvm::StringRef name,
4262                                  llvm::Optional<mlir::Type> resultType,
4263                                  llvm::ArrayRef<fir::ExtendedValue> args,
4264                                  Fortran::lower::StatementContext &stmtCtx) {
4265   return IntrinsicLibrary{builder, loc, &stmtCtx}.genIntrinsicCall(
4266       name, resultType, args);
4267 }
4268 
4269 mlir::Value Fortran::lower::genMax(fir::FirOpBuilder &builder,
4270                                    mlir::Location loc,
4271                                    llvm::ArrayRef<mlir::Value> args) {
4272   assert(args.size() > 0 && "max requires at least one argument");
4273   return IntrinsicLibrary{builder, loc}
4274       .genExtremum<Extremum::Max, ExtremumBehavior::MinMaxss>(args[0].getType(),
4275                                                               args);
4276 }
4277 
4278 mlir::Value Fortran::lower::genMin(fir::FirOpBuilder &builder,
4279                                    mlir::Location loc,
4280                                    llvm::ArrayRef<mlir::Value> args) {
4281   assert(args.size() > 0 && "min requires at least one argument");
4282   return IntrinsicLibrary{builder, loc}
4283       .genExtremum<Extremum::Min, ExtremumBehavior::MinMaxss>(args[0].getType(),
4284                                                               args);
4285 }
4286 
4287 mlir::Value Fortran::lower::genPow(fir::FirOpBuilder &builder,
4288                                    mlir::Location loc, mlir::Type type,
4289                                    mlir::Value x, mlir::Value y) {
4290   // TODO: since there is no libm version of pow with integer exponent,
4291   //       we have to provide an alternative implementation for
4292   //       "precise/strict" FP mode and (!lowerEarlyToLibCall).
4293   //       One option is to generate internal function with inlined
4294   //       implementation and mark it 'strictfp'.
4295   //       Another option is to implement it in Fortran runtime library
4296   //       (just like matmul).
4297   return IntrinsicLibrary{builder, loc}.genRuntimeCall("pow", type, {x, y});
4298 }
4299 
4300 mlir::SymbolRefAttr Fortran::lower::getUnrestrictedIntrinsicSymbolRefAttr(
4301     fir::FirOpBuilder &builder, mlir::Location loc, llvm::StringRef name,
4302     mlir::FunctionType signature) {
4303   return IntrinsicLibrary{builder, loc}.getUnrestrictedIntrinsicSymbolRefAttr(
4304       name, signature);
4305 }
4306