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;
177
178 /// Construct an InterfaceMap with the given set of template types. For
179 /// convenience given that object trait lists may contain other non-interface
180 /// types, not all of the types need to be interfaces. The provided types that
181 /// do not represent interfaces are not added to the interface map.
182 template <typename... Types>
183 static InterfaceMap get() {
184 constexpr size_t numInterfaces =
185 (size_t{0} + ... + detect_get_interface_id<Types>::value);
186 if constexpr (numInterfaces == 0) {
187 return InterfaceMap();
188 } else {
189 InterfaceMap map;
190 // A comma fold registers interfaces in source order. All non-interface
191 // traits share the same no-op specialization.
192 (map.insertPotentialInterface<std::conditional_t<
193 detect_get_interface_id<Types>::value, Types, void>>(),
194 ...);
195 return map;
196 }
197 }
198
199 /// Returns an instance of the concept object for the given interface if it
200 /// was registered to this map, null otherwise.
201 template <typename T>
202 typename T::Concept *lookup() const {
203 return reinterpret_cast<typename T::Concept *>(lookup(T::getInterfaceID()));
204 }
205
206 /// Returns true if the interface map contains an interface for the given id.
207 bool contains(TypeID interfaceID) const { return lookup(interfaceID); }
208
209 /// Insert the given interface models.
210 template <typename... IfaceModels>
212 (insertModel<IfaceModels>(), ...);
213 }
214
215private:
216 /// Insert the given interface type into the map, ignoring it if it doesn't
217 /// actually represent an interface.
218 template <typename T>
219 void insertPotentialInterface() {
220 if constexpr (detect_get_interface_id<T>::value)
221 insertModel<typename T::ModelT>();
222 }
223
224 /// Insert the given interface model into the map.
225 template <typename InterfaceModel>
226 void insertModel() {
227 // FIXME(#59975): Uncomment this when SPIRV no longer awkwardly reimplements
228 // interfaces in a way that isn't clean/compatible.
229 // static_assert(std::is_trivially_destructible_v<InterfaceModel>,
230 // "interface models must be trivially destructible");
231
232 // Generated models contain a fixed table of callbacks. Copy a constant
233 // prototype so the callbacks need not be set up for each model.
234 InterfaceModel *model;
235 if constexpr (IsConstexprGeneratedModel<InterfaceModel>::value) {
236 static constexpr InterfaceModel prototype;
237 model = new (malloc(sizeof(InterfaceModel))) InterfaceModel(prototype);
238 } else {
239 // Construct fallback, external, and custom models normally; they may
240 // need more than a copy of the generated callback table.
241 model = new (malloc(sizeof(InterfaceModel))) InterfaceModel();
242 }
243 // Run model-specific initialization after either path, including links to
244 // registered base interfaces.
245 if constexpr (detect_initialize_method<InterfaceModel>::value)
246 model->initializeInterfaceConcept(*this);
247
248 insert(InterfaceModel::Interface::getInterfaceID(), model);
249 }
250 /// Insert the given set of interface id and concept implementation into the
251 /// interface map.
252 void insert(TypeID interfaceId, void *conceptImpl);
253
254 /// Compare two TypeID instances by comparing the underlying pointer.
255 static bool compare(TypeID lhs, TypeID rhs) {
256 return lhs.getAsOpaquePointer() < rhs.getAsOpaquePointer();
257 }
258
259 /// Returns an instance of the concept object for the given interface id if it
260 /// was registered to this map, null otherwise.
261 void *lookup(TypeID id) const {
262 const auto *it =
263 llvm::lower_bound(interfaces, id, [](const auto &it, TypeID id) {
264 return compare(it.first, id);
265 });
266 return (it != interfaces.end() && it->first == id) ? it->second : nullptr;
267 }
268
269 /// A list of interface instances, sorted by TypeID.
270 SmallVector<std::pair<TypeID, void *>> interfaces;
271};
272
273template <typename ConcreteType, typename ValueT, typename Traits,
274 typename BaseType,
275 template <typename, template <typename> class> class BaseTrait>
277 Interface<ConcreteType, ValueT, Traits, BaseType, BaseTrait> &);
278
279template <typename T>
280using is_interface_t = decltype(isInterfaceImpl(std::declval<T &>()));
281
282template <typename T>
283using IsInterface = llvm::is_detected<is_interface_t, T>;
284
285} // namespace detail
286} // namespace mlir
287
288namespace llvm {
289
290template <typename T>
291struct DenseMapInfo<T, std::enable_if_t<mlir::detail::IsInterface<T>::value>> {
293
294 static unsigned getHashValue(T val) {
295 return ValueTypeInfo::getHashValue(val);
296 }
297
298 static bool isEqual(T lhs, T rhs) { return ValueTypeInfo::isEqual(lhs, rhs); }
299};
300
301} // namespace llvm
302
303#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 &&)
T::Concept * lookup() const
Returns an instance of the concept object for the given interface if it was registered to this map,...
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.
InterfaceMap & operator=(InterfaceMap &&rhs)
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.