Какие задания выполняет типичный компилятор C ++?

После небольшого исследования компиляторов и того, как они работают, я узнал, что процесс часто разбивается на 4 этапа: препроцессор, компилятор, ассемблер и компоновщик. То, как я представлял эти шаги, представляло собой отдельную программу; Программа препроцессора, программа компилятора, программа ассемблера и программа компоновщика. Однако вы узнаете, что иногда процесс создания ассемблерного кода и создания объектных файлов все обрабатывается программой компилятора, а иногда — нет. Кажется, это очень сильно зависит от контекста и используемого языка программирования. Мой вопрос тогда, как типичный процесс перевода разбивается для перевода исходного кода C ++ в машинный код?

  1. Является ли препроцессор отдельной программой от компилятора? Или этот процесс обычно является частью программы компиляции?
  2. За что обычно отвечает компилятор? Генерация кода сборки, а затем преобразование в машинный код?
  3. Является ли компоновщик собственной программой, которая запускается после завершения работы компилятора?

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

6

Решение

Все современные компиляторы (по крайней мере, gcc и clang, но я сомневаюсь, что другие сильно отличаются) имеют предварительную обработку и компилятор как один исполняемый файл. Это происходит главным образом потому, что компилятор хочет иметь возможность генерировать хорошие сообщения об ошибках [которые указывают на правую строку и столбец, а когда он задействован в макросах, может сказать «Вызывается из макроса FOO (x)») и понимать «какой файл мы «войти» легче, когда компилятор имеет фактический исходный код, а не предварительно обработанный код.

Компоновщик, как правило, представляет собой отдельную программу, и ассемблер используется только для встроенного ассемблерного кода [как правило, как встроенная часть компилятора] — в противном случае компилятор будет генерировать машинный код напрямую, без использования ассемблера [по крайней мере в LLVM, который является компилятор, который я знаю лучше всего. Таким образом, из компилятора выходит полностью сформированный объектный файл.

Если у вас есть правильные параметры, будет вызван компоновщик, но это отдельный исполняемый файл, который свяжет объектный файл вместе с библиотекой времени выполнения и стартовым кодом «перед основным» (создание глобального объекта и т. П., А также « готовлюсь к вызову главного «). Это создаст исполняемый файл.

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

Внутренняя часть компилятора, отвечающая за генерацию кода, также обычно занимается оптимизацией и различными преобразованиями кода, чтобы помочь этапам оптимизации — например, Clang + LLVM будет генерировать «однородные» циклы, независимо от того, использовали ли вы while, for или же goto сделать петлю.

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

4

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]