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