И.А. Волкова, И.Г. Головин, Л.Е. Карпов - Системы программирования (1114897), страница 15
Текст из файла (страница 15)
Эффективнаяреализация этого процесса является одной из важнейших характеристик, как системпрограммирования, так и программ системной поддержки.3.3.6. Генерация кодаЗавершающей стадией работы компилятора является генерация кода (команд,констант и т. д.) объектной программы. Генератор кода получает на входпромежуточное представление исходной программы и вырабатывает эквивалентнуюобъектную программу, используя для этой цели всю информацию, накопленную кэтому моменту в информационных таблицах компилятора.Работа генератора не зависит от того, имеется ли перед фазой генерациидополнительная фаза оптимизации, или она отсутствует. Математически проблемагенерации эффективной объектной программы является неразрешимой, но на практике57применяются достаточно эффективные эвристические подходы, дающие хорошиерезультаты, хотя и не абсолютно оптимальные.На вход генератора кода поступает внутреннее, промежуточное представлениекомпилируемой программы, полученное на начальных стадиях компиляции,осуществляющих анализ исходной программы и построение информационных таблицкомпилятора.
Обычно таблица информации используется генератором кода дляопределения тех адресов объектов программы, которые они будут иметь в процессевыполнения программы. Так как перед началом генерации кода уже выполненсинтаксический разбор, отсеяны очевидно ошибочные конструкции и получено полноевнутреннее представление исходной программы, во внутреннем представлениипрограммы эти объекты обозначаются специально построенными внутреннимиименами, по которым записи об объектах легко отыскать в информационных таблицах,а не теми именами, которые присвоил им программист.Внутреннее представление, поступающее на вход генератора кода, может иметьразличные формы. Одно из самых существенных предположений, которые делаютсяпри разработке генераторов кода, состоит в том, что в поступающем на его входвнутреннем представлении программы отсутствуют ошибки (хотя иногда генерациякода выполняется параллельно с семантическим анализом программ).Реальные компиляторы готовят результаты своей работы в виде объектныхпрограмм, для кодирования которых могут использоваться такие виды языков:••••машинные коды в абсолютных адресах;перемещаемый машинный код;язык ассемблера целевой машины;другой язык программирования высокого уровня.Машинный язык в абсолютных адресах имеет свои преимущества в том, чтопрограммы на таком языке могут помещаться в фиксированные места памяти машиныи немедленно выполняться.
Такие системы обычно используются в целях обученияязыкам и методам программирования. Некоторые языки программирования (Алгол-60и Паскаль) специально разрабатывались в расчете на использование подобной схемыкомпиляции. Для других языков больше подходят другие режимы работыкомпиляторов.Генерация перемещаемого объектного модуля с внутренним кодированием намашинном языке обеспечивает возможность раздельной компиляции файлов, изкоторых строится текст программы. Множество перемещаемых объектных модулейможет затем связываться в единое целое и помещаться в память машины длясовместного выполнения. Такое связывание выполняется редактором связей, аразмещение в памяти – загрузчиком.
Этот режим приводит к дополнительным затратамна связывание модулей, но раздельная компиляция имеет так много преимуществ(например, с ее помощью удается строить библиотеки программ), что компенсирует всеэти затраты. Чтобы редактор связей мог провести свою часть работы по созданиюполноценной самодостаточной программы, компилятор передает ему дополнительнуюинформацию в объектных модулях.Значительно облегчить процесс генерации объектной программы можетиспользование в качестве выходного кода языка ассемблера целевой машины.Генерация текстов на языке ассемблера имеет особенный смысл при наличии готовогоассемблера, который можно использовать для завершения работы по компиляции, Этот58подход также используется в составе многоязыковых систем программирования длясвязи компонентов, написанных на разных языках, между собой.Генерация программы на другом языке программирования часто используетсяпри наличии компилятора с некоторого языка программирования.
При такойподдержке разные языки можно транслировать в один, обеспечивая полный циклобработки программ. Эта же технология используется для быстрого созданияпрототипа компилятора или при проведении процедуры “раскрутки”, когда сначаласоздается компилятор для некоторого языкового ядра (ограниченного варианта языка),а затем последовательными переходами от версии к версии создается сериякомпиляторов для все более и более полных вариантов языка. Например, язык Фортрансвоим стандартом определен через ядро и дополнительные слои, которые можноотключать при компиляции, борясь за эффективность программ, но (может быть) вущерб удобству программирования.Любой генератор кода, независимо от формы внутреннего представленияпрограмм в компиляторе и вида выходного представления результатов компиляции,должен в своей работе решить две главные задачи:••выполнить распределение памяти для объектов данных и фрагментовкомпилируемой программы,выполнить поиск и подбор семантических эквивалентов конструкциямвнутреннего представления программ.Важной дополнительной функцией генератора кода в таком процессе, заметноусложняющей весь процесс генерации в целом, является проведение одновременно сгенерацией поиска стандартных последовательностей операторов по некоторымшаблонам и локальной оптимизации потока формируемых команд.Поиск по шаблонам помогает обнаруживать в программах некоторые частоиспользуемые ресурсоемкие последовательности операторов, для которых вконкретной аппаратуре объектной машины могут иметься решения, существенноповышающие эффективность их выполнения.
Например, для машин с конвейерной иливекторной архитектурой очень важно распознавать в программах, так называемыеливерморские циклы. Ливерморские циклы представляют собой 24 программы(первоначально написанные на Фортране) из состава производственных программ,разработанных Ливеpмоpской национальной лабораторией имени Лоуренса (США).Циклы являются вычислительными ядрами, характерными для трудоемких научныхрасчетов. Они включают в себя как общие математические операции (скалярноепроизведение и умножение матриц), так и сложные алгоритмы поиска и хранения(поисковый цикл Монте-Карло), например, так на языке Си выглядит четвертыйливерморский цикл, построенный на базе фрагмента подпрограммы решенияленточных линейных уравнений:void Loop4 (int n, REAL X [AR_1001], REAL Y [AR_1001]){ for (int k = 6; k < AR_1001; k += (AR_1001 - 7) / 2){ int lw = k - 6;for (int j = 4; j < n; j += 5) X [k - 1] -= X [lw ++] * Y [j];X [k - 1] *= Y [4];}}59Обнаружение подобной критической последовательности операторов позволяетвместо прямой подстановки обычной последовательности команд сформироватьпоследовательность, специфическую для данного шаблона и конкретнойвычислительной архитектуры.
Такая замена может уменьшить время выполненияважной программы в десятки или сотни раз.3.4. Редакторы связей: назначение, принципы работыКонечным результатом компиляции является объектный модуль. За один запусккомпилятора всегда порождается ровно один объектный модуль. Если какой-либокомпилятор создает при компиляции с языка программирования текст на языкеассемблера, объектный модуль все равно создается, но работа по его созданиюперекладывается на ассемблер.
Дальнейшая работа над объектными модулямипроводится редактором связей, которые иногда называются компоновщиками.Основное назначение редактора связей – завершить ту часть работы, котораяпринципиально не могла быть выполнена компилятором, а именно, осуществитьпривязку нескольких модулей друг к другу. Компилятор не мог выполнить такуюпривязку потому, что он всегда работает только с одной компилируемой программой,зная о других программных компонентах только то, что они должны существовать, атакже их программные интерфейсы.В отличие от компилятора, который во время своего запуска обрабатываеттолько один объект (программный компонент), редактор связей в общем случаеполучает на вход сразу несколько объектных модулей.
Эти модули могут бытьполучены редактором связей непосредственно от компилятора, а могут извлекаться имиз библиотек, которые также указываются среди параметров запуска компоновщика.Редактор связей должен заново прочитать все объектные модули (какоткомпилированные, так и библиотечные), необходимые для формирования однойполной программы, и выявить в них все упоминания внешних объектов (процедур,функций, констант, переменных и т.
д.). Внешними эти объекты являются поотношению к тому конкретному программному компоненту, в котором ониупоминаются и используются, но не определяются или не реализуются. Задачаредактора связей – отыскать среди всего набора объектных модулей те, которыеопределяют или реализуют внешние объекты других модулей. В конечном итогедолжны быть обнаружены все определения и реализации всех внешних объектов. Еслиже некоторые внешние связи остались неразрешенными, то есть соответствующие имобъекты не обнаружились ни в одном из объектных модулей, поданных наредактирование, редактор связей выдает сообщение об ошибке.Редактор связей в состоянии провести и другой контроль – контрольсоответствия между объектным модулем, в котором упоминается некоторое внешнееимя (используется объект с этим именем), и объектным модулем, в котором данныйобъект определен.