源操作数和目标操作数是否需要相同大小?

sta*_*gun 2 x86 assembly x86-64

我刚刚尝试了这个问题,要求你解释一下代码行有什么问题:

movl %eax, %rdx
Run Code Online (Sandbox Code Playgroud)

解决方案表明目标操作数的大小错误。

仅当从较大尺寸变为较小尺寸时才“非法”,还是源操作数和目标操作数对于所有指令(或至少 mov 类类型)必须具有相同的尺寸?

Pet*_*des 5

是的,除了一些特殊指令(例如shl %cl, %eaxor )之外,操作数必须具有相同的大小movzwl %ax, %edx

CPU 执行机器代码,而不是汇编。在机器代码中,有操作码和前缀(以及 64 位模式提供的默认值)来指定操作数大小。每个操作数没有单独的大小属性;那会浪费比特。

汇编语言是一种用于描述/指定机器指令的文本格式。

ISA 设计者(Intel,然后是 64 位模式的 AMD)选择根据窄操作数大小来定义指令集中部分寄存器的语义。当您写入 AL、AH 或 AX(由 Intel 在 8086 和 808386 中定义,AMD64 与之匹配)时,对完整寄存器的影响将被合并,或者当您写入 EAX 时,隐式零扩展到 RAX(AMD64 中的新语义)寄存器中新的部分)。

请参阅为什么 32 位寄存器上的 x86-64 指令会将整个 64 位寄存器的上部清零? 以及 Intel 的指令集参考,例如movvs. movsx(以及movsxdAMD64 中的新指令)。

在 asm 文本语法中,没有与movl %eax, %rdx.
汇编器正确地告诉你这是没有意义的。

这也是为什么 AT&T 语法将操作数大小后缀 ( b// w/ l)附加q到助记符,而不是单独附加到每个操作数。唯一的歧义是没有寄存器操作数的指令,只有立即数和内存,如andl $1, (%rdi)vs.andb $1, (%rdi)notq (%rdi, %rsi, 8)

有一条指令movsld %eax, %rdx可以将 32 位符号扩展为 64 位。(32 到 64 没有movzx;这是隐含的mov %eax, %edxMOVZX 缺少 32 位寄存器到 64 位寄存器,只有 8 或 16 位源操作数,如movzbl %al, %edx。)

还有其他具有不同大小操作数的特殊指令,例如像 之类的移位shl %cl, %edx

这种语法设计非常适合像mov %eax, (%rdi)or这样的加载/存储add (%rdi), %esi,其中寄存器操作数意味着内存操作数大小。如果mov可以有两个单独的大小,则始终需要指示内存操作数大小,就像作为 movzx/movsx 助记符的一部分所做的那样。movzbl (%rdi), %eax(例如,通过显式写入 EAX,AT&T 语法将内存中的字节隐式零扩展至 RAX。)


描述机器代码的文本语法的其他设计也是可能的例如,您可以发明一种语法,其中movl %eax, %rdx仅使零扩展显式化,并且movl %eax, %edx可能不会被允许,因为没有办法在隐式的情况下编写 32 位寄存器零扩展至 64 位。然后您可以定义movl (%rdi), %rdx为 32 位负载(由l后缀暗示)零扩展到 64 位 RAX。即我们当前在 AT&T 语法中定义为movl (%rdi), %edx.

我认为假设的设计比仅仅说大多数指令要求所有操作数具有相同的宽度更不直观。实际上,AT&T 语法并不是这样设计的。相反,AT&T 采用了与 Intel / AMD 在其手册中使用的相同约定,只是操作数顺序相反。我不知道任何以这种方式工作的 ISA 的语法;当写入窄寄存器隐式零扩展时,这会隐含在 asm 文本语法中(例如在 AArch64 中,以及所有各种 x86-64 语法;Intel、AT&T、Plan9/Go)


参考: