0

As I understand, floating points are stored in XMM registers and not the general purpose registers such as eax, so I did an experiment:

float a = 5;

in this case, a is stored as 1084227584 in the XMM register. Here is an assembly version:

.text
        .global _start
.LCO:
        .long 1084227584
_start:
        mov .LCO, %eax
        movss .LCO, %xmm0

Executing the above assembly and debugging it using gdb shows that the value in eax will be 1084227584, however the value in ymm0 is 5.

Here are is my questions:

1- What's so special about the XMM registers? beside the SIMD instructions, are they the only type of registers to store floating points?

why can't I set the same bits in a regular register?

2- Are float and double values always stored as a floating point?

Can we never store them as a fixed point in C or assembly?

EsmaeelE
  • 2,331
  • 6
  • 22
  • 31
Joe
  • 31
  • 1
  • 4
  • 2
    You totally could move a `float` or `double` between a scalar register and an xmm register with `movd`/`movq`. But there are no FP instructions that operate on the scalar registers. – Iwillnotexist Idonotexist Apr 11 '19 at 01:25
  • 2
    You can store numbers in any format you want, it's just bits to the computer. But the hardware arithmetic instructions only know how to operate on certain formats, and they use specific registers for some formats. You need to use library functions for other formats. – Barmar Apr 11 '19 at 01:29
  • 1
    I'm not sure that x86-64 actually has fixed point support, you usually have to emulate it, but [that's often annoying and fussy](https://stackoverflow.com/questions/39677723/floating-point-number-vs-fixed-point-number-speed-on-intel-i5-cpu). You can generally store any bits you want in any register of a sufficiently large size, but storing floating point values in integer registers isn't really all that useful as there's no way of manipulating them as floating point values in that location. The main problem is the general-purpose floating point registers are 80 bits vs. 64 for int. – tadman Apr 11 '19 at 01:37

2 Answers2

6

however the value in ymm0 is 5.

The bit-pattern in ymm0 is 1084227584. The float interpretation of that number is 5.0.

But you can print /x $xmm0.v4_int32 to see a hex representation of the bits in xmm0.


What's so special about the XMM registers? beside the SIMD instructions, are they the only type of registers to store floating points?

No, in asm everything is just bytes.

Some compilers will use an integer register to copy a float or double from one memory location to another, if not doing any computation on it. (Integer instructions are often smaller.) e.g. clang will do this: https://godbolt.org/z/76EWMY

void copy(float *d, float *s) {   *d = *s; }

# clang8.0 -O3 targeting x86-64 System V
copy:                                   # @copy
    mov     eax, dword ptr [rsi]
    mov     dword ptr [rdi], eax
    ret

XMM/YMM/ZMM registers are special because they're the only registers that FP ALU instructions exist for (ignoring x87, which is only used for 80-bit long double in x86-64).

addsd xmm0, xmm1 (add scalar double) has no equivalent for integer registers.

Usually FP and integer data don't mingle very much, so providing a whole separate set of architectural registers allows more space for more data to be in registers. (Given the same instruction-encoding constraints, it's a choice between 16 FP + 16 GP integer vs. 16 unified registers, not vs. 32 unified registers).

Plus, a major microarchitectural benefit of a separate register file is that it can be physically close to the FP ALUs, while the integer register file can be physically close to the integer ALUs. For more, see Is there any architecture that uses the same register space for scalar integer and floating point operations?


are float and double values always stored as a floating point? can we never store them as a fixed point in C or assembly?

x86 compilers use float = IEEE754 binary32 https://en.wikipedia.org/wiki/Single-precision_floating-point_format. (And double = IEEE754 binary64). This is specified as part of the ABI.

Internally the as-if rule allows the compiler to do whatever it wants, as long as the final result is identical. (Or with -ffast-math, to pretend that FP math is associative, and assume NaN/Inf aren't possible.)

Compilers can't just randomly choose a different object representation for some float that other separately-compiled functions might look at.

There might be rare cases for locals that are never visible to other functions where a "human compiler" (hand-writing asm to implement C) could prove that fixed-point was safe. Or more likely, that the float values were exact integers small enough that double wouldn't round them, so your fixed-point could degenerate to integer (except maybe for a final step).

But it would be rare to know this much about possible values without just being able to do constant propagation and optimize everything away. That's why I say a human would have to be involved, to prove things the compiler wouldn't know to look for.


I think in theory you could have a C implementation that did use a fixed-point float or double. ISO C puts very little restrictions on what float and double actually are.

But limits.h constants like FLT_RADIX and DBL_MAX_EXP have interactions that might not make sense for a fixed-point format, which has constant distance between each representable value, instead of being much closer together near 0 and much farther apart for large number. (Rounding error of 0.5ulp is relative to the magnitude, instead of absolute.)

Still, most programs don't actually do things that would break if the "mantissa" and exponent limits didn't correspond to what you'd expect for DBL_MIN and DBL_MAX.

Another interesting possibility is to make float and double based on the Posit format (similar to traditional floating-point, but with a variable-length exponent encoding. https://www.johndcook.com/blog/2018/04/11/anatomy-of-a-posit-number/ https://posithub.org/index).


Modern hardware, especially Intel CPUs, has very good support for IEEE float/double, so fixed-point is often not a win. There are some nice SIMD instructions for 16-bit fixed-point, though, like high-half-only multiply, and even pmulhrsw which does fixed-point rounding.

But general 32-bit integer multiply has worse throughput than packed-float multiply. (Because the SIMD ALUs optimized for float/double only need 24x24-bit significand multipliers per 32 bits of vector element. Modern Intel CPUs run integer multiply and shift on the FMA execution units, with 2 uops per clock throughput.)

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
  • FP ALU instructions can also be performed on YMM and ZMM registers. – fuz Apr 11 '19 at 06:49
  • @Joe: while addressing fuz's point, I remembered [Is there any architecture that uses the same register space for scalar integer and floating point operations?](//stackoverflow.com/q/51471978) which is related. See my edit to this answer a summary + link. – Peter Cordes Apr 11 '19 at 06:58
2

are they the only type of registers to store floating points?

No. There are the 80-bit floating-point registers (fp0-fp7) in the 8087-compatible FPU which should still be present in most modern CPUs.

Most 32-bit programs use these registers.

Can we store a floating point in a regular [integer] register?

Yes. 30 years ago many PCs contained a CPU without 80x87 FPU, so there were no fp0-fp7 registers. CPUs with XMM registers came even later.

We find a similar situation in mobile devices today.

What's so special about the XMM registers?

Using the 80x87 FPU seems to be more complicated than using XMM registers. Furthermore, I'm not sure if using the 80x87 is allowed in 64-bit programs in every operating system.

If you store a floating-point value in an integer register (such as eax), you don't have any instructions performing arithmetic: On x86 CPUs, there is no instruction for doing a multiplication or addition of floating-point values that are stored in integer registers.

In the case of CPUs without FPU, you have to do floating-point emulation. This means you have to perform one floating-point operation by doing multiple integer operations - just like you would do it with paper and pencil.

However, if you only want to store a floating-point value, you can of course also use an integer register. The same is true for copying a value or checking if two values are equal and similar operations.

Can we never store them as a fixed point in C or assembly?

Fixed point is used a lot when using CPUs that do not have an FPU.

For example when using 8- or 16-bit CPUs which are still used in automotive industry, consumer devices or PC peripheral devices.

However, I doubt that there are C compilers that automatically translate the keyword "float" to fixed point.

Martin Rosenau
  • 17,897
  • 3
  • 19
  • 38
  • 1
    *I'm not sure if using the 80x87 is allowed in 64-bit programs in every operating system.* I think it is, for the mainstream OSes. There was a myth going around for a while that Windows didn't support it, but that was later debunked. Windows x64 does correctly save the x87 state on context switches, and doesn't set the CR bits in a way that would make it fault on use, so x87 and/or MMX can be used in user-space x86-64 code on Windows. FFmpeg and x264 have some MMX code that's still used in 64-bit builds, and AFAIK they're portable to all mainstream x86-64 OSes. – Peter Cordes Apr 11 '19 at 07:01
  • I think the source of this myth was that MSVC removed support for MMX intrinsics in 64-bit code or something, but that's just a toolchain limitation and nothing to do with the OS. There *could* be custom OSes that never enable the x87 FPU for use, only SSE. (This HW bit is designed for lazy FP context switches, so first instruction faults allowing the OS to restore FPU state. But you could instead deliver SIGILL or equivalent on such faults...) Anyway, normally with SSE you use one of the `fxsave` or `xsave` / `xrstor` instruction pairs for FP context, and that saves x87 + SSE + ... – Peter Cordes Apr 11 '19 at 07:06