为什么在数据类型为 64 位时使用 32 位寄存器?

amj*_*jad 1 assembly bit-manipulation x86-64 instruction-set instructions

我正在阅读一本教科书,其中有一个基于 C 代码生成汇编代码的练习:

代码:

long arith(long x, long y, long z)
{
   long t1 = x ^ y;
   long t2 = z * 48;
   long t3 = t1 & 0x0F0F0F0F;
   long t4 = t2 - t3;
   return t4;
}
Run Code Online (Sandbox Code Playgroud)

汇编代码:

//x in %rdi, y in %rsi, z in %rdx
arith:
   xorq %rsi, %rdi               //t1 = x ^ y
   leaq (%rdx,%rdx,2), %rax      //3*z
   salq $4, %rax                 //t2 = 16 * (3*z) = 48*z
   andl $252645135, %edi         //t3 = t1 & 0x0F0F0F0F
   subq %rdi, %rax               //Return t2 - t3
   ret
Run Code Online (Sandbox Code Playgroud)

我对这个汇编代码感到困惑:

andl $252645135, %edi         //t3 = t1 & 0x0F0F0F0F
Run Code Online (Sandbox Code Playgroud)

为什么我们不使用:

andq $252645135, %rdi
Run Code Online (Sandbox Code Playgroud)

问题是,假设所有位 t1都是 1,所以对于原始 C 代码long t3 = t1 & 0x0F0F0F0F;, 的高 32 位t3将是 0。但是如果我们使用andl指令,并且只对 进行操作%edi, 的高 32 位%rdi仍然是 1,所以这真的改变了t4in的值,long t4 = t2 - t3;其中t3的高 32 位都是 1 但它们应该是 0?

fpm*_*phy 5

答案在 Intel 64 和 IA-32 架构软件开发人员手册第 1 卷(基本架构)的第 3.4.1.1 节中指出:

在 64 位模式下,操作数大小决定目标通用寄存器中的有效位数:

  • 64 位操作数在目标通用寄存器中生成 64 位结果。
  • 32 位操作数生成 32 位结果,零扩展到目标通用寄存器中的 64 位结果。
  • 8 位和 16 位操作数生成 8 位或 16 位结果。目标通用寄存器的高 56 位或 48 位(分别)不会被操作修改。如果 8 位或 16 位运算的结果用于 64 位地址计算,则显式将寄存器符号扩展到完整的 64 位。

参见第二个项目符号。

您可以通过阅读以下内容了解为什么会这样:为什么 32 位寄存器上的 x86-64 指令将完整 64 位寄存器的上半部分置零?