+#if __has_feature(objc_fixed_enum) && !defined(__cplusplus)
+typedef enum objc_tag_index_t objc_tag_index_t;
+#endif
+
+
+// Returns true if tagged pointers are enabled.
+// The other functions below must not be called if tagged pointers are disabled.
+static inline bool
+_objc_taggedPointersEnabled(void);
+
+// Register a class for a tagged pointer tag.
+// Aborts if the tag is invalid or already in use.
+OBJC_EXPORT void
+_objc_registerTaggedPointerClass(objc_tag_index_t tag, Class _Nonnull cls)
+ OBJC_AVAILABLE(10.9, 7.0, 9.0, 1.0, 2.0);
+
+// Returns the registered class for the given tag.
+// Returns nil if the tag is valid but has no registered class.
+// Aborts if the tag is invalid.
+OBJC_EXPORT Class _Nullable
+_objc_getClassForTag(objc_tag_index_t tag)
+ OBJC_AVAILABLE(10.9, 7.0, 9.0, 1.0, 2.0);
+
+// Create a tagged pointer object with the given tag and payload.
+// Assumes the tag is valid.
+// Assumes tagged pointers are enabled.
+// The payload will be silently truncated to fit.
+static inline void * _Nonnull
+_objc_makeTaggedPointer(objc_tag_index_t tag, uintptr_t payload);
+
+// Return true if ptr is a tagged pointer object.
+// Does not check the validity of ptr's class.
+static inline bool
+_objc_isTaggedPointer(const void * _Nullable ptr);
+
+// Extract the tag value from the given tagged pointer object.
+// Assumes ptr is a valid tagged pointer object.
+// Does not check the validity of ptr's tag.
+static inline objc_tag_index_t
+_objc_getTaggedPointerTag(const void * _Nullable ptr);
+
+// Extract the payload from the given tagged pointer object.
+// Assumes ptr is a valid tagged pointer object.
+// The payload value is zero-extended.
+static inline uintptr_t
+_objc_getTaggedPointerValue(const void * _Nullable ptr);
+
+// Extract the payload from the given tagged pointer object.
+// Assumes ptr is a valid tagged pointer object.
+// The payload value is sign-extended.
+static inline intptr_t
+_objc_getTaggedPointerSignedValue(const void * _Nullable ptr);
+
+// Don't use the values below. Use the declarations above.
+
+#if __arm64__
+// ARM64 uses a new tagged pointer scheme where normal tags are in
+// the low bits, extended tags are in the high bits, and half of the
+// extended tag space is reserved for unobfuscated payloads.
+# define OBJC_SPLIT_TAGGED_POINTERS 1
+#else
+# define OBJC_SPLIT_TAGGED_POINTERS 0
+#endif
+
+#if (TARGET_OS_OSX || TARGET_OS_MACCATALYST) && __x86_64__
+ // 64-bit Mac - tag bit is LSB
+# define OBJC_MSB_TAGGED_POINTERS 0
+#else
+ // Everything else - tag bit is MSB
+# define OBJC_MSB_TAGGED_POINTERS 1
+#endif
+
+#define _OBJC_TAG_INDEX_MASK 0x7UL
+
+#if OBJC_SPLIT_TAGGED_POINTERS
+#define _OBJC_TAG_SLOT_COUNT 8
+#define _OBJC_TAG_SLOT_MASK 0x7UL
+#else
+// array slot includes the tag bit itself
+#define _OBJC_TAG_SLOT_COUNT 16
+#define _OBJC_TAG_SLOT_MASK 0xfUL
+#endif
+
+#define _OBJC_TAG_EXT_INDEX_MASK 0xff
+// array slot has no extra bits
+#define _OBJC_TAG_EXT_SLOT_COUNT 256
+#define _OBJC_TAG_EXT_SLOT_MASK 0xff
+
+#if OBJC_SPLIT_TAGGED_POINTERS
+# define _OBJC_TAG_MASK (1UL<<63)
+# define _OBJC_TAG_INDEX_SHIFT 0
+# define _OBJC_TAG_SLOT_SHIFT 0
+# define _OBJC_TAG_PAYLOAD_LSHIFT 1
+# define _OBJC_TAG_PAYLOAD_RSHIFT 4
+# define _OBJC_TAG_EXT_MASK (_OBJC_TAG_MASK | 0x7UL)
+# define _OBJC_TAG_NO_OBFUSCATION_MASK ((1UL<<62) | _OBJC_TAG_EXT_MASK)
+# define _OBJC_TAG_CONSTANT_POINTER_MASK \
+ ~(_OBJC_TAG_EXT_MASK | ((uintptr_t)_OBJC_TAG_EXT_SLOT_MASK << _OBJC_TAG_EXT_SLOT_SHIFT))
+# define _OBJC_TAG_EXT_INDEX_SHIFT 55
+# define _OBJC_TAG_EXT_SLOT_SHIFT 55
+# define _OBJC_TAG_EXT_PAYLOAD_LSHIFT 9
+# define _OBJC_TAG_EXT_PAYLOAD_RSHIFT 12
+#elif OBJC_MSB_TAGGED_POINTERS
+# define _OBJC_TAG_MASK (1UL<<63)
+# define _OBJC_TAG_INDEX_SHIFT 60
+# define _OBJC_TAG_SLOT_SHIFT 60
+# define _OBJC_TAG_PAYLOAD_LSHIFT 4
+# define _OBJC_TAG_PAYLOAD_RSHIFT 4
+# define _OBJC_TAG_EXT_MASK (0xfUL<<60)
+# define _OBJC_TAG_EXT_INDEX_SHIFT 52
+# define _OBJC_TAG_EXT_SLOT_SHIFT 52
+# define _OBJC_TAG_EXT_PAYLOAD_LSHIFT 12
+# define _OBJC_TAG_EXT_PAYLOAD_RSHIFT 12
+#else
+# define _OBJC_TAG_MASK 1UL
+# define _OBJC_TAG_INDEX_SHIFT 1
+# define _OBJC_TAG_SLOT_SHIFT 0
+# define _OBJC_TAG_PAYLOAD_LSHIFT 0
+# define _OBJC_TAG_PAYLOAD_RSHIFT 4
+# define _OBJC_TAG_EXT_MASK 0xfUL
+# define _OBJC_TAG_EXT_INDEX_SHIFT 4
+# define _OBJC_TAG_EXT_SLOT_SHIFT 4
+# define _OBJC_TAG_EXT_PAYLOAD_LSHIFT 0
+# define _OBJC_TAG_EXT_PAYLOAD_RSHIFT 12
+#endif
+
+// Map of tags to obfuscated tags.
+extern uintptr_t objc_debug_taggedpointer_obfuscator;
+
+#if OBJC_SPLIT_TAGGED_POINTERS
+extern uint8_t objc_debug_tag60_permutations[8];
+
+static inline uintptr_t _objc_basicTagToObfuscatedTag(uintptr_t tag) {
+ return objc_debug_tag60_permutations[tag];
+}
+
+static inline uintptr_t _objc_obfuscatedTagToBasicTag(uintptr_t tag) {
+ for (unsigned i = 0; i < 7; i++)
+ if (objc_debug_tag60_permutations[i] == tag)
+ return i;
+ return 7;
+}
+#endif
+
+static inline void * _Nonnull
+_objc_encodeTaggedPointer(uintptr_t ptr)
+{
+ uintptr_t value = (objc_debug_taggedpointer_obfuscator ^ ptr);
+#if OBJC_SPLIT_TAGGED_POINTERS
+ if ((value & _OBJC_TAG_NO_OBFUSCATION_MASK) == _OBJC_TAG_NO_OBFUSCATION_MASK)
+ return (void *)ptr;
+ uintptr_t basicTag = (value >> _OBJC_TAG_INDEX_SHIFT) & _OBJC_TAG_INDEX_MASK;
+ uintptr_t permutedTag = _objc_basicTagToObfuscatedTag(basicTag);
+ value &= ~(_OBJC_TAG_INDEX_MASK << _OBJC_TAG_INDEX_SHIFT);
+ value |= permutedTag << _OBJC_TAG_INDEX_SHIFT;
+#endif
+ return (void *)value;
+}
+
+static inline uintptr_t
+_objc_decodeTaggedPointer_noPermute(const void * _Nullable ptr)
+{
+ uintptr_t value = (uintptr_t)ptr;
+#if OBJC_SPLIT_TAGGED_POINTERS
+ if ((value & _OBJC_TAG_NO_OBFUSCATION_MASK) == _OBJC_TAG_NO_OBFUSCATION_MASK)
+ return value;
+#endif
+ return value ^ objc_debug_taggedpointer_obfuscator;
+}
+
+static inline uintptr_t
+_objc_decodeTaggedPointer(const void * _Nullable ptr)
+{
+ uintptr_t value = _objc_decodeTaggedPointer_noPermute(ptr);
+#if OBJC_SPLIT_TAGGED_POINTERS
+ uintptr_t basicTag = (value >> _OBJC_TAG_INDEX_SHIFT) & _OBJC_TAG_INDEX_MASK;
+
+ value &= ~(_OBJC_TAG_INDEX_MASK << _OBJC_TAG_INDEX_SHIFT);
+ value |= _objc_obfuscatedTagToBasicTag(basicTag) << _OBJC_TAG_INDEX_SHIFT;
+#endif
+ return value;
+}
+
+static inline bool
+_objc_taggedPointersEnabled(void)
+{
+ extern uintptr_t objc_debug_taggedpointer_mask;
+ return (objc_debug_taggedpointer_mask != 0);
+}
+
+static inline void * _Nonnull
+_objc_makeTaggedPointer(objc_tag_index_t tag, uintptr_t value)
+{
+ // PAYLOAD_LSHIFT and PAYLOAD_RSHIFT are the payload extraction shifts.
+ // They are reversed here for payload insertion.
+
+ // ASSERT(_objc_taggedPointersEnabled());
+ if (tag <= OBJC_TAG_Last60BitPayload) {
+ // ASSERT(((value << _OBJC_TAG_PAYLOAD_RSHIFT) >> _OBJC_TAG_PAYLOAD_LSHIFT) == value);
+ uintptr_t result =
+ (_OBJC_TAG_MASK |
+ ((uintptr_t)tag << _OBJC_TAG_INDEX_SHIFT) |
+ ((value << _OBJC_TAG_PAYLOAD_RSHIFT) >> _OBJC_TAG_PAYLOAD_LSHIFT));
+ return _objc_encodeTaggedPointer(result);
+ } else {
+ // ASSERT(tag >= OBJC_TAG_First52BitPayload);
+ // ASSERT(tag <= OBJC_TAG_Last52BitPayload);
+ // ASSERT(((value << _OBJC_TAG_EXT_PAYLOAD_RSHIFT) >> _OBJC_TAG_EXT_PAYLOAD_LSHIFT) == value);
+ uintptr_t result =
+ (_OBJC_TAG_EXT_MASK |
+ ((uintptr_t)(tag - OBJC_TAG_First52BitPayload) << _OBJC_TAG_EXT_INDEX_SHIFT) |
+ ((value << _OBJC_TAG_EXT_PAYLOAD_RSHIFT) >> _OBJC_TAG_EXT_PAYLOAD_LSHIFT));
+ return _objc_encodeTaggedPointer(result);
+ }
+}
+
+static inline bool
+_objc_isTaggedPointer(const void * _Nullable ptr)
+{
+ return ((uintptr_t)ptr & _OBJC_TAG_MASK) == _OBJC_TAG_MASK;
+}
+
+static inline bool
+_objc_isTaggedPointerOrNil(const void * _Nullable ptr)
+{
+ // this function is here so that clang can turn this into
+ // a comparison with NULL when this is appropriate
+ // it turns out it's not able to in many cases without this
+ return !ptr || ((uintptr_t)ptr & _OBJC_TAG_MASK) == _OBJC_TAG_MASK;
+}
+
+static inline objc_tag_index_t
+_objc_getTaggedPointerTag(const void * _Nullable ptr)
+{
+ // ASSERT(_objc_isTaggedPointer(ptr));
+ uintptr_t value = _objc_decodeTaggedPointer(ptr);
+ uintptr_t basicTag = (value >> _OBJC_TAG_INDEX_SHIFT) & _OBJC_TAG_INDEX_MASK;
+ uintptr_t extTag = (value >> _OBJC_TAG_EXT_INDEX_SHIFT) & _OBJC_TAG_EXT_INDEX_MASK;
+ if (basicTag == _OBJC_TAG_INDEX_MASK) {
+ return (objc_tag_index_t)(extTag + OBJC_TAG_First52BitPayload);
+ } else {
+ return (objc_tag_index_t)basicTag;
+ }
+}
+
+static inline uintptr_t
+_objc_getTaggedPointerValue(const void * _Nullable ptr)
+{
+ // ASSERT(_objc_isTaggedPointer(ptr));
+ uintptr_t value = _objc_decodeTaggedPointer_noPermute(ptr);
+ uintptr_t basicTag = (value >> _OBJC_TAG_INDEX_SHIFT) & _OBJC_TAG_INDEX_MASK;
+ if (basicTag == _OBJC_TAG_INDEX_MASK) {
+ return (value << _OBJC_TAG_EXT_PAYLOAD_LSHIFT) >> _OBJC_TAG_EXT_PAYLOAD_RSHIFT;
+ } else {
+ return (value << _OBJC_TAG_PAYLOAD_LSHIFT) >> _OBJC_TAG_PAYLOAD_RSHIFT;
+ }
+}
+
+static inline intptr_t
+_objc_getTaggedPointerSignedValue(const void * _Nullable ptr)
+{
+ // ASSERT(_objc_isTaggedPointer(ptr));
+ uintptr_t value = _objc_decodeTaggedPointer_noPermute(ptr);
+ uintptr_t basicTag = (value >> _OBJC_TAG_INDEX_SHIFT) & _OBJC_TAG_INDEX_MASK;
+ if (basicTag == _OBJC_TAG_INDEX_MASK) {
+ return ((intptr_t)value << _OBJC_TAG_EXT_PAYLOAD_LSHIFT) >> _OBJC_TAG_EXT_PAYLOAD_RSHIFT;
+ } else {
+ return ((intptr_t)value << _OBJC_TAG_PAYLOAD_LSHIFT) >> _OBJC_TAG_PAYLOAD_RSHIFT;
+ }
+}