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;
296 ctx
->handlers
= PMCNULL
;
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
;
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
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
))
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
];
338 interp
->ctx_mem
.free_list
[slot
] = *(void **) ptr
;
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
);
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>.
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
;
382 ctx
->current_sub
= old
->current_sub
;
390 =item C<void Parrot_pop_context>
392 Free the context created with C<Parrot_push_context> and restore the previous
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.
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
;
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;
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
);
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
;
488 const size_t to_alloc
= reg_alloc
+ ALIGNED_CTX_SIZE
;
490 ptr
= mem_sys_allocate(to_alloc
);
492 ptr
= mem_sys_allocate_zeroed(to_alloc
);
496 if (Interp_debug_TEST(interp
, PARROT_CTX_DESTROY_DEBUG_FLAG
)) {
497 fprintf(stderr
, "[alloc ctx %p]\n", ptr
);
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
);
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.
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) {
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
);
554 fprintf(stderr
, "[free ctx %p of sub '%s']\n",
556 (doomed
->name
== (void*)0xdeadbeef
558 : (char*)doomed
->name
->strstart
));
561 Parrot_ex_throw_from_c_args(interp
, NULL
, 1,
562 "NULL doomed sub detected in Parrot_free_context");
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)
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.
598 Parrot_set_context_threshold(SHIM_INTERP
, SHIM(struct Parrot_Context
*ctxp
))
607 =head2 Register Stack Functions
613 =item C<void Parrot_clear_i>
615 RT #48260: Not yet documented!!!
623 Parrot_clear_i(PARROT_INTERP
)
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!!!
642 Parrot_clear_s(PARROT_INTERP
)
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!!!
661 Parrot_clear_p(PARROT_INTERP
)
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!!!
680 Parrot_clear_n(PARROT_INTERP
)
683 for (i
= 0; i
< CONTEXT(interp
)->n_regs_used
[REGNO_NUM
]; ++i
)
684 REG_NUM(interp
, i
) = 0.0;
694 F<include/parrot/register.h> and F<src/stacks.c>.
703 * c-file-style: "parrot"
705 * vim: expandtab shiftwidth=4: