0

Why compiler put so much commands before function call (look at the link below)? As I understand, it should pass only function parameters before call.

struct A{
  int c = 5;
void test(unsigned int a){
  a++;
  c++;
}
};

struct C{
  int k =2;
  A a;
};

struct D{
  int k =2;
  C c;
};

struct B{
  int k =2;
 D d;
};

void test(unsigned int a){
  a++;
}

    B *b = new B();
A *ae = new A();

int main() 
{
  int a = 1;
  A ai;
  B bi;
  C ci;

  // 2 operations (why not pop/push ?)
  // movl   -36(%rbp), %eax
  // movl   %eax, %edi
  // call   test(unsigned int)
  test(a);

  // 4 operations (why 4? we pass something else?)
  // movl   -36(%rbp), %edx
  // leaq   -48(%rbp), %rax
  // movl   %edx, %esi
  // movq   %rax, %rdi
  // call   A::test(unsigned int)
  ai.test(a);
  ae->test(a);


  // 5 operations before call (what a hell is going here?, why that "addq" ?)
  // movl   -36(%rbp), %eax
  // leaq   -32(%rbp), %rdx
  // addq   $4, %rdx
  // movl   %eax, %esi
  // movq   %rdx, %rdi
  // call   A::test(unsigned int)
  ci.a.test(a);
  bi.d.c.a.test(a);
  b->d.c.a.test(a);
  // no matter how long this chain will be - it will always took 5 operations
}

http://goo.gl/smFSA6

Why when we call class member, it took 4 additional commands to prepare to call? We load object address to register, as well?

And the last case with 5 ops, is just beyond me...

P.S. In the days of my youth, usually, we put function params to stack (push), than read them (pop). Now what, we pass parameters through registers?

tower120
  • 5,007
  • 6
  • 40
  • 88
  • 64 bit calling convention does not use push. – Raymond Chen Jun 20 '14 at 18:37
  • @RaymondChen what is this? I worked only with old x80/x86 asm :) – tower120 Jun 20 '14 at 18:39
  • @Gluttton registers with q (does not have) / (can not be pushed) to the stack? Doesn't it have some `pushq`? – tower120 Jun 20 '14 at 18:46
  • x80/x86 is pretty much the only architecture that uses push for passing parameters. Everybody else uses registers. See [What are the calling conventions for UNIX & Linux system calls on x86-64](http://stackoverflow.com/questions/2535989/what-are-the-calling-conventions-for-unix-linux-system-calls-on-x86-64) – Raymond Chen Jun 20 '14 at 19:09
  • @Raymond Chen I'm reading that article right now, but I still not see why call member function took 4 ops instead of 2. – tower120 Jun 20 '14 at 19:12
  • Um, you compiled with optimizations disabled. – Raymond Chen Jun 20 '14 at 19:39
  • @tower120, when you call a member function, the compiler passes the object itself as parameters to the assembly function. It's the only way for the code of the function to know for which oject it has to execute, and where to find the object's attributes if needed. – Christophe Jun 20 '14 at 19:46

1 Answers1

3

It's normal. In assembly I intruction is usually only doing one thing. for example in the last case:

movl   -36(%rbp), %eax       ; move a to %eax
leaq   -32(%rbp), %rdx       ; move & ci  to %rdx
addq   $4, %rdx              ; set %rdx to ci->a = ci + offset of a
movl   %eax, %esi            ; move a from %eax to %esi (second parameter) 
movq   %rdx, %rdi            ; move ci->a from %rdx to %rdi (first parameter)
call   A::test(unsigned int) ; call A::test

In 64 bit linux systems function parameters are no longer transferred on the stack, the first 6 integer parameters are transferred in %rdi, %rsi, %rdx, %rcx, %r8, %r9 registers. Floating point values use the %xmm0 - %xmm7 registers, and the others are transferred on the stack.

The local variables of course are located on the stack and accessed through %rbp

Evan Dark
  • 1,311
  • 7
  • 7
  • Just to summarize. We pass "this"(object adress) to %esi, and 1st param to %rdi? – tower120 Jun 20 '14 at 19:54
  • The last parameter(in reverse order) is always object address? `%esi`/`%rsi` in our case – tower120 Jun 20 '14 at 20:01
  • 1
    The first parameter (in %rdi) is the object address (this pointer). The second parameter (in %rsi) is the actual first parameter of the function (the int a). The Third parameter would be the actual second, and so on. – Evan Dark Jun 20 '14 at 20:05
  • And it can't be moved from memory to %esi at once? Without intermediate %eax? Like this `movl -36(%rbp), %esi` – tower120 Jun 20 '14 at 20:10
  • It could be, but what you posted here is an unoptimized assembly output. likewise leaq `-32(%rbp), %rdx` and `addq $4, %rdx` could be written as `leaq -28(%rbp), %rdx` but it's easier for the compiler to first calculate all values, and then move them to the regiters. – Evan Dark Jun 20 '14 at 22:04