Що таке інструкція машини, набір інструкцій і асемблер
Що таке WebAssembly і яку проблему він вирішує
Стекова машина: як WASM обчислює вирази без регістрів
Текстовий формат .wat: модуль, функція, тип
Керування потоком: block, loop, br, br_if
Лінійна пам'ять — попередній огляд
Живе демо: .wat у браузері
Фокус: та сама логіка обчислень, інша модель виконання
Перше входження: що таке інструкція машини
Мови, з якими ти працював, — це текст для **людини**. Процесор такого не читає
Процесор виконує **інструкції**: крихітні команди на кшталт «додай два числа», «поклади число в пам'ять», «перейди на іншу інструкцію, якщо результат нуль»
Набір таких команд для конкретної машини називають **набором інструкцій**, а мову їхнього запису — **асемблером**
Компілятор — це перекладач: із мови для людини в набір інструкцій для машини
Це третя межа курсу: перша була між процесом і ОС, друга — між програмою й пам'яттю, ця — між кодом і набором інструкцій.
Перше входження: що таке WebAssembly
WebAssembly (WASM) — це набір інструкцій **вигаданої** машини, якої не існує в залізі
Її «процесор» — це програма: рушій браузера, Node.js, Wasmtime. І вона однакова всюди
Тому один і той самий `.wasm` працює в Chrome на Windows, у Firefox на Linux і на сервері — без перекомпіляції
Швидкість — майже нативна: рушій перекладає інструкції WASM у справжні інструкції твого CPU перед виконанням
Головна відмінність від справжнього процесора — модель обчислень: замість регістрів тут стек. Наступний слайд.
Яку проблему це вирішує
**Як було:** щоб код працював у браузері, його треба було писати на JavaScript — іншої мови там просто не було
Важкі речі — обробка відео, стиснення, шифрування, 3D — на JS виходили в рази повільніші
**Як стало:** беремо C, C++, Rust, компілюємо у WASM — і воно бігає в браузері майже з нативною швидкістю
Побічний, але великий виграш: WASM виявився зручним і поза браузером — там, де потрібен безпечний плагін чи пісочниця
Для нас важливий саме другий бік: WASM — це найдешевший спосіб побачити набір інструкцій живим, просто в DevTools.
Чим WebAssembly НЕ є
**Не заміна JavaScript**: JS лишається мовою сторінки, WASM — модулем, який JS викликає
**Не мова програмування**: на WAT пишуть руками лише для навчання (як ми сьогодні на ЛР8), у роботі WASM компілюють
**Не доступ до системи**: усередині WASM немає ні файлів, ні мережі, ні системних викликів — лише те, що хост явно передав
**Не той самий асемблер, що x86-64**: справжній набір інструкцій твого процесора ми розберемо наступного разу (Л9) — і побачимо різницю на одному алгоритмі
Третій пункт — це та сама межа з Модуля 1, лише ще вужча: WASM ізольований навіть від того, що бачить контейнер.
Стек замість регістрів: у чому відмінність моделі
У справжнього процесора є **регістри** — кілька десятків надшвидких комірок із іменами (розберемо на Л9)
У WebAssembly регістрів немає: значення кладуть на **операційний стек** і знімають із нього
Це зроблено заради переносності: у різних CPU різна кількість і назви регістрів, а стек однаковий скрізь
Компілюється з C, C++, Rust, Go; рушій виконує майже з нативною швидкістю (AOT/JIT)
Відмова від регістрів — головна ціна переносності: код WASM трохи довший, зате однаковий на всіх машинах
WebAssembly у цифрах
4
типи значень
i32, i64, f32, f64 — і жодних рядків
0
регістрів
замість них — операційний стек
64 КіБ
сторінка лінійної пам'яті
росте через memory.grow
2017
рік специфікації MVP
одна VM у Chrome, Firefox, Node.js, Wasmtime
Кожна функція має явну сигнатуру; невідповідність типів на стеку — помилка ще під час завантаження, не виконання
Стекова машина: як це працює
Немає rax, rbx — є один операційний стек значень (i32, i64, f32, f64)
Кожна інструкція знімає потрібну кількість значень зі стека і кладе результат назад
i32.const 2 кладе 2 на стек; i32.add знімає два верхні значення, кладе суму
Порядок інструкцій = порядок обчислення виразу в постфіксній (зворотній польській) нотації
2 + 3 записується як: i32.const 2; i32.const 3; i32.add — покажіть стан стека після кожної інструкції: [2] → [2, 3] → [5]
block/loop замінюють мітки .L_cond/.L_end з x86-64
Керування потоком: x86-64 ↔ WebAssembly
Конструкція
x86-64 (Л9)
WebAssembly
Аналог у мові
Вихід із ділянки
jmp .L_end
block $b … br $b
break
Повтор
jmp .L_cond
loop $c … br $c
continue
Умовний перехід
cmp + jge
i32.ge_s + br_if
if (…) break
Розгалуження
jcc на мітку
if (result T) … else … end
if / else
Довільний стрибок
jmp на будь-яку адресу
неможливий
goto
br_if знімає i32 зі стека: не нуль — перехід. Структурованість — не стиль, а гарантія: у чужу функцію стрибнути не можна; компілятор генерує ці конструкції з for/while сам
Лінійна пам'ять: перший погляд
Окремо від операційного стека WASM має лінійну пам'ять (*linear memory*) — суцільний масив байтів
Росте сторінками по 64 КіБ, доступ — за i32-адресою через i32.load / i32.store
Рядки, масиви, будь-які складні структури живуть саме тут, а не в локальних змінних
Детально — на Л10 і в ЛР8 (рівень 3): ручний запис/читання байтів
Місток до ЛР8, рівень 3: там студенти вручну пишуть i32.store8/i32.load8_u у лінійну пам'ять і бачать trap при виході за межі
Показати вкладку Network — розмір .wasm-файлу в байтах, набагато менший за еквівалентний JS-бандл
Очікувано: Console друкує 5; той самий .wat відкривається і виконується без змін у Chrome, Firefox, Node.js
x86-64 проти WebAssembly
x86-64 (Л9)
WebAssembly
Обчислення
іменовані регістри
операційний стек значень
Керування потоком
довільний jmp/jcc на мітку
структуровані block/loop/br/if
Пам'ять
увесь адресний простір процесу
ізольована лінійна пам'ять з перевіркою меж
Виклик функції
ABI-угода про регістри/стек
типізована сигнатура, перевірена валідатором
Перевірка коректності
немає — байти є байти
валідація типів при завантаженні
Ціль виконання
конкретний фізичний CPU
будь-який рушій: браузер, Node.js, Wasmtime
Обидва — «мова, близька до заліза»; різниця в тому, чиє залізо і скільки йому довіряють
Пісочниця WebAssembly: модель безпеки
🚫
Немає syscallжодного прямого доступу до файлів, мережі, ОС
🔌
Тільки importєдиний вихід назовні — функції, які хост передав явно
🎟️
Капабіліті-модельмодуль має рівно ті можливості, що йому дали, і жодних інших
🧱
Потік + межі пам'ятіне стрибнути й не прочитати поза дозволеним
Пісочниця (sandbox) — ізольоване середовище, з якого код не дістає до системи; хост (host) — браузер, Node.js, Wasmtime. Тому браузер безпечно запускає WASM з недовіреного сайту
Trap замість UB: інша філософія помилок
C / асемблер: undefined behavior
Вихід за межі — будь-який наслідок можливий
Читає «щось випадкове» або пише в чуже
Перевірки немає — швидко, але наосліп
vs
WebAssembly: trap
Виконання зупиняється з чіткою помилкою
Межі перевіряються при кожному load/store
Плата — трохи повільніше за «сирий» asm
Виняток із правила: memory.grow при відмові повертає -1, а не trap — перевірка на совісті коду
Межа хост ↔ модуль: import і export
JS-хостinstantiate(…, {env: {log}})
→
import env.logмодуль отримує $log
→
func $reportlocal.get · call $log
→
export reportвидно з JS
→
exports.report(42)→ console.log(42)
Модуль сам нічого не друкує: без явного import виклику console.log просто не існувало б
Керування потоком структуроване: block/loop/br/br_if замість довільних jmp
Лінійна пам'ять — окрема адресована ділянка байтів, детально на Л10
🥚 У демо-терміналі wasmtime --invoke pixel pixel.wasm 2 3 повертає 5. Спробуйте аргументи 3000 і 101: стекова машина поверне 3101 — 0xC1D. Це число не випадкове: у курсі воно трапляється не раз.
VTFK · TERMLINK · L08← → · space · f — на весь екран