Как компилируется Rust
Компилятор Rust, известный как
1. Парсинг и проверка синтаксиса
Исходный код, который вы пишете, это последовательность символов. Задача лексического анализатора — преобразовать эту последовательность в токены. Токены — это основные единицы, такие как ключевые слова, идентификаторы, литералы и знаки препинания.
Как только код токенизирован, начинает работу синтаксический анализатор. Он использует токены для создания абстрактного синтаксического дерева (abstract syntax tree). AST — это древовидное представление исходного кода, где каждый узел соответствует конструкции в коде.
Например, рассмотрим код на Rust:
AST может представлять
2. Семантический анализ
На этапе семантического анализа работа ведется уже с AST. Этот шаг включает:
Разрешение (Resolving): Компилятор определяет, что означает каждое имя (например, переменная или функция).
Проверка типов (Type checking): Rust обеспечивает безопасность типов, поэтому компилятор проверяет, чтобы, например, вы не пытались добавить строку к целому числу.
Проверка заимствований (Borrow checking): Одной из уникальных особенностей Rust является его проверка заимствования. Это гарантирует соблюдение правил владения и заимствования для ссылок на данные.
3. Высокоуровневое промежуточное представление (High-Level Intermediate Representation)
Затем компилятор преобразует AST в высокоуровневое промежуточное представление (HIR). HIR упрощает AST, упрощая компилятору выполнение конкретных преобразований и оптимизаций. Оно более абстрактно, чем исходный код, но все же тесно с ним связано.
4. Промежуточное представление среднего уровня (Mid-Level Intermediate Representation)
Затем HIR преобразуется в промежуточное представление среднего уровня (MIR). MIR — это более простое, более абстрактное представление вашего кода на Rust. На этом этапе многие продвинутые функции Rust были преобразованы в набор более простых конструкций.
MIR позволяет проводить продвинутые анализы и преобразования потока данных. Именно здесь работает проверка заимствований, которая гарантирует, что ссылки следуют строгим правилам заимствования и владения Rust.
5. Бекенд компиляция (LLVM)
После MIR, предпоследним этапом вступает backend компиляция. Backend отвечает за преобразование MIR в исполняемый код. Это включает:
Преобразование в промежуточное представление LLVM (LLVM IR): Rust использует инфраструктуру компилятора LLVM (возможны и другие бекенды, например rustc-codegen-ssa или rustc-codegen-cranelift). MIR преобразуется в промежуточное представление LLVM (LLVM IR). LLVM IR — это независимое от платформы представление на низком уровне.
Оптимизация: Как только код преобразован в LLVM IR, запускается серия проходов оптимизации для повышения эффективности кода.
Генерация байт кода: Наконец, оптимизированный LLVM IR переводится в машинный код для целевой платформы.
6. Связывание
Как и большинство языков, Rust позволяет разбивать код на несколько файлов и даже связываться с предварительно скомпилированными библиотеками. Последняя стадия компиляции включает в себя связывание всех этих частей вместе, чтобы создать единый исполняемый файл или библиотеку.
Компилятор Rust, известный как
rustc
, проходит через несколько стадий, чтобы преобразовать исходный код на языке Rust в исполняемый файл.1. Парсинг и проверка синтаксиса
Исходный код, который вы пишете, это последовательность символов. Задача лексического анализатора — преобразовать эту последовательность в токены. Токены — это основные единицы, такие как ключевые слова, идентификаторы, литералы и знаки препинания.
Как только код токенизирован, начинает работу синтаксический анализатор. Он использует токены для создания абстрактного синтаксического дерева (abstract syntax tree). AST — это древовидное представление исходного кода, где каждый узел соответствует конструкции в коде.
Например, рассмотрим код на Rust:
fn main() {
let x = 5;
}
AST может представлять
fn
, main
, ()
, {
, let
, x
, =,
5
и }
как отдельные узлы, формируя дерево, которое отражает их иерархические отношения.2. Семантический анализ
На этапе семантического анализа работа ведется уже с AST. Этот шаг включает:
Разрешение (Resolving): Компилятор определяет, что означает каждое имя (например, переменная или функция).
Проверка типов (Type checking): Rust обеспечивает безопасность типов, поэтому компилятор проверяет, чтобы, например, вы не пытались добавить строку к целому числу.
Проверка заимствований (Borrow checking): Одной из уникальных особенностей Rust является его проверка заимствования. Это гарантирует соблюдение правил владения и заимствования для ссылок на данные.
3. Высокоуровневое промежуточное представление (High-Level Intermediate Representation)
Затем компилятор преобразует AST в высокоуровневое промежуточное представление (HIR). HIR упрощает AST, упрощая компилятору выполнение конкретных преобразований и оптимизаций. Оно более абстрактно, чем исходный код, но все же тесно с ним связано.
4. Промежуточное представление среднего уровня (Mid-Level Intermediate Representation)
Затем HIR преобразуется в промежуточное представление среднего уровня (MIR). MIR — это более простое, более абстрактное представление вашего кода на Rust. На этом этапе многие продвинутые функции Rust были преобразованы в набор более простых конструкций.
MIR позволяет проводить продвинутые анализы и преобразования потока данных. Именно здесь работает проверка заимствований, которая гарантирует, что ссылки следуют строгим правилам заимствования и владения Rust.
5. Бекенд компиляция (LLVM)
После MIR, предпоследним этапом вступает backend компиляция. Backend отвечает за преобразование MIR в исполняемый код. Это включает:
Преобразование в промежуточное представление LLVM (LLVM IR): Rust использует инфраструктуру компилятора LLVM (возможны и другие бекенды, например rustc-codegen-ssa или rustc-codegen-cranelift). MIR преобразуется в промежуточное представление LLVM (LLVM IR). LLVM IR — это независимое от платформы представление на низком уровне.
Оптимизация: Как только код преобразован в LLVM IR, запускается серия проходов оптимизации для повышения эффективности кода.
Генерация байт кода: Наконец, оптимизированный LLVM IR переводится в машинный код для целевой платформы.
6. Связывание
Как и большинство языков, Rust позволяет разбивать код на несколько файлов и даже связываться с предварительно скомпилированными библиотеками. Последняя стадия компиляции включает в себя связывание всех этих частей вместе, чтобы создать единый исполняемый файл или библиотеку.
✍1