Анимация
JavaScript


Главная  Библионтека 

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 [ 52 ] 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69

10.6. В АПЛ все знаки операций имеют одинаковый приоритет, но выражения вычисляются справа налево, т. е.

a + b X c

вычисляются как

a + (b X c)

Как это влияет на реализацию?

10.7. В некотортх ранних компиляторах тип каждого идентификатора включался в текст рядом с самим идентификатором. Каковы преимущества и недостатки такого метода?

10.8. Опишите, как может быть реализован оператор case или switch в том языке, с которым вы знакомы.

10.9. Опишите, как может быть реализован оператор goto.

10.10. Прокомментируйте с точки зрения эффективности вид команд промежуточного кода, рассмотренных в данной главе.



Г л а в а 11. ГЕНЕРАЦИЯ МАШИННОГО КОДА

Эту главу мы посвящаем конечной задаче компилятора - выработке машинного кода для конкретной машины. Для простаты будем считать, что вырабатывается код Ассемблера (что вполне реально). Допустим, промежуточный код (или объектный код), из которого вырабатывается код Ассемблера, не зависит от машины (но может зависеть от языка). В целом любая команда промежуточного кода влечет за собой появление последовательности команд Ассемблера, в том числе и вызовы подпрограмм Ассемблера. Трансляция каждой команды промежуточного кода предполагается не зависящей от трансляции предшествующей или последующей команды промежуточного кода, за исключением, возможно, зависимости, связанной с ее влиянием на глобальные переменные времени компиляции.

Выработку кода из промежуточного мы иногда далее называем трансляцией, чтобы отличать ее от процесса генерации промежуточного кода, который мы называем генерацией кода.

11.1. ОБЩИЕ ПОЛОЖЕНИЯ

Транслятор состоит из последовательности глобальных описаний процедур и т.п. и следующего за ней цикла, включающего вариантное предложение с элементом для каждого типа команды промежуточного кода. Этот цикл имеет вид:

global declarations do case read (n); n

in, , ,

esac

При этом допускается, что все типы команд промежуточного кода представлены целыми числами. (Можно использовать строку, но это менее эффективно, так как тогда для выбора соответствующего элемента вариантного предложения придется производить поиск в таблице). Трансляция завершается считыванием специального значения n и выходом из цикла.

Для каждого элемента вариантного предложения (кроме завершающего работу транслятора) требуется чтение параметров команды промежуточного кода. Эти параметры обычно делятся на две группы:

1) целые числа, соответствующие типам (возможно, указатели на таблицу видов);

2) адреса во время прогона: тип-адреса, номер блока, смещение.

Виды могут включаться в таблицу во время синтаксического анализа из таких описаний, как int j или даже amode k, где amode определяется пользователем. Однако виды могут включаться во времени генерации кода, где они выдаются как результаты операций или с помощью приведений в Алголе 68. Таблица, используемая транслятором, может содержать не все виды, встречаемые в программе, а только те, которые появляются в качестве параметров в промежуточном коде.

Номер блока в адресах времени прогона (там, где он есть) преобразуется в номер уровня блока с помощью таблицы блоков, что дает возможность обращаться к соответствующему уровню дисплея. При трансляции размеры каждой области различных рамок стека, как правило, известны. Это позволяет соотнести поля смещений адресов, которые могут, например, ссылаться на области рабочего стека рамки, с



началом этой рамки. Некоторые типы-адресов на этой стадии исчезают, например те, которые требуют, чтобы смещения соотносились с рабочим стеком. Тем не менее все же придется различать прямые и косвенные адреса. В тех случаях, когда машина не позволяет осуществлять косвенную адресацию, адреса предстоит имитировать с помощью индекснтх регистров. Например, в ICL 1900

LDX 2 0(1)

(0(1) означает адрес 0, модифицированный значением в регистре 1), вызывает загрузку содержимого адреса, на который указывает регистр 1, в регистр 2. Если адресом служит литерал, смещение не нужно преобразовать, а если затрагивается таблица констант, может быть выбран соответствующий ее элемент. На этой стадии компилятор должен проверять, имеют ли элементы таблицы констант необходимую для машины (или реализации) длину. При чрезмерно длинных константах или других отклонениях транслятор выдает сообщение об ошибке. Это - единственное сообщение об ошибке, которое выдается транслятором.

Трансляция команды промежуточного кода заключается в чтении параметров, преобразования адресов можно воспользоваться стандартной процедурой, которая сама генерирует код, что позволяет нам применить косвенный метод. В результате вызова этой процедуры

convert (add1, 1) регистр 1 будет указывать на значение в add1 .

11.2. ПРИМЕРЫ ГЕНЕРАЦИИ МАШИННОГО КОДА

С большинством команд промежуточного кода, трансляция котортх здесь описывается, мы уже встречались в разд. 10.3. Команды выдаются в коде PLAN, представляющем собой код Ассемблера для машин серии ICL 1900.

1. SETLABEL

Элемент вариантного предложения, имеющей дело с этой командой, имеет вид

begin string label; read (label);

print ((newline, label, X3, "NULL")) end

Здесь значение Х3 - это строка, содержащая три пробела. Когда меткой станет L23, генерирует код

L23 NULL

где NULL - фиктивная команда (т.е. она ничего не делает). Для простоты допускается, что метки состоят из трех литер.

2. GOTO



0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 [ 52 ] 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69