В настоящее время я работаю над проектом компилятора, используя llvm. Я следовал различным учебникам до того момента, когда у меня есть парсер для создания синтаксического дерева, а затем дерево преобразуется в модуль llvm с использованием предоставленного IRBuilder.
Моя цель — создать исполняемый файл, и я не понимаю, что делать дальше. Все учебники, которые я нашел, просто создают модуль llvm и распечатывают сборку с помощью Module.dump (). Кроме того, единственная документация, которую я могу найти, предназначена для разработчиков llvm, а не для конечных пользователей проекта.
Если я хочу сгенерировать машинный код, каковы следующие шаги? Похоже, проект llvm-mc может делать то, что я хочу, но я не могу найти какую-либо документацию по нему.
Возможно, я ожидаю, что llvm сделает что-то, чего нет. Я ожидаю, что я смогу построить модуль, тогда будет API, который я могу вызвать с модулем, и будет получена целевая тройка и объектный файл. Я нашел документацию и примеры по созданию JIT, и я не заинтересован в этом. Я ищу, как создавать скомпилированные двоичные файлы.
Я работаю над OS X, если это как-то повлияет.
использование llc -filetype=obj
испустить связываемый объектный файл с вашего IR. Вы можете посмотреть на код llc
чтобы увидеть вызовы API LLVM, он создает такой код. По крайней мере, для Mac OS X и Linux объекты, излучаемые таким образом, должны быть довольно хорошими (то есть, это пока не вариант «альфа-качества»).
Однако LLVM не содержит компоновщика (пока!). Таким образом, чтобы фактически связать этот объектный файл с некоторой исполняемой или общей библиотекой, вам нужно использовать системный компоновщик. Обратите внимание, что даже если у вас есть исполняемый файл, состоящий из одного объектного файла, последний все равно должен быть связан. Разработчики в сообществе LLVM работают над реальным компоновщиком для LLVM, который называется lld
, Вы можете посетить его страница или искать в архивах списка рассылки, чтобы следить за его ходом.
Как вы можете читать дальше руководство ООО, он действительно предназначен просто для генерации сборки, а затем «Выходные данные на языке ассемблера могут затем передаваться через собственный ассемблер и компоновщик для генерации собственного исполняемого файла» — например, Гну ассемблер (as
) и компоновщик (ld
).
Таким образом, основной ответ здесь использовать нативные инструменты для сборки и компоновки.
Тем не менее, существует экспериментальная поддержка для генерации нативного объекта непосредственно из файла IR, через llc
:
-filetype - Choose a file type (not all types are supported by all targets):
=asm - Emit an assembly ('.s') file
=obj - Emit a native object ('.o') file [experimental]
Или вы можете использовать llvm-mc
собрать его из .s
файл:
-filetype - Choose an output file type:
=asm - Emit an assembly ('.s') file
=null - Don't emit anything (for timing purposes)
=obj - Emit a native object ('.o') file
Я не знаю о линкерах, хотя.
Кроме того, я рекомендую проверить tools/bugpoint/ToolRunner.h
файл, который выставляет объединение оболочки llc
и родной C-инструментарий платформы для генерации машинного кода. Из заголовка комментария:
В этом файле представлена абстракция вокруг компилятора платформы C, используемого для компиляции кода C и ассемблера.
Проверьте эти функции в llvm-c/TargetMachine.h
:
/** Emits an asm or object file for the given module to the filename. This
wraps several c++ only classes (among them a file stream). Returns any
error in ErrorMessage. Use LLVMDisposeMessage to dispose the message. */
LLVMBool LLVMTargetMachineEmitToFile(LLVMTargetMachineRef T, LLVMModuleRef M,
char *Filename, LLVMCodeGenFileType codegen, char **ErrorMessage);
/** Compile the LLVM IR stored in \p M and store the result in \p OutMemBuf. */
LLVMBool LLVMTargetMachineEmitToMemoryBuffer(LLVMTargetMachineRef T, LLVMModuleRef M,
LLVMCodeGenFileType codegen, char** ErrorMessage, LLVMMemoryBufferRef *OutMemBuf);
Чтобы запустить пример BrainF
запрограммируйте, скомпилируйте и запустите:
echo ,. > test.bf
./BrainF test.bf -o test.bc
llc -filetype=obj test.bc
gcc test.o -o a.out
./a.out
затем введите одну букву и нажмите Enter. Это должно повторить это письмо обратно к вам. (Это то что ,.
делает.)
Выше был протестирован с LLVM версии 3.5.0.