什么决定gcc是否为对齐做了一些事情?;

什么决定gcc是否为对齐做了一些事情?;,gcc,memory-alignment,stack-frame,Gcc,Memory Alignment,Stack Frame,有两个简单的C源文件。第一个是mainswap.c: void swap(int *x, int *y); int main() { int a, b; a = 5; b = 44; swap(&a, &b); return 0; } void swap(int *x, int *y) { int temp; temp = *x; *x = *y; *y = temp; } main: push

有两个简单的C源文件。第一个是mainswap.c:

void swap(int *x, int *y);

int main()
{
    int a, b;

    a = 5; 
    b = 44;
    swap(&a, &b);

    return 0;
}

void swap(int *x, int *y)
{
    int temp;

    temp = *x;
    *x = *y;
    *y = temp;
}
main:
pushl   %ebp
movl    %esp, %ebp
andl    $-16, %esp
subl    $32, %esp
movl    $5, 24(%esp)
movl    $44, 28(%esp)
leal    28(%esp), %eax
movl    %eax, 4(%esp)
leal    24(%esp), %eax
movl    %eax, (%esp)
call    swap
movl    $0, %eax
leave
ret
另一个是mainfoobar.c:

int bar(int x, int y)
{
    int z = x + y;
    return z;
}

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

int main(void)
{
    foo(2, 3);
    return 0;
}
main:
pushl   %ebp
movl    %esp, %ebp
subl    $8, %esp
movl    $3, 4(%esp)
movl    $2, (%esp)
call    foo
movl    $0, %eax
leave
ret
我得到了两者的可重定位对象文件。我发现gcc在
mainswap.c
中对函数
main
的堆栈框架进行了对齐,而在
mainfoobar.c
中,gcc并没有对函数
main
进行显式对齐

mainswap.c的主要功能:

void swap(int *x, int *y);

int main()
{
    int a, b;

    a = 5; 
    b = 44;
    swap(&a, &b);

    return 0;
}

void swap(int *x, int *y)
{
    int temp;

    temp = *x;
    *x = *y;
    *y = temp;
}
main:
pushl   %ebp
movl    %esp, %ebp
andl    $-16, %esp
subl    $32, %esp
movl    $5, 24(%esp)
movl    $44, 28(%esp)
leal    28(%esp), %eax
movl    %eax, 4(%esp)
leal    24(%esp), %eax
movl    %eax, (%esp)
call    swap
movl    $0, %eax
leave
ret
mainfoobar.c的主要功能:

int bar(int x, int y)
{
    int z = x + y;
    return z;
}

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

int main(void)
{
    foo(2, 3);
    return 0;
}
main:
pushl   %ebp
movl    %esp, %ebp
subl    $8, %esp
movl    $3, 4(%esp)
movl    $2, (%esp)
call    foo
movl    $0, %eax
leave
ret
我知道,
andl$-16、%esp
subl$32、%esp
的意图。相同的gcc版本,相同的选项,相同的计算机,唯一的区别是C源文件。我的问题是为什么gcc对这两个主要功能有不同的看法,这一现象的背后是什么

此外,我使用的gcc版本为:

gcc (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3
Copyright © 2011 Free Software Foundation, Inc.

顺便说一下,在mainswap.c的函数
main
中,我认为
subl$16,%esp
也将满足对齐和使用需求,为什么gcc会浪费16个字节?

似乎您在未启用优化的情况下编译了这两个字节。这就解释了您所看到的一些行为,因为在这种模式下,GCC通常会自上而下地处理文件,而不会尝试跨函数内联或优化。但是,当它到达一个函数时,它将对它所知道的一切起作用

在您的两个程序中,
main
显示在它调用的函数之前或之后。这似乎是这里的关键

如果将mainfoobar.c更改为如下所示:

int main(void)
{
    foo(2, 3);
    return 0;
}

int bar(int x, int y)
{
    int z = x + y;
    return z;
}

int foo(int a, int b)
{
    return bar(a, b);
}
您将在两个版本的输出中看到类似的堆栈对齐操作。我怀疑两者之间的区别在于编译器是否在代码生成过程中到达
main
时看到了被调用方。当
main
排在第一位时,它生成的堆栈帧比
main
排在所有被调用方之后时更保守

显然,随着SSE的出现,GCC的32位x86 ABI现在需要跨函数调用边界的16字节对齐,除非它能证明不是这样。我不知道这有多严格。并且,正如上面的示例所示,当GCC可以证明不需要更严格的对齐时,它会放松对齐


通过快速的谷歌搜索,这讨论了一些问题,并给出了一些其他的提示。是的,这是一个关于FreeBASIC的错误报告,但它直接涉及到这个问题。

你用什么标志编译这个?FWIW,我用稍旧的Ubuntu GCC版本进行了本地测试,
GCC版本4.4.5(Ubuntu/Linaro 4.4.4-14ubuntu5.1)
,但我的结果似乎与你的一致。根据FreeBASIC线程,16字节首选堆栈对齐显示在GCC4.1左右。正如我上面所说,我不确定这有多严格。不过,FreeBASIC线程读起来相当有趣。