如果我们只对它的低 4 字节使用 mov 操作,64 位寄存器的高 4 字节是否设置为零
Are a 64-bit register's higher 4 bytes set to zero if we only use mov operation on its lower 4 bytes
我正在从计算机系统学习 x86-64 汇编:程序员的视角,我遇到了一个练习,该练习要求将一行 C 代码转换为(两个)等效的汇编指令。该代码是关于使用指针将一种类型的变量复制到另一种类型。
指针变量声明如下:
src_t *sp; //src_t and dest_t are typedefs
dest_t *dp;
待翻译的C代码为:
*dp = (dest_t)*sp;
假设指针sp
和dp
分别存储在寄存器%rdi
和%rsi
中,我们应该设置'appropriate portion' %rax
(例如 %eax
、%ax
或 %al
)进行中间数据复制(因为 x86-64 不允许源和目标都是内存引用) .
现在src_t
是unsigned char
,dest_t
是long
,我做了下面的汇编代码:
movzbq (%rdi), %rax //move a byte into %rax with zero extension
movq %rax, (%rsi) //move 8 bytes of 'long' data
但是这本书以及 Godbolt(使用 gcc
和 -O3
)说它应该是
movzbl (%rdi), %eax
movq %rax, (%rsi)
在这种情况下,字节仅(?)零扩展为 4 个字节(%eax
是 4 个字节长),但我读到如果我们喜欢
movl %edx, %rax
那么%rax的高4字节也会被设置为0
我有两个问题:
movl %edx, %rax
是否等同于movl %edx, %eax
,即后一种情况高4字节也设置为0?
-
movzbq (%rdi), %rax
是否等同于 movzbl (%rdi), %eax
,即 movzbl
是否也将高 4 字节设置为零(如 movl
),即使我们 不要提到完整的寄存器 (%rax
) 但只是它的一部分 (%eax)
?
一般来说,在 x86_64,任何以 32 位通用寄存器为目标的指令(任何 %eXX 或 %rNd 寄存器)也会设置相应 64 位寄存器的高 32 位注册为 0。因此,每条具有 32 位目标的指令都将其扩展为 64 位。
来自英特尔 IA32 软件开发人员手册(第 3.4.1.1 节):
When in 64-bit mode, operand size determines the number of valid bits in the destination general-purpose
register:
- 64-bit operands generate a 64-bit result in the destination general-purpose register.
- 32-bit operands generate a 32-bit result, zero-extended to a 64-bit result in the destination general-purpose
register.
- 8-bit and 16-bit operands generate an 8-bit or 16-bit result. The upper 56 bits or 48 bits (respectively) of the
destination general-purpose register are not modified by the operation. If the result of an 8-bit or 16-bit
operation is intended for 64-bit address calculation, explicitly sign-extend the register to the full 64-bits.
我正在从计算机系统学习 x86-64 汇编:程序员的视角,我遇到了一个练习,该练习要求将一行 C 代码转换为(两个)等效的汇编指令。该代码是关于使用指针将一种类型的变量复制到另一种类型。
指针变量声明如下:
src_t *sp; //src_t and dest_t are typedefs
dest_t *dp;
待翻译的C代码为:
*dp = (dest_t)*sp;
假设指针sp
和dp
分别存储在寄存器%rdi
和%rsi
中,我们应该设置'appropriate portion' %rax
(例如 %eax
、%ax
或 %al
)进行中间数据复制(因为 x86-64 不允许源和目标都是内存引用) .
现在src_t
是unsigned char
,dest_t
是long
,我做了下面的汇编代码:
movzbq (%rdi), %rax //move a byte into %rax with zero extension
movq %rax, (%rsi) //move 8 bytes of 'long' data
但是这本书以及 Godbolt(使用 gcc
和 -O3
)说它应该是
movzbl (%rdi), %eax
movq %rax, (%rsi)
在这种情况下,字节仅(?)零扩展为 4 个字节(%eax
是 4 个字节长),但我读到如果我们喜欢
movl %edx, %rax
那么%rax的高4字节也会被设置为0
我有两个问题:
movl %edx, %rax
是否等同于movl %edx, %eax
,即后一种情况高4字节也设置为0?-
movzbq (%rdi), %rax
是否等同于movzbl (%rdi), %eax
,即movzbl
是否也将高 4 字节设置为零(如movl
),即使我们 不要提到完整的寄存器 (%rax
) 但只是它的一部分 (%eax)
?
一般来说,在 x86_64,任何以 32 位通用寄存器为目标的指令(任何 %eXX 或 %rNd 寄存器)也会设置相应 64 位寄存器的高 32 位注册为 0。因此,每条具有 32 位目标的指令都将其扩展为 64 位。
来自英特尔 IA32 软件开发人员手册(第 3.4.1.1 节):
When in 64-bit mode, operand size determines the number of valid bits in the destination general-purpose register:
- 64-bit operands generate a 64-bit result in the destination general-purpose register.
- 32-bit operands generate a 32-bit result, zero-extended to a 64-bit result in the destination general-purpose register.
- 8-bit and 16-bit operands generate an 8-bit or 16-bit result. The upper 56 bits or 48 bits (respectively) of the destination general-purpose register are not modified by the operation. If the result of an 8-bit or 16-bit operation is intended for 64-bit address calculation, explicitly sign-extend the register to the full 64-bits.