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