Emscripten 可用于将几乎所有可移植的 C++/C 代码编译为 JavaScript。
本节解释了哪些类型的代码不可移植(或更难移植),以及哪些代码可以编译但运行速度会很慢。开发人员可以使用这些信息来评估移植和重写代码的工作量。
多线程依赖于 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.