Это может быть странный вопрос.
Парень, пишущий компилятор C ++ (или любой другой язык, не относящийся к VM): должен ли он уметь читать / писать на машинном языке? Как это работает?
РЕДАКТИРОВАТЬ: я специально имею в виду компиляторы, которые компилируются в машинный код, а не в какой-либо другой язык программирования.
compiler
machine-code
Авив Кон
источник
источник
Ответы:
Нет, совсем нет. Вполне возможно (и часто даже предпочтительнее), чтобы ваш компилятор испускал ассемблерный код. Затем ассемблер позаботится о создании фактического машинного кода.
Между прочим, ваше различие между реализацией без виртуальной машины и реализацией виртуальной машины бесполезно.
Для начала, использование виртуальной машины или прекомпиляция в машинный код - это просто разные способы реализации языка; в большинстве случаев язык может быть реализован с использованием любой стратегии. Я действительно должен был использовать интерпретатор C ++ один раз.
Кроме того, многие виртуальные машины, такие как JVM, имеют двоичный машинный код и некоторый ассемблер, как обычная архитектура.
LLVM (который используется компиляторами Clang) заслуживает особого упоминания: он определяет виртуальную машину, для которой инструкции могут быть представлены в виде байтового кода, текстовой сборки или структуры данных, что позволяет очень легко вывести их из компилятора. Поэтому, хотя это было бы полезно для отладки (и для понимания того, что вы делаете), вам даже не нужно знать о языке ассемблера, только об API LLVM.
Приятной особенностью LLVM является то, что его виртуальная машина является просто абстракцией и что байт-код обычно не интерпретируется, а вместо этого прозрачно JITted. Таким образом, вполне возможно написать эффективно скомпилированный язык, даже не зная набора команд вашего процессора.
источник
Нет. Ключевым моментом вашего вопроса является то, что компиляция является чрезвычайно широким термином. Компиляция может происходить с любого языка на любой язык. А ассемблер / машинный код - только один из многих языков для цели компиляции. Например, языки Java и .NET, такие как C #, F # и VB.NET, все компилируются в некоторый промежуточный код вместо машинного кода. Не имеет значения, запускается ли он на виртуальной машине, язык все еще компилируется. Существует также возможность компиляции на какой-то другой язык, например C. C - довольно популярная цель компиляции, и многие инструменты делают это. И, наконец, вы можете использовать какой-нибудь инструмент или библиотеку для тяжелой работы по созданию машинного кода для вас. например, есть LLVM, который может уменьшить усилия, необходимые для создания автономного компилятора.
Кроме того, ваше редактирование не имеет никакого смысла. Это все равно, что спросить: «Каждый инженер должен понимать, как работает двигатель? И я спрашиваю об инженерах, работающих над двигателями». Если вы работаете с программой или библиотекой, которая генерирует машинный код, вы должны это понимать. Дело в том, что вам не нужно делать такие вещи при написании компилятора. Многие люди делали это раньше вас, поэтому у вас должна быть серьезная причина сделать это снова.
источник
Классически компилятор состоит из трех частей: лексического анализа, анализа и генерации кода. Лексический анализ разбивает текст программы на ключевые слова, имена и значения языка. Анализирует, как токены, полученные из лексического анализа, объединяются в синтаксически правильные выражения для языка. Генерация кода берет структуры данных, созданные синтаксическим анализатором, и переводит их в машинный код или другое представление. В настоящее время лексический анализ и анализ могут быть объединены в один шаг.
Очевидно, что человек, пишущий генератор кода, должен понимать целевой машинный код на очень глубоком уровне, включая наборы команд, конвейеры процессора и поведение кэша. В противном случае программы, созданные компилятором, будут медленными и неэффективными. Они вполне могут читать и писать машинный код, представленный восьмеричными или шестнадцатеричными числами, но обычно они пишут функции для генерации машинного кода, ссылаясь внутренне на таблицы машинных инструкций. Теоретически, люди, пишущие лексер и парсер, могут ничего не знать о генерации машинного кода. Фактически, некоторые современные компиляторы позволяют вам подключать свои собственные процедуры генерации кода, которые могут генерировать машинный код для некоторого процессора, о котором никогда не слышали авторы лексера и анализатора.
Однако на практике разработчики компиляторов на каждом этапе много знают о различных архитектурах процессоров, и это помогает им спроектировать структуры данных, необходимые для этапа генерации кода.
источник
Давным-давно я написал компилятор, который конвертировал два разных сценария оболочки. Это не подходило к машинному коду.
Запись компилятора должна понимать их вывод , но это часто не машинный код.
Большинство программистов никогда не напишут компилятор, который выводит машинный код или код сборки, но пользовательские компиляторы могут быть очень полезны во многих проектах для получения других выходных данных.
YACC - один из таких компиляторов, который не выводит машинный код…
источник
Вам не нужно начинать с детального знания семантики ваших входных и выходных языков, но лучше закончить с изящно детальным знанием обоих, иначе ваш компилятор будет ошибочно работать. Поэтому, если ваш ввод - C ++, а ваш вывод - какой-то конкретный машинный язык, вам в конечном итоге нужно будет знать семантику обоих.
Вот некоторые тонкости при компиляции C ++ в машинный код: (я уверен, что есть еще кое-что, о чем я забыл).
Какой размер будет
int
? «Правильный» выбор здесь - это искусство, основанное как на естественном размере указателя машины, производительности АЛУ для различных арифметических операций, так и на выборе, сделанном существующими компиляторами для машины. Есть ли у машины даже 64-битная арифметика? Если нет, то добавление 32-разрядных целых чисел должно переводиться в инструкцию, а добавление 64-разрядных целых чисел должно переводиться в вызов функции для выполнения 64-разрядного сложения. Есть ли в машине 8-битные и 16-битные операции добавления, или вам нужно имитировать операции с 32-битными операциями и маскированием (например, DEC Alpha 21064)?Какое соглашение о вызовах используется другими компиляторами, библиотеками и языками на компьютере? Параметры помещаются в стек справа налево или слева направо? Некоторые параметры входят в регистры, в то время как другие идут в стек? Находятся ли ints и float в разных пространствах регистров? Нужно ли обрабатывать параметры, выделенные регистру, при вызовах varargs? Какие регистры сохраняются вызывающим абонентом, а какие сохраняются вызываемым абонентом? Можете ли вы выполнить оптимизацию листового вызова?
Что делает каждая из инструкций по смене машины? Если вы попросите сдвинуть 64-битное целое число на 65 бит, каков результат? (На многих машинах результат аналогичен сдвигу на 1 бит, на других - «0».)
Какова семантика согласованности памяти машины? C ++ 11 имеет очень четко определенную семантику памяти, которая в некоторых случаях накладывает ограничения на некоторые оптимизации, но разрешает оптимизацию в других случаях. Если вы компилируете язык, который не имеет четко определенной семантики памяти (как каждая версия C / C ++ до C ++ 11 и многих других императивных языков), вам придется изобретать семантику памяти по мере продвижения, и обычно Вы захотите изобрести семантику памяти, которая лучше всего соответствует вашей семантике машины.
источник