【问题标题】:Division by zero not causing runtime exception on Nvidia Jetson在 Nvidia Jetson 上除以零不会导致运行时异常
【发布时间】:2020-01-13 13:57:39
【问题描述】:

我对 ARM 处理器的内部细节不是很熟悉,但我不了解我的 Nvidia Jetson Nano 开发板上的以下行为。

C 代码示例 ...

//main.c

#include <stdio.h>

int main()
{
    int fred = 123;
    int i;

    for(i = -10 ; i <= 10 ; i++)
        printf("%d / %d == %d\n", fred, i, fred / i);

    return 0;
}

编译:

gcc main.c -ggdb

运行生成的 a.out 可执行文件会产生以下输出...

123 / -10 == -12
123 / -9 == -13
123 / -8 == -15
123 / -7 == -17
123 / -6 == -20
123 / -5 == -24
123 / -4 == -30
123 / -3 == -41
123 / -2 == -61
123 / -1 == -123
123 / 0 == 0                  //unexpected!
123 / 1 == 123
123 / 2 == 61
123 / 3 == 41
123 / 4 == 30
123 / 5 == 24
123 / 6 == 20
123 / 7 == 17
123 / 8 == 15
123 / 9 == 13
123 / 10 == 12

使用 gcc 3.7 在古老的 Pentium 4 上编译的完全相同的代码会导致(如预期的那样)在 i 达到 0 并导致除以零时引发运行时异常。

Nvidia 主板运行 Ubuntu 18.04 LTS,gcc 版本 7.4.0(最新),并且在其他方​​面运行良好。我还编译了此代码的等效 Ada 语言版本,并按预期引发了运行时异常 is(因为 Ada 代表我提前进行了安全检查)。

我意识到在 C 中,“除以零会产生未定义的行为”可能是对此的解释,但是对于同一编译器套件的两个版本为同一操作提供如此不同的结果让我感到困惑。

哪些情况会导致 Nvidia Tegra ARM(64 位)CPU 允许除以零被操作系统忽略?

编辑: /etc/cpuinfo 中有关 CPU 的详细信息...

$ cat /proc/cpuinfo
processor       : 0
model name      : ARMv8 Processor rev 1 (v8l)
BogoMIPS        : 38.40
Features        : fp asimd evtstrm aes pmull sha1 sha2 crc32
CPU implementer : 0x41
CPU architecture: 8
CPU variant     : 0x1
CPU part        : 0xd07
CPU revision    : 1

.... truncated ....

【问题讨论】:

  • 你能检查一下FPSCR-&gt;DZE是否被设置。它是Division by Zero exception enable bit。它也可能适用于整数。
  • @FiddlingBits 它似乎在 Linux 下运行(我猜是用户空间?)。所以我想这是关于 SIGTRAP 处理的。好吧,也许内核没有配置为设置这个位......
  • @EugeneSh。是的用户空间,以非 root 用户身份运行,没有特殊权限。
  • 它是什么 ARM 架构?基于此:community.arm.com/developer/ip-products/processors/b/… 在除以零的情况下可能会有不同的行为。例如在 ARMv7-A 上 - 除以零总是返回零结果。

标签: c arm integer-division divide-by-zero armv8


【解决方案1】:

值得注意的是,并非所有 ARM 处理器都是相同的(如前所述)。我正在开发一个带有 STM32L4(包含 cortex-m4)的项目。它确实有 SDIV 和 UDIV 指令。但是,除以零时生成异常是可配置的(请参阅STM32 Cortex®-M4 MCUs and MPUs programming manual,第 231 页)

更复杂的事情是,编译器有时可以识别除以零大小写并生成非法指令(排除我提到的配置)。玩玩这个example

【讨论】:

    【解决方案2】:

    Nvidia Jetson Nano 开发板使用基于 ARMv8 架构的 ARM Cortex-A57 (Link)。 基于 ARMv8 的指令集spec整数除以零返回零且不被困。

    2.3 除法说明

    ARMv8-A 支持 32 位和 64 位大小的值的有符号和无符号除法。

    指令说明

    SDIV 有符号除法

    UDIV 无符号除法

    ...

    溢出和被零除不会被困

    • 任何整数除以零返回零

    所以在这种情况下编译器会生成sdiv(参见example),CPU 会毫无异常地返回 0。当您在不同平台上编译相同的代码时,每个其他 CPU 对被零除的反应可能不同。正如您在问题中提到的,在除以 0 的情况下,C 标准的行为是 undefined

    【讨论】:

    • 我以前从未意识到各种架构对此类事情的处理方式如此不同。我曾经愚蠢地认为,像 POSIX 所要求的那样,这样一个基本的事情总是会导致硬 SIGFPE。迷人。感谢您的努力。
    • @Wossname 你能分享一下POSIX 要求整数除以零会引发SIGFPE 的地方吗?我发现this 建议将SIGFPE 的值设为FPE_INTDIV 是可选的:“实现可能[...] 包含阻止生成某些值的扩展或限制。”
    最近更新 更多