在ALIX / AMD Geode上运行voyage linux的nodejs导致“无效机器指令”

下面的调查结果是:最近Node.js不能移植到AMD Geode(或其他非SSE x86)处理器!

我深入了解了代码,并陷入了ia32汇编器的实现中,它将SSE / SSE2指令深入地集成到它们的代码(macros,macros,macros,…)中。 主要的后果是,由于缺less更新的指令集扩展,您无法在AMD Geode处理器上运行最新版本的node.js。 对387algorithm的回退只适用于node.js代码,但不适用于它所依赖的JavaScript V8编译器实现。 调整V8以支持非SSE x86处理器是一个痛苦和很多努力。

如果有人出示相反的证据,我会很高兴听到;-)

调查历史

我有一个正在运行的ALIX.2D13( https://www.pcengines.ch ),它有一个AMD Geode LX作为主处理器。 它运行voyage linux,一个基于debian jessi的资源受限embedded式设备发行版。

root@voyage:~# cat /proc/cpuinfo processor : 0 vendor_id : AuthenticAMD cpu family : 5 model : 10 model name : Geode(TM) Integrated Processor by AMD PCS stepping : 2 cpu MHz : 498.004 cache size : 128 KB physical id : 0 siblings : 1 core id : 0 cpu cores : 1 apicid : 0 initial apicid : 0 fdiv_bug : no f00f_bug : no coma_bug : no fpu : yes fpu_exception : yes cpuid level : 1 wp : yes flags : fpu de pse tsc msr cx8 sep pge cmov clflush mmx mmxext 3dnowext 3dnow 3dnowprefetch vmmcall bugs : sysret_ss_attrs bogomips : 996.00 clflush size : 32 cache_alignment : 32 address sizes : 32 bits physical, 32 bits virtual 

当我按照https://nodejs.org/en/download/package-manager/上的说明安装nodejs 8.x时,我得到了一些“无效的机器指令”(不确定是否正确,但是从德语错误输出转换)。 当我下载32位x86的二进制文件,以及手动编译时,也会发生这种情况。

在下面的答案后,我通过删除-msse2并添加-march=geode -mtune=geode更改deps/v8/gypfiles/toolchain.gypi的编译器标志。 现在我得到了同样的错误,但有一个堆栈跟踪:

 root@voyage:~/GIT/node# ./node # # Fatal error in ../deps/v8/src/ia32/assembler-ia32.cc, line 109 # Check failed: cpu.has_sse2(). # ==== C stack trace =============================== ./node(v8::base::debug::StackTrace::StackTrace()+0x12) [0x908df36] ./node() [0x8f2b0c3] ./node(V8_Fatal+0x58) [0x908b559] ./node(v8::internal::CpuFeatures::ProbeImpl(bool)+0x19a) [0x8de6d08] ./node(v8::internal::V8::InitializeOncePerProcessImpl()+0x96) [0x8d8daf0] ./node(v8::base::CallOnceImpl(int*, void (*)(void*), void*)+0x35) [0x908bdf5] ./node(v8::internal::V8::Initialize()+0x21) [0x8d8db6d] ./node(v8::V8::Initialize()+0xb) [0x86700a1] ./node(node::Start(int, char**)+0xd3) [0x8e89f27] ./node(main+0x67) [0x846845c] /lib/i386-linux-gnu/libc.so.6(__libc_start_main+0xf3) [0xb74fc723] ./node() [0x846a09c] Ungültiger Maschinenbefehl root@voyage:~/GIT/node# 

如果你现在看这个文件,你会发现以下内容

 ... [line 107-110] void CpuFeatures::ProbeImpl(bool cross_compile) { base::CPU cpu; CHECK(cpu.has_sse2()); // SSE2 support is mandatory. CHECK(cpu.has_cmov()); // CMOV support is mandatory. ... 

我评论了这条线,但仍然是“UngültigerMaschinenbefehl”(无效的机器指令)。

这是gdb ./node显示(执行run ):

 root@voyage:~/GIT/node# gdb ./node GNU gdb (Debian 7.7.1+dfsg-5) 7.7.1 [...] This GDB was configured as "i586-linux-gnu". [...] Reading symbols from ./node...done. (gdb) run Starting program: /root/GIT/node/node [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/i386-linux-gnu/libthread_db.so.1". [New Thread 0xb7ce2b40 (LWP 29876)] [New Thread 0xb74e2b40 (LWP 29877)] [New Thread 0xb6ce2b40 (LWP 29878)] [New Thread 0xb64e2b40 (LWP 29879)] Program received signal SIGILL, Illegal instruction. 0x287a23c0 in ?? () (gdb) 

我认为,有必要编译debugging符号…

 make clean make CFLAGS="-g" 

没有机会解决所有SSE / SSE2-问题…放弃! 看我最上面的部分

结论:在x86上运行时,node.js + V8通常需要SSE2。

在V8端口页面上 : x87(不正式支持)

如果需要的话,联系CL中的x87团队。 为此,请使用邮件列表v8-x87-ports.at.googlegroups.com。

JavaScript通常需要浮点数(每个数字variables都是浮点数,使用整数math只是一个优化),所以很难避免V8实际发出FPmath指令。

V8目前的devise总是 JIT,而不是解释。 它开始/退回到JITing未优化的机器代码,当它仍然分析,或者当它碰到什么使它“去优化”。

有一个解释器添加到V8的努力 ,但它可能没有帮助,因为解释器本身将使用TurboFan JIT后端编写。 这不是为了使V8可移植到架构上,而是目前不知道如何进行JIT。


疯狂的想法:在一个软件仿真层 (如Intel的SDE或者也许是qemu用户 ) 上运行node.js它可以在仅支持x87的x86 CPU上使用SSE / SSE2来模拟x86。 他们使用dynamic翻译,所以可能会运行在接近原生速度的代码,而不使用任何SSE指令。

这可能是疯狂的,因为node.js + V8可能是一些虚拟内存技巧,可能会混淆仿真层。 不过我想这个qemu应该足够强壮。


原始答案留在下面作为调查其他scheme这类问题的通用指南 。 (提示:grep的makefile文件等-msse-msse2 ,或者用pgrep -a gcc检查编译器命令行)。


您的cpuinfo说它有CMOV,这是一个686(ppro / p6)function。 这说 Geode支持i686。 与“普通”CPU相比缺less的是SSE2,在最近的一些编译器版本中,SSE2默认为-m32 (32位模式)。

无论如何,你应该做的是-march=geode -O3编译 ,所以gcc或clang将会使用你的CPU支持的所有东西,但是不会再有。

-O3 -msse2 -march=geode会告诉gcc它可以使用Geode支持的所有东西以及 SSE2,所以你需要删除任何-msse-msse2选项,或者在它们后面添加-mno-sse在node.js中, deps/v8/gypfiles/toolchain.gypi正在设置-msse2


使用-march=geode意味着-mtune=geode ,它会影响不涉及使用新指令的代码生成选项,所以如果运气好的话,它的运行速度会比只使用-mno-sse来控制指令集没有重写-mtune=generic东西。 (如果您正在build立geode,则可以使用-march=native ,这应该与使用-march=geode相同。)


另一种可能性是问题说明是在JIT编译的Javascript函数中。

node.js使用V8。 我做了一个快速的谷歌search,但没有发现任何告诉V8不要假设SSE / SSE2。 如果它没有用于浮点的代码生成策略(x87指令),则可能必须完全禁用JIT,并使其在解释器模式下运行。 (哪个更慢,这可能是一个问题。)

但是希望V8能够很好的运行,并且在JITing之前检查支持哪些指令集。


你应该通过运行gdb /usr/bin/node ,看看它的错误。 在GDB命令行上键入run my_program.js来启动程序。 (当你第一次启动gdb的时候,你不能将args传递给node.js,运行时你必须从gdb中指定args。)

如果引发SIGILL的指令的地址在映射到文件的内存区域中(如果gdb没有告诉你,则在/proc/pid/maps查找),这会告诉你哪个提前编译的可执行文件或图书馆负责。 用-march=geode编译它。

如果它在匿名内存中,则最有可能是JIT编译器输出。

当程序收到SIGILL时,GDB将停止指令地址的打印。 您也可以print $ip来查看EIP (32位模式指令指针)的当前值。