* t/op/lexicals-2.t (added), MANIFEST:
[parrot.git] / src / gc / register.c
blobab88ac4ce8453e73026bd9b07af8f795d4d8e564
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;
297 if (old) {
298 /* some items should better be COW copied */
299 ctx->constants = old->constants;
300 ctx->warns = old->warns;
301 ctx->errors = old->errors;
302 ctx->trace_flags = old->trace_flags;
303 ctx->pred_offset = old->pred_offset;
304 ctx->current_HLL = old->current_HLL;
305 ctx->current_namespace = old->current_namespace;
306 /* end COW */
307 ctx->recursion_depth = old->recursion_depth;
309 /* other stuff is set inside Sub.invoke */
310 clear_regs(interp, ctx);
315 =item C<struct Parrot_Context * Parrot_dup_context>
317 Duplicate the passed context
319 =cut
323 PARROT_WARN_UNUSED_RESULT
324 PARROT_CANNOT_RETURN_NULL
325 struct Parrot_Context *
326 Parrot_dup_context(PARROT_INTERP, ARGIN(const struct Parrot_Context *old))
328 size_t diff;
329 Parrot_Context *ctx;
330 DECL_CONST_CAST;
332 const size_t reg_alloc = old->regs_mem_size;
333 const int slot = CALCULATE_SLOT_NUM(reg_alloc);
334 void * ptr = interp->ctx_mem.free_list[slot];
336 if (ptr)
337 interp->ctx_mem.free_list[slot] = *(void **) ptr;
338 else
339 ptr = (void *)mem_sys_allocate(reg_alloc + ALIGNED_CTX_SIZE);
341 ctx = (Parrot_Context *)ptr;
342 CONTEXT(interp) = ctx;
344 ctx->regs_mem_size = reg_alloc;
345 ctx->n_regs_used = mem_allocate_n_zeroed_typed(4, INTVAL);
346 ctx->n_regs_used[REGNO_INT] = old->n_regs_used[REGNO_INT];
347 ctx->n_regs_used[REGNO_NUM] = old->n_regs_used[REGNO_NUM];
348 ctx->n_regs_used[REGNO_STR] = old->n_regs_used[REGNO_STR];
349 ctx->n_regs_used[REGNO_PMC] = old->n_regs_used[REGNO_PMC];
350 diff = (const long *)ctx - (const long *) old;
352 interp->ctx.bp.regs_i += diff;
353 interp->ctx.bp_ps.regs_s += diff;
354 init_context(interp, ctx, old);
355 return ctx;
360 =item C<struct Parrot_Context * Parrot_push_context>
362 Remember old context in C<caller_ctx>, suitable to use with
363 C<Parrot_pop_context>.
365 =cut
369 PARROT_API
370 PARROT_WARN_UNUSED_RESULT
371 PARROT_CANNOT_RETURN_NULL
372 struct Parrot_Context *
373 Parrot_push_context(PARROT_INTERP, ARGMOD(INTVAL *n_regs_used))
375 Parrot_Context * const old = CONTEXT(interp);
376 Parrot_Context * const ctx = Parrot_alloc_context(interp, n_regs_used);
378 ctx->caller_ctx = old;
380 /* doesn't change */
381 ctx->current_sub = old->current_sub;
383 /* copy more ? */
384 return ctx;
389 =item C<void Parrot_pop_context>
391 Free the context created with C<Parrot_push_context> and restore the previous
392 context.
394 =cut
398 PARROT_API
399 void
400 Parrot_pop_context(PARROT_INTERP)
402 Parrot_Context * const ctx = CONTEXT(interp);
403 Parrot_Context * const old = ctx->caller_ctx;
405 Parrot_free_context(interp, ctx, 1);
407 /* restore old, set cached interpreter base pointers */
408 CONTEXT(interp) = old;
409 interp->ctx.bp = old->bp;
410 interp->ctx.bp_ps = old->bp_ps;
416 =item C<struct Parrot_Context * Parrot_alloc_context>
418 Allocate a new context and set the context pointer. Please note that the
419 register usage C<n_regs_used> is copied. The function returns the new context.
421 =cut
425 PARROT_CANNOT_RETURN_NULL
426 PARROT_WARN_UNUSED_RESULT
427 struct Parrot_Context *
428 Parrot_alloc_context(PARROT_INTERP, ARGMOD(INTVAL *number_regs_used))
430 Parrot_Context *old, *ctx;
431 void *ptr, *p;
434 * RT#46185 (OPT) if we allocate a new context due to a self-recursive call
435 * create a specialized version that just uses caller's size
437 const size_t size_i = sizeof (INTVAL) * number_regs_used[REGNO_INT];
438 const size_t size_n = sizeof (FLOATVAL) * number_regs_used[REGNO_NUM];
439 const size_t size_s = sizeof (STRING*) * number_regs_used[REGNO_STR];
440 const size_t size_p = sizeof (PMC*) * number_regs_used[REGNO_PMC];
442 const size_t size_nip = size_n + size_i + size_p;
443 const size_t all_regs_size = size_n + size_i + size_p + size_s;
444 const size_t reg_alloc = ROUND_ALLOC_SIZE(all_regs_size);
445 const int slot = CALCULATE_SLOT_NUM(reg_alloc);
447 /* this gets attached to the context, which should free it */
448 INTVAL * const n_regs_used = mem_allocate_n_zeroed_typed(4, INTVAL);
449 n_regs_used[REGNO_INT] = number_regs_used[REGNO_INT];
450 n_regs_used[REGNO_NUM] = number_regs_used[REGNO_NUM];
451 n_regs_used[REGNO_STR] = number_regs_used[REGNO_STR];
452 n_regs_used[REGNO_PMC] = number_regs_used[REGNO_PMC];
455 * If slot is beyond the end of the allocated list, extend the list to
456 * allocate more slots.
458 if (slot >= interp->ctx_mem.n_free_slots) {
459 const int extend_size = slot + 1;
460 int i;
462 mem_realloc_n_typed(interp->ctx_mem.free_list, extend_size, void*);
463 for (i = interp->ctx_mem.n_free_slots; i < extend_size; ++i)
464 interp->ctx_mem.free_list[i] = NULL;
465 interp->ctx_mem.n_free_slots = extend_size;
469 * The free_list contains a linked list of pointers for each size (slot
470 * index). Pop off an available context of the desired size from free_list.
471 * If no contexts of the desired size are available, allocate a new one.
473 ptr = interp->ctx_mem.free_list[slot];
474 old = CONTEXT(interp);
476 if (ptr) {
478 * Store the next pointer from the linked list for this size (slot
479 * index) in free_list. On "*(void **) ptr", C won't dereference a void
480 * * pointer (untyped), so type cast ptr to void** (a dereference-able
481 * type) then dereference it to get a void*. Store the dereferenced
482 * value (the next pointer in the linked list) in free_list.
484 interp->ctx_mem.free_list[slot] = *(void **) ptr;
486 else {
487 const size_t to_alloc = reg_alloc + ALIGNED_CTX_SIZE;
488 if (old)
489 ptr = mem_sys_allocate(to_alloc);
490 else
491 ptr = mem_sys_allocate_zeroed(to_alloc);
494 #if CTX_LEAK_DEBUG
495 if (Interp_debug_TEST(interp, PARROT_CTX_DESTROY_DEBUG_FLAG)) {
496 fprintf(stderr, "[alloc ctx %p]\n", ptr);
498 #endif
500 CONTEXT(interp) = ctx = (Parrot_Context *)ptr;
502 ctx->regs_mem_size = reg_alloc;
503 ctx->n_regs_used = n_regs_used;
505 /* regs start past the context */
506 p = (void *) ((char *)ptr + ALIGNED_CTX_SIZE);
508 /* ctx.bp points to I0, which has Nx on the left */
509 interp->ctx.bp.regs_i = (INTVAL*)((char*)p + size_n);
511 /* ctx.bp_ps points to S0, which has Px on the left */
512 interp->ctx.bp_ps.regs_s = (STRING**)((char*)p + size_nip);
513 init_context(interp, ctx, old);
515 return ctx;
520 =item C<void Parrot_free_context>
522 Free the context. If C<re_use> is true, this function is called by a
523 return continuation invoke, else from the destructor of a continuation.
525 =cut
529 PARROT_API
530 void
531 Parrot_free_context(PARROT_INTERP, ARGMOD(struct Parrot_Context *ctxp), int re_use)
534 * The context structure has a reference count, initially 0. This field is
535 * incremented when a continuation that points to it is created -- either
536 * directly, or when a continuation is cloned, or when a retcontinuation is
537 * converted to a full continuation in invalidate_retc. To check for leaks,
538 * (a) disable NDEBUG, (b) enable CTX_LEAK_DEBUG in interpreter.h, and (c)
539 * excecute "debug 0x80" in a (preferably small) test case.
542 if (re_use || --ctxp->ref_count <= 0) {
543 void *ptr;
544 int slot;
546 #ifndef NDEBUG
547 if (Interp_debug_TEST(interp, PARROT_CTX_DESTROY_DEBUG_FLAG)
548 && ctxp->current_sub) {
549 /* can't probably PIO_eprintf here */
550 const Parrot_sub * const doomed = PMC_sub(ctxp->current_sub);
552 if (doomed) {
553 fprintf(stderr, "[free ctx %p of sub '%s']\n",
554 (void *)ctxp,
555 (doomed->name == (void*)0xdeadbeef
556 ? "???"
557 : (char*)doomed->name->strstart));
559 else {
560 real_exception(interp, NULL, 1,
561 "NULL doomed sub detected in Parrot_free_context");
564 #endif
565 if (ctxp->n_regs_used) {
566 mem_sys_free(ctxp->n_regs_used);
567 ctxp->n_regs_used = NULL;
570 /* don't put the same context on the free list multiple times; we don't
571 * have the re-use versus multiple ref count semantics right yet */
572 if (ctxp->ref_count < 0)
573 return;
575 ctxp->ref_count = 0;
576 ptr = ctxp;
577 slot = CALCULATE_SLOT_NUM(ctxp->regs_mem_size);
579 PARROT_ASSERT(slot < interp->ctx_mem.n_free_slots);
580 *(void **)ptr = interp->ctx_mem.free_list[slot];
581 interp->ctx_mem.free_list[slot] = ptr;
587 =item C<void Parrot_set_context_threshold>
589 Mark the context as possible threshold.
591 =cut
595 PARROT_API
596 void
597 Parrot_set_context_threshold(SHIM_INTERP, SHIM(struct Parrot_Context *ctxp))
599 /* nothing to do */
604 =back
606 =head2 Register Stack Functions
608 =over 4
610 =cut
612 =item C<void Parrot_clear_i>
614 RT#48260: Not yet documented!!!
616 =cut
620 PARROT_API
621 void
622 Parrot_clear_i(PARROT_INTERP)
624 int i;
625 for (i = 0; i < CONTEXT(interp)->n_regs_used[REGNO_INT]; ++i)
626 REG_INT(interp, i) = 0;
631 =item C<void Parrot_clear_s>
633 RT#48260: Not yet documented!!!
635 =cut
639 PARROT_API
640 void
641 Parrot_clear_s(PARROT_INTERP)
643 int i;
644 for (i = 0; i < CONTEXT(interp)->n_regs_used[REGNO_STR]; ++i)
645 REG_STR(interp, i) = NULL;
650 =item C<void Parrot_clear_p>
652 RT#48260: Not yet documented!!!
654 =cut
658 PARROT_API
659 void
660 Parrot_clear_p(PARROT_INTERP)
662 int i;
663 for (i = 0; i < CONTEXT(interp)->n_regs_used[REGNO_PMC]; ++i)
664 REG_PMC(interp, i) = PMCNULL;
669 =item C<void Parrot_clear_n>
671 RT#48260: Not yet documented!!!
673 =cut
677 PARROT_API
678 void
679 Parrot_clear_n(PARROT_INTERP)
681 int i;
682 for (i = 0; i < CONTEXT(interp)->n_regs_used[REGNO_NUM]; ++i)
683 REG_NUM(interp, i) = 0.0;
689 =back
691 =head1 SEE ALSO
693 F<include/parrot/register.h> and F<src/stacks.c>.
695 =cut
701 * Local variables:
702 * c-file-style: "parrot"
703 * End:
704 * vim: expandtab shiftwidth=4: