А.В. Ахо, М.С. Лам, Р. Сети, Дж. Д. Ульман - Компиляторы - принципы, технологии и инструментарий (1114947), страница 102
Текст из файла (страница 102)
Сгенерированные таким образом шесть команд показаны иа рис. 6.45, а. Семантическое действие, связанное с последней сверткой в соответствии с  — В! )) М В2, вызывает Ьас/сра)сЬ Я101), 102), после чего сгенерированные команды принимают вид, показанный на рис. 6.45, б. Значение всего выражения истинно тогда и только тогда, когда достигаются безусловные переходы в командах 100 и 104, и ложно тогда и только тогда, когда достигаются команды перехода 103 и 105.
Поля целевых меток этих команд будут 508 Глава 6. Генерация промежуточного кода 100: хй х < 100 досо 101: досо 102: зй х > 200 досо 104 103: до со 104: зй х != у досо 105: доСо а) После обратной поправки 104 в команде 102 100: з1 х < 100 досо 101: доСо 102 102: з~ х > 200 досо 104 103: досо 104: хй х 1= у допо 105: досо б) После обратной поправки 102 в команде 101 Рнс. 6.45. Шаги обратных поправок заполнены позже, когда станет известно, какие действия следует предпринять в зависимости от истинности нли ложности всего выражения. и 6.7.3 Инструкции потока управления Теперь воспользуемся методом обратных поправок для однопроходной трансляции инструкций потока управления. Рассмотрим инструкции, генерируемые следующей грамматикой: Я вЂ” 1Г (В) Я ! !г (В) Я е!ае Я ! «Ы!е (В) Я ~ !В) ~ А; Ь вЂ” ЬЯ)Я Здесь Я вЂ” инструкция, Ь вЂ” список инструкций, А — инструкция присваивания, а  — булево выражение. Заметим, что должны быть и другие продукции, такие как и в случае инструкций присваивания.
Однако приведенных выше продукций вполне достаточно для иллюстрации методов, используемых при трансляции инструкций потока управления. Схема кода инструкций !1; И-е1яе и «1И!е такая же, как и в разделе 6.6. Делается неявное предположение о том, что последовательность в массиве команд отражает естественный поток управления от одной команды к следующей. Если это не 6.7. Обратные поправки 509 так, для реализации естественного последовательного потока управления должны быть вставлены явные команды переходов.
Схема трансляции на рис. 6.46 поддерживает список команд переходов, которые будут заполнены после выяснения их целевых меток, Как и на рис. 6.43, булевы выражения, генерируемые нетерминалом В, имеют два списка переходов, Влгле!Хзг и В~а!зе!Хзб соответствующие выходам из кода В по значению ~ ие и Ха!яе соответственно. Инструкции, генерируемые нетерминаламн Я и Х,, имеют списки незаполненных команд переходов (задаваемые атрибутами лехг!!зг), которые в конечном итоге будут заполнены в результате обратных поправок. В.пктцуя~ представляет собой список всех условных и безусловных переходов к команде, следующей за кодом Я в порядке выполнения. Х,.пехйзг определяется аналогично.
Рассмотрим семантическое действие 3 на рис. 6.46. Схема кода для продукции В - зтЫ!е (В) Я1 выглядиттак же, каки парис. 6.35, в. Два маркера-нетерминала М в продукции В- зяЫ!е ЛХ1 (В) Мз Б~ записывают номера команд начала кода В и начала кода Вп Соответствующие метки на рис. 6.35, в — Ьефл и В.где. Единственная продукция для М вЂ” ЛХ вЂ” е.
Действие 6 на рис. 6.46 устанавливает атрибут Мллягг равным номеру следующей команды. После выполнения тела инструкции иЫ!е Я1 управление передается в начало. Таким образом, при свертке зтЫ!е М1 (В) ЛХз 51 в Я мы выполняем обратную поправку Яндехйи, делая все целевые метки в этом списке равными М1 лт!г.
По окончании кода Я1 добавляется явный переход в начало кода В, иначе управление может "выпасть на дно". В команды В.ггиейзг вносятся обратные поправки для перехода к началу Я1 путем установки целевых меток равными Мз.изгг. Более серьезным аргументом в пользу использования Я.лех~йз! и Х,.пехг!!з! является генерация кода для условной инструкции РХ (В) 51 е!яе Яз. Если управление "выпадает на дно" Яы как, например, в случае присваивания, необходимо добавить в конце кода Я1 переход через весь код Яз.
Для генерации такого перехода после Я1 надо использовать еще один маркер-нетерминал. Пусть этим маркером будет нетерминал ЛХ с продукцией А' — е. Х имеет атрибут ЛХ.пехг!и~, который будет представлять собой список, состоящий из номера команды перехода до~о генерируемой семантическим действием 7 для Х. Семантическое действие 2 на рис.
6.46 работает с инструкциями !Х-е!яе с синтаксисом Я !Х (В) М! Я1 Ю е!яе Мз Яг Обратная поправка для перехода при истинном В состоит во внесении целевой метки М, зтв, которая представляет собой начало кода Яп Аналогично поправка перехода при ложном В назначает ему целевую метку, приводящую к началу кода Вз. Список Я.лех!!!зг включает как все переходы из Я1 и Яв„так и переход, 510 Глава 6. Генерация промежуточного кода 1) Я вЂ” (Г ( В ) М Я! ( Ьас1гра!сЫ,В.!гие1!з1, Млпзгг); Ятехйя! = тегйе(В3а(ге!!яг, Я!.пех!1и!) ) 2) Я— 11(В) Мг Я!Я е(яе Мз Яз ( Ьас)грагсй(Влгие(и!, Мг лт!г); Ьас!гра!сЫ,В~а!хе!иг, Мзлтгг); !етр = тегуе(Бг.пех!1и1, Х.пехг!и!); Б.пехг1и! = тегйе(!етр, Бз.пехг!и!) ) 3) Я- и пйе Мг ( В ) Мз Яг ( Ьас!гра!сЬ(Бг.пех!1!хг, Мг лаз!с); Ьас!грагсЬ(В ггие1!зг, Мзлтгг); Б.пех!1!х! = В~а!хе!!хг; ет!г('досо' Мглт!г) ) 4) Я вЂ” ~ ( В ) ( Я.пех!1и! = 1.пех!1!хг; ) 5) Я вЂ” А! ( Я.пехг1и! = пцН; ) 6)М- ( Млт(г = пех!!пх!г; ) 7) !'з'- ( Ь1.пех!1и! = та!ге!!з!(пех!!т! ); ет!!('досо '); ) 8)  — Т г М Я ( Ьас(гра!сЬ(1 г.пехг!иг, Млпягг); В.пехйз! = Я.пех!1!з1; ) 9) В- Я ( В.пехг1и! = Я.пехйт; ) Рис.
6.46. Трансляция инструкций генерируемый маркером Х. (Переменная !етр представляет собой временную переменную, использующуюся только для объединения списков.) Семантические действия 8 и 9 обрабатывают последовательности инструкций. В случае команда, следующая в порядке выполнения за кодом Вг,представляет собой начало кода Я. Таким образом, в команды списка Вг.пехйз! вносится целевая метка, соответствующая началу кода Я, которую мы получаем при помощи М.!тгг. В случае  — Я значение Втехйи! совпадает с Я.пех!1!з!.
6.7. Обратные поправки Заметим, что в этих семантических правилах нигде не генерируются новые команды, за исключением правил 3 и 7. Весь прочий код генерируется семантическими действиями, связанными с инструкциями присваивания и выражениями. Поток управления обеспечивает правильность обратных поправок, так что присваивания и вычисления булевых выражений оказываются корректно соединенными. 6.7.4 Инструкции ЬгеаК, сопение и по1о Наиболее элементарной конструкцией языков программирования для изменения потока управления является инструкция ао(о. В С инструкция вида доге ?.
передает управление инструкции с меткой ?.(в области видимости должна находиться ровно одна инструкция с меткой 1 ). Инструкции безусловного перехода могут быть реализованы при помощи списка незаполненных команд перехода для каждой метки с последующим внесением обратных поправок, когда положение метки становится известным. Язык программирования )ача обходится без инструкций безусловного перехода. Однако в этом языке имеются переходы, обеспечиваемые инструкциями Ьгеа)с (передающей управление за пределы охватывающей конструкции цикла) и сопГ1пие (запускающей новую итерацию охватывающего цикла).
Приведенный далее фрагмент кода лексического анализатора иллюстрирует действие этих инструкций: 1) аког( ; ; геас1сЬ() ) ( 2) 3) 4) 5) 1й ( рее)с == ' ' (1 рее)с == ''чг' ) сопсспце; е1ве Н ( рее)с == '~п' ) 11пе = 11пе + 1; е1ве Ьгеа1сз Из строки 4 управление передается команде, следующей за охватывающим циклом ?ог. Команда сопгйпце в строке 2 передает управление коду вычисления функции геас(сЬ( ) с последующим переходом к инструкции и в строке 2.
Если с представляет собой охватывающий цикл, то инструкция Ьгеа)с приводит к переходу к первой команде после о. Код для Ьгеа)с можно сгенерировать путем 1) отслеживания охватывающего цикла о, 2) генерации незаполненных переходов для инструкции Ьгеаа и 3) помещения этих незаполненных команд в список а.лехи(хб где пехййц имеет тот же смысл, что и в разделе 6.7.3. В случае двухпроходной начальной стадии компилятора, строящей синтаксические деревья, о'.лехйп( можно реализовать в виде поля в узле с'.