Dan Sugalski wrote:
> Benjamin Goldberg wrote:
>> Jason Gloudon wrote:
>>> Piers Cawley wrote:
>>>
>>>>> I think you're overlooking the "restoreall" done just before
>>>>> the jump-no-save-returnaddress operation...  I see two "saveall"s
>>>>> and two "restoreall"s.
>>>>
>>>> But with proper tail call optimization you'd only need *one*
>>>> saveall. That's why it's an optimization.
>>>
>>>  Tail call optimization is a space (stack size) optimization. The
>>>  scheme definition only requires space efficiency. The time
>>>  optimization is important but a secondary consideration for the
>>>  functional language folks.
>>
>> Here's an idea -- considering that the first op of every sub seems to
>> be a 'saveall' instruction,
> 
> If that's true, we need to thump IMCC some. There's no reason for
> this to be the first op--if the caller wanted any registers saved it
> had darned well beter have taken care of it itself. That's the point
> of caller-save, after all...

Erm, my statement was actually just an assumption that the first op
would be a 'saveall' -- I haven't looked at actual imcc output.  By your
comment, I'll assume that my earlier assumption was wildly wrong.

-------

Is it possible to divide subroutines into two groups, caller-save and
callee-save?  If so, then I *think* that we should be able to make at
least *some* tail-optomizations:

   If a caller-save subroutine is going to return the results of another
caller-save subroutine, the first can jump directly into the other.

   If a callee-save subroutine is going to return the results of another
callee-save subroutine, the first can jump to the Nth opcode of the
second subroutine, in the manner described down below.

In the other cases (callee-save going to caller-save, or vice-versa), I
suppose (but might be mistaken) that the only tail-call optomization
that can be done is to have the caller skip the step of removeing
results from the stack and then pushing them back on... which might
already be done.

---------

There's three or four ways that I can think of for an ordinary
callee-save subroutine to work:
   1/ saveall/restoreall
   2/ push[ipns]/pop[ipns]
   3/ push/pop (with the help of rotate_up)
   4/ a combo of 2 and 3.

I assume that most callee-save subroutines have the following stages:
   1/ save the registers we're going to be dirtying
   2/ (optional, use with methods 3&4 mentioned above)
      rotate_up the stack so that our arguments are on the top.
   3/ pop the arguments into registers
   4/ do our work
   5/ push the return values onto the stack.
   6/ (optional) rotate_up the stack
   7/ restore the registers we've dirtied.
   8/ pop an address off the stack and goto it. (op "ret")

If a caller is going to perform a tail-optomized function call, then it
could look like:
   1/ save the registers that the callee is going to restore in the
callee's step 7
   2/ save any registers that we're using that weren't already saved
   3/ rotate_up the stack, if step 2 used "push" ops.
   4/ pop the arguments into registers.
   5/ do our work
   6/ copy the callee's arguments into whatever registers the callee
would put them into in the callee's step 3.
   7/ restore the registers that were saved in our step 2.
   8/ branch to step 4 of the callee.

It may be possible for the caller to avoid an explicit step 6, if it can
have it's computations put the arguments in the target registers
directly.

Note that steps 2,7 of the caller can't use saveall/restoreall, since
that would interfere with the registers written to in step 6.  (If you
do want to use saveall/restoreall for 2,7, then step 6 becomes "push the
arguments for the callee onto the stack", and step 8 becomes "branch to
step 3 of the callee").  Similarly, if the callee puts arguments into
integer (etc.) registers, you probably can't use savei/restorei.

-- 
$;=qq qJ,krleahciPhueerarsintoitq;sub __{0 &&
my$__;s ee substr$;,$,&&++$__%$,--,1,qq;;;ee;
$__>2&&&__}$,=22+$;=~y yiy y;__ while$;;print

Reply via email to