为什么在64模式下默认操作数大小为32位?

问题描述 投票:-1回答:1

我正在阅读英特尔文档,第一卷。 1并且有一章3.6.164位模式下的操作数大小和地址大小。有三个前缀REX.W,操作数大小66和地址大小67前缀。并且提到了操作数默认为32位大小。并且只能用REX.W指令前缀(在其他前缀之后)对其进行更改以使其长度为64位。

我不知道为什么,为什么不能将完整的64位空间用于int操作数?它与标志有关吗?还是为什么有这个限制? (因此,C unsigned int是否将REX.W前缀与int上的运算一起使用(如前所述,前缀仅对特定指令有效,而对整个段无效,前缀应为(大小,或者地址或操作数的默认值,并包含在段描述符中)。

我理解正确吗?

assembly 64-bit x86-64 machine-code
1个回答
2
投票

TL:DR:您有2个独立的问题。第一个关于C类型的大小,另一个关于x86-64机器代码如何对32位和64位操作数大小进行编码。编码选择是相当任意的,并且可以做得不同。但是int是32位的,因为这是编译器开发人员选择的,与机器代码无关。


int是32位的,因为这仍然是有用的大小。它使用int64_t的一半内存带宽/缓存占用空间。大多数用于64位ISA的C实现都具有32位int,包括用于x86-64(x86-64 System V和Windows)的两种主流ABI。在Windows上,甚至long都是32位类型,大概是为了与为32位编写的代码(具有关于类型大小的假设)的源代码兼容性。

此外,当时的AMD整数乘法器对于32位而言要比64位要快一些,直到Ryzen时都是这样。 (第一代AMD64芯片是AMD的K8微体系结构;有关指令表,请参见https://agner.org/optimize/。)

The advantages of using 32bit registers/instructions in x86-64

x86-64由AMD在2000年左右设计为AMD64。英特尔致力于Itanium,但没有参与。 x86-64的所有设计决策均由AMD架构师做出。

AMD64在写入32位寄存器时设计为隐式零扩展,因此可以有效地使用32位操作数大小with none of the partial-register shenanigans you get with 8 and 16-bit mode

TL:DR:有充分的理由使CPU希望以某种方式使32位操作数大小可用,并使C类型的系统具有易于访问的32位类型。为此,使用int是自然。

如果您想要 64位操作数大小,请使用它。 (然后,如果要为asm全局变量或函数原型编写C声明,则以long long[u]int64_t的形式描述给C编译器)。没什么能阻止您的(除了较大的代码大小,您可能不需要以前可能没有的REX前缀)。


所有这些都是与x86-64机器代码如何编码32位操作数大小完全不同的问题。

AMD选择将32位作为默认值,将64位操作数设为REX前缀。

他们可以采用另一种方法,将64位操作数大小设置为默认值,要求REX.W = 0将其设置为32,或要求0x66操作数大小将其设置为16。这可能导致较小的机器代码,用于不需要r8..r15的情况下通常会处理必须为64位的东西(通常是指针)的代码。

一个REX前缀也必须完全使用r8..r15(甚至是寻址模式的一部分),因此需要大量寄存器的代码无论如何都使用大多数指令的REX前缀来查找自己,即使使用默认值也是如此。操作数大小。

许多代码的确将int用于很多东西,因此32位操作数大小并不罕见。如上所述,有时速度更快。 因此,使最快的指令最紧凑(如果避免使用r8d..r15d)是有意义的。

[如果相同的操作码以相同的方式在32位和64位模式下以无前缀的方式进行解码,也可能使解码器硬件更简单。我认为这是AMD进行此设计选择的真正动机。他们当然可以清理很多x86疣,但选择不这样做,可能还希望继续解码,类似于32位模式。

可能会很有趣,看看是否要为x86-64版本保存总体代码大小,并且默认操作数大小为64位。例如调整编译器并编译一些现有的代码库。您可能想教它的优化器偏爱用于64位而不是32位的传统寄存器RAX..RDI,以尽量减少需要REX前缀的指令的数量。

((尽管只关心低32位,尽管高位垃圾会影响FLAGS结果,但是许多指令,例如addimul reg,reg都可以安全地以64位操作数大小使用,]]


Re:注释中的错误信息:与32位机器代码兼容与此无关。 64位模式与现有的32位机器代码不二进制兼容;这就是x86-64引入新模式的原因

。 64位内核以兼容模式运行32位二进制文​​件,其中解码的工作方式与32位保护模式完全相同。

[https://en.wikipedia.org/wiki/X86-64#OPMODES有一个有用的模式表,包括长模式(以及64位与32位和16位兼容模式)与传统模式(如果您启动的内核不支持x86-64)。

在64位模式下,某些操作码是不同的,push / pop和其他堆栈指令操作码的操作数大小默认为64位。

32位机器代码在该模式下将无法正确解码。例如0x40在兼容模式下为inc eax,但在64位模式下为REX前缀。有关示例,请参见x86-32 / x86-64 polyglot machine-code fragment that detects 64bit mode at run-time?

假定解码器仅具有2个依赖于模式的默认操作数大小(16或32位),可能会更容易对于03 add r, r/m之类的操作码,不是3。仅对03 add r, r/m / push之类的操作码提供特殊保护。 (还请注意,pop;操作数大小保持在64位。)

[AMD的设计决策似乎集中于尽可能共享解码器晶体管,以防万一AMD64未能流行起来,并且在人们不使用它的情况下坚持支持它。

他们本来可以做很多细微的事情来消除令人讨厌的x86古怪之处,例如使REX.W=0 does not let you encode push r32成为64位模式下的32位操作数大小指令,以避免首先需要进行异或归零。还是CISC烦人,例如标志在零计数移位后保持不变(尽管AMD CPU比Intel处理效率更高,所以也许他们故意留了下来。)

或者也许他们认为微妙的调整可能会损害asm源的移植,或者在短期内使获取编译器后端以支持64位代码生成变得更加困难。

© www.soinside.com 2019 - 2024. All rights reserved.