【问题标题】:How are traps generated for floating point exceptions?如何为浮点异常生成陷阱?
【发布时间】:2019-11-06 20:59:05
【问题描述】:

我想知道 glibc 库中的哪些代码和文件在启用陷阱时负责为浮点异常生成陷阱。

目前,RISC-V 的 GCC 不捕获浮点异常。我有兴趣添加此功能。因此,我正在研究如何在 GCC for x86 中实现此功能。

我知道我们可以按照 [问题] 中的描述捕获信号 (Trapping floating-point overflow in C) 但我想知道更多关于它是如何工作的细节。

我浏览了 glibc/math 中的文件,据我说这些文件以某种形式负责生成类似的陷阱

fenv.h
feenablxcpt.c
fegetexpect.c
feupdateenv.c

以及许多其他以fe 开头的文件。

所有这些文件也存在于 RISC-V 的 glibc 中。我不能 弄清楚 glibc for x86 是如何生成陷阱的。

【问题讨论】:

  • 这些天你得到的几乎唯一的“浮点异常”是 integer 除以零。浮点代码可以并且确实会生成无穷大或 NaN(非数字)值,以及非信号种类的 NaN。
  • @JonathanLeffler 我认为这是因为默认情况下仅启用除以零的陷阱。我们可以使用feenableexcept函数为其他浮点异常启用陷阱。

标签: c gcc glibc riscv fenv


【解决方案1】:

这些traps 通常由硬件 本身instruction set architecture (ISA) 级别生成。特别是在 x86-64 上。

我想知道 glibc 库中的哪些代码和文件在启用陷阱时负责为浮点异常生成陷阱。

所以没有这样的文件。然而,operating systemkernel(尤其是 Linux 上的signal(7)-s...)正在将陷阱转换为其他东西。

请阅读Operating Systems: Three Easy Pieces 了解更多信息。并详细研究x86-64指令集。

一个更熟悉的例子是整数除以零。在大多数硬件上,这会产生由内核处理的机器陷阱(或机器exception)。在某些硬件(IIRC,PowerPC)上,它给出 -1 作为结果并在状态寄存器中设置一些位。进一步的机器代码可以测试那个位。我相信GCC 编译器在某些情况下会在某些optimizations 禁用的情况下在每次除法后生成这样的测试。但这不是必须的。

C 语言(阅读 n1570,实际上是 C11 标准)定义了 undefined behavior 的概念,以尽可能快速和简单地处理此类情况。阅读 Lattner 的 What every C programmer should know about undefined behavior 博客。

既然您提到了RISC-V,请阅读上世纪的RISC 理念,并注意设计out-of-ordersuper-scalar 处理器需要大量的工程工作。我的猜测是,如果你投入与英特尔一样多的研发(这意味着数百亿美元或欧元)——或者,在较小程度上,AMD——在 x86-64 上投入 RISC-V 芯片,你可以获得可比的当前 x86-64 处理器的性能。请注意,SPARC 或 PowerPC(或者可能是 ARM)芯片类似于 RISC,它们最好的处理器在性能上几乎可以与英特尔芯片相媲美,但研发投资可能比英特尔在其微处理器中投入的少十倍。

【讨论】:

  • 感谢您的回答。我知道当发生任何浮点错误(如除零)时,会设置一些位。使用 fenv.h 中定义的函数,我们可以启用陷阱。因此,每当发生任何陷阱时,它不会像 NAN 那样为操作提供任何值,而是会中断正常流程并调用陷阱处理例程。如果您可以将代码指向 glibc 或其他负责生成陷阱的地方,那将会很有帮助。或者您是说这只能在 ISA 级别实现。
  • @FrackeR011:当 CPU 执行除以零时,它会生成一个异常,并且 CPU 开始执行内核的异常处理程序。内核的异常处理程序找出谁做了什么以及内核做了什么(以某种方式通知进程)。然后用户空间中的 C 库从内核获取此通知并将其转换为“C 语言信号”。当然,其他语言会做其他事情(例如,对于 Java,虚拟机会将其转换为 throw())。
  • @FrackeR011:请注意,对于某些内核(Linux),“以某种方式通知进程”可能看起来非常类似于“C 语言信号”,因此 C 库中的代码对于这种情况,可能是相对最低的。
  • @FrackeR011:对于 RISC-V(浮点问题会导致设置标志并且不会导致异常)我希望编译器必须在每个浮点指令之后注入检查,所以库中仍然没有代码来生成陷阱,因为它是由编译器而不是库生成的。注意:我也很想假设所有编译器生成的“浮点标志检查”加起来会导致巨大的性能灾难。
  • @Brendan 根据this post on HackerNews(引用RISC-V规范),“每个除法操作只需要添加一条分支指令,可以插入这条分支指令在除法之后,通常应该非常可预测地不采取,增加很少的运行时开销“。浮点“异常”(在标志中注明)可能相同。
猜你喜欢
  • 2011-10-15
  • 1970-01-01
  • 1970-01-01
  • 2021-11-19
  • 1970-01-01
  • 1970-01-01
  • 2011-04-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多