指令

指令 根据描述指令如何操作操作数栈栈类型 [t1][t2]进行分类。

stacktype::=[opdtype][opdtype]opdtype::=valtype | 

这些类型描述了指令弹出的具有操作数类型 t1 的所需输入栈,以及它压入的具有类型 t2 的结果值的输出栈。栈类型类似于函数类型,但它们允许将单个操作数分类为 底部),表示类型不受约束。作为一个辅助概念,如果 t1 或等于 t2,则操作数类型 t1 与另一个操作数类型 t2 匹配。这以逐点的方式扩展到栈类型。

ttt
(tt)[t][t]

注意

例如,指令 i32.add 的类型为 [i32 i32][i32],它消耗两个 i32 值并产生一个值。

类型推导扩展到指令序列 instr。如果执行指令的累积效果是从操作数栈中消耗类型为 t1 的值并压入类型为 t2 的新值,则此序列具有栈类型 [t1][t2]

对于某些指令,类型规则没有完全约束类型,因此允许多种类型。这样的指令称为多态指令。可以区分两种多态性

  • 值多态:一个或多个单个操作数的值类型 t 不受约束。对于所有参数化指令(如 dropselect)都是这种情况。

  • 栈多态:指令的整个(或大部分)栈类型 [t1][t2] 不受约束。对于所有执行无条件控制转移控制指令(例如 unreachablebrbr_tablereturn)都是这种情况。

在这两种情况下,只要满足程序周围部分施加的约束,就可以任意选择不受约束的类型或类型序列。

注意

例如,select 指令对于任何可能的数值类型 t,都可以使用类型 [t t i32][t]。因此,以下两个指令序列

(i32.const 1)  (i32.const 2)  (i32.const 3)  select

(f64.const 1.0)  (f64.const 2.0)  (i32.const 3)  select

都是有效的,其中 select 的类型中的 t 分别被实例化为 i32f64

unreachable 指令对于任何可能的操作数类型 t1t2 序列,都具有类型 [t1][t2]。因此,

unreachable  i32.add

通过为 unreachable 指令假设类型 [][i32 i32] 是有效的。相反,

unreachable  (i64.const 0)  i32.add

是无效的,因为没有可以为 unreachable 指令选择的类型可以使序列类型正确。

附录 描述了一种类型检查算法,该算法有效地实现了此处给定规则规定的指令序列验证。

数值指令

t.const c

  • 该指令对于类型 [][t] 是有效的。

Ct.const c:[][t]

t.unop

  • 该指令对于类型 [t][t] 是有效的。

Ct.unop:[t][t]

t.binop

  • 该指令对于类型 [t t][t] 是有效的。

Ct.binop:[t t][t]

t.testop

  • 该指令对于类型 [t][i32] 是有效的。

Ct.testop:[t][i32]

t.relop

  • 该指令对于类型 [t t][i32] 是有效的。

Ct.relop:[t t][i32]

t2.cvtop_t1_sx?

  • 该指令对于类型 [t1][t2] 是有效的。

Ct2.cvtop_t1_sx?:[t1][t2]

引用指令

ref.null t

  • 该指令对于类型 [][t] 是有效的。

Cref.null t:[][t]

注意

在 WebAssembly 的未来版本中,可能存在不允许空引用的引用类型。

ref.is_null

  • 该指令对于类型 [t][i32] 是有效的,其中 t 为任何引用类型

t=reftypeCref.is_null:[t][i32]

ref.func x

  • 上下文必须定义函数 C.funcs[x]

  • 函数索引 x 必须包含在 C.refs 中。

  • 该指令的类型有效,为 [][funcref]

C.funcs[x]=functypexC.refsCref.func x:[][funcref]

向量指令

向量指令可以带前缀来描述操作数的形状。打包数值类型,i8i16,不是值类型。一个辅助函数将这些打包类型形状映射到值类型。

unpacked(i8x16)=i32unpacked(i16x8)=i32unpacked(txN)=t

以下辅助函数表示向量形状的车道数,即其维度

dim(txN)=N

v128.const c

  • 该指令的类型有效,为 [][v128]

Cv128.const c:[][v128]

v128.vvunop

  • 该指令的类型有效,为 [v128][v128]

Cv128.vvunop:[v128][v128]

v128.vvbinop

  • 该指令的类型有效,为 [v128 v128][v128]

Cv128.vvbinop:[v128 v128][v128]

v128.vvternop

  • 该指令的类型有效,为 [v128 v128 v128][v128]

Cv128.vvternop:[v128 v128 v128][v128]

v128.vvtestop

  • 该指令的类型有效,为 [v128][i32]

Cv128.vvtestop:[v128][i32]

i8x16.swizzle

  • 该指令的类型有效,为 [v128 v128][v128]

Ci8x16.swizzle:[v128 v128][v128]

i8x16.shuffle laneidx16

  • 对于所有 laneidxi,在 laneidx16 中,laneidxi 必须小于 32

  • 该指令的类型有效,为 [v128 v128][v128]

(laneidx<32)16Ci8x16.shuffle laneidx16:[v128 v128][v128]

shape.splat

  • tunpacked(shape)

  • 该指令的类型有效,为 [t][v128]

Cshape.splat:[unpacked(shape)][v128]

shape.extract_lane_sx? laneidx

  • 车道索引 laneidx 必须小于 dim(shape)

  • 该指令的类型有效,为 [v128][unpacked(shape)]

laneidx<dim(shape)Cshape.extract_lane_sx? laneidx:[v128][unpacked(shape)]

shape.replace_lane laneidx

  • 车道索引 laneidx 必须小于 dim(shape)

  • tunpacked(shape)

  • 该指令的类型有效,为 [v128 t][v128]

laneidx<dim(shape)Cshape.replace_lane laneidx:[v128 unpacked(shape)][v128]

shape.vunop

  • 该指令的类型有效,为 [v128][v128]

Cshape.vunop:[v128][v128]

shape.vbinop

  • 该指令的类型有效,为 [v128 v128][v128]

Cshape.vbinop:[v128 v128][v128]

shape.vrelop

  • 该指令的类型有效,为 [v128 v128][v128]

Cshape.vrelop:[v128 v128][v128]

ishape.vishiftop

  • 该指令的类型有效,为 [v128 i32][v128]

Cishape.vishiftop:[v128 i32][v128]

shape.vtestop

  • 该指令的类型有效,为 [v128][i32]

Cshape.vtestop:[v128][i32]

shape.vcvtop_half?_shape_sx?_zero?

  • 该指令的类型有效,为 [v128][v128]

Cshape.vcvtop_half?_shape_sx?_zero?:[v128][v128]

ishape1.narrow_ishape2_sx

  • 该指令的类型有效,为 [v128 v128][v128]

Cishape1.narrow_ishape2_sx:[v128 v128][v128]

ishape.bitmask

  • 该指令的类型有效,为 [v128][i32]

Cishape.bitmask:[v128][i32]

ishape1.dot_ishape2_s

  • 该指令的类型有效,为 [v128 v128][v128]

Cishape1.dot_ishape2_s:[v128 v128][v128]

ishape1.extmul_half_ishape2_sx

  • 该指令的类型有效,为 [v128 v128][v128]

Cishape1.extmul_half_ishape2_sx:[v128 v128][v128]

ishape1.extadd_pairwise_ishape2_sx

  • 该指令的类型有效,为 [v128][v128]

Cishape1.extadd_pairwise_ishape2_sx:[v128][v128]

参数化指令

drop

  • 该指令对于任何操作数类型 t,其类型有效值为 [t][]

Cdrop:[t][]

注意

未带注释的 dropselect 都是值多态指令。

select (t)?

  • 如果存在 t,则

    • t 的长度必须为 1

    • 则该指令类型有效值为 [t t i32][t]

  • 否则

Cselect t:[t t i32][t]tnumtypeCselect:[t t i32][t]tvectypeCselect:[t t i32][t]

注意

在 WebAssembly 的未来版本中,select 可能会允许每个选项有多个值。

变量指令

local.get x

  • 局部变量 C.locals[x] 必须在上下文中定义。

  • t值类型 C.locals[x]

  • 则该指令类型有效值为 [][t]

C.locals[x]=tClocal.get x:[][t]

local.set x

  • 局部变量 C.locals[x] 必须在上下文中定义。

  • t值类型 C.locals[x]

  • 则该指令类型有效值为 [t][]

C.locals[x]=tClocal.set x:[t][]

local.tee x

  • 局部变量 C.locals[x] 必须在上下文中定义。

  • t值类型 C.locals[x]

  • 则该指令类型有效值为 [t][t]

C.locals[x]=tClocal.tee x:[t][t]

global.get x

  • 全局变量 C.globals[x] 必须在上下文中定义。

  • mut t全局类型 C.globals[x]

  • 则该指令类型有效值为 [][t]

C.globals[x]=mut tCglobal.get x:[][t]

global.set x

  • 全局变量 C.globals[x] 必须在上下文中定义。

  • mut t全局类型 C.globals[x]

  • 可变性 mut 必须为 var

  • 则该指令类型有效值为 [t][]

C.globals[x]=var tCglobal.set x:[t][]

表指令

table.get x

  • C.tables[x] 必须在上下文中定义。

  • limits t表类型 C.tables[x]

  • 则该指令类型有效值为 [i32][t]

C.tables[x]=limits tCtable.get x:[i32][t]

table.set x

  • C.tables[x] 必须在上下文中定义。

  • limits t表类型 C.tables[x]

  • 则该指令的类型为 [i32 t][] 时有效。

C.tables[x]=limits tCtable.set x:[i32 t][]

table.size x

  • C.tables[x] 必须在上下文中定义。

  • 则该指令的类型为 [][i32] 时有效。

C.tables[x]=tabletypeCtable.size x:[][i32]

table.grow x

  • C.tables[x] 必须在上下文中定义。

  • limits t表类型 C.tables[x]

  • 则该指令的类型为 [t i32][i32] 时有效。

C.tables[x]=limits tCtable.grow x:[t i32][i32]

table.fill x

  • C.tables[x] 必须在上下文中定义。

  • limits t表类型 C.tables[x]

  • 则该指令的类型为 [i32 t i32][] 时有效。

C.tables[x]=limits tCtable.fill x:[i32 t i32][]

table.copy x y

  • C.tables[x] 必须在上下文中定义。

  • limits1 t1表类型 C.tables[x]

  • C.tables[y] 必须在上下文中定义。

  • limits2 t2表类型 C.tables[y]

  • 引用类型 t1 必须与 t2 相同。

  • 则该指令的类型为 [i32 i32 i32][] 时有效。

C.tables[x]=limits1 tC.tables[y]=limits2 tCtable.copy x y:[i32 i32 i32][]

table.init x y

  • C.tables[x] 必须在上下文中定义。

  • limits t1表类型 C.tables[x]

  • 元素段 C.elems[y] 必须在上下文中定义。

  • t2引用类型 C.elems[y]

  • 引用类型 t1 必须与 t2 相同。

  • 则该指令的类型为 [i32 i32 i32][] 时有效。

C.tables[x]=limits tC.elems[y]=tCtable.init x y:[i32 i32 i32][]

elem.drop x

  • 元素段 C.elems[x] 必须在上下文中定义。

  • 则该指令的类型为 [][] 时有效。

C.elems[x]=tCelem.drop x:[][]

内存指令

t.load memarg

  • 内存 C.mems[0] 必须在上下文中定义。

  • 对齐 2memarg.align 不能大于 t位宽 除以 8

  • 则该指令类型有效值为 [i32][t]

C.mems[0]=memtype2memarg.align|t|/8Ct.load memarg:[i32][t]

t.loadN_sx memarg

  • 内存 C.mems[0] 必须在上下文中定义。

  • 对齐 2memarg.align 不能大于 N/8

  • 则该指令类型有效值为 [i32][t]

C.mems[0]=memtype2memarg.alignN/8Ct.loadN_sx memarg:[i32][t]

t.store memarg

  • 内存 C.mems[0] 必须在上下文中定义。

  • 对齐 2memarg.align 不能大于 t位宽 除以 8

  • 则该指令的类型为 [i32 t][] 时有效。

C.mems[0]=memtype2memarg.align|t|/8Ct.store memarg:[i32 t][]

t.storeN memarg

  • 内存 C.mems[0] 必须在上下文中定义。

  • 对齐 2memarg.align 不能大于 N/8

  • 则该指令的类型为 [i32 t][] 时有效。

C.mems[0]=memtype2memarg.alignN/8Ct.storeN memarg:[i32 t][]

v128.loadNxM_sx memarg

  • 内存 C.mems[0] 必须在上下文中定义。

  • 对齐 2memarg.align 不能大于 N/8M

  • 则该指令的类型为 [i32][v128] 时有效。

C.mems[0]=memtype2memarg.alignN/8MCv128.loadNxM_sx memarg:[i32][v128]

v128.loadN_splat memarg

  • 内存 C.mems[0] 必须在上下文中定义。

  • 对齐 2memarg.align 不能大于 N/8

  • 则该指令的类型为 [i32][v128] 时有效。

C.mems[0]=memtype2memarg.alignN/8Cv128.loadN_splat memarg:[i32][v128]

v128.loadN_zero memarg

  • 内存 C.mems[0] 必须在上下文中定义。

  • 对齐 2memarg.align 不能大于 N/8

  • 则该指令的类型为 [i32][v128] 时有效。

C.mems[0]=memtype2memarg.alignN/8Cv128.loadN_zero memarg:[i32][v128]

v128.loadN_lane memarg laneidx

  • 通道索引 laneidx 必须小于 128/N

  • 内存 C.mems[0] 必须在上下文中定义。

  • 对齐 2memarg.align 不能大于 N/8

  • 然后,该指令的类型有效,为 [i32 v128][v128]

laneidx<128/NC.mems[0]=memtype2memarg.alignN/8Cv128.loadN_lane memarg laneidx:[i32 v128][v128]

v128.storeN_lane memarg laneidx

  • 通道索引 laneidx 必须小于 128/N

  • 内存 C.mems[0] 必须在上下文中定义。

  • 对齐 2memarg.align 不能大于 N/8

  • 然后,该指令的类型有效,为 [i32 v128][v128]

laneidx<128/NC.mems[0]=memtype2memarg.alignN/8Cv128.storeN_lane memarg laneidx:[i32 v128][]

memory.size

  • 内存 C.mems[0] 必须在上下文中定义。

  • 则该指令的类型为 [][i32] 时有效。

C.mems[0]=memtypeCmemory.size:[][i32]

memory.grow

  • 内存 C.mems[0] 必须在上下文中定义。

  • 然后,该指令的类型有效,为 [i32][i32]

C.mems[0]=memtypeCmemory.grow:[i32][i32]

memory.fill

  • 内存 C.mems[0] 必须在上下文中定义。

  • 则该指令的类型为 [i32 i32 i32][] 时有效。

C.mems[0]=memtypeCmemory.fill:[i32 i32 i32][]

memory.copy

  • 内存 C.mems[0] 必须在上下文中定义。

  • 则该指令的类型为 [i32 i32 i32][] 时有效。

C.mems[0]=memtypeCmemory.copy:[i32 i32 i32][]

memory.init x

  • 内存 C.mems[0] 必须在上下文中定义。

  • 数据段 C.datas[x] 必须在上下文中定义。

  • 则该指令的类型为 [i32 i32 i32][] 时有效。

C.mems[0]=memtypeC.datas[x]=okCmemory.init x:[i32 i32 i32][]

data.drop x

  • 数据段 C.datas[x] 必须在上下文中定义。

  • 则该指令的类型为 [][] 时有效。

C.datas[x]=okCdata.drop x:[][]

控制指令

nop

  • 该指令的类型有效,为 [][]

Cnop:[][]

unreachable

  • 该指令的类型有效,为 [t1][t2],对于任何操作数类型序列 operand types t1t2

Cunreachable:[t1][t2]

注意

unreachable 指令是 stack-polymorphic 的。

block blocktype instr end

  • 块类型必须作为某个函数类型 function type [t1][t2] 有效

  • CC 为相同的上下文 context,但将结果类型 result type [t2] 添加到 labels 向量的前端。

  • 在上下文 C 下,指令序列 instr 必须 有效,其类型为 [t1][t2]

  • 然后,复合指令的类型有效,为 [t1][t2]

Cblocktype:[t1][t2]C,labels[t2]instr:[t1][t2]Cblock blocktype instr end:[t1][t2]

注意

符号 C,labels[t] 将新的标签类型插入索引 0 处,并将所有其他标签类型向后移动。notation

loop blocktype instr end

  • 块类型必须作为某个函数类型 function type [t1][t2] 有效

  • CC 为相同的上下文 context,但将结果类型 result type [t1] 添加到 labels 向量的前端。

  • 在上下文 C 下,指令序列 instr 必须 有效,其类型为 [t1][t2]

  • 然后,复合指令的类型有效,为 [t1][t2]

Cblocktype:[t1][t2]C,labels[t1]instr:[t1][t2]Cloop blocktype instr end:[t1][t2]

注意

符号 C,labels[t] 将新的标签类型插入索引 0 处,并将所有其他标签类型向后移动。notation

if blocktype instr1 else instr2 end

  • 块类型必须作为某个函数类型 function type [t1][t2] 有效

  • CC 为相同的上下文 context,但将结果类型 result type [t2] 添加到 labels 向量的前端。

  • 在上下文 C 下,指令序列 instr1 必须 有效,其类型为 [t1][t2]

  • 在上下文 C 下,指令序列 instr2 必须 有效,其类型为 [t1][t2]

  • 然后,复合指令的类型有效,为 [t1 i32][t2]

Cblocktype:[t1][t2]C,labels[t2]instr1:[t1][t2]C,labels[t2]instr2:[t1][t2]Cif blocktype instr1 else instr2 end:[t1 i32][t2]

注意

符号 C,labels[t] 将新的标签类型插入索引 0 处,并将所有其他标签类型向后移动。notation

br l

  • 标签 C.labels[l] 必须在上下文中定义。

  • [t]结果类型 C.labels[l]

  • 则该指令对于任何操作数类型序列 t1t2,其类型有效,为 [t1 t][t2]

C.labels[l]=[t]Cbr l:[t1 t][t2]

注意

上下文 C 中的 标签索引 空间将最新的标签放在最前面,以便 C.labels[l] 按预期执行相对查找。

br 指令是 栈多态的

br_if l

  • 标签 C.labels[l] 必须在上下文中定义。

  • [t]结果类型 C.labels[l]

  • 则该指令类型有效,为 [t i32][t]

C.labels[l]=[t]Cbr_if l:[t i32][t]

注意

上下文 C 中的 标签索引 空间将最新的标签放在最前面,以便 C.labels[l] 按预期执行相对查找。

br_table l lN

  • 标签 C.labels[lN] 必须在上下文中定义。

  • 对于 l 中的每个标签 li,标签 C.labels[li] 必须在上下文中定义。

  • 必须存在一个 操作数类型 序列 t,使得

    • 序列 t 的长度与序列 C.labels[lN] 的长度相同。

    • 对于 t 中的每个 操作数类型 tjC.labels[lN] 中对应的类型 tNjtj 匹配 tNj

    • 对于 l 中的每个标签 li

      • 序列 t 的长度与序列 C.labels[li] 的长度相同。

      • 对于 t 中的每个 操作数类型 tjC.labels[li] 中对应的类型 tijtj 匹配 tij

  • 则该指令对于任何操作数类型序列 t1t2,其类型有效,为 [t1 t i32][t2]

([t]C.labels[l])[t]C.labels[lN]Cbr_table l lN:[t1 t i32][t2]

注意

上下文 C 中的 标签索引 空间将最新的标签放在最前面,以便 C.labels[li] 按预期执行相对查找。

br_table 指令是 栈多态的

return

  • 返回类型 C.return 在上下文中不能缺失。

  • [t]C.return结果类型

  • 则该指令对于任何操作数类型序列 t1t2,其类型有效,为 [t1 t][t2]

C.return=[t]Creturn:[t1 t][t2]

注意

return 指令是 栈多态的

在验证不是函数体的 表达式 时,C.return 缺失(设置为 ϵ)。这与将其设置为空结果类型 ([ϵ]) 不同,后者是对于不返回任何内容的函数的情况。

call x

  • 上下文必须定义函数 C.funcs[x]

  • 则该指令类型有效,为 C.funcs[x]

C.funcs[x]=[t1][t2]Ccall x:[t1][t2]

call_indirect x y

  • C.tables[x] 必须在上下文中定义。

  • limits t表类型 C.tables[x]

  • 引用类型 t 必须为 funcref

  • 类型 C.types[y] 必须在上下文中定义。

  • [t1][t2]函数类型 C.types[y]

  • 则该指令类型有效,为 [t1 i32][t2]

C.tables[x]=limits funcrefC.types[y]=[t1][t2]Ccall_indirect x y:[t1 i32][t2]

指令序列

指令序列的类型化是递归定义的。

空指令序列:ϵ

  • 空指令序列对于任何 操作数类型 序列 t,其类型有效,为 [t][t]

Cϵ:[t][t]

非空指令序列:instr instrN

  • 指令序列 instr 必须对于某些 操作数类型 序列 t1t2,其类型有效,为 [t1][t2]

  • 指令 instrN 必须对于某些 操作数类型 序列 tt3,其类型有效,为 [t][t3]

  • 必须存在一个 操作数类型 序列 t0,使得 t2=t0 t,其中类型序列 tt 一样长。

  • 对于 t 中的每个 操作数类型 tit 中对应的类型 titi 匹配 ti

  • 则组合后的指令序列类型有效,为 [t1][t0 t3]

Cinstr:[t1][t0 t][t][t]CinstrN:[t][t3]Cinstr instrN:[t1][t0 t3]

表达式

表达式 expr结果类型 分类,结果类型的形式为 [t]

instr end

Cinstr:[][t][t][t]Cinstr end:[t]

常量表达式

  • 常量表达式 instr end 中,instr 中的所有指令都必须是常量。

  • 常量指令 instr 必须是

    • 形式为 t.const c

    • 或形式为 ref.null

    • 或形式为 ref.func x

    • 或形式为 global.get x,在这种情况下,C.globals[x] 必须是形式为 const t全局类型

(Cinstrconst)Cinstr endconst
Ct.const cconstCref.null tconstCref.func xconst
C.globals[x]=const tCglobal.get xconst

注意

目前,出现在 全局变量元素数据 段中的常量表达式受到进一步的约束,其中包含的 global.get 指令仅允许引用导入的全局变量。这在 模块验证规则 中通过相应地约束上下文 C 来执行。

常量表达式的定义可能会在 WebAssembly 的未来版本中扩展。