Locals and the Stack
WebAssembly is a stack machine. There is no register file like in x86 or ARM — instead, instructions communicate by pushing and popping values on an implicit value stack. Understanding this model is the key to reading WAT fluently.
The value stack
Section titled “The value stack”Every instruction operates on the top of the stack. An instruction pops its inputs and pushes its output. For example, i32.add pops two i32 values and pushes one i32 result.
flowchart LR A["Stack: []\ni32.const 10"] --> B["Stack: [10]\ni32.const 3"] B --> C["Stack: [10, 3]\ni32.add"] C --> D["Stack: [13]"]
At the end of a function, whatever is left on the stack must exactly match the declared result types. Leave too much or too little and the module fails validation.
Local variables
Section titled “Local variables”Besides the stack, functions can declare local variables using (local $name type). These are mutable slots that live for the duration of the function call.
(module (func (export "example") (param $x i32) (result i32) (local $tmp i32) local.get $x ;; push $x i32.const 1 i32.add ;; pop two, push sum local.set $tmp ;; pop and store in $tmp local.get $tmp)) ;; push $tmp backParameters are also locals — you can read them with local.get just like declared locals, but they are initialized with the call arguments.
local.tee: store and keep
Section titled “local.tee: store and keep”local.tee is like local.set but it leaves a copy of the value on the stack after storing. It is useful when you need to both save a value and immediately use it:
(module (func (export "storeAndDouble") (param $n i32) (result i32) (local $saved i32) local.get $n local.tee $saved ;; stores $n in $saved AND leaves the value on stack i32.const 2 i32.mul)) ;; doubles the value that tee left behindWithout local.tee you would need local.set followed by local.get, which is two instructions instead of one.