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