简体   繁体   English

与简单的汇编代码(IA32)混淆

[英]Confused with simple assembly code (IA32)

Consider the following C functions : 考虑以下C函数:

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

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

and their translation (given by instructor) in assembly language : 及其汇编语言的翻译(由教师提供):

#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

I've tried to draw and write down each step of the two assembly code but I simply cannot see how the two lead to different C codes. 我试图绘制并写下两个汇编代码的每个步骤,但我根本看不到两者如何导致不同的C代码。

By convention, the register %eax contains the returned value of the function. 按照约定,寄存器%eax包含函数的返回值 If I'm not mistaken, the register %eax contains the value (i+a) at the end of BOTH assembly code although f1 returns nothing . 如果我没记错的话,尽管f1不 返回任何内容 ,但寄存器%eax都两个汇编代码的末尾都包含值(i + a)

1) Why's that? 1)为什么呢? What exactly tells that a function is returning a value? 究竟是什么表明函数正在返回值?

Also, in both codes, we have two lines like this two : 另外,在这两个代码中,我们都有两行像这样的两行:

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

the last one seems to be redundant, 2) or isn't it? 最后一个似乎多余, 2)不是吗?

If the ABI says EAX contains the return value, functions that return something will have the return value there. 如果ABI说EAX包含返回值,则返回某些内容的函数将在那里具有返回值。 If a function does not return anything, the register may contain whatever. 如果函数不返回任何内容,则寄存器可以包含任何内容。 In this case it might be the same value, I didn't read the code. 在这种情况下,它可能是相同的值,我没有阅读代码。

If the calling function doesn't read the return value it doesn't matter what that register contains. 如果调用函数不读取返回值,则该寄存器包含的内容无关紧要。 So it is all about the caller and the called function. 因此,这全部与调用方和被调用函数有关。 They must obay the ABI. 他们必须服从ABI。 If a void function is called, the calling code will never try to use that register as anything. 如果调用了void函数,则调用代码将永远不会尝试将该寄存器用作任何东西。

So nothing in the assembly code says the function returns something. 因此,汇编代码中没有任何内容表明该函数返回了某些东西。 It's all in the C code. 全部在C代码中。

As for 2, the MOV is redundant. 至于2, MOV是冗余的。 It is because you didn't compile with optimizations so the compiler will just output whatever simple things it wants and is very much not optimal. 这是因为您没有使用优化进行编译,因此编译器将仅输出所需的任何简单内容,并且不是最佳选择。

It's much easier to understand the difference if you look at compiler output with optimizations enabled: 如果查看启用了优化的编译器输出,则更容易理解它们之间的区别:

gcc 5.3 with -O3 -m32 on the Godbolt Compiler Explorer : 在Godbolt编译器资源管理器上带有-O3 -m32 gcc 5.3

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 is optimized away completely, because it has no externally-visible effects (no return value and no side-effects). f1已完全优化,因为它没有外部可见的效果(没有返回值,也没有副作用)。 Local variables disappear (go out of scope) when a function returns, so there's no need to compute it at all. 函数返回时,局部变量消失(超出范围),因此根本不需要计算它。 (Since it's not volatile ) (因为它不是volatile

Probably your prof was trying to illustrate how locals are stored on the stack. 可能是您的教授试图说明本地存储在堆栈中的方式。 (What C calls "automatic" storage, as opposed to dynamic (malloc) or static (globals and static ).) (与动态(malloc)或静态(globals和static )相反,C称为“自动”存储。)

gcc -O0 is too noisy to be a good illustration of that, esp. gcc -O0太嘈杂,无法很好地说明这一点,尤其是。 the way it copies args from above the return address to locals. 它从返回地址上方将args复制到本地的方式。

gcc -O0 mostly just translates each C statement directly to asm, without considering the rest of the function. gcc -O0通常只将每个C语句直接转换为asm,而无需考虑其余功能。 And also, variables are stored / reloaded between statements, instead of staying live in registers. 而且,变量在语句之间存储/重载,而不是保持在寄存器中。 (Except sometimes they do stay live as part of a large expression). (除了有时它们确实作为大表情的一部分而活着)。

gcc -Og is only slightly optimized, and corresponds to the source fairly well. gcc -Og只是略微优化,并且与源代码相当吻合。 It does still optimize f1 to an empty function. 仍然会将f1优化为空函数。 So does -O1 . -O1

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM