
 
16 
• 
Попытаться ис править код после того, как он сгенерирован.  
Это понятие «щелевой» оптимизации. Основная идея в том, что известно какие 
комбинации инструкций компилятор собирается произвести и также известно 
которые из них «плохие» (такие как код для числа -1). Итак, все что нужно 
сделать — просканировать полученный код, найти такие комбинации инструкций 
и заменить их на более «хорошие». Это вид макрорасширений наоборот и 
прямой пример метода сопоставления с образцом. Единственная сложность в 
том, что может существовать множество таких комбинаций.  Этот метод 
называется «щелевой» оптимизацией просто потому, что оптимизатор работает 
с маленькой группой инструкций. «Щелевая» оптимизация может драматически 
влиять на качество кода и не требует при этом больших изменений в структуре 
компилятора. Но все же за это приходится платить скоростью, размером и 
сложностью компилятора. Поиск всех комбинаций требует проверки множества 
условий, каждая из которых является источником ошибки. И, естественно, это 
требует много времени.  
В классической реализации «щелевого» оптимизатора, оптимизация 
выполняется как второй проход компилятора. Выходной код записывается на 
диск и затем оптимизатор считывает и обрабатывает этот файл снова. 
Фактически, оптимизатор может быть даже отдельной от компилятора 
программой. Так как оптимизатор только обрабатывает код в маленьком «окне» 
инструкций (отсюда и название),  лучшей реализацией было бы буферизировать 
несколько срок выходного кода и сканировать буфер каждый раз после EmitLn.  
• 
Попытаться сразу генерировать лучший код.  
В этом методе выполняется проверка дополнительных условий перед  выводом 
кода. Как тривиальный пример, мы должны были бы идентифицировать нуль и 
выдать CLR вместо загрузки, или даже совсем ничего не делать, как в случае с 
прибавлением нуля, например. Конкретней, если мы решили распознавать 
унарный минус в процедуре Factor вместо Expression, то мы должны 
обрабатывать —1 как обычную константу, а не генерировать ее из 
положительных. Ни одна из этих вещей  не является слишком сложной для 
реализации, просто они требуют включения дополнительных проверок в код, 
поэтому я не включил их в программу. Как только мы дойдем до получения 
работающего компилятора, генерирующего полезный выполнимый код, мы 
всегда сможем вернуться и доработать программу для получения более 
компактного кода. Именно поэтому в мире существует «Версия 2.0». 
     Существует еще один, достойный упоминания, способ оптимизации, обещающий 
достаточно компактный код без излишних хлопот. Это мое «изобретение», в  том смысле, 
что я нигде не видел публикаций по этому методу, хотя я и не питаю иллюзий что это 
придумано мной.  
     Способ заключается в том, чтобы избежать частого использования стека, лучше 
используя регистры центрального процессора. Вспомните, когда мы выполняли только 
сложение и вычитание, то мы использовали регистры D0 и D1 а не стек? Это работало, 
потому для этих двух операций стек никогда не использовал более чем две ячейки.  
     Хорошо, процессор 68000 имеет восемь регистров данных. Почему бы не 
использовать их как стек? В любой момент своей работы синтаксический анализатор 
«знает» как много элементов в стеке, поэтому он может правильно ими манипулировать. 
Мы можем определить частный указатель стека, который следит, на каком уровне мы 
находимся и адресует соответствующий регистр. Процедура Factor, например, должна