Leopold Toetsch <[EMAIL PROTECTED]> writes:
> Piers Cawley <[EMAIL PROTECTED]> wrote:
>> Leopold Toetsch <[EMAIL PROTECTED]> writes:
>
>>> C<savetop> becomes C<pushtopp> if only P-registers are used. Saving only
>>> 3 or 5 registers isn't possible yet. We have no opcodes for that.
>
>> save Pn
>> save Pm
>
> Well, these are already used for pushing items onto the user stack. It
> could be
>
> pushtopp 3 # save P16..P18
> savetop 4 # save 4 regs from all I16 ..., S16, N16, P16 ... P19
>
> and so on.
Out of interest, why do we have distinct register and user stacks?
[...]
>> ... Presumably, because IMCC knows that
>> cont_ret is a continuation target, it can create the appropriate
>> real_cont_ret and add the appropriate stack manipulation code in there?
>> This would be really cool.
>
> The code for creating the continuation and the return must be in sync.
> When you pass the continuation on into a subroutine and want to return
> either normally or through the continuation, we need something like:
>
> $P0 = newcont dest_label FOR _invoker
> _invoker($P0)
> ...
> dest_label:
> ...
But the function the continuation gets passed to is completely
irrelevant. When you make a continuation you want to save exactly the
same state as you'd save if you were making a function call at the same
point.
Say you had code like
...
$P0 = "Something"
$P1 = "Something else"
$P2 = "Some other thing"
.newcont $P3, dest_label
...
do_return:
.pcc_begin_return
.pcc_end_return
dest_label:
print $P0
print $P2
branch do_return
Then it'd be cool if IMCC could look ahead to see that when (if) the
continuation is invoked, the only registers that get used are $P0 and
$P2 and emit something like:
...
$P0 = "Something"
$P1 = "Something else"
$P2 = "Some other thing"
save P1
save P2
save $P0
save $P2
$P3 = newcont Continuation, dest_label
restore $P2
restore $P0
restore P2
restore P1
...
do_return:
.pcc_begin_return
.pcc_end_return
dest_label:
restore $P2
restore $P0
restore P2
restore P1
print $P0
print $P2
branch do_return
But I have the feeling I'm thinking IMCC is rather more sophisticated
than it is in real life. From the point of view of a programmer, the
important thing is that invoking a continuation should return the upper
and control registers (but not the argument registers) to the state
they were in when the continuation was made. How the continuation is
subsequently stored/passed is completely irrelevant to this.
> Creating correct code from that is a bit ugly, because the continuation
> is created, before the actual call sequence is generated. So a bit more
> verbose:
>
> .pcc_begin prototyped
> .arg_newcont dest_label
> .pcc_call _invoker
> .pcc_end
> ...
>
> .CONT_TARGET dest_label:
>
> That's still complicated but doable. That would need searching the
> current unit for subroutine calls that have a C<.arg_newcont> argument,
> compare the labels and create finally the very same register
> restore opcode(s) that the function call has.
>
> OTOH storing a continuation inside a global can't prepare any code for
> the continuation return, because nothing about the continuation's usage
> is known.
This is irrelevant.
>>> assign $P0, P1 # vtable->set_pmc (N/Y)
>
>> Assign would be good. I can't really think of an occasion when you'd
>> want to copy anything less than the full context held by the
>> continuation.
>
> What about:
>
> .sym pmc cont
> cont = newcont dest
> ...
> updatecc cont # assign current P1's context to cont
>
> With
>
> assign cont, P1
>
> we need another syntax extension to actually get the real P1 register:
>
> assign cont, current_P1
>
> or P1 is always restored immediately after function calls (like
> currently P2).
>
> I think, C<assign> and restoring P1 immediately would be the most useful
> combination.
Absolutely.