在x86-64 Tour of Intel Manuals中,我读到了
也许最令人惊讶的事实是,诸如
MOV EAX, EBX自动将指令的高32位归零的指令RAX.
同一来源引用的英特尔文档(3.4.1.1 64位手动基本架构中的通用寄存器)告诉我们:
- 64位操作数在目标通用寄存器中生成64位结果.
- 32位操作数生成32位结果,在目标通用寄存器中零扩展为64位结果.
- 8位和16位操作数生成8位或16位结果.目标通用寄存器的高56位或48位(分别)不会被操作修改.如果8位或16位操作的结果用于64位地址计算,则将寄存器显式符号扩展为完整的64位.
在x86-32和x86-64汇编中,16位指令如
mov ax, bx
Run Code Online (Sandbox Code Playgroud)
不要表现出这种"奇怪"的行为,即eax的上层词被归零.
因此:引入这种行为的原因是什么?乍一看似乎不合逻辑(但原因可能是我习惯了x86-32汇编的怪癖).
看完这个堆栈溢出的答案,而这个文件,我还是不明白之间的差别movq和movabsq.
我目前的理解是movabsq,第一个操作数是一个64位立即数操作数,而movq符号扩展一个32位立即数操作数.从上面引用的第二个文件:
将立即数据移动到64位寄存器可以通过
movq指令进行,该指令将签署扩展32位立即值,或者movabsq在需要完整的64位立即数时使用指令.
在第一篇参考文献中,彼得说:
有趣的实验:
movq $0xFFFFFFFF, %rax可能不可编码,因为它不能用符号扩展的32位立即数表示,并且需要imm64编码或%eax目标编码.
但是,当我组装/运行它时似乎工作正常:
.section .rodata
str:
.string "0x%lx\n"
.text
.globl main
main:
pushq %rbp
movq %rsp, %rbp
movl $str, %edi
movq $0xFFFFFFFF, %rsi
xorl %eax, %eax
call printf
xorl %eax, %eax
popq %rbp
ret
Run Code Online (Sandbox Code Playgroud)
$ clang file.s -o file && ./file
打印0xffffffff.(这适用于较大的值,例如,如果你输入一些额外的"F").movabsq生成相同的输出.
Clang是在推断我想要的吗?如果是,是否有仍然是受益movabsq过度movq?
我错过了什么?
首先,我对movq和之间的区别有点困惑movabsq,我的教科书说:
常规movq指令只能具有可以表示为 32 位二进制补码的立即数源操作数。然后对该值进行符号扩展以生成目标的 64 位值。所述movabsq指令可以具有任意的64位立即值作为其源操作数,并且只能有一个寄存器作为目的地。
我有两个问题。
该movq指令只能具有可以表示为 32 位二进制补码的立即数源操作数。
所以这意味着我们不能做
movq $0x123456789abcdef, %rbp
Run Code Online (Sandbox Code Playgroud)
我们必须这样做:
movabsq $0x123456789abcdef, %rbp
Run Code Online (Sandbox Code Playgroud)
但是为什么movq被设计为不适用于 64 位立即数,这确实违背了q(四分字)的目的,我们需要movabsq为此目的而设置另一个,这不是很麻烦吗?
由于目标movabsq必须是寄存器,而不是内存,所以我们不能将 64 位立即数移动到内存中:
movabsq $0x123456789abcdef, (%rax)
Run Code Online (Sandbox Code Playgroud)
但有一个解决方法:
movabsq $0x123456789abcdef, %rbx
movq %rbx, (%rax) // the source operand is a register, not immediate constant, and the destination of movq can be memory
Run Code Online (Sandbox Code Playgroud)
那么为什么这条规则旨在让事情变得更难呢?
assembly x86-64 instruction-set cpu-architecture immediate-operand