9

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

1 Answers1

11

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.

UPD.

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.

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.

Jens
  • 69,818
  • 15
  • 125
  • 179
Eldar Abusalimov
  • 24,387
  • 4
  • 67
  • 71
  • Shouldn't the `restore` appear before the `ret`? – Alexis Wilke Dec 07 '14 at 00:27
  • 2
    In my code `restore` is executed in a delay slot of `ret` (notice a one space indentation). You could however perform `restore` first, but in such case you should use `retl` (which takes the return address from `%o7`), not `ret` (`%i7`). – Eldar Abusalimov Dec 07 '14 at 00:31
  • @EldarAbusalimov I've not seen the one-space indentation before. Just to clarify, that indentation indicates that the instruction is executed in the delay slot of the preceding instruction? – Jonathon Reinhart Dec 07 '14 at 16:03
  • @Jonathon yes, you're right. This is just an agreement (AFAIK) for SPARC assembly. I've seen it in some projects, in Linux kernel, for instance. The only purpose is to improve readability of the code. – Eldar Abusalimov Dec 07 '14 at 16:48
  • @EldarAbusalimov I don't understand why we should put `retl` after `restore`. Indeed, restore in this case is executed before `retl` (in a delay slot), so `%o7`becomes `%i7`in both cases (`restore` after `ret` and `ret` after `restore`) –  Dec 15 '14 at 05:32
  • @user1773603 No. If an instruction appears in a delay slot of another one, it doesn't mean that it is executed _before_ the latter. Think of it as a momentum of a motion of a car: CPU executes `ret`, but because this instruction is a jump, CPU (the car) can't "turn" at once and also executes the next one "amain". – Eldar Abusalimov Dec 15 '14 at 11:24