-
Notifications
You must be signed in to change notification settings - Fork 10.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[mlir] Move InlinerInterface from Transforms to Interfaces. #84878
Conversation
@llvm/pr-subscribers-mlir-shape @llvm/pr-subscribers-mlir-llvm Author: Christian Sigg (chsigg) ChangesPatch is 60.93 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/84878.diff 46 Files Affected:
diff --git a/mlir/include/mlir/Interfaces/InlinerInterface.h b/mlir/include/mlir/Interfaces/InlinerInterface.h
new file mode 100644
index 00000000000000..1caee6c785f495
--- /dev/null
+++ b/mlir/include/mlir/Interfaces/InlinerInterface.h
@@ -0,0 +1,221 @@
+//===- InlinerInterface.h - Inliner interface -------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This header file defines interfaces for various inlining utility methods.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef MLIR_INTERFACES_INLINERINTERFACE_H
+#define MLIR_INTERFACES_INLINERINTERFACE_H
+
+#include "mlir/IR/BuiltinAttributes.h"
+#include "mlir/IR/DialectInterface.h"
+#include "mlir/IR/Location.h"
+#include "mlir/IR/Region.h"
+#include "mlir/IR/ValueRange.h"
+
+namespace mlir {
+
+class IRMapping;
+class OpBuilder;
+
+//===----------------------------------------------------------------------===//
+// InlinerInterface
+//===----------------------------------------------------------------------===//
+
+/// This is the interface that must be implemented by the dialects of operations
+/// to be inlined. This interface should only handle the operations of the
+/// given dialect.
+class DialectInlinerInterface
+ : public DialectInterface::Base<DialectInlinerInterface> {
+public:
+ DialectInlinerInterface(Dialect *dialect) : Base(dialect) {}
+
+ //===--------------------------------------------------------------------===//
+ // Analysis Hooks
+ //===--------------------------------------------------------------------===//
+
+ /// Returns true if the given operation 'callable', that implements the
+ /// 'CallableOpInterface', can be inlined into the position given call
+ /// operation 'call', that is registered to the current dialect and implements
+ /// the `CallOpInterface`. 'wouldBeCloned' is set to true if the region of the
+ /// given 'callable' is set to be cloned during the inlining process, or false
+ /// if the region is set to be moved in-place(i.e. no duplicates would be
+ /// created).
+ virtual bool isLegalToInline(Operation *call, Operation *callable,
+ bool wouldBeCloned) const {
+ return false;
+ }
+
+ /// Returns true if the given region 'src' can be inlined into the region
+ /// 'dest' that is attached to an operation registered to the current dialect.
+ /// 'wouldBeCloned' is set to true if the given 'src' region is set to be
+ /// cloned during the inlining process, or false if the region is set to be
+ /// moved in-place(i.e. no duplicates would be created). 'valueMapping'
+ /// contains any remapped values from within the 'src' region. This can be
+ /// used to examine what values will replace entry arguments into the 'src'
+ /// region for example.
+ virtual bool isLegalToInline(Region *dest, Region *src, bool wouldBeCloned,
+ IRMapping &valueMapping) const {
+ return false;
+ }
+
+ /// Returns true if the given operation 'op', that is registered to this
+ /// dialect, can be inlined into the given region, false otherwise.
+ /// 'wouldBeCloned' is set to true if the given 'op' is set to be cloned
+ /// during the inlining process, or false if the operation is set to be moved
+ /// in-place(i.e. no duplicates would be created). 'valueMapping' contains any
+ /// remapped values from within the 'src' region. This can be used to examine
+ /// what values may potentially replace the operands to 'op'.
+ virtual bool isLegalToInline(Operation *op, Region *dest, bool wouldBeCloned,
+ IRMapping &valueMapping) const {
+ return false;
+ }
+
+ /// This hook is invoked on an operation that contains regions. It should
+ /// return true if the analyzer should recurse within the regions of this
+ /// operation when computing legality and cost, false otherwise. The default
+ /// implementation returns true.
+ virtual bool shouldAnalyzeRecursively(Operation *op) const { return true; }
+
+ //===--------------------------------------------------------------------===//
+ // Transformation Hooks
+ //===--------------------------------------------------------------------===//
+
+ /// Handle the given inlined terminator by replacing it with a new operation
+ /// as necessary. This overload is called when the inlined region has more
+ /// than one block. The 'newDest' block represents the new final branching
+ /// destination of blocks within this region, i.e. operations that release
+ /// control to the parent operation will likely now branch to this block.
+ /// Its block arguments correspond to any values that need to be replaced by
+ /// terminators within the inlined region.
+ virtual void handleTerminator(Operation *op, Block *newDest) const {
+ llvm_unreachable("must implement handleTerminator in the case of multiple "
+ "inlined blocks");
+ }
+
+ /// Handle the given inlined terminator by replacing it with a new operation
+ /// as necessary. This overload is called when the inlined region only
+ /// contains one block. 'valuesToReplace' contains the previously returned
+ /// values of the call site before inlining. These values must be replaced by
+ /// this callback if they had any users (for example for traditional function
+ /// calls, these are directly replaced with the operands of the `return`
+ /// operation). The given 'op' will be removed by the caller, after this
+ /// function has been called.
+ virtual void handleTerminator(Operation *op,
+ ValueRange valuesToReplace) const {
+ llvm_unreachable(
+ "must implement handleTerminator in the case of one inlined block");
+ }
+
+ /// Attempt to materialize a conversion for a type mismatch between a call
+ /// from this dialect, and a callable region. This method should generate an
+ /// operation that takes 'input' as the only operand, and produces a single
+ /// result of 'resultType'. If a conversion can not be generated, nullptr
+ /// should be returned. For example, this hook may be invoked in the following
+ /// scenarios:
+ /// func @foo(i32) -> i32 { ... }
+ ///
+ /// // Mismatched input operand
+ /// ... = foo.call @foo(%input : i16) -> i32
+ ///
+ /// // Mismatched result type.
+ /// ... = foo.call @foo(%input : i32) -> i16
+ ///
+ /// NOTE: This hook may be invoked before the 'isLegal' checks above.
+ virtual Operation *materializeCallConversion(OpBuilder &builder, Value input,
+ Type resultType,
+ Location conversionLoc) const {
+ return nullptr;
+ }
+
+ /// Hook to transform the call arguments before using them to replace the
+ /// callee arguments. Returns a value of the same type or the `argument`
+ /// itself if nothing changed. The `argumentAttrs` dictionary is non-null even
+ /// if no attribute is present. The hook is called after converting the
+ /// callsite argument types using the materializeCallConversion callback, and
+ /// right before inlining the callee region. Any operations created using the
+ /// provided `builder` are inserted right before the inlined callee region. An
+ /// example use case is the insertion of copies for by value arguments.
+ virtual Value handleArgument(OpBuilder &builder, Operation *call,
+ Operation *callable, Value argument,
+ DictionaryAttr argumentAttrs) const {
+ return argument;
+ }
+
+ /// Hook to transform the callee results before using them to replace the call
+ /// results. Returns a value of the same type or the `result` itself if
+ /// nothing changed. The `resultAttrs` dictionary is non-null even if no
+ /// attribute is present. The hook is called right before handling
+ /// terminators, and obtains the callee result before converting its type
+ /// using the `materializeCallConversion` callback. Any operations created
+ /// using the provided `builder` are inserted right after the inlined callee
+ /// region. An example use case is the insertion of copies for by value
+ /// results. NOTE: This hook is invoked after inlining the `callable` region.
+ virtual Value handleResult(OpBuilder &builder, Operation *call,
+ Operation *callable, Value result,
+ DictionaryAttr resultAttrs) const {
+ return result;
+ }
+
+ /// Process a set of blocks that have been inlined for a call. This callback
+ /// is invoked before inlined terminator operations have been processed.
+ virtual void processInlinedCallBlocks(
+ Operation *call, iterator_range<Region::iterator> inlinedBlocks) const {}
+};
+
+/// This interface provides the hooks into the inlining interface.
+/// Note: this class automatically collects 'DialectInlinerInterface' objects
+/// registered to each dialect within the given context.
+class InlinerInterface
+ : public DialectInterfaceCollection<DialectInlinerInterface> {
+public:
+ using Base::Base;
+
+ /// Process a set of blocks that have been inlined. This callback is invoked
+ /// *before* inlined terminator operations have been processed.
+ virtual void
+ processInlinedBlocks(iterator_range<Region::iterator> inlinedBlocks) {}
+
+ /// These hooks mirror the hooks for the DialectInlinerInterface, with default
+ /// implementations that call the hook on the handler for the dialect 'op' is
+ /// registered to.
+
+ //===--------------------------------------------------------------------===//
+ // Analysis Hooks
+ //===--------------------------------------------------------------------===//
+
+ virtual bool isLegalToInline(Operation *call, Operation *callable,
+ bool wouldBeCloned) const;
+ virtual bool isLegalToInline(Region *dest, Region *src, bool wouldBeCloned,
+ IRMapping &valueMapping) const;
+ virtual bool isLegalToInline(Operation *op, Region *dest, bool wouldBeCloned,
+ IRMapping &valueMapping) const;
+ virtual bool shouldAnalyzeRecursively(Operation *op) const;
+
+ //===--------------------------------------------------------------------===//
+ // Transformation Hooks
+ //===--------------------------------------------------------------------===//
+
+ virtual void handleTerminator(Operation *op, Block *newDest) const;
+ virtual void handleTerminator(Operation *op, ValueRange valuesToRepl) const;
+
+ virtual Value handleArgument(OpBuilder &builder, Operation *call,
+ Operation *callable, Value argument,
+ DictionaryAttr argumentAttrs) const;
+ virtual Value handleResult(OpBuilder &builder, Operation *call,
+ Operation *callable, Value result,
+ DictionaryAttr resultAttrs) const;
+
+ virtual void processInlinedCallBlocks(
+ Operation *call, iterator_range<Region::iterator> inlinedBlocks) const;
+};
+
+} // namespace mlir
+
+#endif // MLIR_INTERFACES_INLINERINTERFACE_H
diff --git a/mlir/include/mlir/Transforms/InliningUtils.h b/mlir/include/mlir/Transforms/InliningUtils.h
index 88fc033a6ab7be..16d12075fee9c4 100644
--- a/mlir/include/mlir/Transforms/InliningUtils.h
+++ b/mlir/include/mlir/Transforms/InliningUtils.h
@@ -13,217 +13,14 @@
#ifndef MLIR_TRANSFORMS_INLININGUTILS_H
#define MLIR_TRANSFORMS_INLININGUTILS_H
-#include "mlir/IR/BuiltinAttributes.h"
-#include "mlir/IR/DialectInterface.h"
-#include "mlir/IR/Location.h"
-#include "mlir/IR/Region.h"
#include "mlir/IR/ValueRange.h"
+#include "mlir/Interfaces/InlinerInterface.h"
#include <optional>
namespace mlir {
-class Block;
-class IRMapping;
class CallableOpInterface;
class CallOpInterface;
-class OpBuilder;
-class Operation;
-class Region;
-class TypeRange;
-class Value;
-class ValueRange;
-
-//===----------------------------------------------------------------------===//
-// InlinerInterface
-//===----------------------------------------------------------------------===//
-
-/// This is the interface that must be implemented by the dialects of operations
-/// to be inlined. This interface should only handle the operations of the
-/// given dialect.
-class DialectInlinerInterface
- : public DialectInterface::Base<DialectInlinerInterface> {
-public:
- DialectInlinerInterface(Dialect *dialect) : Base(dialect) {}
-
- //===--------------------------------------------------------------------===//
- // Analysis Hooks
- //===--------------------------------------------------------------------===//
-
- /// Returns true if the given operation 'callable', that implements the
- /// 'CallableOpInterface', can be inlined into the position given call
- /// operation 'call', that is registered to the current dialect and implements
- /// the `CallOpInterface`. 'wouldBeCloned' is set to true if the region of the
- /// given 'callable' is set to be cloned during the inlining process, or false
- /// if the region is set to be moved in-place(i.e. no duplicates would be
- /// created).
- virtual bool isLegalToInline(Operation *call, Operation *callable,
- bool wouldBeCloned) const {
- return false;
- }
-
- /// Returns true if the given region 'src' can be inlined into the region
- /// 'dest' that is attached to an operation registered to the current dialect.
- /// 'wouldBeCloned' is set to true if the given 'src' region is set to be
- /// cloned during the inlining process, or false if the region is set to be
- /// moved in-place(i.e. no duplicates would be created). 'valueMapping'
- /// contains any remapped values from within the 'src' region. This can be
- /// used to examine what values will replace entry arguments into the 'src'
- /// region for example.
- virtual bool isLegalToInline(Region *dest, Region *src, bool wouldBeCloned,
- IRMapping &valueMapping) const {
- return false;
- }
-
- /// Returns true if the given operation 'op', that is registered to this
- /// dialect, can be inlined into the given region, false otherwise.
- /// 'wouldBeCloned' is set to true if the given 'op' is set to be cloned
- /// during the inlining process, or false if the operation is set to be moved
- /// in-place(i.e. no duplicates would be created). 'valueMapping' contains any
- /// remapped values from within the 'src' region. This can be used to examine
- /// what values may potentially replace the operands to 'op'.
- virtual bool isLegalToInline(Operation *op, Region *dest, bool wouldBeCloned,
- IRMapping &valueMapping) const {
- return false;
- }
-
- /// This hook is invoked on an operation that contains regions. It should
- /// return true if the analyzer should recurse within the regions of this
- /// operation when computing legality and cost, false otherwise. The default
- /// implementation returns true.
- virtual bool shouldAnalyzeRecursively(Operation *op) const { return true; }
-
- //===--------------------------------------------------------------------===//
- // Transformation Hooks
- //===--------------------------------------------------------------------===//
-
- /// Handle the given inlined terminator by replacing it with a new operation
- /// as necessary. This overload is called when the inlined region has more
- /// than one block. The 'newDest' block represents the new final branching
- /// destination of blocks within this region, i.e. operations that release
- /// control to the parent operation will likely now branch to this block.
- /// Its block arguments correspond to any values that need to be replaced by
- /// terminators within the inlined region.
- virtual void handleTerminator(Operation *op, Block *newDest) const {
- llvm_unreachable("must implement handleTerminator in the case of multiple "
- "inlined blocks");
- }
-
- /// Handle the given inlined terminator by replacing it with a new operation
- /// as necessary. This overload is called when the inlined region only
- /// contains one block. 'valuesToReplace' contains the previously returned
- /// values of the call site before inlining. These values must be replaced by
- /// this callback if they had any users (for example for traditional function
- /// calls, these are directly replaced with the operands of the `return`
- /// operation). The given 'op' will be removed by the caller, after this
- /// function has been called.
- virtual void handleTerminator(Operation *op,
- ValueRange valuesToReplace) const {
- llvm_unreachable(
- "must implement handleTerminator in the case of one inlined block");
- }
-
- /// Attempt to materialize a conversion for a type mismatch between a call
- /// from this dialect, and a callable region. This method should generate an
- /// operation that takes 'input' as the only operand, and produces a single
- /// result of 'resultType'. If a conversion can not be generated, nullptr
- /// should be returned. For example, this hook may be invoked in the following
- /// scenarios:
- /// func @foo(i32) -> i32 { ... }
- ///
- /// // Mismatched input operand
- /// ... = foo.call @foo(%input : i16) -> i32
- ///
- /// // Mismatched result type.
- /// ... = foo.call @foo(%input : i32) -> i16
- ///
- /// NOTE: This hook may be invoked before the 'isLegal' checks above.
- virtual Operation *materializeCallConversion(OpBuilder &builder, Value input,
- Type resultType,
- Location conversionLoc) const {
- return nullptr;
- }
-
- /// Hook to transform the call arguments before using them to replace the
- /// callee arguments. Returns a value of the same type or the `argument`
- /// itself if nothing changed. The `argumentAttrs` dictionary is non-null even
- /// if no attribute is present. The hook is called after converting the
- /// callsite argument types using the materializeCallConversion callback, and
- /// right before inlining the callee region. Any operations created using the
- /// provided `builder` are inserted right before the inlined callee region. An
- /// example use case is the insertion of copies for by value arguments.
- virtual Value handleArgument(OpBuilder &builder, Operation *call,
- Operation *callable, Value argument,
- DictionaryAttr argumentAttrs) const {
- return argument;
- }
-
- /// Hook to transform the callee results before using them to replace the call
- /// results. Returns a value of the same type or the `result` itself if
- /// nothing changed. The `resultAttrs` dictionary is non-null even if no
- /// attribute is present. The hook is called right before handling
- /// terminators, and obtains the callee result before converting its type
- /// using the `materializeCallConversion` callback. Any operations created
- /// using the provided `builder` are inserted right after the inlined callee
- /// region. An example use case is the insertion of copies for by value
- /// results. NOTE: This hook is invoked after inlining the `callable` region.
- virtual Value handleResult(OpBuilder &builder, Operation *call,
- Operation *callable, Value result,
- DictionaryAttr resultAttrs) const {
- return result;
- }
-
- /// Process a set of blocks that have been inlined for a call. This callback
- /// is invoked before inlined terminator operations have been processed.
- virtual void processInlinedCallBlocks(
- Operation *call, iterator_range<Region::iterator> inlinedBlocks) const {}
-};
-
-/// This interface provides the hooks into the inlining interface.
-/// Note: this class automatically collects 'DialectInlinerInterface' objects
-/// registered to each dialect within the given context.
-class InlinerInterface
- : public DialectInterfaceCollection<DialectInlinerInterface> {
-public:
- using Base::Base;
-...
[truncated]
|
@llvm/pr-subscribers-mlir-core Author: Christian Sigg (chsigg) ChangesPatch is 60.93 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/84878.diff 46 Files Affected:
diff --git a/mlir/include/mlir/Interfaces/InlinerInterface.h b/mlir/include/mlir/Interfaces/InlinerInterface.h
new file mode 100644
index 00000000000000..1caee6c785f495
--- /dev/null
+++ b/mlir/include/mlir/Interfaces/InlinerInterface.h
@@ -0,0 +1,221 @@
+//===- InlinerInterface.h - Inliner interface -------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This header file defines interfaces for various inlining utility methods.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef MLIR_INTERFACES_INLINERINTERFACE_H
+#define MLIR_INTERFACES_INLINERINTERFACE_H
+
+#include "mlir/IR/BuiltinAttributes.h"
+#include "mlir/IR/DialectInterface.h"
+#include "mlir/IR/Location.h"
+#include "mlir/IR/Region.h"
+#include "mlir/IR/ValueRange.h"
+
+namespace mlir {
+
+class IRMapping;
+class OpBuilder;
+
+//===----------------------------------------------------------------------===//
+// InlinerInterface
+//===----------------------------------------------------------------------===//
+
+/// This is the interface that must be implemented by the dialects of operations
+/// to be inlined. This interface should only handle the operations of the
+/// given dialect.
+class DialectInlinerInterface
+ : public DialectInterface::Base<DialectInlinerInterface> {
+public:
+ DialectInlinerInterface(Dialect *dialect) : Base(dialect) {}
+
+ //===--------------------------------------------------------------------===//
+ // Analysis Hooks
+ //===--------------------------------------------------------------------===//
+
+ /// Returns true if the given operation 'callable', that implements the
+ /// 'CallableOpInterface', can be inlined into the position given call
+ /// operation 'call', that is registered to the current dialect and implements
+ /// the `CallOpInterface`. 'wouldBeCloned' is set to true if the region of the
+ /// given 'callable' is set to be cloned during the inlining process, or false
+ /// if the region is set to be moved in-place(i.e. no duplicates would be
+ /// created).
+ virtual bool isLegalToInline(Operation *call, Operation *callable,
+ bool wouldBeCloned) const {
+ return false;
+ }
+
+ /// Returns true if the given region 'src' can be inlined into the region
+ /// 'dest' that is attached to an operation registered to the current dialect.
+ /// 'wouldBeCloned' is set to true if the given 'src' region is set to be
+ /// cloned during the inlining process, or false if the region is set to be
+ /// moved in-place(i.e. no duplicates would be created). 'valueMapping'
+ /// contains any remapped values from within the 'src' region. This can be
+ /// used to examine what values will replace entry arguments into the 'src'
+ /// region for example.
+ virtual bool isLegalToInline(Region *dest, Region *src, bool wouldBeCloned,
+ IRMapping &valueMapping) const {
+ return false;
+ }
+
+ /// Returns true if the given operation 'op', that is registered to this
+ /// dialect, can be inlined into the given region, false otherwise.
+ /// 'wouldBeCloned' is set to true if the given 'op' is set to be cloned
+ /// during the inlining process, or false if the operation is set to be moved
+ /// in-place(i.e. no duplicates would be created). 'valueMapping' contains any
+ /// remapped values from within the 'src' region. This can be used to examine
+ /// what values may potentially replace the operands to 'op'.
+ virtual bool isLegalToInline(Operation *op, Region *dest, bool wouldBeCloned,
+ IRMapping &valueMapping) const {
+ return false;
+ }
+
+ /// This hook is invoked on an operation that contains regions. It should
+ /// return true if the analyzer should recurse within the regions of this
+ /// operation when computing legality and cost, false otherwise. The default
+ /// implementation returns true.
+ virtual bool shouldAnalyzeRecursively(Operation *op) const { return true; }
+
+ //===--------------------------------------------------------------------===//
+ // Transformation Hooks
+ //===--------------------------------------------------------------------===//
+
+ /// Handle the given inlined terminator by replacing it with a new operation
+ /// as necessary. This overload is called when the inlined region has more
+ /// than one block. The 'newDest' block represents the new final branching
+ /// destination of blocks within this region, i.e. operations that release
+ /// control to the parent operation will likely now branch to this block.
+ /// Its block arguments correspond to any values that need to be replaced by
+ /// terminators within the inlined region.
+ virtual void handleTerminator(Operation *op, Block *newDest) const {
+ llvm_unreachable("must implement handleTerminator in the case of multiple "
+ "inlined blocks");
+ }
+
+ /// Handle the given inlined terminator by replacing it with a new operation
+ /// as necessary. This overload is called when the inlined region only
+ /// contains one block. 'valuesToReplace' contains the previously returned
+ /// values of the call site before inlining. These values must be replaced by
+ /// this callback if they had any users (for example for traditional function
+ /// calls, these are directly replaced with the operands of the `return`
+ /// operation). The given 'op' will be removed by the caller, after this
+ /// function has been called.
+ virtual void handleTerminator(Operation *op,
+ ValueRange valuesToReplace) const {
+ llvm_unreachable(
+ "must implement handleTerminator in the case of one inlined block");
+ }
+
+ /// Attempt to materialize a conversion for a type mismatch between a call
+ /// from this dialect, and a callable region. This method should generate an
+ /// operation that takes 'input' as the only operand, and produces a single
+ /// result of 'resultType'. If a conversion can not be generated, nullptr
+ /// should be returned. For example, this hook may be invoked in the following
+ /// scenarios:
+ /// func @foo(i32) -> i32 { ... }
+ ///
+ /// // Mismatched input operand
+ /// ... = foo.call @foo(%input : i16) -> i32
+ ///
+ /// // Mismatched result type.
+ /// ... = foo.call @foo(%input : i32) -> i16
+ ///
+ /// NOTE: This hook may be invoked before the 'isLegal' checks above.
+ virtual Operation *materializeCallConversion(OpBuilder &builder, Value input,
+ Type resultType,
+ Location conversionLoc) const {
+ return nullptr;
+ }
+
+ /// Hook to transform the call arguments before using them to replace the
+ /// callee arguments. Returns a value of the same type or the `argument`
+ /// itself if nothing changed. The `argumentAttrs` dictionary is non-null even
+ /// if no attribute is present. The hook is called after converting the
+ /// callsite argument types using the materializeCallConversion callback, and
+ /// right before inlining the callee region. Any operations created using the
+ /// provided `builder` are inserted right before the inlined callee region. An
+ /// example use case is the insertion of copies for by value arguments.
+ virtual Value handleArgument(OpBuilder &builder, Operation *call,
+ Operation *callable, Value argument,
+ DictionaryAttr argumentAttrs) const {
+ return argument;
+ }
+
+ /// Hook to transform the callee results before using them to replace the call
+ /// results. Returns a value of the same type or the `result` itself if
+ /// nothing changed. The `resultAttrs` dictionary is non-null even if no
+ /// attribute is present. The hook is called right before handling
+ /// terminators, and obtains the callee result before converting its type
+ /// using the `materializeCallConversion` callback. Any operations created
+ /// using the provided `builder` are inserted right after the inlined callee
+ /// region. An example use case is the insertion of copies for by value
+ /// results. NOTE: This hook is invoked after inlining the `callable` region.
+ virtual Value handleResult(OpBuilder &builder, Operation *call,
+ Operation *callable, Value result,
+ DictionaryAttr resultAttrs) const {
+ return result;
+ }
+
+ /// Process a set of blocks that have been inlined for a call. This callback
+ /// is invoked before inlined terminator operations have been processed.
+ virtual void processInlinedCallBlocks(
+ Operation *call, iterator_range<Region::iterator> inlinedBlocks) const {}
+};
+
+/// This interface provides the hooks into the inlining interface.
+/// Note: this class automatically collects 'DialectInlinerInterface' objects
+/// registered to each dialect within the given context.
+class InlinerInterface
+ : public DialectInterfaceCollection<DialectInlinerInterface> {
+public:
+ using Base::Base;
+
+ /// Process a set of blocks that have been inlined. This callback is invoked
+ /// *before* inlined terminator operations have been processed.
+ virtual void
+ processInlinedBlocks(iterator_range<Region::iterator> inlinedBlocks) {}
+
+ /// These hooks mirror the hooks for the DialectInlinerInterface, with default
+ /// implementations that call the hook on the handler for the dialect 'op' is
+ /// registered to.
+
+ //===--------------------------------------------------------------------===//
+ // Analysis Hooks
+ //===--------------------------------------------------------------------===//
+
+ virtual bool isLegalToInline(Operation *call, Operation *callable,
+ bool wouldBeCloned) const;
+ virtual bool isLegalToInline(Region *dest, Region *src, bool wouldBeCloned,
+ IRMapping &valueMapping) const;
+ virtual bool isLegalToInline(Operation *op, Region *dest, bool wouldBeCloned,
+ IRMapping &valueMapping) const;
+ virtual bool shouldAnalyzeRecursively(Operation *op) const;
+
+ //===--------------------------------------------------------------------===//
+ // Transformation Hooks
+ //===--------------------------------------------------------------------===//
+
+ virtual void handleTerminator(Operation *op, Block *newDest) const;
+ virtual void handleTerminator(Operation *op, ValueRange valuesToRepl) const;
+
+ virtual Value handleArgument(OpBuilder &builder, Operation *call,
+ Operation *callable, Value argument,
+ DictionaryAttr argumentAttrs) const;
+ virtual Value handleResult(OpBuilder &builder, Operation *call,
+ Operation *callable, Value result,
+ DictionaryAttr resultAttrs) const;
+
+ virtual void processInlinedCallBlocks(
+ Operation *call, iterator_range<Region::iterator> inlinedBlocks) const;
+};
+
+} // namespace mlir
+
+#endif // MLIR_INTERFACES_INLINERINTERFACE_H
diff --git a/mlir/include/mlir/Transforms/InliningUtils.h b/mlir/include/mlir/Transforms/InliningUtils.h
index 88fc033a6ab7be..16d12075fee9c4 100644
--- a/mlir/include/mlir/Transforms/InliningUtils.h
+++ b/mlir/include/mlir/Transforms/InliningUtils.h
@@ -13,217 +13,14 @@
#ifndef MLIR_TRANSFORMS_INLININGUTILS_H
#define MLIR_TRANSFORMS_INLININGUTILS_H
-#include "mlir/IR/BuiltinAttributes.h"
-#include "mlir/IR/DialectInterface.h"
-#include "mlir/IR/Location.h"
-#include "mlir/IR/Region.h"
#include "mlir/IR/ValueRange.h"
+#include "mlir/Interfaces/InlinerInterface.h"
#include <optional>
namespace mlir {
-class Block;
-class IRMapping;
class CallableOpInterface;
class CallOpInterface;
-class OpBuilder;
-class Operation;
-class Region;
-class TypeRange;
-class Value;
-class ValueRange;
-
-//===----------------------------------------------------------------------===//
-// InlinerInterface
-//===----------------------------------------------------------------------===//
-
-/// This is the interface that must be implemented by the dialects of operations
-/// to be inlined. This interface should only handle the operations of the
-/// given dialect.
-class DialectInlinerInterface
- : public DialectInterface::Base<DialectInlinerInterface> {
-public:
- DialectInlinerInterface(Dialect *dialect) : Base(dialect) {}
-
- //===--------------------------------------------------------------------===//
- // Analysis Hooks
- //===--------------------------------------------------------------------===//
-
- /// Returns true if the given operation 'callable', that implements the
- /// 'CallableOpInterface', can be inlined into the position given call
- /// operation 'call', that is registered to the current dialect and implements
- /// the `CallOpInterface`. 'wouldBeCloned' is set to true if the region of the
- /// given 'callable' is set to be cloned during the inlining process, or false
- /// if the region is set to be moved in-place(i.e. no duplicates would be
- /// created).
- virtual bool isLegalToInline(Operation *call, Operation *callable,
- bool wouldBeCloned) const {
- return false;
- }
-
- /// Returns true if the given region 'src' can be inlined into the region
- /// 'dest' that is attached to an operation registered to the current dialect.
- /// 'wouldBeCloned' is set to true if the given 'src' region is set to be
- /// cloned during the inlining process, or false if the region is set to be
- /// moved in-place(i.e. no duplicates would be created). 'valueMapping'
- /// contains any remapped values from within the 'src' region. This can be
- /// used to examine what values will replace entry arguments into the 'src'
- /// region for example.
- virtual bool isLegalToInline(Region *dest, Region *src, bool wouldBeCloned,
- IRMapping &valueMapping) const {
- return false;
- }
-
- /// Returns true if the given operation 'op', that is registered to this
- /// dialect, can be inlined into the given region, false otherwise.
- /// 'wouldBeCloned' is set to true if the given 'op' is set to be cloned
- /// during the inlining process, or false if the operation is set to be moved
- /// in-place(i.e. no duplicates would be created). 'valueMapping' contains any
- /// remapped values from within the 'src' region. This can be used to examine
- /// what values may potentially replace the operands to 'op'.
- virtual bool isLegalToInline(Operation *op, Region *dest, bool wouldBeCloned,
- IRMapping &valueMapping) const {
- return false;
- }
-
- /// This hook is invoked on an operation that contains regions. It should
- /// return true if the analyzer should recurse within the regions of this
- /// operation when computing legality and cost, false otherwise. The default
- /// implementation returns true.
- virtual bool shouldAnalyzeRecursively(Operation *op) const { return true; }
-
- //===--------------------------------------------------------------------===//
- // Transformation Hooks
- //===--------------------------------------------------------------------===//
-
- /// Handle the given inlined terminator by replacing it with a new operation
- /// as necessary. This overload is called when the inlined region has more
- /// than one block. The 'newDest' block represents the new final branching
- /// destination of blocks within this region, i.e. operations that release
- /// control to the parent operation will likely now branch to this block.
- /// Its block arguments correspond to any values that need to be replaced by
- /// terminators within the inlined region.
- virtual void handleTerminator(Operation *op, Block *newDest) const {
- llvm_unreachable("must implement handleTerminator in the case of multiple "
- "inlined blocks");
- }
-
- /// Handle the given inlined terminator by replacing it with a new operation
- /// as necessary. This overload is called when the inlined region only
- /// contains one block. 'valuesToReplace' contains the previously returned
- /// values of the call site before inlining. These values must be replaced by
- /// this callback if they had any users (for example for traditional function
- /// calls, these are directly replaced with the operands of the `return`
- /// operation). The given 'op' will be removed by the caller, after this
- /// function has been called.
- virtual void handleTerminator(Operation *op,
- ValueRange valuesToReplace) const {
- llvm_unreachable(
- "must implement handleTerminator in the case of one inlined block");
- }
-
- /// Attempt to materialize a conversion for a type mismatch between a call
- /// from this dialect, and a callable region. This method should generate an
- /// operation that takes 'input' as the only operand, and produces a single
- /// result of 'resultType'. If a conversion can not be generated, nullptr
- /// should be returned. For example, this hook may be invoked in the following
- /// scenarios:
- /// func @foo(i32) -> i32 { ... }
- ///
- /// // Mismatched input operand
- /// ... = foo.call @foo(%input : i16) -> i32
- ///
- /// // Mismatched result type.
- /// ... = foo.call @foo(%input : i32) -> i16
- ///
- /// NOTE: This hook may be invoked before the 'isLegal' checks above.
- virtual Operation *materializeCallConversion(OpBuilder &builder, Value input,
- Type resultType,
- Location conversionLoc) const {
- return nullptr;
- }
-
- /// Hook to transform the call arguments before using them to replace the
- /// callee arguments. Returns a value of the same type or the `argument`
- /// itself if nothing changed. The `argumentAttrs` dictionary is non-null even
- /// if no attribute is present. The hook is called after converting the
- /// callsite argument types using the materializeCallConversion callback, and
- /// right before inlining the callee region. Any operations created using the
- /// provided `builder` are inserted right before the inlined callee region. An
- /// example use case is the insertion of copies for by value arguments.
- virtual Value handleArgument(OpBuilder &builder, Operation *call,
- Operation *callable, Value argument,
- DictionaryAttr argumentAttrs) const {
- return argument;
- }
-
- /// Hook to transform the callee results before using them to replace the call
- /// results. Returns a value of the same type or the `result` itself if
- /// nothing changed. The `resultAttrs` dictionary is non-null even if no
- /// attribute is present. The hook is called right before handling
- /// terminators, and obtains the callee result before converting its type
- /// using the `materializeCallConversion` callback. Any operations created
- /// using the provided `builder` are inserted right after the inlined callee
- /// region. An example use case is the insertion of copies for by value
- /// results. NOTE: This hook is invoked after inlining the `callable` region.
- virtual Value handleResult(OpBuilder &builder, Operation *call,
- Operation *callable, Value result,
- DictionaryAttr resultAttrs) const {
- return result;
- }
-
- /// Process a set of blocks that have been inlined for a call. This callback
- /// is invoked before inlined terminator operations have been processed.
- virtual void processInlinedCallBlocks(
- Operation *call, iterator_range<Region::iterator> inlinedBlocks) const {}
-};
-
-/// This interface provides the hooks into the inlining interface.
-/// Note: this class automatically collects 'DialectInlinerInterface' objects
-/// registered to each dialect within the given context.
-class InlinerInterface
- : public DialectInterfaceCollection<DialectInlinerInterface> {
-public:
- using Base::Base;
-...
[truncated]
|
3c33c20
to
b8b9838
Compare
Nit: your PR does not have a description. The title provides the "what" but the description should provide the context / the "why". |
Thanks, I added a description. |
I don't see this layout as historical actually: to me the Interfaces folder is meant I believe to host the interfaces that aren't tied to specific transforms, instead they were meant for the common traits of the IR, basically what is needed during ODS generation or what describes the IR more generically than what's tied to a specific transform (like control-flow interface). Looking at the list right now, there are a quite a few interfaces which don't belong here and are instead directly attached to a specific transform: to me they don't belong here. @Mogball, @jpienaar, and @ftynse for the layering discussion here. |
I see. Another way to solve the circular dependency is to create a separate bazel target for just InliningUtils. After that, TransformUtils can be merged into Transforms without creating circular dependencies. Sorry for being bazel-centric here. I'm assuming that with CMake the layering is not as strict, but the layering is still not ideal. What is the relationship of MLIRTransform and MLIRTransformUtils? Would it be better to move the InlinerInterface to a separate file and CMake library that still lives under Transforms/Utils? |
I see your point from the perspective of transform-specific interfaces. If you looked at it from a dialect perspective, which currently needs to depend on stuff from transform, the change suggested here might be easier to motivate. That said, I'm happy to get guidance about what makes the most sense. I primarily see all the bazel targets that export the InliningUtils.h header, sticking out like sore thumbs. |
CMake is indeed loose with header includes, and we're taking advantage of this for interfaces includes which are "header only" for implementing in a dialect. I don't know how to model this with Bazel: would you create a "header only target" just for the header inclusion and another one for the link? |
+1 to keeping transform specific interfaces in the TransformUtils library. The |
A couple of counter-points, but I don't have a strong opinion.
|
I haven't checked how big are these libraries, but maybe we want to introduce a level of hierarchy under |
You can do that, but then a user of your dialect needs to explicitly add the link target as a dependency. Ideally, the code is structured so targets are self-contained. This isn't hard in this particular case, we can move InliningUtils.h/cpp in a separate target and move on. I was just aiming for the cleaner approach, not expecting that moving the interface would be controversial.
TransformUtils depends on various dialects via ViewLikeInterface and ArithUtils.
That's an option, yes. I just though the interface directory is the better place than Transform/Utils. The InliningInterface only depends on stuff from IR and nothing from Transforms.
That seems reasonable to me, but it's a bit of work and will likely break many downstream projects. I'm happy to do this if there is consensus that it's worth the trouble. |
This seems like a huge problem. This is also affecting all downstream users. |
Actually, there are much shorter paths, e.g.
I did find two places in TensorFlow where this seemed to be a problem, but no other ones. So hopefully this is still under control. |
No: they are really header-only dependency, you don't need the link target. You need to link to register the interface implementation only IIRC. CMake is lose with the header dependency, but has the same rules as Bazel for the link dependency (when we build with |
I hear the "header only dep" argument but it still makes me quite uncomfortable as it represents project organization lines that should not be crossed: we have got to start thinking of some of these pieces as distinct projects. |
Right, but dialect registration is part of the dialect target as well, and depending on just the header would make it not self-contained. Of course, most binary targets that depend on a few dialects will also depend on TransformUtils somehow. It does work the way it's set up right now, but the layering is not ideal and breaks e.g. managing dependencies automatically by tools. |
I don't follow, can we look at one concrete use-case right now? |
You are right, the
I fully understand that we do not want to restructure code to please the bazel build. We don't need to in this particular case (and I haven't looked into similar issues like the
The alternatives would be:
If we could agree to one of these (or anything else), that would be great. |
I looked at how we handled these cases before in Bazel, I believe the way to not confuse the tool and not have multiple targets exposing the header was to add them as srcs for the users: https://github.com/llvm/llvm-project/blob/main/utils/bazel/llvm-project-overlay/mlir/BUILD.bazel#L330 |
Yes, consuming the header file under We don't need any of those crutches in this particular case though, we can make a target that exposes and implements the |
Various (in-tree as well as downstream) targets currently depend on `InliningUtils.h` to avoid circular dependencies. E.g. `TransformUtils` depends on `ArithDialect`, so `ArithDialect` can't depend on `TransformUtils` exporting `InliningUtils.h`. This change exposes that header and it's implementation as a separate target. Having targets that implement all the declared functions is the preferred approach for bazel build graphs. See also PR #84878, which moves the interface definitions to a separate file in the `Interfaces` directory. This turned out to be controversial and putting it in a different directory didn't seem to have any support either. Instead, this PR only changes the bazel build without moving any C++ code.
Most interfaces live in the mlir/Interfaces directory, but the InlinerInterface was defined (I think for historical reasons) in
mlir/Transforms/Utils/InliningUtils.h
.Telling from the bazel BUILD files (I'm not familiar with the CMake build), this created circular dependencies which required various dialect targets to depend on
InliningUtils.h
. I was trying to clean this up, and the best solution seems to be to move the InlinerInterface from the Transforms directory to Interfaces.