Dav*_*542 5 x86 assembly cpu-architecture instructions conditional-move
我注意到条件移动指令的可扩展性比普通的mov. 例如,它不支持立即数,也不支持寄存器的低字节。
出于好奇,为什么该Cmov命令比一般mov命令的限制性要大得多?例如,为什么两者不允许这样的事情:
mov $2, %rbx # allowed
cmovcc $1, %rbx # I suppose setcc %bl could be used for the '1' immediate case
Run Code Online (Sandbox Code Playgroud)
附带说明一下,我注意到在使用 Compiler Explorer 时, 的使用量比和cmovcc少得多。通常是这种情况吗?如果是,为什么它的使用频率低于其他条件句?jmpccsetcc
作为条件,它已经需要 16 个不同的操作码用于表单cmov r, r/m,每个不同的cc条件一个,就像jccand setcc(当然,同义词共享一个操作码)。
因此,即使有“空间”容纳另外 16 个0F xx操作码,当英特尔为 Pentium Pro 添加它时,也可能不值得花费所有编码空间。好吧,也许是一个符号扩展的 imm8 形式。这将占用其他新操作码的空间,例如 MMX 和 SSE 指令,英特尔可能已经开始设计这些指令,或者至少在 P6 的 ISA 扩展最终确定时考虑用于 Pentium-MMX 和 Pentium III。
当您完全需要 a 时(通常是有条件地将某些内容归零),imm8 形式在大多数情况下会很有用cmov,但这不是必需的。RISC 理念(英特尔在 P6 1中倾向于这种理念)倾向于仅提供一种方式,并在需要时让代码使用 mov-immediate 在另一个寄存器中创建常量。
无序执行通常可以隐藏mov-immediate 将常量放入另一个寄存器的成本。这样的指令独立于其他指令,并且只要它预定的执行端口上有空闲周期就可以执行。(然而,前端通常是一个真正的瓶颈,静态代码大小确实很重要,所以不幸的是它不是免费的。)
脚注 1:RISC 思想对于 P6 微架构来说是一件大事,最引人注目的是革命性的思想,即将 x86 指令解码为 1 个或多个 uops,用于其类似 RISC 的后端,从而允许对一个内存的不同部分进行无序执行-例如,目标指令(加载/ALU/存储)。
但在较小的决策中,例如 P6 没有硬件支持来维持一条指令的微指令之间的 TLB 一致性。这就是为什么adc %reg, (mem)需要比您在 Intel CPU 上预期的更多的微指令。Andy Glew(曾从事 P6 工作的英特尔架构师)在 Stack Overflow 评论中解释道(我在本回答中引用了该评论),其中包括说“当我加入 P6 时,我是 RISC 支持者,我的态度是“让 SW(微码)做它”。'
很容易看出这种态度如何扩展到 x86 ISA 设计,并且只提供cmov. (8 位几乎没有必要;您总是可以移动整个寄存器,并且您通常希望避免高性能代码中的部分寄存器,因为可能会出现停顿。这在 PPro 上比后来的 P6(如 Core 2)上的成本更高。 Sandybridge 系列使部分寄存器合并变得更加便宜。)
但这纯粹是我对哪些因素可能影响设计决策的猜测。
imm8添加晶体管来解码 cmov 、imm32和/或编码的成本(在功率和芯片面积以及可实现的时钟速度方面)r/m8必须与能够使用它的代码所预期的实际加速进行权衡。 以及对抗未来使用更多操作码编码空间的成本。
除了未来编码空间的成本(这让 MMX 和 SSE1 指令只有 2 字节操作码)之外,英特尔可能在这一点上猜测错误,忽略了cmov $sign_extended_imm8, %reg实际上经常有用的内容。
它的使用较少,因为它仅在计算条件两侧的结果并选择其中之一的成本较低时才有用,而不是仅仅分支并只执行其中之一。它作为一种优化很有用,特别是当编译器预计分支预测效果不佳时。 x86 汇编中 cmove 指令的用途?
关于控制依赖关系(分支)与数据依赖关系 (cmov) 的更一般的 CPU 架构背景:条件指令 (cmov) 和跳转指令之间的差异
请参阅GCC 编译器中的条件移动 (cmov)回复:当 GCC 将 if 转换为无分支汇编时。
cmov如果你做错了,使用甚至会造成伤害( gcc 优化标志 -O3 使代码比 -O2 慢),对于分支预测可以非常准确地预测的情况(例如,在排序输入数据的特殊情况下)。
在具有更短/更窄的管道和更小的乱序执行资源(因此错误预测的成本更低)的旧 CPU 上,CMOV 在更少的情况下有用。特别是在 Broadwell 之前的 Intel 上,它需要 2 uops 而不是 1。Linux Torvalds 解释了为什么它在很多常见情况下都很糟糕,并在 2007 年对 Core 2 CPU 进行了一些测试:https: //yarchive.net/comp/linux /cmov.html
不过,如果您编写的代码根据条件从几个值中进行选择,那么编译器生成它的情况当然并不罕见。Clang 的启发式倾向于使用比 GCC 更多的 cmov,即更积极的 if 转换到无分支。
请注意,它setcc也不会被经常使用,除非您经常查看返回布尔值的函数的非内联版本。
我libperl.so在 Arch Linux 桌面上进行了反汇编(只是随机选择了一个大型二进制文件),由 GCC 10.1.0 编译。总共 377835 条指令中 ( objdump -d | egrep ' +[0-9a-f]+:'| wc -l):
setcc出现了 1783 次,经常出现在//setxx a在多个条件下执行一个分支。setxx bor a,b
cmovcc出现1737次。 objdump -drwC -Mintel /usr/lib/perl5/5.32/core_perl/CORE/libperl.so | egrep 'cmov[a-z]+ ' | wc
| 归档时间: |
|
| 查看次数: |
2024 次 |
| 最近记录: |