elem: Wasm definition
The elem definition declares an element segment, which is a series of references that can be copied into a Wasm table. They provide a way to initialize a table on instantiation, analogous to data segments for Wasm memories.
Try it
(module
;; table with 2 slots
(table $return_values 2 funcref)
;; Define functions
(func $f1 (result i32)
i32.const 42
)
(func $f2 (result i32)
i32.const 100
)
;; initialize table slots actively
(elem (table $return_values) (offset i32.const 0) func $f1 $f2)
(func (export "accessTable") (param $index i32) (result i32)
(local.get $index)
(call_indirect (result i32))
)
)
WebAssembly.instantiateStreaming(fetch("{%wasm-url%}")).then((result) => {
const value = result.instance.exports.accessTable(1);
console.log(value);
});
In the above example, we define a table with two slots, define two functions, then initialize the table immediately using an elem definition written in the active form, specifying the index value of the table. We then declare and export a function called accessTable(), which calls one of the functions referenced in our table, specifying the element number to call as its parameter. We invoke that function in JavaScript, then log the returned value to the console.
Syntax
;; Active form, table initialized on instantiation elem name table_identifier offset value_type element_list ;; Passive form, initialized later via table.init elem name value_type element_list ;; Declarative form, declares already existing reference(s) elem name declare value_type element_list
elem-
The
elemdefinition type. Must always be included first. nameOptional-
An optional identifying name for the elem. This must begin with a
$symbol, for example$my_table. If this is omitted, theelemcan be identified (for example when callingelem.drop) by its index, for example0for the firstelemin the wasm module,1for the second, etc. table_identifierOptional-
An identifier representing the
tableinstance to place the table elements into, which must be preceded by thetablekeyword to be interpreted as atable_identifier. This can be one of:name-
An identifying name set for the
tablewhen it was first defined. This must begin with a$symbol and be preceded by atablekeyword, for example(table $my_table). index-
An
i32value representing the index number of the table, for example(table 0)for the first table in the module,(table 1)for the second, etc.
Note: When writing an active form
elemdefinition, theoffsetmust be included, but thetable_identifiercan be omitted, in which case it defaults to(table 0). offsetOptional-
An integer representing the offset at which to start placing the elements into the
table. This value can be any constant expression, meaning that it can include structures like arithmetic expressions as well as numeric values.The full syntax includes the
offsetkeyword before the value, for example(offset i32.const 0), although the keyword can be omitted in the abbreviated form, for example(i32.const 0). declareOptional-
A keyword that identifies the
elemdefinition as being of the declarative form, meaning that it declares references that will be used at runtime (for example, byref.func), without them being inserted into a table. value_type-
A value type that defines which type of reference will be stored in this table. All references in the
element_listmust match this type. The value can be any reference type, such as:func-
An abbreviation that more concisely declares a list of non-nullable function references. For example
func $my_funcis equivalent to(ref func) (ref.func $my_func). funcref-
Function references, for example
(ref.func $my_func),(ref null func),(ref func). externref-
External value references, for example
(ref.null extern),(ref null extern). exnref-
Exception references, for example
(ref.null extern). eqref,structref,arrayref,anyref-
References to garbage collection (GC) values.
nullref,nullfuncref,nullexternref-
Null references.
element_list-
A space-separated list of references to be stored in the
table.
Description
Wasm elem definitions define a series of references. There are three forms of elem definition:
Active form
An active element definition is used to define an element segment that is immediately written into a previously-defined table on instantiation and then discarded. In active form, a table first needs to be defined:
(table $return_values 2 funcref)
You then declare an elem definition that includes the references to store. In this case, we are storing function references in the table:
(func $f1 (result i32)
i32.const 42
)
(func $f2 (result i32)
i32.const 100
)
(elem (table $return_values) (i32.const 0) func $f1 $f2)
This elem definition includes the value_type to be stored (func), and the element_list to store in the table ($f1 $f2). Most significantly, it includes a number indicating the offset to start writing the references at — (i32.const 0) — which indicates the first slot of the table.
We've also included a table_identifier — (table $return_values) — to indicate the table to write the references to, although in this basic example there is only one table, so this is not necessary.
Note:
Active elem segments are dropped automatically during module instantiation, and therefore are not available to drop via elem.drop.
Passive form
In passive form, the elem definition declares the references that should be stored in the table in the same way as in active form. The main difference is that, in passive form, you don't specify the table_identifier or offset value. This means that the references are not stored in the table immediately. Instead, this part of the process is handled manually using a table.init instruction.
Let's see what this looks like in code. We include the elem definition in a similar manner to the active form example, except that this time we don't include the table_identifier. Instead, we include a name value ($funcs) to identify the elem later on.
(elem $funcs funcref (ref.func $f1) (ref.func $f2))
We can then call table.init, referencing the elem name, to copy the references into the specified table:
(func (export "init")
i32.const 0 ;; destination table index
i32.const 0 ;; offset into elem segment
i32.const 2 ;; number of elements to copy
table.init $funcs
)
After table.init has been called, the elem segment is no longer needed, so elem.drop can be called to free up the memory it was using:
elem.drop $funcs
Note:
You can see a full working example at Passive elem example.
Declarative form
The declarative form of elem is used when you want to use a reference in your code without putting it into a table. It allows you to create a reference that can be referenced via ref.func:
(module
;; Create a reference to the $add function
(elem declare func $add)
(func $add (param i32 i32) (result i32)
local.get 0
local.get 1
i32.add
)
(func (export "getRef") (result funcref)
;; only valid because of the declarative elem above
ref.func $add
)
)
This was added to the language because normally you can only reference functions with ref.func that have been made referenceable, for example in a global definition or by being imported from the JavaScript host. Declarative elem definitions exist to make some functions referenceable that otherwise wouldn't be.
Examples
>Passive elem example
This example shows how you can use the passive form of elem to defer copying the specified references to the table on instantiation, later adding them using the table.init instruction.
JavaScript
In our script, we start by grabbing a reference to a <p> element that we will output results to. We then compile and instantiate our Wasm module using the WebAssembly.instantiateStreaming() method. When the result is returned, we invoke the exported Wasm init() function (which as you'll see later, runs table.init), then run the exported accessTable() function, passing it the number 0 as a parameter. Finally, we set the accessTable() function's return value to the <p> element's textContent value so we can inspect it.
const output = document.querySelector("p");
WebAssembly.instantiateStreaming(fetch("{%wasm-url%}")).then((result) => {
result.instance.exports.init();
const value = result.instance.exports.accessTable(1);
output.textContent = value;
});
Wasm
In our Wasm module, we first define a table with two slots, then define two functions called $f1 and $f2, which return the values defined within. Next, we include an elem definition called $funcs, which references the $f1 and $f2 functions.
Finally, we export two functions:
init(): Runs atable.initinstruction to store the functions referenced in the$funcselemin thetable.accessTable(): Takes ani32named$indexas a parameter, and returns ani32. Inside the function body, we usecall_indirectto call the function referenced in the table at the index value$index.
(module
(table $return_values 2 funcref)
(func $f1 (result i32)
i32.const 42
)
(func $f2 (result i32)
i32.const 100
)
(elem $funcs funcref (ref.func $f1) (ref.func $f2))
(func (export "init")
i32.const 0 ;; destination table index
i32.const 0 ;; offset into elem segment
i32.const 2 ;; number of elements to copy
table.init $funcs
)
(func (export "accessTable") (param $index i32) (result i32)
local.get $index
call_indirect (result i32)
)
)
Result
The outputted value is as follows:
This makes sense, as the exported accessTable() function has an index value passed into it. Inside the Wasm module, we call the function available at that index in the defined table, which returns the value we see output.
Note that we have to call init() before we call accessTable(), to initialize the table with references. If we didn't do that, the program would error.
Specifications
| Specification |
|---|
| WebAssembly Core Specification> # element-segments%E2%91%A0> |
Browser compatibility
See also
elem.dropinstructiontabledefinition- WebAssembly table instructions