# 2017q3 team2 WasmVM contributed by <`tina0405`、`workfunction`、`LinRiver`> :::info [分工表](https://hackmd.io/s/BJvz4gFfG#) ::: ### 為什麼要研究 WebAssembly * 首先在網站上看到了 [demo](https://websightjs.com/) 程式,此程式是利用 opencv 來偵測人臉,用來比較 Javascript,Asm.js 和 WebAssembly 的每秒能處理的張數(fps) * 這邊會發現 WebAssembly 能夠每秒能夠處理的張數遠遠超過 Javascript,想探究其原因,所以作了以下的比較 #### Javascript 和 WebAssembly 的比較 ##### 參考 [What makes WebAssembly fast?](https://hacks.mozilla.org/2017/02/what-makes-webassembly-fast/) * Javascript : 文中提到,如果我們想要了解他們之間差在哪?就必須從 JS 引擎的運作開始了解(此圖並非精準的時間軸而是用來比較和 WebAssembly 之間的差異而已) ![](https://i.imgur.com/OHdEHfs.png) * 語法分析(Parsing) — 把原始代碼變成直譯器可以執行的語言。 * 編譯 + 優化 (Compiling + optimizing) — 這是初級編譯器 (baseline compiler) 和優化編譯器 (optimizing compiler)所花的時間。有些最佳化的編譯器工作不在主執行緒,那它就不被包含在這部分 * 重優化 (Re-optimizing) — 這是 JIT 花在重新調整程式碼的時間,因為它可能會假設錯誤,包含了重優化和回到原本基準的程式碼 。 * 執行 (Execution) — code 執行所花費時間 * 垃圾回收 (Garbage collection) — 記憶體的清理 * WebAssembly: ![](https://i.imgur.com/MAuTn0w.png) * 解碼 (Decode): 因為是 Binary Format 所以不用經過直譯器可直接解碼 * 編譯和優化 (Compiling + optimizing): 初級編譯器和優化編譯器花時間。 * 執行 (Execution): 執行所花時間 * 省下重優化 (Re-optimizing) 的原因: 在 WebAssembly 中,類型都是固定的,所以 JIT 不用根據變數類型作優化假設,所以沒有重優化階段。 * 省下垃圾回收 (Garbage collection) 的原因: 目前為止 WebAssembly 不支持垃圾回收 (Garbage collection) 記憶體操作變成使用者要自行負擔成本,這樣雖然對開發者不方便,但執行效率卻提高。 * Asm.js: 是 WebAssembly 的前一階段,所以在記憶體處理這方面也是使用者自行操作,差別在於 WebAssembly 以完全跳脫 Javascript 架構 * Asm.js 是 Javascript 的子集,可以使用 Javascript 部分函式,協助 C 或 C++ 也可以支援 Javascript,因為使用者自己處理記憶體所以節省 Garbage collection 的時間。 * C/C++ 轉成 asm.js 的過程 * C/C++ ⇒ LLVM IR ⇒ Emscripten 編譯器 ⇒ asm.js [圖片來源](http://www.ruanyifeng.com/blog/2017/09/asmjs_ems) ![](https://i.imgur.com/VmOBv9w.png) * WebAssembly 因為是二進位制的編碼,比起 Asm.js 人類可讀的文本來說,運轉速度更快體積更小 ### WebAssembly 發展 * 在 Mozilla 推出 Asm.js 架構前, 網頁程式需要使用 JIT(Just in Time) 逐行編譯JavaScript 程式碼, 因而影響了執行效能 * 雖然在推出 Asm.js 架構後提昇了執行效能, 卻也限制了 JavaScript 某些功能, 主要是去除了會影響執行效能的功能後來進行優化 * 為了改善上述困擾, 2015年6月由Chrome、Edge和Firefox三大瀏覽器及Safari的WebKit的工程師們聯手推動的新網頁格式標準WebAssembly發布, 它可作為一個網站程式碼的新中介層, 可以提供一個二進位檔案格式標準來執行網頁. 更準確來說, WebAssembly 直接定義了一個新的網頁執行層, 讓瀏覽器能直接執行二進位檔案格式的 WebAssembly 檔案, 來達到像執行 Bytecode 程式般的高效能 >>要補上參考資料來源喔! >>[name=tina0405] ### 文字檔與位元檔的轉換,執行 wat2wasm ###### 參考電子書 [以 WasmVM 向 WebAssembly 說哈囉!](https://www.gitbook.com/book/luishsu/wasmvm-webassembly/details) * 因為在 WasmVM 中是以位元格式 (Binary format) 做檔案的讀取,而我們比較方便閱讀的格式是文字格式 (Text format) * 位元格式:會以 .wasm 為副檔名 * 位元格式的範例: ~~~ 0061 736d 0100 0000 0104 0160 0000 0302 0100 0801 000a 1001 0e00 410a 4105 6e00 410b 4110 6b00 0b0a ~~~ * 文字格式:會以 .wat 為副檔名 * 文字格式的範例: ~~~ (module (func $main i32.const 10 i32.const 5 unreachable ) (start $main) ) ~~~ * 這之間的轉換我們可以使用 [The WebAssembly Binary Toolkit ](https://github.com/WebAssembly/wabt) * 先複製 `git clone git@github.com:WebAssembly/wabt.git` * 進入 wabt 資掉夾並建立 build 資料夾 `cd wabt && mkdir build` * 進入 build 資料夾執行 CMake `cd build && cmake -DBUILD_TESTS=OFF ..` * 執行 wat2wasm 檔,使用方法如下: (-v 是為了方便查看位元檔內容) `./wat2wasm 輸入文字檔檔名 -o 輸出位元格式檔名 -v` * 這套工具還支援 `wasm2wat` 的反轉模式 * 得到的位元格式檔就可以放入: `./WasmVM 位元格式檔` ### 理解文字格式 (Text format) 的規則 * 常數宣告 | 宣告方式| 整數/小數 | 類型 | | :--------: | :--------: | :--------: | | i32.const |整數| 32 位元 | | i64.const |整數| 64 位元 | | f32.const |小數|單精度浮點數| | f64.const |小數|雙精度浮點數| ~~~ 使用16進位及科學記號都是被允許的 ~~~ * 以文字格式呈現: ~~~ (module (func $main i32.const 2 i64.const 17 i64.const 0x11 f32.const -0.25 f32.const -inf f32.const nan unreachable ) (start $main) ) ~~~ * 執行 `./WasmVM 數入位元格式檔案` 的結果 ~~~ tina@tina-X550VB:~/Hw/WasmVM/build$ ./WasmVM ~/Hw/wabt/build/ttest Values in the stack: Type: f32, Value: nan Type: f32, Value: -inf Type: f32, Value: -0.25 Type: i64, Value: 17 Type: i64, Value: 17 Type: i32, Value: 2 ~~~ * 我們會發現其常數的確是以堆疊的形式擺放,先進的後出 * 參考[stack 示意圖](https://www.programiz.com/dsa/stack) ![](https://i.imgur.com/xqhLxRu.jpg) * 有了常數的類型,我們可以繼續往下運算: * 整數的一元運算 | 型態 | 解釋 | | -------- | -------- | | i32.clz | 由左邊數來碰到 1 前會遇到幾個 0 | | i32.ctz | 由右邊數來碰到 1 前會遇到幾個 0 | | i32.popcnt|計算整組位元中有幾個 1| | i32.eqz |這個整數是否為 0,是 -> 1,不是 -> 0| * 測試 * 以文字格式呈現: ~~~ (module (func $main i32.const 2248752 ;; 00000000 00100010 01010000 00110000 i32.clz i32.const 2248752 i32.ctz unreachable i32.const 2248752 i32.popcnt unreachable i32.const 2248752 i32.eqz unreachable i32.const 0 i32.eqz unreachable ) (start $main) ) ~~~ * 結果: 會發現 unreachable 會將多型態堆疊 (polymorphic stack) 的值先 pop 出去 * 原因在[電子書](https://luishsu.gitbooks.io/wasmvm-webassembly/content/simple-instructions.html)提到: unreachable 以 Debug 模式編譯,會輸出堆疊裡的數值,不放回堆疊,方便除錯 * 這邊會稱作多型態堆疊是因為放置在堆疊的這些資料型態是沒有限制的,參考 [webassembly 網站](http://webassembly.org/docs/semantics/) ~~~ tina@tina-X550VB:~/Hw/WasmVM/build$ ./WasmVM ~/Hw/wabt/build/ttest Values in the stack: Type: i32, Value: 4 Type: i32, Value: 10 Values in the stack: Type: i32, Value: 6 Values in the stack: Type: i32, Value: 0 Values in the stack: Type: i32, Value: 1 ~~~ * 整數的二元運算 | 四則運算 | 解釋 | | -------- | -------- | | i32.add | 兩數相加 | | i32.sub | 兩數相減 | | i32.mul | 兩數相乘 | | i32.div_s| 兩數當作有號整數相除,取商數,捨去小數部份| | i32.div_u| 兩數當作無號整數相除,取商數,捨去小數部份| | i32.rem_s| 兩數當作有號整數相除,取餘數| | i32.rem_u| 兩數當作無號整數相除,取餘數| * 測試 * 因為對於在 stack 運算中使用比較直觀會將放入的順序反過來,所以我選擇除和減來做測試,會發現雖然放入 stack 的順序是: * A -> B -> sub 但所做的事情卻是 A - B * A -> B -> div_u 但所做的事情卻是 A / B * 文字格式: ( `;;註解` 註解的格式) ~~~ (module (func $main i32.const 10 ;; A i32.const 5 ;; B i32.div_u ;; A/B unreachable i32.const 11 ;; A i32.const 16 ;; B i32.sub ;; A-B unreachable ) (start $main) ) ~~~ * 結果 ~~~ Values in the stack: Type: i32, Value: 2 Values in the stack: Type: i32, Value: -5 ~~~ * 另外在邏輯運算的部份: `先放入的統稱 A ,後放的統稱 B` | 邏輯運算 | 解釋 | | -------- | -------- | | i32.or | 位元和位元之間個別做 or 的運算| | i32.and | 位元和位元之間個別做 and 的運算| | i32.xor | 位元和位元之間個別做 xor 的運算| | i32.shl | A 的位元向左移 B 位,並在右邊補 0| | i32.shr_u| A 的位元向右移 B 位,並在左邊補 0| | i32.shr_s| A 的位元向右移 B 位,如果 a 數的 sign 是 0 的話補 0,1 的話補 1| |i32.rotl| A 的位元向左移 B 位,然後把超出去的位元補回右邊| |i32.rotr| A 的位元向右移 B 位,然後把超出去的位元補回左邊| * 測試: * 文字格式: ( `;;註解` 註解的格式) ~~~ (module (func $main i32.const 10 ;;0000 0000 0000 0000 0000 0000 0000 1010 i32.const 5 ;;0000 0000 0000 0000 0000 0000 0000 0101 i32.or unreachable i32.const 10 ;;0000 0000 0000 0000 0000 0000 0000 1010 i32.const 5 ;;0000 0000 0000 0000 0000 0000 0000 0101 i32.shl unreachable i32.const 10 ;;0000 0000 0000 0000 0000 0000 0000 1010 i32.const 5 ;;0000 0000 0000 0000 0000 0000 0000 0101 i32.rotr unreachable ) (start $main) ) ~~~ * 結果: * 左移 5 個相當於 $10\times2^5=320$ * 右移 5 個補回左邊相當於 $10\times2^{(32-5)}=1342177280$ ~~~ tina@tina-X550VB:~/Hw/WasmVM/build$ ./WasmVM ~/Hw/wabt/build/ttest Values in the stack: Type: i32, Value: 15 Values in the stack: Type: i32, Value: 320 Values in the stack: Type: i32, Value: 1342177280 ~~~ ### 區塊 (Block) 的概念 * block,通常如下圖,是一種空間延伸的概念,也可以在 block 加上一個開頭是 $ 的名稱,像這樣 `block $aaa` ,方便之後的 br 指令操作,而後面提到 `br 1` 和 `br 0` 的意思是: * `br 0`: 跳到 0+1 層外 (if...else外的 i32.const3) * `br 1`: 跳到 1+1 層外(loop 外的 i32.const5) * `drop`: 從堆疊中拿出一個數值,然後捨棄不用 ![](https://i.imgur.com/SujTxLH.png) * `if...else...end` 的一定要判斷式像下圖, 上方只是 `block` 的示意 ~~~ if (result i32) i32.const 2 else i32.const 3 end ~~~ * drop 會捨棄的是 stack 最上面的值: ~~~ (module (func $main block $aaa i32.const 8 i32.const 5 drop unreachable end ) (start $main) ) ~~~ * 結果: ~~~ tina@tina-X550VB:~/Hw/WasmVM/build$ ./WasmVM ~/Hw/wabt/build/ttest Values in the stack: Type: i32, Value: 8 ~~~ ### 變數 * 每一個區域變數,都會有一個編號從 0,1,2,3... ,宣告像:(local i32) 就是 0 * 每一個全域變數,都會有一個編號從 0,1,2,3...,宣告像: (global i32 i32.const 5) * 一般宣告都是不可變動的,如果加上 `mut` 例如: (global (mut i32) i32.const 5) ,就代表之後 `set_global` 指令可以使用。 * 以下操作方式,在後面加上數字,表示要操作第幾個數 * `get_global`: 將全域變數的值放進堆疊裡 * `set_global`: 從堆疊裡取出一個數值,放進全域變數 * `get_local`: 將區域變數的值放進堆疊裡 * `set_local`: 從堆疊裡取出一個數值,放進區域變數 * `tee_local`: 從堆疊裡取出一個數值,放進區域變數,不過數值會再放回堆疊裡 * 於是我們來跑跑看以下兩者差別 第一版: ~~~ (module (func (local i32) i32.const 7 i32.const 6 set_local 0 get_local 0 unreachable ) ) ~~~ 第二版: ~~~ (module (func (local i32) i32.const 7 i32.const 6 tee_local 0 get_local 0 unreachable ) ) ~~~ 兩者比較的結果: 第一版: ~~~ tina@tina-X550VB:~/Hw/WasmVM/build$ ./WasmVM ~/Hw/wabt/build/ttest Values in the stack: Type: i32, Value: 6 Type: i32, Value: 7 ~~~ 第二版:使用 `tee_local` 多一個6,因為放入區域變數後再放回 stack ~~~ tina@tina-X550VB:~/Hw/WasmVM/build$ ./WasmVM ~/Hw/wabt/build/ttest Values in the stack: Type: i32, Value: 6 Type: i32, Value: 6 Type: i32, Value: 7 ~~~ ### 記憶體 #### 記憶體簡介 Webassembly 的記憶體是以 byte 為單位的連續空間,初始值從0開始, 擴充時以64x1024 bytes 為單位進行, 此一單位稱為 page Webassembly 在使用記憶體時須宣告, 如 Webassembly.memory(), 其本身也是 JavaScript 的物件, Webassembly 和 JavaScript 可以直接使用 memory 互相傳值. 經宣告後一模組至多只有一個記憶體, 如下說明: ~~~ (module (memory 1 5) ) ~~~ 在此模組中, 第一個數字代表記憶體之最小值, 同時表示一開始的大小. 第二個數字為最大值, 可自行決定是否設定. 若無, 則此塊記憶體成長無上限, 預增加其記憶體可使用 grow 方法. #### JS 與 Webassembly 相互傳值 Webassembly 宣告的記憶體與 JS 相互傳值, 由 JS 引擎建立一個 ArrayBuffer, 其記憶體大小透過使用者設定. 其記憶體做了兩件事, 一件是做 Webassembly 的記憶體, 另一是做 JavaScript 的物件. 因為如此, JavaScript 也可以獲取這個記憶體中的字元, 這樣的方式使得 Webassembly 和 JavaScript 可以共享記憶體來相互傳值. 兩者之間由陣列的索引來讀取記憶體, 而不是記憶體位址, 如下一簡單圖示說明: * 首先 Webassembly 想要將一個字串如 Hello 寫入至記憶體, 需要將字串轉換成字元碼並且存入記憶體陣列中 ![](https://i.imgur.com/iJC0VST.png) ![](https://i.imgur.com/Q4DrTLB.png) * 將字串存在記憶體位置中的第一個位置, 也就是所對應到記憶體陣列的某一個索引傳給 JavaScript. 藉此, JavaScript 便可以在 ArrayBuffer 中依索引讀取字串 * 以上方式可以在 JS 文件中加入一些函數來協助, 例如 [Emscription](https://github.com/kripken/emscripten) #### 記憶體進行寫入與讀取時更安全 因為 Webassembly 為一 JavaScript 物件, 具備了記憶體隔離, 防止瀏覽器中記憶體洩漏 以下將說明記憶體隔離與瀏覽器記憶體洩漏 * 記憶體洩漏 當自行管理記憶體有可能忘記進行清除, 導致系統記憶體不足. 若是一 Webassembly 模組的實體直接存入記憶體卻超出容量限制, 此時瀏覽器可能發生記憶體洩漏情形.但是, 因為 Webassembly.memory() 本身為 JavaScript 物件, 受到垃圾回收器追蹤. 若是移除 Webassembly 模組的實體時, 其所有記憶體也會被回收. * 記憶體隔離 前面提到的 ArrayBuffer 會設定好記憶體陣列的範圍, 這意味著 Webassembly 模組的實體可直接管理的記憶體是受限, 只能管理受限內的記憶體而無法知道任何超出此範圍的記憶體內容. 具體來說, 每當 Webassembly 中有操作記憶體時, JS 引擎會進行記憶體陣列範圍檢查. 如果超出範圍, 引擎會拋出異常訊號來保護記憶體中的其他部份. 以下圖示為說明: ![](https://i.imgur.com/pFCCvWP.png) ![](https://imgur.com/VVxQqvP.png) ### 函式表 #### 函式表簡介 函式表為放有函式位置的表,可依據函式表上記錄的位置來間接呼叫函式. 此概念若在C語言中稱為函式指針. ![](https://imgur.com/Qu7AvEO.png) 在 web 頁面中, 所以的方法都為 JavaScript 物件並且將它們儲放於 Webassembly 記憶體之外的記憶體地址中. 下列圖示中紅色圓圈即為代表說明. ![](https://imgur.com/ryBKjG1.png) 若想要一個變數來指向函式表中的函式, 便需要把其地址放入 Webassembly 的記憶體中 ![](https://imgur.com/QBd5Lv9.png) #### 確保網頁安全性 ### 模組 模組是 WebAssembly 程式發佈、載入和執行的基本單位. 當使用 JavaScript 啟動 Webassembly 模組, 會建立出一個模組實體(instance), 之後 JavaScript 程式碼可以呼叫該 Webassembly 模組上的實體函式. Webassembly 模組的實體函式, Webassembly.instantiate, 受到呼叫時是先把從 wasm 抓到的二進位程式碼擷取到緩衝區內, 再進而提供給 Webassembly.instantiate, JS 引擎會把模組程式碼編譯為適合負責執行的機器碼格式. ![](https://imgur.com/dAMG7Ps.png) 除此之外, 產生出模組的實體還需要匯入(import)物件 ![](https://i.imgur.com/cYWZRIG.png) 每個模組需要特定的 imports 才能運作, import 有以下四種之一: * 記憶體(Memories) * 全域變數(Global variable), 目前Webassembly 支持 integer 和 float * 函式(Functions), 可送入 JavaScript 函式提供 Webassembly 呼叫 * 函式表(Tables), 藉由此表可使用到函式指標 ### WasmVM 虛擬機架構 ![](https://i.imgur.com/rUXiE4a.png) #### 部件 * 載入器 (Loader) * 讀取檔案,將內容轉換成物件後載入儲存空間和模組 * 載入完之後就會被釋出,不會繼續留在程式裡 * 這邊必須和[位元格式](https://luishsu.gitbooks.io/wasmvm-webassembly/content/binary-format.html)一起看,我們會去處理前文 (Preamble) * 魔術數字 (Magic number) 4 bytes - 0x00 0x61 0x73 0x6d (表達的是 `\0asm`) * 版本 (Version) 4 bytes * 測試: 只輸入`(module)` 搭配[ wabt 工具](https://github.com/webassembly/wabt),就可以觀察到 `Magic number` 和 `Version` 的數值了,用法如下,參考[Converting WebAssembly text format to wasm](https://developer.mozilla.org/en-US/docs/WebAssembly/Text_format_to_wasm): ~~~ tina@tina-X550VB:~/Hw/wabt/build$ ./wat2wasm Hw/wabt/build/webasm.wast -v 0000000: 0061 736d ; WASM_BINARY_MAGIC 0000004: 0100 0000 ; WASM_BINARY_VERSION ~~~ * 再來處理部份標頭 (Section header),和各個 section | 英文名稱 | 代 碼 | | -------- | -------- | | Type | 1 | | Import | 2 | | Function | 3 | | Table | 4 | | Memory | 5 | | Global | 6 | | Export | 7 | | Start | 8 | | Element | 9 | | Code | 10 | | Data | 11 | * 舉例來說: ``` (module (func $main i64.const 1 unreachable ) (start $main) ) ``` * 這邊會發現,沒有的部份 (section) 就不會出現在下面,而 FIXUP section size 最後會取代 section size (guess) 的位置編入 ,每個部份的相對 Bytecode 可以參照 [Binary Encoding](http://webassembly.org/docs/binary-encoding/#high-level-structure) ~~~ 0000000: 0061 736d ; WASM_BINARY_MAGIC 0000004: 0100 0000 ; WASM_BINARY_VERSION ; section "Type" (1) 0000008: 01 ; section code 0000009: 00 ; section size (guess) 000000a: 01 ; num types ; type 0 000000b: 60 ; func 000000c: 00 ; num params 000000d: 00 ; num results 0000009: 04 ; FIXUP section size ; section "Function" (3) 000000e: 03 ; section code 000000f: 00 ; section size (guess) 0000010: 01 ; num functions 0000011: 00 ; function 0 signature index 000000f: 02 ; FIXUP section size ; section "Start" (8) 0000012: 08 ; section code 0000013: 00 ; section size (guess) 0000014: 00 ; start func index 0000013: 01 ; FIXUP section size ; section "Code" (10) 0000015: 0a ; section code 0000016: 00 ; section size (guess) 0000017: 01 ; num functions ; function body 0 0000018: 00 ; func body size (guess) 0000019: 00 ; local decl count 000001a: 42 ; i64.const 000001b: 01 ; i64 literal 000001c: 00 ; unreachable 000001d: 0b ; end 0000018: 05 ; FIXUP func body size 0000016: 07 ; FIXUP section size ~~~ * 讀取檔案,將內容轉換成模組物件(moduleInst) * 核心 (Core) ``` c= void Core::run(ModuleInst* moduleInst) { // Invoke start function Instruction::invoke(*(moduleInst->start), store, coreStack, moduleInst); // Run while (coreStack.curLabel != nullptr) { Decoder::decode(store, coreStack); } } ``` * 執行程式的常駐Deamon * 將指令一行一行傳給Decoder * 解碼器 (Decoder) ```c= switch (bincode) { case OP_Ctrl_unreachable: Instruction::ctrl_unreachable(store, coreStack); break; case OP_Ctrl_nop: break; ... } break; ``` * 從儲存空間取得下一個指令,按照位元格式解碼後執行指令 * 系統呼叫 (System call) * 系統呼叫不在 WebAssembly 規格內,是 WasmVM 特有的功能,可以讓 WebAssembly 直接對作業系統輸出/輸入等等的操作 * 在這裡我們以`tiny-syscall`這個分支,只有支援 3 個 system call: ~~~c= static void sys_stdin(Store& store, Stack& coreStack); static void sys_stdout(Store& store, Stack& coreStack); static void sys_stderr(Store& store, Stack& coreStack); ~~~ * 系統呼叫的機制是先在堆疊中放入指定的參數,再透過 unreachable 指令調用系統呼叫 ### Dhrystone benchmack ###### 參考 [Dhrystone](https://en.wikipedia.org/wiki/Dhrystone),我看的是 [這篇github](https://github.com/Keith-S-Thompson/dhrystone) 的 v2.0 * 虛擬機需要 Dhrystone benchmack 是因為,我們需要一個測試標竿,去計算在虛擬機中的速度,當作一個基準 * 他有 8 個 Proc 函式,每個程式執行順序有先後關係,第一個是 `Proc_5()` , 想利用 webassembly 的 function 形式, 做出虛擬機測試檔 ~~~c= Proc_5 () /* without parameters */ /*******/ /* executed once */ { Ch_1_Glob = 'A'; Bool_Glob = false; } /* Proc_5 */ ~~~ * 因為是全域變數,所以用 `mut` ~~~ (module (global (mut i32) i32.const 0) (global (mut i32) i32.const 0) (func $proc5 i32.const 65 i32.const 0 set_global 0 set_global 1 unreachable ) (fun $proc0 call $proc5 ) (start $proc0) ) ~~~ * 再來是 `proc_4`: 新增 1 個區域變數 ~~~c= Proc_4 () /* without parameters */ /*******/ /* executed once */ { Boolean Bool_Loc; Bool_Loc = Ch_1_Glob == 'A'; Bool_Glob = Bool_Loc | Bool_Glob; Ch_2_Glob = 'B'; } /* Proc_4 */ ~~~ * 因為有一些 `equ` 和 `or` 的運算,所以要在 `stack` 累積到兩個才可進行運算,而且也要確保 `stack` 裡面的值是正確的 ~~~ (module (global (mut i32) i32.const 0) (global (mut i32) i32.const 0) (global (mut i32) i32.const 0) (func $proc5 i32.const 65 i32.const 0 set_global 0 set_global 1 unreachable ) (func $proc4 (local i32) get_global 1 i32.const 65 i32.eq if(result i32) i32.const 1 else i32.const 0 end set_local 0 unreachable get_local 0 get_global 0 i32.or if(result i32) i32.const 1 else i32.const 0 end set_global 0 unreachable i32.const 66 set_global 2 ) (func $proc0 call $proc5 call $proc4 ) (start $proc0) ) ~~~ ### 參考資料 * [以 WasmVM 向 WebAssembly 說哈囉!](https://www.gitbook.com/book/luishsu/wasmvm-webassembly/details) * [What makes WebAssembly fast?](https://hacks.mozilla.org/2017/02/what-makes-webassembly-fast/) * [WebAssembly table imports… what are they?](https://hacks.mozilla.org/2017/07/webassembly-table-imports-what-are-they/) * [Memory in WebAssembly](https://hacks.mozilla.org/2017/07/memory-in-webassembly-and-why-its-safer-than-you-think/) * [webassembly.org](http://webassembly.org/) * [WebAssembly 和 asm.js 的比較 ](https://zhuanlan.zhihu.com/p/25865972) * [websight demo](https://websightjs.com/) * [asm.js 和 Emscripten入門](http://www.ruanyifeng.com/blog/2017/09/asmjs_emscripten.html) * [測試標竿](https://blog.gtwang.org/web-development/asm-js-emscripten-2/)