iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 11
0
Modern Web

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

[練習 10] 對於區塊理解的錯誤以及br_table的寫法

  • 分享至 

  • xImage
  •  

原來區塊可以返回值

昨天測試中,以為區塊是使用新的堆疊,所以無法返回值。不過後來發現不是這麼回事Orz

區塊跟函數一樣,都可以指定返回值,只是預設不返回,所以需要返回的時候,只要宣告就可以啦。

原本的Fibonacci函數長這樣:

(module
	(func $fibn (param $cur i32) (param $nex i32) (param $n i32) (result i32)
		(local $ret i32)
		get_local $n
		i32.const 0
		i32.eq				;;param for if

		if
			get_local $cur
			set_local $ret
		else
			get_local $nex	;;1st param for $fibn
			
			get_local $cur
			get_local $nex
			i32.add			;;2nd param for $fibn

			get_local $n
			i32.const 1
			i32.sub			;;3rd param for fibn

			call $fibn
			set_local $ret
		end

		get_local $ret
	)
	(func (export "fib") (param $n i32) (result i32)
		i32.const 0
		i32.const 1
		get_local $n
		call $fibn
	)
)

set_local $ret以及get_local $ret這兩個動作看起來就很多餘。

只要正確的宣告if,其實不需要用到$ret區域變數:

(module
	(func $fibn (param $cur i32) (param $nex i32) (param $n i32) (result i32)
		get_local $n
		i32.const 0
		i32.eq				;;param for if

		if (result i32)
			get_local $cur
		else
			get_local $nex	;;1st param for $fibn
			
			get_local $cur
			get_local $nex
			i32.add			;;2nd param for $fibn

			get_local $n
			i32.const 1
			i32.sub			;;3rd param for fibn

			call $fibn
		end
	)
	(func (export "fib") (param $n i32) (result i32)
		i32.const 0
		i32.const 1
		get_local $n
		call $fibn
	)
)

以上。

修正昨天的錯誤後,來看分支以及其他流程控制用的指令。

分支:br、br_if、br_table

br的用法

之前也講過br很簡單,就類似goto,只是他後面要跟一個label變數或索引值。不過在Web Assembly裡面,只有區塊可以加上label...所以會參考到label的程式碼,一定是位於區塊內。

br的寫法像這樣:

...
(block $label
    ...
    br $label
)

br_if的用法

其實跟br差不多,只是要先做一個比較運算,根據結果來決定要不要分支。寫起來就像這樣:

...
(block $label
    ...
    get_local $n
    i32.const 0
    i32.eq
    br_if $label

br_table的用法

在比較複雜的區塊結構中,使用br_table可以用一個判斷來決定要分支到哪個列出的label。先來看一下範例:

(module
	(func (export "test") (param $n i32) (result i32)
		(block $a
			(block $b
				(block $c
					(block $d
						(block $e
							(br_table $b $a $c $d $e (get_local $n))
							(return (i32.const 99))
						)
						(return (i32.const 100))
					)
					(return (i32.const 101))
				)
				(return (i32.const 102))
			)
			(return (i32.const 103))
		)
	    (i32.const 104)
	)
)

這個test函數,有一個參數$n,可以用他來在br_table做判斷。在br_table中給定的label列表是:

  1. $a:跳到label $a,就會離開$a標示的這個block,然後函數返回104
  2. $b:跳到label $b,就會離開$b標示的這個block,然後函數返回103
  3. $c:同前,返回102
  4. $d:同前,返回101
  5. $e:同前,返回100

不過在上面的程式中,刻意把列表中的$a跟$b互換,這樣$b就變成索引為0的label,而$a是索引為1的...

然後來測試一下:

<html>
<body>
	<script src="../wasm_util.js"></script>
	<script>
		new Wasm('test012.wasm')
		.getInstance()
		.then(instance => {
			console.log(instance.exports.test(0));
			console.log(instance.exports.test(1));
			console.log(instance.exports.test(2));
			console.log(instance.exports.test(3));
			console.log(instance.exports.test(4));
			console.log(instance.exports.test(5));
		})
	</script>
</body>
</html>

分別傳給函數0~5,看看他返回哪些東西:

Imgur

結果分別是:

  1. 103
  2. 104
  3. 102
  4. 101
  5. 100
  6. 100

分別對應到$b, $a, $c, $d, $e, $e,看起來只要運算式的結果不在索引範圍,就會使用列表中最後一個label。(其實我試過,傳-1,結果也是100)

其他流程控制指令

還有幾個指令跟流程控制相關的:

  1. nop:就...不做事。
  2. return:離開函數,如果有給他返回值,則返回(需要函數有定義,不然編譯就過不去)
  3. end:如果是用線性指令的方式寫程式,函數跟區塊就需要有相應的end。如果是使用S-Expression語法的話,只要括弧可以對上就好

沒了。其他幾個跟流程有相關的是call以及call_indirect,前者呼叫在程式中定義或是輸入的函數,後者用來呼叫透過Table定義的函數。


明天不知道練習什麼比較好XD,先來做一些應用看看好了


上一篇
[練習 09] 流程控制指令
下一篇
[練習 11] 嘗試應用看看...影像轉灰階
系列文
謙虛,踏實的Web Assembly練習20
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言