[FLANG][NFCI]De-duplicate code in SimplifyIntrinsics
[platform/upstream/llvm.git] / flang / lib / Optimizer / Transforms / SimplifyIntrinsics.cpp
1 //===- SimplifyIntrinsics.cpp -- replace intrinsics with simpler form -----===//
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 //===----------------------------------------------------------------------===//
10 /// \file
11 /// This pass looks for suitable calls to runtime library for intrinsics that
12 /// can be simplified/specialized and replaces with a specialized function.
13 ///
14 /// For example, SUM(arr) can be specialized as a simple function with one loop,
15 /// compared to the three arguments (plus file & line info) that the runtime
16 /// call has - when the argument is a 1D-array (multiple loops may be needed
17 //  for higher dimension arrays, of course)
18 ///
19 /// The general idea is that besides making the call simpler, it can also be
20 /// inlined by other passes that run after this pass, which further improves
21 /// performance, particularly when the work done in the function is trivial
22 /// and small in size.
23 //===----------------------------------------------------------------------===//
24
25 #include "flang/Optimizer/Builder/BoxValue.h"
26 #include "flang/Optimizer/Builder/FIRBuilder.h"
27 #include "flang/Optimizer/Builder/Todo.h"
28 #include "flang/Optimizer/Dialect/FIROps.h"
29 #include "flang/Optimizer/Dialect/FIRType.h"
30 #include "flang/Optimizer/Support/FIRContext.h"
31 #include "flang/Optimizer/Transforms/Passes.h"
32 #include "mlir/Dialect/LLVMIR/LLVMDialect.h"
33 #include "mlir/IR/Matchers.h"
34 #include "mlir/IR/TypeUtilities.h"
35 #include "mlir/Pass/Pass.h"
36 #include "mlir/Transforms/DialectConversion.h"
37 #include "mlir/Transforms/GreedyPatternRewriteDriver.h"
38 #include "mlir/Transforms/RegionUtils.h"
39 #include "llvm/ADT/Optional.h"
40 #include "llvm/Support/Debug.h"
41 #include "llvm/Support/raw_ostream.h"
42
43 namespace fir {
44 #define GEN_PASS_DEF_SIMPLIFYINTRINSICS
45 #include "flang/Optimizer/Transforms/Passes.h.inc"
46 } // namespace fir
47
48 #define DEBUG_TYPE "flang-simplify-intrinsics"
49
50 namespace {
51
52 class SimplifyIntrinsicsPass
53     : public fir::impl::SimplifyIntrinsicsBase<SimplifyIntrinsicsPass> {
54   using FunctionTypeGeneratorTy =
55       llvm::function_ref<mlir::FunctionType(fir::FirOpBuilder &)>;
56   using FunctionBodyGeneratorTy =
57       llvm::function_ref<void(fir::FirOpBuilder &, mlir::func::FuncOp &)>;
58   using GenReductionBodyTy = llvm::function_ref<void(
59       fir::FirOpBuilder &builder, mlir::func::FuncOp &funcOp)>;
60
61 public:
62   /// Generate a new function implementing a simplified version
63   /// of a Fortran runtime function defined by \p basename name.
64   /// \p typeGenerator is a callback that generates the new function's type.
65   /// \p bodyGenerator is a callback that generates the new function's body.
66   /// The new function is created in the \p builder's Module.
67   mlir::func::FuncOp getOrCreateFunction(fir::FirOpBuilder &builder,
68                                          const mlir::StringRef &basename,
69                                          FunctionTypeGeneratorTy typeGenerator,
70                                          FunctionBodyGeneratorTy bodyGenerator);
71   void runOnOperation() override;
72   void getDependentDialects(mlir::DialectRegistry &registry) const override;
73
74 private:
75   /// Helper function to replace a reduction type of call with its
76   /// simplified form. The actual function is generated using a callback
77   /// function.
78   /// \p call is the call to be replaced
79   /// \p kindMap is used to create FIROpBuilder
80   /// \p genBodyFunc is the callback that builds the replacement function
81   void simplifyReduction(fir::CallOp call, const fir::KindMapping &kindMap,
82                          GenReductionBodyTy genBodyFunc);
83 };
84
85 } // namespace
86
87 /// Generate function type for the simplified version of FortranASum and
88 /// similar functions with a fir.box<none> type returning \p elementType.
89 static mlir::FunctionType genNoneBoxType(fir::FirOpBuilder &builder,
90                                          const mlir::Type &elementType) {
91   mlir::Type boxType = fir::BoxType::get(builder.getNoneType());
92   return mlir::FunctionType::get(builder.getContext(), {boxType},
93                                  {elementType});
94 }
95
96 using BodyOpGeneratorTy = llvm::function_ref<mlir::Value(
97     fir::FirOpBuilder &, mlir::Location, const mlir::Type &, mlir::Value,
98     mlir::Value)>;
99 using InitValGeneratorTy = llvm::function_ref<mlir::Value(
100     fir::FirOpBuilder &, mlir::Location, const mlir::Type &)>;
101
102 /// Generate the reduction loop into \p funcOp.
103 ///
104 /// \p initVal is a function, called to get the initial value for
105 ///    the reduction value
106 /// \p genBody is called to fill in the actual reduciton operation
107 ///    for example add for SUM, MAX for MAXVAL, etc.
108 static void genReductionLoop(fir::FirOpBuilder &builder,
109                              mlir::func::FuncOp &funcOp,
110                              InitValGeneratorTy initVal,
111                              BodyOpGeneratorTy genBody) {
112   auto loc = mlir::UnknownLoc::get(builder.getContext());
113   mlir::Type elementType = funcOp.getResultTypes()[0];
114   builder.setInsertionPointToEnd(funcOp.addEntryBlock());
115
116   mlir::IndexType idxTy = builder.getIndexType();
117
118   mlir::Block::BlockArgListType args = funcOp.front().getArguments();
119   mlir::Value arg = args[0];
120
121   mlir::Value zeroIdx = builder.createIntegerConstant(loc, idxTy, 0);
122
123   fir::SequenceType::Shape flatShape = {fir::SequenceType::getUnknownExtent()};
124   mlir::Type arrTy = fir::SequenceType::get(flatShape, elementType);
125   mlir::Type boxArrTy = fir::BoxType::get(arrTy);
126   mlir::Value array = builder.create<fir::ConvertOp>(loc, boxArrTy, arg);
127   auto dims =
128       builder.create<fir::BoxDimsOp>(loc, idxTy, idxTy, idxTy, array, zeroIdx);
129   mlir::Value len = dims.getResult(1);
130   mlir::Value one = builder.createIntegerConstant(loc, idxTy, 1);
131   mlir::Value step = one;
132
133   // We use C indexing here, so len-1 as loopcount
134   mlir::Value loopCount = builder.create<mlir::arith::SubIOp>(loc, len, one);
135   mlir::Value init = initVal(builder, loc, elementType);
136   auto loop = builder.create<fir::DoLoopOp>(loc, zeroIdx, loopCount, step,
137                                             /*unordered=*/false,
138                                             /*finalCountValue=*/false, init);
139   mlir::Value reductionVal = loop.getRegionIterArgs()[0];
140
141   // Begin loop code
142   mlir::OpBuilder::InsertPoint loopEndPt = builder.saveInsertionPoint();
143   builder.setInsertionPointToStart(loop.getBody());
144
145   mlir::Type eleRefTy = builder.getRefType(elementType);
146   mlir::Value index = loop.getInductionVar();
147   mlir::Value addr =
148       builder.create<fir::CoordinateOp>(loc, eleRefTy, array, index);
149   mlir::Value elem = builder.create<fir::LoadOp>(loc, addr);
150
151   reductionVal = genBody(builder, loc, elementType, elem, reductionVal);
152
153   builder.create<fir::ResultOp>(loc, reductionVal);
154   // End of loop.
155   builder.restoreInsertionPoint(loopEndPt);
156
157   mlir::Value resultVal = loop.getResult(0);
158   builder.create<mlir::func::ReturnOp>(loc, resultVal);
159 }
160
161 /// Generate function body of the simplified version of FortranASum
162 /// with signature provided by \p funcOp. The caller is responsible
163 /// for saving/restoring the original insertion point of \p builder.
164 /// \p funcOp is expected to be empty on entry to this function.
165 static void genFortranASumBody(fir::FirOpBuilder &builder,
166                                mlir::func::FuncOp &funcOp) {
167   // function FortranASum<T>_simplified(arr)
168   //   T, dimension(:) :: arr
169   //   T sum = 0
170   //   integer iter
171   //   do iter = 0, extent(arr)
172   //     sum = sum + arr[iter]
173   //   end do
174   //   FortranASum<T>_simplified = sum
175   // end function FortranASum<T>_simplified
176   auto zero = [](fir::FirOpBuilder builder, mlir::Location loc,
177                  mlir::Type elementType) {
178     return elementType.isa<mlir::FloatType>()
179                ? builder.createRealConstant(loc, elementType,
180                                             llvm::APFloat(0.0))
181                : builder.createIntegerConstant(loc, elementType, 0);
182   };
183
184   auto genBodyOp = [](fir::FirOpBuilder builder, mlir::Location loc,
185                       mlir::Type elementType, mlir::Value elem1,
186                       mlir::Value elem2) -> mlir::Value {
187     if (elementType.isa<mlir::FloatType>())
188       return builder.create<mlir::arith::AddFOp>(loc, elem1, elem2);
189     if (elementType.isa<mlir::IntegerType>())
190       return builder.create<mlir::arith::AddIOp>(loc, elem1, elem2);
191
192     llvm_unreachable("unsupported type");
193     return {};
194   };
195
196   genReductionLoop(builder, funcOp, zero, genBodyOp);
197 }
198
199 static void genFortranAMaxvalBody(fir::FirOpBuilder &builder,
200                                   mlir::func::FuncOp &funcOp) {
201   auto init = [](fir::FirOpBuilder builder, mlir::Location loc,
202                  mlir::Type elementType) {
203     if (auto ty = elementType.dyn_cast<mlir::FloatType>()) {
204       const llvm::fltSemantics &sem = ty.getFloatSemantics();
205       return builder.createRealConstant(
206           loc, elementType, llvm::APFloat::getLargest(sem, /*Negative=*/true));
207     }
208     unsigned bits = elementType.getIntOrFloatBitWidth();
209     int64_t minInt = llvm::APInt::getSignedMinValue(bits).getSExtValue();
210     return builder.createIntegerConstant(loc, elementType, minInt);
211   };
212
213   auto genBodyOp = [](fir::FirOpBuilder builder, mlir::Location loc,
214                       mlir::Type elementType, mlir::Value elem1,
215                       mlir::Value elem2) -> mlir::Value {
216     if (elementType.isa<mlir::FloatType>())
217       return builder.create<mlir::arith::MaxFOp>(loc, elem1, elem2);
218     if (elementType.isa<mlir::IntegerType>())
219       return builder.create<mlir::arith::MaxSIOp>(loc, elem1, elem2);
220
221     llvm_unreachable("unsupported type");
222     return {};
223   };
224   genReductionLoop(builder, funcOp, init, genBodyOp);
225 }
226
227 /// Generate function type for the simplified version of FortranADotProduct
228 /// operating on the given \p elementType.
229 static mlir::FunctionType genFortranADotType(fir::FirOpBuilder &builder,
230                                              const mlir::Type &elementType) {
231   mlir::Type boxType = fir::BoxType::get(builder.getNoneType());
232   return mlir::FunctionType::get(builder.getContext(), {boxType, boxType},
233                                  {elementType});
234 }
235
236 /// Generate function body of the simplified version of FortranADotProduct
237 /// with signature provided by \p funcOp. The caller is responsible
238 /// for saving/restoring the original insertion point of \p builder.
239 /// \p funcOp is expected to be empty on entry to this function.
240 /// \p arg1ElementTy and \p arg2ElementTy specify elements types
241 /// of the underlying array objects - they are used to generate proper
242 /// element accesses.
243 static void genFortranADotBody(fir::FirOpBuilder &builder,
244                                mlir::func::FuncOp &funcOp,
245                                mlir::Type arg1ElementTy,
246                                mlir::Type arg2ElementTy) {
247   // function FortranADotProduct<T>_simplified(arr1, arr2)
248   //   T, dimension(:) :: arr1, arr2
249   //   T product = 0
250   //   integer iter
251   //   do iter = 0, extent(arr1)
252   //     product = product + arr1[iter] * arr2[iter]
253   //   end do
254   //   FortranADotProduct<T>_simplified = product
255   // end function FortranADotProduct<T>_simplified
256   auto loc = mlir::UnknownLoc::get(builder.getContext());
257   mlir::Type resultElementType = funcOp.getResultTypes()[0];
258   builder.setInsertionPointToEnd(funcOp.addEntryBlock());
259
260   mlir::IndexType idxTy = builder.getIndexType();
261
262   mlir::Value zero =
263       resultElementType.isa<mlir::FloatType>()
264           ? builder.createRealConstant(loc, resultElementType, 0.0)
265           : builder.createIntegerConstant(loc, resultElementType, 0);
266
267   mlir::Block::BlockArgListType args = funcOp.front().getArguments();
268   mlir::Value arg1 = args[0];
269   mlir::Value arg2 = args[1];
270
271   mlir::Value zeroIdx = builder.createIntegerConstant(loc, idxTy, 0);
272
273   fir::SequenceType::Shape flatShape = {fir::SequenceType::getUnknownExtent()};
274   mlir::Type arrTy1 = fir::SequenceType::get(flatShape, arg1ElementTy);
275   mlir::Type boxArrTy1 = fir::BoxType::get(arrTy1);
276   mlir::Value array1 = builder.create<fir::ConvertOp>(loc, boxArrTy1, arg1);
277   mlir::Type arrTy2 = fir::SequenceType::get(flatShape, arg2ElementTy);
278   mlir::Type boxArrTy2 = fir::BoxType::get(arrTy2);
279   mlir::Value array2 = builder.create<fir::ConvertOp>(loc, boxArrTy2, arg2);
280   // This version takes the loop trip count from the first argument.
281   // If the first argument's box has unknown (at compilation time)
282   // extent, then it may be better to take the extent from the second
283   // argument - so that after inlining the loop may be better optimized, e.g.
284   // fully unrolled. This requires generating two versions of the simplified
285   // function and some analysis at the call site to choose which version
286   // is more profitable to call.
287   // Note that we can assume that both arguments have the same extent.
288   auto dims =
289       builder.create<fir::BoxDimsOp>(loc, idxTy, idxTy, idxTy, array1, zeroIdx);
290   mlir::Value len = dims.getResult(1);
291   mlir::Value one = builder.createIntegerConstant(loc, idxTy, 1);
292   mlir::Value step = one;
293
294   // We use C indexing here, so len-1 as loopcount
295   mlir::Value loopCount = builder.create<mlir::arith::SubIOp>(loc, len, one);
296   auto loop = builder.create<fir::DoLoopOp>(loc, zeroIdx, loopCount, step,
297                                             /*unordered=*/false,
298                                             /*finalCountValue=*/false, zero);
299   mlir::Value sumVal = loop.getRegionIterArgs()[0];
300
301   // Begin loop code
302   mlir::OpBuilder::InsertPoint loopEndPt = builder.saveInsertionPoint();
303   builder.setInsertionPointToStart(loop.getBody());
304
305   mlir::Type eleRef1Ty = builder.getRefType(arg1ElementTy);
306   mlir::Value index = loop.getInductionVar();
307   mlir::Value addr1 =
308       builder.create<fir::CoordinateOp>(loc, eleRef1Ty, array1, index);
309   mlir::Value elem1 = builder.create<fir::LoadOp>(loc, addr1);
310   // Convert to the result type.
311   elem1 = builder.create<fir::ConvertOp>(loc, resultElementType, elem1);
312
313   mlir::Type eleRef2Ty = builder.getRefType(arg2ElementTy);
314   mlir::Value addr2 =
315       builder.create<fir::CoordinateOp>(loc, eleRef2Ty, array2, index);
316   mlir::Value elem2 = builder.create<fir::LoadOp>(loc, addr2);
317   // Convert to the result type.
318   elem2 = builder.create<fir::ConvertOp>(loc, resultElementType, elem2);
319
320   if (resultElementType.isa<mlir::FloatType>())
321     sumVal = builder.create<mlir::arith::AddFOp>(
322         loc, builder.create<mlir::arith::MulFOp>(loc, elem1, elem2), sumVal);
323   else if (resultElementType.isa<mlir::IntegerType>())
324     sumVal = builder.create<mlir::arith::AddIOp>(
325         loc, builder.create<mlir::arith::MulIOp>(loc, elem1, elem2), sumVal);
326   else
327     llvm_unreachable("unsupported type");
328
329   builder.create<fir::ResultOp>(loc, sumVal);
330   // End of loop.
331   builder.restoreInsertionPoint(loopEndPt);
332
333   mlir::Value resultVal = loop.getResult(0);
334   builder.create<mlir::func::ReturnOp>(loc, resultVal);
335 }
336
337 mlir::func::FuncOp SimplifyIntrinsicsPass::getOrCreateFunction(
338     fir::FirOpBuilder &builder, const mlir::StringRef &baseName,
339     FunctionTypeGeneratorTy typeGenerator,
340     FunctionBodyGeneratorTy bodyGenerator) {
341   // WARNING: if the function generated here changes its signature
342   //          or behavior (the body code), we should probably embed some
343   //          versioning information into its name, otherwise libraries
344   //          statically linked with older versions of Flang may stop
345   //          working with object files created with newer Flang.
346   //          We can also avoid this by using internal linkage, but
347   //          this may increase the size of final executable/shared library.
348   std::string replacementName = mlir::Twine{baseName, "_simplified"}.str();
349   mlir::ModuleOp module = builder.getModule();
350   // If we already have a function, just return it.
351   mlir::func::FuncOp newFunc =
352       fir::FirOpBuilder::getNamedFunction(module, replacementName);
353   mlir::FunctionType fType = typeGenerator(builder);
354   if (newFunc) {
355     assert(newFunc.getFunctionType() == fType &&
356            "type mismatch for simplified function");
357     return newFunc;
358   }
359
360   // Need to build the function!
361   auto loc = mlir::UnknownLoc::get(builder.getContext());
362   newFunc =
363       fir::FirOpBuilder::createFunction(loc, module, replacementName, fType);
364   auto inlineLinkage = mlir::LLVM::linkage::Linkage::LinkonceODR;
365   auto linkage =
366       mlir::LLVM::LinkageAttr::get(builder.getContext(), inlineLinkage);
367   newFunc->setAttr("llvm.linkage", linkage);
368
369   // Save the position of the original call.
370   mlir::OpBuilder::InsertPoint insertPt = builder.saveInsertionPoint();
371
372   bodyGenerator(builder, newFunc);
373
374   // Now back to where we were adding code earlier...
375   builder.restoreInsertionPoint(insertPt);
376
377   return newFunc;
378 }
379
380 fir::ConvertOp expectConvertOp(mlir::Value val) {
381   if (fir::ConvertOp op =
382           mlir::dyn_cast_or_null<fir::ConvertOp>(val.getDefiningOp()))
383     return op;
384   LLVM_DEBUG(llvm::dbgs() << "Didn't find expected fir::ConvertOp\n");
385   return nullptr;
386 }
387
388 static bool isOperandAbsent(mlir::Value val) {
389   if (auto op = expectConvertOp(val)) {
390     assert(op->getOperands().size() != 0);
391     return mlir::isa_and_nonnull<fir::AbsentOp>(
392         op->getOperand(0).getDefiningOp());
393   }
394   return false;
395 }
396
397 static bool isZero(mlir::Value val) {
398   if (auto op = expectConvertOp(val)) {
399     assert(op->getOperands().size() != 0);
400     if (mlir::Operation *defOp = op->getOperand(0).getDefiningOp())
401       return mlir::matchPattern(defOp, mlir::m_Zero());
402   }
403   return false;
404 }
405
406 static mlir::Value findShape(mlir::Value val) {
407   if (auto op = expectConvertOp(val)) {
408     assert(op->getOperands().size() != 0);
409     if (auto box = mlir::dyn_cast_or_null<fir::EmboxOp>(
410             op->getOperand(0).getDefiningOp()))
411       return box.getShape();
412   }
413   return {};
414 }
415
416 static unsigned getDimCount(mlir::Value val) {
417   if (mlir::Value shapeVal = findShape(val)) {
418     mlir::Type resType = shapeVal.getDefiningOp()->getResultTypes()[0];
419     return fir::getRankOfShapeType(resType);
420   }
421   return 0;
422 }
423
424 /// Given the call operation's box argument \p val, discover
425 /// the element type of the underlying array object.
426 /// \returns the element type or llvm::None if the type cannot
427 /// be reliably found.
428 /// We expect that the argument is a result of fir.convert
429 /// with the destination type of !fir.box<none>.
430 static llvm::Optional<mlir::Type> getArgElementType(mlir::Value val) {
431   mlir::Operation *defOp;
432   do {
433     defOp = val.getDefiningOp();
434     // Analyze only sequences of convert operations.
435     if (!mlir::isa<fir::ConvertOp>(defOp))
436       return llvm::None;
437     val = defOp->getOperand(0);
438     // The convert operation is expected to convert from one
439     // box type to another box type.
440     auto boxType = val.getType().cast<fir::BoxType>();
441     auto elementType = fir::unwrapSeqOrBoxedSeqType(boxType);
442     if (!elementType.isa<mlir::NoneType>())
443       return elementType;
444   } while (true);
445 }
446
447 void SimplifyIntrinsicsPass::simplifyReduction(fir::CallOp call,
448                                                const fir::KindMapping &kindMap,
449                                                GenReductionBodyTy genBodyFunc) {
450   mlir::SymbolRefAttr callee = call.getCalleeAttr();
451   mlir::StringRef funcName = callee.getLeafReference().getValue();
452   mlir::Operation::operand_range args = call.getArgs();
453   // args[1] and args[2] are source filename and line number, ignored.
454   const mlir::Value &dim = args[3];
455   const mlir::Value &mask = args[4];
456   // dim is zero when it is absent, which is an implementation
457   // detail in the runtime library.
458   bool dimAndMaskAbsent = isZero(dim) && isOperandAbsent(mask);
459   unsigned rank = getDimCount(args[0]);
460   if (dimAndMaskAbsent && rank == 1) {
461     mlir::Location loc = call.getLoc();
462     mlir::Type type;
463     fir::FirOpBuilder builder(call, kindMap);
464     if (funcName.endswith("Integer4")) {
465       type = mlir::IntegerType::get(builder.getContext(), 32);
466     } else if (funcName.endswith("Real8")) {
467       type = mlir::FloatType::getF64(builder.getContext());
468     } else {
469       return;
470     }
471     auto typeGenerator = [&type](fir::FirOpBuilder &builder) {
472       return genNoneBoxType(builder, type);
473     };
474     mlir::func::FuncOp newFunc =
475         getOrCreateFunction(builder, funcName, typeGenerator, genBodyFunc);
476     auto newCall =
477         builder.create<fir::CallOp>(loc, newFunc, mlir::ValueRange{args[0]});
478     call->replaceAllUsesWith(newCall.getResults());
479     call->dropAllReferences();
480     call->erase();
481   }
482 }
483
484 void SimplifyIntrinsicsPass::runOnOperation() {
485   LLVM_DEBUG(llvm::dbgs() << "=== Begin " DEBUG_TYPE " ===\n");
486   mlir::ModuleOp module = getOperation();
487   fir::KindMapping kindMap = fir::getKindMapping(module);
488   module.walk([&](mlir::Operation *op) {
489     if (auto call = mlir::dyn_cast<fir::CallOp>(op)) {
490       if (mlir::SymbolRefAttr callee = call.getCalleeAttr()) {
491         mlir::StringRef funcName = callee.getLeafReference().getValue();
492         // Replace call to runtime function for SUM when it has single
493         // argument (no dim or mask argument) for 1D arrays with either
494         // Integer4 or Real8 types. Other forms are ignored.
495         // The new function is added to the module.
496         //
497         // Prototype for runtime call (from sum.cpp):
498         // RTNAME(Sum<T>)(const Descriptor &x, const char *source, int line,
499         //                int dim, const Descriptor *mask)
500         //
501         if (funcName.startswith("_FortranASum")) {
502           simplifyReduction(call, kindMap, genFortranASumBody);
503           return;
504         }
505         if (funcName.startswith("_FortranADotProduct")) {
506           LLVM_DEBUG(llvm::dbgs() << "Handling " << funcName << "\n");
507           LLVM_DEBUG(llvm::dbgs() << "Call operation:\n"; op->dump();
508                      llvm::dbgs() << "\n");
509           mlir::Operation::operand_range args = call.getArgs();
510           const mlir::Value &v1 = args[0];
511           const mlir::Value &v2 = args[1];
512           mlir::Location loc = call.getLoc();
513           fir::FirOpBuilder builder(op, kindMap);
514
515           mlir::Type type = call.getResult(0).getType();
516           if (!type.isa<mlir::FloatType>() && !type.isa<mlir::IntegerType>())
517             return;
518
519           // Try to find the element types of the boxed arguments.
520           auto arg1Type = getArgElementType(v1);
521           auto arg2Type = getArgElementType(v2);
522
523           if (!arg1Type || !arg2Type)
524             return;
525
526           // Support only floating point and integer arguments
527           // now (e.g. logical is skipped here).
528           if (!arg1Type->isa<mlir::FloatType>() &&
529               !arg1Type->isa<mlir::IntegerType>())
530             return;
531           if (!arg2Type->isa<mlir::FloatType>() &&
532               !arg2Type->isa<mlir::IntegerType>())
533             return;
534
535           auto typeGenerator = [&type](fir::FirOpBuilder &builder) {
536             return genFortranADotType(builder, type);
537           };
538           auto bodyGenerator = [&arg1Type,
539                                 &arg2Type](fir::FirOpBuilder &builder,
540                                            mlir::func::FuncOp &funcOp) {
541             genFortranADotBody(builder, funcOp, *arg1Type, *arg2Type);
542           };
543
544           // Suffix the function name with the element types
545           // of the arguments.
546           std::string typedFuncName(funcName);
547           llvm::raw_string_ostream nameOS(typedFuncName);
548           nameOS << "_";
549           arg1Type->print(nameOS);
550           nameOS << "_";
551           arg2Type->print(nameOS);
552
553           mlir::func::FuncOp newFunc = getOrCreateFunction(
554               builder, typedFuncName, typeGenerator, bodyGenerator);
555           auto newCall = builder.create<fir::CallOp>(loc, newFunc,
556                                                      mlir::ValueRange{v1, v2});
557           call->replaceAllUsesWith(newCall.getResults());
558           call->dropAllReferences();
559           call->erase();
560
561           LLVM_DEBUG(llvm::dbgs() << "Replaced with:\n"; newCall.dump();
562                      llvm::dbgs() << "\n");
563           return;
564         }
565         if (funcName.startswith("_FortranAMaxval")) {
566           simplifyReduction(call, kindMap, genFortranAMaxvalBody);
567           return;
568         }
569       }
570     }
571   });
572   LLVM_DEBUG(llvm::dbgs() << "=== End " DEBUG_TYPE " ===\n");
573 }
574
575 void SimplifyIntrinsicsPass::getDependentDialects(
576     mlir::DialectRegistry &registry) const {
577   // LLVM::LinkageAttr creation requires that LLVM dialect is loaded.
578   registry.insert<mlir::LLVM::LLVMDialect>();
579 }
580 std::unique_ptr<mlir::Pass> fir::createSimplifyIntrinsicsPass() {
581   return std::make_unique<SimplifyIntrinsicsPass>();
582 }