Продолжаю эксперименты со строительством компилятора.
Реализовал преобразование кода виртуальной машины в код виртуальной целочисленной БЭСМ-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
Как говорится, разница на лицо. Есть над чем поработать.