WebAssembly Specification Release 1.1 (Draft 2021-12-18) - page 10

 

  Главная      Manuals     WebAssembly Specification Release 1.1 (Draft 2021-12-18)

 

Search            copyright infringement  

 

 

 

 

 

 

 

 

 

 

 

Content      ..     8      9      10      11     ..

 

 

 

WebAssembly Specification Release 1.1 (Draft 2021-12-18) - page 10

 

 

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.

𝑆

;

val

𝑛

(

invoke

𝑎

)

˓

𝑆

;

result

(

if

𝑆.

funcs

[

𝑎

] =

{

type

[

𝑡

𝑛

1

]

[

𝑡

𝑚

2

]

,

hostcode

hf

}

(

𝑆

;

result

)

hf

(

𝑆

;

val

𝑛

))

𝑆

;

val

𝑛

(

invoke

𝑎

)

˓

𝑆

;

val

𝑛

(

invoke

𝑎

)

(

if

𝑆.

funcs

[

𝑎

] =

{

type

[

𝑡

𝑛

1

]

[

𝑡

𝑚

2

]

,

hostcode

hf

}

∧ ⊥ ∈

hf

(

𝑆

;

val

𝑛

))

Here,

hf

(

𝑆

;

val

𝑛

)

denotes the implementation-defined execution of host function

hf

in current store

𝑆

with argu-

ments

val

𝑛

. It yields a set of possible outcomes, where each element is either a pair of a modified store

𝑆

and

a

result

or the special value

indicating divergence. A host function is non-deterministic if there is at least one

argument for which the set of outcomes is not singular.
For a WebAssembly implementation to be

sound

in the presence of host functions, every

host function instance

must be

valid

which means that it adheres to suitable pre- and post-conditions: under a

valid store

𝑆

, and given

arguments

val

𝑛

matching the ascribed parameter types

𝑡

𝑛

1

, executing the host function must yield a non-empty set

of possible outcomes each of which is either divergence or consists of a valid store

𝑆

that is an

extension

of

𝑆

and

a result matching the ascribed return types

𝑡

𝑚

2

. All these notions are made precise in the

Appendix

.

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.

4.4.11 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.

𝑆

;

𝐹

;

instr

*

˓

𝑆

;

𝐹

;

instr

′*

(

if

𝑆

;

𝐹

;

instr

*

end

˓

𝑆

;

𝐹

;

instr

′*

end

)

Note:

Evaluation iterates this reduction rule until reaching a value. Expressions constituting

function

bodies are

executed during function

invocation

.

4.4. Instructions

119

4.5 Modules

For modules, the execution semantics primarily defines

instantiation

which

allocates

instances for a module and

its contained definitions, initializes

tables

and

memories

from contained

element

and

data

segments, and invokes

the

start function

if present. It also includes

invocation

of exported functions.

Instantiation depends on a number of auxiliary notions for

type-checking imports

and

allocating

instances.

4.5.1 External Typing

For the purpose of checking

external values

against

imports

such values are classified by

external types

The

following auxiliary typing rules specify this typing relation relative to a

store

𝑆

in which the referenced instances

live.

func

𝑎

• The store entry

𝑆.

funcs

[

𝑎

]

must exist.

• Then

func

𝑎

is valid with

external type

func

𝑆.

funcs

[

𝑎

]

.

type

.

𝑆

func

𝑎

:

func

𝑆.

funcs

[

𝑎

]

.

type

table

𝑎

• The store entry

𝑆.

tables

[

𝑎

]

must exist.

• Then

table

𝑎

is valid with

external type

table

𝑆.

tables

[

𝑎

]

.

type

.

𝑆

table

𝑎

:

table

𝑆.

tables

[

𝑎

]

.

type

mem

𝑎

• The store entry

𝑆.

mems

[

𝑎

]

must exist.

• Then

mem

𝑎

is valid with

external type

mem

𝑆.

mems

[

𝑎

]

.

type

.

𝑆

mem

𝑎

:

mem

𝑆.

mems

[

𝑎

]

.

type

global

𝑎

• The store entry

𝑆.

globals

[

𝑎

]

must exist.

• Then

global

𝑎

is valid with

external type

global

𝑆.

globals

[

𝑎

]

.

type

.

𝑆

global

𝑎

:

global

𝑆.

globals

[

𝑎

]

.

type

120

Chapter 4. Execution

4.5.2 Value Typing

For the purpose of checking argument

values

against the parameter types of exported

functions

values are classified

by

value types

The following auxiliary typing rules specify this typing relation relative to a

store

𝑆

in which

possibly referenced addresses live.

Numeric Values

𝑡.

const

𝑐

• The value is valid with

number type

𝑡

.

𝑆

𝑡.

const

𝑐

:

𝑡

Null References

ref

.

null

𝑡

• The value is valid with

reference type

𝑡

.

𝑆

ref

.

null

𝑡

:

𝑡

Function References

ref

𝑎

• The

external value

func

𝑎

must be

valid

.

• Then the value is valid with

reference type

funcref

.

𝑆

func

𝑎

:

func

functype

𝑆

ref

𝑎

:

funcref

External References

ref

.

extern

𝑎

• The value is valid with

reference type

externref

.

𝑆

ref

.

extern

𝑎

:

externref

4.5.3 Allocation

New instances of

functions

,

tables

,

memories

and

globals

are

allocated

in a

store

𝑆

, as defined by the following

auxiliary functions.

Functions

1. Let

func

be the

function

to allocate and

moduleinst

its

module instance

.

2. Let

𝑎

be the first free

function address

in

𝑆

.

3. Let

functype

be the

function type

moduleinst

.

types

[

func

.

type

]

.

4. Let

funcinst

be the

function instance

{

type

functype

,

module

moduleinst

,

code

func

}

.

5. Append

funcinst

to the

funcs

of

𝑆

.

6. Return

𝑎

.

4.5. Modules

121

allocfunc

(

𝑆,

func

,

moduleinst

)

=

𝑆

,

funcaddr

where:

funcaddr

=

|

𝑆.

funcs

|

functype

=

moduleinst

.

types

[

func

.

type

]

funcinst

=

{

type

functype

,

module

moduleinst

,

code

func

}

𝑆

=

𝑆

{

funcs

funcinst

}

Host Functions

1. Let

hostfunc

be the

host function

to allocate and

functype

its

function type

.

2. Let

𝑎

be the first free

function address

in

𝑆

.

3. Let

funcinst

be the

function instance

{

type

functype

,

hostcode

hostfunc

}

.

4. Append

funcinst

to the

funcs

of

𝑆

.

5. Return

𝑎

.

allochostfunc

(

𝑆,

functype

,

hostfunc

)

=

𝑆

,

funcaddr

where:

funcaddr

=

|

𝑆.

funcs

|

funcinst

=

{

type

functype

,

hostcode

hostfunc

}

𝑆

=

𝑆

{

funcs

funcinst

}

Note:

Host functions are never allocated by the WebAssembly semantics itself, but may be allocated by the

embedder

.

Tables

1. Let

tabletype

be the

table type

to allocate and

ref

the initialization value.

2. Let

(

{

min

𝑛,

max

𝑚

?

}

reftype

)

be the structure of

table type

tabletype

.

3. Let

𝑎

be the first free

table address

in

𝑆

.

4. Let

tableinst

be the

table instance

{

type

tabletype

,

elem

ref

𝑛

}

with

𝑛

elements set to

ref

.

5. Append

tableinst

to the

tables

of

𝑆

.

6. Return

𝑎

.

alloctable

(

𝑆,

tabletype

,

ref

)

=

𝑆

,

tableaddr

where:

tabletype

=

{

min

𝑛,

max

𝑚

?

}

reftype

tableaddr

=

|

𝑆.

tables

|

tableinst

=

{

type

tabletype

,

elem

ref

𝑛

}

𝑆

=

𝑆

{

tables

tableinst

}

122

Chapter 4. Execution

Memories

1. Let

memtype

be the

memory type

to allocate.

2. Let

{

min

𝑛,

max

𝑚

?

}

be the structure of

memory type

memtype

.

3. Let

𝑎

be the first free

memory address

in

𝑆

.

4. Let

meminst

be the

memory instance

{

type

memtype

,

data

(

0x00

)

𝑛

·

64 Ki

}

that contains

𝑛

pages of zeroed

bytes

.

5. Append

meminst

to the

mems

of

𝑆

.

6. Return

𝑎

.

allocmem

(

𝑆,

memtype

)

=

𝑆

,

memaddr

where:

memtype

=

{

min

𝑛,

max

𝑚

?

}

memaddr

=

|

𝑆.

mems

|

meminst

=

{

type

memtype

,

data

(

0x00

)

𝑛

·

64 Ki

}

𝑆

=

𝑆

{

mems

meminst

}

Globals

1. Let

globaltype

be the

global type

to allocate and

val

the

value

to initialize the global with.

2. Let

𝑎

be the first free

global address

in

𝑆

.

3. Let

globalinst

be the

global instance

{

type

globaltype

,

value

val

}

.

4. Append

globalinst

to the

globals

of

𝑆

.

5. Return

𝑎

.

allocglobal

(

𝑆,

globaltype

,

val

)

=

𝑆

,

globaladdr

where:

globaladdr

=

|

𝑆.

globals

|

globalinst

=

{

type

globaltype

,

value

val

}

𝑆

=

𝑆

{

globals

globalinst

}

Element segments

1. Let

reftype

be the elements’ type and

ref

*

the vector of

references

to allocate.

2. Let

𝑎

be the first free

element address

in

𝑆

.

3. Let

eleminst

be the

element instance

{

type

𝑡,

elem

ref

*

}

.

4. Append

eleminst

to the

elems

of

𝑆

.

5. Return

𝑎

.

allocelem

(

𝑆,

reftype

,

ref

*

)

=

𝑆

,

elemaddr

where:

elemaddr

=

|

𝑆.

elems

|

eleminst

=

{

type

reftype

,

elem

ref

*

}

𝑆

=

𝑆

{

elems

eleminst

}

4.5. Modules

123

Data segments

1. Let

bytes

be the vector of

bytes

to allocate.

2. Let

𝑎

be the first free

data address

in

𝑆

.

3. Let

datainst

be the

data instance

{

data

bytes

}

.

4. Append

datainst

to the

datas

of

𝑆

.

5. Return

𝑎

.

allocdata

(

𝑆,

bytes

)

=

𝑆

,

dataaddr

where:

dataaddr

=

|

𝑆.

datas

|

datainst

=

{

data

bytes

}

𝑆

=

𝑆

{

datas

datainst

}

Growing tables

1. Let

tableinst

be the

table instance

to grow,

𝑛

the number of elements by which to grow it, and

ref

the

initialization value.

2. Let

len

be

𝑛

added to the length of

tableinst

.

elem

.

3. If

len

is larger than or equal to

2

32

, then fail.

4. Let

limits

𝑡

be the structure of

table type

tableinst

.

type

.

5. Let

limits

be

limits

with

min

updated to

len

.

6. If

limits

is not

valid

then fail.

7. Append

ref

𝑛

to

tableinst

.

elem

.

8. Set

tableinst

.

type

to the

table type

limits

𝑡

.

growtable

(

tableinst

, 𝑛,

ref

)

=

tableinst

with

type

=

limits

𝑡

with

elem

=

tableinst

.

elem

ref

𝑛

(

if

len

=

𝑛

+

|

tableinst

.

elem

|

len

<

2

32

limits

𝑡

=

tableinst

.

type

limits

=

limits

with

min

=

len

limits

ok

Growing memories

1. Let

meminst

be the

memory instance

to grow and

𝑛

the number of

pages

by which to grow it.

2. Assert: The length of

meminst

.

data

is divisible by the

page size

64 Ki

.

3. Let

len

be

𝑛

added to the length of

meminst

.

data

divided by the

page size

64 Ki

.

4. If

len

is larger than

2

16

, then fail.

5. Let

limits

be the structure of

memory type

meminst

.

type

.

6. Let

limits

be

limits

with

min

updated to

len

.

7. If

limits

is not

valid

then fail.

8. Append

𝑛

times

64 Ki

bytes

with value

0x00

to

meminst

.

data

.

9. Set

meminst

.

type

to the

memory type

limits

.

124

Chapter 4. Execution

growmem

(

meminst

𝑛

)

=

meminst

with

type

=

limits

with

data

=

meminst

.

data

(

0x00

)

𝑛

·

64 Ki

(

if

len

=

𝑛

+

|

meminst

.

data

|

/

64 Ki

len

2

16

limits

=

meminst

.

type

limits

=

limits

with

min

=

len

limits

ok

Modules

The allocation function for

modules

requires a suitable list of

external values

that are assumed to

match

the

import

vector of the module, a list of initialization

values

for the module’s

globals

and list of

reference

vectors for the

module’s

element segments

.

1. Let

module

be the

module

to allocate and

externval

*

im

the vector of

external values

providing the mod-

ule’s imports,

val

*

the initialization

values

of the module’s

globals

and

(

ref

*

)

*

the

reference

vectors of the

module’s

element segments

.

2. For each

function

func

𝑖

in

module

.

funcs

do:

a. Let

funcaddr

𝑖

be the

function address

resulting from

allocating

func

𝑖

for the

module instance

moduleinst

defined below.

3. For each

table

table

𝑖

in

module

.

tables

do:

a. Let

limits

𝑖

𝑡

𝑖

be the

table type

table

𝑖

.

type

.

b. Let

tableaddr

𝑖

be the

table address

resulting from

allocating

table

𝑖

.

type

with initialization value

ref

.

null

𝑡

𝑖

.

4. For each

memory

mem

𝑖

in

module

.

mems

do:

a. Let

memaddr

𝑖

be the

memory address

resulting from

allocating

mem

𝑖

.

type

.

5. For each

global

global

𝑖

in

module

.

globals

do:

a. Let

globaladdr

𝑖

be the

global address

resulting from

allocating

global

𝑖

.

type

with initializer value

val

*

[

𝑖

]

.

6. For each

element segment

elem

𝑖

in

module

.

elems

do:

a. Let

elemaddr

𝑖

be the

element address

resulting from

allocating

a

element instance

of

reference type

elem

𝑖

.

type

with contents

(

ref

*

)

*

[

𝑖

]

.

7. For each

data segment

data

𝑖

in

module

.

datas

do:

a. Let

dataaddr

𝑖

be the

data address

resulting from

allocating

a

data instance

with contents

data

𝑖

.

init

.

8. Let

funcaddr

*

be the concatenation of the

function addresses

funcaddr

𝑖

in index order.

9. Let

tableaddr

*

be the concatenation of the

table addresses

tableaddr

𝑖

in index order.

10. Let

memaddr

*

be the concatenation of the

memory addresses

memaddr

𝑖

in index order.

11. Let

globaladdr

*

be the concatenation of the

global addresses

globaladdr

𝑖

in index order.

12. Let

elemaddr

*

be the concatenation of the

element addresses

elemaddr

𝑖

in index order.

13. Let

dataaddr

*

be the concatenation of the

data addresses

dataaddr

𝑖

in index order.

14. Let

funcaddr

*

mod

be the list of

function addresses

extracted from

externval

*

im

, concatenated with

funcaddr

*

.

15. Let

tableaddr

*

mod

be the list of

table addresses

extracted from

externval

*

im

, concatenated with

tableaddr

*

.

16. Let

memaddr

*

mod

be the list of

memory addresses

extracted from

externval

*

im

, concatenated with

memaddr

*

.

17. Let

globaladdr

*

mod

be the list of

global addresses

extracted from

externval

*

im

, concatenated with

globaladdr

*

.

4.5. Modules

125

18. For each

export

export

𝑖

in

module

.

exports

do:

a. If

export

𝑖

is a function export for

function index

𝑥

, then let

externval

𝑖

be the

external value

func

(

funcaddr

*

mod

[

𝑥

])

.

b. Else, if

export

𝑖

is a table export for

table index

𝑥

, then let

externval

𝑖

be the

external value

table

(

tableaddr

*

mod

[

𝑥

])

.

c. Else, if

export

𝑖

is a memory export for

memory index

𝑥

, then let

externval

𝑖

be the

external value

mem

(

memaddr

*

mod

[

𝑥

])

.

d. Else, if

export

𝑖

is a global export for

global index

𝑥

, then let

externval

𝑖

be the

external value

global

(

globaladdr

*

mod

[

𝑥

])

.

e. Let

exportinst

𝑖

be the

export instance

{

name

(

export

𝑖

.

name

)

,

value

externval

𝑖

}

.

19. Let

exportinst

*

be the concatenation of the

export instances

exportinst

𝑖

in index order.

20. Let

moduleinst

be the

module instance

{

types

(

module

.

types

)

,

funcaddrs

funcaddr

*

mod

,

tableaddrs

tableaddr

*

mod

,

memaddrs

memaddr

*

mod

,

globaladdrs

globaladdr

*

mod

,

exports

exportinst

*

}

.

21. Return

moduleinst

.

allocmodule

(

𝑆,

module

,

externval

*

im

,

val

*

,

(

ref

*

)

*

)

=

𝑆

,

moduleinst

where:

table

*

=

module

.

tables

mem

*

=

module

.

mems

global

*

=

module

.

globals

elem

*

=

module

.

elems

data

*

=

module

.

datas

export

*

=

module

.

exports

moduleinst

=

{

types

module

.

types

,

funcaddrs

funcs

(

externval

*

im

)

funcaddr

*

,

tableaddrs

tables

(

externval

*

im

)

tableaddr

*

,

memaddrs

mems

(

externval

*

im

)

memaddr

*

,

globaladdrs

globals

(

externval

*

im

)

globaladdr

*

,

elemaddrs

elemaddr

*

,

dataaddrs

dataaddr

*

,

exports

exportinst

*

}

𝑆

1

,

funcaddr

*

=

allocfunc

*

(

𝑆,

module

.

funcs

,

moduleinst

)

𝑆

2

,

tableaddr

*

=

alloctable

*

(

𝑆

1

,

(

table

.

type

)

*

,

(

ref

.

null

𝑡

)

*

)

(

where

(

table

.

type

)

*

(

limits

𝑡

)

*

)

𝑆

3

,

memaddr

*

=

allocmem

*

(

𝑆

2

,

(

mem

.

type

)

*

)

𝑆

4

,

globaladdr

*

=

allocglobal

*

(

𝑆

3

,

(

global

.

type

)

*

,

val

*

)

𝑆

5

,

elemaddr

*

=

allocelem

*

(

𝑆

4

,

(

elem

.

type

)

*

,

(

ref

*

)

*

)

𝑆

,

dataaddr

*

=

allocdata

*

(

𝑆

5

,

(

data

.

init

)

*

)

exportinst

*

=

{

name

(

export

.

name

)

,

value

externval

ex

}

*

funcs

(

externval

*

ex

)

=

(

moduleinst

.

funcaddrs

[

𝑥

])

*

(

where

𝑥

*

=

funcs

(

export

*

))

tables

(

externval

*

ex

)

=

(

moduleinst

.

tableaddrs

[

𝑥

])

*

(

where

𝑥

*

=

tables

(

export

*

))

mems

(

externval

*

ex

)

=

(

moduleinst

.

memaddrs

[

𝑥

])

*

(

where

𝑥

*

=

mems

(

export

*

))

globals

(

externval

*

ex

)

=

(

moduleinst

.

globaladdrs

[

𝑥

])

*

(

where

𝑥

*

=

globals

(

export

*

))

Here, the notation

allocx

*

is shorthand for multiple

allocations

of object kind

𝑋

, defined as follows:

allocx

*

(

𝑆

0

, 𝑋

𝑛

, . . .

)

=

𝑆

𝑛

, 𝑎

𝑛

where for all

𝑖 < 𝑛

:

𝑆

𝑖

+1

, 𝑎

𝑛

[

𝑖

]

=

allocx(

𝑆

𝑖

, 𝑋

𝑛

[

𝑖

]

, . . .

)

Moreover, if the dots

. . .

are a sequence

𝐴

𝑛

(as for globals or tables), then the elements of this sequence are passed

to the allocation function pointwise.

126

Chapter 4. Execution

Note:

The definition of module allocation is mutually recursive with the allocation of its associated functions,

because the resulting module instance

moduleinst

is passed to the function allocator as an argument, in order to

form the necessary closures. In an implementation, this recursion is easily unraveled by mutating one or the other

in a secondary step.

4.5.4 Instantiation

Given a

store

𝑆

, a

module

module

is instantiated with a list of

external values

externval

𝑛

supplying the required

imports as follows.
Instantiation checks that the module is

valid

and the provided imports

match

the declared types, and may

fail

with

an error otherwise. Instantiation can also result in a

trap

from executing the start function. It is up to the

embedder

to define how such conditions are reported.

1. If

module

is not

valid

then:

a. Fail.

2. Assert:

module

is

valid

with

external types

externtype

𝑚

im

classifying its

imports

.

3. If the number

𝑚

of

imports

is not equal to the number

𝑛

of provided

external values

then:

a. Fail.

4. For each

external value

externval

𝑖

in

externval

𝑛

and

external type

externtype

𝑖

in

externtype

𝑛

im

, do:

a. If

externval

𝑖

is not

valid

with an

external type

externtype

𝑖

in store

𝑆

, then:

i. Fail.

b. If

externtype

𝑖

does not

match

externtype

𝑖

, then:

i. Fail.

5. Let

moduleinst

init

be the auxiliary module

instance

{

globaladdrs

globals

(

externval

𝑛

)

,

funcaddrs

moduleinst

.

funcaddrs

}

that only consists of the imported globals and the imported and allocated functions from the final module

instance

moduleinst

defined below.

6. Let

𝐹

init

be the auxiliary

frame

{

module

moduleinst

init

,

locals

𝜖

}

.

7. Push the frame

𝐹

init

to the stack.

8. Let

val

*

be the vector of

global

initialization

values

determined by

module

and

externval

𝑛

. These may be

calculated as follows.

a. For each

global

global

𝑖

in

module

.

globals

do:

i. Let

val

𝑖

be the result of

evaluating

the initializer expression

global

𝑖

.

init

.

b. Assert: due to

validation

the frame

𝐹

init

is now on the top of the stack.

c. Let

val

*

be the concatenation of

val

𝑖

in index order.

9. Let

(

ref

*

)

*

be the list of

reference

vectors determined by the

element segments

in

module

These may be

calculated as follows.

a. For each

element segment

elem

𝑖

in

module

.

elems

and for each element

expression

expr

𝑖𝑗

in

elem

𝑖

.

init

do:

i. Let

ref

𝑖𝑗

be the result of

evaluating

the initializer expression

expr

𝑖𝑗

.

b. Let

ref

*

𝑖

be the concatenation of function elements

ref

𝑖𝑗

in order of index

𝑗

.

c. Let

(

ref

*

)

*

be the concatenation of function element vectors

ref

*

𝑖

in order of index

𝑖

.

10. Pop the frame

𝐹

init

from the stack.

4.5. Modules

127

11. Let

moduleinst

be a new module instance

allocated

from

module

in store

𝑆

with imports

externval

𝑛

, global

initializer values

val

*

, and element segment contents

(

ref

*

)

*

, and let

𝑆

be the extended store produced by

module allocation.

12. Let

𝐹

be the auxiliary

frame

{

module

moduleinst

,

locals

𝜖

}

.

13. Push the frame

𝐹

to the stack.

14. For each

element segment

elem

𝑖

in

module

.

elems

whose

mode

is of the form

active

{

table

tableidx

𝑖

,

offset

einstr

*

𝑖

end

}

, do:

a. Assert:

tableidx

𝑖

is

0

.

b. Let

𝑛

be the length of the vector

elem

𝑖

.

init

.

c.

Execute

the instruction sequence

einstr

*

𝑖

.

d.

Execute

the instruction

i32

.

const

0

.

e.

Execute

the instruction

i32

.

const

𝑛

.

f.

Execute

the instruction

table

.

init

𝑖

.

g.

Execute

the instruction

elem

.

drop

𝑖

.

15. For

each

data

segment

data

𝑖

in

module

.

datas

whose

mode

is

of

the

form

active

{

memory

memidx

𝑖

,

offset

dinstr

*

𝑖

end

}

, do:

a. Assert:

memidx

𝑖

is

0

.

b. Let

𝑛

be the length of the vector

data

𝑖

.

init

.

c.

Execute

the instruction sequence

dinstr

*

𝑖

.

d.

Execute

the instruction

i32

.

const

0

.

e.

Execute

the instruction

i32

.

const

𝑛

.

f.

Execute

the instruction

memory

.

init

𝑖

.

g.

Execute

the instruction

data

.

drop

𝑖

.

16. If the

start function

module

.

start

is not empty, then:

a. Let

start

be the

start function

module

.

start

.

b.

Execute

the instruction

call

start

.

func

.

17. Assert: due to

validation

the frame

𝐹

is now on the top of the stack.

18. Pop the frame

𝐹

from the stack.

128

Chapter 4. Execution

instantiate

(

𝑆,

module

,

externval

𝑘

)

=

𝑆

;

𝐹

; runelem

0

(

elem

𝑛

[0])

. . .

runelem

𝑛

1

(

elem

𝑛

[

𝑛

1])

rundata

0

(

data

𝑚

[0])

. . .

rundata

𝑚

1

(

data

𝑚

[

𝑚

1])

(

call

start

.

func

)

?

(

if

module

:

externtype

𝑘

im

externtype

*

ex

(

𝑆

externval

:

externtype

)

𝑘

(

externtype

externtype

im

)

𝑘

module

.

globals

=

global

*

module

.

elems

=

elem

𝑛

module

.

datas

=

data

𝑚

module

.

start

=

start

?

(

expr

g

=

global

.𝐺𝐼𝑁 𝐼𝑇

)

*

(

expr

*

e

=

elem

.𝐸𝐼𝑁 𝐼𝑇

)

𝑛

𝑆

,

moduleinst

=

allocmodule

(

𝑆,

module

,

externval

𝑘

,

val

*

,

(

ref

*

)

𝑛

)

𝐹

=

{

module

moduleinst

,

locals

𝜖

}

(

𝑆

;

𝐹

;

expr

g

˓

*

𝑆

;

𝐹

;

val

end

)

*

((

𝑆

;

𝐹

;

expr

e

˓

*

𝑆

;

𝐹

;

ref

end

)

*

)

𝑛

(

tableaddr

=

moduleinst

.

tableaddrs

[

elem

.

table

])

*

(

memaddr

=

moduleinst

.

memaddrs

[

data

.

memory

])

*

(

funcaddr

=

moduleinst

.

funcaddrs

[

start

.

func

])

?

)

where:

runelem

𝑖

(

{

type

et

,

init

ref

𝑛

,

mode passive

}

)

=

𝜖

runelem

𝑖

(

{

type

et

,

init

ref

𝑛

,

mode active

{

table

0

,

offset

instr

*

end

}}

)

=

instr

*

(

i32

.

const

0) (

i32

.

const

𝑛

(

table

.

init

𝑖

(

elem

.

drop

𝑖

)

runelem

𝑖

(

{

type

et

,

init

ref

𝑛

,

mode declarative

}

)

=

(

elem

.

drop

𝑖

)

rundata

𝑖

(

{

init

𝑏

𝑛

, 𝐷𝑀 𝑂𝐷𝐸

passive

}

)

=

𝜖

rundata

𝑖

(

{

init

𝑏

𝑛

, 𝐷𝑀 𝑂𝐷𝐸

active

{

memory

0

,

offset

instr

*

end

}}

)

=

instr

*

(

i32

.

const

0) (

i32

.

const

𝑛

(

memory

.

init

𝑖

(

data

.

drop

𝑖

)

Note:

Module

allocation

and the

evaluation

of

global

initializers and

element segments

are mutually recursive be-

cause the global initialization

values

val

*

and element segment contents

(

ref

*

)

*

are passed to the module allocator

while depending on the module instance

moduleinst

and store

𝑆

returned by allocation. However, this recursion

is just a specification device. In practice, the initialization values can

be determined

beforehand by staging module

allocation such that first, the module’s own

𝑓 𝑢𝑛𝑐𝑡𝑖𝑜𝑛𝑖𝑛𝑠𝑡𝑎𝑛𝑐𝑒𝑠 < 𝑠𝑦𝑛𝑡𝑎𝑥

𝑓 𝑢𝑛𝑐𝑖𝑛𝑠𝑡 >

are pre-allocated in the

store, then the initializer expressions are evaluated, then the rest of the module instance is allocated, and finally the

new function instances’

module

fields are set to that module instance. This is possible because

validation

ensures

that initialization expressions cannot actually call a function, only take their reference.
All failure conditions are checked before any observable mutation of the store takes place. Store mutation is not

atomic; it happens in individual steps that may be interleaved with other threads.

Evaluation

of

constant expressions

does not affect the store.

4.5.5 Invocation

Once a

module

has been

instantiated

any exported function can be

invoked

externally via its

function address

funcaddr

in the

store

𝑆

and an appropriate list

val

*

of argument

values

.

Invocation may

fail

with an error if the arguments do not fit the

function type

Invocation can also result in a

trap

.

It is up to the

embedder

to define how such conditions are reported.

Note:

If the

embedder

API performs type checks itself, either statically or dynamically, before performing an

invocation, then no failure other than traps can occur.

4.5. Modules

129

The following steps are performed:

1. Assert:

𝑆.

funcs

[

funcaddr

]

exists.

2. Let

funcinst

be the

function instance

𝑆.

funcs

[

funcaddr

]

.

3. Let

[

𝑡

𝑛

1

]

[

𝑡

𝑚

2

]

be the

function type

funcinst

.

type

.

4. If the length

|

val

*

|

of the provided argument values is different from the number

𝑛

of expected arguments,

then:

a. Fail.

5. For each

value type

𝑡

𝑖

in

𝑡

𝑛

1

and corresponding

value

𝑣𝑎𝑙

𝑖

in

val

*

, do:

a. If

val

𝑖

is not

valid

with value type

𝑡

𝑖

, then:

i. Fail.

6. Let

𝐹

be the dummy

frame

{

module

{}

,

locals

𝜖

}

.

7. Push the frame

𝐹

to the stack.

8. Push the values

val

*

to the stack.

9.

Invoke

the function instance at address

funcaddr

.

Once the function has returned, the following steps are executed:

1. Assert: due to

validation

,

𝑚

values

are on the top of the stack.

2. Pop

val

𝑚

res

from the stack.

The values

val

𝑚

res

are returned as the results of the invocation.

invoke

(

𝑆,

funcaddr

,

val

𝑛

)

=

𝑆

;

𝐹

;

val

𝑛

(

invoke

funcaddr

)

(

if

𝑆.

funcs

[

funcaddr

]

.

type

= [

𝑡

𝑛

1

]

[

𝑡

𝑚

2

]

(

𝑆

val

:

𝑡

1

)

𝑛

𝐹

=

{

module

{}

,

locals

𝜖

}

)

130

Chapter 4. Execution

CHAPTER

5

Binary Format

5.1 Conventions

The binary format for WebAssembly

modules

is a dense linear

encoding

of their

abstract syntax

.

27

The format is defined by an

attribute grammar

whose only terminal symbols are

bytes

A byte sequence is a

well-formed encoding of a module if and only if it is generated by the grammar.
Each production of this grammar has exactly one synthesized attribute: the abstract syntax that the respective byte

sequence encodes. Thus, the attribute grammar implicitly defines a

decoding

function (i.e., a parsing function for

the binary format).
Except for a few exceptions, the binary grammar closely mirrors the grammar of the abstract syntax.

Note:

Some phrases of abstract syntax have multiple possible encodings in the binary format. For example,

numbers may be encoded as if they had optional leading zeros. Implementations of decoders must support all

possible alternatives; implementations of encoders can pick any allowed encoding.

The recommended extension for files containing WebAssembly modules in binary format is “

.

wasm

” and the rec-

ommended

Media Type

26

is “

application

/

wasm

”.

5.1.1 Grammar

The following conventions are adopted in defining grammar rules for the binary format. They mirror the conven-

tions used for

abstract syntax

In order to distinguish symbols of the binary syntax from symbols of the abstract

syntax,

typewriter

font is adopted for the former.

• Terminal symbols are

bytes

expressed in hexadecimal notation:

0x0F

.

• Nonterminal symbols are written in typewriter font:

valtype

,

instr

.

𝐵

𝑛

is a sequence of

𝑛

0

iterations of

𝐵

.

𝐵

*

is a possibly empty sequence of iterations of

𝐵

. (This is a shorthand for

𝐵

𝑛

used where

𝑛

is not relevant.)

27

Additional encoding layers – for example, introducing compression – may be defined on top of the basic representation defined here.

However, such layers are outside the scope of the current specification.

26

https://www.iana.org/assignments/media-types/media-types.xhtml

131

𝐵

?

is an optional occurrence of

𝐵

. (This is a shorthand for

𝐵

𝑛

where

𝑛

1

.)

𝑥

:

𝐵

denotes the same language as the nonterminal

𝐵

, but also binds the variable

𝑥

to the attribute synthesized

for

𝐵

.

• Productions are written

sym

::=

𝐵

1

𝐴

1

|

. . .

|

𝐵

𝑛

𝐴

𝑛

, where each

𝐴

𝑖

is the attribute that is

synthesized for

sym

in the given case, usually from attribute variables bound in

𝐵

𝑖

.

• Some productions are augmented by side conditions in parentheses, which restrict the applicability of the

production. They provide a shorthand for a combinatorial expansion of the production into many separate

cases.

• If the same meta variable or non-terminal symbol appears multiple times in a production (in the syntax or

in an attribute), then all those occurrences must have the same instantiation. (This is a shorthand for a side

condition requiring multiple different variables to be equal.)

Note:

For example, the

binary grammar

for

number types

is given as follows:

numtype

::=

0x7F

i32

|

0x7E

i64

|

0x7D

f32

|

0x7C

f64

Consequently, the byte

0x7F

encodes the type

i32

,

0x7E

encodes the type

i64

and so forth. No other byte value is

allowed as the encoding of a number type.
The

binary grammar

for

limits

is defined as follows:

limits

::=

0x00

𝑛

:

u32

{

min

𝑛,

max

𝜖

}

|

0x01

𝑛

:

u32

𝑚

:

u32

{

min

𝑛,

max

𝑚

}

That is, a limits pair is encoded as either the byte

0x00

followed by the encoding of a

u32

value, or the byte

0x01

followed by two such encodings. The variables

𝑛

and

𝑚

name the attributes of the respective

u32

nonterminals,

which in this case are the actual

unsigned integers

those decode into. The attribute of the complete production then

is the abstract syntax for the limit, expressed in terms of the former values.

5.1.2 Auxiliary Notation

When dealing with binary encodings the following notation is also used:

𝜖

denotes the empty byte sequence.

||

𝐵

||

is the length of the byte sequence generated from the production

𝐵

in a derivation.

5.1.3 Vectors

Vectors

are encoded with their

u32

length followed by the encoding of their element sequence.

vec

(

B

)

::=

𝑛

:

u32

(

𝑥

:

B

)

𝑛

𝑥

𝑛

132

Chapter 5. Binary Format

5.2 Values

5.2.1 Bytes

Bytes

encode themselves.

byte

::=

0x00

0x00

|

. . .

|

0xFF

0xFF

5.2.2 Integers

All

integers

are encoded using the

LEB128

28

variable-length integer encoding, in either unsigned or signed variant.

Unsigned integers

are encoded in

unsigned LEB128

29

format. As an additional constraint, the total number of

bytes encoding a value of type

u

𝑁

must not exceed

ceil(

𝑁/

7)

bytes.

u

𝑁

::=

𝑛

:

byte

𝑛

(

if

𝑛 <

2

7

𝑛 <

2

𝑁

)

|

𝑛

:

byte

𝑚

:

u

(

𝑁

7

)

2

7

·

𝑚

+ (

𝑛

2

7

)

(

if

𝑛

2

7

𝑁 >

7)

Signed integers

are encoded in

signed LEB128

30

format, which uses a two’s complement representation. As an

additional constraint, the total number of bytes encoding a value of type

s

𝑁

must not exceed

ceil(

𝑁/

7)

bytes.

s

𝑁

::=

𝑛

:

byte

𝑛

(

if

𝑛 <

2

6

𝑛 <

2

𝑁

1

)

|

𝑛

:

byte

𝑛

2

7

(

if

2

6

𝑛 <

2

7

𝑛

2

7

2

𝑁

1

)

|

𝑛

:

byte

𝑚

:

s

(

𝑁

7

)

2

7

·

𝑚

+ (

𝑛

2

7

)

(

if

𝑛

2

7

𝑁 >

7)

Uninterpreted integers

are encoded as signed integers.

i

𝑁

::=

𝑛

:

s

𝑁

𝑖

(

if

𝑛

=

signed

i

𝑁

(

𝑖

))

Note:

The side conditions

𝑁 >

7

in the productions for non-terminal bytes of the

u

and

s

encodings restrict

the encoding’s length. However, “trailing zeros” are still allowed within these bounds. For example,

0x03

and

0x83 0x00

are both well-formed encodings for the value

3

as a

u8

Similarly, either of

0x7e

and

0xFE 0x7F

and

0xFE 0xFF 0x7F

are well-formed encodings of the value

2

as a

s16

.

The side conditions on the value

𝑛

of terminal bytes further enforce that any unused bits in these bytes must be

0

for positive values and

1

for negative ones. For example,

0x83 0x10

is malformed as a

u8

encoding. Similarly,

both

0x83 0x3E

and

0xFF 0x7B

are malformed as

s8

encodings.

5.2.3 Floating-Point

Floating-point

values are encoded directly by their

IEEE 754-2019

31

(Section 3.4) bit pattern in

little endian

32

byte

order:

f

𝑁

::=

𝑏

*

:

byte

𝑁/

8

bytes

1

f

𝑁

(

𝑏

*

)

28

https://en.wikipedia.org/wiki/LEB128

29

https://en.wikipedia.org/wiki/LEB128#Unsigned_LEB128

30

https://en.wikipedia.org/wiki/LEB128#Signed_LEB128

31

https://ieeexplore.ieee.org/document/8766229

32

https://en.wikipedia.org/wiki/Endianness#Little-endian

5.2. Values

133

5.2.4 Names

Names

are encoded as a

vector

of bytes containing the

Unicode

33

(Section 3.9) UTF-8 encoding of the name’s

character sequence.

name

::=

𝑏

*

:

vec

(

byte

)

name

(

if

utf8

(

name

=

𝑏

*

)

The auxiliary

utf8

function expressing this encoding is defined as follows:

utf8

(

𝑐

*

)

=

(

utf8

(

𝑐

))

*

utf8

(

𝑐

)

=

𝑏

(

if

𝑐 <

U+80

𝑐

=

𝑏

)

utf8

(

𝑐

)

=

𝑏

1

𝑏

2

(

if

U+80

𝑐 <

U+800

𝑐

= 2

6

(

𝑏

1

0xC0

) + (

𝑏

2

0x80

))

utf8

(

𝑐

)

=

𝑏

1

𝑏

2

𝑏

3

(

if

U+800

𝑐 <

U+D800

U+E000

𝑐 <

U+10000

𝑐

= 2

12

(

𝑏

1

0xE0

) + 2

6

(

𝑏

2

0x80

) + (

𝑏

3

0x80

))

utf8

(

𝑐

)

=

𝑏

1

𝑏

2

𝑏

3

𝑏

4

(

if

U+10000

𝑐 <

U+110000

𝑐

= 2

18

(

𝑏

1

0xF0

) + 2

12

(

𝑏

2

0x80

) + 2

6

(

𝑏

3

0x80

) + (

𝑏

4

0x80

))

where

𝑏

2

, 𝑏

3

, 𝑏

4

<

0xC0

Note:

Unlike in some other formats, name strings are not 0-terminated.

5.3 Types

Note:

In some places, possible types include both type constructors or types denoted by

type indices

Thus, the

binary format for type constructors corresponds to the encodings of small negative

s

𝑁

values, such that they can

unambiguously occur in the same place as (positive) type indices.

5.3.1 Number Types

Number types

are encoded by a single byte.

numtype

::=

0x7F

i32

|

0x7E

i64

|

0x7D

f32

|

0x7C

f64

5.3.2 Vector Types

Vector types

are also encoded by a single byte.

vectype

::=

0x7B

v128

33

https://www.unicode.org/versions/latest/

134

Chapter 5. Binary Format

 

 

 

 

 

 

 

Content      ..     8      9      10      11     ..