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