当前位置: 首页 > 知识库问答 >
问题:

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

崔宇
2023-03-14

为什么我不能在英特尔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个答案

段铭晨
2023-03-14

可以使用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

但是,像第一个示例中那样的两个movxz'e可能更快(处理器可能会优化内存访问)-速度取决于更大的上下文,应该在复杂的环境中进行测试。

您可以利用这样一个事实,即在64位模式下,修改32位寄存器也会清除最高位(63-32),但无论如何,您无法使用movzx指令在64位下对ah寄存器进行编码,即使是在64位模式下出现的新寄存器的32位部分(movzx r13d,ah也无法工作)。

您可以按以下方式使用64位rNN寄存器的8位、16位和32部分:

rNNb-字节rNNw-字rNNd-dword

例如,r10b、r10w、r10d。下面是代码中的示例

    xor     r8d,dword ptr [r9+r10*4]
    .....
    xor     r8b, al
    .....
    xor     eax, r11d

请注意:rNN寄存器的'h'部分不可用,它们仅适用于四个第一寄存器:ah、bh、ch和dh。

另一个注意事项:修改64位寄存器的32位部分时,较高的32位会自动设置为零。

使用寄存器的最快方法是始终清除最高位,以消除对寄存器先前内容的错误依赖。这是英特尔推荐的方法,将允许更好的无序执行(OOE)和寄存器重命名(RR)。除此之外,在现代处理器上使用完整寄存器而不是使用它们的较低部分更快:骑士登陆和炮弹湖。所以这是在这些处理器上运行更快的代码(它将使用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位寄存器上的指令慢得多,因此它们现在正在Knights Landing上。

如果您在编写时考虑到OOB和RR,那么您的汇编代码将更快。

宦瀚
2023-03-14

酌情使用带零或符号扩展名的移动。

movsx-rax,字节[rbp-1]符号扩展到rax。

 类似资料:
  • 我可以使用MOV指令将存储在内存中的数据项移动到我选择的通用寄存器中。 现在,不要向我开枪,但以下是如何实现的:

  • 问题内容: 我试图将ArrayList添加到Jlist,但我给出的唯一理解是编写这样的代码: 让我感到困惑的是,为什么我不能像这样直接将ArrayList直接添加到Jlist中: 提前致谢。 问题答案: 包含“ helper”构造函数的目的是使使用简单数据结构更加容易。 的(和许多Swing组件)实际上是指与提供实际数据视图模型中使用。 原始设计可以追溯到将Swing纳入主库之前(在JDK 1.3

  • 问题内容: 我试图将ArrayList添加到Jlist,但我给出的唯一理解是编写这样的代码: 让我感到困惑的是,为什么我不能像这样直接将ArrayList直接添加到Jlist中: 提前致谢。 问题答案: 包含“ helper”构造函数的目的是使使用简单数据结构更加容易。 的(和许多Swing组件)实际上是指与提供实际数据视图模型中使用。 原始设计可以追溯到将Swing纳入主库之前(在JDK 1.3

  • 基本上指令有8->16、8->32、8->64、16->32和16->64。 32->64的转换在哪里?我必须使用签名版本吗? 如果是的话,您如何使用完整的64位来表示无符号整数?

  • 如果一个表达式包含任何整数大小或更小的内容,其结果总是整数,即使两个字节之和适合一个字节。 为什么我们在一个字节中添加最后两个字节时会发生这种情况?没有编译器错误。

  • 根据Android指南,http://developer.android.com/training/basics/fragments/communicating.html一个片段应该通过宿主Activity向另一个片段发送数据。我想知道这有什么原因。因为在我的代码中,我放置了一个变量来保存指向另一个片段的指针,并在onActivityCreated中赋值 后来,如果我想为FragmentType2