diff --git a/flang/lib/Optimizer/CodeGen/CodeGen.cpp b/flang/lib/Optimizer/CodeGen/CodeGen.cpp --- a/flang/lib/Optimizer/CodeGen/CodeGen.cpp +++ b/flang/lib/Optimizer/CodeGen/CodeGen.cpp @@ -3391,6 +3391,8 @@ struct LLVMIRLoweringPass : public mlir::PassWrapper> { + MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(LLVMIRLoweringPass) + using Printer = fir::LLVMIRLoweringPrinter; LLVMIRLoweringPass(raw_ostream &output, Printer p) : output{output}, printer{p} {} diff --git a/flang/lib/Optimizer/Transforms/ArrayValueCopy.cpp b/flang/lib/Optimizer/Transforms/ArrayValueCopy.cpp --- a/flang/lib/Optimizer/Transforms/ArrayValueCopy.cpp +++ b/flang/lib/Optimizer/Transforms/ArrayValueCopy.cpp @@ -63,6 +63,8 @@ /// loop-carried, then the arrays are conflict-free and no copies are required. class ArrayCopyAnalysis { public: + MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(ArrayCopyAnalysis) + using ConflictSetT = llvm::SmallPtrSet; using UseSetT = llvm::SmallPtrSet; using LoadMapSetsT = llvm::DenseMap; diff --git a/flang/lib/Optimizer/Transforms/MemoryAllocation.cpp b/flang/lib/Optimizer/Transforms/MemoryAllocation.cpp --- a/flang/lib/Optimizer/Transforms/MemoryAllocation.cpp +++ b/flang/lib/Optimizer/Transforms/MemoryAllocation.cpp @@ -37,6 +37,8 @@ class ReturnAnalysis { public: + MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(ReturnAnalysis) + ReturnAnalysis(mlir::Operation *op) { if (auto func = mlir::dyn_cast(op)) for (mlir::Block &block : func) diff --git a/mlir/examples/toy/Ch4/mlir/ShapeInferencePass.cpp b/mlir/examples/toy/Ch4/mlir/ShapeInferencePass.cpp --- a/mlir/examples/toy/Ch4/mlir/ShapeInferencePass.cpp +++ b/mlir/examples/toy/Ch4/mlir/ShapeInferencePass.cpp @@ -44,9 +44,10 @@ /// d) infer the shape of its output from the argument types. /// 3) If the worklist is empty, the algorithm succeeded. /// -class ShapeInferencePass +struct ShapeInferencePass : public mlir::PassWrapper> { -public: + MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(ShapeInferencePass) + void runOnOperation() override { auto f = getOperation(); diff --git a/mlir/examples/toy/Ch5/mlir/LowerToAffineLoops.cpp b/mlir/examples/toy/Ch5/mlir/LowerToAffineLoops.cpp --- a/mlir/examples/toy/Ch5/mlir/LowerToAffineLoops.cpp +++ b/mlir/examples/toy/Ch5/mlir/LowerToAffineLoops.cpp @@ -310,6 +310,8 @@ namespace { struct ToyToAffineLoweringPass : public PassWrapper> { + MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(ToyToAffineLoweringPass) + void getDependentDialects(DialectRegistry ®istry) const override { registry.insert(); } diff --git a/mlir/examples/toy/Ch5/mlir/ShapeInferencePass.cpp b/mlir/examples/toy/Ch5/mlir/ShapeInferencePass.cpp --- a/mlir/examples/toy/Ch5/mlir/ShapeInferencePass.cpp +++ b/mlir/examples/toy/Ch5/mlir/ShapeInferencePass.cpp @@ -44,9 +44,10 @@ /// d) infer the shape of its output from the argument types. /// 3) If the worklist is empty, the algorithm succeeded. /// -class ShapeInferencePass +struct ShapeInferencePass : public mlir::PassWrapper> { -public: + MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(ShapeInferencePass) + void runOnOperation() override { auto f = getOperation(); diff --git a/mlir/examples/toy/Ch6/mlir/LowerToAffineLoops.cpp b/mlir/examples/toy/Ch6/mlir/LowerToAffineLoops.cpp --- a/mlir/examples/toy/Ch6/mlir/LowerToAffineLoops.cpp +++ b/mlir/examples/toy/Ch6/mlir/LowerToAffineLoops.cpp @@ -310,6 +310,8 @@ namespace { struct ToyToAffineLoweringPass : public PassWrapper> { + MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(ToyToAffineLoweringPass) + void getDependentDialects(DialectRegistry ®istry) const override { registry.insert(); } diff --git a/mlir/examples/toy/Ch6/mlir/LowerToLLVM.cpp b/mlir/examples/toy/Ch6/mlir/LowerToLLVM.cpp --- a/mlir/examples/toy/Ch6/mlir/LowerToLLVM.cpp +++ b/mlir/examples/toy/Ch6/mlir/LowerToLLVM.cpp @@ -172,6 +172,8 @@ namespace { struct ToyToLLVMLoweringPass : public PassWrapper> { + MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(ToyToLLVMLoweringPass) + void getDependentDialects(DialectRegistry ®istry) const override { registry.insert(); } diff --git a/mlir/examples/toy/Ch6/mlir/ShapeInferencePass.cpp b/mlir/examples/toy/Ch6/mlir/ShapeInferencePass.cpp --- a/mlir/examples/toy/Ch6/mlir/ShapeInferencePass.cpp +++ b/mlir/examples/toy/Ch6/mlir/ShapeInferencePass.cpp @@ -44,9 +44,10 @@ /// d) infer the shape of its output from the argument types. /// 3) If the worklist is empty, the algorithm succeeded. /// -class ShapeInferencePass +struct ShapeInferencePass : public mlir::PassWrapper> { -public: + MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(ShapeInferencePass) + void runOnOperation() override { auto f = getOperation(); diff --git a/mlir/examples/toy/Ch7/mlir/LowerToAffineLoops.cpp b/mlir/examples/toy/Ch7/mlir/LowerToAffineLoops.cpp --- a/mlir/examples/toy/Ch7/mlir/LowerToAffineLoops.cpp +++ b/mlir/examples/toy/Ch7/mlir/LowerToAffineLoops.cpp @@ -310,6 +310,8 @@ namespace { struct ToyToAffineLoweringPass : public PassWrapper> { + MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(ToyToAffineLoweringPass) + void getDependentDialects(DialectRegistry ®istry) const override { registry.insert(); } diff --git a/mlir/examples/toy/Ch7/mlir/LowerToLLVM.cpp b/mlir/examples/toy/Ch7/mlir/LowerToLLVM.cpp --- a/mlir/examples/toy/Ch7/mlir/LowerToLLVM.cpp +++ b/mlir/examples/toy/Ch7/mlir/LowerToLLVM.cpp @@ -172,6 +172,8 @@ namespace { struct ToyToLLVMLoweringPass : public PassWrapper> { + MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(ToyToLLVMLoweringPass) + void getDependentDialects(DialectRegistry ®istry) const override { registry.insert(); } diff --git a/mlir/examples/toy/Ch7/mlir/ShapeInferencePass.cpp b/mlir/examples/toy/Ch7/mlir/ShapeInferencePass.cpp --- a/mlir/examples/toy/Ch7/mlir/ShapeInferencePass.cpp +++ b/mlir/examples/toy/Ch7/mlir/ShapeInferencePass.cpp @@ -44,9 +44,10 @@ /// d) infer the shape of its output from the argument types. /// 3) If the worklist is empty, the algorithm succeeded. /// -class ShapeInferencePass +struct ShapeInferencePass : public mlir::PassWrapper> { -public: + MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(ShapeInferencePass) + void runOnOperation() override { auto f = getOperation(); diff --git a/mlir/include/mlir/Conversion/SCFToGPU/SCFToGPUPass.h b/mlir/include/mlir/Conversion/SCFToGPU/SCFToGPUPass.h --- a/mlir/include/mlir/Conversion/SCFToGPU/SCFToGPUPass.h +++ b/mlir/include/mlir/Conversion/SCFToGPU/SCFToGPUPass.h @@ -8,12 +8,12 @@ #ifndef MLIR_CONVERSION_SCFTOGPU_SCFTOGPUPASS_H_ #define MLIR_CONVERSION_SCFTOGPU_SCFTOGPUPASS_H_ +#include "mlir/IR/FunctionInterfaces.h" #include "mlir/Support/LLVM.h" #include namespace mlir { -class FunctionOpInterface; template class InterfacePass; class Pass; diff --git a/mlir/include/mlir/Pass/AnalysisManager.h b/mlir/include/mlir/Pass/AnalysisManager.h --- a/mlir/include/mlir/Pass/AnalysisManager.h +++ b/mlir/include/mlir/Pass/AnalysisManager.h @@ -28,7 +28,7 @@ /// A utility class to represent the analyses that are known to be preserved. class PreservedAnalyses { /// A type used to represent all potential analyses. - struct AllAnalysesType; + struct AllAnalysesType {}; public: /// Mark all analyses as preserved. diff --git a/mlir/include/mlir/Support/TypeID.h b/mlir/include/mlir/Support/TypeID.h --- a/mlir/include/mlir/Support/TypeID.h +++ b/mlir/include/mlir/Support/TypeID.h @@ -17,14 +17,15 @@ #include "mlir/Support/LLVM.h" #include "llvm/ADT/DenseMapInfo.h" #include "llvm/ADT/Hashing.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/Support/Allocator.h" #include "llvm/Support/PointerLikeTypeTraits.h" +#include "llvm/Support/TypeName.h" namespace mlir { - -namespace detail { -struct TypeIDExported; -} // namespace detail +//===----------------------------------------------------------------------===// +// TypeID +//===----------------------------------------------------------------------===// /// This class provides an efficient unique identifier for a specific C++ type. /// This allows for a C++ type to be compared, hashed, and stored in an opaque @@ -50,6 +51,56 @@ /// ... /// } /// +/// C++ RTTI is a notoriously difficult topic; given the nature of shared +/// libraries many different approaches fundamentally break down in either the +/// area of support (i.e. only certain types of classes are supported), or in +/// terms of performance (e.g. by using string comparison). This class intends +/// to strike a balance between performance and the setup required to enable its +/// use. +/// +/// Assume we are adding support for some class Foo, below are the set of ways +/// in which a given c++ type may be supported: +/// +/// * Explicitly via `MLIR_DECLARE_EXPLICIT_TYPE_ID` and +/// `MLIR_DEFINE_EXPLICIT_TYPE_ID` +/// +/// - This method explicitly defines the type ID for a given type using the +/// given macros. These should be placed at the top-level of the file (i.e. +/// not within any namespace or class). This is the most effective and +/// efficient method, but requires explicit annotations for each type. +/// +/// Example: +/// +/// // Foo.h +/// MLIR_DECLARE_EXPLICIT_TYPE_ID(Foo); +/// +/// // Foo.cpp +/// MLIR_DEFINE_EXPLICIT_TYPE_ID(Foo); +/// +/// * Explicitly via `MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID` +/// - This method explicitly defines the type ID for a given type by +/// annotating the class directly. This has similar effectiveness and +/// efficiency to the above method, but should only be used on internal +/// classes; i.e. those with definitions constrained to a specific library +/// (generally classes in anonymous namespaces). +/// +/// Example: +/// +/// namespace { +/// class Foo { +/// public: +/// MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(Foo) +/// }; +/// } // namespace +/// +/// * Implicitly via a fallback using the type name +/// - This method implicitly defines a type ID for a given type by using the +/// type name. This method requires nothing explicitly from the user, but +/// pays additional access and initialization cost. Given that this method +/// uses the name of the type, it may not be used for types defined in +/// anonymous namespaces (which is asserted when it can be detected). String +/// names do not provide any guarantees on uniqueness in these contexts. +/// class TypeID { /// This class represents the storage of a type info object. /// Note: We specify an explicit alignment here to allow use with @@ -91,9 +142,6 @@ /// The storage of this type info object. const Storage *storage; - // See TypeIDExported below for an explanation of the trampoline behavior. - friend struct detail::TypeIDExported; - friend class TypeIDAllocator; }; @@ -102,45 +150,142 @@ return DenseMapInfo::getHashValue(id.storage); } +//===----------------------------------------------------------------------===// +// TypeIDResolver +//===----------------------------------------------------------------------===// + namespace detail { +/// This class provides a fallback for resolving TypeIDs. It uses the string +/// name of the type to perform the resolution, and as such does not allow the +/// use of classes defined in "anonymous" contexts. +class FallbackTypeIDResolver { +protected: + /// Register an implicit type ID for the given type name. + static TypeID registerImplicitTypeID(StringRef name); +}; + +/// This class provides a resolver for getting the ID for a given class T. This +/// allows for the derived type to specialize its resolution behavior. The +/// default implementation uses the string name of the type to resolve the ID. +/// This provides a strong definition, but at the cost of performance (we need +/// to do an initial lookup) and is not usable by classes defined in anonymous +/// contexts. +/// +/// TODO: The use of the type name is only necessary when building in the +/// presence of shared libraries. We could add a build flag that guarantees +/// "static"-like environments and switch this to a more optimal implementation +/// when that is enabled. +template +class TypeIDResolver : public FallbackTypeIDResolver { +public: + /// Trait to check if `U` is fully resolved. We use this to verify that `T` is + /// fully resolved when trying to resolve a TypeID. We don't technically need + /// to have the full definition of `T` for the fallback, but it does help + /// prevent situations where a forward declared type uses this fallback even + /// though there is a strong definition for the TypeID in the location where + /// `T` is defined. + template + using is_fully_resolved_trait = decltype(sizeof(U)); + template + using is_fully_resolved = llvm::is_detected; + + static TypeID resolveTypeID() { + static_assert(is_fully_resolved::value, + "TypeID::get<> requires the complete definition of `T`"); + static TypeID id = registerImplicitTypeID(llvm::getTypeName()); + return id; + } +}; + +/// This class provides utilities for resolving the TypeID of a class that +/// provides a `static TypeID resolveTypeID()` method. This allows for +/// simplifying situations when the class can resolve the ID itself. This +/// functionality is separated from the corresponding `TypeIDResolver` +/// specialization below to enable referencing it more easily in different +/// contexts. +struct InlineTypeIDResolver { + /// Trait to check if `T` provides a static `resolveTypeID` method. + template + using has_resolve_typeid_trait = decltype(T::resolveTypeID()); + template + using has_resolve_typeid = llvm::is_detected; -/// The static local instance of each get method must be emitted with -/// "default" (public) visibility across all shared libraries, regardless of -/// whether they are compiled with hidden visibility or not. The only reliable -/// way to make this happen is to set the visibility attribute at the -/// containing namespace/struct scope. We don't do this on the TypeID (internal -/// API) class in order to reduce the scope of what gets exported with -/// public visibility. Instead, the get() methods on TypeID trampoline -/// through those on this detail class with specific visibility controls -/// applied, making visibility declarations on the internal TypeID class not -/// required (all visibility relevant pieces are here). -/// TODO: This currently won't work when using DLLs as it requires properly -/// attaching dllimport and dllexport. Fix this when that information is -/// available within LLVM. -struct LLVM_EXTERNAL_VISIBILITY TypeIDExported { template - static TypeID get() { - static TypeID::Storage instance; - return TypeID(&instance); + static TypeID resolveTypeID() { + return T::resolveTypeID(); } - template