Книга: Системное программное обеспечение. Лабораторный практикум

Листинг П3.13. Построение ассемблерного кода по списку триад

Листинг П3.13. Построение ассемблерного кода по списку триад

unit TrdAsm;

{!!! Зависит от целевой вычислительной системы!!! }

interface

{ Модуль распределения регистров и построения ассемблерного

кода по списку триад }

uses Classes, TrdType, Triads;

const { Префикс наименования временных переменных }

TEMP_VARNAME = _Tmp';

NUM_PROCREG = 6; { Количество доступных регистров }

{ Функция распределения регистров и временных переменных

для хранения промежуточных результатов триад }

function MakeRegisters(listTriad: TTriadList): integer;

{ Функция построения ассемблерного кода по списку триад }

function MakeAsmCode(listTriad: TTriadList;

listCode: TStrings;

flagOpt: Boolean): integer;

implementation

uses SysUtils;

function MakeRegisters(listTriad: TTriadList): integer;

{ Функция распределения регистров и временных переменных

для хранения промежуточных результатов триад.

Результат: количество необходимых временных переменных }

var

i,j,iR,iCnt,iNum: integer;{Счетчики и переменные циклов}

{ Динамический массив для запоминания занятых регистров }

listReg: TList;

begin { Создаем массив для хранения занятых регистров }

listReg:= TList.Create;

Result:= 0;

if listReg <> nil then

try { Обнуляем информационное поле у всех триад }

for i:=listTriad.Count-1 downto 0 do

listTriad[i].Info:= 0;

{ Цикл по всем триадам. Обязательно с конца списка! }

for i:=listTriad.Count-1 downto 0 do

for j:=1 to 2 do { Цикл по всем (2) операндам }

{ Если триада – линейная операция, или «IF»

(первый операнд), или присвоение (второй операнд) }

if ((listTriad[i].TrdType in TriadLineSet)

or (listTriad[i].TrdType = TRD_IF) and (j = 1)

or (listTriad[i].TrdType = TRD_ASSIGN) and (j = 2))

{ и операндом является ссылка на другую триаду }

and (listTriad[i][j].OpType = OP_LINK) then

begin { Запоминаем номер триады, на которую направлена ссылка }

iNum:= listTriad[i][j].TriadNum;

{ Если триаде еще не назначен регистр и если это

не предыдущая триада – надо ей назначить регистр }

if (listTriad[iNum].Info = 0) and (iNum <> i-1) then

begin { Количество назначенных регистров }

iCnt:= listReg.Count-1;

for iR:=0 to iCnt do

begin{ Цикл по массиву назначенных регистров }

{ Если область действия регистра за пределами

текущей триады, то его можно использовать }

if longint(listReg[iR]) >= i then

begin { Запоминаем область действия регистра }

listReg[iR]:= TObject(iNum);

{ Назначаем регистр триаде с номером iNum }

listTriad[iNum].Info:= iR+1;

Break; { Прерываем цикл по массиву регистров }

end;

end; { Если ни один из использованных регистров

не был назначен, надо брать новый регистр }

if listTriad[iNum].Info = 0 then

begin { Добавляем запись в массив регистров,

указываем ей область действия iNum }

listReg.Add(TObject(iNum));

{ Назначаем новый регистр триаде с номером iNum }

listTriad[iNum].Info:= listReg.Count;

end;

end;

end;{ Результат функции: количество записей в массиве

регистров -1, за вычетом числа доступных регистров}

Result:= listReg.Count – (NUM_PROCREG-1);

finally listReg.Free;

end;

end;

function GetRegName(iInfo: integer): string;

{ Функция наименования регистров процессора }

begin

case iInfo of

0: Result:= 'eax';

1: Result:= 'ebx';

2: Result:= 'ecx';

3: Result:= 'edx';

4: Result:= 'esi';

5: Result:= 'edi';

{ Если это не один из регистров – значит,

даем имя временной переменной }

else Result:=

Format(%s%d',[TEMP_VARNAME,iInfo-NUM_PROCREG]);

end{case};

end;

function GetOpName(i: integer; listTriad: TTriadList;

iOp: integer): string;

{ Функция наименования операнда триады

i – номер триады в списке;

listTriad – список триад;

iOp – номер операнда триады }

var iNum: integer; {номенр триады по ссылке}

Triad: TTriad; {текущая триада}

begin

Triad:= listTriad[i]; { Запоминаем текущую триаду }

{ Выборка наименования операнда в зависимости от типа }

case Triad[iOp].OpType of

{ Если константа – значение константы }

OP_CONST: Result:= IntToStr(Triad[iOp].ConstVal);

{ Если переменная – ее имя из таблицы идентификаторов }

OP_VAR:

begin

Result:= Triad[iOp].VarLink.VarName;

{ Если имя совпадает с именем функции,

заменяем его на Result функции }

if Result = NAME_FUNCT then Result:= NAME_RESULT;

end; { Иначе – это регистр }

else { для временного хранения результатов триады }

begin { Запоминаем номер триады }

iNum:= Triad[iOp].TriadNum;

{ Если это предыдущая триада, то операнд не нужен }

if iNum = i-1 then Result:=

else

begin {Берем номер регистра, связанного с триадой}

iNum:= listTriad[iNum].Info;

{ Если регистра нет, то операнд не нужен }

if iNum = 0 then Result:=

{ Иначе имя операнда – это имя регистра }

else Result:= GetRegName(iNum);

end;

end;

end{case};

end;

function MakeMove(const sReg,{имя регистра}

sPrev,{предыдущая команда}

sVal{предыдущая величина в eax}: string;

flagOpt: Boolean{флаг оптимизации}): string;

{ Функция, генерящая код занесения значения в регистр eax }

begin { Если операнд был только что выгружен из eax

или необходимое значение уже есть в аккумуляторе,

нет необходимости записывать его туда снова }

if (Pos(Format(#9'mov'#9 %s,eax',[sReg]), sPrev) = 1)

or (sVal = sReg) then

begin

Result:= ; Exit;

end;

if flagOpt then { Если оптимизация команд включена }

begin

if sReg = 0 then { Если требуемое значение = 0, }

begin{его можно получить из –1 и 1 с помощью INC и DEC}

if sVal = -1 then Result:= #9'inc'#9'eax'

else

if sVal = 1 then Result:= #9'dec'#9'eax'

else Result:= #9'xor'#9'eax,eax'

end {иначе – с помощью XOR}

else

if sReg = 1 then { Если требуемое значение = 1, }

begin{его можно получить из –1 и 0 с помощью NEG и INC}

if sVal = -1 then Result:= #9'neg'#9'eax'

else

if sVal = 0 then Result:= #9'inc'#9'eax'

else

Result:= #9'xor'#9'eax,eax'#13#10#9'inc'#9'eax';

end {иначе – двумя командами: XOR и INC }

else

if sReg = -1 then { Если требуемое значение = -1, }

begin{его можно получить из 1 и 0 с помощью NEG и DEC}

if sVal = 1 then Result:= #9'neg'#9'eax'

else

if sVal = 0 then Result:= #9'dec'#9'eax'

else

Result:= #9'xor'#9'eax,eax'#13#10#9'dec'#9'eax';

end {иначе – двумя командами: XOR и DEC }

{ Иначе заполняем eax командой MOV }

else Result:= Format(#9'mov'#9'eax,%s',[sReg]);

end { Если оптимизация команд выключена,

всегда заполняем eax командой MOV }

else Result:= Format(#9'mov'#9'eax,%s',[sReg]);

end;

function MakeOpcode(i: integer;{номер текущей триады}

listTriad: TTriadList;{список триад}

const sOp,sReg,{код операции и операнд}

sPrev,{предыдущая команда}

sVal{предыдущая величина в eax}: string;

flagOpt: Boolean{флаг оптимизации}): string;

{ Функция, генерящая код линейных операций над eax }

var Triad: TTriad;{текущая триада}

begin { Запоминаем текущую триаду }

Triad:= listTriad[i];

if flagOpt then { Если оптимизация команд включена }

begin

if sReg = 0 then { Если операнд = 0 }

begin

case Triad.TrdType of

TRD_AND: { Для команды AND результат всегда = 0 }

Result:= MakeMove(0 ,sPrev,sVal,flagOpt);

{ Для OR, "+" и «-» ничего не надо делать }

TRD_OR,TRD_ADD,TRD_SUB: Result:= #9#9;

{ Иначе генерируем код выполняемой операции }

else Result:= Format(#9 %s'#9'eax,%s',[sOp,sReg]);

end{case};

end

else

if sReg = 1 then { Если операнд = 1 }

begin

case Triad.TrdType of

TRD_OR: { Для команды OR результат всегда = 1 }

Result:= MakeMove(1 ,sPrev,sVal,flagOpt);

{ Для AND ничего не надо делать }

TRD_AND: Result:= #9#9;

{ Для "+" генерируем операцию INC }

TRD_ADD: Result:= #9'inc'#9'eax';

{ Для «-» генерируем операцию DEC }

TRD_SUB: Result:= #9'dec'#9'eax';

{ Иначе генерируем код выполняемой операции }

else Result:= Format(#9 %s'#9'eax,%s',[sOp,sReg]);

end{case};

end

else

if sReg = -1 then { Если операнд = -1 }

begin

case Triad.TrdType of

{ Для "+" генерируем операцию DEC }

TRD_ADD: Result:= #9'dec'#9'eax';

{ Для «-» генерируем операцию INC }

TRD_SUB: Result:= #9'inc'#9'eax';

{ Иначе генерируем код выполняемой операции }

else Result:= Format(#9 %s'#9'eax,%s',[sOp,sReg]);

end{case};

end { Иначе генерируем код выполняемой операции }

else Result:= Format(#9 %s'#9'eax,%s',[sOp,sReg]);

end { Если оптимизация команд выключена,

всегда генерируем код выполняемой операции }

else Result:= Format(#9 %s'#9'eax,%s',[sOp,sReg]);

{ Добавляем к результату информацию о триаде

в качестве комментария }

Result:= Result + Format(#9 { %s },

[Triad.MakeString(i)]);

end;

function MakeAsmCode(

listTriad: TTriadList;{входной список триад}

listCode: TStrings;{список строк результирующего кода}

flagOpt: Boolean{флаг оптимизации}): integer;

{ Функция построения ассемблерного кода по списку триад }

var i,iCnt: integer;{счетчик и переменная цикла}

sR: string;{строка для имени регистра}

sPrev,sVal: string;

{строки для хранения предыдущей команды и значения eax}

procedure TakePrevAsm;

{ Процедура, выделяющая предыдущую команду и значение eax

из списка результирующих команд }

var j: integer;

begin

j:= listCode.Count;

if j > 0 then

begin

sPrev:= listCode[j-1];

sVal:= StrPas(PChar(listCode.Objects[j-1]));

end

else

begin

sPrev:= ; sVal:= ;

end;

end;

procedure MakeOper1(const sOp,{код операции}

sAddOp: string;{код дополнительной операции}

iOp: integer{номер операнда в триаде});

{ Функция генерации кода для унарных операций }

var sReg{строка для имени регистра}: string;

begin

TakePrevAsm; {Берем предыдущую команду и значение из eax}

{ Запоминаем имя операнда }

sReg:= GetOpName(i,listTriad,iOp);

if sReg <> then { Если имя пустое, операнд уже есть в

регистре eax от выполнения предыдущей триады,}

begin { иначе его нужно занести в eax }

{ Вызываем функцию генерации кода занесения операнда }

sReg:= MakeMove(sReg,sPrev,sVal,flagOpt);

if sReg <> then listCode.Add(sReg);

end; { Генерируем непосредственно код операции }

listCode.Add(Format(#9 %s'#9'eax'#9 { %s },

[sOp,listTriad[i].MakeString(i)]));

if sAddOp <> then { Если есть дополнительная операция,

генерируем ее код }

listCode.Add(Format(#9 %s'#9'eax,1,[sAddOp]));

if listTriad[i].Info <> 0 then { Если триада связана с

begin { регистром, запоминаем результат в этом регистре }

sReg:= GetRegName(listTriad[i].Info);

{ При этом запоминаем, что сейчас находится в eax }

listCode.AddObject(Format(#9'mov'#9 %s,eax',[sReg]),

TObject(PChar(sReg)));

end;

end;

procedure MakeOper2(const sOp,{код операции}

sAddOp: string{код дополнительная операции});

{ Функция генерации кода для бинарных арифметических

и логических операций }

var sReg1,sReg2{строки для имен регистров}: string;

begin

TakePrevAsm; {Берем предыдущую команду и значение из eax}

{ Запоминаем имена первого и второго операндов }

sReg1:= GetOpName(i,listTriad,1);

sReg2:= GetOpName(i,listTriad,2);

{ Если имя первого операнда пустое, значит, он уже

есть в регистре eax от выполнения предыдущей триады -

вызываем функцию генерации кода для второго операнда }

if (sReg1 = ) or (sReg1 = sVal) then

listCode.Add(MakeOpCode(i,listTriad,sOp,sReg2,

sPrev,sVal,flagOpt))

else { Если имя второго операнда пустое, значит он уже

есть в регистре eax от выполнения предыдущей триады -

вызываем функцию генерации кода для первого операнда }

if (sReg2 = ) or (sReg2 = sVal) then

begin

listCode.Add(MakeOpCode(i,listTriad,sOp,sReg1,

sPrev,sVal,flagOpt));

{ Если есть дополнительная операция, генерируем ее код

(когда операция несимметричная – например "-") }

if sAddOp <> then

listCode.Add(Format(#9 %s'#9'eax',[sAddOp]));

end

else { Если оба операнда не пустые, то надо:

– сначала загрузить в eax первый операнд;

– сгенерировать код для обработки второго операнда.}

begin

sReg1:= MakeMove(sReg1,sPrev,sVal,flagOpt);

if sReg1 <> then listCode.Add(sReg1);

listCode.Add(MakeOpCode(i,listTriad,sOp,sReg2,

sPrev,sVal,flagOpt));

end;

if listTriad[i].Info <> 0 then { Если триада связана с

begin { регистром, запоминаем результат в этом регистре }

sReg1:= GetRegName(listTriad[i].Info);

{ При этом запоминаем, что сейчас находится в eax }

listCode.AddObject(Format(#9'mov'#9 %s,eax',[sReg1]),

TObject(PChar(sReg1)));

end;

end;

procedure MakeCompare(const sOp: string

{флаг операции сравнения});

{ Функция генерации кода для операций сравнения }

var sReg1,sReg2{строки для имен регистров}: string;

begin

TakePrevAsm; {Берем предыдущую команду и значение из eax}

{ Запоминаем имена первого и второго операндов }

sReg1:= GetOpName(i,listTriad,1);

sReg2:= GetOpName(i,listTriad,2);

{ Если имя первого операнда пустое, значит он уже

есть в регистре eax от выполнения предыдущей триады -

сравниваем eax со вторым операндом }

if sReg1 = then

listCode.Add(Format(#9'cmp'#9'eax,%s'#9 { %s },

[sReg2,listTriad[i].MakeString(i)]))

else { Если имя второго операнда пустое, значит он уже

есть в регистре eax от выполнения предыдущей триады -

сравниваем eax с первым операндом в обратном порядке }

if sReg2 = then

listCode.Add(Format(#9'cmp'#9 %s,eax'#9 { %s },

[sReg1,listTriad[i].MakeString(i)]))

else { Если оба операнда не пустые, то надо:

– сначала загрузить в eax первый операнд;

– сравнить eax со вторым операндом. }

begin

sReg1:= MakeMove(sReg1,sPrev,sVal,flagOpt);

if sReg1 <> then listCode.Add(sReg1);

listCode.Add(Format(#9'cmp'#9'eax,%s'#9 { %s },

[sReg2,listTriad[i].MakeString(i)]));

end; { Загружаем в младший бит eax 1 или 0

в зависимости от флага сравнения }

listCode.Add(Format(#9'set%s'#9'al',[sOp]));

listCode.Add(#9'and'#9'eax,1); {очищаем остальные биты}

if listTriad[i].Info <> 0 then { Если триада связана с

begin { регистром, запоминаем результат в этом регистре }

sReg1:= GetRegName(listTriad[i].Info);

{ При этом запоминаем, что сейчас находится в eax }

listCode.AddObject(Format(#9'mov'#9 %s,eax',[sReg1]),

TObject(PChar(sReg1)));

end;

end;

begin { Тело главной функции }

iCnt:= listTriad.Count-1; { Количество триад в списке }

for i:=0 to iCnt do

begin { Цикл по всем триадам от начала списка }

{ Если триада помечена, создаем локальную метку

в списке команд ассемблера }

if listTriad[i].IsLinked then

listCode.Add(Format(@M%d:,[i+1]));

{ Генерация кода в зависимости от типа триады }

case listTriad[i].TrdType of

{ Код для триады IF }

TRD_IF: { Если операнд – константа, }

begin {(это возможно в результате оптимизации)}

if listTriad[i][1].OpType = OP_CONST then

begin { Условный переход превращается

в безусловный, если константа = 0,}

if listTriad[i][1].ConstVal = 0 then

listCode.Add(Format(#9'jmp'#9 @M%d'#9 { %s },

[listTriad[i][2].TriadNum+1,

listTriad[i].MakeString(i)]));

end { а иначе вообще генерировать код не нужно.}

else { Если операнд – не константа }

begin { Берем имя первого операнда }

sR:= GetOpName(i,listTriad,1);

{ Если имя первого операнда пустое,

значит он уже есть в регистре eax

от выполнения предыдущей триады, }

if sR = then

{ тогда надо выставить флаг «Z», сравнив eax

с ним самим, но учитывая, что предыдущая

триада для IF – это либо сравнение, либо

логическая операция, это можно опустить}

else { иначе надо сравнить eax с операндом }

listCode.Add(Format(#9'cmp'#9 %s,0,[sR]));

{Переход по условию «NOT Z» на ближайшую метку}

listCode.Add(Format(#9'jnz'#9 @F%d'#9 { %s },

[i,listTriad[i].MakeString(i)]));

{ Переход по прямому условию на дальнюю метку }

listCode.Add(Format(#9'jmp'#9 @M%d',

[listTriad[i][2].TriadNum+1]));

{ Метка для ближнего перехода }

listCode.Add(Format(@F%d:,[i]));

end;

end;

{ Код для бинарных логических операций }

TRD_OR: MakeOper2('or', );

TRD_XOR: MakeOper2('xor', );

TRD_AND: MakeOper2('and', );

{ Код для операции NOT (так как NOT(0)=FFFFFFFF,

то нужна еще операция: AND eax,1 }

TRD_NOT: MakeOper1('not','and',1);

{ Код для операций сравнения по их флагам }

TRD_LT: MakeCompare('l');

TRD_GT: MakeCompare('g');

TRD_EQ: MakeCompare('e');

TRD_NEQ: MakeCompare('ne');

{ Код для бинарных арифметических операций }

TRD_ADD: MakeOper2('add', );

TRD_SUB: MakeOper2('sub','neg');

{ Код для унарного минуса }

TRD_UMIN: MakeOper1('neg', ,2);

TRD_ASSIGN: { Код для операции присвоения }

begin {Берем предыдущую команду и значение из eax}

TakePrevAsm;

sR:= GetOpName(i,listTriad,2); {Имя второго операнда}

{ Если имя второго операнда пустое, значит он уже есть

в регистре eax от выполнения предыдущей триады}

if sR <> then

begin {иначе генерируем код загрузки второго операнда}

sVal:= MakeMove(sR,sPrev,sVal,flagOpt);

if sVal <> then listCode.Add(sVal);

end; { Из eax записываем результат в переменную

с именем первого операнда }

sVal:= listTriad[i][1].VarLink.VarName;

if sVal = NAME_FUNCT then sVal:= NAME_RESULT;

sVal:= Format(#9'mov'#9 %s,eax'#9 { %s },

[sVal,listTriad[i].MakeString(i)]);

{ При этом запоминаем, что было в eax }

listCode.AddObject(sVal,TObject(PChar(sR)));

end;

{ Код для операции безусловного перехода }

TRD_JMP: listCode.Add(

Format(#9'jmp'#9 @M%d'#9 { %s },

[listTriad[i][2].TriadNum+1,

listTriad[i].MakeString(i)]));

{ Код для операции NOP }

TRD_NOP: listCode.Add(Format(#9'nop'#9#9 { %s },

[listTriad[i].MakeString(i)]));

end{case};

end{for};

Result:= listCode.Count;

end;

end.

Оглавление книги


Генерация: 1.150. Запросов К БД/Cache: 3 / 1
поделиться
Вверх Вниз