Кодоген здорового человека
Рассмотрим теперь простой алгоритм преобразования арифметических (или логических) выражений в машинный код БЭСМ-6.
Допустим есть вот эта программа:
После прохода парсера и построения промежуточного вида в кодах виртуальной машины имеем такой код:
Суть алгоритма заключается в том, что мы рассматриваем аккумулятор как вершину стека, соотв. линейно просматриваем код первой машины и начинаем заменять команды манипуляции со стеком на команды манипуляции с аккумулятором.
Загрузка значения на аккумулятор взводит флаг того что он занят.
Если два раза подряд попалась команда загрузки на аккумулятор, мы выталкиваем значение в стэк БЭСМ-6. Если же за командой выталкивания следует арифметическая операция, то операция выталкивания заменяется на арифметическую команду БЭСМ с указанием адреса аргумента. Таким образом отбрасывание вершины стека сводится к сбросу флага занятости аккумулятора. Операции вызова процедуры при занятом аккумуляторе автоматически сбрасывают его значение в стэк.
Итого:
Против метода курильщика:
Как говорится, почувствуйте разницу 😅️
Дальше необходимо разобраться со ссылочными VAR-аргументами доступ к которым осуществляется по адресу значения и починить вычисление константных выражений.
Допустим есть вот эта программа:
MODULE arith; var a : integer; begin a := 5 - 3 * 8 div 6; writeint(a); writeln END arith.
После прохода парсера и построения промежуточного вида в кодах виртуальной машины имеем такой код:
('LABEL', 'arith_main'), ('ALLOC', 1), Вычисление выражения (без оптимизации) ('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')]
Суть алгоритма заключается в том, что мы рассматриваем аккумулятор как вершину стека, соотв. линейно просматриваем код первой машины и начинаем заменять команды манипуляции со стеком на команды манипуляции с аккумулятором.
Загрузка значения на аккумулятор взводит флаг того что он занят.
Если два раза подряд попалась команда загрузки на аккумулятор, мы выталкиваем значение в стэк БЭСМ-6. Если же за командой выталкивания следует арифметическая операция, то операция выталкивания заменяется на арифметическую команду БЭСМ с указанием адреса аргумента. Таким образом отбрасывание вершины стека сводится к сбросу флага занятости аккумулятора. Операции вызова процедуры при занятом аккумуляторе автоматически сбрасывают его значение в стэк.
TEXT: 00000: VTM 21 ,M1 00001: VTM 21 ,M15 00002: VTM 16 ,M2 00003: UJ 4 00004: arith_main UTM 1 ,M15 Вычисление a := 5 - 3 * 8 div 6; 00005: XTA 0 ,M2 00006: XTS 1 ,M2 00007: A*X 2 ,M2 00008: A/X 3 ,M2 00009: X-A 0 ,M15 Запись в А 00010: ATX -1 ,M1 Вызов процедуры. Аргумент на стэк (т.к. XTA -1, M1 соотв. предыдущей команде, она удаляется) 00011: ATX 0 ,M15 00012: *77 0 Корректировка стэка после вызова 00013: UTM -1 ,M15 00014: *77 2 00015: STOP 12345 Константы 00016: WORD 5 00017: WORD 3 00018: WORD 8 00019: WORD 6 00020: WORD 0 CONSTANT TABLE: 00000: 5 5 00001: 3 3 00002: 8 8 00003: 6 6 PROCEDURE TABLE arith_main: 4 1 STOP at PC=15 code=12345 ir=0
Итого:
00005: XTA 0 ,M2 00006: XTS 1 ,M2 00007: A*X 2 ,M2 00008: A/X 3 ,M2 00009: X-A 0 ,M15
Против метода курильщика:
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
Как говорится, почувствуйте разницу 😅️
Дальше необходимо разобраться со ссылочными VAR-аргументами доступ к которым осуществляется по адресу значения и починить вычисление константных выражений.