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进行大小比较,且编译器更愿意优化成这种形式。
结论与反思
当我们难以理解指令的逻辑时,则需要去探究它背后具体的操作过程,才能真正地理解操作的实际效果。