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