07.1 Conspect (ru)

Подпрограммы: 1. Концевые 2. Не концевые (вызывает другую подпрограмму) => мы не можем использовать один и тот же регистр => мы используем стек.

Проблема нехватки реестров. Мы не можем выделить массив данных, а затем его развыдеделить.

Размещаем данные на стеке точно также, как мы размещаем временные переменные.

Регистры бывают: 1. Временными 2. Постоянными

Конвенция о хранении данных:

Все данные, которые нам нужны мы заносим в стек, если нам понадобится динамично выделять какие-то данные, мы их выделяем на стеке, перед выходом из подпрограммы мы все освобождаем и там же на стеке мы храним адрес возврата (регистр $ra).

Какие у этой схемы главное проблемы? • Мы должны постоянно вычислять по какому смещению находятся все наши локальные переменные; все наши сохраненные регистры; параметры нашей функции. С каждым доступом в стек, наши числа меняются. • Мы должны все локальные переменные отдельно очищать в эпилоге, эпилог нужно очистить, нужно вернуть стек в то состояние, что бы его вершина указывала на последний сохраненный регистр.

Пожертвовав регистром frame pointer ($fp), в который будем записывать значение стека, в тот момент, когда будем вызывать функцию. Он не меняется в этот момент, он как был, так и продолжает указывать на то место, на которые указывал stack pointer в момент вызова функции

Frame pointer. • Сохраняет значение регистра $sp перед преамбулой. Такой регистр относительно, которого вся наша адресация постоянная, если $fp = $sp всегда все переменные будут статичными от $fp и никогда не поменяются => все переменные переданные через стек будут адресоваться с известными константным смещением относительно frame pointer’ а с положительным смещением, а остальное, что мы занесем в стек относительно fp будет с отрицательным смещением, но также постоянным. • Нам не нужно калькулировать не только то смещение, которое соответствует нашим данным на стеке, нам ещё не нужно помнить сколько мы их туда напихали в эпилоге. • Положим значение fp в sp, тем самым выкинем все, что мы туда напихали за время пролога. Вместо того, чтобы постоянно вести учет кол-ва данных, положенных в sp, мы полагаемся на то, что мы не испортили fp и в sp лежит то самое значение sp из которого можно взять один адрес возврата и вернуть обратно.

+13 Пунктов конвенции (это не предел, т.к всего их 256) 1. Если меньше 4 параметров функции – передаем через $a0..$a3 2. Если больше 4 параметров функции – передаем через стек 3. Вызываем функцию всегда через jal (jump and link) 4. Ограничение: не портим стек ниже того места, что нам пришло, кадры чужой функции мы не портим, иначе будет все плохо 5. Нужно сохранить fp с которого мы стартовали, ибо он не наш, т.к это указатель на frame pointer предыдущей функции, которая вызвала нас. Он добавляется в список того, что нужно сохранить. Конвенция предусматривает, что мы просто записываем fp по адресу -4 от sp -4($sp) => в то место, куда сейчас попадет вершина стека, на запоминаем именно это значение в fp 6. Запоминаем $sp в $fp, чтоб когда мы вспоминали, нам не пришлось ничего оттуда вычитать 7. Сначала выделяем всю память, то есть прибавить к $sp большое отрицательное число (-24 например – память на 6 мест: одно для хранения старого fp, остальное для ra и других регистров) 8. После выделения памяти – сохраняем все остальные регистры $ra , $s* и локальные переменные 9. Возвращаем значения в $v0 ($v1) 10. Восстанавливаем значения $ra и $s* 11. Чтобы восстановить значения sp на то, которое у нас было на момент вызова функции, мы просто перекидываем fp, т.к это и есть начало нашего кадра 12. Return with jr $ra 13. Если у нас была преамбула, не забываем очистить стек от того, что у нас было внутри преамбулы.

.eqv

Это директива. Это препроцесс. 1. Идет сначала .eqv после идентификатора string(например), а потом некая строчка (value). Это очень коротокое macro. Ассемблер просто заменяет string(из примера) на это значение(value) 2. Смешение секций .data и .text полезно, когда у нас большая программа и элементы из .data и элементы из .text нужно писать вместе. 1.png

3. Мелкое замечание: никогда не используйте метку “b” в Марсе, т.к у него едет крыша и он её путает с инструкцией. 4. .eqv может быть объявлена где и когда угодно

Industrial conventions предназначены не для людей, а для машин => Industrial ABI описывает код для компиляторов (таких как C compiler), который они должны генерировать. • Фиксированный размер фрэйма – очень удобно, т.к мы всегда знаем в каком месте стека лежат параметры функции Строки – это последовательность символов/байтов

Существует договоренность: конец строки это 0 байт, но не “0”, а именно байт операции над сторонами: • Ввод • Вывод • Инструкция asciiz – размещает в памяти строку и в конце строки ставит 0 байт • Инструкция ascii - размещает в памяти строку

Macros

Macro: это кусок кода по имени “name”, который будет вставляться каждый раз, когда “name” будет встречаться в тексте. Работает во время препроцессинга.

Параметрический макро Каждому макро вставить соответствие какое-то кол-во параметров, которые потом можно представить.

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

Macro подстановки Можно вызвать macros внутри macros’а.

HSE/ArchitectureASM/07_FrameAndMacros/Conspect (last edited 2020-06-24 17:14:02 by FrBrGeorge)