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