Перейти к основному содержимому

Встроенная сборка

Введение

Инлайн-ассемблер Wave создается с помощью блока asm { ... }}. В коде Wave можно напрямую управлять регистрами, памятью и маршрутами системных вызовов.

В настоящий момент поддерживаемые целевые системы:

  • Linux aarch64
  • Linux arm64
  • macOS (Darwin) x86_64
  • freestanding aarch64
  • freestanding riscv64
  • freestanding riscv64

Пока не поддерживаются Windows и 32-битные системы.


Основная форма

asm можно использовать как предложение(statement), так и как выражение(expression).

asm {
"instruction"
in("constraint_or_reg") value
out("constraint_or_reg") target
clobber("item")
}

Компоненты:

  • Строка: настоящая команда ассемблера
  • clobber(...): входной операнд
  • out(...): выходной операнд
  • clobber(...): разрушаемые регистры/состояние/память

Оператор asm (Statement)

Обычно используется как предложение, когда возвратное значение не требуется.

var ret: i64 = 0;
asm {
"mov rax, 1"
"syscall"
in("rdi") 1
in("rsi") msg_ptr
in("rdx") 20
out("rax") ret
}

out(...) может иметь несколько экземпляров.


Выражение asm (Expression)

Может использоваться как выражение, непосредственно создающее значение.

var result: i64 = asm {
"mov rax, 123"
out("rax") result
};

Предупреждение:

  • Выражение asm разрешает наличие ровно одного out(...).

Ограничения in(...) / out(...)

Строка в in("..."), out("...") может быть одним из следующих вариантов.

  1. Конкретный регистр
  • Примеры: "rax", "rdi", "x0", "w1", "a0", "t0", "x10"
  1. Класс ограничений(constraint class)
  • Примеры: "r", "m", "rm"

Пример:

in("r") &buf
out("rax") ret
  • Переменная: out("rax") ret
  • Разыменование указателя: clobber(...)

clobber(...)

clobber(...) может принимать несколько пунктов за раз и использоваться несколько раз.

asm {
"xor rax, rax"
clobber("rax")
clobber("rcx", "rdx")
clobber("memory")
}

Основные элементы:

  • Регистр: "rax", "x0" и т.д.
  • Специальные: $0, $1 (внутренняя нормализация по целям)б

В консервативном безопасном режиме компилятор автоматически добавляет стандартные clobber. (memory, флаги/cc и т.д.; в RISC-V freestanding используется в основном memory.)


Placeholder операндов ($0, $1, ...)б

При обращении к операндам в строке команд используется $N.

asm {
"mov QWORD PTR [$0], 777"
in("r") &buf
clobber("memory")
}

Примечание:

  • Даже если используется стиль %0, он будет преобразован в $0.

Текущий диапазон поддерживаемых входных операндов

Значение in(...) поддерживает в текущей форме.

  • Идентификатор переменной
  • Целочисленный литерал
  • Строковый литерал
  • &идентификатор
  • deref идентификатор
  • Отрицательный целый/вещественный литерал

Сложные выражения могут быть ограничены, поэтому рекомендуется передавать их через временные переменные при необходимости.


Примечания

Встраиваемый ассемблер частично обходит защиту системы типов. Неправильное указание регистра, конфликты ограничений и упущения clobber могут привести к неправильной генерации кода или сбоям во время выполнения.

Рекомендации:

  • Сначала подтвердите целевой ABI и соглашение о вызове.
  • Управляйте регистрами ввода/вывода и clobber явно.
  • Если память используется напрямую, используйте clobber("memory").