2 Copyright (C) 2001-2010, Parrot Foundation.
7 src/gc/system.c - CPU-dependent mark/sweep functions
11 These functions setup a trace of the current processor context and the
12 system stack. The trace is set up here in C<trace_system_areas>. This
13 function gets the current processor context and either traces it
14 directly or stores it on the system stack. C<trace_system_stack>
15 sets up a trace of the system stack using two marker addresses as
16 boundaries. The code to actually perform the trace of a memory block
17 between two boundaries is located in C<src/gc/api.c:trace_mem_block>.
19 TT #273: This file needs to be cleaned up significantly.
29 #include "parrot/parrot.h"
30 #include "gc_private.h"
32 /* HEADERIZER HFILE: src/gc/gc_private.h */
34 /* HEADERIZER BEGIN: static */
35 /* Don't modify between HEADERIZER BEGIN / HEADERIZER END. Your changes will be lost. */
38 static size_t find_common_mask(PARROT_INTERP
, size_t val1
, size_t val2
)
39 __attribute__nonnull__(1);
41 PARROT_WARN_UNUSED_RESULT
42 static size_t get_max_buffer_address(ARGIN(const Memory_Pools
*mem_pools
))
43 __attribute__nonnull__(1);
45 PARROT_WARN_UNUSED_RESULT
46 static size_t get_max_pmc_address(ARGIN(const Memory_Pools
*mem_pools
))
47 __attribute__nonnull__(1);
49 PARROT_WARN_UNUSED_RESULT
50 static size_t get_min_buffer_address(ARGIN(const Memory_Pools
*mem_pools
))
51 __attribute__nonnull__(1);
53 PARROT_WARN_UNUSED_RESULT
54 static size_t get_min_pmc_address(ARGIN(const Memory_Pools
*mem_pools
))
55 __attribute__nonnull__(1);
57 PARROT_WARN_UNUSED_RESULT
58 static int is_buffer_ptr(
59 ARGIN(const Memory_Pools
*mem_pools
),
60 ARGIN(const void *ptr
))
61 __attribute__nonnull__(1)
62 __attribute__nonnull__(2);
64 PARROT_WARN_UNUSED_RESULT
65 static int is_pmc_ptr(
66 ARGIN(const Memory_Pools
*mem_pools
),
67 ARGIN(const void *ptr
))
68 __attribute__nonnull__(1)
69 __attribute__nonnull__(2);
71 static void trace_mem_block(PARROT_INTERP
,
72 ARGIN(const Memory_Pools
*mem_pools
),
75 __attribute__nonnull__(1)
76 __attribute__nonnull__(2);
79 static void trace_system_stack(PARROT_INTERP
,
80 ARGIN(const Memory_Pools
*mem_pools
))
81 __attribute__nonnull__(1)
82 __attribute__nonnull__(2);
84 #define ASSERT_ARGS_find_common_mask __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
85 PARROT_ASSERT_ARG(interp))
86 #define ASSERT_ARGS_get_max_buffer_address __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
87 PARROT_ASSERT_ARG(mem_pools))
88 #define ASSERT_ARGS_get_max_pmc_address __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
89 PARROT_ASSERT_ARG(mem_pools))
90 #define ASSERT_ARGS_get_min_buffer_address __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
91 PARROT_ASSERT_ARG(mem_pools))
92 #define ASSERT_ARGS_get_min_pmc_address __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
93 PARROT_ASSERT_ARG(mem_pools))
94 #define ASSERT_ARGS_is_buffer_ptr __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
95 PARROT_ASSERT_ARG(mem_pools) \
96 , PARROT_ASSERT_ARG(ptr))
97 #define ASSERT_ARGS_is_pmc_ptr __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
98 PARROT_ASSERT_ARG(mem_pools) \
99 , PARROT_ASSERT_ARG(ptr))
100 #define ASSERT_ARGS_trace_mem_block __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
101 PARROT_ASSERT_ARG(interp) \
102 , PARROT_ASSERT_ARG(mem_pools))
103 #define ASSERT_ARGS_trace_system_stack __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
104 PARROT_ASSERT_ARG(interp) \
105 , PARROT_ASSERT_ARG(mem_pools))
106 /* Don't modify between HEADERIZER BEGIN / HEADERIZER END. Your changes will be lost. */
107 /* HEADERIZER END: static */
111 =item C<void trace_system_areas(PARROT_INTERP, const Memory_Pools *mem_pools)>
113 Initiates a trace of the system stack, looking for pointers which are being
114 used by functions in the call chain, but which might not be marked as alive
115 in any other way. Setting the trace up, which involves storing the processor
116 context onto the stack, is highly system dependent. However, once stored,
117 tracing the stack is very straightforward.
124 trace_system_areas(PARROT_INTERP
, ARGIN(const Memory_Pools
*mem_pools
))
126 ASSERT_ARGS(trace_system_areas
)
129 /* Flush the register windows. For sparc systems, we use hand-coded
130 assembly language to create a small function that flushes the
131 register windows. Store the code in a union with a double to
132 ensure proper memory alignment. */
133 /* TT #271: This needs to be fixed in a variety of ways */
135 unsigned int insns
[4];
136 double align_hack
[2];
139 0x81580000, /* flushw */
141 0x91d02003, /* ta ST_FLUSH_WINDOWS */
143 0x81c3e008, /* retl */
147 /* Turn the array of machine code values above into a function pointer.
148 Call the new function pointer to flush the register windows. */
149 static void (*fn_ptr
)(void) = (void (*)(void))&u
.align_hack
[0];
152 #elif defined(__ia64__)
157 void *current_regstore_top
;
163 current_regstore_top
= (void*)(uint64_t)_Asm_mov_from_ar(_AREG_BSP
);
165 current_regstore_top
= (void*)(uint32_t)_Asm_mov_from_ar(_AREG_BSP
);
169 struct pst_vm_status buf
;
172 while (pstat_getprocvm(&buf
, sizeof (buf
), 0, i
++) == 1) {
173 if (buf
.pst_type
== PS_RSESTACK
) {
174 base
= (size_t)buf
.pst_vaddr
;
180 /* On IA64 Linux systems, we use the function getcontext() to get the
181 current processor context. This function is located in <ucontext.h>,
184 void *current_regstore_top
;
186 /* Trace the memory block for the register backing stack, which
187 is separate from the normal system stack. The register backing
188 stack starts at memory address 0x80000FFF80000000 and ends at
189 current_regstore_top. */
190 size_t base
= 0x80000fff80000000;
194 /* flush_reg_store() is defined in config/gen/platforms/ia64/asm.s.
195 it calls the flushrs opcode to perform the register flush, and
196 returns the address of the register backing stack. */
197 current_regstore_top
= flush_reg_store();
201 trace_mem_block(interp
, base
,
202 (size_t)current_regstore_top
);
204 #else /* !__ia64__ */
206 # ifdef PARROT_HAS_HEADER_SETJMP
207 /* A jump buffer that is used to store the current processor context.
208 local variables like this are created on the stack. */
209 Parrot_jump_buff env
;
211 /* Zero the Parrot_jump_buff, otherwise you will trace stale objects.
212 Plus, optimizing compilers won't be so quick to optimize the data
213 away if we're passing pointers around. */
214 memset(&env
, 0, sizeof (env
));
216 /* this should put registers in env, which then get marked in
217 * trace_system_stack below
222 #endif /* __ia64__ */
225 /* With the processor context accounted for above, we can trace the
226 system stack here. */
227 trace_system_stack(interp
, mem_pools
);
232 =item C<static void trace_system_stack(PARROT_INTERP, const Memory_Pools
235 Traces the memory block starting at C<< interp->lo_var_ptr >>. This should be
236 the address of a local variable which has been created on the stack early in
237 the interpreter's lifecycle. We trace until the address of another local stack
238 variable in this function, which should be at the "top" of the stack. For this
239 reason, this function must never be inlined.
247 trace_system_stack(PARROT_INTERP
, ARGIN(const Memory_Pools
*mem_pools
))
249 ASSERT_ARGS(trace_system_stack
)
250 /* Create a local variable on the system stack. This represents the
251 "top" of the stack. A value stored in interp->lo_var_ptr represents
252 the "bottom" of the stack. We must trace the entire area between the
254 const size_t lo_var_ptr
= (size_t)interp
->lo_var_ptr
;
255 PARROT_ASSERT(lo_var_ptr
);
257 trace_mem_block(interp
, mem_pools
, (size_t)lo_var_ptr
,
258 (size_t)&lo_var_ptr
);
263 =item C<static size_t get_max_buffer_address(const Memory_Pools *mem_pools)>
265 Calculates the maximum buffer address and returns it. This is done by looping
266 through all the sized pools, and finding the pool whose C<end_arena_memory>
267 field is the highest. Notice that arenas in each pool are not necessarily
268 located directly next to each other in memory, and the last arena in the pool's
269 list may not be located at the highest memory address.
275 PARROT_WARN_UNUSED_RESULT
277 get_max_buffer_address(ARGIN(const Memory_Pools
*mem_pools
))
279 ASSERT_ARGS(get_max_buffer_address
)
283 for (i
= 0; i
< mem_pools
->num_sized
; ++i
) {
284 if (mem_pools
->sized_header_pools
[i
]) {
285 if (mem_pools
->sized_header_pools
[i
]->end_arena_memory
> max
)
286 max
= mem_pools
->sized_header_pools
[i
]->end_arena_memory
;
296 =item C<static size_t get_min_buffer_address(const Memory_Pools *mem_pools)>
298 Calculates the minimum buffer address and returns it. Loops through all sized
299 pools, and finds the one with the smallest C<start_arena_memory> field. Notice
300 that the memory region between C<get_min_buffer_address> and
301 C<get_max_buffer_address> may be fragmented, and parts of it may not be
302 available for Parrot to use directly (such as bookkeeping data for the OS
309 PARROT_WARN_UNUSED_RESULT
311 get_min_buffer_address(ARGIN(const Memory_Pools
*mem_pools
))
313 ASSERT_ARGS(get_min_buffer_address
)
314 size_t min
= (size_t) -1;
317 for (i
= 0; i
< mem_pools
->num_sized
; ++i
) {
318 const Fixed_Size_Pool
* const pool
= mem_pools
->sized_header_pools
[i
];
319 if (pool
&& pool
->start_arena_memory
) {
320 if (pool
->start_arena_memory
< min
)
321 min
= pool
->start_arena_memory
;
331 =item C<static size_t get_max_pmc_address(const Memory_Pools *mem_pools)>
333 Returns the maximum memory address used by the C<pmc_pool>.
339 PARROT_WARN_UNUSED_RESULT
341 get_max_pmc_address(ARGIN(const Memory_Pools
*mem_pools
))
343 ASSERT_ARGS(get_max_pmc_address
)
344 return mem_pools
->pmc_pool
->end_arena_memory
;
350 =item C<static size_t get_min_pmc_address(const Memory_Pools *mem_pools)>
352 Returns the minimum memory address used by the C<pmc_pool>. Notice that the
353 memory region between C<get_min_pmc_address> and C<get_max_pmc_address> may be
354 fragmented, and not all of it may be used directly by Parrot for storing PMCs.
360 PARROT_WARN_UNUSED_RESULT
362 get_min_pmc_address(ARGIN(const Memory_Pools
*mem_pools
))
364 ASSERT_ARGS(get_min_pmc_address
)
365 return mem_pools
->pmc_pool
->start_arena_memory
;
369 #ifndef PLATFORM_STACK_WALK
373 =item C<static size_t find_common_mask(PARROT_INTERP, size_t val1, size_t val2)>
375 Finds a mask covering the longest common bit-prefix of C<val1>
382 PARROT_CONST_FUNCTION
384 find_common_mask(PARROT_INTERP
, size_t val1
, size_t val2
)
386 ASSERT_ARGS(find_common_mask
)
388 const int bound
= sizeof (size_t) * 8;
390 /* Shifting a value by its size (in bits) or larger is undefined behaviour.
391 So need an explicit check to return 0 if there is no prefix, rather than
392 attempting to rely on (say) 0xFFFFFFFF << 32 being 0. */
393 for (i
= 0; i
< bound
; ++i
) {
395 return ~(size_t)0 << i
;
402 PARROT_ASSERT(i
== bound
);
406 Parrot_ex_throw_from_c_args(interp
, NULL
, EXCEPTION_INTERP_ERROR
,
407 "Unexpected condition in find_common_mask()!\n");
412 =item C<static void trace_mem_block(PARROT_INTERP, const Memory_Pools
413 *mem_pools, size_t lo_var_ptr, size_t hi_var_ptr)>
415 Traces the memory block between C<lo_var_ptr> and C<hi_var_ptr>.
416 Attempt to find pointers to PObjs or buffers, and mark them as "alive"
417 if found. See src/cpu_dep.c for more information about tracing memory
425 trace_mem_block(PARROT_INTERP
,
426 ARGIN(const Memory_Pools
*mem_pools
),
427 size_t lo_var_ptr
, size_t hi_var_ptr
)
429 ASSERT_ARGS(trace_mem_block
)
431 ptrdiff_t cur_var_ptr
;
433 const size_t buffer_min
= get_min_buffer_address(mem_pools
);
434 const size_t buffer_max
= get_max_buffer_address(mem_pools
);
435 const size_t pmc_min
= get_min_pmc_address(mem_pools
);
436 const size_t pmc_max
= get_max_pmc_address(mem_pools
);
439 find_common_mask(interp
,
440 buffer_min
< pmc_min
? buffer_min
: pmc_min
,
441 buffer_max
> pmc_max
? buffer_max
: pmc_max
);
443 if (!lo_var_ptr
|| !hi_var_ptr
)
446 if (lo_var_ptr
< hi_var_ptr
) {
447 const size_t tmp_ptr
= hi_var_ptr
;
448 hi_var_ptr
= lo_var_ptr
;
449 lo_var_ptr
= tmp_ptr
;
452 /* Get the expected prefix */
453 prefix
= mask
& buffer_min
;
455 for (cur_var_ptr
= hi_var_ptr
;
456 (ptrdiff_t)cur_var_ptr
< (ptrdiff_t)lo_var_ptr
;
457 cur_var_ptr
= (size_t)((ptrdiff_t)cur_var_ptr
+ sizeof (void *))) {
458 const size_t ptr
= *(size_t *)cur_var_ptr
;
460 /* Do a quick approximate range check by bit-masking */
461 if ((ptr
& mask
) == prefix
|| !prefix
) {
462 /* Note that what we find via the stack or registers are not
463 * guaranteed to be live pmcs/buffers, and could very well have
464 * had their bufstart/vtable destroyed due to the linked list of
468 && is_pmc_ptr(mem_pools
, (void *)ptr
)) {
469 Parrot_gc_mark_PMC_alive(interp
, (PMC
*)ptr
);
471 else if ((buffer_min
<= ptr
) && (ptr
< buffer_max
)
472 && is_buffer_ptr(mem_pools
, (void *)ptr
)) {
473 if (PObj_is_string_TEST((PObj
*)ptr
))
474 Parrot_gc_mark_STRING_alive(interp
, (STRING
*)ptr
);
476 PObj_live_SET((PObj
*)ptr
);
486 =item C<static int is_buffer_ptr(const Memory_Pools *mem_pools, const void
489 Checks whether the given C<ptr> is located within one of the sized
490 header pools. Returns C<1> if it is, and C<0> if not.
496 PARROT_WARN_UNUSED_RESULT
498 is_buffer_ptr(ARGIN(const Memory_Pools
*mem_pools
), ARGIN(const void *ptr
))
500 ASSERT_ARGS(is_buffer_ptr
)
503 for (i
= 0; i
< mem_pools
->num_sized
; ++i
) {
504 if (mem_pools
->sized_header_pools
[i
]
505 && contained_in_pool(mem_pools
->sized_header_pools
[i
], ptr
))
514 =item C<static int is_pmc_ptr(const Memory_Pools *mem_pools, const void *ptr)>
516 Checks that C<ptr> is actually a PMC pointer. Returns C<1> if it is, C<0>
523 PARROT_WARN_UNUSED_RESULT
525 is_pmc_ptr(ARGIN(const Memory_Pools
*mem_pools
), ARGIN(const void *ptr
))
527 ASSERT_ARGS(is_pmc_ptr
)
528 return contained_in_pool(mem_pools
->pmc_pool
, ptr
)
529 && PObj_is_PMC_TEST((PObj
*)ptr
);
541 F<src/gc/api.c> and F<include/parrot/gc_api.h>.
550 * c-file-style: "parrot"
552 * vim: expandtab shiftwidth=4: