2014-06-20 52 views
0

为什么编译器在函数调用之前放置了如此多的命令(查看下面的链接)?据我所知,它应该只在调用之前传递函数参数。C++编译器函数调用

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

为什么当我们调用类的成员,花了额外的4个命令准备打电话?我们也加载对象地址来注册?

,并用5个OPS最后一种情况,就是超越我...

附:在我年轻的时候,通常我们会把函数参数放在堆栈中(推),而不是读取它们(弹出)。现在,我们通过寄存器传递参数?

+0

64位调用约定访问不使用推。 –

+0

@RaymondChen这是什么?我只与旧的x80 /​​ x86 asm :) – tower120

+0

@Gluttton注册与q(没有)/(无法推)到堆栈?它有没有'pushq'? – tower120

回答

3

这很正常。在汇编中,我通常只做一件事。例如,在最后一种情况下:

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 

在64位Linux系统的功能参数不再在堆栈上传送,前6个整数参数在%rdi转移,%rsi%rdx%rcx%r8%r9寄存器。浮点值使用%xmm0 - %xmm7寄存器,而其他寄存器则在堆栈上传输。

课程的局部变量位于堆栈上,并通过%rbp

+0

只是为了总结。我们将“this”(对象地址)传递给%esi,并将第一个参数传递给%rdi? – tower120

+0

最后一个参数(以相反顺序)始终是对象地址?在我们的例子中'%esi' /'%rsi' – tower120

+1

第一个参数(在%rdi中)是对象地址(这个指针)。第二个参数(在%rsi中)是该函数的实际第一个参数(int a)。第三个参数将是实际的第二个参数,依此类推。 –