Help on Generating LLVM Module from C++ file using libClang

Hi everyone,
I want to generate LLVM Module from a C++ file using the LibClang C++ API. I am using clang 5.0.0 compiled from source. Currently, I am using EmitLLVMOnlyAction (as in clang-interpreter) to generate the LLVM Module.

Unfortunately, I got an error “clang: Not enough positional command line arguments specified! Must specify at least 1 positional argument: See: clang -help” when I run ExecuteAction() on a CompilerInvocation object. I though this might be a missing argument problem so I printed out the driver::Command object which I used to create the CompilerInvocation object (I am using CompilerInvocation::CreateFromArgs() to create the object):

“/home/cwz/llvm-root/build-5.0.0/bin/clang++” -cc1 -triple nvptx64-nvidia-cuda -emit-llvm-bc -emit-llvm-uselists -disable-free -main-file-name dummy.cpp -mrelocation-model static -mthread-model posix -mdisable-fp-elim -fmath-errno -no-integrated-as -dwarf-column-info -debugger-tuning=gdb -coverage-notes-file /home/cwz/piko-public/pikoc/samples/rasterPipelineFixPt/dummy.gcno -resource-dir /home/cwz/llvm-root/build-5.0.0/lib/clang/5.0.0 -I /usr/local/cuda/include -I …/…/api/include -I …/…/include -I basicTypes -I EasyBMP -I sceneParser -I util -I assimp/include -I bezmesh -D PIKOC -D PIKOC_DEVICE -I /home/cwz/piko-public/pikoc/samples/rasterPipelineFixPt -I /home/cwz/llvm-root/build-5.0.0/lib/clang/5.0.0/include -I /usr/lib/gcc/x86_64-linux-gnu/5.4.0/…/…/…/…/include/c++/5.4.0 -I /usr/lib/gcc/x86_64-linux-gnu/5.4.0/…/…/…/…/include/x86_64-linux-gnu/c++/5.4.0 -I /usr/lib/gcc/x86_64-linux-gnu/5.4.0/…/…/…/…/include/c++/5.4.0/backward -I /usr/local/include -I /include -I /usr/include -I /usr/include/x86_64-linux-gnu -fdeprecated-macro -fno-dwarf-directory-asm -fdebug-compilation-dir /home/cwz/piko-public/pikoc/samples/rasterPipelineFixPt -ferror-limit 19 -fmessage-length 204 -fobjc-runtime=gcc -fcxx-exceptions -fexceptions -fdiagnostics-show-option -o dummy.bc -x c++ dummy.cpp

The dummy.cpp is the C++ file I want to compile to LLVM Module. The argument seems legit to me and I can compile dummy.cpp using clang from command line so I think the C++ file is good.

Can anyone help me figure out why I am getting the not enogh positional argument error or point me to a better way to do this? Thanks in advance for your help.

Best wishes,
Wenzhi Cui

The complete code snippet is here:

`

bool PikoBackend::createLLVMModule() {
std::string Path = “/home/cwz/llvm-root/build-5.0.0/bin/clang++”;
llvm::errs() << “Path=” << Path << “\n”;

llvm::IntrusiveRefCntPtr DiagOpts = new DiagnosticOptions();
TextDiagnosticPrinter *DiagClient =
new TextDiagnosticPrinter(llvm::errs(), &*DiagOpts);

IntrusiveRefCntPtr DiagID(new DiagnosticIDs());
DiagnosticsEngine Diags(DiagID, &*DiagOpts, DiagClient);

std::string TripleStr = this->getTargetTriple() + “-” + “cuda”;
llvm::Triple T(TripleStr);

Driver TheDriver(Path, T.str(), Diags);
TheDriver.setTitle(“PikoPipe”);
TheDriver.setCheckInputsExist(false);

llvm::SmallVector<const char *, 32> Args(pikocOptions.Argv,
pikocOptions.Argv + pikocOptions.Argc);
Args.push_back("-D__PIKOC__");
Args.push_back("-D__PIKOC_DEVICE__");
Args.push_back("-c");
Args.push_back("-emit-llvm");
Args.push_back("-xc++");

std::string include("-I");
auto include_cwd = include + pikocOptions.workingDir;
Args.push_back(include_cwd.c_str());
auto include_piko = include + pikocOptions.pikoIncludeDir;
Args.push_back(include_piko.c_str());
auto include_res = include + pikocOptions.clangResourceDir;
Args.push_back(include_res.c_str());
for(int i = 0; i < pikocOptions.includeDirs.size(); ++i) {
Args.push_back("-I");
Args.push_back(pikocOptions.includeDirs[i].c_str());
}

std::unique_ptr C(TheDriver.BuildCompilation(Args));
if (!C) {
llvm::errs() << “Compilation has problem building it\n”;
return false;
}

const driver::JobList &Jobs = C->getJobs();
if (Jobs.size() != 1 || !isadriver::Command(*Jobs.begin())) {
SmallString<256> Msg;
llvm::raw_svector_ostream OS(Msg);
Jobs.Print(OS, "; ", true);
Diags.Report(diag::err_fe_expected_compiler_job) << OS.str();
return false;
}

const driver::Command &Cmd = castdriver::Command(*Jobs.begin());
if (llvm::StringRef(Cmd.getCreator().getName()) != “clang”) {
Diags.Report(diag::err_fe_expected_clang_command);
return false;
}

// Initialize a compiler invocation object from the clang (-cc1) arguments.
const driver::ArgStringList &CCArgs = Cmd.getArguments();
Cmd.Print(llvm::errs(), “\n”, false);
std::unique_ptr CI(new CompilerInvocation);
CompilerInvocation::CreateFromArgs(*CI,
const_cast<const char **>(CCArgs.data()),
const_cast<const char **>(CCArgs.data()) +
CCArgs.size() - 1,
Diags);

// Show the invocation, with -v.
if (CI->getHeaderSearchOpts().Verbose) {
llvm::errs() << “clang invocation:\n”;
Jobs.Print(llvm::errs(), “\n”, true);
llvm::errs() << “\n”;
}

// FIXME: This is copied from cc1_main.cpp; simplify and eliminate.

// Create a compiler instance to handle the actual work.
CompilerInstance Clang;
Clang.setInvocation(std::move(CI));
Clang.getHeaderSearchOpts().ResourceDir = pikocOptions.clangResourceDir;

// Create the compilers actual diagnostics engine.
Clang.createDiagnostics();
if (!Clang.hasDiagnostics())
return false;

llvm::LLVMContext& ctx = GlobalContext;

// Create and execute the frontend to generate an LLVM bitcode module.
llvm::errs() << “Codegen…\n”;
std::unique_ptr Act(new EmitLLVMOnlyAction(&ctx));
if (!Clang.ExecuteAction(*Act)) {
llvm::errs() << “Parsing erros…\n”;
return false;
}

if(Clang.getDiagnostics().hasErrorOccurred()) {
llvm::errs() << “Parsing erros in diagnostics…\n”;
return false;
}

this->module = Act->takeModule().get();

return true;
}
`