在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指定的类型签名。Infinity和NaN的类型是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|0→0。
您指出,Infinity是double,但它将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扩展(虚拟化扩展)中添加了UDIV和SDIV指令,并在ARMV7-A处理器中进行了可选(大多数手机和平板电脑都使用这些指令)。您可以使用/proc/cpuinfo检查指令,但请注意,有些内核不知道该指令!解决方法是在进程开始时检查指令,方法是执行指令并使用sigsetjmp/siglongjmp捕获未处理的情况。还有一点需要注意的是,在内核"有用"的情况下,还要在不支持它的处理器上模仿UDIV/IDIV!如果指令不存在,则必须使用C库的整数除法指令(libgcc或compiler_rt包含__udivmoddi4等函数)。请注意,此函数被零除的行为可能在实现之间有所不同,必须使用零分母上的分支进行处理,或者在加载时进行检查(与上面为UDIV或SDIV所概述的情况相同)。
我将留给您一个问题:当执行以下C代码时,asm.js中会发生什么:INT_MIN/-1?
- 因此,这里的一般原则是asm.js继承了所有的JS语义加上额外的数字类型,并由面向asm.js的编译器来实现它们语言的语义(例如c)。INT_MIN/-1在c中是未定义的,但在asm.js中,截短后它又是int_min(有符号溢出),既然行为在c中是未定义的,那还可以吗?
- FrancisAvila有两个编译器:LVVM从C/C++生成As.js代码,从JavaScript中生成SpIDelSmith/V8生成程序集。spidermonkey有一个有效asm.js代码(odimonkey)的快捷方式,但该快捷方式仍然必须是100%有效的javascript。V8与涡轮风扇发动机类似,但它不能验证asm.js子集(只需"使用asm"就足以触发它)。llvm必须生成符合C规则的asm.js代码,这就是为什么INT_MIN/-1可以做它想要做的任何事情,因为它是未定义的,所以javascript的结果是一样好的。蜘蛛猴/V8对C规则一无所知!