1 //===-- IntrinsicCall.cpp -------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // Helper routines for constructing the FIR dialect of MLIR. As FIR is a
10 // dialect of MLIR, it makes extensive use of MLIR interfaces and MLIR's coding
11 // style (https://mlir.llvm.org/getting_started/DeveloperGuide/) is used in this
12 // module.
13 //
14 //===----------------------------------------------------------------------===//
15 
16 #include "flang/Lower/IntrinsicCall.h"
17 #include "flang/Common/static-multimap-view.h"
18 #include "flang/Lower/Mangler.h"
19 #include "flang/Lower/Runtime.h"
20 #include "flang/Lower/StatementContext.h"
21 #include "flang/Lower/SymbolMap.h"
22 #include "flang/Lower/Todo.h"
23 #include "flang/Optimizer/Builder/Character.h"
24 #include "flang/Optimizer/Builder/Complex.h"
25 #include "flang/Optimizer/Builder/FIRBuilder.h"
26 #include "flang/Optimizer/Builder/MutableBox.h"
27 #include "flang/Optimizer/Builder/Runtime/Character.h"
28 #include "flang/Optimizer/Builder/Runtime/Inquiry.h"
29 #include "flang/Optimizer/Builder/Runtime/RTBuilder.h"
30 #include "flang/Optimizer/Builder/Runtime/Reduction.h"
31 #include "flang/Optimizer/Dialect/FIROpsSupport.h"
32 #include "flang/Optimizer/Support/FatalError.h"
33 #include "mlir/Dialect/LLVMIR/LLVMDialect.h"
34 #include "llvm/Support/CommandLine.h"
35 
36 #define DEBUG_TYPE "flang-lower-intrinsic"
37 
38 #define PGMATH_DECLARE
39 #include "flang/Evaluate/pgmath.h.inc"
40 
41 /// Enums used to templatize and share lowering of MIN and MAX.
42 enum class Extremum { Min, Max };
43 
44 // There are different ways to deal with NaNs in MIN and MAX.
45 // Known existing behaviors are listed below and can be selected for
46 // f18 MIN/MAX implementation.
47 enum class ExtremumBehavior {
48   // Note: the Signaling/quiet aspect of NaNs in the behaviors below are
49   // not described because there is no way to control/observe such aspect in
50   // MLIR/LLVM yet. The IEEE behaviors come with requirements regarding this
51   // aspect that are therefore currently not enforced. In the descriptions
52   // below, NaNs can be signaling or quite. Returned NaNs may be signaling
53   // if one of the input NaN was signaling but it cannot be guaranteed either.
54   // Existing compilers using an IEEE behavior (gfortran) also do not fulfill
55   // signaling/quiet requirements.
56   IeeeMinMaximumNumber,
57   // IEEE minimumNumber/maximumNumber behavior (754-2019, section 9.6):
58   // If one of the argument is and number and the other is NaN, return the
59   // number. If both arguements are NaN, return NaN.
60   // Compilers: gfortran.
61   IeeeMinMaximum,
62   // IEEE minimum/maximum behavior (754-2019, section 9.6):
63   // If one of the argument is NaN, return NaN.
64   MinMaxss,
65   // x86 minss/maxss behavior:
66   // If the second argument is a number and the other is NaN, return the number.
67   // In all other cases where at least one operand is NaN, return NaN.
68   // Compilers: xlf (only for MAX), ifort, pgfortran -nollvm, and nagfor.
69   PgfortranLlvm,
70   // "Opposite of" x86 minss/maxss behavior:
71   // If the first argument is a number and the other is NaN, return the
72   // number.
73   // In all other cases where at least one operand is NaN, return NaN.
74   // Compilers: xlf (only for MIN), and pgfortran (with llvm).
75   IeeeMinMaxNum
76   // IEEE minNum/maxNum behavior (754-2008, section 5.3.1):
77   // TODO: Not implemented.
78   // It is the only behavior where the signaling/quiet aspect of a NaN argument
79   // impacts if the result should be NaN or the argument that is a number.
80   // LLVM/MLIR do not provide ways to observe this aspect, so it is not
81   // possible to implement it without some target dependent runtime.
82 };
83 
84 /// This file implements lowering of Fortran intrinsic procedures.
85 /// Intrinsics are lowered to a mix of FIR and MLIR operations as
86 /// well as call to runtime functions or LLVM intrinsics.
87 
88 /// Lowering of intrinsic procedure calls is based on a map that associates
89 /// Fortran intrinsic generic names to FIR generator functions.
90 /// All generator functions are member functions of the IntrinsicLibrary class
91 /// and have the same interface.
92 /// If no generator is given for an intrinsic name, a math runtime library
93 /// is searched for an implementation and, if a runtime function is found,
94 /// a call is generated for it. LLVM intrinsics are handled as a math
95 /// runtime library here.
96 
97 fir::ExtendedValue Fortran::lower::getAbsentIntrinsicArgument() {
98   return fir::UnboxedValue{};
99 }
100 
101 /// Test if an ExtendedValue is absent.
102 static bool isAbsent(const fir::ExtendedValue &exv) {
103   return !fir::getBase(exv);
104 }
105 static bool isAbsent(llvm::ArrayRef<fir::ExtendedValue> args, size_t argIndex) {
106   return args.size() <= argIndex || isAbsent(args[argIndex]);
107 }
108 
109 /// Test if an ExtendedValue is present.
110 static bool isPresent(const fir::ExtendedValue &exv) { return !isAbsent(exv); }
111 
112 /// Process calls to Maxval, Minval, Product, Sum intrinsic functions that
113 /// take a DIM argument.
114 template <typename FD>
115 static fir::ExtendedValue
116 genFuncDim(FD funcDim, mlir::Type resultType, fir::FirOpBuilder &builder,
117            mlir::Location loc, Fortran::lower::StatementContext *stmtCtx,
118            llvm::StringRef errMsg, mlir::Value array, fir::ExtendedValue dimArg,
119            mlir::Value mask, int rank) {
120 
121   // Create mutable fir.box to be passed to the runtime for the result.
122   mlir::Type resultArrayType = builder.getVarLenSeqTy(resultType, rank - 1);
123   fir::MutableBoxValue resultMutableBox =
124       fir::factory::createTempMutableBox(builder, loc, resultArrayType);
125   mlir::Value resultIrBox =
126       fir::factory::getMutableIRBox(builder, loc, resultMutableBox);
127 
128   mlir::Value dim =
129       isAbsent(dimArg)
130           ? builder.createIntegerConstant(loc, builder.getIndexType(), 0)
131           : fir::getBase(dimArg);
132   funcDim(builder, loc, resultIrBox, array, dim, mask);
133 
134   fir::ExtendedValue res =
135       fir::factory::genMutableBoxRead(builder, loc, resultMutableBox);
136   return res.match(
137       [&](const fir::ArrayBoxValue &box) -> fir::ExtendedValue {
138         // Add cleanup code
139         assert(stmtCtx);
140         fir::FirOpBuilder *bldr = &builder;
141         mlir::Value temp = box.getAddr();
142         stmtCtx->attachCleanup(
143             [=]() { bldr->create<fir::FreeMemOp>(loc, temp); });
144         return box;
145       },
146       [&](const fir::CharArrayBoxValue &box) -> fir::ExtendedValue {
147         // Add cleanup code
148         assert(stmtCtx);
149         fir::FirOpBuilder *bldr = &builder;
150         mlir::Value temp = box.getAddr();
151         stmtCtx->attachCleanup(
152             [=]() { bldr->create<fir::FreeMemOp>(loc, temp); });
153         return box;
154       },
155       [&](const auto &) -> fir::ExtendedValue {
156         fir::emitFatalError(loc, errMsg);
157       });
158 }
159 
160 /// Process calls to Product, Sum intrinsic functions
161 template <typename FN, typename FD>
162 static fir::ExtendedValue
163 genProdOrSum(FN func, FD funcDim, mlir::Type resultType,
164              fir::FirOpBuilder &builder, mlir::Location loc,
165              Fortran::lower::StatementContext *stmtCtx, llvm::StringRef errMsg,
166              llvm::ArrayRef<fir::ExtendedValue> args) {
167 
168   assert(args.size() == 3);
169 
170   // Handle required array argument
171   fir::BoxValue arryTmp = builder.createBox(loc, args[0]);
172   mlir::Value array = fir::getBase(arryTmp);
173   int rank = arryTmp.rank();
174   assert(rank >= 1);
175 
176   // Handle optional mask argument
177   auto mask = isAbsent(args[2])
178                   ? builder.create<fir::AbsentOp>(
179                         loc, fir::BoxType::get(builder.getI1Type()))
180                   : builder.createBox(loc, args[2]);
181 
182   bool absentDim = isAbsent(args[1]);
183 
184   // We call the type specific versions because the result is scalar
185   // in the case below.
186   if (absentDim || rank == 1) {
187     mlir::Type ty = array.getType();
188     mlir::Type arrTy = fir::dyn_cast_ptrOrBoxEleTy(ty);
189     auto eleTy = arrTy.cast<fir::SequenceType>().getEleTy();
190     if (fir::isa_complex(eleTy)) {
191       mlir::Value result = builder.createTemporary(loc, eleTy);
192       func(builder, loc, array, mask, result);
193       return builder.create<fir::LoadOp>(loc, result);
194     }
195     auto resultBox = builder.create<fir::AbsentOp>(
196         loc, fir::BoxType::get(builder.getI1Type()));
197     return func(builder, loc, array, mask, resultBox);
198   }
199   // Handle Product/Sum cases that have an array result.
200   return genFuncDim(funcDim, resultType, builder, loc, stmtCtx, errMsg, array,
201                     args[1], mask, rank);
202 }
203 
204 /// Process calls to DotProduct
205 template <typename FN>
206 static fir::ExtendedValue
207 genDotProd(FN func, mlir::Type resultType, fir::FirOpBuilder &builder,
208            mlir::Location loc, Fortran::lower::StatementContext *stmtCtx,
209            llvm::ArrayRef<fir::ExtendedValue> args) {
210 
211   assert(args.size() == 2);
212 
213   // Handle required vector arguments
214   mlir::Value vectorA = fir::getBase(args[0]);
215   mlir::Value vectorB = fir::getBase(args[1]);
216 
217   mlir::Type eleTy = fir::dyn_cast_ptrOrBoxEleTy(vectorA.getType())
218                          .cast<fir::SequenceType>()
219                          .getEleTy();
220   if (fir::isa_complex(eleTy)) {
221     mlir::Value result = builder.createTemporary(loc, eleTy);
222     func(builder, loc, vectorA, vectorB, result);
223     return builder.create<fir::LoadOp>(loc, result);
224   }
225 
226   auto resultBox = builder.create<fir::AbsentOp>(
227       loc, fir::BoxType::get(builder.getI1Type()));
228   return func(builder, loc, vectorA, vectorB, resultBox);
229 }
230 
231 /// Process calls to Maxval, Minval, Product, Sum intrinsic functions
232 template <typename FN, typename FD, typename FC>
233 static fir::ExtendedValue
234 genExtremumVal(FN func, FD funcDim, FC funcChar, mlir::Type resultType,
235                fir::FirOpBuilder &builder, mlir::Location loc,
236                Fortran::lower::StatementContext *stmtCtx,
237                llvm::StringRef errMsg,
238                llvm::ArrayRef<fir::ExtendedValue> args) {
239 
240   assert(args.size() == 3);
241 
242   // Handle required array argument
243   fir::BoxValue arryTmp = builder.createBox(loc, args[0]);
244   mlir::Value array = fir::getBase(arryTmp);
245   int rank = arryTmp.rank();
246   assert(rank >= 1);
247   bool hasCharacterResult = arryTmp.isCharacter();
248 
249   // Handle optional mask argument
250   auto mask = isAbsent(args[2])
251                   ? builder.create<fir::AbsentOp>(
252                         loc, fir::BoxType::get(builder.getI1Type()))
253                   : builder.createBox(loc, args[2]);
254 
255   bool absentDim = isAbsent(args[1]);
256 
257   // For Maxval/MinVal, we call the type specific versions of
258   // Maxval/Minval because the result is scalar in the case below.
259   if (!hasCharacterResult && (absentDim || rank == 1))
260     return func(builder, loc, array, mask);
261 
262   if (hasCharacterResult && (absentDim || rank == 1)) {
263     // Create mutable fir.box to be passed to the runtime for the result.
264     fir::MutableBoxValue resultMutableBox =
265         fir::factory::createTempMutableBox(builder, loc, resultType);
266     mlir::Value resultIrBox =
267         fir::factory::getMutableIRBox(builder, loc, resultMutableBox);
268 
269     funcChar(builder, loc, resultIrBox, array, mask);
270 
271     // Handle cleanup of allocatable result descriptor and return
272     fir::ExtendedValue res =
273         fir::factory::genMutableBoxRead(builder, loc, resultMutableBox);
274     return res.match(
275         [&](const fir::CharBoxValue &box) -> fir::ExtendedValue {
276           // Add cleanup code
277           assert(stmtCtx);
278           fir::FirOpBuilder *bldr = &builder;
279           mlir::Value temp = box.getAddr();
280           stmtCtx->attachCleanup(
281               [=]() { bldr->create<fir::FreeMemOp>(loc, temp); });
282           return box;
283         },
284         [&](const auto &) -> fir::ExtendedValue {
285           fir::emitFatalError(loc, errMsg);
286         });
287   }
288 
289   // Handle Min/Maxval cases that have an array result.
290   return genFuncDim(funcDim, resultType, builder, loc, stmtCtx, errMsg, array,
291                     args[1], mask, rank);
292 }
293 
294 /// Process calls to Minloc, Maxloc intrinsic functions
295 template <typename FN, typename FD>
296 static fir::ExtendedValue genExtremumloc(
297     FN func, FD funcDim, mlir::Type resultType, fir::FirOpBuilder &builder,
298     mlir::Location loc, Fortran::lower::StatementContext *stmtCtx,
299     llvm::StringRef errMsg, llvm::ArrayRef<fir::ExtendedValue> args) {
300 
301   assert(args.size() == 5);
302 
303   // Handle required array argument
304   mlir::Value array = builder.createBox(loc, args[0]);
305   unsigned rank = fir::BoxValue(array).rank();
306   assert(rank >= 1);
307 
308   // Handle optional mask argument
309   auto mask = isAbsent(args[2])
310                   ? builder.create<fir::AbsentOp>(
311                         loc, fir::BoxType::get(builder.getI1Type()))
312                   : builder.createBox(loc, args[2]);
313 
314   // Handle optional kind argument
315   auto kind = isAbsent(args[3]) ? builder.createIntegerConstant(
316                                       loc, builder.getIndexType(),
317                                       builder.getKindMap().defaultIntegerKind())
318                                 : fir::getBase(args[3]);
319 
320   // Handle optional back argument
321   auto back = isAbsent(args[4]) ? builder.createBool(loc, false)
322                                 : fir::getBase(args[4]);
323 
324   bool absentDim = isAbsent(args[1]);
325 
326   if (!absentDim && rank == 1) {
327     // If dim argument is present and the array is rank 1, then the result is
328     // a scalar (since the the result is rank-1 or 0).
329     // Therefore, we use a scalar result descriptor with Min/MaxlocDim().
330     mlir::Value dim = fir::getBase(args[1]);
331     // Create mutable fir.box to be passed to the runtime for the result.
332     fir::MutableBoxValue resultMutableBox =
333         fir::factory::createTempMutableBox(builder, loc, resultType);
334     mlir::Value resultIrBox =
335         fir::factory::getMutableIRBox(builder, loc, resultMutableBox);
336 
337     funcDim(builder, loc, resultIrBox, array, dim, mask, kind, back);
338 
339     // Handle cleanup of allocatable result descriptor and return
340     fir::ExtendedValue res =
341         fir::factory::genMutableBoxRead(builder, loc, resultMutableBox);
342     return res.match(
343         [&](const mlir::Value &tempAddr) -> fir::ExtendedValue {
344           // Add cleanup code
345           assert(stmtCtx);
346           fir::FirOpBuilder *bldr = &builder;
347           stmtCtx->attachCleanup(
348               [=]() { bldr->create<fir::FreeMemOp>(loc, tempAddr); });
349           return builder.create<fir::LoadOp>(loc, resultType, tempAddr);
350         },
351         [&](const auto &) -> fir::ExtendedValue {
352           fir::emitFatalError(loc, errMsg);
353         });
354   }
355 
356   // Note: The Min/Maxloc/val cases below have an array result.
357 
358   // Create mutable fir.box to be passed to the runtime for the result.
359   mlir::Type resultArrayType =
360       builder.getVarLenSeqTy(resultType, absentDim ? 1 : rank - 1);
361   fir::MutableBoxValue resultMutableBox =
362       fir::factory::createTempMutableBox(builder, loc, resultArrayType);
363   mlir::Value resultIrBox =
364       fir::factory::getMutableIRBox(builder, loc, resultMutableBox);
365 
366   if (absentDim) {
367     // Handle min/maxloc/val case where there is no dim argument
368     // (calls Min/Maxloc()/MinMaxval() runtime routine)
369     func(builder, loc, resultIrBox, array, mask, kind, back);
370   } else {
371     // else handle min/maxloc case with dim argument (calls
372     // Min/Max/loc/val/Dim() runtime routine).
373     mlir::Value dim = fir::getBase(args[1]);
374     funcDim(builder, loc, resultIrBox, array, dim, mask, kind, back);
375   }
376 
377   return fir::factory::genMutableBoxRead(builder, loc, resultMutableBox)
378       .match(
379           [&](const fir::ArrayBoxValue &box) -> fir::ExtendedValue {
380             // Add cleanup code
381             assert(stmtCtx);
382             fir::FirOpBuilder *bldr = &builder;
383             mlir::Value temp = box.getAddr();
384             stmtCtx->attachCleanup(
385                 [=]() { bldr->create<fir::FreeMemOp>(loc, temp); });
386             return box;
387           },
388           [&](const auto &) -> fir::ExtendedValue {
389             fir::emitFatalError(loc, errMsg);
390           });
391 }
392 
393 // TODO error handling -> return a code or directly emit messages ?
394 struct IntrinsicLibrary {
395 
396   // Constructors.
397   explicit IntrinsicLibrary(fir::FirOpBuilder &builder, mlir::Location loc,
398                             Fortran::lower::StatementContext *stmtCtx = nullptr)
399       : builder{builder}, loc{loc}, stmtCtx{stmtCtx} {}
400   IntrinsicLibrary() = delete;
401   IntrinsicLibrary(const IntrinsicLibrary &) = delete;
402 
403   /// Generate FIR for call to Fortran intrinsic \p name with arguments \p arg
404   /// and expected result type \p resultType.
405   fir::ExtendedValue genIntrinsicCall(llvm::StringRef name,
406                                       llvm::Optional<mlir::Type> resultType,
407                                       llvm::ArrayRef<fir::ExtendedValue> arg);
408 
409   /// Search a runtime function that is associated to the generic intrinsic name
410   /// and whose signature matches the intrinsic arguments and result types.
411   /// If no such runtime function is found but a runtime function associated
412   /// with the Fortran generic exists and has the same number of arguments,
413   /// conversions will be inserted before and/or after the call. This is to
414   /// mainly to allow 16 bits float support even-though little or no math
415   /// runtime is currently available for it.
416   mlir::Value genRuntimeCall(llvm::StringRef name, mlir::Type,
417                              llvm::ArrayRef<mlir::Value>);
418 
419   using RuntimeCallGenerator = std::function<mlir::Value(
420       fir::FirOpBuilder &, mlir::Location, llvm::ArrayRef<mlir::Value>)>;
421   RuntimeCallGenerator
422   getRuntimeCallGenerator(llvm::StringRef name,
423                           mlir::FunctionType soughtFuncType);
424 
425   /// Lowering for the ABS intrinsic. The ABS intrinsic expects one argument in
426   /// the llvm::ArrayRef. The ABS intrinsic is lowered into MLIR/FIR operation
427   /// if the argument is an integer, into llvm intrinsics if the argument is
428   /// real and to the `hypot` math routine if the argument is of complex type.
429   mlir::Value genAbs(mlir::Type, llvm::ArrayRef<mlir::Value>);
430   mlir::Value genAimag(mlir::Type, llvm::ArrayRef<mlir::Value>);
431   fir::ExtendedValue genAll(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
432   fir::ExtendedValue genAllocated(mlir::Type,
433                                   llvm::ArrayRef<fir::ExtendedValue>);
434   fir::ExtendedValue genAny(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
435   fir::ExtendedValue genAssociated(mlir::Type,
436                                    llvm::ArrayRef<fir::ExtendedValue>);
437   fir::ExtendedValue genChar(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
438   mlir::Value genDim(mlir::Type, llvm::ArrayRef<mlir::Value>);
439   fir::ExtendedValue genDotProduct(mlir::Type,
440                                    llvm::ArrayRef<fir::ExtendedValue>);
441   template <mlir::arith::CmpIPredicate pred>
442   fir::ExtendedValue genCharacterCompare(mlir::Type,
443                                          llvm::ArrayRef<fir::ExtendedValue>);
444   void genCpuTime(llvm::ArrayRef<fir::ExtendedValue>);
445   void genDateAndTime(llvm::ArrayRef<fir::ExtendedValue>);
446   template <Extremum, ExtremumBehavior>
447   mlir::Value genExtremum(mlir::Type, llvm::ArrayRef<mlir::Value>);
448   /// Lowering for the IAND intrinsic. The IAND intrinsic expects two arguments
449   /// in the llvm::ArrayRef.
450   mlir::Value genIand(mlir::Type, llvm::ArrayRef<mlir::Value>);
451   mlir::Value genIbits(mlir::Type, llvm::ArrayRef<mlir::Value>);
452   fir::ExtendedValue genLbound(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
453   fir::ExtendedValue genNull(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
454   fir::ExtendedValue genLen(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
455   fir::ExtendedValue genLenTrim(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
456   fir::ExtendedValue genMaxloc(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
457   fir::ExtendedValue genMaxval(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
458   fir::ExtendedValue genMinloc(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
459   fir::ExtendedValue genMinval(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
460   void genRandomInit(llvm::ArrayRef<fir::ExtendedValue>);
461   void genRandomNumber(llvm::ArrayRef<fir::ExtendedValue>);
462   void genRandomSeed(llvm::ArrayRef<fir::ExtendedValue>);
463   fir::ExtendedValue genSize(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
464   fir::ExtendedValue genSum(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
465   void genSystemClock(llvm::ArrayRef<fir::ExtendedValue>);
466   fir::ExtendedValue genUbound(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
467 
468   /// Define the different FIR generators that can be mapped to intrinsic to
469   /// generate the related code.
470   using ElementalGenerator = decltype(&IntrinsicLibrary::genAbs);
471   using ExtendedGenerator = decltype(&IntrinsicLibrary::genSum);
472   using SubroutineGenerator = decltype(&IntrinsicLibrary::genRandomInit);
473   using Generator =
474       std::variant<ElementalGenerator, ExtendedGenerator, SubroutineGenerator>;
475 
476   template <typename GeneratorType>
477   fir::ExtendedValue
478   outlineInExtendedWrapper(GeneratorType, llvm::StringRef name,
479                            llvm::Optional<mlir::Type> resultType,
480                            llvm::ArrayRef<fir::ExtendedValue> args);
481 
482   template <typename GeneratorType>
483   mlir::FuncOp getWrapper(GeneratorType, llvm::StringRef name,
484                           mlir::FunctionType, bool loadRefArguments = false);
485 
486   /// Generate calls to ElementalGenerator, handling the elemental aspects
487   template <typename GeneratorType>
488   fir::ExtendedValue
489   genElementalCall(GeneratorType, llvm::StringRef name, mlir::Type resultType,
490                    llvm::ArrayRef<fir::ExtendedValue> args, bool outline);
491 
492   /// Helper to invoke code generator for the intrinsics given arguments.
493   mlir::Value invokeGenerator(ElementalGenerator generator,
494                               mlir::Type resultType,
495                               llvm::ArrayRef<mlir::Value> args);
496   mlir::Value invokeGenerator(RuntimeCallGenerator generator,
497                               mlir::Type resultType,
498                               llvm::ArrayRef<mlir::Value> args);
499   mlir::Value invokeGenerator(ExtendedGenerator generator,
500                               mlir::Type resultType,
501                               llvm::ArrayRef<mlir::Value> args);
502   mlir::Value invokeGenerator(SubroutineGenerator generator,
503                               llvm::ArrayRef<mlir::Value> args);
504 
505   /// Add clean-up for \p temp to the current statement context;
506   void addCleanUpForTemp(mlir::Location loc, mlir::Value temp);
507   /// Helper function for generating code clean-up for result descriptors
508   fir::ExtendedValue readAndAddCleanUp(fir::MutableBoxValue resultMutableBox,
509                                        mlir::Type resultType,
510                                        llvm::StringRef errMsg);
511 
512   fir::FirOpBuilder &builder;
513   mlir::Location loc;
514   Fortran::lower::StatementContext *stmtCtx;
515 };
516 
517 struct IntrinsicDummyArgument {
518   const char *name = nullptr;
519   Fortran::lower::LowerIntrinsicArgAs lowerAs =
520       Fortran::lower::LowerIntrinsicArgAs::Value;
521   bool handleDynamicOptional = false;
522 };
523 
524 struct Fortran::lower::IntrinsicArgumentLoweringRules {
525   /// There is no more than 7 non repeated arguments in Fortran intrinsics.
526   IntrinsicDummyArgument args[7];
527   constexpr bool hasDefaultRules() const { return args[0].name == nullptr; }
528 };
529 
530 /// Structure describing what needs to be done to lower intrinsic "name".
531 struct IntrinsicHandler {
532   const char *name;
533   IntrinsicLibrary::Generator generator;
534   // The following may be omitted in the table below.
535   Fortran::lower::IntrinsicArgumentLoweringRules argLoweringRules = {};
536   bool isElemental = true;
537   /// Code heavy intrinsic can be outlined to make FIR
538   /// more readable.
539   bool outline = false;
540 };
541 
542 constexpr auto asValue = Fortran::lower::LowerIntrinsicArgAs::Value;
543 constexpr auto asAddr = Fortran::lower::LowerIntrinsicArgAs::Addr;
544 constexpr auto asBox = Fortran::lower::LowerIntrinsicArgAs::Box;
545 constexpr auto asInquired = Fortran::lower::LowerIntrinsicArgAs::Inquired;
546 using I = IntrinsicLibrary;
547 
548 /// Flag to indicate that an intrinsic argument has to be handled as
549 /// being dynamically optional (e.g. special handling when actual
550 /// argument is an optional variable in the current scope).
551 static constexpr bool handleDynamicOptional = true;
552 
553 /// Table that drives the fir generation depending on the intrinsic.
554 /// one to one mapping with Fortran arguments. If no mapping is
555 /// defined here for a generic intrinsic, genRuntimeCall will be called
556 /// to look for a match in the runtime a emit a call. Note that the argument
557 /// lowering rules for an intrinsic need to be provided only if at least one
558 /// argument must not be lowered by value. In which case, the lowering rules
559 /// should be provided for all the intrinsic arguments for completeness.
560 static constexpr IntrinsicHandler handlers[]{
561     {"abs", &I::genAbs},
562     {"aimag", &I::genAimag},
563     {"all",
564      &I::genAll,
565      {{{"mask", asAddr}, {"dim", asValue}}},
566      /*isElemental=*/false},
567     {"allocated",
568      &I::genAllocated,
569      {{{"array", asInquired}, {"scalar", asInquired}}},
570      /*isElemental=*/false},
571     {"any",
572      &I::genAny,
573      {{{"mask", asAddr}, {"dim", asValue}}},
574      /*isElemental=*/false},
575     {"associated",
576      &I::genAssociated,
577      {{{"pointer", asInquired}, {"target", asInquired}}},
578      /*isElemental=*/false},
579     {"char", &I::genChar},
580     {"cpu_time",
581      &I::genCpuTime,
582      {{{"time", asAddr}}},
583      /*isElemental=*/false},
584     {"date_and_time",
585      &I::genDateAndTime,
586      {{{"date", asAddr, handleDynamicOptional},
587        {"time", asAddr, handleDynamicOptional},
588        {"zone", asAddr, handleDynamicOptional},
589        {"values", asBox, handleDynamicOptional}}},
590      /*isElemental=*/false},
591     {"dim", &I::genDim},
592     {"dot_product",
593      &I::genDotProduct,
594      {{{"vector_a", asBox}, {"vector_b", asBox}}},
595      /*isElemental=*/false},
596     {"iand", &I::genIand},
597     {"ibits", &I::genIbits},
598     {"len",
599      &I::genLen,
600      {{{"string", asInquired}, {"kind", asValue}}},
601      /*isElemental=*/false},
602     {"len_trim", &I::genLenTrim},
603     {"lge", &I::genCharacterCompare<mlir::arith::CmpIPredicate::sge>},
604     {"lgt", &I::genCharacterCompare<mlir::arith::CmpIPredicate::sgt>},
605     {"lle", &I::genCharacterCompare<mlir::arith::CmpIPredicate::sle>},
606     {"llt", &I::genCharacterCompare<mlir::arith::CmpIPredicate::slt>},
607     {"max", &I::genExtremum<Extremum::Max, ExtremumBehavior::MinMaxss>},
608     {"maxloc",
609      &I::genMaxloc,
610      {{{"array", asBox},
611        {"dim", asValue},
612        {"mask", asBox, handleDynamicOptional},
613        {"kind", asValue},
614        {"back", asValue, handleDynamicOptional}}},
615      /*isElemental=*/false},
616     {"maxval",
617      &I::genMaxval,
618      {{{"array", asBox},
619        {"dim", asValue},
620        {"mask", asBox, handleDynamicOptional}}},
621      /*isElemental=*/false},
622     {"min", &I::genExtremum<Extremum::Min, ExtremumBehavior::MinMaxss>},
623     {"minloc",
624      &I::genMinloc,
625      {{{"array", asBox},
626        {"dim", asValue},
627        {"mask", asBox, handleDynamicOptional},
628        {"kind", asValue},
629        {"back", asValue, handleDynamicOptional}}},
630      /*isElemental=*/false},
631     {"minval",
632      &I::genMinval,
633      {{{"array", asBox},
634        {"dim", asValue},
635        {"mask", asBox, handleDynamicOptional}}},
636      /*isElemental=*/false},
637     {"null", &I::genNull, {{{"mold", asInquired}}}, /*isElemental=*/false},
638     {"random_init",
639      &I::genRandomInit,
640      {{{"repeatable", asValue}, {"image_distinct", asValue}}},
641      /*isElemental=*/false},
642     {"random_number",
643      &I::genRandomNumber,
644      {{{"harvest", asBox}}},
645      /*isElemental=*/false},
646     {"random_seed",
647      &I::genRandomSeed,
648      {{{"size", asBox}, {"put", asBox}, {"get", asBox}}},
649      /*isElemental=*/false},
650     {"sum",
651      &I::genSum,
652      {{{"array", asBox},
653        {"dim", asValue},
654        {"mask", asBox, handleDynamicOptional}}},
655      /*isElemental=*/false},
656     {"system_clock",
657      &I::genSystemClock,
658      {{{"count", asAddr}, {"count_rate", asAddr}, {"count_max", asAddr}}},
659      /*isElemental=*/false},
660     {"ubound",
661      &I::genUbound,
662      {{{"array", asBox}, {"dim", asValue}, {"kind", asValue}}},
663      /*isElemental=*/false},
664 };
665 
666 static const IntrinsicHandler *findIntrinsicHandler(llvm::StringRef name) {
667   auto compare = [](const IntrinsicHandler &handler, llvm::StringRef name) {
668     return name.compare(handler.name) > 0;
669   };
670   auto result =
671       std::lower_bound(std::begin(handlers), std::end(handlers), name, compare);
672   return result != std::end(handlers) && result->name == name ? result
673                                                               : nullptr;
674 }
675 
676 /// To make fir output more readable for debug, one can outline all intrinsic
677 /// implementation in wrappers (overrides the IntrinsicHandler::outline flag).
678 static llvm::cl::opt<bool> outlineAllIntrinsics(
679     "outline-intrinsics",
680     llvm::cl::desc(
681         "Lower all intrinsic procedure implementation in their own functions"),
682     llvm::cl::init(false));
683 
684 //===----------------------------------------------------------------------===//
685 // Math runtime description and matching utility
686 //===----------------------------------------------------------------------===//
687 
688 /// Command line option to modify math runtime version used to implement
689 /// intrinsics.
690 enum MathRuntimeVersion { fastVersion, llvmOnly };
691 llvm::cl::opt<MathRuntimeVersion> mathRuntimeVersion(
692     "math-runtime", llvm::cl::desc("Select math runtime version:"),
693     llvm::cl::values(
694         clEnumValN(fastVersion, "fast", "use pgmath fast runtime"),
695         clEnumValN(llvmOnly, "llvm",
696                    "only use LLVM intrinsics (may be incomplete)")),
697     llvm::cl::init(fastVersion));
698 
699 struct RuntimeFunction {
700   // llvm::StringRef comparison operator are not constexpr, so use string_view.
701   using Key = std::string_view;
702   // Needed for implicit compare with keys.
703   constexpr operator Key() const { return key; }
704   Key key; // intrinsic name
705   llvm::StringRef symbol;
706   fir::runtime::FuncTypeBuilderFunc typeGenerator;
707 };
708 
709 #define RUNTIME_STATIC_DESCRIPTION(name, func)                                 \
710   {#name, #func, fir::runtime::RuntimeTableKey<decltype(func)>::getTypeModel()},
711 static constexpr RuntimeFunction pgmathFast[] = {
712 #define PGMATH_FAST
713 #define PGMATH_USE_ALL_TYPES(name, func) RUNTIME_STATIC_DESCRIPTION(name, func)
714 #include "flang/Evaluate/pgmath.h.inc"
715 };
716 
717 static mlir::FunctionType genF32F32FuncType(mlir::MLIRContext *context) {
718   mlir::Type t = mlir::FloatType::getF32(context);
719   return mlir::FunctionType::get(context, {t}, {t});
720 }
721 
722 static mlir::FunctionType genF64F64FuncType(mlir::MLIRContext *context) {
723   mlir::Type t = mlir::FloatType::getF64(context);
724   return mlir::FunctionType::get(context, {t}, {t});
725 }
726 
727 static mlir::FunctionType genF32F32F32FuncType(mlir::MLIRContext *context) {
728   auto t = mlir::FloatType::getF32(context);
729   return mlir::FunctionType::get(context, {t, t}, {t});
730 }
731 
732 static mlir::FunctionType genF64F64F64FuncType(mlir::MLIRContext *context) {
733   auto t = mlir::FloatType::getF64(context);
734   return mlir::FunctionType::get(context, {t, t}, {t});
735 }
736 
737 // TODO : Fill-up this table with more intrinsic.
738 // Note: These are also defined as operations in LLVM dialect. See if this
739 // can be use and has advantages.
740 static constexpr RuntimeFunction llvmIntrinsics[] = {
741     {"abs", "llvm.fabs.f32", genF32F32FuncType},
742     {"abs", "llvm.fabs.f64", genF64F64FuncType},
743     {"pow", "llvm.pow.f32", genF32F32F32FuncType},
744     {"pow", "llvm.pow.f64", genF64F64F64FuncType},
745 };
746 
747 // This helper class computes a "distance" between two function types.
748 // The distance measures how many narrowing conversions of actual arguments
749 // and result of "from" must be made in order to use "to" instead of "from".
750 // For instance, the distance between ACOS(REAL(10)) and ACOS(REAL(8)) is
751 // greater than the one between ACOS(REAL(10)) and ACOS(REAL(16)). This means
752 // if no implementation of ACOS(REAL(10)) is available, it is better to use
753 // ACOS(REAL(16)) with casts rather than ACOS(REAL(8)).
754 // Note that this is not a symmetric distance and the order of "from" and "to"
755 // arguments matters, d(foo, bar) may not be the same as d(bar, foo) because it
756 // may be safe to replace foo by bar, but not the opposite.
757 class FunctionDistance {
758 public:
759   FunctionDistance() : infinite{true} {}
760 
761   FunctionDistance(mlir::FunctionType from, mlir::FunctionType to) {
762     unsigned nInputs = from.getNumInputs();
763     unsigned nResults = from.getNumResults();
764     if (nResults != to.getNumResults() || nInputs != to.getNumInputs()) {
765       infinite = true;
766     } else {
767       for (decltype(nInputs) i = 0; i < nInputs && !infinite; ++i)
768         addArgumentDistance(from.getInput(i), to.getInput(i));
769       for (decltype(nResults) i = 0; i < nResults && !infinite; ++i)
770         addResultDistance(to.getResult(i), from.getResult(i));
771     }
772   }
773 
774   /// Beware both d1.isSmallerThan(d2) *and* d2.isSmallerThan(d1) may be
775   /// false if both d1 and d2 are infinite. This implies that
776   ///  d1.isSmallerThan(d2) is not equivalent to !d2.isSmallerThan(d1)
777   bool isSmallerThan(const FunctionDistance &d) const {
778     return !infinite &&
779            (d.infinite || std::lexicographical_compare(
780                               conversions.begin(), conversions.end(),
781                               d.conversions.begin(), d.conversions.end()));
782   }
783 
784   bool isLosingPrecision() const {
785     return conversions[narrowingArg] != 0 || conversions[extendingResult] != 0;
786   }
787 
788   bool isInfinite() const { return infinite; }
789 
790 private:
791   enum class Conversion { Forbidden, None, Narrow, Extend };
792 
793   void addArgumentDistance(mlir::Type from, mlir::Type to) {
794     switch (conversionBetweenTypes(from, to)) {
795     case Conversion::Forbidden:
796       infinite = true;
797       break;
798     case Conversion::None:
799       break;
800     case Conversion::Narrow:
801       conversions[narrowingArg]++;
802       break;
803     case Conversion::Extend:
804       conversions[nonNarrowingArg]++;
805       break;
806     }
807   }
808 
809   void addResultDistance(mlir::Type from, mlir::Type to) {
810     switch (conversionBetweenTypes(from, to)) {
811     case Conversion::Forbidden:
812       infinite = true;
813       break;
814     case Conversion::None:
815       break;
816     case Conversion::Narrow:
817       conversions[nonExtendingResult]++;
818       break;
819     case Conversion::Extend:
820       conversions[extendingResult]++;
821       break;
822     }
823   }
824 
825   // Floating point can be mlir::FloatType or fir::real
826   static unsigned getFloatingPointWidth(mlir::Type t) {
827     if (auto f{t.dyn_cast<mlir::FloatType>()})
828       return f.getWidth();
829     // FIXME: Get width another way for fir.real/complex
830     // - use fir/KindMapping.h and llvm::Type
831     // - or use evaluate/type.h
832     if (auto r{t.dyn_cast<fir::RealType>()})
833       return r.getFKind() * 4;
834     if (auto cplx{t.dyn_cast<fir::ComplexType>()})
835       return cplx.getFKind() * 4;
836     llvm_unreachable("not a floating-point type");
837   }
838 
839   static Conversion conversionBetweenTypes(mlir::Type from, mlir::Type to) {
840     if (from == to)
841       return Conversion::None;
842 
843     if (auto fromIntTy{from.dyn_cast<mlir::IntegerType>()}) {
844       if (auto toIntTy{to.dyn_cast<mlir::IntegerType>()}) {
845         return fromIntTy.getWidth() > toIntTy.getWidth() ? Conversion::Narrow
846                                                          : Conversion::Extend;
847       }
848     }
849 
850     if (fir::isa_real(from) && fir::isa_real(to)) {
851       return getFloatingPointWidth(from) > getFloatingPointWidth(to)
852                  ? Conversion::Narrow
853                  : Conversion::Extend;
854     }
855 
856     if (auto fromCplxTy{from.dyn_cast<fir::ComplexType>()}) {
857       if (auto toCplxTy{to.dyn_cast<fir::ComplexType>()}) {
858         return getFloatingPointWidth(fromCplxTy) >
859                        getFloatingPointWidth(toCplxTy)
860                    ? Conversion::Narrow
861                    : Conversion::Extend;
862       }
863     }
864     // Notes:
865     // - No conversion between character types, specialization of runtime
866     // functions should be made instead.
867     // - It is not clear there is a use case for automatic conversions
868     // around Logical and it may damage hidden information in the physical
869     // storage so do not do it.
870     return Conversion::Forbidden;
871   }
872 
873   // Below are indexes to access data in conversions.
874   // The order in data does matter for lexicographical_compare
875   enum {
876     narrowingArg = 0,   // usually bad
877     extendingResult,    // usually bad
878     nonExtendingResult, // usually ok
879     nonNarrowingArg,    // usually ok
880     dataSize
881   };
882 
883   std::array<int, dataSize> conversions = {};
884   bool infinite = false; // When forbidden conversion or wrong argument number
885 };
886 
887 /// Build mlir::FuncOp from runtime symbol description and add
888 /// fir.runtime attribute.
889 static mlir::FuncOp getFuncOp(mlir::Location loc, fir::FirOpBuilder &builder,
890                               const RuntimeFunction &runtime) {
891   mlir::FuncOp function = builder.addNamedFunction(
892       loc, runtime.symbol, runtime.typeGenerator(builder.getContext()));
893   function->setAttr("fir.runtime", builder.getUnitAttr());
894   return function;
895 }
896 
897 /// Select runtime function that has the smallest distance to the intrinsic
898 /// function type and that will not imply narrowing arguments or extending the
899 /// result.
900 /// If nothing is found, the mlir::FuncOp will contain a nullptr.
901 mlir::FuncOp searchFunctionInLibrary(
902     mlir::Location loc, fir::FirOpBuilder &builder,
903     const Fortran::common::StaticMultimapView<RuntimeFunction> &lib,
904     llvm::StringRef name, mlir::FunctionType funcType,
905     const RuntimeFunction **bestNearMatch,
906     FunctionDistance &bestMatchDistance) {
907   std::pair<const RuntimeFunction *, const RuntimeFunction *> range =
908       lib.equal_range(name);
909   for (auto iter = range.first; iter != range.second && iter; ++iter) {
910     const RuntimeFunction &impl = *iter;
911     mlir::FunctionType implType = impl.typeGenerator(builder.getContext());
912     if (funcType == implType)
913       return getFuncOp(loc, builder, impl); // exact match
914 
915     FunctionDistance distance(funcType, implType);
916     if (distance.isSmallerThan(bestMatchDistance)) {
917       *bestNearMatch = &impl;
918       bestMatchDistance = std::move(distance);
919     }
920   }
921   return {};
922 }
923 
924 /// Search runtime for the best runtime function given an intrinsic name
925 /// and interface. The interface may not be a perfect match in which case
926 /// the caller is responsible to insert argument and return value conversions.
927 /// If nothing is found, the mlir::FuncOp will contain a nullptr.
928 static mlir::FuncOp getRuntimeFunction(mlir::Location loc,
929                                        fir::FirOpBuilder &builder,
930                                        llvm::StringRef name,
931                                        mlir::FunctionType funcType) {
932   const RuntimeFunction *bestNearMatch = nullptr;
933   FunctionDistance bestMatchDistance{};
934   mlir::FuncOp match;
935   using RtMap = Fortran::common::StaticMultimapView<RuntimeFunction>;
936   static constexpr RtMap pgmathF(pgmathFast);
937   static_assert(pgmathF.Verify() && "map must be sorted");
938   if (mathRuntimeVersion == fastVersion) {
939     match = searchFunctionInLibrary(loc, builder, pgmathF, name, funcType,
940                                     &bestNearMatch, bestMatchDistance);
941   } else {
942     assert(mathRuntimeVersion == llvmOnly && "unknown math runtime");
943   }
944   if (match)
945     return match;
946 
947   // Go through llvm intrinsics if not exact match in libpgmath or if
948   // mathRuntimeVersion == llvmOnly
949   static constexpr RtMap llvmIntr(llvmIntrinsics);
950   static_assert(llvmIntr.Verify() && "map must be sorted");
951   if (mlir::FuncOp exactMatch =
952           searchFunctionInLibrary(loc, builder, llvmIntr, name, funcType,
953                                   &bestNearMatch, bestMatchDistance))
954     return exactMatch;
955 
956   if (bestNearMatch != nullptr) {
957     if (bestMatchDistance.isLosingPrecision()) {
958       // Using this runtime version requires narrowing the arguments
959       // or extending the result. It is not numerically safe. There
960       // is currently no quad math library that was described in
961       // lowering and could be used here. Emit an error and continue
962       // generating the code with the narrowing cast so that the user
963       // can get a complete list of the problematic intrinsic calls.
964       std::string message("TODO: no math runtime available for '");
965       llvm::raw_string_ostream sstream(message);
966       if (name == "pow") {
967         assert(funcType.getNumInputs() == 2 &&
968                "power operator has two arguments");
969         sstream << funcType.getInput(0) << " ** " << funcType.getInput(1);
970       } else {
971         sstream << name << "(";
972         if (funcType.getNumInputs() > 0)
973           sstream << funcType.getInput(0);
974         for (mlir::Type argType : funcType.getInputs().drop_front())
975           sstream << ", " << argType;
976         sstream << ")";
977       }
978       sstream << "'";
979       mlir::emitError(loc, message);
980     }
981     return getFuncOp(loc, builder, *bestNearMatch);
982   }
983   return {};
984 }
985 
986 /// Helpers to get function type from arguments and result type.
987 static mlir::FunctionType getFunctionType(llvm::Optional<mlir::Type> resultType,
988                                           llvm::ArrayRef<mlir::Value> arguments,
989                                           fir::FirOpBuilder &builder) {
990   llvm::SmallVector<mlir::Type> argTypes;
991   for (mlir::Value arg : arguments)
992     argTypes.push_back(arg.getType());
993   llvm::SmallVector<mlir::Type> resTypes;
994   if (resultType)
995     resTypes.push_back(*resultType);
996   return mlir::FunctionType::get(builder.getModule().getContext(), argTypes,
997                                  resTypes);
998 }
999 
1000 /// fir::ExtendedValue to mlir::Value translation layer
1001 
1002 fir::ExtendedValue toExtendedValue(mlir::Value val, fir::FirOpBuilder &builder,
1003                                    mlir::Location loc) {
1004   assert(val && "optional unhandled here");
1005   mlir::Type type = val.getType();
1006   mlir::Value base = val;
1007   mlir::IndexType indexType = builder.getIndexType();
1008   llvm::SmallVector<mlir::Value> extents;
1009 
1010   fir::factory::CharacterExprHelper charHelper{builder, loc};
1011   // FIXME: we may want to allow non character scalar here.
1012   if (charHelper.isCharacterScalar(type))
1013     return charHelper.toExtendedValue(val);
1014 
1015   if (auto refType = type.dyn_cast<fir::ReferenceType>())
1016     type = refType.getEleTy();
1017 
1018   if (auto arrayType = type.dyn_cast<fir::SequenceType>()) {
1019     type = arrayType.getEleTy();
1020     for (fir::SequenceType::Extent extent : arrayType.getShape()) {
1021       if (extent == fir::SequenceType::getUnknownExtent())
1022         break;
1023       extents.emplace_back(
1024           builder.createIntegerConstant(loc, indexType, extent));
1025     }
1026     // Last extent might be missing in case of assumed-size. If more extents
1027     // could not be deduced from type, that's an error (a fir.box should
1028     // have been used in the interface).
1029     if (extents.size() + 1 < arrayType.getShape().size())
1030       mlir::emitError(loc, "cannot retrieve array extents from type");
1031   } else if (type.isa<fir::BoxType>() || type.isa<fir::RecordType>()) {
1032     fir::emitFatalError(loc, "not yet implemented: descriptor or derived type");
1033   }
1034 
1035   if (!extents.empty())
1036     return fir::ArrayBoxValue{base, extents};
1037   return base;
1038 }
1039 
1040 mlir::Value toValue(const fir::ExtendedValue &val, fir::FirOpBuilder &builder,
1041                     mlir::Location loc) {
1042   if (const fir::CharBoxValue *charBox = val.getCharBox()) {
1043     mlir::Value buffer = charBox->getBuffer();
1044     if (buffer.getType().isa<fir::BoxCharType>())
1045       return buffer;
1046     return fir::factory::CharacterExprHelper{builder, loc}.createEmboxChar(
1047         buffer, charBox->getLen());
1048   }
1049 
1050   // FIXME: need to access other ExtendedValue variants and handle them
1051   // properly.
1052   return fir::getBase(val);
1053 }
1054 
1055 //===----------------------------------------------------------------------===//
1056 // IntrinsicLibrary
1057 //===----------------------------------------------------------------------===//
1058 
1059 /// Emit a TODO error message for as yet unimplemented intrinsics.
1060 static void crashOnMissingIntrinsic(mlir::Location loc, llvm::StringRef name) {
1061   TODO(loc, "missing intrinsic lowering: " + llvm::Twine(name));
1062 }
1063 
1064 template <typename GeneratorType>
1065 fir::ExtendedValue IntrinsicLibrary::genElementalCall(
1066     GeneratorType generator, llvm::StringRef name, mlir::Type resultType,
1067     llvm::ArrayRef<fir::ExtendedValue> args, bool outline) {
1068   llvm::SmallVector<mlir::Value> scalarArgs;
1069   for (const fir::ExtendedValue &arg : args)
1070     if (arg.getUnboxed() || arg.getCharBox())
1071       scalarArgs.emplace_back(fir::getBase(arg));
1072     else
1073       fir::emitFatalError(loc, "nonscalar intrinsic argument");
1074   return invokeGenerator(generator, resultType, scalarArgs);
1075 }
1076 
1077 template <>
1078 fir::ExtendedValue
1079 IntrinsicLibrary::genElementalCall<IntrinsicLibrary::ExtendedGenerator>(
1080     ExtendedGenerator generator, llvm::StringRef name, mlir::Type resultType,
1081     llvm::ArrayRef<fir::ExtendedValue> args, bool outline) {
1082   for (const fir::ExtendedValue &arg : args)
1083     if (!arg.getUnboxed() && !arg.getCharBox())
1084       fir::emitFatalError(loc, "nonscalar intrinsic argument");
1085   if (outline)
1086     return outlineInExtendedWrapper(generator, name, resultType, args);
1087   return std::invoke(generator, *this, resultType, args);
1088 }
1089 
1090 template <>
1091 fir::ExtendedValue
1092 IntrinsicLibrary::genElementalCall<IntrinsicLibrary::SubroutineGenerator>(
1093     SubroutineGenerator generator, llvm::StringRef name, mlir::Type resultType,
1094     llvm::ArrayRef<fir::ExtendedValue> args, bool outline) {
1095   for (const fir::ExtendedValue &arg : args)
1096     if (!arg.getUnboxed() && !arg.getCharBox())
1097       // fir::emitFatalError(loc, "nonscalar intrinsic argument");
1098       crashOnMissingIntrinsic(loc, name);
1099   if (outline)
1100     return outlineInExtendedWrapper(generator, name, resultType, args);
1101   std::invoke(generator, *this, args);
1102   return mlir::Value();
1103 }
1104 
1105 static fir::ExtendedValue
1106 invokeHandler(IntrinsicLibrary::ElementalGenerator generator,
1107               const IntrinsicHandler &handler,
1108               llvm::Optional<mlir::Type> resultType,
1109               llvm::ArrayRef<fir::ExtendedValue> args, bool outline,
1110               IntrinsicLibrary &lib) {
1111   assert(resultType && "expect elemental intrinsic to be functions");
1112   return lib.genElementalCall(generator, handler.name, *resultType, args,
1113                               outline);
1114 }
1115 
1116 static fir::ExtendedValue
1117 invokeHandler(IntrinsicLibrary::ExtendedGenerator generator,
1118               const IntrinsicHandler &handler,
1119               llvm::Optional<mlir::Type> resultType,
1120               llvm::ArrayRef<fir::ExtendedValue> args, bool outline,
1121               IntrinsicLibrary &lib) {
1122   assert(resultType && "expect intrinsic function");
1123   if (handler.isElemental)
1124     return lib.genElementalCall(generator, handler.name, *resultType, args,
1125                                 outline);
1126   if (outline)
1127     return lib.outlineInExtendedWrapper(generator, handler.name, *resultType,
1128                                         args);
1129   return std::invoke(generator, lib, *resultType, args);
1130 }
1131 
1132 static fir::ExtendedValue
1133 invokeHandler(IntrinsicLibrary::SubroutineGenerator generator,
1134               const IntrinsicHandler &handler,
1135               llvm::Optional<mlir::Type> resultType,
1136               llvm::ArrayRef<fir::ExtendedValue> args, bool outline,
1137               IntrinsicLibrary &lib) {
1138   if (handler.isElemental)
1139     return lib.genElementalCall(generator, handler.name, mlir::Type{}, args,
1140                                 outline);
1141   if (outline)
1142     return lib.outlineInExtendedWrapper(generator, handler.name, resultType,
1143                                         args);
1144   std::invoke(generator, lib, args);
1145   return mlir::Value{};
1146 }
1147 
1148 fir::ExtendedValue
1149 IntrinsicLibrary::genIntrinsicCall(llvm::StringRef name,
1150                                    llvm::Optional<mlir::Type> resultType,
1151                                    llvm::ArrayRef<fir::ExtendedValue> args) {
1152   if (const IntrinsicHandler *handler = findIntrinsicHandler(name)) {
1153     bool outline = handler->outline || outlineAllIntrinsics;
1154     return std::visit(
1155         [&](auto &generator) -> fir::ExtendedValue {
1156           return invokeHandler(generator, *handler, resultType, args, outline,
1157                                *this);
1158         },
1159         handler->generator);
1160   }
1161 
1162   if (!resultType)
1163     // Subroutine should have a handler, they are likely missing for now.
1164     crashOnMissingIntrinsic(loc, name);
1165 
1166   // Try the runtime if no special handler was defined for the
1167   // intrinsic being called. Maths runtime only has numerical elemental.
1168   // No optional arguments are expected at this point, the code will
1169   // crash if it gets absent optional.
1170 
1171   // FIXME: using toValue to get the type won't work with array arguments.
1172   llvm::SmallVector<mlir::Value> mlirArgs;
1173   for (const fir::ExtendedValue &extendedVal : args) {
1174     mlir::Value val = toValue(extendedVal, builder, loc);
1175     if (!val)
1176       // If an absent optional gets there, most likely its handler has just
1177       // not yet been defined.
1178       crashOnMissingIntrinsic(loc, name);
1179     mlirArgs.emplace_back(val);
1180   }
1181   mlir::FunctionType soughtFuncType =
1182       getFunctionType(*resultType, mlirArgs, builder);
1183 
1184   IntrinsicLibrary::RuntimeCallGenerator runtimeCallGenerator =
1185       getRuntimeCallGenerator(name, soughtFuncType);
1186   return genElementalCall(runtimeCallGenerator, name, *resultType, args,
1187                           /* outline */ true);
1188 }
1189 
1190 mlir::Value
1191 IntrinsicLibrary::invokeGenerator(ElementalGenerator generator,
1192                                   mlir::Type resultType,
1193                                   llvm::ArrayRef<mlir::Value> args) {
1194   return std::invoke(generator, *this, resultType, args);
1195 }
1196 
1197 mlir::Value
1198 IntrinsicLibrary::invokeGenerator(RuntimeCallGenerator generator,
1199                                   mlir::Type resultType,
1200                                   llvm::ArrayRef<mlir::Value> args) {
1201   return generator(builder, loc, args);
1202 }
1203 
1204 mlir::Value
1205 IntrinsicLibrary::invokeGenerator(ExtendedGenerator generator,
1206                                   mlir::Type resultType,
1207                                   llvm::ArrayRef<mlir::Value> args) {
1208   llvm::SmallVector<fir::ExtendedValue> extendedArgs;
1209   for (mlir::Value arg : args)
1210     extendedArgs.emplace_back(toExtendedValue(arg, builder, loc));
1211   auto extendedResult = std::invoke(generator, *this, resultType, extendedArgs);
1212   return toValue(extendedResult, builder, loc);
1213 }
1214 
1215 mlir::Value
1216 IntrinsicLibrary::invokeGenerator(SubroutineGenerator generator,
1217                                   llvm::ArrayRef<mlir::Value> args) {
1218   llvm::SmallVector<fir::ExtendedValue> extendedArgs;
1219   for (mlir::Value arg : args)
1220     extendedArgs.emplace_back(toExtendedValue(arg, builder, loc));
1221   std::invoke(generator, *this, extendedArgs);
1222   return {};
1223 }
1224 
1225 template <typename GeneratorType>
1226 mlir::FuncOp IntrinsicLibrary::getWrapper(GeneratorType generator,
1227                                           llvm::StringRef name,
1228                                           mlir::FunctionType funcType,
1229                                           bool loadRefArguments) {
1230   std::string wrapperName = fir::mangleIntrinsicProcedure(name, funcType);
1231   mlir::FuncOp function = builder.getNamedFunction(wrapperName);
1232   if (!function) {
1233     // First time this wrapper is needed, build it.
1234     function = builder.createFunction(loc, wrapperName, funcType);
1235     function->setAttr("fir.intrinsic", builder.getUnitAttr());
1236     auto internalLinkage = mlir::LLVM::linkage::Linkage::Internal;
1237     auto linkage =
1238         mlir::LLVM::LinkageAttr::get(builder.getContext(), internalLinkage);
1239     function->setAttr("llvm.linkage", linkage);
1240     function.addEntryBlock();
1241 
1242     // Create local context to emit code into the newly created function
1243     // This new function is not linked to a source file location, only
1244     // its calls will be.
1245     auto localBuilder =
1246         std::make_unique<fir::FirOpBuilder>(function, builder.getKindMap());
1247     localBuilder->setInsertionPointToStart(&function.front());
1248     // Location of code inside wrapper of the wrapper is independent from
1249     // the location of the intrinsic call.
1250     mlir::Location localLoc = localBuilder->getUnknownLoc();
1251     llvm::SmallVector<mlir::Value> localArguments;
1252     for (mlir::BlockArgument bArg : function.front().getArguments()) {
1253       auto refType = bArg.getType().dyn_cast<fir::ReferenceType>();
1254       if (loadRefArguments && refType) {
1255         auto loaded = localBuilder->create<fir::LoadOp>(localLoc, bArg);
1256         localArguments.push_back(loaded);
1257       } else {
1258         localArguments.push_back(bArg);
1259       }
1260     }
1261 
1262     IntrinsicLibrary localLib{*localBuilder, localLoc};
1263 
1264     if constexpr (std::is_same_v<GeneratorType, SubroutineGenerator>) {
1265       localLib.invokeGenerator(generator, localArguments);
1266       localBuilder->create<mlir::func::ReturnOp>(localLoc);
1267     } else {
1268       assert(funcType.getNumResults() == 1 &&
1269              "expect one result for intrinsic function wrapper type");
1270       mlir::Type resultType = funcType.getResult(0);
1271       auto result =
1272           localLib.invokeGenerator(generator, resultType, localArguments);
1273       localBuilder->create<mlir::func::ReturnOp>(localLoc, result);
1274     }
1275   } else {
1276     // Wrapper was already built, ensure it has the sought type
1277     assert(function.getType() == funcType &&
1278            "conflict between intrinsic wrapper types");
1279   }
1280   return function;
1281 }
1282 
1283 /// Helpers to detect absent optional (not yet supported in outlining).
1284 bool static hasAbsentOptional(llvm::ArrayRef<fir::ExtendedValue> args) {
1285   for (const fir::ExtendedValue &arg : args)
1286     if (!fir::getBase(arg))
1287       return true;
1288   return false;
1289 }
1290 
1291 template <typename GeneratorType>
1292 fir::ExtendedValue IntrinsicLibrary::outlineInExtendedWrapper(
1293     GeneratorType generator, llvm::StringRef name,
1294     llvm::Optional<mlir::Type> resultType,
1295     llvm::ArrayRef<fir::ExtendedValue> args) {
1296   if (hasAbsentOptional(args))
1297     TODO(loc, "cannot outline call to intrinsic " + llvm::Twine(name) +
1298                   " with absent optional argument");
1299   llvm::SmallVector<mlir::Value> mlirArgs;
1300   for (const auto &extendedVal : args)
1301     mlirArgs.emplace_back(toValue(extendedVal, builder, loc));
1302   mlir::FunctionType funcType = getFunctionType(resultType, mlirArgs, builder);
1303   mlir::FuncOp wrapper = getWrapper(generator, name, funcType);
1304   auto call = builder.create<fir::CallOp>(loc, wrapper, mlirArgs);
1305   if (resultType)
1306     return toExtendedValue(call.getResult(0), builder, loc);
1307   // Subroutine calls
1308   return mlir::Value{};
1309 }
1310 
1311 IntrinsicLibrary::RuntimeCallGenerator
1312 IntrinsicLibrary::getRuntimeCallGenerator(llvm::StringRef name,
1313                                           mlir::FunctionType soughtFuncType) {
1314   mlir::FuncOp funcOp = getRuntimeFunction(loc, builder, name, soughtFuncType);
1315   if (!funcOp) {
1316     std::string buffer("not yet implemented: missing intrinsic lowering: ");
1317     llvm::raw_string_ostream sstream(buffer);
1318     sstream << name << "\nrequested type was: " << soughtFuncType << '\n';
1319     fir::emitFatalError(loc, buffer);
1320   }
1321 
1322   mlir::FunctionType actualFuncType = funcOp.getType();
1323   assert(actualFuncType.getNumResults() == soughtFuncType.getNumResults() &&
1324          actualFuncType.getNumInputs() == soughtFuncType.getNumInputs() &&
1325          actualFuncType.getNumResults() == 1 && "Bad intrinsic match");
1326 
1327   return [funcOp, actualFuncType,
1328           soughtFuncType](fir::FirOpBuilder &builder, mlir::Location loc,
1329                           llvm::ArrayRef<mlir::Value> args) {
1330     llvm::SmallVector<mlir::Value> convertedArguments;
1331     for (auto [fst, snd] : llvm::zip(actualFuncType.getInputs(), args))
1332       convertedArguments.push_back(builder.createConvert(loc, fst, snd));
1333     auto call = builder.create<fir::CallOp>(loc, funcOp, convertedArguments);
1334     mlir::Type soughtType = soughtFuncType.getResult(0);
1335     return builder.createConvert(loc, soughtType, call.getResult(0));
1336   };
1337 }
1338 
1339 void IntrinsicLibrary::addCleanUpForTemp(mlir::Location loc, mlir::Value temp) {
1340   assert(stmtCtx);
1341   fir::FirOpBuilder *bldr = &builder;
1342   stmtCtx->attachCleanup([=]() { bldr->create<fir::FreeMemOp>(loc, temp); });
1343 }
1344 
1345 fir::ExtendedValue
1346 IntrinsicLibrary::readAndAddCleanUp(fir::MutableBoxValue resultMutableBox,
1347                                     mlir::Type resultType,
1348                                     llvm::StringRef intrinsicName) {
1349   fir::ExtendedValue res =
1350       fir::factory::genMutableBoxRead(builder, loc, resultMutableBox);
1351   return res.match(
1352       [&](const fir::ArrayBoxValue &box) -> fir::ExtendedValue {
1353         // Add cleanup code
1354         addCleanUpForTemp(loc, box.getAddr());
1355         return box;
1356       },
1357       [&](const fir::BoxValue &box) -> fir::ExtendedValue {
1358         // Add cleanup code
1359         auto addr =
1360             builder.create<fir::BoxAddrOp>(loc, box.getMemTy(), box.getAddr());
1361         addCleanUpForTemp(loc, addr);
1362         return box;
1363       },
1364       [&](const fir::CharArrayBoxValue &box) -> fir::ExtendedValue {
1365         // Add cleanup code
1366         addCleanUpForTemp(loc, box.getAddr());
1367         return box;
1368       },
1369       [&](const mlir::Value &tempAddr) -> fir::ExtendedValue {
1370         // Add cleanup code
1371         addCleanUpForTemp(loc, tempAddr);
1372         return builder.create<fir::LoadOp>(loc, resultType, tempAddr);
1373       },
1374       [&](const fir::CharBoxValue &box) -> fir::ExtendedValue {
1375         // Add cleanup code
1376         addCleanUpForTemp(loc, box.getAddr());
1377         return box;
1378       },
1379       [&](const auto &) -> fir::ExtendedValue {
1380         fir::emitFatalError(loc, "unexpected result for " + intrinsicName);
1381       });
1382 }
1383 
1384 //===----------------------------------------------------------------------===//
1385 // Code generators for the intrinsic
1386 //===----------------------------------------------------------------------===//
1387 
1388 mlir::Value IntrinsicLibrary::genRuntimeCall(llvm::StringRef name,
1389                                              mlir::Type resultType,
1390                                              llvm::ArrayRef<mlir::Value> args) {
1391   mlir::FunctionType soughtFuncType =
1392       getFunctionType(resultType, args, builder);
1393   return getRuntimeCallGenerator(name, soughtFuncType)(builder, loc, args);
1394 }
1395 
1396 // ABS
1397 mlir::Value IntrinsicLibrary::genAbs(mlir::Type resultType,
1398                                      llvm::ArrayRef<mlir::Value> args) {
1399   assert(args.size() == 1);
1400   mlir::Value arg = args[0];
1401   mlir::Type type = arg.getType();
1402   if (fir::isa_real(type)) {
1403     // Runtime call to fp abs. An alternative would be to use mlir
1404     // math::AbsFOp but it does not support all fir floating point types.
1405     return genRuntimeCall("abs", resultType, args);
1406   }
1407   if (auto intType = type.dyn_cast<mlir::IntegerType>()) {
1408     // At the time of this implementation there is no abs op in mlir.
1409     // So, implement abs here without branching.
1410     mlir::Value shift =
1411         builder.createIntegerConstant(loc, intType, intType.getWidth() - 1);
1412     auto mask = builder.create<mlir::arith::ShRSIOp>(loc, arg, shift);
1413     auto xored = builder.create<mlir::arith::XOrIOp>(loc, arg, mask);
1414     return builder.create<mlir::arith::SubIOp>(loc, xored, mask);
1415   }
1416   if (fir::isa_complex(type)) {
1417     // Use HYPOT to fulfill the no underflow/overflow requirement.
1418     auto parts = fir::factory::Complex{builder, loc}.extractParts(arg);
1419     llvm::SmallVector<mlir::Value> args = {parts.first, parts.second};
1420     return genRuntimeCall("hypot", resultType, args);
1421   }
1422   llvm_unreachable("unexpected type in ABS argument");
1423 }
1424 
1425 // AIMAG
1426 mlir::Value IntrinsicLibrary::genAimag(mlir::Type resultType,
1427                                        llvm::ArrayRef<mlir::Value> args) {
1428   assert(args.size() == 1);
1429   return fir::factory::Complex{builder, loc}.extractComplexPart(
1430       args[0], true /* isImagPart */);
1431 }
1432 
1433 // ALL
1434 fir::ExtendedValue
1435 IntrinsicLibrary::genAll(mlir::Type resultType,
1436                          llvm::ArrayRef<fir::ExtendedValue> args) {
1437 
1438   assert(args.size() == 2);
1439   // Handle required mask argument
1440   mlir::Value mask = builder.createBox(loc, args[0]);
1441 
1442   fir::BoxValue maskArry = builder.createBox(loc, args[0]);
1443   int rank = maskArry.rank();
1444   assert(rank >= 1);
1445 
1446   // Handle optional dim argument
1447   bool absentDim = isAbsent(args[1]);
1448   mlir::Value dim =
1449       absentDim ? builder.createIntegerConstant(loc, builder.getIndexType(), 1)
1450                 : fir::getBase(args[1]);
1451 
1452   if (rank == 1 || absentDim)
1453     return builder.createConvert(loc, resultType,
1454                                  fir::runtime::genAll(builder, loc, mask, dim));
1455 
1456   // else use the result descriptor AllDim() intrinsic
1457 
1458   // Create mutable fir.box to be passed to the runtime for the result.
1459 
1460   mlir::Type resultArrayType = builder.getVarLenSeqTy(resultType, rank - 1);
1461   fir::MutableBoxValue resultMutableBox =
1462       fir::factory::createTempMutableBox(builder, loc, resultArrayType);
1463   mlir::Value resultIrBox =
1464       fir::factory::getMutableIRBox(builder, loc, resultMutableBox);
1465 
1466   // Call runtime. The runtime is allocating the result.
1467   fir::runtime::genAllDescriptor(builder, loc, resultIrBox, mask, dim);
1468   return fir::factory::genMutableBoxRead(builder, loc, resultMutableBox)
1469       .match(
1470           [&](const fir::ArrayBoxValue &box) -> fir::ExtendedValue {
1471             addCleanUpForTemp(loc, box.getAddr());
1472             return box;
1473           },
1474           [&](const auto &) -> fir::ExtendedValue {
1475             fir::emitFatalError(loc, "Invalid result for ALL");
1476           });
1477 }
1478 
1479 // ALLOCATED
1480 fir::ExtendedValue
1481 IntrinsicLibrary::genAllocated(mlir::Type resultType,
1482                                llvm::ArrayRef<fir::ExtendedValue> args) {
1483   assert(args.size() == 1);
1484   return args[0].match(
1485       [&](const fir::MutableBoxValue &x) -> fir::ExtendedValue {
1486         return fir::factory::genIsAllocatedOrAssociatedTest(builder, loc, x);
1487       },
1488       [&](const auto &) -> fir::ExtendedValue {
1489         fir::emitFatalError(loc,
1490                             "allocated arg not lowered to MutableBoxValue");
1491       });
1492 }
1493 
1494 // ANY
1495 fir::ExtendedValue
1496 IntrinsicLibrary::genAny(mlir::Type resultType,
1497                          llvm::ArrayRef<fir::ExtendedValue> args) {
1498 
1499   assert(args.size() == 2);
1500   // Handle required mask argument
1501   mlir::Value mask = builder.createBox(loc, args[0]);
1502 
1503   fir::BoxValue maskArry = builder.createBox(loc, args[0]);
1504   int rank = maskArry.rank();
1505   assert(rank >= 1);
1506 
1507   // Handle optional dim argument
1508   bool absentDim = isAbsent(args[1]);
1509   mlir::Value dim =
1510       absentDim ? builder.createIntegerConstant(loc, builder.getIndexType(), 1)
1511                 : fir::getBase(args[1]);
1512 
1513   if (rank == 1 || absentDim)
1514     return builder.createConvert(loc, resultType,
1515                                  fir::runtime::genAny(builder, loc, mask, dim));
1516 
1517   // else use the result descriptor AnyDim() intrinsic
1518 
1519   // Create mutable fir.box to be passed to the runtime for the result.
1520 
1521   mlir::Type resultArrayType = builder.getVarLenSeqTy(resultType, rank - 1);
1522   fir::MutableBoxValue resultMutableBox =
1523       fir::factory::createTempMutableBox(builder, loc, resultArrayType);
1524   mlir::Value resultIrBox =
1525       fir::factory::getMutableIRBox(builder, loc, resultMutableBox);
1526 
1527   // Call runtime. The runtime is allocating the result.
1528   fir::runtime::genAnyDescriptor(builder, loc, resultIrBox, mask, dim);
1529   return fir::factory::genMutableBoxRead(builder, loc, resultMutableBox)
1530       .match(
1531           [&](const fir::ArrayBoxValue &box) -> fir::ExtendedValue {
1532             addCleanUpForTemp(loc, box.getAddr());
1533             return box;
1534           },
1535           [&](const auto &) -> fir::ExtendedValue {
1536             fir::emitFatalError(loc, "Invalid result for ANY");
1537           });
1538 }
1539 
1540 // ASSOCIATED
1541 fir::ExtendedValue
1542 IntrinsicLibrary::genAssociated(mlir::Type resultType,
1543                                 llvm::ArrayRef<fir::ExtendedValue> args) {
1544   assert(args.size() == 2);
1545   auto *pointer =
1546       args[0].match([&](const fir::MutableBoxValue &x) { return &x; },
1547                     [&](const auto &) -> const fir::MutableBoxValue * {
1548                       fir::emitFatalError(loc, "pointer not a MutableBoxValue");
1549                     });
1550   const fir::ExtendedValue &target = args[1];
1551   if (isAbsent(target))
1552     return fir::factory::genIsAllocatedOrAssociatedTest(builder, loc, *pointer);
1553 
1554   mlir::Value targetBox = builder.createBox(loc, target);
1555   if (fir::valueHasFirAttribute(fir::getBase(target),
1556                                 fir::getOptionalAttrName())) {
1557     // Subtle: contrary to other intrinsic optional arguments, disassociated
1558     // POINTER and unallocated ALLOCATABLE actual argument are not considered
1559     // absent here. This is because ASSOCIATED has special requirements for
1560     // TARGET actual arguments that are POINTERs. There is no precise
1561     // requirements for ALLOCATABLEs, but all existing Fortran compilers treat
1562     // them similarly to POINTERs. That is: unallocated TARGETs cause ASSOCIATED
1563     // to rerun false.  The runtime deals with the disassociated/unallocated
1564     // case. Simply ensures that TARGET that are OPTIONAL get conditionally
1565     // emboxed here to convey the optional aspect to the runtime.
1566     auto isPresent = builder.create<fir::IsPresentOp>(loc, builder.getI1Type(),
1567                                                       fir::getBase(target));
1568     auto absentBox = builder.create<fir::AbsentOp>(loc, targetBox.getType());
1569     targetBox = builder.create<mlir::arith::SelectOp>(loc, isPresent, targetBox,
1570                                                       absentBox);
1571   }
1572   mlir::Value pointerBoxRef =
1573       fir::factory::getMutableIRBox(builder, loc, *pointer);
1574   auto pointerBox = builder.create<fir::LoadOp>(loc, pointerBoxRef);
1575   return Fortran::lower::genAssociated(builder, loc, pointerBox, targetBox);
1576 }
1577 
1578 // CHAR
1579 fir::ExtendedValue
1580 IntrinsicLibrary::genChar(mlir::Type type,
1581                           llvm::ArrayRef<fir::ExtendedValue> args) {
1582   // Optional KIND argument.
1583   assert(args.size() >= 1);
1584   const mlir::Value *arg = args[0].getUnboxed();
1585   // expect argument to be a scalar integer
1586   if (!arg)
1587     mlir::emitError(loc, "CHAR intrinsic argument not unboxed");
1588   fir::factory::CharacterExprHelper helper{builder, loc};
1589   fir::CharacterType::KindTy kind = helper.getCharacterType(type).getFKind();
1590   mlir::Value cast = helper.createSingletonFromCode(*arg, kind);
1591   mlir::Value len =
1592       builder.createIntegerConstant(loc, builder.getCharacterLengthType(), 1);
1593   return fir::CharBoxValue{cast, len};
1594 }
1595 
1596 // DIM
1597 mlir::Value IntrinsicLibrary::genDim(mlir::Type resultType,
1598                                      llvm::ArrayRef<mlir::Value> args) {
1599   assert(args.size() == 2);
1600   if (resultType.isa<mlir::IntegerType>()) {
1601     mlir::Value zero = builder.createIntegerConstant(loc, resultType, 0);
1602     auto diff = builder.create<mlir::arith::SubIOp>(loc, args[0], args[1]);
1603     auto cmp = builder.create<mlir::arith::CmpIOp>(
1604         loc, mlir::arith::CmpIPredicate::sgt, diff, zero);
1605     return builder.create<mlir::arith::SelectOp>(loc, cmp, diff, zero);
1606   }
1607   assert(fir::isa_real(resultType) && "Only expects real and integer in DIM");
1608   mlir::Value zero = builder.createRealZeroConstant(loc, resultType);
1609   auto diff = builder.create<mlir::arith::SubFOp>(loc, args[0], args[1]);
1610   auto cmp = builder.create<mlir::arith::CmpFOp>(
1611       loc, mlir::arith::CmpFPredicate::OGT, diff, zero);
1612   return builder.create<mlir::arith::SelectOp>(loc, cmp, diff, zero);
1613 }
1614 
1615 // DOT_PRODUCT
1616 fir::ExtendedValue
1617 IntrinsicLibrary::genDotProduct(mlir::Type resultType,
1618                                 llvm::ArrayRef<fir::ExtendedValue> args) {
1619   return genDotProd(fir::runtime::genDotProduct, resultType, builder, loc,
1620                     stmtCtx, args);
1621 }
1622 
1623 // CPU_TIME
1624 void IntrinsicLibrary::genCpuTime(llvm::ArrayRef<fir::ExtendedValue> args) {
1625   assert(args.size() == 1);
1626   const mlir::Value *arg = args[0].getUnboxed();
1627   assert(arg && "nonscalar cpu_time argument");
1628   mlir::Value res1 = Fortran::lower::genCpuTime(builder, loc);
1629   mlir::Value res2 =
1630       builder.createConvert(loc, fir::dyn_cast_ptrEleTy(arg->getType()), res1);
1631   builder.create<fir::StoreOp>(loc, res2, *arg);
1632 }
1633 
1634 // DATE_AND_TIME
1635 void IntrinsicLibrary::genDateAndTime(llvm::ArrayRef<fir::ExtendedValue> args) {
1636   assert(args.size() == 4 && "date_and_time has 4 args");
1637   llvm::SmallVector<llvm::Optional<fir::CharBoxValue>> charArgs(3);
1638   for (unsigned i = 0; i < 3; ++i)
1639     if (const fir::CharBoxValue *charBox = args[i].getCharBox())
1640       charArgs[i] = *charBox;
1641 
1642   mlir::Value values = fir::getBase(args[3]);
1643   if (!values)
1644     values = builder.create<fir::AbsentOp>(
1645         loc, fir::BoxType::get(builder.getNoneType()));
1646 
1647   Fortran::lower::genDateAndTime(builder, loc, charArgs[0], charArgs[1],
1648                                  charArgs[2], values);
1649 }
1650 
1651 // IAND
1652 mlir::Value IntrinsicLibrary::genIand(mlir::Type resultType,
1653                                       llvm::ArrayRef<mlir::Value> args) {
1654   assert(args.size() == 2);
1655   return builder.create<mlir::arith::AndIOp>(loc, args[0], args[1]);
1656 }
1657 
1658 // IBITS
1659 mlir::Value IntrinsicLibrary::genIbits(mlir::Type resultType,
1660                                        llvm::ArrayRef<mlir::Value> args) {
1661   // A conformant IBITS(I,POS,LEN) call satisfies:
1662   //     POS >= 0
1663   //     LEN >= 0
1664   //     POS + LEN <= BIT_SIZE(I)
1665   // Return:  LEN == 0 ? 0 : (I >> POS) & (-1 >> (BIT_SIZE(I) - LEN))
1666   // For a conformant call, implementing (I >> POS) with a signed or an
1667   // unsigned shift produces the same result.  For a nonconformant call,
1668   // the two choices may produce different results.
1669   assert(args.size() == 3);
1670   mlir::Value pos = builder.createConvert(loc, resultType, args[1]);
1671   mlir::Value len = builder.createConvert(loc, resultType, args[2]);
1672   mlir::Value bitSize = builder.createIntegerConstant(
1673       loc, resultType, resultType.cast<mlir::IntegerType>().getWidth());
1674   auto shiftCount = builder.create<mlir::arith::SubIOp>(loc, bitSize, len);
1675   mlir::Value zero = builder.createIntegerConstant(loc, resultType, 0);
1676   mlir::Value ones = builder.createIntegerConstant(loc, resultType, -1);
1677   auto mask = builder.create<mlir::arith::ShRUIOp>(loc, ones, shiftCount);
1678   auto res1 = builder.create<mlir::arith::ShRSIOp>(loc, args[0], pos);
1679   auto res2 = builder.create<mlir::arith::AndIOp>(loc, res1, mask);
1680   auto lenIsZero = builder.create<mlir::arith::CmpIOp>(
1681       loc, mlir::arith::CmpIPredicate::eq, len, zero);
1682   return builder.create<mlir::arith::SelectOp>(loc, lenIsZero, zero, res2);
1683 }
1684 
1685 // LEN
1686 // Note that this is only used for an unrestricted intrinsic LEN call.
1687 // Other uses of LEN are rewritten as descriptor inquiries by the front-end.
1688 fir::ExtendedValue
1689 IntrinsicLibrary::genLen(mlir::Type resultType,
1690                          llvm::ArrayRef<fir::ExtendedValue> args) {
1691   // Optional KIND argument reflected in result type and otherwise ignored.
1692   assert(args.size() == 1 || args.size() == 2);
1693   mlir::Value len = fir::factory::readCharLen(builder, loc, args[0]);
1694   return builder.createConvert(loc, resultType, len);
1695 }
1696 
1697 // LEN_TRIM
1698 fir::ExtendedValue
1699 IntrinsicLibrary::genLenTrim(mlir::Type resultType,
1700                              llvm::ArrayRef<fir::ExtendedValue> args) {
1701   // Optional KIND argument reflected in result type and otherwise ignored.
1702   assert(args.size() == 1 || args.size() == 2);
1703   const fir::CharBoxValue *charBox = args[0].getCharBox();
1704   if (!charBox)
1705     TODO(loc, "character array len_trim");
1706   auto len =
1707       fir::factory::CharacterExprHelper(builder, loc).createLenTrim(*charBox);
1708   return builder.createConvert(loc, resultType, len);
1709 }
1710 
1711 // LGE, LGT, LLE, LLT
1712 template <mlir::arith::CmpIPredicate pred>
1713 fir::ExtendedValue
1714 IntrinsicLibrary::genCharacterCompare(mlir::Type type,
1715                                       llvm::ArrayRef<fir::ExtendedValue> args) {
1716   assert(args.size() == 2);
1717   return fir::runtime::genCharCompare(
1718       builder, loc, pred, fir::getBase(args[0]), fir::getLen(args[0]),
1719       fir::getBase(args[1]), fir::getLen(args[1]));
1720 }
1721 
1722 // Compare two FIR values and return boolean result as i1.
1723 template <Extremum extremum, ExtremumBehavior behavior>
1724 static mlir::Value createExtremumCompare(mlir::Location loc,
1725                                          fir::FirOpBuilder &builder,
1726                                          mlir::Value left, mlir::Value right) {
1727   static constexpr mlir::arith::CmpIPredicate integerPredicate =
1728       extremum == Extremum::Max ? mlir::arith::CmpIPredicate::sgt
1729                                 : mlir::arith::CmpIPredicate::slt;
1730   static constexpr mlir::arith::CmpFPredicate orderedCmp =
1731       extremum == Extremum::Max ? mlir::arith::CmpFPredicate::OGT
1732                                 : mlir::arith::CmpFPredicate::OLT;
1733   mlir::Type type = left.getType();
1734   mlir::Value result;
1735   if (fir::isa_real(type)) {
1736     // Note: the signaling/quit aspect of the result required by IEEE
1737     // cannot currently be obtained with LLVM without ad-hoc runtime.
1738     if constexpr (behavior == ExtremumBehavior::IeeeMinMaximumNumber) {
1739       // Return the number if one of the inputs is NaN and the other is
1740       // a number.
1741       auto leftIsResult =
1742           builder.create<mlir::arith::CmpFOp>(loc, orderedCmp, left, right);
1743       auto rightIsNan = builder.create<mlir::arith::CmpFOp>(
1744           loc, mlir::arith::CmpFPredicate::UNE, right, right);
1745       result =
1746           builder.create<mlir::arith::OrIOp>(loc, leftIsResult, rightIsNan);
1747     } else if constexpr (behavior == ExtremumBehavior::IeeeMinMaximum) {
1748       // Always return NaNs if one the input is NaNs
1749       auto leftIsResult =
1750           builder.create<mlir::arith::CmpFOp>(loc, orderedCmp, left, right);
1751       auto leftIsNan = builder.create<mlir::arith::CmpFOp>(
1752           loc, mlir::arith::CmpFPredicate::UNE, left, left);
1753       result = builder.create<mlir::arith::OrIOp>(loc, leftIsResult, leftIsNan);
1754     } else if constexpr (behavior == ExtremumBehavior::MinMaxss) {
1755       // If the left is a NaN, return the right whatever it is.
1756       result =
1757           builder.create<mlir::arith::CmpFOp>(loc, orderedCmp, left, right);
1758     } else if constexpr (behavior == ExtremumBehavior::PgfortranLlvm) {
1759       // If one of the operand is a NaN, return left whatever it is.
1760       static constexpr auto unorderedCmp =
1761           extremum == Extremum::Max ? mlir::arith::CmpFPredicate::UGT
1762                                     : mlir::arith::CmpFPredicate::ULT;
1763       result =
1764           builder.create<mlir::arith::CmpFOp>(loc, unorderedCmp, left, right);
1765     } else {
1766       // TODO: ieeeMinNum/ieeeMaxNum
1767       static_assert(behavior == ExtremumBehavior::IeeeMinMaxNum,
1768                     "ieeeMinNum/ieeeMaxNum behavior not implemented");
1769     }
1770   } else if (fir::isa_integer(type)) {
1771     result =
1772         builder.create<mlir::arith::CmpIOp>(loc, integerPredicate, left, right);
1773   } else if (fir::isa_char(type)) {
1774     // TODO: ! character min and max is tricky because the result
1775     // length is the length of the longest argument!
1776     // So we may need a temp.
1777     TODO(loc, "CHARACTER min and max");
1778   }
1779   assert(result && "result must be defined");
1780   return result;
1781 }
1782 
1783 // MAXLOC
1784 fir::ExtendedValue
1785 IntrinsicLibrary::genMaxloc(mlir::Type resultType,
1786                             llvm::ArrayRef<fir::ExtendedValue> args) {
1787   return genExtremumloc(fir::runtime::genMaxloc, fir::runtime::genMaxlocDim,
1788                         resultType, builder, loc, stmtCtx,
1789                         "unexpected result for Maxloc", args);
1790 }
1791 
1792 // MAXVAL
1793 fir::ExtendedValue
1794 IntrinsicLibrary::genMaxval(mlir::Type resultType,
1795                             llvm::ArrayRef<fir::ExtendedValue> args) {
1796   return genExtremumVal(fir::runtime::genMaxval, fir::runtime::genMaxvalDim,
1797                         fir::runtime::genMaxvalChar, resultType, builder, loc,
1798                         stmtCtx, "unexpected result for Maxval", args);
1799 }
1800 
1801 // MINLOC
1802 fir::ExtendedValue
1803 IntrinsicLibrary::genMinloc(mlir::Type resultType,
1804                             llvm::ArrayRef<fir::ExtendedValue> args) {
1805   return genExtremumloc(fir::runtime::genMinloc, fir::runtime::genMinlocDim,
1806                         resultType, builder, loc, stmtCtx,
1807                         "unexpected result for Minloc", args);
1808 }
1809 
1810 // MINVAL
1811 fir::ExtendedValue
1812 IntrinsicLibrary::genMinval(mlir::Type resultType,
1813                             llvm::ArrayRef<fir::ExtendedValue> args) {
1814   return genExtremumVal(fir::runtime::genMinval, fir::runtime::genMinvalDim,
1815                         fir::runtime::genMinvalChar, resultType, builder, loc,
1816                         stmtCtx, "unexpected result for Minval", args);
1817 }
1818 
1819 // MIN and MAX
1820 template <Extremum extremum, ExtremumBehavior behavior>
1821 mlir::Value IntrinsicLibrary::genExtremum(mlir::Type,
1822                                           llvm::ArrayRef<mlir::Value> args) {
1823   assert(args.size() >= 1);
1824   mlir::Value result = args[0];
1825   for (auto arg : args.drop_front()) {
1826     mlir::Value mask =
1827         createExtremumCompare<extremum, behavior>(loc, builder, result, arg);
1828     result = builder.create<mlir::arith::SelectOp>(loc, mask, result, arg);
1829   }
1830   return result;
1831 }
1832 
1833 // NULL
1834 fir::ExtendedValue
1835 IntrinsicLibrary::genNull(mlir::Type, llvm::ArrayRef<fir::ExtendedValue> args) {
1836   // NULL() without MOLD must be handled in the contexts where it can appear
1837   // (see table 16.5 of Fortran 2018 standard).
1838   assert(args.size() == 1 && isPresent(args[0]) &&
1839          "MOLD argument required to lower NULL outside of any context");
1840   const auto *mold = args[0].getBoxOf<fir::MutableBoxValue>();
1841   assert(mold && "MOLD must be a pointer or allocatable");
1842   fir::BoxType boxType = mold->getBoxTy();
1843   mlir::Value boxStorage = builder.createTemporary(loc, boxType);
1844   mlir::Value box = fir::factory::createUnallocatedBox(
1845       builder, loc, boxType, mold->nonDeferredLenParams());
1846   builder.create<fir::StoreOp>(loc, box, boxStorage);
1847   return fir::MutableBoxValue(boxStorage, mold->nonDeferredLenParams(), {});
1848 }
1849 
1850 // RANDOM_INIT
1851 void IntrinsicLibrary::genRandomInit(llvm::ArrayRef<fir::ExtendedValue> args) {
1852   assert(args.size() == 2);
1853   Fortran::lower::genRandomInit(builder, loc, fir::getBase(args[0]),
1854                                 fir::getBase(args[1]));
1855 }
1856 
1857 // RANDOM_NUMBER
1858 void IntrinsicLibrary::genRandomNumber(
1859     llvm::ArrayRef<fir::ExtendedValue> args) {
1860   assert(args.size() == 1);
1861   Fortran::lower::genRandomNumber(builder, loc, fir::getBase(args[0]));
1862 }
1863 
1864 // RANDOM_SEED
1865 void IntrinsicLibrary::genRandomSeed(llvm::ArrayRef<fir::ExtendedValue> args) {
1866   assert(args.size() == 3);
1867   for (int i = 0; i < 3; ++i)
1868     if (isPresent(args[i])) {
1869       Fortran::lower::genRandomSeed(builder, loc, i, fir::getBase(args[i]));
1870       return;
1871     }
1872   Fortran::lower::genRandomSeed(builder, loc, -1, mlir::Value{});
1873 }
1874 
1875 // SUM
1876 fir::ExtendedValue
1877 IntrinsicLibrary::genSum(mlir::Type resultType,
1878                          llvm::ArrayRef<fir::ExtendedValue> args) {
1879   return genProdOrSum(fir::runtime::genSum, fir::runtime::genSumDim, resultType,
1880                       builder, loc, stmtCtx, "unexpected result for Sum", args);
1881 }
1882 
1883 // SYSTEM_CLOCK
1884 void IntrinsicLibrary::genSystemClock(llvm::ArrayRef<fir::ExtendedValue> args) {
1885   assert(args.size() == 3);
1886   Fortran::lower::genSystemClock(builder, loc, fir::getBase(args[0]),
1887                                  fir::getBase(args[1]), fir::getBase(args[2]));
1888 }
1889 
1890 // SIZE
1891 fir::ExtendedValue
1892 IntrinsicLibrary::genSize(mlir::Type resultType,
1893                           llvm::ArrayRef<fir::ExtendedValue> args) {
1894   // Note that the value of the KIND argument is already reflected in the
1895   // resultType
1896   assert(args.size() == 3);
1897   if (const auto *boxValue = args[0].getBoxOf<fir::BoxValue>())
1898     if (boxValue->hasAssumedRank())
1899       TODO(loc, "SIZE intrinsic with assumed rank argument");
1900 
1901   // Get the ARRAY argument
1902   mlir::Value array = builder.createBox(loc, args[0]);
1903 
1904   // The front-end rewrites SIZE without the DIM argument to
1905   // an array of SIZE with DIM in most cases, but it may not be
1906   // possible in some cases like when in SIZE(function_call()).
1907   if (isAbsent(args, 1))
1908     return builder.createConvert(loc, resultType,
1909                                  fir::runtime::genSize(builder, loc, array));
1910 
1911   // Get the DIM argument.
1912   mlir::Value dim = fir::getBase(args[1]);
1913   if (!fir::isa_ref_type(dim.getType()))
1914     return builder.createConvert(
1915         loc, resultType, fir::runtime::genSizeDim(builder, loc, array, dim));
1916 
1917   mlir::Value isDynamicallyAbsent = builder.genIsNull(loc, dim);
1918   return builder
1919       .genIfOp(loc, {resultType}, isDynamicallyAbsent,
1920                /*withElseRegion=*/true)
1921       .genThen([&]() {
1922         mlir::Value size = builder.createConvert(
1923             loc, resultType, fir::runtime::genSize(builder, loc, array));
1924         builder.create<fir::ResultOp>(loc, size);
1925       })
1926       .genElse([&]() {
1927         mlir::Value dimValue = builder.create<fir::LoadOp>(loc, dim);
1928         mlir::Value size = builder.createConvert(
1929             loc, resultType,
1930             fir::runtime::genSizeDim(builder, loc, array, dimValue));
1931         builder.create<fir::ResultOp>(loc, size);
1932       })
1933       .getResults()[0];
1934 }
1935 
1936 // LBOUND
1937 fir::ExtendedValue
1938 IntrinsicLibrary::genLbound(mlir::Type resultType,
1939                             llvm::ArrayRef<fir::ExtendedValue> args) {
1940   // Calls to LBOUND that don't have the DIM argument, or for which
1941   // the DIM is a compile time constant, are folded to descriptor inquiries by
1942   // semantics.  This function covers the situations where a call to the
1943   // runtime is required.
1944   assert(args.size() == 3);
1945   assert(!isAbsent(args[1]));
1946   if (const auto *boxValue = args[0].getBoxOf<fir::BoxValue>())
1947     if (boxValue->hasAssumedRank())
1948       TODO(loc, "LBOUND intrinsic with assumed rank argument");
1949 
1950   const fir::ExtendedValue &array = args[0];
1951   mlir::Value box = array.match(
1952       [&](const fir::BoxValue &boxValue) -> mlir::Value {
1953         // This entity is mapped to a fir.box that may not contain the local
1954         // lower bound information if it is a dummy. Rebox it with the local
1955         // shape information.
1956         mlir::Value localShape = builder.createShape(loc, array);
1957         mlir::Value oldBox = boxValue.getAddr();
1958         return builder.create<fir::ReboxOp>(
1959             loc, oldBox.getType(), oldBox, localShape, /*slice=*/mlir::Value{});
1960       },
1961       [&](const auto &) -> mlir::Value {
1962         // This a pointer/allocatable, or an entity not yet tracked with a
1963         // fir.box. For pointer/allocatable, createBox will forward the
1964         // descriptor that contains the correct lower bound information. For
1965         // other entities, a new fir.box will be made with the local lower
1966         // bounds.
1967         return builder.createBox(loc, array);
1968       });
1969 
1970   mlir::Value dim = fir::getBase(args[1]);
1971   return builder.createConvert(
1972       loc, resultType,
1973       fir::runtime::genLboundDim(builder, loc, fir::getBase(box), dim));
1974 }
1975 
1976 // UBOUND
1977 fir::ExtendedValue
1978 IntrinsicLibrary::genUbound(mlir::Type resultType,
1979                             llvm::ArrayRef<fir::ExtendedValue> args) {
1980   assert(args.size() == 3 || args.size() == 2);
1981   if (args.size() == 3) {
1982     // Handle calls to UBOUND with the DIM argument, which return a scalar
1983     mlir::Value extent = fir::getBase(genSize(resultType, args));
1984     mlir::Value lbound = fir::getBase(genLbound(resultType, args));
1985 
1986     mlir::Value one = builder.createIntegerConstant(loc, resultType, 1);
1987     mlir::Value ubound = builder.create<mlir::arith::SubIOp>(loc, lbound, one);
1988     return builder.create<mlir::arith::AddIOp>(loc, ubound, extent);
1989   } else {
1990     // Handle calls to UBOUND without the DIM argument, which return an array
1991     mlir::Value kind = isAbsent(args[1])
1992                            ? builder.createIntegerConstant(
1993                                  loc, builder.getIndexType(),
1994                                  builder.getKindMap().defaultIntegerKind())
1995                            : fir::getBase(args[1]);
1996 
1997     // Create mutable fir.box to be passed to the runtime for the result.
1998     mlir::Type type = builder.getVarLenSeqTy(resultType, /*rank=*/1);
1999     fir::MutableBoxValue resultMutableBox =
2000         fir::factory::createTempMutableBox(builder, loc, type);
2001     mlir::Value resultIrBox =
2002         fir::factory::getMutableIRBox(builder, loc, resultMutableBox);
2003 
2004     fir::runtime::genUbound(builder, loc, resultIrBox, fir::getBase(args[0]),
2005                             kind);
2006 
2007     return readAndAddCleanUp(resultMutableBox, resultType, "UBOUND");
2008   }
2009   return mlir::Value();
2010 }
2011 
2012 //===----------------------------------------------------------------------===//
2013 // Argument lowering rules interface
2014 //===----------------------------------------------------------------------===//
2015 
2016 const Fortran::lower::IntrinsicArgumentLoweringRules *
2017 Fortran::lower::getIntrinsicArgumentLowering(llvm::StringRef intrinsicName) {
2018   if (const IntrinsicHandler *handler = findIntrinsicHandler(intrinsicName))
2019     if (!handler->argLoweringRules.hasDefaultRules())
2020       return &handler->argLoweringRules;
2021   return nullptr;
2022 }
2023 
2024 /// Return how argument \p argName should be lowered given the rules for the
2025 /// intrinsic function.
2026 Fortran::lower::ArgLoweringRule Fortran::lower::lowerIntrinsicArgumentAs(
2027     mlir::Location loc, const IntrinsicArgumentLoweringRules &rules,
2028     llvm::StringRef argName) {
2029   for (const IntrinsicDummyArgument &arg : rules.args) {
2030     if (arg.name && arg.name == argName)
2031       return {arg.lowerAs, arg.handleDynamicOptional};
2032   }
2033   fir::emitFatalError(
2034       loc, "internal: unknown intrinsic argument name in lowering '" + argName +
2035                "'");
2036 }
2037 
2038 //===----------------------------------------------------------------------===//
2039 // Public intrinsic call helpers
2040 //===----------------------------------------------------------------------===//
2041 
2042 fir::ExtendedValue
2043 Fortran::lower::genIntrinsicCall(fir::FirOpBuilder &builder, mlir::Location loc,
2044                                  llvm::StringRef name,
2045                                  llvm::Optional<mlir::Type> resultType,
2046                                  llvm::ArrayRef<fir::ExtendedValue> args,
2047                                  Fortran::lower::StatementContext &stmtCtx) {
2048   return IntrinsicLibrary{builder, loc, &stmtCtx}.genIntrinsicCall(
2049       name, resultType, args);
2050 }
2051 
2052 mlir::Value Fortran::lower::genMax(fir::FirOpBuilder &builder,
2053                                    mlir::Location loc,
2054                                    llvm::ArrayRef<mlir::Value> args) {
2055   assert(args.size() > 0 && "max requires at least one argument");
2056   return IntrinsicLibrary{builder, loc}
2057       .genExtremum<Extremum::Max, ExtremumBehavior::MinMaxss>(args[0].getType(),
2058                                                               args);
2059 }
2060 
2061 mlir::Value Fortran::lower::genPow(fir::FirOpBuilder &builder,
2062                                    mlir::Location loc, mlir::Type type,
2063                                    mlir::Value x, mlir::Value y) {
2064   return IntrinsicLibrary{builder, loc}.genRuntimeCall("pow", type, {x, y});
2065 }
2066