14

I'm learning Assembly language. What exactly is argument push order? I understand it's how arguments are pushed to the stack but what does the left and right part mean? Left or right of what?

Or is this merely to do with the way the command is semantically written, i.e.:

mov ebp, esp             ;esp is moved into ebp, right to left.

Is this correct or could someone enlighten me?

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Axolotl
  • 385
  • 1
  • 4
  • 10

2 Answers2

19

The processor doesn't know 'function arguments'. Therefore when you want to write f(a,b,c), you really need to push the arguments 'somewhere'.

How and where to push them is mere convention. I know that on most x86 machines, function arguments are pushed on the stack from right to left, i.e. first c, then b, then a.

push c
push b
push a
call f

Pushing to the stack would, on x86, decrease the stack 'top' with a word. Three words have been pushed, and the return address, so the called function can use top + 1*W for a, top + 2*W for b and top + 3*W for c.

You could as well establish a convention that says: first two arguments are in registers ebx and ecx, rest are on the stack. As long as the caller and callee agree, you're fine.

xtofl
  • 40,723
  • 12
  • 105
  • 192
  • 2
    Where did `ebx-1` come from? If those are supposed to be addresses, `push` decrements the stack pointer by 4 (or 8 in 64-bit mode), so each stack slot is 4 or 8 bytes apart, like `ebx - 4` (c), `ebx - 8` (b) etc. And that's only if EBX = ESP right before those pushes. That's unusual; if you were going to do that with any register, it would be EBP. But the *callee* `f` can't assume anything about where the caller left EBP relative to its incoming stack args. It needs to look above its return address, and `[esp+4]` (a), `[esp+8]` (b), etc. Or higher offsets after pushing anything. – Peter Cordes Aug 06 '22 at 09:38
  • But yes, your final paragraph is correct, register calling conventions work, like `fastcall` passing in ECX, EDX, or gcc's `regparm(3)` passing in EAX, EDX, ECX. And your earlier stuff is correct about pushing the first arg last, so it's at the lowest address. – Peter Cordes Aug 06 '22 at 09:40
  • Thanks, you're right. I should have left it more high level. – xtofl Aug 08 '22 at 05:59
  • 1
    That partially fixes the problem, but `c` is at a higher address than `a`, since it was pushed earlier, before two `stack_pointer-=stack_width` from two pushes. The standard location for a frame pointer to point is right below the return address, in which case `frame+2*W` for `a`, `frame+3*W` for `b`. But regardless of where your reference point is, `c`'s address is higher, + more not - more. – Peter Cordes Aug 08 '22 at 06:16
  • OMG ;(. I guess it's unconventional to define `W` as `-4`... Note to self: start a compiler backend hobby project. – xtofl Aug 09 '22 at 07:21
13

In addition to xtofl's explanation you might like to have a look at this table of x86 calling conventions. What you'll note, with regards to argument order is that almost all of the arguments are pushed on right to left (rightmost argument is pushed first) with the exception of Pascal.

Another scenario xtofl doesn't cover are register arguments - some ABIs require that some arguments are in registers as opposed to on the stack. On a x86_64 system, for example, the function:

int add3(int a, int b, int c)

will put arguments:

a -> rdi
b -> rsi
c -> rdx

Specifically, this would look like (Intel syntax):

mov     rdi, [source-of-a]
mov     rsi, [source-of-b]
mov     rdx, [source-of-c]
call    add3

So the registers are filled up from the register list left to right and then the stack is used right to left.

As xtofl says, it doesn't matter what you do provided the caller and the callee agree - clearly, however, if the caller and the callee disagree that'll cause incompatibility problems and this is actually a concern not only for assembler, but for higher level languages too - luckily, compilers largely operate right to left. For further reading, you might find the callee/caller cleanup of the stack interesting - and note how it was standardised to one method for x86_64.

You don't say you're using x86 - your architecture will most certainly have a standard calling convention as working without it is difficult.

Community
  • 1
  • 1
  • 1
    Thanks very much to both of you. That was very helpful and I understand it much better now! Thanks a mil!! – Axolotl Feb 06 '12 at 12:19
  • See [What are the calling conventions for UNIX & Linux system calls (and user-space functions) on i386 and x86-64](https://stackoverflow.com/q/2535989) for details on the x86 calling conventions for 32-bit and 64-bit Linux (and other non-Windows x86-64 systems) – Peter Cordes Aug 09 '22 at 07:27