使用 NASM 的 __?float?__ 宏

Use of NASM's __?float?__ macros

NASM 手册讨论了这些宏,但据我所知并没有真正解释如何使用它们。 Section 3.4.6 状态:

Floating-point constants are acceptable only as arguments to DB, DW, DD, DQ, DT, and DO, or as arguments to the special operators __?float8?__, __?float16?__, __?bfloat16?__, __?float32?__, __?float64?__, __?float80m?__, __?float80e?__, __?float128l?__, and __?float128h?__

起初,我以为这是在数据段之外使用浮点常量。但是当我尝试 mov xmm0, __?float32?__(1.23) 时,我收到了“操作码和操作数的无效组合”错误。最终,我看到 foo: dd __?float32?__(1.23) 有效。不过,这对我来说很奇怪;如果你可以直接做dd 1.23,那么这些宏有什么意义呢?一种可能性是,如果您可能需要定义,例如,四字中的单精度浮点数。这真的是这些宏的唯一用途吗,还是我用错了?

这些宏不会改变 x86 没有带有直接源和 XMM 或 x87 目标的指令的事实。请记住,NASM 是汇编器,而不是编译器。

用例包括您希望将 FP 位模式立即移动到整数寄存器的罕见情况,例如 mov eax, __?float32?__(1.23)。之后你可以做 movd xmm0, eax,甚至 AVX-512 vpbroadcastd xmm0, eax.

通常,编译器将 FP 数据从内存中的常量加载到寄存器中(这通常是一个不错的选择),但这不是唯一的方法。

(AVX-512 由于有效的广播使立即数更具吸引力,但您也可以仅使用 AVX1 或 SSE3 movddup 从内存中广播双精度数。编译器仍然使用内存常量作为标量浮点数,并且这通常仍然是我推荐的,除非分析显示有很多数据缓存未命中,并且通常在您的程序中没有很多 I-cache 未命中。)

或者对于像 if (x) *fp_ptr = 1.0; 这样的东西,你可能想要像 mov dword [rdi], __?float32?__(1.0).

这样的立即移动到内存

另一个用例可能是 NASM %if conditional assembly directive,或者您希望 FP 位模式作为 不是 的整数值的其他情况一个dd。虽然没有什么明智的想法。

或者作为像 __?float32?__(1.0) >> 23 这样的表达式的一部分来获取要用于某事的浮点常量的指数(和符号位)。


备案:

mov eax, __?float32?__(1.23)
mov eax, __?float32?__(1.0) >> 23
mov dword [rdi], __?float32?__(1.0)

nasm -felf64 foo.asm组装,用objdump -drwC -Mintel foo.o反汇编

   0:   b8 a4 70 9d 3f          mov    eax,0x3f9d70a4
   5:   b8 7f 00 00 00          mov    eax,0x7f
   a:   c7 07 00 00 80 3f       mov    DWORD PTR [rdi],0x3f800000