Генерация кода
Назначение адресов
Назначение адресов переменным, параметрам и полям записей происходит при обработке соответствующих объявлений. В однопроходном трансляторе это может производиться вместе с построением основной таблицы символов и соответствующие адреса (или смещения) могут храниться в этой же таблице. В промежуточном представлении Лидер объявления сохранены, что делает это промежуточное представление машинно-независимым. Напомним, что в Лидер-представлении каждому описанию соответствует некоторый номер. В процессе работы генератора кодов поддерживается таблица Table, в которой по этому номеру (входу) содержится следующая информация:
- для типа: его размер;
- для переменной: смещение в области процедуры (или глобальной области);
- для поля записи: смещение внутри записи;
- для процедуры: размер локальных параметров;
- для массива: размер массива, размер элемента, значение левой и правой границы.
Для вычисления адресов определим для каждого объявления два синтезируемых атрибута: DISP будет обозначать смещение внутри области процедуры (или единицы компиляции), а SIZE - размер. Тогда семантика правила для списка объявлений принимает вид
RULE DeclPart ::= ( Decl ) SEMANTICS Disp<1>=0; 1A: Disp<1>=Disp<1>+Size<1>; Size<0>=Disp<1>.
Все объявления, кроме объявлений переменных, имеют нулевой размер. Размер объявления переменной определяется следующим правилом:
RULE Decl ::= 'VAR' TypeDes SEMANTICS Tablentry Entry; 0: Entry=IncTab; Size<0>=((Table[VAL<2>]+1) / 2)*2; // Выравнивание на границу слова Table[Entry]=Disp<0>+Size<0>.
В качестве примера трансляции определения типа рассмотрим обработку описания записи:
RULE
TypeDes ::= 'REC' ( TypeDes ) 'END'
SEMANTICS
int Disp;
Tablentry Temp;
0: Entry<0>=IncTab;
Disp=0;
2A: {Temp=IncTab;
Table[Temp]=Disp;
Disp=Disp+Table[Entry<2>]+1) / 2)*2;
// Выравнивание на границу слова
}
Table[Entry<0>]=Disp.Трансляция переменных
Переменные отражают все многообразие механизмов доступа в языке. Переменная имеет синтезированный атрибут ADDRESS - это запись, описывающая адрес в команде МС68020. Этот атрибут сопоставляется всем нетерминалам, представляющим значения. В системе команд МС68020 много способов адресации, и они отражены в структуре значения атрибута ADDRESS, имеющего следующий тип:
enum Register
{D0,D1,D2,D3,D4,D5,D6,D7,
A0,A1,A2,A3,A4,A5,A6,SP,NO};
enum AddrMode
{D,A,Post,Pre,Indirect,IndPre,IndPost,IndirPC,
IndPrePC,IndPostPC,InDisp,Index,IndexPC,Abs,Imm};
struct AddrType{
Register AddrReg,IndexReg;
int IndexDisp,AddrDisp;
short Scale;
};Значение регистра NO означает, что соответствующий регистр в адресации не используется.
Доступ к переменным осуществляется в зависимости от их уровня: глобальные переменные адресуются с помощью абсолютной адресации; переменные в процедуре текущего уровня адресуются через регистр базы А6.
Если стек организован с помощью статической цепочки, то переменные предыдущего статического уровня адресуются через регистр статической цепочки А5 ; переменные остальных уровней адресуются "пробеганием" по статической цепочке с использованием вспомогательного регистра. Адрес переменной формируется при обработке структуры переменной слева направо и передается сначала сверху вниз как наследуемый атрибут нетерминала VarTail, а затем передается снизу-вверх как глобальный атрибут нетерминала Variable. Таким образом, правило для обращения к переменной имеет вид (первое вхождение Number в правую часть - это уровень переменной, второе - ее Лидер-номер):
RULE
Variable ::= VarMode Number Number VarTail
SEMANTICS
int Temp;
struct AddrType AddrTmp1, AddrTmp2;
3: if (Val<2>==0) // Глобальная переменная
{Address<4>.AddrMode=Abs;
Address<4>.AddrDisp=0;
}
else // Локальная переменная
{Address<4>.AddrMode=Index;
if (Val<2>==Level<Block>) // Переменная
// текущего уровня
Address<4>.AddrReg=A6;
else if (Val<2>==Level<Block>-1)
// Переменная предыдущего уровня
Address<4>.AddrReg=A5;
else
{Address<4>.Addreg=
GetFree(RegSet<Block>);
AddrTmp1.AddrMode=Indirect;
AddrTmp1.AddrReg=A5;
Emit2(MOVEA,AddrTmp1,
Address<4>.AddrReg);
AddrTmp1.AddrReg=Address<4>.AddrReg;
AddrTmp2.AddrMode=A;
AddrTmp2.AddrReg=Address<4>.AddrReg;
for (Temp=Level<Block>-Val<2>;
Temp>=2;Temp--)
Emit2(MOVEA,AddrTmp1,AddrTmp2);
}
if (Val<2>==Level<Block>)
Address<4>.AddrDisp=Table[Val<3>];
else
Address<4>.AddrDisp=
Table[Val<3>]+Table[LevelTab[Val<2>]];
}.Функция GetFree выбирает очередной свободный регистр (либо регистр данных, либо адресный регистр) и отмечает его как использованный в атрибуте RegSet нетерминала Block. Процедура Emit2 генерирует двухадресную команду. Первый параметр этой процедуры - код команды, второй и третий параметры имеют тип AddrType и служат операндами команды. Смещение переменной текущего уровня отсчитывается от базы ( А6 ), а других уровней - от указателя статической цепочки, поэтому оно определяется как алгебраическая сумма размера локальных параметров и величины смещения переменной. Таблица LevelTab - это таблица уровней процедур, содержащая указатели на последовательно вложенные процедуры.
Если стек организован с помощью дисплея, то трансляция для доступа к переменным может быть осуществлена следующим образом:
RULE
Variable ::= VarMode Number Number VarTail
SEMANTICS
int Temp;
3: if (Val<2>==0) // Глобальная переменная
{Address<4>.AddrMode=Abs;
Address<4>.AddrDisp=0;
}
else // Локальная переменная
{Address<4>.AddrMode=Index;
if (Val<2>=Level<Block>) // Переменная
// текущего уровня
{Address<4>.AddrReg=A6;
Address<4>.AddrDisp=Table[Val<3>];
}
else
{Address<4>.AddrMode=IndPost;
Address<4>.AddrReg=NO;
Address<4>.IndexReg=NO;
Address<4>.AddrDisp=Display[Val<2>];
Address<4>.IndexDisp=Table[Val<3>];
}
}.Рассмотрим трансляцию доступа к полям записи. Она описывается следующим правилом ( Number - это Лидер- номер описания поля):
RULE
VarTail ::= 'FIL' Number VarTail
SEMANTICS
if (Address<0>.AddrMode==Abs)
{Address<3>.AddrMode=Abs;
Address<3>.AddrDisp=
Address<0>.AddrDisp+Table[Val<2>];
}
else
{Address<3>=Address<0>;
if (Address<0>.AddrMode==Index)
Address<3>.AddrDisp=
Address<0>.AddrDisp+Table[Val<2>];
else
Address<3>.IndexDisp=
Address<0>.IndexDisp+Table[Val<2>];
}.