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     auto buffTy = buffer.getType();
1408     if (buffTy.isa<mlir::FunctionType>())
1409       fir::emitFatalError(
1410           loc, "A character's buffer type cannot be a function type.");
1411     if (buffTy.isa<fir::BoxCharType>())
1412       return buffer;
1413     return fir::factory::CharacterExprHelper{builder, loc}.createEmboxChar(
1414         buffer, charBox->getLen());
1415   }
1416 
1417   // FIXME: need to access other ExtendedValue variants and handle them
1418   // properly.
1419   return fir::getBase(val);
1420 }
1421 
1422 //===----------------------------------------------------------------------===//
1423 // IntrinsicLibrary
1424 //===----------------------------------------------------------------------===//
1425 
1426 static bool isIntrinsicModuleProcedure(llvm::StringRef name) {
1427   return name.startswith("c_") || name.startswith("compiler_") ||
1428          name.startswith("ieee_");
1429 }
1430 
1431 /// Return the generic name of an intrinsic module procedure specific name.
1432 /// Remove any "__builtin_" prefix, and any specific suffix of the form
1433 /// {_[ail]?[0-9]+}*, such as _1 or _a4.
1434 llvm::StringRef genericName(llvm::StringRef specificName) {
1435   const std::string builtin = "__builtin_";
1436   llvm::StringRef name = specificName.startswith(builtin)
1437                              ? specificName.drop_front(builtin.size())
1438                              : specificName;
1439   size_t size = name.size();
1440   if (isIntrinsicModuleProcedure(name))
1441     while (isdigit(name[size - 1]))
1442       while (name[--size] != '_')
1443         ;
1444   return name.drop_back(name.size() - size);
1445 }
1446 
1447 /// Generate a TODO error message for an as yet unimplemented intrinsic.
1448 void crashOnMissingIntrinsic(mlir::Location loc, llvm::StringRef name) {
1449   if (isIntrinsicModuleProcedure(name))
1450     TODO(loc, "intrinsic module procedure: " + llvm::Twine(name));
1451   else
1452     TODO(loc, "intrinsic: " + llvm::Twine(name));
1453 }
1454 
1455 template <typename GeneratorType>
1456 fir::ExtendedValue IntrinsicLibrary::genElementalCall(
1457     GeneratorType generator, llvm::StringRef name, mlir::Type resultType,
1458     llvm::ArrayRef<fir::ExtendedValue> args, bool outline) {
1459   llvm::SmallVector<mlir::Value> scalarArgs;
1460   for (const fir::ExtendedValue &arg : args)
1461     if (arg.getUnboxed() || arg.getCharBox())
1462       scalarArgs.emplace_back(fir::getBase(arg));
1463     else
1464       fir::emitFatalError(loc, "nonscalar intrinsic argument");
1465   if (outline)
1466     return outlineInWrapper(generator, name, resultType, scalarArgs);
1467   return invokeGenerator(generator, resultType, scalarArgs);
1468 }
1469 
1470 template <>
1471 fir::ExtendedValue
1472 IntrinsicLibrary::genElementalCall<IntrinsicLibrary::ExtendedGenerator>(
1473     ExtendedGenerator generator, llvm::StringRef name, mlir::Type resultType,
1474     llvm::ArrayRef<fir::ExtendedValue> args, bool outline) {
1475   for (const fir::ExtendedValue &arg : args)
1476     if (!arg.getUnboxed() && !arg.getCharBox())
1477       fir::emitFatalError(loc, "nonscalar intrinsic argument");
1478   if (outline)
1479     return outlineInExtendedWrapper(generator, name, resultType, args);
1480   return std::invoke(generator, *this, resultType, args);
1481 }
1482 
1483 template <>
1484 fir::ExtendedValue
1485 IntrinsicLibrary::genElementalCall<IntrinsicLibrary::SubroutineGenerator>(
1486     SubroutineGenerator generator, llvm::StringRef name, mlir::Type resultType,
1487     llvm::ArrayRef<fir::ExtendedValue> args, bool outline) {
1488   for (const fir::ExtendedValue &arg : args)
1489     if (!arg.getUnboxed() && !arg.getCharBox())
1490       // fir::emitFatalError(loc, "nonscalar intrinsic argument");
1491       crashOnMissingIntrinsic(loc, name);
1492   if (outline)
1493     return outlineInExtendedWrapper(generator, name, resultType, args);
1494   std::invoke(generator, *this, args);
1495   return mlir::Value();
1496 }
1497 
1498 static fir::ExtendedValue
1499 invokeHandler(IntrinsicLibrary::ElementalGenerator generator,
1500               const IntrinsicHandler &handler,
1501               llvm::Optional<mlir::Type> resultType,
1502               llvm::ArrayRef<fir::ExtendedValue> args, bool outline,
1503               IntrinsicLibrary &lib) {
1504   assert(resultType && "expect elemental intrinsic to be functions");
1505   return lib.genElementalCall(generator, handler.name, *resultType, args,
1506                               outline);
1507 }
1508 
1509 static fir::ExtendedValue
1510 invokeHandler(IntrinsicLibrary::ExtendedGenerator generator,
1511               const IntrinsicHandler &handler,
1512               llvm::Optional<mlir::Type> resultType,
1513               llvm::ArrayRef<fir::ExtendedValue> args, bool outline,
1514               IntrinsicLibrary &lib) {
1515   assert(resultType && "expect intrinsic function");
1516   if (handler.isElemental)
1517     return lib.genElementalCall(generator, handler.name, *resultType, args,
1518                                 outline);
1519   if (outline)
1520     return lib.outlineInExtendedWrapper(generator, handler.name, *resultType,
1521                                         args);
1522   return std::invoke(generator, lib, *resultType, args);
1523 }
1524 
1525 static fir::ExtendedValue
1526 invokeHandler(IntrinsicLibrary::SubroutineGenerator generator,
1527               const IntrinsicHandler &handler,
1528               llvm::Optional<mlir::Type> resultType,
1529               llvm::ArrayRef<fir::ExtendedValue> args, bool outline,
1530               IntrinsicLibrary &lib) {
1531   if (handler.isElemental)
1532     return lib.genElementalCall(generator, handler.name, mlir::Type{}, args,
1533                                 outline);
1534   if (outline)
1535     return lib.outlineInExtendedWrapper(generator, handler.name, resultType,
1536                                         args);
1537   std::invoke(generator, lib, args);
1538   return mlir::Value{};
1539 }
1540 
1541 fir::ExtendedValue
1542 IntrinsicLibrary::genIntrinsicCall(llvm::StringRef specificName,
1543                                    llvm::Optional<mlir::Type> resultType,
1544                                    llvm::ArrayRef<fir::ExtendedValue> args) {
1545   llvm::StringRef name = genericName(specificName);
1546   if (const IntrinsicHandler *handler = findIntrinsicHandler(name)) {
1547     bool outline = handler->outline || outlineAllIntrinsics;
1548     return std::visit(
1549         [&](auto &generator) -> fir::ExtendedValue {
1550           return invokeHandler(generator, *handler, resultType, args, outline,
1551                                *this);
1552         },
1553         handler->generator);
1554   }
1555 
1556   if (!resultType)
1557     // Subroutine should have a handler, they are likely missing for now.
1558     crashOnMissingIntrinsic(loc, name);
1559 
1560   // Try the runtime if no special handler was defined for the
1561   // intrinsic being called. Maths runtime only has numerical elemental.
1562   // No optional arguments are expected at this point, the code will
1563   // crash if it gets absent optional.
1564 
1565   // FIXME: using toValue to get the type won't work with array arguments.
1566   llvm::SmallVector<mlir::Value> mlirArgs;
1567   for (const fir::ExtendedValue &extendedVal : args) {
1568     mlir::Value val = toValue(extendedVal, builder, loc);
1569     if (!val)
1570       // If an absent optional gets there, most likely its handler has just
1571       // not yet been defined.
1572       crashOnMissingIntrinsic(loc, name);
1573     mlirArgs.emplace_back(val);
1574   }
1575   mlir::FunctionType soughtFuncType =
1576       getFunctionType(*resultType, mlirArgs, builder);
1577 
1578   IntrinsicLibrary::RuntimeCallGenerator runtimeCallGenerator =
1579       getRuntimeCallGenerator(name, soughtFuncType);
1580   return genElementalCall(runtimeCallGenerator, name, *resultType, args,
1581                           /* outline */ true);
1582 }
1583 
1584 mlir::Value
1585 IntrinsicLibrary::invokeGenerator(ElementalGenerator generator,
1586                                   mlir::Type resultType,
1587                                   llvm::ArrayRef<mlir::Value> args) {
1588   return std::invoke(generator, *this, resultType, args);
1589 }
1590 
1591 mlir::Value
1592 IntrinsicLibrary::invokeGenerator(RuntimeCallGenerator generator,
1593                                   mlir::Type resultType,
1594                                   llvm::ArrayRef<mlir::Value> args) {
1595   return generator(builder, loc, args);
1596 }
1597 
1598 mlir::Value
1599 IntrinsicLibrary::invokeGenerator(ExtendedGenerator generator,
1600                                   mlir::Type resultType,
1601                                   llvm::ArrayRef<mlir::Value> args) {
1602   llvm::SmallVector<fir::ExtendedValue> extendedArgs;
1603   for (mlir::Value arg : args)
1604     extendedArgs.emplace_back(toExtendedValue(arg, builder, loc));
1605   auto extendedResult = std::invoke(generator, *this, resultType, extendedArgs);
1606   return toValue(extendedResult, builder, loc);
1607 }
1608 
1609 mlir::Value
1610 IntrinsicLibrary::invokeGenerator(SubroutineGenerator generator,
1611                                   llvm::ArrayRef<mlir::Value> args) {
1612   llvm::SmallVector<fir::ExtendedValue> extendedArgs;
1613   for (mlir::Value arg : args)
1614     extendedArgs.emplace_back(toExtendedValue(arg, builder, loc));
1615   std::invoke(generator, *this, extendedArgs);
1616   return {};
1617 }
1618 
1619 template <typename GeneratorType>
1620 mlir::func::FuncOp IntrinsicLibrary::getWrapper(GeneratorType generator,
1621                                                 llvm::StringRef name,
1622                                                 mlir::FunctionType funcType,
1623                                                 bool loadRefArguments) {
1624   std::string wrapperName = fir::mangleIntrinsicProcedure(name, funcType);
1625   mlir::func::FuncOp function = builder.getNamedFunction(wrapperName);
1626   if (!function) {
1627     // First time this wrapper is needed, build it.
1628     function = builder.createFunction(loc, wrapperName, funcType);
1629     function->setAttr("fir.intrinsic", builder.getUnitAttr());
1630     auto internalLinkage = mlir::LLVM::linkage::Linkage::Internal;
1631     auto linkage =
1632         mlir::LLVM::LinkageAttr::get(builder.getContext(), internalLinkage);
1633     function->setAttr("llvm.linkage", linkage);
1634     function.addEntryBlock();
1635 
1636     // Create local context to emit code into the newly created function
1637     // This new function is not linked to a source file location, only
1638     // its calls will be.
1639     auto localBuilder =
1640         std::make_unique<fir::FirOpBuilder>(function, builder.getKindMap());
1641     localBuilder->setInsertionPointToStart(&function.front());
1642     // Location of code inside wrapper of the wrapper is independent from
1643     // the location of the intrinsic call.
1644     mlir::Location localLoc = localBuilder->getUnknownLoc();
1645     llvm::SmallVector<mlir::Value> localArguments;
1646     for (mlir::BlockArgument bArg : function.front().getArguments()) {
1647       auto refType = bArg.getType().dyn_cast<fir::ReferenceType>();
1648       if (loadRefArguments && refType) {
1649         auto loaded = localBuilder->create<fir::LoadOp>(localLoc, bArg);
1650         localArguments.push_back(loaded);
1651       } else {
1652         localArguments.push_back(bArg);
1653       }
1654     }
1655 
1656     IntrinsicLibrary localLib{*localBuilder, localLoc};
1657 
1658     if constexpr (std::is_same_v<GeneratorType, SubroutineGenerator>) {
1659       localLib.invokeGenerator(generator, localArguments);
1660       localBuilder->create<mlir::func::ReturnOp>(localLoc);
1661     } else {
1662       assert(funcType.getNumResults() == 1 &&
1663              "expect one result for intrinsic function wrapper type");
1664       mlir::Type resultType = funcType.getResult(0);
1665       auto result =
1666           localLib.invokeGenerator(generator, resultType, localArguments);
1667       localBuilder->create<mlir::func::ReturnOp>(localLoc, result);
1668     }
1669   } else {
1670     // Wrapper was already built, ensure it has the sought type
1671     assert(function.getFunctionType() == funcType &&
1672            "conflict between intrinsic wrapper types");
1673   }
1674   return function;
1675 }
1676 
1677 /// Helpers to detect absent optional (not yet supported in outlining).
1678 bool static hasAbsentOptional(llvm::ArrayRef<mlir::Value> args) {
1679   for (const mlir::Value &arg : args)
1680     if (!arg)
1681       return true;
1682   return false;
1683 }
1684 bool static hasAbsentOptional(llvm::ArrayRef<fir::ExtendedValue> args) {
1685   for (const fir::ExtendedValue &arg : args)
1686     if (!fir::getBase(arg))
1687       return true;
1688   return false;
1689 }
1690 
1691 template <typename GeneratorType>
1692 mlir::Value
1693 IntrinsicLibrary::outlineInWrapper(GeneratorType generator,
1694                                    llvm::StringRef name, mlir::Type resultType,
1695                                    llvm::ArrayRef<mlir::Value> args) {
1696   if (hasAbsentOptional(args)) {
1697     // TODO: absent optional in outlining is an issue: we cannot just ignore
1698     // them. Needs a better interface here. The issue is that we cannot easily
1699     // tell that a value is optional or not here if it is presents. And if it is
1700     // absent, we cannot tell what it type should be.
1701     TODO(loc, "cannot outline call to intrinsic " + llvm::Twine(name) +
1702                   " with absent optional argument");
1703   }
1704 
1705   mlir::FunctionType funcType = getFunctionType(resultType, args, builder);
1706   mlir::func::FuncOp wrapper = getWrapper(generator, name, funcType);
1707   return builder.create<fir::CallOp>(loc, wrapper, args).getResult(0);
1708 }
1709 
1710 template <typename GeneratorType>
1711 fir::ExtendedValue IntrinsicLibrary::outlineInExtendedWrapper(
1712     GeneratorType generator, llvm::StringRef name,
1713     llvm::Optional<mlir::Type> resultType,
1714     llvm::ArrayRef<fir::ExtendedValue> args) {
1715   if (hasAbsentOptional(args))
1716     TODO(loc, "cannot outline call to intrinsic " + llvm::Twine(name) +
1717                   " with absent optional argument");
1718   llvm::SmallVector<mlir::Value> mlirArgs;
1719   for (const auto &extendedVal : args)
1720     mlirArgs.emplace_back(toValue(extendedVal, builder, loc));
1721   mlir::FunctionType funcType = getFunctionType(resultType, mlirArgs, builder);
1722   mlir::func::FuncOp wrapper = getWrapper(generator, name, funcType);
1723   auto call = builder.create<fir::CallOp>(loc, wrapper, mlirArgs);
1724   if (resultType)
1725     return toExtendedValue(call.getResult(0), builder, loc);
1726   // Subroutine calls
1727   return mlir::Value{};
1728 }
1729 
1730 IntrinsicLibrary::RuntimeCallGenerator
1731 IntrinsicLibrary::getRuntimeCallGenerator(llvm::StringRef name,
1732                                           mlir::FunctionType soughtFuncType) {
1733   mlir::func::FuncOp funcOp =
1734       getRuntimeFunction(loc, builder, name, soughtFuncType);
1735   if (!funcOp) {
1736     std::string nameAndType;
1737     llvm::raw_string_ostream sstream(nameAndType);
1738     sstream << name << "\nrequested type: " << soughtFuncType;
1739     crashOnMissingIntrinsic(loc, nameAndType);
1740   }
1741 
1742   mlir::FunctionType actualFuncType = funcOp.getFunctionType();
1743   assert(actualFuncType.getNumResults() == soughtFuncType.getNumResults() &&
1744          actualFuncType.getNumInputs() == soughtFuncType.getNumInputs() &&
1745          actualFuncType.getNumResults() == 1 && "Bad intrinsic match");
1746 
1747   return [funcOp, actualFuncType,
1748           soughtFuncType](fir::FirOpBuilder &builder, mlir::Location loc,
1749                           llvm::ArrayRef<mlir::Value> args) {
1750     llvm::SmallVector<mlir::Value> convertedArguments;
1751     for (auto [fst, snd] : llvm::zip(actualFuncType.getInputs(), args))
1752       convertedArguments.push_back(builder.createConvert(loc, fst, snd));
1753     auto call = builder.create<fir::CallOp>(loc, funcOp, convertedArguments);
1754     mlir::Type soughtType = soughtFuncType.getResult(0);
1755     return builder.createConvert(loc, soughtType, call.getResult(0));
1756   };
1757 }
1758 
1759 mlir::SymbolRefAttr IntrinsicLibrary::getUnrestrictedIntrinsicSymbolRefAttr(
1760     llvm::StringRef name, mlir::FunctionType signature) {
1761   // Unrestricted intrinsics signature follows implicit rules: argument
1762   // are passed by references. But the runtime versions expect values.
1763   // So instead of duplicating the runtime, just have the wrappers loading
1764   // this before calling the code generators.
1765   bool loadRefArguments = true;
1766   mlir::func::FuncOp funcOp;
1767   if (const IntrinsicHandler *handler = findIntrinsicHandler(name))
1768     funcOp = std::visit(
1769         [&](auto generator) {
1770           return getWrapper(generator, name, signature, loadRefArguments);
1771         },
1772         handler->generator);
1773 
1774   if (!funcOp) {
1775     llvm::SmallVector<mlir::Type> argTypes;
1776     for (mlir::Type type : signature.getInputs()) {
1777       if (auto refType = type.dyn_cast<fir::ReferenceType>())
1778         argTypes.push_back(refType.getEleTy());
1779       else
1780         argTypes.push_back(type);
1781     }
1782     mlir::FunctionType soughtFuncType =
1783         builder.getFunctionType(argTypes, signature.getResults());
1784     IntrinsicLibrary::RuntimeCallGenerator rtCallGenerator =
1785         getRuntimeCallGenerator(name, soughtFuncType);
1786     funcOp = getWrapper(rtCallGenerator, name, signature, loadRefArguments);
1787   }
1788 
1789   return mlir::SymbolRefAttr::get(funcOp);
1790 }
1791 
1792 void IntrinsicLibrary::addCleanUpForTemp(mlir::Location loc, mlir::Value temp) {
1793   assert(stmtCtx);
1794   fir::FirOpBuilder *bldr = &builder;
1795   stmtCtx->attachCleanup([=]() { bldr->create<fir::FreeMemOp>(loc, temp); });
1796 }
1797 
1798 fir::ExtendedValue
1799 IntrinsicLibrary::readAndAddCleanUp(fir::MutableBoxValue resultMutableBox,
1800                                     mlir::Type resultType,
1801                                     llvm::StringRef intrinsicName) {
1802   fir::ExtendedValue res =
1803       fir::factory::genMutableBoxRead(builder, loc, resultMutableBox);
1804   return res.match(
1805       [&](const fir::ArrayBoxValue &box) -> fir::ExtendedValue {
1806         // Add cleanup code
1807         addCleanUpForTemp(loc, box.getAddr());
1808         return box;
1809       },
1810       [&](const fir::BoxValue &box) -> fir::ExtendedValue {
1811         // Add cleanup code
1812         auto addr =
1813             builder.create<fir::BoxAddrOp>(loc, box.getMemTy(), box.getAddr());
1814         addCleanUpForTemp(loc, addr);
1815         return box;
1816       },
1817       [&](const fir::CharArrayBoxValue &box) -> fir::ExtendedValue {
1818         // Add cleanup code
1819         addCleanUpForTemp(loc, box.getAddr());
1820         return box;
1821       },
1822       [&](const mlir::Value &tempAddr) -> fir::ExtendedValue {
1823         // Add cleanup code
1824         addCleanUpForTemp(loc, tempAddr);
1825         return builder.create<fir::LoadOp>(loc, resultType, tempAddr);
1826       },
1827       [&](const fir::CharBoxValue &box) -> fir::ExtendedValue {
1828         // Add cleanup code
1829         addCleanUpForTemp(loc, box.getAddr());
1830         return box;
1831       },
1832       [&](const auto &) -> fir::ExtendedValue {
1833         fir::emitFatalError(loc, "unexpected result for " + intrinsicName);
1834       });
1835 }
1836 
1837 //===----------------------------------------------------------------------===//
1838 // Code generators for the intrinsic
1839 //===----------------------------------------------------------------------===//
1840 
1841 mlir::Value IntrinsicLibrary::genRuntimeCall(llvm::StringRef name,
1842                                              mlir::Type resultType,
1843                                              llvm::ArrayRef<mlir::Value> args) {
1844   mlir::FunctionType soughtFuncType =
1845       getFunctionType(resultType, args, builder);
1846   return getRuntimeCallGenerator(name, soughtFuncType)(builder, loc, args);
1847 }
1848 
1849 mlir::Value IntrinsicLibrary::genConversion(mlir::Type resultType,
1850                                             llvm::ArrayRef<mlir::Value> args) {
1851   // There can be an optional kind in second argument.
1852   assert(args.size() >= 1);
1853   return builder.convertWithSemantics(loc, resultType, args[0]);
1854 }
1855 
1856 // ABS
1857 mlir::Value IntrinsicLibrary::genAbs(mlir::Type resultType,
1858                                      llvm::ArrayRef<mlir::Value> args) {
1859   assert(args.size() == 1);
1860   mlir::Value arg = args[0];
1861   mlir::Type type = arg.getType();
1862   if (fir::isa_real(type)) {
1863     // Runtime call to fp abs. An alternative would be to use mlir
1864     // math::AbsFOp but it does not support all fir floating point types.
1865     return genRuntimeCall("abs", resultType, args);
1866   }
1867   if (auto intType = type.dyn_cast<mlir::IntegerType>()) {
1868     // At the time of this implementation there is no abs op in mlir.
1869     // So, implement abs here without branching.
1870     mlir::Value shift =
1871         builder.createIntegerConstant(loc, intType, intType.getWidth() - 1);
1872     auto mask = builder.create<mlir::arith::ShRSIOp>(loc, arg, shift);
1873     auto xored = builder.create<mlir::arith::XOrIOp>(loc, arg, mask);
1874     return builder.create<mlir::arith::SubIOp>(loc, xored, mask);
1875   }
1876   if (fir::isa_complex(type)) {
1877     // Use HYPOT to fulfill the no underflow/overflow requirement.
1878     auto parts = fir::factory::Complex{builder, loc}.extractParts(arg);
1879     llvm::SmallVector<mlir::Value> args = {parts.first, parts.second};
1880     return genRuntimeCall("hypot", resultType, args);
1881   }
1882   llvm_unreachable("unexpected type in ABS argument");
1883 }
1884 
1885 // ADJUSTL & ADJUSTR
1886 template <void (*CallRuntime)(fir::FirOpBuilder &, mlir::Location loc,
1887                               mlir::Value, mlir::Value)>
1888 fir::ExtendedValue
1889 IntrinsicLibrary::genAdjustRtCall(mlir::Type resultType,
1890                                   llvm::ArrayRef<fir::ExtendedValue> args) {
1891   assert(args.size() == 1);
1892   mlir::Value string = builder.createBox(loc, args[0]);
1893   // Create a mutable fir.box to be passed to the runtime for the result.
1894   fir::MutableBoxValue resultMutableBox =
1895       fir::factory::createTempMutableBox(builder, loc, resultType);
1896   mlir::Value resultIrBox =
1897       fir::factory::getMutableIRBox(builder, loc, resultMutableBox);
1898 
1899   // Call the runtime -- the runtime will allocate the result.
1900   CallRuntime(builder, loc, resultIrBox, string);
1901 
1902   // Read result from mutable fir.box and add it to the list of temps to be
1903   // finalized by the StatementContext.
1904   fir::ExtendedValue res =
1905       fir::factory::genMutableBoxRead(builder, loc, resultMutableBox);
1906   return res.match(
1907       [&](const fir::CharBoxValue &box) -> fir::ExtendedValue {
1908         addCleanUpForTemp(loc, fir::getBase(box));
1909         return box;
1910       },
1911       [&](const auto &) -> fir::ExtendedValue {
1912         fir::emitFatalError(loc, "result of ADJUSTL is not a scalar character");
1913       });
1914 }
1915 
1916 // AIMAG
1917 mlir::Value IntrinsicLibrary::genAimag(mlir::Type resultType,
1918                                        llvm::ArrayRef<mlir::Value> args) {
1919   assert(args.size() == 1);
1920   return fir::factory::Complex{builder, loc}.extractComplexPart(
1921       args[0], true /* isImagPart */);
1922 }
1923 
1924 // AINT
1925 mlir::Value IntrinsicLibrary::genAint(mlir::Type resultType,
1926                                       llvm::ArrayRef<mlir::Value> args) {
1927   assert(args.size() >= 1 && args.size() <= 2);
1928   // Skip optional kind argument to search the runtime; it is already reflected
1929   // in result type.
1930   return genRuntimeCall("aint", resultType, {args[0]});
1931 }
1932 
1933 // ALL
1934 fir::ExtendedValue
1935 IntrinsicLibrary::genAll(mlir::Type resultType,
1936                          llvm::ArrayRef<fir::ExtendedValue> args) {
1937 
1938   assert(args.size() == 2);
1939   // Handle required mask argument
1940   mlir::Value mask = builder.createBox(loc, args[0]);
1941 
1942   fir::BoxValue maskArry = builder.createBox(loc, args[0]);
1943   int rank = maskArry.rank();
1944   assert(rank >= 1);
1945 
1946   // Handle optional dim argument
1947   bool absentDim = isStaticallyAbsent(args[1]);
1948   mlir::Value dim =
1949       absentDim ? builder.createIntegerConstant(loc, builder.getIndexType(), 1)
1950                 : fir::getBase(args[1]);
1951 
1952   if (rank == 1 || absentDim)
1953     return builder.createConvert(loc, resultType,
1954                                  fir::runtime::genAll(builder, loc, mask, dim));
1955 
1956   // else use the result descriptor AllDim() intrinsic
1957 
1958   // Create mutable fir.box to be passed to the runtime for the result.
1959 
1960   mlir::Type resultArrayType = builder.getVarLenSeqTy(resultType, rank - 1);
1961   fir::MutableBoxValue resultMutableBox =
1962       fir::factory::createTempMutableBox(builder, loc, resultArrayType);
1963   mlir::Value resultIrBox =
1964       fir::factory::getMutableIRBox(builder, loc, resultMutableBox);
1965 
1966   // Call runtime. The runtime is allocating the result.
1967   fir::runtime::genAllDescriptor(builder, loc, resultIrBox, mask, dim);
1968   return fir::factory::genMutableBoxRead(builder, loc, resultMutableBox)
1969       .match(
1970           [&](const fir::ArrayBoxValue &box) -> fir::ExtendedValue {
1971             addCleanUpForTemp(loc, box.getAddr());
1972             return box;
1973           },
1974           [&](const auto &) -> fir::ExtendedValue {
1975             fir::emitFatalError(loc, "Invalid result for ALL");
1976           });
1977 }
1978 
1979 // ALLOCATED
1980 fir::ExtendedValue
1981 IntrinsicLibrary::genAllocated(mlir::Type resultType,
1982                                llvm::ArrayRef<fir::ExtendedValue> args) {
1983   assert(args.size() == 1);
1984   return args[0].match(
1985       [&](const fir::MutableBoxValue &x) -> fir::ExtendedValue {
1986         return fir::factory::genIsAllocatedOrAssociatedTest(builder, loc, x);
1987       },
1988       [&](const auto &) -> fir::ExtendedValue {
1989         fir::emitFatalError(loc,
1990                             "allocated arg not lowered to MutableBoxValue");
1991       });
1992 }
1993 
1994 // ANINT
1995 mlir::Value IntrinsicLibrary::genAnint(mlir::Type resultType,
1996                                        llvm::ArrayRef<mlir::Value> args) {
1997   assert(args.size() >= 1 && args.size() <= 2);
1998   // Skip optional kind argument to search the runtime; it is already reflected
1999   // in result type.
2000   return genRuntimeCall("anint", resultType, {args[0]});
2001 }
2002 
2003 // ANY
2004 fir::ExtendedValue
2005 IntrinsicLibrary::genAny(mlir::Type resultType,
2006                          llvm::ArrayRef<fir::ExtendedValue> args) {
2007 
2008   assert(args.size() == 2);
2009   // Handle required mask argument
2010   mlir::Value mask = builder.createBox(loc, args[0]);
2011 
2012   fir::BoxValue maskArry = builder.createBox(loc, args[0]);
2013   int rank = maskArry.rank();
2014   assert(rank >= 1);
2015 
2016   // Handle optional dim argument
2017   bool absentDim = isStaticallyAbsent(args[1]);
2018   mlir::Value dim =
2019       absentDim ? builder.createIntegerConstant(loc, builder.getIndexType(), 1)
2020                 : fir::getBase(args[1]);
2021 
2022   if (rank == 1 || absentDim)
2023     return builder.createConvert(loc, resultType,
2024                                  fir::runtime::genAny(builder, loc, mask, dim));
2025 
2026   // else use the result descriptor AnyDim() intrinsic
2027 
2028   // Create mutable fir.box to be passed to the runtime for the result.
2029 
2030   mlir::Type resultArrayType = builder.getVarLenSeqTy(resultType, rank - 1);
2031   fir::MutableBoxValue resultMutableBox =
2032       fir::factory::createTempMutableBox(builder, loc, resultArrayType);
2033   mlir::Value resultIrBox =
2034       fir::factory::getMutableIRBox(builder, loc, resultMutableBox);
2035 
2036   // Call runtime. The runtime is allocating the result.
2037   fir::runtime::genAnyDescriptor(builder, loc, resultIrBox, mask, dim);
2038   return fir::factory::genMutableBoxRead(builder, loc, resultMutableBox)
2039       .match(
2040           [&](const fir::ArrayBoxValue &box) -> fir::ExtendedValue {
2041             addCleanUpForTemp(loc, box.getAddr());
2042             return box;
2043           },
2044           [&](const auto &) -> fir::ExtendedValue {
2045             fir::emitFatalError(loc, "Invalid result for ANY");
2046           });
2047 }
2048 
2049 // ASSOCIATED
2050 fir::ExtendedValue
2051 IntrinsicLibrary::genAssociated(mlir::Type resultType,
2052                                 llvm::ArrayRef<fir::ExtendedValue> args) {
2053   assert(args.size() == 2);
2054   auto *pointer =
2055       args[0].match([&](const fir::MutableBoxValue &x) { return &x; },
2056                     [&](const auto &) -> const fir::MutableBoxValue * {
2057                       fir::emitFatalError(loc, "pointer not a MutableBoxValue");
2058                     });
2059   const fir::ExtendedValue &target = args[1];
2060   if (isStaticallyAbsent(target))
2061     return fir::factory::genIsAllocatedOrAssociatedTest(builder, loc, *pointer);
2062 
2063   mlir::Value targetBox = builder.createBox(loc, target);
2064   if (fir::valueHasFirAttribute(fir::getBase(target),
2065                                 fir::getOptionalAttrName())) {
2066     // Subtle: contrary to other intrinsic optional arguments, disassociated
2067     // POINTER and unallocated ALLOCATABLE actual argument are not considered
2068     // absent here. This is because ASSOCIATED has special requirements for
2069     // TARGET actual arguments that are POINTERs. There is no precise
2070     // requirements for ALLOCATABLEs, but all existing Fortran compilers treat
2071     // them similarly to POINTERs. That is: unallocated TARGETs cause ASSOCIATED
2072     // to rerun false.  The runtime deals with the disassociated/unallocated
2073     // case. Simply ensures that TARGET that are OPTIONAL get conditionally
2074     // emboxed here to convey the optional aspect to the runtime.
2075     auto isPresent = builder.create<fir::IsPresentOp>(loc, builder.getI1Type(),
2076                                                       fir::getBase(target));
2077     auto absentBox = builder.create<fir::AbsentOp>(loc, targetBox.getType());
2078     targetBox = builder.create<mlir::arith::SelectOp>(loc, isPresent, targetBox,
2079                                                       absentBox);
2080   }
2081   mlir::Value pointerBoxRef =
2082       fir::factory::getMutableIRBox(builder, loc, *pointer);
2083   auto pointerBox = builder.create<fir::LoadOp>(loc, pointerBoxRef);
2084   return Fortran::lower::genAssociated(builder, loc, pointerBox, targetBox);
2085 }
2086 
2087 // BTEST
2088 mlir::Value IntrinsicLibrary::genBtest(mlir::Type resultType,
2089                                        llvm::ArrayRef<mlir::Value> args) {
2090   // A conformant BTEST(I,POS) call satisfies:
2091   //     POS >= 0
2092   //     POS < BIT_SIZE(I)
2093   // Return:  (I >> POS) & 1
2094   assert(args.size() == 2);
2095   mlir::Type argType = args[0].getType();
2096   mlir::Value pos = builder.createConvert(loc, argType, args[1]);
2097   auto shift = builder.create<mlir::arith::ShRUIOp>(loc, args[0], pos);
2098   mlir::Value one = builder.createIntegerConstant(loc, argType, 1);
2099   auto res = builder.create<mlir::arith::AndIOp>(loc, shift, one);
2100   return builder.createConvert(loc, resultType, res);
2101 }
2102 
2103 // CEILING
2104 mlir::Value IntrinsicLibrary::genCeiling(mlir::Type resultType,
2105                                          llvm::ArrayRef<mlir::Value> args) {
2106   // Optional KIND argument.
2107   assert(args.size() >= 1);
2108   mlir::Value arg = args[0];
2109   // Use ceil that is not an actual Fortran intrinsic but that is
2110   // an llvm intrinsic that does the same, but return a floating
2111   // point.
2112   mlir::Value ceil = genRuntimeCall("ceil", arg.getType(), {arg});
2113   return builder.createConvert(loc, resultType, ceil);
2114 }
2115 
2116 // CHAR
2117 fir::ExtendedValue
2118 IntrinsicLibrary::genChar(mlir::Type type,
2119                           llvm::ArrayRef<fir::ExtendedValue> args) {
2120   // Optional KIND argument.
2121   assert(args.size() >= 1);
2122   const mlir::Value *arg = args[0].getUnboxed();
2123   // expect argument to be a scalar integer
2124   if (!arg)
2125     mlir::emitError(loc, "CHAR intrinsic argument not unboxed");
2126   fir::factory::CharacterExprHelper helper{builder, loc};
2127   fir::CharacterType::KindTy kind = helper.getCharacterType(type).getFKind();
2128   mlir::Value cast = helper.createSingletonFromCode(*arg, kind);
2129   mlir::Value len =
2130       builder.createIntegerConstant(loc, builder.getCharacterLengthType(), 1);
2131   return fir::CharBoxValue{cast, len};
2132 }
2133 
2134 // CMPLX
2135 mlir::Value IntrinsicLibrary::genCmplx(mlir::Type resultType,
2136                                        llvm::ArrayRef<mlir::Value> args) {
2137   assert(args.size() >= 1);
2138   fir::factory::Complex complexHelper(builder, loc);
2139   mlir::Type partType = complexHelper.getComplexPartType(resultType);
2140   mlir::Value real = builder.createConvert(loc, partType, args[0]);
2141   mlir::Value imag = isStaticallyAbsent(args, 1)
2142                          ? builder.createRealZeroConstant(loc, partType)
2143                          : builder.createConvert(loc, partType, args[1]);
2144   return fir::factory::Complex{builder, loc}.createComplex(resultType, real,
2145                                                            imag);
2146 }
2147 
2148 // COMMAND_ARGUMENT_COUNT
2149 fir::ExtendedValue IntrinsicLibrary::genCommandArgumentCount(
2150     mlir::Type resultType, llvm::ArrayRef<fir::ExtendedValue> args) {
2151   assert(args.size() == 0);
2152   assert(resultType == builder.getDefaultIntegerType() &&
2153          "result type is not default integer kind type");
2154   return builder.createConvert(
2155       loc, resultType, fir::runtime::genCommandArgumentCount(builder, loc));
2156   ;
2157 }
2158 
2159 // CONJG
2160 mlir::Value IntrinsicLibrary::genConjg(mlir::Type resultType,
2161                                        llvm::ArrayRef<mlir::Value> args) {
2162   assert(args.size() == 1);
2163   if (resultType != args[0].getType())
2164     llvm_unreachable("argument type mismatch");
2165 
2166   mlir::Value cplx = args[0];
2167   auto imag = fir::factory::Complex{builder, loc}.extractComplexPart(
2168       cplx, /*isImagPart=*/true);
2169   auto negImag = builder.create<mlir::arith::NegFOp>(loc, imag);
2170   return fir::factory::Complex{builder, loc}.insertComplexPart(
2171       cplx, negImag, /*isImagPart=*/true);
2172 }
2173 
2174 // COUNT
2175 fir::ExtendedValue
2176 IntrinsicLibrary::genCount(mlir::Type resultType,
2177                            llvm::ArrayRef<fir::ExtendedValue> args) {
2178   assert(args.size() == 3);
2179 
2180   // Handle mask argument
2181   fir::BoxValue mask = builder.createBox(loc, args[0]);
2182   unsigned maskRank = mask.rank();
2183 
2184   assert(maskRank > 0);
2185 
2186   // Handle optional dim argument
2187   bool absentDim = isStaticallyAbsent(args[1]);
2188   mlir::Value dim =
2189       absentDim ? builder.createIntegerConstant(loc, builder.getIndexType(), 0)
2190                 : fir::getBase(args[1]);
2191 
2192   if (absentDim || maskRank == 1) {
2193     // Result is scalar if no dim argument or mask is rank 1.
2194     // So, call specialized Count runtime routine.
2195     return builder.createConvert(
2196         loc, resultType,
2197         fir::runtime::genCount(builder, loc, fir::getBase(mask), dim));
2198   }
2199 
2200   // Call general CountDim runtime routine.
2201 
2202   // Handle optional kind argument
2203   bool absentKind = isStaticallyAbsent(args[2]);
2204   mlir::Value kind = absentKind ? builder.createIntegerConstant(
2205                                       loc, builder.getIndexType(),
2206                                       builder.getKindMap().defaultIntegerKind())
2207                                 : fir::getBase(args[2]);
2208 
2209   // Create mutable fir.box to be passed to the runtime for the result.
2210   mlir::Type type = builder.getVarLenSeqTy(resultType, maskRank - 1);
2211   fir::MutableBoxValue resultMutableBox =
2212       fir::factory::createTempMutableBox(builder, loc, type);
2213 
2214   mlir::Value resultIrBox =
2215       fir::factory::getMutableIRBox(builder, loc, resultMutableBox);
2216 
2217   fir::runtime::genCountDim(builder, loc, resultIrBox, fir::getBase(mask), dim,
2218                             kind);
2219 
2220   // Handle cleanup of allocatable result descriptor and return
2221   fir::ExtendedValue res =
2222       fir::factory::genMutableBoxRead(builder, loc, resultMutableBox);
2223   return res.match(
2224       [&](const fir::ArrayBoxValue &box) -> fir::ExtendedValue {
2225         // Add cleanup code
2226         addCleanUpForTemp(loc, box.getAddr());
2227         return box;
2228       },
2229       [&](const auto &) -> fir::ExtendedValue {
2230         fir::emitFatalError(loc, "unexpected result for COUNT");
2231       });
2232 }
2233 
2234 // CPU_TIME
2235 void IntrinsicLibrary::genCpuTime(llvm::ArrayRef<fir::ExtendedValue> args) {
2236   assert(args.size() == 1);
2237   const mlir::Value *arg = args[0].getUnboxed();
2238   assert(arg && "nonscalar cpu_time argument");
2239   mlir::Value res1 = Fortran::lower::genCpuTime(builder, loc);
2240   mlir::Value res2 =
2241       builder.createConvert(loc, fir::dyn_cast_ptrEleTy(arg->getType()), res1);
2242   builder.create<fir::StoreOp>(loc, res2, *arg);
2243 }
2244 
2245 // CSHIFT
2246 fir::ExtendedValue
2247 IntrinsicLibrary::genCshift(mlir::Type resultType,
2248                             llvm::ArrayRef<fir::ExtendedValue> args) {
2249   assert(args.size() == 3);
2250 
2251   // Handle required ARRAY argument
2252   fir::BoxValue arrayBox = builder.createBox(loc, args[0]);
2253   mlir::Value array = fir::getBase(arrayBox);
2254   unsigned arrayRank = arrayBox.rank();
2255 
2256   // Create mutable fir.box to be passed to the runtime for the result.
2257   mlir::Type resultArrayType = builder.getVarLenSeqTy(resultType, arrayRank);
2258   fir::MutableBoxValue resultMutableBox =
2259       fir::factory::createTempMutableBox(builder, loc, resultArrayType);
2260   mlir::Value resultIrBox =
2261       fir::factory::getMutableIRBox(builder, loc, resultMutableBox);
2262 
2263   if (arrayRank == 1) {
2264     // Vector case
2265     // Handle required SHIFT argument as a scalar
2266     const mlir::Value *shiftAddr = args[1].getUnboxed();
2267     assert(shiftAddr && "nonscalar CSHIFT argument");
2268     auto shift = builder.create<fir::LoadOp>(loc, *shiftAddr);
2269 
2270     fir::runtime::genCshiftVector(builder, loc, resultIrBox, array, shift);
2271   } else {
2272     // Non-vector case
2273     // Handle required SHIFT argument as an array
2274     mlir::Value shift = builder.createBox(loc, args[1]);
2275 
2276     // Handle optional DIM argument
2277     mlir::Value dim =
2278         isStaticallyAbsent(args[2])
2279             ? builder.createIntegerConstant(loc, builder.getIndexType(), 1)
2280             : fir::getBase(args[2]);
2281     fir::runtime::genCshift(builder, loc, resultIrBox, array, shift, dim);
2282   }
2283   return readAndAddCleanUp(resultMutableBox, resultType, "CSHIFT");
2284 }
2285 
2286 // DATE_AND_TIME
2287 void IntrinsicLibrary::genDateAndTime(llvm::ArrayRef<fir::ExtendedValue> args) {
2288   assert(args.size() == 4 && "date_and_time has 4 args");
2289   llvm::SmallVector<llvm::Optional<fir::CharBoxValue>> charArgs(3);
2290   for (unsigned i = 0; i < 3; ++i)
2291     if (const fir::CharBoxValue *charBox = args[i].getCharBox())
2292       charArgs[i] = *charBox;
2293 
2294   mlir::Value values = fir::getBase(args[3]);
2295   if (!values)
2296     values = builder.create<fir::AbsentOp>(
2297         loc, fir::BoxType::get(builder.getNoneType()));
2298 
2299   Fortran::lower::genDateAndTime(builder, loc, charArgs[0], charArgs[1],
2300                                  charArgs[2], values);
2301 }
2302 
2303 // DIM
2304 mlir::Value IntrinsicLibrary::genDim(mlir::Type resultType,
2305                                      llvm::ArrayRef<mlir::Value> args) {
2306   assert(args.size() == 2);
2307   if (resultType.isa<mlir::IntegerType>()) {
2308     mlir::Value zero = builder.createIntegerConstant(loc, resultType, 0);
2309     auto diff = builder.create<mlir::arith::SubIOp>(loc, args[0], args[1]);
2310     auto cmp = builder.create<mlir::arith::CmpIOp>(
2311         loc, mlir::arith::CmpIPredicate::sgt, diff, zero);
2312     return builder.create<mlir::arith::SelectOp>(loc, cmp, diff, zero);
2313   }
2314   assert(fir::isa_real(resultType) && "Only expects real and integer in DIM");
2315   mlir::Value zero = builder.createRealZeroConstant(loc, resultType);
2316   auto diff = builder.create<mlir::arith::SubFOp>(loc, args[0], args[1]);
2317   auto cmp = builder.create<mlir::arith::CmpFOp>(
2318       loc, mlir::arith::CmpFPredicate::OGT, diff, zero);
2319   return builder.create<mlir::arith::SelectOp>(loc, cmp, diff, zero);
2320 }
2321 
2322 // DOT_PRODUCT
2323 fir::ExtendedValue
2324 IntrinsicLibrary::genDotProduct(mlir::Type resultType,
2325                                 llvm::ArrayRef<fir::ExtendedValue> args) {
2326   return genDotProd(fir::runtime::genDotProduct, resultType, builder, loc,
2327                     stmtCtx, args);
2328 }
2329 
2330 // DPROD
2331 mlir::Value IntrinsicLibrary::genDprod(mlir::Type resultType,
2332                                        llvm::ArrayRef<mlir::Value> args) {
2333   assert(args.size() == 2);
2334   assert(fir::isa_real(resultType) &&
2335          "Result must be double precision in DPROD");
2336   mlir::Value a = builder.createConvert(loc, resultType, args[0]);
2337   mlir::Value b = builder.createConvert(loc, resultType, args[1]);
2338   return builder.create<mlir::arith::MulFOp>(loc, a, b);
2339 }
2340 
2341 // EOSHIFT
2342 fir::ExtendedValue
2343 IntrinsicLibrary::genEoshift(mlir::Type resultType,
2344                              llvm::ArrayRef<fir::ExtendedValue> args) {
2345   assert(args.size() == 4);
2346 
2347   // Handle required ARRAY argument
2348   fir::BoxValue arrayBox = builder.createBox(loc, args[0]);
2349   mlir::Value array = fir::getBase(arrayBox);
2350   unsigned arrayRank = arrayBox.rank();
2351 
2352   // Create mutable fir.box to be passed to the runtime for the result.
2353   mlir::Type resultArrayType = builder.getVarLenSeqTy(resultType, arrayRank);
2354   fir::MutableBoxValue resultMutableBox =
2355       fir::factory::createTempMutableBox(builder, loc, resultArrayType);
2356   mlir::Value resultIrBox =
2357       fir::factory::getMutableIRBox(builder, loc, resultMutableBox);
2358 
2359   // Handle optional BOUNDARY argument
2360   mlir::Value boundary =
2361       isStaticallyAbsent(args[2])
2362           ? builder.create<fir::AbsentOp>(
2363                 loc, fir::BoxType::get(builder.getNoneType()))
2364           : builder.createBox(loc, args[2]);
2365 
2366   if (arrayRank == 1) {
2367     // Vector case
2368     // Handle required SHIFT argument as a scalar
2369     const mlir::Value *shiftAddr = args[1].getUnboxed();
2370     assert(shiftAddr && "nonscalar EOSHIFT SHIFT argument");
2371     auto shift = builder.create<fir::LoadOp>(loc, *shiftAddr);
2372     fir::runtime::genEoshiftVector(builder, loc, resultIrBox, array, shift,
2373                                    boundary);
2374   } else {
2375     // Non-vector case
2376     // Handle required SHIFT argument as an array
2377     mlir::Value shift = builder.createBox(loc, args[1]);
2378 
2379     // Handle optional DIM argument
2380     mlir::Value dim =
2381         isStaticallyAbsent(args[3])
2382             ? builder.createIntegerConstant(loc, builder.getIndexType(), 1)
2383             : fir::getBase(args[3]);
2384     fir::runtime::genEoshift(builder, loc, resultIrBox, array, shift, boundary,
2385                              dim);
2386   }
2387   return readAndAddCleanUp(resultMutableBox, resultType,
2388                            "unexpected result for EOSHIFT");
2389 }
2390 
2391 // EXIT
2392 void IntrinsicLibrary::genExit(llvm::ArrayRef<fir::ExtendedValue> args) {
2393   assert(args.size() == 1);
2394 
2395   mlir::Value status =
2396       isStaticallyAbsent(args[0])
2397           ? builder.createIntegerConstant(loc, builder.getDefaultIntegerType(),
2398                                           EXIT_SUCCESS)
2399           : fir::getBase(args[0]);
2400 
2401   assert(status.getType() == builder.getDefaultIntegerType() &&
2402          "STATUS parameter must be an INTEGER of default kind");
2403 
2404   fir::runtime::genExit(builder, loc, status);
2405 }
2406 
2407 // EXPONENT
2408 mlir::Value IntrinsicLibrary::genExponent(mlir::Type resultType,
2409                                           llvm::ArrayRef<mlir::Value> args) {
2410   assert(args.size() == 1);
2411 
2412   return builder.createConvert(
2413       loc, resultType,
2414       fir::runtime::genExponent(builder, loc, resultType,
2415                                 fir::getBase(args[0])));
2416 }
2417 
2418 // FLOOR
2419 mlir::Value IntrinsicLibrary::genFloor(mlir::Type resultType,
2420                                        llvm::ArrayRef<mlir::Value> args) {
2421   // Optional KIND argument.
2422   assert(args.size() >= 1);
2423   mlir::Value arg = args[0];
2424   // Use LLVM floor that returns real.
2425   mlir::Value floor = genRuntimeCall("floor", arg.getType(), {arg});
2426   return builder.createConvert(loc, resultType, floor);
2427 }
2428 
2429 // FRACTION
2430 mlir::Value IntrinsicLibrary::genFraction(mlir::Type resultType,
2431                                           llvm::ArrayRef<mlir::Value> args) {
2432   assert(args.size() == 1);
2433 
2434   return builder.createConvert(
2435       loc, resultType,
2436       fir::runtime::genFraction(builder, loc, fir::getBase(args[0])));
2437 }
2438 
2439 // GET_COMMAND_ARGUMENT
2440 void IntrinsicLibrary::genGetCommandArgument(
2441     llvm::ArrayRef<fir::ExtendedValue> args) {
2442   assert(args.size() == 5);
2443   mlir::Value number = fir::getBase(args[0]);
2444   const fir::ExtendedValue &value = args[1];
2445   const fir::ExtendedValue &length = args[2];
2446   const fir::ExtendedValue &status = args[3];
2447   const fir::ExtendedValue &errmsg = args[4];
2448 
2449   if (!number)
2450     fir::emitFatalError(loc, "expected NUMBER parameter");
2451 
2452   if (isStaticallyPresent(value) || isStaticallyPresent(status) ||
2453       isStaticallyPresent(errmsg)) {
2454     mlir::Type boxNoneTy = fir::BoxType::get(builder.getNoneType());
2455     mlir::Value valBox =
2456         isStaticallyPresent(value)
2457             ? fir::getBase(value)
2458             : builder.create<fir::AbsentOp>(loc, boxNoneTy).getResult();
2459     mlir::Value errBox =
2460         isStaticallyPresent(errmsg)
2461             ? fir::getBase(errmsg)
2462             : builder.create<fir::AbsentOp>(loc, boxNoneTy).getResult();
2463     mlir::Value stat =
2464         fir::runtime::genArgumentValue(builder, loc, number, valBox, errBox);
2465     if (isStaticallyPresent(status)) {
2466       mlir::Value statAddr = fir::getBase(status);
2467       mlir::Value statIsPresentAtRuntime =
2468           builder.genIsNotNullAddr(loc, statAddr);
2469       builder.genIfThen(loc, statIsPresentAtRuntime)
2470           .genThen(
2471               [&]() { builder.createStoreWithConvert(loc, stat, statAddr); })
2472           .end();
2473     }
2474   }
2475   if (isStaticallyPresent(length)) {
2476     mlir::Value lenAddr = fir::getBase(length);
2477     mlir::Value lenIsPresentAtRuntime = builder.genIsNotNullAddr(loc, lenAddr);
2478     builder.genIfThen(loc, lenIsPresentAtRuntime)
2479         .genThen([&]() {
2480           mlir::Value len =
2481               fir::runtime::genArgumentLength(builder, loc, number);
2482           builder.createStoreWithConvert(loc, len, lenAddr);
2483         })
2484         .end();
2485   }
2486 }
2487 
2488 // GET_ENVIRONMENT_VARIABLE
2489 void IntrinsicLibrary::genGetEnvironmentVariable(
2490     llvm::ArrayRef<fir::ExtendedValue> args) {
2491   assert(args.size() == 6);
2492   mlir::Value name = fir::getBase(args[0]);
2493   const fir::ExtendedValue &value = args[1];
2494   const fir::ExtendedValue &length = args[2];
2495   const fir::ExtendedValue &status = args[3];
2496   const fir::ExtendedValue &trimName = args[4];
2497   const fir::ExtendedValue &errmsg = args[5];
2498 
2499   // Handle optional TRIM_NAME argument
2500   mlir::Value trim;
2501   if (isStaticallyAbsent(trimName)) {
2502     trim = builder.createBool(loc, true);
2503   } else {
2504     mlir::Type i1Ty = builder.getI1Type();
2505     mlir::Value trimNameAddr = fir::getBase(trimName);
2506     mlir::Value trimNameIsPresentAtRuntime =
2507         builder.genIsNotNullAddr(loc, trimNameAddr);
2508     trim = builder
2509                .genIfOp(loc, {i1Ty}, trimNameIsPresentAtRuntime,
2510                         /*withElseRegion=*/true)
2511                .genThen([&]() {
2512                  auto trimLoad = builder.create<fir::LoadOp>(loc, trimNameAddr);
2513                  mlir::Value cast = builder.createConvert(loc, i1Ty, trimLoad);
2514                  builder.create<fir::ResultOp>(loc, cast);
2515                })
2516                .genElse([&]() {
2517                  mlir::Value trueVal = builder.createBool(loc, true);
2518                  builder.create<fir::ResultOp>(loc, trueVal);
2519                })
2520                .getResults()[0];
2521   }
2522 
2523   if (isStaticallyPresent(value) || isStaticallyPresent(status) ||
2524       isStaticallyPresent(errmsg)) {
2525     mlir::Type boxNoneTy = fir::BoxType::get(builder.getNoneType());
2526     mlir::Value valBox =
2527         isStaticallyPresent(value)
2528             ? fir::getBase(value)
2529             : builder.create<fir::AbsentOp>(loc, boxNoneTy).getResult();
2530     mlir::Value errBox =
2531         isStaticallyPresent(errmsg)
2532             ? fir::getBase(errmsg)
2533             : builder.create<fir::AbsentOp>(loc, boxNoneTy).getResult();
2534     mlir::Value stat = fir::runtime::genEnvVariableValue(builder, loc, name,
2535                                                          valBox, trim, errBox);
2536     if (isStaticallyPresent(status)) {
2537       mlir::Value statAddr = fir::getBase(status);
2538       mlir::Value statIsPresentAtRuntime =
2539           builder.genIsNotNullAddr(loc, statAddr);
2540       builder.genIfThen(loc, statIsPresentAtRuntime)
2541           .genThen(
2542               [&]() { builder.createStoreWithConvert(loc, stat, statAddr); })
2543           .end();
2544     }
2545   }
2546 
2547   if (isStaticallyPresent(length)) {
2548     mlir::Value lenAddr = fir::getBase(length);
2549     mlir::Value lenIsPresentAtRuntime = builder.genIsNotNullAddr(loc, lenAddr);
2550     builder.genIfThen(loc, lenIsPresentAtRuntime)
2551         .genThen([&]() {
2552           mlir::Value len =
2553               fir::runtime::genEnvVariableLength(builder, loc, name, trim);
2554           builder.createStoreWithConvert(loc, len, lenAddr);
2555         })
2556         .end();
2557   }
2558 }
2559 
2560 // IAND
2561 mlir::Value IntrinsicLibrary::genIand(mlir::Type resultType,
2562                                       llvm::ArrayRef<mlir::Value> args) {
2563   assert(args.size() == 2);
2564   auto arg0 = builder.createConvert(loc, resultType, args[0]);
2565   auto arg1 = builder.createConvert(loc, resultType, args[1]);
2566   return builder.create<mlir::arith::AndIOp>(loc, arg0, arg1);
2567 }
2568 
2569 // IBCLR
2570 mlir::Value IntrinsicLibrary::genIbclr(mlir::Type resultType,
2571                                        llvm::ArrayRef<mlir::Value> args) {
2572   // A conformant IBCLR(I,POS) call satisfies:
2573   //     POS >= 0
2574   //     POS < BIT_SIZE(I)
2575   // Return:  I & (!(1 << POS))
2576   assert(args.size() == 2);
2577   mlir::Value pos = builder.createConvert(loc, resultType, args[1]);
2578   mlir::Value one = builder.createIntegerConstant(loc, resultType, 1);
2579   mlir::Value ones = builder.createIntegerConstant(loc, resultType, -1);
2580   auto mask = builder.create<mlir::arith::ShLIOp>(loc, one, pos);
2581   auto res = builder.create<mlir::arith::XOrIOp>(loc, ones, mask);
2582   return builder.create<mlir::arith::AndIOp>(loc, args[0], res);
2583 }
2584 
2585 // IBITS
2586 mlir::Value IntrinsicLibrary::genIbits(mlir::Type resultType,
2587                                        llvm::ArrayRef<mlir::Value> args) {
2588   // A conformant IBITS(I,POS,LEN) call satisfies:
2589   //     POS >= 0
2590   //     LEN >= 0
2591   //     POS + LEN <= BIT_SIZE(I)
2592   // Return:  LEN == 0 ? 0 : (I >> POS) & (-1 >> (BIT_SIZE(I) - LEN))
2593   // For a conformant call, implementing (I >> POS) with a signed or an
2594   // unsigned shift produces the same result.  For a nonconformant call,
2595   // the two choices may produce different results.
2596   assert(args.size() == 3);
2597   mlir::Value pos = builder.createConvert(loc, resultType, args[1]);
2598   mlir::Value len = builder.createConvert(loc, resultType, args[2]);
2599   mlir::Value bitSize = builder.createIntegerConstant(
2600       loc, resultType, resultType.cast<mlir::IntegerType>().getWidth());
2601   auto shiftCount = builder.create<mlir::arith::SubIOp>(loc, bitSize, len);
2602   mlir::Value zero = builder.createIntegerConstant(loc, resultType, 0);
2603   mlir::Value ones = builder.createIntegerConstant(loc, resultType, -1);
2604   auto mask = builder.create<mlir::arith::ShRUIOp>(loc, ones, shiftCount);
2605   auto res1 = builder.create<mlir::arith::ShRSIOp>(loc, args[0], pos);
2606   auto res2 = builder.create<mlir::arith::AndIOp>(loc, res1, mask);
2607   auto lenIsZero = builder.create<mlir::arith::CmpIOp>(
2608       loc, mlir::arith::CmpIPredicate::eq, len, zero);
2609   return builder.create<mlir::arith::SelectOp>(loc, lenIsZero, zero, res2);
2610 }
2611 
2612 // IBSET
2613 mlir::Value IntrinsicLibrary::genIbset(mlir::Type resultType,
2614                                        llvm::ArrayRef<mlir::Value> args) {
2615   // A conformant IBSET(I,POS) call satisfies:
2616   //     POS >= 0
2617   //     POS < BIT_SIZE(I)
2618   // Return:  I | (1 << POS)
2619   assert(args.size() == 2);
2620   mlir::Value pos = builder.createConvert(loc, resultType, args[1]);
2621   mlir::Value one = builder.createIntegerConstant(loc, resultType, 1);
2622   auto mask = builder.create<mlir::arith::ShLIOp>(loc, one, pos);
2623   return builder.create<mlir::arith::OrIOp>(loc, args[0], mask);
2624 }
2625 
2626 // ICHAR
2627 fir::ExtendedValue
2628 IntrinsicLibrary::genIchar(mlir::Type resultType,
2629                            llvm::ArrayRef<fir::ExtendedValue> args) {
2630   // There can be an optional kind in second argument.
2631   assert(args.size() == 2);
2632   const fir::CharBoxValue *charBox = args[0].getCharBox();
2633   if (!charBox)
2634     llvm::report_fatal_error("expected character scalar");
2635 
2636   fir::factory::CharacterExprHelper helper{builder, loc};
2637   mlir::Value buffer = charBox->getBuffer();
2638   mlir::Type bufferTy = buffer.getType();
2639   mlir::Value charVal;
2640   if (auto charTy = bufferTy.dyn_cast<fir::CharacterType>()) {
2641     assert(charTy.singleton());
2642     charVal = buffer;
2643   } else {
2644     // Character is in memory, cast to fir.ref<char> and load.
2645     mlir::Type ty = fir::dyn_cast_ptrEleTy(bufferTy);
2646     if (!ty)
2647       llvm::report_fatal_error("expected memory type");
2648     // The length of in the character type may be unknown. Casting
2649     // to a singleton ref is required before loading.
2650     fir::CharacterType eleType = helper.getCharacterType(ty);
2651     fir::CharacterType charType =
2652         fir::CharacterType::get(builder.getContext(), eleType.getFKind(), 1);
2653     mlir::Type toTy = builder.getRefType(charType);
2654     mlir::Value cast = builder.createConvert(loc, toTy, buffer);
2655     charVal = builder.create<fir::LoadOp>(loc, cast);
2656   }
2657   LLVM_DEBUG(llvm::dbgs() << "ichar(" << charVal << ")\n");
2658   auto code = helper.extractCodeFromSingleton(charVal);
2659   if (code.getType() == resultType)
2660     return code;
2661   return builder.create<mlir::arith::ExtUIOp>(loc, resultType, code);
2662 }
2663 
2664 // IEEE_CLASS_TYPE OPERATOR(==), OPERATOR(/=)
2665 // IEEE_ROUND_TYPE OPERATOR(==), OPERATOR(/=)
2666 template <mlir::arith::CmpIPredicate pred>
2667 fir::ExtendedValue
2668 IntrinsicLibrary::genIeeeTypeCompare(mlir::Type resultType,
2669                                      llvm::ArrayRef<fir::ExtendedValue> args) {
2670   assert(args.size() == 2);
2671   mlir::Value arg0 = fir::getBase(args[0]);
2672   mlir::Value arg1 = fir::getBase(args[1]);
2673   auto recType =
2674       fir::unwrapPassByRefType(arg0.getType()).dyn_cast<fir::RecordType>();
2675   assert(recType.getTypeList().size() == 1 && "expected exactly one component");
2676   auto [fieldName, fieldType] = recType.getTypeList().front();
2677   mlir::Type fieldIndexType = fir::FieldType::get(recType.getContext());
2678   mlir::Value field = builder.create<fir::FieldIndexOp>(
2679       loc, fieldIndexType, fieldName, recType, fir::getTypeParams(arg0));
2680   mlir::Value left = builder.create<fir::LoadOp>(
2681       loc, fieldType,
2682       builder.create<fir::CoordinateOp>(loc, builder.getRefType(fieldType),
2683                                         arg0, field));
2684   mlir::Value right = builder.create<fir::LoadOp>(
2685       loc, fieldType,
2686       builder.create<fir::CoordinateOp>(loc, builder.getRefType(fieldType),
2687                                         arg1, field));
2688   return builder.create<mlir::arith::CmpIOp>(loc, pred, left, right);
2689 }
2690 
2691 // IEEE_IS_FINITE
2692 mlir::Value
2693 IntrinsicLibrary::genIeeeIsFinite(mlir::Type resultType,
2694                                   llvm::ArrayRef<mlir::Value> args) {
2695   // IEEE_IS_FINITE(X) is true iff exponent(X) is the max exponent of kind(X).
2696   assert(args.size() == 1);
2697   mlir::Value floatVal = fir::getBase(args[0]);
2698   mlir::FloatType floatType = floatVal.getType().dyn_cast<mlir::FloatType>();
2699   int floatBits = floatType.getWidth();
2700   mlir::Type intType = builder.getIntegerType(
2701       floatType.isa<mlir::Float80Type>() ? 128 : floatBits);
2702   mlir::Value intVal =
2703       builder.create<mlir::arith::BitcastOp>(loc, intType, floatVal);
2704   int significandBits;
2705   if (floatType.isa<mlir::Float32Type>())
2706     significandBits = 23;
2707   else if (floatType.isa<mlir::Float64Type>())
2708     significandBits = 52;
2709   else // problems elsewhere for other kinds
2710     TODO(loc, "intrinsic module procedure: ieee_is_finite");
2711   mlir::Value significand =
2712       builder.createIntegerConstant(loc, intType, significandBits);
2713   int exponentBits = floatBits - 1 - significandBits;
2714   mlir::Value maxExponent =
2715       builder.createIntegerConstant(loc, intType, (1 << exponentBits) - 1);
2716   mlir::Value exponent = genIbits(
2717       intType, {intVal, significand,
2718                 builder.createIntegerConstant(loc, intType, exponentBits)});
2719   return builder.createConvert(
2720       loc, resultType,
2721       builder.create<mlir::arith::CmpIOp>(loc, mlir::arith::CmpIPredicate::ne,
2722                                           exponent, maxExponent));
2723 }
2724 
2725 // IEOR
2726 mlir::Value IntrinsicLibrary::genIeor(mlir::Type resultType,
2727                                       llvm::ArrayRef<mlir::Value> args) {
2728   assert(args.size() == 2);
2729   return builder.create<mlir::arith::XOrIOp>(loc, args[0], args[1]);
2730 }
2731 
2732 // INDEX
2733 fir::ExtendedValue
2734 IntrinsicLibrary::genIndex(mlir::Type resultType,
2735                            llvm::ArrayRef<fir::ExtendedValue> args) {
2736   assert(args.size() >= 2 && args.size() <= 4);
2737 
2738   mlir::Value stringBase = fir::getBase(args[0]);
2739   fir::KindTy kind =
2740       fir::factory::CharacterExprHelper{builder, loc}.getCharacterKind(
2741           stringBase.getType());
2742   mlir::Value stringLen = fir::getLen(args[0]);
2743   mlir::Value substringBase = fir::getBase(args[1]);
2744   mlir::Value substringLen = fir::getLen(args[1]);
2745   mlir::Value back =
2746       isStaticallyAbsent(args, 2)
2747           ? builder.createIntegerConstant(loc, builder.getI1Type(), 0)
2748           : fir::getBase(args[2]);
2749   if (isStaticallyAbsent(args, 3))
2750     return builder.createConvert(
2751         loc, resultType,
2752         fir::runtime::genIndex(builder, loc, kind, stringBase, stringLen,
2753                                substringBase, substringLen, back));
2754 
2755   // Call the descriptor-based Index implementation
2756   mlir::Value string = builder.createBox(loc, args[0]);
2757   mlir::Value substring = builder.createBox(loc, args[1]);
2758   auto makeRefThenEmbox = [&](mlir::Value b) {
2759     fir::LogicalType logTy = fir::LogicalType::get(
2760         builder.getContext(), builder.getKindMap().defaultLogicalKind());
2761     mlir::Value temp = builder.createTemporary(loc, logTy);
2762     mlir::Value castb = builder.createConvert(loc, logTy, b);
2763     builder.create<fir::StoreOp>(loc, castb, temp);
2764     return builder.createBox(loc, temp);
2765   };
2766   mlir::Value backOpt = isStaticallyAbsent(args, 2)
2767                             ? builder.create<fir::AbsentOp>(
2768                                   loc, fir::BoxType::get(builder.getI1Type()))
2769                             : makeRefThenEmbox(fir::getBase(args[2]));
2770   mlir::Value kindVal = isStaticallyAbsent(args, 3)
2771                             ? builder.createIntegerConstant(
2772                                   loc, builder.getIndexType(),
2773                                   builder.getKindMap().defaultIntegerKind())
2774                             : fir::getBase(args[3]);
2775   // Create mutable fir.box to be passed to the runtime for the result.
2776   fir::MutableBoxValue mutBox =
2777       fir::factory::createTempMutableBox(builder, loc, resultType);
2778   mlir::Value resBox = fir::factory::getMutableIRBox(builder, loc, mutBox);
2779   // Call runtime. The runtime is allocating the result.
2780   fir::runtime::genIndexDescriptor(builder, loc, resBox, string, substring,
2781                                    backOpt, kindVal);
2782   // Read back the result from the mutable box.
2783   return readAndAddCleanUp(mutBox, resultType, "INDEX");
2784 }
2785 
2786 // IOR
2787 mlir::Value IntrinsicLibrary::genIor(mlir::Type resultType,
2788                                      llvm::ArrayRef<mlir::Value> args) {
2789   assert(args.size() == 2);
2790   return builder.create<mlir::arith::OrIOp>(loc, args[0], args[1]);
2791 }
2792 
2793 // ISHFT
2794 mlir::Value IntrinsicLibrary::genIshft(mlir::Type resultType,
2795                                        llvm::ArrayRef<mlir::Value> args) {
2796   // A conformant ISHFT(I,SHIFT) call satisfies:
2797   //     abs(SHIFT) <= BIT_SIZE(I)
2798   // Return:  abs(SHIFT) >= BIT_SIZE(I)
2799   //              ? 0
2800   //              : SHIFT < 0
2801   //                    ? I >> abs(SHIFT)
2802   //                    : I << abs(SHIFT)
2803   assert(args.size() == 2);
2804   mlir::Value bitSize = builder.createIntegerConstant(
2805       loc, resultType, resultType.cast<mlir::IntegerType>().getWidth());
2806   mlir::Value zero = builder.createIntegerConstant(loc, resultType, 0);
2807   mlir::Value shift = builder.createConvert(loc, resultType, args[1]);
2808   mlir::Value absShift = genAbs(resultType, {shift});
2809   auto left = builder.create<mlir::arith::ShLIOp>(loc, args[0], absShift);
2810   auto right = builder.create<mlir::arith::ShRUIOp>(loc, args[0], absShift);
2811   auto shiftIsLarge = builder.create<mlir::arith::CmpIOp>(
2812       loc, mlir::arith::CmpIPredicate::sge, absShift, bitSize);
2813   auto shiftIsNegative = builder.create<mlir::arith::CmpIOp>(
2814       loc, mlir::arith::CmpIPredicate::slt, shift, zero);
2815   auto sel =
2816       builder.create<mlir::arith::SelectOp>(loc, shiftIsNegative, right, left);
2817   return builder.create<mlir::arith::SelectOp>(loc, shiftIsLarge, zero, sel);
2818 }
2819 
2820 // ISHFTC
2821 mlir::Value IntrinsicLibrary::genIshftc(mlir::Type resultType,
2822                                         llvm::ArrayRef<mlir::Value> args) {
2823   // A conformant ISHFTC(I,SHIFT,SIZE) call satisfies:
2824   //     SIZE > 0
2825   //     SIZE <= BIT_SIZE(I)
2826   //     abs(SHIFT) <= SIZE
2827   // if SHIFT > 0
2828   //     leftSize = abs(SHIFT)
2829   //     rightSize = SIZE - abs(SHIFT)
2830   // else [if SHIFT < 0]
2831   //     leftSize = SIZE - abs(SHIFT)
2832   //     rightSize = abs(SHIFT)
2833   // unchanged = SIZE == BIT_SIZE(I) ? 0 : (I >> SIZE) << SIZE
2834   // leftMaskShift = BIT_SIZE(I) - leftSize
2835   // rightMaskShift = BIT_SIZE(I) - rightSize
2836   // left = (I >> rightSize) & (-1 >> leftMaskShift)
2837   // right = (I & (-1 >> rightMaskShift)) << leftSize
2838   // Return:  SHIFT == 0 || SIZE == abs(SHIFT) ? I : (unchanged | left | right)
2839   assert(args.size() == 3);
2840   mlir::Value bitSize = builder.createIntegerConstant(
2841       loc, resultType, resultType.cast<mlir::IntegerType>().getWidth());
2842   mlir::Value I = args[0];
2843   mlir::Value shift = builder.createConvert(loc, resultType, args[1]);
2844   mlir::Value size =
2845       args[2] ? builder.createConvert(loc, resultType, args[2]) : bitSize;
2846   mlir::Value zero = builder.createIntegerConstant(loc, resultType, 0);
2847   mlir::Value ones = builder.createIntegerConstant(loc, resultType, -1);
2848   mlir::Value absShift = genAbs(resultType, {shift});
2849   auto elseSize = builder.create<mlir::arith::SubIOp>(loc, size, absShift);
2850   auto shiftIsZero = builder.create<mlir::arith::CmpIOp>(
2851       loc, mlir::arith::CmpIPredicate::eq, shift, zero);
2852   auto shiftEqualsSize = builder.create<mlir::arith::CmpIOp>(
2853       loc, mlir::arith::CmpIPredicate::eq, absShift, size);
2854   auto shiftIsNop =
2855       builder.create<mlir::arith::OrIOp>(loc, shiftIsZero, shiftEqualsSize);
2856   auto shiftIsPositive = builder.create<mlir::arith::CmpIOp>(
2857       loc, mlir::arith::CmpIPredicate::sgt, shift, zero);
2858   auto leftSize = builder.create<mlir::arith::SelectOp>(loc, shiftIsPositive,
2859                                                         absShift, elseSize);
2860   auto rightSize = builder.create<mlir::arith::SelectOp>(loc, shiftIsPositive,
2861                                                          elseSize, absShift);
2862   auto hasUnchanged = builder.create<mlir::arith::CmpIOp>(
2863       loc, mlir::arith::CmpIPredicate::ne, size, bitSize);
2864   auto unchangedTmp1 = builder.create<mlir::arith::ShRUIOp>(loc, I, size);
2865   auto unchangedTmp2 =
2866       builder.create<mlir::arith::ShLIOp>(loc, unchangedTmp1, size);
2867   auto unchanged = builder.create<mlir::arith::SelectOp>(loc, hasUnchanged,
2868                                                          unchangedTmp2, zero);
2869   auto leftMaskShift =
2870       builder.create<mlir::arith::SubIOp>(loc, bitSize, leftSize);
2871   auto leftMask =
2872       builder.create<mlir::arith::ShRUIOp>(loc, ones, leftMaskShift);
2873   auto leftTmp = builder.create<mlir::arith::ShRUIOp>(loc, I, rightSize);
2874   auto left = builder.create<mlir::arith::AndIOp>(loc, leftTmp, leftMask);
2875   auto rightMaskShift =
2876       builder.create<mlir::arith::SubIOp>(loc, bitSize, rightSize);
2877   auto rightMask =
2878       builder.create<mlir::arith::ShRUIOp>(loc, ones, rightMaskShift);
2879   auto rightTmp = builder.create<mlir::arith::AndIOp>(loc, I, rightMask);
2880   auto right = builder.create<mlir::arith::ShLIOp>(loc, rightTmp, leftSize);
2881   auto resTmp = builder.create<mlir::arith::OrIOp>(loc, unchanged, left);
2882   auto res = builder.create<mlir::arith::OrIOp>(loc, resTmp, right);
2883   return builder.create<mlir::arith::SelectOp>(loc, shiftIsNop, I, res);
2884 }
2885 
2886 // LEN
2887 // Note that this is only used for an unrestricted intrinsic LEN call.
2888 // Other uses of LEN are rewritten as descriptor inquiries by the front-end.
2889 fir::ExtendedValue
2890 IntrinsicLibrary::genLen(mlir::Type resultType,
2891                          llvm::ArrayRef<fir::ExtendedValue> args) {
2892   // Optional KIND argument reflected in result type and otherwise ignored.
2893   assert(args.size() == 1 || args.size() == 2);
2894   mlir::Value len = fir::factory::readCharLen(builder, loc, args[0]);
2895   return builder.createConvert(loc, resultType, len);
2896 }
2897 
2898 // LEN_TRIM
2899 fir::ExtendedValue
2900 IntrinsicLibrary::genLenTrim(mlir::Type resultType,
2901                              llvm::ArrayRef<fir::ExtendedValue> args) {
2902   // Optional KIND argument reflected in result type and otherwise ignored.
2903   assert(args.size() == 1 || args.size() == 2);
2904   const fir::CharBoxValue *charBox = args[0].getCharBox();
2905   if (!charBox)
2906     TODO(loc, "character array len_trim");
2907   auto len =
2908       fir::factory::CharacterExprHelper(builder, loc).createLenTrim(*charBox);
2909   return builder.createConvert(loc, resultType, len);
2910 }
2911 
2912 // LGE, LGT, LLE, LLT
2913 template <mlir::arith::CmpIPredicate pred>
2914 fir::ExtendedValue
2915 IntrinsicLibrary::genCharacterCompare(mlir::Type resultType,
2916                                       llvm::ArrayRef<fir::ExtendedValue> args) {
2917   assert(args.size() == 2);
2918   return fir::runtime::genCharCompare(
2919       builder, loc, pred, fir::getBase(args[0]), fir::getLen(args[0]),
2920       fir::getBase(args[1]), fir::getLen(args[1]));
2921 }
2922 
2923 // MATMUL
2924 fir::ExtendedValue
2925 IntrinsicLibrary::genMatmul(mlir::Type resultType,
2926                             llvm::ArrayRef<fir::ExtendedValue> args) {
2927   assert(args.size() == 2);
2928 
2929   // Handle required matmul arguments
2930   fir::BoxValue matrixTmpA = builder.createBox(loc, args[0]);
2931   mlir::Value matrixA = fir::getBase(matrixTmpA);
2932   fir::BoxValue matrixTmpB = builder.createBox(loc, args[1]);
2933   mlir::Value matrixB = fir::getBase(matrixTmpB);
2934   unsigned resultRank =
2935       (matrixTmpA.rank() == 1 || matrixTmpB.rank() == 1) ? 1 : 2;
2936 
2937   // Create mutable fir.box to be passed to the runtime for the result.
2938   mlir::Type resultArrayType = builder.getVarLenSeqTy(resultType, resultRank);
2939   fir::MutableBoxValue resultMutableBox =
2940       fir::factory::createTempMutableBox(builder, loc, resultArrayType);
2941   mlir::Value resultIrBox =
2942       fir::factory::getMutableIRBox(builder, loc, resultMutableBox);
2943   // Call runtime. The runtime is allocating the result.
2944   fir::runtime::genMatmul(builder, loc, resultIrBox, matrixA, matrixB);
2945   // Read result from mutable fir.box and add it to the list of temps to be
2946   // finalized by the StatementContext.
2947   return readAndAddCleanUp(resultMutableBox, resultType,
2948                            "unexpected result for MATMUL");
2949 }
2950 
2951 // MERGE
2952 fir::ExtendedValue
2953 IntrinsicLibrary::genMerge(mlir::Type,
2954                            llvm::ArrayRef<fir::ExtendedValue> args) {
2955   assert(args.size() == 3);
2956   mlir::Value tsource = fir::getBase(args[0]);
2957   mlir::Value fsource = fir::getBase(args[1]);
2958   mlir::Value rawMask = fir::getBase(args[2]);
2959   mlir::Type type0 = fir::unwrapRefType(tsource.getType());
2960   bool isCharRslt = fir::isa_char(type0); // result is same as first argument
2961   mlir::Value mask = builder.createConvert(loc, builder.getI1Type(), rawMask);
2962   // FSOURCE has the same type as TSOURCE, but they may not have the same MLIR
2963   // types (one can have dynamic length while the other has constant lengths,
2964   // or one may be a fir.logical<> while the other is an i1). Insert a cast to
2965   // fulfill mlir::SelectOp constraint that the MLIR types must be the same.
2966   mlir::Value fsourceCast =
2967       builder.createConvert(loc, tsource.getType(), fsource);
2968   auto rslt =
2969       builder.create<mlir::arith::SelectOp>(loc, mask, tsource, fsourceCast);
2970   if (isCharRslt) {
2971     // Need a CharBoxValue for character results
2972     const fir::CharBoxValue *charBox = args[0].getCharBox();
2973     fir::CharBoxValue charRslt(rslt, charBox->getLen());
2974     return charRslt;
2975   }
2976   return rslt;
2977 }
2978 
2979 // MOD
2980 mlir::Value IntrinsicLibrary::genMod(mlir::Type resultType,
2981                                      llvm::ArrayRef<mlir::Value> args) {
2982   assert(args.size() == 2);
2983   if (resultType.isa<mlir::IntegerType>())
2984     return builder.create<mlir::arith::RemSIOp>(loc, args[0], args[1]);
2985 
2986   // Use runtime. Note that mlir::arith::RemFOp implements floating point
2987   // remainder, but it does not work with fir::Real type.
2988   // TODO: consider using mlir::arith::RemFOp when possible, that may help
2989   // folding and  optimizations.
2990   return genRuntimeCall("mod", resultType, args);
2991 }
2992 
2993 // MODULO
2994 mlir::Value IntrinsicLibrary::genModulo(mlir::Type resultType,
2995                                         llvm::ArrayRef<mlir::Value> args) {
2996   assert(args.size() == 2);
2997   // No floored modulo op in LLVM/MLIR yet. TODO: add one to MLIR.
2998   // In the meantime, use a simple inlined implementation based on truncated
2999   // modulo (MOD(A, P) implemented by RemIOp, RemFOp). This avoids making manual
3000   // division and multiplication from MODULO formula.
3001   //  - If A/P > 0 or MOD(A,P)=0, then INT(A/P) = FLOOR(A/P), and MODULO = MOD.
3002   //  - Otherwise, when A/P < 0 and MOD(A,P) !=0, then MODULO(A, P) =
3003   //    A-FLOOR(A/P)*P = A-(INT(A/P)-1)*P = A-INT(A/P)*P+P = MOD(A,P)+P
3004   // Note that A/P < 0 if and only if A and P signs are different.
3005   if (resultType.isa<mlir::IntegerType>()) {
3006     auto remainder =
3007         builder.create<mlir::arith::RemSIOp>(loc, args[0], args[1]);
3008     auto argXor = builder.create<mlir::arith::XOrIOp>(loc, args[0], args[1]);
3009     mlir::Value zero = builder.createIntegerConstant(loc, argXor.getType(), 0);
3010     auto argSignDifferent = builder.create<mlir::arith::CmpIOp>(
3011         loc, mlir::arith::CmpIPredicate::slt, argXor, zero);
3012     auto remainderIsNotZero = builder.create<mlir::arith::CmpIOp>(
3013         loc, mlir::arith::CmpIPredicate::ne, remainder, zero);
3014     auto mustAddP = builder.create<mlir::arith::AndIOp>(loc, remainderIsNotZero,
3015                                                         argSignDifferent);
3016     auto remPlusP =
3017         builder.create<mlir::arith::AddIOp>(loc, remainder, args[1]);
3018     return builder.create<mlir::arith::SelectOp>(loc, mustAddP, remPlusP,
3019                                                  remainder);
3020   }
3021   // Real case
3022   auto remainder = builder.create<mlir::arith::RemFOp>(loc, args[0], args[1]);
3023   mlir::Value zero = builder.createRealZeroConstant(loc, remainder.getType());
3024   auto remainderIsNotZero = builder.create<mlir::arith::CmpFOp>(
3025       loc, mlir::arith::CmpFPredicate::UNE, remainder, zero);
3026   auto aLessThanZero = builder.create<mlir::arith::CmpFOp>(
3027       loc, mlir::arith::CmpFPredicate::OLT, args[0], zero);
3028   auto pLessThanZero = builder.create<mlir::arith::CmpFOp>(
3029       loc, mlir::arith::CmpFPredicate::OLT, args[1], zero);
3030   auto argSignDifferent =
3031       builder.create<mlir::arith::XOrIOp>(loc, aLessThanZero, pLessThanZero);
3032   auto mustAddP = builder.create<mlir::arith::AndIOp>(loc, remainderIsNotZero,
3033                                                       argSignDifferent);
3034   auto remPlusP = builder.create<mlir::arith::AddFOp>(loc, remainder, args[1]);
3035   return builder.create<mlir::arith::SelectOp>(loc, mustAddP, remPlusP,
3036                                                remainder);
3037 }
3038 
3039 // MVBITS
3040 void IntrinsicLibrary::genMvbits(llvm::ArrayRef<fir::ExtendedValue> args) {
3041   // A conformant MVBITS(FROM,FROMPOS,LEN,TO,TOPOS) call satisfies:
3042   //     FROMPOS >= 0
3043   //     LEN >= 0
3044   //     TOPOS >= 0
3045   //     FROMPOS + LEN <= BIT_SIZE(FROM)
3046   //     TOPOS + LEN <= BIT_SIZE(TO)
3047   // MASK = -1 >> (BIT_SIZE(FROM) - LEN)
3048   // TO = LEN == 0 ? TO : ((!(MASK << TOPOS)) & TO) |
3049   //                      (((FROM >> FROMPOS) & MASK) << TOPOS)
3050   assert(args.size() == 5);
3051   auto unbox = [&](fir::ExtendedValue exv) {
3052     const mlir::Value *arg = exv.getUnboxed();
3053     assert(arg && "nonscalar mvbits argument");
3054     return *arg;
3055   };
3056   mlir::Value from = unbox(args[0]);
3057   mlir::Type resultType = from.getType();
3058   mlir::Value frompos = builder.createConvert(loc, resultType, unbox(args[1]));
3059   mlir::Value len = builder.createConvert(loc, resultType, unbox(args[2]));
3060   mlir::Value toAddr = unbox(args[3]);
3061   assert(fir::dyn_cast_ptrEleTy(toAddr.getType()) == resultType &&
3062          "mismatched mvbits types");
3063   auto to = builder.create<fir::LoadOp>(loc, resultType, toAddr);
3064   mlir::Value topos = builder.createConvert(loc, resultType, unbox(args[4]));
3065   mlir::Value zero = builder.createIntegerConstant(loc, resultType, 0);
3066   mlir::Value ones = builder.createIntegerConstant(loc, resultType, -1);
3067   mlir::Value bitSize = builder.createIntegerConstant(
3068       loc, resultType, resultType.cast<mlir::IntegerType>().getWidth());
3069   auto shiftCount = builder.create<mlir::arith::SubIOp>(loc, bitSize, len);
3070   auto mask = builder.create<mlir::arith::ShRUIOp>(loc, ones, shiftCount);
3071   auto unchangedTmp1 = builder.create<mlir::arith::ShLIOp>(loc, mask, topos);
3072   auto unchangedTmp2 =
3073       builder.create<mlir::arith::XOrIOp>(loc, unchangedTmp1, ones);
3074   auto unchanged = builder.create<mlir::arith::AndIOp>(loc, unchangedTmp2, to);
3075   auto frombitsTmp1 = builder.create<mlir::arith::ShRUIOp>(loc, from, frompos);
3076   auto frombitsTmp2 =
3077       builder.create<mlir::arith::AndIOp>(loc, frombitsTmp1, mask);
3078   auto frombits = builder.create<mlir::arith::ShLIOp>(loc, frombitsTmp2, topos);
3079   auto resTmp = builder.create<mlir::arith::OrIOp>(loc, unchanged, frombits);
3080   auto lenIsZero = builder.create<mlir::arith::CmpIOp>(
3081       loc, mlir::arith::CmpIPredicate::eq, len, zero);
3082   auto res = builder.create<mlir::arith::SelectOp>(loc, lenIsZero, to, resTmp);
3083   builder.create<fir::StoreOp>(loc, res, toAddr);
3084 }
3085 
3086 // NEAREST
3087 mlir::Value IntrinsicLibrary::genNearest(mlir::Type resultType,
3088                                          llvm::ArrayRef<mlir::Value> args) {
3089   assert(args.size() == 2);
3090 
3091   mlir::Value realX = fir::getBase(args[0]);
3092   mlir::Value realS = fir::getBase(args[1]);
3093 
3094   return builder.createConvert(
3095       loc, resultType, fir::runtime::genNearest(builder, loc, realX, realS));
3096 }
3097 
3098 // NINT
3099 mlir::Value IntrinsicLibrary::genNint(mlir::Type resultType,
3100                                       llvm::ArrayRef<mlir::Value> args) {
3101   assert(args.size() >= 1);
3102   // Skip optional kind argument to search the runtime; it is already reflected
3103   // in result type.
3104   return genRuntimeCall("nint", resultType, {args[0]});
3105 }
3106 
3107 // NOT
3108 mlir::Value IntrinsicLibrary::genNot(mlir::Type resultType,
3109                                      llvm::ArrayRef<mlir::Value> args) {
3110   assert(args.size() == 1);
3111   mlir::Value allOnes = builder.createIntegerConstant(loc, resultType, -1);
3112   return builder.create<mlir::arith::XOrIOp>(loc, args[0], allOnes);
3113 }
3114 
3115 // NULL
3116 fir::ExtendedValue
3117 IntrinsicLibrary::genNull(mlir::Type, llvm::ArrayRef<fir::ExtendedValue> args) {
3118   // NULL() without MOLD must be handled in the contexts where it can appear
3119   // (see table 16.5 of Fortran 2018 standard).
3120   assert(args.size() == 1 && isStaticallyPresent(args[0]) &&
3121          "MOLD argument required to lower NULL outside of any context");
3122   const auto *mold = args[0].getBoxOf<fir::MutableBoxValue>();
3123   assert(mold && "MOLD must be a pointer or allocatable");
3124   fir::BoxType boxType = mold->getBoxTy();
3125   mlir::Value boxStorage = builder.createTemporary(loc, boxType);
3126   mlir::Value box = fir::factory::createUnallocatedBox(
3127       builder, loc, boxType, mold->nonDeferredLenParams());
3128   builder.create<fir::StoreOp>(loc, box, boxStorage);
3129   return fir::MutableBoxValue(boxStorage, mold->nonDeferredLenParams(), {});
3130 }
3131 
3132 // PACK
3133 fir::ExtendedValue
3134 IntrinsicLibrary::genPack(mlir::Type resultType,
3135                           llvm::ArrayRef<fir::ExtendedValue> args) {
3136   [[maybe_unused]] auto numArgs = args.size();
3137   assert(numArgs == 2 || numArgs == 3);
3138 
3139   // Handle required array argument
3140   mlir::Value array = builder.createBox(loc, args[0]);
3141 
3142   // Handle required mask argument
3143   mlir::Value mask = builder.createBox(loc, args[1]);
3144 
3145   // Handle optional vector argument
3146   mlir::Value vector = isStaticallyAbsent(args, 2)
3147                            ? builder.create<fir::AbsentOp>(
3148                                  loc, fir::BoxType::get(builder.getI1Type()))
3149                            : builder.createBox(loc, args[2]);
3150 
3151   // Create mutable fir.box to be passed to the runtime for the result.
3152   mlir::Type resultArrayType = builder.getVarLenSeqTy(resultType, 1);
3153   fir::MutableBoxValue resultMutableBox =
3154       fir::factory::createTempMutableBox(builder, loc, resultArrayType);
3155   mlir::Value resultIrBox =
3156       fir::factory::getMutableIRBox(builder, loc, resultMutableBox);
3157 
3158   fir::runtime::genPack(builder, loc, resultIrBox, array, mask, vector);
3159 
3160   return readAndAddCleanUp(resultMutableBox, resultType,
3161                            "unexpected result for PACK");
3162 }
3163 
3164 // PRESENT
3165 fir::ExtendedValue
3166 IntrinsicLibrary::genPresent(mlir::Type,
3167                              llvm::ArrayRef<fir::ExtendedValue> args) {
3168   assert(args.size() == 1);
3169   return builder.create<fir::IsPresentOp>(loc, builder.getI1Type(),
3170                                           fir::getBase(args[0]));
3171 }
3172 
3173 // PRODUCT
3174 fir::ExtendedValue
3175 IntrinsicLibrary::genProduct(mlir::Type resultType,
3176                              llvm::ArrayRef<fir::ExtendedValue> args) {
3177   return genProdOrSum(fir::runtime::genProduct, fir::runtime::genProductDim,
3178                       resultType, builder, loc, stmtCtx,
3179                       "unexpected result for Product", args);
3180 }
3181 
3182 // RANDOM_INIT
3183 void IntrinsicLibrary::genRandomInit(llvm::ArrayRef<fir::ExtendedValue> args) {
3184   assert(args.size() == 2);
3185   Fortran::lower::genRandomInit(builder, loc, fir::getBase(args[0]),
3186                                 fir::getBase(args[1]));
3187 }
3188 
3189 // RANDOM_NUMBER
3190 void IntrinsicLibrary::genRandomNumber(
3191     llvm::ArrayRef<fir::ExtendedValue> args) {
3192   assert(args.size() == 1);
3193   Fortran::lower::genRandomNumber(builder, loc, fir::getBase(args[0]));
3194 }
3195 
3196 // RANDOM_SEED
3197 void IntrinsicLibrary::genRandomSeed(llvm::ArrayRef<fir::ExtendedValue> args) {
3198   assert(args.size() == 3);
3199   for (int i = 0; i < 3; ++i)
3200     if (isStaticallyPresent(args[i])) {
3201       Fortran::lower::genRandomSeed(builder, loc, i, fir::getBase(args[i]));
3202       return;
3203     }
3204   Fortran::lower::genRandomSeed(builder, loc, -1, mlir::Value{});
3205 }
3206 
3207 // REPEAT
3208 fir::ExtendedValue
3209 IntrinsicLibrary::genRepeat(mlir::Type resultType,
3210                             llvm::ArrayRef<fir::ExtendedValue> args) {
3211   assert(args.size() == 2);
3212   mlir::Value string = builder.createBox(loc, args[0]);
3213   mlir::Value ncopies = fir::getBase(args[1]);
3214   // Create mutable fir.box to be passed to the runtime for the result.
3215   fir::MutableBoxValue resultMutableBox =
3216       fir::factory::createTempMutableBox(builder, loc, resultType);
3217   mlir::Value resultIrBox =
3218       fir::factory::getMutableIRBox(builder, loc, resultMutableBox);
3219   // Call runtime. The runtime is allocating the result.
3220   fir::runtime::genRepeat(builder, loc, resultIrBox, string, ncopies);
3221   // Read result from mutable fir.box and add it to the list of temps to be
3222   // finalized by the StatementContext.
3223   return readAndAddCleanUp(resultMutableBox, resultType, "REPEAT");
3224 }
3225 
3226 // RESHAPE
3227 fir::ExtendedValue
3228 IntrinsicLibrary::genReshape(mlir::Type resultType,
3229                              llvm::ArrayRef<fir::ExtendedValue> args) {
3230   assert(args.size() == 4);
3231 
3232   // Handle source argument
3233   mlir::Value source = builder.createBox(loc, args[0]);
3234 
3235   // Handle shape argument
3236   mlir::Value shape = builder.createBox(loc, args[1]);
3237   assert(fir::BoxValue(shape).rank() == 1);
3238   mlir::Type shapeTy = shape.getType();
3239   mlir::Type shapeArrTy = fir::dyn_cast_ptrOrBoxEleTy(shapeTy);
3240   auto resultRank = shapeArrTy.cast<fir::SequenceType>().getShape();
3241 
3242   assert(resultRank[0] != fir::SequenceType::getUnknownExtent() &&
3243          "shape arg must have constant size");
3244 
3245   // Handle optional pad argument
3246   mlir::Value pad = isStaticallyAbsent(args[2])
3247                         ? builder.create<fir::AbsentOp>(
3248                               loc, fir::BoxType::get(builder.getI1Type()))
3249                         : builder.createBox(loc, args[2]);
3250 
3251   // Handle optional order argument
3252   mlir::Value order = isStaticallyAbsent(args[3])
3253                           ? builder.create<fir::AbsentOp>(
3254                                 loc, fir::BoxType::get(builder.getI1Type()))
3255                           : builder.createBox(loc, args[3]);
3256 
3257   // Create mutable fir.box to be passed to the runtime for the result.
3258   mlir::Type type = builder.getVarLenSeqTy(resultType, resultRank[0]);
3259   fir::MutableBoxValue resultMutableBox =
3260       fir::factory::createTempMutableBox(builder, loc, type);
3261 
3262   mlir::Value resultIrBox =
3263       fir::factory::getMutableIRBox(builder, loc, resultMutableBox);
3264 
3265   fir::runtime::genReshape(builder, loc, resultIrBox, source, shape, pad,
3266                            order);
3267 
3268   return readAndAddCleanUp(resultMutableBox, resultType,
3269                            "unexpected result for RESHAPE");
3270 }
3271 
3272 // RRSPACING
3273 mlir::Value IntrinsicLibrary::genRRSpacing(mlir::Type resultType,
3274                                            llvm::ArrayRef<mlir::Value> args) {
3275   assert(args.size() == 1);
3276 
3277   return builder.createConvert(
3278       loc, resultType,
3279       fir::runtime::genRRSpacing(builder, loc, fir::getBase(args[0])));
3280 }
3281 
3282 // SCALE
3283 mlir::Value IntrinsicLibrary::genScale(mlir::Type resultType,
3284                                        llvm::ArrayRef<mlir::Value> args) {
3285   assert(args.size() == 2);
3286 
3287   mlir::Value realX = fir::getBase(args[0]);
3288   mlir::Value intI = fir::getBase(args[1]);
3289 
3290   return builder.createConvert(
3291       loc, resultType, fir::runtime::genScale(builder, loc, realX, intI));
3292 }
3293 
3294 // SCAN
3295 fir::ExtendedValue
3296 IntrinsicLibrary::genScan(mlir::Type resultType,
3297                           llvm::ArrayRef<fir::ExtendedValue> args) {
3298 
3299   assert(args.size() == 4);
3300 
3301   if (isStaticallyAbsent(args[3])) {
3302     // Kind not specified, so call scan/verify runtime routine that is
3303     // specialized on the kind of characters in string.
3304 
3305     // Handle required string base arg
3306     mlir::Value stringBase = fir::getBase(args[0]);
3307 
3308     // Handle required set string base arg
3309     mlir::Value setBase = fir::getBase(args[1]);
3310 
3311     // Handle kind argument; it is the kind of character in this case
3312     fir::KindTy kind =
3313         fir::factory::CharacterExprHelper{builder, loc}.getCharacterKind(
3314             stringBase.getType());
3315 
3316     // Get string length argument
3317     mlir::Value stringLen = fir::getLen(args[0]);
3318 
3319     // Get set string length argument
3320     mlir::Value setLen = fir::getLen(args[1]);
3321 
3322     // Handle optional back argument
3323     mlir::Value back =
3324         isStaticallyAbsent(args[2])
3325             ? builder.createIntegerConstant(loc, builder.getI1Type(), 0)
3326             : fir::getBase(args[2]);
3327 
3328     return builder.createConvert(loc, resultType,
3329                                  fir::runtime::genScan(builder, loc, kind,
3330                                                        stringBase, stringLen,
3331                                                        setBase, setLen, back));
3332   }
3333   // else use the runtime descriptor version of scan/verify
3334 
3335   // Handle optional argument, back
3336   auto makeRefThenEmbox = [&](mlir::Value b) {
3337     fir::LogicalType logTy = fir::LogicalType::get(
3338         builder.getContext(), builder.getKindMap().defaultLogicalKind());
3339     mlir::Value temp = builder.createTemporary(loc, logTy);
3340     mlir::Value castb = builder.createConvert(loc, logTy, b);
3341     builder.create<fir::StoreOp>(loc, castb, temp);
3342     return builder.createBox(loc, temp);
3343   };
3344   mlir::Value back = fir::isUnboxedValue(args[2])
3345                          ? makeRefThenEmbox(*args[2].getUnboxed())
3346                          : builder.create<fir::AbsentOp>(
3347                                loc, fir::BoxType::get(builder.getI1Type()));
3348 
3349   // Handle required string argument
3350   mlir::Value string = builder.createBox(loc, args[0]);
3351 
3352   // Handle required set argument
3353   mlir::Value set = builder.createBox(loc, args[1]);
3354 
3355   // Handle kind argument
3356   mlir::Value kind = fir::getBase(args[3]);
3357 
3358   // Create result descriptor
3359   fir::MutableBoxValue resultMutableBox =
3360       fir::factory::createTempMutableBox(builder, loc, resultType);
3361   mlir::Value resultIrBox =
3362       fir::factory::getMutableIRBox(builder, loc, resultMutableBox);
3363 
3364   fir::runtime::genScanDescriptor(builder, loc, resultIrBox, string, set, back,
3365                                   kind);
3366 
3367   // Handle cleanup of allocatable result descriptor and return
3368   return readAndAddCleanUp(resultMutableBox, resultType, "SCAN");
3369 }
3370 
3371 // SET_EXPONENT
3372 mlir::Value IntrinsicLibrary::genSetExponent(mlir::Type resultType,
3373                                              llvm::ArrayRef<mlir::Value> args) {
3374   assert(args.size() == 2);
3375 
3376   return builder.createConvert(
3377       loc, resultType,
3378       fir::runtime::genSetExponent(builder, loc, fir::getBase(args[0]),
3379                                    fir::getBase(args[1])));
3380 }
3381 
3382 // SIGN
3383 mlir::Value IntrinsicLibrary::genSign(mlir::Type resultType,
3384                                       llvm::ArrayRef<mlir::Value> args) {
3385   assert(args.size() == 2);
3386   if (resultType.isa<mlir::IntegerType>()) {
3387     mlir::Value abs = genAbs(resultType, {args[0]});
3388     mlir::Value zero = builder.createIntegerConstant(loc, resultType, 0);
3389     auto neg = builder.create<mlir::arith::SubIOp>(loc, zero, abs);
3390     auto cmp = builder.create<mlir::arith::CmpIOp>(
3391         loc, mlir::arith::CmpIPredicate::slt, args[1], zero);
3392     return builder.create<mlir::arith::SelectOp>(loc, cmp, neg, abs);
3393   }
3394   return genRuntimeCall("sign", resultType, args);
3395 }
3396 
3397 // SIZE
3398 fir::ExtendedValue
3399 IntrinsicLibrary::genSize(mlir::Type resultType,
3400                           llvm::ArrayRef<fir::ExtendedValue> args) {
3401   // Note that the value of the KIND argument is already reflected in the
3402   // resultType
3403   assert(args.size() == 3);
3404   if (const auto *boxValue = args[0].getBoxOf<fir::BoxValue>())
3405     if (boxValue->hasAssumedRank())
3406       TODO(loc, "SIZE intrinsic with assumed rank argument");
3407 
3408   // Get the ARRAY argument
3409   mlir::Value array = builder.createBox(loc, args[0]);
3410 
3411   // The front-end rewrites SIZE without the DIM argument to
3412   // an array of SIZE with DIM in most cases, but it may not be
3413   // possible in some cases like when in SIZE(function_call()).
3414   if (isStaticallyAbsent(args, 1))
3415     return builder.createConvert(loc, resultType,
3416                                  fir::runtime::genSize(builder, loc, array));
3417 
3418   // Get the DIM argument.
3419   mlir::Value dim = fir::getBase(args[1]);
3420   if (!fir::isa_ref_type(dim.getType()))
3421     return builder.createConvert(
3422         loc, resultType, fir::runtime::genSizeDim(builder, loc, array, dim));
3423 
3424   mlir::Value isDynamicallyAbsent = builder.genIsNullAddr(loc, dim);
3425   return builder
3426       .genIfOp(loc, {resultType}, isDynamicallyAbsent,
3427                /*withElseRegion=*/true)
3428       .genThen([&]() {
3429         mlir::Value size = builder.createConvert(
3430             loc, resultType, fir::runtime::genSize(builder, loc, array));
3431         builder.create<fir::ResultOp>(loc, size);
3432       })
3433       .genElse([&]() {
3434         mlir::Value dimValue = builder.create<fir::LoadOp>(loc, dim);
3435         mlir::Value size = builder.createConvert(
3436             loc, resultType,
3437             fir::runtime::genSizeDim(builder, loc, array, dimValue));
3438         builder.create<fir::ResultOp>(loc, size);
3439       })
3440       .getResults()[0];
3441 }
3442 
3443 static bool hasDefaultLowerBound(const fir::ExtendedValue &exv) {
3444   return exv.match(
3445       [](const fir::ArrayBoxValue &arr) { return arr.getLBounds().empty(); },
3446       [](const fir::CharArrayBoxValue &arr) {
3447         return arr.getLBounds().empty();
3448       },
3449       [](const fir::BoxValue &arr) { return arr.getLBounds().empty(); },
3450       [](const auto &) { return false; });
3451 }
3452 
3453 /// Compute the lower bound in dimension \p dim (zero based) of \p array
3454 /// taking care of returning one when the related extent is zero.
3455 static mlir::Value computeLBOUND(fir::FirOpBuilder &builder, mlir::Location loc,
3456                                  const fir::ExtendedValue &array, unsigned dim,
3457                                  mlir::Value zero, mlir::Value one) {
3458   assert(dim < array.rank() && "invalid dimension");
3459   if (hasDefaultLowerBound(array))
3460     return one;
3461   mlir::Value lb = fir::factory::readLowerBound(builder, loc, array, dim, one);
3462   if (dim + 1 == array.rank() && array.isAssumedSize())
3463     return lb;
3464   mlir::Value extent = fir::factory::readExtent(builder, loc, array, dim);
3465   zero = builder.createConvert(loc, extent.getType(), zero);
3466   auto dimIsEmpty = builder.create<mlir::arith::CmpIOp>(
3467       loc, mlir::arith::CmpIPredicate::eq, extent, zero);
3468   one = builder.createConvert(loc, lb.getType(), one);
3469   return builder.create<mlir::arith::SelectOp>(loc, dimIsEmpty, one, lb);
3470 }
3471 
3472 /// Create a fir.box to be passed to the LBOUND runtime.
3473 /// This ensure that local lower bounds of assumed shape are propagated and that
3474 /// a fir.box with equivalent LBOUNDs but an explicit shape is created for
3475 /// assumed size arrays to avoid undefined behaviors in codegen or the runtime.
3476 static mlir::Value createBoxForLBOUND(mlir::Location loc,
3477                                       fir::FirOpBuilder &builder,
3478                                       const fir::ExtendedValue &array) {
3479   if (!array.isAssumedSize())
3480     return array.match(
3481         [&](const fir::BoxValue &boxValue) -> mlir::Value {
3482           // This entity is mapped to a fir.box that may not contain the local
3483           // lower bound information if it is a dummy. Rebox it with the local
3484           // shape information.
3485           mlir::Value localShape = builder.createShape(loc, array);
3486           mlir::Value oldBox = boxValue.getAddr();
3487           return builder.create<fir::ReboxOp>(loc, oldBox.getType(), oldBox,
3488                                               localShape,
3489                                               /*slice=*/mlir::Value{});
3490         },
3491         [&](const auto &) -> mlir::Value {
3492           // This a pointer/allocatable, or an entity not yet tracked with a
3493           // fir.box. For pointer/allocatable, createBox will forward the
3494           // descriptor that contains the correct lower bound information. For
3495           // other entities, a new fir.box will be made with the local lower
3496           // bounds.
3497           return builder.createBox(loc, array);
3498         });
3499   // Assumed sized are not meant to be emboxed. This could cause the undefined
3500   // extent cannot safely be understood by the runtime/codegen that will
3501   // consider that the dimension is empty and that the related LBOUND value must
3502   // be one. Pretend that the related extent is one to get the correct LBOUND
3503   // value.
3504   llvm::SmallVector<mlir::Value> shape =
3505       fir::factory::getExtents(loc, builder, array);
3506   assert(!shape.empty() && "assumed size must have at least one dimension");
3507   shape.back() = builder.createIntegerConstant(loc, builder.getIndexType(), 1);
3508   auto safeToEmbox = array.match(
3509       [&](const fir::CharArrayBoxValue &x) -> fir::ExtendedValue {
3510         return fir::CharArrayBoxValue{x.getAddr(), x.getLen(), shape,
3511                                       x.getLBounds()};
3512       },
3513       [&](const fir::ArrayBoxValue &x) -> fir::ExtendedValue {
3514         return fir::ArrayBoxValue{x.getAddr(), shape, x.getLBounds()};
3515       },
3516       [&](const auto &) -> fir::ExtendedValue {
3517         fir::emitFatalError(loc, "not an assumed size array");
3518       });
3519   return builder.createBox(loc, safeToEmbox);
3520 }
3521 
3522 // LBOUND
3523 fir::ExtendedValue
3524 IntrinsicLibrary::genLbound(mlir::Type resultType,
3525                             llvm::ArrayRef<fir::ExtendedValue> args) {
3526   assert(args.size() == 2 || args.size() == 3);
3527   const fir::ExtendedValue &array = args[0];
3528   if (const auto *boxValue = array.getBoxOf<fir::BoxValue>())
3529     if (boxValue->hasAssumedRank())
3530       TODO(loc, "LBOUND intrinsic with assumed rank argument");
3531 
3532   //===----------------------------------------------------------------------===//
3533   mlir::Type indexType = builder.getIndexType();
3534 
3535   // Semantics builds signatures for LBOUND calls as either
3536   // LBOUND(array, dim, [kind]) or LBOUND(array, [kind]).
3537   if (args.size() == 2 || isStaticallyAbsent(args, 1)) {
3538     // DIM is absent.
3539     mlir::Type lbType = fir::unwrapSequenceType(resultType);
3540     unsigned rank = array.rank();
3541     mlir::Type lbArrayType = fir::SequenceType::get(
3542         {static_cast<fir::SequenceType::Extent>(array.rank())}, lbType);
3543     mlir::Value lbArray = builder.createTemporary(loc, lbArrayType);
3544     mlir::Type lbAddrType = builder.getRefType(lbType);
3545     mlir::Value one = builder.createIntegerConstant(loc, lbType, 1);
3546     mlir::Value zero = builder.createIntegerConstant(loc, indexType, 0);
3547     for (unsigned dim = 0; dim < rank; ++dim) {
3548       mlir::Value lb = computeLBOUND(builder, loc, array, dim, zero, one);
3549       lb = builder.createConvert(loc, lbType, lb);
3550       auto index = builder.createIntegerConstant(loc, indexType, dim);
3551       auto lbAddr =
3552           builder.create<fir::CoordinateOp>(loc, lbAddrType, lbArray, index);
3553       builder.create<fir::StoreOp>(loc, lb, lbAddr);
3554     }
3555     mlir::Value lbArrayExtent =
3556         builder.createIntegerConstant(loc, indexType, rank);
3557     llvm::SmallVector<mlir::Value> extents{lbArrayExtent};
3558     return fir::ArrayBoxValue{lbArray, extents};
3559   }
3560   // DIM is present.
3561   mlir::Value dim = fir::getBase(args[1]);
3562 
3563   // If it is a compile time constant, skip the runtime call.
3564   if (llvm::Optional<std::int64_t> cstDim =
3565           fir::factory::getIntIfConstant(dim)) {
3566     mlir::Value one = builder.createIntegerConstant(loc, resultType, 1);
3567     mlir::Value zero = builder.createIntegerConstant(loc, indexType, 0);
3568     mlir::Value lb = computeLBOUND(builder, loc, array, *cstDim - 1, zero, one);
3569     return builder.createConvert(loc, resultType, lb);
3570   }
3571 
3572   fir::ExtendedValue box = createBoxForLBOUND(loc, builder, array);
3573   return builder.createConvert(
3574       loc, resultType,
3575       fir::runtime::genLboundDim(builder, loc, fir::getBase(box), dim));
3576 }
3577 
3578 // UBOUND
3579 fir::ExtendedValue
3580 IntrinsicLibrary::genUbound(mlir::Type resultType,
3581                             llvm::ArrayRef<fir::ExtendedValue> args) {
3582   assert(args.size() == 3 || args.size() == 2);
3583   if (args.size() == 3) {
3584     // Handle calls to UBOUND with the DIM argument, which return a scalar
3585     mlir::Value extent = fir::getBase(genSize(resultType, args));
3586     mlir::Value lbound = fir::getBase(genLbound(resultType, args));
3587 
3588     mlir::Value one = builder.createIntegerConstant(loc, resultType, 1);
3589     mlir::Value ubound = builder.create<mlir::arith::SubIOp>(loc, lbound, one);
3590     return builder.create<mlir::arith::AddIOp>(loc, ubound, extent);
3591   } else {
3592     // Handle calls to UBOUND without the DIM argument, which return an array
3593     mlir::Value kind = isStaticallyAbsent(args[1])
3594                            ? builder.createIntegerConstant(
3595                                  loc, builder.getIndexType(),
3596                                  builder.getKindMap().defaultIntegerKind())
3597                            : fir::getBase(args[1]);
3598 
3599     // Create mutable fir.box to be passed to the runtime for the result.
3600     mlir::Type type = builder.getVarLenSeqTy(resultType, /*rank=*/1);
3601     fir::MutableBoxValue resultMutableBox =
3602         fir::factory::createTempMutableBox(builder, loc, type);
3603     mlir::Value resultIrBox =
3604         fir::factory::getMutableIRBox(builder, loc, resultMutableBox);
3605 
3606     fir::runtime::genUbound(builder, loc, resultIrBox, fir::getBase(args[0]),
3607                             kind);
3608 
3609     return readAndAddCleanUp(resultMutableBox, resultType, "UBOUND");
3610   }
3611   return mlir::Value();
3612 }
3613 
3614 // SPACING
3615 mlir::Value IntrinsicLibrary::genSpacing(mlir::Type resultType,
3616                                          llvm::ArrayRef<mlir::Value> args) {
3617   assert(args.size() == 1);
3618 
3619   return builder.createConvert(
3620       loc, resultType,
3621       fir::runtime::genSpacing(builder, loc, fir::getBase(args[0])));
3622 }
3623 
3624 // SPREAD
3625 fir::ExtendedValue
3626 IntrinsicLibrary::genSpread(mlir::Type resultType,
3627                             llvm::ArrayRef<fir::ExtendedValue> args) {
3628 
3629   assert(args.size() == 3);
3630 
3631   // Handle source argument
3632   mlir::Value source = builder.createBox(loc, args[0]);
3633   fir::BoxValue sourceTmp = source;
3634   unsigned sourceRank = sourceTmp.rank();
3635 
3636   // Handle Dim argument
3637   mlir::Value dim = fir::getBase(args[1]);
3638 
3639   // Handle ncopies argument
3640   mlir::Value ncopies = fir::getBase(args[2]);
3641 
3642   // Generate result descriptor
3643   mlir::Type resultArrayType =
3644       builder.getVarLenSeqTy(resultType, sourceRank + 1);
3645   fir::MutableBoxValue resultMutableBox =
3646       fir::factory::createTempMutableBox(builder, loc, resultArrayType);
3647   mlir::Value resultIrBox =
3648       fir::factory::getMutableIRBox(builder, loc, resultMutableBox);
3649 
3650   fir::runtime::genSpread(builder, loc, resultIrBox, source, dim, ncopies);
3651 
3652   return readAndAddCleanUp(resultMutableBox, resultType,
3653                            "unexpected result for SPREAD");
3654 }
3655 
3656 // SUM
3657 fir::ExtendedValue
3658 IntrinsicLibrary::genSum(mlir::Type resultType,
3659                          llvm::ArrayRef<fir::ExtendedValue> args) {
3660   return genProdOrSum(fir::runtime::genSum, fir::runtime::genSumDim, resultType,
3661                       builder, loc, stmtCtx, "unexpected result for Sum", args);
3662 }
3663 
3664 // SYSTEM_CLOCK
3665 void IntrinsicLibrary::genSystemClock(llvm::ArrayRef<fir::ExtendedValue> args) {
3666   assert(args.size() == 3);
3667   Fortran::lower::genSystemClock(builder, loc, fir::getBase(args[0]),
3668                                  fir::getBase(args[1]), fir::getBase(args[2]));
3669 }
3670 
3671 // TRANSFER
3672 fir::ExtendedValue
3673 IntrinsicLibrary::genTransfer(mlir::Type resultType,
3674                               llvm::ArrayRef<fir::ExtendedValue> args) {
3675 
3676   assert(args.size() >= 2); // args.size() == 2 when size argument is omitted.
3677 
3678   // Handle source argument
3679   mlir::Value source = builder.createBox(loc, args[0]);
3680 
3681   // Handle mold argument
3682   mlir::Value mold = builder.createBox(loc, args[1]);
3683   fir::BoxValue moldTmp = mold;
3684   unsigned moldRank = moldTmp.rank();
3685 
3686   bool absentSize = (args.size() == 2);
3687 
3688   // Create mutable fir.box to be passed to the runtime for the result.
3689   mlir::Type type = (moldRank == 0 && absentSize)
3690                         ? resultType
3691                         : builder.getVarLenSeqTy(resultType, 1);
3692   fir::MutableBoxValue resultMutableBox =
3693       fir::factory::createTempMutableBox(builder, loc, type);
3694 
3695   if (moldRank == 0 && absentSize) {
3696     // This result is a scalar in this case.
3697     mlir::Value resultIrBox =
3698         fir::factory::getMutableIRBox(builder, loc, resultMutableBox);
3699 
3700     Fortran::lower::genTransfer(builder, loc, resultIrBox, source, mold);
3701   } else {
3702     // The result is a rank one array in this case.
3703     mlir::Value resultIrBox =
3704         fir::factory::getMutableIRBox(builder, loc, resultMutableBox);
3705 
3706     if (absentSize) {
3707       Fortran::lower::genTransfer(builder, loc, resultIrBox, source, mold);
3708     } else {
3709       mlir::Value sizeArg = fir::getBase(args[2]);
3710       Fortran::lower::genTransferSize(builder, loc, resultIrBox, source, mold,
3711                                       sizeArg);
3712     }
3713   }
3714   return readAndAddCleanUp(resultMutableBox, resultType,
3715                            "unexpected result for TRANSFER");
3716 }
3717 
3718 // TRANSPOSE
3719 fir::ExtendedValue
3720 IntrinsicLibrary::genTranspose(mlir::Type resultType,
3721                                llvm::ArrayRef<fir::ExtendedValue> args) {
3722 
3723   assert(args.size() == 1);
3724 
3725   // Handle source argument
3726   mlir::Value source = builder.createBox(loc, args[0]);
3727 
3728   // Create mutable fir.box to be passed to the runtime for the result.
3729   mlir::Type resultArrayType = builder.getVarLenSeqTy(resultType, 2);
3730   fir::MutableBoxValue resultMutableBox =
3731       fir::factory::createTempMutableBox(builder, loc, resultArrayType);
3732   mlir::Value resultIrBox =
3733       fir::factory::getMutableIRBox(builder, loc, resultMutableBox);
3734   // Call runtime. The runtime is allocating the result.
3735   fir::runtime::genTranspose(builder, loc, resultIrBox, source);
3736   // Read result from mutable fir.box and add it to the list of temps to be
3737   // finalized by the StatementContext.
3738   return readAndAddCleanUp(resultMutableBox, resultType,
3739                            "unexpected result for TRANSPOSE");
3740 }
3741 
3742 // TRIM
3743 fir::ExtendedValue
3744 IntrinsicLibrary::genTrim(mlir::Type resultType,
3745                           llvm::ArrayRef<fir::ExtendedValue> args) {
3746   assert(args.size() == 1);
3747   mlir::Value string = builder.createBox(loc, args[0]);
3748   // Create mutable fir.box to be passed to the runtime for the result.
3749   fir::MutableBoxValue resultMutableBox =
3750       fir::factory::createTempMutableBox(builder, loc, resultType);
3751   mlir::Value resultIrBox =
3752       fir::factory::getMutableIRBox(builder, loc, resultMutableBox);
3753   // Call runtime. The runtime is allocating the result.
3754   fir::runtime::genTrim(builder, loc, resultIrBox, string);
3755   // Read result from mutable fir.box and add it to the list of temps to be
3756   // finalized by the StatementContext.
3757   return readAndAddCleanUp(resultMutableBox, resultType, "TRIM");
3758 }
3759 
3760 // Compare two FIR values and return boolean result as i1.
3761 template <Extremum extremum, ExtremumBehavior behavior>
3762 static mlir::Value createExtremumCompare(mlir::Location loc,
3763                                          fir::FirOpBuilder &builder,
3764                                          mlir::Value left, mlir::Value right) {
3765   static constexpr mlir::arith::CmpIPredicate integerPredicate =
3766       extremum == Extremum::Max ? mlir::arith::CmpIPredicate::sgt
3767                                 : mlir::arith::CmpIPredicate::slt;
3768   static constexpr mlir::arith::CmpFPredicate orderedCmp =
3769       extremum == Extremum::Max ? mlir::arith::CmpFPredicate::OGT
3770                                 : mlir::arith::CmpFPredicate::OLT;
3771   mlir::Type type = left.getType();
3772   mlir::Value result;
3773   if (fir::isa_real(type)) {
3774     // Note: the signaling/quit aspect of the result required by IEEE
3775     // cannot currently be obtained with LLVM without ad-hoc runtime.
3776     if constexpr (behavior == ExtremumBehavior::IeeeMinMaximumNumber) {
3777       // Return the number if one of the inputs is NaN and the other is
3778       // a number.
3779       auto leftIsResult =
3780           builder.create<mlir::arith::CmpFOp>(loc, orderedCmp, left, right);
3781       auto rightIsNan = builder.create<mlir::arith::CmpFOp>(
3782           loc, mlir::arith::CmpFPredicate::UNE, right, right);
3783       result =
3784           builder.create<mlir::arith::OrIOp>(loc, leftIsResult, rightIsNan);
3785     } else if constexpr (behavior == ExtremumBehavior::IeeeMinMaximum) {
3786       // Always return NaNs if one the input is NaNs
3787       auto leftIsResult =
3788           builder.create<mlir::arith::CmpFOp>(loc, orderedCmp, left, right);
3789       auto leftIsNan = builder.create<mlir::arith::CmpFOp>(
3790           loc, mlir::arith::CmpFPredicate::UNE, left, left);
3791       result = builder.create<mlir::arith::OrIOp>(loc, leftIsResult, leftIsNan);
3792     } else if constexpr (behavior == ExtremumBehavior::MinMaxss) {
3793       // If the left is a NaN, return the right whatever it is.
3794       result =
3795           builder.create<mlir::arith::CmpFOp>(loc, orderedCmp, left, right);
3796     } else if constexpr (behavior == ExtremumBehavior::PgfortranLlvm) {
3797       // If one of the operand is a NaN, return left whatever it is.
3798       static constexpr auto unorderedCmp =
3799           extremum == Extremum::Max ? mlir::arith::CmpFPredicate::UGT
3800                                     : mlir::arith::CmpFPredicate::ULT;
3801       result =
3802           builder.create<mlir::arith::CmpFOp>(loc, unorderedCmp, left, right);
3803     } else {
3804       // TODO: ieeeMinNum/ieeeMaxNum
3805       static_assert(behavior == ExtremumBehavior::IeeeMinMaxNum,
3806                     "ieeeMinNum/ieeeMaxNum behavior not implemented");
3807     }
3808   } else if (fir::isa_integer(type)) {
3809     result =
3810         builder.create<mlir::arith::CmpIOp>(loc, integerPredicate, left, right);
3811   } else if (fir::isa_char(type)) {
3812     // TODO: ! character min and max is tricky because the result
3813     // length is the length of the longest argument!
3814     // So we may need a temp.
3815     TODO(loc, "CHARACTER min and max");
3816   }
3817   assert(result && "result must be defined");
3818   return result;
3819 }
3820 
3821 // UNPACK
3822 fir::ExtendedValue
3823 IntrinsicLibrary::genUnpack(mlir::Type resultType,
3824                             llvm::ArrayRef<fir::ExtendedValue> args) {
3825   assert(args.size() == 3);
3826 
3827   // Handle required vector argument
3828   mlir::Value vector = builder.createBox(loc, args[0]);
3829 
3830   // Handle required mask argument
3831   fir::BoxValue maskBox = builder.createBox(loc, args[1]);
3832   mlir::Value mask = fir::getBase(maskBox);
3833   unsigned maskRank = maskBox.rank();
3834 
3835   // Handle required field argument
3836   mlir::Value field = builder.createBox(loc, args[2]);
3837 
3838   // Create mutable fir.box to be passed to the runtime for the result.
3839   mlir::Type resultArrayType = builder.getVarLenSeqTy(resultType, maskRank);
3840   fir::MutableBoxValue resultMutableBox =
3841       fir::factory::createTempMutableBox(builder, loc, resultArrayType);
3842   mlir::Value resultIrBox =
3843       fir::factory::getMutableIRBox(builder, loc, resultMutableBox);
3844 
3845   fir::runtime::genUnpack(builder, loc, resultIrBox, vector, mask, field);
3846 
3847   return readAndAddCleanUp(resultMutableBox, resultType,
3848                            "unexpected result for UNPACK");
3849 }
3850 
3851 // VERIFY
3852 fir::ExtendedValue
3853 IntrinsicLibrary::genVerify(mlir::Type resultType,
3854                             llvm::ArrayRef<fir::ExtendedValue> args) {
3855 
3856   assert(args.size() == 4);
3857 
3858   if (isStaticallyAbsent(args[3])) {
3859     // Kind not specified, so call scan/verify runtime routine that is
3860     // specialized on the kind of characters in string.
3861 
3862     // Handle required string base arg
3863     mlir::Value stringBase = fir::getBase(args[0]);
3864 
3865     // Handle required set string base arg
3866     mlir::Value setBase = fir::getBase(args[1]);
3867 
3868     // Handle kind argument; it is the kind of character in this case
3869     fir::KindTy kind =
3870         fir::factory::CharacterExprHelper{builder, loc}.getCharacterKind(
3871             stringBase.getType());
3872 
3873     // Get string length argument
3874     mlir::Value stringLen = fir::getLen(args[0]);
3875 
3876     // Get set string length argument
3877     mlir::Value setLen = fir::getLen(args[1]);
3878 
3879     // Handle optional back argument
3880     mlir::Value back =
3881         isStaticallyAbsent(args[2])
3882             ? builder.createIntegerConstant(loc, builder.getI1Type(), 0)
3883             : fir::getBase(args[2]);
3884 
3885     return builder.createConvert(
3886         loc, resultType,
3887         fir::runtime::genVerify(builder, loc, kind, stringBase, stringLen,
3888                                 setBase, setLen, back));
3889   }
3890   // else use the runtime descriptor version of scan/verify
3891 
3892   // Handle optional argument, back
3893   auto makeRefThenEmbox = [&](mlir::Value b) {
3894     fir::LogicalType logTy = fir::LogicalType::get(
3895         builder.getContext(), builder.getKindMap().defaultLogicalKind());
3896     mlir::Value temp = builder.createTemporary(loc, logTy);
3897     mlir::Value castb = builder.createConvert(loc, logTy, b);
3898     builder.create<fir::StoreOp>(loc, castb, temp);
3899     return builder.createBox(loc, temp);
3900   };
3901   mlir::Value back = fir::isUnboxedValue(args[2])
3902                          ? makeRefThenEmbox(*args[2].getUnboxed())
3903                          : builder.create<fir::AbsentOp>(
3904                                loc, fir::BoxType::get(builder.getI1Type()));
3905 
3906   // Handle required string argument
3907   mlir::Value string = builder.createBox(loc, args[0]);
3908 
3909   // Handle required set argument
3910   mlir::Value set = builder.createBox(loc, args[1]);
3911 
3912   // Handle kind argument
3913   mlir::Value kind = fir::getBase(args[3]);
3914 
3915   // Create result descriptor
3916   fir::MutableBoxValue resultMutableBox =
3917       fir::factory::createTempMutableBox(builder, loc, resultType);
3918   mlir::Value resultIrBox =
3919       fir::factory::getMutableIRBox(builder, loc, resultMutableBox);
3920 
3921   fir::runtime::genVerifyDescriptor(builder, loc, resultIrBox, string, set,
3922                                     back, kind);
3923 
3924   // Handle cleanup of allocatable result descriptor and return
3925   return readAndAddCleanUp(resultMutableBox, resultType, "VERIFY");
3926 }
3927 
3928 // MAXLOC
3929 fir::ExtendedValue
3930 IntrinsicLibrary::genMaxloc(mlir::Type resultType,
3931                             llvm::ArrayRef<fir::ExtendedValue> args) {
3932   return genExtremumloc(fir::runtime::genMaxloc, fir::runtime::genMaxlocDim,
3933                         resultType, builder, loc, stmtCtx,
3934                         "unexpected result for Maxloc", args);
3935 }
3936 
3937 // MAXVAL
3938 fir::ExtendedValue
3939 IntrinsicLibrary::genMaxval(mlir::Type resultType,
3940                             llvm::ArrayRef<fir::ExtendedValue> args) {
3941   return genExtremumVal(fir::runtime::genMaxval, fir::runtime::genMaxvalDim,
3942                         fir::runtime::genMaxvalChar, resultType, builder, loc,
3943                         stmtCtx, "unexpected result for Maxval", args);
3944 }
3945 
3946 // MINLOC
3947 fir::ExtendedValue
3948 IntrinsicLibrary::genMinloc(mlir::Type resultType,
3949                             llvm::ArrayRef<fir::ExtendedValue> args) {
3950   return genExtremumloc(fir::runtime::genMinloc, fir::runtime::genMinlocDim,
3951                         resultType, builder, loc, stmtCtx,
3952                         "unexpected result for Minloc", args);
3953 }
3954 
3955 // MINVAL
3956 fir::ExtendedValue
3957 IntrinsicLibrary::genMinval(mlir::Type resultType,
3958                             llvm::ArrayRef<fir::ExtendedValue> args) {
3959   return genExtremumVal(fir::runtime::genMinval, fir::runtime::genMinvalDim,
3960                         fir::runtime::genMinvalChar, resultType, builder, loc,
3961                         stmtCtx, "unexpected result for Minval", args);
3962 }
3963 
3964 // MIN and MAX
3965 template <Extremum extremum, ExtremumBehavior behavior>
3966 mlir::Value IntrinsicLibrary::genExtremum(mlir::Type,
3967                                           llvm::ArrayRef<mlir::Value> args) {
3968   assert(args.size() >= 1);
3969   mlir::Value result = args[0];
3970   for (auto arg : args.drop_front()) {
3971     mlir::Value mask =
3972         createExtremumCompare<extremum, behavior>(loc, builder, result, arg);
3973     result = builder.create<mlir::arith::SelectOp>(loc, mask, result, arg);
3974   }
3975   return result;
3976 }
3977 
3978 //===----------------------------------------------------------------------===//
3979 // Argument lowering rules interface
3980 //===----------------------------------------------------------------------===//
3981 
3982 const Fortran::lower::IntrinsicArgumentLoweringRules *
3983 Fortran::lower::getIntrinsicArgumentLowering(llvm::StringRef intrinsicName) {
3984   if (const IntrinsicHandler *handler = findIntrinsicHandler(intrinsicName))
3985     if (!handler->argLoweringRules.hasDefaultRules())
3986       return &handler->argLoweringRules;
3987   return nullptr;
3988 }
3989 
3990 /// Return how argument \p argName should be lowered given the rules for the
3991 /// intrinsic function.
3992 Fortran::lower::ArgLoweringRule Fortran::lower::lowerIntrinsicArgumentAs(
3993     const IntrinsicArgumentLoweringRules &rules, unsigned position) {
3994   assert(position < sizeof(rules.args) / sizeof(decltype(*rules.args)) &&
3995          "invalid argument");
3996   return {rules.args[position].lowerAs,
3997           rules.args[position].handleDynamicOptional};
3998 }
3999 
4000 //===----------------------------------------------------------------------===//
4001 // Public intrinsic call helpers
4002 //===----------------------------------------------------------------------===//
4003 
4004 fir::ExtendedValue
4005 Fortran::lower::genIntrinsicCall(fir::FirOpBuilder &builder, mlir::Location loc,
4006                                  llvm::StringRef name,
4007                                  llvm::Optional<mlir::Type> resultType,
4008                                  llvm::ArrayRef<fir::ExtendedValue> args,
4009                                  Fortran::lower::StatementContext &stmtCtx) {
4010   return IntrinsicLibrary{builder, loc, &stmtCtx}.genIntrinsicCall(
4011       name, resultType, args);
4012 }
4013 
4014 mlir::Value Fortran::lower::genMax(fir::FirOpBuilder &builder,
4015                                    mlir::Location loc,
4016                                    llvm::ArrayRef<mlir::Value> args) {
4017   assert(args.size() > 0 && "max requires at least one argument");
4018   return IntrinsicLibrary{builder, loc}
4019       .genExtremum<Extremum::Max, ExtremumBehavior::MinMaxss>(args[0].getType(),
4020                                                               args);
4021 }
4022 
4023 mlir::Value Fortran::lower::genMin(fir::FirOpBuilder &builder,
4024                                    mlir::Location loc,
4025                                    llvm::ArrayRef<mlir::Value> args) {
4026   assert(args.size() > 0 && "min requires at least one argument");
4027   return IntrinsicLibrary{builder, loc}
4028       .genExtremum<Extremum::Min, ExtremumBehavior::MinMaxss>(args[0].getType(),
4029                                                               args);
4030 }
4031 
4032 mlir::Value Fortran::lower::genPow(fir::FirOpBuilder &builder,
4033                                    mlir::Location loc, mlir::Type type,
4034                                    mlir::Value x, mlir::Value y) {
4035   return IntrinsicLibrary{builder, loc}.genRuntimeCall("pow", type, {x, y});
4036 }
4037 
4038 mlir::SymbolRefAttr Fortran::lower::getUnrestrictedIntrinsicSymbolRefAttr(
4039     fir::FirOpBuilder &builder, mlir::Location loc, llvm::StringRef name,
4040     mlir::FunctionType signature) {
4041   return IntrinsicLibrary{builder, loc}.getUnrestrictedIntrinsicSymbolRefAttr(
4042       name, signature);
4043 }
4044