曲径通幽论坛

标题: GCC __attribute__ 之 regparm [打印本页]

作者: beyes    时间: 2011-4-15 13:51
标题: GCC __attribute__ 之 regparm
       在 x86 上,regparm 属性会传递一个数值给编译器,这个数值会告诉编译器要用多少个寄存器来传递函数参数(EAX, EDX 或 ECX,最多 3 个寄存器),通常情况下函数是用堆栈来传递参数的。如果参数比较多,那么其余的参数仍然通过堆栈来传递。

比如我们指定 __attribute__((regparm(0))) ,则表示不用寄存器来传递参数,所有参数都通过堆栈来传递;如果我们指定 __attribute__((regparm(3))),那么就是说会用 3 个寄存器来传递参数(EAX, EDX, ECX),其余的参数通过堆栈来传递。

下面先看不用寄存器来传递参数的情况:
[C++] 纯文本查看 复制代码
#include <stdio.h>

__attribute__((regparm(0))) int parm_to_register (int reg1, int reg2, int reg3, int stack)
{
        return (reg1 + reg2 + reg3 + stack);
}

int main()
{
        int reg1 = 1;
        int reg2 = 2;
        int reg3 = 3;
        int stack = 4;

         parm_to_register(reg1, reg2, reg3, stack);

        return (0);
}

反汇编代码:
.file   "regparm.c"
         .text
.globl parm_to_register
         .type   parm_to_register, @function
parm_to_register:
         pushl   %ebp
         movl    %esp, %ebp
         movl    12(%ebp), %eax
         movl    8(%ebp), %edx
         
         leal    (%edx,%eax), %eax    #leal 指令将 edx 和 eax 中的值相加然后将其和看做是一个地址值送往 eax 中(这样比 movl 更快)
         
         addl    16(%ebp), %eax
         addl    20(%ebp), %eax
         popl    %ebp
         ret
         .size   parm_to_register, .-parm_to_register
.globl main
         .type   main, @function
main:
         pushl   %ebp
         movl    %esp, %ebp
         subl    $32, %esp
         
         #main 中 4 个参数入栈保存
         movl    $1, -16(%ebp)         
         movl    $2, -12(%ebp)
         movl    $3, -8(%ebp)
         movl    $4, -4(%ebp)
         
         #照函数参数从右到左依次入栈保存
         movl    -4(%ebp), %eax
         movl    %eax, 12(%esp)
         movl    -8(%ebp), %eax
         movl    %eax, 8(%esp)
         movl    -12(%ebp), %eax
         movl    %eax, 4(%esp)
         movl    -16(%ebp), %eax
         movl    %eax, (%esp)
         
         call    parm_to_register
         movl    $0, %eax
         leave
         movl    $0, %eax
         addl    $20, %esp
         popl    %ebx
         popl    %ebp
         ret
         .size   main, .-main
         .ident  "GCC: (GNU) 4.4.4 20100726 (Red Hat 4.4.4-13)"
         .section        .note.GNU-stack,"",@progbits

下面用 regparm 指定函数参数使用 3 个寄存器,即 EAX, EDX, ECX 来保存。将上面 C 程序中的 __attribute__((regparm(0))) 改为 __attribute__((regparm(3))),然后再看一下反汇编:
.file   "regparm.c"
         .text
.globl parm_to_register
         .type   parm_to_register, @function
parm_to_register:
         pushl   %ebp
         movl    %esp, %ebp
         subl    $12, %esp
         
         #分别从 eax, edx, ecx 取出相应的参数值入栈
         movl    %eax, -4(%ebp)
         movl    %edx, -8(%ebp)
         movl    %ecx, -12(%ebp)
         
         #从栈中取出参数进行相加
         movl    -8(%ebp), %eax
         movl    -4(%ebp), %edx
         leal    (%edx,%eax), %eax
         addl    -12(%ebp), %eax
         addl    8(%ebp), %eax
         
         leave
         ret
         .size   parm_to_register, .-parm_to_register
.globl main
         .type   main, @function
main:
         pushl   %ebp
         movl    %esp, %ebp
         pushl   %ebx
         subl    $20, %esp
         
         # main() 中的 4 个参数入栈保存
         movl    $1, -20(%ebp)
         movl    $2, -16(%ebp)
         movl    $3, -12(%ebp)
         movl    $4, -8(%ebp)
         
         #使用 eax, ecx, edx 来传递参数。注意参数存入寄存器的顺序:(reg3-reg1)依次进入 ecx, edx, eax
         movl    -12(%ebp), %ecx
         movl    -16(%ebp), %edx
         movl    -20(%ebp), %eax
         
         #第 4 个参数仍然用栈来传递
         movl    -8(%ebp), %ebx
         movl    %ebx, (%esp)
        
          call    parm_to_register
         movl    $0, %eax
         addl    $20, %esp
         popl    %ebx
         popl    %ebp
         ret
         .size   main, .-main
         .ident  "GCC: (GNU) 4.4.4 20100726 (Red Hat 4.4.4-13)"
         .section        .note.GNU-stack,"",@progbits





欢迎光临 曲径通幽论坛 (http://www.groad.net/bbs/) Powered by Discuz! X3.2