1 | #pragma once |
2 | |
3 | #include <atomic> |
4 | #include <cassert> |
5 | #include <complex> |
6 | #include <cstdlib> |
7 | #include <memory> |
8 | #include <mutex> |
9 | #include <type_traits> |
10 | #include <unordered_map> |
11 | #include <unordered_set> |
12 | #include <vector> |
13 | #ifdef __GXX_RTTI |
14 | #include <typeinfo> |
15 | #endif |
16 | |
17 | #include <exception> |
18 | |
19 | #include <c10/macros/Macros.h> |
20 | #include <c10/util/Backtrace.h> |
21 | #include <c10/util/C++17.h> |
22 | #include <c10/util/Exception.h> |
23 | #include <c10/util/IdWrapper.h> |
24 | #include <c10/util/Type.h> |
25 | #include <c10/util/TypeIndex.h> |
26 | #include <c10/util/TypeTraits.h> |
27 | #include <c10/util/flat_hash_map.h> |
28 | |
29 | #include <c10/core/ScalarType.h> |
30 | #include <c10/util/irange.h> |
31 | |
32 | /* |
33 | * TypeIdentifier is a small type containing an id. |
34 | * Types must be registered using CAFFE_DECLARE_KNOWN_TYPE() (in their header) |
35 | * and CAFFE_DEFINE_KNOWN_TYPE() (in their .cpp file) for them to have a type |
36 | * id. If a type is registered, you can also create an object containing meta |
37 | * data like constructor, destructor, stringified name, ... about the type by |
38 | * calling TypeMeta::Make<T>. This returns a TypeMeta() object, which is |
39 | * basically just a pointer to the type information, so it's cheap to pass |
40 | * around. |
41 | */ |
42 | |
43 | // TODO: This file is still in the caffe2 namespace, despite living |
44 | // in the ATen directory. This is because the macro |
45 | // CAFFE_KNOWN_TYPE (and CAFFE_DECLARE_KNOWN_TYPE) defines a template |
46 | // specialization, which relies |
47 | // on the namespace of TypeMeta matching the namespace where the macro is |
48 | // called. This requires us to fix all of the call-sites, which I want to do |
49 | // later. So the namespace is not fixed at the moment. |
50 | |
51 | // Make at::Half a fundamental type. |
52 | namespace c10 { |
53 | namespace guts { |
54 | template <> |
55 | struct is_fundamental<at::Half> : std::true_type {}; |
56 | } // namespace guts |
57 | } // namespace c10 |
58 | |
59 | namespace caffe2 { |
60 | |
61 | /** |
62 | * A type id is a unique id for a given C++ type. |
63 | * You need to register your types using CAFFE_KNOWN_TYPE(MyType) to be able to |
64 | * use TypeIdentifier with custom types. This is for example used to store the |
65 | * dtype of tensors. |
66 | */ |
67 | class C10_API TypeIdentifier final |
68 | : public at::IdWrapper<TypeIdentifier, c10::util::type_index> { |
69 | public: |
70 | friend std::ostream& operator<<(std::ostream& stream, TypeIdentifier typeId); |
71 | friend constexpr bool operator<(TypeIdentifier lhs, TypeIdentifier rhs); |
72 | |
73 | /** |
74 | * Returns the unique id for the given type T. The id is unique for the type T |
75 | * in the sense that for any two different types, their ids are different; for |
76 | * the same type T, the id remains the same over different calls of the |
77 | * function. However, this is not guaranteed over different runs, as the id |
78 | * is generated during run-time. Do NOT serialize the id for storage. |
79 | */ |
80 | template <typename T> |
81 | static C10_HOST_CONSTEXPR_EXCEPT_WIN_CUDA TypeIdentifier Get() noexcept { |
82 | return TypeIdentifier(c10::util::get_type_index<T>()); |
83 | } |
84 | |
85 | static constexpr TypeIdentifier uninitialized() { |
86 | return TypeIdentifier(c10::util::type_index{0}); |
87 | } |
88 | |
89 | private: |
90 | constexpr explicit TypeIdentifier(c10::util::type_index id) : IdWrapper(id) {} |
91 | }; |
92 | |
93 | // Allow usage in std::map / std::set |
94 | // TODO Disallow this and rather use std::unordered_map/set everywhere |
95 | inline constexpr bool operator<(TypeIdentifier lhs, TypeIdentifier rhs) { |
96 | return lhs.underlyingId() < rhs.underlyingId(); |
97 | } |
98 | |
99 | inline std::ostream& operator<<( |
100 | std::ostream& stream, |
101 | caffe2::TypeIdentifier typeId) { |
102 | return stream << typeId.underlyingId(); |
103 | } |
104 | |
105 | } // namespace caffe2 |
106 | |
107 | namespace at { |
108 | using DataType = caffe2::TypeIdentifier; |
109 | } |
110 | |
111 | C10_DEFINE_HASH_FOR_IDWRAPPER(caffe2::TypeIdentifier) |
112 | |
113 | namespace caffe2 { |
114 | |
115 | namespace detail { |
116 | |
117 | // This struct holds the actual type information. There will be |
118 | // one allocated per type. TypeMeta objects will then point to the struct |
119 | // instance for the type they're configured for. |
120 | struct TypeMetaData final { |
121 | using New = void*(); |
122 | using PlacementNew = void(void*, size_t); |
123 | using Copy = void(const void*, void*, size_t); |
124 | using PlacementDelete = void(void*, size_t); |
125 | using Delete = void(void*); |
126 | |
127 | constexpr TypeMetaData() noexcept |
128 | : itemsize_(0), |
129 | new_(nullptr), |
130 | placementNew_(nullptr), |
131 | copy_(nullptr), |
132 | placementDelete_(nullptr), |
133 | delete_(nullptr), |
134 | id_(TypeIdentifier::uninitialized()), |
135 | name_("nullptr (uninitialized)" ) {} |
136 | |
137 | constexpr TypeMetaData( |
138 | size_t itemsize, |
139 | New* newFn, |
140 | PlacementNew* placementNew, |
141 | Copy* copy, |
142 | PlacementDelete* placementDelete, |
143 | Delete* deleteFn, |
144 | TypeIdentifier id, |
145 | c10::string_view name) noexcept |
146 | : itemsize_(itemsize), |
147 | new_(newFn), |
148 | placementNew_(placementNew), |
149 | copy_(copy), |
150 | placementDelete_(placementDelete), |
151 | delete_(deleteFn), |
152 | id_(id), |
153 | name_(name) {} |
154 | |
155 | size_t itemsize_; |
156 | New* new_; |
157 | PlacementNew* placementNew_; |
158 | Copy* copy_; |
159 | PlacementDelete* placementDelete_; |
160 | Delete* delete_; |
161 | TypeIdentifier id_; |
162 | c10::string_view name_; |
163 | }; |
164 | |
165 | // Mechanism for throwing errors which can't be prevented at compile time |
166 | // due to type erasure. E.g. somebody calling TypeMeta::copy() for |
167 | // non-copyable type. Right now just throws exception but is implemented |
168 | // in .cpp to manage dependencies |
169 | [[noreturn]] C10_API void _ThrowRuntimeTypeLogicError(const std::string& msg); |
170 | |
171 | /** |
172 | * Placement new function for the type. |
173 | */ |
174 | template <typename T> |
175 | inline void _PlacementNew(void* ptr, size_t n) { |
176 | T* typed_ptr = static_cast<T*>(ptr); |
177 | for (const auto i : c10::irange(n)) { |
178 | new (typed_ptr + i) T; |
179 | } |
180 | } |
181 | |
182 | template <typename T> |
183 | inline void _PlacementNewNotDefault(void* /*ptr*/, size_t /*n*/) { |
184 | _ThrowRuntimeTypeLogicError( |
185 | "Type " + std::string(c10::util::get_fully_qualified_type_name<T>()) + |
186 | " is not default-constructible." ); |
187 | } |
188 | |
189 | template < |
190 | typename T, |
191 | std::enable_if_t<std::is_default_constructible<T>::value>* = nullptr> |
192 | inline constexpr TypeMetaData::PlacementNew* _PickPlacementNew() { |
193 | return (c10::guts::is_fundamental<T>::value || std::is_pointer<T>::value) |
194 | ? nullptr |
195 | : &_PlacementNew<T>; |
196 | } |
197 | |
198 | template < |
199 | typename T, |
200 | std::enable_if_t<!std::is_default_constructible<T>::value>* = nullptr> |
201 | inline constexpr TypeMetaData::PlacementNew* _PickPlacementNew() { |
202 | static_assert( |
203 | !c10::guts::is_fundamental<T>::value && !std::is_pointer<T>::value, |
204 | "this should have picked the other SFINAE case" ); |
205 | return &_PlacementNewNotDefault<T>; |
206 | } |
207 | |
208 | template <typename T> |
209 | inline void* _New() { |
210 | return new T; |
211 | } |
212 | |
213 | template <typename T> |
214 | inline void* _NewNotDefault() { |
215 | _ThrowRuntimeTypeLogicError( |
216 | "Type " + std::string(c10::util::get_fully_qualified_type_name<T>()) + |
217 | " is not default-constructible." ); |
218 | } |
219 | |
220 | template < |
221 | typename T, |
222 | std::enable_if_t<std::is_default_constructible<T>::value>* = nullptr> |
223 | inline constexpr TypeMetaData::New* _PickNew() { |
224 | return &_New<T>; |
225 | } |
226 | |
227 | template < |
228 | typename T, |
229 | std::enable_if_t<!std::is_default_constructible<T>::value>* = nullptr> |
230 | inline constexpr TypeMetaData::New* _PickNew() { |
231 | return &_NewNotDefault<T>; |
232 | } |
233 | |
234 | /** |
235 | * Typed copy function for classes. |
236 | */ |
237 | template <typename T> |
238 | inline void _Copy(const void* src, void* dst, size_t n) { |
239 | const T* typed_src = static_cast<const T*>(src); |
240 | T* typed_dst = static_cast<T*>(dst); |
241 | for (const auto i : c10::irange(n)) { |
242 | typed_dst[i] = typed_src[i]; |
243 | } |
244 | } |
245 | |
246 | /** |
247 | * A placeholder function for types that do not allow assignment. |
248 | */ |
249 | template <typename T> |
250 | inline void _CopyNotAllowed(const void* /*src*/, void* /*dst*/, size_t /*n*/) { |
251 | _ThrowRuntimeTypeLogicError( |
252 | "Type " + std::string(c10::util::get_fully_qualified_type_name<T>()) + |
253 | " does not allow assignment." ); |
254 | } |
255 | |
256 | template < |
257 | typename T, |
258 | std::enable_if_t<std::is_copy_assignable<T>::value>* = nullptr> |
259 | inline constexpr TypeMetaData::Copy* _PickCopy() { |
260 | return (c10::guts::is_fundamental<T>::value || std::is_pointer<T>::value) |
261 | ? nullptr |
262 | : &_Copy<T>; |
263 | } |
264 | |
265 | template < |
266 | typename T, |
267 | std::enable_if_t<!std::is_copy_assignable<T>::value>* = nullptr> |
268 | inline constexpr TypeMetaData::Copy* _PickCopy() { |
269 | static_assert( |
270 | !c10::guts::is_fundamental<T>::value && !std::is_pointer<T>::value, |
271 | "this should have picked the other SFINAE case" ); |
272 | return &_CopyNotAllowed<T>; |
273 | } |
274 | |
275 | /** |
276 | * Destructor for non-fundamental types. |
277 | */ |
278 | template <typename T> |
279 | inline void _PlacementDelete(void* ptr, size_t n) { |
280 | T* typed_ptr = static_cast<T*>(ptr); |
281 | for (const auto i : c10::irange(n)) { |
282 | typed_ptr[i].~T(); |
283 | } |
284 | } |
285 | |
286 | template <typename T> |
287 | inline constexpr TypeMetaData::PlacementDelete* _PickPlacementDelete() { |
288 | return (c10::guts::is_fundamental<T>::value || std::is_pointer<T>::value) |
289 | ? nullptr |
290 | : &_PlacementDelete<T>; |
291 | } |
292 | |
293 | template <typename T> |
294 | inline void _Delete(void* ptr) { |
295 | T* typed_ptr = static_cast<T*>(ptr); |
296 | delete typed_ptr; |
297 | } |
298 | |
299 | template <class T> |
300 | inline constexpr TypeMetaData::Delete* _PickDelete() noexcept { |
301 | return &_Delete<T>; |
302 | } |
303 | |
304 | class _Uninitialized final {}; |
305 | |
306 | } // namespace detail |
307 | |
308 | // |
309 | // note: this is outside TypeMeta bc gcc seems to have trouble |
310 | // with scalarTypeItemSizes as a constexpr static member used by |
311 | // a public inline instance method |
312 | // |
313 | |
314 | // item sizes for TypeMeta::itemsize() fast path |
315 | static constexpr uint8_t scalarTypeItemSizes[NumScalarTypes] = { |
316 | #define SCALAR_TYPE_SIZE(T, name) sizeof(T), |
317 | AT_FORALL_SCALAR_TYPES_WITH_COMPLEX_AND_QINTS(SCALAR_TYPE_SIZE) |
318 | #undef SCALAR_TYPE_SIZE |
319 | 0, // Undefined |
320 | }; |
321 | |
322 | /** |
323 | * TypeMeta is a thin class that allows us to store the type of a container such |
324 | * as a blob, or the data type of a tensor, with a unique run-time id. It also |
325 | * stores some additional data such as the item size and the name of the type |
326 | * for run-time inspection. |
327 | */ |
328 | class C10_API TypeMeta final { |
329 | public: |
330 | using New = detail::TypeMetaData::New; |
331 | using PlacementNew = detail::TypeMetaData::PlacementNew; |
332 | using Copy = detail::TypeMetaData::Copy; |
333 | using PlacementDelete = detail::TypeMetaData::PlacementDelete; |
334 | using Delete = detail::TypeMetaData::Delete; |
335 | |
336 | /** Create a dummy TypeMeta object. To create a TypeMeta object for a specific |
337 | * type, use TypeMeta::Make<T>(). |
338 | */ |
339 | TypeMeta() noexcept; |
340 | |
341 | /** |
342 | * Copy constructor. |
343 | */ |
344 | TypeMeta(const TypeMeta& src) noexcept = default; |
345 | |
346 | /** |
347 | * Assignment operators. |
348 | */ |
349 | TypeMeta& operator=(const TypeMeta& src) noexcept = default; |
350 | |
351 | TypeMeta(TypeMeta&& rhs) noexcept = default; |
352 | |
353 | inline TypeMeta& operator=(ScalarType scalar_type) noexcept { |
354 | index_ = static_cast<uint16_t>(scalar_type); |
355 | return *this; |
356 | } |
357 | |
358 | private: |
359 | // TypeMeta can only be created by Make, making sure that we do not |
360 | // create incorrectly mixed up TypeMeta objects. |
361 | explicit TypeMeta(const uint16_t index) noexcept : index_(index) {} |
362 | |
363 | public: |
364 | /** |
365 | * Returns the type id. |
366 | */ |
367 | TypeIdentifier id() const noexcept { |
368 | return data().id_; |
369 | } |
370 | /** |
371 | * true if we represent some ScalarType type |
372 | */ |
373 | inline bool isScalarType() const noexcept { |
374 | return index_ < NumScalarTypes; |
375 | } |
376 | /** |
377 | * true if we represent ScalarType scalar_type |
378 | */ |
379 | inline bool isScalarType(ScalarType scalar_type) const noexcept { |
380 | return index_ == static_cast<uint16_t>(scalar_type); |
381 | } |
382 | /** |
383 | * Returns the size of the item. |
384 | */ |
385 | inline size_t itemsize() const noexcept { |
386 | if (C10_LIKELY(isScalarType())) { |
387 | return scalarTypeItemSizes[index_]; |
388 | } |
389 | return data().itemsize_; |
390 | } |
391 | /** |
392 | * Returns the new function pointer for individual items. |
393 | */ |
394 | New* newFn() const noexcept { |
395 | return data().new_; |
396 | } |
397 | /** |
398 | * Returns the placement new function pointer for individual items. |
399 | */ |
400 | PlacementNew* placementNew() const noexcept { |
401 | return data().placementNew_; |
402 | } |
403 | /** |
404 | * Returns the typed copy function pointer for individual iterms. |
405 | */ |
406 | Copy* copy() const noexcept { |
407 | return data().copy_; |
408 | } |
409 | /** |
410 | * Returns the destructor function pointer for individual items. |
411 | */ |
412 | PlacementDelete* placementDelete() const noexcept { |
413 | return data().placementDelete_; |
414 | } |
415 | Delete* deleteFn() const noexcept { |
416 | return data().delete_; |
417 | } |
418 | /** |
419 | * Returns a printable name for the type. |
420 | */ |
421 | c10::string_view name() const noexcept { |
422 | return data().name_; |
423 | } |
424 | |
425 | friend bool operator==(const TypeMeta lhs, const TypeMeta rhs) noexcept; |
426 | |
427 | template <typename T> |
428 | bool Match() const noexcept { |
429 | return (*this == Make<T>()); |
430 | } |
431 | |
432 | // Below are static functions that can be called by passing a specific type. |
433 | |
434 | template <class T> |
435 | static C10_HOST_CONSTEXPR_EXCEPT_WIN_CUDA TypeIdentifier Id() noexcept { |
436 | return TypeIdentifier::Get<T>(); |
437 | } |
438 | |
439 | template <class T> |
440 | static c10::string_view TypeName() noexcept { |
441 | return c10::util::get_fully_qualified_type_name<T>(); |
442 | } |
443 | |
444 | template <class T> |
445 | static constexpr size_t ItemSize() noexcept { |
446 | return sizeof(T); |
447 | } |
448 | |
449 | /** |
450 | * Returns a TypeMeta object that corresponds to the typename T. |
451 | */ |
452 | template <typename T> |
453 | static TypeMeta Make() { |
454 | // The instance pointed to is declared here, but defined in a .cpp file. |
455 | // We need to silence the compiler warning about using an undefined |
456 | // variable template. '-Wpragmas' and '-Wunknown-warning-option' has to be |
457 | // disabled for compilers that don't know '-Wundefined-var-template' and |
458 | // would error at our attempt to disable it. |
459 | #ifndef _MSC_VER |
460 | #pragma GCC diagnostic push |
461 | #pragma GCC diagnostic ignored "-Wpragmas" |
462 | #pragma GCC diagnostic ignored "-Wunknown-warning-option" |
463 | #pragma GCC diagnostic ignored "-Wundefined-var-template" |
464 | #endif |
465 | return TypeMeta(_typeMetaData<T>()); |
466 | #ifndef _MSC_VER |
467 | #pragma GCC diagnostic pop |
468 | #endif |
469 | } |
470 | |
471 | /** |
472 | * convert ScalarType enum values to TypeMeta handles |
473 | */ |
474 | static inline caffe2::TypeMeta fromScalarType(ScalarType scalar_type) { |
475 | const auto index = static_cast<uint16_t>(scalar_type); |
476 | TORCH_INTERNAL_ASSERT_DEBUG_ONLY( |
477 | index < NumScalarTypes, |
478 | "Unrecognized Scalartype " , |
479 | scalar_type, |
480 | " (please report this error)" ); |
481 | return TypeMeta(index); |
482 | } |
483 | |
484 | /** |
485 | * convert TypeMeta handles to ScalarType enum values |
486 | */ |
487 | inline ScalarType toScalarType() { |
488 | if (C10_LIKELY(isScalarType())) { |
489 | return static_cast<ScalarType>(index_); |
490 | } |
491 | error_unsupported_typemeta(*this); |
492 | } |
493 | |
494 | private: |
495 | [[noreturn]] static void error_unsupported_typemeta(caffe2::TypeMeta dtype); |
496 | |
497 | // hard limit number of registered types |
498 | // note: constexpr provokes Windows compilation error "member may not be |
499 | // initialized" static constexpr size_t MaxTypeIndex = 32; |
500 | // |
501 | #if defined C10_MOBILE |
502 | // The reason for this to be 32 and not UINT8_MAX is that the array |
503 | // initialization takes space which is proportional to the size of the array. |
504 | // The compiler seems to add code (or data padding) to initialize the array with |
505 | // empty elements. In practice, this array doesn't hold more than 18 elements |
506 | // (on mobile), so 32 should be plenty for now. Please see |
507 | // https://github.com/pytorch/pytorch/pull/51881 for details. |
508 | // |
509 | #define MaxTypeIndex 32 |
510 | #else |
511 | #define MaxTypeIndex UINT8_MAX |
512 | #endif |
513 | |
514 | // Protects type metadata allocation. |
515 | // NOLINTNEXTLINE(facebook-hte-NonPodStaticDeclaration) |
516 | static std::mutex typeMetaDatasLock; |
517 | static uint16_t nextTypeIndex; |
518 | |
519 | static detail::TypeMetaData* typeMetaDatas(); |
520 | |
521 | static uint16_t existingMetaDataIndexForType(TypeIdentifier identifier); |
522 | |
523 | #ifdef __CUDACC__ |
524 | // NOTE [ TypeIdentifier::Get nvcc/clang discrepancy] |
525 | // nvcc and clang do not produce identical results for |
526 | // TypeIdentifier::Get, because TypeIdentifier::Get relies on |
527 | // __PRETTY_FUNCTION__ and they don't agree on the canonical names |
528 | // of types (e.g., nvcc normalizes to `short unsigned int`, but clang |
529 | // calls it `unsigned short`). Hide the implementation of this function |
530 | // from nvcc so that we always use clang (or whatever host C++ compiler) |
531 | // for TypeIdentifier::Get. |
532 | template <class T> |
533 | C10_EXPORT static uint16_t addTypeMetaData(); |
534 | #else |
535 | template <class T> |
536 | C10_EXPORT static uint16_t addTypeMetaData() { |
537 | const auto identifier = TypeIdentifier::Get<T>(); |
538 | // Need to hold this for the rest of the function, protecting: |
539 | // 1) existingMetaDataIndexForType() |
540 | // 2) nextTypeIndex++ |
541 | // 3) the write into typeMetaDatas() |
542 | std::lock_guard<std::mutex> lock(typeMetaDatasLock); |
543 | // It may exist already if added in a different dynamic shared library. |
544 | const uint16_t existing_index = existingMetaDataIndexForType(identifier); |
545 | if (existing_index != MaxTypeIndex) { |
546 | return existing_index; |
547 | } |
548 | const uint16_t index = nextTypeIndex++; |
549 | TORCH_CHECK( |
550 | index <= MaxTypeIndex, |
551 | "Maximum number of CAFFE_KNOWN_TYPE declarations has been exceeded. " , |
552 | "Please report this issue." ); |
553 | typeMetaDatas()[index] = detail::TypeMetaData{ |
554 | sizeof(T), |
555 | detail::_PickNew<T>(), |
556 | detail::_PickPlacementNew<T>(), |
557 | detail::_PickCopy<T>(), |
558 | detail::_PickPlacementDelete<T>(), |
559 | detail::_PickDelete<T>(), |
560 | identifier, |
561 | c10::util::get_fully_qualified_type_name<T>()}; |
562 | return index; |
563 | } |
564 | #endif |
565 | |
566 | // specializations return indexes into typeMetaDataInstances() |
567 | template <class T> |
568 | C10_API static uint16_t _typeMetaData() noexcept; |
569 | |
570 | // |
571 | // TypeMeta just wraps this index |
572 | // |
573 | |
574 | uint16_t index_; |
575 | |
576 | inline const detail::TypeMetaData& data() const { |
577 | return typeMetaDatas()[index_]; |
578 | } |
579 | }; |
580 | |
581 | // specializations of TypeMeta::_typeMetaData for ScalarType types |
582 | |
583 | #define DEFINE_SCALAR_METADATA_INSTANCE(T, name) \ |
584 | template <> \ |
585 | constexpr uint16_t TypeMeta::_typeMetaData<T>() noexcept { \ |
586 | return static_cast<uint16_t>(ScalarType::name); \ |
587 | } |
588 | AT_FORALL_SCALAR_TYPES_WITH_COMPLEX_AND_QINTS(DEFINE_SCALAR_METADATA_INSTANCE) |
589 | #undef DEFINE_SCALAR_METADATA_INSTANCE |
590 | |
591 | template <> |
592 | C10_EXPORT constexpr uint16_t TypeMeta::_typeMetaData< |
593 | detail::_Uninitialized>() noexcept { |
594 | return static_cast<uint16_t>(ScalarType::Undefined); |
595 | } |
596 | |
597 | inline TypeMeta::TypeMeta() noexcept |
598 | : index_(_typeMetaData<detail::_Uninitialized>()) {} |
599 | |
600 | inline bool operator==(const TypeMeta lhs, const TypeMeta rhs) noexcept { |
601 | return (lhs.index_ == rhs.index_); |
602 | } |
603 | inline bool operator!=(const TypeMeta lhs, const TypeMeta rhs) noexcept { |
604 | return !operator==(lhs, rhs); |
605 | } |
606 | |
607 | inline std::ostream& operator<<( |
608 | std::ostream& stream, |
609 | caffe2::TypeMeta typeMeta) { |
610 | return stream << typeMeta.name(); |
611 | } |
612 | |
613 | /** |
614 | * Register unique id for a type so it can be used in TypeMeta context, e.g. be |
615 | * used as a type for Blob or for Tensor elements. |
616 | * |
617 | * CAFFE_KNOWN_TYPE is deprecated; prefer CAFFE_DECLARE_KNOWN_TYPE and |
618 | * CAFFE_DEFINE_KNOWN_TYPE. |
619 | * |
620 | * CAFFE_KNOWN_TYPE does explicit instantiation of TypeIdentifier::Get<T> |
621 | * template function and thus needs to be put in a single translation unit (.cpp |
622 | * file) for a given type T. Other translation units that use type T as a type |
623 | * of the caffe2::Blob or element type of caffe2::Tensor need to depend on the |
624 | * translation unit that contains CAFFE_KNOWN_TYPE declaration via regular |
625 | * linkage dependencies. |
626 | * |
627 | * NOTE: the macro needs to be invoked in ::caffe2 namespace |
628 | */ |
629 | // Implementation note: in MSVC, we will need to prepend the C10_API |
630 | // keyword in order to get things compiled properly. in Linux, gcc seems to |
631 | // create attribute ignored error for explicit template instantiations, see |
632 | // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0537r0.html |
633 | // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=51930 |
634 | // and as a result, we define these two macros slightly differently. |
635 | #if defined(_MSC_VER) || defined(__clang__) |
636 | #define EXPORT_IF_NOT_GCC C10_EXPORT |
637 | #else |
638 | #define EXPORT_IF_NOT_GCC |
639 | #endif |
640 | |
641 | // CAFFE_KNOWN_TYPE is deprecated! Use CAFFE_DECLARE_KNOWN_TYPE and |
642 | // CAFFE_DEFINE_KNOWN_TYPE instead. |
643 | #define CAFFE_KNOWN_TYPE(T) \ |
644 | template uint16_t TypeMeta::addTypeMetaData<T>(); \ |
645 | template <> \ |
646 | EXPORT_IF_NOT_GCC uint16_t TypeMeta::_typeMetaData<T>() noexcept { \ |
647 | static const uint16_t index = addTypeMetaData<T>(); \ |
648 | return index; \ |
649 | } |
650 | |
651 | #define CAFFE_DEFINE_KNOWN_TYPE(T) \ |
652 | template uint16_t TypeMeta::addTypeMetaData<T>(); |
653 | |
654 | // Unlike CAFFE_KNOWN_TYPE, CAFFE_DECLARE_KNOWN_TYPE avoids a function |
655 | // call to access _typeMetaData in the common case. |
656 | #ifdef __CUDACC__ |
657 | // nvcc needs its own specialization that doesn't use |
658 | // C10_ALWAYS_INLINE so that it doesn't need to see a definition for |
659 | // _addTypeMeta. See NOTE [ TypeIdentifier::Get nvcc/clang discrepancy |
660 | // ]. |
661 | #define CAFFE_DECLARE_KNOWN_TYPE(T) \ |
662 | extern template uint16_t TypeMeta::addTypeMetaData<T>(); \ |
663 | template <> \ |
664 | EXPORT_IF_NOT_GCC inline uint16_t TypeMeta::_typeMetaData<T>() noexcept { \ |
665 | static const uint16_t index = addTypeMetaData<T>(); \ |
666 | return index; \ |
667 | } |
668 | #else |
669 | #define CAFFE_DECLARE_KNOWN_TYPE(T) \ |
670 | extern template uint16_t TypeMeta::addTypeMetaData<T>(); \ |
671 | template <> \ |
672 | EXPORT_IF_NOT_GCC C10_ALWAYS_INLINE uint16_t \ |
673 | TypeMeta::_typeMetaData<T>() noexcept { \ |
674 | static const uint16_t index = addTypeMetaData<T>(); \ |
675 | return index; \ |
676 | } |
677 | #endif |
678 | |
679 | #define CAFFE_KNOWN_TYPE_NOEXPORT(T) \ |
680 | template <> \ |
681 | uint16_t TypeMeta::_typeMetaData<T>() noexcept { \ |
682 | static const uint16_t index = addTypeMetaData<T>(); \ |
683 | return index; \ |
684 | } |
685 | |
686 | CAFFE_DECLARE_KNOWN_TYPE(std::string) |
687 | CAFFE_DECLARE_KNOWN_TYPE(uint16_t) |
688 | CAFFE_DECLARE_KNOWN_TYPE(char) |
689 | CAFFE_DECLARE_KNOWN_TYPE(std::unique_ptr<std::mutex>) |
690 | CAFFE_DECLARE_KNOWN_TYPE(std::unique_ptr<std::atomic<bool>>) |
691 | CAFFE_DECLARE_KNOWN_TYPE(std::vector<int32_t>) |
692 | CAFFE_DECLARE_KNOWN_TYPE(std::vector<int64_t>) |
693 | CAFFE_DECLARE_KNOWN_TYPE(std::vector<unsigned long>) |
694 | CAFFE_DECLARE_KNOWN_TYPE(bool*) |
695 | CAFFE_DECLARE_KNOWN_TYPE(char*) |
696 | CAFFE_DECLARE_KNOWN_TYPE(int*) |
697 | |
698 | // For some of the compilers, long is defined separately from int32_t and |
699 | // int64_t. As a result we will need to actually define them separately. |
700 | // It is recommended that one does NOT use long - use int32_t and int64_t |
701 | // explicitly. Explicit long type annotation may go away in the future. |
702 | // details: This hack works by defining a _guard_long_unique type, which is |
703 | // long iff the compiler has a separate long type and is a dummy type otherwise. |
704 | // we then allocate a type id to that _guard_long_unique. If the compiler has a |
705 | // separate long type, this allocates a type id for long. Otherwise, it |
706 | // allocates a type id for the dummy type, which doesn't matter. |
707 | namespace detail { |
708 | template <class T> |
709 | class _guard_long_unique_dummy final {}; |
710 | template <class T> |
711 | using _guard_long_unique = std::conditional_t< |
712 | std::is_same<long, int32_t>::value || std::is_same<long, int64_t>::value, |
713 | _guard_long_unique_dummy<T>, |
714 | T>; |
715 | } // namespace detail |
716 | |
717 | CAFFE_DECLARE_KNOWN_TYPE(detail::_guard_long_unique<long>); |
718 | CAFFE_DECLARE_KNOWN_TYPE(detail::_guard_long_unique<std::vector<long>>) |
719 | |
720 | CAFFE_DECLARE_KNOWN_TYPE(float*) |
721 | CAFFE_DECLARE_KNOWN_TYPE(at::Half*) |
722 | |
723 | } // namespace caffe2 |
724 | |