
- •Содержание
- •Введение
- •1.3. Множества цепочек
- •1.4. Языки
- •1.5. Алгоритмы
- •1.6. Некоторые понятия теории графов
- •Контрольные вопросы
- •2. Введение в компиляцию
- •2.1. Задание языков программирования
- •2.2. Синтаксис и семантика
- •2.3. Процесс компиляции
- •2.4. Лексический анализ
- •2.5. Работа с таблицами
- •Переменная с плавающей точкой
- •2.6. Синтаксический анализ
- •2.7. Генератор кода
- •2.8. Оптимизация кода
- •2.8. Оптимизация кода
- •2.9. Исправление ошибок
- •2.10. Резюме
- •Контрольные вопросы
- •3. Теория языков
- •3.1. Способы определения языков
- •3.2. Грамматики
- •3.4. Распознаватели
- •3.5. Регулярные множества, их распознавание и порождение
- •5.2. LR(1) - таблица разбора
- •5.3. Построение LR – таблицы разбора
- •5.4. Сравнение LL – и LR – методов разбора
- •Контрольные вопросы
- •6. Оптимизация кода
- •6.1. Оптимизация линейного участка
- •6.1.1. Модель линейного участка
- •6.1.2. Преобразование блока
- •6.1.3. Графическое представление блоков
- •6.1.4. Критерий эквивалентности блоков
- •6.1.5. Оптимизация блоков
- •6.1.6. Алгебраические преобразования
- •6.2. Арифметические выражения
- •6.2.1. Модель машины
- •6.2.2. Разметка дерева
- •6.2.3. Программы с командами STORE
- •6.2.4. Влияние некоторых алгебраических законов
- •6.3. Программы с циклами
- •6.3.1. Модель программы
- •6.3.2. Анализ потока управления
- •Алгоритм вычисления прямого доминирования
- •6.3.3. Примеры преобразования программ
- •Удаления бесполезных операторов
- •Замена сложных операций
- •6.3.4. Оптимизация циклов
- •Перемещение кода
- •Индуктивное перемещение
- •Замена сложных операций
- •6.4. Анализ потоков данных
- •6.4.1. Интервалы
- •6.4.2. Анализ потоков данных с помощью интервалов
- •6.4.3. Несводимые графы управления
- •7. Включение действий в синтаксис
- •7.1. Получение четверок
- •7.2. Работа с таблицей символов
- •Контрольные вопросы
- •8. Проектирование компиляторов
- •8.1. Число проходов
- •8.2. Таблицы символов
- •8.3. Таблица видов
- •Контрольные вопросы
- •9. Распределение памяти
- •9.1. Стек времени прогона
- •9.2. Методы вызова параметров
- •9.3. Обстановка выполнения процедур
- •9.4. «Куча»
- •9.5. Счетчик ссылок
- •9.6. Сборка мусора
- •Контрольные вопросы
- •10. Генерация кода
- •10.1. Генерация промежуточного кода.
- •10.2. Структура данных для генерации кода
- •10.3. Генерация кода для типичных конструкций
- •10.3.1. Присвоение
- •10.3.2. Условные зависимости
- •10.3.3. Описание идентификаторов
- •10.3.4. Циклы
- •10.3.5. Вход и выход из блока
- •10.3.6. Прикладные реализации
- •10.4. Проблемы, связанные с типами
- •10.5. Время компиляции и время прогона
- •Контрольные вопросы
- •11. Исправление и диагностика ошибок
- •11.1. Типы ошибок
- •11.2. Лексические ошибки
- •11.3. Ошибки в употреблении скобок
- •11.4. Синтаксические ошибки
- •11.5. Методы исправления синтаксических ошибок
- •11.6. Предупреждения
- •11.7. Сообщения о синтаксических ошибках
- •11.8. Контекстно-зависимые ошибки
- •11.9. Ошибки, связанные с употреблением типов
- •11.10. Ошибки, допускаемые во время прогона
- •Контрольные вопросы
- •Список литературы
142
6.3.2.Анализ потока управления
Первый шаг на пути оптимизации программ заключается в определении потока управления внутри программы. Для того чтобы сделать это, разобьем программу на группу операторов ,такчтобы внутри группу управление передавалось только на первый оператор, и если он выполнился, все остальные операторы группы выполняются последовательно. Такую группу операторов будем называть линейным участком, или просто участком.
Определение. Оператор S в программе P называется входом в линейный участок, если он
1)первый оператор в P или
2)помечен идентификатором, появляющимся после goto в операторе перехода либо в условном операторе, или
3)непосредственно следует за условным оператором.
Линейный участок, относящийся к входу в участок S, состоит из S
ивсех операторов , следующих за S,
1)вплоть до оператора останова и включая его, или
2)вплоть до входа в следующий линейный участок, но не вклю-
чая его.
Пример. |
read p |
Участок 1 |
|
|
read q |
Участок 2 |
цикл:r ¬ remainder(p, q) |
|
if r = 0 goto выход |
Участок 3 |
p ¬ q |
|
q ¬ r |
|
goto цикл |
участок 4 |
выход: write q |
|
halt |
Из участков программы сконструировать граф, весьма похожий на блок-схему программы.
Определение. Графом управления назовем помеченный ориентированный граф G, содержащий выделенную вершину n, из которой достижима каждая вершина G. Вершину n назовем начальной.
Граф управления программы – это граф управления, в котором каждая вершина соответствует какому-нибудь участку программы. Предположим, что вершины i и j графа управления соответствуют участкам i и j программы. Тогда дуга идет из вершины i в вершину j, если

143
1)последний оператор участкаi не является ни оператором перехода, ни оператором перехода, ни оператором останова, а участок j следует за участком i, или
2)последний оператор участка i является оператором goto L, где L -метка первого оператора участка j.
Участок, содержащий первый оператор программы, назовем
начальной вершиной.
Ясно, что любой участок , недостижимый из начальной вершины, можно удалить из данной программы, не меняя ее значения. Далее будем считать, что все такие участки уже удалены из программы.
Пример. Граф управления программой изображен на рис. 11.14.
read p
Участок 1
read q
цикл: r ¬ remainder(p, q) |
Участок 2 |
|
if r=0 goto выход |
||
|
||
|
|
|
p ¬ q |
|
выход:write q |
Участок 4 |
Участок 3 |
q ¬ r |
|
halt |
|
|
|
|||
|
goto цикл |
|
|
|
|
|
|
Рис. 11.14. Граф управления.
Многие оптимизирующие преобразования программ требуют знания тех мест в программы, где переменная определяется, и тех, где на ее определение есть ссылка.
Эти связи между определением и ссылкой зависят от последовательности выполняемых участков. Первым участком в последовательности будет начальная вершина, а в каждый должна вести дуга из предыдущего. Иногда предикаты, используемые в условных операторах, могут запрещать переходы по некоторым путям в графе управления. Однако, алгоритма для выявления всех таких операций нет, и мы будем предполагать, что нет «запрещенных путей».
Удобно так же знать, существует ли для участка , такой участок
¢, что всякий раз когда выполняется , перед ним выполняется ¢. В
частности, если мы знаем это и если в обоих участках и ¢ вычисля-

144
ется одно и то же значение, можно заполнить его после вычисления
¢и избежать тем самым перевычисления его в . Формализуем эти предположения.
Определение. Пусть F-граф управления, имена участков которого выбираются из некоторого множества D.
Последовательность участков 1, 2, . n из D назовем путем вычисления (участков) в F, если
1)1- начальная вершина в F,
2)для 1< i £ n существует дуга , ведущая из i-1 в i.
Другими словами, путь вычисления 1, 2, . n – это путь в F из
1 в n в котором 1 - начальная вершина.
Будем говорить, что участок ¢ доминирует над участком , если
¢ ¹ и каждый путь, ведущий из начальной вершины в , содержит
¢.
Будем говорить, что ¢ прямо доминирует над , если
1)¢ доминирует над и
2)если ² доминирует над и ² ¹ ¢, то ² доминирует над
¢.
Таким образом, участок ¢, прямо доминирует над , если – «бли-
жайший» к ¢ участок, доминирующий над .
Пример. На рис. ??? – это путь вычисления. Участок 1 прямо доминирует над участком 2 и доминирует над участками 3 и 4. Участок 2 прямо доминирует над участком 3 и 4.
Лемма.
1)Если 1 доминирует над 2, а 2 над 3, то 1 доминирует над 3 (транзитивность).
2)Если 1 доминирует над 2, то 2 не доминирует над 1 (асимметричность).
3)Если 1 и 2 доминируют над 3, то либо 1 доминирует над
2, либо 2 над 1.
Лемма. Каждый участок, кроме начальной вершины, имеет единственный прямой доминатор.
Алгоритм вычисления прямого доминирования
Вход. Граф управления F и множество D = { 1, 2, . }.
145
Выход. Прямой доминатор DOM( ) участка для которого 'D, кроме начальной вершины.
Метод. DOM( ) вычисляется рекурсивно для каждого из D–{ 1}.
В любой момент DOM( ) будет участком ближайшим к . Среди всех участков, для которых уже известно, что они доминируют над . В
конечном итоге DOM( ), будет прямым доминатором участка . Вна-
чале DOM( ) – это 1 для всех из D–{ 1}. Для i =1,2,3..n выполняются следующие два шага:
1) Исключаем участок i из F. Находим все участки , ставшие теперь недостижимыми из начальной вершиныF. Участок i домини-
рует над тогда и только тогда , когда становится недостижимым
из начальной вершины после исключения i из F. Снова заносим i в
F.
2) Предположим, что на шаге 1) обнаружено, что i доминирует над . Если DOM( )=DOM( i ), берем i в качестве DOM( ). В про-
тивном случае DOM( ) не меняем.
Пример. Применим данный алгоритм к графу управления алгорит-
ма Евклида. Здесь D={ 1, 2, |
3, 4}. Последовательные значения |
|||
DOM( ) после исследований |
участков i 2£ i £ 4 приведены в табл. |
|||
11.5. |
|
|
|
|
Таблица 11.5. |
|
|
|
|
I |
DOM( 2) |
|
DOM( 3) |
DOM( 4) |
Вначале |
1 |
|
1 |
1 |
2 |
1 |
|
2 |
2 |
3 |
|
|||
4 |
1 |
|
2 |
2 |
|
1 |
|
2 |
2 |
Вычислим строку 2. После исключения 2 участки 3 и 4 стано-
вятся недостижимые. Таким образом, 2 доминирует над 3 и 4. Пе-
ред этим DOM( 2) = DOM( 3) = 1, тогда в соответствии с шагом 2
выберем 2 в качестве DOM( 3). Аналогично, полагаем 2 =
DOM( 4). Исключение 3 и 4 не делает никакой участок недостижимым.