关于javascript:asm.js如何处理被零除?

How does asm.js handle divide-by-zero?

在javascript中,用"integer"参数除零的操作类似于浮点:

1
2
3
 1/0;    // Infinity
-1/0;    // -Infinity
 0/0;    // NaN

JS规范指出,带整数参数的除法返回intish,必须立即强制将其转换为有符号或无符号。如果在javascript中这样做,用"integer"参数除零后,coersion始终返回零:

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

然而,在像Java和C这样的实际整数类型的语言中,将整数除以0是一个错误,并且执行会以某种方式停止(例如,抛出异常、触发陷阱等)。

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

(signed, signed) → intish ∧
(unsigned, unsigned) → intish ∧
(double?, double?) → double ∧
(float?, float?) → floatish

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

(double?, double?) → double

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


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等函数)。请注意,此函数被零除的行为可能在实现之间有所不同,必须使用零分母上的分支进行处理,或者在加载时进行检查(与上面为UDIVSDIV所概述的情况相同)。

我将留给您一个问题:当执行以下C代码时,asm.js中会发生什么:INT_MIN/-1