混淆了简单的汇编代码(IA32)
Confused with simple assembly code (IA32)
考虑以下 C 函数:
void f1(int i)
{
int j=i+a;
}
int f2(int i)
{
return i+a;
}
及其汇编语言的翻译(由教师提供):
#f1 translation :
subl , %esp
movl 12(%esp), %eax
movl %eax, 4(%esp)
movl 4(%esp), %eax
addl a, %eax
movl %eax, (%esp)
addl , %esp
ret
#f2 translation :
subl , %esp
movl 12(%esp), %eax
movl %eax, 4(%esp)
movl a, %eax
movl %eax, (%esp)
movl (%esp), %eax
addl 4(%esp), %eax
addl , %esp
ret
我试图画出并记下这两个汇编代码的每一步,但我根本看不出这两个代码如何导致不同的 C 代码。
按照惯例,寄存器%eax 包含函数的返回值。如果我没记错的话,寄存器 %eax 在 BOTH[ 的末尾包含值 (i+a) =38=]汇编代码虽然f1returns什么都没有。
1) 这是为什么呢?究竟是什么告诉一个函数正在返回一个值?
此外,在这两个代码中,我们都有两行,如下所示:
movl %eax, (%esp)
movl (%esp), %eax
最后一个好像是多余的,2)是不是?
如果 ABI 说 EAX
包含 return 值,那么函数 return 某些东西将在那里具有 return 值。如果函数没有 return 任何内容,则寄存器可能包含任何内容。在这种情况下它可能是相同的值,我没有阅读代码。
如果调用函数不读取 return 值,则该寄存器包含的内容无关紧要。所以这都是关于调用者和被调用函数的。他们必须遵守 ABI。如果调用 void 函数,调用代码将永远不会尝试将该寄存器用作任何东西。
所以汇编代码中没有任何内容说明函数 return 是什么。都在C代码里了。
至于2,MOV
是多余的。这是因为您没有进行优化编译,所以编译器只会输出它想要的任何简单的东西,而且非常不理想。
如果您在启用优化的情况下查看编译器输出,就会更容易理解其中的区别:
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
被完全优化掉了,因为它没有外部可见的效果(没有 return 值,也没有副作用)。当函数 returns 时,局部变量消失(超出范围),因此根本不需要计算它。 (因为它不是 volatile
)
可能您的教授试图说明局部变量是如何存储在堆栈中的。 (C 称之为 "automatic" 存储,与动态(malloc)或静态(全局变量和 static
)相对。)
gcc -O0
太吵了,无法很好地说明这一点,尤其是。它将 args 从 return 地址上方复制到 locals.
的方式
gcc -O0
大多只是将每个 C 语句直接翻译成 asm,而不考虑函数的其余部分。而且,变量在语句之间存储/重新加载,而不是留在寄存器中。 (除了有时它们确实作为大型表达式的一部分保持活动状态)。
gcc -Og
仅略微优化,与源代码相对应。它仍然将 f1
优化为一个空函数。 -O1
.
也是
考虑以下 C 函数:
void f1(int i)
{
int j=i+a;
}
int f2(int i)
{
return i+a;
}
及其汇编语言的翻译(由教师提供):
#f1 translation :
subl , %esp
movl 12(%esp), %eax
movl %eax, 4(%esp)
movl 4(%esp), %eax
addl a, %eax
movl %eax, (%esp)
addl , %esp
ret
#f2 translation :
subl , %esp
movl 12(%esp), %eax
movl %eax, 4(%esp)
movl a, %eax
movl %eax, (%esp)
movl (%esp), %eax
addl 4(%esp), %eax
addl , %esp
ret
我试图画出并记下这两个汇编代码的每一步,但我根本看不出这两个代码如何导致不同的 C 代码。
按照惯例,寄存器%eax 包含函数的返回值。如果我没记错的话,寄存器 %eax 在 BOTH[ 的末尾包含值 (i+a) =38=]汇编代码虽然f1returns什么都没有。
1) 这是为什么呢?究竟是什么告诉一个函数正在返回一个值?
此外,在这两个代码中,我们都有两行,如下所示:
movl %eax, (%esp)
movl (%esp), %eax
最后一个好像是多余的,2)是不是?
如果 ABI 说 EAX
包含 return 值,那么函数 return 某些东西将在那里具有 return 值。如果函数没有 return 任何内容,则寄存器可能包含任何内容。在这种情况下它可能是相同的值,我没有阅读代码。
如果调用函数不读取 return 值,则该寄存器包含的内容无关紧要。所以这都是关于调用者和被调用函数的。他们必须遵守 ABI。如果调用 void 函数,调用代码将永远不会尝试将该寄存器用作任何东西。
所以汇编代码中没有任何内容说明函数 return 是什么。都在C代码里了。
至于2,MOV
是多余的。这是因为您没有进行优化编译,所以编译器只会输出它想要的任何简单的东西,而且非常不理想。
如果您在启用优化的情况下查看编译器输出,就会更容易理解其中的区别:
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
被完全优化掉了,因为它没有外部可见的效果(没有 return 值,也没有副作用)。当函数 returns 时,局部变量消失(超出范围),因此根本不需要计算它。 (因为它不是 volatile
)
可能您的教授试图说明局部变量是如何存储在堆栈中的。 (C 称之为 "automatic" 存储,与动态(malloc)或静态(全局变量和 static
)相对。)
gcc -O0
太吵了,无法很好地说明这一点,尤其是。它将 args 从 return 地址上方复制到 locals.
gcc -O0
大多只是将每个 C 语句直接翻译成 asm,而不考虑函数的其余部分。而且,变量在语句之间存储/重新加载,而不是留在寄存器中。 (除了有时它们确实作为大型表达式的一部分保持活动状态)。
gcc -Og
仅略微优化,与源代码相对应。它仍然将 f1
优化为一个空函数。 -O1
.