MLIR 24.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
21#include <type_traits>
22
23namespace mlir {
24namespace detail {
25//===----------------------------------------------------------------------===//
26// Interface
27//===----------------------------------------------------------------------===//
28
29/// This class represents an abstract interface. An interface is a simplified
30/// mechanism for attaching concept based polymorphism to a class hierarchy. An
31/// interface is comprised of two components:
32/// * The derived interface class: This is what users interact with, and invoke
33/// methods on.
34/// * An interface `Trait` class: This is the class that is attached to the
35/// object implementing the interface. It is the mechanism with which models
36/// are specialized.
37///
38/// Derived interfaces types must provide the following template types:
39/// * ConcreteType: The CRTP derived type.
40/// * ValueT: The opaque type the derived interface operates on. For example
41/// `Operation*` for operation interfaces, or `Attribute` for
42/// attribute interfaces.
43/// * Traits: A class that contains definitions for a 'Concept' and a 'Model'
44/// class. The 'Concept' class defines an abstract virtual interface,
45/// where as the 'Model' class implements this interface for a
46/// specific derived T type. Both of these classes *must* not contain
47/// non-static data. A simple example is shown below:
48///
49/// ```c++
50/// struct ExampleInterfaceTraits {
51/// struct Concept {
52/// virtual unsigned getNumInputs(T t) const = 0;
53/// };
54/// template <typename DerivedT> class Model {
55/// unsigned getNumInputs(T t) const final {
56/// return cast<DerivedT>(t).getNumInputs();
57/// }
58/// };
59/// };
60/// ```
61///
62/// * BaseType: A desired base type for the interface. This is a class
63/// that provides specific functionality for the `ValueT`
64/// value. For instance the specific `Op` that will wrap the
65/// `Operation*` for an `OpInterface`.
66/// * BaseTrait: The base type for the interface trait. This is the base class
67/// to use for the interface trait that will be attached to each
68/// instance of `ValueT` that implements this interface.
69///
70template <typename ConcreteType, typename ValueT, typename Traits,
71 typename BaseType,
72 template <typename, template <typename> class> class BaseTrait>
73class Interface : public BaseType {
74public:
75 using Concept = typename Traits::Concept;
76 template <typename T>
77 using Model = typename Traits::template Model<T>;
78 template <typename T>
79 using FallbackModel = typename Traits::template FallbackModel<T>;
82 template <typename T, typename U>
83 using ExternalModel = typename Traits::template ExternalModel<T, U>;
84 using ValueType = ValueT;
85
86 /// This is a special trait that registers a given interface with an object.
87 template <typename ConcreteT>
88 struct Trait : public BaseTrait<ConcreteT, Trait> {
90
91 /// Define an accessor for the ID of this interface.
93 };
94
95 /// Construct an interface from an instance of the value type.
96 explicit Interface(ValueT t = ValueT())
97 : BaseType(t),
98 conceptImpl(t ? ConcreteType::getInterfaceFor(t) : nullptr) {
99 assert((!t || conceptImpl) &&
100 "expected value to provide interface instance");
101 }
102 Interface(std::nullptr_t) : BaseType(ValueT()), conceptImpl(nullptr) {}
103
104 /// Construct an interface instance from a type that implements this
105 /// interface's trait.
106 template <typename T,
107 std::enable_if_t<std::is_base_of<Trait<T>, T>::value> * = nullptr>
109 : BaseType(t),
110 conceptImpl(t ? ConcreteType::getInterfaceFor(t) : nullptr) {
111 assert((!t || conceptImpl) &&
112 "expected value to provide interface instance");
113 }
114
115 /// Constructor for a known concept.
116 Interface(ValueT t, const Concept *conceptImpl)
117 : BaseType(t), conceptImpl(const_cast<Concept *>(conceptImpl)) {
118 assert(!t || ConcreteType::getInterfaceFor(t) == conceptImpl);
119 }
120
121 /// Constructor for DenseMapInfo's empty key.
122 Interface(ValueT t, std::nullptr_t) : BaseType(t), conceptImpl(nullptr) {}
123
124 /// Support 'classof' by checking if the given object defines the concrete
125 /// interface.
126 static bool classof(ValueT t) { return ConcreteType::getInterfaceFor(t); }
127
128 /// Define an accessor for the ID of this interface.
130
131protected:
132 /// Get the raw concept in the correct derived concept type.
133 const Concept *getImpl() const { return conceptImpl; }
134 Concept *getImpl() { return conceptImpl; }
135
136private:
137 /// A pointer to the impl concept object.
138 Concept *conceptImpl;
139};
140
141//===----------------------------------------------------------------------===//
142// InterfaceMap
143//===----------------------------------------------------------------------===//
144
145/// This class provides an efficient mapping between a given `Interface` type,
146/// and a particular implementation of its concept.
148 /// Trait to check if T provides a static 'getInterfaceID' method.
149 template <typename T, typename... Args>
150 using has_get_interface_id = decltype(T::getInterfaceID());
151 template <typename T>
152 using detect_get_interface_id = llvm::is_detected<has_get_interface_id, T>;
153
154 /// Trait to check if T provides a 'initializeInterfaceConcept' method.
155 template <typename T, typename... Args>
156 using has_initialize_method =
157 decltype(std::declval<T>().initializeInterfaceConcept(
158 std::declval<InterfaceMap &>()));
159 template <typename T>
160 using detect_initialize_method = llvm::is_detected<has_initialize_method, T>;
161
162 // Only exact generated models with trivial copy construction can be copied
163 // from a constant prototype. Derived models may carry additional state or
164 // use a custom constructor to configure callbacks.
165 template <typename T, typename = void>
166 struct IsConstexprGeneratedModel : std::false_type {};
167 template <typename T>
168 struct IsConstexprGeneratedModel<T, std::void_t<typename T::GeneratedModel>>
169 : std::bool_constant<std::is_same_v<T, typename T::GeneratedModel> &&
170 std::is_trivially_copy_constructible_v<T>> {};
171
172public:
173 InterfaceMap() = default;
176 for (auto &it : interfaces)
177 free(it.second);
178 interfaces = std::move(rhs.interfaces);
179 return *this;
180 }
182 for (auto &it : interfaces)
183 free(it.second);
184 }
185
186 /// Construct an InterfaceMap with the given set of template types. For
187 /// convenience given that object trait lists may contain other non-interface
188 /// types, not all of the types need to be interfaces. The provided types that
189 /// do not represent interfaces are not added to the interface map.
190 template <typename... Types>
191 static InterfaceMap get() {
192 constexpr size_t numInterfaces =
193 (size_t{0} + ... + detect_get_interface_id<Types>::value);
194 if constexpr (numInterfaces == 0) {
195 return InterfaceMap();
196 } else {
197 InterfaceMap map;
198 map.insertPotentialInterfaces<Types...>();
199 return map;
200 }
201 }
202
203 /// Returns an instance of the concept object for the given interface if it
204 /// was registered to this map, null otherwise.
205 template <typename T>
206 typename T::Concept *lookup() const {
207 return reinterpret_cast<typename T::Concept *>(lookup(T::getInterfaceID()));
208 }
209
210 /// Returns true if the interface map contains an interface for the given id.
211 bool contains(TypeID interfaceID) const { return lookup(interfaceID); }
212
213 /// Insert the given interface models.
214 template <typename... IfaceModels>
216 (insertModel<IfaceModels>(), ...);
217 }
218
219private:
220 /// Insert the given interface types in source order. A comma fold is
221 /// sequenced left to right in C++17.
222 template <typename... Types>
223 void insertPotentialInterfaces() {
224 (insertPotentialInterface<Types>(), ...);
225 }
226
227 /// Insert the given interface type into the map, ignoring it if it doesn't
228 /// actually represent an interface.
229 template <typename T>
230 void insertPotentialInterface() {
231 if constexpr (detect_get_interface_id<T>::value)
232 insertModel<typename T::ModelT>();
233 }
234
235 /// Insert the given interface model into the map.
236 template <typename InterfaceModel>
237 void insertModel() {
238 // FIXME(#59975): Uncomment this when SPIRV no longer awkwardly reimplements
239 // interfaces in a way that isn't clean/compatible.
240 // static_assert(std::is_trivially_destructible_v<InterfaceModel>,
241 // "interface models must be trivially destructible");
242
243 // Generated models contain a fixed table of callbacks. Copy a constant
244 // prototype so the callbacks need not be set up for each model.
245 InterfaceModel *model;
246 if constexpr (IsConstexprGeneratedModel<InterfaceModel>::value) {
247 static constexpr InterfaceModel prototype;
248 model = new (malloc(sizeof(InterfaceModel))) InterfaceModel(prototype);
249 } else {
250 // Construct fallback, external, and custom models normally; they may
251 // need more than a copy of the generated callback table.
252 model = new (malloc(sizeof(InterfaceModel))) InterfaceModel();
253 }
254 // Run model-specific initialization after either path, including links to
255 // registered base interfaces.
256 if constexpr (detect_initialize_method<InterfaceModel>::value)
257 model->initializeInterfaceConcept(*this);
258
259 insert(InterfaceModel::Interface::getInterfaceID(), model);
260 }
261 /// Insert the given set of interface id and concept implementation into the
262 /// interface map.
263 void insert(TypeID interfaceId, void *conceptImpl);
264
265 /// Compare two TypeID instances by comparing the underlying pointer.
266 static bool compare(TypeID lhs, TypeID rhs) {
267 return lhs.getAsOpaquePointer() < rhs.getAsOpaquePointer();
268 }
269
270 /// Returns an instance of the concept object for the given interface id if it
271 /// was registered to this map, null otherwise.
272 void *lookup(TypeID id) const {
273 const auto *it =
274 llvm::lower_bound(interfaces, id, [](const auto &it, TypeID id) {
275 return compare(it.first, id);
276 });
277 return (it != interfaces.end() && it->first == id) ? it->second : nullptr;
278 }
279
280 /// A list of interface instances, sorted by TypeID.
281 SmallVector<std::pair<TypeID, void *>> interfaces;
282};
283
284template <typename ConcreteType, typename ValueT, typename Traits,
285 typename BaseType,
286 template <typename, template <typename> class> class BaseTrait>
288 Interface<ConcreteType, ValueT, Traits, BaseType, BaseTrait> &);
289
290template <typename T>
291using is_interface_t = decltype(isInterfaceImpl(std::declval<T &>()));
292
293template <typename T>
294using IsInterface = llvm::is_detected<is_interface_t, T>;
295
296} // namespace detail
297} // namespace mlir
298
299namespace llvm {
300
301template <typename T>
302struct DenseMapInfo<T, std::enable_if_t<mlir::detail::IsInterface<T>::value>> {
304
305 static unsigned getHashValue(T val) {
306 return ValueTypeInfo::getHashValue(val);
307 }
308
309 static bool isEqual(T lhs, T rhs) { return ValueTypeInfo::isEqual(lhs, rhs); }
310};
311
312} // namespace llvm
313
314#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.
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:227
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 > &)
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.