Given the Perl6 expression:
@a[$i] = @b[1] + $k;
This should translate to
add P0[I0], P1[1], I2
But having multi-keyed variants of all relevant opcodes would burst
our opcode count to #of-keyed-opcodes * #of-key-permutations. That's
not feasable.
So here is another proposal to implement these ops.
1) The assembler splits above PASM statement into two:
keyed P0[I0], P1[1], 0
add Px, Py, I2
2) There is one keyed opcode with all possible key-permutations.
3) For keyed arguments get_pointer_keyed* is called on source operands,
non-keyed operands are replaced by 0.
get_pointer_keyed* return a PMC* ptr to the store inside the
aggregate.
4) For a keyed destination the set_pointer_keyed* is called,
preparing the aggregate store and returning that PMC*.
5) These returned pointers are stored in REG_PMC(x) .. REG_PMC(z)
(x = 32, y = 33, z = 34) [2]
struct PReg has PMC *registers[NUM_REGISTERS + 3];
6) The next (add) opcode is modified, so that for all keyed operands a
plain PMC operand is emitted, the register number is x..z.
Non-keyed operands are as is.
This adds one opcode dispatch and increases code size a bit, but we
we wouldn't need any additional keyed vtables. And all the checks for
passed NULL keys (i.e. no key) aren't necessary.
Comments welcome,
leo
[1] And I hear Dan groaning: ONNTA
[2] we could of course reserve regs# 29..31 or 0..2 too for this purpose
Definitely not. Here's an alternative. I'd originally planned on
there being a single keyed variant of each op, so the above would be
written:
add P0[S0], P1[S1], P2[]
Note that a key is passed in for $k, just a NULL key, all keys were
meant to be in S registers, and there weren't going to be constant
keys or pure-integer. Things have changed a bit. :)
Note that I'm perfectly OK mandating the following:
1) All keys must be of the same type (integer key or key struct keys)
2a) No constant keys
or
2b) All keys must be constant or register based
3) If one parameter is keyed they all are
I'm comfortable with 1, 2a, and 3, though I can see the case for 2b.
Doubles the number of keyed ops, though. (At least only for PMC-based
operations, which is something)
>So here is another proposal to implement these ops.
>
>1) The assembler splits above PASM statement into two:
This is the one thing that I'll grumble about -- we can argue over
other things, but the assembler should *not* implicitly split stuff
out like this. Maybe (Only maybe!) IMCC should, but I'd argue not
there as well. It should be explicitly specified in the source where
the keys should be fetched.
>5) These returned pointers are stored in REG_PMC(x) .. REG_PMC(z)
> (x = 32, y = 33, z = 34) [2]
> struct PReg has PMC *registers[NUM_REGISTERS + 3];
This would be the clever bit -- key registers. I'm fine with key
registers however... if we're going to add them, why not just have a
separate set of key registers and be done with it?
>This adds one opcode dispatch and increases code size a bit, but we
>we wouldn't need any additional keyed vtables. And all the checks for
>passed NULL keys (i.e. no key) aren't necessary.
>
>Comments welcome,
>leo
>
>[1] And I hear Dan groaning: ONNTA
:) Groan or not this needs a final decision. Nobody ever told me I
had to *like* the job...
--
Dan
--------------------------------------"it's like this"-------------------
Dan Sugalski even samurai
d...@sidhe.org have teddy bears and even
teddy bears get drunk
> Definitely not. Here's an alternative.
I meant #of-implemented-key-permutations, which is anything from 1 to
4^3 (or more if string keyes are supported in ops, or we consider the
no-key case too ...)
> ... I'd originally planned on
> there being a single keyed variant of each op, so the above would be
> written:
> add P0[S0], P1[S1], P2[]
> Note that a key is passed in for $k, just a NULL key, all keys were
> meant to be in S registers, and there weren't going to be constant
> keys or pure-integer. Things have changed a bit. :)
I know that The Plan was, to use one *all* keyed opcode. But - "Things
have changed a bit" - when going with only one multi-keyed we have:
- one constant PMC per argument, these have to be constructed at program
load time, albeit keys are folded (identical keys in different
instructions are only generated once) and they don't need marking, its
some overhead
- each keyed access needs a check, if the key is non NULL - that are
three ifs per vtable meth
- and finally extracting the index
This all reduces the advantage of having all keyed ops towards zero.
Further: We have then e.g. add_p_p_p and add_p_k_p_k_p_k. Overloading
the add operator would need to delegate 2 vtable methods and implement 2
different subs to do the job.
> Note that I'm perfectly OK mandating the following:
> 1) All keys must be of the same type (integer key or key struct keys)
Already starting permutations ...
>>1) The assembler splits above PASM statement into two:
> This is the one thing that I'll grumble about -- we can argue over
> other things, but the assembler should *not* implicitly split stuff
> out like this. Maybe (Only maybe!) IMCC should, but I'd argue not
> there as well.
Imcc *is* the assembler. Assembler input is a multi-keyed opcode as
defined in pdd06. Its like a macro expansion one line generating 2
opcodes.
> ... It should be explicitly specified in the source where
> the keys should be fetched.
These 2 operations are always adjacent.
>>5) These returned pointers are stored in REG_PMC(x) .. REG_PMC(z)
>> (x = 32, y = 33, z = 34) [2]
>> struct PReg has PMC *registers[NUM_REGISTERS + 3];
> This would be the clever bit -- key registers. I'm fine with key
> registers however... if we're going to add them, why not just have a
> separate set of key registers and be done with it?
The problem is addressing these key registers in the opcode. A non keyed
C<add> calls a vtable on and with PREG(i), where PREG(i) is
REG_PMC(cur_opcode(i)). When key registers are accessed with a
different scheme, we again get code duplication with all the drawbacks.
So my proposal uses key registers, which are more or less special
depending, if they are outside or inside the regular register file.
But e.g. using REG_PMC(32) doesn't need any further change to the
vtables. C<add> is C<add> nothing more. And there is no overhead for
e.g. adding a constant to a keyed operand. We can continue using all the
optimized (e.g. integer indexed) vtable meths.
leo