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/Inquiry.h"
28 #include "flang/Optimizer/Builder/Runtime/RTBuilder.h"
29 #include "flang/Optimizer/Builder/Runtime/Reduction.h"
30 #include "flang/Optimizer/Dialect/FIROpsSupport.h"
31 #include "flang/Optimizer/Support/FatalError.h"
32 #include "mlir/Dialect/LLVMIR/LLVMDialect.h"
33 #include "llvm/Support/CommandLine.h"
34 
35 #define DEBUG_TYPE "flang-lower-intrinsic"
36 
37 #define PGMATH_DECLARE
38 #include "flang/Evaluate/pgmath.h.inc"
39 
40 /// Enums used to templatize and share lowering of MIN and MAX.
41 enum class Extremum { Min, Max };
42 
43 // There are different ways to deal with NaNs in MIN and MAX.
44 // Known existing behaviors are listed below and can be selected for
45 // f18 MIN/MAX implementation.
46 enum class ExtremumBehavior {
47   // Note: the Signaling/quiet aspect of NaNs in the behaviors below are
48   // not described because there is no way to control/observe such aspect in
49   // MLIR/LLVM yet. The IEEE behaviors come with requirements regarding this
50   // aspect that are therefore currently not enforced. In the descriptions
51   // below, NaNs can be signaling or quite. Returned NaNs may be signaling
52   // if one of the input NaN was signaling but it cannot be guaranteed either.
53   // Existing compilers using an IEEE behavior (gfortran) also do not fulfill
54   // signaling/quiet requirements.
55   IeeeMinMaximumNumber,
56   // IEEE minimumNumber/maximumNumber behavior (754-2019, section 9.6):
57   // If one of the argument is and number and the other is NaN, return the
58   // number. If both arguements are NaN, return NaN.
59   // Compilers: gfortran.
60   IeeeMinMaximum,
61   // IEEE minimum/maximum behavior (754-2019, section 9.6):
62   // If one of the argument is NaN, return NaN.
63   MinMaxss,
64   // x86 minss/maxss behavior:
65   // If the second argument is a number and the other is NaN, return the number.
66   // In all other cases where at least one operand is NaN, return NaN.
67   // Compilers: xlf (only for MAX), ifort, pgfortran -nollvm, and nagfor.
68   PgfortranLlvm,
69   // "Opposite of" x86 minss/maxss behavior:
70   // If the first argument is a number and the other is NaN, return the
71   // number.
72   // In all other cases where at least one operand is NaN, return NaN.
73   // Compilers: xlf (only for MIN), and pgfortran (with llvm).
74   IeeeMinMaxNum
75   // IEEE minNum/maxNum behavior (754-2008, section 5.3.1):
76   // TODO: Not implemented.
77   // It is the only behavior where the signaling/quiet aspect of a NaN argument
78   // impacts if the result should be NaN or the argument that is a number.
79   // LLVM/MLIR do not provide ways to observe this aspect, so it is not
80   // possible to implement it without some target dependent runtime.
81 };
82 
83 /// This file implements lowering of Fortran intrinsic procedures.
84 /// Intrinsics are lowered to a mix of FIR and MLIR operations as
85 /// well as call to runtime functions or LLVM intrinsics.
86 
87 /// Lowering of intrinsic procedure calls is based on a map that associates
88 /// Fortran intrinsic generic names to FIR generator functions.
89 /// All generator functions are member functions of the IntrinsicLibrary class
90 /// and have the same interface.
91 /// If no generator is given for an intrinsic name, a math runtime library
92 /// is searched for an implementation and, if a runtime function is found,
93 /// a call is generated for it. LLVM intrinsics are handled as a math
94 /// runtime library here.
95 
96 fir::ExtendedValue Fortran::lower::getAbsentIntrinsicArgument() {
97   return fir::UnboxedValue{};
98 }
99 
100 /// Test if an ExtendedValue is absent.
101 static bool isAbsent(const fir::ExtendedValue &exv) {
102   return !fir::getBase(exv);
103 }
104 static bool isAbsent(llvm::ArrayRef<fir::ExtendedValue> args, size_t argIndex) {
105   return args.size() <= argIndex || isAbsent(args[argIndex]);
106 }
107 
108 /// Process calls to Maxval, Minval, Product, Sum intrinsic functions that
109 /// take a DIM argument.
110 template <typename FD>
111 static fir::ExtendedValue
112 genFuncDim(FD funcDim, mlir::Type resultType, fir::FirOpBuilder &builder,
113            mlir::Location loc, Fortran::lower::StatementContext *stmtCtx,
114            llvm::StringRef errMsg, mlir::Value array, fir::ExtendedValue dimArg,
115            mlir::Value mask, int rank) {
116 
117   // Create mutable fir.box to be passed to the runtime for the result.
118   mlir::Type resultArrayType = builder.getVarLenSeqTy(resultType, rank - 1);
119   fir::MutableBoxValue resultMutableBox =
120       fir::factory::createTempMutableBox(builder, loc, resultArrayType);
121   mlir::Value resultIrBox =
122       fir::factory::getMutableIRBox(builder, loc, resultMutableBox);
123 
124   mlir::Value dim =
125       isAbsent(dimArg)
126           ? builder.createIntegerConstant(loc, builder.getIndexType(), 0)
127           : fir::getBase(dimArg);
128   funcDim(builder, loc, resultIrBox, array, dim, mask);
129 
130   fir::ExtendedValue res =
131       fir::factory::genMutableBoxRead(builder, loc, resultMutableBox);
132   return res.match(
133       [&](const fir::ArrayBoxValue &box) -> fir::ExtendedValue {
134         // Add cleanup code
135         assert(stmtCtx);
136         fir::FirOpBuilder *bldr = &builder;
137         mlir::Value temp = box.getAddr();
138         stmtCtx->attachCleanup(
139             [=]() { bldr->create<fir::FreeMemOp>(loc, temp); });
140         return box;
141       },
142       [&](const fir::CharArrayBoxValue &box) -> fir::ExtendedValue {
143         // Add cleanup code
144         assert(stmtCtx);
145         fir::FirOpBuilder *bldr = &builder;
146         mlir::Value temp = box.getAddr();
147         stmtCtx->attachCleanup(
148             [=]() { bldr->create<fir::FreeMemOp>(loc, temp); });
149         return box;
150       },
151       [&](const auto &) -> fir::ExtendedValue {
152         fir::emitFatalError(loc, errMsg);
153       });
154 }
155 
156 /// Process calls to Product, Sum intrinsic functions
157 template <typename FN, typename FD>
158 static fir::ExtendedValue
159 genProdOrSum(FN func, FD funcDim, mlir::Type resultType,
160              fir::FirOpBuilder &builder, mlir::Location loc,
161              Fortran::lower::StatementContext *stmtCtx, llvm::StringRef errMsg,
162              llvm::ArrayRef<fir::ExtendedValue> args) {
163 
164   assert(args.size() == 3);
165 
166   // Handle required array argument
167   fir::BoxValue arryTmp = builder.createBox(loc, args[0]);
168   mlir::Value array = fir::getBase(arryTmp);
169   int rank = arryTmp.rank();
170   assert(rank >= 1);
171 
172   // Handle optional mask argument
173   auto mask = isAbsent(args[2])
174                   ? builder.create<fir::AbsentOp>(
175                         loc, fir::BoxType::get(builder.getI1Type()))
176                   : builder.createBox(loc, args[2]);
177 
178   bool absentDim = isAbsent(args[1]);
179 
180   // We call the type specific versions because the result is scalar
181   // in the case below.
182   if (absentDim || rank == 1) {
183     mlir::Type ty = array.getType();
184     mlir::Type arrTy = fir::dyn_cast_ptrOrBoxEleTy(ty);
185     auto eleTy = arrTy.cast<fir::SequenceType>().getEleTy();
186     if (fir::isa_complex(eleTy)) {
187       mlir::Value result = builder.createTemporary(loc, eleTy);
188       func(builder, loc, array, mask, result);
189       return builder.create<fir::LoadOp>(loc, result);
190     }
191     auto resultBox = builder.create<fir::AbsentOp>(
192         loc, fir::BoxType::get(builder.getI1Type()));
193     return func(builder, loc, array, mask, resultBox);
194   }
195   // Handle Product/Sum cases that have an array result.
196   return genFuncDim(funcDim, resultType, builder, loc, stmtCtx, errMsg, array,
197                     args[1], mask, rank);
198 }
199 
200 // TODO error handling -> return a code or directly emit messages ?
201 struct IntrinsicLibrary {
202 
203   // Constructors.
204   explicit IntrinsicLibrary(fir::FirOpBuilder &builder, mlir::Location loc,
205                             Fortran::lower::StatementContext *stmtCtx = nullptr)
206       : builder{builder}, loc{loc}, stmtCtx{stmtCtx} {}
207   IntrinsicLibrary() = delete;
208   IntrinsicLibrary(const IntrinsicLibrary &) = delete;
209 
210   /// Generate FIR for call to Fortran intrinsic \p name with arguments \p arg
211   /// and expected result type \p resultType.
212   fir::ExtendedValue genIntrinsicCall(llvm::StringRef name,
213                                       llvm::Optional<mlir::Type> resultType,
214                                       llvm::ArrayRef<fir::ExtendedValue> arg);
215 
216   /// Search a runtime function that is associated to the generic intrinsic name
217   /// and whose signature matches the intrinsic arguments and result types.
218   /// If no such runtime function is found but a runtime function associated
219   /// with the Fortran generic exists and has the same number of arguments,
220   /// conversions will be inserted before and/or after the call. This is to
221   /// mainly to allow 16 bits float support even-though little or no math
222   /// runtime is currently available for it.
223   mlir::Value genRuntimeCall(llvm::StringRef name, mlir::Type,
224                              llvm::ArrayRef<mlir::Value>);
225 
226   using RuntimeCallGenerator = std::function<mlir::Value(
227       fir::FirOpBuilder &, mlir::Location, llvm::ArrayRef<mlir::Value>)>;
228   RuntimeCallGenerator
229   getRuntimeCallGenerator(llvm::StringRef name,
230                           mlir::FunctionType soughtFuncType);
231 
232   /// Lowering for the ABS intrinsic. The ABS intrinsic expects one argument in
233   /// the llvm::ArrayRef. The ABS intrinsic is lowered into MLIR/FIR operation
234   /// if the argument is an integer, into llvm intrinsics if the argument is
235   /// real and to the `hypot` math routine if the argument is of complex type.
236   mlir::Value genAbs(mlir::Type, llvm::ArrayRef<mlir::Value>);
237   fir::ExtendedValue genAssociated(mlir::Type,
238                                    llvm::ArrayRef<fir::ExtendedValue>);
239   fir::ExtendedValue genChar(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
240   template <Extremum, ExtremumBehavior>
241   mlir::Value genExtremum(mlir::Type, llvm::ArrayRef<mlir::Value>);
242   /// Lowering for the IAND intrinsic. The IAND intrinsic expects two arguments
243   /// in the llvm::ArrayRef.
244   mlir::Value genIand(mlir::Type, llvm::ArrayRef<mlir::Value>);
245   fir::ExtendedValue genLbound(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
246   fir::ExtendedValue genSize(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
247   fir::ExtendedValue genSum(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
248   fir::ExtendedValue genUbound(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
249 
250   /// Define the different FIR generators that can be mapped to intrinsic to
251   /// generate the related code.
252   using ElementalGenerator = decltype(&IntrinsicLibrary::genAbs);
253   using ExtendedGenerator = decltype(&IntrinsicLibrary::genSum);
254   using Generator = std::variant<ElementalGenerator, ExtendedGenerator>;
255 
256   template <typename GeneratorType>
257   fir::ExtendedValue
258   outlineInExtendedWrapper(GeneratorType, llvm::StringRef name,
259                            llvm::Optional<mlir::Type> resultType,
260                            llvm::ArrayRef<fir::ExtendedValue> args);
261 
262   template <typename GeneratorType>
263   mlir::FuncOp getWrapper(GeneratorType, llvm::StringRef name,
264                           mlir::FunctionType, bool loadRefArguments = false);
265 
266   /// Generate calls to ElementalGenerator, handling the elemental aspects
267   template <typename GeneratorType>
268   fir::ExtendedValue
269   genElementalCall(GeneratorType, llvm::StringRef name, mlir::Type resultType,
270                    llvm::ArrayRef<fir::ExtendedValue> args, bool outline);
271 
272   /// Helper to invoke code generator for the intrinsics given arguments.
273   mlir::Value invokeGenerator(ElementalGenerator generator,
274                               mlir::Type resultType,
275                               llvm::ArrayRef<mlir::Value> args);
276   mlir::Value invokeGenerator(RuntimeCallGenerator generator,
277                               mlir::Type resultType,
278                               llvm::ArrayRef<mlir::Value> args);
279   mlir::Value invokeGenerator(ExtendedGenerator generator,
280                               mlir::Type resultType,
281                               llvm::ArrayRef<mlir::Value> args);
282 
283   /// Add clean-up for \p temp to the current statement context;
284   void addCleanUpForTemp(mlir::Location loc, mlir::Value temp);
285   /// Helper function for generating code clean-up for result descriptors
286   fir::ExtendedValue readAndAddCleanUp(fir::MutableBoxValue resultMutableBox,
287                                        mlir::Type resultType,
288                                        llvm::StringRef errMsg);
289 
290   fir::FirOpBuilder &builder;
291   mlir::Location loc;
292   Fortran::lower::StatementContext *stmtCtx;
293 };
294 
295 struct IntrinsicDummyArgument {
296   const char *name = nullptr;
297   Fortran::lower::LowerIntrinsicArgAs lowerAs =
298       Fortran::lower::LowerIntrinsicArgAs::Value;
299   bool handleDynamicOptional = false;
300 };
301 
302 struct Fortran::lower::IntrinsicArgumentLoweringRules {
303   /// There is no more than 7 non repeated arguments in Fortran intrinsics.
304   IntrinsicDummyArgument args[7];
305   constexpr bool hasDefaultRules() const { return args[0].name == nullptr; }
306 };
307 
308 /// Structure describing what needs to be done to lower intrinsic "name".
309 struct IntrinsicHandler {
310   const char *name;
311   IntrinsicLibrary::Generator generator;
312   // The following may be omitted in the table below.
313   Fortran::lower::IntrinsicArgumentLoweringRules argLoweringRules = {};
314   bool isElemental = true;
315 };
316 
317 constexpr auto asValue = Fortran::lower::LowerIntrinsicArgAs::Value;
318 constexpr auto asBox = Fortran::lower::LowerIntrinsicArgAs::Box;
319 constexpr auto asInquired = Fortran::lower::LowerIntrinsicArgAs::Inquired;
320 using I = IntrinsicLibrary;
321 
322 /// Flag to indicate that an intrinsic argument has to be handled as
323 /// being dynamically optional (e.g. special handling when actual
324 /// argument is an optional variable in the current scope).
325 static constexpr bool handleDynamicOptional = true;
326 
327 /// Table that drives the fir generation depending on the intrinsic.
328 /// one to one mapping with Fortran arguments. If no mapping is
329 /// defined here for a generic intrinsic, genRuntimeCall will be called
330 /// to look for a match in the runtime a emit a call. Note that the argument
331 /// lowering rules for an intrinsic need to be provided only if at least one
332 /// argument must not be lowered by value. In which case, the lowering rules
333 /// should be provided for all the intrinsic arguments for completeness.
334 static constexpr IntrinsicHandler handlers[]{
335     {"abs", &I::genAbs},
336     {"associated",
337      &I::genAssociated,
338      {{{"pointer", asInquired}, {"target", asInquired}}},
339      /*isElemental=*/false},
340     {"char", &I::genChar},
341     {"iand", &I::genIand},
342     {"sum",
343      &I::genSum,
344      {{{"array", asBox},
345        {"dim", asValue},
346        {"mask", asBox, handleDynamicOptional}}},
347      /*isElemental=*/false},
348     {"ubound",
349      &I::genUbound,
350      {{{"array", asBox}, {"dim", asValue}, {"kind", asValue}}},
351      /*isElemental=*/false},
352 };
353 
354 static const IntrinsicHandler *findIntrinsicHandler(llvm::StringRef name) {
355   auto compare = [](const IntrinsicHandler &handler, llvm::StringRef name) {
356     return name.compare(handler.name) > 0;
357   };
358   auto result =
359       std::lower_bound(std::begin(handlers), std::end(handlers), name, compare);
360   return result != std::end(handlers) && result->name == name ? result
361                                                               : nullptr;
362 }
363 
364 //===----------------------------------------------------------------------===//
365 // Math runtime description and matching utility
366 //===----------------------------------------------------------------------===//
367 
368 /// Command line option to modify math runtime version used to implement
369 /// intrinsics.
370 enum MathRuntimeVersion { fastVersion, llvmOnly };
371 llvm::cl::opt<MathRuntimeVersion> mathRuntimeVersion(
372     "math-runtime", llvm::cl::desc("Select math runtime version:"),
373     llvm::cl::values(
374         clEnumValN(fastVersion, "fast", "use pgmath fast runtime"),
375         clEnumValN(llvmOnly, "llvm",
376                    "only use LLVM intrinsics (may be incomplete)")),
377     llvm::cl::init(fastVersion));
378 
379 struct RuntimeFunction {
380   // llvm::StringRef comparison operator are not constexpr, so use string_view.
381   using Key = std::string_view;
382   // Needed for implicit compare with keys.
383   constexpr operator Key() const { return key; }
384   Key key; // intrinsic name
385   llvm::StringRef symbol;
386   fir::runtime::FuncTypeBuilderFunc typeGenerator;
387 };
388 
389 #define RUNTIME_STATIC_DESCRIPTION(name, func)                                 \
390   {#name, #func, fir::runtime::RuntimeTableKey<decltype(func)>::getTypeModel()},
391 static constexpr RuntimeFunction pgmathFast[] = {
392 #define PGMATH_FAST
393 #define PGMATH_USE_ALL_TYPES(name, func) RUNTIME_STATIC_DESCRIPTION(name, func)
394 #include "flang/Evaluate/pgmath.h.inc"
395 };
396 
397 static mlir::FunctionType genF32F32FuncType(mlir::MLIRContext *context) {
398   mlir::Type t = mlir::FloatType::getF32(context);
399   return mlir::FunctionType::get(context, {t}, {t});
400 }
401 
402 static mlir::FunctionType genF64F64FuncType(mlir::MLIRContext *context) {
403   mlir::Type t = mlir::FloatType::getF64(context);
404   return mlir::FunctionType::get(context, {t}, {t});
405 }
406 
407 static mlir::FunctionType genF32F32F32FuncType(mlir::MLIRContext *context) {
408   auto t = mlir::FloatType::getF32(context);
409   return mlir::FunctionType::get(context, {t, t}, {t});
410 }
411 
412 static mlir::FunctionType genF64F64F64FuncType(mlir::MLIRContext *context) {
413   auto t = mlir::FloatType::getF64(context);
414   return mlir::FunctionType::get(context, {t, t}, {t});
415 }
416 
417 // TODO : Fill-up this table with more intrinsic.
418 // Note: These are also defined as operations in LLVM dialect. See if this
419 // can be use and has advantages.
420 static constexpr RuntimeFunction llvmIntrinsics[] = {
421     {"abs", "llvm.fabs.f32", genF32F32FuncType},
422     {"abs", "llvm.fabs.f64", genF64F64FuncType},
423     {"pow", "llvm.pow.f32", genF32F32F32FuncType},
424     {"pow", "llvm.pow.f64", genF64F64F64FuncType},
425 };
426 
427 // This helper class computes a "distance" between two function types.
428 // The distance measures how many narrowing conversions of actual arguments
429 // and result of "from" must be made in order to use "to" instead of "from".
430 // For instance, the distance between ACOS(REAL(10)) and ACOS(REAL(8)) is
431 // greater than the one between ACOS(REAL(10)) and ACOS(REAL(16)). This means
432 // if no implementation of ACOS(REAL(10)) is available, it is better to use
433 // ACOS(REAL(16)) with casts rather than ACOS(REAL(8)).
434 // Note that this is not a symmetric distance and the order of "from" and "to"
435 // arguments matters, d(foo, bar) may not be the same as d(bar, foo) because it
436 // may be safe to replace foo by bar, but not the opposite.
437 class FunctionDistance {
438 public:
439   FunctionDistance() : infinite{true} {}
440 
441   FunctionDistance(mlir::FunctionType from, mlir::FunctionType to) {
442     unsigned nInputs = from.getNumInputs();
443     unsigned nResults = from.getNumResults();
444     if (nResults != to.getNumResults() || nInputs != to.getNumInputs()) {
445       infinite = true;
446     } else {
447       for (decltype(nInputs) i = 0; i < nInputs && !infinite; ++i)
448         addArgumentDistance(from.getInput(i), to.getInput(i));
449       for (decltype(nResults) i = 0; i < nResults && !infinite; ++i)
450         addResultDistance(to.getResult(i), from.getResult(i));
451     }
452   }
453 
454   /// Beware both d1.isSmallerThan(d2) *and* d2.isSmallerThan(d1) may be
455   /// false if both d1 and d2 are infinite. This implies that
456   ///  d1.isSmallerThan(d2) is not equivalent to !d2.isSmallerThan(d1)
457   bool isSmallerThan(const FunctionDistance &d) const {
458     return !infinite &&
459            (d.infinite || std::lexicographical_compare(
460                               conversions.begin(), conversions.end(),
461                               d.conversions.begin(), d.conversions.end()));
462   }
463 
464   bool isLosingPrecision() const {
465     return conversions[narrowingArg] != 0 || conversions[extendingResult] != 0;
466   }
467 
468   bool isInfinite() const { return infinite; }
469 
470 private:
471   enum class Conversion { Forbidden, None, Narrow, Extend };
472 
473   void addArgumentDistance(mlir::Type from, mlir::Type to) {
474     switch (conversionBetweenTypes(from, to)) {
475     case Conversion::Forbidden:
476       infinite = true;
477       break;
478     case Conversion::None:
479       break;
480     case Conversion::Narrow:
481       conversions[narrowingArg]++;
482       break;
483     case Conversion::Extend:
484       conversions[nonNarrowingArg]++;
485       break;
486     }
487   }
488 
489   void addResultDistance(mlir::Type from, mlir::Type to) {
490     switch (conversionBetweenTypes(from, to)) {
491     case Conversion::Forbidden:
492       infinite = true;
493       break;
494     case Conversion::None:
495       break;
496     case Conversion::Narrow:
497       conversions[nonExtendingResult]++;
498       break;
499     case Conversion::Extend:
500       conversions[extendingResult]++;
501       break;
502     }
503   }
504 
505   // Floating point can be mlir::FloatType or fir::real
506   static unsigned getFloatingPointWidth(mlir::Type t) {
507     if (auto f{t.dyn_cast<mlir::FloatType>()})
508       return f.getWidth();
509     // FIXME: Get width another way for fir.real/complex
510     // - use fir/KindMapping.h and llvm::Type
511     // - or use evaluate/type.h
512     if (auto r{t.dyn_cast<fir::RealType>()})
513       return r.getFKind() * 4;
514     if (auto cplx{t.dyn_cast<fir::ComplexType>()})
515       return cplx.getFKind() * 4;
516     llvm_unreachable("not a floating-point type");
517   }
518 
519   static Conversion conversionBetweenTypes(mlir::Type from, mlir::Type to) {
520     if (from == to)
521       return Conversion::None;
522 
523     if (auto fromIntTy{from.dyn_cast<mlir::IntegerType>()}) {
524       if (auto toIntTy{to.dyn_cast<mlir::IntegerType>()}) {
525         return fromIntTy.getWidth() > toIntTy.getWidth() ? Conversion::Narrow
526                                                          : Conversion::Extend;
527       }
528     }
529 
530     if (fir::isa_real(from) && fir::isa_real(to)) {
531       return getFloatingPointWidth(from) > getFloatingPointWidth(to)
532                  ? Conversion::Narrow
533                  : Conversion::Extend;
534     }
535 
536     if (auto fromCplxTy{from.dyn_cast<fir::ComplexType>()}) {
537       if (auto toCplxTy{to.dyn_cast<fir::ComplexType>()}) {
538         return getFloatingPointWidth(fromCplxTy) >
539                        getFloatingPointWidth(toCplxTy)
540                    ? Conversion::Narrow
541                    : Conversion::Extend;
542       }
543     }
544     // Notes:
545     // - No conversion between character types, specialization of runtime
546     // functions should be made instead.
547     // - It is not clear there is a use case for automatic conversions
548     // around Logical and it may damage hidden information in the physical
549     // storage so do not do it.
550     return Conversion::Forbidden;
551   }
552 
553   // Below are indexes to access data in conversions.
554   // The order in data does matter for lexicographical_compare
555   enum {
556     narrowingArg = 0,   // usually bad
557     extendingResult,    // usually bad
558     nonExtendingResult, // usually ok
559     nonNarrowingArg,    // usually ok
560     dataSize
561   };
562 
563   std::array<int, dataSize> conversions = {};
564   bool infinite = false; // When forbidden conversion or wrong argument number
565 };
566 
567 /// Build mlir::FuncOp from runtime symbol description and add
568 /// fir.runtime attribute.
569 static mlir::FuncOp getFuncOp(mlir::Location loc, fir::FirOpBuilder &builder,
570                               const RuntimeFunction &runtime) {
571   mlir::FuncOp function = builder.addNamedFunction(
572       loc, runtime.symbol, runtime.typeGenerator(builder.getContext()));
573   function->setAttr("fir.runtime", builder.getUnitAttr());
574   return function;
575 }
576 
577 /// Select runtime function that has the smallest distance to the intrinsic
578 /// function type and that will not imply narrowing arguments or extending the
579 /// result.
580 /// If nothing is found, the mlir::FuncOp will contain a nullptr.
581 mlir::FuncOp searchFunctionInLibrary(
582     mlir::Location loc, fir::FirOpBuilder &builder,
583     const Fortran::common::StaticMultimapView<RuntimeFunction> &lib,
584     llvm::StringRef name, mlir::FunctionType funcType,
585     const RuntimeFunction **bestNearMatch,
586     FunctionDistance &bestMatchDistance) {
587   std::pair<const RuntimeFunction *, const RuntimeFunction *> range =
588       lib.equal_range(name);
589   for (auto iter = range.first; iter != range.second && iter; ++iter) {
590     const RuntimeFunction &impl = *iter;
591     mlir::FunctionType implType = impl.typeGenerator(builder.getContext());
592     if (funcType == implType)
593       return getFuncOp(loc, builder, impl); // exact match
594 
595     FunctionDistance distance(funcType, implType);
596     if (distance.isSmallerThan(bestMatchDistance)) {
597       *bestNearMatch = &impl;
598       bestMatchDistance = std::move(distance);
599     }
600   }
601   return {};
602 }
603 
604 /// Search runtime for the best runtime function given an intrinsic name
605 /// and interface. The interface may not be a perfect match in which case
606 /// the caller is responsible to insert argument and return value conversions.
607 /// If nothing is found, the mlir::FuncOp will contain a nullptr.
608 static mlir::FuncOp getRuntimeFunction(mlir::Location loc,
609                                        fir::FirOpBuilder &builder,
610                                        llvm::StringRef name,
611                                        mlir::FunctionType funcType) {
612   const RuntimeFunction *bestNearMatch = nullptr;
613   FunctionDistance bestMatchDistance{};
614   mlir::FuncOp match;
615   using RtMap = Fortran::common::StaticMultimapView<RuntimeFunction>;
616   static constexpr RtMap pgmathF(pgmathFast);
617   static_assert(pgmathF.Verify() && "map must be sorted");
618   if (mathRuntimeVersion == fastVersion) {
619     match = searchFunctionInLibrary(loc, builder, pgmathF, name, funcType,
620                                     &bestNearMatch, bestMatchDistance);
621   } else {
622     assert(mathRuntimeVersion == llvmOnly && "unknown math runtime");
623   }
624   if (match)
625     return match;
626 
627   // Go through llvm intrinsics if not exact match in libpgmath or if
628   // mathRuntimeVersion == llvmOnly
629   static constexpr RtMap llvmIntr(llvmIntrinsics);
630   static_assert(llvmIntr.Verify() && "map must be sorted");
631   if (mlir::FuncOp exactMatch =
632           searchFunctionInLibrary(loc, builder, llvmIntr, name, funcType,
633                                   &bestNearMatch, bestMatchDistance))
634     return exactMatch;
635 
636   if (bestNearMatch != nullptr) {
637     if (bestMatchDistance.isLosingPrecision()) {
638       // Using this runtime version requires narrowing the arguments
639       // or extending the result. It is not numerically safe. There
640       // is currently no quad math library that was described in
641       // lowering and could be used here. Emit an error and continue
642       // generating the code with the narrowing cast so that the user
643       // can get a complete list of the problematic intrinsic calls.
644       std::string message("TODO: no math runtime available for '");
645       llvm::raw_string_ostream sstream(message);
646       if (name == "pow") {
647         assert(funcType.getNumInputs() == 2 &&
648                "power operator has two arguments");
649         sstream << funcType.getInput(0) << " ** " << funcType.getInput(1);
650       } else {
651         sstream << name << "(";
652         if (funcType.getNumInputs() > 0)
653           sstream << funcType.getInput(0);
654         for (mlir::Type argType : funcType.getInputs().drop_front())
655           sstream << ", " << argType;
656         sstream << ")";
657       }
658       sstream << "'";
659       mlir::emitError(loc, message);
660     }
661     return getFuncOp(loc, builder, *bestNearMatch);
662   }
663   return {};
664 }
665 
666 /// Helpers to get function type from arguments and result type.
667 static mlir::FunctionType getFunctionType(llvm::Optional<mlir::Type> resultType,
668                                           llvm::ArrayRef<mlir::Value> arguments,
669                                           fir::FirOpBuilder &builder) {
670   llvm::SmallVector<mlir::Type> argTypes;
671   for (mlir::Value arg : arguments)
672     argTypes.push_back(arg.getType());
673   llvm::SmallVector<mlir::Type> resTypes;
674   if (resultType)
675     resTypes.push_back(*resultType);
676   return mlir::FunctionType::get(builder.getModule().getContext(), argTypes,
677                                  resTypes);
678 }
679 
680 /// fir::ExtendedValue to mlir::Value translation layer
681 
682 fir::ExtendedValue toExtendedValue(mlir::Value val, fir::FirOpBuilder &builder,
683                                    mlir::Location loc) {
684   assert(val && "optional unhandled here");
685   mlir::Type type = val.getType();
686   mlir::Value base = val;
687   mlir::IndexType indexType = builder.getIndexType();
688   llvm::SmallVector<mlir::Value> extents;
689 
690   fir::factory::CharacterExprHelper charHelper{builder, loc};
691   // FIXME: we may want to allow non character scalar here.
692   if (charHelper.isCharacterScalar(type))
693     return charHelper.toExtendedValue(val);
694 
695   if (auto refType = type.dyn_cast<fir::ReferenceType>())
696     type = refType.getEleTy();
697 
698   if (auto arrayType = type.dyn_cast<fir::SequenceType>()) {
699     type = arrayType.getEleTy();
700     for (fir::SequenceType::Extent extent : arrayType.getShape()) {
701       if (extent == fir::SequenceType::getUnknownExtent())
702         break;
703       extents.emplace_back(
704           builder.createIntegerConstant(loc, indexType, extent));
705     }
706     // Last extent might be missing in case of assumed-size. If more extents
707     // could not be deduced from type, that's an error (a fir.box should
708     // have been used in the interface).
709     if (extents.size() + 1 < arrayType.getShape().size())
710       mlir::emitError(loc, "cannot retrieve array extents from type");
711   } else if (type.isa<fir::BoxType>() || type.isa<fir::RecordType>()) {
712     fir::emitFatalError(loc, "not yet implemented: descriptor or derived type");
713   }
714 
715   if (!extents.empty())
716     return fir::ArrayBoxValue{base, extents};
717   return base;
718 }
719 
720 mlir::Value toValue(const fir::ExtendedValue &val, fir::FirOpBuilder &builder,
721                     mlir::Location loc) {
722   if (const fir::CharBoxValue *charBox = val.getCharBox()) {
723     mlir::Value buffer = charBox->getBuffer();
724     if (buffer.getType().isa<fir::BoxCharType>())
725       return buffer;
726     return fir::factory::CharacterExprHelper{builder, loc}.createEmboxChar(
727         buffer, charBox->getLen());
728   }
729 
730   // FIXME: need to access other ExtendedValue variants and handle them
731   // properly.
732   return fir::getBase(val);
733 }
734 
735 //===----------------------------------------------------------------------===//
736 // IntrinsicLibrary
737 //===----------------------------------------------------------------------===//
738 
739 /// Emit a TODO error message for as yet unimplemented intrinsics.
740 static void crashOnMissingIntrinsic(mlir::Location loc, llvm::StringRef name) {
741   TODO(loc, "missing intrinsic lowering: " + llvm::Twine(name));
742 }
743 
744 template <typename GeneratorType>
745 fir::ExtendedValue IntrinsicLibrary::genElementalCall(
746     GeneratorType generator, llvm::StringRef name, mlir::Type resultType,
747     llvm::ArrayRef<fir::ExtendedValue> args, bool outline) {
748   llvm::SmallVector<mlir::Value> scalarArgs;
749   for (const fir::ExtendedValue &arg : args)
750     if (arg.getUnboxed() || arg.getCharBox())
751       scalarArgs.emplace_back(fir::getBase(arg));
752     else
753       fir::emitFatalError(loc, "nonscalar intrinsic argument");
754   return invokeGenerator(generator, resultType, scalarArgs);
755 }
756 
757 template <>
758 fir::ExtendedValue
759 IntrinsicLibrary::genElementalCall<IntrinsicLibrary::ExtendedGenerator>(
760     ExtendedGenerator generator, llvm::StringRef name, mlir::Type resultType,
761     llvm::ArrayRef<fir::ExtendedValue> args, bool outline) {
762   for (const fir::ExtendedValue &arg : args)
763     if (!arg.getUnboxed() && !arg.getCharBox())
764       fir::emitFatalError(loc, "nonscalar intrinsic argument");
765   if (outline)
766     return outlineInExtendedWrapper(generator, name, resultType, args);
767   return std::invoke(generator, *this, resultType, args);
768 }
769 
770 static fir::ExtendedValue
771 invokeHandler(IntrinsicLibrary::ElementalGenerator generator,
772               const IntrinsicHandler &handler,
773               llvm::Optional<mlir::Type> resultType,
774               llvm::ArrayRef<fir::ExtendedValue> args, bool outline,
775               IntrinsicLibrary &lib) {
776   assert(resultType && "expect elemental intrinsic to be functions");
777   return lib.genElementalCall(generator, handler.name, *resultType, args,
778                               outline);
779 }
780 
781 static fir::ExtendedValue
782 invokeHandler(IntrinsicLibrary::ExtendedGenerator generator,
783               const IntrinsicHandler &handler,
784               llvm::Optional<mlir::Type> resultType,
785               llvm::ArrayRef<fir::ExtendedValue> args, bool outline,
786               IntrinsicLibrary &lib) {
787   assert(resultType && "expect intrinsic function");
788   if (handler.isElemental)
789     return lib.genElementalCall(generator, handler.name, *resultType, args,
790                                 outline);
791   if (outline)
792     return lib.outlineInExtendedWrapper(generator, handler.name, *resultType,
793                                         args);
794   return std::invoke(generator, lib, *resultType, args);
795 }
796 
797 fir::ExtendedValue
798 IntrinsicLibrary::genIntrinsicCall(llvm::StringRef name,
799                                    llvm::Optional<mlir::Type> resultType,
800                                    llvm::ArrayRef<fir::ExtendedValue> args) {
801   if (const IntrinsicHandler *handler = findIntrinsicHandler(name)) {
802     bool outline = false;
803     return std::visit(
804         [&](auto &generator) -> fir::ExtendedValue {
805           return invokeHandler(generator, *handler, resultType, args, outline,
806                                *this);
807         },
808         handler->generator);
809   }
810 
811   if (!resultType)
812     // Subroutine should have a handler, they are likely missing for now.
813     crashOnMissingIntrinsic(loc, name);
814 
815   // Try the runtime if no special handler was defined for the
816   // intrinsic being called. Maths runtime only has numerical elemental.
817   // No optional arguments are expected at this point, the code will
818   // crash if it gets absent optional.
819 
820   // FIXME: using toValue to get the type won't work with array arguments.
821   llvm::SmallVector<mlir::Value> mlirArgs;
822   for (const fir::ExtendedValue &extendedVal : args) {
823     mlir::Value val = toValue(extendedVal, builder, loc);
824     if (!val)
825       // If an absent optional gets there, most likely its handler has just
826       // not yet been defined.
827       crashOnMissingIntrinsic(loc, name);
828     mlirArgs.emplace_back(val);
829   }
830   mlir::FunctionType soughtFuncType =
831       getFunctionType(*resultType, mlirArgs, builder);
832 
833   IntrinsicLibrary::RuntimeCallGenerator runtimeCallGenerator =
834       getRuntimeCallGenerator(name, soughtFuncType);
835   return genElementalCall(runtimeCallGenerator, name, *resultType, args,
836                           /* outline */ true);
837 }
838 
839 mlir::Value
840 IntrinsicLibrary::invokeGenerator(ElementalGenerator generator,
841                                   mlir::Type resultType,
842                                   llvm::ArrayRef<mlir::Value> args) {
843   return std::invoke(generator, *this, resultType, args);
844 }
845 
846 mlir::Value
847 IntrinsicLibrary::invokeGenerator(RuntimeCallGenerator generator,
848                                   mlir::Type resultType,
849                                   llvm::ArrayRef<mlir::Value> args) {
850   return generator(builder, loc, args);
851 }
852 
853 mlir::Value
854 IntrinsicLibrary::invokeGenerator(ExtendedGenerator generator,
855                                   mlir::Type resultType,
856                                   llvm::ArrayRef<mlir::Value> args) {
857   llvm::SmallVector<fir::ExtendedValue> extendedArgs;
858   for (mlir::Value arg : args)
859     extendedArgs.emplace_back(toExtendedValue(arg, builder, loc));
860   auto extendedResult = std::invoke(generator, *this, resultType, extendedArgs);
861   return toValue(extendedResult, builder, loc);
862 }
863 
864 template <typename GeneratorType>
865 mlir::FuncOp IntrinsicLibrary::getWrapper(GeneratorType generator,
866                                           llvm::StringRef name,
867                                           mlir::FunctionType funcType,
868                                           bool loadRefArguments) {
869   std::string wrapperName = fir::mangleIntrinsicProcedure(name, funcType);
870   mlir::FuncOp function = builder.getNamedFunction(wrapperName);
871   if (!function) {
872     // First time this wrapper is needed, build it.
873     function = builder.createFunction(loc, wrapperName, funcType);
874     function->setAttr("fir.intrinsic", builder.getUnitAttr());
875     auto internalLinkage = mlir::LLVM::linkage::Linkage::Internal;
876     auto linkage =
877         mlir::LLVM::LinkageAttr::get(builder.getContext(), internalLinkage);
878     function->setAttr("llvm.linkage", linkage);
879     function.addEntryBlock();
880 
881     // Create local context to emit code into the newly created function
882     // This new function is not linked to a source file location, only
883     // its calls will be.
884     auto localBuilder =
885         std::make_unique<fir::FirOpBuilder>(function, builder.getKindMap());
886     localBuilder->setInsertionPointToStart(&function.front());
887     // Location of code inside wrapper of the wrapper is independent from
888     // the location of the intrinsic call.
889     mlir::Location localLoc = localBuilder->getUnknownLoc();
890     llvm::SmallVector<mlir::Value> localArguments;
891     for (mlir::BlockArgument bArg : function.front().getArguments()) {
892       auto refType = bArg.getType().dyn_cast<fir::ReferenceType>();
893       if (loadRefArguments && refType) {
894         auto loaded = localBuilder->create<fir::LoadOp>(localLoc, bArg);
895         localArguments.push_back(loaded);
896       } else {
897         localArguments.push_back(bArg);
898       }
899     }
900 
901     IntrinsicLibrary localLib{*localBuilder, localLoc};
902 
903     assert(funcType.getNumResults() == 1 &&
904            "expect one result for intrinsic function wrapper type");
905     mlir::Type resultType = funcType.getResult(0);
906     auto result =
907         localLib.invokeGenerator(generator, resultType, localArguments);
908     localBuilder->create<mlir::func::ReturnOp>(localLoc, result);
909   } else {
910     // Wrapper was already built, ensure it has the sought type
911     assert(function.getType() == funcType &&
912            "conflict between intrinsic wrapper types");
913   }
914   return function;
915 }
916 
917 /// Helpers to detect absent optional (not yet supported in outlining).
918 bool static hasAbsentOptional(llvm::ArrayRef<fir::ExtendedValue> args) {
919   for (const fir::ExtendedValue &arg : args)
920     if (!fir::getBase(arg))
921       return true;
922   return false;
923 }
924 
925 template <typename GeneratorType>
926 fir::ExtendedValue IntrinsicLibrary::outlineInExtendedWrapper(
927     GeneratorType generator, llvm::StringRef name,
928     llvm::Optional<mlir::Type> resultType,
929     llvm::ArrayRef<fir::ExtendedValue> args) {
930   if (hasAbsentOptional(args))
931     TODO(loc, "cannot outline call to intrinsic " + llvm::Twine(name) +
932                   " with absent optional argument");
933   llvm::SmallVector<mlir::Value> mlirArgs;
934   for (const auto &extendedVal : args)
935     mlirArgs.emplace_back(toValue(extendedVal, builder, loc));
936   mlir::FunctionType funcType = getFunctionType(resultType, mlirArgs, builder);
937   mlir::FuncOp wrapper = getWrapper(generator, name, funcType);
938   auto call = builder.create<fir::CallOp>(loc, wrapper, mlirArgs);
939   if (resultType)
940     return toExtendedValue(call.getResult(0), builder, loc);
941   // Subroutine calls
942   return mlir::Value{};
943 }
944 
945 IntrinsicLibrary::RuntimeCallGenerator
946 IntrinsicLibrary::getRuntimeCallGenerator(llvm::StringRef name,
947                                           mlir::FunctionType soughtFuncType) {
948   mlir::FuncOp funcOp = getRuntimeFunction(loc, builder, name, soughtFuncType);
949   if (!funcOp) {
950     std::string buffer("not yet implemented: missing intrinsic lowering: ");
951     llvm::raw_string_ostream sstream(buffer);
952     sstream << name << "\nrequested type was: " << soughtFuncType << '\n';
953     fir::emitFatalError(loc, buffer);
954   }
955 
956   mlir::FunctionType actualFuncType = funcOp.getType();
957   assert(actualFuncType.getNumResults() == soughtFuncType.getNumResults() &&
958          actualFuncType.getNumInputs() == soughtFuncType.getNumInputs() &&
959          actualFuncType.getNumResults() == 1 && "Bad intrinsic match");
960 
961   return [funcOp, actualFuncType,
962           soughtFuncType](fir::FirOpBuilder &builder, mlir::Location loc,
963                           llvm::ArrayRef<mlir::Value> args) {
964     llvm::SmallVector<mlir::Value> convertedArguments;
965     for (auto [fst, snd] : llvm::zip(actualFuncType.getInputs(), args))
966       convertedArguments.push_back(builder.createConvert(loc, fst, snd));
967     auto call = builder.create<fir::CallOp>(loc, funcOp, convertedArguments);
968     mlir::Type soughtType = soughtFuncType.getResult(0);
969     return builder.createConvert(loc, soughtType, call.getResult(0));
970   };
971 }
972 
973 void IntrinsicLibrary::addCleanUpForTemp(mlir::Location loc, mlir::Value temp) {
974   assert(stmtCtx);
975   fir::FirOpBuilder *bldr = &builder;
976   stmtCtx->attachCleanup([=]() { bldr->create<fir::FreeMemOp>(loc, temp); });
977 }
978 
979 fir::ExtendedValue
980 IntrinsicLibrary::readAndAddCleanUp(fir::MutableBoxValue resultMutableBox,
981                                     mlir::Type resultType,
982                                     llvm::StringRef intrinsicName) {
983   fir::ExtendedValue res =
984       fir::factory::genMutableBoxRead(builder, loc, resultMutableBox);
985   return res.match(
986       [&](const fir::ArrayBoxValue &box) -> fir::ExtendedValue {
987         // Add cleanup code
988         addCleanUpForTemp(loc, box.getAddr());
989         return box;
990       },
991       [&](const fir::BoxValue &box) -> fir::ExtendedValue {
992         // Add cleanup code
993         auto addr =
994             builder.create<fir::BoxAddrOp>(loc, box.getMemTy(), box.getAddr());
995         addCleanUpForTemp(loc, addr);
996         return box;
997       },
998       [&](const fir::CharArrayBoxValue &box) -> fir::ExtendedValue {
999         // Add cleanup code
1000         addCleanUpForTemp(loc, box.getAddr());
1001         return box;
1002       },
1003       [&](const mlir::Value &tempAddr) -> fir::ExtendedValue {
1004         // Add cleanup code
1005         addCleanUpForTemp(loc, tempAddr);
1006         return builder.create<fir::LoadOp>(loc, resultType, tempAddr);
1007       },
1008       [&](const fir::CharBoxValue &box) -> fir::ExtendedValue {
1009         // Add cleanup code
1010         addCleanUpForTemp(loc, box.getAddr());
1011         return box;
1012       },
1013       [&](const auto &) -> fir::ExtendedValue {
1014         fir::emitFatalError(loc, "unexpected result for " + intrinsicName);
1015       });
1016 }
1017 
1018 //===----------------------------------------------------------------------===//
1019 // Code generators for the intrinsic
1020 //===----------------------------------------------------------------------===//
1021 
1022 mlir::Value IntrinsicLibrary::genRuntimeCall(llvm::StringRef name,
1023                                              mlir::Type resultType,
1024                                              llvm::ArrayRef<mlir::Value> args) {
1025   mlir::FunctionType soughtFuncType =
1026       getFunctionType(resultType, args, builder);
1027   return getRuntimeCallGenerator(name, soughtFuncType)(builder, loc, args);
1028 }
1029 
1030 // ABS
1031 mlir::Value IntrinsicLibrary::genAbs(mlir::Type resultType,
1032                                      llvm::ArrayRef<mlir::Value> args) {
1033   assert(args.size() == 1);
1034   mlir::Value arg = args[0];
1035   mlir::Type type = arg.getType();
1036   if (fir::isa_real(type)) {
1037     // Runtime call to fp abs. An alternative would be to use mlir
1038     // math::AbsFOp but it does not support all fir floating point types.
1039     return genRuntimeCall("abs", resultType, args);
1040   }
1041   if (auto intType = type.dyn_cast<mlir::IntegerType>()) {
1042     // At the time of this implementation there is no abs op in mlir.
1043     // So, implement abs here without branching.
1044     mlir::Value shift =
1045         builder.createIntegerConstant(loc, intType, intType.getWidth() - 1);
1046     auto mask = builder.create<mlir::arith::ShRSIOp>(loc, arg, shift);
1047     auto xored = builder.create<mlir::arith::XOrIOp>(loc, arg, mask);
1048     return builder.create<mlir::arith::SubIOp>(loc, xored, mask);
1049   }
1050   if (fir::isa_complex(type)) {
1051     // Use HYPOT to fulfill the no underflow/overflow requirement.
1052     auto parts = fir::factory::Complex{builder, loc}.extractParts(arg);
1053     llvm::SmallVector<mlir::Value> args = {parts.first, parts.second};
1054     return genRuntimeCall("hypot", resultType, args);
1055   }
1056   llvm_unreachable("unexpected type in ABS argument");
1057 }
1058 
1059 // ASSOCIATED
1060 fir::ExtendedValue
1061 IntrinsicLibrary::genAssociated(mlir::Type resultType,
1062                                 llvm::ArrayRef<fir::ExtendedValue> args) {
1063   assert(args.size() == 2);
1064   auto *pointer =
1065       args[0].match([&](const fir::MutableBoxValue &x) { return &x; },
1066                     [&](const auto &) -> const fir::MutableBoxValue * {
1067                       fir::emitFatalError(loc, "pointer not a MutableBoxValue");
1068                     });
1069   const fir::ExtendedValue &target = args[1];
1070   if (isAbsent(target))
1071     return fir::factory::genIsAllocatedOrAssociatedTest(builder, loc, *pointer);
1072 
1073   mlir::Value targetBox = builder.createBox(loc, target);
1074   if (fir::valueHasFirAttribute(fir::getBase(target),
1075                                 fir::getOptionalAttrName())) {
1076     // Subtle: contrary to other intrinsic optional arguments, disassociated
1077     // POINTER and unallocated ALLOCATABLE actual argument are not considered
1078     // absent here. This is because ASSOCIATED has special requirements for
1079     // TARGET actual arguments that are POINTERs. There is no precise
1080     // requirements for ALLOCATABLEs, but all existing Fortran compilers treat
1081     // them similarly to POINTERs. That is: unallocated TARGETs cause ASSOCIATED
1082     // to rerun false.  The runtime deals with the disassociated/unallocated
1083     // case. Simply ensures that TARGET that are OPTIONAL get conditionally
1084     // emboxed here to convey the optional aspect to the runtime.
1085     auto isPresent = builder.create<fir::IsPresentOp>(loc, builder.getI1Type(),
1086                                                       fir::getBase(target));
1087     auto absentBox = builder.create<fir::AbsentOp>(loc, targetBox.getType());
1088     targetBox = builder.create<mlir::arith::SelectOp>(loc, isPresent, targetBox,
1089                                                       absentBox);
1090   }
1091   mlir::Value pointerBoxRef =
1092       fir::factory::getMutableIRBox(builder, loc, *pointer);
1093   auto pointerBox = builder.create<fir::LoadOp>(loc, pointerBoxRef);
1094   return Fortran::lower::genAssociated(builder, loc, pointerBox, targetBox);
1095 }
1096 
1097 // CHAR
1098 fir::ExtendedValue
1099 IntrinsicLibrary::genChar(mlir::Type type,
1100                           llvm::ArrayRef<fir::ExtendedValue> args) {
1101   // Optional KIND argument.
1102   assert(args.size() >= 1);
1103   const mlir::Value *arg = args[0].getUnboxed();
1104   // expect argument to be a scalar integer
1105   if (!arg)
1106     mlir::emitError(loc, "CHAR intrinsic argument not unboxed");
1107   fir::factory::CharacterExprHelper helper{builder, loc};
1108   fir::CharacterType::KindTy kind = helper.getCharacterType(type).getFKind();
1109   mlir::Value cast = helper.createSingletonFromCode(*arg, kind);
1110   mlir::Value len =
1111       builder.createIntegerConstant(loc, builder.getCharacterLengthType(), 1);
1112   return fir::CharBoxValue{cast, len};
1113 }
1114 
1115 // IAND
1116 mlir::Value IntrinsicLibrary::genIand(mlir::Type resultType,
1117                                       llvm::ArrayRef<mlir::Value> args) {
1118   assert(args.size() == 2);
1119   return builder.create<mlir::arith::AndIOp>(loc, args[0], args[1]);
1120 }
1121 
1122 // Compare two FIR values and return boolean result as i1.
1123 template <Extremum extremum, ExtremumBehavior behavior>
1124 static mlir::Value createExtremumCompare(mlir::Location loc,
1125                                          fir::FirOpBuilder &builder,
1126                                          mlir::Value left, mlir::Value right) {
1127   static constexpr mlir::arith::CmpIPredicate integerPredicate =
1128       extremum == Extremum::Max ? mlir::arith::CmpIPredicate::sgt
1129                                 : mlir::arith::CmpIPredicate::slt;
1130   static constexpr mlir::arith::CmpFPredicate orderedCmp =
1131       extremum == Extremum::Max ? mlir::arith::CmpFPredicate::OGT
1132                                 : mlir::arith::CmpFPredicate::OLT;
1133   mlir::Type type = left.getType();
1134   mlir::Value result;
1135   if (fir::isa_real(type)) {
1136     // Note: the signaling/quit aspect of the result required by IEEE
1137     // cannot currently be obtained with LLVM without ad-hoc runtime.
1138     if constexpr (behavior == ExtremumBehavior::IeeeMinMaximumNumber) {
1139       // Return the number if one of the inputs is NaN and the other is
1140       // a number.
1141       auto leftIsResult =
1142           builder.create<mlir::arith::CmpFOp>(loc, orderedCmp, left, right);
1143       auto rightIsNan = builder.create<mlir::arith::CmpFOp>(
1144           loc, mlir::arith::CmpFPredicate::UNE, right, right);
1145       result =
1146           builder.create<mlir::arith::OrIOp>(loc, leftIsResult, rightIsNan);
1147     } else if constexpr (behavior == ExtremumBehavior::IeeeMinMaximum) {
1148       // Always return NaNs if one the input is NaNs
1149       auto leftIsResult =
1150           builder.create<mlir::arith::CmpFOp>(loc, orderedCmp, left, right);
1151       auto leftIsNan = builder.create<mlir::arith::CmpFOp>(
1152           loc, mlir::arith::CmpFPredicate::UNE, left, left);
1153       result = builder.create<mlir::arith::OrIOp>(loc, leftIsResult, leftIsNan);
1154     } else if constexpr (behavior == ExtremumBehavior::MinMaxss) {
1155       // If the left is a NaN, return the right whatever it is.
1156       result =
1157           builder.create<mlir::arith::CmpFOp>(loc, orderedCmp, left, right);
1158     } else if constexpr (behavior == ExtremumBehavior::PgfortranLlvm) {
1159       // If one of the operand is a NaN, return left whatever it is.
1160       static constexpr auto unorderedCmp =
1161           extremum == Extremum::Max ? mlir::arith::CmpFPredicate::UGT
1162                                     : mlir::arith::CmpFPredicate::ULT;
1163       result =
1164           builder.create<mlir::arith::CmpFOp>(loc, unorderedCmp, left, right);
1165     } else {
1166       // TODO: ieeeMinNum/ieeeMaxNum
1167       static_assert(behavior == ExtremumBehavior::IeeeMinMaxNum,
1168                     "ieeeMinNum/ieeeMaxNum behavior not implemented");
1169     }
1170   } else if (fir::isa_integer(type)) {
1171     result =
1172         builder.create<mlir::arith::CmpIOp>(loc, integerPredicate, left, right);
1173   } else if (fir::isa_char(type)) {
1174     // TODO: ! character min and max is tricky because the result
1175     // length is the length of the longest argument!
1176     // So we may need a temp.
1177     TODO(loc, "CHARACTER min and max");
1178   }
1179   assert(result && "result must be defined");
1180   return result;
1181 }
1182 
1183 // MIN and MAX
1184 template <Extremum extremum, ExtremumBehavior behavior>
1185 mlir::Value IntrinsicLibrary::genExtremum(mlir::Type,
1186                                           llvm::ArrayRef<mlir::Value> args) {
1187   assert(args.size() >= 1);
1188   mlir::Value result = args[0];
1189   for (auto arg : args.drop_front()) {
1190     mlir::Value mask =
1191         createExtremumCompare<extremum, behavior>(loc, builder, result, arg);
1192     result = builder.create<mlir::arith::SelectOp>(loc, mask, result, arg);
1193   }
1194   return result;
1195 }
1196 
1197 // SUM
1198 fir::ExtendedValue
1199 IntrinsicLibrary::genSum(mlir::Type resultType,
1200                          llvm::ArrayRef<fir::ExtendedValue> args) {
1201   return genProdOrSum(fir::runtime::genSum, fir::runtime::genSumDim, resultType,
1202                       builder, loc, stmtCtx, "unexpected result for Sum", args);
1203 }
1204 
1205 // SIZE
1206 fir::ExtendedValue
1207 IntrinsicLibrary::genSize(mlir::Type resultType,
1208                           llvm::ArrayRef<fir::ExtendedValue> args) {
1209   // Note that the value of the KIND argument is already reflected in the
1210   // resultType
1211   assert(args.size() == 3);
1212   if (const auto *boxValue = args[0].getBoxOf<fir::BoxValue>())
1213     if (boxValue->hasAssumedRank())
1214       TODO(loc, "SIZE intrinsic with assumed rank argument");
1215 
1216   // Get the ARRAY argument
1217   mlir::Value array = builder.createBox(loc, args[0]);
1218 
1219   // The front-end rewrites SIZE without the DIM argument to
1220   // an array of SIZE with DIM in most cases, but it may not be
1221   // possible in some cases like when in SIZE(function_call()).
1222   if (isAbsent(args, 1))
1223     return builder.createConvert(loc, resultType,
1224                                  fir::runtime::genSize(builder, loc, array));
1225 
1226   // Get the DIM argument.
1227   mlir::Value dim = fir::getBase(args[1]);
1228   if (!fir::isa_ref_type(dim.getType()))
1229     return builder.createConvert(
1230         loc, resultType, fir::runtime::genSizeDim(builder, loc, array, dim));
1231 
1232   mlir::Value isDynamicallyAbsent = builder.genIsNull(loc, dim);
1233   return builder
1234       .genIfOp(loc, {resultType}, isDynamicallyAbsent,
1235                /*withElseRegion=*/true)
1236       .genThen([&]() {
1237         mlir::Value size = builder.createConvert(
1238             loc, resultType, fir::runtime::genSize(builder, loc, array));
1239         builder.create<fir::ResultOp>(loc, size);
1240       })
1241       .genElse([&]() {
1242         mlir::Value dimValue = builder.create<fir::LoadOp>(loc, dim);
1243         mlir::Value size = builder.createConvert(
1244             loc, resultType,
1245             fir::runtime::genSizeDim(builder, loc, array, dimValue));
1246         builder.create<fir::ResultOp>(loc, size);
1247       })
1248       .getResults()[0];
1249 }
1250 
1251 // LBOUND
1252 fir::ExtendedValue
1253 IntrinsicLibrary::genLbound(mlir::Type resultType,
1254                             llvm::ArrayRef<fir::ExtendedValue> args) {
1255   // Calls to LBOUND that don't have the DIM argument, or for which
1256   // the DIM is a compile time constant, are folded to descriptor inquiries by
1257   // semantics.  This function covers the situations where a call to the
1258   // runtime is required.
1259   assert(args.size() == 3);
1260   assert(!isAbsent(args[1]));
1261   if (const auto *boxValue = args[0].getBoxOf<fir::BoxValue>())
1262     if (boxValue->hasAssumedRank())
1263       TODO(loc, "LBOUND intrinsic with assumed rank argument");
1264 
1265   const fir::ExtendedValue &array = args[0];
1266   mlir::Value box = array.match(
1267       [&](const fir::BoxValue &boxValue) -> mlir::Value {
1268         // This entity is mapped to a fir.box that may not contain the local
1269         // lower bound information if it is a dummy. Rebox it with the local
1270         // shape information.
1271         mlir::Value localShape = builder.createShape(loc, array);
1272         mlir::Value oldBox = boxValue.getAddr();
1273         return builder.create<fir::ReboxOp>(
1274             loc, oldBox.getType(), oldBox, localShape, /*slice=*/mlir::Value{});
1275       },
1276       [&](const auto &) -> mlir::Value {
1277         // This a pointer/allocatable, or an entity not yet tracked with a
1278         // fir.box. For pointer/allocatable, createBox will forward the
1279         // descriptor that contains the correct lower bound information. For
1280         // other entities, a new fir.box will be made with the local lower
1281         // bounds.
1282         return builder.createBox(loc, array);
1283       });
1284 
1285   mlir::Value dim = fir::getBase(args[1]);
1286   return builder.createConvert(
1287       loc, resultType,
1288       fir::runtime::genLboundDim(builder, loc, fir::getBase(box), dim));
1289 }
1290 
1291 // UBOUND
1292 fir::ExtendedValue
1293 IntrinsicLibrary::genUbound(mlir::Type resultType,
1294                             llvm::ArrayRef<fir::ExtendedValue> args) {
1295   assert(args.size() == 3 || args.size() == 2);
1296   if (args.size() == 3) {
1297     // Handle calls to UBOUND with the DIM argument, which return a scalar
1298     mlir::Value extent = fir::getBase(genSize(resultType, args));
1299     mlir::Value lbound = fir::getBase(genLbound(resultType, args));
1300 
1301     mlir::Value one = builder.createIntegerConstant(loc, resultType, 1);
1302     mlir::Value ubound = builder.create<mlir::arith::SubIOp>(loc, lbound, one);
1303     return builder.create<mlir::arith::AddIOp>(loc, ubound, extent);
1304   } else {
1305     // Handle calls to UBOUND without the DIM argument, which return an array
1306     mlir::Value kind = isAbsent(args[1])
1307                            ? builder.createIntegerConstant(
1308                                  loc, builder.getIndexType(),
1309                                  builder.getKindMap().defaultIntegerKind())
1310                            : fir::getBase(args[1]);
1311 
1312     // Create mutable fir.box to be passed to the runtime for the result.
1313     mlir::Type type = builder.getVarLenSeqTy(resultType, /*rank=*/1);
1314     fir::MutableBoxValue resultMutableBox =
1315         fir::factory::createTempMutableBox(builder, loc, type);
1316     mlir::Value resultIrBox =
1317         fir::factory::getMutableIRBox(builder, loc, resultMutableBox);
1318 
1319     fir::runtime::genUbound(builder, loc, resultIrBox, fir::getBase(args[0]),
1320                             kind);
1321 
1322     return readAndAddCleanUp(resultMutableBox, resultType, "UBOUND");
1323   }
1324   return mlir::Value();
1325 }
1326 
1327 //===----------------------------------------------------------------------===//
1328 // Argument lowering rules interface
1329 //===----------------------------------------------------------------------===//
1330 
1331 const Fortran::lower::IntrinsicArgumentLoweringRules *
1332 Fortran::lower::getIntrinsicArgumentLowering(llvm::StringRef intrinsicName) {
1333   if (const IntrinsicHandler *handler = findIntrinsicHandler(intrinsicName))
1334     if (!handler->argLoweringRules.hasDefaultRules())
1335       return &handler->argLoweringRules;
1336   return nullptr;
1337 }
1338 
1339 /// Return how argument \p argName should be lowered given the rules for the
1340 /// intrinsic function.
1341 Fortran::lower::ArgLoweringRule Fortran::lower::lowerIntrinsicArgumentAs(
1342     mlir::Location loc, const IntrinsicArgumentLoweringRules &rules,
1343     llvm::StringRef argName) {
1344   for (const IntrinsicDummyArgument &arg : rules.args) {
1345     if (arg.name && arg.name == argName)
1346       return {arg.lowerAs, arg.handleDynamicOptional};
1347   }
1348   fir::emitFatalError(
1349       loc, "internal: unknown intrinsic argument name in lowering '" + argName +
1350                "'");
1351 }
1352 
1353 //===----------------------------------------------------------------------===//
1354 // Public intrinsic call helpers
1355 //===----------------------------------------------------------------------===//
1356 
1357 fir::ExtendedValue
1358 Fortran::lower::genIntrinsicCall(fir::FirOpBuilder &builder, mlir::Location loc,
1359                                  llvm::StringRef name,
1360                                  llvm::Optional<mlir::Type> resultType,
1361                                  llvm::ArrayRef<fir::ExtendedValue> args,
1362                                  Fortran::lower::StatementContext &stmtCtx) {
1363   return IntrinsicLibrary{builder, loc, &stmtCtx}.genIntrinsicCall(
1364       name, resultType, args);
1365 }
1366 
1367 mlir::Value Fortran::lower::genMax(fir::FirOpBuilder &builder,
1368                                    mlir::Location loc,
1369                                    llvm::ArrayRef<mlir::Value> args) {
1370   assert(args.size() > 0 && "max requires at least one argument");
1371   return IntrinsicLibrary{builder, loc}
1372       .genExtremum<Extremum::Max, ExtremumBehavior::MinMaxss>(args[0].getType(),
1373                                                               args);
1374 }
1375 
1376 mlir::Value Fortran::lower::genPow(fir::FirOpBuilder &builder,
1377                                    mlir::Location loc, mlir::Type type,
1378                                    mlir::Value x, mlir::Value y) {
1379   return IntrinsicLibrary{builder, loc}.genRuntimeCall("pow", type, {x, y});
1380 }
1381