【问题标题】:Can we store a floating point in a regular register?我们可以将浮点数存储在常规寄存器中吗?
【发布时间】:2019-09-01 13:12:09
【问题描述】:

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

float a = 5;

在这种情况下,a 在XMM 寄存器中存储为1084227584。 这是一个汇编版本:

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

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

这是我的问题:

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

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

2- float 和 double 值是否始终存储为浮点数?

我们不能将它们存储为 C 或汇编中的 fixed point 吗?

【问题讨论】:

  • 您完全可以使用movd/movq 在标量寄存器和xmm 寄存器之间移动float 或double。但是没有对标量寄存器进行操作的 FP 指令。
  • 您可以以任何您想要的格式存储数字,它只是计算机的位。但是硬件算术指令只知道如何对某些格式进行操作,并且它们对某些格式使用特定的寄存器。您需要使用其他格式的库函数。
  • 我不确定 x86-64 是否真的有定点支持,你通常必须模拟它,但是that's often annoying and fussy。您通常可以将所需的任何位存储在任何足够大的寄存器中,但是将浮点值存储在整数寄存器中并不是那么有用,因为无法在该位置将它们作为浮点值进行操作。主要问题是通用浮点寄存器是 80 位,而 int 是 64 位。

标签: c assembly floating-point x86-64


【解决方案1】:

但是 ymm0 中的值是 5。

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

但您可以print /x $xmm0.v4_int32 来查看 xmm0 中位的十六进制表示。


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

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

如果不对它进行任何计算,一些编译器将使用整数寄存器将浮点数或双精度数从一个内存位置复制到另一个内存位置。 (整数指令通常更小。)例如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(加双精度标量)没有整数寄存器的等价物。

通常 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?


float 和 double 值是否始终存储为浮点数?我们不能将它们作为固定点存储在 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 实现确实使用定点 float 或 double。 ISO C 对什么限制很少float 和 double 实际上是。

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

不过,如果“尾数”和指数限制与您对 DBL_MIN 和 DBL_MAX 的预期不符,大多数程序实际上并没有做会破坏的事情。

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


现代硬件,尤其是英特尔 CPU,对 IEEE 浮点/双精度非常有很好的支持,因此定点通常不是赢家。不过,对于 16 位定点,有一些不错的 SIMD 指令,例如仅高半乘法,甚至是进行定点舍入的 pmulhrsw。

但一般 32 位整数乘法的吞吐量比打包的-float 乘法更差。 (因为针对浮点/双精度优化的 SIMD ALU 每 32 位向量元素只需要 24x24 位有效位乘法器。现代英特尔 CPU 在 FMA 执行单元上运行整数乘法和移位,每个时钟吞吐量为 2 微秒。)

【讨论】:

【解决方案2】:

它们是唯一一种存储浮点数的寄存器吗?

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

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

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

是的。 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”转换为定点。

【讨论】:

  • 我不确定在每个操作系统的 64 位程序中是否允许使用 80x87。 我认为对于主流操作系统来说是允许的。 Windows 不支持它的说法有一段时间流传,但后来被揭穿了。 Windows x64 确实在上下文切换时正确保存 x87 状态,并且不会以会使其在使用时出错的方式设置 CR 位,因此 x87 和/或 MMX 可以在 Windows 上的用户空间 x86-64 代码中使用. FFmpeg 和 x264 有一些 MMX 代码仍在 64 位构建中使用,并且 AFAIK 它们可移植到所有主流 x86-64 操作系统。
  • 我认为这个神话的来源是 MSVC 在 64 位代码或其他东西中删除了对 MMX 内在函数的支持,但这只是工具链的限制,与操作系统无关。 可能有自定义操作系统永远不会启用 x87 FPU,只有 SSE。 (这个 HW 位是为惰性 FP 上下文切换而设计的,所以第一条指令错误允许操作系统恢复 FPU 状态。但是您可以在此类错误上提供 SIGILL 或等效项......)无论如何,通常对于 SSE,您使用 @ 之一FP 上下文的 987654327@ 或 xsave / xrstor 指令对,节省 x87 + SSE + ...
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-11-29
相关资源
最近更新 更多