Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ТЯП_шпоры.doc
Скачиваний:
0
Добавлен:
01.04.2025
Размер:
3.21 Mб
Скачать

Вопрос 39

Определение 4.13. Непосредственная генерация кода – это процесс порождения машинных команд по записи программы на промежуточном языке.

Задача генерации машинного кода обычно делится на две более простые задачи:

1) расчленение операций промежуточного языка на операции, эквивалентные машинным, и представление программы в виде псевдокода;

2) формирование компьютерных команд по псевдокоду.

При формировании псевдокода по промежуточной программе можно использовать заготовки. Продемонстрируем данную методику на примере преобразования дерева операций в код на языке ассемблера.

В качестве языка ассемблера возьмем язык ассемблера процессоров типа Intel 80x86. При этом будем считать, что операнды могут быть помещены в 16-разрядные регистры процессора и в коде результирующей объектной программы могут использоваться регистры АХ (аккумулятор) и DX (регистр данных), а также стек для хранения промежуточных результатов.

Введем обозначения:

1) Code(U) - функция, реализующая перевод данного узла U дерева в последовательность команд ассемблера;

2) operl, oper2 - операнды (листья дерева);

3) U1, U2 – узлы дерева, не являющиеся листьями;

4) act - обозначение одной из арифметических операций - сложения (+), вычитания (-), умножения (*) и деления (/), которым будут соответствовать команды ассемблера add, sub, mul и div.

Тогда четырём возможным формам текущего узла дерева для каждой арифметической операции будут соответствовать фрагменты кода на языке ассемблера, приведенные в таблице 4.5.

Таблица 4.5 - Преобразование узлов дерева операций в код на языке ассемблера для арифметических операций

Таблица 4.6 - Преобразование узлов дерева операций в код на языке ассемблера для операции присвоения

Пример 4.11. Рассмотрим в качестве примера выражение X:=Y*2-Z/3. Соответствующее ему дерево операций приведено на рисунке 4.7.

Рисунок 4.7 - Дерево операций для выражения X:=Y*2-Z/3

Вопрос 40

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

Определение 4.14. Оптимизация программы - это обработка, связанная с переупорядочиванием и изменением операций в компилируемой программе с целью получения более эффективной результирующей объектной программы.

Оптимизация выполняется на этапах подготовки к генерации и непосредственно при генерации объектного кода.

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

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

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

1) объем памяти, необходимый для выполнения результирующей программы (для хранения всех ее данных и кода);

2) скорость выполнения (быстродействие) программы.

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

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

На практике для оценки эффективности результирующей программы, полученной с помощью компилятора, сравнивают ее с эквивалентной программой, полученной из исходной программы, написанной на языке ассемблера. Обычно объектная программа, построенная с помощью компилятора с языка высокого уровня, содержит на 10-30 % больше команд, чем эквивалентная ей объектная программа, построенная с помощью ассемблера, а также выполняется на 10-30 % медленнее.

Выделяют два основных вида оптимизирующих преобразований.

1 Преобразования исходной программы.

2 Преобразования результирующей объектной программы.

По типу синтаксических конструкций методы оптимизации делятся на методы преобразования:

  • линейных участков программы;

  • логических выражений;

  • вызовов процедур и функций;

  • других конструкций входного языка.

Оптимизация линейных участков программ

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

- удаление бесполезных присваиваний;

- исключение избыточных вычислений (лишних операций);

- свертка операций объектного кода;

- перестановка операций;

- арифметические преобразования.

Определение 4.15. Если в составе линейного участка программы имеется операция присвоения значения некоторой переменной А с номером i, а также операция присвоения значения той же переменной А с номером j, i<j и ни в одной операции между i и j не используется значение переменной А, то операция присвоения с номером i является бесполезной.

X:= Y/Z;

D:= Y-Z;

X:= D/Z;

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

Определение 4.16. Операция линейного участка с порядковым номером j считается лишней, если существует идентичная ей операция с порядковым номером i, i<j и никакой операнд, обрабатываемый этой операцией, не изменялся никакой операцией, имеющей порядковый номер между i и j.

Определение 4.17. Свертка объектного кода - это выполнение во время компиляции тех операций исходной программы, для которых значения операндов уже известны.

Пример 4.13. Тривиальный вариант свертки - это вычисление выражений, все операнды которых являются константами.

Выражение X:=5*Y*Z*2 в результате свертки может быть преобразовано к виду X:=10*Y*Z.

Хорошим стилем программирования является объединение вместе операций, производимых над константами, чтобы облегчить компилятору выполнение свертки.

Определение 4.18. Перестановка операций – это изменение порядка следования операций, которое может повысить эффективность программы, не изменяя конечный результат вычислений.

Пример 4.13. Операции умножения в выражении X:=5*Y*Z*2 можно переставить без изменения конечного результата и выполнить в порядке X:=(5*2)*(Y*Z). Тогда возможно выполнить свертку и сократить количество операций.

Арифметические преобразования представляют собой изменение характера и порядка следования операций на основании известных алгебраических и логических тождеств.

Пример 4.14. Выражение X:=2*Y+2*Z может быть заменено на X:=2*(Y+Z). Конечный результат при этом не изменится, но объектный код будет содержать на одну операцию умножения меньше.

К арифметическим преобразованиям относятся и такие действия, как замена возведения в степень на умножение; целочисленного умножения на константы, кратные 2, - выполнением операций сдвига. В этих случаях быстродействие программы повышается за счет замены сложных операций более простыми.