可移植性指南

Emscripten 可用于将几乎所有可移植的 C++/C 代码编译为 JavaScript。

本节解释了哪些类型的代码不可移植(或更难移植),以及哪些代码可以编译但运行速度会很慢。开发人员可以使用这些信息来评估移植和重写代码的工作量。

当前 Web 限制

  • 多线程依赖于 SharedArrayBuffer,它仍在浏览器中进行标准化和实现。Emscripten 对线程具有工作支持,您可以在开发浏览器中通过设置适当的偏好来尝试。

  • SIMD 也正在进行标准化和实现。

无法编译的代码

以下类型的代码需要重写才能与 Emscripten 一起使用。(虽然理论上 Emscripten 可能会使用仿真来解决这些问题,但这会非常慢。)

  • 依赖于大端架构的代码。目前,使用 Emscripten 编译的代码需要一个小端主机才能运行,这占互联网连接的机器的 99%。这是因为用于内存视图的 JavaScript 类型化数组遵循主机字节顺序,而 LLVM 需要知道要针对哪个字节序。

  • 使用本机环境的底层功能的代码,例如与 setjmp/longjmp 结合的本机堆栈操作(我们支持正确的 setjmp/longjmp,即跳到堆栈下方,但不跳到展开的堆栈上方,这是未定义的行为)。

  • 扫描寄存器或堆栈的代码。这将不起作用,因为寄存器或堆栈中的变量可能保存在 JavaScript 局部变量中(无法扫描)。

注意

这种类型的代码可能用于保守的垃圾回收。当堆栈上没有其他代码时,例如从主事件循环的迭代中,您可以进行保守扫描。其他解决方案包括 Binaryen 中的 SpillPointers 通道。

  • 使用特定于架构的内联汇编的代码(例如包含 x86 代码的 asm())不可移植。该代码需要用可移植的 C 或 C++ 替换。有时代码库既有可移植的代码,也有可选的内联汇编作为优化,因此您可能会找到一个选项来禁用内联汇编。

可以编译但可能运行速度很慢的代码

注意

了解这些问题有助于优化代码。

以下类型的代码将编译,但可能无法像预期的那样快运行

  • 在 asm.js 中(但不在 WebAssembly 中),64 位 int 变量。数学运算(+、-、*、/)速度很慢,因为它们是模拟的(按位运算相当快)。JavaScript 没有本机的 64 位 int 类型,因此这是不可避免的。

  • C++ 异常。在 JavaScript 中,此类代码通常会导致 JavaScript 引擎关闭各种优化。为此,在 -O1 及更高版本中默认情况下会关闭异常。要重新启用它们,请使用 -sDISABLE_EXCEPTION_CATCHING=0 运行emcc(参见 src/settings.js)。

  • setjmp 也阻止了围绕它的 relooping,迫使我们使用效率较低的方法模拟控制流。

其他问题

  • 依赖于 x86 对齐行为的代码。x86 允许未对齐的读写(因此例如您可以从非偶数地址读取 16 位值),但其他架构不允许(32 位 ARM 将引发 SIGILL)。对于 asm.js,加载和存储被强制为对齐的偏移量;对于 WebAssembly,未对齐的加载和存储将起作用,但速度可能会很慢,具体取决于底层 CPU。如果您使用 SAFE_HEAP=1 构建代码,那么您将获得一个明确的运行时异常,请参见 Debugging.