Продолжаю эксперименты со строительством компилятора.
Реализовал преобразование кода виртуальной машины в код виртуальной целочисленной БЭСМ-6 методом “курильщика” так же известного как Poor Man’s codegen 😅️.
В качестве программы для тестирования возьмем вычисление выражения:
В обратной польской записи получается так:
Преобразуется в такой код “первой” виртуальной машины:
Метод курильщика заключается в том, что каждую команду первой машины мы преобразуем буквально в код БЭСМ без каких либо оптимизаций, будто выполняется условный эмулятор первой ВМ на процессоре БЭСМ.
Пусть у нас регистр M1 указывает на последнюю локальную переменную текущего кадра + 1, регистр M2 - на начало блока констант, а М15 - на первый пустой элемент стека, который растет в сторону увеличения адресов.
Работать метод будет тоже со стеком, но пока без учета архитектуры БЭСМ, где аккумулятор является вершиной стека, поэтому код генерируется такой:
Вот такой вот глупый код, но при этом рабочий.
Рассмотрим как это можно записать с учетом того что аккумулятор является вершиной стека:
Как говорится, разница на лицо. Есть над чем поработать.
Реализовал преобразование кода виртуальной машины в код виртуальной целочисленной БЭСМ-6 методом “курильщика” так же известного как Poor Man’s codegen 😅️.
В качестве программы для тестирования возьмем вычисление выражения:
var
a : integer;
begin
a := 5 - 3 * 8 div 6;
writeint(a);
writeln
В обратной польской записи получается так:
5 3 8 * 6 / - >a
Преобразуется в такой код “первой” виртуальной машины:
('CLOAD', 0),
('CLOAD', 1),
('CLOAD', 2),
('BINOP', '*'),
('CLOAD', 3),
('BINOP', 'DIV'),
('BINOP', '-'),
('VSTOR', -1, 'a'),
('VLOAD', -1, 'a'),
('SYSCALL', 'writeint'),
('DEALLOC', 1),
('SYSCALL', 'writeln'),
('STOP', '12345'),
Таблица констант:
('LABEL', 'const_table'),
('WORD', 5),
('WORD', 3),
('WORD', 8),
('WORD', 6),
('WORD', 0),
('LABEL', 'locals_base'),
('LABEL', 'stack_base')]
Место для локальной переменной a:
('WORD', 0),
('LABEL', 'locals_base'),
('LABEL', 'stack_base')]
Метод курильщика заключается в том, что каждую команду первой машины мы преобразуем буквально в код БЭСМ без каких либо оптимизаций, будто выполняется условный эмулятор первой ВМ на процессоре БЭСМ.
Пусть у нас регистр M1 указывает на последнюю локальную переменную текущего кадра + 1, регистр M2 - на начало блока констант, а М15 - на первый пустой элемент стека, который растет в сторону увеличения адресов.
Работать метод будет тоже со стеком, но пока без учета архитектуры БЭСМ, где аккумулятор является вершиной стека, поэтому код генерируется такой:
00005: XTA 0 ,M2 00006: ATX 0 ,M15 00007: XTA 1 ,M2 00008: ATX 0 ,M15 00009: XTA 2 ,M2 00010: ATX 0 ,M15 00011: XTA -2 ,M15 00012: A*X -1 ,M15 00013: ATX -2 ,M15 00014: UTM -1 ,M15 00015: XTA 3 ,M2 00016: ATX 0 ,M15 00017: XTA -2 ,M15 00018: A/X -1 ,M15 00019: ATX -2 ,M15 00020: UTM -1 ,M15 00021: XTA -2 ,M15 00022: A-X -1 ,M15 00023: ATX -2 ,M15 00024: UTM -1 ,M15 00025: XTA 0 ,M15 Сохранение результата 00026: ATX -1 ,M1 Печать числа 00027: XTA -1 ,M1 00028: ATX 0 ,M15 00029: *77 0 00030: UTM -1 ,M15 Печать перевода строки 00031: *77 2 00032: STOP 12345
Вот такой вот глупый код, но при этом рабочий.
Рассмотрим как это можно записать с учетом того что аккумулятор является вершиной стека:
5 3 8 * 6 / - >a XTA =5 XTS =3 A*X =8 A/X =6 X-A ,M15 ATX =a
Как говорится, разница на лицо. Есть над чем поработать.