1 //===- SimplifyIntrinsics.cpp -- replace intrinsics with simpler form -----===//
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
7 //===----------------------------------------------------------------------===//
9 //===----------------------------------------------------------------------===//
11 /// This pass looks for suitable calls to runtime library for intrinsics that
12 /// can be simplified/specialized and replaces with a specialized function.
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)
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 //===----------------------------------------------------------------------===//
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 "flang/Runtime/entry-names.h"
33 #include "mlir/Dialect/LLVMIR/LLVMDialect.h"
34 #include "mlir/IR/Matchers.h"
35 #include "mlir/IR/TypeUtilities.h"
36 #include "mlir/Pass/Pass.h"
37 #include "mlir/Transforms/DialectConversion.h"
38 #include "mlir/Transforms/GreedyPatternRewriteDriver.h"
39 #include "mlir/Transforms/RegionUtils.h"
40 #include "llvm/ADT/Optional.h"
41 #include "llvm/Support/Debug.h"
42 #include "llvm/Support/raw_ostream.h"
45 #define GEN_PASS_DEF_SIMPLIFYINTRINSICS
46 #include "flang/Optimizer/Transforms/Passes.h.inc"
49 #define RTNAME_STRINGIFY2(x) #x
50 #define RTNAME_STRINGIFY(x) RTNAME_STRINGIFY2(x)
51 #define RTNAME_STRING(x) RTNAME_STRINGIFY(RTNAME(x))
53 #define DEBUG_TYPE "flang-simplify-intrinsics"
57 class SimplifyIntrinsicsPass
58 : public fir::impl::SimplifyIntrinsicsBase<SimplifyIntrinsicsPass> {
59 using FunctionTypeGeneratorTy =
60 llvm::function_ref<mlir::FunctionType(fir::FirOpBuilder &)>;
61 using FunctionBodyGeneratorTy =
62 llvm::function_ref<void(fir::FirOpBuilder &, mlir::func::FuncOp &)>;
63 using GenReductionBodyTy = llvm::function_ref<void(
64 fir::FirOpBuilder &builder, mlir::func::FuncOp &funcOp)>;
67 /// Generate a new function implementing a simplified version
68 /// of a Fortran runtime function defined by \p basename name.
69 /// \p typeGenerator is a callback that generates the new function's type.
70 /// \p bodyGenerator is a callback that generates the new function's body.
71 /// The new function is created in the \p builder's Module.
72 mlir::func::FuncOp getOrCreateFunction(fir::FirOpBuilder &builder,
73 const mlir::StringRef &basename,
74 FunctionTypeGeneratorTy typeGenerator,
75 FunctionBodyGeneratorTy bodyGenerator);
76 void runOnOperation() override;
77 void getDependentDialects(mlir::DialectRegistry ®istry) const override;
80 /// Helper function to replace a reduction type of call with its
81 /// simplified form. The actual function is generated using a callback
83 /// \p call is the call to be replaced
84 /// \p kindMap is used to create FIROpBuilder
85 /// \p genBodyFunc is the callback that builds the replacement function
86 void simplifyReduction(fir::CallOp call, const fir::KindMapping &kindMap,
87 GenReductionBodyTy genBodyFunc);
92 /// Generate function type for the simplified version of RTNAME(Sum) and
93 /// similar functions with a fir.box<none> type returning \p elementType.
94 static mlir::FunctionType genNoneBoxType(fir::FirOpBuilder &builder,
95 const mlir::Type &elementType) {
96 mlir::Type boxType = fir::BoxType::get(builder.getNoneType());
97 return mlir::FunctionType::get(builder.getContext(), {boxType},
101 using BodyOpGeneratorTy = llvm::function_ref<mlir::Value(
102 fir::FirOpBuilder &, mlir::Location, const mlir::Type &, mlir::Value,
104 using InitValGeneratorTy = llvm::function_ref<mlir::Value(
105 fir::FirOpBuilder &, mlir::Location, const mlir::Type &)>;
107 /// Generate the reduction loop into \p funcOp.
109 /// \p initVal is a function, called to get the initial value for
110 /// the reduction value
111 /// \p genBody is called to fill in the actual reduciton operation
112 /// for example add for SUM, MAX for MAXVAL, etc.
113 static void genReductionLoop(fir::FirOpBuilder &builder,
114 mlir::func::FuncOp &funcOp,
115 InitValGeneratorTy initVal,
116 BodyOpGeneratorTy genBody) {
117 auto loc = mlir::UnknownLoc::get(builder.getContext());
118 mlir::Type elementType = funcOp.getResultTypes()[0];
119 builder.setInsertionPointToEnd(funcOp.addEntryBlock());
121 mlir::IndexType idxTy = builder.getIndexType();
123 mlir::Block::BlockArgListType args = funcOp.front().getArguments();
124 mlir::Value arg = args[0];
126 mlir::Value zeroIdx = builder.createIntegerConstant(loc, idxTy, 0);
128 fir::SequenceType::Shape flatShape = {fir::SequenceType::getUnknownExtent()};
129 mlir::Type arrTy = fir::SequenceType::get(flatShape, elementType);
130 mlir::Type boxArrTy = fir::BoxType::get(arrTy);
131 mlir::Value array = builder.create<fir::ConvertOp>(loc, boxArrTy, arg);
133 builder.create<fir::BoxDimsOp>(loc, idxTy, idxTy, idxTy, array, zeroIdx);
134 mlir::Value len = dims.getResult(1);
135 mlir::Value one = builder.createIntegerConstant(loc, idxTy, 1);
136 mlir::Value step = one;
138 // We use C indexing here, so len-1 as loopcount
139 mlir::Value loopCount = builder.create<mlir::arith::SubIOp>(loc, len, one);
140 mlir::Value init = initVal(builder, loc, elementType);
141 auto loop = builder.create<fir::DoLoopOp>(loc, zeroIdx, loopCount, step,
143 /*finalCountValue=*/false, init);
144 mlir::Value reductionVal = loop.getRegionIterArgs()[0];
147 mlir::OpBuilder::InsertPoint loopEndPt = builder.saveInsertionPoint();
148 builder.setInsertionPointToStart(loop.getBody());
150 mlir::Type eleRefTy = builder.getRefType(elementType);
151 mlir::Value index = loop.getInductionVar();
153 builder.create<fir::CoordinateOp>(loc, eleRefTy, array, index);
154 mlir::Value elem = builder.create<fir::LoadOp>(loc, addr);
156 reductionVal = genBody(builder, loc, elementType, elem, reductionVal);
158 builder.create<fir::ResultOp>(loc, reductionVal);
160 builder.restoreInsertionPoint(loopEndPt);
162 mlir::Value resultVal = loop.getResult(0);
163 builder.create<mlir::func::ReturnOp>(loc, resultVal);
166 /// Generate function body of the simplified version of RTNAME(Sum)
167 /// with signature provided by \p funcOp. The caller is responsible
168 /// for saving/restoring the original insertion point of \p builder.
169 /// \p funcOp is expected to be empty on entry to this function.
170 static void genRuntimeSumBody(fir::FirOpBuilder &builder,
171 mlir::func::FuncOp &funcOp) {
172 // function RTNAME(Sum)<T>_simplified(arr)
173 // T, dimension(:) :: arr
176 // do iter = 0, extent(arr)
177 // sum = sum + arr[iter]
179 // RTNAME(Sum)<T>_simplified = sum
180 // end function RTNAME(Sum)<T>_simplified
181 auto zero = [](fir::FirOpBuilder builder, mlir::Location loc,
182 mlir::Type elementType) {
183 return elementType.isa<mlir::FloatType>()
184 ? builder.createRealConstant(loc, elementType,
186 : builder.createIntegerConstant(loc, elementType, 0);
189 auto genBodyOp = [](fir::FirOpBuilder builder, mlir::Location loc,
190 mlir::Type elementType, mlir::Value elem1,
191 mlir::Value elem2) -> mlir::Value {
192 if (elementType.isa<mlir::FloatType>())
193 return builder.create<mlir::arith::AddFOp>(loc, elem1, elem2);
194 if (elementType.isa<mlir::IntegerType>())
195 return builder.create<mlir::arith::AddIOp>(loc, elem1, elem2);
197 llvm_unreachable("unsupported type");
201 genReductionLoop(builder, funcOp, zero, genBodyOp);
204 static void genRuntimeMaxvalBody(fir::FirOpBuilder &builder,
205 mlir::func::FuncOp &funcOp) {
206 auto init = [](fir::FirOpBuilder builder, mlir::Location loc,
207 mlir::Type elementType) {
208 if (auto ty = elementType.dyn_cast<mlir::FloatType>()) {
209 const llvm::fltSemantics &sem = ty.getFloatSemantics();
210 return builder.createRealConstant(
211 loc, elementType, llvm::APFloat::getLargest(sem, /*Negative=*/true));
213 unsigned bits = elementType.getIntOrFloatBitWidth();
214 int64_t minInt = llvm::APInt::getSignedMinValue(bits).getSExtValue();
215 return builder.createIntegerConstant(loc, elementType, minInt);
218 auto genBodyOp = [](fir::FirOpBuilder builder, mlir::Location loc,
219 mlir::Type elementType, mlir::Value elem1,
220 mlir::Value elem2) -> mlir::Value {
221 if (elementType.isa<mlir::FloatType>())
222 return builder.create<mlir::arith::MaxFOp>(loc, elem1, elem2);
223 if (elementType.isa<mlir::IntegerType>())
224 return builder.create<mlir::arith::MaxSIOp>(loc, elem1, elem2);
226 llvm_unreachable("unsupported type");
229 genReductionLoop(builder, funcOp, init, genBodyOp);
232 /// Generate function type for the simplified version of RTNAME(DotProduct)
233 /// operating on the given \p elementType.
234 static mlir::FunctionType genRuntimeDotType(fir::FirOpBuilder &builder,
235 const mlir::Type &elementType) {
236 mlir::Type boxType = fir::BoxType::get(builder.getNoneType());
237 return mlir::FunctionType::get(builder.getContext(), {boxType, boxType},
241 /// Generate function body of the simplified version of RTNAME(DotProduct)
242 /// with signature provided by \p funcOp. The caller is responsible
243 /// for saving/restoring the original insertion point of \p builder.
244 /// \p funcOp is expected to be empty on entry to this function.
245 /// \p arg1ElementTy and \p arg2ElementTy specify elements types
246 /// of the underlying array objects - they are used to generate proper
247 /// element accesses.
248 static void genRuntimeDotBody(fir::FirOpBuilder &builder,
249 mlir::func::FuncOp &funcOp,
250 mlir::Type arg1ElementTy,
251 mlir::Type arg2ElementTy) {
252 // function RTNAME(DotProduct)<T>_simplified(arr1, arr2)
253 // T, dimension(:) :: arr1, arr2
256 // do iter = 0, extent(arr1)
257 // product = product + arr1[iter] * arr2[iter]
259 // RTNAME(ADotProduct)<T>_simplified = product
260 // end function RTNAME(DotProduct)<T>_simplified
261 auto loc = mlir::UnknownLoc::get(builder.getContext());
262 mlir::Type resultElementType = funcOp.getResultTypes()[0];
263 builder.setInsertionPointToEnd(funcOp.addEntryBlock());
265 mlir::IndexType idxTy = builder.getIndexType();
268 resultElementType.isa<mlir::FloatType>()
269 ? builder.createRealConstant(loc, resultElementType, 0.0)
270 : builder.createIntegerConstant(loc, resultElementType, 0);
272 mlir::Block::BlockArgListType args = funcOp.front().getArguments();
273 mlir::Value arg1 = args[0];
274 mlir::Value arg2 = args[1];
276 mlir::Value zeroIdx = builder.createIntegerConstant(loc, idxTy, 0);
278 fir::SequenceType::Shape flatShape = {fir::SequenceType::getUnknownExtent()};
279 mlir::Type arrTy1 = fir::SequenceType::get(flatShape, arg1ElementTy);
280 mlir::Type boxArrTy1 = fir::BoxType::get(arrTy1);
281 mlir::Value array1 = builder.create<fir::ConvertOp>(loc, boxArrTy1, arg1);
282 mlir::Type arrTy2 = fir::SequenceType::get(flatShape, arg2ElementTy);
283 mlir::Type boxArrTy2 = fir::BoxType::get(arrTy2);
284 mlir::Value array2 = builder.create<fir::ConvertOp>(loc, boxArrTy2, arg2);
285 // This version takes the loop trip count from the first argument.
286 // If the first argument's box has unknown (at compilation time)
287 // extent, then it may be better to take the extent from the second
288 // argument - so that after inlining the loop may be better optimized, e.g.
289 // fully unrolled. This requires generating two versions of the simplified
290 // function and some analysis at the call site to choose which version
291 // is more profitable to call.
292 // Note that we can assume that both arguments have the same extent.
294 builder.create<fir::BoxDimsOp>(loc, idxTy, idxTy, idxTy, array1, zeroIdx);
295 mlir::Value len = dims.getResult(1);
296 mlir::Value one = builder.createIntegerConstant(loc, idxTy, 1);
297 mlir::Value step = one;
299 // We use C indexing here, so len-1 as loopcount
300 mlir::Value loopCount = builder.create<mlir::arith::SubIOp>(loc, len, one);
301 auto loop = builder.create<fir::DoLoopOp>(loc, zeroIdx, loopCount, step,
303 /*finalCountValue=*/false, zero);
304 mlir::Value sumVal = loop.getRegionIterArgs()[0];
307 mlir::OpBuilder::InsertPoint loopEndPt = builder.saveInsertionPoint();
308 builder.setInsertionPointToStart(loop.getBody());
310 mlir::Type eleRef1Ty = builder.getRefType(arg1ElementTy);
311 mlir::Value index = loop.getInductionVar();
313 builder.create<fir::CoordinateOp>(loc, eleRef1Ty, array1, index);
314 mlir::Value elem1 = builder.create<fir::LoadOp>(loc, addr1);
315 // Convert to the result type.
316 elem1 = builder.create<fir::ConvertOp>(loc, resultElementType, elem1);
318 mlir::Type eleRef2Ty = builder.getRefType(arg2ElementTy);
320 builder.create<fir::CoordinateOp>(loc, eleRef2Ty, array2, index);
321 mlir::Value elem2 = builder.create<fir::LoadOp>(loc, addr2);
322 // Convert to the result type.
323 elem2 = builder.create<fir::ConvertOp>(loc, resultElementType, elem2);
325 if (resultElementType.isa<mlir::FloatType>())
326 sumVal = builder.create<mlir::arith::AddFOp>(
327 loc, builder.create<mlir::arith::MulFOp>(loc, elem1, elem2), sumVal);
328 else if (resultElementType.isa<mlir::IntegerType>())
329 sumVal = builder.create<mlir::arith::AddIOp>(
330 loc, builder.create<mlir::arith::MulIOp>(loc, elem1, elem2), sumVal);
332 llvm_unreachable("unsupported type");
334 builder.create<fir::ResultOp>(loc, sumVal);
336 builder.restoreInsertionPoint(loopEndPt);
338 mlir::Value resultVal = loop.getResult(0);
339 builder.create<mlir::func::ReturnOp>(loc, resultVal);
342 mlir::func::FuncOp SimplifyIntrinsicsPass::getOrCreateFunction(
343 fir::FirOpBuilder &builder, const mlir::StringRef &baseName,
344 FunctionTypeGeneratorTy typeGenerator,
345 FunctionBodyGeneratorTy bodyGenerator) {
346 // WARNING: if the function generated here changes its signature
347 // or behavior (the body code), we should probably embed some
348 // versioning information into its name, otherwise libraries
349 // statically linked with older versions of Flang may stop
350 // working with object files created with newer Flang.
351 // We can also avoid this by using internal linkage, but
352 // this may increase the size of final executable/shared library.
353 std::string replacementName = mlir::Twine{baseName, "_simplified"}.str();
354 mlir::ModuleOp module = builder.getModule();
355 // If we already have a function, just return it.
356 mlir::func::FuncOp newFunc =
357 fir::FirOpBuilder::getNamedFunction(module, replacementName);
358 mlir::FunctionType fType = typeGenerator(builder);
360 assert(newFunc.getFunctionType() == fType &&
361 "type mismatch for simplified function");
365 // Need to build the function!
366 auto loc = mlir::UnknownLoc::get(builder.getContext());
368 fir::FirOpBuilder::createFunction(loc, module, replacementName, fType);
369 auto inlineLinkage = mlir::LLVM::linkage::Linkage::LinkonceODR;
371 mlir::LLVM::LinkageAttr::get(builder.getContext(), inlineLinkage);
372 newFunc->setAttr("llvm.linkage", linkage);
374 // Save the position of the original call.
375 mlir::OpBuilder::InsertPoint insertPt = builder.saveInsertionPoint();
377 bodyGenerator(builder, newFunc);
379 // Now back to where we were adding code earlier...
380 builder.restoreInsertionPoint(insertPt);
385 fir::ConvertOp expectConvertOp(mlir::Value val) {
386 if (fir::ConvertOp op =
387 mlir::dyn_cast_or_null<fir::ConvertOp>(val.getDefiningOp()))
389 LLVM_DEBUG(llvm::dbgs() << "Didn't find expected fir::ConvertOp\n");
393 static bool isOperandAbsent(mlir::Value val) {
394 if (auto op = expectConvertOp(val)) {
395 assert(op->getOperands().size() != 0);
396 return mlir::isa_and_nonnull<fir::AbsentOp>(
397 op->getOperand(0).getDefiningOp());
402 static bool isZero(mlir::Value val) {
403 if (auto op = expectConvertOp(val)) {
404 assert(op->getOperands().size() != 0);
405 if (mlir::Operation *defOp = op->getOperand(0).getDefiningOp())
406 return mlir::matchPattern(defOp, mlir::m_Zero());
411 static mlir::Value findShape(mlir::Value val) {
412 if (auto op = expectConvertOp(val)) {
413 assert(op->getOperands().size() != 0);
414 if (auto box = mlir::dyn_cast_or_null<fir::EmboxOp>(
415 op->getOperand(0).getDefiningOp()))
416 return box.getShape();
421 static unsigned getDimCount(mlir::Value val) {
422 if (mlir::Value shapeVal = findShape(val)) {
423 mlir::Type resType = shapeVal.getDefiningOp()->getResultTypes()[0];
424 return fir::getRankOfShapeType(resType);
429 /// Given the call operation's box argument \p val, discover
430 /// the element type of the underlying array object.
431 /// \returns the element type or llvm::None if the type cannot
432 /// be reliably found.
433 /// We expect that the argument is a result of fir.convert
434 /// with the destination type of !fir.box<none>.
435 static llvm::Optional<mlir::Type> getArgElementType(mlir::Value val) {
436 mlir::Operation *defOp;
438 defOp = val.getDefiningOp();
439 // Analyze only sequences of convert operations.
440 if (!mlir::isa<fir::ConvertOp>(defOp))
442 val = defOp->getOperand(0);
443 // The convert operation is expected to convert from one
444 // box type to another box type.
445 auto boxType = val.getType().cast<fir::BoxType>();
446 auto elementType = fir::unwrapSeqOrBoxedSeqType(boxType);
447 if (!elementType.isa<mlir::NoneType>())
452 void SimplifyIntrinsicsPass::simplifyReduction(fir::CallOp call,
453 const fir::KindMapping &kindMap,
454 GenReductionBodyTy genBodyFunc) {
455 mlir::SymbolRefAttr callee = call.getCalleeAttr();
456 mlir::StringRef funcName = callee.getLeafReference().getValue();
457 mlir::Operation::operand_range args = call.getArgs();
458 // args[1] and args[2] are source filename and line number, ignored.
459 const mlir::Value &dim = args[3];
460 const mlir::Value &mask = args[4];
461 // dim is zero when it is absent, which is an implementation
462 // detail in the runtime library.
463 bool dimAndMaskAbsent = isZero(dim) && isOperandAbsent(mask);
464 unsigned rank = getDimCount(args[0]);
465 if (dimAndMaskAbsent && rank == 1) {
466 mlir::Location loc = call.getLoc();
468 fir::FirOpBuilder builder(call, kindMap);
469 if (funcName.endswith("Integer4")) {
470 type = mlir::IntegerType::get(builder.getContext(), 32);
471 } else if (funcName.endswith("Real8")) {
472 type = mlir::FloatType::getF64(builder.getContext());
476 auto typeGenerator = [&type](fir::FirOpBuilder &builder) {
477 return genNoneBoxType(builder, type);
479 mlir::func::FuncOp newFunc =
480 getOrCreateFunction(builder, funcName, typeGenerator, genBodyFunc);
482 builder.create<fir::CallOp>(loc, newFunc, mlir::ValueRange{args[0]});
483 call->replaceAllUsesWith(newCall.getResults());
484 call->dropAllReferences();
489 void SimplifyIntrinsicsPass::runOnOperation() {
490 LLVM_DEBUG(llvm::dbgs() << "=== Begin " DEBUG_TYPE " ===\n");
491 mlir::ModuleOp module = getOperation();
492 fir::KindMapping kindMap = fir::getKindMapping(module);
493 module.walk([&](mlir::Operation *op) {
494 if (auto call = mlir::dyn_cast<fir::CallOp>(op)) {
495 if (mlir::SymbolRefAttr callee = call.getCalleeAttr()) {
496 mlir::StringRef funcName = callee.getLeafReference().getValue();
497 // Replace call to runtime function for SUM when it has single
498 // argument (no dim or mask argument) for 1D arrays with either
499 // Integer4 or Real8 types. Other forms are ignored.
500 // The new function is added to the module.
502 // Prototype for runtime call (from sum.cpp):
503 // RTNAME(Sum<T>)(const Descriptor &x, const char *source, int line,
504 // int dim, const Descriptor *mask)
506 if (funcName.startswith(RTNAME_STRING(Sum))) {
507 simplifyReduction(call, kindMap, genRuntimeSumBody);
510 if (funcName.startswith(RTNAME_STRING(DotProduct))) {
511 LLVM_DEBUG(llvm::dbgs() << "Handling " << funcName << "\n");
512 LLVM_DEBUG(llvm::dbgs() << "Call operation:\n"; op->dump();
513 llvm::dbgs() << "\n");
514 mlir::Operation::operand_range args = call.getArgs();
515 const mlir::Value &v1 = args[0];
516 const mlir::Value &v2 = args[1];
517 mlir::Location loc = call.getLoc();
518 fir::FirOpBuilder builder(op, kindMap);
520 mlir::Type type = call.getResult(0).getType();
521 if (!type.isa<mlir::FloatType>() && !type.isa<mlir::IntegerType>())
524 // Try to find the element types of the boxed arguments.
525 auto arg1Type = getArgElementType(v1);
526 auto arg2Type = getArgElementType(v2);
528 if (!arg1Type || !arg2Type)
531 // Support only floating point and integer arguments
532 // now (e.g. logical is skipped here).
533 if (!arg1Type->isa<mlir::FloatType>() &&
534 !arg1Type->isa<mlir::IntegerType>())
536 if (!arg2Type->isa<mlir::FloatType>() &&
537 !arg2Type->isa<mlir::IntegerType>())
540 auto typeGenerator = [&type](fir::FirOpBuilder &builder) {
541 return genRuntimeDotType(builder, type);
543 auto bodyGenerator = [&arg1Type,
544 &arg2Type](fir::FirOpBuilder &builder,
545 mlir::func::FuncOp &funcOp) {
546 genRuntimeDotBody(builder, funcOp, *arg1Type, *arg2Type);
549 // Suffix the function name with the element types
551 std::string typedFuncName(funcName);
552 llvm::raw_string_ostream nameOS(typedFuncName);
554 arg1Type->print(nameOS);
556 arg2Type->print(nameOS);
558 mlir::func::FuncOp newFunc = getOrCreateFunction(
559 builder, typedFuncName, typeGenerator, bodyGenerator);
560 auto newCall = builder.create<fir::CallOp>(loc, newFunc,
561 mlir::ValueRange{v1, v2});
562 call->replaceAllUsesWith(newCall.getResults());
563 call->dropAllReferences();
566 LLVM_DEBUG(llvm::dbgs() << "Replaced with:\n"; newCall.dump();
567 llvm::dbgs() << "\n");
570 if (funcName.startswith(RTNAME_STRING(Maxval))) {
571 simplifyReduction(call, kindMap, genRuntimeMaxvalBody);
577 LLVM_DEBUG(llvm::dbgs() << "=== End " DEBUG_TYPE " ===\n");
580 void SimplifyIntrinsicsPass::getDependentDialects(
581 mlir::DialectRegistry ®istry) const {
582 // LLVM::LinkageAttr creation requires that LLVM dialect is loaded.
583 registry.insert<mlir::LLVM::LLVMDialect>();
585 std::unique_ptr<mlir::Pass> fir::createSimplifyIntrinsicsPass() {
586 return std::make_unique<SimplifyIntrinsicsPass>();