我们可以在常规寄存器中存储浮点数吗?

问题描述 投票:1回答:2

据我所知,浮点数存储在XMM寄存器中,而不是通用寄存器,如eax,所以我做了一个实验:

float a = 5;

在这种情况下,a1084227584登记册中存储为XMM。这是一个装配版本:

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

执行上面的程序集并使用gdb调试它表明eax中的值将是1084227584,但ymm0中的值是5

以下是我的问题:

1- XMM寄存器有什么特别之处?在SIMD指令旁边,它们是存储浮点的唯一类型的寄存器吗?

为什么我不能在常规寄存器中设置相同的位?

2- floatdouble值是否始终存储为浮点值?

我们是否永远不会将它们存储为C或汇编中的fixed point

c assembly floating-point x86-64
2个回答
6
投票

但是ymm0中的值是5。

ymm0中的位模式是1084227584。该数字的浮点解释是5.0

但是你可以在print /x $xmm0.v4_int32中看到xmm0中位的十六进制表示。


XMM寄存器有什么特别之处?在SIMD指令旁边,它们是存储浮点的唯一类型的寄存器吗?

不,在asm中,一切都只是字节。

如果没有对它进行任何计算,一些编译器将使用整数寄存器将float或double从一个内存位置复制到另一个内存位置。 (整数指令通常较小。)例如clang会这样做: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寄存器是特殊的,因为它们是FP ALU指令所存在的唯一寄存器(忽略x87,它仅用于x86-64中的80位long double)。

addsd xmm0, xmm1(add scalar double)与整数寄存器没有对应关系。

通常FP和整数数据不会混合很多,因此提供一组完整的架构寄存器可以为寄存器中的更多数据提供更多空间。 (给定相同的指令编码约束,它是16 FP + 16 GP整数与16个统一寄存器之间的选择,而不是32个统一寄存器)。

另外,单独的寄存器文件的主要微体系结构优点是它可以在物理上靠近FP ALU,而整数寄存器文件可以在物理上接近整数ALU。有关更多信息,请参阅Is there any architecture that uses the same register space for scalar integer and floating point operations?


floatdouble值总是存储为浮点?我们是否永远不会将它们存储为C或汇编中的固定点?

x86编译器使用float = IEEE754 binary32 https://en.wikipedia.org/wiki/Single-precision_floating-point_format。 (并且double = IEEE754 binary64)。这被指定为ABI的一部分。

在内部,as-if规则允许编译器执行任何想要的操作,只要最终结果相同即可。 (或者使用-ffast-math,假装FP数学是关联的,并假设NaN / Inf是不可能的。)

编译器不能随便为其他单独编译的函数可能会查看的某些float选择不同的对象表示。

对于其他函数来说永远不可见的本地人可能很少见,其中“人工编译器”(手写asm来实现C)可以证明定点是安全的。或者更可能的是,float值是精确的整数,足够小,以至于double不会绕它们,所以你的定点可以退化为整数(除了最后一步)。

但是,如果不仅仅能够进行持续传播并优化一切,那么很难了解可能的值。这就是为什么我说人类必须参与其中,以证明编译器不会想要寻找的东西。


我认为理论上你可以有一个C实现,确实使用定点floatdouble。 ISO C对floatdouble实际上的限制几乎没有限制。

但是limits.h constants like FLT_RADIX and DBL_MAX_EXP的相互作用可能对定点格式没有意义,定点格式在每个可表示的值之间具有恒定的距离,而不是在0附近更加接近,而对于大数目则更远。 (0.5ulp的舍入误差相对于幅度而不是绝对值。)

尽管如此,如果“尾数”和指数限制与你对DBL_MINDBL_MAX的期望不一致,大多数程序实际上并没有做出会破坏的事情。

另一个有趣的可能性是基于Posit格式制作floatdouble(类似于传统的浮点数,但使用可变长度指数编码.https://www.johndcook.com/blog/2018/04/11/anatomy-of-a-posit-number/ https://posithub.org/index)。


现代硬件,特别是Intel CPU,对IEEE float / double有很好的支持,所以定点往往不是一个胜利。然而,有一些很好的SIMD指令用于16位定点,就像高半仅乘法,甚至pmulhrsw进行定点舍入。

但是一般的32位整数乘法的吞吐量比packed-float乘以更差。 (因为针对float / double优化的SIMD ALU仅需要每32位向量元素24x24位有效数乘法器。现代Intel CPU在FMA执行单元上运行整数乘法和移位,每个时钟吞吐量为2 uop。)


2
投票

它们是存储浮点的唯一类型的寄存器吗?

在8087兼容的FPU中有80位浮点寄存器(fp0-fp7),它仍然应该存在于大多数现代CPU中。

大多数32位程序使用这些寄存器。

我们可以在常规[整数]寄存器中存储浮点数吗?

是。 30年前,许多PC包含一个没有80x87 FPU的CPU,所以没有fp0-fp7寄存器。带有XMM寄存器的CPU甚至更晚。

我们今天在移动设备中发现了类似的情况。

XMM寄存器有什么特别之处?

使用80x87 FPU似乎比使用XMM寄存器更复杂。此外,我不确定在每个操作系统中是否允许在64位程序中使用80x87。

如果将浮点值存储在整数寄存器(例如eax)中,则没有执行算术的任何指令:在x86 CPU上,没有用于执行乘法或添加存储的浮点值的指令在整数寄存器中。

对于没有FPU的CPU,您必须进行浮点仿真。这意味着您必须通过执行多个整数操作来执行一个浮点操作 - 就像使用纸和笔一样。

但是,如果您只想存储浮点值,您当然也可以使用整数寄存器。复制值或检查两个值是否相等以及类似操作也是如此。

我们永远不能将它们存储为C或汇编中的固定点吗?

使用没有FPU的CPU时,使用固定点很多。

例如,当使用仍在汽车行业,消费者设备或PC外围设备中使用的8位或16位CPU时。

但是,我怀疑是否有C编译器自动将关键字“float”转换为定点。

© www.soinside.com 2019 - 2024. All rights reserved.