Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/assembly/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Assembly 为什么x86通常不允许目标寄存器不是第一个源寄存器?_Assembly_X86_Cpu Architecture_Riscv - Fatal编程技术网

Assembly 为什么x86通常不允许目标寄存器不是第一个源寄存器?

Assembly 为什么x86通常不允许目标寄存器不是第一个源寄存器?,assembly,x86,cpu-architecture,riscv,Assembly,X86,Cpu Architecture,Riscv,在RISC-V中,可以执行整数运算Regs[x1],几乎可以重复执行该运算来解释机器代码的原因(以及一般情况下的一些例外情况) 如果是的话,不允许独立目的地提供了什么效率 只是代码大小。这让其他一切都变得更糟,这就是为什么所有现代高性能设计都提供3操作数指令的原因,以及任何人如果从头开始重新设计x86-64以提高性能都会做什么 x86使用紧凑的可变长度指令编码,它或多或少是一个1操作数ISA,其中通常是累加器 可以说,作为CISC ISA,x86将其额外的编码空间用于内存源操作数,而不是单独的目

在RISC-V中,可以执行整数运算
Regs[x1],几乎可以重复执行该运算来解释机器代码的原因(以及一般情况下的一些例外情况)

如果是的话,不允许独立目的地提供了什么效率

只是代码大小。这让其他一切都变得更糟,这就是为什么所有现代高性能设计都提供3操作数指令的原因,以及任何人如果从头开始重新设计x86-64以提高性能都会做什么

x86使用紧凑的可变长度指令编码,它或多或少是一个1操作数ISA,其中通常是累加器

可以说,作为CISC ISA,x86将其额外的编码空间用于内存源操作数,而不是单独的目标操作数。虽然这只是一种说法,因为只有2位编码寄存器vs.[register]间接vs.[reg+disp8]vs.[reg+disp32]。剩下的空间就不在了,因为典型的指令只有2字节长,操作码+modrm。(加上前缀、立即数和/或寻址模式的额外字节)

有趣的是,16位的长度与ARM Thumb的长度相同,ARM Thumb也选择了2操作数编码,因为这样可以使指令保持较小,但有时需要更多指令。在最初的8086(尤其是带有半宽总线的8088)上,代码获取是主要的瓶颈,而节省代码字节通常会提高性能,而不管指令的数量如何

x86机器代码当时是一成不变的,我们仍然坚持使用它。这对于今天的CPU来说非常不方便,32位模式下的VEX和EVEX编码被其他指令的无效编码所掩盖;这是一个完全混乱和非常缓慢+能源密集型解码。e、 g.Intel CPU有一个单独的流水线阶段,只需在将指令长度/边界馈送到解码器之前查找指令长度/边界。这就是为什么现代CPU有一个解码的uop缓存,以避免在“热”代码区域重新解码,以及为什么因为这些长管道而需要良好的分支预测

任何抛弃2操作数编码以腾出更多空间的小修都会引发一个问题:为什么要保留任何遗留行李,为什么不从头开始?那么,为什么不是x86-64,为什么不是像AArch64这样干净的设计呢


还要注意的是and
ADDSD
是两个操作数的SSE指令。同一条指令的3操作数非破坏性目标编码是AVX的新功能,称为
VADDPD
/
VADDSD


MOV+ADD的效率
mov
/
add
(和shift)可以通过
lea
完成,例如
leaeax、[rdi+rsi*4]
来实现
返回x+y*4这样就解决了最常见指令的问题。看看x86-64优化的编译器输出

x86微体系结构在实践中并没有宏融合mov+op,尽管这在理论上是可能的。实际上,编译器确实需要使用大量的
mov-reg,reg
指令,但每个ALU指令的使用量明显少于1条。这还不够,硬件供应商还没有在解码时找到融合的机会。目前,它们仅将cmp/test+分支融合到单个uop中。(或者,也包括其他ALU+分支指令,如AND+分支或DEC+分支。)还包括内存源CISC指令中load+ALU UOP的微融合

问题/重命名时间确实会使关键路径的MOV+ALU对仍然只有1个周期的延迟。(尽管有时通过让关键路径使用原始路径和一些较短的延迟或独立的dep链使用副本可以获得相同的延迟好处。但这通常需要循环展开。)

然而,
mov
-消除对前端吞吐量没有帮助,也无助于减小故障窗口。对于管道的其余部分,MOV的成本与NOP相同


Haswell到Skylake的前端宽度与后端ALU执行单元的数量相同。即使使用Ice Lake和Zen(更宽的前端,仍然“仅”4个整数ALU执行单元),未消除的
mov
也很少会成为瓶颈。大多数代码包括临时存储或非微融合加载uop。

英特尔8086的两个操作数设计的最初动机是为了保持指令解码器的简单,其中目标和第一个操作数必须是同一寄存器。8086只有27000个晶体管。英特尔没有晶体管预算来实现三操作数指令集

虽然x86指令集经常被批评需要复杂的解码器,需要大量的晶体管,但这仅适用于尝试尽可能快地解码现代x86指令集的情况。正如最初的8086设计所示,它基本上不需要很多晶体管来解码基本指令集

在设计8086时,两个操作数的指令集没有什么不寻常之处。它的主要竞争对手68000也有两个操作数的指令集,IBM大型机也是如此。这实际上是对8位微处理器设计的改进,如Intel 8080,其晶体管预算小得多,通常实现一个操作数指令集,其中目标和第一个操作数始终是累加器

虽然双操作数指令集允许更紧凑的编码,但这并不是目标。英特尔做出的一些简化解码的设计决定实际上增加了代码大小。指令前缀占用了整个字节,有效地为指令编码添加了一些位。然而,通过将它们视为单字节指令,在处理器中设置隐藏的内部标志,它们很容易实现。用处不大
add x1,x2,x3
mov x1,x2
add x1,x3