0

Для чего предназначен компилятор

Компиля́тор — программа или техническое средство, выполняющее компиляцию программы [1] [2] [3] .

Компиля́ция — сборка программы, включающая трансляцию всех модулей программы, написанных на одном или нескольких исходных языках программирования высокого уровня и/или языке ассемблера, в эквивалентные программные модули на низкоуровневом языке, близком машинному коду (абсолютный код, объектный модуль, иногда на язык ассемблера) [2] [3] [4] или непосредственно на машинном языке или ином двоичнокодовом низкоуровневом командном языке и последующую сборку исполняемой машинной программы. Если компилятор генерирует исполняемую машинную программу на машинном языке, то такая программа непосредственно исполняется физической программируемой машиной (например компьютером). В других случаях исполняемая машинная программа выполняется соответствующей виртуальной машиной. Входной информацией для компилятора (исходный код) является описание алгоритма или программы на предметно-ориентированном языке, а на выходе компилятора — эквивалентное описание алгоритма на машинно-ориентированном языке (объектный код [5] , байт-код).

Компили́ровать — проводить трансляцию машинной программы с предметно-ориентированного языка на машинно-ориентированный язык [3] .

Содержание

Виды компиляторов [ править | править код ]

  • Векторизующий. Базируется на трансляторе, транслирующем исходный код в машинный код компьютеров, оснащённых векторным процессором.
  • Гибкий. Сконструирован по модульному принципу, управляется таблицами и запрограммирован на языке высокого уровня или реализован с помощью компилятора компиляторов.
  • Диалоговый. См.: диалоговый транслятор.
  • Инкрементальный. Пересобирает программу, заново транслируя только изменённые фрагменты программы без перетрансляции всей программы.
  • Интерпретирующий (пошаговый). Последовательно выполняет независимую компиляцию каждого отдельного оператора (команды) исходной программы.
  • Компилятор компиляторов. Транслятор, воспринимающий формальное описание языка программирования и генерирующий компилятор для этого языка.
  • Отладочный. Устраняет отдельные виды синтаксических ошибок.
  • Резидентный. Постоянно находится в оперативной памяти и доступен для повторного использования многими задачами.
  • Самокомпилируемый. Написан на том же языке программирования, с которого осуществляется трансляция.
  • Универсальный. Основан на формальном описании синтаксиса и семантики входного языка. Составными частями такого компилятора являются: ядро, синтаксический и семантический загрузчики.

Виды компиляции [ править | править код ]

  • Пакетная. Компиляция нескольких исходных модулей в одном задании.
  • Построчная. Машинный код порождается и затем исполняется для каждой завершённой грамматической конструкции языка. Внешне воспринимается как интерпретация, но устройство имеет иное.
  • Условная. Компиляция, при которой транслируемый текст зависит от условий, заданных в исходной программе директивами компилятора. (Яркий пример — работа препроцессора языка С и производных от него.) Так, в зависимости от значения некой константы некая заданная часть исходного текста программы транслируется или не транслируется.

Структура компилятора [ править | править код ]

Процесс компиляции состоит из следующих этапов:

  1. Трансляция программы — трансляция всех или только изменённых модулей исходной программы.
  2. компоновка машинно-ориентированной программы.

В первом случае компилятор представляет собой пакет программ, включающий в себя трансляторы с разных языков программирования и компоновщики. Такой компилятор может компилировать программу, разные части исходного текста которой написаны на разных языках программирования. Нередко такие компиляторы управляются встроенным интерпретатором того или иного командного языка. Яркий пример таких компиляторов — имеющийся во всех UNIX-системах (в частности в Linux) компилятор make.

Во втором случае компилятор де-факто выполняет только трансляцию и далее вызывает компоновщик как внешнюю подпрограмму, который и компонует машинно-ориентированную программу. Этот факт нередко служит поводом считать компилятор разновидностью транслятора, что естественно неверно, — все современные компиляторы такого типа поддерживают организацию импорта программой процедуры (функции) из уже оттранслированого программного модуля, написанного на другом языке программирования. Так в программу на С/С++ можно импортировать функцию написанную например Pascal или Fortran. Аналогично и напротив написанная на С/С++ функция может быть импортирована в Pascal- или Fortran-программу соответственно. Это как правило было бы невозможно без поддержки многими современными компиляторами организации обработки входных данных в процедуру (функций) в соответствии с соглашениями других языков программирования. Например современные компиляторы с языка Pascal помимо соглашения самого Pascal поддерживает организацию обработки процедурой/функцией входных в соответствии с соглашениями языка С/С++. (Чтобы на уровне машинного кода написанная на Pascal процедура/функция работала с входными параметрами в соответствии с соглашениями языка С/С++, — оператор объявления такой Pascal-процедуры/Pascal-функции должен содержать ключевое слово cdecl.) Примерами таких компиляторов являются компиляторы со всех без исключения языков программирования, используемые непосредственно.

Трансляция программы как неотъемлемая составляющая компиляции включает в себя:

  1. Лексический анализ. На этом этапе последовательность символов исходного файла преобразуется в последовательность лексем.
  2. Синтаксический (грамматический) анализ. Последовательность лексем преобразуется в дерево разбора.
  3. Семантический анализ. Дерево разбора обрабатывается с целью установления его семантики (смысла) — например, привязка идентификаторов к их декларациям, типам, проверка совместимости, определение типов выражений и т. д. Результат обычно называется «промежуточным представлением/кодом», и может быть дополненным деревом разбора, новым деревом, абстрактным набором команд или чем-то ещё, удобным для дальнейшей обработки.
  4. Оптимизация. Выполняется удаление излишних конструкций и упрощение кода с сохранением его смысла. Оптимизация может быть на разных уровнях и этапах — например, над промежуточным кодом или над конечным машинным кодом.
  5. Генерация кода. Из промежуточного представления порождается код на целевом машинно-ориентированном языке.
Читайте также:  Дивинити ориджинал син 2 дефинитив едитион изменения

Генерация кода [ править | править код ]

Генерация машинного кода [ править | править код ]

Большинство компиляторов переводит программу с некоторого высокоуровневого языка программирования в машинный код, который может быть непосредственно выполнен физическим процессором. Как правило, этот код также ориентирован на исполнение в среде конкретной операционной системы, поскольку использует предоставляемые ею возможности (системные вызовы, библиотеки функций). Архитектура (набор программно-аппаратных средств), для которой компилируется (собирается) машинно-ориентированная программа, называется целевой машиной.

Результат компиляции — исполнимый программный модуль — обладает максимально возможной производительностью, однако привязан к конкретной операционной системе (семейству или подсемейству ОС) и процессору (семейству процессоров) и не будет работать на других.

Для каждой целевой машины (IBM, Apple, Sun, Эльбрус и т. д.) и каждой операционной системы или семейства операционных систем, работающих на целевой машине, требуется написание своего компилятора. Существуют также так называемые кросс-компиляторы, позволяющие на одной машине и в среде одной ОС генерировать код, предназначенный для выполнения на другой целевой машине и/или в среде другой ОС. Кроме того, компиляторы могут оптимизировать код под разные модели из одного семейства процессоров (путём поддержки специфичных для этих моделей особенностей или расширений наборов команд). Например, код, скомпилированный под процессоры семейства Pentium, может учитывать особенности распараллеливания инструкций и использовать их специфичные расширения — MMX, SSE и т. п.

Некоторые компиляторы переводят программу с языка высокого уровня не прямо в машинный код, а на язык ассемблера. (Пример: PureBasic, транслирующий бейсик-код в ассемблер FASM.) Это делается для упрощения части компилятора, отвечающей за генерацию кода, и повышения его переносимости (задача окончательной генерации кода и привязки его к требуемой целевой платформе перекладывается на ассемблер), либо для возможности контроля и исправления результата компиляции (в том числе ручной оптимизации) программистом.

Генерация байт-кода [ править | править код ]

Результатом работы компилятора может быть программа на специально созданном низкоуровневом языке двоично-кодовых команд, выполняемых виртуальной машиной. Такой язык называется псевдокодом или байт-кодом. Как правило, он не есть машинный код какого-либо компьютера и программы на нём могут исполняться на различных архитектурах, где имеется соответствующая виртуальная машина, но в некоторых случаях создаются аппаратные платформы, напрямую выполняющие псевдокод какого-либо языка. Например, псевдокод языка Java называется байт-кодом Java и выполняется в Java Virtual Machine, для его прямого исполнения была создана спецификация процессора picoJava. Для платформы .NET Framework псевдокод называется Common Intermediate Language (CIL), а среда исполнения — Common Language Runtime (CLR).

Некоторые реализации интерпретируемых языков высокого уровня (например, Perl) используют байт-код для оптимизации исполнения: затратные этапы синтаксического анализа и преобразование текста программы в байт-код выполняются один раз при загрузке, затем соответствующий код может многократно использоваться без перекомляции.

Динамическая компиляция [ править | править код ]

Из-за необходимости интерпретации байт-код выполняется значительно медленнее машинного кода сравнимой функциональности, однако он более переносим (не зависит от операционной системы и модели процессора). Чтобы ускорить выполнение байт-кода, используется динамическая компиляция, когда виртуальная машина транслирует псевдокод в машинный код непосредственно перед его первым исполнением (и при повторных обращениях к коду исполняется уже скомпилированный вариант).

Наиболее популярной разновидностью динамической компиляции является JIT. Другой разновидностью является инкрементальная компиляция [en] .

CIL-код также компилируется в код целевой машины JIT-компилятором, а библиотеки .NET Framework компилируются заранее.

Трансляция байт-кода в машинный код [ править | править код ]

Трансляция байт-кода в машинный код специальным транслятором байт-кода как указано выше неотъемлемая фаза динамической компиляции. Но трансляция байт-кода применима и для простого преобразования программы на байт-коде в эквивалентную программу на машинном языке. В машинный код может транслироваться как заранее скомпилированный байт-код. Но также трансляция байт-кода в машинный код может выполняться компилятором байт-кода сразу следом за компиляцией байт-кода. Практически всегда в последнем случае трансляция байт-кода выполняется внешним транслятором, вызываемым компилятором байт-кода.

Декомпиляция [ править | править код ]

Существуют программы, которые решают обратную задачу — перевод программы с низкоуровневого языка на высокоуровневый. Этот процесс называют декомпиляцией, а такие программы — декомпиляторами. Но поскольку компиляция — это процесс с потерями, точно восстановить исходный код, скажем, на C++, в общем случае невозможно. Более эффективно декомпилируются программы в байт-кодах — например, существует довольно надёжный декомпилятор для Flash. Разновидностью декомпиляции является дизассемблирование машинного кода в код на языке ассемблера, который почти всегда благополучно выполняется (при этом сложность может представлять самомодифицирующийся код или код, в котором собственно код и данные не разделены). Связано это с тем, что между кодами машинных команд и командами ассемблера имеется практически взаимно-однозначное соответствие.

Раздельная компиляция [ править | править код ]

Раздельная компиляция (англ. separate compilation ) — трансляция частей программы по отдельности с последующим объединением их компоновщиком в единый загрузочный модуль [2] .

Исторически особенностью компилятора, отражённой в его названии (англ. compile — собирать вместе, составлять), являлось то, что он производил как трансляцию, так и компоновку, при этом компилятор мог порождать сразу машинный код. Однако позже, с ростом сложности и размера программ (и увеличением времени, затрачиваемого на перекомпиляцию), возникла необходимость разделять программы на части и выделять библиотеки, которые можно компилировать независимо друг от друга. В процессе трансляции программы сам компилятор или вызываемый компилятором транслятор порождает объектный модуль, содержащий дополнительную информацию, которая потом — в процессе компоновки частей в исполнимый модуль — используется для связывания и разрешения ссылок между частями программы. Раздельная компиляция также позволяет писать разные части исходного текста программы на разных языках программирования.

Читайте также:  Ваз 2114 постоянно работает вентилятор охлаждения

Появление раздельной компиляции и выделение компоновки как отдельной стадии произошло значительно позже создания компиляторов. В связи с этим вместо термина «компилятор» иногда используют термин «транслятор» как его синоним: либо в старой литературе, либо когда хотят подчеркнуть его способность переводить программу в машинный код (и наоборот, используют термин «компилятор» для подчёркивания способности собирать из многих файлов один). Вот только использование в таком контексте терминов «компилятор» и «транслятор» неправильно. Даже если компилятор выполняет трансляцию программы самостоятельно, поручая компоновку вызываемой внешней программе-компоновщику, такой компилятор не может считаться разновидностью транслятора, — транслятор выполняет трансляцию исходной программы и только. И уж тем более не являются трансляторами компиляторы вроде системной утилиты-компилятора make, имеющейся во всех UNIX-системах.

Собственно утилита make — яркий пример довольно удачной реализации раздельной компиляции. Работа утилиты make управляется сценарием на интерпретируемым утилитой входном языке, известном как makefile, содержащемся в задаваемом при запуске утилиты входном текстовом файле. Сама утилита не выполняет ни трансляцию, ни компоновку — де-факто утилита make функционирует как диспетчер процесса компиляции, организующий компиляцию программы в соответствии с заданным сценарием. В частности в ходе компиляции целевой программы утилита make вызывает трансляторы с языков программирования, транслирующие разные части исходной программы в объектный код, и уже после этого вызывается тот или иной компоновщик, компонующий конечный исполняемый программный или библиотечный программный модуль. При этом разные части программы, оформляемые в виде отдельных файлов исходного текста, могут быть написаны как на одном языке программирования, так и на разных языках программирования. В процессе перекомпиляции программы транслируются только изменённые части-файлы исходного текста программы, вследствие чего длительность перекомпиляции программы значительно (порой на порядок) сокращается.

История [ править | править код ]

На заре развития компьютеров первые компиляторы (трансляторы) называли «программирующими программами» [6] (так как в тот момент программой считался только машинный код, а «программирующая программа» была способна из человеческого текста сделать машинный код, то есть запрограммировать ЭВМ).

Процесс компиляции состоит из следующих этапов:

  1. Лексический анализ. На этом этапе последовательность символов исходного файла преобразуется в последовательность лексем.
  2. Синтаксический (грамматический) анализ. Последовательность лексем преобразуется в дерево разбора.
  3. Семантический анализ. Дерево разбора обрабатывается с целью установления его семантики (смысла) — например, привязка идентификаторов к их декларациям, типам, проверка совместимости, определение типов выражений и т. д. Результат обычно называется «промежуточным представлением/кодом», и может быть дополненным деревом разбора, новым деревом, абстрактным набором команд или чем-то ещё, удобным для дальнейшей обработки.
  4. Оптимизация. Выполняется удаление излишних конструкций и упрощение кода с сохранением его смысла. Оптимизация может быть на разных уровнях и этапах — например, над промежуточным кодом или над конечным машинным кодом.
  5. Генерация кода. Из промежуточного представления порождается код на целевом языке.

В конкретных реализациях компиляторов эти этапы могут быть разделены или, наоборот, совмещены в том или ином виде.

Генерация кода

Генерация машинного кода

Большинство компиляторов переводит программу с некоторого высокоуровневого языка программирования в машинный код, который может быть непосредственно выполнен процессором. Как правило, этот код также ориентирован на исполнение в среде конкретной операционной системы, поскольку использует предоставляемые ею возможности (системные вызовы, библиотеки функций). Архитектура (набор программно-аппаратных средств), для которой производится компиляция, называется целевой машиной.

Результат компиляции — исполнимый модуль — обладает максимальной возможной производительностью, однако привязан к определённой операционной системе и процессору (и не будет работать на других).

Для каждой целевой машины (IBM, Apple, Sun и т. д.) и каждой операционной системы или семейства операционных систем, работающих на целевой машине, требуется написание своего компилятора. Существуют также так называемые кросс-компиляторы, позволяющие на одной машине и в среде одной ОС генерировать код, предназначенный для выполнения на другой целевой машине и/или в среде другой ОС. Кроме того, компиляторы могут оптимизировать код под разные модели из одного семейства процессоров (путём поддержки специфичных для этих моделей особенностей или расширений наборов инструкций). Например, код, скомпилированный под процессоры семейства Pentium, может учитывать особенности распараллеливания инструкций и использовать их специфичные расширения — MMX, SSE и т. п.

Некоторые компиляторы переводят программу с языка высокого уровня не прямо в машинный код, а на язык ассемблера. Это делается для упрощения части компилятора, отвечающей за кодогенерацию, и повышения его переносимости (задача окончательной генерации кода и привязки его к требуемой целевой платформе перекладывается на ассемблер), либо для возможности контроля и исправления результата компиляции программистом.

Читайте также:  В каком году создали фейсбук

Генерация байт-кода

Результатом работы компилятора может быть программа на специально созданном низкоуровневом языке, подлежащем интерпретации виртуальной машиной. Такой язык называется псевдокодом или байт-кодом. Как правило, он не является машинным кодом какого-либо компьютера и программы на нём могут исполняться на различных архитектурах, где имеется соответствующая виртуальная машина, но в некоторых случаях создаются аппаратные платформы, напрямую поддерживающие псевдокод какого-либо языка. Например, псевдокод языка Java называется байт-кодом Java и выполняется в Java Virtual Machine, для его прямого исполнения была создана спецификация процессора picoJava. Для платформы .NET Framework псевдокод называется Common Intermediate Language (CIL), а среда исполнения — Common Language Runtime (CLR).

Некоторые реализации интерпретируемых языков высокого уровня (например, Perl) используют байт-код для оптимизации исполнения: затратные этапы синтаксического анализа и преобразование текста программы в байт-код выполняются один раз при загрузке, затем соответствующий код может многократно использоваться без промежуточных этапов.

Динамическая компиляция

Из-за необходимости интерпретации байт-код выполняется значительно медленнее машинного кода сравнимой функциональности, однако он более переносим (не зависит от операционной системы и модели процессора). Чтобы ускорить выполнение байт-кода, используется динамическая компиляция, когда виртуальная машина транслирует псевдокод в машинный код непосредственно перед его первым исполнением (и в при повторных обращениях к коду исполняется уже скомпилированный вариант).

CIL-код также компилируется в код целевой машины JIT-компилятором, а библиотеки .NET Framework компилируются заранее.

Декомпиляция

Существуют программы, которые решают обратную задачу — перевод программы с низкоуровневого языка на высокоуровневый. Этот процесс называют декомпиляцией, а такие программы — декомпиляторами. Но поскольку компиляция — это процесс с потерями, точно восстановить исходный код, скажем, на C++, в общем случае невозможно. Более эффективно декомпилируются программы в байт-кодах — например, существует довольно надёжный декомпилятор для Flash. Разновидностью декомпилирования является дизассемблирование машинного кода в код на языке ассемблера, который почти всегда выполняется успешно (при этом сложность может представлять самомодифицирующийся код или код, в котором собственно код и данные не разделены). Связано это с тем, что между кодами машинных команд и командами ассемблера имеется практически взаимно-однозначное соответствие.

Раздельная компиляция

Раздельная компиляция (англ. separate compilation ) — трансляция частей программы по отдельности с последующим объединением их компоновщиком в единый загрузочный модуль. [2]

Исторически особенностью компилятора, отражённой в его названии (англ. compile — собирать вместе, составлять), являлось то, что он производил как трансляцию, так и компоновку, при этом компилятор мог порождать сразу абсолютный код. Однако позже, с ростом сложности и размера программ (и увеличением времени, затрачиваемого на перекомпиляцию), возникла необходимость разделять программы на части и выделять библиотеки, которые можно компилировать независимо друг от друга. При трансляции каждой части программы компилятор порождает объектный модуль, содержащий дополнительную информацию, которая потом, при компоновке частей в исполнимый модуль, используется для связывания и разрешения ссылок между частями.

Появление раздельной компиляции и выделение компоновки как отдельной стадии произошло значительно позже создания компиляторов. В связи с этим вместо термина «компилятор» иногда используют термин «транслятор» как его синоним: либо в старой литературе, либо когда хотят подчеркнуть его способность переводить программу в машинный код (и наоборот, используют термин «компилятор» для подчёркивания способности собирать из многих файлов один).

Интересные факты

На заре развития компьютеров первые компиляторы (трансляторы) называли «программирующими программами» [6] (так как в тот момент программой считался только машинный код, а «программирующая программа» была способна из человеческого текста сделать машинный код, то есть запрограммировать ЭВМ).

Если не брать компьютер, то сейчас компиляторы те, кто "заимствуют" у других тексты: таких полно интернете, немало в научных публикациях. Можно привести два примера, которые на слуху, как Австрии и ФРГ высшие должностные лица подали в отставку, после того, как их уличили в компиляциях в их диссертациях. Вот так. Вот если бы они полностью скопировали работу и поставили свое имя, они бы стали плагиаторами.

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

Сейчас пользователю предлагается такой интерфейс (способ взаимодействия) , который ему понятен, но который надо перевести на язык, понятный всем, кто будет пользоваться создаваемым продуктом. И в первую очередь, понятный самому компьютеру с его собственной системой команд.

Конечно, можно писать программы и на языке компьютера, но это — высший пилотаж, удел немногих, чьи программы гарантированно будут использовать миллионы, и даже неоднократно. Или когда требуется «выжать» из компьютера максимальные параметры, например, быстродействие.

Несмотря на то, что Windows используется сверхмногократно, она пишется не на языке компьютера, и ее приходится компилировать. Причина простая: найдешь такого специалиста-виртуоза, что напишет тебе программу на языке низкого уровня, а кто потом сможет в ней разобраться? Только такой же. А где его взять?

admin

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *