1
+ /*! Precise Garbage Collector
2
+
3
+ The precise GC exposes two functions, gc and
4
+ cleanup_stack_for_failure. The gc function is the entry point to the
5
+ garbage collector itself. The cleanup_stack_for_failure is the entry
6
+ point for GC-based cleanup.
7
+
8
+ Precise GC depends on changes to LLVM's GC which add support for
9
+ automatic rooting and addrspace-based metadata marking. Rather than
10
+ explicitly rooting pointers with LLVM's gcroot intrinsic, the GC
11
+ merely creates allocas for pointers, and allows an LLVM pass to
12
+ automatically infer roots based on the allocas present in a function
13
+ (and live at a given location). The compiler communicates the type of
14
+ the pointer to LLVM by setting the addrspace of the pointer type. The
15
+ compiler then emits a map from addrspace to tydesc, which LLVM then
16
+ uses to match pointers with their tydesc. The GC reads the metadata
17
+ table produced by LLVM, and uses it to determine which glue functions
18
+ to call to free objects on their respective heaps.
19
+
20
+ GC-based cleanup is a replacement for landing pads which relies on the
21
+ GC infrastructure to find pointers on the stack to cleanup. Whereas
22
+ the normal GC needs to walk task-local heap allocations, the cleanup
23
+ code needs to walk exchange heap allocations and stack-allocations
24
+ with destructors.
25
+
26
+ */
27
+
1
28
import stackwalk:: Word ;
2
29
import libc:: size_t;
3
30
import libc:: uintptr_t;
@@ -27,6 +54,7 @@ extern mod rustrt {
27
54
fn rust_get_stack_segment ( ) -> * StackSegment ;
28
55
}
29
56
57
+ // Is fp contained in segment?
30
58
unsafe fn is_frame_in_segment ( fp : * Word , segment : * StackSegment ) -> bool {
31
59
let begin: Word = unsafe :: reinterpret_cast ( & segment) ;
32
60
let end: Word = unsafe :: reinterpret_cast ( & ( * segment) . end ) ;
@@ -37,6 +65,8 @@ unsafe fn is_frame_in_segment(fp: *Word, segment: *StackSegment) -> bool {
37
65
38
66
type SafePoint = { sp_meta : * Word , fn_meta : * Word } ;
39
67
68
+ // Returns the safe point metadata for the given program counter, if
69
+ // any.
40
70
unsafe fn is_safe_point ( pc : * Word ) -> Option < SafePoint > {
41
71
let module_meta = rustrt:: rust_gc_metadata ( ) ;
42
72
let num_safe_points_ptr: * u32 = unsafe :: reinterpret_cast ( & module_meta) ;
@@ -48,6 +78,7 @@ unsafe fn is_safe_point(pc: *Word) -> Option<SafePoint> {
48
78
return None ;
49
79
}
50
80
81
+ // FIXME (#2997): Use binary rather than linear search.
51
82
let mut sp = 0 as Word ;
52
83
while sp < num_safe_points {
53
84
let sp_loc = * ptr:: offset ( safe_points, sp* 3 ) as * Word ;
@@ -74,6 +105,8 @@ unsafe fn align_to_pointer<T>(ptr: *T) -> *T {
74
105
return unsafe :: reinterpret_cast ( & ptr) ;
75
106
}
76
107
108
+ // Walks the list of roots for the given safe point, and calls visitor
109
+ // on each root.
77
110
unsafe fn walk_safe_point ( fp : * Word , sp : SafePoint , visitor : Visitor ) {
78
111
let fp_bytes: * u8 = unsafe :: reinterpret_cast ( & fp) ;
79
112
let sp_meta_u32s: * u32 = unsafe :: reinterpret_cast ( & sp. sp_meta ) ;
@@ -127,6 +160,10 @@ const stack: Memory = 4;
127
160
128
161
const need_cleanup: Memory = exchange_heap | stack;
129
162
163
+ // Find and return the segment containing the given frame pointer. At
164
+ // stack segment boundaries, returns true for boundary, so that the
165
+ // caller can do any special handling to identify where the correct
166
+ // return address is in the stack frame.
130
167
unsafe fn find_segment_for_frame ( fp : * Word , segment : * StackSegment )
131
168
-> { segment : * StackSegment , boundary : bool } {
132
169
// Check if frame is in either current frame or previous frame.
@@ -154,6 +191,8 @@ unsafe fn find_segment_for_frame(fp: *Word, segment: *StackSegment)
154
191
return { segment: segment, boundary: false } ;
155
192
}
156
193
194
+ // Walks stack, searching for roots of the requested type, and passes
195
+ // each root to the visitor.
157
196
unsafe fn walk_gc_roots ( mem : Memory , sentinel : * * Word , visitor : Visitor ) {
158
197
let mut segment = rustrt:: rust_get_stack_segment ( ) ;
159
198
let mut last_ret: * Word = ptr:: null ( ) ;
@@ -168,6 +207,15 @@ unsafe fn walk_gc_roots(mem: Memory, sentinel: **Word, visitor: Visitor) {
168
207
let { segment: next_segment , boundary: boundary} =
169
208
find_segment_for_frame ( frame. fp , segment) ;
170
209
segment = next_segment;
210
+ // Each stack segment is bounded by a morestack frame. The
211
+ // morestack frame includes two return addresses, one for
212
+ // morestack itself, at the normal offset from the frame
213
+ // pointer, and then a second return address for the
214
+ // function prologue (which called morestack after
215
+ // determining that it had hit the end of the stack).
216
+ // Since morestack itself takes two parameters, the offset
217
+ // for this second return address is 3 greater than the
218
+ // return address for morestack.
171
219
let ret_offset = if boundary { 4 } else { 1 } ;
172
220
last_ret = * ptr:: offset ( frame. fp , ret_offset) as * Word ;
173
221
@@ -238,6 +286,10 @@ fn expect_sentinel() -> bool { true }
238
286
#[ cfg( nogc) ]
239
287
fn expect_sentinel( ) -> bool { false }
240
288
289
+ // Entry point for GC-based cleanup. Walks stack looking for exchange
290
+ // heap and stack allocations requiring drop, and runs all
291
+ // destructors.
292
+ //
241
293
// This should only be called from fail, as it will drop the roots
242
294
// which are *live* on the stack, rather than dropping those that are
243
295
// dead.
0 commit comments