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