所有计算都在寄存器中进行。为什么堆栈不在这里存储寄存器计算的结果
All the calculations take place in registers. Why is the stack not storing the result of the register computation here
我正在用 C++ 调试一个简单的代码,并查看反汇编。
在反汇编中,所有的计算都是在寄存器中完成的。稍后,返回操作结果。我只看到 a 和 b 变量被压入堆栈(代码如下)。我没有看到将结果 c 变量压入堆栈。我错过了什么吗?
我上网查了一下。但在互联网上,看起来所有变量 a、b 和 c 都应该被压入堆栈。但是在我的反汇编中,我没有看到结果变量 c 被压入堆栈。
C++代码:
#include<iostream>
using namespace std;
int AddMe(int a, int b)
{
int c;
c = a + b;
return c;
}
int main()
{
AddMe(10, 20);
return 0;
}
相关汇编代码:
int main()
{
00832020 push ebp
00832021 mov ebp,esp
00832023 sub esp,0C0h
00832029 push ebx
0083202A push esi
0083202B push edi
0083202C lea edi,[ebp-0C0h]
00832032 mov ecx,30h
00832037 mov eax,0CCCCCCCCh
0083203C rep stos dword ptr es:[edi]
0083203E mov ecx,offset _E7BF1688_Function@cpp (0849025h)
00832043 call @__CheckForDebuggerJustMyCode@4 (083145Bh)
AddMe(10, 20);
00832048 push 14h
0083204A push 0Ah
0083204C call std::operator<<<std::char_traits<char> > (08319FBh)
00832051 add esp,8
return 0;
00832054 xor eax,eax
}
如上所示,14h 和 0Ah 被压入堆栈 - 对应于 AddMe(10, 20);
但是,当我们查看 AddMe 函数的反汇编时,我们看到变量 c (c = a + b) 没有被压入堆栈。
反汇编中的 AddMe 片段:
…
int c;
c = a + b;
00836028 mov eax,dword ptr [a]
0083602B add eax,dword ptr [b]
0083602E mov dword ptr [c],eax
return c;
00836031 mov eax,dword ptr [c]
}
这个程序中c是不是应该压栈?我错过了什么吗?
__cdecl
调用约定,AddMe()
默认使用(取决于编译器的配置),需要参数在堆栈上传递。但是不需要将局部变量存储在堆栈中。只要保留代码的意图,编译器就可以使用寄存器作为优化。
All the calculations take place in registers.
是的,但它们是在之后存储的。
使用内存目标 add
而不是仅使用累加器寄存器 (EAX) 将是一种优化。当结果需要位于与表达式的任何输入不同的位置时,这是不可能的。
Why is the stack not storing the result of the register computation here
是的,只是与 push
不同
您在禁用优化(调试模式)的情况下进行编译,因此每个 C 对象在 asm 中确实有自己的地址,并且在 C 语句之间保持同步。即不将 C 变量保存在寄存器中。 (Why does clang produce inefficient asm with -O0 (for this simple floating point sum)?)。这是调试模式特别慢的原因之一:它不仅仅是避免优化,它还强制 store/reload.
但是编译器使用 mov
而不是 push
因为它不是函数参数。这是所有编译器共享的错过的优化,但在这种情况下,它甚至没有尝试优化。 ()。编译器当然有可能在存储它的同一指令中使用 push
为 c
保留 space。但是编译器改为在一个函数的入口处为所有局部变量分配堆栈 sub esp, constant
.
在溢出 c
到其堆栈槽的 mov dword ptr [c],eax
之前的某处,有一个 sub esp, 12
或为 [= 保留堆栈 space 的东西15=]. 在这种情况下,MSVC 使用虚拟 push
保留 4 个字节 space,作为对 sub esp, 4
.
的优化
在 MSVC asm 输出中,编译器将发出 c = ebp-4
行或将 c
定义为 ebp-4
的文本替换的内容。如果您查看反汇编,您只会看到 [ebp-4]
或任何寻址模式而不是。
在 MSVC asm 输出中,不要假设 [c]
指的是静态存储。 它实际上仍然是预期的堆栈 space,但是使用了偏移量的符号名称。
使用 32 位 MSVC 19.22 将您的代码放在 Godbolt compiler explorer 上,我们得到以下 asm,它仅使用符号 asm 常量作为偏移量,而不是整个寻址模式。所以 [c]
可能只是进一步简化的列表形式。
_c$ = -4 ; size = 4
_a$ = 8 ; size = 4
_b$ = 12 ; size = 4
int AddMe(int,int) PROC ; AddMe
push ebp
mov ebp, esp ## setup a legacy frame pointer
push ecx # RESERVE 4B OF STACK SPACE FOR c
mov eax, DWORD PTR _a$[ebp]
add eax, DWORD PTR _b$[ebp] # c = a+b
mov DWORD PTR _c$[ebp], eax # spill c to the stack
mov eax, DWORD PTR _c$[ebp] # reload it as the return value
mov esp, ebp # restore ESP
pop ebp # tear down the stack frame
ret 0
int AddMe(int,int) ENDP ; AddMe
我正在用 C++ 调试一个简单的代码,并查看反汇编。 在反汇编中,所有的计算都是在寄存器中完成的。稍后,返回操作结果。我只看到 a 和 b 变量被压入堆栈(代码如下)。我没有看到将结果 c 变量压入堆栈。我错过了什么吗?
我上网查了一下。但在互联网上,看起来所有变量 a、b 和 c 都应该被压入堆栈。但是在我的反汇编中,我没有看到结果变量 c 被压入堆栈。
C++代码:
#include<iostream>
using namespace std;
int AddMe(int a, int b)
{
int c;
c = a + b;
return c;
}
int main()
{
AddMe(10, 20);
return 0;
}
相关汇编代码:
int main()
{
00832020 push ebp
00832021 mov ebp,esp
00832023 sub esp,0C0h
00832029 push ebx
0083202A push esi
0083202B push edi
0083202C lea edi,[ebp-0C0h]
00832032 mov ecx,30h
00832037 mov eax,0CCCCCCCCh
0083203C rep stos dword ptr es:[edi]
0083203E mov ecx,offset _E7BF1688_Function@cpp (0849025h)
00832043 call @__CheckForDebuggerJustMyCode@4 (083145Bh)
AddMe(10, 20);
00832048 push 14h
0083204A push 0Ah
0083204C call std::operator<<<std::char_traits<char> > (08319FBh)
00832051 add esp,8
return 0;
00832054 xor eax,eax
}
如上所示,14h 和 0Ah 被压入堆栈 - 对应于 AddMe(10, 20);
但是,当我们查看 AddMe 函数的反汇编时,我们看到变量 c (c = a + b) 没有被压入堆栈。
反汇编中的 AddMe 片段:
…
int c;
c = a + b;
00836028 mov eax,dword ptr [a]
0083602B add eax,dword ptr [b]
0083602E mov dword ptr [c],eax
return c;
00836031 mov eax,dword ptr [c]
}
这个程序中c是不是应该压栈?我错过了什么吗?
__cdecl
调用约定,AddMe()
默认使用(取决于编译器的配置),需要参数在堆栈上传递。但是不需要将局部变量存储在堆栈中。只要保留代码的意图,编译器就可以使用寄存器作为优化。
All the calculations take place in registers.
是的,但它们是在之后存储的。
使用内存目标 add
而不是仅使用累加器寄存器 (EAX) 将是一种优化。当结果需要位于与表达式的任何输入不同的位置时,这是不可能的。
Why is the stack not storing the result of the register computation here
是的,只是与 push
您在禁用优化(调试模式)的情况下进行编译,因此每个 C 对象在 asm 中确实有自己的地址,并且在 C 语句之间保持同步。即不将 C 变量保存在寄存器中。 (Why does clang produce inefficient asm with -O0 (for this simple floating point sum)?)。这是调试模式特别慢的原因之一:它不仅仅是避免优化,它还强制 store/reload.
但是编译器使用 mov
而不是 push
因为它不是函数参数。这是所有编译器共享的错过的优化,但在这种情况下,它甚至没有尝试优化。 (push
为 c
保留 space。但是编译器改为在一个函数的入口处为所有局部变量分配堆栈 sub esp, constant
.
在溢出 c
到其堆栈槽的 mov dword ptr [c],eax
之前的某处,有一个 sub esp, 12
或为 [= 保留堆栈 space 的东西15=]. 在这种情况下,MSVC 使用虚拟 push
保留 4 个字节 space,作为对 sub esp, 4
.
在 MSVC asm 输出中,编译器将发出 c = ebp-4
行或将 c
定义为 ebp-4
的文本替换的内容。如果您查看反汇编,您只会看到 [ebp-4]
或任何寻址模式而不是。
在 MSVC asm 输出中,不要假设 [c]
指的是静态存储。 它实际上仍然是预期的堆栈 space,但是使用了偏移量的符号名称。
使用 32 位 MSVC 19.22 将您的代码放在 Godbolt compiler explorer 上,我们得到以下 asm,它仅使用符号 asm 常量作为偏移量,而不是整个寻址模式。所以 [c]
可能只是进一步简化的列表形式。
_c$ = -4 ; size = 4
_a$ = 8 ; size = 4
_b$ = 12 ; size = 4
int AddMe(int,int) PROC ; AddMe
push ebp
mov ebp, esp ## setup a legacy frame pointer
push ecx # RESERVE 4B OF STACK SPACE FOR c
mov eax, DWORD PTR _a$[ebp]
add eax, DWORD PTR _b$[ebp] # c = a+b
mov DWORD PTR _c$[ebp], eax # spill c to the stack
mov eax, DWORD PTR _c$[ebp] # reload it as the return value
mov esp, ebp # restore ESP
pop ebp # tear down the stack frame
ret 0
int AddMe(int,int) ENDP ; AddMe