А.В. Ахо, М.С. Лам, Р. Сети, Дж. Д. Ульман - Компиляторы - принципы, технологии и инструментарий (1114947), страница 104
Текст из файла (страница 104)
6.9 Промежуточный код процедур Процедуры и их реализация будут подробно рассматриваться в главе 7 вместе с управлением памятью для имен в процессе выполнения программы. В этом разделе для процедуры, которая возвращает значение, будет использоваться термин 518 Глава 6. Генерация промежуточного кода "функция". Здесь мы вкратце рассмотрим объявления функций и трехадресный код для их вызовов. В трехадресном коде вызов функции раскрывается в вычисление параметров при подготовке к последующему вызову функции. Для простоты считаем, что все параметры передаются по значению (методы передачи параметров рассматриваются в разделе 1.6.6).
Пример 6.25. Предположим, что а — массив целых чисел и что й — функция от целочисленного значения, возвращающая целое число. Тогда присваивание и = 1[а[1))' может быть транслировано в следующий трехадресный код: 1) Пз = з. * 4 2) сз = а [ п~ ) 3) рагааз сз 4) ~з = са11 й, 1 5) п=пз Две первые строки вычисляют значение выражения а[11 во временную переменную ~з, как рассматривалось в разделе 6.4.
Строка 3 делает переменную гз фактическим параметром вызова функции й с одним параметром в строке 4. Строка 4 также присваивает значение, возвращаемое функцией, переменной Пз, а в строке 5 зто значение присваивается переменной и. и Продукции на рис. 6.52 делают возможными определения функций и их вызовы. (Приведенный синтаксис генерирует лишнюю запятую после последнего параметра, но для дидактических целей вполне пригоден.) Нетерминалы Р и Т генерируют соответственно обьявления и типы, как в разделе 6.3. Определение функции, генерируемое нетерминалом Р, состоит из ключевого слова пейпе, возвращаемого типа, имени функции, формальных параметров в скобках и тела функции, представляющего собой инструкцию в фигурных скобках. Нетерминал Е генерирует несколько формальных параметров [возможно, нуль); каждый формальный параметр состоит из типа, за которым следует идентификатор.
Нстсрминазы Я и Е генерируют соответственно инструкции и выражения. Продукция для Я добавляет инструкцию, возвращающую значение выражения, а продукция для Š— вызов функции с фактическим параметром, генерируемым А. Фактический параметр представляет собой выражение. Определения и вызовы функций могут транслироваться с использованием концепций, которые уже рассматривались в данной главе. ° Типы функций. Тип функции должен кодировать возвращаемый тип и типы формальных параметров. Обозначим через тои[ специальный тип, означающий отсутствие параметра или возвращаемого значения.
Таким образом, 519 6.9. Промежуточный код процедур 1) — ахейце Т и1 (Е) 1о') Е ~тЫ,Е Я вЂ” гетпгп Е; Š— Ы (А) А — е~Е, А Рис. 6.52. Добавление функций в исходный язык программирования тип функции рор О, которая возвращает целое число, — "функция от гоЫ, возвращающая 1пгеяер'. Типы функций могут быть представлены с применением конструктора)йп к возвращаемому типу и упорядоченному списку типов параметров. ° Таблицы символов.
Пусть в — текущая таблица символов при достижении определения функции. Имя функции вносится в таблицу символов в для использования в оставшейся части программы. Формальные параметры функции могут быть обработаны точно так же, как и имена полей записи 1см. рис. 6.18). В продукции 0 после дебпе и имени функции мы заносим в в стек и создаем новую таблицу символов Епжригп 1гор); гор = петг Епг (!ор); Назовем новую таблицу символов 1. Обратите внимание, что юр передается в качестве параметра в вызов пен Ели(1ор), так что новая таблица символов 1 может быть связана с предыдущей — в. Новая таблица символов 1 используется для трансляции тела функции. После трансляции тела функции мы вернемся к предыдущей таблице символов в. ° Проверка типов.
В выражениях функция рассматривается как и любой другой оператор. Таким образом, материал из раздела 6.5.2, включая неявные преобразования типов, распространяется и на функции. Например, если 1' — функция с параметром, представляющим собой действительное число, то при вызове Т" (2) целое число 2 преобразуется в действительное число. ° Вызовы функций. При генерации трехадресных команд для вызова функции Ы (Е, Е,..., Е) достаточно сгенерировать трехадресные команды для вычисления или преобразования параметров Е в адреса, за которыми следуют команды рагааз для каждого из параметров. Если мы не хотим смешивать команды вычисления параметров и команды рагат, то для каждого выражения Е можно сохранить атрибут Е.агЫг в структуре данных вроде очереди.
После того, как все выражения транслированы, генерируются команды ракааз до полного опустошения очереди. 520 Глава 6. Генерация промежуточного кода Процедуры — настолько важная и часто используемая программная конструкция, что от компилятора, безусловно, требуется генерация эффективного кода для вызова процедур и возврата из них. Подпрограммы времени выполнения, обрабатывающие передачу параметров, вызовы и возвраты, являются частью пакета поддержки времени выполнения. Механизмы поддержки времени выполнения рассматриваются в главе 7.
6.10 Резюме к главе 6 Методы, описанные в этой главе, могут быть обьединены для построения простой начальной стадии компилятора наподобие приведенной в приложении А. Начальная стадия компилятора может строиться инкрементно.
+ Выбор промежуточного представления. Промежуточное представление обычно является некоторой комбинацией графических обозначений и трех- адресного кода. В графическом представлении синтаксических деревьев узлы представляют конструкции языка; дочерние узлы представляют их подконструкции. Трехадресный код получил свое название от команд вида гс = у ор г, с не более чем одним оператором в команде. Имеются также дополнительные команды для потока управления.
+ Трансляция выражений. Выражения со встроенными операциями могут быть развернуты в последовательность отдельных операций путем связывания действий с каждой продукцией вида Š— Е1 ор Ез. Действие либо создает узел для Е с дочерними узлами Е1 и Ез, либо генерирует трехадресную команду, которая применяет ор к адресам Е1 и Ез и помещает результат в новое временное имя, которое становится адресом Е.
+ Проверка типов. Тип выражения Е1 ор Ез определяется оператором ор и типами Е1 и Ез. Зачастую применяется неявное преобразование типов, такое как преобразование т~еяег в роак Промежуточный код содержит явные преобразования типов, обеспечивающие точное соответствие типов операндов типам, ожидаемым оператором. + Использование таблицы символов для реализации объявлений. Объявление определяет тип имени.
Размер типа равен количеству памяти, необходимой для имени с данным типом. Использование размеров позволяет вычислять относительные адреса имен времени выполнения как смещения от начала области данных. Тип и относительный адрес имени помещаются в таблицу символов объявлением, так что в дальнейшем транслятор может использовать их всякий раз, когда это имя встречается в выражении. 52! б.11. Список литературы к главе б + Массивы.
Для быстрого обращения элементы массивов хранятся в последовательных ячейках памяти. Массивы массивов располагаются таким образом, что могут рассматриваться как одномерные массивы отдельных элементов. Тнп массива используется для вычисления адресов элементов массива относительно его базы. + Генерация кода с переходами для булевых выражений. При сокращенных вычислениях или в коде с переходами значение булева выражения неявно определяется достигнутой позицией кода. Применимость кода с переходами обусловлена тем, что обычно булевы выражения используются для потока управления, как в случае !!' (В) Я.
Булево выражение может быть вычислено путем перехода к команде Г = сх не или Г = йа1ве, где Г.— временное имя. При использовании меток переходов булево выражение может быть транслировано при помощи наследуемых меток, соответствующих переходам по истинному и ложному значениям. Константы 1гие и Га1зе транслируются в переходы к соответствующим меткам. + Реализация инструкций с использованием потока управления.
Инструкции могут транслироваться с использованием наследуемой метки пехд которая маркирует первую команду после кода данной инструкции. Условная инструкция Я вЂ” !!' [В) Ь| может транслироваться путем назначения новой метки, маРкиРУющей начало кода Яы и использованиа ее и Я.пех! в качестве переходов для истинного и ложного значений В соответственно. + Использование обратных поправок. Обратные поправки представляют собой метод однопроходной генерации кода булевых выражений и инструкций. Идея заключается в поддержании списков незавершенных команд переходов, так что все команды в списке имеют одну и ту же целевую метку. Когда эта метка становится известной, выполняется завершение команд из списка путем заполнения их полей целевой метки. + Реализация записей.
Имена полей в записи или классе могут рассматриваться как последовательности объявлений. Тип записи кодирует типы н относительные адреса полей. Для этой цели может использоваться таблица символов. 6.11 Список литературы к главе 6 Большинство описанных в этой главе методов берут свое начало из бурной деятельности, сопровождавшей появление А!яо! бО. Синтаксически управляемая трансляция в промежуточный код твердо устоялась во времена создания Рааса! [11] и С [б и 9).