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.