Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Скачиваний:
31
Добавлен:
02.05.2014
Размер:
21 Кб
Скачать
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
  Result := 0;
  { Создаем массив для хранения занятых регистров }
  listReg := TList.Create;
  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
    { Цикл по всем (2) операндам }
    for j:=1 to 2 do
    { Если триада - линейная операция, или "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
            { Запоминаем новую область действия регистра
              (iNum) }
            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
    { Если значение = 0, то заносим его с помощью XOR }
    if sReg = '0' then
    begin
      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
    else
    { Если = 1 - двумя командами: XOR и INC }
    if sReg = '1' then
    begin
      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
    else
    { Если = -1 - двумя командами: XOR и DEC }
    if sReg = '-1' then
    begin
      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
    { Иначе заполняем 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
    { Если операнд = 0 }
    if sReg = '0' then
    begin
      case Triad.TrdType of
        { Для команды AND результат всегда = 0 }
        TRD_AND:
          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
    { Если операнд = 1 }
    if sReg = '1' then
    begin
      case Triad.TrdType of
        { Для команды OR результат всегда = 1 }
        TRD_OR:
          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
    { Если операнд = -1 }
    if sReg = '-1' then
    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
  { Берем предыдущую команду и значение из eax }
  TakePrevAsm;
  { Запоминаем имя операнда }
  sReg := GetOpName(i,listTriad,iOp);
  { Если имя пустое, операнд уже есть в регистре EAX
    от выполнения предыдущей триады, иначе его нужно
    занести в EAX - вызываем функцию генерации кода }
  if sReg <> '' then
  begin
    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
  { Берем предыдущую команду и значение из eax }
  TakePrevAsm;
  { Запоминаем имя 1 операнда }
  sReg1 := GetOpName(i,listTriad,1);
  { Запоминаем имя 2 операнда }
  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
  { Берем предыдущую команду и значение из eax }
  TakePrevAsm;
  { Запоминаем имя 1 операнда }
  sReg1 := GetOpName(i,listTriad,1);
  { Запоминаем имя 2 операнда }
  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]));
  (* Один из вариантов кода:
  { Преобразуем 8 младших бит в 16 бит }
  listCode.Add(#9'cbw');
  { Преобразуем 16 младших бит в 32 бита }
  listCode.Add(#9'cwde'); *)
  (* Другой вариант: *)
  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 - это либо сравнение, либо
               логическая операция, это можно опустить}
             (* listCode.Add(#9'test'#9'eax,eax') *)
            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
            { иначе генерим код загрузки операнда в EAX }
            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.
Соседние файлы в папке Cursov