1 //===-- IntrinsicCall.cpp -------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // Helper routines for constructing the FIR dialect of MLIR. As FIR is a
10 // dialect of MLIR, it makes extensive use of MLIR interfaces and MLIR's coding
11 // style (https://mlir.llvm.org/getting_started/DeveloperGuide/) is used in this
12 // module.
13 //
14 //===----------------------------------------------------------------------===//
15 
16 #include "flang/Lower/IntrinsicCall.h"
17 #include "flang/Common/static-multimap-view.h"
18 #include "flang/Lower/Mangler.h"
19 #include "flang/Lower/Runtime.h"
20 #include "flang/Lower/StatementContext.h"
21 #include "flang/Lower/SymbolMap.h"
22 #include "flang/Lower/Todo.h"
23 #include "flang/Optimizer/Builder/Character.h"
24 #include "flang/Optimizer/Builder/Complex.h"
25 #include "flang/Optimizer/Builder/FIRBuilder.h"
26 #include "flang/Optimizer/Builder/MutableBox.h"
27 #include "flang/Optimizer/Builder/Runtime/Character.h"
28 #include "flang/Optimizer/Builder/Runtime/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/Transformational.h"
33 #include "flang/Optimizer/Dialect/FIROpsSupport.h"
34 #include "flang/Optimizer/Support/FatalError.h"
35 #include "mlir/Dialect/LLVMIR/LLVMDialect.h"
36 #include "llvm/Support/CommandLine.h"
37 #include "llvm/Support/Debug.h"
38 
39 #define DEBUG_TYPE "flang-lower-intrinsic"
40 
41 #define PGMATH_DECLARE
42 #include "flang/Evaluate/pgmath.h.inc"
43 
44 /// This file implements lowering of Fortran intrinsic procedures.
45 /// Intrinsics are lowered to a mix of FIR and MLIR operations as
46 /// well as call to runtime functions or LLVM intrinsics.
47 
48 /// Lowering of intrinsic procedure calls is based on a map that associates
49 /// Fortran intrinsic generic names to FIR generator functions.
50 /// All generator functions are member functions of the IntrinsicLibrary class
51 /// and have the same interface.
52 /// If no generator is given for an intrinsic name, a math runtime library
53 /// is searched for an implementation and, if a runtime function is found,
54 /// a call is generated for it. LLVM intrinsics are handled as a math
55 /// runtime library here.
56 
57 /// Enums used to templatize and share lowering of MIN and MAX.
58 enum class Extremum { Min, Max };
59 
60 // There are different ways to deal with NaNs in MIN and MAX.
61 // Known existing behaviors are listed below and can be selected for
62 // f18 MIN/MAX implementation.
63 enum class ExtremumBehavior {
64   // Note: the Signaling/quiet aspect of NaNs in the behaviors below are
65   // not described because there is no way to control/observe such aspect in
66   // MLIR/LLVM yet. The IEEE behaviors come with requirements regarding this
67   // aspect that are therefore currently not enforced. In the descriptions
68   // below, NaNs can be signaling or quite. Returned NaNs may be signaling
69   // if one of the input NaN was signaling but it cannot be guaranteed either.
70   // Existing compilers using an IEEE behavior (gfortran) also do not fulfill
71   // signaling/quiet requirements.
72   IeeeMinMaximumNumber,
73   // IEEE minimumNumber/maximumNumber behavior (754-2019, section 9.6):
74   // If one of the argument is and number and the other is NaN, return the
75   // number. If both arguements are NaN, return NaN.
76   // Compilers: gfortran.
77   IeeeMinMaximum,
78   // IEEE minimum/maximum behavior (754-2019, section 9.6):
79   // If one of the argument is NaN, return NaN.
80   MinMaxss,
81   // x86 minss/maxss behavior:
82   // If the second argument is a number and the other is NaN, return the number.
83   // In all other cases where at least one operand is NaN, return NaN.
84   // Compilers: xlf (only for MAX), ifort, pgfortran -nollvm, and nagfor.
85   PgfortranLlvm,
86   // "Opposite of" x86 minss/maxss behavior:
87   // If the first argument is a number and the other is NaN, return the
88   // number.
89   // In all other cases where at least one operand is NaN, return NaN.
90   // Compilers: xlf (only for MIN), and pgfortran (with llvm).
91   IeeeMinMaxNum
92   // IEEE minNum/maxNum behavior (754-2008, section 5.3.1):
93   // TODO: Not implemented.
94   // It is the only behavior where the signaling/quiet aspect of a NaN argument
95   // impacts if the result should be NaN or the argument that is a number.
96   // LLVM/MLIR do not provide ways to observe this aspect, so it is not
97   // possible to implement it without some target dependent runtime.
98 };
99 
100 fir::ExtendedValue Fortran::lower::getAbsentIntrinsicArgument() {
101   return fir::UnboxedValue{};
102 }
103 
104 /// Test if an ExtendedValue is absent.
105 static bool isAbsent(const fir::ExtendedValue &exv) {
106   return !fir::getBase(exv);
107 }
108 static bool isAbsent(llvm::ArrayRef<fir::ExtendedValue> args, size_t argIndex) {
109   return args.size() <= argIndex || isAbsent(args[argIndex]);
110 }
111 
112 /// Test if an ExtendedValue is present.
113 static bool isPresent(const fir::ExtendedValue &exv) { return !isAbsent(exv); }
114 
115 /// Process calls to Maxval, Minval, Product, Sum intrinsic functions that
116 /// take a DIM argument.
117 template <typename FD>
118 static fir::ExtendedValue
119 genFuncDim(FD funcDim, mlir::Type resultType, fir::FirOpBuilder &builder,
120            mlir::Location loc, Fortran::lower::StatementContext *stmtCtx,
121            llvm::StringRef errMsg, mlir::Value array, fir::ExtendedValue dimArg,
122            mlir::Value mask, int rank) {
123 
124   // Create mutable fir.box to be passed to the runtime for the result.
125   mlir::Type resultArrayType = builder.getVarLenSeqTy(resultType, rank - 1);
126   fir::MutableBoxValue resultMutableBox =
127       fir::factory::createTempMutableBox(builder, loc, resultArrayType);
128   mlir::Value resultIrBox =
129       fir::factory::getMutableIRBox(builder, loc, resultMutableBox);
130 
131   mlir::Value dim =
132       isAbsent(dimArg)
133           ? builder.createIntegerConstant(loc, builder.getIndexType(), 0)
134           : fir::getBase(dimArg);
135   funcDim(builder, loc, resultIrBox, array, dim, mask);
136 
137   fir::ExtendedValue res =
138       fir::factory::genMutableBoxRead(builder, loc, resultMutableBox);
139   return res.match(
140       [&](const fir::ArrayBoxValue &box) -> fir::ExtendedValue {
141         // Add cleanup code
142         assert(stmtCtx);
143         fir::FirOpBuilder *bldr = &builder;
144         mlir::Value temp = box.getAddr();
145         stmtCtx->attachCleanup(
146             [=]() { bldr->create<fir::FreeMemOp>(loc, temp); });
147         return box;
148       },
149       [&](const fir::CharArrayBoxValue &box) -> fir::ExtendedValue {
150         // Add cleanup code
151         assert(stmtCtx);
152         fir::FirOpBuilder *bldr = &builder;
153         mlir::Value temp = box.getAddr();
154         stmtCtx->attachCleanup(
155             [=]() { bldr->create<fir::FreeMemOp>(loc, temp); });
156         return box;
157       },
158       [&](const auto &) -> fir::ExtendedValue {
159         fir::emitFatalError(loc, errMsg);
160       });
161 }
162 
163 /// Process calls to Product, Sum intrinsic functions
164 template <typename FN, typename FD>
165 static fir::ExtendedValue
166 genProdOrSum(FN func, FD funcDim, mlir::Type resultType,
167              fir::FirOpBuilder &builder, mlir::Location loc,
168              Fortran::lower::StatementContext *stmtCtx, llvm::StringRef errMsg,
169              llvm::ArrayRef<fir::ExtendedValue> args) {
170 
171   assert(args.size() == 3);
172 
173   // Handle required array argument
174   fir::BoxValue arryTmp = builder.createBox(loc, args[0]);
175   mlir::Value array = fir::getBase(arryTmp);
176   int rank = arryTmp.rank();
177   assert(rank >= 1);
178 
179   // Handle optional mask argument
180   auto mask = isAbsent(args[2])
181                   ? builder.create<fir::AbsentOp>(
182                         loc, fir::BoxType::get(builder.getI1Type()))
183                   : builder.createBox(loc, args[2]);
184 
185   bool absentDim = isAbsent(args[1]);
186 
187   // We call the type specific versions because the result is scalar
188   // in the case below.
189   if (absentDim || rank == 1) {
190     mlir::Type ty = array.getType();
191     mlir::Type arrTy = fir::dyn_cast_ptrOrBoxEleTy(ty);
192     auto eleTy = arrTy.cast<fir::SequenceType>().getEleTy();
193     if (fir::isa_complex(eleTy)) {
194       mlir::Value result = builder.createTemporary(loc, eleTy);
195       func(builder, loc, array, mask, result);
196       return builder.create<fir::LoadOp>(loc, result);
197     }
198     auto resultBox = builder.create<fir::AbsentOp>(
199         loc, fir::BoxType::get(builder.getI1Type()));
200     return func(builder, loc, array, mask, resultBox);
201   }
202   // Handle Product/Sum cases that have an array result.
203   return genFuncDim(funcDim, resultType, builder, loc, stmtCtx, errMsg, array,
204                     args[1], mask, rank);
205 }
206 
207 /// Process calls to DotProduct
208 template <typename FN>
209 static fir::ExtendedValue
210 genDotProd(FN func, mlir::Type resultType, fir::FirOpBuilder &builder,
211            mlir::Location loc, Fortran::lower::StatementContext *stmtCtx,
212            llvm::ArrayRef<fir::ExtendedValue> args) {
213 
214   assert(args.size() == 2);
215 
216   // Handle required vector arguments
217   mlir::Value vectorA = fir::getBase(args[0]);
218   mlir::Value vectorB = fir::getBase(args[1]);
219 
220   mlir::Type eleTy = fir::dyn_cast_ptrOrBoxEleTy(vectorA.getType())
221                          .cast<fir::SequenceType>()
222                          .getEleTy();
223   if (fir::isa_complex(eleTy)) {
224     mlir::Value result = builder.createTemporary(loc, eleTy);
225     func(builder, loc, vectorA, vectorB, result);
226     return builder.create<fir::LoadOp>(loc, result);
227   }
228 
229   auto resultBox = builder.create<fir::AbsentOp>(
230       loc, fir::BoxType::get(builder.getI1Type()));
231   return func(builder, loc, vectorA, vectorB, resultBox);
232 }
233 
234 /// Process calls to Maxval, Minval, Product, Sum intrinsic functions
235 template <typename FN, typename FD, typename FC>
236 static fir::ExtendedValue
237 genExtremumVal(FN func, FD funcDim, FC funcChar, mlir::Type resultType,
238                fir::FirOpBuilder &builder, mlir::Location loc,
239                Fortran::lower::StatementContext *stmtCtx,
240                llvm::StringRef errMsg,
241                llvm::ArrayRef<fir::ExtendedValue> args) {
242 
243   assert(args.size() == 3);
244 
245   // Handle required array argument
246   fir::BoxValue arryTmp = builder.createBox(loc, args[0]);
247   mlir::Value array = fir::getBase(arryTmp);
248   int rank = arryTmp.rank();
249   assert(rank >= 1);
250   bool hasCharacterResult = arryTmp.isCharacter();
251 
252   // Handle optional mask argument
253   auto mask = isAbsent(args[2])
254                   ? builder.create<fir::AbsentOp>(
255                         loc, fir::BoxType::get(builder.getI1Type()))
256                   : builder.createBox(loc, args[2]);
257 
258   bool absentDim = isAbsent(args[1]);
259 
260   // For Maxval/MinVal, we call the type specific versions of
261   // Maxval/Minval because the result is scalar in the case below.
262   if (!hasCharacterResult && (absentDim || rank == 1))
263     return func(builder, loc, array, mask);
264 
265   if (hasCharacterResult && (absentDim || rank == 1)) {
266     // Create mutable fir.box to be passed to the runtime for the result.
267     fir::MutableBoxValue resultMutableBox =
268         fir::factory::createTempMutableBox(builder, loc, resultType);
269     mlir::Value resultIrBox =
270         fir::factory::getMutableIRBox(builder, loc, resultMutableBox);
271 
272     funcChar(builder, loc, resultIrBox, array, mask);
273 
274     // Handle cleanup of allocatable result descriptor and return
275     fir::ExtendedValue res =
276         fir::factory::genMutableBoxRead(builder, loc, resultMutableBox);
277     return res.match(
278         [&](const fir::CharBoxValue &box) -> fir::ExtendedValue {
279           // Add cleanup code
280           assert(stmtCtx);
281           fir::FirOpBuilder *bldr = &builder;
282           mlir::Value temp = box.getAddr();
283           stmtCtx->attachCleanup(
284               [=]() { bldr->create<fir::FreeMemOp>(loc, temp); });
285           return box;
286         },
287         [&](const auto &) -> fir::ExtendedValue {
288           fir::emitFatalError(loc, errMsg);
289         });
290   }
291 
292   // Handle Min/Maxval cases that have an array result.
293   return genFuncDim(funcDim, resultType, builder, loc, stmtCtx, errMsg, array,
294                     args[1], mask, rank);
295 }
296 
297 /// Process calls to Minloc, Maxloc intrinsic functions
298 template <typename FN, typename FD>
299 static fir::ExtendedValue genExtremumloc(
300     FN func, FD funcDim, mlir::Type resultType, fir::FirOpBuilder &builder,
301     mlir::Location loc, Fortran::lower::StatementContext *stmtCtx,
302     llvm::StringRef errMsg, llvm::ArrayRef<fir::ExtendedValue> args) {
303 
304   assert(args.size() == 5);
305 
306   // Handle required array argument
307   mlir::Value array = builder.createBox(loc, args[0]);
308   unsigned rank = fir::BoxValue(array).rank();
309   assert(rank >= 1);
310 
311   // Handle optional mask argument
312   auto mask = isAbsent(args[2])
313                   ? builder.create<fir::AbsentOp>(
314                         loc, fir::BoxType::get(builder.getI1Type()))
315                   : builder.createBox(loc, args[2]);
316 
317   // Handle optional kind argument
318   auto kind = isAbsent(args[3]) ? builder.createIntegerConstant(
319                                       loc, builder.getIndexType(),
320                                       builder.getKindMap().defaultIntegerKind())
321                                 : fir::getBase(args[3]);
322 
323   // Handle optional back argument
324   auto back = isAbsent(args[4]) ? builder.createBool(loc, false)
325                                 : fir::getBase(args[4]);
326 
327   bool absentDim = isAbsent(args[1]);
328 
329   if (!absentDim && rank == 1) {
330     // If dim argument is present and the array is rank 1, then the result is
331     // a scalar (since the the result is rank-1 or 0).
332     // Therefore, we use a scalar result descriptor with Min/MaxlocDim().
333     mlir::Value dim = fir::getBase(args[1]);
334     // Create mutable fir.box to be passed to the runtime for the result.
335     fir::MutableBoxValue resultMutableBox =
336         fir::factory::createTempMutableBox(builder, loc, resultType);
337     mlir::Value resultIrBox =
338         fir::factory::getMutableIRBox(builder, loc, resultMutableBox);
339 
340     funcDim(builder, loc, resultIrBox, array, dim, mask, kind, back);
341 
342     // Handle cleanup of allocatable result descriptor and return
343     fir::ExtendedValue res =
344         fir::factory::genMutableBoxRead(builder, loc, resultMutableBox);
345     return res.match(
346         [&](const mlir::Value &tempAddr) -> fir::ExtendedValue {
347           // Add cleanup code
348           assert(stmtCtx);
349           fir::FirOpBuilder *bldr = &builder;
350           stmtCtx->attachCleanup(
351               [=]() { bldr->create<fir::FreeMemOp>(loc, tempAddr); });
352           return builder.create<fir::LoadOp>(loc, resultType, tempAddr);
353         },
354         [&](const auto &) -> fir::ExtendedValue {
355           fir::emitFatalError(loc, errMsg);
356         });
357   }
358 
359   // Note: The Min/Maxloc/val cases below have an array result.
360 
361   // Create mutable fir.box to be passed to the runtime for the result.
362   mlir::Type resultArrayType =
363       builder.getVarLenSeqTy(resultType, absentDim ? 1 : rank - 1);
364   fir::MutableBoxValue resultMutableBox =
365       fir::factory::createTempMutableBox(builder, loc, resultArrayType);
366   mlir::Value resultIrBox =
367       fir::factory::getMutableIRBox(builder, loc, resultMutableBox);
368 
369   if (absentDim) {
370     // Handle min/maxloc/val case where there is no dim argument
371     // (calls Min/Maxloc()/MinMaxval() runtime routine)
372     func(builder, loc, resultIrBox, array, mask, kind, back);
373   } else {
374     // else handle min/maxloc case with dim argument (calls
375     // Min/Max/loc/val/Dim() runtime routine).
376     mlir::Value dim = fir::getBase(args[1]);
377     funcDim(builder, loc, resultIrBox, array, dim, mask, kind, back);
378   }
379 
380   return fir::factory::genMutableBoxRead(builder, loc, resultMutableBox)
381       .match(
382           [&](const fir::ArrayBoxValue &box) -> fir::ExtendedValue {
383             // Add cleanup code
384             assert(stmtCtx);
385             fir::FirOpBuilder *bldr = &builder;
386             mlir::Value temp = box.getAddr();
387             stmtCtx->attachCleanup(
388                 [=]() { bldr->create<fir::FreeMemOp>(loc, temp); });
389             return box;
390           },
391           [&](const auto &) -> fir::ExtendedValue {
392             fir::emitFatalError(loc, errMsg);
393           });
394 }
395 
396 // TODO error handling -> return a code or directly emit messages ?
397 struct IntrinsicLibrary {
398 
399   // Constructors.
400   explicit IntrinsicLibrary(fir::FirOpBuilder &builder, mlir::Location loc,
401                             Fortran::lower::StatementContext *stmtCtx = nullptr)
402       : builder{builder}, loc{loc}, stmtCtx{stmtCtx} {}
403   IntrinsicLibrary() = delete;
404   IntrinsicLibrary(const IntrinsicLibrary &) = delete;
405 
406   /// Generate FIR for call to Fortran intrinsic \p name with arguments \p arg
407   /// and expected result type \p resultType.
408   fir::ExtendedValue genIntrinsicCall(llvm::StringRef name,
409                                       llvm::Optional<mlir::Type> resultType,
410                                       llvm::ArrayRef<fir::ExtendedValue> arg);
411 
412   /// Search a runtime function that is associated to the generic intrinsic name
413   /// and whose signature matches the intrinsic arguments and result types.
414   /// If no such runtime function is found but a runtime function associated
415   /// with the Fortran generic exists and has the same number of arguments,
416   /// conversions will be inserted before and/or after the call. This is to
417   /// mainly to allow 16 bits float support even-though little or no math
418   /// runtime is currently available for it.
419   mlir::Value genRuntimeCall(llvm::StringRef name, mlir::Type,
420                              llvm::ArrayRef<mlir::Value>);
421 
422   using RuntimeCallGenerator = std::function<mlir::Value(
423       fir::FirOpBuilder &, mlir::Location, llvm::ArrayRef<mlir::Value>)>;
424   RuntimeCallGenerator
425   getRuntimeCallGenerator(llvm::StringRef name,
426                           mlir::FunctionType soughtFuncType);
427 
428   /// Lowering for the ABS intrinsic. The ABS intrinsic expects one argument in
429   /// the llvm::ArrayRef. The ABS intrinsic is lowered into MLIR/FIR operation
430   /// if the argument is an integer, into llvm intrinsics if the argument is
431   /// real and to the `hypot` math routine if the argument is of complex type.
432   mlir::Value genAbs(mlir::Type, llvm::ArrayRef<mlir::Value>);
433   template <void (*CallRuntime)(fir::FirOpBuilder &, mlir::Location loc,
434                                 mlir::Value, mlir::Value)>
435   fir::ExtendedValue genAdjustRtCall(mlir::Type,
436                                      llvm::ArrayRef<fir::ExtendedValue>);
437   mlir::Value genAimag(mlir::Type, llvm::ArrayRef<mlir::Value>);
438   fir::ExtendedValue genAll(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
439   fir::ExtendedValue genAllocated(mlir::Type,
440                                   llvm::ArrayRef<fir::ExtendedValue>);
441   fir::ExtendedValue genAny(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
442   fir::ExtendedValue genAssociated(mlir::Type,
443                                    llvm::ArrayRef<fir::ExtendedValue>);
444   fir::ExtendedValue genChar(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
445   fir::ExtendedValue genCount(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
446   template <mlir::arith::CmpIPredicate pred>
447   fir::ExtendedValue genCharacterCompare(mlir::Type,
448                                          llvm::ArrayRef<fir::ExtendedValue>);
449   void genCpuTime(llvm::ArrayRef<fir::ExtendedValue>);
450   fir::ExtendedValue genCshift(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
451   void genDateAndTime(llvm::ArrayRef<fir::ExtendedValue>);
452   mlir::Value genDim(mlir::Type, llvm::ArrayRef<mlir::Value>);
453   fir::ExtendedValue genDotProduct(mlir::Type,
454                                    llvm::ArrayRef<fir::ExtendedValue>);
455   fir::ExtendedValue genEoshift(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
456   mlir::Value genExponent(mlir::Type, llvm::ArrayRef<mlir::Value>);
457   template <Extremum, ExtremumBehavior>
458   mlir::Value genExtremum(mlir::Type, llvm::ArrayRef<mlir::Value>);
459   mlir::Value genFloor(mlir::Type, llvm::ArrayRef<mlir::Value>);
460   mlir::Value genFraction(mlir::Type resultType,
461                           mlir::ArrayRef<mlir::Value> args);
462   /// Lowering for the IAND intrinsic. The IAND intrinsic expects two arguments
463   /// in the llvm::ArrayRef.
464   mlir::Value genIand(mlir::Type, llvm::ArrayRef<mlir::Value>);
465   mlir::Value genIbclr(mlir::Type, llvm::ArrayRef<mlir::Value>);
466   mlir::Value genIbits(mlir::Type, llvm::ArrayRef<mlir::Value>);
467   mlir::Value genIbset(mlir::Type, llvm::ArrayRef<mlir::Value>);
468   fir::ExtendedValue genIchar(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
469   mlir::Value genIeor(mlir::Type, llvm::ArrayRef<mlir::Value>);
470   mlir::Value genIshft(mlir::Type, llvm::ArrayRef<mlir::Value>);
471   mlir::Value genIshftc(mlir::Type, llvm::ArrayRef<mlir::Value>);
472   fir::ExtendedValue genLbound(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
473   fir::ExtendedValue genLen(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
474   fir::ExtendedValue genLenTrim(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
475   fir::ExtendedValue genMaxloc(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
476   fir::ExtendedValue genMaxval(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
477   fir::ExtendedValue genMinloc(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
478   fir::ExtendedValue genMinval(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
479   mlir::Value genMod(mlir::Type, llvm::ArrayRef<mlir::Value>);
480   mlir::Value genModulo(mlir::Type, llvm::ArrayRef<mlir::Value>);
481   mlir::Value genNint(mlir::Type, llvm::ArrayRef<mlir::Value>);
482   mlir::Value genNot(mlir::Type, llvm::ArrayRef<mlir::Value>);
483   fir::ExtendedValue genNull(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
484   fir::ExtendedValue genPack(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
485   fir::ExtendedValue genProduct(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
486   void genRandomInit(llvm::ArrayRef<fir::ExtendedValue>);
487   void genRandomNumber(llvm::ArrayRef<fir::ExtendedValue>);
488   void genRandomSeed(llvm::ArrayRef<fir::ExtendedValue>);
489   fir::ExtendedValue genScan(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
490   mlir::Value genSetExponent(mlir::Type resultType,
491                              llvm::ArrayRef<mlir::Value> args);
492   fir::ExtendedValue genSize(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
493   fir::ExtendedValue genSum(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
494   void genSystemClock(llvm::ArrayRef<fir::ExtendedValue>);
495   fir::ExtendedValue genTransfer(mlir::Type,
496                                  llvm::ArrayRef<fir::ExtendedValue>);
497   fir::ExtendedValue genUbound(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
498   fir::ExtendedValue genUnpack(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
499   fir::ExtendedValue genVerify(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
500 
501   /// Define the different FIR generators that can be mapped to intrinsic to
502   /// generate the related code.
503   using ElementalGenerator = decltype(&IntrinsicLibrary::genAbs);
504   using ExtendedGenerator = decltype(&IntrinsicLibrary::genSum);
505   using SubroutineGenerator = decltype(&IntrinsicLibrary::genRandomInit);
506   using Generator =
507       std::variant<ElementalGenerator, ExtendedGenerator, SubroutineGenerator>;
508 
509   template <typename GeneratorType>
510   fir::ExtendedValue
511   outlineInExtendedWrapper(GeneratorType, llvm::StringRef name,
512                            llvm::Optional<mlir::Type> resultType,
513                            llvm::ArrayRef<fir::ExtendedValue> args);
514 
515   template <typename GeneratorType>
516   mlir::FuncOp getWrapper(GeneratorType, llvm::StringRef name,
517                           mlir::FunctionType, bool loadRefArguments = false);
518 
519   /// Generate calls to ElementalGenerator, handling the elemental aspects
520   template <typename GeneratorType>
521   fir::ExtendedValue
522   genElementalCall(GeneratorType, llvm::StringRef name, mlir::Type resultType,
523                    llvm::ArrayRef<fir::ExtendedValue> args, bool outline);
524 
525   /// Helper to invoke code generator for the intrinsics given arguments.
526   mlir::Value invokeGenerator(ElementalGenerator generator,
527                               mlir::Type resultType,
528                               llvm::ArrayRef<mlir::Value> args);
529   mlir::Value invokeGenerator(RuntimeCallGenerator generator,
530                               mlir::Type resultType,
531                               llvm::ArrayRef<mlir::Value> args);
532   mlir::Value invokeGenerator(ExtendedGenerator generator,
533                               mlir::Type resultType,
534                               llvm::ArrayRef<mlir::Value> args);
535   mlir::Value invokeGenerator(SubroutineGenerator generator,
536                               llvm::ArrayRef<mlir::Value> args);
537 
538   /// Add clean-up for \p temp to the current statement context;
539   void addCleanUpForTemp(mlir::Location loc, mlir::Value temp);
540   /// Helper function for generating code clean-up for result descriptors
541   fir::ExtendedValue readAndAddCleanUp(fir::MutableBoxValue resultMutableBox,
542                                        mlir::Type resultType,
543                                        llvm::StringRef errMsg);
544 
545   fir::FirOpBuilder &builder;
546   mlir::Location loc;
547   Fortran::lower::StatementContext *stmtCtx;
548 };
549 
550 struct IntrinsicDummyArgument {
551   const char *name = nullptr;
552   Fortran::lower::LowerIntrinsicArgAs lowerAs =
553       Fortran::lower::LowerIntrinsicArgAs::Value;
554   bool handleDynamicOptional = false;
555 };
556 
557 struct Fortran::lower::IntrinsicArgumentLoweringRules {
558   /// There is no more than 7 non repeated arguments in Fortran intrinsics.
559   IntrinsicDummyArgument args[7];
560   constexpr bool hasDefaultRules() const { return args[0].name == nullptr; }
561 };
562 
563 /// Structure describing what needs to be done to lower intrinsic "name".
564 struct IntrinsicHandler {
565   const char *name;
566   IntrinsicLibrary::Generator generator;
567   // The following may be omitted in the table below.
568   Fortran::lower::IntrinsicArgumentLoweringRules argLoweringRules = {};
569   bool isElemental = true;
570   /// Code heavy intrinsic can be outlined to make FIR
571   /// more readable.
572   bool outline = false;
573 };
574 
575 constexpr auto asValue = Fortran::lower::LowerIntrinsicArgAs::Value;
576 constexpr auto asAddr = Fortran::lower::LowerIntrinsicArgAs::Addr;
577 constexpr auto asBox = Fortran::lower::LowerIntrinsicArgAs::Box;
578 constexpr auto asInquired = Fortran::lower::LowerIntrinsicArgAs::Inquired;
579 using I = IntrinsicLibrary;
580 
581 /// Flag to indicate that an intrinsic argument has to be handled as
582 /// being dynamically optional (e.g. special handling when actual
583 /// argument is an optional variable in the current scope).
584 static constexpr bool handleDynamicOptional = true;
585 
586 /// Table that drives the fir generation depending on the intrinsic.
587 /// one to one mapping with Fortran arguments. If no mapping is
588 /// defined here for a generic intrinsic, genRuntimeCall will be called
589 /// to look for a match in the runtime a emit a call. Note that the argument
590 /// lowering rules for an intrinsic need to be provided only if at least one
591 /// argument must not be lowered by value. In which case, the lowering rules
592 /// should be provided for all the intrinsic arguments for completeness.
593 static constexpr IntrinsicHandler handlers[]{
594     {"abs", &I::genAbs},
595     {"adjustl",
596      &I::genAdjustRtCall<fir::runtime::genAdjustL>,
597      {{{"string", asAddr}}},
598      /*isElemental=*/true},
599     {"adjustr",
600      &I::genAdjustRtCall<fir::runtime::genAdjustR>,
601      {{{"string", asAddr}}},
602      /*isElemental=*/true},
603     {"aimag", &I::genAimag},
604     {"all",
605      &I::genAll,
606      {{{"mask", asAddr}, {"dim", asValue}}},
607      /*isElemental=*/false},
608     {"allocated",
609      &I::genAllocated,
610      {{{"array", asInquired}, {"scalar", asInquired}}},
611      /*isElemental=*/false},
612     {"any",
613      &I::genAny,
614      {{{"mask", asAddr}, {"dim", asValue}}},
615      /*isElemental=*/false},
616     {"associated",
617      &I::genAssociated,
618      {{{"pointer", asInquired}, {"target", asInquired}}},
619      /*isElemental=*/false},
620     {"char", &I::genChar},
621     {"count",
622      &I::genCount,
623      {{{"mask", asAddr}, {"dim", asValue}, {"kind", asValue}}},
624      /*isElemental=*/false},
625     {"cpu_time",
626      &I::genCpuTime,
627      {{{"time", asAddr}}},
628      /*isElemental=*/false},
629     {"cshift",
630      &I::genCshift,
631      {{{"array", asAddr}, {"shift", asAddr}, {"dim", asValue}}},
632      /*isElemental=*/false},
633     {"date_and_time",
634      &I::genDateAndTime,
635      {{{"date", asAddr, handleDynamicOptional},
636        {"time", asAddr, handleDynamicOptional},
637        {"zone", asAddr, handleDynamicOptional},
638        {"values", asBox, handleDynamicOptional}}},
639      /*isElemental=*/false},
640     {"dim", &I::genDim},
641     {"dot_product",
642      &I::genDotProduct,
643      {{{"vector_a", asBox}, {"vector_b", asBox}}},
644      /*isElemental=*/false},
645     {"eoshift",
646      &I::genEoshift,
647      {{{"array", asBox},
648        {"shift", asAddr},
649        {"boundary", asBox, handleDynamicOptional},
650        {"dim", asValue}}},
651      /*isElemental=*/false},
652     {"exponent", &I::genExponent},
653     {"floor", &I::genFloor},
654     {"fraction", &I::genFraction},
655     {"iachar", &I::genIchar},
656     {"iand", &I::genIand},
657     {"ibclr", &I::genIbclr},
658     {"ibits", &I::genIbits},
659     {"ibset", &I::genIbset},
660     {"ichar", &I::genIchar},
661     {"ieor", &I::genIeor},
662     {"ishft", &I::genIshft},
663     {"ishftc", &I::genIshftc},
664     {"len",
665      &I::genLen,
666      {{{"string", asInquired}, {"kind", asValue}}},
667      /*isElemental=*/false},
668     {"len_trim", &I::genLenTrim},
669     {"lge", &I::genCharacterCompare<mlir::arith::CmpIPredicate::sge>},
670     {"lgt", &I::genCharacterCompare<mlir::arith::CmpIPredicate::sgt>},
671     {"lle", &I::genCharacterCompare<mlir::arith::CmpIPredicate::sle>},
672     {"llt", &I::genCharacterCompare<mlir::arith::CmpIPredicate::slt>},
673     {"max", &I::genExtremum<Extremum::Max, ExtremumBehavior::MinMaxss>},
674     {"maxloc",
675      &I::genMaxloc,
676      {{{"array", asBox},
677        {"dim", asValue},
678        {"mask", asBox, handleDynamicOptional},
679        {"kind", asValue},
680        {"back", asValue, handleDynamicOptional}}},
681      /*isElemental=*/false},
682     {"maxval",
683      &I::genMaxval,
684      {{{"array", asBox},
685        {"dim", asValue},
686        {"mask", asBox, handleDynamicOptional}}},
687      /*isElemental=*/false},
688     {"min", &I::genExtremum<Extremum::Min, ExtremumBehavior::MinMaxss>},
689     {"minloc",
690      &I::genMinloc,
691      {{{"array", asBox},
692        {"dim", asValue},
693        {"mask", asBox, handleDynamicOptional},
694        {"kind", asValue},
695        {"back", asValue, handleDynamicOptional}}},
696      /*isElemental=*/false},
697     {"minval",
698      &I::genMinval,
699      {{{"array", asBox},
700        {"dim", asValue},
701        {"mask", asBox, handleDynamicOptional}}},
702      /*isElemental=*/false},
703     {"mod", &I::genMod},
704     {"modulo", &I::genModulo},
705     {"nint", &I::genNint},
706     {"not", &I::genNot},
707     {"null", &I::genNull, {{{"mold", asInquired}}}, /*isElemental=*/false},
708     {"pack",
709      &I::genPack,
710      {{{"array", asBox},
711        {"mask", asBox},
712        {"vector", asBox, handleDynamicOptional}}},
713      /*isElemental=*/false},
714     {"product",
715      &I::genProduct,
716      {{{"array", asBox},
717        {"dim", asValue},
718        {"mask", asBox, handleDynamicOptional}}},
719      /*isElemental=*/false},
720     {"random_init",
721      &I::genRandomInit,
722      {{{"repeatable", asValue}, {"image_distinct", asValue}}},
723      /*isElemental=*/false},
724     {"random_number",
725      &I::genRandomNumber,
726      {{{"harvest", asBox}}},
727      /*isElemental=*/false},
728     {"random_seed",
729      &I::genRandomSeed,
730      {{{"size", asBox}, {"put", asBox}, {"get", asBox}}},
731      /*isElemental=*/false},
732     {"scan",
733      &I::genScan,
734      {{{"string", asAddr},
735        {"set", asAddr},
736        {"back", asValue, handleDynamicOptional},
737        {"kind", asValue}}},
738      /*isElemental=*/true},
739     {"set_exponent", &I::genSetExponent},
740     {"size",
741      &I::genSize,
742      {{{"array", asBox},
743        {"dim", asAddr, handleDynamicOptional},
744        {"kind", asValue}}},
745      /*isElemental=*/false},
746     {"sum",
747      &I::genSum,
748      {{{"array", asBox},
749        {"dim", asValue},
750        {"mask", asBox, handleDynamicOptional}}},
751      /*isElemental=*/false},
752     {"system_clock",
753      &I::genSystemClock,
754      {{{"count", asAddr}, {"count_rate", asAddr}, {"count_max", asAddr}}},
755      /*isElemental=*/false},
756     {"transfer",
757      &I::genTransfer,
758      {{{"source", asAddr}, {"mold", asAddr}, {"size", asValue}}},
759      /*isElemental=*/false},
760     {"ubound",
761      &I::genUbound,
762      {{{"array", asBox}, {"dim", asValue}, {"kind", asValue}}},
763      /*isElemental=*/false},
764     {"unpack",
765      &I::genUnpack,
766      {{{"vector", asBox}, {"mask", asBox}, {"field", asBox}}},
767      /*isElemental=*/false},
768     {"verify",
769      &I::genVerify,
770      {{{"string", asAddr},
771        {"set", asAddr},
772        {"back", asValue, handleDynamicOptional},
773        {"kind", asValue}}},
774      /*isElemental=*/true},
775 };
776 
777 static const IntrinsicHandler *findIntrinsicHandler(llvm::StringRef name) {
778   auto compare = [](const IntrinsicHandler &handler, llvm::StringRef name) {
779     return name.compare(handler.name) > 0;
780   };
781   auto result =
782       std::lower_bound(std::begin(handlers), std::end(handlers), name, compare);
783   return result != std::end(handlers) && result->name == name ? result
784                                                               : nullptr;
785 }
786 
787 /// To make fir output more readable for debug, one can outline all intrinsic
788 /// implementation in wrappers (overrides the IntrinsicHandler::outline flag).
789 static llvm::cl::opt<bool> outlineAllIntrinsics(
790     "outline-intrinsics",
791     llvm::cl::desc(
792         "Lower all intrinsic procedure implementation in their own functions"),
793     llvm::cl::init(false));
794 
795 //===----------------------------------------------------------------------===//
796 // Math runtime description and matching utility
797 //===----------------------------------------------------------------------===//
798 
799 /// Command line option to modify math runtime version used to implement
800 /// intrinsics.
801 enum MathRuntimeVersion { fastVersion, llvmOnly };
802 llvm::cl::opt<MathRuntimeVersion> mathRuntimeVersion(
803     "math-runtime", llvm::cl::desc("Select math runtime version:"),
804     llvm::cl::values(
805         clEnumValN(fastVersion, "fast", "use pgmath fast runtime"),
806         clEnumValN(llvmOnly, "llvm",
807                    "only use LLVM intrinsics (may be incomplete)")),
808     llvm::cl::init(fastVersion));
809 
810 struct RuntimeFunction {
811   // llvm::StringRef comparison operator are not constexpr, so use string_view.
812   using Key = std::string_view;
813   // Needed for implicit compare with keys.
814   constexpr operator Key() const { return key; }
815   Key key; // intrinsic name
816   llvm::StringRef symbol;
817   fir::runtime::FuncTypeBuilderFunc typeGenerator;
818 };
819 
820 #define RUNTIME_STATIC_DESCRIPTION(name, func)                                 \
821   {#name, #func, fir::runtime::RuntimeTableKey<decltype(func)>::getTypeModel()},
822 static constexpr RuntimeFunction pgmathFast[] = {
823 #define PGMATH_FAST
824 #define PGMATH_USE_ALL_TYPES(name, func) RUNTIME_STATIC_DESCRIPTION(name, func)
825 #include "flang/Evaluate/pgmath.h.inc"
826 };
827 
828 static mlir::FunctionType genF32F32FuncType(mlir::MLIRContext *context) {
829   mlir::Type t = mlir::FloatType::getF32(context);
830   return mlir::FunctionType::get(context, {t}, {t});
831 }
832 
833 static mlir::FunctionType genF64F64FuncType(mlir::MLIRContext *context) {
834   mlir::Type t = mlir::FloatType::getF64(context);
835   return mlir::FunctionType::get(context, {t}, {t});
836 }
837 
838 static mlir::FunctionType genF32F32F32FuncType(mlir::MLIRContext *context) {
839   auto t = mlir::FloatType::getF32(context);
840   return mlir::FunctionType::get(context, {t, t}, {t});
841 }
842 
843 static mlir::FunctionType genF64F64F64FuncType(mlir::MLIRContext *context) {
844   auto t = mlir::FloatType::getF64(context);
845   return mlir::FunctionType::get(context, {t, t}, {t});
846 }
847 
848 template <int Bits>
849 static mlir::FunctionType genIntF64FuncType(mlir::MLIRContext *context) {
850   auto t = mlir::FloatType::getF64(context);
851   auto r = mlir::IntegerType::get(context, Bits);
852   return mlir::FunctionType::get(context, {t}, {r});
853 }
854 
855 template <int Bits>
856 static mlir::FunctionType genIntF32FuncType(mlir::MLIRContext *context) {
857   auto t = mlir::FloatType::getF32(context);
858   auto r = mlir::IntegerType::get(context, Bits);
859   return mlir::FunctionType::get(context, {t}, {r});
860 }
861 
862 // TODO : Fill-up this table with more intrinsic.
863 // Note: These are also defined as operations in LLVM dialect. See if this
864 // can be use and has advantages.
865 static constexpr RuntimeFunction llvmIntrinsics[] = {
866     {"abs", "llvm.fabs.f32", genF32F32FuncType},
867     {"abs", "llvm.fabs.f64", genF64F64FuncType},
868     // llvm.floor is used for FLOOR, but returns real.
869     {"floor", "llvm.floor.f32", genF32F32FuncType},
870     {"floor", "llvm.floor.f64", genF64F64FuncType},
871     {"nint", "llvm.lround.i64.f64", genIntF64FuncType<64>},
872     {"nint", "llvm.lround.i64.f32", genIntF32FuncType<64>},
873     {"nint", "llvm.lround.i32.f64", genIntF64FuncType<32>},
874     {"nint", "llvm.lround.i32.f32", genIntF32FuncType<32>},
875     {"pow", "llvm.pow.f32", genF32F32F32FuncType},
876     {"pow", "llvm.pow.f64", genF64F64F64FuncType},
877 };
878 
879 // This helper class computes a "distance" between two function types.
880 // The distance measures how many narrowing conversions of actual arguments
881 // and result of "from" must be made in order to use "to" instead of "from".
882 // For instance, the distance between ACOS(REAL(10)) and ACOS(REAL(8)) is
883 // greater than the one between ACOS(REAL(10)) and ACOS(REAL(16)). This means
884 // if no implementation of ACOS(REAL(10)) is available, it is better to use
885 // ACOS(REAL(16)) with casts rather than ACOS(REAL(8)).
886 // Note that this is not a symmetric distance and the order of "from" and "to"
887 // arguments matters, d(foo, bar) may not be the same as d(bar, foo) because it
888 // may be safe to replace foo by bar, but not the opposite.
889 class FunctionDistance {
890 public:
891   FunctionDistance() : infinite{true} {}
892 
893   FunctionDistance(mlir::FunctionType from, mlir::FunctionType to) {
894     unsigned nInputs = from.getNumInputs();
895     unsigned nResults = from.getNumResults();
896     if (nResults != to.getNumResults() || nInputs != to.getNumInputs()) {
897       infinite = true;
898     } else {
899       for (decltype(nInputs) i = 0; i < nInputs && !infinite; ++i)
900         addArgumentDistance(from.getInput(i), to.getInput(i));
901       for (decltype(nResults) i = 0; i < nResults && !infinite; ++i)
902         addResultDistance(to.getResult(i), from.getResult(i));
903     }
904   }
905 
906   /// Beware both d1.isSmallerThan(d2) *and* d2.isSmallerThan(d1) may be
907   /// false if both d1 and d2 are infinite. This implies that
908   ///  d1.isSmallerThan(d2) is not equivalent to !d2.isSmallerThan(d1)
909   bool isSmallerThan(const FunctionDistance &d) const {
910     return !infinite &&
911            (d.infinite || std::lexicographical_compare(
912                               conversions.begin(), conversions.end(),
913                               d.conversions.begin(), d.conversions.end()));
914   }
915 
916   bool isLosingPrecision() const {
917     return conversions[narrowingArg] != 0 || conversions[extendingResult] != 0;
918   }
919 
920   bool isInfinite() const { return infinite; }
921 
922 private:
923   enum class Conversion { Forbidden, None, Narrow, Extend };
924 
925   void addArgumentDistance(mlir::Type from, mlir::Type to) {
926     switch (conversionBetweenTypes(from, to)) {
927     case Conversion::Forbidden:
928       infinite = true;
929       break;
930     case Conversion::None:
931       break;
932     case Conversion::Narrow:
933       conversions[narrowingArg]++;
934       break;
935     case Conversion::Extend:
936       conversions[nonNarrowingArg]++;
937       break;
938     }
939   }
940 
941   void addResultDistance(mlir::Type from, mlir::Type to) {
942     switch (conversionBetweenTypes(from, to)) {
943     case Conversion::Forbidden:
944       infinite = true;
945       break;
946     case Conversion::None:
947       break;
948     case Conversion::Narrow:
949       conversions[nonExtendingResult]++;
950       break;
951     case Conversion::Extend:
952       conversions[extendingResult]++;
953       break;
954     }
955   }
956 
957   // Floating point can be mlir::FloatType or fir::real
958   static unsigned getFloatingPointWidth(mlir::Type t) {
959     if (auto f{t.dyn_cast<mlir::FloatType>()})
960       return f.getWidth();
961     // FIXME: Get width another way for fir.real/complex
962     // - use fir/KindMapping.h and llvm::Type
963     // - or use evaluate/type.h
964     if (auto r{t.dyn_cast<fir::RealType>()})
965       return r.getFKind() * 4;
966     if (auto cplx{t.dyn_cast<fir::ComplexType>()})
967       return cplx.getFKind() * 4;
968     llvm_unreachable("not a floating-point type");
969   }
970 
971   static Conversion conversionBetweenTypes(mlir::Type from, mlir::Type to) {
972     if (from == to)
973       return Conversion::None;
974 
975     if (auto fromIntTy{from.dyn_cast<mlir::IntegerType>()}) {
976       if (auto toIntTy{to.dyn_cast<mlir::IntegerType>()}) {
977         return fromIntTy.getWidth() > toIntTy.getWidth() ? Conversion::Narrow
978                                                          : Conversion::Extend;
979       }
980     }
981 
982     if (fir::isa_real(from) && fir::isa_real(to)) {
983       return getFloatingPointWidth(from) > getFloatingPointWidth(to)
984                  ? Conversion::Narrow
985                  : Conversion::Extend;
986     }
987 
988     if (auto fromCplxTy{from.dyn_cast<fir::ComplexType>()}) {
989       if (auto toCplxTy{to.dyn_cast<fir::ComplexType>()}) {
990         return getFloatingPointWidth(fromCplxTy) >
991                        getFloatingPointWidth(toCplxTy)
992                    ? Conversion::Narrow
993                    : Conversion::Extend;
994       }
995     }
996     // Notes:
997     // - No conversion between character types, specialization of runtime
998     // functions should be made instead.
999     // - It is not clear there is a use case for automatic conversions
1000     // around Logical and it may damage hidden information in the physical
1001     // storage so do not do it.
1002     return Conversion::Forbidden;
1003   }
1004 
1005   // Below are indexes to access data in conversions.
1006   // The order in data does matter for lexicographical_compare
1007   enum {
1008     narrowingArg = 0,   // usually bad
1009     extendingResult,    // usually bad
1010     nonExtendingResult, // usually ok
1011     nonNarrowingArg,    // usually ok
1012     dataSize
1013   };
1014 
1015   std::array<int, dataSize> conversions = {};
1016   bool infinite = false; // When forbidden conversion or wrong argument number
1017 };
1018 
1019 /// Build mlir::FuncOp from runtime symbol description and add
1020 /// fir.runtime attribute.
1021 static mlir::FuncOp getFuncOp(mlir::Location loc, fir::FirOpBuilder &builder,
1022                               const RuntimeFunction &runtime) {
1023   mlir::FuncOp function = builder.addNamedFunction(
1024       loc, runtime.symbol, runtime.typeGenerator(builder.getContext()));
1025   function->setAttr("fir.runtime", builder.getUnitAttr());
1026   return function;
1027 }
1028 
1029 /// Select runtime function that has the smallest distance to the intrinsic
1030 /// function type and that will not imply narrowing arguments or extending the
1031 /// result.
1032 /// If nothing is found, the mlir::FuncOp will contain a nullptr.
1033 mlir::FuncOp searchFunctionInLibrary(
1034     mlir::Location loc, fir::FirOpBuilder &builder,
1035     const Fortran::common::StaticMultimapView<RuntimeFunction> &lib,
1036     llvm::StringRef name, mlir::FunctionType funcType,
1037     const RuntimeFunction **bestNearMatch,
1038     FunctionDistance &bestMatchDistance) {
1039   std::pair<const RuntimeFunction *, const RuntimeFunction *> range =
1040       lib.equal_range(name);
1041   for (auto iter = range.first; iter != range.second && iter; ++iter) {
1042     const RuntimeFunction &impl = *iter;
1043     mlir::FunctionType implType = impl.typeGenerator(builder.getContext());
1044     if (funcType == implType)
1045       return getFuncOp(loc, builder, impl); // exact match
1046 
1047     FunctionDistance distance(funcType, implType);
1048     if (distance.isSmallerThan(bestMatchDistance)) {
1049       *bestNearMatch = &impl;
1050       bestMatchDistance = std::move(distance);
1051     }
1052   }
1053   return {};
1054 }
1055 
1056 /// Search runtime for the best runtime function given an intrinsic name
1057 /// and interface. The interface may not be a perfect match in which case
1058 /// the caller is responsible to insert argument and return value conversions.
1059 /// If nothing is found, the mlir::FuncOp will contain a nullptr.
1060 static mlir::FuncOp getRuntimeFunction(mlir::Location loc,
1061                                        fir::FirOpBuilder &builder,
1062                                        llvm::StringRef name,
1063                                        mlir::FunctionType funcType) {
1064   const RuntimeFunction *bestNearMatch = nullptr;
1065   FunctionDistance bestMatchDistance{};
1066   mlir::FuncOp match;
1067   using RtMap = Fortran::common::StaticMultimapView<RuntimeFunction>;
1068   static constexpr RtMap pgmathF(pgmathFast);
1069   static_assert(pgmathF.Verify() && "map must be sorted");
1070   if (mathRuntimeVersion == fastVersion) {
1071     match = searchFunctionInLibrary(loc, builder, pgmathF, name, funcType,
1072                                     &bestNearMatch, bestMatchDistance);
1073   } else {
1074     assert(mathRuntimeVersion == llvmOnly && "unknown math runtime");
1075   }
1076   if (match)
1077     return match;
1078 
1079   // Go through llvm intrinsics if not exact match in libpgmath or if
1080   // mathRuntimeVersion == llvmOnly
1081   static constexpr RtMap llvmIntr(llvmIntrinsics);
1082   static_assert(llvmIntr.Verify() && "map must be sorted");
1083   if (mlir::FuncOp exactMatch =
1084           searchFunctionInLibrary(loc, builder, llvmIntr, name, funcType,
1085                                   &bestNearMatch, bestMatchDistance))
1086     return exactMatch;
1087 
1088   if (bestNearMatch != nullptr) {
1089     if (bestMatchDistance.isLosingPrecision()) {
1090       // Using this runtime version requires narrowing the arguments
1091       // or extending the result. It is not numerically safe. There
1092       // is currently no quad math library that was described in
1093       // lowering and could be used here. Emit an error and continue
1094       // generating the code with the narrowing cast so that the user
1095       // can get a complete list of the problematic intrinsic calls.
1096       std::string message("TODO: no math runtime available for '");
1097       llvm::raw_string_ostream sstream(message);
1098       if (name == "pow") {
1099         assert(funcType.getNumInputs() == 2 &&
1100                "power operator has two arguments");
1101         sstream << funcType.getInput(0) << " ** " << funcType.getInput(1);
1102       } else {
1103         sstream << name << "(";
1104         if (funcType.getNumInputs() > 0)
1105           sstream << funcType.getInput(0);
1106         for (mlir::Type argType : funcType.getInputs().drop_front())
1107           sstream << ", " << argType;
1108         sstream << ")";
1109       }
1110       sstream << "'";
1111       mlir::emitError(loc, message);
1112     }
1113     return getFuncOp(loc, builder, *bestNearMatch);
1114   }
1115   return {};
1116 }
1117 
1118 /// Helpers to get function type from arguments and result type.
1119 static mlir::FunctionType getFunctionType(llvm::Optional<mlir::Type> resultType,
1120                                           llvm::ArrayRef<mlir::Value> arguments,
1121                                           fir::FirOpBuilder &builder) {
1122   llvm::SmallVector<mlir::Type> argTypes;
1123   for (mlir::Value arg : arguments)
1124     argTypes.push_back(arg.getType());
1125   llvm::SmallVector<mlir::Type> resTypes;
1126   if (resultType)
1127     resTypes.push_back(*resultType);
1128   return mlir::FunctionType::get(builder.getModule().getContext(), argTypes,
1129                                  resTypes);
1130 }
1131 
1132 /// fir::ExtendedValue to mlir::Value translation layer
1133 
1134 fir::ExtendedValue toExtendedValue(mlir::Value val, fir::FirOpBuilder &builder,
1135                                    mlir::Location loc) {
1136   assert(val && "optional unhandled here");
1137   mlir::Type type = val.getType();
1138   mlir::Value base = val;
1139   mlir::IndexType indexType = builder.getIndexType();
1140   llvm::SmallVector<mlir::Value> extents;
1141 
1142   fir::factory::CharacterExprHelper charHelper{builder, loc};
1143   // FIXME: we may want to allow non character scalar here.
1144   if (charHelper.isCharacterScalar(type))
1145     return charHelper.toExtendedValue(val);
1146 
1147   if (auto refType = type.dyn_cast<fir::ReferenceType>())
1148     type = refType.getEleTy();
1149 
1150   if (auto arrayType = type.dyn_cast<fir::SequenceType>()) {
1151     type = arrayType.getEleTy();
1152     for (fir::SequenceType::Extent extent : arrayType.getShape()) {
1153       if (extent == fir::SequenceType::getUnknownExtent())
1154         break;
1155       extents.emplace_back(
1156           builder.createIntegerConstant(loc, indexType, extent));
1157     }
1158     // Last extent might be missing in case of assumed-size. If more extents
1159     // could not be deduced from type, that's an error (a fir.box should
1160     // have been used in the interface).
1161     if (extents.size() + 1 < arrayType.getShape().size())
1162       mlir::emitError(loc, "cannot retrieve array extents from type");
1163   } else if (type.isa<fir::BoxType>() || type.isa<fir::RecordType>()) {
1164     fir::emitFatalError(loc, "not yet implemented: descriptor or derived type");
1165   }
1166 
1167   if (!extents.empty())
1168     return fir::ArrayBoxValue{base, extents};
1169   return base;
1170 }
1171 
1172 mlir::Value toValue(const fir::ExtendedValue &val, fir::FirOpBuilder &builder,
1173                     mlir::Location loc) {
1174   if (const fir::CharBoxValue *charBox = val.getCharBox()) {
1175     mlir::Value buffer = charBox->getBuffer();
1176     if (buffer.getType().isa<fir::BoxCharType>())
1177       return buffer;
1178     return fir::factory::CharacterExprHelper{builder, loc}.createEmboxChar(
1179         buffer, charBox->getLen());
1180   }
1181 
1182   // FIXME: need to access other ExtendedValue variants and handle them
1183   // properly.
1184   return fir::getBase(val);
1185 }
1186 
1187 //===----------------------------------------------------------------------===//
1188 // IntrinsicLibrary
1189 //===----------------------------------------------------------------------===//
1190 
1191 /// Emit a TODO error message for as yet unimplemented intrinsics.
1192 static void crashOnMissingIntrinsic(mlir::Location loc, llvm::StringRef name) {
1193   TODO(loc, "missing intrinsic lowering: " + llvm::Twine(name));
1194 }
1195 
1196 template <typename GeneratorType>
1197 fir::ExtendedValue IntrinsicLibrary::genElementalCall(
1198     GeneratorType generator, llvm::StringRef name, mlir::Type resultType,
1199     llvm::ArrayRef<fir::ExtendedValue> args, bool outline) {
1200   llvm::SmallVector<mlir::Value> scalarArgs;
1201   for (const fir::ExtendedValue &arg : args)
1202     if (arg.getUnboxed() || arg.getCharBox())
1203       scalarArgs.emplace_back(fir::getBase(arg));
1204     else
1205       fir::emitFatalError(loc, "nonscalar intrinsic argument");
1206   return invokeGenerator(generator, resultType, scalarArgs);
1207 }
1208 
1209 template <>
1210 fir::ExtendedValue
1211 IntrinsicLibrary::genElementalCall<IntrinsicLibrary::ExtendedGenerator>(
1212     ExtendedGenerator generator, llvm::StringRef name, mlir::Type resultType,
1213     llvm::ArrayRef<fir::ExtendedValue> args, bool outline) {
1214   for (const fir::ExtendedValue &arg : args)
1215     if (!arg.getUnboxed() && !arg.getCharBox())
1216       fir::emitFatalError(loc, "nonscalar intrinsic argument");
1217   if (outline)
1218     return outlineInExtendedWrapper(generator, name, resultType, args);
1219   return std::invoke(generator, *this, resultType, args);
1220 }
1221 
1222 template <>
1223 fir::ExtendedValue
1224 IntrinsicLibrary::genElementalCall<IntrinsicLibrary::SubroutineGenerator>(
1225     SubroutineGenerator generator, llvm::StringRef name, mlir::Type resultType,
1226     llvm::ArrayRef<fir::ExtendedValue> args, bool outline) {
1227   for (const fir::ExtendedValue &arg : args)
1228     if (!arg.getUnboxed() && !arg.getCharBox())
1229       // fir::emitFatalError(loc, "nonscalar intrinsic argument");
1230       crashOnMissingIntrinsic(loc, name);
1231   if (outline)
1232     return outlineInExtendedWrapper(generator, name, resultType, args);
1233   std::invoke(generator, *this, args);
1234   return mlir::Value();
1235 }
1236 
1237 static fir::ExtendedValue
1238 invokeHandler(IntrinsicLibrary::ElementalGenerator generator,
1239               const IntrinsicHandler &handler,
1240               llvm::Optional<mlir::Type> resultType,
1241               llvm::ArrayRef<fir::ExtendedValue> args, bool outline,
1242               IntrinsicLibrary &lib) {
1243   assert(resultType && "expect elemental intrinsic to be functions");
1244   return lib.genElementalCall(generator, handler.name, *resultType, args,
1245                               outline);
1246 }
1247 
1248 static fir::ExtendedValue
1249 invokeHandler(IntrinsicLibrary::ExtendedGenerator generator,
1250               const IntrinsicHandler &handler,
1251               llvm::Optional<mlir::Type> resultType,
1252               llvm::ArrayRef<fir::ExtendedValue> args, bool outline,
1253               IntrinsicLibrary &lib) {
1254   assert(resultType && "expect intrinsic function");
1255   if (handler.isElemental)
1256     return lib.genElementalCall(generator, handler.name, *resultType, args,
1257                                 outline);
1258   if (outline)
1259     return lib.outlineInExtendedWrapper(generator, handler.name, *resultType,
1260                                         args);
1261   return std::invoke(generator, lib, *resultType, args);
1262 }
1263 
1264 static fir::ExtendedValue
1265 invokeHandler(IntrinsicLibrary::SubroutineGenerator generator,
1266               const IntrinsicHandler &handler,
1267               llvm::Optional<mlir::Type> resultType,
1268               llvm::ArrayRef<fir::ExtendedValue> args, bool outline,
1269               IntrinsicLibrary &lib) {
1270   if (handler.isElemental)
1271     return lib.genElementalCall(generator, handler.name, mlir::Type{}, args,
1272                                 outline);
1273   if (outline)
1274     return lib.outlineInExtendedWrapper(generator, handler.name, resultType,
1275                                         args);
1276   std::invoke(generator, lib, args);
1277   return mlir::Value{};
1278 }
1279 
1280 fir::ExtendedValue
1281 IntrinsicLibrary::genIntrinsicCall(llvm::StringRef name,
1282                                    llvm::Optional<mlir::Type> resultType,
1283                                    llvm::ArrayRef<fir::ExtendedValue> args) {
1284   if (const IntrinsicHandler *handler = findIntrinsicHandler(name)) {
1285     bool outline = handler->outline || outlineAllIntrinsics;
1286     return std::visit(
1287         [&](auto &generator) -> fir::ExtendedValue {
1288           return invokeHandler(generator, *handler, resultType, args, outline,
1289                                *this);
1290         },
1291         handler->generator);
1292   }
1293 
1294   if (!resultType)
1295     // Subroutine should have a handler, they are likely missing for now.
1296     crashOnMissingIntrinsic(loc, name);
1297 
1298   // Try the runtime if no special handler was defined for the
1299   // intrinsic being called. Maths runtime only has numerical elemental.
1300   // No optional arguments are expected at this point, the code will
1301   // crash if it gets absent optional.
1302 
1303   // FIXME: using toValue to get the type won't work with array arguments.
1304   llvm::SmallVector<mlir::Value> mlirArgs;
1305   for (const fir::ExtendedValue &extendedVal : args) {
1306     mlir::Value val = toValue(extendedVal, builder, loc);
1307     if (!val)
1308       // If an absent optional gets there, most likely its handler has just
1309       // not yet been defined.
1310       crashOnMissingIntrinsic(loc, name);
1311     mlirArgs.emplace_back(val);
1312   }
1313   mlir::FunctionType soughtFuncType =
1314       getFunctionType(*resultType, mlirArgs, builder);
1315 
1316   IntrinsicLibrary::RuntimeCallGenerator runtimeCallGenerator =
1317       getRuntimeCallGenerator(name, soughtFuncType);
1318   return genElementalCall(runtimeCallGenerator, name, *resultType, args,
1319                           /* outline */ true);
1320 }
1321 
1322 mlir::Value
1323 IntrinsicLibrary::invokeGenerator(ElementalGenerator generator,
1324                                   mlir::Type resultType,
1325                                   llvm::ArrayRef<mlir::Value> args) {
1326   return std::invoke(generator, *this, resultType, args);
1327 }
1328 
1329 mlir::Value
1330 IntrinsicLibrary::invokeGenerator(RuntimeCallGenerator generator,
1331                                   mlir::Type resultType,
1332                                   llvm::ArrayRef<mlir::Value> args) {
1333   return generator(builder, loc, args);
1334 }
1335 
1336 mlir::Value
1337 IntrinsicLibrary::invokeGenerator(ExtendedGenerator generator,
1338                                   mlir::Type resultType,
1339                                   llvm::ArrayRef<mlir::Value> args) {
1340   llvm::SmallVector<fir::ExtendedValue> extendedArgs;
1341   for (mlir::Value arg : args)
1342     extendedArgs.emplace_back(toExtendedValue(arg, builder, loc));
1343   auto extendedResult = std::invoke(generator, *this, resultType, extendedArgs);
1344   return toValue(extendedResult, builder, loc);
1345 }
1346 
1347 mlir::Value
1348 IntrinsicLibrary::invokeGenerator(SubroutineGenerator generator,
1349                                   llvm::ArrayRef<mlir::Value> args) {
1350   llvm::SmallVector<fir::ExtendedValue> extendedArgs;
1351   for (mlir::Value arg : args)
1352     extendedArgs.emplace_back(toExtendedValue(arg, builder, loc));
1353   std::invoke(generator, *this, extendedArgs);
1354   return {};
1355 }
1356 
1357 template <typename GeneratorType>
1358 mlir::FuncOp IntrinsicLibrary::getWrapper(GeneratorType generator,
1359                                           llvm::StringRef name,
1360                                           mlir::FunctionType funcType,
1361                                           bool loadRefArguments) {
1362   std::string wrapperName = fir::mangleIntrinsicProcedure(name, funcType);
1363   mlir::FuncOp function = builder.getNamedFunction(wrapperName);
1364   if (!function) {
1365     // First time this wrapper is needed, build it.
1366     function = builder.createFunction(loc, wrapperName, funcType);
1367     function->setAttr("fir.intrinsic", builder.getUnitAttr());
1368     auto internalLinkage = mlir::LLVM::linkage::Linkage::Internal;
1369     auto linkage =
1370         mlir::LLVM::LinkageAttr::get(builder.getContext(), internalLinkage);
1371     function->setAttr("llvm.linkage", linkage);
1372     function.addEntryBlock();
1373 
1374     // Create local context to emit code into the newly created function
1375     // This new function is not linked to a source file location, only
1376     // its calls will be.
1377     auto localBuilder =
1378         std::make_unique<fir::FirOpBuilder>(function, builder.getKindMap());
1379     localBuilder->setInsertionPointToStart(&function.front());
1380     // Location of code inside wrapper of the wrapper is independent from
1381     // the location of the intrinsic call.
1382     mlir::Location localLoc = localBuilder->getUnknownLoc();
1383     llvm::SmallVector<mlir::Value> localArguments;
1384     for (mlir::BlockArgument bArg : function.front().getArguments()) {
1385       auto refType = bArg.getType().dyn_cast<fir::ReferenceType>();
1386       if (loadRefArguments && refType) {
1387         auto loaded = localBuilder->create<fir::LoadOp>(localLoc, bArg);
1388         localArguments.push_back(loaded);
1389       } else {
1390         localArguments.push_back(bArg);
1391       }
1392     }
1393 
1394     IntrinsicLibrary localLib{*localBuilder, localLoc};
1395 
1396     if constexpr (std::is_same_v<GeneratorType, SubroutineGenerator>) {
1397       localLib.invokeGenerator(generator, localArguments);
1398       localBuilder->create<mlir::func::ReturnOp>(localLoc);
1399     } else {
1400       assert(funcType.getNumResults() == 1 &&
1401              "expect one result for intrinsic function wrapper type");
1402       mlir::Type resultType = funcType.getResult(0);
1403       auto result =
1404           localLib.invokeGenerator(generator, resultType, localArguments);
1405       localBuilder->create<mlir::func::ReturnOp>(localLoc, result);
1406     }
1407   } else {
1408     // Wrapper was already built, ensure it has the sought type
1409     assert(function.getFunctionType() == funcType &&
1410            "conflict between intrinsic wrapper types");
1411   }
1412   return function;
1413 }
1414 
1415 /// Helpers to detect absent optional (not yet supported in outlining).
1416 bool static hasAbsentOptional(llvm::ArrayRef<fir::ExtendedValue> args) {
1417   for (const fir::ExtendedValue &arg : args)
1418     if (!fir::getBase(arg))
1419       return true;
1420   return false;
1421 }
1422 
1423 template <typename GeneratorType>
1424 fir::ExtendedValue IntrinsicLibrary::outlineInExtendedWrapper(
1425     GeneratorType generator, llvm::StringRef name,
1426     llvm::Optional<mlir::Type> resultType,
1427     llvm::ArrayRef<fir::ExtendedValue> args) {
1428   if (hasAbsentOptional(args))
1429     TODO(loc, "cannot outline call to intrinsic " + llvm::Twine(name) +
1430                   " with absent optional argument");
1431   llvm::SmallVector<mlir::Value> mlirArgs;
1432   for (const auto &extendedVal : args)
1433     mlirArgs.emplace_back(toValue(extendedVal, builder, loc));
1434   mlir::FunctionType funcType = getFunctionType(resultType, mlirArgs, builder);
1435   mlir::FuncOp wrapper = getWrapper(generator, name, funcType);
1436   auto call = builder.create<fir::CallOp>(loc, wrapper, mlirArgs);
1437   if (resultType)
1438     return toExtendedValue(call.getResult(0), builder, loc);
1439   // Subroutine calls
1440   return mlir::Value{};
1441 }
1442 
1443 IntrinsicLibrary::RuntimeCallGenerator
1444 IntrinsicLibrary::getRuntimeCallGenerator(llvm::StringRef name,
1445                                           mlir::FunctionType soughtFuncType) {
1446   mlir::FuncOp funcOp = getRuntimeFunction(loc, builder, name, soughtFuncType);
1447   if (!funcOp) {
1448     std::string buffer("not yet implemented: missing intrinsic lowering: ");
1449     llvm::raw_string_ostream sstream(buffer);
1450     sstream << name << "\nrequested type was: " << soughtFuncType << '\n';
1451     fir::emitFatalError(loc, buffer);
1452   }
1453 
1454   mlir::FunctionType actualFuncType = funcOp.getFunctionType();
1455   assert(actualFuncType.getNumResults() == soughtFuncType.getNumResults() &&
1456          actualFuncType.getNumInputs() == soughtFuncType.getNumInputs() &&
1457          actualFuncType.getNumResults() == 1 && "Bad intrinsic match");
1458 
1459   return [funcOp, actualFuncType,
1460           soughtFuncType](fir::FirOpBuilder &builder, mlir::Location loc,
1461                           llvm::ArrayRef<mlir::Value> args) {
1462     llvm::SmallVector<mlir::Value> convertedArguments;
1463     for (auto [fst, snd] : llvm::zip(actualFuncType.getInputs(), args))
1464       convertedArguments.push_back(builder.createConvert(loc, fst, snd));
1465     auto call = builder.create<fir::CallOp>(loc, funcOp, convertedArguments);
1466     mlir::Type soughtType = soughtFuncType.getResult(0);
1467     return builder.createConvert(loc, soughtType, call.getResult(0));
1468   };
1469 }
1470 
1471 void IntrinsicLibrary::addCleanUpForTemp(mlir::Location loc, mlir::Value temp) {
1472   assert(stmtCtx);
1473   fir::FirOpBuilder *bldr = &builder;
1474   stmtCtx->attachCleanup([=]() { bldr->create<fir::FreeMemOp>(loc, temp); });
1475 }
1476 
1477 fir::ExtendedValue
1478 IntrinsicLibrary::readAndAddCleanUp(fir::MutableBoxValue resultMutableBox,
1479                                     mlir::Type resultType,
1480                                     llvm::StringRef intrinsicName) {
1481   fir::ExtendedValue res =
1482       fir::factory::genMutableBoxRead(builder, loc, resultMutableBox);
1483   return res.match(
1484       [&](const fir::ArrayBoxValue &box) -> fir::ExtendedValue {
1485         // Add cleanup code
1486         addCleanUpForTemp(loc, box.getAddr());
1487         return box;
1488       },
1489       [&](const fir::BoxValue &box) -> fir::ExtendedValue {
1490         // Add cleanup code
1491         auto addr =
1492             builder.create<fir::BoxAddrOp>(loc, box.getMemTy(), box.getAddr());
1493         addCleanUpForTemp(loc, addr);
1494         return box;
1495       },
1496       [&](const fir::CharArrayBoxValue &box) -> fir::ExtendedValue {
1497         // Add cleanup code
1498         addCleanUpForTemp(loc, box.getAddr());
1499         return box;
1500       },
1501       [&](const mlir::Value &tempAddr) -> fir::ExtendedValue {
1502         // Add cleanup code
1503         addCleanUpForTemp(loc, tempAddr);
1504         return builder.create<fir::LoadOp>(loc, resultType, tempAddr);
1505       },
1506       [&](const fir::CharBoxValue &box) -> fir::ExtendedValue {
1507         // Add cleanup code
1508         addCleanUpForTemp(loc, box.getAddr());
1509         return box;
1510       },
1511       [&](const auto &) -> fir::ExtendedValue {
1512         fir::emitFatalError(loc, "unexpected result for " + intrinsicName);
1513       });
1514 }
1515 
1516 //===----------------------------------------------------------------------===//
1517 // Code generators for the intrinsic
1518 //===----------------------------------------------------------------------===//
1519 
1520 mlir::Value IntrinsicLibrary::genRuntimeCall(llvm::StringRef name,
1521                                              mlir::Type resultType,
1522                                              llvm::ArrayRef<mlir::Value> args) {
1523   mlir::FunctionType soughtFuncType =
1524       getFunctionType(resultType, args, builder);
1525   return getRuntimeCallGenerator(name, soughtFuncType)(builder, loc, args);
1526 }
1527 
1528 // ABS
1529 mlir::Value IntrinsicLibrary::genAbs(mlir::Type resultType,
1530                                      llvm::ArrayRef<mlir::Value> args) {
1531   assert(args.size() == 1);
1532   mlir::Value arg = args[0];
1533   mlir::Type type = arg.getType();
1534   if (fir::isa_real(type)) {
1535     // Runtime call to fp abs. An alternative would be to use mlir
1536     // math::AbsFOp but it does not support all fir floating point types.
1537     return genRuntimeCall("abs", resultType, args);
1538   }
1539   if (auto intType = type.dyn_cast<mlir::IntegerType>()) {
1540     // At the time of this implementation there is no abs op in mlir.
1541     // So, implement abs here without branching.
1542     mlir::Value shift =
1543         builder.createIntegerConstant(loc, intType, intType.getWidth() - 1);
1544     auto mask = builder.create<mlir::arith::ShRSIOp>(loc, arg, shift);
1545     auto xored = builder.create<mlir::arith::XOrIOp>(loc, arg, mask);
1546     return builder.create<mlir::arith::SubIOp>(loc, xored, mask);
1547   }
1548   if (fir::isa_complex(type)) {
1549     // Use HYPOT to fulfill the no underflow/overflow requirement.
1550     auto parts = fir::factory::Complex{builder, loc}.extractParts(arg);
1551     llvm::SmallVector<mlir::Value> args = {parts.first, parts.second};
1552     return genRuntimeCall("hypot", resultType, args);
1553   }
1554   llvm_unreachable("unexpected type in ABS argument");
1555 }
1556 
1557 // ADJUSTL & ADJUSTR
1558 template <void (*CallRuntime)(fir::FirOpBuilder &, mlir::Location loc,
1559                               mlir::Value, mlir::Value)>
1560 fir::ExtendedValue
1561 IntrinsicLibrary::genAdjustRtCall(mlir::Type resultType,
1562                                   llvm::ArrayRef<fir::ExtendedValue> args) {
1563   assert(args.size() == 1);
1564   mlir::Value string = builder.createBox(loc, args[0]);
1565   // Create a mutable fir.box to be passed to the runtime for the result.
1566   fir::MutableBoxValue resultMutableBox =
1567       fir::factory::createTempMutableBox(builder, loc, resultType);
1568   mlir::Value resultIrBox =
1569       fir::factory::getMutableIRBox(builder, loc, resultMutableBox);
1570 
1571   // Call the runtime -- the runtime will allocate the result.
1572   CallRuntime(builder, loc, resultIrBox, string);
1573 
1574   // Read result from mutable fir.box and add it to the list of temps to be
1575   // finalized by the StatementContext.
1576   fir::ExtendedValue res =
1577       fir::factory::genMutableBoxRead(builder, loc, resultMutableBox);
1578   return res.match(
1579       [&](const fir::CharBoxValue &box) -> fir::ExtendedValue {
1580         addCleanUpForTemp(loc, fir::getBase(box));
1581         return box;
1582       },
1583       [&](const auto &) -> fir::ExtendedValue {
1584         fir::emitFatalError(loc, "result of ADJUSTL is not a scalar character");
1585       });
1586 }
1587 
1588 // AIMAG
1589 mlir::Value IntrinsicLibrary::genAimag(mlir::Type resultType,
1590                                        llvm::ArrayRef<mlir::Value> args) {
1591   assert(args.size() == 1);
1592   return fir::factory::Complex{builder, loc}.extractComplexPart(
1593       args[0], true /* isImagPart */);
1594 }
1595 
1596 // ALL
1597 fir::ExtendedValue
1598 IntrinsicLibrary::genAll(mlir::Type resultType,
1599                          llvm::ArrayRef<fir::ExtendedValue> args) {
1600 
1601   assert(args.size() == 2);
1602   // Handle required mask argument
1603   mlir::Value mask = builder.createBox(loc, args[0]);
1604 
1605   fir::BoxValue maskArry = builder.createBox(loc, args[0]);
1606   int rank = maskArry.rank();
1607   assert(rank >= 1);
1608 
1609   // Handle optional dim argument
1610   bool absentDim = isAbsent(args[1]);
1611   mlir::Value dim =
1612       absentDim ? builder.createIntegerConstant(loc, builder.getIndexType(), 1)
1613                 : fir::getBase(args[1]);
1614 
1615   if (rank == 1 || absentDim)
1616     return builder.createConvert(loc, resultType,
1617                                  fir::runtime::genAll(builder, loc, mask, dim));
1618 
1619   // else use the result descriptor AllDim() intrinsic
1620 
1621   // Create mutable fir.box to be passed to the runtime for the result.
1622 
1623   mlir::Type resultArrayType = builder.getVarLenSeqTy(resultType, rank - 1);
1624   fir::MutableBoxValue resultMutableBox =
1625       fir::factory::createTempMutableBox(builder, loc, resultArrayType);
1626   mlir::Value resultIrBox =
1627       fir::factory::getMutableIRBox(builder, loc, resultMutableBox);
1628 
1629   // Call runtime. The runtime is allocating the result.
1630   fir::runtime::genAllDescriptor(builder, loc, resultIrBox, mask, dim);
1631   return fir::factory::genMutableBoxRead(builder, loc, resultMutableBox)
1632       .match(
1633           [&](const fir::ArrayBoxValue &box) -> fir::ExtendedValue {
1634             addCleanUpForTemp(loc, box.getAddr());
1635             return box;
1636           },
1637           [&](const auto &) -> fir::ExtendedValue {
1638             fir::emitFatalError(loc, "Invalid result for ALL");
1639           });
1640 }
1641 
1642 // ALLOCATED
1643 fir::ExtendedValue
1644 IntrinsicLibrary::genAllocated(mlir::Type resultType,
1645                                llvm::ArrayRef<fir::ExtendedValue> args) {
1646   assert(args.size() == 1);
1647   return args[0].match(
1648       [&](const fir::MutableBoxValue &x) -> fir::ExtendedValue {
1649         return fir::factory::genIsAllocatedOrAssociatedTest(builder, loc, x);
1650       },
1651       [&](const auto &) -> fir::ExtendedValue {
1652         fir::emitFatalError(loc,
1653                             "allocated arg not lowered to MutableBoxValue");
1654       });
1655 }
1656 
1657 // ANY
1658 fir::ExtendedValue
1659 IntrinsicLibrary::genAny(mlir::Type resultType,
1660                          llvm::ArrayRef<fir::ExtendedValue> args) {
1661 
1662   assert(args.size() == 2);
1663   // Handle required mask argument
1664   mlir::Value mask = builder.createBox(loc, args[0]);
1665 
1666   fir::BoxValue maskArry = builder.createBox(loc, args[0]);
1667   int rank = maskArry.rank();
1668   assert(rank >= 1);
1669 
1670   // Handle optional dim argument
1671   bool absentDim = isAbsent(args[1]);
1672   mlir::Value dim =
1673       absentDim ? builder.createIntegerConstant(loc, builder.getIndexType(), 1)
1674                 : fir::getBase(args[1]);
1675 
1676   if (rank == 1 || absentDim)
1677     return builder.createConvert(loc, resultType,
1678                                  fir::runtime::genAny(builder, loc, mask, dim));
1679 
1680   // else use the result descriptor AnyDim() intrinsic
1681 
1682   // Create mutable fir.box to be passed to the runtime for the result.
1683 
1684   mlir::Type resultArrayType = builder.getVarLenSeqTy(resultType, rank - 1);
1685   fir::MutableBoxValue resultMutableBox =
1686       fir::factory::createTempMutableBox(builder, loc, resultArrayType);
1687   mlir::Value resultIrBox =
1688       fir::factory::getMutableIRBox(builder, loc, resultMutableBox);
1689 
1690   // Call runtime. The runtime is allocating the result.
1691   fir::runtime::genAnyDescriptor(builder, loc, resultIrBox, mask, dim);
1692   return fir::factory::genMutableBoxRead(builder, loc, resultMutableBox)
1693       .match(
1694           [&](const fir::ArrayBoxValue &box) -> fir::ExtendedValue {
1695             addCleanUpForTemp(loc, box.getAddr());
1696             return box;
1697           },
1698           [&](const auto &) -> fir::ExtendedValue {
1699             fir::emitFatalError(loc, "Invalid result for ANY");
1700           });
1701 }
1702 
1703 // ASSOCIATED
1704 fir::ExtendedValue
1705 IntrinsicLibrary::genAssociated(mlir::Type resultType,
1706                                 llvm::ArrayRef<fir::ExtendedValue> args) {
1707   assert(args.size() == 2);
1708   auto *pointer =
1709       args[0].match([&](const fir::MutableBoxValue &x) { return &x; },
1710                     [&](const auto &) -> const fir::MutableBoxValue * {
1711                       fir::emitFatalError(loc, "pointer not a MutableBoxValue");
1712                     });
1713   const fir::ExtendedValue &target = args[1];
1714   if (isAbsent(target))
1715     return fir::factory::genIsAllocatedOrAssociatedTest(builder, loc, *pointer);
1716 
1717   mlir::Value targetBox = builder.createBox(loc, target);
1718   if (fir::valueHasFirAttribute(fir::getBase(target),
1719                                 fir::getOptionalAttrName())) {
1720     // Subtle: contrary to other intrinsic optional arguments, disassociated
1721     // POINTER and unallocated ALLOCATABLE actual argument are not considered
1722     // absent here. This is because ASSOCIATED has special requirements for
1723     // TARGET actual arguments that are POINTERs. There is no precise
1724     // requirements for ALLOCATABLEs, but all existing Fortran compilers treat
1725     // them similarly to POINTERs. That is: unallocated TARGETs cause ASSOCIATED
1726     // to rerun false.  The runtime deals with the disassociated/unallocated
1727     // case. Simply ensures that TARGET that are OPTIONAL get conditionally
1728     // emboxed here to convey the optional aspect to the runtime.
1729     auto isPresent = builder.create<fir::IsPresentOp>(loc, builder.getI1Type(),
1730                                                       fir::getBase(target));
1731     auto absentBox = builder.create<fir::AbsentOp>(loc, targetBox.getType());
1732     targetBox = builder.create<mlir::arith::SelectOp>(loc, isPresent, targetBox,
1733                                                       absentBox);
1734   }
1735   mlir::Value pointerBoxRef =
1736       fir::factory::getMutableIRBox(builder, loc, *pointer);
1737   auto pointerBox = builder.create<fir::LoadOp>(loc, pointerBoxRef);
1738   return Fortran::lower::genAssociated(builder, loc, pointerBox, targetBox);
1739 }
1740 
1741 // CHAR
1742 fir::ExtendedValue
1743 IntrinsicLibrary::genChar(mlir::Type type,
1744                           llvm::ArrayRef<fir::ExtendedValue> args) {
1745   // Optional KIND argument.
1746   assert(args.size() >= 1);
1747   const mlir::Value *arg = args[0].getUnboxed();
1748   // expect argument to be a scalar integer
1749   if (!arg)
1750     mlir::emitError(loc, "CHAR intrinsic argument not unboxed");
1751   fir::factory::CharacterExprHelper helper{builder, loc};
1752   fir::CharacterType::KindTy kind = helper.getCharacterType(type).getFKind();
1753   mlir::Value cast = helper.createSingletonFromCode(*arg, kind);
1754   mlir::Value len =
1755       builder.createIntegerConstant(loc, builder.getCharacterLengthType(), 1);
1756   return fir::CharBoxValue{cast, len};
1757 }
1758 
1759 // COUNT
1760 fir::ExtendedValue
1761 IntrinsicLibrary::genCount(mlir::Type resultType,
1762                            llvm::ArrayRef<fir::ExtendedValue> args) {
1763   assert(args.size() == 3);
1764 
1765   // Handle mask argument
1766   fir::BoxValue mask = builder.createBox(loc, args[0]);
1767   unsigned maskRank = mask.rank();
1768 
1769   assert(maskRank > 0);
1770 
1771   // Handle optional dim argument
1772   bool absentDim = isAbsent(args[1]);
1773   mlir::Value dim =
1774       absentDim ? builder.createIntegerConstant(loc, builder.getIndexType(), 0)
1775                 : fir::getBase(args[1]);
1776 
1777   if (absentDim || maskRank == 1) {
1778     // Result is scalar if no dim argument or mask is rank 1.
1779     // So, call specialized Count runtime routine.
1780     return builder.createConvert(
1781         loc, resultType,
1782         fir::runtime::genCount(builder, loc, fir::getBase(mask), dim));
1783   }
1784 
1785   // Call general CountDim runtime routine.
1786 
1787   // Handle optional kind argument
1788   bool absentKind = isAbsent(args[2]);
1789   mlir::Value kind = absentKind ? builder.createIntegerConstant(
1790                                       loc, builder.getIndexType(),
1791                                       builder.getKindMap().defaultIntegerKind())
1792                                 : fir::getBase(args[2]);
1793 
1794   // Create mutable fir.box to be passed to the runtime for the result.
1795   mlir::Type type = builder.getVarLenSeqTy(resultType, maskRank - 1);
1796   fir::MutableBoxValue resultMutableBox =
1797       fir::factory::createTempMutableBox(builder, loc, type);
1798 
1799   mlir::Value resultIrBox =
1800       fir::factory::getMutableIRBox(builder, loc, resultMutableBox);
1801 
1802   fir::runtime::genCountDim(builder, loc, resultIrBox, fir::getBase(mask), dim,
1803                             kind);
1804 
1805   // Handle cleanup of allocatable result descriptor and return
1806   fir::ExtendedValue res =
1807       fir::factory::genMutableBoxRead(builder, loc, resultMutableBox);
1808   return res.match(
1809       [&](const fir::ArrayBoxValue &box) -> fir::ExtendedValue {
1810         // Add cleanup code
1811         addCleanUpForTemp(loc, box.getAddr());
1812         return box;
1813       },
1814       [&](const auto &) -> fir::ExtendedValue {
1815         fir::emitFatalError(loc, "unexpected result for COUNT");
1816       });
1817 }
1818 
1819 // CPU_TIME
1820 void IntrinsicLibrary::genCpuTime(llvm::ArrayRef<fir::ExtendedValue> args) {
1821   assert(args.size() == 1);
1822   const mlir::Value *arg = args[0].getUnboxed();
1823   assert(arg && "nonscalar cpu_time argument");
1824   mlir::Value res1 = Fortran::lower::genCpuTime(builder, loc);
1825   mlir::Value res2 =
1826       builder.createConvert(loc, fir::dyn_cast_ptrEleTy(arg->getType()), res1);
1827   builder.create<fir::StoreOp>(loc, res2, *arg);
1828 }
1829 
1830 // CSHIFT
1831 fir::ExtendedValue
1832 IntrinsicLibrary::genCshift(mlir::Type resultType,
1833                             llvm::ArrayRef<fir::ExtendedValue> args) {
1834   assert(args.size() == 3);
1835 
1836   // Handle required ARRAY argument
1837   fir::BoxValue arrayBox = builder.createBox(loc, args[0]);
1838   mlir::Value array = fir::getBase(arrayBox);
1839   unsigned arrayRank = arrayBox.rank();
1840 
1841   // Create mutable fir.box to be passed to the runtime for the result.
1842   mlir::Type resultArrayType = builder.getVarLenSeqTy(resultType, arrayRank);
1843   fir::MutableBoxValue resultMutableBox =
1844       fir::factory::createTempMutableBox(builder, loc, resultArrayType);
1845   mlir::Value resultIrBox =
1846       fir::factory::getMutableIRBox(builder, loc, resultMutableBox);
1847 
1848   if (arrayRank == 1) {
1849     // Vector case
1850     // Handle required SHIFT argument as a scalar
1851     const mlir::Value *shiftAddr = args[1].getUnboxed();
1852     assert(shiftAddr && "nonscalar CSHIFT argument");
1853     auto shift = builder.create<fir::LoadOp>(loc, *shiftAddr);
1854 
1855     fir::runtime::genCshiftVector(builder, loc, resultIrBox, array, shift);
1856   } else {
1857     // Non-vector case
1858     // Handle required SHIFT argument as an array
1859     mlir::Value shift = builder.createBox(loc, args[1]);
1860 
1861     // Handle optional DIM argument
1862     mlir::Value dim =
1863         isAbsent(args[2])
1864             ? builder.createIntegerConstant(loc, builder.getIndexType(), 1)
1865             : fir::getBase(args[2]);
1866     fir::runtime::genCshift(builder, loc, resultIrBox, array, shift, dim);
1867   }
1868   return readAndAddCleanUp(resultMutableBox, resultType, "CSHIFT");
1869 }
1870 
1871 // DATE_AND_TIME
1872 void IntrinsicLibrary::genDateAndTime(llvm::ArrayRef<fir::ExtendedValue> args) {
1873   assert(args.size() == 4 && "date_and_time has 4 args");
1874   llvm::SmallVector<llvm::Optional<fir::CharBoxValue>> charArgs(3);
1875   for (unsigned i = 0; i < 3; ++i)
1876     if (const fir::CharBoxValue *charBox = args[i].getCharBox())
1877       charArgs[i] = *charBox;
1878 
1879   mlir::Value values = fir::getBase(args[3]);
1880   if (!values)
1881     values = builder.create<fir::AbsentOp>(
1882         loc, fir::BoxType::get(builder.getNoneType()));
1883 
1884   Fortran::lower::genDateAndTime(builder, loc, charArgs[0], charArgs[1],
1885                                  charArgs[2], values);
1886 }
1887 
1888 // DIM
1889 mlir::Value IntrinsicLibrary::genDim(mlir::Type resultType,
1890                                      llvm::ArrayRef<mlir::Value> args) {
1891   assert(args.size() == 2);
1892   if (resultType.isa<mlir::IntegerType>()) {
1893     mlir::Value zero = builder.createIntegerConstant(loc, resultType, 0);
1894     auto diff = builder.create<mlir::arith::SubIOp>(loc, args[0], args[1]);
1895     auto cmp = builder.create<mlir::arith::CmpIOp>(
1896         loc, mlir::arith::CmpIPredicate::sgt, diff, zero);
1897     return builder.create<mlir::arith::SelectOp>(loc, cmp, diff, zero);
1898   }
1899   assert(fir::isa_real(resultType) && "Only expects real and integer in DIM");
1900   mlir::Value zero = builder.createRealZeroConstant(loc, resultType);
1901   auto diff = builder.create<mlir::arith::SubFOp>(loc, args[0], args[1]);
1902   auto cmp = builder.create<mlir::arith::CmpFOp>(
1903       loc, mlir::arith::CmpFPredicate::OGT, diff, zero);
1904   return builder.create<mlir::arith::SelectOp>(loc, cmp, diff, zero);
1905 }
1906 
1907 // DOT_PRODUCT
1908 fir::ExtendedValue
1909 IntrinsicLibrary::genDotProduct(mlir::Type resultType,
1910                                 llvm::ArrayRef<fir::ExtendedValue> args) {
1911   return genDotProd(fir::runtime::genDotProduct, resultType, builder, loc,
1912                     stmtCtx, args);
1913 }
1914 
1915 // EOSHIFT
1916 fir::ExtendedValue
1917 IntrinsicLibrary::genEoshift(mlir::Type resultType,
1918                              llvm::ArrayRef<fir::ExtendedValue> args) {
1919   assert(args.size() == 4);
1920 
1921   // Handle required ARRAY argument
1922   fir::BoxValue arrayBox = builder.createBox(loc, args[0]);
1923   mlir::Value array = fir::getBase(arrayBox);
1924   unsigned arrayRank = arrayBox.rank();
1925 
1926   // Create mutable fir.box to be passed to the runtime for the result.
1927   mlir::Type resultArrayType = builder.getVarLenSeqTy(resultType, arrayRank);
1928   fir::MutableBoxValue resultMutableBox =
1929       fir::factory::createTempMutableBox(builder, loc, resultArrayType);
1930   mlir::Value resultIrBox =
1931       fir::factory::getMutableIRBox(builder, loc, resultMutableBox);
1932 
1933   // Handle optional BOUNDARY argument
1934   mlir::Value boundary =
1935       isAbsent(args[2]) ? builder.create<fir::AbsentOp>(
1936                               loc, fir::BoxType::get(builder.getNoneType()))
1937                         : builder.createBox(loc, args[2]);
1938 
1939   if (arrayRank == 1) {
1940     // Vector case
1941     // Handle required SHIFT argument as a scalar
1942     const mlir::Value *shiftAddr = args[1].getUnboxed();
1943     assert(shiftAddr && "nonscalar EOSHIFT SHIFT argument");
1944     auto shift = builder.create<fir::LoadOp>(loc, *shiftAddr);
1945     fir::runtime::genEoshiftVector(builder, loc, resultIrBox, array, shift,
1946                                    boundary);
1947   } else {
1948     // Non-vector case
1949     // Handle required SHIFT argument as an array
1950     mlir::Value shift = builder.createBox(loc, args[1]);
1951 
1952     // Handle optional DIM argument
1953     mlir::Value dim =
1954         isAbsent(args[3])
1955             ? builder.createIntegerConstant(loc, builder.getIndexType(), 1)
1956             : fir::getBase(args[3]);
1957     fir::runtime::genEoshift(builder, loc, resultIrBox, array, shift, boundary,
1958                              dim);
1959   }
1960   return readAndAddCleanUp(resultMutableBox, resultType,
1961                            "unexpected result for EOSHIFT");
1962 }
1963 
1964 // EXPONENT
1965 mlir::Value IntrinsicLibrary::genExponent(mlir::Type resultType,
1966                                           llvm::ArrayRef<mlir::Value> args) {
1967   assert(args.size() == 1);
1968 
1969   return builder.createConvert(
1970       loc, resultType,
1971       fir::runtime::genExponent(builder, loc, resultType,
1972                                 fir::getBase(args[0])));
1973 }
1974 
1975 // FLOOR
1976 mlir::Value IntrinsicLibrary::genFloor(mlir::Type resultType,
1977                                        llvm::ArrayRef<mlir::Value> args) {
1978   // Optional KIND argument.
1979   assert(args.size() >= 1);
1980   mlir::Value arg = args[0];
1981   // Use LLVM floor that returns real.
1982   mlir::Value floor = genRuntimeCall("floor", arg.getType(), {arg});
1983   return builder.createConvert(loc, resultType, floor);
1984 }
1985 
1986 // FRACTION
1987 mlir::Value IntrinsicLibrary::genFraction(mlir::Type resultType,
1988                                           llvm::ArrayRef<mlir::Value> args) {
1989   assert(args.size() == 1);
1990 
1991   return builder.createConvert(
1992       loc, resultType,
1993       fir::runtime::genFraction(builder, loc, fir::getBase(args[0])));
1994 }
1995 
1996 // IAND
1997 mlir::Value IntrinsicLibrary::genIand(mlir::Type resultType,
1998                                       llvm::ArrayRef<mlir::Value> args) {
1999   assert(args.size() == 2);
2000   return builder.create<mlir::arith::AndIOp>(loc, args[0], args[1]);
2001 }
2002 
2003 // IBCLR
2004 mlir::Value IntrinsicLibrary::genIbclr(mlir::Type resultType,
2005                                        llvm::ArrayRef<mlir::Value> args) {
2006   // A conformant IBCLR(I,POS) call satisfies:
2007   //     POS >= 0
2008   //     POS < BIT_SIZE(I)
2009   // Return:  I & (!(1 << POS))
2010   assert(args.size() == 2);
2011   mlir::Value pos = builder.createConvert(loc, resultType, args[1]);
2012   mlir::Value one = builder.createIntegerConstant(loc, resultType, 1);
2013   mlir::Value ones = builder.createIntegerConstant(loc, resultType, -1);
2014   auto mask = builder.create<mlir::arith::ShLIOp>(loc, one, pos);
2015   auto res = builder.create<mlir::arith::XOrIOp>(loc, ones, mask);
2016   return builder.create<mlir::arith::AndIOp>(loc, args[0], res);
2017 }
2018 
2019 // IBITS
2020 mlir::Value IntrinsicLibrary::genIbits(mlir::Type resultType,
2021                                        llvm::ArrayRef<mlir::Value> args) {
2022   // A conformant IBITS(I,POS,LEN) call satisfies:
2023   //     POS >= 0
2024   //     LEN >= 0
2025   //     POS + LEN <= BIT_SIZE(I)
2026   // Return:  LEN == 0 ? 0 : (I >> POS) & (-1 >> (BIT_SIZE(I) - LEN))
2027   // For a conformant call, implementing (I >> POS) with a signed or an
2028   // unsigned shift produces the same result.  For a nonconformant call,
2029   // the two choices may produce different results.
2030   assert(args.size() == 3);
2031   mlir::Value pos = builder.createConvert(loc, resultType, args[1]);
2032   mlir::Value len = builder.createConvert(loc, resultType, args[2]);
2033   mlir::Value bitSize = builder.createIntegerConstant(
2034       loc, resultType, resultType.cast<mlir::IntegerType>().getWidth());
2035   auto shiftCount = builder.create<mlir::arith::SubIOp>(loc, bitSize, len);
2036   mlir::Value zero = builder.createIntegerConstant(loc, resultType, 0);
2037   mlir::Value ones = builder.createIntegerConstant(loc, resultType, -1);
2038   auto mask = builder.create<mlir::arith::ShRUIOp>(loc, ones, shiftCount);
2039   auto res1 = builder.create<mlir::arith::ShRSIOp>(loc, args[0], pos);
2040   auto res2 = builder.create<mlir::arith::AndIOp>(loc, res1, mask);
2041   auto lenIsZero = builder.create<mlir::arith::CmpIOp>(
2042       loc, mlir::arith::CmpIPredicate::eq, len, zero);
2043   return builder.create<mlir::arith::SelectOp>(loc, lenIsZero, zero, res2);
2044 }
2045 
2046 // IBSET
2047 mlir::Value IntrinsicLibrary::genIbset(mlir::Type resultType,
2048                                        llvm::ArrayRef<mlir::Value> args) {
2049   // A conformant IBSET(I,POS) call satisfies:
2050   //     POS >= 0
2051   //     POS < BIT_SIZE(I)
2052   // Return:  I | (1 << POS)
2053   assert(args.size() == 2);
2054   mlir::Value pos = builder.createConvert(loc, resultType, args[1]);
2055   mlir::Value one = builder.createIntegerConstant(loc, resultType, 1);
2056   auto mask = builder.create<mlir::arith::ShLIOp>(loc, one, pos);
2057   return builder.create<mlir::arith::OrIOp>(loc, args[0], mask);
2058 }
2059 
2060 // ICHAR
2061 fir::ExtendedValue
2062 IntrinsicLibrary::genIchar(mlir::Type resultType,
2063                            llvm::ArrayRef<fir::ExtendedValue> args) {
2064   // There can be an optional kind in second argument.
2065   assert(args.size() == 2);
2066   const fir::CharBoxValue *charBox = args[0].getCharBox();
2067   if (!charBox)
2068     llvm::report_fatal_error("expected character scalar");
2069 
2070   fir::factory::CharacterExprHelper helper{builder, loc};
2071   mlir::Value buffer = charBox->getBuffer();
2072   mlir::Type bufferTy = buffer.getType();
2073   mlir::Value charVal;
2074   if (auto charTy = bufferTy.dyn_cast<fir::CharacterType>()) {
2075     assert(charTy.singleton());
2076     charVal = buffer;
2077   } else {
2078     // Character is in memory, cast to fir.ref<char> and load.
2079     mlir::Type ty = fir::dyn_cast_ptrEleTy(bufferTy);
2080     if (!ty)
2081       llvm::report_fatal_error("expected memory type");
2082     // The length of in the character type may be unknown. Casting
2083     // to a singleton ref is required before loading.
2084     fir::CharacterType eleType = helper.getCharacterType(ty);
2085     fir::CharacterType charType =
2086         fir::CharacterType::get(builder.getContext(), eleType.getFKind(), 1);
2087     mlir::Type toTy = builder.getRefType(charType);
2088     mlir::Value cast = builder.createConvert(loc, toTy, buffer);
2089     charVal = builder.create<fir::LoadOp>(loc, cast);
2090   }
2091   LLVM_DEBUG(llvm::dbgs() << "ichar(" << charVal << ")\n");
2092   auto code = helper.extractCodeFromSingleton(charVal);
2093   return builder.create<mlir::arith::ExtUIOp>(loc, resultType, code);
2094 }
2095 
2096 // IEOR
2097 mlir::Value IntrinsicLibrary::genIeor(mlir::Type resultType,
2098                                       llvm::ArrayRef<mlir::Value> args) {
2099   assert(args.size() == 2);
2100   return builder.create<mlir::arith::XOrIOp>(loc, args[0], args[1]);
2101 }
2102 
2103 // ISHFT
2104 mlir::Value IntrinsicLibrary::genIshft(mlir::Type resultType,
2105                                        llvm::ArrayRef<mlir::Value> args) {
2106   // A conformant ISHFT(I,SHIFT) call satisfies:
2107   //     abs(SHIFT) <= BIT_SIZE(I)
2108   // Return:  abs(SHIFT) >= BIT_SIZE(I)
2109   //              ? 0
2110   //              : SHIFT < 0
2111   //                    ? I >> abs(SHIFT)
2112   //                    : I << abs(SHIFT)
2113   assert(args.size() == 2);
2114   mlir::Value bitSize = builder.createIntegerConstant(
2115       loc, resultType, resultType.cast<mlir::IntegerType>().getWidth());
2116   mlir::Value zero = builder.createIntegerConstant(loc, resultType, 0);
2117   mlir::Value shift = builder.createConvert(loc, resultType, args[1]);
2118   mlir::Value absShift = genAbs(resultType, {shift});
2119   auto left = builder.create<mlir::arith::ShLIOp>(loc, args[0], absShift);
2120   auto right = builder.create<mlir::arith::ShRUIOp>(loc, args[0], absShift);
2121   auto shiftIsLarge = builder.create<mlir::arith::CmpIOp>(
2122       loc, mlir::arith::CmpIPredicate::sge, absShift, bitSize);
2123   auto shiftIsNegative = builder.create<mlir::arith::CmpIOp>(
2124       loc, mlir::arith::CmpIPredicate::slt, shift, zero);
2125   auto sel =
2126       builder.create<mlir::arith::SelectOp>(loc, shiftIsNegative, right, left);
2127   return builder.create<mlir::arith::SelectOp>(loc, shiftIsLarge, zero, sel);
2128 }
2129 
2130 // ISHFTC
2131 mlir::Value IntrinsicLibrary::genIshftc(mlir::Type resultType,
2132                                         llvm::ArrayRef<mlir::Value> args) {
2133   // A conformant ISHFTC(I,SHIFT,SIZE) call satisfies:
2134   //     SIZE > 0
2135   //     SIZE <= BIT_SIZE(I)
2136   //     abs(SHIFT) <= SIZE
2137   // if SHIFT > 0
2138   //     leftSize = abs(SHIFT)
2139   //     rightSize = SIZE - abs(SHIFT)
2140   // else [if SHIFT < 0]
2141   //     leftSize = SIZE - abs(SHIFT)
2142   //     rightSize = abs(SHIFT)
2143   // unchanged = SIZE == BIT_SIZE(I) ? 0 : (I >> SIZE) << SIZE
2144   // leftMaskShift = BIT_SIZE(I) - leftSize
2145   // rightMaskShift = BIT_SIZE(I) - rightSize
2146   // left = (I >> rightSize) & (-1 >> leftMaskShift)
2147   // right = (I & (-1 >> rightMaskShift)) << leftSize
2148   // Return:  SHIFT == 0 || SIZE == abs(SHIFT) ? I : (unchanged | left | right)
2149   assert(args.size() == 3);
2150   mlir::Value bitSize = builder.createIntegerConstant(
2151       loc, resultType, resultType.cast<mlir::IntegerType>().getWidth());
2152   mlir::Value I = args[0];
2153   mlir::Value shift = builder.createConvert(loc, resultType, args[1]);
2154   mlir::Value size =
2155       args[2] ? builder.createConvert(loc, resultType, args[2]) : bitSize;
2156   mlir::Value zero = builder.createIntegerConstant(loc, resultType, 0);
2157   mlir::Value ones = builder.createIntegerConstant(loc, resultType, -1);
2158   mlir::Value absShift = genAbs(resultType, {shift});
2159   auto elseSize = builder.create<mlir::arith::SubIOp>(loc, size, absShift);
2160   auto shiftIsZero = builder.create<mlir::arith::CmpIOp>(
2161       loc, mlir::arith::CmpIPredicate::eq, shift, zero);
2162   auto shiftEqualsSize = builder.create<mlir::arith::CmpIOp>(
2163       loc, mlir::arith::CmpIPredicate::eq, absShift, size);
2164   auto shiftIsNop =
2165       builder.create<mlir::arith::OrIOp>(loc, shiftIsZero, shiftEqualsSize);
2166   auto shiftIsPositive = builder.create<mlir::arith::CmpIOp>(
2167       loc, mlir::arith::CmpIPredicate::sgt, shift, zero);
2168   auto leftSize = builder.create<mlir::arith::SelectOp>(loc, shiftIsPositive,
2169                                                         absShift, elseSize);
2170   auto rightSize = builder.create<mlir::arith::SelectOp>(loc, shiftIsPositive,
2171                                                          elseSize, absShift);
2172   auto hasUnchanged = builder.create<mlir::arith::CmpIOp>(
2173       loc, mlir::arith::CmpIPredicate::ne, size, bitSize);
2174   auto unchangedTmp1 = builder.create<mlir::arith::ShRUIOp>(loc, I, size);
2175   auto unchangedTmp2 =
2176       builder.create<mlir::arith::ShLIOp>(loc, unchangedTmp1, size);
2177   auto unchanged = builder.create<mlir::arith::SelectOp>(loc, hasUnchanged,
2178                                                          unchangedTmp2, zero);
2179   auto leftMaskShift =
2180       builder.create<mlir::arith::SubIOp>(loc, bitSize, leftSize);
2181   auto leftMask =
2182       builder.create<mlir::arith::ShRUIOp>(loc, ones, leftMaskShift);
2183   auto leftTmp = builder.create<mlir::arith::ShRUIOp>(loc, I, rightSize);
2184   auto left = builder.create<mlir::arith::AndIOp>(loc, leftTmp, leftMask);
2185   auto rightMaskShift =
2186       builder.create<mlir::arith::SubIOp>(loc, bitSize, rightSize);
2187   auto rightMask =
2188       builder.create<mlir::arith::ShRUIOp>(loc, ones, rightMaskShift);
2189   auto rightTmp = builder.create<mlir::arith::AndIOp>(loc, I, rightMask);
2190   auto right = builder.create<mlir::arith::ShLIOp>(loc, rightTmp, leftSize);
2191   auto resTmp = builder.create<mlir::arith::OrIOp>(loc, unchanged, left);
2192   auto res = builder.create<mlir::arith::OrIOp>(loc, resTmp, right);
2193   return builder.create<mlir::arith::SelectOp>(loc, shiftIsNop, I, res);
2194 }
2195 
2196 // LEN
2197 // Note that this is only used for an unrestricted intrinsic LEN call.
2198 // Other uses of LEN are rewritten as descriptor inquiries by the front-end.
2199 fir::ExtendedValue
2200 IntrinsicLibrary::genLen(mlir::Type resultType,
2201                          llvm::ArrayRef<fir::ExtendedValue> args) {
2202   // Optional KIND argument reflected in result type and otherwise ignored.
2203   assert(args.size() == 1 || args.size() == 2);
2204   mlir::Value len = fir::factory::readCharLen(builder, loc, args[0]);
2205   return builder.createConvert(loc, resultType, len);
2206 }
2207 
2208 // LEN_TRIM
2209 fir::ExtendedValue
2210 IntrinsicLibrary::genLenTrim(mlir::Type resultType,
2211                              llvm::ArrayRef<fir::ExtendedValue> args) {
2212   // Optional KIND argument reflected in result type and otherwise ignored.
2213   assert(args.size() == 1 || args.size() == 2);
2214   const fir::CharBoxValue *charBox = args[0].getCharBox();
2215   if (!charBox)
2216     TODO(loc, "character array len_trim");
2217   auto len =
2218       fir::factory::CharacterExprHelper(builder, loc).createLenTrim(*charBox);
2219   return builder.createConvert(loc, resultType, len);
2220 }
2221 
2222 // LGE, LGT, LLE, LLT
2223 template <mlir::arith::CmpIPredicate pred>
2224 fir::ExtendedValue
2225 IntrinsicLibrary::genCharacterCompare(mlir::Type type,
2226                                       llvm::ArrayRef<fir::ExtendedValue> args) {
2227   assert(args.size() == 2);
2228   return fir::runtime::genCharCompare(
2229       builder, loc, pred, fir::getBase(args[0]), fir::getLen(args[0]),
2230       fir::getBase(args[1]), fir::getLen(args[1]));
2231 }
2232 
2233 // Compare two FIR values and return boolean result as i1.
2234 template <Extremum extremum, ExtremumBehavior behavior>
2235 static mlir::Value createExtremumCompare(mlir::Location loc,
2236                                          fir::FirOpBuilder &builder,
2237                                          mlir::Value left, mlir::Value right) {
2238   static constexpr mlir::arith::CmpIPredicate integerPredicate =
2239       extremum == Extremum::Max ? mlir::arith::CmpIPredicate::sgt
2240                                 : mlir::arith::CmpIPredicate::slt;
2241   static constexpr mlir::arith::CmpFPredicate orderedCmp =
2242       extremum == Extremum::Max ? mlir::arith::CmpFPredicate::OGT
2243                                 : mlir::arith::CmpFPredicate::OLT;
2244   mlir::Type type = left.getType();
2245   mlir::Value result;
2246   if (fir::isa_real(type)) {
2247     // Note: the signaling/quit aspect of the result required by IEEE
2248     // cannot currently be obtained with LLVM without ad-hoc runtime.
2249     if constexpr (behavior == ExtremumBehavior::IeeeMinMaximumNumber) {
2250       // Return the number if one of the inputs is NaN and the other is
2251       // a number.
2252       auto leftIsResult =
2253           builder.create<mlir::arith::CmpFOp>(loc, orderedCmp, left, right);
2254       auto rightIsNan = builder.create<mlir::arith::CmpFOp>(
2255           loc, mlir::arith::CmpFPredicate::UNE, right, right);
2256       result =
2257           builder.create<mlir::arith::OrIOp>(loc, leftIsResult, rightIsNan);
2258     } else if constexpr (behavior == ExtremumBehavior::IeeeMinMaximum) {
2259       // Always return NaNs if one the input is NaNs
2260       auto leftIsResult =
2261           builder.create<mlir::arith::CmpFOp>(loc, orderedCmp, left, right);
2262       auto leftIsNan = builder.create<mlir::arith::CmpFOp>(
2263           loc, mlir::arith::CmpFPredicate::UNE, left, left);
2264       result = builder.create<mlir::arith::OrIOp>(loc, leftIsResult, leftIsNan);
2265     } else if constexpr (behavior == ExtremumBehavior::MinMaxss) {
2266       // If the left is a NaN, return the right whatever it is.
2267       result =
2268           builder.create<mlir::arith::CmpFOp>(loc, orderedCmp, left, right);
2269     } else if constexpr (behavior == ExtremumBehavior::PgfortranLlvm) {
2270       // If one of the operand is a NaN, return left whatever it is.
2271       static constexpr auto unorderedCmp =
2272           extremum == Extremum::Max ? mlir::arith::CmpFPredicate::UGT
2273                                     : mlir::arith::CmpFPredicate::ULT;
2274       result =
2275           builder.create<mlir::arith::CmpFOp>(loc, unorderedCmp, left, right);
2276     } else {
2277       // TODO: ieeeMinNum/ieeeMaxNum
2278       static_assert(behavior == ExtremumBehavior::IeeeMinMaxNum,
2279                     "ieeeMinNum/ieeeMaxNum behavior not implemented");
2280     }
2281   } else if (fir::isa_integer(type)) {
2282     result =
2283         builder.create<mlir::arith::CmpIOp>(loc, integerPredicate, left, right);
2284   } else if (fir::isa_char(type)) {
2285     // TODO: ! character min and max is tricky because the result
2286     // length is the length of the longest argument!
2287     // So we may need a temp.
2288     TODO(loc, "CHARACTER min and max");
2289   }
2290   assert(result && "result must be defined");
2291   return result;
2292 }
2293 
2294 // MAXLOC
2295 fir::ExtendedValue
2296 IntrinsicLibrary::genMaxloc(mlir::Type resultType,
2297                             llvm::ArrayRef<fir::ExtendedValue> args) {
2298   return genExtremumloc(fir::runtime::genMaxloc, fir::runtime::genMaxlocDim,
2299                         resultType, builder, loc, stmtCtx,
2300                         "unexpected result for Maxloc", args);
2301 }
2302 
2303 // MAXVAL
2304 fir::ExtendedValue
2305 IntrinsicLibrary::genMaxval(mlir::Type resultType,
2306                             llvm::ArrayRef<fir::ExtendedValue> args) {
2307   return genExtremumVal(fir::runtime::genMaxval, fir::runtime::genMaxvalDim,
2308                         fir::runtime::genMaxvalChar, resultType, builder, loc,
2309                         stmtCtx, "unexpected result for Maxval", args);
2310 }
2311 
2312 // MINLOC
2313 fir::ExtendedValue
2314 IntrinsicLibrary::genMinloc(mlir::Type resultType,
2315                             llvm::ArrayRef<fir::ExtendedValue> args) {
2316   return genExtremumloc(fir::runtime::genMinloc, fir::runtime::genMinlocDim,
2317                         resultType, builder, loc, stmtCtx,
2318                         "unexpected result for Minloc", args);
2319 }
2320 
2321 // MINVAL
2322 fir::ExtendedValue
2323 IntrinsicLibrary::genMinval(mlir::Type resultType,
2324                             llvm::ArrayRef<fir::ExtendedValue> args) {
2325   return genExtremumVal(fir::runtime::genMinval, fir::runtime::genMinvalDim,
2326                         fir::runtime::genMinvalChar, resultType, builder, loc,
2327                         stmtCtx, "unexpected result for Minval", args);
2328 }
2329 
2330 // MIN and MAX
2331 template <Extremum extremum, ExtremumBehavior behavior>
2332 mlir::Value IntrinsicLibrary::genExtremum(mlir::Type,
2333                                           llvm::ArrayRef<mlir::Value> args) {
2334   assert(args.size() >= 1);
2335   mlir::Value result = args[0];
2336   for (auto arg : args.drop_front()) {
2337     mlir::Value mask =
2338         createExtremumCompare<extremum, behavior>(loc, builder, result, arg);
2339     result = builder.create<mlir::arith::SelectOp>(loc, mask, result, arg);
2340   }
2341   return result;
2342 }
2343 
2344 // MOD
2345 mlir::Value IntrinsicLibrary::genMod(mlir::Type resultType,
2346                                      llvm::ArrayRef<mlir::Value> args) {
2347   assert(args.size() == 2);
2348   if (resultType.isa<mlir::IntegerType>())
2349     return builder.create<mlir::arith::RemSIOp>(loc, args[0], args[1]);
2350 
2351   // Use runtime. Note that mlir::arith::RemFOp implements floating point
2352   // remainder, but it does not work with fir::Real type.
2353   // TODO: consider using mlir::arith::RemFOp when possible, that may help
2354   // folding and  optimizations.
2355   return genRuntimeCall("mod", resultType, args);
2356 }
2357 
2358 // MODULO
2359 mlir::Value IntrinsicLibrary::genModulo(mlir::Type resultType,
2360                                         llvm::ArrayRef<mlir::Value> args) {
2361   assert(args.size() == 2);
2362   // No floored modulo op in LLVM/MLIR yet. TODO: add one to MLIR.
2363   // In the meantime, use a simple inlined implementation based on truncated
2364   // modulo (MOD(A, P) implemented by RemIOp, RemFOp). This avoids making manual
2365   // division and multiplication from MODULO formula.
2366   //  - If A/P > 0 or MOD(A,P)=0, then INT(A/P) = FLOOR(A/P), and MODULO = MOD.
2367   //  - Otherwise, when A/P < 0 and MOD(A,P) !=0, then MODULO(A, P) =
2368   //    A-FLOOR(A/P)*P = A-(INT(A/P)-1)*P = A-INT(A/P)*P+P = MOD(A,P)+P
2369   // Note that A/P < 0 if and only if A and P signs are different.
2370   if (resultType.isa<mlir::IntegerType>()) {
2371     auto remainder =
2372         builder.create<mlir::arith::RemSIOp>(loc, args[0], args[1]);
2373     auto argXor = builder.create<mlir::arith::XOrIOp>(loc, args[0], args[1]);
2374     mlir::Value zero = builder.createIntegerConstant(loc, argXor.getType(), 0);
2375     auto argSignDifferent = builder.create<mlir::arith::CmpIOp>(
2376         loc, mlir::arith::CmpIPredicate::slt, argXor, zero);
2377     auto remainderIsNotZero = builder.create<mlir::arith::CmpIOp>(
2378         loc, mlir::arith::CmpIPredicate::ne, remainder, zero);
2379     auto mustAddP = builder.create<mlir::arith::AndIOp>(loc, remainderIsNotZero,
2380                                                         argSignDifferent);
2381     auto remPlusP =
2382         builder.create<mlir::arith::AddIOp>(loc, remainder, args[1]);
2383     return builder.create<mlir::arith::SelectOp>(loc, mustAddP, remPlusP,
2384                                                  remainder);
2385   }
2386   // Real case
2387   auto remainder = builder.create<mlir::arith::RemFOp>(loc, args[0], args[1]);
2388   mlir::Value zero = builder.createRealZeroConstant(loc, remainder.getType());
2389   auto remainderIsNotZero = builder.create<mlir::arith::CmpFOp>(
2390       loc, mlir::arith::CmpFPredicate::UNE, remainder, zero);
2391   auto aLessThanZero = builder.create<mlir::arith::CmpFOp>(
2392       loc, mlir::arith::CmpFPredicate::OLT, args[0], zero);
2393   auto pLessThanZero = builder.create<mlir::arith::CmpFOp>(
2394       loc, mlir::arith::CmpFPredicate::OLT, args[1], zero);
2395   auto argSignDifferent =
2396       builder.create<mlir::arith::XOrIOp>(loc, aLessThanZero, pLessThanZero);
2397   auto mustAddP = builder.create<mlir::arith::AndIOp>(loc, remainderIsNotZero,
2398                                                       argSignDifferent);
2399   auto remPlusP = builder.create<mlir::arith::AddFOp>(loc, remainder, args[1]);
2400   return builder.create<mlir::arith::SelectOp>(loc, mustAddP, remPlusP,
2401                                                remainder);
2402 }
2403 
2404 // NINT
2405 mlir::Value IntrinsicLibrary::genNint(mlir::Type resultType,
2406                                       llvm::ArrayRef<mlir::Value> args) {
2407   assert(args.size() >= 1);
2408   // Skip optional kind argument to search the runtime; it is already reflected
2409   // in result type.
2410   return genRuntimeCall("nint", resultType, {args[0]});
2411 }
2412 
2413 // NOT
2414 mlir::Value IntrinsicLibrary::genNot(mlir::Type resultType,
2415                                      llvm::ArrayRef<mlir::Value> args) {
2416   assert(args.size() == 1);
2417   mlir::Value allOnes = builder.createIntegerConstant(loc, resultType, -1);
2418   return builder.create<mlir::arith::XOrIOp>(loc, args[0], allOnes);
2419 }
2420 
2421 // NULL
2422 fir::ExtendedValue
2423 IntrinsicLibrary::genNull(mlir::Type, llvm::ArrayRef<fir::ExtendedValue> args) {
2424   // NULL() without MOLD must be handled in the contexts where it can appear
2425   // (see table 16.5 of Fortran 2018 standard).
2426   assert(args.size() == 1 && isPresent(args[0]) &&
2427          "MOLD argument required to lower NULL outside of any context");
2428   const auto *mold = args[0].getBoxOf<fir::MutableBoxValue>();
2429   assert(mold && "MOLD must be a pointer or allocatable");
2430   fir::BoxType boxType = mold->getBoxTy();
2431   mlir::Value boxStorage = builder.createTemporary(loc, boxType);
2432   mlir::Value box = fir::factory::createUnallocatedBox(
2433       builder, loc, boxType, mold->nonDeferredLenParams());
2434   builder.create<fir::StoreOp>(loc, box, boxStorage);
2435   return fir::MutableBoxValue(boxStorage, mold->nonDeferredLenParams(), {});
2436 }
2437 
2438 // PACK
2439 fir::ExtendedValue
2440 IntrinsicLibrary::genPack(mlir::Type resultType,
2441                           llvm::ArrayRef<fir::ExtendedValue> args) {
2442   [[maybe_unused]] auto numArgs = args.size();
2443   assert(numArgs == 2 || numArgs == 3);
2444 
2445   // Handle required array argument
2446   mlir::Value array = builder.createBox(loc, args[0]);
2447 
2448   // Handle required mask argument
2449   mlir::Value mask = builder.createBox(loc, args[1]);
2450 
2451   // Handle optional vector argument
2452   mlir::Value vector = isAbsent(args, 2)
2453                            ? builder.create<fir::AbsentOp>(
2454                                  loc, fir::BoxType::get(builder.getI1Type()))
2455                            : builder.createBox(loc, args[2]);
2456 
2457   // Create mutable fir.box to be passed to the runtime for the result.
2458   mlir::Type resultArrayType = builder.getVarLenSeqTy(resultType, 1);
2459   fir::MutableBoxValue resultMutableBox =
2460       fir::factory::createTempMutableBox(builder, loc, resultArrayType);
2461   mlir::Value resultIrBox =
2462       fir::factory::getMutableIRBox(builder, loc, resultMutableBox);
2463 
2464   fir::runtime::genPack(builder, loc, resultIrBox, array, mask, vector);
2465 
2466   return readAndAddCleanUp(resultMutableBox, resultType,
2467                            "unexpected result for PACK");
2468 }
2469 
2470 // PRODUCT
2471 fir::ExtendedValue
2472 IntrinsicLibrary::genProduct(mlir::Type resultType,
2473                              llvm::ArrayRef<fir::ExtendedValue> args) {
2474   return genProdOrSum(fir::runtime::genProduct, fir::runtime::genProductDim,
2475                       resultType, builder, loc, stmtCtx,
2476                       "unexpected result for Product", args);
2477 }
2478 
2479 // RANDOM_INIT
2480 void IntrinsicLibrary::genRandomInit(llvm::ArrayRef<fir::ExtendedValue> args) {
2481   assert(args.size() == 2);
2482   Fortran::lower::genRandomInit(builder, loc, fir::getBase(args[0]),
2483                                 fir::getBase(args[1]));
2484 }
2485 
2486 // RANDOM_NUMBER
2487 void IntrinsicLibrary::genRandomNumber(
2488     llvm::ArrayRef<fir::ExtendedValue> args) {
2489   assert(args.size() == 1);
2490   Fortran::lower::genRandomNumber(builder, loc, fir::getBase(args[0]));
2491 }
2492 
2493 // RANDOM_SEED
2494 void IntrinsicLibrary::genRandomSeed(llvm::ArrayRef<fir::ExtendedValue> args) {
2495   assert(args.size() == 3);
2496   for (int i = 0; i < 3; ++i)
2497     if (isPresent(args[i])) {
2498       Fortran::lower::genRandomSeed(builder, loc, i, fir::getBase(args[i]));
2499       return;
2500     }
2501   Fortran::lower::genRandomSeed(builder, loc, -1, mlir::Value{});
2502 }
2503 
2504 // SCAN
2505 fir::ExtendedValue
2506 IntrinsicLibrary::genScan(mlir::Type resultType,
2507                           llvm::ArrayRef<fir::ExtendedValue> args) {
2508 
2509   assert(args.size() == 4);
2510 
2511   if (isAbsent(args[3])) {
2512     // Kind not specified, so call scan/verify runtime routine that is
2513     // specialized on the kind of characters in string.
2514 
2515     // Handle required string base arg
2516     mlir::Value stringBase = fir::getBase(args[0]);
2517 
2518     // Handle required set string base arg
2519     mlir::Value setBase = fir::getBase(args[1]);
2520 
2521     // Handle kind argument; it is the kind of character in this case
2522     fir::KindTy kind =
2523         fir::factory::CharacterExprHelper{builder, loc}.getCharacterKind(
2524             stringBase.getType());
2525 
2526     // Get string length argument
2527     mlir::Value stringLen = fir::getLen(args[0]);
2528 
2529     // Get set string length argument
2530     mlir::Value setLen = fir::getLen(args[1]);
2531 
2532     // Handle optional back argument
2533     mlir::Value back =
2534         isAbsent(args[2])
2535             ? builder.createIntegerConstant(loc, builder.getI1Type(), 0)
2536             : fir::getBase(args[2]);
2537 
2538     return builder.createConvert(loc, resultType,
2539                                  fir::runtime::genScan(builder, loc, kind,
2540                                                        stringBase, stringLen,
2541                                                        setBase, setLen, back));
2542   }
2543   // else use the runtime descriptor version of scan/verify
2544 
2545   // Handle optional argument, back
2546   auto makeRefThenEmbox = [&](mlir::Value b) {
2547     fir::LogicalType logTy = fir::LogicalType::get(
2548         builder.getContext(), builder.getKindMap().defaultLogicalKind());
2549     mlir::Value temp = builder.createTemporary(loc, logTy);
2550     mlir::Value castb = builder.createConvert(loc, logTy, b);
2551     builder.create<fir::StoreOp>(loc, castb, temp);
2552     return builder.createBox(loc, temp);
2553   };
2554   mlir::Value back = fir::isUnboxedValue(args[2])
2555                          ? makeRefThenEmbox(*args[2].getUnboxed())
2556                          : builder.create<fir::AbsentOp>(
2557                                loc, fir::BoxType::get(builder.getI1Type()));
2558 
2559   // Handle required string argument
2560   mlir::Value string = builder.createBox(loc, args[0]);
2561 
2562   // Handle required set argument
2563   mlir::Value set = builder.createBox(loc, args[1]);
2564 
2565   // Handle kind argument
2566   mlir::Value kind = fir::getBase(args[3]);
2567 
2568   // Create result descriptor
2569   fir::MutableBoxValue resultMutableBox =
2570       fir::factory::createTempMutableBox(builder, loc, resultType);
2571   mlir::Value resultIrBox =
2572       fir::factory::getMutableIRBox(builder, loc, resultMutableBox);
2573 
2574   fir::runtime::genScanDescriptor(builder, loc, resultIrBox, string, set, back,
2575                                   kind);
2576 
2577   // Handle cleanup of allocatable result descriptor and return
2578   return readAndAddCleanUp(resultMutableBox, resultType, "SCAN");
2579 }
2580 
2581 // SET_EXPONENT
2582 mlir::Value IntrinsicLibrary::genSetExponent(mlir::Type resultType,
2583                                              llvm::ArrayRef<mlir::Value> args) {
2584   assert(args.size() == 2);
2585 
2586   return builder.createConvert(
2587       loc, resultType,
2588       fir::runtime::genSetExponent(builder, loc, fir::getBase(args[0]),
2589                                    fir::getBase(args[1])));
2590 }
2591 
2592 // SUM
2593 fir::ExtendedValue
2594 IntrinsicLibrary::genSum(mlir::Type resultType,
2595                          llvm::ArrayRef<fir::ExtendedValue> args) {
2596   return genProdOrSum(fir::runtime::genSum, fir::runtime::genSumDim, resultType,
2597                       builder, loc, stmtCtx, "unexpected result for Sum", args);
2598 }
2599 
2600 // SYSTEM_CLOCK
2601 void IntrinsicLibrary::genSystemClock(llvm::ArrayRef<fir::ExtendedValue> args) {
2602   assert(args.size() == 3);
2603   Fortran::lower::genSystemClock(builder, loc, fir::getBase(args[0]),
2604                                  fir::getBase(args[1]), fir::getBase(args[2]));
2605 }
2606 
2607 // SIZE
2608 fir::ExtendedValue
2609 IntrinsicLibrary::genSize(mlir::Type resultType,
2610                           llvm::ArrayRef<fir::ExtendedValue> args) {
2611   // Note that the value of the KIND argument is already reflected in the
2612   // resultType
2613   assert(args.size() == 3);
2614   if (const auto *boxValue = args[0].getBoxOf<fir::BoxValue>())
2615     if (boxValue->hasAssumedRank())
2616       TODO(loc, "SIZE intrinsic with assumed rank argument");
2617 
2618   // Get the ARRAY argument
2619   mlir::Value array = builder.createBox(loc, args[0]);
2620 
2621   // The front-end rewrites SIZE without the DIM argument to
2622   // an array of SIZE with DIM in most cases, but it may not be
2623   // possible in some cases like when in SIZE(function_call()).
2624   if (isAbsent(args, 1))
2625     return builder.createConvert(loc, resultType,
2626                                  fir::runtime::genSize(builder, loc, array));
2627 
2628   // Get the DIM argument.
2629   mlir::Value dim = fir::getBase(args[1]);
2630   if (!fir::isa_ref_type(dim.getType()))
2631     return builder.createConvert(
2632         loc, resultType, fir::runtime::genSizeDim(builder, loc, array, dim));
2633 
2634   mlir::Value isDynamicallyAbsent = builder.genIsNull(loc, dim);
2635   return builder
2636       .genIfOp(loc, {resultType}, isDynamicallyAbsent,
2637                /*withElseRegion=*/true)
2638       .genThen([&]() {
2639         mlir::Value size = builder.createConvert(
2640             loc, resultType, fir::runtime::genSize(builder, loc, array));
2641         builder.create<fir::ResultOp>(loc, size);
2642       })
2643       .genElse([&]() {
2644         mlir::Value dimValue = builder.create<fir::LoadOp>(loc, dim);
2645         mlir::Value size = builder.createConvert(
2646             loc, resultType,
2647             fir::runtime::genSizeDim(builder, loc, array, dimValue));
2648         builder.create<fir::ResultOp>(loc, size);
2649       })
2650       .getResults()[0];
2651 }
2652 
2653 // TRANSFER
2654 fir::ExtendedValue
2655 IntrinsicLibrary::genTransfer(mlir::Type resultType,
2656                               llvm::ArrayRef<fir::ExtendedValue> args) {
2657 
2658   assert(args.size() >= 2); // args.size() == 2 when size argument is omitted.
2659 
2660   // Handle source argument
2661   mlir::Value source = builder.createBox(loc, args[0]);
2662 
2663   // Handle mold argument
2664   mlir::Value mold = builder.createBox(loc, args[1]);
2665   fir::BoxValue moldTmp = mold;
2666   unsigned moldRank = moldTmp.rank();
2667 
2668   bool absentSize = (args.size() == 2);
2669 
2670   // Create mutable fir.box to be passed to the runtime for the result.
2671   mlir::Type type = (moldRank == 0 && absentSize)
2672                         ? resultType
2673                         : builder.getVarLenSeqTy(resultType, 1);
2674   fir::MutableBoxValue resultMutableBox =
2675       fir::factory::createTempMutableBox(builder, loc, type);
2676 
2677   if (moldRank == 0 && absentSize) {
2678     // This result is a scalar in this case.
2679     mlir::Value resultIrBox =
2680         fir::factory::getMutableIRBox(builder, loc, resultMutableBox);
2681 
2682     Fortran::lower::genTransfer(builder, loc, resultIrBox, source, mold);
2683   } else {
2684     // The result is a rank one array in this case.
2685     mlir::Value resultIrBox =
2686         fir::factory::getMutableIRBox(builder, loc, resultMutableBox);
2687 
2688     if (absentSize) {
2689       Fortran::lower::genTransfer(builder, loc, resultIrBox, source, mold);
2690     } else {
2691       mlir::Value sizeArg = fir::getBase(args[2]);
2692       Fortran::lower::genTransferSize(builder, loc, resultIrBox, source, mold,
2693                                       sizeArg);
2694     }
2695   }
2696   return readAndAddCleanUp(resultMutableBox, resultType,
2697                            "unexpected result for TRANSFER");
2698 }
2699 
2700 // LBOUND
2701 fir::ExtendedValue
2702 IntrinsicLibrary::genLbound(mlir::Type resultType,
2703                             llvm::ArrayRef<fir::ExtendedValue> args) {
2704   // Calls to LBOUND that don't have the DIM argument, or for which
2705   // the DIM is a compile time constant, are folded to descriptor inquiries by
2706   // semantics.  This function covers the situations where a call to the
2707   // runtime is required.
2708   assert(args.size() == 3);
2709   assert(!isAbsent(args[1]));
2710   if (const auto *boxValue = args[0].getBoxOf<fir::BoxValue>())
2711     if (boxValue->hasAssumedRank())
2712       TODO(loc, "LBOUND intrinsic with assumed rank argument");
2713 
2714   const fir::ExtendedValue &array = args[0];
2715   mlir::Value box = array.match(
2716       [&](const fir::BoxValue &boxValue) -> mlir::Value {
2717         // This entity is mapped to a fir.box that may not contain the local
2718         // lower bound information if it is a dummy. Rebox it with the local
2719         // shape information.
2720         mlir::Value localShape = builder.createShape(loc, array);
2721         mlir::Value oldBox = boxValue.getAddr();
2722         return builder.create<fir::ReboxOp>(
2723             loc, oldBox.getType(), oldBox, localShape, /*slice=*/mlir::Value{});
2724       },
2725       [&](const auto &) -> mlir::Value {
2726         // This a pointer/allocatable, or an entity not yet tracked with a
2727         // fir.box. For pointer/allocatable, createBox will forward the
2728         // descriptor that contains the correct lower bound information. For
2729         // other entities, a new fir.box will be made with the local lower
2730         // bounds.
2731         return builder.createBox(loc, array);
2732       });
2733 
2734   mlir::Value dim = fir::getBase(args[1]);
2735   return builder.createConvert(
2736       loc, resultType,
2737       fir::runtime::genLboundDim(builder, loc, fir::getBase(box), dim));
2738 }
2739 
2740 // UBOUND
2741 fir::ExtendedValue
2742 IntrinsicLibrary::genUbound(mlir::Type resultType,
2743                             llvm::ArrayRef<fir::ExtendedValue> args) {
2744   assert(args.size() == 3 || args.size() == 2);
2745   if (args.size() == 3) {
2746     // Handle calls to UBOUND with the DIM argument, which return a scalar
2747     mlir::Value extent = fir::getBase(genSize(resultType, args));
2748     mlir::Value lbound = fir::getBase(genLbound(resultType, args));
2749 
2750     mlir::Value one = builder.createIntegerConstant(loc, resultType, 1);
2751     mlir::Value ubound = builder.create<mlir::arith::SubIOp>(loc, lbound, one);
2752     return builder.create<mlir::arith::AddIOp>(loc, ubound, extent);
2753   } else {
2754     // Handle calls to UBOUND without the DIM argument, which return an array
2755     mlir::Value kind = isAbsent(args[1])
2756                            ? builder.createIntegerConstant(
2757                                  loc, builder.getIndexType(),
2758                                  builder.getKindMap().defaultIntegerKind())
2759                            : fir::getBase(args[1]);
2760 
2761     // Create mutable fir.box to be passed to the runtime for the result.
2762     mlir::Type type = builder.getVarLenSeqTy(resultType, /*rank=*/1);
2763     fir::MutableBoxValue resultMutableBox =
2764         fir::factory::createTempMutableBox(builder, loc, type);
2765     mlir::Value resultIrBox =
2766         fir::factory::getMutableIRBox(builder, loc, resultMutableBox);
2767 
2768     fir::runtime::genUbound(builder, loc, resultIrBox, fir::getBase(args[0]),
2769                             kind);
2770 
2771     return readAndAddCleanUp(resultMutableBox, resultType, "UBOUND");
2772   }
2773   return mlir::Value();
2774 }
2775 
2776 // UNPACK
2777 fir::ExtendedValue
2778 IntrinsicLibrary::genUnpack(mlir::Type resultType,
2779                             llvm::ArrayRef<fir::ExtendedValue> args) {
2780   assert(args.size() == 3);
2781 
2782   // Handle required vector argument
2783   mlir::Value vector = builder.createBox(loc, args[0]);
2784 
2785   // Handle required mask argument
2786   fir::BoxValue maskBox = builder.createBox(loc, args[1]);
2787   mlir::Value mask = fir::getBase(maskBox);
2788   unsigned maskRank = maskBox.rank();
2789 
2790   // Handle required field argument
2791   mlir::Value field = builder.createBox(loc, args[2]);
2792 
2793   // Create mutable fir.box to be passed to the runtime for the result.
2794   mlir::Type resultArrayType = builder.getVarLenSeqTy(resultType, maskRank);
2795   fir::MutableBoxValue resultMutableBox =
2796       fir::factory::createTempMutableBox(builder, loc, resultArrayType);
2797   mlir::Value resultIrBox =
2798       fir::factory::getMutableIRBox(builder, loc, resultMutableBox);
2799 
2800   fir::runtime::genUnpack(builder, loc, resultIrBox, vector, mask, field);
2801 
2802   return readAndAddCleanUp(resultMutableBox, resultType,
2803                            "unexpected result for UNPACK");
2804 }
2805 
2806 // VERIFY
2807 fir::ExtendedValue
2808 IntrinsicLibrary::genVerify(mlir::Type resultType,
2809                             llvm::ArrayRef<fir::ExtendedValue> args) {
2810 
2811   assert(args.size() == 4);
2812 
2813   if (isAbsent(args[3])) {
2814     // Kind not specified, so call scan/verify runtime routine that is
2815     // specialized on the kind of characters in string.
2816 
2817     // Handle required string base arg
2818     mlir::Value stringBase = fir::getBase(args[0]);
2819 
2820     // Handle required set string base arg
2821     mlir::Value setBase = fir::getBase(args[1]);
2822 
2823     // Handle kind argument; it is the kind of character in this case
2824     fir::KindTy kind =
2825         fir::factory::CharacterExprHelper{builder, loc}.getCharacterKind(
2826             stringBase.getType());
2827 
2828     // Get string length argument
2829     mlir::Value stringLen = fir::getLen(args[0]);
2830 
2831     // Get set string length argument
2832     mlir::Value setLen = fir::getLen(args[1]);
2833 
2834     // Handle optional back argument
2835     mlir::Value back =
2836         isAbsent(args[2])
2837             ? builder.createIntegerConstant(loc, builder.getI1Type(), 0)
2838             : fir::getBase(args[2]);
2839 
2840     return builder.createConvert(
2841         loc, resultType,
2842         fir::runtime::genVerify(builder, loc, kind, stringBase, stringLen,
2843                                 setBase, setLen, back));
2844   }
2845   // else use the runtime descriptor version of scan/verify
2846 
2847   // Handle optional argument, back
2848   auto makeRefThenEmbox = [&](mlir::Value b) {
2849     fir::LogicalType logTy = fir::LogicalType::get(
2850         builder.getContext(), builder.getKindMap().defaultLogicalKind());
2851     mlir::Value temp = builder.createTemporary(loc, logTy);
2852     mlir::Value castb = builder.createConvert(loc, logTy, b);
2853     builder.create<fir::StoreOp>(loc, castb, temp);
2854     return builder.createBox(loc, temp);
2855   };
2856   mlir::Value back = fir::isUnboxedValue(args[2])
2857                          ? makeRefThenEmbox(*args[2].getUnboxed())
2858                          : builder.create<fir::AbsentOp>(
2859                                loc, fir::BoxType::get(builder.getI1Type()));
2860 
2861   // Handle required string argument
2862   mlir::Value string = builder.createBox(loc, args[0]);
2863 
2864   // Handle required set argument
2865   mlir::Value set = builder.createBox(loc, args[1]);
2866 
2867   // Handle kind argument
2868   mlir::Value kind = fir::getBase(args[3]);
2869 
2870   // Create result descriptor
2871   fir::MutableBoxValue resultMutableBox =
2872       fir::factory::createTempMutableBox(builder, loc, resultType);
2873   mlir::Value resultIrBox =
2874       fir::factory::getMutableIRBox(builder, loc, resultMutableBox);
2875 
2876   fir::runtime::genVerifyDescriptor(builder, loc, resultIrBox, string, set,
2877                                     back, kind);
2878 
2879   // Handle cleanup of allocatable result descriptor and return
2880   return readAndAddCleanUp(resultMutableBox, resultType, "VERIFY");
2881 }
2882 
2883 //===----------------------------------------------------------------------===//
2884 // Argument lowering rules interface
2885 //===----------------------------------------------------------------------===//
2886 
2887 const Fortran::lower::IntrinsicArgumentLoweringRules *
2888 Fortran::lower::getIntrinsicArgumentLowering(llvm::StringRef intrinsicName) {
2889   if (const IntrinsicHandler *handler = findIntrinsicHandler(intrinsicName))
2890     if (!handler->argLoweringRules.hasDefaultRules())
2891       return &handler->argLoweringRules;
2892   return nullptr;
2893 }
2894 
2895 /// Return how argument \p argName should be lowered given the rules for the
2896 /// intrinsic function.
2897 Fortran::lower::ArgLoweringRule Fortran::lower::lowerIntrinsicArgumentAs(
2898     mlir::Location loc, const IntrinsicArgumentLoweringRules &rules,
2899     llvm::StringRef argName) {
2900   for (const IntrinsicDummyArgument &arg : rules.args) {
2901     if (arg.name && arg.name == argName)
2902       return {arg.lowerAs, arg.handleDynamicOptional};
2903   }
2904   fir::emitFatalError(
2905       loc, "internal: unknown intrinsic argument name in lowering '" + argName +
2906                "'");
2907 }
2908 
2909 //===----------------------------------------------------------------------===//
2910 // Public intrinsic call helpers
2911 //===----------------------------------------------------------------------===//
2912 
2913 fir::ExtendedValue
2914 Fortran::lower::genIntrinsicCall(fir::FirOpBuilder &builder, mlir::Location loc,
2915                                  llvm::StringRef name,
2916                                  llvm::Optional<mlir::Type> resultType,
2917                                  llvm::ArrayRef<fir::ExtendedValue> args,
2918                                  Fortran::lower::StatementContext &stmtCtx) {
2919   return IntrinsicLibrary{builder, loc, &stmtCtx}.genIntrinsicCall(
2920       name, resultType, args);
2921 }
2922 
2923 mlir::Value Fortran::lower::genMax(fir::FirOpBuilder &builder,
2924                                    mlir::Location loc,
2925                                    llvm::ArrayRef<mlir::Value> args) {
2926   assert(args.size() > 0 && "max requires at least one argument");
2927   return IntrinsicLibrary{builder, loc}
2928       .genExtremum<Extremum::Max, ExtremumBehavior::MinMaxss>(args[0].getType(),
2929                                                               args);
2930 }
2931 
2932 mlir::Value Fortran::lower::genPow(fir::FirOpBuilder &builder,
2933                                    mlir::Location loc, mlir::Type type,
2934                                    mlir::Value x, mlir::Value y) {
2935   return IntrinsicLibrary{builder, loc}.genRuntimeCall("pow", type, {x, y});
2936 }
2937