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