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