1. 1. Introduction
  2. 2. Введение
  3. 3. C чего начать
  4. 4. Изучение Rust
    1. 4.1. Угадайка
    2. 4.2. Обедающие философы
    3. 4.3. Вызов кода на Rust из других языков
  5. 5. Синтаксис и семантика
    1. 5.1. Связывание имён
    2. 5.2. Функции
    3. 5.3. Простые типы
    4. 5.4. Комментарии
    5. 5.5. Конструкция `if`
    6. 5.6. Циклы
    7. 5.7. Владение
    8. 5.8. Ссылки и заимствование
    9. 5.9. Время жизни
    10. 5.10. Изменяемость
    11. 5.11. Структуры
    12. 5.12. Перечисления
    13. 5.13. Конструкция `match`
    14. 5.14. Шаблоны сопоставления `match`
    15. 5.15. Синтаксис методов
    16. 5.16. Вектора
    17. 5.17. Строки
    18. 5.18. Обобщённое программирование
    19. 5.19. Типажи
    20. 5.20. Типаж `Drop`
    21. 5.21. Конструкция `if let`
    22. 5.22. Типажи-объекты
    23. 5.23. Замыкания
    24. 5.24. Универсальный синтаксис вызова функций
    25. 5.25. Контейнеры и модули
    26. 5.26. `const` и `static`
    27. 5.27. Атрибуты
    28. 5.28. Псевдонимы типов
    29. 5.29. Приведение типов
    30. 5.30. Ассоциированные типы
    31. 5.31. Безразмерные типы
    32. 5.32. Перегрузка операций
    33. 5.33. Преобразования при разыменовании
    34. 5.34. Макросы
    35. 5.35. Сырые указатели
    36. 5.36. Небезопасный код
  6. 6. Эффективное использование Rust
    1. 6.1. Стек и куча
    2. 6.2. Тестирование
    3. 6.3. Условная компиляция
    4. 6.4. Документация
    5. 6.5. Итераторы
    6. 6.6. Многозадачность
    7. 6.7. Обработка ошибок
    8. 6.8. Выбор гарантий
    9. 6.9. Интерфейс внешних функций
    10. 6.10. Типажи `Borrow` и `AsRef`
    11. 6.11. Каналы сборок
    12. 6.12. Using Rust without the standard library
  7. 7. Нестабильные возможности Rust
    1. 7.1. Плагины к компилятору
    2. 7.2. Встроенный ассемблерный код
    3. 7.3. Без stdlib
    4. 7.4. Внутренние средства
    5. 7.5. Элементы языка
    6. 7.6. Продвинутое руководство по компоновке
    7. 7.7. Тесты производительности
    8. 7.8. Синтаксис упаковки и шаблоны `match`
    9. 7.9. Шаблоны `match` для срезов
    10. 7.10. Ассоциированные константы
    11. 7.11. Пользовательские менеджеры памяти
  8. 8. Глоссарий
  9. 9. Syntax Index
  10. 10. Библиография

Встроенный ассемблерный код

Если вам нужно работать на самом низком уровне или повысить производительность программы, то у вас может возникнуть необходимость управлять процессором напрямую. Rust поддерживает использование встроенного ассемблера и делает это с помощью с помощью макроса asm!. Синтаксис примерно соответствует синтаксису GCC и Clang:

asm!(assembly template
   : output operands
   : input operands
   : clobbers
   : options
   );Run

Использование asm является закрытой возможностью (требуется указать #![feature(asm)] для контейнера, чтобы разрешить ее использование) и, конечно же, требует unsafe блока.

Примечание: здесь примеры приведены для x86/x86-64 ассемблера, но поддерживаются все платформы.

Шаблон инструкции ассемблера

Шаблон инструкции ассемблера (assembly template) является единственным обязательным параметром, и он должен быть представлен строкой символов (т.е. "")

#![feature(asm)]

#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
fn foo() {
    unsafe {
        asm!("NOP");
    }
}

// other platforms
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
fn foo() { /* ... */ }

fn main() {
    // ...
    foo();
    // ...
}Run

(Далее атрибуты feature(asm) и #[cfg] будут опущены.)

Выходные операнды (output operands), входные операнды (input operands), затираемое (clobbers) и опции (options) не являются обязательными, но вы должны будете добавить соответствующее количество : если хотите пропустить их:

asm!("xor %eax, %eax"
    :
    :
    : "{eax}"
   );Run

Пробелы и отступы также не имеют значения:

asm!("xor %eax, %eax" ::: "{eax}");Run

Операнды

Входные и выходные операнды имеют одинаковый формат: :"ограничение1"(выражение1), "ограничение2"(выражение2), ...". Выражения для выходных операндов должны быть либо изменяемыми, либо неизменяемыми, но еще не инициализированными, L-значениями:

fn add(a: i32, b: i32) -> i32 {
    let c: i32;
    unsafe {
        asm!("add $2, $0"
             : "=r"(c)
             : "0"(a), "r"(b)
             );
    }
    c
}

fn main() {
    assert_eq!(add(3, 14159), 14162)
}Run

Однако, если вы захотите использовать реальные операнды (регистры) в этой позиции, то вам потребуется заключить используемый регистр в фигурные скобки {}, и вы должны будете указать конкретный размер операнда. Это полезно для очень низкоуровневого программирования, когда важны регистры, которые вы используете:

let result: u8;
asm!("in %dx, %al" : "={al}"(result) : "{dx}"(port));
resultRun

Затираемое (Clobbers)

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

// Put the value 0x200 in eax
asm!("mov $$0x200, %eax" : /* no outputs */ : /* no inputs */ : "{eax}");Run

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

Если ассемблер изменяет регистр кода условия cc, то он должен быть указан в качестве одного из затираемых. Точно так же, если ассемблер модифицирует память, то должно быть указано memory.

Опции

Последний раздел, options, специфичен для Rust. Формат представляет собой разделенные запятыми текстовые строки (т.е. :"foo", "bar", "baz"). Он используется для того, чтобы задать некоторые дополнительные данные для встроенного ассемблера:

На текущий момент разрешены следующие опции:

  1. volatile — эта опция аналогична __asm__ __volatile__ (...) в gcc/clang;

  2. alignstack — некоторые инструкции ожидают, что стек был выровнен определенным образом (т.е. SSE), и эта опция указывает компилятору вставить свой обычный код выравнивания стека;

  3. intel — эта опция указывает использовать синтаксис Intel вместо используемого по умолчанию синтаксиса AT&T.

let result: i32;
unsafe {
   asm!("mov eax, 2" : "={eax}"(result) : : : "intel")
}
println!("eax is currently {}", result);Run

Больше информации

Текущая реализация макроса asm! --- это прямое связывание с встроенным ассемблером LLVM, поэтому изучите и их документацию, чтобы лучше понять список затираемого, ограничения и др.