fix codetest failure - ASSERT_ARGS does not have a ; after and
[parrot.git] / src / gc / system.c
bloba27126d4fc5d6fd055200abed606fe75f13e37a6
1 /*
2 Copyright (C) 2001-2010, Parrot Foundation.
3 $Id$
5 =head1 NAME
7 src/gc/system.c - CPU-dependent mark/sweep functions
9 =head1 DESCRIPTION
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.
21 =head2 Functions
23 =over 4
25 =cut
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. */
37 PARROT_CONST_FUNCTION
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),
73 size_t lo_var_ptr,
74 size_t hi_var_ptr)
75 __attribute__nonnull__(1)
76 __attribute__nonnull__(2);
78 PARROT_NOINLINE
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.
119 =cut
123 void
124 trace_system_areas(PARROT_INTERP, ARGIN(const Memory_Pools *mem_pools))
126 ASSERT_ARGS(trace_system_areas)
128 #if defined(__sparc)
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 */
134 static union {
135 unsigned int insns[4];
136 double align_hack[2];
137 } u = { {
138 # ifdef __sparcv9
139 0x81580000, /* flushw */
140 # else
141 0x91d02003, /* ta ST_FLUSH_WINDOWS */
142 # endif
143 0x81c3e008, /* retl */
144 0x01000000 /* nop */
145 } };
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];
150 fn_ptr();
152 #elif defined(__ia64__)
155 # if defined(__hpux)
156 ucontext_t ucp;
157 void *current_regstore_top;
159 getcontext(&ucp);
160 _Asm_flushrs();
162 # if defined(_LP64)
163 current_regstore_top = (void*)(uint64_t)_Asm_mov_from_ar(_AREG_BSP);
164 # else
165 current_regstore_top = (void*)(uint32_t)_Asm_mov_from_ar(_AREG_BSP);
166 # endif
168 size_t base = 0;
169 struct pst_vm_status buf;
170 int i = 0;
172 while (pstat_getprocvm(&buf, sizeof (buf), 0, i++) == 1) {
173 if (buf.pst_type == PS_RSESTACK) {
174 base = (size_t)buf.pst_vaddr;
175 break;
179 # else /* !__hpux */
180 /* On IA64 Linux systems, we use the function getcontext() to get the
181 current processor context. This function is located in <ucontext.h>,
182 included above. */
183 struct ucontext ucp;
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;
192 getcontext(&ucp);
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();
199 # endif /* __hpux */
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
219 setjmp(env);
220 # endif
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
233 *mem_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.
241 =cut
245 PARROT_NOINLINE
246 static void
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
253 top and bottom. */
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.
271 =cut
275 PARROT_WARN_UNUSED_RESULT
276 static size_t
277 get_max_buffer_address(ARGIN(const Memory_Pools *mem_pools))
279 ASSERT_ARGS(get_max_buffer_address)
280 size_t max = 0;
281 UINTVAL i;
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;
290 return max;
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
303 memory manager).
305 =cut
309 PARROT_WARN_UNUSED_RESULT
310 static size_t
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;
315 UINTVAL i;
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;
325 return min;
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>.
335 =cut
339 PARROT_WARN_UNUSED_RESULT
340 static size_t
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.
356 =cut
360 PARROT_WARN_UNUSED_RESULT
361 static size_t
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>
376 and C<val2>.
378 =cut
382 PARROT_CONST_FUNCTION
383 static size_t
384 find_common_mask(PARROT_INTERP, size_t val1, size_t val2)
386 ASSERT_ARGS(find_common_mask)
387 int i;
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) {
394 if (val1 == val2)
395 return ~(size_t)0 << i;
397 val1 >>= 1;
398 val2 >>= 1;
401 if (val1 == val2) {
402 PARROT_ASSERT(i == bound);
403 return 0;
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
418 areas.
420 =cut
424 static void
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)
430 size_t prefix;
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);
438 const size_t mask =
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)
444 return;
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
465 * free headers... */
466 if ((pmc_min <= ptr)
467 && (ptr < pmc_max)
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);
475 else
476 PObj_live_SET((PObj *)ptr);
481 return;
486 =item C<static int is_buffer_ptr(const Memory_Pools *mem_pools, const void
487 *ptr)>
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.
492 =cut
496 PARROT_WARN_UNUSED_RESULT
497 static int
498 is_buffer_ptr(ARGIN(const Memory_Pools *mem_pools), ARGIN(const void *ptr))
500 ASSERT_ARGS(is_buffer_ptr)
501 UINTVAL i;
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))
506 return 1;
509 return 0;
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>
517 otherwise.
519 =cut
523 PARROT_WARN_UNUSED_RESULT
524 static int
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);
533 #endif
537 =back
539 =head1 SEE ALSO
541 F<src/gc/api.c> and F<include/parrot/gc_api.h>.
543 =cut
549 * Local variables:
550 * c-file-style: "parrot"
551 * End:
552 * vim: expandtab shiftwidth=4: