2

The NASM manual talks about these macros, but it doesn't really explain how to use them as far as I can see. Section 3.4.6 states:

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?__

At first, I thought this was to use floating-point constants outside the data section. But when I tried mov xmm0, __?float32?__(1.23), I got an "Invalid combination of opcode and operands" error. Eventually, I saw that foo: dd __?float32?__(1.23) works. This feels strange to me though; if you can do dd 1.23 directly, what is the point of these macros? One possibility could be if maybe you need to define, for example, a single-precision float in a quadword. Is this truly the only use for these macros, or am I using them wrong?

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
mediocrevegetable1
  • 4,086
  • 1
  • 11
  • 33

1 Answers1

3

These macros don't change the fact that x86 has no instructions with an immediate source and an XMM or x87 destination. Remember, NASM is an assembler, not a compiler.

The use-cases include the rare case where you want to mov-immediate an FP bit-pattern into an integer register, like mov eax, __?float32?__(1.23). After which you might do movd xmm0, eax, or even AVX-512 vpbroadcastd xmm0, eax.

Normally compilers load FP data into registers from constants in memory (and that's usually a good option), but it's not the only way to go about it.

(AVX-512 makes immediates more attractive because of efficient broadcast, but you can broadcast from memory too with only AVX1, or SSE3 movddup for a double. Compilers still use memory constants for scalar float, and that's still generally what I'd recommend unless profiling shows lots of data cache misses on that, and not a lot of I-cache misses in your program in general.)

Or for something like if (x) *fp_ptr = 1.0;, you might want to mov-immediate to memory like mov dword [rdi], __?float32?__(1.0).

Another use-case could possibly be in a NASM %if conditional assembly directive, or some other case where you want an FP bit-pattern as an integer value that isn't a dd. Although nothing sensible comes to mind.

Or as part of an expression like __?float32?__(1.0) >> 23 to get the exponent (and sign bit) of a float constant you want to use for something.


For the record:

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

assembled with nasm -felf64 foo.asm and disassembled with 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

Related:

Older NASM documented __float32__(1.23); that still assembles, but the NASM manual currently only documents the form with ? used in this answer. I think that makes it not a valid NASM preprocessor macro/token, in case that's relevant. And not a valid symbol name.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
  • 1
    Ah yes, I just tried it with `eax` and it worked. Pretty stupid of me not to try that in hindsight... I hadn't realized you couldn't put literals at all into `xmm` registers (I'm trying to learn vectors and SIMD). Either way, you have given me the answer I've been looking for. – mediocrevegetable1 Mar 04 '21 at 13:53
  • 2
    @mediocrevegetable1: Also keep in mind that `mov` can *never* have an XMM destination (https://www.felixcloutier.com/x86/mov), so that's another thing to check on next time an instruction won't assemble. Instructions that can write an XMM register include `movd xmm, r/m32`, `movdqu xmm, xmm/mem`, `vbroadcastss xmm, xmm/mem` – Peter Cordes Mar 04 '21 at 13:56
  • Ah ok, I hadn't realized that. I'll look up those instructions, and thanks for the link. – mediocrevegetable1 Mar 04 '21 at 13:58
  • 2
    @mediocrevegetable1: Updated my answer with another plausible and maybe-interesting use-cases: mov-immediate to memory, or doing further constant-expression math on the float32 bit-pattern. – Peter Cordes Mar 04 '21 at 14:06
  • Thanks for the update. I hadn't thought about using them for that, especially with the bit-shifting. Interesting. – mediocrevegetable1 Mar 04 '21 at 14:10