I am learning SPARC assembly with a simple example that you can see below. I have several questions about this example which shows passing parameters for a procedure.
In main section, I set 5
to first input parameter %o0
and 7
to second input parameter %o1
. After, I do the sum of these registers and put it into %o2
. Then, I call the "test" function where I print this sum.
fmt0:
.asciz "%d\n"
.align 4
.global main
main:
save %sp, -64, %sp
mov 5, %o0
mov 7, %o1
add %o0, %o1, %o2
!st %o2, [%fp-4] for storing %o2 at adress %fp-4
call test
nop
ret
test:
mov %i2, %o1
!ld [%fp-4], %o1 for loading from %fp-4 into %o1
set fmt0, %o0
call printf
nop
ret
With this above code, it prints the value "-273929364" and not "12" (compiled with gcc).
It seems that "mov %i2, %o1
" doesn't work. I know that %o2
register in main section becomes %i2
in called procedure but why I can't set directly the value of %i2
into %o1
register with this "mov
" instruction ?
Second question: If I uncomment the instructions "st %o2, [%fp-4]
" in main section and "ld [%fp-4], %o1
" in test function and comment "mov %i2, %o1
", it prints correctly "12". How can we know the correct offset to put as a function of passing parameters ?
From I have seen, %sp
becomes %fp
after "save %sp, -64, %sp
" insctruction ? Has %fp
the same value in main section
and test function
?
Finally, I have seen on different examples the instruction "call function, 0
" or "call printf, 0
" : why do I have to add a "0" after the name of the function called ? Is this the returned value (like with int main(void){ ... return 0;}
) ?
Thanks for your help
%o
registers only become%i
after doingsave
, usually at the beginning of a function being called. In your exampletest
function doesn't havesave
/restore
.It is
save
/restore
that rotates register windows, notcall
/ret
. Sincetest
is not a leaf function (it callsprintf
from inside it), it must have its own register window. So you have to wraptest
function withsave
/restore
:Otherwise, your argument is still available through
%i2
, but anyway the code is wrong becausecall printf
instruction would destroy a return address oftest
which is stored in%o7
.UPD.
Concerning a question in an edit suggestion (BTW don't do that, ask in comments instead):
There is no problem in case of a non-leaf procedure:
save
/restore
do the trick. You might think ofsave
as a "batch" push: it provides you a new register window - a set of 16 registers (8%i
+ 8%l
) that preserve their values across nested procedure calls. And accordingly,restore
brings you back to a previously saved window.All
%o
registers are accessible through%i
in a new window. That is, the caller sets arguments to%o0 .. %o5
(up to 6, because%o6
and %o7
are reserved for stack pointer and return address). Callee makessave
and gets the arguments from%i0 .. %i5
. If it wants to return a value, it puts it into%i0
. Upon returning it makesrestore
, and caller can see the return value (if any) in%o0
.This also answers to another your question:
%sp
is just an alias for%o6
, and%fp
- for%i6
. Apart from rotating windows,save
is also able to add values just like ordinaladd
instruction.save %sp, -64, %sp
means the following: take a value of%sp
of the old window, rotate windows (%sp
becomes%fp
), add -64 to that value and put the result into%sp
of a new window.In other words,
does the same as
BTW, -64 is just the size of the register window (16 registers, 4 bytes each). And it is negative because stack grows down.
Here is an excellent answer explaining the concept of SPARC register windows.
UPD. 2
Just noticed your "Looking for an answer drawing from credible and/or official sources" statement. SPARC v8 architecture manual is a must-read, especially chapters covering delay slots, register windows, leaf procedure optimization and software considerations.