JS_CANONICALIZE_NAN在spidermonkey引擎中的目的是什么?

时间:2012-02-24 17:35:14

标签: spidermonkey

我想知道JS_CANONICALIZE_NAN的目标是什么,是否在所有平台上都需要它?

1 个答案:

答案 0 :(得分:7)

这是一个有趣的!因此,SpiderMonkey在内部使用标记值表示来表示JavScript的“无类型值” - 这允许VM确定“a中存储的变量是一个数字,以及存储在b中的值是一个数字,因此运行a + b会进行数字加法“。

有很多不同的价值标记方案,SpiderMonkey使用一种称为“NaN拳击”的方案。这意味着引擎中的所有无类型值都由64位值表示,这些值可以是:

  • 一个双人,或
  • 一个标记的非double,它位于IEEE双精度浮点值的“NaN空间”中。

这里真正的技巧是现代系统通常使用单个位模式来表示NaN,您可以通过math.h的sqrt(-1)log(0)来观察。但是根据IEEE浮点规范,有一些很多的位模式也被认为是NaN。

double由子字段组成:

{sign: 1, exponent: 11, significand: 52}

通过用1s填充指数字段并在有效数字中放置非零值来表示NaN。

如果您运行这样的小程序来查看平台的NaN值:

#include <stdio.h>
#include <math.h>
#include <limits>

static unsigned long long 
DoubleAsULL(double d) {
    return *((unsigned long long *) &d);
}

int main() {
    double sqrtNaN = sqrt(-1);
    printf("%5f 0x%llx\n", sqrtNaN, DoubleAsULL(sqrtNaN));
    double logNaN = log(-1);
    printf("%5f 0x%llx\n", logNaN, DoubleAsULL(logNaN));
    double compilerNaN = NAN;
    printf("%5f 0x%llx\n", compilerNaN, DoubleAsULL(compilerNaN));
    double compilerSNAN = std::numeric_limits<double>::signaling_NaN();
    printf("%5f 0x%llx\n", compilerSNAN, DoubleAsULL(compilerSNAN));
    return 0;
}

你会看到这样的输出:

 -nan 0xfff8000000000000 // Canonical qNaNs...
  nan 0x7ff8000000000000
  nan 0x7ff8000000000000
  nan 0x7ff4000000000000 // sNaN (signaling)

请注意,安静NaN的唯一区别在于符号位,后面跟着12位1,满足上面提到的NaN要求。最后一个,即信号NaN,清除第12个(is_quiet)NaN位并使第13个保持上面提到的NaN不变量。

除此之外,NaN空间可以自由播放--11位来填充指数,确保signficand非零,并且你还有很多空间。在x64上,我们使用47位虚拟地址假设,留下64 - 47 - 11 = 6位用于注释值类型。在x86上,所有对象指针都适合低32位。

然而,我们仍然需要确保非规范的NaN,如果它们通过像js-ctypes这样的东西进行爬行,就不会产生类似于标记非双值的东西,因为这可能导致可利用的行为VM。 (将数字视为对象是非常多的坏消息。)因此,当我们形成双精度数(如在DOUBLE_TO_JSVAL中)时,我们确保规范化d != d到规范NaN形式的所有双精度数。

更多信息位于bug 584168