Алексей Молчанов - Системное программное обеспечение. Лабораторный практикум
- Название:Системное программное обеспечение. Лабораторный практикум
- Автор:
- Жанр:
- Издательство:Array Издательство «Питер»
- Год:2005
- Город:Санкт-Петербург
- ISBN:978-5-469-00391-4
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Алексей Молчанов - Системное программное обеспечение. Лабораторный практикум краткое содержание
Книга ориентирована на студентов, обучающихся в технических вузах по специальностям, связанным с вычислительной техникой. Но она будет также полезна всем, чья деятельность так или иначе касается разработки программного обеспечения.
Системное программное обеспечение. Лабораторный практикум - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
Для унарных линейных операций последовательность действий при генерации ассемблерного кода такова:
1. Запоминается имя операнда. Для переменных именем операнда является имя переменной, для константы – значение константы, а для ссылки на другую триаду, кроме предыдущей, – имя регистра или временной переменной, в которой хранится результат вычисления триады (для предыдущей триады имя операнда пустое).
2. Если имя операнда не пустое, то операнд надо загрузить в регистр eax. Для этого порождается команда mov, но если операнд – результат вычисления предыдущей триады (имя операнда пустое), то загружать в eax его не нужно, так как он уже находится там после вычисления триады, и никакая команда на этом шаге не порождается.
3. Порождается команда, соответствующая унарной операции над регистром eax (в данном результирующем языке: not – для логического отрицания; neg – для унарного арифметического минуса).
4. Если одной команды недостаточно, порождается еще одна команда (в данном случае для логического отрицания требуется еще команда and).
5. Если для триады требуется сохранить промежуточный результат, порождается команда mov, которая сохраняет результат из регистра eax в регистр или временную переменную, связанную с триадой.
Для бинарных линейных операций последовательность действий при генерации ассемблерного кода такова:
1. Запоминаются имена обоих операндов. Для переменных именем операнда является имя переменной, для константы – значение константы, а для ссылки на другую триаду, кроме предыдущей – имя регистра или временной переменной, в которой хранится результат вычисления триады (для предыдущей триады имя операнда пустое).
2. Если имя одного из операндов пустое (операнд получен при вычислении предыдущей триады), то нет необходимости загружать его в регистр eax, иначе порождается команда mov, которая загружает первый операнд в регистр eax.
3. Порождается команда, соответствующая бинарной операции над регистром eax. Если имя второго операнда пустое, то первый операнд триады становится вторым операндом команды, иначе – второй операнд триады становится вторым операндом команды.
4. Если одной команды недостаточно, порождается еще одна (в данном результирующем языке это необходимо только для команды вычитания sub в том случае, если операнды менялись местами – чтобы получить верный результат, требуется еще команда neg).
5. Если для триады требуется сохранить промежуточный результат, порождается команда mov, которая сохраняет результат из регистра eax в регистр или временную переменную, связанную с триадой.
Определение имени операнда выполняется вспомогательной функцией GetOpName. Порождение ассемблерного кода выполняется функцией MakeOper1 – для унарных операций, и функцией MakeOper2 – для бинарных операций. Можно обратить внимание, что функция GetOpName проверяет имя переменной на совпадение его с предопределенным именем CompileTest, и если имена совпадают, заменяет имя переменной на предопределенное имя Result. Эта проверка и подстановка – простейший пример модификации компилятором результирующего кода в зависимости от семантических соглашений (предопределенное имя Result всегда обозначает результат функции в выходном языке). В промышленных компиляторах такие модификации, как правило, связаны с неявными преобразованиями типов данных, принятыми во входном языке.
Последовательность порождения ассемблерного кода для триад, представляющих линейные операции, практически не зависит от внутреннего представления программы и может быть использована для любых типов триад, соответствующих линейным операциям (от типа триады зависит только тип порождаемой ассемблерной команды).
Для триад присваивания значений и для триад безусловного перехода (JMP) порождение команд элементарно просто и не требует пояснений.
Для операций сравнения интерес представляет получение результата, поскольку при выполнении команд сравнения в различных процессорах результатом, как правило, являются биты в специальном регистре – регистре флагов. Биты в регистре флагов могут быть непосредственно использованы в командах условных переходов, и если компилятор порождает код для логических операций, основанный на порядке их вычисления (неполное вычисление логических выражений было рассмотрено ранее), то он может этим воспользоваться. Но когда операции сравнения обрабатываются как линейные операции, нужно загрузить результат из регистра флагов в регистр общего назначения. Для этого также можно использовать условные переходы, например для триады типа:
1: < (a, b)
можно построить по
mov eax, a
cmp eax, b
jl @M1_1
xor eax, eax
jmp @M1_2
@M1_1: xor eax, eax
inc eax
@M1_2:
которая будет обеспечивать запись в регистр аккумулятора (eax) логического результата операции сравнения (0 – «ложь», 1 – «истина»).
Однако, как уже было сказано, большое количество операций передачи управления не способствует эффективности выполнения программы. К тому же рассмотренный выше подход порождает много лишних команд. Как правило, в процессорах есть команды, позволяющие организовать либо прямой обмен между регистром флагов и регистром аккумулятора, либо обмен данными через стек. В процессорах типа Intel 80x86 это команды группы set<*>, где <*> зависит от необходимого флага [41, 44]. Тогда для того же самого примера порядок команд будет иным:
mov eax, a
cmp eax, b
setl al
and eax, 1
В предлагаемом генераторе кода используется именно такой подход. А в остальном порождение кода для операций сравнения не отличается от порождения кода для прочих линейных операций.
Еще несколько слов необходимо сказать о триаде условного перехода IF. Для нее ситуация иная, чем для операций сравнения – чтобы выполнить условный переход, надо установить регистр флагов на основе регистра аккумулятора. Для этого можно воспользоваться простейшей командой процессора для сравнения регистра аккумулятора с ним самим, например:
test eax, eax
однако эффективность результирующего кода можно увеличить, если учесть, что триаде IF всегда предшествует либо триада сравнения, либо триада логической операции, а следовательно, при выполнении кода, порожденного для этих триад, флаги уже будут установлены соответствующим образом. Тогда нет необходимости порождать дополнительную команду для установки флагов и для триады IF достаточно построить только команду условного перехода по флагу «ноль» (в процессорах типа Intel 80x86 это команда jz).
Читать дальшеИнтервал:
Закладка: