将 64 位常量移动到内存

Moving 64bit constants to memory

我正在玩弄 asmjit 并生成程序集。因此,我注意到不能将 64 位常量用于指令(不包括 mov,这是有道理的)。

因此,我将 64 位常量压入堆栈并通过访问堆栈来使用它们,而不是将常量用作操作数。 不同的资源说,可以将内存用作 and 指令的操作数(例如 [1], [2])。

但是,我注意到 and 指令没有按预期工作。我将从我的代码中给你一个例子:

mov r14, qword ptr [r15+32]   ; r14 holds a masked pointer now
mov qword ptr [rsp], 281474976710655    ; 0xFFFFFFFFFFFF is the mask for the pointer
and r14, [rsp]                ; Using pointer&mask I want to unmask the pointer

在那条and指令之后,r14中的值保持不变:

当改用寄存器时,一切都按预期工作:

mov r14, qword ptr [r15+32]   ; r14 holds a masked pointer now
mov r13, 281474976710655      ; 0xFFFFFFFFFFFF is the mask for the pointer
and r14, r13                  ; Using pointer&mask I want to unmask the pointer

当然,我可以使用寄存器而不是访问堆栈,但我想知道为什么它的行为不同。

您似乎没有检查 asmjit 错误。 The docs say有一个 kErrorInvalidImmediate - 无效立即数(在 X86 上越界,在 ARM 上无效模式)。

唯一可以使用 64 位立即数的 x86-64 指令是 mov-立即数 注册 ,特殊的 no-modrm 操作码给出我们 5 字节 mov eax, 12345 或 10 字节 mov rax, 0x0123456789abcdef,其中 REX.W 前缀更改该操作码以查找 64 位立即数。参见 https://www.felixcloutier.com/x86/mov / why we can't move a 64-bit immediate value to memory?


你的标题是一个转移话题。这与 andm64 操作数无关,问题在于常数。您可以通过使用调试器 single-stepping asm 并检查 and 之前的两个操作数来验证这一点,包括内存中的操作数。 (它可能是 0xFFFFFFFF 中的 -1 作为 mov m64, sign_extended_imm32 的立即数,这将解释并且不会更改 R14 中的值。

JITed 机器代码的反汇编也应该向您展示实际编码的立即数;同样,调试器可以在您 single-step 通过它时提供它。


使用您的临时寄存器作为常量(如mov r14, 0xFFFFFFFFFFFF),然后and reg,mem到load-and-mask。

或者更好的是,如果你使用 JITint 的目标机器有 BMI1 andn,在循环外使用 mov r13, ~0xFFFFFFFFFFFF 构造倒置常量,然后在循环内使用 andn r14, r13, [r15+32]一个负载+并且不破坏掩码,所有指令都可以在 Intel/AMD CPU 上解码为单个 uop。

Of 如果您不能在循环中重用常量寄存器,也许 mov reg,imm64,然后 push regmov mem,reg 并在以后的 AND 指令中使用它。或者在足够接近的地方发出一些常量数据以使用 RIP-relative 寻址模式进行引用,尽管在每个 and 指令中需要更多 code-size 。 (ModRM + 4 字节 rel32,与 ModRM + SIB + 0 或 1 字节用于堆栈上靠近 RSP 的数据)。


顺便说一句,如果你只是截断而不是 sign-extending,你还假设这是地址在虚拟地址 space 的低半部分(即 user-space) .不过没关系。有趣的事实:未来的 x86 CPU (first Sapphire Rapids) will have an optional feature that OSes can enable to transparently ignore the high bits, except for the MSB: LAM = Linear Address Masking. See Intel's future-extensions manual.

因此,如果为 user-space 启用此功能并使用 48 位掩码,您可以完全跳过 AND 掩码。 (如果您的代码确保第 47 位与第 63 位匹配;您可能希望保持最高位不变或为 0,以便您的代码可以在可用于保存指令时利用 LAM)。


如果您要屏蔽以保持低位 32,您可以将 mov r14d, [r15+32] 到 zero-extend 值的低位双字转换为 64 位 R14。但是为了保留高 48 位或 57 位,您需要一个掩码或 BMI2 bzhi 和寄存器中的 48