条件设置指令的形式是:SETcc r/m8,其中r/m8是表示8位寄存器或单字节内存单元.
条件设置指令根据处理器定义的16种条件cc,测试一些标志位,然后把结果记录到目标操作数中.当条件满足时,目标操作数会被置1,否则置0.这16种条件与条件转移指令jcc中的条件是一样的.
条件设置指令可以用来消除程序中的转移指令.在C语言里,常会见到执行以下功能的语句:
c=(a<B)?C1:C2;
如果允许出现条件分支,编译器会产生如下的代码或者是非常类似的代码:
- cmpa,b
- moveax,c1
- moveax,c2
- @@:
如果使用条件设置指令,编译器将会产生不包含条件分支的逻辑判断代码:
- xoreax,eax
- cmpa,b
- setgeal;a>=b,则al置1,否则al置0
- deceax
- addeax, (c1-c2)
- addeax,c2
也可用条件转移传输指令cmov或fcmov去除程序中的转移指令,但是他们仅仅被Pentium Pro以后的处理器支持.实现同样功能的代码入下:
- moveax,c2
- cmpa,b
- cmov1eax,c1
一些编译器优化时,在不改变原逻辑的情况下,使用数学技巧把源代码中的一些逻辑分支转变成算术操作,消除或减少程序中出现的条件转移指令.可以提高CPU的流水线性能
看一段C的代码:
- int main()
- {
- if(FindWindow(NULL,/"计算器/"))
- return 1;
- else
- return 2;
- }
用VC编译连接后,其反汇编代码如下:
- 00401000/$68 30604000 PUSH 4_5_5.00406030; /Title = /"计算器/"
- 00401005|.6A 00 PUSH 0 ; |Class = 0
- 00401007|.FF15 9C504000 CALL DWORD PTR DS:[<&USER32.FindWindowA>>;
- 0040100D|.F7D8NEG EAX
- 0040100F|.1BC0SBB EAX,EAX
- 00401011|.24 FC AND AL,0FC
- 00401013|.83C0 05 ADD EAX,2
- 00401016//.C3RETN
编译生成的代码没有一句跳转转移指令,却实现原程序的逻辑.代码首先用neg指令检查eax是否为0,结果存放在CF标志位中.sbb指令将目的操作数减去源操作数,再减去减去借位CF(进位),结果送到目的操作数/"sbb eax,eax/"这句的结果由CF决定,当CF为1时,eax为-1,否则为0,用伪码来表示:
- if(eax)
- cf=1;
- esle
- cf=0;
- eax=-cf;
接下来两句指令根据eax的值:FFFFFFFh和0来决定最终结果,当eax是FFFFFFFFh的结果是1;当eax是0时,计算结果为2.
- 00401011|.24 FC AND AL,0FC
- 00401013|.83C0 05 ADD EAX,2
这类代码比较常见,当知道是条件转移转移指令优化生成的,还原就比较简单了.