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