22

Wasm 介绍(三):内存

 4 years ago
source link: https://mp.weixin.qq.com/s/Wup2Jl_tMw05ch7xR7NJog
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

mQZZraB.jpg!web

上一篇文章介绍了WebAssembly(简称Wasm)指令集以及指令的操作码(Opcode)、立即数(Immediate Arguments)、操作数(Operands)、操作数栈(Operand Stack,简称栈)等概念,并且详细介绍了参数指令和数值指令。 这篇文章将介绍Wasm内存和相关指令。

内存

每个Wasm模块都可以定义或者导入一个内存,内存大小以页为单位,每一页是64K。 定义内存时,需要指定内存的页数下限。 页数上限可选,可以指定也可以不指定。 内存的初始数据则可以在数据段中指定。 下面是一个WAT例子,展示了内存和数据段的定义:

(module

(memory 1 8) ;; { min: 1, max: 8 }

(data 0 (offset (i32.const 100)) "hello")

;; ...

)

和内存相关的指令共有25条,下面分别介绍。

memory.size

memory.size 指令(操作码 0x3F )把内存的当前页数按 i32 类型推入栈顶。 memory.size 指令带有一个1字节立即数,可以指定操作的是哪个内存。由于Wasm1.0规范规定最多只能有一个内存,所以目前这个立即数只能是0。下面是 memory.size 指令的示意图:

bytecode:

...][ memory.size ][ 0 ][...


stack:

| | | |

| | ➘| p(i32) | # page count

| d | | d |

| c | | c |

| b | | b |

| a | | a |

└───────────┘ └───────────┘

memory.grow

memory.grow 指令(操作码 0x40 )将内存增长 n 页,其中 n 是一个 i32 类型的整数,从栈顶弹出。如果操作成功,将 增长前 的页数按 i32 类型推入栈顶,否则将 -1 推入栈顶。和 memory.size 指令一样, memory.grow 指令也带有一个1字节立即数,且取值必须为0。下面是 memory.grow 指令的示意图:

bytecode:

...][ memory.grow ][ 0 ][...


stack:

| | | |

| n(i32) |➚ ➘| p(i32) | # grow n pages

| d | | d |

| c | | c |

| b | | b |

| a | | a |

└───────────┘ └───────────┘

load

load 指令从内存读取数据,然后推入栈顶。具体读取多少字节的数据,以及将数据解释为何种类型的数,因指令而异。Wasm采用了“立即数+操作数”的内存寻址方式,所有 load 指令都带有两个 u32 类型(LEB28编码的32位无符号整数)的立即数,一个表示对齐方式,另一个表示内存偏移量。 load 指令还需要从栈顶弹出一个 i32 类型的操作数,立即数和操作数相加即可得到实际要读取的内存起始地址。对齐方式仅起提示作用,不影响实际操作,本文不做介绍,具体请参考Wasm规范。以 i64.load 指令(操作码 0x29 )为例,下面是它的示意图:

bytecode:

...][ i64.load ][ align ][ offset ][...


stack:

| | | |

| | | |

| d(i32) |➚ ➘|m[offset+d]| # i64

| c | | c |

| b | | b |

| a | | a |

└───────────┘ └───────────┘

load 指令一共有14条,为了统一说明这些指令,我们假设指令执行时计算出的内存地址是 a ,此处存放的数据是 0xABCDEF1234567890 。由于Wasm使用小端在前的方式存放数据,因此内存数据看起来是下面这样:

mem:

...[ 0x90 ][ 0x78 ][ 0x56 ][ 0x34 ][ 0x12 ][ 0xEF ][ 0xCD ][ 0xAB ]...

下表给出这14条 load 指令的操作码、实际读取到的字节,以及如何解释这些字节:

am2MRji.jpg!web

store

store 指令从栈顶弹出操作数,然后写入内存。具体如何解释操作数,以及写入多少字节,因指令而异。所有的 store 指令也都带有两个立即数,含义和  load 指令一样。和 load 指令不同的是, store 指令要从栈顶弹出两个操作数,一个用于计算内存地址,另一个是要写入的数据。以 i64.store 指令(操作码 0x37 )为例,下面是它的示意图:

bytecode:

...][ i64.store ][ align ][ offset ][...


stack:

| | | |

| e(i64) |➚ | |

| d(i32) |➚ | | # m[offset+d]=e

| c | | c |

| b | | b |

| a | | a |

└───────────┘ └───────────┘

store 指令一共有9条,为了统一说明这些指令,我们也假设指令执行时计算出的内存地址是 a 。下表给出这9条指令的操作码、栈顶操作数以及实际执行效果(Go伪代码, mem 表示内存, LE 表示小端编码后的字节数组):

zmUbMrb.jpg!web

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


Recommend

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK