iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 2
1
Modern Web

謙虛,踏實的Web Assembly練習系列 第 2

[練習 01] 參考文件、可用工具、Hello add與習作

文件

最重要的當然是Web Assembly的官網:http://webassembly.org/

裡面有比較好閱讀的文件:http://webassembly.org/docs/high-level-goals/

也有比較難閱讀的規格書:https://webassembly.github.io/spec/(可以看到有Web Assembly本身的規格,以及跟Javascript串接會使用到的物件與API)

另外,MDN的文件也是不錯的參考來源:https://developer.mozilla.org/en-US/docs/WebAssembly

MDN的文件底下有MDN做的範例,拿來學習還蠻容易進入狀況:https://github.com/mdn/webassembly-examples/

目前參考到的文件大概就這些。

工具

Web Assembly是從asm.js進一步發展出來的,提到asm.js,不能不用的工具就是:emscripten,不過我的練習主要集中在直接用Web Assembly的文字格式來撰寫程式,所以不太會用到。現在emscripten除了支援asm.js,也支援輸出成Web Assembly。

目前主要會用到的是:wabt(Web Assembly Binary Toolkit),用他可以把文字格式的wat檔編譯成Binary格式的wasm檔,也可以反過來,把Binary格式轉成文字格式。

Hello add

要真的用Web Assembly寫Hello World反而有點複雜,因為只有四種型別,就是32位元以及64位元的整數以及浮點數。先用emscripten做一個來看看...首先寫一個hello.c

#include <stdio.h>
int main(int argc, char ** argv) {
	printf("Hello, world!\n");
}

用emscripten把他編譯成Web Assembly後,看到個檔案:

  • hello.c
  • hello.html
  • hello.js
  • hello.wasm

放到伺服器上打開:
webasm0001.png

嗯...看起來會在Canvs跟Console都輸出:Hello, world...不過呢,編譯出來的hello.wasm檔案有44,481 bytes,再把他用wabt工具轉成hello.wat更有513,509 bytes。這樣很難用來學習呢XD

所以先改成Hello add,其實就是寫一個最簡單的add函數做整數加法。

myadd.wat

(module
	(func (export "add") (param $a i32) (param $b i32) (result i32)
		get_local $a
		get_local $b
		i32.add))

這個Web Assembly會輸出一個函數給Javascript環境使用。

編譯成myadd.wasm之後,放到html跑跑看(我用MDN的範例改了一下,其實上面程式碼跟範例裡面有一個add.wat也差不多。執行也是呼叫範例裡面包裝好WebAssembly API的函數),就是做3+4的簡單整數加法:

myadd.html

<html>
<body>
<script src="../wasm-utils.js"></script>
<script>
fetchAndInstantiate('myadd.wasm')
.then(instance => {
	console.log(instance.exports.add(3,4));
});
</script>
</body>
</html>

跑起來的畫面:
webasm0002.png

bonus:根據文件的說明,簡單看一下Binary格式的結構

之前在看規格書中關於Binary Format的部份,發現很難看懂。不過今天看了一下Binary Encoding文件,竟然就對上了...所以來個bonus...

範例是上面的myadd.wasm,內容用Hex String表示是長這樣:

0061736d0100000001070160027f7f017f030201000707010361646400000a09010700200020016a0b

文件閱讀的順序大約是:

  1. Module structure:第一個uint32是\0asm,這是檔頭的magic number,第二個uint32是版本號01000000,在wasm中數字編碼都是用Little Endian,所以這裡的版本號就是1
  2. 接下來就是由各種Section構成的內容,每個區塊第一個varuint7(簡單說,就是不超過0b01111111的整數),代表區塊的種類。接下來是用varuint32來指示內容長度。varuintX是用LEB128(Little Endian Base128)這種可變長度的編碼方式來表現的數字,可以用比較小的空間來儲存資料。varuint32最小佔用一個位元組,最多可以到四個,可以用來放最多28位元的無號整數。後面的payload,就是這個長度(單位:byte)。
  3. 每種區塊,還有各自的結構,而Code Section,裡面就會有真正要執行的程式碼

上面這個wasm的內容,依照這個規則可以分析出來他的結構大概像這樣:

第一欄是資料的定義,第二欄是資料的內容(Hex String),定義前的indent,代表是前面資料內容的更詳細分析:

magic number			0061736d
version 				01000000

type_section			01
payload_len  			07
payload 				0160027f7f017f

	count				01

	entry[0]:
	func 				60
	param_count			02
	param_types			7f7f

		i32				7f
		i32				7f

	return_count		01
	return_type			7f

		i32				7f

function_section		03
payload_len 			02
payload 				0100

	count 				01
	type 				00

export_section			07
payload_len 			07
payload 				01036164640000

	count 				01

	entry[0]:
	field_length		03
	field_string		616464 ;"add"
	kind				00
	index				00

code_section			0a
payload_len 			09
payload 				010700200020016a0b

    count 				01

	func_body[0]:
	body_size			07
	local_count			00
	code 				200020016a

		i32.getlocal	20
		param[0]		00
		i32.getlocal 	20
        param[1]		01
		i32.add			6a

	func_end 			0b

簡單地說,wasm的結構就是由一個一個section構成的。本來還怕LEB128不好讀,但是因為程式很短,所有的長度都用一個byte就可以表示,所以就不用另外寫程式來算出解碼後的數字,直接靠工人智慧...


之後大概不會再碰Binary格式了,都是用文字格式來寫。所以明天開始就先來了解一下文字格式,順便看看wabt的使用。


上一篇
[練習 00] 為甚麼挑Web Assembly來練習?
下一篇
[練習 02] 編譯工具以及文字格式
系列文
謙虛,踏實的Web Assembly練習20
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中
0
SunAllen
iT邦研究生 1 級 ‧ 2017-12-21 20:59:04

費大耶,開始筆記

0
ddixw66
iT邦新手 5 級 ‧ 2017-12-22 14:31:51

有點看不太懂,不是直接改 hello.c 就好了
為什麼需要去動 wasm 呢

fillano iT邦超人 1 級 ‧ 2017-12-22 15:04:04 檢舉

哈哈,因為只是想寫wasm跟熟悉他,不是要熟悉c。

0
海綿寶寶
iT邦大神 1 級 ‧ 2017-12-25 17:20:31

雖然我看不懂
還是按個讚先
/images/emoticon/emoticon12.gif

我要留言

立即登入留言