将 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
中的值保持不变:
r14
在 and
之前:421609184805440
r14
在 and
之后:421609184805440
当改用寄存器时,一切都按预期工作:
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
r14
在 and
之前:421723605418560
r14
在 and
之后:140248628707904
当然,我可以使用寄存器而不是访问堆栈,但我想知道为什么它的行为不同。
您似乎没有检查 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?
你的标题是一个转移话题。这与 and
的 m64
操作数无关,问题在于常数。您可以通过使用调试器 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 reg
或 mov 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
。
我正在玩弄 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
中的值保持不变:
r14
在and
之前:421609184805440
r14
在and
之后:421609184805440
当改用寄存器时,一切都按预期工作:
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
r14
在and
之前:421723605418560
r14
在and
之后:140248628707904
当然,我可以使用寄存器而不是访问堆栈,但我想知道为什么它的行为不同。
您似乎没有检查 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?
你的标题是一个转移话题。这与 and
的 m64
操作数无关,问题在于常数。您可以通过使用调试器 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 reg
或 mov 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
。