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?
答案在 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 位寄存器的上半部分置零?