1

I have started to learn assembly language and immediately stumbled upon arguments passing. Every tutorial I've seen on the web (example: http://www.delorie.com/djgpp/doc/ug/asm/calling.html) explains arguments passing to functions as pushing them to the stack. However, when I started experimenting, it is quite clear this is not the case. For the simple function

int foo(int a, int b) {
    return a + b;
}

Following asm is generated (compiled on AMD64 with gcc):

    movl    %edi, -4(%rbp)
    movl    %esi, -8(%rbp)
    movl    -8(%rbp), %eax
    movl    -4(%rbp), %edx
    leal    (%rdx,%rax), %eax

It is quite clear arguments are passed in EDI and ESI registers. As I add more argument to the function, I see more registers are used, and only after I reach 6 arguments I start to see values actually read from the stack. Yet googling EDI/ESI registers gives me no explanation of their special role in arguments passing. What am I missing here?

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
SergeyA
  • 61,605
  • 5
  • 78
  • 137

2 Answers2

2

It looks like you're expecting the traditional 32-bit calling conventions in a 64-bit program.

If you compile and build your application as a 32-bit app, then the compiled code for foo() should work as you expect, with the arguments pushed onto the stack.

For 64-bit applications, the calling conventions (or ABI) are different, and most arguments are passed in register as you are seeing. See this other question here: Where is the X86-64 ABI documented? for more details.

When the AMD64 architecture was introduced, there were a number of reasons for creating a new ABI, one of which was to make good use of the larger number of CPU registers. Passing some of the arguements in registers instead of pushing them on the stack is more effiecient.

Community
  • 1
  • 1
Die in Sente
  • 9,546
  • 3
  • 35
  • 41
  • Sorry, too late - I already got one above :) I totally understand the idea of calling convention, just all the materials i found on the web somehow failed to mention AMD64 ABI. – SergeyA Oct 16 '15 at 19:29
1

Passing arguments in registers is faster and uses no additional stack memory compared to pushing them onto the stack. This is an optimization feature. By convention, the compiler may put the first couple of arguments in registers, depending on how many registers are available in the CPU architecture. C++ compilers will always place the "this" argument in a register when calling object functions.

The use of registers versus stack, and the order of arguments on the stack is a design decision made by the people writing the compilers. If you are not interfacing with any external functions, you are free to make up your own convention. If you are, you will need to know where that function expects to find the arguments.

Every compiler will publish its calling conventions. This is what the "__cdecl" and "__stdcall" modifiers on function declarations are telling you.

See also the Microsoft calling conventions, and Wikipedia explanation.

TeasingDart
  • 371
  • 1
  • 6
  • Didn't help, sorry. You've reiterated something I've already figured out. The real thing is, I can easily call something compiled with one C compiler from something compiled with another. And __stdcall is something from MSFT world, very far away from me. There should be something which allows compilers to use this calling convention, and this should be some sort of standard. – SergeyA Oct 16 '15 at 16:24
  • Alright, your latest edit clarified things. It is System V AMD64 ABI, as described in Wiki. Thanks. – SergeyA Oct 16 '15 at 16:26
  • Yes, the answer depends entirely on your environment. It is a generally agreed upon convention, not a law of physics. – TeasingDart Oct 16 '15 at 16:29