Instructions

WebAssembly computation is performed by executing individual instructions.

Numeric Instructions

Numeric instructions are defined in terms of the generic numeric operators. The mapping of numeric instructions to their underlying operators is expressed by the following definition:

opiN(i1,,ik)=iopN(i1,,ik)opfN(z1,,zk)=fopN(z1,,zk)opvN(i1,,ik)=iopN(i1,,ik)

And for conversion operators:

cvtopt1,t2sx?(c)=cvtop|t1|,|t2|sx?(c)

Where the underlying operators are partial, the corresponding instruction will trap when the result is not defined. Where the underlying operators are non-deterministic, because they may return one of multiple possible NaN values, so are the corresponding instructions.

Note

For example, the result of instruction i32.add applied to operands i1,i2 invokes addi32(i1,i2), which maps to the generic iadd32(i1,i2) via the above definition. Similarly, i64.trunc_f32_s applied to z invokes truncf32,i64s(z), which maps to the generic trunc32,64s(z).

t.const c

  1. Push the value t.const c to the stack.

Note

No formal reduction rule is required for this instruction, since const instructions already are values.

t.unop

  1. Assert: due to validation, a value of value type t is on the top of the stack.

  2. Pop the value t.const c1 from the stack.

  3. If unopt(c1) is defined, then:

    1. Let c be a possible result of computing unopt(c1).

    2. Push the value t.const c to the stack.

  4. Else:

    1. Trap.

(t.const c1) t.unop(t.const c)(ifcunopt(c1))(t.const c1) t.unoptrap(ifunopt(c1)={})

t.binop

  1. Assert: due to validation, two values of value type t are on the top of the stack.

  2. Pop the value t.const c2 from the stack.

  3. Pop the value t.const c1 from the stack.

  4. If binopt(c1,c2) is defined, then:

    1. Let c be a possible result of computing binopt(c1,c2).

    2. Push the value t.const c to the stack.

  5. Else:

    1. Trap.

(t.const c1) (t.const c2) t.binop(t.const c)(ifcbinopt(c1,c2))(t.const c1) (t.const c2) t.binoptrap(ifbinopt(c1,c2)={})

t.testop

  1. Assert: due to validation, a value of value type t is on the top of the stack.

  2. Pop the value t.const c1 from the stack.

  3. Let c be the result of computing testopt(c1).

  4. Push the value i32.const c to the stack.

(t.const c1) t.testop(i32.const c)(ifc=testopt(c1))

t.relop

  1. Assert: due to validation, two values of value type t are on the top of the stack.

  2. Pop the value t.const c2 from the stack.

  3. Pop the value t.const c1 from the stack.

  4. Let c be the result of computing relopt(c1,c2).

  5. Push the value i32.const c to the stack.

(t.const c1) (t.const c2) t.relop(i32.const c)(ifc=relopt(c1,c2))

t2.cvtop_t1_sx?

  1. Assert: due to validation, a value of value type t1 is on the top of the stack.

  2. Pop the value t1.const c1 from the stack.

  3. If cvtopt1,t2sx?(c1) is defined:

    1. Let c2 be a possible result of computing cvtopt1,t2sx?(c1).

    2. Push the value t2.const c2 to the stack.

  4. Else:

    1. Trap.

(t1.const c1) t2.cvtop_t1_sx?(t2.const c2)(ifc2cvtopt1,t2sx?(c1))(t1.const c1) t2.cvtop_t1_sx?trap(ifcvtopt1,t2sx?(c1)={})

Reference Instructions

ref.null t

  1. Push the value ref.null t to the stack.

Note

No formal reduction rule is required for this instruction, since the ref.null instruction is already a value.

ref.is_null

  1. Assert: due to validation, a reference value is on the top of the stack.

  2. Pop the value val from the stack.

  3. If val is ref.null t, then:

    1. Push the value i32.const 1 to the stack.

  4. Else:

    1. Push the value i32.const 0 to the stack.

val ref.is_null(i32.const 1)(ifval=ref.null t)val ref.is_null(i32.const 0)(otherwise)

ref.func x

  1. Let F be the current frame.

  2. Assert: due to validation, F.module.funcaddrs[x] exists.

  3. Let a be the function address F.module.funcaddrs[x].

  4. Push the value ref a to the stack.

F;(ref.func x)F;(ref a)(ifa=F.module.funcaddrs[x])

Vector Instructions

Most vector instructions are defined in terms of generic numeric operators applied lane-wise based on the shape.

optxN(n1,,nk)=lanestxN1(opt(lanestxN(n1)  lanestxN(nk))

Note

For example, the result of instruction i32x4.add applied to operands i1,i2 invokes addi32x4(i1,i2), which maps to lanesi32x41(addi32(i1+,i2+)), where i1+ and i2+ are sequences resulting from invoking lanesi32x4(i1) and lanesi32x4(i2) respectively.

v128.const c

  1. Push the value v128.const c to the stack.

Note

No formal reduction rule is required for this instruction, since const instructions coincide with values.

v128.vvunop

  1. Assert: due to validation, a value of value type v128 is on the top of the stack.

  2. Pop the value v128.const c1 from the stack.

  3. Let c be the result of computing vvunopv128(c1).

  4. Push the value v128.const c to the stack.

(v128.const c1) v128.vvunop(v128.const c)(ifc=vvunopv128(c1))

v128.vvbinop

  1. Assert: due to validation, two values of value type v128 are on the top of the stack.

  2. Pop the value v128.const c2 from the stack.

  3. Pop the value v128.const c1 from the stack.

  4. Let c be the result of computing vvbinopv128(c1,c2).

  5. Push the value v128.const c to the stack.

(v128.const c1) (v128.const c2) v128.vvbinop(v128.const c)(ifc=vvbinopv128(c1,c2))

v128.vvternop

  1. Assert: due to validation, three values of value type v128 are on the top of the stack.

  2. Pop the value v128.const c3 from the stack.

  3. Pop the value v128.const c2 from the stack.

  4. Pop the value v128.const c1 from the stack.

  5. Let c be the result of computing vvternopv128(c1,c2,c3).

  6. Push the value v128.const c to the stack.

(v128.const c1) (v128.const c2) (v128.const c3) v128.vvternop(v128.const c)(ifc=vvternopv128(c1,c2,c3))

v128.any_true

  1. Assert: due to validation, a value of value type v128 is on the top of the stack.

  2. Pop the value v128.const c1 from the stack.

  3. Let i be the result of computing ine128(c1,0).

  4. Push the value i32.const i onto the stack.

(v128.const c1) v128.any_true(i32.const i)(ifi=ine128(c1,0))

i8x16.swizzle

  1. Assert: due to validation, two values of value type v128 are on the top of the stack.

  2. Pop the value v128.const c2 from the stack.

  3. Let i be the result of computing lanesi8x16(c2).

  4. Pop the value v128.const c1 from the stack.

  5. Let j be the result of computing lanesi8x16(c1).

  6. Let c be the concatenation of the two sequences j and 0240.

  7. Let c be the result of computing lanesi8x161(c[i[0]]c[i[15]]).

  8. Push the value v128.const c onto the stack.

(v128.const c1) (v128.const c2) i8x16.swizzle(v128.const c)(ifi=lanesi8x16(c2)c=lanesi8x16(c1) 0240c=lanesi8x161(c[i[0]]c[i[15]]))

i8x16.shuffle x

  1. Assert: due to validation, two values of value type v128 are on the top of the stack.

  2. Assert: due to validation, for all xi in x it holds that xi<32.

  3. Pop the value v128.const c2 from the stack.

  4. Let i2 be the result of computing lanesi8x16(c2).

  5. Pop the value v128.const c1 from the stack.

  6. Let i1 be the result of computing lanesi8x16(c1).

  7. Let i be the concatenation of the two sequences i1 and i2.

  8. Let c be the result of computing lanesi8x161(i[x[0]]i[x[15]]).

  9. Push the value v128.const c onto the stack.

(v128.const c1) (v128.const c2) (i8x16.shuffle x)(v128.const c)(ifi=lanesi8x16(c1) lanesi8x16(c2)c=lanesi8x161(i[x[0]]i[x[15]]))

shape.splat

  1. Let t be the type unpacked(shape).

  2. Assert: due to validation, a value of value type t is on the top of the stack.

  3. Pop the value t.const c1 from the stack.

  4. Let N be the integer dim(shape).

  5. Let c be the result of computing lanesshape1(c1N).

  6. Push the value v128.const c to the stack.

(t.const c1) shape.splat(v128.const c)(ift=unpacked(shape)c=lanesshape1(c1dim(shape)))

t1xN.extract_lane_sx? x

  1. Assert: due to validation, x<N.

  2. Assert: due to validation, a value of value type v128 is on the top of the stack.

  3. Pop the value v128.const c1 from the stack.

  4. Let i be the result of computing lanest1xN(c1).

  5. Let t2 be the type unpacked(t1xN).

  6. Let c2 be the result of computing extendt1,t2sx?(i[x]).

  7. Push the value t2.const c2 to the stack.

(v128.const c1) (t1xN.extract_lane x)(t2.const c2)(ift2=unpacked(t1xN)c2=extendt1,t2sx?(lanest1xN(c1)[x]))

shape.replace_lane x

  1. Assert: due to validation, x<dim(shape).

  2. Let t1 be the type unpacked(shape).

  3. Assert: due to validation, a value of value type t1 is on the top of the stack.

  4. Pop the value t1.const c1 from the stack.

  5. Assert: due to validation, a value of value type v128 is on the top of the stack.

  6. Pop the value v128.const c2 from the stack.

  7. Let i be the result of computing lanesshape(c2).

  8. Let c be the result of computing lanesshape1(iwith[x]=c1).

  9. Push v128.const c on the stack.

(t1.const c1) (v128.const c2) (shape.replace_lane x)(v128.const c)(ifi=lanesshape(c2)c=lanesshape1(iwith[x]=c1))

shape.vunop

  1. Assert: due to validation, a value of value type v128 is on the top of the stack.

  2. Pop the value v128.const c1 from the stack.

  3. Let c be the result of computing vunopshape(c1).

  4. Push the value v128.const c to the stack.

(v128.const c1) v128.vunop(v128.const c)(ifc=vunopshape(c1))

shape.vbinop

  1. Assert: due to validation, two values of value type v128 are on the top of the stack.

  2. Pop the value v128.const c2 from the stack.

  3. Pop the value v128.const c1 from the stack.

  4. If vbinopshape(c1,c2) is defined:

    1. Let c be a possible result of computing vbinopshape(c1,c2).

    2. Push the value v128.const c to the stack.

  5. Else:

    1. Trap.

(v128.const c1) (v128.const c2) shape.vbinop(v128.const c)(ifcvbinopshape(c1,c2))(v128.const c1) (v128.const c2) shape.vbinoptrap(ifvbinopshape(c1,c2)={})

txN.vrelop

  1. Assert: due to validation, two values of value type v128 are on the top of the stack.

  2. Pop the value v128.const c2 from the stack.

  3. Pop the value v128.const c1 from the stack.

  4. Let i1 be the result of computing lanestxN(c1).

  5. Let i2 be the result of computing lanestxN(c2).

  6. Let i be the result of computing vrelopt(i1,i2).

  7. Let j be the result of computing extend1,|t|s(i).

  8. Let c be the result of computing lanestxN1(j).

  9. Push the value v128.const c to the stack.

(v128.const c1) (v128.const c2) txN.vrelop(v128.const c)(ifc=lanestxN1(extend1,|t|s(vrelopt(lanestxN(c1),lanestxN(c2)))))

txN.vishiftop

  1. Assert: due to validation, a value of value type i32 is on the top of the stack.

  2. Pop the value i32.const s from the stack.

  3. Assert: due to validation, a value of value type v128 is on the top of the stack.

  4. Pop the value v128.const c1 from the stack.

  5. Let i be the result of computing lanestxN(c1).

  6. Let j be the result of computing vishiftopt(i,sN).

  7. Let c be the result of computing lanestxN1(j).

  8. Push the value v128.const c to the stack.

(v128.const c1) (i32.const s) txN.vishiftop(v128.const c)(ifi=lanestxN(c1)c=lanestxN1(vishiftopt(i,sN)))

shape.all_true

  1. Assert: due to validation, a value of value type v128 is on the top of the stack.

  2. Pop the value v128.const c1 from the stack.

  3. Let i1 be the result of computing lanesshape(c1).

  4. Let i be the result of computing bool((i10)).

  5. Push the value i32.const i onto the stack.

(v128.const c1) shape.all_true(i32.const i)(ifi1=lanesshape(c)i=bool((i10)))

txN.bitmask

  1. Assert: due to validation, a value of value type v128 is on the top of the stack.

  2. Pop the value v128.const c1 from the stack.

  3. Let i1N be the result of computing lanestxN(c).

  4. Let B be the bit width |t| of value type t.

  5. Let i2N be the result of computing ilt_sB(i1N,0N).

  6. Let j be the concatenation of the two sequences i2N and 032N.

  7. Let c be the result of computing ibits321(j).

  8. Push the value i32.const c onto the stack.

(v128.const c1) txN.bitmask(i32.const c)(ifc=ibits321(ilt_s|t|(lanestxN(c),0N)))

t2xN.narrow_t1xM_sx

  1. Assert: due to syntax, N=2M.

  2. Assert: due to validation, two values of value type v128 are on the top of the stack.

  3. Pop the value v128.const c2 from the stack.

  4. Let i2M be the result of computing lanest1xM(c2).

  5. Let d2M be the result of computing narrow|t1|,|t2|sx(i2M).

  6. Pop the value v128.const c1 from the stack.

  7. Let i1M be the result of computing lanest1xM(c1).

  8. Let d1M be the result of computing narrow|t1|,|t2|sx(i1M).

  9. Let jN be the concatenation of the two sequences d1M and d2M.

  10. Let c be the result of computing lanest2xN1(jN).

  11. Push the value v128.const c onto the stack.

(v128.const c1) (v128.const c2) t2xN.narrow_t1xM_sx(v128.const c)(ifd1M=narrow|t1|,|t2|sx(lanest1xM(c1))d2M=narrow|t1|,|t2|sx(lanest1xM(c2))c=lanest2xN1(d1M d2M))

t2xN.vcvtop_t1xM_sx

  1. Assert: due to syntax, N=M.

  2. Assert: due to validation, a value of value type v128 is on the top of the stack.

  3. Pop the value v128.const c1 from the stack.

  4. Let i be the result of computing lanest1xM(c1).

  5. Let j be the result of computing vcvtop|t1|,|t2|sx(i).

  6. Let c be the result of computing lanest2xN1(j).

  7. Push the value v128.const c onto the stack.

(v128.const c1) t2xN.vcvtop_t1xM_sx(v128.const c)(ifc=lanest2xN1(vcvtop|t1|,|t2|sx(lanest1xM(c1))))

t2xN.vcvtop_half_t1xM_sx?

  1. Assert: due to syntax, N=M/2.

  2. Assert: due to validation, a value of value type v128 is on the top of the stack.

  3. Pop the value v128.const c1 from the stack.

  4. Let i be the result of computing lanest1xM(c1).

  5. If half is low, then:

    1. Let j be the sequence i[0:N].

  6. Else:

    1. Let j be the sequence i[N:N].

  7. Let k be the result of computing vcvtop|t1|,|t2|sx?(j).

  8. Let c be the result of computing lanest2xN1(k).

  9. Push the value v128.const c onto the stack.

(v128.const c1) t2xN.vcvtop_half_t1xM_sx?(v128.const c)(ifc=lanest2xN1(vcvtop|t1|,|t2|sx?(lanest1xM(c1)[half(0,N):N])))

where:

low(x,y)=xhigh(x,y)=y

t2xN.vcvtop_t1xM_sx_zero

  1. Assert: due to syntax, N=2M.

  2. Assert: due to validation, a value of value type v128 is on the top of the stack.

  3. Pop the value v128.const c1 from the stack.

  4. Let i be the result of computing lanest1xM(c1).

  5. Let j be the result of computing vcvtop|t1|,|t2|sx(i).

  6. Let k be the concatenation of the two sequences j and 0M.

  7. Let c be the result of computing lanest2xN1(k).

  8. Push the value v128.const c onto the stack.

(v128.const c1) t2xN.vcvtop_t1xM_sx_zero(v128.const c)(ifc=lanest2xN1(vcvtop|t1|,|t2|sx(lanest1xM(c1)) 0M))

i32x4.dot_i16x8_s

  1. Assert: due to validation, two values of value type v128 are on the top of the stack.

  2. Pop the value v128.const c2 from the stack.

  3. Pop the value v128.const c1 from the stack.

  4. Let i1 be the result of computing lanesi16x8(c1).

  5. Let j1 be the result of computing extend16,32s(i1).

  6. Let i2 be the result of computing lanesi16x8(c2).

  7. Let j2 be the result of computing extend16,32s(i2).

  8. Let (k1 k2) be the result of computing imul32(j1,j2).

  9. Let k be the result of computing iadd32(k1,k2).

  10. Let c be the result of computing lanesi32x41(k).

  11. Push the value v128.const c onto the stack.

(v128.const c1) (v128.const c2) i32x4.dot_i16x8_s(v128.const c)(if(i1 i2)=imul32(extend16,32s(lanesi16x8(c1)),extend16,32s(lanesi16x8(c2)))j=iadd32(i1,i2)c=lanesi32x41(j))

t2xN.extmul_half_t1xM_sx

  1. Assert: due to syntax, N=M/2.

  2. Assert: due to validation, two values of value type v128 are on the top of the stack.

  3. Pop the value v128.const c2 from the stack.

  4. Pop the value v128.const c1 from the stack.

  5. Let i1 be the result of computing lanest1xM(c1).

  6. Let i2 be the result of computing lanest1xM(c2).

  7. If half is low, then:

    1. Let j1 be the sequence i1[0:N].

    2. Let j2 be the sequence i2[0:N].

  8. Else:

    1. Let j1 be the sequence i1[N:N].

    2. Let j2 be the sequence i2[N:N].

  9. Let k1 be the result of computing extend|t1|,|t2|sx(j1).

  10. Let k2 be the result of computing extend|t1|,|t2|sx(j2).

  11. Let k be the result of computing imult2xN(k1,k2).

  12. Let c be the result of computing lanest2xN1(k).

  13. Push the value v128.const c onto the stack.

(v128.const c1) (v128.const c2) t2xN.extmul_half_t1xM_sx(v128.const c)(ifi=lanest1xM(c1)[half(0,N):N]j=lanest1xM(c2)[half(0,N):N]c=lanest2xN1(imult2xN(extend|t1|,|t2|sx(i),extend|t1|,|t2|sx(j))))

where:

low(x,y)=xhigh(x,y)=y

t2xN.extadd_pairwise_t1xM_sx

  1. Assert: due to syntax, N=M/2.

  2. Assert: due to validation, a value of value type v128 is on the top of the stack.

  3. Pop the value v128.const c1 from the stack.

  4. Let i be the result of computing lanest1xM(c1).

  5. Let (j1 j2) be the result of computing extend|t1|,|t2|sx(i).

  6. Let k be the result of computing iaddN(j1,j2).

  7. Let c be the result of computing lanest2xN1(k).

  8. Push the value v128.const c to the stack.

(v128.const c1) t2xN.extadd_pairwise_t1xM_sx(v128.const c)(if(i1 i2)=extend|t1|,|t2|sx(lanest1xM(c1))j=iaddN(i1,i2)c=lanest2xN1(j))

Parametric Instructions

drop

  1. Assert: due to validation, a value is on the top of the stack.

  2. Pop the value val from the stack.

val  dropϵ

select (t)?

  1. Assert: due to validation, a value of value type i32 is on the top of the stack.

  2. Pop the value i32.const c from the stack.

  3. Assert: due to validation, two more values (of the same value type) are on the top of the stack.

  4. Pop the value val2 from the stack.

  5. Pop the value val1 from the stack.

  6. If c is not 0, then:

    1. Push the value val1 back to the stack.

  7. Else:

    1. Push the value val2 back to the stack.

val1 val2 (i32.const c) (select t?)val1(ifc0)val1 val2 (i32.const c) (select t?)val2(ifc=0)

Note

In future versions of WebAssembly, select may allow more than one value per choice.

Variable Instructions

local.get x

  1. Let F be the current frame.

  2. Assert: due to validation, F.locals[x] exists.

  3. Let val be the value F.locals[x].

  4. Push the value val to the stack.

F;(local.get x)F;val(ifF.locals[x]=val)

local.set x

  1. Let F be the current frame.

  2. Assert: due to validation, F.locals[x] exists.

  3. Assert: due to validation, a value is on the top of the stack.

  4. Pop the value val from the stack.

  5. Replace F.locals[x] with the value val.

F;val (local.set x)F;ϵ(ifF=Fwithlocals[x]=val)

local.tee x

  1. Assert: due to validation, a value is on the top of the stack.

  2. Pop the value val from the stack.

  3. Push the value val to the stack.

  4. Push the value val to the stack.

  5. Execute the instruction local.set x.

val (local.tee x)val val (local.set x)

global.get x

  1. Let F be the current frame.

  2. Assert: due to validation, F.module.globaladdrs[x] exists.

  3. Let a be the global address F.module.globaladdrs[x].

  4. Assert: due to validation, S.globals[a] exists.

  5. Let glob be the global instance S.globals[a].

  6. Let val be the value glob.value.

  7. Push the value val to the stack.

S;F;(global.get x)S;F;val(ifS.globals[F.module.globaladdrs[x]].value=val)

global.set x

  1. Let F be the current frame.

  2. Assert: due to validation, F.module.globaladdrs[x] exists.

  3. Let a be the global address F.module.globaladdrs[x].

  4. Assert: due to validation, S.globals[a] exists.

  5. Let glob be the global instance S.globals[a].

  6. Assert: due to validation, a value is on the top of the stack.

  7. Pop the value val from the stack.

  8. Replace glob.value with the value val.

S;F;val (global.set x)S;F;ϵ(ifS=Swithglobals[F.module.globaladdrs[x]].value=val)

Note

Validation ensures that the global is, in fact, marked as mutable.

Table Instructions

table.get x

  1. Let F be the current frame.

  2. Assert: due to validation, F.module.tableaddrs[x] exists.

  3. Let a be the table address F.module.tableaddrs[x].

  4. Assert: due to validation, S.tables[a] exists.

  5. Let tab be the table instance S.tables[a].

  6. Assert: due to validation, a value of value type i32 is on the top of the stack.

  7. Pop the value i32.const i from the stack.

  8. If i is not smaller than the length of tab.elem, then:

    1. Trap.

  9. Let val be the value tab.elem[i].

  10. Push the value val to the stack.

 S;F;(i32.const i) (table.get x)S;F;val(ifS.tables[F.module.tableaddrs[x]].elem[i]=val)S;F;(i32.const i) (table.get x)S;F;trap(otherwise)

table.set x

  1. Let F be the current frame.

  2. Assert: due to validation, F.module.tableaddrs[x] exists.

  3. Let a be the table address F.module.tableaddrs[x].

  4. Assert: due to validation, S.tables[a] exists.

  5. Let tab be the table instance S.tables[a].

  6. Assert: due to validation, a reference value is on the top of the stack.

  7. Pop the value val from the stack.

  8. Assert: due to validation, a value of value type i32 is on the top of the stack.

  9. Pop the value i32.const i from the stack.

  10. If i is not smaller than the length of tab.elem, then:

    1. Trap.

  11. Replace the element tab.elem[i] with val.

 S;F;(i32.const i) val (table.set x)S;F;ϵ(ifS=Swithtables[F.module.tableaddrs[x]].elem[i]=val)S;F;(i32.const i) val (table.set x)S;F;trap(otherwise)

table.size x

  1. Let F be the current frame.

  2. Assert: due to validation, F.module.tableaddrs[x] exists.

  3. Let a be the table address F.module.tableaddrs[x].

  4. Assert: due to validation, S.tables[a] exists.

  5. Let tab be the table instance S.tables[a].

  6. Let sz be the length of tab.elem.

  7. Push the value i32.const sz to the stack.

S;F;(table.size x)S;F;(i32.const sz)(if|S.tables[F.module.tableaddrs[x]].elem|=sz)

table.grow x

  1. Let F be the current frame.

  2. Assert: due to validation, F.module.tableaddrs[x] exists.

  3. Let a be the table address F.module.tableaddrs[x].

  4. Assert: due to validation, S.tables[a] exists.

  5. Let tab be the table instance S.tables[a].

  6. Let sz be the length of S.tables[a].

  7. Assert: due to validation, a value of value type i32 is on the top of the stack.

  8. Pop the value i32.const n from the stack.

  9. Assert: due to validation, a reference value is on the top of the stack.

  10. Pop the value val from the stack.

  11. Let err be the i32 value 2321, for which signed32(err) is 1.

  12. Either:

  1. If growing tab by n entries with initialization value val succeeds, then:

    1. Push the value i32.const sz to the stack.

  2. Else:

    1. Push the value i32.const err to the stack.

  1. Or:

  1. push the value i32.const err to the stack.

 S;F;val (i32.const n) (table.grow x)S;F;(i32.const sz)(ifF.module.tableaddrs[x]=asz=|S.tables[a].elem|S=Swithtables[a]=growtable(S.tables[a],n,val))S;F;val (i32.const n) (table.grow x)S;F;(i32.const signed321(1))

Note

The table.grow instruction is non-deterministic. It may either succeed, returning the old table size sz, or fail, returning 1. Failure must occur if the referenced table instance has a maximum size defined that would be exceeded. However, failure can occur in other cases as well. In practice, the choice depends on the resources available to the embedder.

table.fill x

  1. Let F be the current frame.

  2. Assert: due to validation, F.module.tableaddrs[x] exists.

  3. Let ta be the table address F.module.tableaddrs[x].

  4. Assert: due to validation, S.tables[ta] exists.

  5. Let tab be the table instance S.tables[ta].

  6. Assert: due to validation, a value of value type i32 is on the top of the stack.

  7. Pop the value i32.const n from the stack.

  8. Assert: due to validation, a reference value is on the top of the stack.

  9. Pop the value val from the stack.

  10. Assert: due to validation, a value of value type i32 is on the top of the stack.

  11. Pop the value i32.const i from the stack.

  12. If i+n is larger than the length of tab.elem, then:

    1. Trap.

  1. If n is 0, then:

    1. Return.

  2. Push the value i32.const i to the stack.

  3. Push the value val to the stack.

  4. Execute the instruction table.set x.

  5. Push the value i32.const (i+1) to the stack.

  6. Push the value val to the stack.

  7. Push the value i32.const (n1) to the stack.

  8. Execute the instruction table.fill x.

S;F;(i32.const i) val (i32.const n) (table.fill x)S;F;trap(ifi+n>|S.tables[F.module.tableaddrs[x]].elem|)S;F;(i32.const i) val (i32.const 0) (table.fill x)S;F;ϵ(otherwise)S;F;(i32.const i) val (i32.const n+1) (table.fill x)S;F;(i32.const i) val (table.set x)(i32.const i+1) val (i32.const n) (table.fill x)(otherwise)

table.copy x y

  1. Let F be the current frame.

  2. Assert: due to validation, F.module.tableaddrs[x] exists.

  3. Let tax be the table address F.module.tableaddrs[x].

  4. Assert: due to validation, S.tables[tax] exists.

  5. Let tabx be the table instance S.tables[tax].

  6. Assert: due to validation, F.module.tableaddrs[y] exists.

  7. Let tay be the table address F.module.tableaddrs[y].

  8. Assert: due to validation, S.tables[tay] exists.

  9. Let taby be the table instance S.tables[tay].

  10. Assert: due to validation, a value of value type i32 is on the top of the stack.

  11. Pop the value i32.const n from the stack.

  12. Assert: due to validation, a value of value type i32 is on the top of the stack.

  13. Pop the value i32.const s from the stack.

  14. Assert: due to validation, a value of value type i32 is on the top of the stack.

  15. Pop the value i32.const d from the stack.

  16. If s+n is larger than the length of taby.elem or d+n is larger than the length of tabx.elem, then:

    1. Trap.

  17. If n=0, then:

  1. Return.

  1. If ds, then:

  1. Push the value i32.const d to the stack.

  2. Push the value i32.const s to the stack.

  3. Execute the instruction table.get y.

  4. Execute the instruction table.set x.

  5. Assert: due to the earlier check against the table size, d+1<232.

  6. Push the value i32.const (d+1) to the stack.

  7. Assert: due to the earlier check against the table size, s+1<232.

  8. Push the value i32.const (s+1) to the stack.

  1. Else:

  1. Assert: due to the earlier check against the table size, d+n1<232.

  2. Push the value i32.const (d+n1) to the stack.

  3. Assert: due to the earlier check against the table size, s+n1<232.

  4. Push the value i32.const (s+n1) to the stack.

  1. Execute the instruction table.get y.

  1. Execute the instruction table.set x.

  2. Push the value i32.const d to the stack.

  3. Push the value i32.const s to the stack.

  1. Push the value i32.const (n1) to the stack.

  2. Execute the instruction table.copy x y.

 S;F;(i32.const d) (i32.const s) (i32.const n) (table.copy x y)S;F;trap(ifs+n>|S.tables[F.module.tableaddrs[y]].elem|d+n>|S.tables[F.module.tableaddrs[x]].elem|)S;F;(i32.const d) (i32.const s) (i32.const 0) (table.copy x y)S;F;ϵ(otherwise)S;F;(i32.const d) (i32.const s) (i32.const n+1) (table.copy x y)S;F;(i32.const d) (i32.const s) (table.get y) (table.set x)(i32.const d+1) (i32.const s+1) (i32.const n) (table.copy x y)(otherwise,ifds)S;F;(i32.const d) (i32.const s) (i32.const n+1) (table.copy x y)S;F;(i32.const d+n) (i32.const s+n) (table.get y) (table.set x)(i32.const d) (i32.const s) (i32.const n) (table.copy x y)(otherwise,ifd>s)

table.init x y

  1. Let F be the current frame.

  2. Assert: due to validation, F.module.tableaddrs[x] exists.

  3. Let ta be the table address F.module.tableaddrs[x].

  4. Assert: due to validation, S.tables[ta] exists.

  5. Let tab be the table instance S.tables[ta].

  6. Assert: due to validation, F.module.elemaddrs[y] exists.

  7. Let ea be the element address F.module.elemaddrs[y].

  8. Assert: due to validation, S.elems[ea] exists.

  9. Let elem be the element instance S.elems[ea].

  10. Assert: due to validation, a value of value type i32 is on the top of the stack.

  11. Pop the value i32.const n from the stack.

  12. Assert: due to validation, a value of value type i32 is on the top of the stack.

  13. Pop the value i32.const s from the stack.

  14. Assert: due to validation, a value of value type i32 is on the top of the stack.

  15. Pop the value i32.const d from the stack.

  16. If s+n is larger than the length of elem.elem or d+n is larger than the length of tab.elem, then:

    1. Trap.

  17. If n=0, then:

    1. Return.

  18. Let val be the reference value elem.elem[s].

  19. Push the value i32.const d to the stack.

  20. Push the value val to the stack.

  21. Execute the instruction table.set x.

  22. Assert: due to the earlier check against the table size, d+1<232.

  23. Push the value i32.const (d+1) to the stack.

  24. Assert: due to the earlier check against the segment size, s+1<232.

  25. Push the value i32.const (s+1) to the stack.

  26. Push the value i32.const (n1) to the stack.

  27. Execute the instruction table.init x y.

 S;F;(i32.const d) (i32.const s) (i32.const n) (table.init x y)S;F;trap(ifs+n>|S.elems[F.module.elemaddrs[y]].elem|d+n>|S.tables[F.module.tableaddrs[x]].elem|)S;F;(i32.const d) (i32.const s) (i32.const 0) (table.init x y)S;F;ϵ(otherwise)S;F;(i32.const d) (i32.const s) (i32.const n+1) (table.init x y)S;F;(i32.const d) val (table.set x)(i32.const d+1) (i32.const s+1) (i32.const n) (table.init x y)(otherwise,ifval=S.elems[F.module.elemaddrs[y]].elem[s])

elem.drop x

  1. Let F be the current frame.

  2. Assert: due to validation, F.module.elemaddrs[x] exists.

  3. Let a be the element address F.module.elemaddrs[x].

  4. Assert: due to validation, S.elems[a] exists.

  5. Replace S.elems[a].elem with ϵ.

 S;F;(elem.drop x)S;F;ϵ(ifS=Swithelems[F.module.elemaddrs[x]].elem=ϵ)

Memory Instructions

Note

The alignment memarg.align in load and store instructions does not affect the semantics. It is an indication that the offset ea at which the memory is accessed is intended to satisfy the property eamod2memarg.align=0. A WebAssembly implementation can use this hint to optimize for the intended use. Unaligned access violating that property is still allowed and must succeed regardless of the annotation. However, it may be substantially slower on some hardware.

t.load memarg and t.loadN_sx memarg

  1. Let ord be unord.

  2. Let F be the current frame.

  3. Assert: due to validation, F.module.memaddrs[0] exists.

  4. Let a be the memory address F.module.memaddrs[0].

  5. Assert: due to validation, S.mems[a] exists.

  6. Let mem be the memory instance S.mems[a].

  7. Assert: due to validation, a value of value type i32 is on the top of the stack.

  8. Pop the value i32.const i from the stack.

  9. Let ea be the integer i+memarg.offset.

  10. If N is not part of the instruction, then:

  1. Let N be the bit width |t| of number type t.

  1. If both ord is seqcst and ea modulo N/8 is not equal to 0, then:

  1. Trap.

  1. If mem.type is limits unshared, then:

    1. If ea+N/8 is larger than the length of mem.data, then:

      1. Trap.

    2. Let b be the byte sequence mem.data[ea:N/8].

  2. Else:

    1. Perform the action (rd a.len n) to read the length n of the shared memory instance at memory address a.

    2. If ea+N/8 is larger than n, then:

      1. Trap.

    3. Let notears? be tearing(t,N,ea).

    4. Perform the action (rdord a.data[ea] b notears?) to read N/8 bytes b from data offset ea of the shared memory instance at memory address a.

  3. Let cN be the integer for which bytesiN(n)=b.

  4. Let c be the result of computing extendN,|t|sx(cN).

  5. Push the value t.const c to the stack.

 S;F;(i32.const i) (t.load(N_sx)? memarg)S;F;(t.const c)(ifmem.type=limits unsharedea+N/8|mem.data|(ord=unordeamodN/8=0)c=extendN,|t|sx(bytesiN1(mem.data[ea:N/8])))S;F;(i32.const i) (t.load(N_sx)? memarg)S;F;trap(otherwise,ifmem.type=limits unshared) S;F;(i32.const i) (t.load(N_sx)? memarg)(rd a.len n) (rdord a.data[ea] b notears?)S;F;(t.const c)(ifmem.type=limits sharedea+N/8n(ord=unordeamodN/8=0)c=extendN,|t|sx(bytesiN1(b)))S;F;(i32.const i) (t.load(N_sx)? memarg)(rd a.len n)S;F;trap(ifmem.type=limits shared(ea+N/8>nord=seqcsteamodN/80)) (whereord=unordN=|t|ifN not presentsx=uifsx not presenta=F.module.memaddrs[0]mem=S.mems[a]ea=i+memarg.offsetnotears?=tearing(t,N,ea))

v128.loadMxN_sx memarg

  1. Let ord be unord.

  2. Let F be the current frame.

  3. Assert: due to validation, F.module.memaddrs[0] exists.

  4. Let a be the memory address F.module.memaddrs[0].

  5. Assert: due to validation, S.mems[a] exists.

  6. Let mem be the memory instance S.mems[a].

  7. Assert: due to validation, a value of value type i32 is on the top of the stack.

  8. Pop the value i32.const i from the stack.

  9. Let ea be the integer i+memarg.offset.

  10. If mem.type is limits unshared, then:

  1. If ea+MN/8 is larger than the length of mem.data, then:

    1. Trap.

  2. Let b be the byte sequence mem.data[ea:MN/8].

  1. Else:

    1. Perform the action (rd a.len n) to read the length n of the shared memory instance at memory address a.

    2. If ea+MN/8 is larger than n, then:

      1. Trap.

    3. Perform the action (rdord a.data[ea] b) to read MN/8 bytes b from data offset ea of the shared memory instance at memory address a.

  2. Let mk be the integer for which bytesiM(mk)=b[kM/8:M/8].

  3. Let W be the integer M2.

  4. Let nk be the result of computing extendM,Wsx(mk).

  5. Let c be the result of computing lanesiWxN1(n0nN1).

  6. Push the value v128.const c to the stack.

 S;F;(i32.const i) (v128.loadMxN_sx memarg)S;F;(v128.const c)(ifmem.type=limits unsharedea+MN/8|mem.data|bytesiM(mk)=mem.data[ea+kM/8:M/8]c=lanesiWxN1(extendM,Wsx(m0)extendM,Wsx(mN1)))S;F;(i32.const i) (v128.loadMxN_sx memarg)S;F;trap(otherwise,ifmem.type=limits unshared) S;F;(i32.const i) (v128.loadMxN_sx memarg)(rd a.len n) (rdord a.data[ea] b)S;F;(v128.const c)(ifmem.type=limits sharedea+MN/8nbytesiM(mk)=b[kM/8:M/8]c=lanesiWxN1(extendM,Wsx(m0)extendM,Wsx(mN1)))S;F;(i32.const i) (v128.loadMxN_sx memarg)(rd a.len n) (rdord a.data[ea] b)S;F;trap(ifmem.type=limits shared(ea+MN/8>n) (whereord=unorda=F.module.memaddrs[0]mem=S.mems[a]ea=i+memarg.offsetW=M2)

v128.loadN_splat memarg

  1. Let ord be unord.

  2. Let F be the current frame.

  3. Assert: due to validation, F.module.memaddrs[0] exists.

  4. Let a be the memory address F.module.memaddrs[0].

  5. Assert: due to validation, S.mems[a] exists.

  6. Let mem be the memory instance S.mems[a].

  7. Assert: due to validation, a value of value type i32 is on the top of the stack.

  8. Pop the value i32.const i from the stack.

  9. Let ea be the integer i+memarg.offset.

  10. If mem.type is limits unshared, then:

  1. If ea+N/8 is larger than the length of mem.data, then:

    1. Trap.

  2. Let b be the byte sequence mem.data[ea:N/8].

  1. Else:

    1. Perform the action (rd a.len n) to read the length n of the shared memory instance at memory address a.

    2. If ea+N/8 is larger than n, then:

      1. Trap.

    3. Perform the action (rdord a.data[ea] b) to read N/8 bytes b from data offset ea of the shared memory instance at memory address a.

  2. Let n be the integer for which bytesiN(n)=b.

  3. Let L be the integer 128/N.

  4. Let c be the result of computing lanesiNxL1(nL).

  5. Push the value v128.const c to the stack.

 S;F;(i32.const i) (v128.loadN_splat memarg)S;F;(v128.const c)(ifmem.type=limits unsharedea+N/8|mem.data|bytesiN(n)=mem.data[ea:N/8]c=lanesiNxL1(nL))S;F;(i32.const i) (v128.loadN_splat memarg)S;F;trap(otherwise,ifmem.type=limits unshared) S;F;(i32.const i) (v128.loadN_splat memarg)(rd a.len n) (rdord a.data[ea] b)S;F;(t.const c)(ifmem.type=limits sharedea+N/8nbytesiN(n)=bc=lanesiNxL1(nL))S;F;(i32.const i) (v128.loadN_splat memarg)(rd a.len n)S;F;trap(ifmem.type=limits shared(ea+N/8>n) (whereord=unorda=F.module.memaddrs[0]mem=S.mems[a]ea=i+memarg.offset)

v128.loadN_zero memarg

  1. Let ord be unord.

  2. Let F be the current frame.

  3. Assert: due to validation, F.module.memaddrs[0] exists.

  4. Let a be the memory address F.module.memaddrs[0].

  5. Assert: due to validation, S.mems[a] exists.

  6. Let mem be the memory instance S.mems[a].

  7. Assert: due to validation, a value of value type i32 is on the top of the stack.

  8. Pop the value i32.const i from the stack.

  9. Let ea be the integer i+memarg.offset.

  10. If mem.type is limits unshared, then:

  1. If ea+N/8 is larger than the length of mem.data, then:

    1. Trap.

  2. Let b be the byte sequence mem.data[ea:N/8].

  1. Else:

    1. Perform the action (rd a.len n) to read the length n of the shared memory instance at memory address a.

    2. If ea+N/8 is larger than n, then:

      1. Trap.

    3. Perform the action (rdord a.data[ea] b) to read N/8 bytes b from data offset ea of the shared memory instance at memory address a.

  2. Let n be the integer for which bytesiN(n)=b.

  3. Let c be the result of computing extendN,128u(n).

  4. Push the value v128.const c to the stack.

 S;F;(i32.const i) (v128.loadN_zero memarg)S;F;(v128.const c)(ifmem.type=limits unsharedea+N/8|mem.data|bytesiN(n)=mem.data[ea:N/8]c=extendN,128u(n))S;F;(i32.const i) (v128.loadN_zero memarg)S;F;trap(otherwise,ifmem.type=limits unshared) S;F;(i32.const i) (v128.loadN_zero memarg)(rd a.len n) (rdord a.data[ea] b)S;F;(t.const c)(ifmem.type=limits sharedea+N/8nbytesiN(n)=bc=extendN,128u(n))S;F;(i32.const i) (v128.loadN_zero memarg)(rd a.len n)S;F;trap(ifmem.type=limits shared(ea+N/8>n) (whereord=unorda=F.module.memaddrs[0]mem=S.mems[a]ea=i+memarg.offset)

v128.loadN_lane memarg x

  1. Let ord be unord.

  2. Let F be the current frame.

  3. Assert: due to validation, F.module.memaddrs[0] exists.

  4. Let a be the memory address F.module.memaddrs[0].

  5. Assert: due to validation, S.mems[a] exists.

  6. Let mem be the memory instance S.mems[a].

  7. Assert: due to validation, a value of value type v128 is on the top of the stack.

  8. Pop the value v128.const v from the stack.

  9. Assert: due to validation, a value of value type i32 is on the top of the stack.

  10. Pop the value i32.const i from the stack.

  11. Let ea be the integer i+memarg.offset.

  12. If mem.type is limits unshared, then:

    1. If ea+N/8 is larger than the length of mem.data, then:

      1. Trap.

    2. Let b be the byte sequence mem.data[ea:N/8].

  13. Else:

    1. Perform the action (rd a.len n) to read the length n of the shared memory instance at memory address a.

    2. If ea+N/8 is larger than n, then:

      1. Trap.

    3. Perform the action (rdord a.data[ea] b) to read N/8 bytes b from data offset ea of the shared memory instance at memory address a.

  14. Let r be the constant for which bytesiN(r)=b.

  15. Let L be 128/N.

  16. Let j be the result of computing lanesiNxL(v).

  17. Let c be the result of computing lanesiNxL(jwith[x]=r).

  18. Push the value v128.const c to the stack.

 S;F;(i32.const i) (v128.const v) (v128.loadN_lane memarg x)S;F;(v128.const c)(ifmem.type=limits unsharedea+N/8|mem.data|bytesiN(n)=mem.data[ea:N/8]c=lanesiNxL1(lanesiNxL(v)with[x]=r))S;F;(i32.const i) (v128.const v) (v128.loadN_lane memarg x)S;F;trap(otherwise,ifmem.type=limits unshared) S;F;(i32.const i) (v128.const v) (v128.loadN_lane memarg x)(rd a.len n) (rdord a.data[ea] b)S;F;(t.const c)(ifmem.type=limits sharedea+N/8nbytesiN(n)=bc=lanesiNxL1(lanesiNxL(v)with[x]=r))S;F;(i32.const i) (v128.const v) (v128.loadN_lane memarg x)(rd a.len n)S;F;trap(ifmem.type=limits shared(ea+N/8>n) (whereord=unorda=F.module.memaddrs[0]mem=S.mems[a]ea=i+memarg.offset)L=128/N

t.store memarg and t.storeN memarg

  1. Let ord be unord.

  2. Let F be the current frame.

  3. Assert: due to validation, F.module.memaddrs[0] exists.

  4. Let a be the memory address F.module.memaddrs[0].

  5. Assert: due to validation, S.mems[a] exists.

  6. Let mem be the memory instance S.mems[a].

  7. Assert: due to validation, a value of value type t is on the top of the stack.

  8. Pop the value t.const c from the stack.

  9. Assert: due to validation, a value of value type i32 is on the top of the stack.

  10. Pop the value i32.const i from the stack.

  11. Let ea be the integer i+memarg.offset.

  12. If N is not part of the instruction, then:

    1. Let N be the bit width |t| of number type t.

  13. If both ord is seqcst and ea modulo N/8 is not equal to 0, then:

  1. Trap.

  1. Let n be the result of computing wrap|t|,N(c).

  2. Let b be the byte sequence bytesiN(n).

  3. If mem.type is limits unshared, then:

    1. If ea+N/8 is larger than the length of mem.data, then:

      1. Trap.

    2. Replace the bytes mem.data[ea:N/8] with b.

  4. Else:

    1. Perform the action (rd a.len n) to read the length n of the shared memory instance at memory address a.

    2. If ea+N/8 is larger than n, then:

      1. Trap.

    3. Let notears? be tearing(t,N,ea).

    4. Perform the action (wrord a.data[ea] b notears?) to write the bytes b to data offset ea of the shared memory instance at memory address a.

 S;F;(i32.const i) (t.const c) (t.storeN? memarg)S;F;ϵ(ifmem.type=limits unsharedea+N/8|mem.data|(ord=unordeamodN/8=0)S=Swithmems[F.module.memaddrs[0]].data[ea:N/8]=bytesiN(wrap|t|,N(c)))S;F;(i32.const i) (t.const c) (t.storeN? memarg)S;F;trap(otherwise,ifmem.type=limits unshared) S;F;(i32.const i) (t.const c) (t.storeN? memarg)(rd a.len n) (wrord a.data[ea] b) notearsS;F;ϵ(ifmem.type=limits sharedea+N/8n(ord=unordeamodN/8=0)b=bytesiN(wrap|t|,N(c)))S;F;(i32.const i) (t.const c) (t.storeN? memarg)(rd a.len n)S;F;trap(ifmem.type=limits shared(ea+N/8>nord=seqcsteamodN/80)) (whereord=unordN=|t|ifN not presenta=F.module.memaddrs[0]mem=S.mems[a]ea=i+memarg.offsetnotears?=tearing(t,N,ea))

v128.storeN_lane memarg x

  1. Let ord be unord.

  2. Let F be the current frame.

  3. Assert: due to validation, F.module.memaddrs[0] exists.

  4. Let a be the memory address F.module.memaddrs[0].

  5. Assert: due to validation, S.mems[a] exists.

  6. Let mem be the memory instance S.mems[a].

  7. Assert: due to validation, a value of value type v128 is on the top of the stack.

  8. Pop the value v128.const c from the stack.

  9. Assert: due to validation, a value of value type i32 is on the top of the stack.

  10. Pop the value i32.const i from the stack.

  11. Let ea be the integer i+memarg.offset.

  12. Let L be 128/N.

  13. Let j be the result of computing lanesiNxL(c).

  14. Let b be the result of computing bytesiN(j[x]).

  15. If mem.type is limits unshared, then:

    1. If ea+N/8 is larger than the length of mem.data, then:

      1. Trap.

    2. Replace the bytes mem.data[ea:N/8] with b.

  16. Else:

    1. Perform the action (rd a.len n) to read the length n of the shared memory instance at memory address a.

    2. If ea+N/8 is larger than n, then:

      1. Trap.

    3. Perform the action (wrord a.data[ea] b) to write the bytes b to data offset ea of the shared memory instance at memory address a.

 S;F;(i32.const i) (v128.const c) (v128.storeN_lane memarg x)S;F;ϵ(ifmem.type=limits unsharedea+N/8|mem.data|S=Swithmems[F.module.memaddrs[0]].data[ea:N/8]=bytesiN(lanesiNxL(c)[x]))S;F;(i32.const i) (v128.const c) (v128.storeN_lane memarg x)S;F;trap(otherwise,ifmem.type=limits unshared) S;F;(i32.const i) (v128.const c) (v128.storeN_lane memarg x)(rd a.len n) (wrord a.data[ea] b)S;F;ϵ(ifmem.type=limits sharedea+N/8nb=bytesiN(lanesiNxL(c)[x]))S;F;(i32.const i) (v128.const c) (v128.storeN_lane memarg x)(rd a.len n)S;F;trap(ifmem.type=limits shared(ea+N/8>nord=seqcsteamodN/80)) (whereord=unorda=F.module.memaddrs[0]mem=S.mems[a]ea=i+memarg.offsetL=128/N)

memory.size

  1. Let F be the current frame.

  2. Assert: due to validation, F.module.memaddrs[0] exists.

  3. Let a be the memory address F.module.memaddrs[0].

  4. Assert: due to validation, S.mems[a] exists.

  5. Let mem be the memory instance S.mems[a].

  6. If mem.type is limits unshared, then:

    1. Let n be the length of mem.data.

  7. Else:

    1. Perform the action (rdseqcst a.len n) to read the length n of the shared memory instance at memory address a.

  8. Let sz be n divided by the page size.

  9. Push the value i32.const sz to the stack.

S;F;memory.sizeS;F;(i32.const sz)(ifmem.type=limits unshared|mem.data|=sz64Ki S;F;memory.size(rdseqcst a.len n)S;F;(i32.const sz)(ifmem.type=limits sharedn=sz64Ki) (wherea=F.module.memaddrs[0]mem=S.mems[a])

memory.grow

  1. Let F be the current frame.

  2. Assert: due to validation, F.module.memaddrs[0] exists.

  3. Let a be the memory address F.module.memaddrs[0].

  4. Assert: due to validation, S.mems[a] exists.

  5. Let mem be the memory instance S.mems[a].

  6. Assert: due to validation, a value of value type i32 is on the top of the stack.

  7. Pop the value i32.const n from the stack.

  8. Let err be the i32 value 2321, for which signed32(err) is 1.

  9. If mem.type is limits unshared, then:

    1. Let sz be the length of S.mems[a] divided by the page size.

    2. Either, try growing mem by n pages:

      1. If it succeeds, push the value i32.const sz to the stack.

      2. Else, push the value i32.const err to the stack.

    3. Or, push the value i32.const err to the stack.

  10. Else:

  1. Either successfully grow the memory:

    1. Let k be n multiplied by the page size.

    2. Perform the action (rmw a.len l (l+k)) to update the current length l of the shared memory instance at memory address a to l+k.

    3. Perform the action (wr a.data[l] (0)k) to append k zero bytes to the end of the shared memory instance at memory address a.

    4. Let sz be l divided by the page size.

    5. Push the value i32.const sz to the stack.

  2. Or indicate failure:

    1. Perform the action (rdseqcst a.len l) to read the length l of the shared memory instance at memory address a.

    2. Push the value i32.const err to the stack.

 S;F;(i32.const n) memory.growS;F;(i32.const sz)(ifmem.type=limits unsharedsz=|mem.data|/64KiS=Swithmems[a]=growmem(S.mems[a],n))S;F;(i32.const n) memory.growS;F;(i32.const signed321(1))(ifmem.type=limits unshared)S;F;(i32.const n) memory.grow(rmw a.len l (l+k)) (wr a.data[l] (0)k)S;F;(i32.const sz)(ifmem.type=limits sharedsz=l/64Kin=k/64Ki)S;F;(i32.const n) memory.grow(rdseqcst a.len n)S;F;(i32.const 1)(ifmem.type=limits shared) (wherea=F.module.memaddrs[0]mem=S.mems[a])

Note

The memory.grow instruction is non-deterministic, even on unshared memories. It may either succeed, returning the old memory size sz, or fail, returning 1. Failure must occur if the referenced memory instance has a maximum size defined that would be exceeded. However, failure can occur in other cases as well. In practice, the choice depends on the resources available to the embedder.

memory.fill

  1. Let F be the current frame.

  2. Assert: due to validation, F.module.memaddrs[0] exists.

  3. Let ma be the memory address F.module.memaddrs[0].

  4. Assert: due to validation, S.mems[ma] exists.

  5. Let mem be the memory instance S.mems[ma].

  6. Assert: due to validation, a value of value type i32 is on the top of the stack.

  7. Pop the value i32.const n from the stack.

  8. Assert: due to validation, a value of value type i32 is on the top of the stack.

  9. Pop the value val from the stack.

  10. Assert: due to validation, a value of value type i32 is on the top of the stack.

  11. Pop the value i32.const d from the stack.

  12. If mem.type is limits unshared, then:

    1. If d+n is larger than the length of mem.data, then:

      1. Trap.

  13. Else:

    1. Perform the action (rd a.len l) to read the length l of the shared memory instance at memory address a.

    2. If d+n is larger than l, then:

      1. Trap.

  14. If n=0, then:

    1. Return.

  15. Push the value i32.const d to the stack.

  16. Push the value val to the stack.

  17. Execute the instruction i32.store8 {offset 0,align 0}.

  18. Assert: due to the earlier check against the memory size, d+1<232.

  19. Push the value i32.const (d+1) to the stack.

  20. Push the value val to the stack.

  21. Push the value i32.const (n1) to the stack.

  22. Execute the instruction memory.fill.

 S;F;(i32.const d) val (i32.const n) memory.fillactS;F;trap(ifd+n>lS;F;(i32.const d) val (i32.const 0) memory.fillactS;F;ϵ(otherwise)S;F;(i32.const d) val (i32.const n+1) memory.fillactS;F;(i32.const d) val (i32.store8 {offset 0,align 0})(i32.const d+1) val (i32.const n) memory.fill(otherwise) (wherea=F.module.memaddrs[0]mem=S.mems[a]((mem.type=limits unsharedl=|mem.data|act=ϵ)(mem.type=limits sharedact=(rd a.len l)))

memory.copy

  1. Let F be the current frame.

  2. Assert: due to validation, F.module.memaddrs[0] exists.

  3. Let ma be the memory address F.module.memaddrs[0].

  4. Assert: due to validation, S.mems[ma] exists.

  5. Let mem be the memory instance S.mems[ma].

  6. Assert: due to validation, a value of value type i32 is on the top of the stack.

  7. Pop the value i32.const n from the stack.

  8. Assert: due to validation, a value of value type i32 is on the top of the stack.

  9. Pop the value i32.const s from the stack.

  10. Assert: due to validation, a value of value type i32 is on the top of the stack.

  11. Pop the value i32.const d from the stack.

  12. If mem.type is limits unshared, then:

    1. If s+n is larger than the length of mem.data or d+n is larger than the length of mem.data, then:

      1. Trap.

  13. Else:

    1. Perform the action (rd a.len l) to read the length l of the shared memory instance at memory address a.

    1. If s+n is larger than l or d+n is larger than l, then:

      1. Trap.

  14. If n=0, then:

  1. Return.

  1. If ds, then:

  1. Push the value i32.const d to the stack.

  2. Push the value i32.const s to the stack.

  3. Execute the instruction i32.load8_u {offset 0,align 0}.

  4. Execute the instruction i32.store8 {offset 0,align 0}.

  5. Assert: due to the earlier check against the memory size, d+1<232.

  6. Push the value i32.const (d+1) to the stack.

  7. Assert: due to the earlier check against the memory size, s+1<232.

  8. Push the value i32.const (s+1) to the stack.

  1. Else:

  1. Assert: due to the earlier check against the memory size, d+n1<232.

  2. Push the value i32.const (d+n1) to the stack.

  3. Assert: due to the earlier check against the memory size, s+n1<232.

  4. Push the value i32.const (s+n1) to the stack.

  5. Execute the instruction i32.load8_u {offset 0,align 0}.

  6. Execute the instruction i32.store8 {offset 0,align 0}.

  7. Push the value i32.const d to the stack.

  8. Push the value i32.const s to the stack.

  1. Push the value i32.const (n1) to the stack.

  2. Execute the instruction memory.copy.

 S;F;(i32.const d) (i32.const s) (i32.const n) memory.copyactS;F;trap(if(s+n>ld+n>l))S;F;(i32.const d) (i32.const s) (i32.const 0) memory.copyS;F;ϵ(otherwise)S;F;(i32.const d) (i32.const s) (i32.const n+1) memory.copyactS;F;(i32.const d)(i32.const s) (i32.load8_u {offset 0,align 0})(i32.store8 {offset 0,align 0})(i32.const d+1) (i32.const s+1) (i32.const n) memory.copy(otherwise)S;F;(i32.const d) (i32.const s) (i32.const n+1) memory.copyactS;F;(i32.const d+n)(i32.const s+n) (i32.load8_u {offset 0,align 0})(i32.store8 {offset 0,align 0})(i32.const d) (i32.const s) (i32.const n) memory.copy(otherwise) (wherea=F.module.memaddrs[0]mem=S.mems[a]((mem.type=limits unsharedl=|mem.data|act=ϵ)(mem.type=limits sharedact=(rd a.len l)))

memory.init x

  1. Let F be the current frame.

  2. Assert: due to validation, F.module.memaddrs[0] exists.

  3. Let ma be the memory address F.module.memaddrs[0].

  4. Assert: due to validation, S.mems[ma] exists.

  5. Let mem be the memory instance S.mems[ma].

  6. Assert: due to validation, F.module.dataaddrs[x] exists.

  7. Let da be the data address F.module.dataaddrs[x].

  8. Assert: due to validation, S.datas[da] exists.

  9. Let data be the data instance S.datas[da].

  10. Assert: due to validation, a value of value type i32 is on the top of the stack.

  11. Pop the value i32.const n from the stack.

  12. Assert: due to validation, a value of value type i32 is on the top of the stack.

  13. Pop the value i32.const s from the stack.

  14. Assert: due to validation, a value of value type i32 is on the top of the stack.

  15. Pop the value i32.const d from the stack.

  16. If mem.type is limits unshared, then:

    1. If s+n is larger than the length of data.data or d+n is larger than the length of mem.data, then:

      1. Trap.

  17. Else:

    1. Perform the action (rd a.len l) to read the length l of the shared memory instance at memory address a.

    1. If s+n is larger than the length of data.data or d+n is larger than l, then:

      1. Trap.

  18. If n=0, then:

    1. Return.

  19. Let b be the byte data.data[s].

  20. Push the value i32.const d to the stack.

  21. Push the value i32.const b to the stack.

  22. Execute the instruction i32.store8 {offset 0,align 0}.

  23. Assert: due to the earlier check against the memory size, d+1<232.

  24. Push the value i32.const (d+1) to the stack.

  25. Assert: due to the earlier check against the memory size, s+1<232.

  26. Push the value i32.const (s+1) to the stack.

  27. Push the value i32.const (n1) to the stack.

  28. Execute the instruction memory.init x.

 S;F;(i32.const d) (i32.const s) (i32.const n) (memory.init x)actS;F;trap(if(s+n>|S.datas[F.module.dataaddrs[x]].data|d+n>l))S;F;(i32.const d) (i32.const s) (i32.const 0) (memory.init x)actS;F;ϵ(otherwise)S;F;(i32.const d) (i32.const s) (i32.const n+1) (memory.init x)actS;F;(i32.const d) (i32.const b) (i32.store8 {offset 0,align 0})(i32.const d+1) (i32.const s+1) (i32.const n) (memory.init x)(otherwise,ifb=S.datas[F.module.dataaddrs[x]].data[s]) (wherea=F.module.memaddrs[0]mem=S.mems[a]((mem.type=limits unsharedl=|mem.data|act=ϵ)(mem.type=limits sharedact=(rd a.len l)))

data.drop x

  1. Let F be the current frame.

  2. Assert: due to validation, F.module.dataaddrs[x] exists.

  3. Let a be the data address F.module.dataaddrs[x].

  4. Assert: due to validation, S.datas[a] exists.

  5. Replace S.datas[a] with the data instance {data ϵ}.

 S;F;(data.drop x)S;F;ϵ(ifS=Swithdatas[F.module.dataaddrs[x]]={data ϵ})

Atomic Memory Instructions

t.atomic.load(N_u)? memarg

The rules are identical to non-atomic loads, except that ord=seqcst.

t.atomic.storeN? memarg

The rules are identical to non-atomic stores, except that ord=seqcst.

t.atomic.rmw(N_u)?.atop memarg

  1. If N is not part of the instruction, then:

    1. Let N be the bit width |t| of value type t.

  1. Assert: due to validation, a value of value type t is on the top of the stack.

  2. Pop the value t.const c2 from the stack.

  3. Assert: due to validation, a value of value type i32 is on the top of the stack.

  4. Pop the value i32.const i from the stack.

  5. Let ea be i+memarg.offset.

  6. If ea modulo N/8 is not equal to 0, then:

    1. Trap.

  7. Let F be the current frame.

  8. Assert: due to validation, F.module.memaddrs[0] exists.

  9. Let a be the memory address F.module.memaddrs[0].

  10. Assert: due to validation, S.mems[a] exists.

  11. Let mem be the memory instance S.mems[a].

  12. If mem.type is limits unshared, then:

    1. If ea+N/8 is larger than the length of mem.data, then:

      1. Trap.

    2. Let br be the byte sequence mem.data[ea:N/8].

  1. Else:

    1. Perform the action (rd a.len n) to read the length n of the shared memory instance at memory address a.

    2. If ea+N/8 is larger than n, then:

      1. Trap.

    3. Let br be chosen to represent N/8 bytes of memory at location ea of mem.

  2. Let cr be the integer for which bytesiN(n)=br.

  3. Let c1 be the result of computing extendN,|t|u(cr).

  4. Let c be the result of computing atopt(c1,c2).

  5. Let cw be the result of computing wrap|t|,N(c).

  6. Let bw be the byte sequence bytesiN(cw).

  7. If mem.type is limits unshared, then:

    1. Replace the bytes mem.data[ea:N/8] with bw.

  1. Else:

    1. Perform the atomic action (rmwseqcst a.data[ea] br bw) to read N/8 bytes br from data offset ea of the shared memory instance at memory address a and replace them with bytes bw.

  1. Push the value t.const c to the stack.

S;F;(i32.const i) (t.const c2) (t.atomic.rmw(N_u)?.atop memarg)S;F;(t.const c1)(ifmem.type=limits unsharedea+N/8|mem.data|eamodN/8=0c1=extendN,|t|u(bytesiN1(mem.data[ea:N/8]))S=Swithmems[a].data[ea:N/8]=bytesiN(wrap|t|,N(atopt(c1,c2))))S;F;(i32.const k) (t.const c) (t.atomic.rmw(N_u)?.atop memarg)S;F;trap(ifmem.type=limits unsharedea+N/8>|mem.data|eamodN/80) S;F;(i32.const i) (t.const c2) (t.atomic.rmw(N_u)?.atop memarg)(rd a.len n) (rmw a.data[ea] br bw)S;F;(t.const c1)(ifmem.type=limits sharedea+N/8neamodN/8=0c1=extendN,|t|u(bytesiN1(br))bw=bytesiN(wrap|t|,N(atopt(c1,c2))))S;F;(i32.const k) (t.const c) (t.atomic.rmw(N_u)?.atop memarg)(rd a.len n)S;F;trap(ifmem.type=limits sharedea+N/8>neamodN/80) (whereN=|t|ifN not presenta=F.module.memaddrs[0]mem=S.mems[a]ea=i+memarg.offset)

t.atomic.rmw(N_u)?.cmpxchg memarg

  1. If N is not part of the instruction, then:

    1. Let N be the bit width |t| of value type t.

  1. Assert: due to validation, two values of value type t are on the top of the stack.

  2. Pop the value t.const c3 from the stack.

  3. Pop the value t.const c2 from the stack.

  4. Assert: due to validation, a value of value type i32 is on the top of the stack.

  5. Pop the value i32.const i from the stack.

  6. Let ea be i+memarg.offset.

  7. If ea modulo N/8 is not equal to 0, then:

    1. Trap.

  8. Let F be the current frame.

  9. Assert: due to validation, F.module.memaddrs[0] exists.

  10. Let a be the memory address F.module.memaddrs[0].

  11. Let mem be the memory instance S.mems[a].

  12. If mem.type is limits unshared, then:

    1. If ea+N/8 is larger than the length of mem.data, then:

      1. Trap.

    2. Let br be the byte sequence mem.data[ea:N/8].

  13. Else:

    1. Perform the action (rd a.len n) to read the length n of the shared memory instance at memory address a.

    2. If ea+N/8 is larger than n, then:

      1. Trap.

    3. Let br be chosen to represent N/8 bytes of memory at location ea of mem.

  14. Let cr be the integer for which bytesiN(n)=br.

  15. Let cex be the result of computing wrap|t|,N(c2).

  16. If cr equals cex, then:

    1. Let cw be the result of computing wrap|t|,N(c3).

    2. Let bw be the byte sequence bytesiN(cw).

    3. If mem.type is limits unshared, then:

      1. Replace the bytes mem.data[ea:N/8] with bw.

    4. Else:

      1. Perform the atomic action (rmwseqcst a.data[ea] br bw) to read N/8 bytes br from data offset ea of the shared memory instance at memory address a and replace them with bytes bw.

  17. Else:

    1. If mem.type is limits shared, then:

      1. Perform the action (rdseqcst a.data[ea] br) to read N/8 bytes br from data offset ea of the shared memory instance at memory address a.

  18. Let c1 be the result of computing extendN,|t|u(cr).

  19. Push the value t.const c1 to the stack.

S;F;(i32.const i) (t.const c2) (t.const c3) (t.atomic.rmw(N_u)?.cmpxchg memarg)S;F;(t.const (extendN,|t|u(cr)))(ifmem.type=limits unsharedea+N/8|mem.data|eamodN/8=0cr=bytesiN1(mem.data[ea:N/8])cex=wrap|t|,N(c2)((cr=cexc=wrap|t|,N(c3))(crcexc=cr))S=Swithmems[a].data[ea:N/8]=bytesiN(c))S;F;(i32.const i) (t.const c2) (t.const c3) (t.atomic.rmw(N_u)?.cmpxchg memarg)S;F;trap(ifmem.type=limits unsharedea+N/8>|mem.data|eamodN/80) S;F;(i32.const i) (t.const c2) (t.const c3) (t.atomic.rmw(N_u)?.cmpxchg memarg)(rd a.len n) (rmw a.data[ea] br bw)S;F;(t.const c)(ifmem.type=limits sharedea+N/8neamodN/8=0cr=bytesiN1(br)cex=wrap|t|,N(c2)cr=cexbw=bytesiN(wrap|t|,N(c3)))S;F;(i32.const i) (t.const c2) (t.const c3) (t.atomic.rmw(N_u)?.cmpxchg memarg)(rd a.len n) (rdseqcst a.data[ea] br)S;F;(t.const c)(ifmem.type=limits sharedea+N/8neamodN/8=0cr=bytesiN1(br)cex=wrap|t|,N(c2)crcexS;F;(i32.const i) (t.const c2) (t.const c3) (t.atomic.rmw(N_u)?.cmpxchg memarg)(rd a.len n)S;F;trap(ifmem.type=limits sharedea+N/8>neamodN/80) (whereN=|t|ifN not presenta=F.module.memaddrs[0]mem=S.mems[a]ea=i+memarg.offset)

memory.atomic.notify memarg

  1. Let N be 32.

  1. Assert: due to validation, a value of value type i32 is on the top of the stack.

  2. Pop the value i32.const k from the stack.

  1. Assert: due to validation, a value of value type i32 is on the top of the stack.

  2. Pop the value i32.const i from the stack.

  3. Let ea be i+memarg.offset.

  4. If ea modulo N/8 is not equal to 0, then:

    1. Trap.

  5. Let F be the current frame.

  6. Assert: due to validation, F.module.memaddrs[0] exists.

  7. Let a be the memory address F.module.memaddrs[0].

  8. Let mem be the memory instance S.mems[a].

  9. If mem.type is limits unshared, then:

    1. If ea+N/8 is larger than the length of mem.data, then:

      1. Trap.

    2. Else:

      1. Push the value t.const 0 to the stack.

  10. Else:

    1. Perform the action (rd a.len n) to read the length n of the shared memory instance at memory address a.

    2. If ea+N/8 is larger than n, then:

      1. Trap.

    3. Else:

      1. Perform the action (notify a.data[ea] n k) to notify n threads (up to k) waiting at data offset ea of the shared memory instance at memory address a.

      2. Push the value t.const n to the stack.

S;F;(i32.const i) (i32.const k) memory.atomic.notify memargS;F;(i32.const 0)(ifmem.type=limits unsharedea+N/8|mem.data|eamodN/8=0)S;F;(i32.const i) (i32.const k) memory.atomic.notify memargS;F;trap(ifmem.type=limits unshared(ea+N/8>|mem.data|  eamod N/80)) S;F;(i32.const i) (i32.const k) memory.atomic.notify memarg(rd a.len n) (notify a.data[ea] j k)S;F;(i32.const j)(ifmem.type=limits sharedea+N/8njkeamodN/8=0)S;F;(i32.const i) (i32.const k) memory.atomic.notify memarg(rd a.len n)S;F;trap(ifmem.type=limits shared(ea+N/8>n  eamodN/80)) (wherea=F.module.memaddrs[0]mem=S.mems[a]ea=i+memarg.offsetN=32)

memory.atomic.waitN memarg

  1. Assert: due to validation, a value of value type i64 is on the top of the stack.

  2. Pop the value i64.const k from the stack.

  1. Assert: due to validation, a value of value type iN is on the top of the stack.

  1. Pop the value iN.const c from the stack.

  2. Assert: due to validation, a value of value type i32 is on the top of the stack.

  3. Pop the value i32.const i from the stack.

  4. Let ea be i+memarg.offset.

  5. If ea modulo N/8 is not equal to 0, then:

    1. Trap.

  6. Let F be the current frame.

  7. Assert: due to validation, F.module.memaddrs[0] exists.

  8. Let a be the memory address F.module.memaddrs[0].

  9. Let mem be the memory instance S.mems[a].

  10. If mem.type is limits unshared, then:

    1. Trap.

  11. Else:

    1. Perform the action (rd a.len n) to read the length n of the shared memory instance at memory address a.

    2. If ea+N/8 is larger than n, then:

      1. Trap.

    3. Perform the action (rdseqcst a.data[ea] b) to read N/8 bytes b from data offset ea of the shared memory instance at memory address a.

    4. If bytesiN(c) is equal to b then:

      1. Let t be signedN(k).

      2. Perform the action (wait a.data[ea] t) to register the current thread as waiting for a signal at data offset ea of the shared memory instance at memory address a.

      3. Execute the instruction wait a.data[ea] k.

    5. Else:

      1. Push the value t.const 1 to the stack.

S;F;(i32.const i) (iN.const c) (i64.const k) memory.atomic.waitN memargS;F;trap(ifmem.type=limits unshared) S;F;(i32.const i) (iN.const c) (i64.const k) memory.atomic.waitN memarg(rd a.len n) (rdseqcst a.data[ea] b) (wait a.data[ea] t)S;F;(wait a.data[ea] k)(ifmem.type=limits sharedea+N/8neamodN/8=0b=bytesiN(c))t=signedN(k)S;F;(i32.const i) (iN.const c) (i64.const k) memory.atomic.waitN memarg(rd a.len n) (rdseqcst a.data[ea] b)S;F;(i32.const 1)(ifmem.type=limits sharedea+N/8neamodN/8=0bbytesiN(c))S;F;(i32.const i) (iN.const c) (i64.const k) memory.atomic.waitN memarg(rd a.len n)S;F;trap(ifmem.type=limits shared(ea+N/8>n  eamodN/80)) (wherea=F.module.memaddrs[0]mem=S.mems[a]ea=i+memarg.offset)

wait loc n

  1. Either the thread has been notified by an (notify loc) action performed in another thread:

    1. Perform the action (woken loc).

    2. Push the value t.const 0 to the stack.

  2. Or:

    1. If n is less than 0:

      1. The thread remains suspended.

    2. Else:

      1. Either the thread’s suspension times out:

        1. Perform the action (timeout loc).

        2. Push the value t.const 2 to the stack.

      2. Or the thread remains suspended.

wait loc n(woken loc)(i32.const 0)wait loc n(timeout loc)(i32.const 2)(ifn0)wait loc nwait loc n

atomic.fence

  1. Perform the action (fenceseqcst).

atomic.fence(fenceseqcst)ϵ

Control Instructions

nop

  1. Do nothing.

nopϵ

unreachable

  1. Trap.

unreachabletrap

block blocktype instr end

  1. Let F be the current frame.

  2. Assert: due to validation, expandF(blocktype) is defined.

  3. Let [t1m][t2n] be the function type expandF(blocktype).

  4. Let L be the label whose arity is n and whose continuation is the end of the block.

  5. Assert: due to validation, there are at least m values on the top of the stack.

  6. Pop the values valm from the stack.

  7. Enter the block valm instr with label L.

 F;valm block bt instr endF;labeln{ϵ} valm instr end(ifexpandF(bt)=[t1m][t2n])

loop blocktype instr end

  1. Let F be the current frame.

  2. Assert: due to validation, expandF(blocktype) is defined.

  3. Let [t1m][t2n] be the function type expandF(blocktype).

  4. Let L be the label whose arity is m and whose continuation is the start of the loop.

  5. Assert: due to validation, there are at least m values on the top of the stack.

  6. Pop the values valm from the stack.

  7. Enter the block valm instr with label L.

 F;valm loop bt instr endF;labelm{loop bt instr end} valm instr end(ifexpandF(bt)=[t1m][t2n])

if blocktype instr1 else instr2 end

  1. Assert: due to validation, a value of value type i32 is on the top of the stack.

  2. Pop the value i32.const c from the stack.

  3. If c is non-zero, then:

    1. Execute the block instruction block blocktype instr1 end.

  4. Else:

    1. Execute the block instruction block blocktype instr2 end.

 (i32.const c) if bt instr1 else instr2 endblock bt instr1 end(ifc0)(i32.const c) if bt instr1 else instr2 endblock bt instr2 end(ifc=0)

br l

  1. Assert: due to validation, the stack contains at least l+1 labels.

  2. Let L be the l-th label appearing on the stack, starting from the top and counting from zero.

  3. Let n be the arity of L.

  4. Assert: due to validation, there are at least n values on the top of the stack.

  5. Pop the values valn from the stack.

  6. Repeat l+1 times:

    1. While the top of the stack is a value, do:

      1. Pop the value from the stack.

    2. Assert: due to validation, the top of the stack now is a label.

    3. Pop the label from the stack.

  7. Push the values valn to the stack.

  8. Jump to the continuation of L.

 labeln{instr} Bl[valn (br l)] endvaln instr

br_if l

  1. Assert: due to validation, a value of value type i32 is on the top of the stack.

  2. Pop the value i32.const c from the stack.

  3. If c is non-zero, then:

    1. Execute the instruction br l.

  4. Else:

    1. Do nothing.

 (i32.const c) (br_if l)(br l)(ifc0)(i32.const c) (br_if l)ϵ(ifc=0)

br_table l lN

  1. Assert: due to validation, a value of value type i32 is on the top of the stack.

  2. Pop the value i32.const i from the stack.

  3. If i is smaller than the length of l, then:

    1. Let li be the label l[i].

    2. Execute the instruction br li.

  4. Else:

    1. Execute the instruction br lN.

 (i32.const i) (br_table l lN)(br li)(ifl[i]=li)(i32.const i) (br_table l lN)(br lN)(if|l|i)

return

  1. Let F be the current frame.

  2. Let n be the arity of F.

  3. Assert: due to validation, there are at least n values on the top of the stack.

  4. Pop the results valn from the stack.

  5. Assert: due to validation, the stack contains at least one frame.

  6. While the top of the stack is not a frame, do:

    1. Pop the top element from the stack.

  7. Assert: the top of the stack is the frame F.

  8. Pop the frame from the stack.

  9. Push valn to the stack.

  10. Jump to the instruction after the original call that pushed the frame.

 framen{F} Bk[valn return] endvaln

call x

  1. Let F be the current frame.

  2. Assert: due to validation, F.module.funcaddrs[x] exists.

  3. Let a be the function address F.module.funcaddrs[x].

  4. Invoke the function instance at address a.

F;(call x)F;(invoke a)(ifF.module.funcaddrs[x]=a)

call_indirect x y

  1. Let F be the current frame.

  2. Assert: due to validation, F.module.tableaddrs[x] exists.

  3. Let ta be the table address F.module.tableaddrs[x].

  4. Assert: due to validation, S.tables[ta] exists.

  5. Let tab be the table instance S.tables[ta].

  6. Assert: due to validation, F.module.types[y] exists.

  7. Let ftexpect be the function type F.module.types[y].

  8. Assert: due to validation, a value with value type i32 is on the top of the stack.

  9. Pop the value i32.const i from the stack.

  10. If i is not smaller than the length of tab.elem, then:

    1. Trap.

  11. Let r be the reference tab.elem[i].

  12. If r is ref.null t, then:

    1. Trap.

  13. Assert: due to validation of table mutation, r is a function reference.

  14. Let ref a be the function reference r.

  15. Assert: due to validation of table mutation, S.funcs[a] exists.

  16. Let f be the function instance S.funcs[a].

  17. Let ftactual be the function type f.type.

  18. If ftactual and ftexpect differ, then:

    1. Trap.

  19. Invoke the function instance at address a.

 S;F;(i32.const i) (call_indirect x y)S;F;(invoke a)(ifS.tables[F.module.tableaddrs[x]].elem[i]=ref aS.funcs[a]=fF.module.types[y]=f.type)S;F;(i32.const i) (call_indirect x y)S;F;trap(otherwise)

Blocks

The following auxiliary rules define the semantics of executing an instruction sequence that forms a block.

Entering instr with label L

  1. Push L to the stack.

  2. Jump to the start of the instruction sequence instr.

Note

No formal reduction rule is needed for entering an instruction sequence, because the label L is embedded in the administrative instruction that structured control instructions reduce to directly.

Exiting instr with label L

When the end of a block is reached without a jump or trap aborting it, then the following steps are performed.

  1. Pop all values val from the top of the stack.

  2. Assert: due to validation, the label L is now on the top of the stack.

  3. Pop the label from the stack.

  4. Push val back to the stack.

  5. Jump to the position after the end of the structured control instruction associated with the label L.

 labeln{instr} val endval

Note

This semantics also applies to the instruction sequence contained in a loop instruction. Therefore, execution of a loop falls off the end, unless a backwards branch is performed explicitly.

Function Calls

The following auxiliary rules define the semantics of invoking a function instance through one of the call instructions and returning from it.

Invocation of function address a

  1. Assert: due to validation, S.funcs[a] exists.

  2. Let f be the function instance, S.funcs[a].

  3. Let [t1n][t2m] be the function type f.type.

  4. Let t be the list of value types f.code.locals.

  5. Let instr end be the expression f.code.body.

  6. Assert: due to validation, n values are on the top of the stack.

  7. Pop the values valn from the stack.

  8. Let F be the frame {module f.module,locals valn (defaultt)}.

  9. Push the activation of F with arity m to the stack.

  10. Let L be the label whose arity is m and whose continuation is the end of the function.

  11. Enter the instruction sequence instr with label L.

 S;valn (invoke a)S;framem{F} labelm{} instr end end(ifS.funcs[a]=ff.type=[t1n][t2m]f.code={type x,locals tk,body instr end}F={module f.module, locals valn (defaultt)k})

Returning from a function

When the end of a function is reached without a jump (i.e., return) or trap aborting it, then the following steps are performed.

  1. Let F be the current frame.

  2. Let n be the arity of the activation of F.

  3. Assert: due to validation, there are n values on the top of the stack.

  4. Pop the results valn from the stack.

  5. Assert: due to validation, the frame F is now on the top of the stack.

  6. Pop the frame from the stack.

  7. Push valn back to the stack.

  8. Jump to the instruction after the original call.

 framen{F} valn endvaln

Host Functions

Invoking a host function has non-deterministic behavior. It may either terminate with a trap or return regularly. However, in the latter case, it must consume and produce the right number and types of WebAssembly values on the stack, according to its function type.

A host function may also modify the store. However, all store modifications must result in an extension of the original store, i.e., they must only modify mutable contents and must not have instances removed. Furthermore, the resulting store must be valid, i.e., all data and code in it is well-typed.

 S;valn (invoke a)S;host [t2m](ifS.funcs[a]={type [t1n][t2m],hostcode hf}

During its execution, a host function call may do any of the following.

  • Instantiate an arbitrary WebAssembly module.

  • Allocate a new host function.

  • Continue execution, possibly spawning a new thread, with no other observable effects.

  • Terminate with a list of values that respects the host function’s type annotation.

  • Terminate with a trap.

 S;F;(host [t])S;F;(frame0{F0} e end) (host [t])(ifinstantiate(S,module,externvalk)=S;F0;e)S;F;(host [t])S;F;(host [t])(ifallochostfunc(S,functype,hostfunc)=S,funcaddr)S;F;(host [t])spawn?S;F;(host [t])S;F;(host [t])S;F;val(ifSval:[t])S;F;(host [t])S;F;trap

Note

A host function can call back into WebAssembly by invoking a function exported from a module. However, the effects of any such call are subsumed by the non-deterministic behavior allowed for the host function.

Expressions

An expression is evaluated relative to a current frame pointing to its containing module instance.

  1. Jump to the start of the instruction sequence instr of the expression.

  2. Execute the instruction sequence.

  3. Assert: due to validation, the top of the stack contains a value.

  4. Pop the value val from the stack.

The value val is the result of the evaluation.

S;F;instractS;F;instr(ifS;F;instr endactS;F;instr end)

Note

Evaluation iterates this reduction rule until reaching a value. Expressions constituting function bodies are executed during function invocation.