我很困惑在哪里使用cmov
说明以及在汇编中使用jump
指令的位置?
从效果的角度来看:
如果可能,请通过示例解释他们的不同之处。
答案 0 :(得分:16)
movcc 是所谓的谓词指令。这就是说,这个指令在条件(谓词)"下执行。
在执行算术运算(尤其是比较指令)之后,许多处理器(包括x86)设置条件代码位以指示操作结果的状态。
条件跳转指令检查条件代码位的状态,如果为真,则跳转到指定的目标。
因为跳转是有条件的,并且处理器通常具有深度流水线,所以当CPU遇到jmp指令时,条件代码位实际上还没有准备好让jmp指令处理。芯片设计人员可以简单地等待管道耗尽(通常是很多时钟周期),然后执行jmp,但这会使处理器变慢。
相反,他们中的大多数人选择使用分支预测算法,该算法预测条件跳转的方式。处理器然后可以获取,解码和执行预测的分支(或不执行),并继续快速执行,条件是如果最终到达的条件代码位变为错误对于条件( branch mispredict ),处理器撤消分支后它所做的所有工作,并重新执行沿另一条路径运行的程序。
条件跳转对于流水线执行比正常数据依赖更难,因为它们可以更改流经管道的指令流中的下一条指令。这称为control dependency,而不是数据依赖(如add
,其中两个输入都是其他最近指令的输出)。
分支预测变得非常好,因为大多数分支往往偏向于他们的方向。 (大多数循环结束时的分支,通常会分支回到顶部)。所以大部分时间处理器都不必退出错误预测的工作。
如果分支的方向高度不可预测,则处理器将在大约50%的时间内猜错,因此必须退出工作。这很贵。
好的,现在,人们常常找到这样的代码:
cmp ...
jcc $
mov register1, register2
$: ; continue here
...
; use register1
如果分支预测器猜对了,那么无论分支走哪条,这段代码都很快。如果猜错了很多......哎哟。
因此条件移动指令。这是根据条件代码位有条件地移动数据的移动。我们可以改写上面的内容:
cmp ...
movcc register1, register2
$: ; continue here
...
; use register1
现在我们没有分支指令,因此没有错误预测使处理器撤消所有工作。由于没有控制依赖性,因此无论movcc
是mov
还是nop
,都需要提取和解码以下指令。管道可以保持完整而不预测条件并推测性地执行使用register1
的指令。 (你可以用这种方式构建一个CPU,但它会破坏movcc
的目的。)
movcc
将控件依赖项转换为数据依赖项。 CPU将其视为一个3输入数学指令,其输入为EFLAGS,其输入为两个"常规"输入(dest寄存器和源寄存器或存储器)。在x86上,adc
与cmovae
(mov if CF==0
)完全相同,无序执行跟踪依赖关系:输入是CF,还有两个GP寄存器。输出是目标寄存器。
对于x86,每个条件组合cc都有cmovcc
,jcc
和setcc
条指令。 (setcc
根据条件将目标设置为0或1。因此它对标志有数据依赖性,没有其他输入依赖性。)