我只是通过检查编译器生成的二进制代码来学习x86 asm.
在Visual Studio 2010 beta 2中使用C++编译器编译的代码.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.21003.01 for 80x86
Run Code Online (Sandbox Code Playgroud)
int mainCRTStartup()
{
int x=5;int y=1024;
while(x) { x--; y/=2; }
return x+y;
}
Run Code Online (Sandbox Code Playgroud)
cl /c /O2 /Oy- /MD sandbox.c
link /NODEFAULTLIB /MANIFEST:NO /SUBSYSTEM:CONSOLE sandbox.obj
Run Code Online (Sandbox Code Playgroud)
以下从入口点开始.
00401000 >/$ B9 05000000 MOV ECX,5
00401005 |. B8 00040000 MOV EAX,400
0040100A |. 8D9B 00000000 LEA EBX,DWORD PTR DS:[EBX]
00401010 |> 99 /CDQ
00401011 |. 2BC2 |SUB EAX,EDX
00401013 |. D1F8 |SAR EAX,1
00401015 |. 49 |DEC ECX
00401016 |.^75 F8 \JNZ SHORT sandbox.00401010
00401018 \. C3 RETN
Run Code Online (Sandbox Code Playgroud)
MOV ECX, 5 int x=5;
MOV EAX, 400 int y=1024;
LEA ... // no idea what LEA does here. seems like ebx=ebx. elaborate please.
// in fact, NOPing it does nothing to the original procedure and the values.
CQD // sign extends EAX into EDX:EAX, which here: edx = 0. no idea why.
SUB EAX, EDX // eax=eax-edx, here: eax=eax-0. no idea, pretty redundant.
SAR EAX,1 // okay, y/= 2
DEC ECX // okay, x--, sets the zero flag when reaches 0.
JNZ ... // okay, jump back to CQD if the zero flag is not set.
Run Code Online (Sandbox Code Playgroud)
这部分困扰我:
0040100A |. 8D9B 00000000 LEA EBX,DWORD PTR DS:[EBX]
00401010 |> 99 /CDQ
00401011 |. 2BC2 |SUB EAX,EDX
Run Code Online (Sandbox Code Playgroud)
您可以将其全部删除,EAX和ECX的值最终将保持不变.那么,这些指示的重点是什么?
AnT*_*AnT 11
整个东西
00401010 |> 99 /CDQ
00401011 |. 2BC2 |SUB EAX,EDX
00401013 |. D1F8 |SAR EAX,1
Run Code Online (Sandbox Code Playgroud)
代表着y /= 2
.你看,独立的SAR
不会像编译器作者那样执行带符号的整数除法.C++ 98标准建议有符号整数除法将结果SAR
舍入为0,而单独将向负无穷大舍入.(允许向负无穷大舍入,选择留给实现).为了对负操作数实现舍入为0,使用上述技巧.如果使用无符号类型而不是有符号类型,则编译器将仅生成单个移位指令,因为不会发生负除法问题.
诀窍很简单:对于负y
号扩展,将放置一个11111...1
in 的模式,EDX
实际上-1
是2的补码表示.如果原始值为负SUB
,EAX
则以下将有效地添加1 y
.如果原来y
是正(或0),EDX
将持有0
的符号扩展后EAX
会保持不变.
换句话说,当您y /= 2
使用signed进行编写时y
,编译器会生成执行更多类似操作的代码
y = (y < 0 ? y + 1 : y) >> 1;
Run Code Online (Sandbox Code Playgroud)
或更好
y = (y + (y < 0)) >> 1;
Run Code Online (Sandbox Code Playgroud)
请注意,C++标准不要求将除法结果舍入为零,因此即使对于有符号类型,编译器也有权进行单次移位.但是,通常编译器会遵循建议向零舍入(或提供控制行为的选项).
PS我不确定该LEA
指令的目的是什么.这确实是一个无操作.但是,我怀疑这可能只是插入代码中的占位符指令以进行进一步修补.如果我没记错的话,MS编译器有一个选项,强制在每个函数的开头和结尾插入占位符指令.今后该指令可以改写通过与该修补CALL
或JMP
将执行补丁代码指令.LEA
选择此特定原因仅仅是因为它产生了正确长度的无操作占位符指令.当然,它可能是完全不同的东西.
这lea ebx,[ebx]
只是一个NOP操作.它的目的是在内存中对齐循环的开头,这将使它更快.正如您在此处所看到的,循环的开始在地址0x00401010处开始,由于此指令,该地址可被16整除.
在CDQ
与SUB EAX,EDX
操作确保该司将向零舍入负数-否则SAR将磨圆下来,让不正确的结果为负数.
归档时间: |
|
查看次数: |
2600 次 |
最近记录: |