@@ -28,6 +28,67 @@ public struct ClassMetadata {
2828 var ivarDestroyer : UnsafeRawPointer ?
2929}
3030
31+ /*
32+ Embedded Swift Refcounting Scheme
33+ =================================
34+
35+ The scheme for storing and maintaining a refcount on heap objects is very simple in Embedded Swift, and is much
36+ simpler than regular Swift's. This is mainly due to the fact that we currently only maintain the regular ("strong")
37+ refcount and we don't allow weak references, unowned references and we don't track refcount during deinit of the
38+ object.
39+
40+ The refcount is always stored directly inline in the heap object, in the `refcount` field (see HeapObject struct
41+ below). This field has the following structure (on 32-bit, and similar on other bitwidths):
42+
43+ ┌──────────────┬──────────────────────────────────────────────┐
44+ │ b31 │ b30:b0 │
45+ ├──────────────┼──────────────────────────────────────────────┤
46+ │ doNotFreeBit │ actual number of references │
47+ └──────────────┴──────────────────────────────────────────────┘
48+
49+ If the highest bit (doNotFreeBit) is set, the behavior of dropping the last reference (release operation where
50+ refcount ends up being 0) is altered to avoid calling free() on the object (deinit is still run). This is crutial for
51+ class instances that are promoted by the compiler from being heap-allocated to instead be located on the stack
52+ (see swift_initStackObject).
53+
54+ To retrieve the actual number of references from the `refcount` field, refcountMask needs to be applied, which masks
55+ off the doNotFreeBit.
56+
57+ The actual number of references has one possible value that has a special meaning, immortalRefCount (all bits set,
58+ i.e. 0x7fff_ffff on 32-bit systems). When used, retain and release operations do nothing, references are not counted,
59+ and the object can never be deinit'd / free'd. This is used for class instances that are promoted by the compiler to
60+ be allocated statically in global memory (see swift_initStaticObject). Note that there are two different scenarios for
61+ this currently:
62+
63+ - In most cases, a class instance that is promoted to a global, is still dynamically initialized with a runtime call
64+ to swift_initStaticObject. This function will set the refcount field to immortalRefCount | doNotFreeBit.
65+ - As a special case to allow arrays be fully statically initialized without runtime overhead, instances of
66+ _ContiguousArrayStorage can be promoted to __StaticArrayStorage with the HeapObject header emitted directly by the
67+ compiler and refcount field directly set to immortalRefCount | doNotFreeBit (see irgen::emitConstantObject).
68+
69+ Tne immortalRefCount is additionally also used as a placeholder value for objects (heap-allocated or stack-allocated)
70+ when they're currently inside their deinit(). This is done to prevent further retains and releases inside deinit from
71+ triggering deinitialization again, without the need to reserve another bit for this purpose. Retains and releases in
72+ deinit() are allowed, as long as they are balanced at the end, i.e. the object is not escaped (user's responsibility)
73+ and not over-released (this can only be caused by unsafe code).
74+
75+ The following table summarizes the meaning of the possible combinations of doNotFreeBit and have immortal refcount
76+ value:
77+
78+ ┌───────────╥──────────╥─────────────────────────────────────────────────┐
79+ │ doNotFree ║ immortal ║ │
80+ ╞═══════════╬══════════╬═════════════════════════════════════════════════╡
81+ │ 0 ║ no ║ regular class instance │
82+ ├───────────╫──────────╫─────────────────────────────────────────────────┤
83+ │ 0 ║ yes ║ regular class instance during deinit() │
84+ ├───────────╫──────────╫─────────────────────────────────────────────────┤
85+ │ 1 ║ no ║ stack-allocated │
86+ ├───────────╫──────────╫─────────────────────────────────────────────────┤
87+ │ 1 ║ yes ║ global-allocated, no need to track references, │
88+ │ ║ ║ or stack-allocated instance during deinit() │
89+ └───────────╨──────────╨─────────────────────────────────────────────────┘
90+ */
91+
3192public struct HeapObject {
3293 // There is no way to express the custom ptrauth signature on the metadata
3394 // field, so let's use UnsafeRawPointer and a helper function in C instead
0 commit comments