困惑于简单的汇编代码(IA32)

时间:2016-05-22 16:17:43

标签: c assembly translation

考虑以下C函数:

void f1(int i) 
{ 
    int j=i+a; 
}

int f2(int i) 
{
    return i+a; 
}

以汇编语言翻译(由讲师提供):

#f1 translation :

subl $8, %esp 
movl 12(%esp), %eax 
movl %eax, 4(%esp) 
movl 4(%esp), %eax 
addl a, %eax 
movl %eax, (%esp) 
addl $8, %esp 
ret

#f2 translation :

subl $8, %esp 
movl 12(%esp), %eax 
movl %eax, 4(%esp) 
movl a, %eax 
movl %eax, (%esp) 
movl (%esp), %eax 
addl 4(%esp), %eax 
addl $8, %esp 
ret

我试图绘制并记下两个汇编代码的每一步,但我根本无法看到两者如何导致不同的C代码。

按照惯例,寄存器%eax 包含函数的返回值。如果我没弄错的话,寄存器%eax BOTH 汇编代码的末尾包含值(i + a),尽管 f1 不返回任何内容

1)为什么?究竟是什么告诉函数返回一个值?

此外,在这两个代码中,我们有两行,如下两行:

movl %eax, (%esp) 
movl (%esp), %eax

最后一个似乎是多余的, 2)或者不是吗?

2 个答案:

答案 0 :(得分:5)

如果ABI说EAX包含返回值,则返回某些内容的函数将返回值。如果函数没有返回任何内容,则寄存器可能包含任何内容。在这种情况下,它可能是相同的值,我没有阅读代码。

如果调用函数没有读取返回值,则该寄存器包含的内容无关紧要。所以关于调用者和被调用函数都是如此。他们必须遵守ABI。如果调用void函数,调用代码将永远不会尝试将该寄存器用作任何东西。

因此汇编代码中没有任何内容表示该函数返回了一些内容。它全部在C代码中。

至于2,MOV是多余的。这是因为你没有使用优化进行编译,所以编译器只会输出它想要的任何简单的东西,而且非常不理想。

答案 1 :(得分:1)

如果您在启用优化的情况下查看编译器输出,则更容易理解差异:

gcc 5.3 with -O3 -m32 on the Godbolt Compiler Explorer

int a = 1234;  // global, not static or const, so it has to get loaded from memory

void f1(int i) { int j=i+a; }
// 3 : warning: unused variable 'j' [-Wunused-variable]
    ret

int f2(int i) { return i+a; }
    movl    a, %eax         # load a
    addl    4(%esp), %eax   # add i from its arg-passing location on the stack
    ret
完全优化

f1,因为它没有外部可见效果(没有返回值,没有副作用)。当函数返回时,局部变量消失(超出范围),因此根本不需要计算它。 (因为它不是volatile

可能你的教授试图说明本地人如何存储在堆栈上。 (C调用"自动"存储,而不是动态(malloc)或静态(全局和static)。)

gcc -O0太吵了,不能很好地说明这一点,尤其是它将args从返回地址复制到本地人的方式。

gcc -O0主要只是将每个C语句直接转换为asm,而不考虑函数的其余部分。而且,变量在语句之间存储/重新加载,而不是保持在寄存器中。 (除非有时他们确实留在现场作为大表达的一部分)。

gcc -Og仅略微优化,并且相当好地对应于来源。它仍然将f1优化为空函数。 -O1也是如此。