MLIR 23.0.0git
StorageUniquerSupport.h
Go to the documentation of this file.
1//===- StorageUniquerSupport.h - MLIR Storage Uniquer Utilities -*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file defines utility classes for interfacing with StorageUniquer.
10//
11//===----------------------------------------------------------------------===//
12
13#ifndef MLIR_IR_STORAGEUNIQUERSUPPORT_H
14#define MLIR_IR_STORAGEUNIQUERSUPPORT_H
15
20#include "mlir/Support/TypeID.h"
21#include "llvm/ADT/FunctionExtras.h"
22
23namespace mlir {
25class Location;
26class MLIRContext;
27
28namespace detail {
29/// Utility method to generate a callback that can be used to generate a
30/// diagnostic when checking the construction invariants of a storage object.
31/// This is defined out-of-line to avoid the need to include Location.h.
32llvm::unique_function<InFlightDiagnostic()>
33getDefaultDiagnosticEmitFn(MLIRContext *ctx);
34llvm::unique_function<InFlightDiagnostic()>
35getDefaultDiagnosticEmitFn(const Location &loc);
36
37//===----------------------------------------------------------------------===//
38// StorageUserTraitBase
39//===----------------------------------------------------------------------===//
40
41/// Helper class for implementing traits for storage classes. Clients are not
42/// expected to interact with this directly, so its members are all protected.
43template <typename ConcreteType, template <typename> class TraitType>
45protected:
46 /// Return the derived instance.
47 ConcreteType getInstance() const {
48 // We have to cast up to the trait type, then to the concrete type because
49 // the concrete type will multiply derive from the (content free) TraitBase
50 // class, and we need to be able to disambiguate the path for the C++
51 // compiler.
52 auto *trait = static_cast<const TraitType<ConcreteType> *>(this);
53 return *static_cast<const ConcreteType *>(trait);
54 }
55};
56
58/// This trait is used to determine if a storage user, like Type, is mutable
59/// or not. A storage user is mutable if ImplType of the derived class defines
60/// a `mutate` function with a proper signature. Note that this trait is not
61/// supposed to be used publicly. Users should use alias names like
62/// `TypeTrait::IsMutable` instead.
63template <typename ConcreteType>
64struct IsMutable : public StorageUserTraitBase<ConcreteType, IsMutable> {};
65} // namespace StorageUserTrait
66
67//===----------------------------------------------------------------------===//
68// StorageUserBase
69//===----------------------------------------------------------------------===//
70
72/// Returns true if this given Trait ID matches the IDs of any of the provided
73/// trait types `Traits`.
74template <template <typename T> class... Traits>
75bool hasTrait(TypeID traitID) {
76 TypeID traitIDs[] = {TypeID::get<Traits>()...};
77 for (unsigned i = 0, e = sizeof...(Traits); i != e; ++i)
78 if (traitIDs[i] == traitID)
79 return true;
80 return false;
81}
82
83// We specialize for the empty case to not define an empty array.
84template <>
85inline bool hasTrait(TypeID traitID) {
86 return false;
87}
88} // namespace storage_user_base_impl
89
90/// Utility class for implementing users of storage classes uniqued by a
91/// StorageUniquer. Clients are not expected to interact with this class
92/// directly.
93template <typename ConcreteT, typename BaseT, typename StorageT,
94 typename UniquerT, template <typename T> class... Traits>
95class StorageUserBase : public BaseT, public Traits<ConcreteT>... {
96public:
97 using BaseT::BaseT;
98
99 /// Utility declarations for the concrete attribute class.
100 using Base = StorageUserBase<ConcreteT, BaseT, StorageT, UniquerT, Traits...>;
101 using ImplType = StorageT;
102 using HasTraitFn = bool (*)(TypeID);
103
104 /// Return a unique identifier for the concrete type.
106
107 /// Provide an implementation of 'classof' that compares the type id of the
108 /// provided value with that of the concrete type.
109 template <typename T>
110 static bool classof(T val) {
111 static_assert(std::is_convertible<ConcreteT, T>::value,
112 "casting from a non-convertible type");
113 return val.getTypeID() == getTypeID();
114 }
115
116 /// Returns an interface map for the interfaces registered to this storage
117 /// user. This should not be used directly.
119 return detail::InterfaceMap::template get<Traits<ConcreteT>...>();
120 }
121
122 /// Returns the function that returns true if the given Trait ID matches the
123 /// IDs of any of the traits defined by the storage user.
125 return [](TypeID id) {
126 return storage_user_base_impl::hasTrait<Traits...>(id);
127 };
128 }
129
130 /// Returns a function that walks immediate sub elements of a given instance
131 /// of the storage user.
133 return [](auto instance, function_ref<void(Attribute)> walkAttrsFn,
134 function_ref<void(Type)> walkTypesFn) {
136 llvm::cast<ConcreteT>(instance), walkAttrsFn, walkTypesFn);
137 };
138 }
139
140 /// Returns a function that replaces immediate sub elements of a given
141 /// instance of the storage user.
143 return [](auto instance, ArrayRef<Attribute> replAttrs,
144 ArrayRef<Type> replTypes) {
145 return ::mlir::detail::replaceImmediateSubElementsImpl(
146 llvm::cast<ConcreteT>(instance), replAttrs, replTypes);
147 };
148 }
149
150 /// Attach the given models as implementations of the corresponding interfaces
151 /// for the concrete storage user class. The type must be registered with the
152 /// context, i.e. the dialect to which the type belongs must be loaded. The
153 /// call will abort otherwise.
154 template <typename... IfaceModels>
155 static void attachInterface(MLIRContext &context) {
156 typename ConcreteT::AbstractTy *abstract =
157 ConcreteT::AbstractTy::lookupMutable(TypeID::get<ConcreteT>(),
158 &context);
159 if (!abstract)
160 llvm::report_fatal_error("Registering an interface for an attribute/type "
161 "that is not itself registered.");
162
163 // Handle the case where the models resolve a promised interface.
165 abstract->getDialect(), abstract->getTypeID(),
166 IfaceModels::Interface::getInterfaceID()),
167 ...);
168
169 (checkInterfaceTarget<IfaceModels>(), ...);
170 abstract->interfaceMap.template insertModels<IfaceModels...>();
171 }
172
173 /// Get or create a new ConcreteT instance within the ctx. This
174 /// function is guaranteed to return a non null object and will assert if
175 /// the arguments provided are invalid.
176 template <typename... Args>
177 static ConcreteT get(MLIRContext *ctx, Args &&...args) {
178 // Ensure that the invariants are correct for construction.
179 assert(succeeded(
180 ConcreteT::verifyInvariants(getDefaultDiagnosticEmitFn(ctx), args...)));
181 return UniquerT::template get<ConcreteT>(ctx, std::forward<Args>(args)...);
182 }
183
184 /// Get or create a new ConcreteT instance within the ctx, defined at
185 /// the given, potentially unknown, location. If the arguments provided are
186 /// invalid, errors are emitted using the provided location and a null object
187 /// is returned.
188 template <typename... Args>
189 static ConcreteT getChecked(const Location &loc, Args &&...args) {
190 return ConcreteT::getChecked(getDefaultDiagnosticEmitFn(loc),
191 std::forward<Args>(args)...);
192 }
193
194 /// Get or create a new ConcreteT instance within the ctx. If the arguments
195 /// provided are invalid, errors are emitted using the provided `emitError`
196 /// and a null object is returned.
197 ///
198 /// Workaround: We use Arg1 && instead of MLIRContext * in the parameter list
199 /// to work around a bug in Clang 21.1.8 where Clang segfaults during SFINAE
200 /// overload resolution when attempting to match non-pointer arguments
201 /// against an unconstrained MLIRContext * parameter.
202 template <typename Arg1, typename... Args,
203 typename = std::enable_if_t<std::is_convertible_v<
204 llvm::remove_cvref_t<Arg1>, MLIRContext *>>>
205 static ConcreteT getChecked(function_ref<InFlightDiagnostic()> emitErrorFn,
206 Arg1 &&ctx, Args... args) {
207 // If the construction invariants fail then we return a null attribute.
208 if (failed(ConcreteT::verifyInvariants(emitErrorFn, args...)))
209 return ConcreteT();
210 return UniquerT::template get<ConcreteT>(ctx, std::forward<Args>(args)...);
211 }
212
213 /// Get an instance of the concrete type from a void pointer.
214 static ConcreteT getFromOpaquePointer(const void *ptr) {
215 return ConcreteT((const typename BaseT::ImplType *)ptr);
216 }
217
218 /// Utility for easy access to the storage instance.
219 ImplType *getImpl() const { return static_cast<ImplType *>(this->impl); }
220
221protected:
222 /// Mutate the current storage instance. This will not change the unique key.
223 /// The arguments are forwarded to 'ConcreteT::mutate'.
224 template <typename... Args>
225 LogicalResult mutate(Args &&...args) {
226 static_assert(std::is_base_of<StorageUserTrait::IsMutable<ConcreteT>,
227 ConcreteT>::value,
228 "The `mutate` function expects mutable trait "
229 "(e.g. TypeTrait::IsMutable) to be attached on parent.");
230 return UniquerT::template mutate<ConcreteT>(this->getContext(), getImpl(),
231 std::forward<Args>(args)...);
232 }
233
234 /// Default implementation that just returns success.
235 template <typename... Args>
236 static LogicalResult verifyInvariants(Args... args) {
237 return success();
238 }
239
240private:
241 /// Trait to check if T provides a 'ConcreteEntity' type alias.
242 template <typename T>
243 using has_concrete_entity_t = typename T::ConcreteEntity;
244
245 /// A struct-wrapped type alias to T::ConcreteEntity if provided and to
246 /// ConcreteT otherwise. This is akin to std::conditional but doesn't fail on
247 /// the missing typedef. Useful for checking if the interface is targeting the
248 /// right class.
249 template <typename T,
250 bool = llvm::is_detected<has_concrete_entity_t, T>::value>
251 struct IfaceTargetOrConcreteT {
252 using type = typename T::ConcreteEntity;
253 };
254 template <typename T>
255 struct IfaceTargetOrConcreteT<T, false> {
256 using type = ConcreteT;
257 };
258
259 /// A hook for static assertion that the external interface model T is
260 /// targeting a base class of the concrete attribute/type. The model can also
261 /// be a fallback model that works for every attribute/type.
262 template <typename T>
263 static void checkInterfaceTarget() {
264 static_assert(std::is_base_of<typename IfaceTargetOrConcreteT<T>::type,
265 ConcreteT>::value,
266 "attaching an interface to the wrong attribute/type kind");
267 }
268};
269} // namespace detail
270} // namespace mlir
271
272#endif
return success()
b getContext())
false
Parses a map_entries map type from a string format back into its numeric value.
Attributes are known-constant values of operations.
Definition Attributes.h:25
This class represents a diagnostic that is inflight and set to be reported.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition Location.h:76
MLIRContext is the top-level object for a collection of MLIR operations.
Definition MLIRContext.h:63
This class provides an efficient unique identifier for a specific C++ type.
Definition TypeID.h:107
static TypeID get()
Construct a type info object for the given type T.
Definition TypeID.h:245
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition Types.h:74
This class provides an efficient mapping between a given Interface type, and a particular implementat...
Utility class for implementing users of storage classes uniqued by a StorageUniquer.
static LogicalResult verifyInvariants(Args... args)
Default implementation that just returns success.
static void attachInterface(MLIRContext &context)
Attach the given models as implementations of the corresponding interfaces for the concrete storage u...
static bool classof(T val)
Provide an implementation of 'classof' that compares the type id of the provided value with that of t...
LogicalResult mutate(Args &&...args)
Mutate the current storage instance.
static TypeID getTypeID()
Return a unique identifier for the concrete type.
static ConcreteT getFromOpaquePointer(const void *ptr)
Get an instance of the concrete type from a void pointer.
StorageUserBase< ConcreteType, BaseType, StorageType, detail::AttributeUniquer, Traits... > Base
static HasTraitFn getHasTraitFn()
Returns the function that returns true if the given Trait ID matches the IDs of any of the traits def...
static ConcreteT getChecked(function_ref< InFlightDiagnostic()> emitErrorFn, Arg1 &&ctx, Args... args)
Get or create a new ConcreteT instance within the ctx.
static ConcreteT getChecked(const Location &loc, Args &&...args)
Get or create a new ConcreteT instance within the ctx, defined at the given, potentially unknown,...
ImplType * getImpl() const
Utility for easy access to the storage instance.
static auto getWalkImmediateSubElementsFn()
Returns a function that walks immediate sub elements of a given instance of the storage user.
static detail::InterfaceMap getInterfaceMap()
Returns an interface map for the interfaces registered to this storage user.
static auto getReplaceImmediateSubElementsFn()
Returns a function that replaces immediate sub elements of a given instance of the storage user.
Helper class for implementing traits for storage classes.
ConcreteType getInstance() const
Return the derived instance.
bool hasTrait(TypeID traitID)
Returns true if this given Trait ID matches the IDs of any of the provided trait types Traits.
AttrTypeReplacer.
void walkImmediateSubElementsImpl(T derived, function_ref< void(Attribute)> walkAttrsFn, function_ref< void(Type)> walkTypesFn)
This function provides the underlying implementation for the SubElementInterface walk method,...
llvm::unique_function< InFlightDiagnostic()> getDefaultDiagnosticEmitFn(MLIRContext *ctx)
Utility method to generate a callback that can be used to generate a diagnostic when checking the con...
void handleAdditionOfUndefinedPromisedInterface(Dialect &dialect, TypeID interfaceRequestorID, TypeID interfaceID)
Checks if the given interface, which is attempting to be attached, is a promised interface of this di...
Definition Dialect.cpp:157
Include the generated interface declarations.
llvm::function_ref< Fn > function_ref
Definition LLVM.h:147
This trait is used to determine if a storage user, like Type, is mutable or not.