The attached patch creates a C<return_stack> in C<Parrot_Context> for
the exclusive use of the C<bsr/jsr/ret> ops.  There is a minor boost in
functionality (i.e. C<push_eh> and C<clear_eh> no longer have to nest
with respect to C<bsr> and C<ret> because the patch gives them different
stacks), but the real reason for wanting to do this is to get the return
addresses out of the C<control_stack>, so that it only has entries that
are dynamically scoped.

   The C<return_stack> is not initialized until it is needed, which is
optimal for the majority of Parrotcode that doesn't use this feature,
but makes the implementation somewhat uglier (IMHO, anyway).  I tried to
initialize C<return_stack> along with the other context stuff, but
couldn't get it to fly.  Presumably this is because context
initialization happens before pool initialization, and pools are
required for C<Stack_Chunk> allocation.  If that is correct, and
C<Stack_Chunk> objects are truly managed by GC (something that only
dawned on me just now), then my comments about C<stack_destroy> are off
the mark [1], but the patch is probably OK otherwise.  Is that right?

   However, since C<return_stack> entries always contain a
STACK_ENTRY_DESTINATION with STACK_CLEANUP_NULL, and are only ever
referenced from their owning context, using a C<Stack_Chunk> to store
them is way overkill.  Would it be better to design something more
specific to return addresses so that it can be lighter in weight?

   One final note:  You will need to do "make clean" after applying this
patch, as otherwise changing include/parrot/interpreter.h does not force
anything in src/dynpmc/ or src/dynoplibs/ to be recompiled.  It would be
great if somebody who understands the build system (i.e. better than I)
could investigate.  Thanks,

                                        -- Bob Rogers
                                           http://rgrjr.dyndns.org/

[1]  Probably off the sweep as well.

Diffs between last version checked in and current workfile(s):

Index: include/parrot/interpreter.h
===================================================================
--- include/parrot/interpreter.h        (revision 14695)
+++ include/parrot/interpreter.h        (working copy)
@@ -213,6 +213,7 @@
 
     struct Stack_Chunk *user_stack;     /* Base of the scratch stack */
     struct Stack_Chunk *control_stack;  /* Base of the flow control stack */
+    struct Stack_Chunk *return_stack;   /* Base of the bsr/jsr/ret stack */
     PMC      *lex_pad;                  /* LexPad PMC */
     struct Parrot_Context *outer_ctx;   /* outer context, if a closure */
     UINTVAL warns;             /* Keeps track of what warnings
Index: src/register.c
===================================================================
--- src/register.c      (revision 14695)
+++ src/register.c      (working copy)
@@ -234,6 +234,9 @@
     ctx->current_method = NULL; /* XXX who clears it? */
     ctx->current_object = NULL; /* XXX who clears it?  */
     ctx->current_HLL = 0;
+    /* each context gets its own return stack, but we may not be able to
+       initialize it yet if we're still in create_initial_context. */
+    ctx->return_stack = NULL;
     if (old) {
         /* some items should better be COW copied */
         ctx->constants = old->constants;
@@ -393,6 +396,12 @@
         ptr = ctxp;
         slot = ctxp->regs_mem_size >> 3;
 
+        if (ctxp->return_stack) {
+            /* [stack_destroy is currently a noop.  -- rgr, 23-Sep-06.] */
+            stack_destroy(ctxp->return_stack);
+            ctxp->return_stack = NULL;
+        }
+
         assert(slot < interpreter->ctx_mem.n_free_slots);
         *(void **)ptr = interpreter->ctx_mem.free_list[slot];
         interpreter->ctx_mem.free_list[slot] = ptr;
Index: src/inter_create.c
===================================================================
--- src/inter_create.c  (revision 14695)
+++ src/inter_create.c  (working copy)
@@ -416,8 +416,13 @@
     /* deinit op_lib */
     (void) PARROT_CORE_OPLIB_INIT(0);
 
+    /* [stack_destroy is currently a noop, otherwise these would probably fail,
+       as the entries are shared with other contexts.  -- rgr, 23-Sep-06.]
+    */
     stack_destroy(CONTEXT(interpreter->ctx)->user_stack);
     stack_destroy(CONTEXT(interpreter->ctx)->control_stack);
+    if (CONTEXT(interpreter->ctx)->control_stack)
+        stack_destroy(CONTEXT(interpreter->ctx)->control_stack);
 
     destroy_context(interpreter);
 
Index: src/ops/core.ops
===================================================================
--- src/ops/core.ops    (revision 14695)
+++ src/ops/core.ops    (working copy)
@@ -203,7 +203,13 @@
 =cut
 
 inline op bsr (label INT) :base_core,check_event {
-  stack_push(interpreter, &CONTEXT(interpreter->ctx)->control_stack, 
+  struct Parrot_Context *context = CONTEXT(interpreter->ctx);
+  if (! context->return_stack) {
+      /* this is necessary in order to work around strange context
+        initiation rites.  -- rgr, 21-Sep-06. */
+      context->return_stack = new_stack(interpreter, "Return");
+  }
+  stack_push(interpreter, &context->return_stack, 
              expr NEXT(),  STACK_ENTRY_DESTINATION, STACK_CLEANUP_NULL);
   goto OFFSET($1);
 }
@@ -230,7 +236,13 @@
 
 inline op jsr(label INT) :base_core,check_event {
   opcode_t * loc;
-  stack_push(interpreter, &CONTEXT(interpreter->ctx)->control_stack, 
+  struct Parrot_Context *context = CONTEXT(interpreter->ctx);
+  if (! context->return_stack) {
+      /* this is necessary in order to work around strange context
+        initiation rites.  -- rgr, 21-Sep-06. */
+      context->return_stack = new_stack(interpreter, "Return");
+  }
+  stack_push(interpreter, &context->return_stack,
              expr NEXT(),  STACK_ENTRY_DESTINATION, STACK_CLEANUP_NULL);
   loc = INTVAL2PTR(opcode_t *, $1);
   goto ADDRESS(loc);
Index: src/stacks.c
===================================================================
--- src/stacks.c        (revision 14695)
+++ src/stacks.c        (working copy)
@@ -363,7 +363,13 @@
     /* We don't mind the extra call, so we do this: (previous comment
      * said we *do* mind, but I say let the compiler decide) */
     void *dest;
-    (void)stack_pop(interpreter, &CONTEXT(interpreter->ctx)->control_stack,
+    struct Parrot_Context *context = CONTEXT(interpreter->ctx);
+
+    if (! context->return_stack) {
+        real_exception(interpreter, NULL, INVALID_OPERATION,
+                       "Return address stack is empty.");
+    }
+    (void)stack_pop(interpreter, &CONTEXT(interpreter->ctx)->return_stack,
                     &dest, STACK_ENTRY_DESTINATION);
     return dest;
 }
Index: src/sub.c
===================================================================
--- src/sub.c   (revision 14695)
+++ src/sub.c   (working copy)
@@ -40,6 +40,8 @@
 
     mark_stack(interpreter, ctx->user_stack);
     mark_stack(interpreter, ctx->control_stack);
+    if (ctx->return_stack)
+        mark_stack(interpreter, ctx->return_stack);
     mark_register_stack(interpreter, ctx->reg_stack);
     obj = (PObj*)ctx->current_sub;
     if (obj)

End of diffs.

Reply via email to