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