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