1//===- InliningUtils.cpp ---- Misc utilities for inlining -----------------===//
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// This file implements miscellaneous inlining utilities.
10//
11//===----------------------------------------------------------------------===//
12
13#include "mlir/Transforms/InliningUtils.h"
14
15#include "mlir/IR/Builders.h"
16#include "mlir/IR/IRMapping.h"
17#include "mlir/IR/Operation.h"
18#include "mlir/Interfaces/CallInterfaces.h"
19#include "llvm/Support/Debug.h"
20#include "llvm/Support/raw_ostream.h"
21#include <optional>
22
23#define DEBUG_TYPE "inlining"
24
25using namespace mlir;
26
27/// Combine `callee` location with `caller` location to create a stack that
28/// represents the call chain.
29/// If `callee` location is a `CallSiteLoc`, indicating an existing stack of
30/// locations, the `caller` location is appended to the end of it, extending
31/// the chain.
32/// Otherwise, a single `CallSiteLoc` is created, representing a direct call
33/// from `caller` to `callee`.
34static LocationAttr stackLocations(Location callee, Location caller) {
35 Location lastCallee = callee;
36 SmallVector<CallSiteLoc> calleeInliningStack;
37 while (auto nextCallSite = dyn_cast<CallSiteLoc>(Val&: lastCallee)) {
38 calleeInliningStack.push_back(Elt: nextCallSite);
39 lastCallee = nextCallSite.getCaller();
40 }
41
42 CallSiteLoc firstCallSite = CallSiteLoc::get(callee: lastCallee, caller);
43 for (CallSiteLoc currentCallSite : reverse(C&: calleeInliningStack))
44 firstCallSite =
45 CallSiteLoc::get(callee: currentCallSite.getCallee(), caller: firstCallSite);
46
47 return firstCallSite;
48}
49
50/// Remap all locations reachable from the inlined blocks with CallSiteLoc
51/// locations with the provided caller location.
52static void
53remapInlinedLocations(iterator_range<Region::iterator> inlinedBlocks,
54 Location callerLoc) {
55 DenseMap<Location, LocationAttr> mappedLocations;
56 auto remapLoc = [&](Location loc) {
57 auto [it, inserted] = mappedLocations.try_emplace(Key: loc);
58 // Only query the attribute uniquer once per callsite attribute.
59 if (inserted) {
60 LocationAttr newLoc = stackLocations(callee: loc, caller: callerLoc);
61 it->getSecond() = newLoc;
62 }
63 return it->second;
64 };
65
66 AttrTypeReplacer attrReplacer;
67 attrReplacer.addReplacement(
68 callback: [&](LocationAttr loc) -> std::pair<LocationAttr, WalkResult> {
69 return {remapLoc(loc), WalkResult::skip()};
70 });
71
72 for (Block &block : inlinedBlocks) {
73 for (BlockArgument &arg : block.getArguments())
74 if (LocationAttr newLoc = remapLoc(arg.getLoc()))
75 arg.setLoc(newLoc);
76
77 for (Operation &op : block)
78 attrReplacer.recursivelyReplaceElementsIn(op: &op, /*replaceAttrs=*/false,
79 /*replaceLocs=*/true);
80 }
81}
82
83static void remapInlinedOperands(iterator_range<Region::iterator> inlinedBlocks,
84 IRMapping &mapper) {
85 auto remapOperands = [&](Operation *op) {
86 for (auto &operand : op->getOpOperands())
87 if (auto mappedOp = mapper.lookupOrNull(from: operand.get()))
88 operand.set(mappedOp);
89 };
90 for (auto &block : inlinedBlocks)
91 block.walk(callback&: remapOperands);
92}
93
94//===----------------------------------------------------------------------===//
95// InlinerInterface
96//===----------------------------------------------------------------------===//
97
98bool InlinerInterface::isLegalToInline(Operation *call, Operation *callable,
99 bool wouldBeCloned) const {
100 if (auto *handler = getInterfaceFor(obj: call))
101 return handler->isLegalToInline(call, callable, wouldBeCloned);
102 return false;
103}
104
105bool InlinerInterface::isLegalToInline(Region *dest, Region *src,
106 bool wouldBeCloned,
107 IRMapping &valueMapping) const {
108 if (auto *handler = getInterfaceFor(obj: dest->getParentOp()))
109 return handler->isLegalToInline(dest, src, wouldBeCloned, valueMapping);
110 return false;
111}
112
113bool InlinerInterface::isLegalToInline(Operation *op, Region *dest,
114 bool wouldBeCloned,
115 IRMapping &valueMapping) const {
116 if (auto *handler = getInterfaceFor(obj: op))
117 return handler->isLegalToInline(op, dest, wouldBeCloned, valueMapping);
118 return false;
119}
120
121bool InlinerInterface::shouldAnalyzeRecursively(Operation *op) const {
122 auto *handler = getInterfaceFor(obj: op);
123 return handler ? handler->shouldAnalyzeRecursively(op) : true;
124}
125
126/// Handle the given inlined terminator by replacing it with a new operation
127/// as necessary.
128void InlinerInterface::handleTerminator(Operation *op, Block *newDest) const {
129 auto *handler = getInterfaceFor(obj: op);
130 assert(handler && "expected valid dialect handler");
131 handler->handleTerminator(op, newDest);
132}
133
134/// Handle the given inlined terminator by replacing it with a new operation
135/// as necessary.
136void InlinerInterface::handleTerminator(Operation *op,
137 ValueRange valuesToRepl) const {
138 auto *handler = getInterfaceFor(obj: op);
139 assert(handler && "expected valid dialect handler");
140 handler->handleTerminator(op, valuesToReplace: valuesToRepl);
141}
142
143/// Returns true if the inliner can assume a fast path of not creating a
144/// new block, if there is only one block.
145bool InlinerInterface::allowSingleBlockOptimization(
146 iterator_range<Region::iterator> inlinedBlocks) const {
147 if (inlinedBlocks.empty()) {
148 return true;
149 }
150 auto *handler = getInterfaceFor(obj: inlinedBlocks.begin()->getParentOp());
151 assert(handler && "expected valid dialect handler");
152 return handler->allowSingleBlockOptimization(inlinedBlocks);
153}
154
155Value InlinerInterface::handleArgument(OpBuilder &builder, Operation *call,
156 Operation *callable, Value argument,
157 DictionaryAttr argumentAttrs) const {
158 auto *handler = getInterfaceFor(obj: callable);
159 assert(handler && "expected valid dialect handler");
160 return handler->handleArgument(builder, call, callable, argument,
161 argumentAttrs);
162}
163
164Value InlinerInterface::handleResult(OpBuilder &builder, Operation *call,
165 Operation *callable, Value result,
166 DictionaryAttr resultAttrs) const {
167 auto *handler = getInterfaceFor(obj: callable);
168 assert(handler && "expected valid dialect handler");
169 return handler->handleResult(builder, call, callable, result, resultAttrs);
170}
171
172void InlinerInterface::processInlinedCallBlocks(
173 Operation *call, iterator_range<Region::iterator> inlinedBlocks) const {
174 auto *handler = getInterfaceFor(obj: call);
175 assert(handler && "expected valid dialect handler");
176 handler->processInlinedCallBlocks(call, inlinedBlocks);
177}
178
179/// Utility to check that all of the operations within 'src' can be inlined.
180static bool isLegalToInline(InlinerInterface &interface, Region *src,
181 Region *insertRegion, bool shouldCloneInlinedRegion,
182 IRMapping &valueMapping) {
183 for (auto &block : *src) {
184 for (auto &op : block) {
185 // Check this operation.
186 if (!interface.isLegalToInline(op: &op, dest: insertRegion,
187 wouldBeCloned: shouldCloneInlinedRegion, valueMapping)) {
188 LLVM_DEBUG({
189 llvm::dbgs() << "* Illegal to inline because of op: ";
190 op.dump();
191 });
192 return false;
193 }
194 // Check any nested regions.
195 if (interface.shouldAnalyzeRecursively(op: &op) &&
196 llvm::any_of(Range: op.getRegions(), P: [&](Region &region) {
197 return !isLegalToInline(interface, src: &region, insertRegion,
198 shouldCloneInlinedRegion, valueMapping);
199 }))
200 return false;
201 }
202 }
203 return true;
204}
205
206//===----------------------------------------------------------------------===//
207// Inline Methods
208//===----------------------------------------------------------------------===//
209
210static void handleArgumentImpl(InlinerInterface &interface, OpBuilder &builder,
211 CallOpInterface call,
212 CallableOpInterface callable,
213 IRMapping &mapper) {
214 // Unpack the argument attributes if there are any.
215 SmallVector<DictionaryAttr> argAttrs(
216 callable.getCallableRegion()->getNumArguments(),
217 builder.getDictionaryAttr(value: {}));
218 if (ArrayAttr arrayAttr = callable.getArgAttrsAttr()) {
219 assert(arrayAttr.size() == argAttrs.size());
220 for (auto [idx, attr] : llvm::enumerate(First&: arrayAttr))
221 argAttrs[idx] = cast<DictionaryAttr>(Val: attr);
222 }
223
224 // Run the argument attribute handler for the given argument and attribute.
225 for (auto [blockArg, argAttr] :
226 llvm::zip(t: callable.getCallableRegion()->getArguments(), u&: argAttrs)) {
227 Value newArgument = interface.handleArgument(
228 builder, call, callable, argument: mapper.lookup(from: blockArg), argumentAttrs: argAttr);
229 assert(newArgument.getType() == mapper.lookup(blockArg).getType() &&
230 "expected the argument type to not change");
231
232 // Update the mapping to point the new argument returned by the handler.
233 mapper.map(from: blockArg, to: newArgument);
234 }
235}
236
237static void handleResultImpl(InlinerInterface &interface, OpBuilder &builder,
238 CallOpInterface call, CallableOpInterface callable,
239 ValueRange results) {
240 // Unpack the result attributes if there are any.
241 SmallVector<DictionaryAttr> resAttrs(results.size(),
242 builder.getDictionaryAttr(value: {}));
243 if (ArrayAttr arrayAttr = callable.getResAttrsAttr()) {
244 assert(arrayAttr.size() == resAttrs.size());
245 for (auto [idx, attr] : llvm::enumerate(First&: arrayAttr))
246 resAttrs[idx] = cast<DictionaryAttr>(Val: attr);
247 }
248
249 // Run the result attribute handler for the given result and attribute.
250 for (auto [result, resAttr] : llvm::zip(t&: results, u&: resAttrs)) {
251 // Store the original result users before running the handler.
252 DenseSet<Operation *> resultUsers(llvm::from_range, result.getUsers());
253
254 Value newResult =
255 interface.handleResult(builder, call, callable, result, resultAttrs: resAttr);
256 assert(newResult.getType() == result.getType() &&
257 "expected the result type to not change");
258
259 // Replace the result uses except for the ones introduce by the handler.
260 result.replaceUsesWithIf(newValue: newResult, shouldReplace: [&](OpOperand &operand) {
261 return resultUsers.count(V: operand.getOwner());
262 });
263 }
264}
265
266static LogicalResult inlineRegionImpl(
267 InlinerInterface &interface,
268 function_ref<InlinerInterface::CloneCallbackSigTy> cloneCallback,
269 Region *src, Block *inlineBlock, Block::iterator inlinePoint,
270 IRMapping &mapper, ValueRange resultsToReplace, TypeRange regionResultTypes,
271 std::optional<Location> inlineLoc, bool shouldCloneInlinedRegion,
272 CallOpInterface call = {}) {
273 assert(resultsToReplace.size() == regionResultTypes.size());
274 // We expect the region to have at least one block.
275 if (src->empty())
276 return failure();
277
278 // Check that all of the region arguments have been mapped.
279 auto *srcEntryBlock = &src->front();
280 if (llvm::any_of(Range: srcEntryBlock->getArguments(),
281 P: [&](BlockArgument arg) { return !mapper.contains(from: arg); }))
282 return failure();
283
284 // Check that the operations within the source region are valid to inline.
285 Region *insertRegion = inlineBlock->getParent();
286 if (!interface.isLegalToInline(dest: insertRegion, src, wouldBeCloned: shouldCloneInlinedRegion,
287 valueMapping&: mapper) ||
288 !isLegalToInline(interface, src, insertRegion, shouldCloneInlinedRegion,
289 valueMapping&: mapper))
290 return failure();
291
292 // Run the argument attribute handler before inlining the callable region.
293 OpBuilder builder(inlineBlock, inlinePoint);
294 auto callable = dyn_cast<CallableOpInterface>(Val: src->getParentOp());
295 if (call && callable)
296 handleArgumentImpl(interface, builder, call, callable, mapper);
297
298 // Clone the callee's source into the caller.
299 Block *postInsertBlock = inlineBlock->splitBlock(splitBefore: inlinePoint);
300 cloneCallback(builder, src, inlineBlock, postInsertBlock, mapper,
301 shouldCloneInlinedRegion);
302
303 // Get the range of newly inserted blocks.
304 auto newBlocks = llvm::make_range(x: std::next(x: inlineBlock->getIterator()),
305 y: postInsertBlock->getIterator());
306 Block *firstNewBlock = &*newBlocks.begin();
307
308 // Remap the locations of the inlined operations if a valid source location
309 // was provided.
310 if (inlineLoc && !llvm::isa<UnknownLoc>(Val: *inlineLoc))
311 remapInlinedLocations(inlinedBlocks: newBlocks, callerLoc: *inlineLoc);
312
313 // If the blocks were moved in-place, make sure to remap any necessary
314 // operands.
315 if (!shouldCloneInlinedRegion)
316 remapInlinedOperands(inlinedBlocks: newBlocks, mapper);
317
318 // Process the newly inlined blocks.
319 if (call)
320 interface.processInlinedCallBlocks(call, inlinedBlocks: newBlocks);
321 interface.processInlinedBlocks(inlinedBlocks: newBlocks);
322
323 bool singleBlockFastPath = interface.allowSingleBlockOptimization(inlinedBlocks: newBlocks);
324
325 // Handle the case where only a single block was inlined.
326 if (singleBlockFastPath && llvm::hasSingleElement(C&: newBlocks)) {
327 // Run the result attribute handler on the terminator operands.
328 Operation *firstBlockTerminator = firstNewBlock->getTerminator();
329 builder.setInsertionPoint(firstBlockTerminator);
330 if (call && callable)
331 handleResultImpl(interface, builder, call, callable,
332 results: firstBlockTerminator->getOperands());
333
334 // Have the interface handle the terminator of this block.
335 interface.handleTerminator(op: firstBlockTerminator, valuesToRepl: resultsToReplace);
336 firstBlockTerminator->erase();
337
338 // Merge the post insert block into the cloned entry block.
339 firstNewBlock->getOperations().splice(where: firstNewBlock->end(),
340 L2&: postInsertBlock->getOperations());
341 postInsertBlock->erase();
342 } else {
343 // Otherwise, there were multiple blocks inlined. Add arguments to the post
344 // insertion block to represent the results to replace.
345 for (const auto &resultToRepl : llvm::enumerate(First&: resultsToReplace)) {
346 resultToRepl.value().replaceAllUsesWith(
347 newValue: postInsertBlock->addArgument(type: regionResultTypes[resultToRepl.index()],
348 loc: resultToRepl.value().getLoc()));
349 }
350
351 // Run the result attribute handler on the post insertion block arguments.
352 builder.setInsertionPointToStart(postInsertBlock);
353 if (call && callable)
354 handleResultImpl(interface, builder, call, callable,
355 results: postInsertBlock->getArguments());
356
357 /// Handle the terminators for each of the new blocks.
358 for (auto &newBlock : newBlocks)
359 interface.handleTerminator(op: newBlock.getTerminator(), newDest: postInsertBlock);
360 }
361
362 // Splice the instructions of the inlined entry block into the insert block.
363 inlineBlock->getOperations().splice(where: inlineBlock->end(),
364 L2&: firstNewBlock->getOperations());
365 firstNewBlock->erase();
366 return success();
367}
368
369static LogicalResult inlineRegionImpl(
370 InlinerInterface &interface,
371 function_ref<InlinerInterface::CloneCallbackSigTy> cloneCallback,
372 Region *src, Block *inlineBlock, Block::iterator inlinePoint,
373 ValueRange inlinedOperands, ValueRange resultsToReplace,
374 std::optional<Location> inlineLoc, bool shouldCloneInlinedRegion,
375 CallOpInterface call = {}) {
376 // We expect the region to have at least one block.
377 if (src->empty())
378 return failure();
379
380 auto *entryBlock = &src->front();
381 if (inlinedOperands.size() != entryBlock->getNumArguments())
382 return failure();
383
384 // Map the provided call operands to the arguments of the region.
385 IRMapping mapper;
386 for (unsigned i = 0, e = inlinedOperands.size(); i != e; ++i) {
387 // Verify that the types of the provided values match the function argument
388 // types.
389 BlockArgument regionArg = entryBlock->getArgument(i);
390 if (inlinedOperands[i].getType() != regionArg.getType())
391 return failure();
392 mapper.map(from: regionArg, to: inlinedOperands[i]);
393 }
394
395 // Call into the main region inliner function.
396 return inlineRegionImpl(interface, cloneCallback, src, inlineBlock,
397 inlinePoint, mapper, resultsToReplace,
398 regionResultTypes: resultsToReplace.getTypes(), inlineLoc,
399 shouldCloneInlinedRegion, call);
400}
401
402LogicalResult mlir::inlineRegion(
403 InlinerInterface &interface,
404 function_ref<InlinerInterface::CloneCallbackSigTy> cloneCallback,
405 Region *src, Operation *inlinePoint, IRMapping &mapper,
406 ValueRange resultsToReplace, TypeRange regionResultTypes,
407 std::optional<Location> inlineLoc, bool shouldCloneInlinedRegion) {
408 return inlineRegion(interface, cloneCallback, src, inlineBlock: inlinePoint->getBlock(),
409 inlinePoint: ++inlinePoint->getIterator(), mapper, resultsToReplace,
410 regionResultTypes, inlineLoc, shouldCloneInlinedRegion);
411}
412
413LogicalResult mlir::inlineRegion(
414 InlinerInterface &interface,
415 function_ref<InlinerInterface::CloneCallbackSigTy> cloneCallback,
416 Region *src, Block *inlineBlock, Block::iterator inlinePoint,
417 IRMapping &mapper, ValueRange resultsToReplace, TypeRange regionResultTypes,
418 std::optional<Location> inlineLoc, bool shouldCloneInlinedRegion) {
419 return inlineRegionImpl(
420 interface, cloneCallback, src, inlineBlock, inlinePoint, mapper,
421 resultsToReplace, regionResultTypes, inlineLoc, shouldCloneInlinedRegion);
422}
423
424LogicalResult mlir::inlineRegion(
425 InlinerInterface &interface,
426 function_ref<InlinerInterface::CloneCallbackSigTy> cloneCallback,
427 Region *src, Operation *inlinePoint, ValueRange inlinedOperands,
428 ValueRange resultsToReplace, std::optional<Location> inlineLoc,
429 bool shouldCloneInlinedRegion) {
430 return inlineRegion(interface, cloneCallback, src, inlineBlock: inlinePoint->getBlock(),
431 inlinePoint: ++inlinePoint->getIterator(), inlinedOperands,
432 resultsToReplace, inlineLoc, shouldCloneInlinedRegion);
433}
434
435LogicalResult mlir::inlineRegion(
436 InlinerInterface &interface,
437 function_ref<InlinerInterface::CloneCallbackSigTy> cloneCallback,
438 Region *src, Block *inlineBlock, Block::iterator inlinePoint,
439 ValueRange inlinedOperands, ValueRange resultsToReplace,
440 std::optional<Location> inlineLoc, bool shouldCloneInlinedRegion) {
441 return inlineRegionImpl(interface, cloneCallback, src, inlineBlock,
442 inlinePoint, inlinedOperands, resultsToReplace,
443 inlineLoc, shouldCloneInlinedRegion);
444}
445
446/// Utility function used to generate a cast operation from the given interface,
447/// or return nullptr if a cast could not be generated.
448static Value materializeConversion(const DialectInlinerInterface *interface,
449 SmallVectorImpl<Operation *> &castOps,
450 OpBuilder &castBuilder, Value arg, Type type,
451 Location conversionLoc) {
452 if (!interface)
453 return nullptr;
454
455 // Check to see if the interface for the call can materialize a conversion.
456 Operation *castOp = interface->materializeCallConversion(builder&: castBuilder, input: arg,
457 resultType: type, conversionLoc);
458 if (!castOp)
459 return nullptr;
460 castOps.push_back(Elt: castOp);
461
462 // Ensure that the generated cast is correct.
463 assert(castOp->getNumOperands() == 1 && castOp->getOperand(0) == arg &&
464 castOp->getNumResults() == 1 && *castOp->result_type_begin() == type);
465 return castOp->getResult(idx: 0);
466}
467
468/// This function inlines a given region, 'src', of a callable operation,
469/// 'callable', into the location defined by the given call operation. This
470/// function returns failure if inlining is not possible, success otherwise. On
471/// failure, no changes are made to the module. 'shouldCloneInlinedRegion'
472/// corresponds to whether the source region should be cloned into the 'call' or
473/// spliced directly.
474LogicalResult mlir::inlineCall(
475 InlinerInterface &interface,
476 function_ref<InlinerInterface::CloneCallbackSigTy> cloneCallback,
477 CallOpInterface call, CallableOpInterface callable, Region *src,
478 bool shouldCloneInlinedRegion) {
479 // We expect the region to have at least one block.
480 if (src->empty())
481 return failure();
482 auto *entryBlock = &src->front();
483 ArrayRef<Type> callableResultTypes = callable.getResultTypes();
484
485 // Make sure that the number of arguments and results matchup between the call
486 // and the region.
487 SmallVector<Value, 8> callOperands(call.getArgOperands());
488 SmallVector<Value, 8> callResults(call->getResults());
489 if (callOperands.size() != entryBlock->getNumArguments() ||
490 callResults.size() != callableResultTypes.size())
491 return failure();
492
493 // A set of cast operations generated to matchup the signature of the region
494 // with the signature of the call.
495 SmallVector<Operation *, 4> castOps;
496 castOps.reserve(N: callOperands.size() + callResults.size());
497
498 // Functor used to cleanup generated state on failure.
499 auto cleanupState = [&] {
500 for (auto *op : castOps) {
501 op->getResult(idx: 0).replaceAllUsesWith(newValue: op->getOperand(idx: 0));
502 op->erase();
503 }
504 return failure();
505 };
506
507 // Builder used for any conversion operations that need to be materialized.
508 OpBuilder castBuilder(call);
509 Location castLoc = call.getLoc();
510 const auto *callInterface = interface.getInterfaceFor(obj: call->getDialect());
511
512 // Map the provided call operands to the arguments of the region.
513 IRMapping mapper;
514 for (unsigned i = 0, e = callOperands.size(); i != e; ++i) {
515 BlockArgument regionArg = entryBlock->getArgument(i);
516 Value operand = callOperands[i];
517
518 // If the call operand doesn't match the expected region argument, try to
519 // generate a cast.
520 Type regionArgType = regionArg.getType();
521 if (operand.getType() != regionArgType) {
522 if (!(operand = materializeConversion(interface: callInterface, castOps, castBuilder,
523 arg: operand, type: regionArgType, conversionLoc: castLoc)))
524 return cleanupState();
525 }
526 mapper.map(from: regionArg, to: operand);
527 }
528
529 // Ensure that the resultant values of the call match the callable.
530 castBuilder.setInsertionPointAfter(call);
531 for (unsigned i = 0, e = callResults.size(); i != e; ++i) {
532 Value callResult = callResults[i];
533 if (callResult.getType() == callableResultTypes[i])
534 continue;
535
536 // Generate a conversion that will produce the original type, so that the IR
537 // is still valid after the original call gets replaced.
538 Value castResult =
539 materializeConversion(interface: callInterface, castOps, castBuilder, arg: callResult,
540 type: callResult.getType(), conversionLoc: castLoc);
541 if (!castResult)
542 return cleanupState();
543 callResult.replaceAllUsesWith(newValue: castResult);
544 castResult.getDefiningOp()->replaceUsesOfWith(from: castResult, to: callResult);
545 }
546
547 // Check that it is legal to inline the callable into the call.
548 if (!interface.isLegalToInline(call, callable, wouldBeCloned: shouldCloneInlinedRegion))
549 return cleanupState();
550
551 // Attempt to inline the call.
552 if (failed(Result: inlineRegionImpl(interface, cloneCallback, src, inlineBlock: call->getBlock(),
553 inlinePoint: ++call->getIterator(), mapper, resultsToReplace: callResults,
554 regionResultTypes: callableResultTypes, inlineLoc: call.getLoc(),
555 shouldCloneInlinedRegion, call)))
556 return cleanupState();
557 return success();
558}
559

source code of mlir/lib/Transforms/Utils/InliningUtils.cpp