Компиляция C++ на лету: clang / libtooling не может установить Triple для LLVM IR

Скажем, я хочу на лету скомпилировать строку C++:

llvm::LLVMContext context;

std::unique_ptr<clang::CodeGenAction> action = std::make_unique<clang::EmitLLVMOnlyAction>(&context);
clang::tooling::runToolOnCode/*WithArgs*/(action.get(), "int foo(int x){ return ++x;}");

std::unique_ptr<llvm::Module> module = action->takeModule();

К сожалению, кажется, что когда LLVM пытается преобразовать IR, возникает исключение, в котором говорится, что Triple не установлен (https://clang.llvm.org/docs/CrossCompilation.html#target-triple).

Можно ли для этого использовать libtooling или libclang?

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
4
0
625
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

К сожалению, эти интерфейсы сложно использовать для создания надлежащего модуля LLVM. Единственный способ - создать файл и скомпилировать его, задав все пути включения:

Во-первых, нужно добавить множество включений:

#include <clang/AST/ASTContext.h>
#include <clang/AST/ASTConsumer.h>
#include <clang/Basic/DiagnosticOptions.h>
#include <clang/Basic/Diagnostic.h>
#include <clang/Basic/FileManager.h>
#include <clang/Basic/FileSystemOptions.h>
#include <clang/Basic/LangOptions.h>
#include <clang/Basic/MemoryBufferCache.h>
#include <clang/Basic/SourceManager.h>
#include <clang/Basic/TargetInfo.h>
#include <clang/CodeGen/CodeGenAction.h>
#include <clang/Frontend/CompilerInstance.h>
#include <clang/Frontend/CompilerInvocation.h>
#include <clang/Frontend/TextDiagnosticPrinter.h>
#include <clang/Lex/HeaderSearch.h>
#include <clang/Lex/HeaderSearchOptions.h>
#include <clang/Lex/Preprocessor.h>
#include <clang/Lex/PreprocessorOptions.h>
#include <clang/Parse/ParseAST.h>
#include <clang/Sema/Sema.h>

Затем нам нужно настроить все движки вокруг экземпляра компилятора:

clang::DiagnosticOptions diagnosticOptions;
std::unique_ptr<clang::TextDiagnosticPrinter> textDiagnosticPrinter =
  std::make_unique<clang::TextDiagnosticPrinter>(llvm::outs(),
                                                 &diagnosticOptions);
llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs> diagIDs;

std::unique_ptr<clang::DiagnosticsEngine> diagnosticsEngine =
  std::make_unique<clang::DiagnosticsEngine>(diagIDs, &diagnosticOptions, textDiagnosticPrinter.get());

clang::CompilerInstance compilerInstance;
auto& compilerInvocation = compilerInstance.getInvocation();

Здесь мы можем установить тройку, а также тип языка, который мы хотим:

std::stringstream ss;
ss << "-triple = " << llvm::sys::getDefaultTargetTriple();
ss << " -x c++"; // to activate C++
ss << " -fcxx-exceptions";
ss << " -std=c++17";

std::istream_iterator<std::string> begin(ss);
std::istream_iterator<std::string> end;
std::istream_iterator<std::string> i = begin;
std::vector<const char*> itemcstrs;
std::vector<std::string> itemstrs;
while (i != end) {
  itemstrs.push_back(*i);
  ++i;
}

for (unsigned idx = 0; idx < itemstrs.size(); idx++) {
  // note: if itemstrs is modified after this, itemcstrs will be full
  // of invalid pointers! Could make copies, but would have to clean up then...
  itemcstrs.push_back(itemstrs[idx].c_str());
}

clang::CompilerInvocation::CreateFromArgs(compilerInvocation, itemcstrs.data(), itemcstrs.data() + itemcstrs.size(),
 *diagnosticsEngine.release());

Затем мы можем проверить параметры, которые были установлены (просто изменить параметры здесь недостаточно) и добавить подробности:

auto* languageOptions = compilerInvocation.getLangOpts();
auto& preprocessorOptions = compilerInvocation.getPreprocessorOpts();
auto& targetOptions = compilerInvocation.getTargetOpts();
auto& frontEndOptions = compilerInvocation.getFrontendOpts();
#ifdef DEBUG
frontEndOptions.ShowStats = true;
#endif
auto& headerSearchOptions = compilerInvocation.getHeaderSearchOpts();

Давайте добавим все пути заголовка include:

constexpr std::string_view paths[] = {"/usr/include/c++/8",
  "/usr/include/x86_64-linux-gnu/c++/8",
  "/usr/include/c++/8/backward",
  "/usr/include/clang/6.0.0/include",
  "/usr/local/include",
  "/usr/include/x86_64-linux-gnu",
  "/usr/include"};

for(auto path: paths)
{
    headerSearchOptions.AddPath(std::string(path), clang::frontend::IncludeDirGroup::Angled, false, false);
}

#ifdef DEBUG
headerSearchOptions.Verbose = true;
#endif
auto& codeGenOptions = compilerInvocation.getCodeGenOpts();

Здесь должен быть способ установки файловой строки (без использования FrontendInputFile), но, к сожалению, в LLVM 7 есть проверка, гарантирующая, что это настоящий файл ...

frontEndOptions.Inputs.clear();
frontEndOptions.Inputs.push_back(clang::FrontendInputFile(filename, clang::InputKind::CXX));

targetOptions.Triple = llvm::sys::getDefaultTargetTriple();
compilerInstance.createDiagnostics(textDiagnosticPrinter.get(), false);

LLVM::Context context;

Теперь создайте действие генератора кода и заставьте экземпляр компилятора выполнить действие:

std::unique_ptr<clang::CodeGenAction> action = std::make_unique<clang::EmitLLVMOnlyAction>(&context);

if (!compilerInstance.ExecuteAction(*action))
{
    // Failed to compile, and should display on cout the result of the compilation
}

Другие вопросы по теме