reusing an ExecutionEngine instance

Greetings, LLVM wizards,

I am trying to use Clang to compile a function in memory once, then to execute it several times with different parameters. That is, I want to keep the resulting ExecutionEngine instance around and reuse it. (Or, if there is a better way to do this, I am open to suggestions.) (And if this is not the right mailing list for this kind of question, I am open to suggestions, too.)

In the code below, the ExecutionEngine works fine if I use it immediately. But if I return from the function and use it afterward, I get memory corruption and crashes, presumably because it contains references to something that has been deleted. And in fact, if I change the code so that the CodeGenAction is not deleted, it seems to work.

My question is: why should deleting the CodeGenAction corrupt the ExecutionEngine? Are there other things I should avoid deleting as well? (The following code was adapted from the clang-interpreter example in the v4.0.1 source code.)

Geoff

ExecutionEngine* compile(const std::string& filename)
{
const char *resource_dir = getenv(“CLANG_RESOURCE_DIR”);
if (resource_dir == NULL) {
std::cerr << “Clang resource directory has not been defined.\n”;
return NULL;
}

IntrusiveRefCntPtr diag_opts(new DiagnosticOptions);
TextDiagnosticPrinter *diag_client =
new TextDiagnosticPrinter(llvm::errs(), diag_opts.get());
IntrusiveRefCntPtr diag_ids(new DiagnosticIDs);
DiagnosticsEngine diags(diag_ids, diag_opts.get(), diag_client);
std::string executable = “dg2”;
Driver driver(executable, llvm::sys::getDefaultTargetTriple(), diags);

SmallVector<const char*, 16> args;
args.push_back(executable.c_str());
args.push_back(filename.c_str());
args.push_back("-fsyntax-only");
args.push_back("-fno-use-cxa-atexit");
args.push_back("-resource-dir");
args.push_back(resource_dir);
args.push_back("-std=c++11");
args.push_back("-O3");

std::unique_ptr compilation(driver.BuildCompilation(args));
const JobList& jobs = compilation->getJobs();
if (jobs.size() != 1 || !llvm::isa(*jobs.begin())) {
std::cerr << “Expected a single Command job from Compilation…\n”;
return NULL;
}

const Command& cmd = llvm::cast(*jobs.begin());
if (StringRef(cmd.getCreator().getName()) != “clang”) {
std::cerr << “Expected a single clang Command from Compilation…\n”;
return NULL;
}

const ArgStringList& ccargs = cmd.getArguments();
std::unique_ptr invocation(new CompilerInvocation);
CompilerInvocation::CreateFromArgs(
invocation,
const_cast<const char
*>(ccargs.data()),
const_cast<const char**>(ccargs.data()) + ccargs.size(),
diags);

CompilerInstance clang;
clang.setInvocation(std::move(invocation));
clang.createDiagnostics();

std::unique_ptr action(new EmitLLVMOnlyAction);
if (!clang.ExecuteAction(*action)) {
return NULL;
}
std::unique_ptr module(action->takeModule());

llvm::InitializeNativeTarget();
llvm::InitializeNativeTargetAsmPrinter();

std::string error;

ExecutionEngine *exec_engine =
EngineBuilder(std::move(module))
.setEngineKind(llvm::EngineKind::Either)
.setErrorStr(&error)
.create();

if (!exec_engine) {
std::cerr << "Could not create execution engine: " << error << std::endl;
return NULL;
}

exec_engine->finalizeObject();
return exec_engine;
}

Well, if anybody out there comes across a similar problem, I found the solution: create your own LLVMContext object and pass it to the EmitLLVMOnlyAction constructor. If you don’t, CodeGenAction creates a temporary context and deletes it when the CodeGenAction is deleted. And the LLVMContext destructor deletes the module that was created, even though you may have a std::unique_ptr to it…

Geoff