Author: leo
Date: Wed Nov  9 06:08:36 2005
New Revision: 9853

Modified:
   trunk/classes/sub.pmc
   trunk/include/parrot/inter_call.h
   trunk/include/parrot/interpreter.h
   trunk/include/parrot/register.h
   trunk/src/inter_call.c
   trunk/src/register.c
   trunk/t/pmc/object-meths.t
Log:
Variable-sized reg frames again - redo tailcall argument passing

* reallocating the whole context for tailcalls is wrong, as
  continuations can point to the context structure
* follow Plan C: allocate a new register area only and use normal
  argument passsing code (almost)
  + no problem with overlapping memory area
  - additional allocation
* can be heavily optimized for recursive tailcalls

This should make tcl/t/cmd_array.t more happy, albeit there is still some
bogus mark_context() reported by valgrind.


Modified: trunk/classes/sub.pmc
==============================================================================
--- trunk/classes/sub.pmc       (original)
+++ trunk/classes/sub.pmc       Wed Nov  9 06:08:36 2005
@@ -161,7 +161,7 @@ Sets the name of the subroutine.
 
 =item C<void set_pointer(void *value)>
 
-Sets the pointer to the actual subroutine. 
+Sets the pointer to the actual subroutine.
 
 *** Don't use that - use .Sub constants instead ***
 

Modified: trunk/include/parrot/inter_call.h
==============================================================================
--- trunk/include/parrot/inter_call.h   (original)
+++ trunk/include/parrot/inter_call.h   Wed Nov  9 06:08:36 2005
@@ -17,8 +17,7 @@
 enum call_state_mode {
     CALL_STATE_SIG        =  0x001,     /* runops, nci */
     CALL_STATE_OP         =  0x002,     /* get_, set_ ops */
-    CALL_STATE_TC         =  0x004,     /* taicall, fetch from tc_args */
-    CALL_STATE_MASK       =  0x007,
+    CALL_STATE_MASK       =  0x003,
 
     CALL_STATE_FLATTEN    =  0x010,
 
@@ -36,7 +35,6 @@ struct call_state_1 {
             opcode_t *pc;
             PMC *signature;
         } op;
-        HashEntry *tc_args;     /* tailcall arg store - a typed union */
     } u;
     struct PackFile_Constant **constants;
     parrot_context_t *ctx;

Modified: trunk/include/parrot/interpreter.h
==============================================================================
--- trunk/include/parrot/interpreter.h  (original)
+++ trunk/include/parrot/interpreter.h  Wed Nov  9 06:08:36 2005
@@ -172,6 +172,7 @@ typedef struct Parrot_Context {
     Regs_ni                bp;          /* pointers to FLOATVAL & INTVAL */
     Regs_ps                bp_ps;       /* pointers to PMC & STR */
     /* end common header */
+    void  *malloced_mem;                /* used during resize */
     INTVAL n_regs_used[4];             /* INSP in PBC */
     size_t regs_mem_size;               /* memory occupied by registers */
     INTVAL ref_count;                   /* how often refered to */

Modified: trunk/include/parrot/register.h
==============================================================================
--- trunk/include/parrot/register.h     (original)
+++ trunk/include/parrot/register.h     Wed Nov  9 06:08:36 2005
@@ -75,7 +75,7 @@ void Parrot_clear_n(Interp *);
 
 struct Parrot_Context;        /* parrot/interpreter.h */
 void Parrot_alloc_context(Interp *, INTVAL *n_regs_used);
-void Parrot_realloc_context(Interp *, INTVAL *n_regs_used);
+void * Parrot_realloc_context(Interp *, INTVAL *n_regs_used);
 void Parrot_free_context(Interp *, struct Parrot_Context *, int re_use);
 void Parrot_set_context_threshold(Interp *, struct Parrot_Context *);
 void parrot_gc_context(Interp *);

Modified: trunk/src/inter_call.c
==============================================================================
--- trunk/src/inter_call.c      (original)
+++ trunk/src/inter_call.c      Wed Nov  9 06:08:36 2005
@@ -315,9 +315,6 @@ next_arg(Interp *interpreter, struct cal
                     st->sig = PARROT_ARG_PMC; break;
             }
             break;
-        case CALL_STATE_TC:
-            st->sig = st->u.tc_args[st->i].type;
-            break;
     }
     return 1;
 }
@@ -350,14 +347,6 @@ fetch_arg(Interp *interpreter, struct ca
                     return fetch_arg_pmc_sig(interpreter, st);
             }
             break;
-        case CALL_STATE_TC:
-            {
-                HashEntry *e =  st->src.u.tc_args + st->src.i;
-                st->val = e->val;
-                st->src.sig = e->type;
-                st->src.mode |= CALL_STATE_NEXT_ARG;
-            }
-            break;
     }
     return 0;
 }
@@ -682,59 +671,34 @@ parrot_pass_args_tail_call(Interp* inter
 {
     struct call_state st;
     int todo, i, n;
+    struct Interp_Context src_ctx;
+    void *old_mem;
 
     if (*pc != PARROT_OP_get_params_pc)
         return pc;
-    todo = Parrot_init_arg_op(interpreter, interpreter->code,
+    Parrot_init_arg_op(interpreter, interpreter->code,
             CONTEXT(interpreter->ctx),
             interpreter->current_args, &st.src);
-    if (!todo)
-        return pc;
-    todo = Parrot_init_arg_op(interpreter, dest_sub->seg,
-            CONTEXT(interpreter->ctx),
-            pc, &st.dest);
-    if (!todo)
-        return pc;
-    /* allocate helper storage
-     * due to flatten, we need max(src, dest)
+    /* remember old register base ptrs */
+    memcpy(&src_ctx, &interpreter->ctx, sizeof(struct Interp_Context));
+    /*
+     * TODO optimize recursive tail calls
      */
-    n = st.src.n;
-    if (st.dest.n > n)
-        n = st.dest.n;
-    st.dest.u.tc_args = mem_sys_allocate(n * sizeof(HashEntry));
-    /* fetch args */
-
-    st.dest.mode = CALL_STATE_TC;
-    for (i = 0;  ; ++i) {
-        if (!Parrot_fetch_arg(interpreter, &st))
-            break;
-        assert(i < n);
-        st.dest.u.tc_args[i].val = st.val;
-        st.dest.u.tc_args[i].type = st.src.sig;
-    }
-    /* now set new register structure, realloc if needed */
-    Parrot_realloc_context(interpreter, dest_sub->n_regs_used);
-    /* ctx might have moved */
-    st.dest.ctx = CONTEXT(interpreter->ctx);
-    /* and store args */
-
-    st.src.i = 0;
-    st.src.n = i;
-    st.opt_so_far = 0;
-    st.src.mode = CALL_STATE_TC;
-    st.src.u.tc_args = st.dest.u.tc_args;
-    /* reinit dest */
-    Parrot_init_arg_op(interpreter, dest_sub->seg,
+    old_mem = Parrot_realloc_context(interpreter, dest_sub->n_regs_used);
+    todo = Parrot_init_arg_op(interpreter, dest_sub->seg,
             CONTEXT(interpreter->ctx),
             pc, &st.dest);
-    for (;;) {
+    /* reset st.src.ctx pointing to old regs */
+    st.src.ctx = (parrot_context_t *) &src_ctx;
+    st.opt_so_far = 0;  /* XXX */
+    while (todo) {
         Parrot_fetch_arg(interpreter, &st);
         Parrot_convert_arg(interpreter, &st);
-        if (!Parrot_store_arg(interpreter, &st))
-            break;
+        todo = Parrot_store_arg(interpreter, &st);
     }
-    /* free helper */
-    mem_sys_free(st.src.u.tc_args);
+    if (old_mem)
+        mem_sys_free(old_mem);
+
     /* done. return position past get_params opcode */
     return pc + st.dest.n + 2;
 }

Modified: trunk/src/register.c
==============================================================================
--- trunk/src/register.c        (original)
+++ trunk/src/register.c        Wed Nov  9 06:08:36 2005
@@ -214,14 +214,10 @@ parrot_gc_context(Interp *interpreter)
 }
 
 static void
-init_context(Interp *interpreter, parrot_context_t *ctx)
+clear_regs(Interp *interpreter, parrot_context_t *ctx)
 {
     int i;
 
-    ctx->ref_count = 0;
-    ctx->current_results = NULL;
-    ctx->current_args = NULL;
-
     /* NULL out registers
      *
      * if the architecture has 0x := NULL and 0.0 we could memset too
@@ -259,6 +255,16 @@ init_context(Interp *interpreter, parrot
 #endif
 }
 
+static void
+init_context(Interp *interpreter, parrot_context_t *ctx)
+{
+    ctx->ref_count = 0;
+    ctx->current_results = NULL;
+    ctx->current_args = NULL;
+    ctx->malloced_mem = NULL;
+    clear_regs(interpreter, ctx);
+}
+
 #if CHUNKED_CTX_MEM
 void
 Parrot_alloc_context(Interp *interpreter, INTVAL *n_regs_used)
@@ -422,13 +428,13 @@ Parrot_alloc_context(Interp *interpreter
     init_context(interpreter, ctx);
 }
 
-void
+void *
 Parrot_realloc_context(Interp *interpreter, INTVAL *n_regs_used)
 {
     struct Parrot_Context *ctx;
     size_t to_alloc, reg_alloc, size_n, size_nip;
-    void *p;
-    int i, slot;
+    void *p, *old_mem;
+    int i;
 
     size_n = sizeof(FLOATVAL) * n_regs_used[REGNO_NUM];
     size_nip = size_n +
@@ -437,32 +443,30 @@ Parrot_realloc_context(Interp *interpret
     reg_alloc = size_nip +
         sizeof(STRING*) *  n_regs_used[REGNO_STR];
 
-    slot = (reg_alloc + 7) >> 3;
-    reg_alloc = slot << 3;
     ctx = CONTEXT(interpreter->ctx);
-    /* need a bigger one? */
-    if (reg_alloc > ctx->regs_mem_size) {
-        CONTEXT(interpreter->ctx) =
-            ctx = mem_sys_realloc(ctx, reg_alloc + ALIGNED_CTX_SIZE);
-        ctx->regs_mem_size = reg_alloc;
-        /* if we realloced beyond the free_list, resize that too */
-        if (slot >= interpreter->ctx_mem.n_free_slots) {
-            int n = slot + 1;
-            interpreter->ctx_mem.free_list = mem_sys_realloc(
-                    interpreter->ctx_mem.free_list, n * sizeof(void*));
-            for (i = interpreter->ctx_mem.n_free_slots; i < n; ++i)
-                interpreter->ctx_mem.free_list[i] = NULL;
-            interpreter->ctx_mem.n_free_slots = n;
-        }
-    }
+    /* Need a distinct reg memory area, but we can't reallocate the
+     * context as a whole, because continuations might point to it.
+     * Therefore we just allocate the register memory and remember
+     * this in the malloced_mem pointer
+     *
+     * TODO investigate if we should preserve the original slot
+     *      so that we can reuse the context memory in _free
+     *
+     * If this is a recursive tailcall, we need the old memory
+     * which is freed after argument passing. See also
+     * src/inter_call.c
+     */
+    old_mem = ctx->malloced_mem;
+    ctx->malloced_mem = p = mem_sys_allocate(reg_alloc);
+    ctx->regs_mem_size = reg_alloc;
     for (i = 0; i < 4; ++i)
         ctx->n_regs_used[i] = n_regs_used[i];
-    p = (void *) ((char *)ctx + ALIGNED_CTX_SIZE);
     /* ctx.bp points to I0, which has Nx at left */
     interpreter->ctx.bp.regs_i = (INTVAL*)((char*)p + size_n);
     /* this points to S0 */
     interpreter->ctx.bp_ps.regs_s = (STRING**)((char*)p + size_nip);
-    init_context(interpreter, ctx);
+    clear_regs(interpreter, ctx);
+    return old_mem;
 }
 
 void
@@ -481,12 +485,20 @@ Parrot_free_context(Interp *interpreter,
      *
      */
     if (re_use || --ctxp->ref_count == 0) {
-        ptr = ctxp;
-        slot = ctxp->regs_mem_size >> 3;
+        if (ctxp->malloced_mem) {
+            /* we don't have the orig size anymore, just free all
+            */
+            mem_sys_free(ctxp->malloced_mem);
+            mem_sys_free(ctxp);
+        }
+        else {
+            ptr = ctxp;
+            slot = ctxp->regs_mem_size >> 3;
 
-        assert(slot < interpreter->ctx_mem.n_free_slots);
-        *(void **)ptr = interpreter->ctx_mem.free_list[slot];
-        interpreter->ctx_mem.free_list[slot] = ptr;
+            assert(slot < interpreter->ctx_mem.n_free_slots);
+            *(void **)ptr = interpreter->ctx_mem.free_list[slot];
+            interpreter->ctx_mem.free_list[slot] = ptr;
+        }
 #if CTX_LEAK_DEBUG
         fprintf(stderr, "free  %p\n", ctxp);
 #endif

Modified: trunk/t/pmc/object-meths.t
==============================================================================
--- trunk/t/pmc/object-meths.t  (original)
+++ trunk/t/pmc/object-meths.t  Wed Nov  9 06:08:36 2005
@@ -912,7 +912,7 @@ pir_output_is(<<'CODE', <<'OUTPUT', "tai
     n = getattribute self, "Foo\0n"
     dec n
     unless n goto done
-    tailcallmethod self, "go"
+    .return self."go"()
 done:
 .end
 CODE

Reply via email to