Skip to content

Call Convention

Understanding WebAssembly's call convention is essential for interop between WASM and JavaScript (or other languages).

Stack-based Call Convention

WebAssembly uses a stack-based call convention:

mermaid
flowchart LR
    subgraph Caller["Caller"]
        P1[Push arg 1] --> P2[Push arg 2]
        P2 --> CALL[Call function]
    end
    CALL --> CALLEE[Callee pops args]
    CALLEE --> COMPUTE[Compute result]
    COMPUTE --> PUSH[Push result]
    PUSH --> RET[Return]
    RET --> POP[Caller pops result]

Local Variables

wat
(func $factorial (param $n i32) (result i32)
  (local $result i32)
  (local $temp i32)

  (local.set $result (i32.const 1))

  (block $done (result i32)
    (loop $loop
      (br_if $done (i32.le_s (local.get $n) (i32.const 1)))
      (local.set $temp (local.get $result))
      (local.set $result (i32.mul (local.get $result) (local.get $n)))
      (local.set $n (i32.sub (local.get $n) (i32.const 1)))
      (br $loop))
    (local.get $result)))

Branching and Control Flow

If-Else

wat
(func $max (param $a i32) (param $b i32) (result i32)
  (if (i32.gt_s (local.get $a) (local.get $b))
    (then (local.get $a))
    (else (local.get $b))))

Loop

wat
(func $sum_to (param $n i32) (result i32)
  (local $sum i32)
  (local.set $sum (i32.const 0))
  (block $done
    (loop $start
      (br_if $done (i32.eqz (local.get $n)))
      (local.set $sum (i32.add (local.get $sum) (local.get $n)))
      (local.set $n (i32.sub (local.get $n) (i32.const 1)))
      (br $start)))
  (local.get $sum))

Performance Tips

TechniqueNote
Use smaller typesi32 faster than i64 on 32-bit architectures
Minimize parametersPass structs via pointer, not by value
Inline small functionsReduce call overhead
Avoid excessive branchingDeep branching affects performance

Continue learning Import and Export to understand module communication.

Released under the MIT License.