[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [f-cpu] calling conventions



On Thu, Jun 06, 2002 at 12:15:00AM +0200, Thomas Lavergne wrote:
[...]
> 15 registers on the whole right for the passages of the parameters?  13 for the 
> donnee 1 for the stack of parameters and 1 for the number of parameters.  

Right, although I had something different in mind when I proposed the
calling conventions: r1-r13 should hold the first 13 parameters,
r14/r15 length and pointer of *the rest of them*, not all of them as
the manual states.

> After read this section I have made a small script for scann all my source file 
> and see the average number of parameter used and I obtained approximately 3 
> parameters (a little more or a little less according to the languages) what I 
> want to say that there is ten unused register in each functions.  

As I mentioned here before, a function is free to use the unused
parameter registers as local (unsaved) temporary registers.

> Moreover there is a stack of register for each calls, which want to say that 
> for calling a function we need 3 stages:  
>  1/ put the 13 parameters in the registers r1-r13 
>  2/ push all the registers in r14 and put their number in r15 

Not all of them - just the rest. Since there are few function calls
with 14 or more parameters, this is quite a rare case. There are some
examples, however - like long calls to printf()/scanf().

>  3/ make a jmp on the function (address of return in r60) 
> At my opinion the 2nd stages is very ugly, need to store all the parameters in 
> memory will impose many accesses in memory which are penalisant, or which will 
> be found to cache memory and replace more useful donnee in it.  

You're right, that would be pointless.

> What I propose has the place : first an observation, as well the caller 
> function as the function to call know at the compile time the number of 
> parameters.

Not true. In some languages, there are functions taking a variable
number of arguments - which is of course unknown when the function is
compiled.

> That is to say N the number of parameters:  
>  * if n inferior to 13
>      r1         : number of parameters
>      r2-r(n+1)  : parameters
>      r(n+2)-r31 : temporari registers

That was in fact my proposal.

>  * Si n est superieur a 13
>      r1         : number of parameters
>      r2-r14     : parameters
>      r15        : pointer over the parameters list
>      r16-r31    : temporari registers

Whether you put the first 13 args in r1-r13 or in r2-r14 doesn't make
much of a difference. The advantage of using r1-r13 is that functions
with fixed and variable argument lists remain call-compatible (which
they won't be if we follow your proposal).

> This has the advantage of optimizing the number of register to use according to 
> the number of parameters, a function which uses 3 parameters will mobilize 4 
> register for the parameters and will have 27 temporary registers.  In the best 
> cases, a function which does not have parameters, only one register is to 
> mobilize and 30 others are available.  In the worst cases, it is has to say 
> more than 13 parameters, this calling convention is similar to the proposal of 
> the handbook with only an inversion of register.

We could drop the `number of arguments' argument completely:

	r1-r14 -> first 14 arguments
	r15    -> pointer to additional arguments

In most languages there's no need to pass the *number* of arguments
(unless it's checked at runtime, e.g. in Lisp or Scheme - but those
languages need different calling conventions anyway).  But if we have
to do so, r1 is a logical choice (that is, the function has a `hidden'
or `0th' argument).

> In all the cases the register r1 is used (by the number of parameters), it is 
> thus available to store the value of return of the function.  The number of 
> parameters was present, same if it is known of the two functions, in order to 
> be able to possibly make checks.

You can always store the result in r1, whether it's the first argument
or the number of arguments. Since you usually do so at the *end* of a
function, it makes no difference.

> In the case of more than 13 parameters I don't know if it is useful to put all 
> the parameters in the list pointed by r15. Indeed to have all the parameters 
> lists some in very practical, for the function such as [printf] in C or 
> [format] in Pascal, but I am not sure time to gain in these function is a 
> superior than time wasted to put in memory parameters which are already in the 
> registers.

You're right again (and the manual is wrong wrt. this point).

> Otherwise, the registers r32 with r59 are register has to preserve and it is to 
> the called to save them.  It is a good thing because it save only those which 
> it use. But how save does ?  With my opinion there are 2 methods : use the SRB 
> or a store, for the SRB I don't know if it is possible of chain about thirty 
> call of function with as much [srb_save] ? For the second solution I think that 
> it was interresting to have a register pointing on a memory area reserved for 
> this save.

In conventional (C- or Pascal-style) languages, one of the `global'
registers (r63) serves as a `stack pointer' that can be used for explicit
register saves. *How* you save them (and how many you save) will depend
on the function's needs; therefore the compiler will have to choose an
appropriate method (explicit `load'/`store', `loadm'/`storem', ...)

BTW: The border between `caller-saved' and `global' registers was at
r48 in my proposal, not r60. I intentionally left some global registers
unassigned, for use by applications (and even the current assignments
for r60-r63 can be debated - e.g. if a language doesn't need stack or
frame pointers). That is, the assignment should be:

	r0      = always zero
	r1      = function return value, and also
	r1-r14  = function arguments (call-clobbered)
	r15     = pointer to additional function arguments (call-clobbered)
	r16-r31 = temporary registers (call-clobbered)
	r32-r47 = local registers (saved by callee)
	r48-r63 = global registers (shared by all functions)

with the following assignments for C- and Pascal-style languages (but
not necessarily others):

	r60 = function return address
	r61 = global pointer
	r62 = frame pointer
	r63 = stack pointer

Note that r48-r63 are *not* callee-saved as the manual states. A function
*must not* expect them to be unchanged across function calls (but *may*
save/restore them on entry/exit and use them like the callee-saved
registers r32-r47 if it doesn't need the global values).  Analogously,
a function *may* use r1-r15 as temporary registers if there aren't so
many arguments (or it doesn't need them).

-- 
 Michael "Tired" Riepe <Michael.Riepe@stud.uni-hannover.de>
 "All I wanna do is have a little fun before I die"
*************************************************************
To unsubscribe, send an e-mail to majordomo@seul.org with
unsubscribe f-cpu       in the body. http://f-cpu.seul.org/