Доброго времени!
Продолжаю эксперименты с компиляторо-строением.
Добавил возможность возвращать результаты работы процедуры через VAR-аргументы.
Поэтому подобный код компилируется и работает:
MODULE Samples;
const
pass = 12345;
fail = 54321;
var
t : integer;
procedure gcd(a,b : integer; var res : integer);
begin
while b > 0 do
res := b;
b := a mod b;
a := res
end
end gcd;
begin
gcd(27+25*3-(10 div 5),15, t);
if t = 5 then
halt(pass)
else
halt(fail)
end
END Samples.
Передача аргументов осуществляется через стек, на котором формируется рабочая область (фрейм) куда записываются аргументы процедуры и внутренние локальные переменные.
Схема такая:
| old frame | arg0 | arg1 | v0 | v1 | v2 | old_bp | return address | free RAM cells |
|-----------+------+------+----+----+----+--------+----------------+----------------|
| | | | | | | ^ BP | | ^ SP |
Сделал я это для того, чтобы была возможность работы с рекурсией, но боком вышло то, что глобальные переменные по отношению к телу процедуры “дали прикурить” т.к. их адреса во время компиляции программы неизвестны (так же из-за возможности объявлять вложенные процедуры (своего рода замыкания)). Можно было ввести некоторый GP который бы показывал на локальные переменные вышестоящей области видимости, но тогда пришлось бы организовывать какой-то механизм многократной относительной адресации. Понятно, что эта задача решаема, но в книге по Оберону я пока не добрался до соотв. главы. Если запретить рекурсию, то задача размещения и адресации переменных становится тривиальной, т.к. все адреса можно вычислить прямо на этапе компиляции.
Результатом работы компилятора является такой питонячий объект:
{'c_mem': [12345, 54321],
'consts': {'fail': {'line': 4, 'offset': 1, 'val': 54321},
'pass': {'line': 3, 'offset': 0, 'val': 12345}},
'main': 'Samples_main',
'name': 'Samples',
'p_text': [('LABEL', 'L0'),
('VLOAD', -2, 'b'),
('CONST', 0),
('RELOP', '>'),
('BR_ZERO', 'L1'),
('VLOAD', -2, 'b'),
('RSTOR', -1, 'res'),
('VLOAD', -3, 'a'),
('VLOAD', -2, 'b'),
('BINOP', 'MOD'),
('VSTOR', -2, 'b'),
('RLOAD', -1, 'res'),
('VSTOR', -3, 'a'),
('BR', 'L0'),
('LABEL', 'L1'),
('RETURN', ''),
('ALLOC', 1),
('CONST', 27),
('CONST', 25),
('CONST', 3),
('BINOP', '*'),
('BINOP', '+'),
('CONST', 10),
('CONST', 5),
('BINOP', 'DIV'),
('BINOP', '-'),
('CONST', 15),
('ADR_LOAD', -1, 't'),
('CALL', 0, 'gcd'),
('DEALLOC', 3),
('VLOAD', -1, 't'),
('CONST', 5),
('RELOP', '='),
('BR_ZERO', 'L3'),
('CLOAD', 0, 'pass'),
('SYSCALL', 'halt'),
('DEALLOC', 1),
('BR', 'L3'),
('LABEL', 'L2'),
('CLOAD', 1, 'fail'),
('SYSCALL', 'halt'),
('DEALLOC', 1),
('LABEL', 'L3'),
('STOP', '12345')],
'proc_tab': {'Samples_main': {'offset': 16},
'gcd': {'arg_sz': 3,
'args': {'a': {'offset': 2,
'typ': 'integer',
'var': False},
'b': {'offset': 1,
'typ': 'integer',
'var': False},
'res': {'offset': 0,
'typ': 'integer',
'var': True}},
'consts': {},
'offset': 0,
'v_size': 0,
'vars': {}}},
'v_size': 1,
'vars': {'t': {'offset': 0, 'size': 1, 'typ': 'integer'}}}
Интерес представляет поле p_text, в котором находится текст программы в виде инструкций для абстрактной виртуальной машины. Я выбрал одноадресное промежуточное представление чтобы быть ближе к инструкциям машины МЭСМ-6, третий аргумент у некоторых операций носит косметический характер и сохраняется для удобочитаемости человеком.
Трассировка вычисления НОД для чисел 100 и 15:
OP=('ALLOC', 1) PC=16 BP=1 SP=1 D_MEM=[0]
OP=('CONST', 27) PC=17 BP=1 SP=2 D_MEM=[0, 27]
OP=('CONST', 25) PC=18 BP=1 SP=3 D_MEM=[0, 27, 25]
OP=('CONST', 3) PC=19 BP=1 SP=4 D_MEM=[0, 27, 25, 3]
OP=('BINOP', '*') PC=20 BP=1 SP=3 D_MEM=[0, 27, 75]
OP=('BINOP', '+') PC=21 BP=1 SP=2 D_MEM=[0, 102]
OP=('CONST', 10) PC=22 BP=1 SP=3 D_MEM=[0, 102, 10]
OP=('CONST', 5) PC=23 BP=1 SP=4 D_MEM=[0, 102, 10, 5]
OP=('BINOP', 'DIV') PC=24 BP=1 SP=3 D_MEM=[0, 102, 2]
OP=('BINOP', '-') PC=25 BP=1 SP=2 D_MEM=[0, 100]
OP=('CONST', 15) PC=26 BP=1 SP=3 D_MEM=[0, 100, 15]
OP=('ADR_LOAD', -1, 't') PC=27 BP=1 SP=4 D_MEM=[0, 100, 15, 0]
OP=('CALL', 0, 'gcd') PC=28 BP=4 SP=6 D_MEM=[0, 100, 15, 0, 1, 29]
OP=('LABEL', 'L0') PC=0 BP=4 SP=6 D_MEM=[0, 100, 15, 0, 1, 29]
OP=('VLOAD', -2, 'b') PC=1 BP=4 SP=7 D_MEM=[0, 100, 15, 0, 1, 29, 15]
OP=('CONST', 0) PC=2 BP=4 SP=8 D_MEM=[0, 100, 15, 0, 1, 29, 15, 0]
OP=('RELOP', '>') PC=3 BP=4 SP=7 D_MEM=[0, 100, 15, 0, 1, 29, True]
OP=('BR_ZERO', 'L1') PC=4 BP=4 SP=6 D_MEM=[0, 100, 15, 0, 1, 29]
OP=('VLOAD', -2, 'b') PC=5 BP=4 SP=7 D_MEM=[0, 100, 15, 0, 1, 29, 15]
OP=('RSTOR', -1, 'res') PC=6 BP=4 SP=6 D_MEM=[15, 100, 15, 0, 1, 29]
OP=('VLOAD', -3, 'a') PC=7 BP=4 SP=7 D_MEM=[15, 100, 15, 0, 1, 29, 100]
OP=('VLOAD', -2, 'b') PC=8 BP=4 SP=8 D_MEM=[15, 100, 15, 0, 1, 29, 100, 15]
OP=('BINOP', 'MOD') PC=9 BP=4 SP=7 D_MEM=[15, 100, 15, 0, 1, 29, 10]
OP=('VSTOR', -2, 'b') PC=10 BP=4 SP=6 D_MEM=[15, 100, 10, 0, 1, 29]
OP=('RLOAD', -1, 'res') PC=11 BP=4 SP=7 D_MEM=[15, 100, 10, 0, 1, 29, 15]
OP=('VSTOR', -3, 'a') PC=12 BP=4 SP=6 D_MEM=[15, 15, 10, 0, 1, 29]
OP=('BR', 'L0') PC=13 BP=4 SP=6 D_MEM=[15, 15, 10, 0, 1, 29]
OP=('LABEL', 'L0') PC=0 BP=4 SP=6 D_MEM=[15, 15, 10, 0, 1, 29]
OP=('VLOAD', -2, 'b') PC=1 BP=4 SP=7 D_MEM=[15, 15, 10, 0, 1, 29, 10]
OP=('CONST', 0) PC=2 BP=4 SP=8 D_MEM=[15, 15, 10, 0, 1, 29, 10, 0]
OP=('RELOP', '>') PC=3 BP=4 SP=7 D_MEM=[15, 15, 10, 0, 1, 29, True]
OP=('BR_ZERO', 'L1') PC=4 BP=4 SP=6 D_MEM=[15, 15, 10, 0, 1, 29]
OP=('VLOAD', -2, 'b') PC=5 BP=4 SP=7 D_MEM=[15, 15, 10, 0, 1, 29, 10]
OP=('RSTOR', -1, 'res') PC=6 BP=4 SP=6 D_MEM=[10, 15, 10, 0, 1, 29]
OP=('VLOAD', -3, 'a') PC=7 BP=4 SP=7 D_MEM=[10, 15, 10, 0, 1, 29, 15]
OP=('VLOAD', -2, 'b') PC=8 BP=4 SP=8 D_MEM=[10, 15, 10, 0, 1, 29, 15, 10]
OP=('BINOP', 'MOD') PC=9 BP=4 SP=7 D_MEM=[10, 15, 10, 0, 1, 29, 5]
OP=('VSTOR', -2, 'b') PC=10 BP=4 SP=6 D_MEM=[10, 15, 5, 0, 1, 29]
OP=('RLOAD', -1, 'res') PC=11 BP=4 SP=7 D_MEM=[10, 15, 5, 0, 1, 29, 10]
OP=('VSTOR', -3, 'a') PC=12 BP=4 SP=6 D_MEM=[10, 10, 5, 0, 1, 29]
OP=('BR', 'L0') PC=13 BP=4 SP=6 D_MEM=[10, 10, 5, 0, 1, 29]
OP=('LABEL', 'L0') PC=0 BP=4 SP=6 D_MEM=[10, 10, 5, 0, 1, 29]
OP=('VLOAD', -2, 'b') PC=1 BP=4 SP=7 D_MEM=[10, 10, 5, 0, 1, 29, 5]
OP=('CONST', 0) PC=2 BP=4 SP=8 D_MEM=[10, 10, 5, 0, 1, 29, 5, 0]
OP=('RELOP', '>') PC=3 BP=4 SP=7 D_MEM=[10, 10, 5, 0, 1, 29, True]
OP=('BR_ZERO', 'L1') PC=4 BP=4 SP=6 D_MEM=[10, 10, 5, 0, 1, 29]
OP=('VLOAD', -2, 'b') PC=5 BP=4 SP=7 D_MEM=[10, 10, 5, 0, 1, 29, 5]
OP=('RSTOR', -1, 'res') PC=6 BP=4 SP=6 D_MEM=[5, 10, 5, 0, 1, 29]
OP=('VLOAD', -3, 'a') PC=7 BP=4 SP=7 D_MEM=[5, 10, 5, 0, 1, 29, 10]
OP=('VLOAD', -2, 'b') PC=8 BP=4 SP=8 D_MEM=[5, 10, 5, 0, 1, 29, 10, 5]
OP=('BINOP', 'MOD') PC=9 BP=4 SP=7 D_MEM=[5, 10, 5, 0, 1, 29, 0]
OP=('VSTOR', -2, 'b') PC=10 BP=4 SP=6 D_MEM=[5, 10, 0, 0, 1, 29]
OP=('RLOAD', -1, 'res') PC=11 BP=4 SP=7 D_MEM=[5, 10, 0, 0, 1, 29, 5]
OP=('VSTOR', -3, 'a') PC=12 BP=4 SP=6 D_MEM=[5, 5, 0, 0, 1, 29]
OP=('BR', 'L0') PC=13 BP=4 SP=6 D_MEM=[5, 5, 0, 0, 1, 29]
OP=('LABEL', 'L0') PC=0 BP=4 SP=6 D_MEM=[5, 5, 0, 0, 1, 29]
OP=('VLOAD', -2, 'b') PC=1 BP=4 SP=7 D_MEM=[5, 5, 0, 0, 1, 29, 0]
OP=('CONST', 0) PC=2 BP=4 SP=8 D_MEM=[5, 5, 0, 0, 1, 29, 0, 0]
OP=('RELOP', '>') PC=3 BP=4 SP=7 D_MEM=[5, 5, 0, 0, 1, 29, False]
OP=('BR_ZERO', 'L1') PC=4 BP=4 SP=6 D_MEM=[5, 5, 0, 0, 1, 29]
OP=('LABEL', 'L1') PC=14 BP=4 SP=6 D_MEM=[5, 5, 0, 0, 1, 29]
OP=('RETURN', '') PC=15 BP=1 SP=4 D_MEM=[5, 5, 0, 0]
OP=('DEALLOC', 3) PC=29 BP=1 SP=1 D_MEM=[5]
OP=('VLOAD', -1, 't') PC=30 BP=1 SP=2 D_MEM=[5, 5]
OP=('CONST', 5) PC=31 BP=1 SP=3 D_MEM=[5, 5, 5]
OP=('RELOP', '=') PC=32 BP=1 SP=2 D_MEM=[5, True]
OP=('BR_ZERO', 'L3') PC=33 BP=1 SP=1 D_MEM=[5]
OP=('CLOAD', 0, 'pass') PC=34 BP=1 SP=2 D_MEM=[5, 12345]
Program halted with code: 12345
SUCCESS