【问题标题】:How does asm.js handle divide-by-zero?asm.js 如何处理被零除?
【发布时间】:2015-03-21 06:21:00
【问题描述】:

在 javascript 中,使用“整数”参数除以零的行为类似于浮点应该:

 1/0;    // Infinity
-1/0;    // -Infinity
 0/0;    // NaN

asm.js 规范说,带整数参数的除法返回intish,必须立即强制转换为有符号或无符号。如果我们在 javascript 中执行此操作,使用“整数”参数除以零在强制转换后总是返回零:

(1/0)|0;    // == 0, signed case.
(1/0) >> 0; // == 0, unsigned case.

但是,在 Java 和 C 等具有实际整数类型的语言中,将整数除以零是错误的,并且执行会以某种方式停止(例如,引发异常、触发陷阱等)。

这似乎也违反了 asm.js 指定的类型签名。 InfinityNaN 的类型是 double/ 应该是(来自规范):

(签名,签名)→ intish ∧ (无符号,无符号)→ intish ∧ (double?, double?) → double ∧ (浮动?,浮动?)→浮动

但是,如果其中任何一个的分母为零,则结果为double,因此类型似乎只能是:

(双倍?,双倍?) → 双倍

asm.js 代码中预期会发生什么?它遵循javascript并返回0还是被零除会产生运行时错误?如果它遵循javascript,为什么输入错误可以?如果它产生运行时错误,为什么规范没有提到它?

【问题讨论】:

    标签: javascript asm.js


    【解决方案1】:

    asm.js 是 JavaScript 的子集,因此它必须返回 JavaScript 所做的事情:Infinity|00

    您指出 Infinitydouble,但这将 asm.js 类型系统与 C 类型系统混淆了(在 JavaScript 中是 number):asm.js 使用 JavaScript 类型强制来生成中间结果当他们不是时,“正确”的类型。当 JavaScript 中的一个小整数溢出到 double 时,也会发生同样的情况:它会使用按位运算强制转换回整数。

    这里的关键是它给编译器一个提示,它不需要计算 JavaScript 通常会让它计算的所有东西:如果一个小整数溢出也没关系,因为它被强制转换回整数,因此编译器可以省略溢出检查并发出直线整数运算。请注意,对于每个可能的值,它仍然必须正确运行!类型系统基本上会提示编译器进行一系列强度降低。

    现在回到整数除法:在 x86 上,这会导致浮点异常(是的!整数除法会导致 SIGFPE!)。编译器知道输出是一个整数,所以它可以进行整数除法,但如果分母为零,它就不能停止程序。这里有两种选择:

    • 如果输入为零,则围绕除法进行分支,并直接返回零。
    • 使用提供的输入进行除法,但在程序开始时安装一个信号处理程序,捕获SIGFPE。当它出错时查找代码位置,如果编译器的元数据表明这是一个除法位置,则将返回值修改为零并继续执行。

    前者是V8和OdinMonkey实现的。

    在 ARM 上,整数除法指令被定义为始终返回零,除了 ARM 的 ARMv7-R 配置文件出现故障(故障是未定义的指令,或者如果 SCTRL.DZ == 0 可以更改为返回零)。 ARM 最近仅在 ARMv7VE 扩展(虚拟化扩展)中添加了 UDIVSDIV 指令,并使其在 ARMv7-A 处理器中成为可选(大多数手机和平板电脑都使用这些)。您可以使用/proc/cpuinfo 检查指令,但请注意,某些内核不知道该指令!一种解决方法是在进程开始时检查该指令,方法是执行指令并使用sigsetjmp/siglongjmp 捕获未处理的情况。这还有一个进一步的警告,即在不支持它的处理器上捕获内核“有用”并模拟UDIV/IDIV 的情况!如果该指令不存在,那么您必须使用 C 库的整数除法指令(libgcccompiler_rt 包含诸如 __udivmoddi4 之类的函数)。请注意,此函数在除以零时的行为可能因实现而异,并且必须使用零分母上的分支来处理或在加载时检查(与上面针对UDIV/SDIV 概述的相同)。

    我先问你一个问题:在 asm.js 中执行以下 C 代码时会发生什么:INT_MIN/-1?

    【讨论】:

    • 所以这里的一般原则是 asm.js 继承了所有 JS 语义以及额外的数字类型,并且它取决于以 asm.js 为目标的编译器以在这些之上实现其语言的语义(例如 C 语言)。 INT_MIN/-1 在 C 中未定义,但在 asm.js 中,截断后又是 INT_MIN(有符号溢出),并且由于行为在 C 中未定义,这没关系吗?
    • @FrancisAvila 有两个编译器:LLVM 从 C/C++ 生成 asm.js 代码,以及 SpiderMonkey/V8 从 JavaScript 生成程序集。 SpiderMonkey 有一个有效 asm.js 代码 (OdinMonkey) 的快捷方式,但该快捷方式仍然必须是 100% 有效的 JavaScript。 V8 与 TurboFan 做了类似的事情,但它不验证 asm.js 子集(只需“使用 asm”就足以触发它)。 LLVM 必须生成遵循 C 规则的 asm.js 代码,这就是为什么 INT_MIN/-1 可以为所欲为,因为它是未定义的,因此 JavaScript 的结果与任何代码一样好。 SpiderMonkey/V8 对 C 规则一无所知!
    猜你喜欢
    • 2014-07-15
    • 1970-01-01
    • 2023-03-14
    • 2021-09-28
    • 2021-02-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多