gpt4 book ai didi

assembly - x86-64 AT&T 指令 movq 和 movabsq 有什么区别?

转载 作者:行者123 更新时间:2023-12-03 06:25:32 43 4
gpt4 key购买 nike

阅读后this stack overflow answer , 和 this document ,我还是不明白 movq 的区别和 movabsq .
我目前的理解是在 movabsq ,第一个操作数是 64 位立即数操作数,而 movq对 32 位立即数操作数进行符号扩展。从上面引用的第二个文件中:

Moving immediate data to a 64-bit register can be done either with the movq instruction, which will sign extend a 32-bit immediate value, or with the movabsq instruction, when a full 64-bit immediate is required.


the first reference ,彼得说:

Interesting experiment: movq $0xFFFFFFFF, %rax is probably not encodeable, because it's not representable with a sign-extended 32-bit immediate, and needs either the imm64 encoding or the %eax destination encoding.

(editor's note: this mistaken assumption is fixed in the current version of that answer).


但是,当我组装/运行它时,它似乎工作正常:
        .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
$ clang file.s -o file && ./file版画 0xffffffff . (对于较大的值,这类似,例如,如果您添加一些额外的“F”)。 movabsq生成相同的输出。
Clang 是在推断我想要什么吗?如果是,对 movabsq还有好处吗?在 movq ?
我错过了什么?

最佳答案

填充 64 位寄存器有三种移动:

  • 移动到低 32 位部分 :B8 +rd id , 5 个字节
    示例:mov eax, 241/mov[l] $241, %eax移动到低 32 位部分将使高部分归零。
  • 使用 64 位立即数移动 :48 B8 +rd io , 10 字节
    示例:mov rax, 0xf1f1f1f1f1f1f1f1/mov[abs][q] $0xf1f1f1f1f1f1f1f1, %rax移动一个完整的 64 位立即数。
  • 使用符号扩展的 32 位立即数移动 :48 C7 /0 id , 7 个字节
    示例:mov rax, 0xffffffffffffffff/mov[q] $0xffffffffffffffff, %rax将有符号的 32 位立即数移动到完整的 64 位寄存器。

  • 请注意在程序集级别如何存在 room for ambiguity , movq用于第二种和第三种情况。
    对于每个直接值,我们有:
  • (a) 中的值[0, 0x7fff_ffff] 可以用(1)、(2)和(3)编码。
  • (b) 中的值[0x8000_0000, 0xffff_ffff] 可以用(1)和(2)编码。
  • (c) 中的值[0x1_0000_0000, 0xffff_ffff_7fff_ffff] 可以用 (2)
  • 编码
  • (d) 中的值[0xffff_ffff_8000_0000, 0xffff_ffff_ffff_ffff] 可以用(2)和(3)编码。

  • 除第三种情况外,所有情况都至少有两种可能的编码。
    如果有多个编码可用,汇编器通常会选择最短的一个,但情况并非总是如此。
    对于气体: movabs[q]总是对应于(2)。 mov[q]对应于情况(a)和(d)的(3),对应于其他情况的(2)。
    它永远不会为移动到 64 位寄存器生成 (1)。
    为了让它启动(1),我们必须使用 mov[l] $0xffffffff, %edi这是等效的(我相信 GAS 不会将移动到 64 位寄存器转换为一个到其较低的 32 位寄存器,即使这是等效的)。

    在 16/32 位时代,区分 (1) 和 (3) 并不被认为非常重要(但 in GAS it's possible to pick one specific form ),因为它不是符号扩展操作,而是 8086 中原始编码的人工制品。 mov指令从未被分成两种形式来解释(1)和(3),而是单个 mov与汇编器一起使用时几乎总是选择 (1) 而不是 (3)。
    使用具有 64 位立即数的新 64 位寄存器会使代码过于稀疏(并且很容易违反当前 16 字节的最大指令长度),因此扩展 (1) 以始终采用 64 位是不值得的即时。
    相反,(1) 仍然有 32 位立即数和零扩展(以打破任何错误的数据依赖性),并且 (2) 被引入用于实际需要 64 位立即数操作数的罕见情况。
    借此机会,(3) 也被更改为仍然采用 32 位立即数,但也对其进行符号扩展。
    (1) 和 (3) 应该足以满足最常见的立即数(如 1 或 -1)。
    然而 (1)/(3) 和 (2) 之间的差异比过去 (1) 和 (3) 之间的差异更深,因为 while (1) 和 (3) 都具有相同大小的操作数,32 位, (3) 有一个 64 位立即数操作数。
    为什么想要一个 artificially lengthened instruction ?
    如链接的答案中所述,一个用例可能是填充,以便下一个循环的顶部是 16/32 字节的倍数,而无需任何 NOP 指令。
    这牺牲了代码密度(指令缓存中的更多空间)和循环外的解码效率,以获得更好的每次循环迭代的前端效率。但是对于前端来说,更长的指令通常仍然比解码一些 NOP 更便宜。
    另一种更常见的用例是只需要生成机器代码模板。
    例如,在 JIT 中,人们可能希望准备指令序列以仅在运行时使用和填充立即数。
    在这种情况下,使用 (2) 将大大简化处理,因为总是有足够的空间容纳所有可能的值。
    另一种情况是对于某些修补功能,在软件的调试版本中,可以使用刚刚加载 (2) 的寄存器中的地址间接进行特定调用,以便调试器可以轻松劫持对任何新目标的调用。

    关于assembly - x86-64 AT&T 指令 movq 和 movabsq 有什么区别?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52434073/

    43 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com