Leopold Toetsch wrote:

[ proposal about a new function calling scheme ]

Attached is a minimal patch that shows the concept of the proposed function calling scheme.
* works only with "function-less" run-cores [1]
* the 2 new opcodes "mycall" and "return" abuse the _pointer_keyed vtable slots of sub.pmc
* no recursive calls (would need that the prederefed code is attached to the sub)


Here are timings of the attached programs:

$ time parrot -j -Oc c.imc
in main
200000

real    0m1.069s
user    0m0.910s
sys     0m0.020s

$ time parrot -C ch.imc
in main
200000

real    0m0.356s
user    0m0.250s
sys     0m0.000s

I think that a factor 3 improvement in function call speed (and a factor of ~4 for overloaded vtables) is an argument to have a closer look at this scheme. Here are again the key ideas:

* the interpreter structure is the context, and the continuation
* instead of pushing registers onto the register frame stacks an
  interpreter structure gets attached to each sub, and the
  subsequent code is running with that interpreter

Comments welcome,
leo

[1] switch, cgoto, CGP. Other run-cores would need a special restart-notification that the interpreter had changed.
JIT needs a bit more.
--- parrot/classes/sub.pmc      Mon Jul 26 10:27:40 2004
+++ parrot-leo/classes/sub.pmc  Tue Jul 27 11:16:04 2004
@@ -33,6 +33,113 @@
 
 */
 
+typedef struct _my_hack {
+    opcode_t *start; /* original struct_val - sub localtion */
+    opcode_t *end;
+    opcode_t *next;     /* for return continuation */
+    struct PackFile_ByteCode *seg;      /* bytecode segment */
+    STRING *name;
+    Interp *frame;
+    Interp *caller;     /* frame->caller */
+} my_hack;
+
+static void
+my_init(Interp *interpreter, PMC *self) {
+    my_hack *sub = mem_sys_allocate(sizeof(my_hack));
+    parrot_sub_t old = PMC_sub(self);
+    sub->start = PMC_struct_val(self);
+    sub->end = old->end;
+    sub->next = NULL;
+    sub->name = old->name;
+    sub->seg = old->seg;
+    sub->frame = NULL;
+    sub->caller = NULL;
+    PMC_pmc_val(self) = (PMC*) sub;
+}
+
+static Interp*
+copy_interp(Interp *src)
+{
+    Interp *dest = mem_sys_allocate(sizeof(Interp));
+    memcpy(dest, src, sizeof(Interp));
+    return dest;
+}
+
+static void
+update_context(Interp *dest, Interp *src)
+{
+}
+
+static void
+copy_func_params(Interp *dest, Interp *interpreter)
+{
+    int i;
+    for (i = 0; i < 5 + REG_INT(1); ++i)
+        dest->int_reg.registers[i] = REG_INT(i);
+    for (i = 0; i < REG_INT(2); ++i)
+        dest->string_reg.registers[i+5] = REG_STR(i+5);
+    for (i = 0; i < REG_INT(3); ++i)
+        dest->pmc_reg.registers[i+5] = REG_PMC(i+5);
+    for (i = 0; i < REG_INT(4); ++i)
+        dest->num_reg.registers[i+5] = REG_NUM(i+5);
+}
+
+static opcode_t*
+switch_to_segment(Interp *interpreter, my_hack *sub)
+{
+    if (interpreter->code->cur_cs != sub->seg) {
+        Parrot_switch_to_cs(interpreter, sub->seg, 1);
+    }
+    return sub->start;
+}
+
+static void*
+my_call(Interp** interp, PMC* self, void *next)
+{
+    Interp *caller = *interp;
+    my_hack *sub = (my_hack*)PMC_pmc_val(self);
+    Interp *frame = sub->frame;
+    if (!frame)
+        frame = copy_interp(caller);
+    else if (sub->caller) {   /* frame->caller */
+        self = VTABLE_clone(caller, self);  // copy sub PMC
+        sub = (my_hack*)PMC_pmc_val(self);
+        frame = copy_interp(caller);
+    }
+    else
+        update_context(frame, caller);
+    sub->frame = frame;
+    sub->caller = caller; /* frame->caller */
+    sub->next = next;
+    /* frame->sub_pmc = self; */
+    frame->pmc_reg.registers[0] = self;
+    copy_func_params(frame, caller);
+    *interp = frame;
+    next = switch_to_segment(frame, sub);
+    return next;
+}
+
+static void
+copy_return_values(Interp* dest, Interp *src)
+{
+
+    copy_func_params(dest, src);
+}
+
+static void*
+my_return(Interp** interp, PMC* self)
+{
+    my_hack *sub = (my_hack*)PMC_pmc_val(self);
+    Interp *frame = sub->frame;
+    Interp *caller = sub->caller;  /* frame->caller */
+    sub->caller = NULL;
+    copy_return_values(caller, frame);
+    // add_frame_cache(frame, sub);
+    *interp = caller;
+    switch_to_segment(caller, sub);
+    return sub->next;
+}
+
 static void
 print_sub_name(Parrot_Interp interpreter, PMC* sub)
 {
@@ -102,6 +209,17 @@
             printf("Address of base segment is %p\n",
                 ((struct Parrot_Sub *)PMC_sub(SELF))->seg->base.pf->byte_code);
 #endif
+    }
+
+    void push_pmc(PMC* x) {
+        my_init(INTERP, SELF);
+    }
+
+    void set_pointer_keyed(PMC* key, void* val) {
+        *(void**) val = my_call((Interp**)key, SELF, *(void**)val);
+    }
+    void* get_pointer_keyed(PMC* key) {
+        return my_return((Interp**)key, SELF);
     }
 
 /*
--- parrot/ops/core.ops Mon Jul 26 09:35:04 2004
+++ parrot-leo/ops/core.ops     Tue Jul 27 10:49:24 2004
@@ -1177,6 +1177,20 @@
   goto NEXT();
 }
 
+op mycall(in PMC) {
+    opcode_t *next = expr NEXT();
+    PMC *p = $1;
+    p->vtable->set_pointer_keyed(interpreter, p, (PMC*)&interpreter,
+           (void*)&next);
+    goto ADDRESS(next);
+}
+
+op return(in PMC) {
+    PMC *p = $1;
+    opcode_t *next =
+       p->vtable->get_pointer_keyed(interpreter, p, (PMC*)&interpreter);
+    goto ADDRESS(next);
+}
 =back
 
 =cut
--- parrot/src/interpreter.c    Wed Jul 21 07:50:02 2004
+++ parrot-leo/src/interpreter.c        Tue Jul 27 09:10:38 2004
@@ -1020,7 +1020,10 @@
     op_variant = Parrot_sprintf_c(interpreter, "%s_ops%s",
             new_lib->name, cg_lib->suffix);
     lib_variant = Parrot_load_lib(interpreter, op_variant, NULL);
-    if (lib_variant) {
+    /*
+     * XXX running CG and CGP ops currently works only via the wrapper
+     */
+    if (0 /*lib_variant */) {
         new_init_func = get_op_lib_init(0, 0, lib_variant);
         new_lib = new_init_func(1);
         for (i = n_old; i < n_tot; ++i)
.sub main @MAIN
    $P0 = global "f"
    $P1 = global "g"
    $I0 = 0
    $I1 = 0
    set S10, "in main\n"
loop:
    $I1 = $P0($P1, $I0)
    inc $I0
    if $I0 < 200000 goto loop
    print S10
    print $I1
    print "\n"
.end

.sub f prototyped
    .param pmc g
    .param int i
    i = g(i)
    .pcc_begin_return
    .return i
    .pcc_end_return
.end

.sub g prototyped
    .param int i
    inc i
    null S10
    .pcc_begin_return
    .return i
    .pcc_end_return
.end
.sub main @MAIN
    $P0 = global "f"
    push $P0, $P0       # init
    $P1 = global "g"
    push $P1, $P1       # init
    $I0 = 0
    $I1 = 0
    set S10, "in main\n"
loop:
    I0 = 1
    I1 = 1
    I5 = $I0
    I2 = 0
    I3 = 1
    I4 = 0
    P5 = $P1
    mycall $P0
    $I1 = I5
    inc $I0
    if $I0 < 200000 goto loop
    print S10
    print $I1
    print "\n"
.end

.sub f prototyped
    .param pmc g
    .param int i
    I0 = 1
    I1 = 1
    I5 = i
    I2 = 0
    I3 = 0
    I4 = 0
    mycall g
    set I5, i
    return P0
.end

.sub g prototyped
    .param int i
    inc i
    null S10
    set I5, i
    I2 = 0
    return P0
.end

Reply via email to