为什么编译器在函数调用之前输入了这么多命令(请看下面的链接)?据我了解,它应该在调用之前只传递函数参数。
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
}
为什么当我们调用类成员时,需要另外4个命令来准备调用?我们还加载对象地址进行注册?
最后一个案例有5个操作,就在我之外......
P.S。在我年轻的时候,通常,我们把函数参数叠加(推),而不是读(pop)。那么,我们通过寄存器传递参数?
答案 0 :(得分: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