Вы не видели новостей от меня (Питера) последние несколько дней, потому что я был крайне непродуктивен. Я также решил сменить ОС на своем домашнем сервере, что заняло у меня много времени. Оправдания, оправдания. Я приступил к работе сегодня, и все пошло по хорошему клипу. Вот краткий обзор:

  • Настроена тестовая инфраструктура
  • Инструкционный симпатичный принтер перемещен в библиотеку, поэтому теперь он вызывается при сбое теста, чтобы упростить отладку.
  • Реализовано несколько инструкций
  • Начало работы с этикетками
  • Полностью удалено все, что связано с сегментом данных из компилятора.

Почему мы решили выдрать все, что связано с сегментом данных? После быстрого анализа затрат и результатов мы решили, что это ничего не добавляет к тому, что изучает студент, и мы не пытаемся учить студенческое собрание. Это было действительно так просто. Это было начато, потому что я понял, что потребуются некоторые усилия, чтобы получить адреса от эталонного и тестового компилятора, чтобы выровняться в тестовых случаях. Удаление этой функции не меняет структуру ни одного из уроков. На самом деле, метки данных могут быть определены вместе с остальной частью программы, поэтому нам не хватает ровно 0 функциональных возможностей. Ву!

Платформа тестирования

Часы, которые я потратил на написание небольших инструментов на прошлой неделе, действительно окупились, поскольку я смог настроить среду тестирования, в основном, склеивая эти части вместе. Была инструкция pretty-printer, которая теперь используется для вывода ожидаемых и фактических результатов для отладки в случае сбоя теста. Парсер ELF используется для чтения вывода эталонного компилятора и извлечения необходимой информации. Платформа тестирования обращается к эталонному компилятору и считывает результаты. И, конечно же, сам компилятор, с которым теперь связаны как среда тестирования, так и инструмент командной строки. Конечно, была некоторая работа по переносу функциональности в библиотеки, но я с самого начала настроил все так, чтобы переход был как можно проще, поэтому переход занял всего несколько минут.

Тестовый поток

Тесты довольно простые. Тест выглядит примерно так:

#[test]
// Tests that the subu instruction is properly encoded
fn subu_test() {
    let (test_text, reference_text) = compile_test_and_reference(
        r#"
        subu $t0, $t1, $t2
        subu $t0, $t2, $t1
        subu $t1, $t0, $t2
        subu $t1, $t2, $t0
        subu $t2, $t1, $t0
        subu $t2, $t0, $t1
        "#);
    check_test_and_reference(&test_text, &reference_text);
}

Есть много небольших вспомогательных функций, чтобы сделать эту работу гладкой, но на самом деле это просто вопрос компиляции программы с эталонным компилятором и сравнения вывода с нашим компилятором. Эта функция check_test_and_reference предназначена для предотвращения нелепого дублирования кода для красивой печати инструкций.

Самая сложная часть тестового фреймворка — это вызов эталонного компилятора, но даже это не так уж сложно. Единственная причина сложности в том, что эталонный компилятор не может писать в стандартный вывод. Многие ассемблеры делают много проходов по выходному файлу на диске, поэтому им приходится иметь возможность искать. Однако это преодолевается созданием временного файла, записью в него эталонного компилятора, а затем чтением содержимого. Это некрасиво, но просто и работает. Есть более умные способы сделать то же самое, но этот работает и совершенно очевиден для всех, кто должен его поддерживать.

Жду с нетерпением

После того, как компилятор завершится, какая работа с инфраструктурой останется?

  • Внедрение компилятора во внешний интерфейс. Яш отвечал за это, и он надеется сделать это к концу сегодняшнего дня, что было бы здорово. Во время написания он столкнулся с некоторыми загадочными ошибками, но ему есть что сказать об этом.
  • Горячая перезагрузка кода пользователя
  • Абстракция памяти
  • Зарегистрировать абстракцию
  • Код поддержки для первого урока

Я хочу поближе взглянуть на абстракцию памяти, так как это будет самая сложная часть (я думаю).

Абстракция памяти

Мы собираемся предоставить объект JavaScript, который эмулирует память, чтобы мы могли выполнять проверку их операций чтения и записи. Это также позволит нам использовать все 32-битное адресное пространство. В большинстве операционных систем стек растет от старшего адреса вниз по направлению к остальной части программы. Однако мы не хотим выделять в браузере массив размером 4 ГБ, поэтому по сути собираемся воссоздать абстракцию виртуальной памяти.

Память будет разделена на набор страниц (размер уточняется). Когда страница впервые записывается, она будет создана и инициализирована до 0, чтобы сохранить эту запись. Если считывается неиспользуемая страница, возвращается 0 (или ошибка, но это еще не решено). Это позволит нам представить, что у пользователя есть целая куча памяти для работы. Абстракция памяти в сочетании с абстракцией регистров позволит нам реализовать нужные нам анимации, а также сможет проверять чтение и запись ученика, отображая всплывающую информацию, которая будет полезна для его прогресса.

Дополнительный раздел Surprise: слот Branch-Delay

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

Представьте, что у вас есть процессор, выполняющий ровно одну инструкцию за такт. Процессор должен получить инструкцию, декодировать ее, выполнить и записать результаты. Когда один этап выполнен, он должен дождаться завершения всей инструкции, прежде чем его можно будет использовать снова. Здесь в игру вступает конвейеризация. Он разбивает выполнение одной инструкции на несколько отдельных шагов, так что несколько инструкций могут выполняться одновременно. Пока один читается, другой выполняется, записывается обратно и т. д. Это резко увеличивает использование и пропускную способность процессора. Процессор MIPS имеет пятиэтапный конвейер:

  • Fetch: взять инструкцию из памяти
  • Декодирование: установите все соответствующие провода, чтобы настроить процессор для чтения и записи в правильные места [
  • Выполнение: выполнение арифметической операции с любыми данными, которые у вас могут быть (на этом этапе ничего не делается для загрузки или сохранения).
  • Память: загрузить или сохранить в память
  • Обратная запись: запись результата в регистровый файл

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

Всякий раз, когда выполняется переход, инструкция сразу после перехода всегда выполняется. Возьмем, к примеру, следующую программу:

beq $zero, $zero, label
addi $t0, $zero, 1
label:
addi $t0, $zero, 10

Значение $t0 в конце равно 11, несмотря на то, что первая инструкция addi происходит после ветки (которая тривиально всегда выполняется). Поскольку эта инструкция выполняется всегда, конвейер может оставаться более заполненным, чем в противном случае, и, следовательно, процессор может работать немного быстрее, если есть много ветвей. В некоторых случаях нет никакой инструкции, которую вы можете поместить туда, поэтому либо вы (или компилятор), либо вставляете nop.

В наши дни слот задержки перехода не существует по нескольким причинам (многие из которых я, конечно, не знаю). Вероятно, наиболее важно то, что процессоры в наши дни все равно не запускают программы по порядку, поэтому наличие слота задержки перехода лишит некоторую гибкость порядка инструкций. Современные процессоры полны черной магии, которая заставляет их работать невероятно быстро, но они не раскрывают большую часть этого пользователю, поскольку она имеет тенденцию меняться между поколениями процессоров.

Завтра утром я попытаюсь настроить все метки и заставить их работать, что должно сделать компилятор полным. Следующая задача — несколько улучшить обработку ошибок. Конечный результат должен быть в состоянии указать пользователю, где в файле произошла ошибка, но пока мы все еще находимся в разработке и тестируем наших друзей, мы будем находиться в одной комнате или рядом, поэтому сообщений об ошибках будет меньше. важный. Это пока все!

-Питер