The Compiler
c-local-scanning

Local variable scanning. WAT requires all (local ...) declarations at the top of a function. We scan the body before compiling it:

var local_names: [64][]const u8 = undefined;
var local_count: usize = 0;

fn scan_for_locals(start: usize, end_pos: usize) void {
    const saved: usize = pos;
    pos = start;
    local_count = 0;
    while (pos < end_pos) {
        if (is_letter(cur())) {
            const w: []const u8 = read_name();
            if (streq(w, "var") or streq(w, "const")) {
                const vname: []const u8 = read_name();
                if (cur() == ':') { pos += 1; skip(); _ = read_name(); }
                var dup: bool = false;
                for (0..local_count) |i| {
                    if (streq(local_names[i], vname)) { dup = true; break; }
                }
                if (!dup and !c_is_global(vname)) {
                    local_names[local_count] = vname;
                    local_count += 1;
                }
            }
        } else {
            pos += 1;
        }
    }
    pos = saved;
}

fn emit_locals() void {
    for (0..local_count) |i| {
        emit_str("  (local $");
        emit_str(local_names[i]);
        emit_str(" i64)\n");
    }
    emit_str("  (local $__tmp i64)\n");
}

Every function gets a $__tmp local -- the store instructions need it to swap operand order on the stack.