0

我已经存储了一个字节的值,8我想把它移到rax寄存器中。我目前正在这样做以movzx对字节进行零扩展:

.globl main
main:
    push %rbp
    mov %rsp, %rbp
    movb $8, -1(%rbp)
    movzx -1(%rbp), %rax <-- here
    ...

指令如何movzx“知道” at 的值-1(%rbp)只有一个字节长?从这里说,如果我正确阅读它,它可以在 abyte和 a上工作word,但它怎么知道?例如,如果我添加了一个两字节的值,-2(%rbp)它怎么知道要抓取这个两字节的值?是否有另一条指令可以让我在地址处获取一个onetwofour字节值并将其插入 64 位寄存器?

我想另一种方法是首先将寄存器清零,然后将其添加到 8 位(或许多位)组件中,例如:

mov $0, %rax
mov -1(%rbp), %al

有没有一种方法比另一种方法更受欢迎?

4

2 回答 2

4

它是模棱两可的并且依赖于一些默认值,你不应该写这样的代码。

这就是为什么 AT&T 语法具有movzbmovzw指令(通常用作),用于 Intel 语法助记符movzbl -1(%rbp), %eax的两种不同源大小。movzx请参阅x86 汇编助记符是否标准化?(不,AT&T 编造了新名字。)

是的,您可以 xor %eax,%eax/mov -1(%rbp), %al合并到低字节中,但这毫无意义地低效。x86-64 保证 movzx 等 386 条指令的可用性。

令人惊讶的是,movzx -1(%rbp), %rax确实组装。如果你组装它,然后用 反汇编回 AT&T 语法objdump -d foo.o,你会得到movzbq(字节到四边形),包括一个无用的 REX 前缀,而不是在编写 EAX 后让隐式零扩展来完成这项工作。

48 0f b6 45 ff          movzbq -0x1(%rbp),%rax

或使用以下命令反汇编为 Intel 语法objdump -drwC -Mintel

48 0f b6 45 ff          movzx  rax,BYTE PTR [rbp-0x1]

有趣的事实:GAS 不能推断movzbmovzw如果你写 just movz,因为movz它不是指令助记符。与可以从操作数推断出的操作数大小后缀不同,bandw被视为助记符的一部分。但是您可以编写movzx,然后它将从寄存器操作数中推断出两种大小,就像在 Intel 语法模式中一样。

   5:   0f b6 c0                movzbl %al,%eax         # source: movzx %al, %eax
   8:   0f b7 c0                movzwl %ax,%eax         # source: movzx %ax, %eax

movzw并且movzb本身就像指令助记符(可以从目标寄存器推断出大小后缀)。半相关: MOVZBL 指令在 IA-32 AT&T 语法中有什么作用?

还相关:cdq 等等价物movsx和 AT&T 等价物的表格:cltq 在汇编中做了什么?

也相关:MOVZX 缺少 32 位寄存器到 64 位寄存器- 因为这在写入 32 位寄存器时是隐含的。

于 2020-08-13T06:36:00.430 回答
3

指令如何movzx“知道” at 的值-1(%rbp)只有一个字节长?

有两个(甚至三个)指令:

movzxb(-1(%rbp)是一个字节长) 和movzxw(-1(%rbp)是一个 16 位字长)。

我的汇编程序解释movzxmovzxb;但是,您不应该依赖它!

最好使用包含源大小(movzxbmovzxw)的指令名称,以确保汇编器使用正确的指令。

于 2020-08-13T06:23:08.903 回答