@@ -28,6 +28,67 @@ public struct ClassMetadata {
28
28
var ivarDestroyer : UnsafeRawPointer ?
29
29
}
30
30
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
+
31
92
public struct HeapObject {
32
93
// There is no way to express the custom ptrauth signature on the metadata
33
94
// field, so let's use UnsafeRawPointer and a helper function in C instead
0 commit comments