Nub*_*bok 97 x86 assembly x86-64 cpu-registers zero-extension
在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汇编的怪癖).
har*_*old 82
我不是AMD或为他们说话,但我会以同样的方式做到这一点.因为将高半部分归零不会对先前的值产生依赖性,所以cpu必须等待.如果没有这样做,寄存器重命名机制基本上会被打败.这样,您就可以在64位模式下编写快速32位代码,而无需始终明确地断开依赖关系.如果没有这种行为,64位模式下的每个32位指令都必须等待之前发生的事情,即使这个高部分几乎不会被使用.
16位指令的行为很奇怪.依赖性疯狂是现在避免16位指令的原因之一.
Bo *_*son 10
它只是节省了指令和指令集中的空间.您可以使用现有(32位)指令将小立即值移动到64位寄存器.
它还使您不必编码8字节值MOV RAX, 42,何时MOV EAX, 42可以重复使用.
这种优化对于8位和16位操作并不重要(因为它们更小),并且更改规则也会破坏旧代码.
如果没有零扩展到 64 位,则意味着读取的指令rax对其操作数有 2 个依赖性rax(写入的指令eax和在其之前写入的指令rax),这将导致部分寄存器停顿,这开始变得当有 3 种可能的宽度时,这很棘手,因此它有助于做到这一点rax并eax写入完整的寄存器,这意味着 64 位指令集不会引入任何新的部分重命名层。
mov rdx, 1
mov rax, 6
imul rax, rdx
mov rbx, rax
mov eax, 7 //retires before add rax, 6
mov rdx, rax // has to wait for both imul rax, rdx and mov eax, 7 to finish before dispatch to the execution units, even though the higher order bits are identical anyway
Run Code Online (Sandbox Code Playgroud)
非零扩展的唯一好处是确保包含 的高阶位rax,例如,如果它最初包含 0xffffffffffffffff,则结果将是 0xffffffff00000007,但 ISA 没有理由以如此大的代价做出此保证,并且更有可能的是,实际上需要更多零扩展的好处,因此它节省了额外的代码行mov rax, 0。通过保证它始终为零扩展到 64 位,编译器可以在 时牢记这一公理mov rdx, rax,rax只需等待其单个依赖项,这意味着它可以更快地开始执行并退出,从而释放执行单元。此外,它还允许更有效的零习惯用法,例如xor eax, eax零rax,而不需要 REX 字节。
| 归档时间: |
|
| 查看次数: |
24417 次 |
| 最近记录: |