MLIR 23.0.0git
InterfaceSupport.h
Go to the documentation of this file.
1//===- InterfaceSupport.h - MLIR Interface Support Classes ------*- 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 several support classes for defining interfaces.
10//
11//===----------------------------------------------------------------------===//
12
13#ifndef MLIR_SUPPORT_INTERFACESUPPORT_H
14#define MLIR_SUPPORT_INTERFACESUPPORT_H
15
16#include "mlir/Support/TypeID.h"
17#include "llvm/ADT/ArrayRef.h"
18#include "llvm/ADT/DenseMap.h"
19#include "llvm/Support/TypeName.h"
20
21namespace mlir {
22namespace detail {
23//===----------------------------------------------------------------------===//
24// Interface
25//===----------------------------------------------------------------------===//
26
27/// This class represents an abstract interface. An interface is a simplified
28/// mechanism for attaching concept based polymorphism to a class hierarchy. An
29/// interface is comprised of two components:
30/// * The derived interface class: This is what users interact with, and invoke
31/// methods on.
32/// * An interface `Trait` class: This is the class that is attached to the
33/// object implementing the interface. It is the mechanism with which models
34/// are specialized.
35///
36/// Derived interfaces types must provide the following template types:
37/// * ConcreteType: The CRTP derived type.
38/// * ValueT: The opaque type the derived interface operates on. For example
39/// `Operation*` for operation interfaces, or `Attribute` for
40/// attribute interfaces.
41/// * Traits: A class that contains definitions for a 'Concept' and a 'Model'
42/// class. The 'Concept' class defines an abstract virtual interface,
43/// where as the 'Model' class implements this interface for a
44/// specific derived T type. Both of these classes *must* not contain
45/// non-static data. A simple example is shown below:
46///
47/// ```c++
48/// struct ExampleInterfaceTraits {
49/// struct Concept {
50/// virtual unsigned getNumInputs(T t) const = 0;
51/// };
52/// template <typename DerivedT> class Model {
53/// unsigned getNumInputs(T t) const final {
54/// return cast<DerivedT>(t).getNumInputs();
55/// }
56/// };
57/// };
58/// ```
59///
60/// * BaseType: A desired base type for the interface. This is a class
61/// that provides specific functionality for the `ValueT`
62/// value. For instance the specific `Op` that will wrap the
63/// `Operation*` for an `OpInterface`.
64/// * BaseTrait: The base type for the interface trait. This is the base class
65/// to use for the interface trait that will be attached to each
66/// instance of `ValueT` that implements this interface.
67///
68template <typename ConcreteType, typename ValueT, typename Traits,
69 typename BaseType,
70 template <typename, template <typename> class> class BaseTrait>
71class Interface : public BaseType {
72public:
73 using Concept = typename Traits::Concept;
74 template <typename T>
75 using Model = typename Traits::template Model<T>;
76 template <typename T>
77 using FallbackModel = typename Traits::template FallbackModel<T>;
80 template <typename T, typename U>
81 using ExternalModel = typename Traits::template ExternalModel<T, U>;
82 using ValueType = ValueT;
83
84 /// This is a special trait that registers a given interface with an object.
85 template <typename ConcreteT>
86 struct Trait : public BaseTrait<ConcreteT, Trait> {
88
89 /// Define an accessor for the ID of this interface.
91 };
92
93 /// Construct an interface from an instance of the value type.
94 explicit Interface(ValueT t = ValueT())
95 : BaseType(t),
96 conceptImpl(t ? ConcreteType::getInterfaceFor(t) : nullptr) {
97 assert((!t || conceptImpl) &&
98 "expected value to provide interface instance");
99 }
100 Interface(std::nullptr_t) : BaseType(ValueT()), conceptImpl(nullptr) {}
101
102 /// Construct an interface instance from a type that implements this
103 /// interface's trait.
104 template <typename T,
105 std::enable_if_t<std::is_base_of<Trait<T>, T>::value> * = nullptr>
107 : BaseType(t),
108 conceptImpl(t ? ConcreteType::getInterfaceFor(t) : nullptr) {
109 assert((!t || conceptImpl) &&
110 "expected value to provide interface instance");
111 }
112
113 /// Constructor for a known concept.
114 Interface(ValueT t, const Concept *conceptImpl)
115 : BaseType(t), conceptImpl(const_cast<Concept *>(conceptImpl)) {
116 assert(!t || ConcreteType::getInterfaceFor(t) == conceptImpl);
117 }
118
119 /// Constructor for DenseMapInfo's empty key and tombstone key.
120 Interface(ValueT t, std::nullptr_t) : BaseType(t), conceptImpl(nullptr) {}
121
122 /// Support 'classof' by checking if the given object defines the concrete
123 /// interface.
124 static bool classof(ValueT t) { return ConcreteType::getInterfaceFor(t); }
125
126 /// Define an accessor for the ID of this interface.
128
129protected:
130 /// Get the raw concept in the correct derived concept type.
131 const Concept *getImpl() const { return conceptImpl; }
132 Concept *getImpl() { return conceptImpl; }
133
134private:
135 /// A pointer to the impl concept object.
136 Concept *conceptImpl;
137};
138
139//===----------------------------------------------------------------------===//
140// InterfaceMap
141//===----------------------------------------------------------------------===//
142
143/// Template utility that computes the number of elements within `T` that
144/// satisfy the given predicate.
145template <template <class> class Pred, size_t N, typename... Ts>
146struct count_if_t_impl : public std::integral_constant<size_t, N> {};
147template <template <class> class Pred, size_t N, typename T, typename... Us>
148struct count_if_t_impl<Pred, N, T, Us...>
149 : public std::integral_constant<
150 size_t,
151 count_if_t_impl<Pred, N + (Pred<T>::value ? 1 : 0), Us...>::value> {};
152template <template <class> class Pred, typename... Ts>
153using count_if_t = count_if_t_impl<Pred, 0, Ts...>;
154
155/// This class provides an efficient mapping between a given `Interface` type,
156/// and a particular implementation of its concept.
158 /// Trait to check if T provides a static 'getInterfaceID' method.
159 template <typename T, typename... Args>
160 using has_get_interface_id = decltype(T::getInterfaceID());
161 template <typename T>
162 using detect_get_interface_id = llvm::is_detected<has_get_interface_id, T>;
163 template <typename... Types>
164 using num_interface_types_t = count_if_t<detect_get_interface_id, Types...>;
165
166 /// Trait to check if T provides a 'initializeInterfaceConcept' method.
167 template <typename T, typename... Args>
168 using has_initialize_method =
169 decltype(std::declval<T>().initializeInterfaceConcept(
170 std::declval<InterfaceMap &>()));
171 template <typename T>
172 using detect_initialize_method = llvm::is_detected<has_initialize_method, T>;
173
174public:
175 InterfaceMap() = default;
178 for (auto &it : interfaces)
179 free(it.second);
180 interfaces = std::move(rhs.interfaces);
181 return *this;
182 }
184 for (auto &it : interfaces)
185 free(it.second);
186 }
187
188 /// Construct an InterfaceMap with the given set of template types. For
189 /// convenience given that object trait lists may contain other non-interface
190 /// types, not all of the types need to be interfaces. The provided types that
191 /// do not represent interfaces are not added to the interface map.
192 template <typename... Types>
193 static InterfaceMap get() {
194 constexpr size_t numInterfaces = num_interface_types_t<Types...>::value;
195 if constexpr (numInterfaces == 0) {
196 return InterfaceMap();
197 } else {
198 InterfaceMap map;
199 map.insertPotentialInterfaces<Types...>();
200 return map;
201 }
202 }
203
204 /// Returns an instance of the concept object for the given interface if it
205 /// was registered to this map, null otherwise.
206 template <typename T>
207 typename T::Concept *lookup() const {
208 return reinterpret_cast<typename T::Concept *>(lookup(T::getInterfaceID()));
209 }
210
211 /// Returns true if the interface map contains an interface for the given id.
212 bool contains(TypeID interfaceID) const { return lookup(interfaceID); }
213
214 /// Insert the given interface models.
215 template <typename... IfaceModels>
217 (insertModel<IfaceModels>(), ...);
218 }
219
220private:
221 /// Insert the given interface types into the map (recursive expansion to
222 /// guarantee sequential, left-to-right evaluation across all compilers).
223 template <typename T>
224 void insertPotentialInterfaces() {
225 insertPotentialInterface<T>();
226 }
227 template <typename T, typename T2, typename... Rest>
228 void insertPotentialInterfaces() {
229 insertPotentialInterface<T>();
230 insertPotentialInterfaces<T2, Rest...>();
231 }
232
233 /// Insert the given interface type into the map, ignoring it if it doesn't
234 /// actually represent an interface.
235 template <typename T>
236 inline void insertPotentialInterface() {
237 if constexpr (detect_get_interface_id<T>::value)
238 insertModel<typename T::ModelT>();
239 }
240
241 /// Insert the given interface model into the map.
242 template <typename InterfaceModel>
243 void insertModel() {
244 // FIXME(#59975): Uncomment this when SPIRV no longer awkwardly reimplements
245 // interfaces in a way that isn't clean/compatible.
246 // static_assert(std::is_trivially_destructible_v<InterfaceModel>,
247 // "interface models must be trivially destructible");
248
249 // Build the interface model, optionally initializing if necessary.
250 InterfaceModel *model =
251 new (malloc(sizeof(InterfaceModel))) InterfaceModel();
252 if constexpr (detect_initialize_method<InterfaceModel>::value)
253 model->initializeInterfaceConcept(*this);
254
255 insert(InterfaceModel::Interface::getInterfaceID(), model);
256 }
257 /// Insert the given set of interface id and concept implementation into the
258 /// interface map.
259 void insert(TypeID interfaceId, void *conceptImpl);
260
261 /// Compare two TypeID instances by comparing the underlying pointer.
262 static bool compare(TypeID lhs, TypeID rhs) {
263 return lhs.getAsOpaquePointer() < rhs.getAsOpaquePointer();
264 }
265
266 /// Returns an instance of the concept object for the given interface id if it
267 /// was registered to this map, null otherwise.
268 void *lookup(TypeID id) const {
269 const auto *it =
270 llvm::lower_bound(interfaces, id, [](const auto &it, TypeID id) {
271 return compare(it.first, id);
272 });
273 return (it != interfaces.end() && it->first == id) ? it->second : nullptr;
274 }
275
276 /// A list of interface instances, sorted by TypeID.
277 SmallVector<std::pair<TypeID, void *>> interfaces;
278};
279
280template <typename ConcreteType, typename ValueT, typename Traits,
281 typename BaseType,
282 template <typename, template <typename> class> class BaseTrait>
284 Interface<ConcreteType, ValueT, Traits, BaseType, BaseTrait> &);
285
286template <typename T>
287using is_interface_t = decltype(isInterfaceImpl(std::declval<T &>()));
288
289template <typename T>
290using IsInterface = llvm::is_detected<is_interface_t, T>;
291
292} // namespace detail
293} // namespace mlir
294
295namespace llvm {
296
297template <typename T>
298struct DenseMapInfo<T, std::enable_if_t<mlir::detail::IsInterface<T>::value>> {
300
301 static T getEmptyKey() { return T(ValueTypeInfo::getEmptyKey(), nullptr); }
302
303 static T getTombstoneKey() {
304 return T(ValueTypeInfo::getTombstoneKey(), nullptr);
305 }
306
307 static unsigned getHashValue(T val) {
308 return ValueTypeInfo::getHashValue(val);
309 }
310
311 static bool isEqual(T lhs, T rhs) { return ValueTypeInfo::isEqual(lhs, rhs); }
312};
313
314} // namespace llvm
315
316#endif
lhs
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
void insertModels()
Insert the given interface models.
InterfaceMap(InterfaceMap &&)=default
T::Concept * lookup() const
Returns an instance of the concept object for the given interface if it was registered to this map,...
InterfaceMap & operator=(InterfaceMap &&rhs)
static InterfaceMap get()
Construct an InterfaceMap with the given set of template types.
bool contains(TypeID interfaceID) const
Returns true if the interface map contains an interface for the given id.
This class represents an abstract interface.
static bool classof(ValueT t)
Support 'classof' by checking if the given object defines the concrete interface.
Interface(ValueT t, const Concept *conceptImpl)
Constructor for a known concept.
Interface(ValueT t, std::nullptr_t)
Constructor for DenseMapInfo's empty key and tombstone key.
Interface(ValueT t=ValueT())
Construct an interface from an instance of the value type.
static TypeID getInterfaceID()
Define an accessor for the ID of this interface.
Interface< ConcreteType, Attribute, Traits, Attribute, AttributeTrait::TraitBase > InterfaceBase
const Concept * getImpl() const
Get the raw concept in the correct derived concept type.
Interface(T t)
Construct an interface instance from a type that implements this interface's trait.
The OpAsmOpInterface, see OpAsmInterface.td for more details.
Definition CallGraph.h:229
AttrTypeReplacer.
decltype(isInterfaceImpl(std::declval< T & >())) is_interface_t
llvm::is_detected< is_interface_t, T > IsInterface
void isInterfaceImpl(Interface< ConcreteType, ValueT, Traits, BaseType, BaseTrait > &)
count_if_t_impl< Pred, 0, Ts... > count_if_t
Include the generated interface declarations.
This is a special trait that registers a given interface with an object.
static TypeID getInterfaceID()
Define an accessor for the ID of this interface.
Template utility that computes the number of elements within T that satisfy the given predicate.