可移植性準則

Emscripten 可用於將幾乎任何可移植的 C++/C 程式碼編譯為 JavaScript。

本節說明哪些類型的程式碼不可移植(或更難移植),以及哪些程式碼可以編譯但執行速度會很慢。開發人員可以使用此資訊來評估移植和重寫程式碼的工作量。

當前 Web 限制

  • 多執行緒取決於 SharedArrayBuffer,而 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 也會阻止它周圍的重新迴圈,迫使我們使用效率較低的方法來模擬控制流程。

其他問題

  • 依賴 x86 對齊行為的程式碼。x86 允許未對齊的讀取和寫入 (例如,您可以從非偶數位址讀取 16 位元值),但其他架構不允許 (32 位元 ARM 會引發 SIGILL)。對於 asm.js,載入和儲存會強制對齊偏移;對於 WebAssembly,未對齊的載入和儲存會運作,但速度可能會很慢,具體取決於底層 CPU。如果您使用 SAFE_HEAP=1 建置程式碼,則會收到明確的執行階段例外,請參閱除錯