当我只有一个4字节的局部变量时,为什么在这种反汇编中堆栈增加16个字节?

时间:2019-01-16 05:18:45

标签: c assembly x86 calling-convention gcc8

我很难理解为什么编译器选择用我编写的代码来补偿堆栈空间。

当我想出一个简单的代码使它困惑时,我正在研究Godbolt的Compiler Explorer,以研究C调用约定。

可以在this link中找到该代码。我选择了GCC 8.2 x86-64,但目标是x86处理器,这一点很重要。波纹管是C代码和由编译器资源管理器报告的生成程序集的转录。

// C code
int testing(char a, int b, char c) {
    return 42;
}

int main() {
    int x = testing('0', 0, '7');

    return 0;
}
; Generated assembly
testing(char, int, char):
        push    ebp
        mov     ebp, esp
        sub     esp, 8
        mov     edx, DWORD PTR [ebp+8]
        mov     eax, DWORD PTR [ebp+16]
        mov     BYTE PTR [ebp-4], dl
        mov     BYTE PTR [ebp-8], al
        mov     eax, 42
        leave
        ret
main:
        push    ebp
        mov     ebp, esp
        sub     esp, 16
        push    55
        push    0
        push    48
        call    testing(char, int, char)
        add     esp, 12
        mov     DWORD PTR [ebp-4], eax
        mov     eax, 0
        leave
        ret

从现在开始,从汇编列来看,据我所知,第15行负责为本地变量保留堆栈中的空间。问题是我只有一个本地int,并且偏移量是16个字节而不是4个字节。这感觉像是在浪费空间。

这与单词对齐有关吗?但是,即使是这样,如果通用寄存器的大小为4个字节,那么这种对齐方式是否也不应该与4个字节有关?

我看到的另一件奇怪的事情是关于char函数的本地testing的放置。如第7-8行所示,它们似乎在堆栈中每个占用4个字节,但只有低字节被操纵。为什么不每个都只使用1个字节?

这些选择可能是有目的的,我真的很想了解它们的目的(或是否没有目的)。也许我只是感到困惑而没有完全理解。

2 个答案:

答案 0 :(得分:2)

因此,通过评论,我可以弄清楚堆栈增长问题是由于@PeterCordes所说的i386 SystemV ABI要求引起的。

字符对齐的原因可能是由于GCC的默认行为以提高速度,如@ Ped7g的注释所推断。尽管不确定,但这对我来说已经足够了。

答案 1 :(得分:0)

由于以下几个原因,今天通常需要获取这种大小倍数的堆栈空间:

  • 缓存行通过在缓存中保留整个数据来支持这种行为。
  • 临时空间是预先分配的,避免在需要CPU临时存储的情况下使用推入和弹出指令。
  • 单独的推入和弹出指令通过要求在执行下一条指令之前更新数据来降低流水线执行。这样可以使连续指令之间的数据依赖关系解耦,并使它们能够更快地运行。

因此,实际的编译器会指定要以此方式设计的ABI。