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
