1.11.8. ГЕНЕРАТОР КОДА При генерации машинного кода порождается его промежуточное представление в виде объектного терма, имеющее следующий синтаксис: $ t.Code =
$ (Seq { t.Code } ) |
$ (Instr s.Instr s.Operand) |
$ (Label s.Label) |
$ (Block s.Value).
$ s.Operand = s.Label | s.Value.
$ s.Label = s.Box.
$ s.Value = s.Int.
$
$ s.Instr =
$ Add | Sub | Div | Mul | Load | Store |
$ Addc | Subc | Divc | Mulc | Loadc |
$ Jumpeq | Jumpne | Jumplt | Jumpgt | Jumple | Jumpge
$ Jump | Read | Write | Halt |
Промежуточное представление может содержать метки, которые впоследствии, после ассемблирования, заменяются на адреса. Ассемблирование выполняется в два прохода. На первом проходе выясняются адреса всех инструкций и переменных, при этом адреса, соответствующие меткам, предварительно помещаются в ящики, на которые указывают метки. На втором проходе метки заменяются на их значения, т.е. ссылки на ящики заменяются не содержимое ящиков.**
** File: CMPGEN.RF
**
$use STDIO;
$use CLASS;
$use ARITHM;
$use BOX;
$use CMPDIC;
$func Enc-Program t.Program s.Dic = t.Code;
$func Enc-St t.St s.Dic = t.Code;
$func Enc-Test t.Test s.Label s.Dic = t.TestC;
$func Unless-Op s.Op = s.Jump-If;
$func Enc-Expr t.Expr s.Dic = t.ExprC;
$func Enc-Sub-Expr t.Expr sN s.Dic = t.ExprC;
$func Literal-Op s.Op = s.OpCode;
$func Memory-Op s.Op = s.OpCode;
$func Assemble t.Code s.StartAddr = s.FreeAddr;
$func Assemble-Seq e.CodeSeq s.Addr = s.FreeAddr;
$func Dereference t.Code = t.Target;
$func Dereference-Seq e.CodeSeq = e.CodeSeqD;
$func Write-Code-Seq e.CodeSeq = ;
** Генерация кода из абстрактной программы.
Gen-Code t.Program =
** Создается пустой словарь.
<Make-Dic> :: s.Dic,
** Компилируется абстрактная программа.
<Enc-Program t.Program s.Dic> :: t.Code,
** Распределяется память под инструкции.
<Assemble t.Code 1> :: s.FreeAddr,
** Распределяется память под переменные.
<Allocate-Dic s.Dic s.FreeAddr> :: s.EndAddr,
** Метки заменяются на адреса.
<Dereference t.Code> :: t.CodeD,
** Генерируется директива BLOCK.
<"-" s.EndAddr s.FreeAddr> :: s.BlockLength,
(Seq t.CodeD (Block s.BlockLength)) :: t.Target,
= t.Target;
** Компиляция программы.
Enc-Program (Program t.St) s.Dic =
<Enc-St t.St s.Dic> :: t.StC,
<Box> :: s.L,
= (Seq t.StC (Instr Halt 0) (Label s.L));
** Компиляция оператора.
Enc-St (s.KeyWord e.Info) s.Dic =
(s.KeyWord e.Info) :
{
(Assign sX t.Expr) =
<Lookup-Dic sX s.Dic> :: s.Addr,
<Enc-Expr t.Expr s.Dic> :: t.ExprC,
= (Seq t.ExprC (Instr Store s.Addr));
(If t.Test t.Then t.Else) =
<Box> :: s.L1, <Box> :: s.L2,
<Enc-Test t.Test s.L1 s.Dic> :: t.TestC,
<Enc-St t.Then s.Dic> :: t.ThenC,
<Enc-St t.Else s.Dic> :: t.ElseC,
= (Seq
t.TestC
t.ThenC
(Instr Jump s.L2)
(Label s.L1)
t.ElseC
(Label s.L2)
);
(While t.Test t.Do) =
<Box> :: s.L1, <Box> :: s.L2,
<Enc-Test t.Test s.L2 s.Dic> :: t.TestC,
<Enc-St t.Do s.Dic> :: t.DoC,
= (Seq
(Label s.L1)
t.TestC
t.DoC
(Instr Jump s.L1)
(Label s.L2)
);
(Read s.X) =
<Lookup-Dic s.X s.Dic> :: s.Addr,
= (Instr Read s.Addr);
(Write t.Expr) =
<Enc-Expr t.Expr s.Dic> :: t.ExprC,
= (Seq t.ExprC (Instr Write 0));
(Seq t.St1 t.St2) =
<Enc-St t.St1 s.Dic> :: t.StC1,
<Enc-St t.St2 s.Dic> :: t.StC2,
= (Seq t.StC1 t.StC2);
(Skip) =
= (Seq );
};
** Компиляция условия.
Enc-Test (Test s.Op t.Arg1 t.Arg2) s.Label s.Dic =
<Enc-Expr (Op Sub t.Arg1 t.Arg2) s.Dic> :: t.ExprC,
<Unless-Op s.Op> :: s.Jump-If,
= (Seq t.ExprC (Instr s.Jump-If s.Label));
Unless-Op ** Генерация операции перехода.
{
Eq = Jumpne; Ne = Jumpeq;
Lt = Jumpge; Gt = Jumple;
Le = Jumpgt; Ge = Jumplt;
};
** Компиляция арифметического выражения.
** При этом происходит выделение рабочих переменных
** для хранения результатов вычисления подвыражений.
** При компиляции бинарных операций выбирается такой
** порядок вычислений, который позволяет уменьшить
** количество используемых рабочих ячеек.
Enc-Expr t.Expr s.Dic
= <Enc-Sub-Expr t.Expr 0 s.Dic>;
Enc-Sub-Expr (s.KeyWord e.Info) sN s.Dic =
(s.KeyWord e.Info) :
{
(Const sC) =
= (Instr Loadc sC);
(Name sX) =
<Lookup-Dic sX s.Dic> :: s.Addr,
= (Instr Load s.Addr);
(Op s.Op t.Expr1 t.Expr2) =
t.Expr2 :
{
(Const sC2) =
<Enc-Sub-Expr t.Expr1 sN s.Dic> :: t.Expr1C,
<Literal-Op s.Op> :: s.OpCode,
= (Seq t.Expr1C (Instr s.OpCode sC2));
(Name sX2) =
<Enc-Sub-Expr t.Expr1 sN s.Dic> :: t.Expr1C,
<Memory-Op s.Op> :: s.OpCode,
<Lookup-Dic sX2 s.Dic> :: s.Addr,
= (Seq t.Expr1C (Instr s.OpCode s.Addr));
(Op e) =
<Lookup-Dic sN s.Dic> :: s.Addr,
<Enc-Sub-Expr t.Expr2 sN s.Dic> :: t.Expr2C,
<"+" sN 1> :: sN1,
<Enc-Sub-Expr t.Expr1 sN1 s.Dic> :: t.Expr1C,
<Memory-Op s.Op> :: s.OpCode,
= (Seq
t.Expr2C
(Instr Store s.Addr)
t.Expr1C
(Instr s.OpCode s.Addr)
);
};
};
Literal-Op ** Генерация имен инструкций
{ ** с непосредственным операндом.
Add = Addc; Sub = Subc;
Mul = Mulc; Div = Divc;
};
Memory-Op ** Генерация имен инструкций
{ ** с операндом-адресом.
Add = Add; Sub = Sub;
Mul = Mul; Div = Div;
};
** Распределение памяти под инструкции.
Assemble t.Code s.A0 =
t.Code :
{
(Seq e.CodeSeq) =
= <Assemble-Seq e.CodeSeq s.A0>;
(Instr s s) =
= <"+" s.A0 1>;
(Label s.Label) =
<Store s.Label s.A0>
= s.A0;
};
Assemble-Seq e.CodeSeq s.A0 =
e.CodeSeq :
{
t.Code e.Rest =
<Assemble t.Code s.A0> :: s.A1,
= <Assemble-Seq e.Rest s.A1>;
=
= s.A0;
};
** Замена меток на их значения.
Dereference t.Code =
t.Code :
{
(Seq e.CodeSeq) =
(Seq <Dereference-Seq e.CodeSeq>);
(Instr s.Instr s.Value) =
{
<Int? s.Value>
= t.Code;
<Box? s.Value>
= (Instr s.Instr <? s.Value>);
};
(Label s.Label) =
(Label <? s.Label>);
};
Dereference-Seq
{
t.Code e.CodeSeq =
<Dereference t.Code><Dereference-Seq e.CodeSeq>;
= ;
};
** Преобразование машинного кода в поток литер
** и вывод на стандартное устройство вывода.
Write-Code
{
(Seq e.CodeSeq) =
<Write-Code-Seq e.CodeSeq>;
(Instr s.Instr s.Value) =
<Print " "><Print s.Instr><Print ",">
<Print s.Value><Print ";\n">;
(Label s.Label) =
<Print s.Label><Print ":\n">;
(Block s.Value) =
<Print " BLOCK,"><Print s.Value><Print ";\n">;
};
Write-Code-Seq
{
t.Code e.CodeSeq =
<Write-Code t.Code><Write-Code-Seq e.CodeSeq>;
= ;
};