Первый модуль вручную: сумма
Собираем свой первый полный WAT-модуль руками.
Цель урока — написать рабочий модуль
(module ...)с функцией сложения и понять каждую его строку.
Полный модуль
Вот наш первый завершённый модуль. Он объявляет функцию сложения двух целых и экспортирует её под именем "add":
(module
(func $add (param $a i32) (param $b i32) (result i32)
local.get $a ;; кладём a на стек -> [a]
local.get $b ;; кладём b на стек -> [a, b]
i32.add) ;; складываем -> [a+b]
(export "add" (func $add)))
Каждая строка прокомментирована состоянием стека справа. После i32.add на стеке ровно одно значение — оно и станет результатом, как обещано в (result i32).
Расширяем: складываем три числа
Усложним — функция трёх аргументов. Складываем первые два, результат остаётся на стеке, добавляем третий:
(module
(func $add3 (param $a i32) (param $b i32) (param $c i32) (result i32)
local.get $a
local.get $b
i32.add ;; на стеке: [a+b]
local.get $c
i32.add) ;; на стеке: [a+b+c]
(export "add3" (func $add3)))
Заметьте: после первого i32.add промежуточная сумма уже лежит на стеке, и второй i32.add просто складывает её с $c. Стек естественно накапливает результат — в этом красота стековой модели.
Проверим логику на JS
Прежде чем компилировать WAT в браузере, удобно проверить ожидаемые числа. Повторим логику обеих функций на JavaScript:
function add(a, b) { return a + b; }
function add3(a, b, c) { return a + b + c; }
console.log("add(2, 3) =", add(2, 3));
console.log("add3(10, 20, 30) =", add3(10, 20, 30));
Вывод:
add(2, 3) = 5 add3(10, 20, 30) = 60
Как это скомпилировать
Сохраните WAT в файл add.wat и прогоните через wat2wasm:
wat2wasm add.wat -o add.wasm
# теперь add.wasm можно загрузить из JS
Как работает под капотом
Когда wat2wasm читает наш текст, он сначала разбирает s-выражения в дерево, затем превращает имена ($add, $a) в числовые индексы, проверяет, что типы на стеке сходятся, и наконец записывает компактный бинарь с секциями: тип функции (i32, i32) -> i32, тело [local.get 0, local.get 1, i32.add] и таблицу экспортов с записью "add". Имена-псевдонимы в бинаре не нужны — там работают индексы.
Частые ошибки
- Лишняя инструкция на стеке — если случайно положить значение и не использовать его, форма стека не совпадёт с
result. - Перепутали порядок аргументов — для сложения это незаметно, но для вычитания
i32.subпорядок важен: снимаетсяa - bв порядке push. - Забыли экспорт — модуль скомпилируется, но из JS функция будет недоступна.
Итоги
- Полный модуль:
(module (func ...) (export ...)). - Стек естественно накапливает промежуточные результаты.
wat2wasm add.wat -o add.wasmкомпилирует текст в бинарь.- В бинаре имена становятся индексами; экспорт хранит публичное имя.