x86汇编语言中,cmp指令常用于两数值之间比较大小,test指令常用于位级判断。那么,test可不可以也用于比较数值大小呢?
提出问题
今天有朋友向我提出了这样的问题,
为什么test
还有这种操作,set
指令和test
究竟是怎么凑到一起的。
查找信息
CMP指令与SUB指令的行为是一样的。在ATT格式中,列出操作数的顺序是相反的,这使代码有点难读。如果两个操作数相等,这些指令会将零标志位设置为1,而其他的标志可以用来确定两个操作数之间的大小关系。
TEST指令的行为和AND指令一样,除了它们只设置条件码而不改变目的寄存器的值。典型的用法是,两个操作数是一样的(例如,testq %rax, %rax用来检查%rax是负数,零,还是正数),或其中的一个操作数是一个掩码,用来指示哪些位应该被测试。
唔,看来课本只是重复了我们的问题,并没有给出解答。
典型的用法是,两个操作数是一样的(例如,testq %rax, %rax用来检查%rax是负数,零,还是正数)
但从这可以看出,test
确实有这个用法。
这里找到线索,set
指令是和条件码直接关联的。那么就从test
如何影响和改变条件码入手吧。
推导
机器是通过某些指令来改变条件码,然后通过判断条件码的组合来完成控制操作。个人理解是,这些控制指令是对条件码判断的一种封装。
回到我们纠结的这个问题上面:为什么test
可以用于判断大小,以及 如何形成判断。
cmp
的行为相对容易理解,右操作数减去左操作数,然后修改CF
ZF
SF
OF
的值。
而test
则是两个操作数进行与
,不会影响到OF
CF
,仅能改变SF
ZF
,确定真的可以用来比较大小吗?
不过,test
用于比较大小的范围极窄,仅仅是用于判断符号,也就是和数值0
作比较。只有这种特殊情况的话,进行推理则方便多了,大不了穷举嘛。
下面就来对比test S, S
和cmp $0,S
。
当S
看作有符号数,且大于0
时:
test S, S
,SF
置0,ZF
置0,OF
置0cmp $0,S
,SF
置0,ZF
置0,OF
置0
当S
看作有符号数,且等于0
时:
test S, S
,SF
置0,ZF
置1,OF
置0cmp $0,S
,SF
置0,ZF
置1,OF
置0
当S
看作有符号数,且小于0
时:
test S, S
,SF
置1,ZF
置0,OF
置0cmp $0,S
,SF
置1,ZF
置0,OF
置0
当S
看作无符号数,且等于0
时:
test S, S
,ZF
置1,CF
置0cmp $0,S
,ZF
置1,CF
置0
当S
看作无符号数,且不等于0
时:
test S, S
,ZF
置0,CF
置0cmp $0,S
,ZF
置0,CF
置0
惊奇地发现这两句指令的行为是一模一样的。原因在于cmp
在与0
作比较时,是不可能发生溢出和进(借)位的,因而不可能影响OF
CF
位,这和test
指令是一样的。剩下的SF
和ZF
很容易推理,这里不赘述。
实验
对这样一段C代码进行汇编处理。
不进行优化
|
|
得到汇编代码(节选):
进行 O1 优化
|
|
得到汇编代码(节选):
事实证明,test
确实可以用于与0
进行大小比较,且编译器更愿意优化成这种形式。
结论与反思
当我们难以理解指令的逻辑时,则需要去探究它背后具体的操作过程,才能真正地理解操作的实际效果。