为什么我不能直接将字节移动到64位寄存器?

时间:2014-03-24 21:50:47

标签: assembly x86-64

为什么我不能在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

为什么我们需要这样做?

有更简单的方法吗?

感谢。

2 个答案:

答案 0 :(得分:5)

根据需要使用零或符号扩展名移动。

例如:movzx eax, byte [rbp - 1]零延伸into RAX

movsx rax, byte [rbp - 1]签署延伸到RAX。

答案 1 :(得分:2)

分配值

时,将8位寄存器扩展为64位

您可以使用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寄存器

您可以通过以下方式使用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时,您的汇编代码将更多更快。