tagged release 0.7.1
[parrot.git] / src / gc / register.c
blob326e3926f5df4ebd1ac85ae45c33146bed07e0e8
1 /*
2 Copyright (C) 2001-2008, The Perl Foundation.
3 $Id$
5 =head1 NAME
7 src/gc/register.c - Register handling routines
9 =head1 DESCRIPTION
11 Parrot has 4 register sets, one for each of its basic types. The
12 number of registers in each set varies depending on the use counts of
13 the subroutine and is determined by the PASM/PIR compiler in the
14 register allocation pass (F<imcc/reg_alloc.c>).
16 =cut
20 #include "parrot/parrot.h"
21 #include "parrot/register.h"
23 /* HEADERIZER HFILE: include/parrot/register.h */
25 /* HEADERIZER BEGIN: static */
26 /* Don't modify between HEADERIZER BEGIN / HEADERIZER END. Your changes will be lost. */
28 static void clear_regs(PARROT_INTERP, ARGMOD(parrot_context_t *ctx))
29 __attribute__nonnull__(1)
30 __attribute__nonnull__(2)
31 FUNC_MODIFIES(*ctx);
33 static void init_context(PARROT_INTERP,
34 ARGMOD(parrot_context_t *ctx),
35 ARGIN_NULLOK(const parrot_context_t *old))
36 __attribute__nonnull__(1)
37 __attribute__nonnull__(2)
38 FUNC_MODIFIES(*ctx);
40 /* Don't modify between HEADERIZER BEGIN / HEADERIZER END. Your changes will be lost. */
41 /* HEADERIZER END: static */
45 =head2 Context and register frame layout
47 +----------++----+------+------------+----+
48 | context || N | I | P | S +
49 +----------++----+------+------------+----+
50 ^ ^ ^ ^
51 | | ctx.bp ctx.bp_ps
52 ctx.state opt
53 padding
55 Registers are addressed as usual via the register base pointer ctx.bp.
57 The macro CONTEXT() hides these details
59 =cut
64 =head2 Context and register frame allocation
66 There are two allocation strategies: chunked memory and malloced
67 with a free list.
69 CHUNKED_CTX_MEM = 1
71 C<ctx_mem.data> is a pointer to an allocated chunk of memory.
72 The pointer C<ctx_mem.free> holds the next usable
73 location. With (full) continuations the C<ctx_mem.free> pointer can't be
74 moved below the C<ctx_mem.threshold>, which is the highest context pointer
75 of all active continuations.
76 [the code for this is incomplete; it had suffered some bit-rot and was
77 getting in the way of maintaining the other case. -- rgr, 4-Feb-06.]
79 RT #46177 GC has to lower this threshold when collecting continuations.
81 CHUNKED_CTX_MEM = 0
83 Context/register memory is malloced. C<ctx_mem.free> is used as a free
84 list of reusable items.
86 =cut
90 #define CTX_ALLOC_SIZE 0x20000
92 #define ALIGNED_CTX_SIZE (((sizeof (struct Parrot_Context) + NUMVAL_SIZE - 1) \
93 / NUMVAL_SIZE) * NUMVAL_SIZE)
97 =pod
99 Round register allocation size up to the nearest multiple of 8. A
100 granularity of 8 is arbitrary, it could have been some bigger power of 2. A
101 "slot" is an index into the free_list array. Each slot in free_list has a
102 linked list of pointers to already allocated contexts available for (re)use.
103 The slot where an available context is stored corresponds to the size of the
104 context.
106 =cut
110 #define SLOT_CHUNK_SIZE 8
112 #define ROUND_ALLOC_SIZE(size) ((((size) + SLOT_CHUNK_SIZE - 1) \
113 / SLOT_CHUNK_SIZE) * SLOT_CHUNK_SIZE)
114 #define CALCULATE_SLOT_NUM(size) ((size) / SLOT_CHUNK_SIZE)
116 #if CHUNKED_CTX_MEM
117 # error "Non-working code removed."
118 #endif
122 =head2 Context and Register Allocation Functions
124 =over 4
126 =cut
132 =item C<void destroy_context>
134 Free allocated context memory
136 =cut
140 void
141 destroy_context(PARROT_INTERP)
143 int slot;
144 Parrot_Context *context;
146 /* clear active contexts all the way back to the initial context */
147 context = CONTEXT(interp);
148 while (context) {
149 Parrot_Context * const prev = context->caller_ctx;
150 if (context->n_regs_used) {
151 mem_sys_free(context->n_regs_used);
152 context->n_regs_used = NULL;
154 mem_sys_free(context);
155 context = prev;
158 /* clear freed contexts */
159 for (slot = 0; slot < interp->ctx_mem.n_free_slots; ++slot) {
160 void *ptr = interp->ctx_mem.free_list[slot];
161 while (ptr) {
162 void * const next = *(void **) ptr;
163 mem_sys_free(ptr);
164 ptr = next;
167 mem_sys_free(interp->ctx_mem.free_list);
172 =item C<void create_initial_context>
174 Create initial interpreter context.
176 =cut
180 void
181 create_initial_context(PARROT_INTERP)
183 static INTVAL num_regs[] = {32, 32, 32, 32};
184 Parrot_Context *ignored;
186 /* Create some initial free_list slots. */
188 #define INITIAL_FREE_SLOTS 8
189 interp->ctx_mem.n_free_slots = INITIAL_FREE_SLOTS;
190 interp->ctx_mem.free_list = mem_allocate_n_zeroed_typed(INITIAL_FREE_SLOTS, void *);
192 /* For now create context with 32 regs each. Some src tests (and maybe
193 * other extenders) assume the presence of these registers */
194 ignored = Parrot_alloc_context(interp, num_regs);
195 UNUSED(ignored);
200 =item C<void parrot_gc_context>
202 Cleanup dead context memory. Called by the garbage collector.
204 =cut
208 PARROT_API
209 void
210 parrot_gc_context(PARROT_INTERP)
212 #if CHUNKED_CTX_MEM
213 parrot_context_t ctx;
215 if (!interp->ctx_mem.threshold)
216 return;
217 LVALUE_CAST(char *, ctx.bp) = interp->ctx_mem.threshold -
218 sizeof (parrot_regs_t);
219 /* RT #46187 */
220 #else
221 UNUSED(interp);
222 #endif
227 =item C<static void clear_regs>
229 RT #48260: Not yet documented!!!
231 =cut
235 static void
236 clear_regs(PARROT_INTERP, ARGMOD(parrot_context_t *ctx))
238 int i;
240 /* NULL out registers - P/S have to be NULL for GC
242 * if the architecture has 0x := NULL and 0.0 we could memset too
244 ctx->bp.regs_i = interp->ctx.bp.regs_i;
245 ctx->bp_ps.regs_s = interp->ctx.bp_ps.regs_s;
246 for (i = 0; i < ctx->n_regs_used[REGNO_PMC]; i++) {
247 CTX_REG_PMC(ctx, i) = PMCNULL;
249 for (i = 0; i < ctx->n_regs_used[REGNO_STR]; i++) {
250 CTX_REG_STR(ctx, i) = NULL;
253 if (Interp_debug_TEST(interp, PARROT_REG_DEBUG_FLAG)) {
254 /* depending on -D40 we set int, num to garbage different garbage
255 * RT #46179 remove this code for parrot 1.0
257 for (i = 0; i < ctx->n_regs_used[REGNO_INT]; i++) {
258 CTX_REG_INT(ctx, i) = -999;
260 for (i = 0; i < ctx->n_regs_used[REGNO_NUM]; i++) {
261 CTX_REG_NUM(ctx, i) = -99.9;
264 else {
265 for (i = 0; i < ctx->n_regs_used[REGNO_INT]; i++) {
266 CTX_REG_INT(ctx, i) = -888;
268 for (i = 0; i < ctx->n_regs_used[REGNO_NUM]; i++) {
269 CTX_REG_NUM(ctx, i) = -88.8;
276 =item C<static void init_context>
278 RT #48260: Not yet documented!!!
280 =cut
284 static void
285 init_context(PARROT_INTERP, ARGMOD(parrot_context_t *ctx),
286 ARGIN_NULLOK(const parrot_context_t *old))
288 ctx->ref_count = 0; /* RT #46191 1 - Exceptions !!! */
289 ctx->current_results = NULL;
290 ctx->results_signature = NULL;
291 ctx->lex_pad = PMCNULL;
292 ctx->outer_ctx = NULL;
293 ctx->current_cont = NULL;
294 ctx->current_object = NULL; /* RT #46181 who clears it? */
295 ctx->current_HLL = 0;
296 ctx->handlers = PMCNULL;
298 if (old) {
299 /* some items should better be COW copied */
300 ctx->constants = old->constants;
301 ctx->warns = old->warns;
302 ctx->errors = old->errors;
303 ctx->trace_flags = old->trace_flags;
304 ctx->pred_offset = old->pred_offset;
305 ctx->current_HLL = old->current_HLL;
306 ctx->current_namespace = old->current_namespace;
307 /* end COW */
308 ctx->recursion_depth = old->recursion_depth;
310 /* other stuff is set inside Sub.invoke */
311 clear_regs(interp, ctx);
316 =item C<struct Parrot_Context * Parrot_dup_context>
318 Duplicate the passed context
320 =cut
324 PARROT_WARN_UNUSED_RESULT
325 PARROT_CANNOT_RETURN_NULL
326 struct Parrot_Context *
327 Parrot_dup_context(PARROT_INTERP, ARGIN(const struct Parrot_Context *old))
329 size_t diff;
330 Parrot_Context *ctx;
331 DECL_CONST_CAST;
333 const size_t reg_alloc = old->regs_mem_size;
334 const int slot = CALCULATE_SLOT_NUM(reg_alloc);
335 void * ptr = interp->ctx_mem.free_list[slot];
337 if (ptr)
338 interp->ctx_mem.free_list[slot] = *(void **) ptr;
339 else
340 ptr = (void *)mem_sys_allocate(reg_alloc + ALIGNED_CTX_SIZE);
342 ctx = (Parrot_Context *)ptr;
343 CONTEXT(interp) = ctx;
345 ctx->regs_mem_size = reg_alloc;
346 ctx->n_regs_used = mem_allocate_n_zeroed_typed(4, INTVAL);
347 ctx->n_regs_used[REGNO_INT] = old->n_regs_used[REGNO_INT];
348 ctx->n_regs_used[REGNO_NUM] = old->n_regs_used[REGNO_NUM];
349 ctx->n_regs_used[REGNO_STR] = old->n_regs_used[REGNO_STR];
350 ctx->n_regs_used[REGNO_PMC] = old->n_regs_used[REGNO_PMC];
351 diff = (const long *)ctx - (const long *) old;
353 interp->ctx.bp.regs_i += diff;
354 interp->ctx.bp_ps.regs_s += diff;
355 init_context(interp, ctx, old);
356 return ctx;
361 =item C<struct Parrot_Context * Parrot_push_context>
363 Remember old context in C<caller_ctx>, suitable to use with
364 C<Parrot_pop_context>.
366 =cut
370 PARROT_API
371 PARROT_WARN_UNUSED_RESULT
372 PARROT_CANNOT_RETURN_NULL
373 struct Parrot_Context *
374 Parrot_push_context(PARROT_INTERP, ARGMOD(INTVAL *n_regs_used))
376 Parrot_Context * const old = CONTEXT(interp);
377 Parrot_Context * const ctx = Parrot_alloc_context(interp, n_regs_used);
379 ctx->caller_ctx = old;
381 /* doesn't change */
382 ctx->current_sub = old->current_sub;
384 /* copy more ? */
385 return ctx;
390 =item C<void Parrot_pop_context>
392 Free the context created with C<Parrot_push_context> and restore the previous
393 context.
395 =cut
399 PARROT_API
400 void
401 Parrot_pop_context(PARROT_INTERP)
403 Parrot_Context * const ctx = CONTEXT(interp);
404 Parrot_Context * const old = ctx->caller_ctx;
406 Parrot_free_context(interp, ctx, 1);
408 /* restore old, set cached interpreter base pointers */
409 CONTEXT(interp) = old;
410 interp->ctx.bp = old->bp;
411 interp->ctx.bp_ps = old->bp_ps;
417 =item C<struct Parrot_Context * Parrot_alloc_context>
419 Allocate a new context and set the context pointer. Please note that the
420 register usage C<n_regs_used> is copied. The function returns the new context.
422 =cut
426 PARROT_CANNOT_RETURN_NULL
427 PARROT_WARN_UNUSED_RESULT
428 struct Parrot_Context *
429 Parrot_alloc_context(PARROT_INTERP, ARGMOD(INTVAL *number_regs_used))
431 Parrot_Context *old, *ctx;
432 void *ptr, *p;
435 * RT #46185 (OPT) if we allocate a new context due to a self-recursive call
436 * create a specialized version that just uses caller's size
438 const size_t size_i = sizeof (INTVAL) * number_regs_used[REGNO_INT];
439 const size_t size_n = sizeof (FLOATVAL) * number_regs_used[REGNO_NUM];
440 const size_t size_s = sizeof (STRING*) * number_regs_used[REGNO_STR];
441 const size_t size_p = sizeof (PMC*) * number_regs_used[REGNO_PMC];
443 const size_t size_nip = size_n + size_i + size_p;
444 const size_t all_regs_size = size_n + size_i + size_p + size_s;
445 const size_t reg_alloc = ROUND_ALLOC_SIZE(all_regs_size);
446 const int slot = CALCULATE_SLOT_NUM(reg_alloc);
448 /* this gets attached to the context, which should free it */
449 INTVAL * const n_regs_used = mem_allocate_n_zeroed_typed(4, INTVAL);
450 n_regs_used[REGNO_INT] = number_regs_used[REGNO_INT];
451 n_regs_used[REGNO_NUM] = number_regs_used[REGNO_NUM];
452 n_regs_used[REGNO_STR] = number_regs_used[REGNO_STR];
453 n_regs_used[REGNO_PMC] = number_regs_used[REGNO_PMC];
456 * If slot is beyond the end of the allocated list, extend the list to
457 * allocate more slots.
459 if (slot >= interp->ctx_mem.n_free_slots) {
460 const int extend_size = slot + 1;
461 int i;
463 mem_realloc_n_typed(interp->ctx_mem.free_list, extend_size, void*);
464 for (i = interp->ctx_mem.n_free_slots; i < extend_size; ++i)
465 interp->ctx_mem.free_list[i] = NULL;
466 interp->ctx_mem.n_free_slots = extend_size;
470 * The free_list contains a linked list of pointers for each size (slot
471 * index). Pop off an available context of the desired size from free_list.
472 * If no contexts of the desired size are available, allocate a new one.
474 ptr = interp->ctx_mem.free_list[slot];
475 old = CONTEXT(interp);
477 if (ptr) {
479 * Store the next pointer from the linked list for this size (slot
480 * index) in free_list. On "*(void **) ptr", C won't dereference a void
481 * * pointer (untyped), so type cast ptr to void** (a dereference-able
482 * type) then dereference it to get a void*. Store the dereferenced
483 * value (the next pointer in the linked list) in free_list.
485 interp->ctx_mem.free_list[slot] = *(void **) ptr;
487 else {
488 const size_t to_alloc = reg_alloc + ALIGNED_CTX_SIZE;
489 if (old)
490 ptr = mem_sys_allocate(to_alloc);
491 else
492 ptr = mem_sys_allocate_zeroed(to_alloc);
495 #if CTX_LEAK_DEBUG
496 if (Interp_debug_TEST(interp, PARROT_CTX_DESTROY_DEBUG_FLAG)) {
497 fprintf(stderr, "[alloc ctx %p]\n", ptr);
499 #endif
501 CONTEXT(interp) = ctx = (Parrot_Context *)ptr;
503 ctx->regs_mem_size = reg_alloc;
504 ctx->n_regs_used = n_regs_used;
506 /* regs start past the context */
507 p = (void *) ((char *)ptr + ALIGNED_CTX_SIZE);
509 /* ctx.bp points to I0, which has Nx on the left */
510 interp->ctx.bp.regs_i = (INTVAL*)((char*)p + size_n);
512 /* ctx.bp_ps points to S0, which has Px on the left */
513 interp->ctx.bp_ps.regs_s = (STRING**)((char*)p + size_nip);
514 init_context(interp, ctx, old);
516 return ctx;
521 =item C<void Parrot_free_context>
523 Free the context. If C<re_use> is true, this function is called by a
524 return continuation invoke, else from the destructor of a continuation.
526 =cut
530 PARROT_API
531 void
532 Parrot_free_context(PARROT_INTERP, ARGMOD(struct Parrot_Context *ctxp), int re_use)
535 * The context structure has a reference count, initially 0. This field is
536 * incremented when a continuation that points to it is created -- either
537 * directly, or when a continuation is cloned, or when a retcontinuation is
538 * converted to a full continuation in invalidate_retc. To check for leaks,
539 * (a) disable NDEBUG, (b) enable CTX_LEAK_DEBUG in interpreter.h, and (c)
540 * excecute "debug 0x80" in a (preferably small) test case.
543 if (re_use || --ctxp->ref_count <= 0) {
544 void *ptr;
545 int slot;
547 #ifndef NDEBUG
548 if (Interp_debug_TEST(interp, PARROT_CTX_DESTROY_DEBUG_FLAG)
549 && ctxp->current_sub) {
550 /* can't probably PIO_eprintf here */
551 const Parrot_sub * const doomed = PMC_sub(ctxp->current_sub);
553 if (doomed) {
554 fprintf(stderr, "[free ctx %p of sub '%s']\n",
555 (void *)ctxp,
556 (doomed->name == (void*)0xdeadbeef
557 ? "???"
558 : (char*)doomed->name->strstart));
560 else {
561 Parrot_ex_throw_from_c_args(interp, NULL, 1,
562 "NULL doomed sub detected in Parrot_free_context");
565 #endif
566 if (ctxp->n_regs_used) {
567 mem_sys_free(ctxp->n_regs_used);
568 ctxp->n_regs_used = NULL;
571 /* don't put the same context on the free list multiple times; we don't
572 * have the re-use versus multiple ref count semantics right yet */
573 if (ctxp->ref_count < 0)
574 return;
576 ctxp->ref_count = 0;
577 ptr = ctxp;
578 slot = CALCULATE_SLOT_NUM(ctxp->regs_mem_size);
580 PARROT_ASSERT(slot < interp->ctx_mem.n_free_slots);
581 *(void **)ptr = interp->ctx_mem.free_list[slot];
582 interp->ctx_mem.free_list[slot] = ptr;
588 =item C<void Parrot_set_context_threshold>
590 Mark the context as possible threshold.
592 =cut
596 PARROT_API
597 void
598 Parrot_set_context_threshold(SHIM_INTERP, SHIM(struct Parrot_Context *ctxp))
600 /* nothing to do */
605 =back
607 =head2 Register Stack Functions
609 =over 4
611 =cut
613 =item C<void Parrot_clear_i>
615 RT #48260: Not yet documented!!!
617 =cut
621 PARROT_API
622 void
623 Parrot_clear_i(PARROT_INTERP)
625 int i;
626 for (i = 0; i < CONTEXT(interp)->n_regs_used[REGNO_INT]; ++i)
627 REG_INT(interp, i) = 0;
632 =item C<void Parrot_clear_s>
634 RT #48260: Not yet documented!!!
636 =cut
640 PARROT_API
641 void
642 Parrot_clear_s(PARROT_INTERP)
644 int i;
645 for (i = 0; i < CONTEXT(interp)->n_regs_used[REGNO_STR]; ++i)
646 REG_STR(interp, i) = NULL;
651 =item C<void Parrot_clear_p>
653 RT #48260: Not yet documented!!!
655 =cut
659 PARROT_API
660 void
661 Parrot_clear_p(PARROT_INTERP)
663 int i;
664 for (i = 0; i < CONTEXT(interp)->n_regs_used[REGNO_PMC]; ++i)
665 REG_PMC(interp, i) = PMCNULL;
670 =item C<void Parrot_clear_n>
672 RT #48260: Not yet documented!!!
674 =cut
678 PARROT_API
679 void
680 Parrot_clear_n(PARROT_INTERP)
682 int i;
683 for (i = 0; i < CONTEXT(interp)->n_regs_used[REGNO_NUM]; ++i)
684 REG_NUM(interp, i) = 0.0;
690 =back
692 =head1 SEE ALSO
694 F<include/parrot/register.h> and F<src/stacks.c>.
696 =cut
702 * Local variables:
703 * c-file-style: "parrot"
704 * End:
705 * vim: expandtab shiftwidth=4: