Автор книги: Алексей Молчанов
Жанр: Программирование, Компьютеры
сообщить о неприемлемом содержимом
Текущая страница: 15 (всего у книги 21 страниц) [доступный отрывок для чтения: 7 страниц]
Такое распределение можно выполнить элементарным образом, если с каждой триадой связать временную переменную, имя которой можно дать в зависимости от порядкового номера триады. Тогда после вычисления триады результат вычисления записывается в эту переменную, а если он будет востребован позже, то читается из этой переменной.
Однако такое распределение будет чрезвычайно неэффективно хотя бы потому, что оно потребует столько же временных переменных, сколько в списке имеется триад, порождающих результаты. В то же время, нет необходимости хранить результаты вычисления всех триад – например, этого не надо делать в том случае, если результат вычисления триады используется только в следующей по списку триаде и более нигде не требуется. Поэтому простейшее распределение можно улучшить, если пометить в списке такие триады, результат вычисления которых используется где бы то ни было, кроме следующих по списку триад, и временные переменные создавать только для этих триад.
Но эффективность алгоритма распределения временных переменных и регистров процессора можно еще увеличить, если принять во внимание область действия каждой триады. Областью действия триады будем считать фрагмент списка триад от порядкового номера триады, следующей за данной триадой, до порядкового номера триады, где последний раз используется ее результат.
Например, последовательности операторов:
d:= a + b + c;
с:= d *(a + b);
a:= d *(a + b) + 1;
будет соответствовать последовательность триад:
1: + (a, b)
2: + (^1, c)
3::= (d, ^2)
4: * (d, ^1)
5::= (с, ^4)
6: + (^4, 1)
7::= (a, ^6)
Область действия для каждой триады в этой последовательности показана на рис. 5.3.
Если отбросить триады, область действия которых не распространяется дальше одной триады (как было сказано выше, для них не требуется хранение промежуточных результатов), то по рис. 5.3 видно, что для данной последовательности триад достаточно одной временной переменной, в которую сначала необходимо занести значение триады № 1, а затем – значение триады № 4. Если пользоваться рассмотренным ранее алгоритмом, то потребовалось бы как минимум две временных переменных.
Рис. 5.3. Области действия триад в списке триад.
Область действия каждой триады можно легко определить, если просматривать список триад с конца: тогда первая же встреченная ссылка на триаду будет максимальной границей ее области действия, а номер триады будет определять минимальную границу ее области действия.
Именно такой алгоритм распределения временных переменных и регистров реализован в функции MakeRegisters в модуле TrdAsm. Эта функция просматривает список триад с конца и распределяет регистры по порядку начиная от первого упоминания каждой триады. Номер закрепленного регистра записывается в информационное поле каждой триады (если это поле равно 0, считается, что нет необходимости хранить промежуточный результат вычисления триады). Минимальная граница области действия триады, в пределах которой регистр не может быть распределен повторно, запоминается в специальном списке регистров (в функции этот список представлен переменной listReg). Количество регистров, упомянутых в нем, и будет равно необходимому количеству регистров для вычисления списка триад.
Генератор ассемблерного кода ориентирован на процессоры типа Intel 80386 и более поздних модификаций в защищенном режиме работы. В этом режиме в процессоре доступно шесть регистров общего назначения по 32 бит каждый [41, 44]:
• еах;
• ebx;
• есх;
• edx;
• esi;
• edi.
Регистр esp используется как указатель стека, а регистр ebp – как базовый указатель стека (хранение временных переменных в стеке с использованием двух регистров описано в разделе, посвященном организации дисплеев памяти процедур и функций в [2, 3, 7]).
С учетом того, что регистр eax необходим для организации вычислений, остается пять регистров, доступных для хранения промежуточных результатов вычислений триад. Если алгоритму требуется больше регистров, то остальные временные результаты размещаются во временных переменных, которые генератор кода в свою очередь размещает в стеке.
Предложенный алгоритм правильно определяет минимально необходимое количество регистров процессора и временных переменных, необходимых для хранения промежуточных результатов вычисления триад. Однако доступные регистры он распределяет произвольным образом (каждая триада получает для хранения своего результата первый попавшийся свободный регистр). Логично было бы в первую очередь выделять регистры для тех триад, чьи результаты используются наиболее часто, а для хранения результатов других триад использовать временные переменные, поскольку доступ к регистру осуществляется быстрее, чем к области памяти, в которой хранится переменная. Алгоритмы такого распределения существуют, но в данном случае в них нет необходимости, поскольку для простейшего компилятора, обрабатывающего незначительные по объему входные программы, не требуется столь сложная подготовка результирующего кода.
После того как регистры распределены, остается построить ассемблерный код. Для этого для каждой триады строится соответствующий ей фрагмент ассемблерного кода, и все построенные фрагменты объединяются в общую последовательность команд результирующей программы по порядку следования триад в списке.
Для выполнения всех операций и хранения их результатов в пределах одной триады будем использовать регистр аккумулятора – eax. Кроме того, что это наглядно и удобно, в процессорах серии Intel 80x86 некоторые команды с этим регистром занимают меньше памяти и выполняются быстрее, чем команды с другими регистрами (а в ряде команд этот регистр является единственно возможным) [41, 44].
Порождение ассемблерного кода по списку триад выполняется функцией MakeAsmCode из модуля TrdAsm (листинг П3.13, приложение 3).
Для унарных линейных операций последовательность действий при генерации ассемблерного кода такова:
1. Запоминается имя операнда. Для переменных именем операнда является имя переменной, для константы – значение константы, а для ссылки на другую триаду, кроме предыдущей, – имя регистра или временной переменной, в которой хранится результат вычисления триады (для предыдущей триады имя операнда пустое).
2. Если имя операнда не пустое, то операнд надо загрузить в регистр eax. Для этого порождается команда mov, но если операнд – результат вычисления предыдущей триады (имя операнда пустое), то загружать в eax его не нужно, так как он уже находится там после вычисления триады, и никакая команда на этом шаге не порождается.
3. Порождается команда, соответствующая унарной операции над регистром eax (в данном результирующем языке: not – для логического отрицания; neg – для унарного арифметического минуса).
4. Если одной команды недостаточно, порождается еще одна команда (в данном случае для логического отрицания требуется еще команда and).
5. Если для триады требуется сохранить промежуточный результат, порождается команда mov, которая сохраняет результат из регистра eax в регистр или временную переменную, связанную с триадой.
Для бинарных линейных операций последовательность действий при генерации ассемблерного кода такова:
1. Запоминаются имена обоих операндов. Для переменных именем операнда является имя переменной, для константы – значение константы, а для ссылки на другую триаду, кроме предыдущей – имя регистра или временной переменной, в которой хранится результат вычисления триады (для предыдущей триады имя операнда пустое).
2. Если имя одного из операндов пустое (операнд получен при вычислении предыдущей триады), то нет необходимости загружать его в регистр eax, иначе порождается команда mov, которая загружает первый операнд в регистр eax.
3. Порождается команда, соответствующая бинарной операции над регистром eax. Если имя второго операнда пустое, то первый операнд триады становится вторым операндом команды, иначе – второй операнд триады становится вторым операндом команды.
4. Если одной команды недостаточно, порождается еще одна (в данном результирующем языке это необходимо только для команды вычитания sub в том случае, если операнды менялись местами – чтобы получить верный результат, требуется еще команда neg).
5. Если для триады требуется сохранить промежуточный результат, порождается команда mov, которая сохраняет результат из регистра eax в регистр или временную переменную, связанную с триадой.
Определение имени операнда выполняется вспомогательной функцией GetOpName. Порождение ассемблерного кода выполняется функцией MakeOper1 – для унарных операций, и функцией MakeOper2 – для бинарных операций. Можно обратить внимание, что функция GetOpName проверяет имя переменной на совпадение его с предопределенным именем CompileTest, и если имена совпадают, заменяет имя переменной на предопределенное имя Result. Эта проверка и подстановка – простейший пример модификации компилятором результирующего кода в зависимости от семантических соглашений (предопределенное имя Result всегда обозначает результат функции в выходном языке). В промышленных компиляторах такие модификации, как правило, связаны с неявными преобразованиями типов данных, принятыми во входном языке.
Последовательность порождения ассемблерного кода для триад, представляющих линейные операции, практически не зависит от внутреннего представления программы и может быть использована для любых типов триад, соответствующих линейным операциям (от типа триады зависит только тип порождаемой ассемблерной команды).
Для триад присваивания значений и для триад безусловного перехода (JMP) порождение команд элементарно просто и не требует пояснений.
Для операций сравнения интерес представляет получение результата, поскольку при выполнении команд сравнения в различных процессорах результатом, как правило, являются биты в специальном регистре – регистре флагов. Биты в регистре флагов могут быть непосредственно использованы в командах условных переходов, и если компилятор порождает код для логических операций, основанный на порядке их вычисления (неполное вычисление логических выражений было рассмотрено ранее), то он может этим воспользоваться. Но когда операции сравнения обрабатываются как линейные операции, нужно загрузить результат из регистра флагов в регистр общего назначения. Для этого также можно использовать условные переходы, например для триады типа:
1: < (a, b)
можно построить по
mov eax, a
cmp eax, b
jl @M1_1
xor eax, eax
jmp @M1_2
@M1_1: xor eax, eax
inc eax
@M1_2:
которая будет обеспечивать запись в регистр аккумулятора (eax) логического результата операции сравнения (0 – «ложь», 1 – «истина»).
Однако, как уже было сказано, большое количество операций передачи управления не способствует эффективности выполнения программы. К тому же рассмотренный выше подход порождает много лишних команд. Как правило, в процессорах есть команды, позволяющие организовать либо прямой обмен между регистром флагов и регистром аккумулятора, либо обмен данными через стек. В процессорах типа Intel 80x86 это команды группы set<*>, где <*> зависит от необходимого флага [41, 44]. Тогда для того же самого примера порядок команд будет иным:
mov eax, a
cmp eax, b
setl al
and eax, 1
В предлагаемом генераторе кода используется именно такой подход. А в остальном порождение кода для операций сравнения не отличается от порождения кода для прочих линейных операций.
Еще несколько слов необходимо сказать о триаде условного перехода IF. Для нее ситуация иная, чем для операций сравнения – чтобы выполнить условный переход, надо установить регистр флагов на основе регистра аккумулятора. Для этого можно воспользоваться простейшей командой процессора для сравнения регистра аккумулятора с ним самим, например:
test eax, eax
однако эффективность результирующего кода можно увеличить, если учесть, что триаде IF всегда предшествует либо триада сравнения, либо триада логической операции, а следовательно, при выполнении кода, порожденного для этих триад, флаги уже будут установлены соответствующим образом. Тогда нет необходимости порождать дополнительную команду для установки флагов и для триады IF достаточно построить только команду условного перехода по флагу «ноль» (в процессорах типа Intel 80x86 это команда jz).
Но система команд процессоров типа Intel 80x86 имеет одну особенность: команды условного перехода могут передавать управление не далее, чем на 128 байт вперед или назад от места команды. В момент генерации кода для триады IF, как правило, не известно, будет ли передача управления происходить в пределах 128 байт кода или выйдет за рамки данного ограничения. Чтобы обойти это ограничение, передачу управления можно организовать с помощью двух команд: сначала команда условного перехода по обратному условию «не ноль» передает управление на локальную метку, а потом команда безусловного перехода передает управление на требуемую «дальнюю» метку:
jnz @Fx
jmp @Mx
Fx:…
Здесь @Fx – локальная («обходная») метка, а @Mx – та метка, на которую необходимо передать управление. Именно такой подход реализован в разработанном генераторе ассемблерного кода.[11]11
На самом деле эту проблему можно было бы оставить без внимания, поскольку ассемблерный код порождается для системы программирования Delphi 5, которая сама умеет обрабатывать такие ситуации. Но в примере выполнения работы целенаправленно обращается внимание на эту проблему, чтобы проиллюстрировать, какие ситуации могут возникать при порождении результирующего кода для различных целевых вычислительных систем.
[Закрыть]
Есть еще одна особенность в генерации кода для триады IF: поскольку в разработанном генераторе триад операции сравнения и логические операции обрабатываются как линейные операции, а потому могут быть оптимизированы, первый операнд триады может оказаться константой. При этом триада IF будет выполнять не условный, а безусловный переход на одну из частей условного оператора в зависимости от значения этого операнда. Например, в последовательности операторов:
a:= 1;
if (a<0) b:=0 else b:=1;
первая часть условного оператора (b:=0) никогда не будет выполнена и в результате выполнения оптимизации это станет очевидным (первый операнд триады IF будет равен 0). Генератор ассемблерного кода порождает соответствующий код: если первый операнд равен 0 – команду безусловного перехода; если первый операнд не равен 0, никаких команд для триады IF вообще не порождается.
Можно отметить, что в этом случае вообще нет необходимости порождать код для одной из ветвей условного оператора, что сократит объем результирующего кода, но такая оптимизация требует существенных модификаций всего списка триад, что не предусмотрено в данном примере выполнения работы.
Описание используемого метода оптимизацииМашинно-независимые методы оптимизации
Оба используемых машинно-независимых метода оптимизации – метод свертки объектного кода и метод исключения лишних операций – были описаны при выполнении лабораторной работы № 4, поэтому нет необходимости описывать их здесь повторно. Эти методы оптимизации не зависят ни от входного, ни от результирующего языка, а потому реализующие их алгоритмы, разработанные при выполнении лабораторной работы № 4, могут быть без модификаций использованы в курсовой работе.
Функции, осуществляющие оба метода машинно-независимой оптимизации, реализованы в модуле TrdOpt (листинг П3.11, приложение 3). Для алгоритма оптимизации методом свертки объектного кода необходимо вычислять значения триад, которые могут входить в состав линейных участков кода. Типы таких триад, а также функции вычисления их значений зависят от входного языка (поэтому при выполнении лабораторной работы № 4 они были выделены в отдельный модуль). Вычисления триад для алгоритма свертки объектного кода для курсовой работы реализованы в модуле TrdCalc (листинг П3.9, приложение 3).
Кроме этих двух методов при генерации результирующего кода реализован еще один простейший метод оптимизации, который зависит от семантики входного языка. Этот метод основан на особенностях выполнения арифметических и логических операций. Учитываются следующие особенности:
• для логической операции OR нет необходимости порождать код, выполняющий эту операцию, если один из операндов равен 0;
• для операции AND нет необходимости порождать код, выполняющий эту операцию, если один из операндов равен 1;
• для арифметической операции сложения нет необходимости порождать код, выполняющий эту операцию, если любой из операндов равен 0, а для арифметической операции вычитания – если второй операнд равен 0.
В отличие от двух ранее рассмотренных методов оптимизации, эта оптимизация выполняется не над внутренним представлением программы (триадами), а над результирующей программой при генерации ассемблерного кода. Поэтому соответствующие действия реализованы в функции MakeOpcode в модуле TrdAsm (листинг П3.13, приложение 3).
Машинно-зависимые методы оптимизации
В качестве дополнительных возможностей в компиляторе, построенном в ходе выполнения примера курсовой работы, реализованы простейшие машинно-зависимые методы оптимизации. Эти методы не претендуют ни на полноту, ни на существенное повышение эффективности результирующей программы, но на их основе можно показать, как выполняется машинно-зависимая оптимизация.
Реализованные методы машинно-зависимой оптимизации основаны на двух особенностях системы команд процессоров типа Intel 80x86 [41, 44]:
1. Особенность загрузки данных в регистр аккумулятора eax.
2. Особенность выполнения арифметических операций.
В первом случае учитывается, что команда загрузки нулевого значения в регистр аккумулятора eax
mov eax, 0
выполняется дольше и имеет большую длину, чем команда очистки регистра eax, которая может быть осуществлена с помощью операций xor (исключающее или) или sub (вычитание) над этим регистром процессора. Например:
xor eax, eax
Поэтому в тех случаях, когда в регистр аккумулятора eax требуется загрузить нулевое значение, генератор ассемблерного кода порождает именно команду очистки регистра. Аналогично, если необходимо загрузить значение, равное 1, то порождается пара команд
xor eax, eax
inc eax
а для значения -1 – пара команд
xor eax, eax
dec eax
Оптимизация загрузки регистра аккумулятора выполняется при порождении результирующего кода. Она реализована в функции MakeMove в модуле TrdAsm (листинг П3.13, приложение 3).
Надо отметить, что эта оптимизация существенно зависит и от целевой вычислительной системы (поскольку она использует особенности системы команд процессоров типа Intel 80x86), и от результирующего языка (например, если бы операндами были однобайтовые величины, эффективность такой оптимизации была бы сомнительна).
Во втором случае учитывается, что при выполнении операций сложения и вычитания в тех случаях, когда один из операндов равен 1 или -1, результирующий код будет более эффективным, если использовать ассемблерные команды увеличения и уменьшения значения регистра на 1 (команды inc и dec):
• для операции сложения порождается команда inc вместо команды add, если один из операндов равен 1;
• для операции сложения порождается команда dec вместо команды add, если один из операндов равен -1;
• для операции вычитания порождается команда inc вместо команды sub, если второй операнд равен -1;
• для операции вычитания порождается команда dec вместо команды sub, если второй операнд равен 1.
Оптимизация арифметических операций также происходит при генерации результирующего кода. Она реализована в функции MakeOpcode в модуле TrdAsm (листинг П3.13, приложение 3).
Надо отметить, что эта оптимизация меньше зависит от целевой вычислительной системы (поскольку практически во всех типах процессоров есть команды, увеличивающие или уменьшающие значение регистра на 1) и совсем не зависит от результирующего языка.
Машинно-зависимые методы оптимизации выполняются компилятором на этапе порождения результирующей программы. Причем функции генерации кода, упомянутые выше, сочетают в себе машинно-зависимую и машинно-независимую оптимизацию.
Текст программы компилятораПолный текст всех модулей компилятора, созданного при реализации примера выполнения курсовой работы, приведен в Приложении 3. Те из этих модулей, которые не зависят от входного языка, были использованы ранее при выполнении лабораторных работ № 1–4. Кроме того, модули можно найти в архиве, располагающемся на веб-сайте издательства, в подкаталогах CURSOV и COMMON.
Все функциональные модули и их назначение в работе были рассмотрены выше.
Организация интерфейса с пользователем
По заданию компилятор должен получать входные данные из командной строки (обработка командной строки описана далее). Дополнительно для созданного компилятора реализован графический интерфейс с пользователем, аналогичный интерфейсу, использованному в лабораторных работах № 2–4. Окно графического интерфейса открывается в том случае, когда командная строка не указана.
Модуль, обеспечивающий интерфейс с пользователем (FormLab4), реализует графическое окно TCursovForm на основе класса TForm библиотеки VCL. Он обеспечивает интерфейс средствами Graphical User Interface (GUI) в ОС типа Windows на основе стандартных органов управления из системных библиотек данной ОС. Этот модуль обеспечивает также обработку входной командной строки компилятора и включает в себя две составляющие:
• файл программного кода (файл FormLab4.pas);
• файл описания ресурсов пользовательского интерфейса (файл FormLab4.dfm).
Более подробно принципы организации пользовательского интерфейса на основе GUI и работа систем программирования с ресурсами интерфейса описаны в [3, 5–7]. Полный текст программного кода модуля интерфейса с пользователем приведен в листинге П3.14 в приложении 3. Описание ресурсов пользовательского интерфейса, связанное с этим модулем, можно найти в архиве, располагающемся на веб-сайте издательства, в файле FormLab4.dfm в подкаталоге CURSOV.
Модуль FormLab4 построен на основе такого же модуля, который использовался для реализации интерфейса с пользователем в лабораторной работе № 4. Он содержит все данные, управляющие и интерфейсные элементы, которые были использованы в лабораторных работах № 2–4. Такой подход оправдан, поскольку этапы компиляции при выполнении курсовой работы совпадают с этапами выполнения соответствующих лабораторных работ.
Кроме органов управления, использованных в лабораторных работах № 2–4, интерфейсная форма, описанная в модуле FormLab4, содержит органы управления для генератора ассемблерного кода, которые созданы для курсовой работы:
• в многостраничной вкладке (PageControl1) появилась новая закладка (SheetAsm) под названием «Команды»;
• на закладке SheetAsm расположены интерфейсные элементы: ListAsm – список для вывода и просмотра порожденных ассемблерных команд;
• на первой закладке SheetFile («Исходный файл») появился дополнительный орган управления – флажок с двумя состояниями («пусто» или «отмечено»): CheckAsm – при включении этого флажка выполняется оптимизация результирующего кода, а при отключении – не выполняется.
Внешний вид новой закладки интерфейсной формы TCursovForm приведен на рис. 5.4.
Рис. 5.4. Внешний вид пятой закладки интерфейсной формы для курсовой работы.
Обработка входного файла в курсовой работе происходит в той же последовательности и в том же порядке, как это было описано при выполнении лабораторной работы № 4. Последним этапом, который отсутствовал в лабораторной работе, является этап порождения результирующего кода. На этом этапе выполняется следующая последовательность действий:
• вызывается функция MakeRegisters (модуль TrdAsm – листинг П3.13, приложение 3), которая распределяет регистры процессора по списку триад, результатом выполнения функции является количество необходимых временных переменных для списка триад (если регистров процессора не хватило), это значение запоминается;
• очищается содержимое списка ассемблерных команд ListAsm;
• в список ассемблерных команд записываются строки заголовка программы в соответствии с заданием;
• запоминается перечень всех идентификаторов программы (с помощью функции IdentList из модуля FncTree – листинг П3.2, приложение 3);
• если перечень идентификаторов не пустой, то:
– в список строк записывается ключевое слово маг;
– в список строк записываются все идентификаторы через запятую с указанием требуемого типа данных (integer);
• в список ассемблерных команд записываются строки заголовка функции CompileTest в соответствии с заданием;
• если количество необходимых временных переменных больше нуля, то:
– в список строк в тело функции помещается ключевое слово маг;
– за ключевым словом в списке строк записываются все имена временных переменных – таким образом временные переменные для хранения значений триад становятся локальными переменными функции Compi 1 eTest и размещаются в стеке;
• в список заносится заголовок ассемблерного кода (ключевые слова begin и asm и команда pushad для сохранения значений регистров процессора в стеке);
• вызывается функция MakeAsmCode (листинг П3.13, приложение 3), которая заполняет список текстом ассемблерных команд;
• в список заносится конец ассемблерного кода (команда popad для восстановления значений регистров процессора из стека и два ключевых слова end);
• в список помещаются строки тела главной программы в соответствии с заданием.
В отличие от лабораторных работ вся обработка данных в курсовой работе вынесена в отдельную функцию CompRun. Это сделано для того, чтобы для выполнения компиляции одна и та же функция вызывалась вне зависимости от того, как запущен компилятор – с командной строкой или без нее.
Обработка командной строки
Как требуется по заданию, созданный компилятор должен уметь работать с командной строкой. Для созданного в данном примере компилятора командная строка должна иметь вид:
<компилятор> <входной_файл> [<ключи>]
где:
<компилятор> – имя исполняемого файла компилятора;
<входнойфайл> – имя входного файла и путь к нему (если путь не указан, то компилятор ищет файл в текущем каталоге), первый обязательный параметр командной строки;
<ключи> – необязательные параметры (ключи) запуска компилятора (второй и последующие параметры).
Если входной файл не указан (нет ни одного параметра в командной строке), то открывается окно графического интерфейса с пользователем, которое было описано выше. Иначе, если входной файл указан, компилятор читает исходную программу из этого файла, обрабатывает ее, и если программа не содержит ошибок – помещает результаты в выходной файл, а сообщения об ошибках – в специальный файл ошибок. После этого компилятор сразу же завершает свою работу (никакое окно на экране в этом случае не отображается). Имя выходного файла и имя файла для сообщений об ошибках компилятор определяет, исходя из указанных параметров (ключей запуска).
Если указанный в строке запуска входной файл не найден, то компилятор выдает сообщение об ошибке чтения файла и после этого открывает окно графического интерфейса с пользователем (как если бы имя файла не было указано).
Проверка наличия параметров в командной строке запуска компилятора выполняется сразу при старте компилятора (функция FormCreate, модуль FormLab4, листинг П3.14 в приложении 3). Для этого используется системная функция Param-Count из библиотеки языка Object Pascal, которая возвращает количество параметров в командной строке (0 – параметры отсутствуют). Для анализа параметров командной строки используется системная функция ParamStr из библиотеки языка Object Pascal, которая возвращает строковое значение параметра по его порядковому номеру в командной строке. При этом нулевым параметром считается имя исполняемого файла.
Второй и последующий параметры в командной строке считаются ключами – необязательными параметрами запуска компилятора. Ключей в командной строке может быть любое количество (в том числе может не быть ни одного ключа, в этом случае командная строка содержит только один параметр – имя входного файла). Ключи могут следовать в командной строке в любом порядке. Ключи определяют режим работы компилятора и некоторые условия компиляции. Каждый ключ является отдельным параметром. Ключи отделяются друг от друга пробелами (если ключ должен содержать пробелы внутри себя, его следует взять в двойные кавычки – "…", – как это принято для параметров командных строк в ОС).
Каждый ключ должен начинаться с символа «-» (минус), за которым следует символ ключа (строчная или прописная буква латинского алфавита), а за ним – параметр ключа, если требуется.
Символы ключей имеют следующее значение (строчные и прописные буквы имеют одинаковое значение, поэтому здесь рассматриваются только прописные буквы):
• А – флаг оптимизации команд ассемблера, определяет, выполняется или нет оптимизация результирующего кода; за символом должно идти указание значения флага:
1 оптимизация выполняется (флаг включен);
0 – оптимизация не выполняется (флаг выключен), любой другой символ, следующий за символом А, воспринимается как 0;
• С – флаг свертки объектного кода, определяет, выполняется или нет оптимизация списка триад методом свертки объектного кода; за символом должно идти указание значения флага:
1 оптимизация выполняется (флаг включен);
0 – оптимизация не выполняется (флаг выключен), любой другой символ, следующий за символом С, воспринимается как 0;
• S – флаг исключения лишних операций, определяет, выполняется или нет оптимизация списка триад методом исключения лишних операций; за символом должно идти указание значения флага:
1 оптимизация выполняется (флаг включен);
0 – оптимизация не выполняется (флаг выключен), любой другой символ, следующий за символом S, воспринимается как 0;
• 0 – имя результирующего файла, непосредственно за символом должно идти имя файла и путь к нему (если путь не указан, считается, что файл будет находиться в текущем каталоге, откуда запущен компилятор);
• Е – имя файла с ошибками, непосредственно за символом должно идти имя файла и путь к нему (если путь не указан, считается, что файл будет находиться в текущем каталоге, откуда запущен компилятор).
Если какой-то из ключей не указан, то компилятор принимает значение ключа по умолчанию. Для флагов установлены следующие значения по умолчанию:
• флаг оптимизации команд ассемблера – включен;
• флаг свертки объектного кода – включен;
• флаг исключения лишних операций – включен.
Имя файла результирующей программы по умолчанию считается совпадающим с именем входного файла, но с расширением. asm. Имя файла с ошибками компиляции по умолчанию также считается совпадающим с именем входного файла, но с расширением. err. Путь к этим файлам по умолчанию устанавливается таким же, как и к входному файлу (первый параметр командной строки).
Анализ параметров командной строки запуска компилятора реализован в функции ProcessParams (модуль FormLab4, листинг П3.14 в приложении 3).
Правообладателям!
Данное произведение размещено по согласованию с ООО "ЛитРес" (20% исходного текста). Если размещение книги нарушает чьи-либо права, то сообщите об этом.Читателям!
Оплатили, но не знаете что делать дальше?