2 Copyright (C) 2001-2008, The Perl Foundation.
7 src/gc/register.c - Register handling routines
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>).
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)
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)
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 +----------++----+------+------------+----+
55 Registers are addressed as usual via the register base pointer ctx.bp.
57 The macro CONTEXT() hides these details
64 =head2 Context and register frame allocation
66 There are two allocation strategies: chunked memory and malloced
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.
83 Context/register memory is malloced. C<ctx_mem.free> is used as a free
84 list of reusable items.
90 #define CTX_ALLOC_SIZE 0x20000
92 #define ALIGNED_CTX_SIZE (((sizeof (struct Parrot_Context) + NUMVAL_SIZE - 1) \
93 / NUMVAL_SIZE) * NUMVAL_SIZE)
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
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)
117 # error "Non-working code removed."
122 =head2 Context and Register Allocation Functions
132 =item C<void destroy_context>
134 Free allocated context memory
141 destroy_context(PARROT_INTERP
)
144 Parrot_Context
*context
;
146 /* clear active contexts all the way back to the initial context */
147 context
= CONTEXT(interp
);
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
);
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
];
162 void * const next
= *(void **) ptr
;
167 mem_sys_free(interp
->ctx_mem
.free_list
);
172 =item C<void create_initial_context>
174 Create initial interpreter context.
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
);
200 =item C<void parrot_gc_context>
202 Cleanup dead context memory. Called by the garbage collector.
210 parrot_gc_context(PARROT_INTERP
)
213 parrot_context_t ctx
;
215 if (!interp
->ctx_mem
.threshold
)
217 LVALUE_CAST(char *, ctx
.bp
) = interp
->ctx_mem
.threshold
-
218 sizeof (parrot_regs_t
);
227 =item C<static void clear_regs>
229 RT#48260: Not yet documented!!!
236 clear_regs(PARROT_INTERP
, ARGMOD(parrot_context_t
*ctx
))
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;
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!!!
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;
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
;
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
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
))
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
];
337 interp
->ctx_mem
.free_list
[slot
] = *(void **) ptr
;
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
);
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>.
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
;
381 ctx
->current_sub
= old
->current_sub
;
389 =item C<void Parrot_pop_context>
391 Free the context created with C<Parrot_push_context> and restore the previous
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.
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
;
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;
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
);
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
;
487 const size_t to_alloc
= reg_alloc
+ ALIGNED_CTX_SIZE
;
489 ptr
= mem_sys_allocate(to_alloc
);
491 ptr
= mem_sys_allocate_zeroed(to_alloc
);
495 if (Interp_debug_TEST(interp
, PARROT_CTX_DESTROY_DEBUG_FLAG
)) {
496 fprintf(stderr
, "[alloc ctx %p]\n", ptr
);
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
);
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.
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) {
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
);
553 fprintf(stderr
, "[free ctx %p of sub '%s']\n",
555 (doomed
->name
== (void*)0xdeadbeef
557 : (char*)doomed
->name
->strstart
));
560 real_exception(interp
, NULL
, 1,
561 "NULL doomed sub detected in Parrot_free_context");
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)
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.
597 Parrot_set_context_threshold(SHIM_INTERP
, SHIM(struct Parrot_Context
*ctxp
))
606 =head2 Register Stack Functions
612 =item C<void Parrot_clear_i>
614 RT#48260: Not yet documented!!!
622 Parrot_clear_i(PARROT_INTERP
)
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!!!
641 Parrot_clear_s(PARROT_INTERP
)
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!!!
660 Parrot_clear_p(PARROT_INTERP
)
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!!!
679 Parrot_clear_n(PARROT_INTERP
)
682 for (i
= 0; i
< CONTEXT(interp
)->n_regs_used
[REGNO_NUM
]; ++i
)
683 REG_NUM(interp
, i
) = 0.0;
693 F<include/parrot/register.h> and F<src/stacks.c>.
702 * c-file-style: "parrot"
704 * vim: expandtab shiftwidth=4: