WebAssembly (Wasm) syntax provides a structured and efficient way to write low-level code that executes at high speed within web browsers.
What is WebAssembly Syntax?
Each instruction in WebAssembly is represented by a specific opcode (operation code), which is optimized to perform operations like arithmetic, logic and memory manipulation directly on this stack.
WebAssembly syntax can be written in two forms:
- Text Format (WAT): This is a human-readable format known as WebAssembly Text format (WAT), commonly used for learning, debugging or writing WebAssembly manually.
- Binary Format (.wasm): This is the compiled, machine-readable binary format that is executed by the browser. WAT is usually compiled to the binary format using WebAssembly tools.
Basic Structure of WebAssembly Syntax in WAT
A basic WebAssembly module written in WAT format includes several key components:
- Module Declaration: Defines the WebAssembly module, encapsulating all other components.
- Memory: Defines memory space for the module.
- Function Definitions: Defines functions with inputs and outputs.
- Exports: Makes functions and variables accessible to other programs or JavaScript.
Let’s examine each component in more detail.
Example: A Simple WebAssembly Module
Here’s a minimal example of a WebAssembly module that performs a simple addition:
(module
(func $add (param $a i32) (param $b i32) (result i32)
local.get $a
local.get $b
i32.add)
(export "add" (func $add))
)
Explanation of the Syntax
- (module): This declares a new WebAssembly module. All code inside the (module) block is encapsulated within the module.
- (func $add …): This declares a function named add. The function has two parameters, $a and $b, both of type i32 (32-bit integer) and it returns an i32 result.
- local.get $a / local.get $b: These instructions retrieve the values of $a and $b from the function parameters and push them onto the stack.
- i32.add: This instruction pops two values off the stack, adds them and pushes the result back onto the stack.
- (export “add” (func $add)): This line exports the function as add, making it accessible from JavaScript or other modules.
WebAssembly Data Types
WebAssembly has four primary data types:
- i32: 32-bit integer
- i64: 64-bit integer
- f32: 32-bit floating point
- f64: 64-bit floating point
Each data type has a set of operations associated with it, like add, sub (subtract), mul (multiply) and div (divide).
Calling WebAssembly from JavaScript
Once the WebAssembly module is compiled, it can be called directly from JavaScript. Here’s how you can load and execute the add function from JavaScript.
fetch('add.wasm')
.then(response => response.arrayBuffer())
.then(bytes => WebAssembly.instantiate(bytes))
.then(({ instance }) => {
// Call the add function from the WebAssembly module
console.log(instance.exports.add(5, 3)); // Outputs: 8
});
Working with Variables and Control Flow
In WebAssembly, variables are handled with local declarations, and control flow is managed with conditional and loop instructions. Here’s an example of a factorial function in WAT:
(module
(func $factorial (param $n i32) (result i32)
(local $result i32)
(local.set $result (i32.const 1))
(block $exit
(loop $loop
local.get $n
i32.const 1
i32.lt_s
br_if $exit
local.get $result
local.get $n
i32.mul
local.set $result
local.get $n
i32.const 1
i32.sub
local.set $n
br $loop))
local.get $result)
(export "factorial" (func $factorial))
)
In this code:
- (block $exit): A block that allows for an early exit from the loop.
- (loop $loop): A loop to repeatedly calculate factorial.
- br_if and br: Branching instructions that allow for conditional exits from loops.
Using Memory in WebAssembly
WebAssembly syntax supports linear memory, which is an array of bytes. Memory can be read and written with i32.load and i32.store instructions. Here’s an example that demonstrates loading and storing data in WebAssembly memory.
(module
(memory (export "mem") 1) ;; Define and export memory with one page (64KB)
(func $store (param $index i32) (param $value i32)
(i32.store (local.get $index) (local.get $value))) ;; Store value at index
(func $load (param $index i32) (result i32)
(i32.load (local.get $index))) ;; Load value from index
(export "store" (func $store))
(export "load" (func $load))
)
This module provides two functions:
- store: Stores an integer value at a specified index in memory.
- load: Loads an integer value from a specified index in memory.
JavaScript can then interact with this memory, storing and retrieving data as needed.