- •Университет наяновой
- •М. А. Шамашов основные структуры данных и алгоритмы компиляции
- •Предисловие
- •Введение
- •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.4. Вынесение инвариантных вычислений за цикл
Если вычисления внутри цикла зависят от переменной, которая в этом цикле не изменяется, то это вычисление можно вынести из цикла Это приведет к реорганизации части матрицы тетрад. При этом необходимо решить три задачи:
Распознавание инвариантных вычислений – это наиболее сложная из них. Например, требуется определить, что можно вынести из цикла в следующей программе:
FOR i 1 TO 10 DO BEGIN
FOR j 1 TO 10 DO BEGIN
LA A 10;
LB B Y[i +2];
LC C C+10
END;
END;
Оператор с меткой LA приводит всегда к одному и тому же результату и поэтому может быть вынесен за цикл. В операторе LB индексное выражение i + 2 также не меняется при выполнении внутреннего цикла и может быть без ущерба вынесено из него. Можно также проверить, изменяется ли во внутреннем цикле значение Y и B и если они не меняется, то за внутренний цикл можно вынести весь оператор с меткой LB. Оператор LC включает в себя переменную C, значение которой меняется во внутреннем цикле, и, следовательно, этот оператор из цикла выносить нельзя. Заметим, что, если перед LA имеются операторы (внутри цикла), которые ссылаются на A или B, то в этом случае ничего кроме вычисления индексного выражения мы из цикла вынести не сможем.
Определение места, куда выносить инвариантное вычисление. В общем случае такое вычисление необходимо помещать в ту часть программы, которая непосредственно предшествует циклу. Для этого необходимо, просматривая матрицу тетрад, найти точку начала цикла. Поэтому семантическая фаза должна специальным образом отмечать элементы матрицы, соответствующие началу каждого цикла и в каждом таком элементе должна быть обратная ссылка, с помощью которой можно оптимизировать процесс выхода во внешний цикл.
Вынесение инвариантных вычислений. Используя подход, аналогичный методу исключения общих подвыражений, можно исключить инвариантное вычисление из исходной позиции в матрице и поместить его в соответствующие места.
Для циклов можно предложить и другие методы оптимизации. Иногда два цикла можно объединить в один, уменьшив таким образом число команд проверки и приращения значения переменной цикла. Например, следующие операторы:
FOR i 1 TO 100 DO A[ i ] 0
FOR j 1 TO 100 DO B[ j ] 3C[ j ]
можно объединить в один цикл:
FOR k 1 TO 100 DO BEGIN A[ k ] 0 B[ k ] 3C[ k ] END;
В некоторых случаях один цикл можно разбить на два, из которых будет выполняться только один. Например, цикл
FOR i 1 TO 100 DO
IF X > Y THEN A[ i ] B[ i ] + X
ELSE A[ i ] B[ i ] Y;
можно превратить в
IF X > Y THEN
FOR i 1 TO 100 DO A[ i ] B[ i ] + X
ELSE
FOR i 1 TO 100 DO A[ i ] B[ i ] X;
В преобразованной программе условный оператор выполняется лишь один раз, в то время как в исходной он бы исполнялся 100 раз.
Внутри циклов целесообразно по возможности уменьшать силу операций. Например цикл
FOR i 1 TO 100 DO Q[ i ] i 5;
можно заменить на
J5; FOR i 1 TO 100 DO BEGIN Q[ i ] J; J J 5 END;
Умножение i 5 в исходном цикле заменено более быстрым сложением: J 5.
Трудно найти компилятор, в котором были бы реализованы все перечисленные методы оптимизации. Да, скорее всего, это и не нужно, - такой компилятор работал бы чрезвычайно медленно. Мы здесь лишь коснулись возможных методов оптимизации, не давая готовых рецептов реализации. От вашего учебного компилятора не потребуется оптимизировать все и вся. Этот материал дан в первую очередь для того, чтобы вы сами разрабатывали более эффективные программы, не смотря на сверхпроизводительность вашего компьютера. Эффективное программирование – это немаловажный фактор, по которому судят о квалификации программиста.