使用 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
NASM 手册讨论了这些宏,但据我所知并没有真正解释如何使用它们。 Section 3.4.6 状态:
Floating-point constants are acceptable only as arguments to
DB
,DW
,DD
,DQ
,DT
, andDO
, 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