🌌 Асемблер нейроімпланта — С.І.Д. знайомить ранера зі Стосом — небалакучим мешканцем WebAssembly-кварталу, що тримає операційний стек і обслуговує кожну інструкцію лише з вершини стопки. Перш ніж підкидати нейроімплант охороні, треба відчути цю стійку до останнього байта: одна переплутана інструкція чи неврахований залишок на стеці — і Стос не випустить модуль, а антивірус монітора помітить чужий почерк.
На відміну від x86-64 чи ARM (регістрових архітектур, де операнди зберігаються в іменованих регістрах, а інструкція явно вказує, з якими регістрами працює), WebAssembly визначає стекову машину (stack machine) — модель, у якій усі значення лежать на спільній «стопці» (стеку): кожна інструкція бере операнди виключно з вершини операційного стека і кладе результат туди ж. Інструкція не має власного «імені регістру», з яким працює, — весь потік даних неявно проходить через стек. Це не деталь реалізації, а частина формальної специфікації: валідатор WASM статично перевіряє, що для кожної точки виконання типи значень на стеці відповідають тому, чого очікує наступна інструкція — саме тому WASM можна перевірити на коректність типів ще до виконання, за лінійний прохід по байткоду, без символьного виконання.
Текстовий формат .wat (WebAssembly Text Format) — людяне,
S-виразне (у стилі Lisp) представлення того самого байткоду, що
компілюється в бінарний .wasm інструментом wat2wasm (пакет WABT —
WebAssembly Binary Toolkit) і назад декомпілюється через wasm2wat.
Модуль оголошується як (module ...), а функція — як (func $ім'я (param $x i32) (result i32) ...) з тілом із послідовності інструкцій.
Приклад обчислення (2 + 3) * 4:
(module
(func $calc (result i32)
i32.const 2
i32.const 3
i32.add ;; стек: [5]
i32.const 4
i32.mul)) ;; стек: [20]
(export "calc" (func $calc)))
Простежмо стан стека покроково: i32.const 2 кладе значення 2
(стек: [2]); i32.const 3 кладе 3 (стек: [2, 3]); i32.add
знімає два верхні значення (3 і 2), додає їх і кладе результат
назад (стек: [5]); i32.const 4 кладе 4 (стек: [5, 4]); i32.mul
знову знімає два верхні значення (4 і 5), перемножує й кладе
результат (стек: [20]). Функція з (result i32) повинна залишити на
стеці рівно одне значення відповідного типу в момент виходу — валідатор
відхилить модуль, якщо на стеці лишиться забагато значень, замало, або
невідповідного типу.
WASM визначає чотири базові числові типи: i32, i64 (цілі, без
розрізнення знаковості на рівні типу — знаковість визначає сама
інструкція, наприклад i32.div_s проти i32.div_u), f32, f64
(з плаваючою точкою за IEC 60559). Кожна арифметична інструкція
строго типізована суфіксом (i32.add працює лише з парою i32 на
стеці) — спроба виконати i32.add, коли на стеці лежить f64,
провалить валідацію модуля ще до завантаження в рушій, а не викличе
помилку рантайму.
Керування потоком виконання (умовні переходи, цикли) у WASM теж
побудоване навколо структурованих блоків (block, loop, if/else)
зі своєю власною «сигнатурою стека» для кожного блоку — на відміну
від довільних goto/jmp у традиційному асемблері, WASM свідомо
забороняє довільні переходи всередину/назовні довільних точок коду,
що спрощує і статичну верифікацію, і компіляцію в машинний код
браузерним JIT-компілятором.
Прочитати розділи «Structure» і «Execution — Stack» WebAssembly Core Specification (W3C); написати вручну три рядки .wat, що обчислюють (2 + 3) * 4 через i32.const/i32.add/i32.mul, і перевірити результат через wat2wasm та wasm-interp з пакета WABT.
Що лежить на стеці після виконання i32.const 2; i32.const 3; i32.add; i32.const 4 — і скільки значень зніме з нього наступна інструкція i32.mul?
i32.const 3101 і подивись на модуль через wasm-objdump -d: Стос запише число трьома байтами — 41 9d 18, LEB128-кодування 0xC1D. С.І.Д. каже, що це перше число, яке творці змусили його порахувати вручну, — і що кіт тоді сидів на клавіатурі.Кожна команда терміналу з цієї сторінки — одним реченням. Позначка нове — команда зустрічається в курсі вперше; далі вважаємо її знайомою.
cat/proc/....wat2wasm.wasm (wat2wasm m.wat -o m.wasm).wasm-interpнове.wasm без браузера (--run-all-exports — викликати всі експортовані функції).wasm2wat.wasm назад у читабельний WAT.💡 Прочитати README прямо в терміналі: cat README.md (виведе весь файл) або less README.md (посторінково; вихід — клавіша q).
Довідка з будь-якої команди: man curl (повний посібник, вихід — q) або коротко curl --help; для підкоманд — docker run --help, cargo build --help.