1//===- InliningUtils.h - Inliner utilities ----------------------*- C++ -*-===//
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 header file defines interfaces for various inlining utility methods.
10//
11//===----------------------------------------------------------------------===//
12
13#ifndef MLIR_TRANSFORMS_INLININGUTILS_H
14#define MLIR_TRANSFORMS_INLININGUTILS_H
15
16#include "mlir/IR/BuiltinAttributes.h"
17#include "mlir/IR/DialectInterface.h"
18#include "mlir/IR/Location.h"
19#include "mlir/IR/Region.h"
20#include "mlir/IR/ValueRange.h"
21#include <optional>
22
23namespace mlir {
24
25class Block;
26class IRMapping;
27class CallableOpInterface;
28class CallOpInterface;
29class OpBuilder;
30class Operation;
31class Region;
32class TypeRange;
33class Value;
34class ValueRange;
35
36//===----------------------------------------------------------------------===//
37// InlinerInterface
38//===----------------------------------------------------------------------===//
39
40/// This is the interface that must be implemented by the dialects of operations
41/// to be inlined. This interface should only handle the operations of the
42/// given dialect.
43class DialectInlinerInterface
44 : public DialectInterface::Base<DialectInlinerInterface> {
45public:
46 DialectInlinerInterface(Dialect *dialect) : Base(dialect) {}
47
48 //===--------------------------------------------------------------------===//
49 // Analysis Hooks
50 //===--------------------------------------------------------------------===//
51
52 /// Returns true if the given operation 'callable', that implements the
53 /// 'CallableOpInterface', can be inlined into the position given call
54 /// operation 'call', that is registered to the current dialect and implements
55 /// the `CallOpInterface`. 'wouldBeCloned' is set to true if the region of the
56 /// given 'callable' is set to be cloned during the inlining process, or false
57 /// if the region is set to be moved in-place(i.e. no duplicates would be
58 /// created).
59 virtual bool isLegalToInline(Operation *call, Operation *callable,
60 bool wouldBeCloned) const {
61 return false;
62 }
63
64 /// Returns true if the given region 'src' can be inlined into the region
65 /// 'dest' that is attached to an operation registered to the current dialect.
66 /// 'wouldBeCloned' is set to true if the given 'src' region is set to be
67 /// cloned during the inlining process, or false if the region is set to be
68 /// moved in-place(i.e. no duplicates would be created). 'valueMapping'
69 /// contains any remapped values from within the 'src' region. This can be
70 /// used to examine what values will replace entry arguments into the 'src'
71 /// region for example.
72 virtual bool isLegalToInline(Region *dest, Region *src, bool wouldBeCloned,
73 IRMapping &valueMapping) const {
74 return false;
75 }
76
77 /// Returns true if the given operation 'op', that is registered to this
78 /// dialect, can be inlined into the given region, false otherwise.
79 /// 'wouldBeCloned' is set to true if the given 'op' is set to be cloned
80 /// during the inlining process, or false if the operation is set to be moved
81 /// in-place(i.e. no duplicates would be created). 'valueMapping' contains any
82 /// remapped values from within the 'src' region. This can be used to examine
83 /// what values may potentially replace the operands to 'op'.
84 virtual bool isLegalToInline(Operation *op, Region *dest, bool wouldBeCloned,
85 IRMapping &valueMapping) const {
86 return false;
87 }
88
89 /// This hook is invoked on an operation that contains regions. It should
90 /// return true if the analyzer should recurse within the regions of this
91 /// operation when computing legality and cost, false otherwise. The default
92 /// implementation returns true.
93 virtual bool shouldAnalyzeRecursively(Operation *op) const { return true; }
94
95 //===--------------------------------------------------------------------===//
96 // Transformation Hooks
97 //===--------------------------------------------------------------------===//
98
99 /// Handle the given inlined terminator by replacing it with a new operation
100 /// as necessary. This overload is called when the inlined region has more
101 /// than one block. The 'newDest' block represents the new final branching
102 /// destination of blocks within this region, i.e. operations that release
103 /// control to the parent operation will likely now branch to this block.
104 /// Its block arguments correspond to any values that need to be replaced by
105 /// terminators within the inlined region.
106 virtual void handleTerminator(Operation *op, Block *newDest) const {
107 llvm_unreachable("must implement handleTerminator in the case of multiple "
108 "inlined blocks");
109 }
110
111 /// Handle the given inlined terminator by replacing it with a new operation
112 /// as necessary. This overload is called when the inlined region only
113 /// contains one block. 'valuesToReplace' contains the previously returned
114 /// values of the call site before inlining. These values must be replaced by
115 /// this callback if they had any users (for example for traditional function
116 /// calls, these are directly replaced with the operands of the `return`
117 /// operation). The given 'op' will be removed by the caller, after this
118 /// function has been called.
119 virtual void handleTerminator(Operation *op,
120 ValueRange valuesToReplace) const {
121 llvm_unreachable(
122 "must implement handleTerminator in the case of one inlined block");
123 }
124
125 /// Attempt to materialize a conversion for a type mismatch between a call
126 /// from this dialect, and a callable region. This method should generate an
127 /// operation that takes 'input' as the only operand, and produces a single
128 /// result of 'resultType'. If a conversion can not be generated, nullptr
129 /// should be returned. For example, this hook may be invoked in the following
130 /// scenarios:
131 /// func @foo(i32) -> i32 { ... }
132 ///
133 /// // Mismatched input operand
134 /// ... = foo.call @foo(%input : i16) -> i32
135 ///
136 /// // Mismatched result type.
137 /// ... = foo.call @foo(%input : i32) -> i16
138 ///
139 /// NOTE: This hook may be invoked before the 'isLegal' checks above.
140 virtual Operation *materializeCallConversion(OpBuilder &builder, Value input,
141 Type resultType,
142 Location conversionLoc) const {
143 return nullptr;
144 }
145
146 /// Hook to transform the call arguments before using them to replace the
147 /// callee arguments. Returns a value of the same type or the `argument`
148 /// itself if nothing changed. The `argumentAttrs` dictionary is non-null even
149 /// if no attribute is present. The hook is called after converting the
150 /// callsite argument types using the materializeCallConversion callback, and
151 /// right before inlining the callee region. Any operations created using the
152 /// provided `builder` are inserted right before the inlined callee region. An
153 /// example use case is the insertion of copies for by value arguments.
154 virtual Value handleArgument(OpBuilder &builder, Operation *call,
155 Operation *callable, Value argument,
156 DictionaryAttr argumentAttrs) const {
157 return argument;
158 }
159
160 /// Hook to transform the callee results before using them to replace the call
161 /// results. Returns a value of the same type or the `result` itself if
162 /// nothing changed. The `resultAttrs` dictionary is non-null even if no
163 /// attribute is present. The hook is called right before handling
164 /// terminators, and obtains the callee result before converting its type
165 /// using the `materializeCallConversion` callback. Any operations created
166 /// using the provided `builder` are inserted right after the inlined callee
167 /// region. An example use case is the insertion of copies for by value
168 /// results. NOTE: This hook is invoked after inlining the `callable` region.
169 virtual Value handleResult(OpBuilder &builder, Operation *call,
170 Operation *callable, Value result,
171 DictionaryAttr resultAttrs) const {
172 return result;
173 }
174
175 /// Process a set of blocks that have been inlined for a call. This callback
176 /// is invoked before inlined terminator operations have been processed.
177 virtual void processInlinedCallBlocks(
178 Operation *call, iterator_range<Region::iterator> inlinedBlocks) const {}
179};
180
181/// This interface provides the hooks into the inlining interface.
182/// Note: this class automatically collects 'DialectInlinerInterface' objects
183/// registered to each dialect within the given context.
184class InlinerInterface
185 : public DialectInterfaceCollection<DialectInlinerInterface> {
186public:
187 using Base::Base;
188
189 /// Process a set of blocks that have been inlined. This callback is invoked
190 /// *before* inlined terminator operations have been processed.
191 virtual void
192 processInlinedBlocks(iterator_range<Region::iterator> inlinedBlocks) {}
193
194 /// These hooks mirror the hooks for the DialectInlinerInterface, with default
195 /// implementations that call the hook on the handler for the dialect 'op' is
196 /// registered to.
197
198 //===--------------------------------------------------------------------===//
199 // Analysis Hooks
200 //===--------------------------------------------------------------------===//
201
202 virtual bool isLegalToInline(Operation *call, Operation *callable,
203 bool wouldBeCloned) const;
204 virtual bool isLegalToInline(Region *dest, Region *src, bool wouldBeCloned,
205 IRMapping &valueMapping) const;
206 virtual bool isLegalToInline(Operation *op, Region *dest, bool wouldBeCloned,
207 IRMapping &valueMapping) const;
208 virtual bool shouldAnalyzeRecursively(Operation *op) const;
209
210 //===--------------------------------------------------------------------===//
211 // Transformation Hooks
212 //===--------------------------------------------------------------------===//
213
214 virtual void handleTerminator(Operation *op, Block *newDest) const;
215 virtual void handleTerminator(Operation *op, ValueRange valuesToRepl) const;
216
217 virtual Value handleArgument(OpBuilder &builder, Operation *call,
218 Operation *callable, Value argument,
219 DictionaryAttr argumentAttrs) const;
220 virtual Value handleResult(OpBuilder &builder, Operation *call,
221 Operation *callable, Value result,
222 DictionaryAttr resultAttrs) const;
223
224 virtual void processInlinedCallBlocks(
225 Operation *call, iterator_range<Region::iterator> inlinedBlocks) const;
226};
227
228//===----------------------------------------------------------------------===//
229// Inline Methods.
230//===----------------------------------------------------------------------===//
231
232/// This function inlines a region, 'src', into another. This function returns
233/// failure if it is not possible to inline this function. If the function
234/// returned failure, then no changes to the module have been made.
235///
236/// The provided 'inlinePoint' must be within a region, and corresponds to the
237/// location where the 'src' region should be inlined. 'mapping' contains any
238/// remapped operands that are used within the region, and *must* include
239/// remappings for the entry arguments to the region. 'resultsToReplace'
240/// corresponds to any results that should be replaced by terminators within the
241/// inlined region. 'regionResultTypes' specifies the expected return types of
242/// the terminators in the region. 'inlineLoc' is an optional Location that, if
243/// provided, will be used to update the inlined operations' location
244/// information. 'shouldCloneInlinedRegion' corresponds to whether the source
245/// region should be cloned into the 'inlinePoint' or spliced directly.
246LogicalResult inlineRegion(InlinerInterface &interface, Region *src,
247 Operation *inlinePoint, IRMapping &mapper,
248 ValueRange resultsToReplace,
249 TypeRange regionResultTypes,
250 std::optional<Location> inlineLoc = std::nullopt,
251 bool shouldCloneInlinedRegion = true);
252LogicalResult inlineRegion(InlinerInterface &interface, Region *src,
253 Block *inlineBlock, Block::iterator inlinePoint,
254 IRMapping &mapper, ValueRange resultsToReplace,
255 TypeRange regionResultTypes,
256 std::optional<Location> inlineLoc = std::nullopt,
257 bool shouldCloneInlinedRegion = true);
258
259/// This function is an overload of the above 'inlineRegion' that allows for
260/// providing the set of operands ('inlinedOperands') that should be used
261/// in-favor of the region arguments when inlining.
262LogicalResult inlineRegion(InlinerInterface &interface, Region *src,
263 Operation *inlinePoint, ValueRange inlinedOperands,
264 ValueRange resultsToReplace,
265 std::optional<Location> inlineLoc = std::nullopt,
266 bool shouldCloneInlinedRegion = true);
267LogicalResult inlineRegion(InlinerInterface &interface, Region *src,
268 Block *inlineBlock, Block::iterator inlinePoint,
269 ValueRange inlinedOperands,
270 ValueRange resultsToReplace,
271 std::optional<Location> inlineLoc = std::nullopt,
272 bool shouldCloneInlinedRegion = true);
273
274/// This function inlines a given region, 'src', of a callable operation,
275/// 'callable', into the location defined by the given call operation. This
276/// function returns failure if inlining is not possible, success otherwise. On
277/// failure, no changes are made to the module. 'shouldCloneInlinedRegion'
278/// corresponds to whether the source region should be cloned into the 'call' or
279/// spliced directly.
280LogicalResult inlineCall(InlinerInterface &interface, CallOpInterface call,
281 CallableOpInterface callable, Region *src,
282 bool shouldCloneInlinedRegion = true);
283
284} // namespace mlir
285
286#endif // MLIR_TRANSFORMS_INLININGUTILS_H
287

source code of mlir/include/mlir/Transforms/InliningUtils.h