#include "clang/Interpreter/Interpreter.h"
#include "IncrementalExecutor.h"
#include "IncrementalParser.h"
#include "clang/AST/ASTContext.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/CodeGen/ModuleBuilder.h"
#include "clang/CodeGen/ObjectFilePCHContainerOperations.h"
#include "clang/Driver/Compilation.h"
#include "clang/Driver/Driver.h"
#include "clang/Driver/Job.h"
#include "clang/Driver/Options.h"
#include "clang/Driver/Tool.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/TextDiagnosticBuffer.h"
#include "clang/Lex/PreprocessorOptions.h"
#include "llvm/IR/Module.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/Host.h"
using namespace clang;
namespace {
static llvm::Expected<const llvm::opt::ArgStringList *>
GetCC1Arguments(DiagnosticsEngine *Diagnostics,
driver::Compilation *Compilation) {
const driver::JobList &Jobs = Compilation->getJobs();
if (!Jobs.size() || !isa<driver::Command>(*Jobs.begin()))
return llvm::createStringError(llvm::errc::not_supported,
"Driver initialization failed. "
"Unable to create a driver job");
const driver::Command *Cmd = cast<driver::Command>(&(*Jobs.begin()));
if (llvm::StringRef(Cmd->getCreator().getName()) != "clang")
return llvm::createStringError(llvm::errc::not_supported,
"Driver initialization failed");
return &Cmd->getArguments();
}
static llvm::Expected<std::unique_ptr<CompilerInstance>>
CreateCI(const llvm::opt::ArgStringList &Argv) {
std::unique_ptr<CompilerInstance> Clang(new CompilerInstance());
IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
auto PCHOps = Clang->getPCHContainerOperations();
PCHOps->registerWriter(std::make_unique<ObjectFilePCHContainerWriter>());
PCHOps->registerReader(std::make_unique<ObjectFilePCHContainerReader>());
IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
TextDiagnosticBuffer *DiagsBuffer = new TextDiagnosticBuffer;
DiagnosticsEngine Diags(DiagID, &*DiagOpts, DiagsBuffer);
bool Success = CompilerInvocation::CreateFromArgs(
Clang->getInvocation(), llvm::makeArrayRef(Argv.begin(), Argv.size()),
Diags);
if (Clang->getHeaderSearchOpts().UseBuiltinIncludes &&
Clang->getHeaderSearchOpts().ResourceDir.empty())
Clang->getHeaderSearchOpts().ResourceDir =
CompilerInvocation::GetResourcesPath(Argv[0], nullptr);
Clang->createDiagnostics();
if (!Clang->hasDiagnostics())
return llvm::createStringError(llvm::errc::not_supported,
"Initialization failed. "
"Unable to create diagnostics engine");
DiagsBuffer->FlushDiagnostics(Clang->getDiagnostics());
if (!Success)
return llvm::createStringError(llvm::errc::not_supported,
"Initialization failed. "
"Unable to flush diagnostics");
llvm::MemoryBuffer *MB = llvm::MemoryBuffer::getMemBuffer("").release();
Clang->getPreprocessorOpts().addRemappedFile("<<< inputs >>>", MB);
Clang->setTarget(TargetInfo::CreateTargetInfo(
Clang->getDiagnostics(), Clang->getInvocation().TargetOpts));
if (!Clang->hasTarget())
return llvm::createStringError(llvm::errc::not_supported,
"Initialization failed. "
"Target is missing");
Clang->getTarget().adjust(Clang->getDiagnostics(), Clang->getLangOpts());
Clang->getCodeGenOpts().ClearASTBeforeBackend = false;
Clang->getFrontendOpts().DisableFree = false;
Clang->getCodeGenOpts().DisableFree = false;
return std::move(Clang);
}
}
llvm::Expected<std::unique_ptr<CompilerInstance>>
IncrementalCompilerBuilder::create(std::vector<const char *> &ClangArgv) {
std::string MainExecutableName =
llvm::sys::fs::getMainExecutable(nullptr, nullptr);
ClangArgv.insert(ClangArgv.begin(), MainExecutableName.c_str());
ClangArgv.insert(ClangArgv.begin() + 1, "-c");
if (!llvm::is_contained(ClangArgv, " -x")) {
ClangArgv.push_back("-x");
ClangArgv.push_back("c++");
}
ClangArgv.push_back("<<< inputs >>>");
IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts =
CreateAndPopulateDiagOpts(ClangArgv);
TextDiagnosticBuffer *DiagsBuffer = new TextDiagnosticBuffer;
DiagnosticsEngine Diags(DiagID, &*DiagOpts, DiagsBuffer);
driver::Driver Driver(ClangArgv[0],
llvm::sys::getProcessTriple(), Diags);
Driver.setCheckInputsExist(false); llvm::ArrayRef<const char *> RF = llvm::makeArrayRef(ClangArgv);
std::unique_ptr<driver::Compilation> Compilation(Driver.BuildCompilation(RF));
if (Compilation->getArgs().hasArg(driver::options::OPT_v))
Compilation->getJobs().Print(llvm::errs(), "\n", false);
auto ErrOrCC1Args = GetCC1Arguments(&Diags, Compilation.get());
if (auto Err = ErrOrCC1Args.takeError())
return std::move(Err);
return CreateCI(**ErrOrCC1Args);
}
Interpreter::Interpreter(std::unique_ptr<CompilerInstance> CI,
llvm::Error &Err) {
llvm::ErrorAsOutParameter EAO(&Err);
auto LLVMCtx = std::make_unique<llvm::LLVMContext>();
TSCtx = std::make_unique<llvm::orc::ThreadSafeContext>(std::move(LLVMCtx));
IncrParser = std::make_unique<IncrementalParser>(std::move(CI),
*TSCtx->getContext(), Err);
}
Interpreter::~Interpreter() {}
llvm::Expected<std::unique_ptr<Interpreter>>
Interpreter::create(std::unique_ptr<CompilerInstance> CI) {
llvm::Error Err = llvm::Error::success();
auto Interp =
std::unique_ptr<Interpreter>(new Interpreter(std::move(CI), Err));
if (Err)
return std::move(Err);
return std::move(Interp);
}
const CompilerInstance *Interpreter::getCompilerInstance() const {
return IncrParser->getCI();
}
const llvm::orc::LLJIT *Interpreter::getExecutionEngine() const {
if (IncrExecutor)
return IncrExecutor->getExecutionEngine();
return nullptr;
}
llvm::Expected<PartialTranslationUnit &>
Interpreter::Parse(llvm::StringRef Code) {
return IncrParser->Parse(Code);
}
llvm::Error Interpreter::Execute(PartialTranslationUnit &T) {
assert(T.TheModule);
if (!IncrExecutor) {
const clang::TargetInfo &TI =
getCompilerInstance()->getASTContext().getTargetInfo();
llvm::Error Err = llvm::Error::success();
IncrExecutor = std::make_unique<IncrementalExecutor>(*TSCtx, Err, TI);
if (Err)
return Err;
}
if (auto Err = IncrExecutor->addModule(T))
return Err;
if (auto Err = IncrExecutor->runCtors())
return Err;
return llvm::Error::success();
}
llvm::Expected<llvm::JITTargetAddress>
Interpreter::getSymbolAddress(GlobalDecl GD) const {
if (!IncrExecutor)
return llvm::make_error<llvm::StringError>("Operation failed. "
"No execution engine",
std::error_code());
llvm::StringRef MangledName = IncrParser->GetMangledName(GD);
return getSymbolAddress(MangledName);
}
llvm::Expected<llvm::JITTargetAddress>
Interpreter::getSymbolAddress(llvm::StringRef IRName) const {
if (!IncrExecutor)
return llvm::make_error<llvm::StringError>("Operation failed. "
"No execution engine",
std::error_code());
return IncrExecutor->getSymbolAddress(IRName, IncrementalExecutor::IRName);
}
llvm::Expected<llvm::JITTargetAddress>
Interpreter::getSymbolAddressFromLinkerName(llvm::StringRef Name) const {
if (!IncrExecutor)
return llvm::make_error<llvm::StringError>("Operation failed. "
"No execution engine",
std::error_code());
return IncrExecutor->getSymbolAddress(Name, IncrementalExecutor::LinkerName);
}
llvm::Error Interpreter::Undo(unsigned N) {
std::list<PartialTranslationUnit> &PTUs = IncrParser->getPTUs();
if (N > PTUs.size())
return llvm::make_error<llvm::StringError>("Operation failed. "
"Too many undos",
std::error_code());
for (unsigned I = 0; I < N; I++) {
if (IncrExecutor) {
if (llvm::Error Err = IncrExecutor->removeModule(PTUs.back()))
return Err;
}
IncrParser->CleanUpPTU(PTUs.back());
PTUs.pop_back();
}
return llvm::Error::success();
}