Вход/Регистрация
Давайте создадим компилятор!
вернуться

Креншоу Джек

Шрифт:

program main;

begin

end.

Мой тест измеряет время, требуемое на компиляцию и связывание и размер сгенерированного объектного файла. Бесспорный проигравший в этом тесте – компилятор DEC C для VAX, который тратит 60 секунд на компиляцию на VAX 11/780 и генерирует объектный файл 50k. Компилятор Джона бесспорно сейчас, в будущем и навсегда король по части размера кода. Для данной пустой программе Whimsical генерирует точно два байта, реализуя одну инструкцию:

RET

Устанавливая опцию компилятора генерировать include файл а не автономную программу, Джон может даже урезать этот размер с двух байт до нуля! Несколько трудно добиться нулевого обьектного файла, вы не согласны?

Само собой разумеется, что я рассматриваю Джона как эксперта в оптимизации кода и мне нравится что он однажды сказал: «Лучший способ оптимизации – не оптимизировать вообще, а изначально производить хороший код». Слова, по которым стоит жить. Когда мы начнем оптимизацию мы будем следовать уведомлению Джона и нашим первым шагом будет не добавление щелевого оптимизатора или другого постфактного устройства, но улучшение качества выдаваемого кода перед оптимизацией. Поэтому пометьте SignedFactor как первого хорошего кандидата на внимание и пока оставим его.

Термы и выражения

Я уверен вы знаете, что будет дальше. Мы должны еще раз создать остальные процедуры, которые реализуют синтаксический анализ выражений по методу рекурсивного спуска. Все мы знаем, что иерархия процедур для арифметических выражений такая:

выражение

терм

показатель

Однако сейчас давайте продолжим разработку по шагам и рассмотрим выражения только с аддитивными термами. Код для реализации выражений, включающих возможно первый терм со знаком, показан ниже:

{–}

{ Parse and Translate an Expression }

procedure Expression;

begin

SignedFactor;

while IsAddop(Look) do

case Look of

'+': Add;

'-': Subtract;

end;

end;

{–}

Эта процедура вызывает две другие процедуры для обработки операций:

{–}

{ Parse and Translate an Addition Operation }

procedure Add;

begin

Match('+');

Push;

Factor;

PopAdd;

end;

{–}

{ Parse and Translate a Subtraction Operation }

procedure Subtract;

begin

Match('-');

Push;

Factor;

PopSub;

end;

{–}

Эти три процедуры Push, PopAdd и PopSub – новые подпрограммы генерации кода. Как подразумевает имя, процедура Push генерирует код для помещения основного регистра (D0 в нашей реализации для 68000) в стек. PopAdd и PopSub выталкивают вершину стека и прибавляют или вычитают ее из основного регистра. Код показан ниже:

{–}

{ Push Primary to Stack }

procedure Push;

begin

EmitLn('MOVE D0,-(SP)');

end;

{–}

{ Add TOS to Primary }

procedure PopAdd;

begin

EmitLn('ADD (SP)+,D0');

end;

{–}

{ Subtract TOS from Primary }

procedure PopSub;

begin

EmitLn('SUB (SP)+,D0');

Negate;

end;

{–}

Добавьте эти подпрограммы в Parser и CodeGen и измените основную программу для вызова Expression. Вуаля!

Следующий шаг, конечно, это добавление возможности работы с мульпликативными термами. С этой целью мы добавим процедуру Term и процедуры генерации кода PopMul и PopDiv. Эти процедуры генерации кода показаны ниже:

{–}

{ Multiply TOS by Primary }

procedure PopMul;

begin

EmitLn('MULS (SP)+,D0');

end;

{–}

{ Divide Primary by TOS }

procedure PopDiv;

begin

EmitLn('MOVE (SP)+,D7');

EmitLn('EXT.L D7');

EmitLn('DIVS D0,D7');

EmitLn('MOVE D7,D0');

end;

{–}

Я должен признать, что подпрограмма деления немного перегружена, но с этим ничего нельзя поделать. К сожалению, хотя процессор 68000 позволяет выполнять деление используя вершину стека (TOS), он требует аргументы в неправильном порядке, подобно тому как для вычитания. Поэтому наше единственное спасение в том чтобы вытолкнуть стек в рабочий регистр (D7), выполнить там деление, и затем поместить результат обратно в наш основной регистр D0. Обратите внимание на использование знаковых операций умножения и деления. Этим неявно подразумевается что все наши переменные будут 16-разрядными целыми числами со знаком. Это решение затронет нас позднее, когда мы начнем рассматривать множественные типы данных, преобразования типов и т.п.

Наша процедура Term это практически аналог Expression и выглядит так:

{–}

{ Parse and Translate a Term }

procedure Term;

begin

Factor;

while IsMulop(Look) do

case Look of

'*': Multiply;

'/': Divide;

end;

end;

{–}

Наш следующий шаг – изменение некоторых имен. SignedFactor теперь становится SignedTerm а вызовы Factor в Expression, Add, Subtract и SignedTerm заменяются на вызов Term:

{–}

{ Parse and Translate a Term with Optional Leading Sign }

procedure SignedTerm;

var Sign: char;

begin

Sign := Look;

if IsAddop(Look) then

GetChar;

Term;

if Sign = '-' then Negate;

end;

{–}

...

{–}

{ Parse and Translate an Expression }

procedure Expression;

begin

SignedTerm;

while IsAddop(Look) do

case Look of

'+': Add;

'-': Subtract;

end;

end;

{–}

  • Читать дальше
  • 1
  • ...
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98

Ебукер (ebooker) – онлайн-библиотека на русском языке. Книги доступны онлайн, без утомительной регистрации. Огромный выбор и удобный дизайн, позволяющий читать без проблем. Добавляйте сайт в закладки! Все произведения загружаются пользователями: если считаете, что ваши авторские права нарушены – используйте форму обратной связи.

Полезные ссылки

  • Моя полка

Контакты

  • chitat.ebooker@gmail.com

Подпишитесь на рассылку: