翻譯:雲荒杯傾
本文是Emscripten-WebAssembly專欄系列文章之一,更多文章請查看專欄。
也能夠去做者的博客閱讀文章。
歡迎加入Wasm和emscripten技術交流羣,羣聊號碼:939206522。html
Emscripten代碼移植主題涵蓋了將C、C++代碼移植到Emscripten時須要考慮的全部核心考慮問題,以及通常的編碼和調試指南。
共有如下主題。c++
每一部份內容都比較多,本文主要講第一部分,代碼可移植性與限制。下面是正文:git
Emscripten幾乎能夠編譯任何可移植的c/c++代碼到JavaScript。但因爲瀏覽器環境限制和Emscripten編譯出來的代碼的限制,一些代碼爲了能被編譯須要作改動,本文就幫咱們找出這部分代碼。程序員
本節解釋了哪些類型的代碼是不可移植的(或者更難於移植);哪些代碼能夠編譯,但會運行得很慢。開發人員可使用這些信息來評估移植代碼和重寫代碼的工做量。github
爲了使Emscripten工做,下面類型的代碼須要重寫。(理論上,在使用模擬的狀況下,可使用Emscripten解決這些問題,但速度很是慢。)web
Note: 若是JavaScript標準機構將共享狀態添加到webworker中,支持多線程代碼將成爲可能。
SAFE_HEAP = 1構建您的代碼,那麼您將獲得一個清晰的運行時異常,參見調試。編程
jumping down the stack, but not jumping up to an unwound stack, which is undefined behavior).segmentfault
NOTE: 若是你是一個喜歡本身寫垃圾回收程序的程序員,可能對這類代碼比較熟。。。
Note: 當你要優化代碼的時候,就會知道了解這些事項是有用的。
下面類型的代碼會被編譯,可是可能運行的很慢:windows
瀏覽器環境和JavaScript不一樣於C/C++一般運行的本地環境。這些差別對如何調用和使用本地API施加了一些限制。本部分列出了一些比較明顯的限制。api
Emscripten支持libc庫的網絡函數,但您必須限制他們是異步(非阻塞)操做。這是由於底層的JavaScript網絡函數是異步的。
Emscripten支持libc文件系統函數,C /C++代碼能夠以正常方式編寫。
在瀏覽器環境中運行的代碼是沙盒sandboxed,而且不直接訪問本地文件系統。而後,Emscripten就建立了一個虛擬文件系統,它能夠預裝數據,或者連接到url來懶加載。這會影響同步文件系統函數調用以及一個項目如何被編譯。關於這方面,請參見文件系統概述。
瀏覽器事件模型使用合做模式的多任務處理——每一個事件都有一個運行的「turn」,而後必須將控制權返回給瀏覽器事件循環,這樣其餘事件就能夠處理了。
HTML頁面掛起的一個常見緣由是JavaScript未完成而且未將控制權返回給瀏覽器。
這將影響含有死循環的主函數的代碼編寫。有關更多信息,請參見Emscripten Runtime環境。
函數指針有三個主要的問題:
一、指針類型轉換會引發指針調用失敗。
針對函數聲明時的簽名不一樣,函數指針會被存儲到不一樣的表中。當一個函數被調用時,它會在與當前函數指針簽名關聯的表中搜索它。若是你進行了指針類型轉換,而指針和全部的表並無被修改,則調用代碼將在錯誤的表中查找。而錯誤的表中實際上極可能並無一個叫該名字的指針,這樣就出錯了。
例如,一個聲明爲int(int)(返回int,接收int)的函數,會被添加到表FUNCTION_TABLE_ii。若是您將一個指向該函數指針投射到void(int)(不返回,接收int),那麼代碼將在FUNCTION_TABLE_vi中查找函數。
你可能看到編譯警告:
warning: implicit declaration of function
推薦的解決方案是重構代碼以免這種狀況,以下面的Asm指針轉換所描述的那樣。
二、當你使用-o2以及更高優化級別的時候,比較不一樣類型的函數指針會產生錯誤的結果,而錯誤的函數指針可能更具誤導性。要檢查你的代碼出問題的緣由,能夠將aliasing_function_pointer設爲零,(- s aliasing_function_pointer= 0)進行編譯。
NOTE: 在asm.js中,函數指針存儲在特定函數類型的表中。如FUNCTION_TABLE_ii。 在較低級別的優化中,每一個函數指針在全部函數類型表上都有一個唯一的索引值(一個函數指針只在其中一個表的某個索引位置存在,在全部其餘表中 這個索引位置都是一個空槽)。所以,比較函數指針(索引)能給出了一個準確的結果,但若是是試圖在錯誤的表中調用函數指針,將會拋出一個錯誤,由於該索引是空的。 在-o2和更高級別的優化設置下,表被優化,以致於全部函數指針都在順序索引中。這是一個有用的優化,由於若是沒有全部空槽,表就更緊湊, 但它確實意味着函數索引再也不是「全局」的唯一(由於一個函數指針在這張表中的索引位置與在另外一張表中的索引位置不一樣了)。此時須要一張特定的表 和在這樣表中的特定位置索引纔可以惟一索引到一個函數。 所以,高級別的優化編譯: 一、因爲不一樣類型的函數能夠有相同的索引(儘管在不一樣的表中),函數指針的比較可能會產生錯誤的結果。 二、函數指針代碼中的錯誤更難於調試,由於它們致使錯誤的代碼被調用,而不是顯式的錯誤(就像在表中的「漏洞」中那樣)。
三、結構體按值傳遞時,老版本的clang會爲c和c++代碼生成兩種不一樣的代碼,這兩種格式的代碼不兼容,你可能會收到一個警告。
解決方案是按引用傳遞結構體,或者不要在有結構體的位置混淆c和c++(好比,重命名.c爲.cpp)。
如上所述,在asm.js模式下,函數指針必須使用正確的類型調用,不然調用將失敗。這是由於在函數聲明的時候,每一個函數指針會基於這個函數的簽名被存儲在一個特定的表中: 將指針轉換爲另外一個類型會致使調用代碼在錯誤的位置(表)查找函數指針。
NOTE: 對於每種類型的函數指針都有一個單獨的表,可讓JavaScript引擎知道每一個函數指針調用的確切類型,這樣也好進而優化他們。
有三種解決辦法,優先選擇第二種:
添加額外的參數/刪除參數/更改參數類型/添加或刪除返回類型等。這能夠增長顯著的運行時開銷,所以不推薦,但值得嘗試。
本頁面列出了一些 與Emscripten編譯出來的應用程序和遊戲相關的 主要瀏覽器的最新版本之間的差別:
Opera 12.16和Windows谷歌Chrome 28.0.1500.95有一個限制,即計時器的精度僅爲毫秒。
在其餘主流瀏覽器上(IE10,firefox22,非windows的Chrome 28),也都是亞毫秒精度。
這意味着不可能使用瀏覽器的圖像編解碼器來解碼Emscripten虛擬文件系統中的預加載文件。
Emscripten代碼移植主題系列文章是emscripten中文站點的一部份內容。
第一個主題介紹代碼可移植性與限制
第二個主題介紹Emscripten的運行時環境
第三個主題第一篇文章介紹鏈接C++和JavaScript
第三個主題第二篇文章介紹embind
第四個主題介紹文件和文件系統
第六個主題介紹Emscripten如何調試代碼