当前位置:首页 > 资讯 > 区块链新闻 > 正文

Wasm介绍之5:控制指令

发布:中币网   时间:2020-03-05 15:40:03   加入收藏 打赏

WebAssembly(简称Wasm)控制指令一共有11条,其中unreachable指令(操作码0x00)和nop指令(操作码0x01)比较简单,不介绍。

 

WebAssembly(简称Wasm)控制指令一共有11条,其中unreachable指令(操作码0x00)和nop指令(操作码0x01)比较简单,不介绍。call指令(操作码0x10)已经在上一篇文章里介绍,call_indirect指令(操作码0x11)将在下一篇文章里介绍。本文重点讨论block(操作码0x02)、loop(操作码0x03)、if(操作码0x04)、br(操作码0x0C)、br_if(操作码0x0D)、br_table(操作码0x0E)和return(操作码0x0F)这7条指令。

 

block

block指令相当于一个无参的内联(inline)函数调用。函数的返回值类型,也就是block指令的结果类型(Result Type,在后面的示意图中简称rt)编码后存储在指令的第一个立即数参数里。函数的指令(可能有很多条)编码后存储在第二个立即数参数里。block指令必须以end指令(操作码0x0B)结尾。由于end指令和后面将要介绍的else指令(操作码0x05)只起到标记作用,没有任何执行效果,所以没有把这两条指令计入控制指令。

 

Wasm1.0规范规定block指令的结果不能超过一个,所以rt可以用一个字节表示:0x40表示没有结果、0x7F表示i32类型、0x7E表示i64类型、0x7D表示f32类型、0x7C表示f64类型。根据讨论可知,block指令在执行时不会使用栈上已经存在的任何操作数,执行完毕后可能会在栈顶留下一个操作数,下面是它的示意图:

 

bytecode:...][ block ][ rt ][ instrs ][ end ][...stack:|           |          |           | |           |         ➘|   ?(rt)   | # exec(instrs)|     d     |          |     d     | |     c     |          |     c     | |     b     |          |     b     |  |     a     |          |     a     | └───────────┘          └───────────┘

下面是一个非常简单的WAT例子,展示了block指令的用法:

 

(module  (func (result i32)    ;; ... other instructions    (block (result i32)      (i32.const 100)    )  ))

loop

loop指令和block指令非常相似,区别仅在于如何跳出控制块。后文介绍br指令时会进一步讨论这个区别,下面先给出一个跳转示意图:

 

...][ block ][ rt ]...[ br ]...[ end ][...                         |        ↑                         └────────┘...][ loop ][ rt ]...[ br ]...[ end ][...       ↑               |       └───────────────┘

if

block指令类似,if指令也类似于一个内联函数。区别主要有两点。第一,if内联函数带一个i32类型的参数。第二,if内联函数带有两份代码(两条分支),中间用else指令隔开。if指令执行时,会先从栈顶弹出这个i32类型的参数,如果参数值不等于0,则执行分支1代码,否则执行分支2代码。下面是if指令的示意图:

 

bytecode:...][ if ][ rt ][ instrs1 ][ else ][ instrs2 ][ end ][...stack:|           |          |           | |   e(i32)  |➚        ➘|   ?(rt)   | # exec(e!=0?instrs1:instrs2)|     d     |          |     d     ||     c     |          |     c     | |     b     |          |     b     |  |     a     |          |     a     | └───────────┘          └───────────┘

也可以把if指令的else分支省略,但是这种情况下if指令不能有任何结果。下面是省略else分支时if指令的示意图:

 

bytecode:...][ if ][ 0x40 ][ instrs1 ][ end ][...stack:|           |          |           | |   e(i32)  |➚         |           | # e==1?exec(instrs1)|     d     |          |     d     ||     c     |          |     c     | |     b     |          |     b     |  |     a     |          |     a     | └───────────┘          └───────────┘

下面这个WAT例子展示了如何用if指令实现max()函数:

 

(module  (func $max (param $a i32) (param $b i32) (result i32)    (i32.gt_s (local.get $a) (local.get $b))    (if (result i32)      (then (local.get $a))      (else (local.get $b))    )  ))

br

br指令(可以理解为break,或者branch)可以进行无条件跳转。和传统汇编语言里的JUMP指令不同,br指令并不能跳转到任意位置,而只能跳出(相对于blockif指令而言是跳出,相对于loop指令而言是重新开始,后面不再强调)其他控制指令产生的控制块。br指令带有一个u32类型(32位无符号整数)的立即数参数,指定跳转的层数:0表示跳出当前循环,1代表跳出2层循环,以此类推。下面是一个WAT例子,展示了嵌套的block,以及br指令的用法:

 

(module  (func (export "main") (result i32)    (i32.const 100) (block (result i32)      (i32.const 200) (block (result i32)          (i32.const 300) (block (result i32)           (i32.const 123) (br n)        ) (i32.add)      ) (i32.add)    ) (i32.add)  ))

假设上面例子中的main()函数已经执行到了br指令这里,下图展示了跳转层数分别为0、1、2、3时操作数栈的变化情况:

 

bytecode:...][ br ][ depth ][...stack:|           |  |           |  |           |  |           |  |           ||           |  |           |  |           |  |           |  |           ||    123    |  |    123    |  |           |  |           |  |           ||    300    |  |    300    |  |    123    |  |           |  |           ||    200    |  |    200    |  |    200    |  |    123    |  |           ||    100    |  |    100    |  |    100    |  |    100    |  |    123    |└───────────┘  └───────────┘  └───────────┘  └───────────┘  └───────────┘ (before br)        n==0           n==1           n==2           n==3    

br_if

br_if指令从栈顶弹出一个i32类型的操作数,如果操作数的值为0,则不跳转,否则执行br逻辑。下面是br_if指令的示意图:

 

bytecode:...][ br_if ][ depth ][...stack:|           |          |           | |   e(i32)  |➚         |           | |     d     |          |     ?     | # e!=0?br(depth)|     c     |          |           | |     b     |          |           |  |     a     |          |           | └───────────┘          └───────────┘

下面是一个稍微复杂一些的WAT例子,展示了如何用loopbr_if指令实现sum()函数:

 

(module  (func $sum (param $from i32) (param $to i32) (result i32)    (local $n i32)    (loop $l      ;; $n += $from      (local.set $n (i32.add (local.get $n) (local.get $from)))      ;; $from++       (local.set $from (i32.add (local.get $from) (i32.const 1)))      ;; if $from <= $to { continue }      (br_if $l (i32.le_s (local.get $from) (local.get $to)))    )    ;; return $n    (local.get $n)  ))

请注意loop指令并不会自动循环,必须和br等跳转指令配合使用。

 

br_table

不管是br还是br_if指令,都只有一个立即数参数,能指定一个跳转深度。br_table指令打破了这种限制,可以带N+1个立即数参数,指定N+1个跳转深度。br_table指令执行时,会从栈顶弹出一个i32类型的操作数n。如果n小于或等于N,则按第n个深度跳转,否则按照最后一个深度跳转。下面是br_table指令的示意图:

 

 

 

bytecode:...][ br_table ][ labels... ][ default ][...stack:|           |          |           | |   n(i32)  |➚         |           ||     d     |          |     ?     | # d|     c     |          |           | |     b     |          |           |  |     a     |          |           |└───────────┘          └───────────┘

return

return指令可以认为是br指令的特殊形式:直接跳出最外层循环(也就是整个函数)。return指令没有立即数参数,不画示意图了。下面的WAT例子展示了如何用blockbr_tablereturn指令实现Go等高级语言中的switch-case语句:

 

(module  (func $select3 (param $n i32) (param i32 i32 i32) (result i32)    (block      (block        (block          (local.get $n)          (br_table 0 1 2)        )        (return (local.get 1))      )      (return (local.get 2))    )    (return (local.get 3))  ))

如果把上面例子中定义的select3()函数翻译成Go语言代码的话,应该是下面这样:

 

func select3(n, a, b, c int32) int32 {  switch n {    case 0 : return a    case 1 : return b    default: return c  }}

*本文由CoinEx Chain开发团队成员Chase撰写。CoinEx Chain是全球首条基于Tendermint共识协议和Cosmos SDK开发的DEX专用公链,借助IBC来实现DEX公链、智能合约链、隐私链三条链合一的方式去解决可扩展性(Scalability)、去中心化(Decentralization)、安全性(security)区块链不可能三角的问题,能够高性能的支持数字资产的交易以及基于智能合约的Defi应用。

 

来源:链向财经




来源:中币网  https://www.zhongbi.net/news/blocknews/238693.html
声明:登载此文仅出于分享区块链知识,并不意味着赞同其观点或证实其描述。文章内容仅供参考,不构成投资建议。投资者据此操作,风险自担。 此文如侵犯到您的合法权益,请联系我们3111859717@qq.com,我们将第一时间处理。