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
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?
%o
registers only become %i
after doing save
, usually at the beginning of a function being called. In your example test
function doesn't have save
/restore
.
It is save
/restore
that rotates register windows, not call
/ret
. Since test
is not a leaf function (it calls printf
from inside it), it must have its own register window. So you have to wrap test
function with save
/restore
:
test:
save %sp, -64, %sp
mov %i2, %o1
set fmt0, %o0
call printf
nop
ret
restore
Otherwise, your argument is still available through %i2
, but anyway the code is wrong because call printf
instruction would destroy a return address of test
which is stored in %o7
.
Concerning a question in an edit suggestion (BTW don't do that, ask in comments instead):
If %o7 is overwritten in a non-leaf procedure, how to circumvent this problem? I think that I have to push %o7 at the beginning of the non-leaf procedure in another register and pop it at the end, i.e after the call of the nested procedure, is it right?
There is no problem in case of a non-leaf procedure: save
/restore
do the trick. You might think of save
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 makes save
and gets the arguments from %i0 .. %i5
. If it wants to return a value, it puts it into %i0
. Upon returning it makes restore
, and caller can see the return value (if any) in %o0
.
This also answers to another your question:
From I have seen, %sp becomes %fp after "save %sp, -64, %sp" instruction? Has %fp the same value in main section and test function?
%sp
is just an alias for %o6
, and %fp
- for %i6
. Apart from rotating windows,save
is also able to add values just like ordinal add
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,
save %sp, -64, %sp
does the same as
save
add %fp, -64, %sp ! notice that the source register is now %fp, not %sp
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With