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 |

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


Модуль CMPGEN имеет следующую реализацию:

**

** 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>;

= ;

};