Executable codegen output for LLVM IR

Hi all,

I am very, very new to LLVM. I’ve been working for about one to two weeks on a little toy language and compiler in C++ using the Kaleidoscope tutorial as a general guide (w/ LLVM 15.0.7). It’s been great so far, and I think I am like 99% there in terms of something extremely basic that Just Works.

I can generate LLVM IR just fine. My issue is the next step. Chapter eight of the Kaleidoscope tutorial goes over generating object files (8. Kaleidoscope: Compiling to Object Code — LLVM 17.0.0git documentation), and copy pasting the code from there is basically all I needed to do save for some minor tweaks (ir::mod here is my llvm Module):

llvm::InitializeAllTargetInfos();
llvm::InitializeAllTargets();
llvm::InitializeAllTargetMCs();
llvm::InitializeAllAsmParsers();
llvm::InitializeAllAsmPrinters();

auto TargetTriple = llvm::sys::getDefaultTargetTriple();
ir::mod->setTargetTriple(TargetTriple);

std::string Error;
auto Target = llvm::TargetRegistry::lookupTarget(TargetTriple, Error);

if (!Target)
{
    llvm::errs() << Error;
    return 1;
}

auto CPU = "generic";
auto Features = "";

llvm::TargetOptions opt;
auto RM = llvm::Optional<llvm::Reloc::Model>();
auto TheTargetMachine =
    Target->createTargetMachine(TargetTriple, CPU, Features, opt, RM, llvm::None, llvm::CodeGenOpt::Aggressive);

ir::mod->setDataLayout(TheTargetMachine->createDataLayout());

auto Filename = "out.o";
std::error_code EC;
llvm::raw_fd_ostream dest(Filename, EC, llvm::sys::fs::OF_None);

if (EC)
{
    llvm::errs() << "Could not open file: " << EC.message();
    return 1;
}

llvm::legacy::PassManager pass;
auto FileType = llvm::CGFT_ObjectFile;

if (TheTargetMachine->addPassesToEmitFile(pass, dest, nullptr, FileType))
{
    llvm::errs() << "TheTargetMachine can't emit a file of this type";
    return 1;
}

pass.run(*ir::mod);
dest.flush();

llvm::outs() << "Wrote " << Filename << "\n";

This works fine, and running my compiler I can then compile the object file in a terminal with clang. However, I don’t want my user to deal with object files, I want my compiler to do all the linking behind the scenes.

Is there an API for doing effectively what clang++ out.o would accomplish? How do other compilers approach this issue?

Thanks in advance!

The simplest thing is just to make your compiler run system("clang++ out.o").

Other possibilities:

  • clang::driver::Driver is a public API, if you want to access the logic clang uses to invoke the linker more directly.
  • You can try to figure out how to invoke the linker yourself.

In any case, you end up invoking an external process.

I considered system("clang out.o") but I have two main issues with that approach:

  1. I don’t really have a way to control which clang a user has.
  2. The object file is still created and will exist in the directory. I could system("rm out.o") afterwards but this all just feels like a very ‘hacky’ solution.

Both of the other possibilities seem promising but I just don’t know where to get started learning how to implement them.
clang::driver::Driver seems like it’s maybe what I’m looking for but the documentation is daunting and I can’t find any guides or examples of it being used. Any help here would be immensely appreciated!