为什么我不能在Intel x86-64汇编中直接将字节从内存移动到64位寄存器?
例如,这段代码:
extern printf
global main
segment .text
main:
enter 2, 0
mov byte [rbp - 1], 'A'
mov byte [rbp - 2], 'B'
mov r12, [rbp - 1]
mov r13, [rbp - 2]
xor rax, rax
mov rdi, Format
mov rsi, r12
mov rdx, r13
call printf
leave
ret
segment .data
Format: db "%d %d", 10, 0
打印:
65 16706
我需要将移动字节更改为寄存器r12和r13,以使代码正常工作:
xor rax, rax
mov al, byte [rbp - 1]
mov r12, rax
xor rax, rax
mov al, byte [rbp - 2]
mov r13, rax
现在,它打印出预期的内容:
65 66
为什么我们需要这样做?
有更简单的方法吗?
感谢。
答案 0 :(得分:5)
答案 1 :(得分:2)
您可以使用movzx指令将字节移动到64位寄存器。
在你的情况下,它将是
movzx r12, byte ptr [rbp - 1]
movzx r13, byte ptr [rbp - 2]
另一种避免将内存寻址到时间的方法是
mov ax, word ptr [rbp - 2]
movzx r12, al
movzx r13, ah
但不会编译最后一条指令。请参阅http://www.felixcloutier.com/x86/MOVZX.html"在64位模式下,如果使用REX前缀,则无法对r / m8进行编码以访问以下字节寄存器:AH,BH,CH,DH。"
所以我们必须做到以下几点:
mov ax, word ptr [rbp - 2]
movzx r12, al
mov al, ah
movzx r13, al
但是,在第一个示例中,只有两个类似可能更快(处理器可能优化内存访问) - 速度取决于更大的上下文,应该在复杂的情况下进行测试。
您可以利用以下事实:在64位模式下,修改32位寄存器也会清除最高位(63-32),但是,无论如何,您不能使用movzx指令对ah
寄存器进行编码。在64位模式下出现64位甚至是新寄存器的32位部分(movzx r13d, ah
不起作用)。
您可以通过以下方式使用8位,16位和32位64位rNN寄存器:
rNNb - 字节 rNNw - 字 rNNd - dword
例如,r10b,r10w,r10d。以下是代码中的示例
xor r8d,dword ptr [r9+r10*4]
.....
xor r8b, al
.....
xor eax, r11d
请注意:' h'部分rNN寄存器不可用,它们仅适用于四个第一个寄存器:ah,bh,ch和dh。
另一个注意事项:当修改64位寄存器的32位部分时,高32位会自动设置为零。
使用寄存器的最快方法是始终清除最高位,以消除对寄存器先前内容的错误依赖性。这是英特尔推荐的方式,可以提供更好的Out-of-Order Execution(OOE)和Register Renaming(RR)。除此之外,在现代处理器上使用完整的寄存器而不是更低的部件更快:Knights Landing和Cannonlake。所以这是在这些处理器上运行得更快的代码(它将使用OOE和RR):
movzx rax, word ptr [rbp - 2]
movzx r12, al
shr rax, 8
mov r13, rax
关于Knights Landing和未来的主流处理器如CannonLake - 英特尔明确表示8位和16位寄存器上的指令比CannonLake上的32位或64位寄存器慢得多,因此它们现在开启了骑士登陆。
如果您在编写OOB和RR时,您的汇编代码将更多更快。