Модель памяти
Отслеживание выделения, чтение инициализированных значений, арифметика указателей, правила размещения и хранения строк.
Wave Foundation
Отслеживание распределения
Отслеживаемый указатель связывает адрес с идентификатором выделения, генерацией, границами, смещением и разрешениями на доступ. Модель памяти также отслеживает время жизни выделения и состояние инициализации. Доступ должен удовлетворять этим условиям; нарушение вызывает ловушку.
Несмотря на то, что один и тот же физический адрес используется повторно, каждую жизнь разделяют поколения. Само наличие адреса не определяет, что указатель действителен или что вызывающая сторона имеет доступ к этому пространству памяти.
Адрес native поддерживает 64 бита. Отдельные shadow metadata передаются вместе с указателем при копировании/сохранении/вызове/возврате. Начальная область памяти native представляет собой отслеживаемый стек и глобальные выделения. C Для границ требуется явный адаптер, и передача владения произвольной внешней памятью не включена в эту область.
Инициализация и чтение
Объявление места для хранения не инициализирует значение. Проверяет, был ли инициализирован диапазон фактически читаемых байтов. Если хотя бы часть значения читается в неинициализированном состоянии, это trap. Он не заменяет результат чтения на 0 или неопределенное значение.
Проверка инициализации считанного значения исключает байт padding. Например, если все поля структуры инициализированы, значения чтения не будут неверными просто потому, что пустые байты, возникающие в результате выравнивания полей, не инициализированы.
Копия памяти содержит статус инициализации вместе с байтами. Копирование неинициализированного пространства хранения не превращает его в инициализированное пространство хранения. При чтении значений из места назначения в дальнейшем применяются те же проверки, что и при чтении источника.
Тот факт, что физический байт BSS равен 0, не позволяет инициализировать переменную IR.
IR в инициализированной скалярной памяти.
Следующий модуль был настроен как builder и прошел проверку. store перед чтением значения, и все три инструкции памяти определяют ненулевую сортировку по степени 2.
module {
format_version 2
semantics_version 1
target "x86_64-whale-linux"
datalayout { ptr=64, endian=little }
declare @f0 "initialized_local": whale () -> i32, linkage internal
fn @initialized_local() -> i32, id @f0 {
entry:
%v0: ptr<i32> = alloca i32, align 4
%v1: i32 = const i32 42
store i32 %v1, ptr<i32> %v0, align 4
%v2: i32 = load i32, ptr<i32> %v0, align 4
ret i32 %v2
}
}
alloca создает хранилище i32, store записывает 42, а load определяет возвращаемое значение. Это выходные данные печатного ИК-принтера, а не собственная трассировка выполнения. Выравнивание 3 является ошибкой проверки. В этой модели памяти для удаления хранилища потребуется ловушка неинициализированного чтения, но отслеживание инициализации и соответствующие проверки ловушек во время выполнения еще не реализованы. Передача только текущего верификатора не означает, что это чтение безопасно.
Сравнение с арифметикой указателей
Расчет адреса overflow равен trap. Вы можете создать указатель one-past, который будет указывать сразу после диапазона выделения, но вы не сможете через него получить доступ к памяти.
Для равенства указателей используется идентификатор выделения, а не просто числовой адрес. Упорядочение или вычитание указателей из разных распределений приводит к ловушке. Восстановление адреса из целого числа не восстанавливает права доступа.
GEP
GEP вычисляет адреса на основе элементов и полей. Это не команда для чтения значения.
Первый индекс — это единица элемента offset типа, на который указывает базовый указатель. Последующий индекс выбирает элемент массива или поле структуры/кортежа. Индекс поля структуры/кортежа — это порядковый номер поля на момент компиляции, а не байт offset. Выбранный тип определяет результирующий тип указателя.
Если базовым типом является ptr<array<i32, 4>>, индекс [0, 2] выбирает третий элемент i32 массива, в результате чего получается ptr<i32>. Указание первого индекса равным 1 перемещает i32 на один элемент, а не на один элемент в массиве. Этот пример иллюстрирует семантику индекса, а не синтаксис текстовых команд.
Начальная цель native отклоняет арифметику указателей на элементах размера 0. Вычисление адреса не исключает проверки времени жизни, области действия, инициализации и разрешений, необходимые для последующего доступа.
расположение данных
Цель вывода определяет размер поля выравнивания offset·массива stride. Порядок полей в структурах и кортежах сохраняет порядок их объявления. Макет хоста, на котором работает компилятор, не следует считать макетом цели вывода.
| ценность | Сохранить правила |
|---|---|
| Bool, signed i1, unsigned u1 | Минимум 1 байт |
| Пустая структура/кортеж | Размер 0, выравнивание 1 |
| Массив | Целевой макет определяет элемент stride |
| Структура/кортеж | Сохранять порядок объявления и требования к выравниванию цели. |
Завершенное выравнивание IR представляет собой степень 2, а не 0. Перед созданием этого IR необходимо определить автоматическое выравнивание. Макеты packed·union·bitfield не поддерживаются в этом профиле и должны быть отклонены.
Запрос макета вывода
Rust API вычисляет структуру хранилища независимо от хоста сборки. В следующем примере имеется 7 байтов padding перед полем u64 и 6 байтов в конце padding.
use ir::{allocation_align, layout_of, Target, Type};
fn main() {
let target = Target::X86_64WhaleLinux;
let record = Type::Struct(vec![Type::U8, Type::U64, Type::U16]);
let layout = layout_of(&record, target).unwrap();
assert_eq!((layout.size, layout.align), (24, 8));
assert_eq!(layout.field_offsets, [0, 8, 16]);
let array = Type::Array(Box::new(record), 3);
let layout = layout_of(&array, target).unwrap();
assert_eq!((layout.size, layout.align), (72, 8));
assert_eq!(layout.element_stride, Some(24));
assert_eq!(allocation_align(&array, target).unwrap(), 16);
}
struct{u8, u64, u16}: size 24, natural alignment 8
field 0: byte 0
field 1: byte 8
field 2: byte 16
array of 3: size 72, element stride 24
standalone array placement alignment: 16
Размер, возвращаемый layout_of, и шаг массива включают завершающее заполнение. Структуры и кортежи используют одни и те же правила порядка полей. Bool, i1 и u1 каждый занимает один байт. Пустые структуры и кортежи имеют размер 0 и выравнивание 1; Массив нулевой длины сохраняет естественное выравнивание своих элементов. void не имеет структуры хранения, а ptr<void> занимает 8 байт.
Используйте естественную сортировку для полей и элементов массива. allocation_align применяет правило SysV AMD64, которое требует выравнивания как минимум по 16 байтам, к независимым локальным/глобальным массивам размером 16 байт и более. Это не увеличивает выравнивание полей массива или элемента stride. AST lowering использует этот поиск пакетной сортировки для локального хранилища.
Умножение величины, сложение поля offset, overflow в вычислении padding возвращает LayoutError::Overflow. layout_of имеет ограничение вложенности сложных типов в 128 уровней, а layout_of_with_limit позволяет вызывающему объекту указать ограничение. Он не выделяет пространство для хранения, равное количеству элементов массива. pointer_stride отклоняет pointee с размером 0 в арифметике указателей native, но сама структура хранения этого типа действительна. Packed·union·bitfield не имеет поддерживаемого выражения типа. Этот сохраненный поиск макета не реализует соглашения о вызовах сложных типов или проверку границ во время выполнения.
Строка и граница C
Строка — это неизменяемая строка байтов заданной длины. Кодировка по умолчанию — UTF-8. Разрешает внутренний NUL и не добавляет окончание NUL автоматически. O0 не объединяет автоматически строковые объекты с одинаковым содержимым.
Следовательно, длина строки байтов, состоящей из A, NUL и B, равна 3. Невозможно использовать для явных преобразований строк C, которые отвергают внутренний NUL. Интерфейс не должен молча обрезать это значение до A.
Внешний C·Необработанный адрес·Встроенная сборка — это отдельная граница контракта. Проверка памяти трассировки во время выполнения не гарантирует обнаружение всего неправильного поведения во внешнем коде.