I tried to apply Bram's patch from last year, but it had a lot of fuzz 
(especially related to the removal of register stacks).  Here's what I ended 
up with.

It doesn't work, so I'm not applying it as it is... but if there's still a 
problem, this might be a better starting point.

-- c

=== include/parrot/interpreter.h
==================================================================
--- include/parrot/interpreter.h	(revision 28361)
+++ include/parrot/interpreter.h	(local)
@@ -279,7 +279,6 @@
 #define CHUNKED_CTX_MEM 0           /* no longer works, but will be reinstated
                                      * some day; see src/register.c for details.
                                     */
-
 typedef struct _context_mem {
 #if CHUNKED_CTX_MEM
     char *data;                     /* ctx + register store */
@@ -410,6 +409,10 @@
     opcode_t *current_args;                   /* ptr into code w/ set_args op */
     opcode_t *current_params;                 /* ... w/ get_params op */
     opcode_t *current_returns;                /* ... w/ get_returns op */
+
+    parrot_context_t *fromc_result_ctx;       /* ptr to context of set_returns,
+                                               * if we're about to pass return
+                                               * values to C */
     PMC      *args_signature;                 /* non-const args signature PMC */
     PMC      *params_signature;               /* non-const params sig PMC     */
     PMC      *returns_signature;              /* non-const returns sig PMC    */
=== include/parrot/register.h
==================================================================
--- include/parrot/register.h	(revision 28361)
+++ include/parrot/register.h	(local)
@@ -108,6 +108,15 @@
 void Parrot_set_context_threshold(SHIM_INTERP,
     SHIM(struct Parrot_Context *ctxp));
 
+void copy_context_registers(PARROT_INTERP,
+    ARGMOD(struct Parrot_Context *dest),
+    ARGMOD(struct Parrot_Context *src))
+        __attribute__nonnull__(1)
+        __attribute__nonnull__(2)
+        __attribute__nonnull__(3)
+        FUNC_MODIFIES(*dest)
+        FUNC_MODIFIES(*src);
+
 void create_initial_context(PARROT_INTERP)
         __attribute__nonnull__(1);
 
=== src/gc/register.c
==================================================================
--- src/gc/register.c	(revision 28361)
+++ src/gc/register.c	(local)
@@ -25,14 +25,14 @@
 /* HEADERIZER BEGIN: static */
 /* Don't modify between HEADERIZER BEGIN / HEADERIZER END.  Your changes will be lost. */
 
-static void clear_regs(PARROT_INTERP, ARGMOD(parrot_context_t *ctx))
+static void clear_regs(PARROT_INTERP, ARGMOD(Parrot_Context *ctx))
         __attribute__nonnull__(1)
         __attribute__nonnull__(2)
         FUNC_MODIFIES(*ctx);
 
 static void init_context(PARROT_INTERP,
-    ARGMOD(parrot_context_t *ctx),
-    ARGIN_NULLOK(const parrot_context_t *old))
+    ARGMOD(Parrot_Context *ctx),
+    ARGIN_NULLOK(const Parrot_Context *old))
         __attribute__nonnull__(1)
         __attribute__nonnull__(2)
         FUNC_MODIFIES(*ctx);
@@ -210,7 +210,7 @@
 parrot_gc_context(PARROT_INTERP)
 {
 #if CHUNKED_CTX_MEM
-    parrot_context_t ctx;
+    Parrot_context_t ctx;
 
     if (!interp->ctx_mem.threshold)
         return;
@@ -233,7 +233,7 @@
 */
 
 static void
-clear_regs(PARROT_INTERP, ARGMOD(parrot_context_t *ctx))
+clear_regs(PARROT_INTERP, ARGMOD(Parrot_Context *ctx))
 {
     int i;
 
@@ -282,8 +282,8 @@
 */
 
 static void
-init_context(PARROT_INTERP, ARGMOD(parrot_context_t *ctx),
-        ARGIN_NULLOK(const parrot_context_t *old))
+init_context(PARROT_INTERP, ARGMOD(Parrot_Context *ctx),
+        ARGIN_NULLOK(const Parrot_Context *old))
 {
     ctx->ref_count = 0;                 /* RT#46191 1 - Exceptions !!! */
     ctx->current_results = NULL;
@@ -347,7 +347,7 @@
     ctx->n_regs_used[REGNO_NUM] = old->n_regs_used[REGNO_NUM];
     ctx->n_regs_used[REGNO_STR] = old->n_regs_used[REGNO_STR];
     ctx->n_regs_used[REGNO_PMC] = old->n_regs_used[REGNO_PMC];
-    diff                        = (const long *)ctx - (const long *) old;
+    diff                        = (INTVAL *)ctx - (INTVAL *) old;
 
     interp->ctx.bp.regs_i    += diff;
     interp->ctx.bp_ps.regs_s += diff;
@@ -577,6 +577,12 @@
         slot            = CALCULATE_SLOT_NUM(ctxp->regs_mem_size);
 
         PARROT_ASSERT(slot < interp->ctx_mem.n_free_slots);
+
+#ifndef NDEBUG
+        if (ptr == interp->ctx_mem.free_list[slot])
+            internal_exception(1,
+                "Error: tried to free an already-freed context\n" );
+#endif
         *(void **)ptr                   = interp->ctx_mem.free_list[slot];
         interp->ctx_mem.free_list[slot] = ptr;
     }
@@ -683,7 +689,47 @@
         REG_NUM(interp, i) = 0.0;
 }
 
+/*
 
+=item C<void copy_context_registers>
+
+Copy all registers from one context to another.
+
+=cut
+
+*/
+
+void
+copy_context_registers(PARROT_INTERP, ARGMOD(Parrot_Context *dest),
+    ARGMOD(Parrot_Context *src))
+{
+    int i;
+    int max = src->n_regs_used[REGNO_INT];
+
+    assert(max == dest->n_regs_used[REGNO_INT]);
+
+    for (i = 0; i < max; ++i)
+        CTX_REG_INT(dest, i) = CTX_REG_INT(src, i);
+
+    max = src->n_regs_used[REGNO_NUM];
+    assert(max == dest->n_regs_used[REGNO_NUM]);
+
+    for (i = 0; i < max; ++i)
+        CTX_REG_NUM(dest, i) = CTX_REG_NUM(src, i);
+
+    max = src->n_regs_used[REGNO_STR];
+    assert(max == dest->n_regs_used[REGNO_STR]);
+
+    for (i = 0; i < max; ++i)
+        CTX_REG_STR(dest, i) = CTX_REG_STR(src, i);
+
+    max = src->n_regs_used[REGNO_PMC];
+    assert(max == dest->n_regs_used[REGNO_PMC]);
+
+    for (i = 0; i < max; ++i)
+        CTX_REG_PMC(dest, i) = CTX_REG_PMC(src, i);
+}
+
 /*
 
 =back
=== src/inter_run.c
==================================================================
--- src/inter_run.c	(revision 28361)
+++ src/inter_run.c	(local)
@@ -53,6 +53,7 @@
 =item C<void runops>
 
 Run parrot ops. Set exception handler and/or resume after exception.
+This is the low level run ops routine that just takes an offset.
 
 =cut
 
@@ -149,7 +150,7 @@
     parrot_context_t *ctx;
 
     /* we need one return continuation with a NULL offset */
-    PMC * const ret_c = new_ret_continuation_pmc(interp, NULL);
+    PMC * const ret_c    = new_ret_continuation_pmc(interp, NULL);
     interp->current_cont = ret_c;
 #if defined GC_VERBOSE && GC_VERBOSE
     PObj_report_SET(ret_c);     /* s. also dod.c */
@@ -191,12 +192,19 @@
     const char *sig_p;
     parrot_context_t * const old_ctx = CONTEXT(interp);
 
-    interp->current_cont  = new_ret_continuation_pmc(interp, NULL);
+    interp->current_cont   = new_ret_continuation_pmc(interp, NULL);
     interp->current_object = obj;
+
+    /* call the invoke entry to get the address in the bytecode */
     dest = VTABLE_invoke(interp, sub, NULL);
-    if (!dest) {
+
+    if (!dest)
         real_exception(interp, NULL, 1, "Subroutine returned a NULL address");
-    }
+
+    /* Build the call signature. If we have an object, need to make sure we
+     * get an O as the first parameter (the final else branch does this).
+     * We always skip over the first character since that's the return type. */
+
     if (PMC_IS_NULL(obj)) {
         /* skip over the return type */
         sig_p = sig + 1;
@@ -215,6 +223,7 @@
         sig_p = new_sig;
     }
 
+    /* If we have arguments, do the passing of them. */
     if (*sig_p && dest[0] == PARROT_OP_get_params_pc) {
         dest = parrot_pass_args_fromc(interp, sig_p, dest, old_ctx, ap);
     }
@@ -227,9 +236,29 @@
      }
      */
 
-    ctx = CONTEXT(interp);
-    offset = dest - interp->code->base.data;
+    interp->fromc_result_ctx = 0;
+    ctx                      = CONTEXT(interp);
+    offset                   = dest - interp->code->base.data;
+
+    /* If set_returns was called somewhere, there is a copy of the context we
+     * need to return in interp->fromc_result_ctx. */
+    if (interp->fromc_result_ctx) {
+        ctx = interp->fromc_result_ctx;
+        /* Make sure it is freed by Parrot_free_context(.., .., 0)
+         * when finally returning to C. Non-copied contexts have a
+         * ref_count of 0 by now, so --ref_count will be -1 in
+         * Parrot_free_context, and -1 != 0 so non-copied contexts
+         * will not really be freed. This is good, as they have
+         * already been freed by RetContinuation->invoke. */
+        ctx->ref_count++;
+    }
+
+    /* Reset fromc_result_ctx for maybe an outer runloop */
+    interp->fromc_result_ctx = 0;
+
+    /* Hand back the context so we can get the args out of it. */
     runops(interp, offset);
+
     return ctx;
 }
 
@@ -296,11 +325,14 @@
 {
     va_list args;
     parrot_context_t *ctx;
+    PMC *retval;
 
     va_start(args, sig);
     ctx = runops_args(interp, sub, PMCNULL, NULL, sig, args);
     va_end(args);
-    return (PMC *)set_retval(interp, *sig, ctx);
+    retval = (PMC *)set_retval(interp, *sig, ctx);
+    Parrot_free_context(interp, ctx, 0);
+    return retval;
 }
 
 /*
@@ -339,6 +371,7 @@
     ctx = runops_args(interp, sub, PMCNULL, NULL, sig, args);
     va_end(args);
     retval = set_retval(interp, *sig, ctx);
+    Parrot_free_context(interp, ctx, 0);
 
     interp->current_args     = cargs;
     interp->current_params   = params;
@@ -365,11 +398,14 @@
 {
     va_list args;
     parrot_context_t *ctx;
+    INTVAL retval;
 
     va_start(args, sig);
     ctx = runops_args(interp, sub, PMCNULL, NULL, sig, args);
     va_end(args);
-    return set_retval_i(interp, *sig, ctx);
+    retval = set_retval_i(interp, *sig, ctx);
+    Parrot_free_context(interp, ctx, 0);
+    return retval;
 }
 
 /*
@@ -390,11 +426,14 @@
 {
     va_list args;
     parrot_context_t *ctx;
+    FLOATVAL retval;
 
     va_start(args, sig);
     ctx = runops_args(interp, sub, PMCNULL, NULL, sig, args);
     va_end(args);
-    return set_retval_f(interp, *sig, ctx);
+    retval = set_retval_f(interp, *sig, ctx);
+    Parrot_free_context(interp, ctx, 0);
+    return retval;
 }
 
 /*
@@ -416,11 +455,14 @@
 {
     va_list args;
     parrot_context_t *ctx;
+    void *retval;
 
     va_start(args, sig);
     ctx = runops_args(interp, sub, obj, meth, sig, args);
     va_end(args);
-    return set_retval(interp, *sig, ctx);
+    retval = set_retval(interp, *sig, ctx);
+    Parrot_free_context(interp, ctx, 0);
+    return retval;
 }
 
 /*
@@ -441,11 +483,14 @@
 {
     va_list args;
     parrot_context_t *ctx;
+    INTVAL retval;
 
     va_start(args, sig);
     ctx = runops_args(interp, sub, obj, meth, sig, args);
     va_end(args);
-    return set_retval_i(interp, *sig, ctx);
+    retval = set_retval_i(interp, *sig, ctx);
+    Parrot_free_context(interp, ctx, 0);
+    return retval;
 }
 
 /*
@@ -466,11 +511,14 @@
 {
     va_list args;
     parrot_context_t *ctx;
+    FLOATVAL retval;
 
     va_start(args, sig);
     ctx = runops_args(interp, sub, obj, meth, sig, args);
     va_end(args);
-    return set_retval_f(interp, *sig, ctx);
+    retval = set_retval_f(interp, *sig, ctx);
+    Parrot_free_context(interp, ctx, 0);
+    return retval;
 }
 
 /*
@@ -554,10 +602,11 @@
 Parrot_run_meth_fromc_arglist(PARROT_INTERP, ARGIN(PMC *sub), ARGIN_NULLOK(PMC *obj),
         ARGIN(STRING *meth), ARGIN(const char *sig), va_list args)
 {
-    parrot_context_t *ctx;
+    parrot_context_t *ctx    = runops_args(interp, sub, obj, meth, sig, args);
+    void             *retval = set_retval(interp, *sig, ctx);
 
-    ctx = runops_args(interp, sub, obj, meth, sig, args);
-    return set_retval(interp, *sig, ctx);
+    Parrot_free_context(interp, ctx, 0);
+    return retval;
 }
 
 /*
=== src/ops/core.ops
==================================================================
--- src/ops/core.ops	(revision 28361)
+++ src/ops/core.ops	(local)
@@ -544,7 +544,7 @@
 
 op set_returns(inconst PMC) :flow {
     opcode_t * const _this = CUR_OPCODE;
-    parrot_context_t *ctx;
+    parrot_context_t *ctx, *copy;
     PMC *ccont;
     PMC *signature = $1;
     INTVAL argc;
@@ -571,6 +571,21 @@
 
         parrot_pass_args(interp, ctx, caller_ctx, src_indexes, dest_indexes, PARROT_PASS_RESULTS);
     }
+    else {
+        if (interp->fromc_result_ctx)
+            internal_exception(1, "Error: called set_returns twice in one context.\n");
+
+        copy = Parrot_dup_context(interp, ctx);
+        copy_context_registers(interp, copy, ctx);
+        interp->fromc_result_ctx = copy;
+
+        /* If we don't do this, running with -D80 will crash */
+        copy->current_sub = ctx->current_sub;
+        /* Parrot_dup_context sets the current context to the copy (which
+           has a current_cont of NULL), restore the original */
+        CONTEXT(interp) = ctx;
+    }
+
     argc = SIG_ELEMS(signature);
     goto OFFSET(argc + 2);
 }
=== t/op/calling.t
==================================================================
--- t/op/calling.t	(revision 28361)
+++ t/op/calling.t	(local)
@@ -1,5 +1,5 @@
 #!perl
-# Copyright (C) 2001-2007, The Perl Foundation.
+# Copyright (C) 2001-2008, The Perl Foundation.
 # $Id$
 
 use strict;
@@ -7,7 +7,7 @@
 use lib qw( . lib ../lib ../../lib );
 
 use Test::More;
-use Parrot::Test tests => 97;
+use Parrot::Test tests => 98;
 
 =head1 NAME
 
@@ -2489,6 +2489,37 @@
 /too many arguments passed\(1\) - 0 params expected/
 OUTPUT
 
+pir_output_is( <<'CODE', <<'OUTPUT', "RT #41583 - Tail calls from within vtable methods broken" );
+.sub main :main
+    $P1 = newclass "Foo"
+    $P2 = new "Foo"
+
+    ## Should return 2, but doesn't.
+    $I1 = elements $P2
+    $S1 = $I1
+    say $S1
+    end
+.end
+
+.namespace ["Foo"]
+
+.sub elements :method :vtable
+    I0 = 13
+    I1 = 2
+    .return identity(I1)
+.end
+
+.sub identity
+    .param int arg
+    ## arg is I0, taken from the elements context (which is set
+    ## to 13). If we put "I0 = 14" here and don't optimize, we
+    ## return 2. (elements's context's I1)
+    .return (arg)
+.end
+CODE
+2
+OUTPUT
+
 # Local Variables:
 #   mode: cperl
 #   cperl-indent-level: 4

Reply via email to