- •Университет наяновой
- •М. А. Шамашов основные структуры данных и алгоритмы компиляции
- •Предисловие
- •Введение
- •1. Краткий обзор процесса компиляции
- •2. Лексический анализ
- •0123...9 Пробел
- •3. Организация таблиц компилятора
- •3.1. Общий вид таблиц
- •3.2. Прямой доступ к таблице или метод индексов
- •3.3. Неупорядоченная таблица или метод линейного списка
- •3.4. Упорядоченная таблица. Бинарный, двоичный или логарифмический поиск
- •3.5. Сбалансированные деревья
- •3.6. Деревья оптимального поиска
- •3.7.1. Рехеширование
- •3.7.3. Метод цепочек или гроздей
- •4. Общие методы синтаксического анализа
- •4.1. Нисходящий разбор с возвратами
- •4.2. Восходящий разбор с возвратами
- •4.3. Символьный препроцессор на основе бэктрекинга
- •4.3.1. Фаза анализа и перевода грамматики во внутреннее представление
- •4.3.2. Лексичекий анализ в сп
- •4.3.3. Синтаксический анализ в сп
- •4.3.4. Выполнение семантических действий
- •5. Однопроходный синтаксический анализ без возвратов
- •5.1. Ll(k) языки и грамматики
- •5.1.1. Предсказывающие алгоритмы разбора и разбор для ll(1)-грамматик
- •5.1.2. Рекурсивный спуск
- •5.2. Языки и грамматики простого предшествования
- •Xy, если u xy
- •X y, если u xU1) (y l(u1))
- •X y, если (u u1y) (X r(u1)) or
- •5.2.1. Алгоритм Вирта–Вебера для анализа языков простого предшествования
- •5.2.2. Функции предшествования.
- •5.2.3. Проблемы построения грамматик предшествования
- •5.3. Операторная грамматика предшествования
- •6. Введение в семантику
- •6.1. Внутренние формы исходной программы
- •6.1.1. Польская инверсная запись
- •If выр then инстр 1 else инстр 2
- •6.1.2. Интерпретация полиЗа
- •6.1.3. Генерирование команд по полиЗу
- •6.1.4. Тетрады и триады
- •6.2. Семантические подпрограммы перевода инфиксной записи в полиз и аспекты их реализации
- •6.3. Семантические подпрограммы для перевода в тетрады
- •6.4. Метод замельсона–бауэра для перевода в полиз и тетрады
- •6.5. Нейтрализация ошибок
- •6.5.1. Исправления орфографических ошибок
- •6.5.2. Нейтрализация семантических ошибок
- •6.5.3. Нейтрализация синтаксических ошибок
- •7. Машинно-независимая оптимизация программ
- •7.1. Исключение общих подвыражений
- •7.2. Вычисления во время компиляции
- •7.3. Оптимизация булевых выражений
- •7.4. Вынесение инвариантных вычислений за цикл
- •8. Машинно-зависимые фазы компиляции
- •8.1. Распределение памяти
- •8.2. Генерация кода и сборка
- •8.3. Трансляция с языка ассемблера
- •Заключение
- •Список литературы
- •Содержание
- •1. Краткий обзор процесса компиляции 5
- •2. Лексический анализ 10
- •3. Организация таблиц компилятора 16
- •4. Общие методы синтаксического анализа 28
- •5. Однопроходный синтаксический анализ без возвратов 52
- •6. Введение в семантику 78
- •7. Машинно-независимая оптимизация программ 102
- •8. Машинно-зависимые фазы компиляции 109
7. Машинно-независимая оптимизация программ
Делая в первой главе обзор процесса компиляции, мы уже выделили четыре основных направления оптимизации, которые не зависят от конкретной ЭВМ и ее машинного кода, а связанны только с языком программирования и алгоритмами, представленными на этом языке. К ним относятся:
• исключение общих подвыражений в арифметических и логических выражениях операторов присваивания или условий;
• вычисления во время компиляции;
• оптимизация булевских выражений с целью формирования эффективных условных переходов;
• вынесение инвариантных вычислений за цикл.
Идеальной исходной информацией для машинно-независимой оптимизации может служить промежуточная форма программы, представленная в матрице известных нам тетрад. Каждую тетраду, при этом, лучше всего представлять элементом двухсвязного списка, содержащего ссылки на предыдущую и последующую тетрады. То есть форма тетрады при этом примет вид:
<операция> <операнд 1> <операнд 2> <результат>
<прямой указатель> <обратный указатель>
Первые три направления реализуются с помощью достаточно простых алгоритмов. Тем не менее, для того, чтобы решить, включать или не включать ту или иную схему оптимизации в компилятор необходимо сопоставить ожидаемый выигрыш от увеличения эффективности объектной программы с дополнительными накладными расходами, связанными с увеличением времени и сложности трансляции.
Ниже мы обсудим все четыре направления оптимизации. Нам кажется, что обсуждаемый здесь материал будет интересен не только с точки зрения предлагаемых алгоритмов, но и поможет Вам впредь писать более эффективные программы, для которых компьютерная оптимизация уже не понадобиться.
7.1. Исключение общих подвыражений
Сразу оговоримся, что общие подвыражения должны быть идентичными, иначе машина вряд ли их обнаружит, а также они должны содержаться в одном операторе. Например, в программе
XAB C) BB 2 YAB C )
мы не сможем исключить одинаковые элементы матрицы тетрад, соответствующие подвыражению BC, поскольку значение B изменяется между вычислениями двух выражений.
Надо также учесть тот факт, что в большинстве языков программирования есть возможность написать собственные программы обработки прерываний, которые способны изменить значения переменных в любой момент времени. Это может привести к тому, что два подвыражения будут неэквивалентными, даже в случае их расположения в одном операторе.
Рассмотрим работу алгоритма исключения общих подвыражений на конкретном примере. Пусть нам задан следующий фрагмент программы:
BA ACD(DCB);
Матрица тетрад для этого фрагмента представлена на рис. 7.1 а.
Алгоритм исключения общих подвыражений может быть следующим:
1). Упорядочить операнды симметричных операций в лексикографическом порядке, проверяя каждый элемент и меняя их местами, если они не упорядочены (в нашем примере изменяются только тетрады с номерами 3 и 4).
2). Найти границы операторов - в данном случае конец каждого блока характеризуется тетрадой с операцией присваивания - ‘’. После шагов 1) и 2) матрица примет вид, представленный на рис. 7.1 б.
3). Найти общие подвыражения (совпадающие тетрады) и исключить одно из них (в нашем примере совпадают тетрады 2, 3 и мы исключаем третью тетраду, соединяя в цепочку тетрады с номерами 2 и 4). Если одинаковых тетрад не обнаружено перейти к пункту 6) алгоритма.
4). В результирующей матрице отразить исключение элемента, изменив ссылки на его результат (в нашем случае все ссылки к M2 меняются ссылками к M1). После шагов 3) и 4) мы получим матрицу представленную на рис. 7.1 в).
5). Повторить шаги 1) - 4) алгоритма.
6). Исключить из таблицы идентификаторов все элементы временной памяти, которые нам больше не нужны (в рассматриваемом примере элемент M2).