From 504bb5c0dd7ce2907938d5bb1d0649e56537d04a Mon Sep 17 00:00:00 2001 From: aidint Date: Sun, 30 Nov 2025 19:40:51 +0100 Subject: [PATCH 1/6] feat(mlir-tblgen): Add support for dialect interfaces --- mlir/include/mlir/IR/Interfaces.td | 5 + mlir/include/mlir/TableGen/Interfaces.h | 7 + mlir/lib/TableGen/Interfaces.cpp | 8 + mlir/test/mlir-tblgen/dialect-interface.td | 66 +++++++ mlir/tools/mlir-tblgen/CMakeLists.txt | 1 + .../mlir-tblgen/DialectInterfacesGen.cpp | 176 ++++++++++++++++++ 6 files changed, 263 insertions(+) create mode 100644 mlir/test/mlir-tblgen/dialect-interface.td create mode 100644 mlir/tools/mlir-tblgen/DialectInterfacesGen.cpp diff --git a/mlir/include/mlir/IR/Interfaces.td b/mlir/include/mlir/IR/Interfaces.td index 0cbe3fa25c9e7..746ad3408f424 100644 --- a/mlir/include/mlir/IR/Interfaces.td +++ b/mlir/include/mlir/IR/Interfaces.td @@ -147,6 +147,11 @@ class TypeInterface baseInterfaces = []> !if(!empty(cppNamespace),"", cppNamespace # "::") # name >; +// DialectInterface represents an interface registered to an operation. +class DialectInterface baseInterfaces = []> + : Interface, OpInterfaceTrait; + + // Whether to declare the interface methods in the user entity's header. This // class simply wraps an Interface but is used to indicate that the method // declarations should be generated. This class takes an optional set of methods diff --git a/mlir/include/mlir/TableGen/Interfaces.h b/mlir/include/mlir/TableGen/Interfaces.h index 7c36cbc1192ac..f62d21da467a1 100644 --- a/mlir/include/mlir/TableGen/Interfaces.h +++ b/mlir/include/mlir/TableGen/Interfaces.h @@ -157,6 +157,13 @@ struct TypeInterface : public Interface { static bool classof(const Interface *interface); }; +// An interface that is registered to a Dialect. +struct DialectInterface : public Interface { + using Interface::Interface; + + static bool classof(const Interface *interface); +}; + } // namespace tblgen } // namespace mlir diff --git a/mlir/lib/TableGen/Interfaces.cpp b/mlir/lib/TableGen/Interfaces.cpp index b0ad3ee59a089..77a6cecebbeaf 100644 --- a/mlir/lib/TableGen/Interfaces.cpp +++ b/mlir/lib/TableGen/Interfaces.cpp @@ -208,3 +208,11 @@ bool OpInterface::classof(const Interface *interface) { bool TypeInterface::classof(const Interface *interface) { return interface->getDef().isSubClassOf("TypeInterface"); } + +//===----------------------------------------------------------------------===// +// DialectInterface +//===----------------------------------------------------------------------===// + +bool DialectInterface::classof(const Interface *interface) { + return interface->getDef().isSubClassOf("DialectInterface"); +} diff --git a/mlir/test/mlir-tblgen/dialect-interface.td b/mlir/test/mlir-tblgen/dialect-interface.td new file mode 100644 index 0000000000000..9b424bf501be3 --- /dev/null +++ b/mlir/test/mlir-tblgen/dialect-interface.td @@ -0,0 +1,66 @@ +// RUN: mlir-tblgen -gen-dialect-interface-decls -I %S/../../include %s | FileCheck %s --check-prefix=DECL + +include "mlir/IR/Interfaces.td" + +def NoDefaultMethod : DialectInterface<"NoDefaultMethod"> { + let description = [{ + This is an example dialect interface without default method body. + }]; + + let cppNamespace = "::mlir::example"; + + let methods = [ + InterfaceMethod< + /*desc=*/ "Check if it's an example dialect", + /*returnType=*/ "bool", + /*methodName=*/ "isExampleDialect", + /*args=*/ (ins) + >, + InterfaceMethod< + /*desc=*/ "second method to check if multiple methods supported", + /*returnType=*/ "unsigned", + /*methodName=*/ "supportSecondMethod", + /*args=*/ (ins "::mlir::Type":$type) + > + + ]; +} + +// DECL: class NoDefaultMethod : public {{.*}}DialectInterface::Base +// DECL: virtual bool isExampleDialect() const = 0; +// DECL: virtual unsigned supportSecondMethod(::mlir::Type type) const = 0; +// DECL: protected: +// DECL-NEXT: NoDefaultMethod(::mlir::Dialect *dialect) : Base(dialect) {} + +def WithDefaultMethodInterface : DialectInterface<"WithDefaultMethodInterface"> { + let description = [{ + This is an example dialect interface with default method bodies. + }]; + + let cppNamespace = "::mlir::example"; + + let methods = [ + InterfaceMethod< + /*desc=*/ "Check if it's an example dialect", + /*returnType=*/ "bool", + /*methodName=*/ "isExampleDialect", + /*args=*/ (ins), + /*methodBody=*/ [{ + return true; + }] + >, + InterfaceMethod< + /*desc=*/ "second method to check if multiple methods supported", + /*returnType=*/ "unsigned", + /*methodName=*/ "supportSecondMethod", + /*args=*/ (ins "::mlir::Type":$type) + > + + ]; +} + +// DECL: virtual bool isExampleDialect() const; +// DECL: bool ::mlir::example::WithDefaultMethodInterface::isExampleDialect() const { +// DECL-NEXT: return true; +// DECL-NEXT: } + diff --git a/mlir/tools/mlir-tblgen/CMakeLists.txt b/mlir/tools/mlir-tblgen/CMakeLists.txt index 2a7ef7e0576c8..d7087cba3c874 100644 --- a/mlir/tools/mlir-tblgen/CMakeLists.txt +++ b/mlir/tools/mlir-tblgen/CMakeLists.txt @@ -12,6 +12,7 @@ add_tablegen(mlir-tblgen MLIR AttrOrTypeFormatGen.cpp BytecodeDialectGen.cpp DialectGen.cpp + DialectInterfacesGen.cpp DirectiveCommonGen.cpp EnumsGen.cpp EnumPythonBindingGen.cpp diff --git a/mlir/tools/mlir-tblgen/DialectInterfacesGen.cpp b/mlir/tools/mlir-tblgen/DialectInterfacesGen.cpp new file mode 100644 index 0000000000000..2fc500343501c --- /dev/null +++ b/mlir/tools/mlir-tblgen/DialectInterfacesGen.cpp @@ -0,0 +1,176 @@ +//===- DialectInterfacesGen.cpp - MLIR dialect interface utility generator ===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// DialectInterfaceGen generates definitions for Dialect interfaces. +// +//===----------------------------------------------------------------------===// + +#include "CppGenUtilities.h" +#include "DocGenUtilities.h" +#include "mlir/TableGen/GenInfo.h" +#include "mlir/TableGen/Interfaces.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/TableGen/CodeGenHelpers.h" +#include "llvm/TableGen/Error.h" +#include "llvm/TableGen/Record.h" +#include "llvm/TableGen/TableGenBackend.h" + +using namespace mlir; +using llvm::Record; +using llvm::RecordKeeper; +using mlir::tblgen::Interface; +using mlir::tblgen::InterfaceMethod; + +/// Emit a string corresponding to a C++ type, followed by a space if necessary. +static raw_ostream &emitCPPType(StringRef type, raw_ostream &os) { + type = type.trim(); + os << type; + if (type.back() != '&' && type.back() != '*') + os << " "; + return os; +} + +/// Emit the method name and argument list for the given method. +static void emitMethodNameAndArgs(const InterfaceMethod &method, StringRef name, + raw_ostream &os) { + os << name << '('; + llvm::interleaveComma(method.getArguments(), os, + [&](const InterfaceMethod::Argument &arg) { + os << arg.type << " " << arg.name; + }); + os << ") const"; +} + +/// Get an array of all Dialect Interface definitions +static std::vector +getAllInterfaceDefinitions(const RecordKeeper &records) { + std::vector defs = + records.getAllDerivedDefinitions("DialectInterface"); + + llvm::erase_if(defs, [&](const Record *def) { + // Ignore interfaces defined outside of the top-level file. + return llvm::SrcMgr.FindBufferContainingLoc(def->getLoc()[0]) != + llvm::SrcMgr.getMainFileID(); + }); + return defs; +} + +namespace { +/// This struct is the generator used when processing tablegen dialect +/// interfaces. +class DialectInterfaceGenerator { +public: + DialectInterfaceGenerator(const RecordKeeper &records, raw_ostream &os) + : defs(getAllInterfaceDefinitions(records)), os(os) {} + + bool emitInterfaceDecls(); + +protected: + void emitInterfaceDecl(const Interface &interface); + void emitInterfaceMethodsDef(const Interface &interface); + + /// The set of interface records to emit. + std::vector defs; + // The stream to emit to. + raw_ostream &os; +}; +} // namespace + +//===----------------------------------------------------------------------===// +// GEN: Interface declarations +//===----------------------------------------------------------------------===// + +static void emitInterfaceMethodDoc(const InterfaceMethod &method, + raw_ostream &os, StringRef prefix = "") { + if (std::optional description = method.getDescription()) + tblgen::emitDescriptionComment(*description, os, prefix); +} + +static void emitInterfaceDeclMethods(const Interface &interface, + raw_ostream &os) { + for (auto &method : interface.getMethods()) { + emitInterfaceMethodDoc(method, os, " "); + os << " virtual "; + emitCPPType(method.getReturnType(), os); + emitMethodNameAndArgs(method, method.getName(), os); + if (!method.getBody()) + // no default method body + os << " = 0"; + os << ";\n"; + } +} + +void DialectInterfaceGenerator::emitInterfaceDecl(const Interface &interface) { + llvm::NamespaceEmitter ns(os, interface.getCppNamespace()); + + StringRef interfaceName = interface.getName(); + + tblgen::emitSummaryAndDescComments(os, "", + interface.getDescription().value_or("")); + + // Emit the main interface class declaration. + os << llvm::formatv( + "class {0} : public ::mlir::DialectInterface::Base<{0}> {{\n" + "public:\n", + interfaceName); + + emitInterfaceDeclMethods(interface, os); + os << llvm::formatv("\nprotected:\n" + " {0}(::mlir::Dialect *dialect) : Base(dialect) {{}\n", + interfaceName); + + os << "};\n"; +} + +void DialectInterfaceGenerator::emitInterfaceMethodsDef( + const Interface &interface) { + + for (auto &method : interface.getMethods()) { + if (auto body = method.getBody()) { + emitCPPType(method.getReturnType(), os); + os << interface.getCppNamespace() << "::"; + os << interface.getName() << "::"; + emitMethodNameAndArgs(method, method.getName(), os); + os << " {\n " << body.value() << "\n}\n"; + } + } +} + +bool DialectInterfaceGenerator::emitInterfaceDecls() { + + llvm::emitSourceFileHeader("Dialect Interface Declarations", os); + + // Sort according to ID, so defs are emitted in the order in which they appear + // in the Tablegen file. + std::vector sortedDefs(defs); + llvm::sort(sortedDefs, [](const Record *lhs, const Record *rhs) { + return lhs->getID() < rhs->getID(); + }); + + for (const Record *def : sortedDefs) + emitInterfaceDecl(Interface(def)); + + os << "\n"; + for (const Record *def : sortedDefs) + emitInterfaceMethodsDef(Interface(def)); + + return false; +} + +//===----------------------------------------------------------------------===// +// GEN: Interface registration hooks +//===----------------------------------------------------------------------===// + +static mlir::GenRegistration genDecls( + "gen-dialect-interface-decls", + "Generate dialect interface declarations.", + [](const RecordKeeper &records, raw_ostream &os) { + return DialectInterfaceGenerator(records, os).emitInterfaceDecls(); + }); From 4ca9d25f184b808021289ea921a3494a01c1ff93 Mon Sep 17 00:00:00 2001 From: aidint Date: Sun, 30 Nov 2025 20:00:59 +0100 Subject: [PATCH 2/6] resolve clang-format problem --- mlir/tools/mlir-tblgen/DialectInterfacesGen.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/mlir/tools/mlir-tblgen/DialectInterfacesGen.cpp b/mlir/tools/mlir-tblgen/DialectInterfacesGen.cpp index 2fc500343501c..0ce4c3ef603b3 100644 --- a/mlir/tools/mlir-tblgen/DialectInterfacesGen.cpp +++ b/mlir/tools/mlir-tblgen/DialectInterfacesGen.cpp @@ -169,8 +169,7 @@ bool DialectInterfaceGenerator::emitInterfaceDecls() { //===----------------------------------------------------------------------===// static mlir::GenRegistration genDecls( - "gen-dialect-interface-decls", - "Generate dialect interface declarations.", + "gen-dialect-interface-decls", "Generate dialect interface declarations.", [](const RecordKeeper &records, raw_ostream &os) { return DialectInterfaceGenerator(records, os).emitInterfaceDecls(); }); From e3d4a439f237b0d7349c785b488ebe8531ab0c55 Mon Sep 17 00:00:00 2001 From: aidint Date: Tue, 2 Dec 2025 23:24:32 +0100 Subject: [PATCH 3/6] convert DialectInlinerInterface and use ODS --- mlir/include/mlir/IR/Interfaces.td | 2 +- mlir/include/mlir/Transforms/CMakeLists.txt | 4 + .../Transforms/DialectInlinerInterface.td | 217 ++++++++++++++++++ mlir/include/mlir/Transforms/InliningUtils.h | 155 +------------ mlir/lib/Transforms/CMakeLists.txt | 1 + mlir/test/mlir-tblgen/dialect-interface.td | 9 +- .../mlir-tblgen/DialectInterfacesGen.cpp | 40 +--- 7 files changed, 240 insertions(+), 188 deletions(-) create mode 100644 mlir/include/mlir/Transforms/DialectInlinerInterface.td diff --git a/mlir/include/mlir/IR/Interfaces.td b/mlir/include/mlir/IR/Interfaces.td index 746ad3408f424..e51bbd5620280 100644 --- a/mlir/include/mlir/IR/Interfaces.td +++ b/mlir/include/mlir/IR/Interfaces.td @@ -147,7 +147,7 @@ class TypeInterface baseInterfaces = []> !if(!empty(cppNamespace),"", cppNamespace # "::") # name >; -// DialectInterface represents an interface registered to an operation. +// DialectInterface represents a Dialect Interface. class DialectInterface baseInterfaces = []> : Interface, OpInterfaceTrait; diff --git a/mlir/include/mlir/Transforms/CMakeLists.txt b/mlir/include/mlir/Transforms/CMakeLists.txt index 5fa52b28b6f1d..1b57a3482c1bb 100644 --- a/mlir/include/mlir/Transforms/CMakeLists.txt +++ b/mlir/include/mlir/Transforms/CMakeLists.txt @@ -5,4 +5,8 @@ mlir_tablegen(Transforms.capi.h.inc -gen-pass-capi-header --prefix Transforms) mlir_tablegen(Transforms.capi.cpp.inc -gen-pass-capi-impl --prefix Transforms) add_mlir_dialect_tablegen_target(MLIRTransformsPassIncGen) +set(LLVM_TARGET_DEFINITIONS DialectInlinerInterface.td) +mlir_tablegen(DialectInlinerInterface.h.inc -gen-dialect-interface-decls) +add_mlir_dialect_tablegen_target(MLIRTransformsDialectInterfaceIncGen) + add_mlir_doc(Passes GeneralPasses ./ -gen-pass-doc) diff --git a/mlir/include/mlir/Transforms/DialectInlinerInterface.td b/mlir/include/mlir/Transforms/DialectInlinerInterface.td new file mode 100644 index 0000000000000..f8fd341cf8ea2 --- /dev/null +++ b/mlir/include/mlir/Transforms/DialectInlinerInterface.td @@ -0,0 +1,217 @@ +#ifndef DIALECTINLINERINTERFACE +#define DIALECTINLINERINTERFACE + +include "mlir/IR/Interfaces.td" + +def DialectInlinerInterface : DialectInterface<"DialectInlinerInterface"> { + let description = [{ + 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. + }]; + + + let cppNamespace = "::mlir"; + + let methods = [ + InterfaceMethod< + /*desc=*/ [{ + 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). + }], + /*returnType=*/ "bool", + /*methodName=*/ "isLegalToInline", + /*args=*/ (ins "::mlir::Operation *":$call, "::mlir::Operation *":$callable, "bool":$wouldBeCloned), + /*methodBody=*/ [{ + return false; + }] + >, + InterfaceMethod< + /*desc=*/ [{ + 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. + }], + /*returnType=*/ "bool", + /*methodName=*/ "isLegalToInline", + /*args=*/ (ins "::mlir::Region *":$dest, "::mlir::Region *":$src, "bool":$wouldBeCloned, + "::mlir::IRMapping &":$valueMapping), + /*methodBody=*/ [{ + return false; + }] + >, + InterfaceMethod< + /*desc=*/ [{ + 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. + }], + /*returnType=*/ "bool", + /*methodName=*/ "isLegalToInline", + /*args=*/ (ins "::mlir::Operation *":$op, "::mlir::Region *":$dest, "bool":$wouldBeCloned, + "::mlir::IRMapping &":$valueMapping), + /*methodBody=*/ [{ + return false; + }] + >, + InterfaceMethod< + /*desc=*/ [{ + 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. + }], + /*returnType=*/ "bool", + /*methodName=*/ "shouldAnalyzeRecursively", + /*args=*/ (ins "::mlir::Operation *":$op), + /*methodBody=*/ [{ + return true; + }] + >, + InterfaceMethod< + /*desc=*/ [{ + 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. + }], + /*returnType=*/ "void", + /*methodName=*/ "handleTerminator", + /*args=*/ (ins "::mlir::Operation *":$op, "::mlir::Block *":$newDest), + /*methodBody=*/ [{ + llvm_unreachable("must implement handleTerminator in the case of multiple " + "inlined blocks"); + }] + >, + InterfaceMethod< + /*desc=*/ [{ + 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. + }], + /*returnType=*/ "void", + /*methodName=*/ "handleTerminator", + /*args=*/ (ins "::mlir::Operation *":$op, "::mlir::ValueRange":$valuesToReplace), + /*methodBody=*/ [{ + llvm_unreachable( + "must implement handleTerminator in the case of one inlined block"); + }] + >, + InterfaceMethod< + /*desc=*/ [{ + 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. + }], + /*returnType=*/ "::mlir::Operation *", + /*methodName=*/ "materializeCallConversion", + /*args=*/ (ins "::mlir::OpBuilder &":$builder, "::mlir::Value":$input, + "::mlir::Type":$resultType, "::mlir::Location":$conversionLoc), + /*methodBody=*/ [{ + return nullptr; + }] + >, + InterfaceMethod< + /*desc=*/ [{ + 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. + }], + /*returnType=*/ "::mlir::Value", + /*methodName=*/ "handleArgument", + /*args=*/ (ins "::mlir::OpBuilder &":$builder, "::mlir::Operation *":$call, + "::mlir::Operation *":$callable, "::mlir::Value":$argument, + "::mlir::DictionaryAttr":$argumentAttrs), + /*methodBody=*/ [{ + return argument; + }] + >, + InterfaceMethod< + /*desc=*/ [{ + 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. + }], + /*returnType=*/ "::mlir::Value", + /*methodName=*/ "handleResult", + /*args=*/ (ins "::mlir::OpBuilder &":$builder, "::mlir::Operation *":$call, + "::mlir::Operation *":$callable, "::mlir::Value":$result, + "::mlir::DictionaryAttr":$resultAttrs), + /*methodBody=*/ [{ + return result; + }] + >, + InterfaceMethod< + /*desc=*/ [{ + Process a set of blocks that have been inlined for a call. This callback + is invoked before inlined terminator operations have been processed. + }], + /*returnType=*/ "void", + /*methodName=*/ "processInlinedCallBlocks", + /*args=*/ (ins "::mlir::Operation *":$call, + "::mlir::iterator_range<::mlir::Region::iterator>":$inlinedBlocks), + /*methodBody=*/ [{}] + >, + InterfaceMethod< + /*desc=*/ [{ + Returns true if the inliner can assume a fast path of not creating a new + block, if there is only one block. + }], + /*returnType=*/ "bool", + /*methodName=*/ "allowSingleBlockOptimization", + /*args=*/ (ins "::mlir::iterator_range<::mlir::Region::iterator>":$inlinedBlocks), + /*methodBody=*/ [{ + return true; + }] + > + ]; +} + + +#endif diff --git a/mlir/include/mlir/Transforms/InliningUtils.h b/mlir/include/mlir/Transforms/InliningUtils.h index ed6413d8cd44c..b6c6da3ddcc9b 100644 --- a/mlir/include/mlir/Transforms/InliningUtils.h +++ b/mlir/include/mlir/Transforms/InliningUtils.h @@ -32,158 +32,7 @@ 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 { -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 inlinedBlocks) const {} - - /// Returns true if the inliner can assume a fast path of not creating a new - /// block, if there is only one block. - virtual bool allowSingleBlockOptimization( - iterator_range inlinedBlocks) const { - return true; - } -}; +class DialectInlinerInterface; /// This interface provides the hooks into the inlining interface. /// Note: this class automatically collects 'DialectInlinerInterface' objects @@ -307,4 +156,6 @@ inlineCall(InlinerInterface &interface, } // namespace mlir +#include "mlir/Transforms/DialectInlinerInterface.h.inc" + #endif // MLIR_TRANSFORMS_INLININGUTILS_H diff --git a/mlir/lib/Transforms/CMakeLists.txt b/mlir/lib/Transforms/CMakeLists.txt index 54b67f5c7a91e..6c5303b4dd8a4 100644 --- a/mlir/lib/Transforms/CMakeLists.txt +++ b/mlir/lib/Transforms/CMakeLists.txt @@ -27,6 +27,7 @@ add_mlir_library(MLIRTransforms DEPENDS MLIRTransformsPassIncGen + MLIRTransformsDialectInterfaceIncGen LINK_LIBS PUBLIC MLIRAnalysis diff --git a/mlir/test/mlir-tblgen/dialect-interface.td b/mlir/test/mlir-tblgen/dialect-interface.td index 9b424bf501be3..ff39fd941f300 100644 --- a/mlir/test/mlir-tblgen/dialect-interface.td +++ b/mlir/test/mlir-tblgen/dialect-interface.td @@ -27,10 +27,10 @@ def NoDefaultMethod : DialectInterface<"NoDefaultMethod"> { } // DECL: class NoDefaultMethod : public {{.*}}DialectInterface::Base -// DECL: virtual bool isExampleDialect() const = 0; -// DECL: virtual unsigned supportSecondMethod(::mlir::Type type) const = 0; -// DECL: protected: +// DECL: public: // DECL-NEXT: NoDefaultMethod(::mlir::Dialect *dialect) : Base(dialect) {} +// DECL: virtual bool isExampleDialect() const {} +// DECL: virtual unsigned supportSecondMethod(::mlir::Type type) const {} def WithDefaultMethodInterface : DialectInterface<"WithDefaultMethodInterface"> { let description = [{ @@ -59,8 +59,7 @@ def WithDefaultMethodInterface : DialectInterface<"WithDefaultMethodInterface"> ]; } -// DECL: virtual bool isExampleDialect() const; -// DECL: bool ::mlir::example::WithDefaultMethodInterface::isExampleDialect() const { +// DECL: virtual bool isExampleDialect() const { // DECL-NEXT: return true; // DECL-NEXT: } diff --git a/mlir/tools/mlir-tblgen/DialectInterfacesGen.cpp b/mlir/tools/mlir-tblgen/DialectInterfacesGen.cpp index 0ce4c3ef603b3..4400c381fe7b2 100644 --- a/mlir/tools/mlir-tblgen/DialectInterfacesGen.cpp +++ b/mlir/tools/mlir-tblgen/DialectInterfacesGen.cpp @@ -74,7 +74,6 @@ class DialectInterfaceGenerator { protected: void emitInterfaceDecl(const Interface &interface); - void emitInterfaceMethodsDef(const Interface &interface); /// The set of interface records to emit. std::vector defs; @@ -93,17 +92,18 @@ static void emitInterfaceMethodDoc(const InterfaceMethod &method, tblgen::emitDescriptionComment(*description, os, prefix); } -static void emitInterfaceDeclMethods(const Interface &interface, - raw_ostream &os) { +static void emitInterfaceMethodsDef(const Interface &interface, + raw_ostream &os) { for (auto &method : interface.getMethods()) { emitInterfaceMethodDoc(method, os, " "); os << " virtual "; emitCPPType(method.getReturnType(), os); emitMethodNameAndArgs(method, method.getName(), os); - if (!method.getBody()) + if (auto body = method.getBody()) + os << " {\n " << body << "\n }\n"; + else // no default method body - os << " = 0"; - os << ";\n"; + os << " {}\n"; } } @@ -117,32 +117,16 @@ void DialectInterfaceGenerator::emitInterfaceDecl(const Interface &interface) { // Emit the main interface class declaration. os << llvm::formatv( - "class {0} : public ::mlir::DialectInterface::Base<{0}> {{\n" - "public:\n", + "class {0} : public ::mlir::DialectInterface::Base<{0}> {\n" + "public:\n" + " {0}(::mlir::Dialect *dialect) : Base(dialect) {{}\n", interfaceName); - emitInterfaceDeclMethods(interface, os); - os << llvm::formatv("\nprotected:\n" - " {0}(::mlir::Dialect *dialect) : Base(dialect) {{}\n", - interfaceName); + emitInterfaceMethodsDef(interface, os); os << "};\n"; } -void DialectInterfaceGenerator::emitInterfaceMethodsDef( - const Interface &interface) { - - for (auto &method : interface.getMethods()) { - if (auto body = method.getBody()) { - emitCPPType(method.getReturnType(), os); - os << interface.getCppNamespace() << "::"; - os << interface.getName() << "::"; - emitMethodNameAndArgs(method, method.getName(), os); - os << " {\n " << body.value() << "\n}\n"; - } - } -} - bool DialectInterfaceGenerator::emitInterfaceDecls() { llvm::emitSourceFileHeader("Dialect Interface Declarations", os); @@ -157,10 +141,6 @@ bool DialectInterfaceGenerator::emitInterfaceDecls() { for (const Record *def : sortedDefs) emitInterfaceDecl(Interface(def)); - os << "\n"; - for (const Record *def : sortedDefs) - emitInterfaceMethodsDef(Interface(def)); - return false; } From 3f0fc8be2e2d0ffc1a7d86559b420545e27bbd92 Mon Sep 17 00:00:00 2001 From: aidint Date: Wed, 3 Dec 2025 23:05:48 +0100 Subject: [PATCH 4/6] add documentation for dialect interface ods support --- mlir/docs/Interfaces.md | 62 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 58 insertions(+), 4 deletions(-) diff --git a/mlir/docs/Interfaces.md b/mlir/docs/Interfaces.md index 7e1c5fe075675..71ed0966a2f9b 100644 --- a/mlir/docs/Interfaces.md +++ b/mlir/docs/Interfaces.md @@ -85,6 +85,64 @@ if (DialectInlinerInterface *interface = dyn_cast(diale } ``` +#### Utilizing the ODS framework + +Note: Before reading this section, the reader should have some familiarity with +the concepts described in the +[`Operation Definition Specification`](DefiningDialects/Operations.md) documentation. + +MLIR also supports defining dialect interfaces directly in **TableGen**. +This reduces boilerplate and allows authors to specify high-level interface +structure declaratively. + +For example, the above interface can be defined using ODS as follows: +```tablegen +def DialectInlinerInterface : DialectInterface<"DialectInlinerInterface"> { + let description = [{ + Define a base inlining interface class to allow for dialects to opt-in to + the inliner. + }]; + + let methods = [ + InterfaceMethod< + /*desc=*/ [{ + 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. + '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. + }], + /*returnType=*/ "bool", + /*methodName=*/ "isLegalToInline", + /*args=*/ (ins "Region *":$dest, "Region *":$src, + "IRMapping &":$valueMapping), + /*methodBody=*/ [{ + return false; + }] + > + ]; +} +``` + +`DialectInterfaces` class make use of the following components: + +* C++ Class Name (Provided via template parameter) + - The name of the C++ interface class. +* Description (`description`) + - A string description of the interface, its invariants, example usages, + etc. +* C++ Namespace (`cppNamespace`) + - The C++ namespace that the interface class should be generated in. +* Methods (`methods`) + - The list of interface hook methods that are defined by the IR object. + - The structure of these methods is defined [here](#interface-methods). + +The header file can be generated via the following command: +```bash +mlir-tblgen gen-dialect-interface-decls DialectInterface.td + +``` + #### DialectInterfaceCollection An additional utility is provided via `DialectInterfaceCollection`. This class @@ -364,10 +422,6 @@ void *TestDialect::getRegisteredInterfaceForOp(TypeID typeID, #### Utilizing the ODS Framework -Note: Before reading this section, the reader should have some familiarity with -the concepts described in the -[`Operation Definition Specification`](DefiningDialects/Operations.md) documentation. - As detailed above, [Interfaces](#attributeoperationtype-interfaces) allow for attributes, operations, and types to expose method calls without requiring that the caller know the specific derived type. The downside to this infrastructure, From aa94279121ec1a859d7a4688ac0b121ccfcf4120 Mon Sep 17 00:00:00 2001 From: aidint Date: Wed, 3 Dec 2025 23:07:10 +0100 Subject: [PATCH 5/6] remove extra line --- mlir/docs/Interfaces.md | 1 - 1 file changed, 1 deletion(-) diff --git a/mlir/docs/Interfaces.md b/mlir/docs/Interfaces.md index 71ed0966a2f9b..b0b60c0721960 100644 --- a/mlir/docs/Interfaces.md +++ b/mlir/docs/Interfaces.md @@ -140,7 +140,6 @@ def DialectInlinerInterface : DialectInterface<"DialectInlinerInterface"> { The header file can be generated via the following command: ```bash mlir-tblgen gen-dialect-interface-decls DialectInterface.td - ``` #### DialectInterfaceCollection From 0531140d31a36cd6eacd28e3a2b1b46fd615c402 Mon Sep 17 00:00:00 2001 From: aidint Date: Thu, 4 Dec 2025 23:23:36 +0100 Subject: [PATCH 6/6] Resolve documentation issues and use indented ostream --- mlir/docs/Interfaces.md | 25 +++- .../Transforms/DialectInlinerInterface.td | 141 ++++++++---------- .../mlir-tblgen/DialectInterfacesGen.cpp | 27 ++-- 3 files changed, 95 insertions(+), 98 deletions(-) diff --git a/mlir/docs/Interfaces.md b/mlir/docs/Interfaces.md index b0b60c0721960..d3e1888c48baf 100644 --- a/mlir/docs/Interfaces.md +++ b/mlir/docs/Interfaces.md @@ -96,6 +96,7 @@ This reduces boilerplate and allows authors to specify high-level interface structure declaratively. For example, the above interface can be defined using ODS as follows: + ```tablegen def DialectInlinerInterface : DialectInterface<"DialectInlinerInterface"> { let description = [{ @@ -104,19 +105,16 @@ def DialectInlinerInterface : DialectInterface<"DialectInlinerInterface"> { }]; let methods = [ - InterfaceMethod< - /*desc=*/ [{ + InterfaceMethod<[{ 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. '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. }], - /*returnType=*/ "bool", - /*methodName=*/ "isLegalToInline", - /*args=*/ (ins "Region *":$dest, "Region *":$src, - "IRMapping &":$valueMapping), - /*methodBody=*/ [{ + "bool", "isLegalToInline", + (ins "Region *":$dest, "Region *":$src, "IRMapping &":$valueMapping), + [{ return false; }] > @@ -138,10 +136,21 @@ def DialectInlinerInterface : DialectInterface<"DialectInlinerInterface"> { - The structure of these methods is defined [here](#interface-methods). The header file can be generated via the following command: + ```bash -mlir-tblgen gen-dialect-interface-decls DialectInterface.td +mlir-tblgen --gen-dialect-interface-decls DialectInterface.td ``` +To generate dialect interface declarations using the ODS framework in CMake, you would write: + +```cmake +set(LLVM_TARGET_DEFINITIONS DialectInlinerInterface.td) +mlir_tablegen(DialectInlinerInterface.h.inc -gen-dialect-interface-decls) +``` + +An example of this can be found in the DialectInlinerInterface implementation +and the related `CMakeLists.txt` under `mlir/include/mlir/Transforms`. + #### DialectInterfaceCollection An additional utility is provided via `DialectInterfaceCollection`. This class diff --git a/mlir/include/mlir/Transforms/DialectInlinerInterface.td b/mlir/include/mlir/Transforms/DialectInlinerInterface.td index f8fd341cf8ea2..0975b84179d3c 100644 --- a/mlir/include/mlir/Transforms/DialectInlinerInterface.td +++ b/mlir/include/mlir/Transforms/DialectInlinerInterface.td @@ -1,5 +1,5 @@ -#ifndef DIALECTINLINERINTERFACE -#define DIALECTINLINERINTERFACE +#ifndef MLIR_INTERFACES_DIALECTINLINERINTERFACE +#define MLIR_INTERFACES_DIALECTINLINERINTERFACE include "mlir/IR/Interfaces.td" @@ -8,14 +8,11 @@ def DialectInlinerInterface : DialectInterface<"DialectInlinerInterface"> { 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. - }]; - - + }]; let cppNamespace = "::mlir"; let methods = [ - InterfaceMethod< - /*desc=*/ [{ + InterfaceMethod<[{ 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 @@ -24,34 +21,31 @@ def DialectInlinerInterface : DialectInterface<"DialectInlinerInterface"> { if the region is set to be moved in-place(i.e. no duplicates would be created). }], - /*returnType=*/ "bool", - /*methodName=*/ "isLegalToInline", - /*args=*/ (ins "::mlir::Operation *":$call, "::mlir::Operation *":$callable, "bool":$wouldBeCloned), - /*methodBody=*/ [{ + "bool", "isLegalToInline", + (ins "::mlir::Operation *":$call, "::mlir::Operation *":$callable, + "bool":$wouldBeCloned), + [{ return false; }] >, - InterfaceMethod< - /*desc=*/ [{ + InterfaceMethod<[{ 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' + 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. }], - /*returnType=*/ "bool", - /*methodName=*/ "isLegalToInline", - /*args=*/ (ins "::mlir::Region *":$dest, "::mlir::Region *":$src, "bool":$wouldBeCloned, + "bool", "isLegalToInline", + (ins "::mlir::Region *":$dest, "::mlir::Region *":$src, "bool":$wouldBeCloned, "::mlir::IRMapping &":$valueMapping), - /*methodBody=*/ [{ + [{ return false; }] >, - InterfaceMethod< - /*desc=*/ [{ + InterfaceMethod<[{ 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 @@ -61,30 +55,26 @@ def DialectInlinerInterface : DialectInterface<"DialectInlinerInterface"> { used to examine what values will replace entry arguments into the 'src' region for example. }], - /*returnType=*/ "bool", - /*methodName=*/ "isLegalToInline", - /*args=*/ (ins "::mlir::Operation *":$op, "::mlir::Region *":$dest, "bool":$wouldBeCloned, - "::mlir::IRMapping &":$valueMapping), - /*methodBody=*/ [{ + "bool", "isLegalToInline", + (ins "::mlir::Operation *":$op, "::mlir::Region *":$dest, + "bool":$wouldBeCloned, "::mlir::IRMapping &":$valueMapping), + [{ return false; }] >, - InterfaceMethod< - /*desc=*/ [{ + InterfaceMethod<[{ 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. }], - /*returnType=*/ "bool", - /*methodName=*/ "shouldAnalyzeRecursively", - /*args=*/ (ins "::mlir::Operation *":$op), - /*methodBody=*/ [{ + "bool", "shouldAnalyzeRecursively", + (ins "::mlir::Operation *":$op), + [{ return true; }] >, - InterfaceMethod< - /*desc=*/ [{ + InterfaceMethod<[{ 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 @@ -93,16 +83,14 @@ def DialectInlinerInterface : DialectInterface<"DialectInlinerInterface"> { Its block arguments correspond to any values that need to be replaced by terminators within the inlined region. }], - /*returnType=*/ "void", - /*methodName=*/ "handleTerminator", - /*args=*/ (ins "::mlir::Operation *":$op, "::mlir::Block *":$newDest), - /*methodBody=*/ [{ + "void", "handleTerminator", + (ins "::mlir::Operation *":$op, "::mlir::Block *":$newDest), + [{ llvm_unreachable("must implement handleTerminator in the case of multiple " "inlined blocks"); }] >, - InterfaceMethod< - /*desc=*/ [{ + InterfaceMethod<[{ 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 @@ -112,42 +100,40 @@ def DialectInlinerInterface : DialectInterface<"DialectInlinerInterface"> { operation). The given 'op' will be removed by the caller, after this function has been called. }], - /*returnType=*/ "void", - /*methodName=*/ "handleTerminator", - /*args=*/ (ins "::mlir::Operation *":$op, "::mlir::ValueRange":$valuesToReplace), - /*methodBody=*/ [{ + "void", "handleTerminator", + (ins "::mlir::Operation *":$op, "::mlir::ValueRange":$valuesToReplace), + [{ llvm_unreachable( "must implement handleTerminator in the case of one inlined block"); }] >, - InterfaceMethod< - /*desc=*/ [{ + InterfaceMethod<[{ 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: + + ```mlir func @foo(i32) -> i32 { ... } - // Mismatched input operand - ... = foo.call @foo(%input : i16) -> 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. }], - /*returnType=*/ "::mlir::Operation *", - /*methodName=*/ "materializeCallConversion", - /*args=*/ (ins "::mlir::OpBuilder &":$builder, "::mlir::Value":$input, - "::mlir::Type":$resultType, "::mlir::Location":$conversionLoc), - /*methodBody=*/ [{ + "::mlir::Operation *", "materializeCallConversion", + (ins "::mlir::OpBuilder &":$builder, "::mlir::Value":$input, + "::mlir::Type":$resultType, "::mlir::Location":$conversionLoc), + [{ return nullptr; }] >, - InterfaceMethod< - /*desc=*/ [{ + InterfaceMethod<[{ 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 @@ -157,17 +143,15 @@ def DialectInlinerInterface : DialectInterface<"DialectInlinerInterface"> { provided `builder` are inserted right before the inlined callee region. An example use case is the insertion of copies for by value arguments. }], - /*returnType=*/ "::mlir::Value", - /*methodName=*/ "handleArgument", - /*args=*/ (ins "::mlir::OpBuilder &":$builder, "::mlir::Operation *":$call, - "::mlir::Operation *":$callable, "::mlir::Value":$argument, - "::mlir::DictionaryAttr":$argumentAttrs), - /*methodBody=*/ [{ + "::mlir::Value", "handleArgument", + (ins "::mlir::OpBuilder &":$builder, "::mlir::Operation *":$call, + "::mlir::Operation *":$callable, "::mlir::Value":$argument, + "::mlir::DictionaryAttr":$argumentAttrs), + [{ return argument; }] >, - InterfaceMethod< - /*desc=*/ [{ + InterfaceMethod<[{ 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 @@ -178,35 +162,30 @@ def DialectInlinerInterface : DialectInterface<"DialectInlinerInterface"> { region. An example use case is the insertion of copies for by value results. NOTE: This hook is invoked after inlining the `callable` region. }], - /*returnType=*/ "::mlir::Value", - /*methodName=*/ "handleResult", - /*args=*/ (ins "::mlir::OpBuilder &":$builder, "::mlir::Operation *":$call, - "::mlir::Operation *":$callable, "::mlir::Value":$result, - "::mlir::DictionaryAttr":$resultAttrs), - /*methodBody=*/ [{ + "::mlir::Value", "handleResult", + (ins "::mlir::OpBuilder &":$builder, "::mlir::Operation *":$call, + "::mlir::Operation *":$callable, "::mlir::Value":$result, + "::mlir::DictionaryAttr":$resultAttrs), + [{ return result; }] >, - InterfaceMethod< - /*desc=*/ [{ + InterfaceMethod<[{ Process a set of blocks that have been inlined for a call. This callback is invoked before inlined terminator operations have been processed. }], - /*returnType=*/ "void", - /*methodName=*/ "processInlinedCallBlocks", - /*args=*/ (ins "::mlir::Operation *":$call, - "::mlir::iterator_range<::mlir::Region::iterator>":$inlinedBlocks), - /*methodBody=*/ [{}] + "void", "processInlinedCallBlocks", + (ins "::mlir::Operation *":$call, + "::mlir::iterator_range<::mlir::Region::iterator>":$inlinedBlocks), + [{}] >, - InterfaceMethod< - /*desc=*/ [{ + InterfaceMethod<[{ Returns true if the inliner can assume a fast path of not creating a new block, if there is only one block. }], - /*returnType=*/ "bool", - /*methodName=*/ "allowSingleBlockOptimization", - /*args=*/ (ins "::mlir::iterator_range<::mlir::Region::iterator>":$inlinedBlocks), - /*methodBody=*/ [{ + "bool", "allowSingleBlockOptimization", + (ins "::mlir::iterator_range<::mlir::Region::iterator>":$inlinedBlocks), + [{ return true; }] > diff --git a/mlir/tools/mlir-tblgen/DialectInterfacesGen.cpp b/mlir/tools/mlir-tblgen/DialectInterfacesGen.cpp index 4400c381fe7b2..1d3b24a7aee15 100644 --- a/mlir/tools/mlir-tblgen/DialectInterfacesGen.cpp +++ b/mlir/tools/mlir-tblgen/DialectInterfacesGen.cpp @@ -12,6 +12,7 @@ #include "CppGenUtilities.h" #include "DocGenUtilities.h" +#include "mlir/Support/IndentedOstream.h" #include "mlir/TableGen/GenInfo.h" #include "mlir/TableGen/Interfaces.h" #include "llvm/ADT/StringExtras.h" @@ -94,16 +95,24 @@ static void emitInterfaceMethodDoc(const InterfaceMethod &method, static void emitInterfaceMethodsDef(const Interface &interface, raw_ostream &os) { + + raw_indented_ostream ios(os); + ios.indent(2); + for (auto &method : interface.getMethods()) { - emitInterfaceMethodDoc(method, os, " "); - os << " virtual "; - emitCPPType(method.getReturnType(), os); - emitMethodNameAndArgs(method, method.getName(), os); - if (auto body = method.getBody()) - os << " {\n " << body << "\n }\n"; - else - // no default method body - os << " {}\n"; + emitInterfaceMethodDoc(method, ios); + ios << "virtual "; + emitCPPType(method.getReturnType(), ios); + emitMethodNameAndArgs(method, method.getName(), ios); + ios << " {"; + + if (auto body = method.getBody()) { + ios << "\n"; + ios.indent(4); + ios << body << "\n"; + ios.indent(2); + } + os << "}\n"; } }