On Sat, 6 Oct 2001, Simon Cozens wrote: > On Sat, Oct 06, 2001 at 09:01:34AM -0500, Gibbs Tanton - tgibbs wrote: > > Also, how will adds of different types be handled. In the above if pmc2 is > > an int and pmc3 is a float we're going to have to know that and do a switch > > or something to convert to/create the right type. > > There'll actually (and I need to change my vtable code to reflect this) be > several versions of each vtable function, depending on the relative type of > each PMC. Basically, there'll be two easily optimizable versions (i.e. types > are the same, or types can be easily converted with a cast or simple function) > and a non-optimized version, which would actually be the naive implementation > in many cases. ("These types are way out of my depth - call ->get_integer on > each one, and add the result.")
So would it be something like(ultimtaely put into a macro): AUTO_OP add_p_p_p { if (!P1) CREATE_PMC(P1); if (!P2 || !P3) throw exception; // however this is done in Parrot P2->vtable->add[P3->type]->(interp, P1, P2, P3); //in macro } In this way each vtable operation is really an array of handlers for each possible type of input. This avoids any comparisons. Invalid comparisons all share a common function (which throws an "invalid data-intermingling" exception). int_pmc_vtable = { ....., { pmc_vtable_add_int_int, pmc_vtable_add_int_float, pmc_vtable_add_int_string, pmc_vtable_add_int_iconst, pmc_vtable_add_int_fconst, ... }, ... }; // maps to "RET_DT pmc_vtable_add_int_int(interp_t*, PMC*, PMC*, PMC*) AUTO_VOP add_int_int { UPGRADE(P1,PMC_INT); P1->ival = P2->ival + P3->ival; } AUTO_VOP add_int_float { UPGRADE(P1,PMC_INT); P1->ival = P2->ival + P3->fval; } AUTO_VOP add_int_iconst { UPGRADE(P1,PMC_INT); P1->ival = P2->ival + P3; } AUTO_VOP add_int_fconst { UPGRADE(P1,PMC_INT); P1->ival = P2->ival + Parrot_float_constants[P3]; } AUTO_VOP add_int_string { UPGRADE(P3,PMC_INT); UPGRADE(P1,PMC_INT); P1->ival = P2->ival + P3->ival; } Alternatively, if we can't be both a string AND an int in PMC: AUTO_VOP add_int_string { int p3ival = PMC_STR_TO_INT(P3); UPGRADE(P1,PMC_INT); P1->ival = P2->ival + p3ival; } This assumes that a = b op c will be the same as a = b.op( c ), which I think is fair. Thus add_float_int produces a float while add_int_float produces an int. The compiler can worry about the order or the parameters. I don't think there's much value in writing separate a op= b since you could just do: P1->vtable->add[P1->type]->(interp, P1, P1, P2); With hardly any additional overhead. The "optimized" code might have been: AUTO_VOP inc_int_int { P1->ival += P2->ival; // avoids casting P1 } But now you have LOTS more vtable ops. My question at this point is if the PMC's are polymorphic like Perl5 or if there is an explicit type type. Polymorphics can make for vary large vtable sub-arrays. (int, int_float, int_float_string, int_string, etc). If PMC-types are bit-masked (for easy upgrading) such as: O O O OOOOO ^ ^ ^ ^^^^ | | | ... INT FLOAT STR We could apply a macro that extract the desired type. Such as GET_PMC_TYPE_INT(Px) which if it is of type int, it returns int, else float else string. #define GET_PMC_TYPE_INT(type) (type & PMC_INT)?PMC_INT : (type & PMC_FLOAT)?PMC_FLOAT : (type & PMC_STRING)?PMC_STRING : type Likewise GET_PMC_TYPE_FLOAT would return first float then int then string It's not as fast because we're not avoiding the nested if-statements, but it's easy enough to read. P2->vtable->add[ GET_PMC_TYPE_INT(P3->type) ]->(...) Ideally, the bit-pattern for the pmc-type is numerically small (for small sub-arrays). enum PMC_TYPES { PMC_INT, PMC_FLOAT, PMC_STR, PMC_INT_FLOAT, PMC_INT_STR, PMC_INT_FLOAT_STR, PMC_FLOAT, ... }; In this way we simply map everything that has INT in it to the same handler. No conditionals at all (but lots and lots of vtable space). Thankfully this is constant and could be assigned globally such that there is no intialization overhead. -Michael