diff --git a/flang/include/flang/Lower/Support/Utils.h b/flang/include/flang/Lower/Support/Utils.h --- a/flang/include/flang/Lower/Support/Utils.h +++ b/flang/include/flang/Lower/Support/Utils.h @@ -16,6 +16,7 @@ #include "flang/Common/indirection.h" #include "flang/Parser/char-block.h" #include "flang/Semantics/tools.h" +#include "mlir/Dialect/Arithmetic/IR/Arithmetic.h" #include "mlir/Dialect/StandardOps/IR/Ops.h" #include "mlir/IR/BuiltinAttributes.h" #include "llvm/ADT/StringRef.h" diff --git a/flang/include/flang/Optimizer/Builder/Runtime/Character.h b/flang/include/flang/Optimizer/Builder/Runtime/Character.h --- a/flang/include/flang/Optimizer/Builder/Runtime/Character.h +++ b/flang/include/flang/Optimizer/Builder/Runtime/Character.h @@ -9,6 +9,7 @@ #ifndef FORTRAN_OPTIMIZER_BUILDER_RUNTIME_CHARACTER_H #define FORTRAN_OPTIMIZER_BUILDER_RUNTIME_CHARACTER_H +#include "mlir/Dialect/Arithmetic/IR/Arithmetic.h" #include "mlir/Dialect/StandardOps/IR/Ops.h" namespace fir { diff --git a/flang/include/flang/Optimizer/Support/InitFIR.h b/flang/include/flang/Optimizer/Support/InitFIR.h --- a/flang/include/flang/Optimizer/Support/InitFIR.h +++ b/flang/include/flang/Optimizer/Support/InitFIR.h @@ -16,6 +16,7 @@ #include "flang/Optimizer/Dialect/FIRDialect.h" #include "mlir/Conversion/Passes.h" #include "mlir/Dialect/Affine/Passes.h" +#include "mlir/Dialect/StandardOps/Extensions/InlinerExtension.h" #include "mlir/InitAllDialects.h" #include "mlir/Pass/Pass.h" #include "mlir/Pass/PassRegistry.h" @@ -30,20 +31,28 @@ mlir::arith::ArithmeticDialect, mlir::cf::ControlFlowDialect, \ mlir::StandardOpsDialect, mlir::vector::VectorDialect +#define FLANG_CODEGEN_DIALECT_LIST FIRCodeGenDialect, mlir::LLVM::LLVMDialect + // The definitive list of dialects used by flang. #define FLANG_DIALECT_LIST \ - FLANG_NONCODEGEN_DIALECT_LIST, FIRCodeGenDialect, mlir::LLVM::LLVMDialect + FLANG_NONCODEGEN_DIALECT_LIST, FLANG_CODEGEN_DIALECT_LIST inline void registerNonCodegenDialects(mlir::DialectRegistry ®istry) { registry.insert(); + mlir::standard::registerInlinerExtension(registry); } /// Register all the dialects used by flang. inline void registerDialects(mlir::DialectRegistry ®istry) { - registry.insert(); + registerNonCodegenDialects(registry); + registry.insert(); } inline void loadNonCodegenDialects(mlir::MLIRContext &context) { + mlir::DialectRegistry registry; + registerNonCodegenDialects(registry); + context.appendDialectRegistry(registry); + context.loadDialect(); } @@ -51,6 +60,10 @@ /// pass, but a producer of FIR and MLIR. It is therefore a requirement that the /// dialects be preloaded to be able to build the IR. inline void loadDialects(mlir::MLIRContext &context) { + mlir::DialectRegistry registry; + registerDialects(registry); + context.appendDialectRegistry(registry); + context.loadDialect(); } diff --git a/flang/lib/Frontend/CMakeLists.txt b/flang/lib/Frontend/CMakeLists.txt --- a/flang/lib/Frontend/CMakeLists.txt +++ b/flang/lib/Frontend/CMakeLists.txt @@ -1,4 +1,5 @@ get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS) +get_property(extension_libs GLOBAL PROPERTY MLIR_EXTENSION_LIBS) add_flang_library(flangFrontend CompilerInstance.cpp @@ -16,6 +17,7 @@ FIRDialect FIRSupport ${dialect_libs} + ${extension_libs} LINK_LIBS FortranParser @@ -34,6 +36,7 @@ MLIRLLVMToLLVMIRTranslation MLIRSCFToControlFlow ${dialect_libs} + ${extension_libs} LINK_COMPONENTS Option diff --git a/flang/lib/Lower/CMakeLists.txt b/flang/lib/Lower/CMakeLists.txt --- a/flang/lib/Lower/CMakeLists.txt +++ b/flang/lib/Lower/CMakeLists.txt @@ -1,4 +1,5 @@ get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS) +get_property(extension_libs GLOBAL PROPERTY MLIR_EXTENSION_LIBS) add_flang_library(FortranLower Bridge.cpp @@ -21,6 +22,7 @@ FIRSupport FIRTransforms ${dialect_libs} + ${extension_libs} LINK_LIBS FIRDialect @@ -28,6 +30,7 @@ FIRSupport FIRTransforms ${dialect_libs} + ${extension_libs} FortranCommon FortranParser FortranEvaluate diff --git a/flang/lib/Optimizer/Builder/CMakeLists.txt b/flang/lib/Optimizer/Builder/CMakeLists.txt --- a/flang/lib/Optimizer/Builder/CMakeLists.txt +++ b/flang/lib/Optimizer/Builder/CMakeLists.txt @@ -1,4 +1,5 @@ get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS) +get_property(extension_libs GLOBAL PROPERTY MLIR_EXTENSION_LIBS) add_flang_library(FIRBuilder BoxValue.cpp @@ -21,9 +22,11 @@ FIRDialect FIRSupport ${dialect_libs} + ${extension_libs} LINK_LIBS FIRDialect FIRSupport ${dialect_libs} + ${extension_libs} ) diff --git a/flang/lib/Optimizer/Support/CMakeLists.txt b/flang/lib/Optimizer/Support/CMakeLists.txt --- a/flang/lib/Optimizer/Support/CMakeLists.txt +++ b/flang/lib/Optimizer/Support/CMakeLists.txt @@ -1,4 +1,5 @@ get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS) +get_property(extension_libs GLOBAL PROPERTY MLIR_EXTENSION_LIBS) add_flang_library(FIRSupport FIRContext.cpp @@ -10,9 +11,11 @@ FIROpsIncGen MLIRIR ${dialect_libs} + ${extension_libs} LINK_LIBS ${dialect_libs} + ${extension_libs} MLIROpenMPToLLVMIRTranslation MLIRLLVMToLLVMIRTranslation MLIRTargetLLVMIRExport diff --git a/flang/tools/bbc/CMakeLists.txt b/flang/tools/bbc/CMakeLists.txt --- a/flang/tools/bbc/CMakeLists.txt +++ b/flang/tools/bbc/CMakeLists.txt @@ -6,12 +6,14 @@ llvm_update_compile_flags(bbc) get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS) +get_property(extension_libs GLOBAL PROPERTY MLIR_EXTENSION_LIBS) target_link_libraries(bbc PRIVATE FIRDialect FIRSupport FIRTransforms FIRBuilder ${dialect_libs} +${extension_libs} MLIRAffineToStandard MLIRSCFToControlFlow FortranCommon diff --git a/flang/tools/fir-opt/CMakeLists.txt b/flang/tools/fir-opt/CMakeLists.txt --- a/flang/tools/fir-opt/CMakeLists.txt +++ b/flang/tools/fir-opt/CMakeLists.txt @@ -1,6 +1,7 @@ add_flang_tool(fir-opt fir-opt.cpp) llvm_update_compile_flags(fir-opt) get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS) +get_property(extension_libs GLOBAL PROPERTY MLIR_EXTENSION_LIBS) target_link_libraries(fir-opt PRIVATE FIRDialect @@ -8,6 +9,7 @@ FIRTransforms FIRCodeGen ${dialect_libs} + ${extension_libs} # TODO: these should be transitive dependencies from a target providing # "registerFIRPasses()" diff --git a/flang/tools/tco/CMakeLists.txt b/flang/tools/tco/CMakeLists.txt --- a/flang/tools/tco/CMakeLists.txt +++ b/flang/tools/tco/CMakeLists.txt @@ -1,6 +1,7 @@ add_flang_tool(tco tco.cpp) llvm_update_compile_flags(tco) get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS) +get_property(extension_libs GLOBAL PROPERTY MLIR_EXTENSION_LIBS) target_link_libraries(tco PRIVATE FIRCodeGen FIRDialect @@ -8,12 +9,12 @@ FIRTransforms FIRBuilder ${dialect_libs} + ${extension_libs} MLIRIR MLIRLLVMIR MLIRLLVMToLLVMIRTranslation MLIRTargetLLVMIRExport MLIRPass - MLIRStandardToLLVM MLIRTransforms MLIRAffineToStandard MLIRAnalysis diff --git a/flang/unittests/Optimizer/CMakeLists.txt b/flang/unittests/Optimizer/CMakeLists.txt --- a/flang/unittests/Optimizer/CMakeLists.txt +++ b/flang/unittests/Optimizer/CMakeLists.txt @@ -1,4 +1,5 @@ get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS) +get_property(extension_libs GLOBAL PROPERTY MLIR_EXTENSION_LIBS) set(LIBS FIRBuilder @@ -6,6 +7,7 @@ FIRDialect FIRSupport ${dialect_libs} + ${extension_libs} ) add_flang_unittest(FlangOptimizerTests diff --git a/mlir/cmake/modules/AddMLIR.cmake b/mlir/cmake/modules/AddMLIR.cmake --- a/mlir/cmake/modules/AddMLIR.cmake +++ b/mlir/cmake/modules/AddMLIR.cmake @@ -416,6 +416,12 @@ add_mlir_library(${ARGV} DEPENDS mlir-headers) endfunction(add_mlir_conversion_library) +# Declare the library associated with an extension. +function(add_mlir_extension_library name) + set_property(GLOBAL APPEND PROPERTY MLIR_EXTENSION_LIBS ${name}) + add_mlir_library(${ARGV} DEPENDS mlir-headers) +endfunction(add_mlir_extension_library) + # Declare the library associated with a translation. function(add_mlir_translation_library name) set_property(GLOBAL APPEND PROPERTY MLIR_TRANSLATION_LIBS ${name}) diff --git a/mlir/cmake/modules/CMakeLists.txt b/mlir/cmake/modules/CMakeLists.txt --- a/mlir/cmake/modules/CMakeLists.txt +++ b/mlir/cmake/modules/CMakeLists.txt @@ -18,6 +18,7 @@ get_property(MLIR_ALL_LIBS GLOBAL PROPERTY MLIR_ALL_LIBS) get_property(MLIR_DIALECT_LIBS GLOBAL PROPERTY MLIR_DIALECT_LIBS) get_property(MLIR_CONVERSION_LIBS GLOBAL PROPERTY MLIR_CONVERSION_LIBS) +get_property(MLIR_EXTENSION_LIBS GLOBAL PROPERTY MLIR_EXTENSION_LIBS) get_property(MLIR_TRANSLATION_LIBS GLOBAL PROPERTY MLIR_TRANSLATION_LIBS) # Generate MlirConfig.cmake for the build tree. diff --git a/mlir/cmake/modules/MLIRConfig.cmake.in b/mlir/cmake/modules/MLIRConfig.cmake.in --- a/mlir/cmake/modules/MLIRConfig.cmake.in +++ b/mlir/cmake/modules/MLIRConfig.cmake.in @@ -19,6 +19,7 @@ set_property(GLOBAL PROPERTY MLIR_ALL_LIBS "@MLIR_ALL_LIBS@") set_property(GLOBAL PROPERTY MLIR_DIALECT_LIBS "@MLIR_DIALECT_LIBS@") set_property(GLOBAL PROPERTY MLIR_CONVERSION_LIBS "@MLIR_CONVERSION_LIBS@") +set_property(GLOBAL PROPERTY MLIR_EXTENSION_LIBS "@MLIR_EXTENSION_LIBS@") set_property(GLOBAL PROPERTY MLIR_TRANSLATION_LIBS "@MLIR_TRANSLATION_LIBS@") # Provide all our library targets to users. diff --git a/mlir/examples/toy/Ch5/CMakeLists.txt b/mlir/examples/toy/Ch5/CMakeLists.txt --- a/mlir/examples/toy/Ch5/CMakeLists.txt +++ b/mlir/examples/toy/Ch5/CMakeLists.txt @@ -28,9 +28,11 @@ include_directories(${CMAKE_CURRENT_BINARY_DIR}) include_directories(${CMAKE_CURRENT_BINARY_DIR}/include/) get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS) +get_property(extension_libs GLOBAL PROPERTY MLIR_EXTENSION_LIBS) target_link_libraries(toyc-ch5 PRIVATE ${dialect_libs} + ${extension_libs} MLIRAnalysis MLIRCallInterfaces MLIRCastInterfaces diff --git a/mlir/examples/toy/Ch5/toyc.cpp b/mlir/examples/toy/Ch5/toyc.cpp --- a/mlir/examples/toy/Ch5/toyc.cpp +++ b/mlir/examples/toy/Ch5/toyc.cpp @@ -10,6 +10,7 @@ // //===----------------------------------------------------------------------===// +#include "mlir/Dialect/StandardOps/Extensions/InlinerExtension.h" #include "toy/Dialect.h" #include "toy/MLIRGen.h" #include "toy/Parser.h" @@ -107,8 +108,14 @@ } int dumpMLIR() { - mlir::MLIRContext context; - // Load our Dialect in this MLIR Context. + // Prepare a dialect registry for things used in toy. + mlir::DialectRegistry registry; + + // Register extensions for transformations used by dialects generated in the + // toy compilation pipeline. + mlir::standard::registerInlinerExtension(registry); + + mlir::MLIRContext context(registry); context.getOrLoadDialect(); mlir::OwningOpRef module; diff --git a/mlir/examples/toy/Ch6/CMakeLists.txt b/mlir/examples/toy/Ch6/CMakeLists.txt --- a/mlir/examples/toy/Ch6/CMakeLists.txt +++ b/mlir/examples/toy/Ch6/CMakeLists.txt @@ -33,10 +33,12 @@ include_directories(${CMAKE_CURRENT_BINARY_DIR}/include/) get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS) get_property(conversion_libs GLOBAL PROPERTY MLIR_CONVERSION_LIBS) +get_property(extension_libs GLOBAL PROPERTY MLIR_EXTENSION_LIBS) target_link_libraries(toyc-ch6 PRIVATE ${dialect_libs} ${conversion_libs} + ${extension_libs} MLIRAnalysis MLIRCallInterfaces MLIRCastInterfaces diff --git a/mlir/examples/toy/Ch6/toyc.cpp b/mlir/examples/toy/Ch6/toyc.cpp --- a/mlir/examples/toy/Ch6/toyc.cpp +++ b/mlir/examples/toy/Ch6/toyc.cpp @@ -16,6 +16,7 @@ #include "toy/Passes.h" #include "mlir/Dialect/Affine/Passes.h" +#include "mlir/Dialect/StandardOps/Extensions/InlinerExtension.h" #include "mlir/ExecutionEngine/ExecutionEngine.h" #include "mlir/ExecutionEngine/OptUtils.h" #include "mlir/IR/AsmState.h" @@ -264,8 +265,15 @@ // If we aren't dumping the AST, then we are compiling with/to MLIR. - mlir::MLIRContext context; + // Prepare a dialect registry for things used in toy. + mlir::DialectRegistry registry; + + // Register extensions for transformations used by dialects generated in the + // toy compilation pipeline. + mlir::standard::registerInlinerExtension(registry); + // Load our Dialect in this MLIR Context. + mlir::MLIRContext context(registry); context.getOrLoadDialect(); mlir::OwningOpRef module; diff --git a/mlir/examples/toy/Ch7/CMakeLists.txt b/mlir/examples/toy/Ch7/CMakeLists.txt --- a/mlir/examples/toy/Ch7/CMakeLists.txt +++ b/mlir/examples/toy/Ch7/CMakeLists.txt @@ -33,10 +33,12 @@ include_directories(${CMAKE_CURRENT_BINARY_DIR}/include/) get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS) get_property(conversion_libs GLOBAL PROPERTY MLIR_CONVERSION_LIBS) +get_property(extension_libs GLOBAL PROPERTY MLIR_EXTENSION_LIBS) target_link_libraries(toyc-ch7 PRIVATE ${dialect_libs} ${conversion_libs} + ${extension_libs} MLIRAnalysis MLIRCallInterfaces MLIRCastInterfaces diff --git a/mlir/examples/toy/Ch7/toyc.cpp b/mlir/examples/toy/Ch7/toyc.cpp --- a/mlir/examples/toy/Ch7/toyc.cpp +++ b/mlir/examples/toy/Ch7/toyc.cpp @@ -16,6 +16,7 @@ #include "toy/Passes.h" #include "mlir/Dialect/Affine/Passes.h" +#include "mlir/Dialect/StandardOps/Extensions/InlinerExtension.h" #include "mlir/ExecutionEngine/ExecutionEngine.h" #include "mlir/ExecutionEngine/OptUtils.h" #include "mlir/IR/AsmState.h" @@ -265,8 +266,15 @@ // If we aren't dumping the AST, then we are compiling with/to MLIR. - mlir::MLIRContext context; + // Prepare a dialect registry for things used in toy. + mlir::DialectRegistry registry; + + // Register extensions for transformations used by dialects generated in the + // toy compilation pipeline. + mlir::standard::registerInlinerExtension(registry); + // Load our Dialect in this MLIR Context. + mlir::MLIRContext context(registry); context.getOrLoadDialect(); mlir::OwningOpRef module; diff --git a/mlir/include/mlir/Dialect/StandardOps/Extensions/AllExtensions.h b/mlir/include/mlir/Dialect/StandardOps/Extensions/AllExtensions.h new file mode 100644 --- /dev/null +++ b/mlir/include/mlir/Dialect/StandardOps/Extensions/AllExtensions.h @@ -0,0 +1,30 @@ +//===- AllExtensions.h - All Standard Extensions ----------------*- 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 file defines a common entry point for registering all extensions to the +// standard dialect. +// +//===----------------------------------------------------------------------===// + +#ifndef MLIR_DIALECT_STANDARDOPS_EXTENSIONS_ALLEXTENSIONS_H +#define MLIR_DIALECT_STANDARDOPS_EXTENSIONS_ALLEXTENSIONS_H + +namespace mlir { +class DialectRegistry; + +namespace standard { +/// Register all extensions of the standard dialect. This should generally only +/// be used by tools, or other use cases that really do want *all* extensions of +/// the dialect. All other cases should prefer to instead register the specific +/// extensions they intend to take advantage of. +void registerAllExtensions(DialectRegistry ®istry); +} // namespace standard + +} // namespace mlir + +#endif // MLIR_DIALECT_STANDARDOPS_EXTENSIONS_ALLEXTENSIONS_H diff --git a/mlir/include/mlir/Dialect/StandardOps/Extensions/InlinerExtension.h b/mlir/include/mlir/Dialect/StandardOps/Extensions/InlinerExtension.h new file mode 100644 --- /dev/null +++ b/mlir/include/mlir/Dialect/StandardOps/Extensions/InlinerExtension.h @@ -0,0 +1,27 @@ +//===- InlinerExtension.h - Standard Inliner Extension ----------*- 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 file defines an extension for the standard dialect that implements the +// interfaces necessary to support inlining. +// +//===----------------------------------------------------------------------===// + +#ifndef MLIR_DIALECT_STANDARDOPS_EXTENSIONS_INLINEREXTENSION_H +#define MLIR_DIALECT_STANDARDOPS_EXTENSIONS_INLINEREXTENSION_H + +namespace mlir { +class DialectRegistry; + +namespace standard { +/// Register the extension used to support inlining the standard dialect. +void registerInlinerExtension(DialectRegistry ®istry); +} // namespace standard + +} // namespace mlir + +#endif // MLIR_DIALECT_STANDARDOPS_EXTENSIONS_INLINEREXTENSION_H diff --git a/mlir/include/mlir/Dialect/StandardOps/IR/Ops.h b/mlir/include/mlir/Dialect/StandardOps/IR/Ops.h --- a/mlir/include/mlir/Dialect/StandardOps/IR/Ops.h +++ b/mlir/include/mlir/Dialect/StandardOps/IR/Ops.h @@ -14,7 +14,6 @@ #ifndef MLIR_DIALECT_STANDARDOPS_IR_OPS_H #define MLIR_DIALECT_STANDARDOPS_IR_OPS_H -#include "mlir/Dialect/ControlFlow/IR/ControlFlow.h" #include "mlir/IR/Builders.h" #include "mlir/IR/BuiltinTypes.h" #include "mlir/IR/Dialect.h" diff --git a/mlir/include/mlir/Dialect/StandardOps/IR/Ops.td b/mlir/include/mlir/Dialect/StandardOps/IR/Ops.td --- a/mlir/include/mlir/Dialect/StandardOps/IR/Ops.td +++ b/mlir/include/mlir/Dialect/StandardOps/IR/Ops.td @@ -24,7 +24,6 @@ def StandardOps_Dialect : Dialect { let name = "std"; let cppNamespace = "::mlir"; - let dependentDialects = ["cf::ControlFlowDialect"]; let hasConstantMaterializer = 1; let emitAccessorPrefix = kEmitAccessorPrefix_Prefixed; } diff --git a/mlir/include/mlir/IR/Attributes.h b/mlir/include/mlir/IR/Attributes.h --- a/mlir/include/mlir/IR/Attributes.h +++ b/mlir/include/mlir/IR/Attributes.h @@ -224,6 +224,14 @@ private: /// Returns the impl interface instance for the given type. static typename InterfaceBase::Concept *getInterfaceFor(Attribute attr) { +#ifndef NDEBUG + // Check that the current interface isn't an unresolved promise for the + // given attribute. + dialect_extension_detail::handleUseOfUndefinedPromisedInterface( + attr.getDialect(), ConcreteType::getInterfaceID(), + llvm::getTypeName()); +#endif + return attr.getAbstractAttribute().getInterface(); } diff --git a/mlir/include/mlir/IR/Dialect.h b/mlir/include/mlir/IR/Dialect.h --- a/mlir/include/mlir/IR/Dialect.h +++ b/mlir/include/mlir/IR/Dialect.h @@ -158,10 +158,20 @@ /// Lookup an interface for the given ID if one is registered, otherwise /// nullptr. const DialectInterface *getRegisteredInterface(TypeID interfaceID) { +#ifndef NDEBUG + handleUseOfUndefinedPromisedInterface(interfaceID); +#endif + auto it = registeredInterfaces.find(interfaceID); return it != registeredInterfaces.end() ? it->getSecond().get() : nullptr; } - template const InterfaceT *getRegisteredInterface() { + template + const InterfaceT *getRegisteredInterface() { +#ifndef NDEBUG + handleUseOfUndefinedPromisedInterface(InterfaceT::getInterfaceID(), + llvm::getTypeName()); +#endif + return static_cast( getRegisteredInterface(InterfaceT::getInterfaceID())); } @@ -189,6 +199,37 @@ 0, (addInterface(std::make_unique(this)), 0)...}; } + /// Declare that the given interface will be implemented, but has a delayed + /// registration. The promised interface type can be an interface of any type + /// not just a dialect interface, i.e. it may also be an + /// AttributeInterface/OpInterface/TypeInterface/etc. + template + void declarePromisedInterface() { + unresolvedPromisedInterfaces.insert(InterfaceT::getInterfaceID()); + } + + /// Checks if the given interface, which is attempting to be used, is a + /// promised interface of this dialect that has yet to be implemented. If so, + /// emits a fatal error. `interfaceName` is an optional string that contains a + /// more user readable name for the interface (such as the class name). + void handleUseOfUndefinedPromisedInterface(TypeID interfaceID, + StringRef interfaceName = "") { + if (unresolvedPromisedInterfaces.count(interfaceID)) { + llvm::report_fatal_error( + "checking for an interface (`" + interfaceName + + "`) that was promised by dialect '" + getNamespace() + + "' but never implemented. This is generally an indication " + "that the dialect extension implementing the interface was never " + "registered."); + } + } + /// Checks if the given interface, which is attempting to be attached to a + /// construct owned by this dialect, is a promised interface of this dialect + /// that has yet to be implemented. If so, it resolves the interface promise. + void handleAdditionOfUndefinedPromisedInterface(TypeID interfaceID) { + unresolvedPromisedInterfaces.erase(interfaceID); + } + protected: /// The constructor takes a unique namespace for this dialect as well as the /// context to bind to. @@ -269,6 +310,11 @@ /// A collection of registered dialect interfaces. DenseMap> registeredInterfaces; + /// A set of interfaces that the dialect (or its constructs, i.e. + /// Attributes/Operations/Types/etc.) has promised to implement, but has yet + /// to provide an implementation for. + DenseSet unresolvedPromisedInterfaces; + friend class DialectRegistry; friend void registerDialect(); friend class MLIRContext; diff --git a/mlir/include/mlir/IR/DialectRegistry.h b/mlir/include/mlir/IR/DialectRegistry.h --- a/mlir/include/mlir/IR/DialectRegistry.h +++ b/mlir/include/mlir/IR/DialectRegistry.h @@ -94,6 +94,22 @@ } }; +namespace dialect_extension_detail { + +/// Checks if the given interface, which is attempting to be used, is a +/// promised interface of this dialect that has yet to be implemented. If so, +/// emits a fatal error. +void handleUseOfUndefinedPromisedInterface(Dialect &dialect, TypeID interfaceID, + StringRef interfaceName); + +/// Checks if the given interface, which is attempting to be attached, is a +/// promised interface of this dialect that has yet to be implemented. If so, +/// the promised interface is marked as resolved. +void handleAdditionOfUndefinedPromisedInterface(Dialect &dialect, + TypeID interfaceID); + +} // namespace dialect_extension_detail + //===----------------------------------------------------------------------===// // DialectRegistry //===----------------------------------------------------------------------===// diff --git a/mlir/include/mlir/IR/OpDefinition.h b/mlir/include/mlir/IR/OpDefinition.h --- a/mlir/include/mlir/IR/OpDefinition.h +++ b/mlir/include/mlir/IR/OpDefinition.h @@ -1875,6 +1875,16 @@ static typename InterfaceBase::Concept *getInterfaceFor(Operation *op) { OperationName name = op->getName(); +#ifndef NDEBUG + // Check that the current interface isn't an unresolved promise for the + // given operation. + if (Dialect *dialect = name.getDialect()) { + dialect_extension_detail::handleUseOfUndefinedPromisedInterface( + *dialect, ConcreteType::getInterfaceID(), + llvm::getTypeName()); + } +#endif + // Access the raw interface from the operation info. if (Optional rInfo = name.getRegisteredInfo()) { if (auto *opIface = rInfo->getInterface()) diff --git a/mlir/include/mlir/IR/OperationSupport.h b/mlir/include/mlir/IR/OperationSupport.h --- a/mlir/include/mlir/IR/OperationSupport.h +++ b/mlir/include/mlir/IR/OperationSupport.h @@ -314,6 +314,12 @@ /// for the concrete operation. template void attachInterface() { + // Handle the case where the models resolve a promised interface. + (void)std::initializer_list{ + (dialect_extension_detail::handleAdditionOfUndefinedPromisedInterface( + getDialect(), Models::Interface::getInterfaceID()), + 0)...}; + impl->interfaceMap.insert(); } diff --git a/mlir/include/mlir/IR/StorageUniquerSupport.h b/mlir/include/mlir/IR/StorageUniquerSupport.h --- a/mlir/include/mlir/IR/StorageUniquerSupport.h +++ b/mlir/include/mlir/IR/StorageUniquerSupport.h @@ -13,6 +13,7 @@ #ifndef MLIR_IR_STORAGEUNIQUERSUPPORT_H #define MLIR_IR_STORAGEUNIQUERSUPPORT_H +#include "mlir/IR/DialectRegistry.h" #include "mlir/Support/InterfaceSupport.h" #include "mlir/Support/LogicalResult.h" #include "mlir/Support/StorageUniquer.h" @@ -127,6 +128,13 @@ if (!abstract) llvm::report_fatal_error("Registering an interface for an attribute/type " "that is not itself registered."); + + // Handle the case where the models resolve a promised interface. + (void)std::initializer_list{ + (dialect_extension_detail::handleAdditionOfUndefinedPromisedInterface( + abstract->getDialect(), IfaceModels::Interface::getInterfaceID()), + 0)...}; + abstract->interfaceMap.template insert(); } diff --git a/mlir/include/mlir/IR/Types.h b/mlir/include/mlir/IR/Types.h --- a/mlir/include/mlir/IR/Types.h +++ b/mlir/include/mlir/IR/Types.h @@ -215,6 +215,14 @@ private: /// Returns the impl interface instance for the given type. static typename InterfaceBase::Concept *getInterfaceFor(Type type) { +#ifndef NDEBUG + // Check that the current interface isn't an unresolved promise for the + // given type. + dialect_extension_detail::handleUseOfUndefinedPromisedInterface( + type.getDialect(), ConcreteType::getInterfaceID(), + llvm::getTypeName()); +#endif + return type.getAbstractType().getInterface(); } diff --git a/mlir/include/mlir/InitAllExtensions.h b/mlir/include/mlir/InitAllExtensions.h new file mode 100644 --- /dev/null +++ b/mlir/include/mlir/InitAllExtensions.h @@ -0,0 +1,34 @@ +//===- InitAllExtensions.h - MLIR Extension Registration --------*- 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 file defines a helper to trigger the registration of all dialect +// extensions to the system. +// +//===----------------------------------------------------------------------===// + +#ifndef MLIR_INITALLEXTENSIONS_H_ +#define MLIR_INITALLEXTENSIONS_H_ + +#include "mlir/Dialect/StandardOps/Extensions/AllExtensions.h" + +#include + +namespace mlir { + +/// This function may be called to register all MLIR dialect extensions with the +/// provided registry. +/// If you're building a compiler, you generally shouldn't use this: you would +/// individually register the specific extensions that are useful for the +/// pipelines and transformations you are using. +inline void registerAllExtensions(DialectRegistry ®istry) { + standard::registerAllExtensions(registry); +} + +} // namespace mlir + +#endif // MLIR_INITALLEXTENSIONS_H_ diff --git a/mlir/lib/Dialect/StandardOps/CMakeLists.txt b/mlir/lib/Dialect/StandardOps/CMakeLists.txt --- a/mlir/lib/Dialect/StandardOps/CMakeLists.txt +++ b/mlir/lib/Dialect/StandardOps/CMakeLists.txt @@ -1,22 +1,3 @@ -add_mlir_dialect_library(MLIRStandard - IR/Ops.cpp - - ADDITIONAL_HEADER_DIRS - ${MLIR_MAIN_INCLUDE_DIR}/mlir/Dialect/StandardOps - - DEPENDS - MLIRStandardOpsIncGen - - LINK_LIBS PUBLIC - MLIRArithmetic - MLIRCallInterfaces - MLIRCastInterfaces - MLIRControlFlow - MLIRControlFlowInterfaces - MLIRInferTypeOpInterface - MLIRIR - MLIRSideEffectInterfaces - MLIRVectorInterfaces - ) - +add_subdirectory(Extensions) +add_subdirectory(IR) add_subdirectory(Transforms) diff --git a/mlir/lib/Dialect/StandardOps/Extensions/AllExtensions.cpp b/mlir/lib/Dialect/StandardOps/Extensions/AllExtensions.cpp new file mode 100644 --- /dev/null +++ b/mlir/lib/Dialect/StandardOps/Extensions/AllExtensions.cpp @@ -0,0 +1,16 @@ +//===- Ops.cpp - Standard MLIR Operations ---------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "mlir/Dialect/StandardOps/Extensions/AllExtensions.h" +#include "mlir/Dialect/StandardOps/Extensions/InlinerExtension.h" + +using namespace mlir; + +void mlir::standard::registerAllExtensions(DialectRegistry ®istry) { + registerInlinerExtension(registry); +} diff --git a/mlir/lib/Dialect/StandardOps/Extensions/CMakeLists.txt b/mlir/lib/Dialect/StandardOps/Extensions/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/mlir/lib/Dialect/StandardOps/Extensions/CMakeLists.txt @@ -0,0 +1,27 @@ +set(LLVM_OPTIONAL_SOURCES + AllExtensions.cpp + InlinerExtension.cpp + ) + +add_mlir_extension_library(MLIRStandardInlinerExtension + InlinerExtension.cpp + + ADDITIONAL_HEADER_DIRS + ${MLIR_MAIN_INCLUDE_DIR}/mlir/Dialect/StandardOps/Extensions + + LINK_LIBS PUBLIC + MLIRControlFlow + MLIRInferTypeOpInterface + MLIRIR + MLIRStandard + ) + +add_mlir_extension_library(MLIRStandardAllExtensions + AllExtensions.cpp + + ADDITIONAL_HEADER_DIRS + ${MLIR_MAIN_INCLUDE_DIR}/mlir/Dialect/StandardOps/Extensions + + LINK_LIBS PUBLIC + MLIRStandardInlinerExtension + ) diff --git a/mlir/lib/Dialect/StandardOps/Extensions/InlinerExtension.cpp b/mlir/lib/Dialect/StandardOps/Extensions/InlinerExtension.cpp new file mode 100644 --- /dev/null +++ b/mlir/lib/Dialect/StandardOps/Extensions/InlinerExtension.cpp @@ -0,0 +1,86 @@ +//===- Ops.cpp - Standard MLIR Operations ---------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "mlir/Dialect/StandardOps/Extensions/InlinerExtension.h" +#include "mlir/Dialect/ControlFlow/IR/ControlFlowOps.h" +#include "mlir/Dialect/StandardOps/IR/Ops.h" +#include "mlir/IR/DialectInterface.h" +#include "mlir/Transforms/InliningUtils.h" + +using namespace mlir; + +//===----------------------------------------------------------------------===// +// StandardOpsDialect Interfaces +//===----------------------------------------------------------------------===// +namespace { +/// This class defines the interface for handling inlining with standard +/// operations. +struct StdInlinerInterface : public DialectInlinerInterface { + using DialectInlinerInterface::DialectInlinerInterface; + + //===--------------------------------------------------------------------===// + // Analysis Hooks + //===--------------------------------------------------------------------===// + + /// All call operations within standard ops can be inlined. + bool isLegalToInline(Operation *call, Operation *callable, + bool wouldBeCloned) const final { + return true; + } + + /// All operations within standard ops can be inlined. + bool isLegalToInline(Operation *, Region *, bool, + BlockAndValueMapping &) const final { + return true; + } + + //===--------------------------------------------------------------------===// + // Transformation Hooks + //===--------------------------------------------------------------------===// + + /// Handle the given inlined terminator by replacing it with a new operation + /// as necessary. + void handleTerminator(Operation *op, Block *newDest) const final { + // Only "std.return" needs to be handled here. + auto returnOp = dyn_cast(op); + if (!returnOp) + return; + + // Replace the return with a branch to the dest. + OpBuilder builder(op); + builder.create(op->getLoc(), newDest, returnOp.getOperands()); + op->erase(); + } + + /// Handle the given inlined terminator by replacing it with a new operation + /// as necessary. + void handleTerminator(Operation *op, + ArrayRef valuesToRepl) const final { + // Only "std.return" needs to be handled here. + auto returnOp = cast(op); + + // Replace the values directly with the return operands. + assert(returnOp.getNumOperands() == valuesToRepl.size()); + for (const auto &it : llvm::enumerate(returnOp.getOperands())) + valuesToRepl[it.index()].replaceAllUsesWith(it.value()); + } +}; +} // namespace + +//===----------------------------------------------------------------------===// +// Registration +//===----------------------------------------------------------------------===// + +void mlir::standard::registerInlinerExtension(DialectRegistry ®istry) { + registry.addExtension(+[](MLIRContext *ctx, StandardOpsDialect *dialect) { + dialect->addInterfaces(); + + // The inliner extension relies on the ControlFlow dialect. + ctx->getOrLoadDialect(); + }); +} diff --git a/mlir/lib/Dialect/StandardOps/CMakeLists.txt b/mlir/lib/Dialect/StandardOps/IR/CMakeLists.txt copy from mlir/lib/Dialect/StandardOps/CMakeLists.txt copy to mlir/lib/Dialect/StandardOps/IR/CMakeLists.txt --- a/mlir/lib/Dialect/StandardOps/CMakeLists.txt +++ b/mlir/lib/Dialect/StandardOps/IR/CMakeLists.txt @@ -1,22 +1,17 @@ add_mlir_dialect_library(MLIRStandard - IR/Ops.cpp + Ops.cpp ADDITIONAL_HEADER_DIRS - ${MLIR_MAIN_INCLUDE_DIR}/mlir/Dialect/StandardOps + ${MLIR_MAIN_INCLUDE_DIR}/mlir/Dialect/StandardOps/IR DEPENDS MLIRStandardOpsIncGen LINK_LIBS PUBLIC - MLIRArithmetic MLIRCallInterfaces MLIRCastInterfaces - MLIRControlFlow MLIRControlFlowInterfaces MLIRInferTypeOpInterface MLIRIR MLIRSideEffectInterfaces - MLIRVectorInterfaces ) - -add_subdirectory(Transforms) diff --git a/mlir/lib/Dialect/StandardOps/IR/Ops.cpp b/mlir/lib/Dialect/StandardOps/IR/Ops.cpp --- a/mlir/lib/Dialect/StandardOps/IR/Ops.cpp +++ b/mlir/lib/Dialect/StandardOps/IR/Ops.cpp @@ -9,7 +9,6 @@ #include "mlir/Dialect/StandardOps/IR/Ops.h" #include "mlir/Dialect/CommonFolders.h" -#include "mlir/Dialect/ControlFlow/IR/ControlFlowOps.h" #include "mlir/IR/AffineExpr.h" #include "mlir/IR/AffineMap.h" #include "mlir/IR/BlockAndValueMapping.h" @@ -37,64 +36,6 @@ using namespace mlir; -//===----------------------------------------------------------------------===// -// StandardOpsDialect Interfaces -//===----------------------------------------------------------------------===// -namespace { -/// This class defines the interface for handling inlining with standard -/// operations. -struct StdInlinerInterface : public DialectInlinerInterface { - using DialectInlinerInterface::DialectInlinerInterface; - - //===--------------------------------------------------------------------===// - // Analysis Hooks - //===--------------------------------------------------------------------===// - - /// All call operations within standard ops can be inlined. - bool isLegalToInline(Operation *call, Operation *callable, - bool wouldBeCloned) const final { - return true; - } - - /// All operations within standard ops can be inlined. - bool isLegalToInline(Operation *, Region *, bool, - BlockAndValueMapping &) const final { - return true; - } - - //===--------------------------------------------------------------------===// - // Transformation Hooks - //===--------------------------------------------------------------------===// - - /// Handle the given inlined terminator by replacing it with a new operation - /// as necessary. - void handleTerminator(Operation *op, Block *newDest) const final { - // Only "std.return" needs to be handled here. - auto returnOp = dyn_cast(op); - if (!returnOp) - return; - - // Replace the return with a branch to the dest. - OpBuilder builder(op); - builder.create(op->getLoc(), newDest, returnOp.getOperands()); - op->erase(); - } - - /// Handle the given inlined terminator by replacing it with a new operation - /// as necessary. - void handleTerminator(Operation *op, - ArrayRef valuesToRepl) const final { - // Only "std.return" needs to be handled here. - auto returnOp = cast(op); - - // Replace the values directly with the return operands. - assert(returnOp.getNumOperands() == valuesToRepl.size()); - for (const auto &it : llvm::enumerate(returnOp.getOperands())) - valuesToRepl[it.index()].replaceAllUsesWith(it.value()); - } -}; -} // namespace - //===----------------------------------------------------------------------===// // StandardOpsDialect //===----------------------------------------------------------------------===// @@ -104,7 +45,7 @@ #define GET_OP_LIST #include "mlir/Dialect/StandardOps/IR/Ops.cpp.inc" >(); - addInterfaces(); + declarePromisedInterface(); } /// Materialize a single constant operation from a given attribute value with @@ -112,8 +53,6 @@ Operation *StandardOpsDialect::materializeConstant(OpBuilder &builder, Attribute value, Type type, Location loc) { - if (arith::ConstantOp::isBuildableWith(value, type)) - return builder.create(loc, type, value); if (ConstantOp::isBuildableWith(value, type)) return builder.create(loc, type, value.cast()); diff --git a/mlir/lib/IR/Dialect.cpp b/mlir/lib/IR/Dialect.cpp --- a/mlir/lib/IR/Dialect.cpp +++ b/mlir/lib/IR/Dialect.cpp @@ -95,6 +95,9 @@ /// Register a set of dialect interfaces with this dialect instance. void Dialect::addInterface(std::unique_ptr interface) { + // Handle the case where the models resolve a promised interface. + handleAdditionOfUndefinedPromisedInterface(interface->getID()); + auto it = registeredInterfaces.try_emplace(interface->getID(), std::move(interface)); (void)it; @@ -138,6 +141,16 @@ DialectExtensionBase::~DialectExtensionBase() = default; +void dialect_extension_detail::handleUseOfUndefinedPromisedInterface( + Dialect &dialect, TypeID interfaceID, StringRef interfaceName) { + dialect.handleUseOfUndefinedPromisedInterface(interfaceID, interfaceName); +} + +void dialect_extension_detail::handleAdditionOfUndefinedPromisedInterface( + Dialect &dialect, TypeID interfaceID) { + dialect.handleAdditionOfUndefinedPromisedInterface(interfaceID); +} + //===----------------------------------------------------------------------===// // DialectRegistry //===----------------------------------------------------------------------===// diff --git a/mlir/test/CAPI/ir.c b/mlir/test/CAPI/ir.c --- a/mlir/test/CAPI/ir.c +++ b/mlir/test/CAPI/ir.c @@ -1531,10 +1531,10 @@ fprintf(stderr, "@registration\n"); // CHECK-LABEL: @registration - // CHECK: cf.cond_br is_registered: 1 - fprintf(stderr, "cf.cond_br is_registered: %d\n", + // CHECK: std.return is_registered: 1 + fprintf(stderr, "std.return is_registered: %d\n", mlirContextIsRegisteredOperation( - ctx, mlirStringRefCreateFromCString("cf.cond_br"))); + ctx, mlirStringRefCreateFromCString("std.return"))); // CHECK: std.not_existing_op is_registered: 0 fprintf(stderr, "std.not_existing_op is_registered: %d\n", diff --git a/mlir/test/lib/IR/TestMatchers.cpp b/mlir/test/lib/IR/TestMatchers.cpp --- a/mlir/test/lib/IR/TestMatchers.cpp +++ b/mlir/test/lib/IR/TestMatchers.cpp @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// -#include "mlir/Dialect/StandardOps/IR/Ops.h" +#include "mlir/Dialect/Arithmetic/IR/Arithmetic.h" #include "mlir/IR/BuiltinOps.h" #include "mlir/IR/Matchers.h" #include "mlir/Pass/Pass.h" diff --git a/mlir/tools/mlir-opt/CMakeLists.txt b/mlir/tools/mlir-opt/CMakeLists.txt --- a/mlir/tools/mlir-opt/CMakeLists.txt +++ b/mlir/tools/mlir-opt/CMakeLists.txt @@ -4,6 +4,7 @@ get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS) get_property(conversion_libs GLOBAL PROPERTY MLIR_CONVERSION_LIBS) +get_property(extension_libs GLOBAL PROPERTY MLIR_EXTENSION_LIBS) set(LLVM_LINK_COMPONENTS Core Support @@ -38,7 +39,9 @@ set(LIBS ${dialect_libs} ${conversion_libs} + ${extension_libs} ${test_libs} + MLIRAffineAnalysis MLIRAnalysis MLIRDialect diff --git a/mlir/tools/mlir-opt/mlir-opt.cpp b/mlir/tools/mlir-opt/mlir-opt.cpp --- a/mlir/tools/mlir-opt/mlir-opt.cpp +++ b/mlir/tools/mlir-opt/mlir-opt.cpp @@ -14,6 +14,7 @@ #include "mlir/IR/Dialect.h" #include "mlir/IR/MLIRContext.h" #include "mlir/InitAllDialects.h" +#include "mlir/InitAllExtensions.h" #include "mlir/InitAllPasses.h" #include "mlir/Pass/Pass.h" #include "mlir/Pass/PassManager.h" @@ -205,6 +206,8 @@ #endif DialectRegistry registry; registerAllDialects(registry); + registerAllExtensions(registry); + #ifdef MLIR_INCLUDE_TESTS ::test::registerTestDialect(registry); #endif diff --git a/mlir/unittests/Conversion/PDLToPDLInterp/CMakeLists.txt b/mlir/unittests/Conversion/PDLToPDLInterp/CMakeLists.txt --- a/mlir/unittests/Conversion/PDLToPDLInterp/CMakeLists.txt +++ b/mlir/unittests/Conversion/PDLToPDLInterp/CMakeLists.txt @@ -3,6 +3,6 @@ ) target_link_libraries(MLIRPDLToPDLInterpTests PRIVATE - MLIRStandard + MLIRArithmetic MLIRPDLToPDLInterp ) diff --git a/mlir/unittests/Interfaces/CMakeLists.txt b/mlir/unittests/Interfaces/CMakeLists.txt --- a/mlir/unittests/Interfaces/CMakeLists.txt +++ b/mlir/unittests/Interfaces/CMakeLists.txt @@ -6,6 +6,7 @@ target_link_libraries(MLIRInterfacesTests PRIVATE + MLIRArithmetic MLIRControlFlowInterfaces MLIRDataLayoutInterfaces MLIRDLTI