6#ifndef CPPINTEROP_INTERPRETER_H
7#define CPPINTEROP_INTERPRETER_H
13#include "clang/Interpreter/Interpreter.h"
14#include "clang/Interpreter/PartialTranslationUnit.h"
16#include "clang/AST/Decl.h"
17#include "clang/AST/DeclarationName.h"
18#include "clang/AST/GlobalDecl.h"
19#include "clang/Basic/LangOptions.h"
20#include "clang/Basic/TargetOptions.h"
21#include "clang/Frontend/CompilerInstance.h"
22#include "clang/Frontend/FrontendOptions.h"
23#include "clang/Lex/Preprocessor.h"
24#include "clang/Sema/Lookup.h"
25#include "clang/Sema/Redeclaration.h"
26#include "clang/Sema/Sema.h"
27#include "clang/Serialization/ModuleFileExtension.h"
29#include "llvm/ADT/DenseMap.h"
30#include "llvm/ADT/SmallSet.h"
31#include "llvm/ADT/SmallVector.h"
32#include "llvm/ADT/StringRef.h"
33#include "llvm/ExecutionEngine/Orc/LLJIT.h"
34#include "llvm/Support/Compiler.h"
35#include "llvm/Support/Error.h"
36#include "llvm/Support/TargetSelect.h"
37#include "llvm/Support/raw_ostream.h"
38#include "llvm/TargetParser/Triple.h"
53class CompilerInstance;
57template <
typename D>
static D* LookupResult2Decl(clang::LookupResult& R) {
63 if (R.isSingleResult())
64 return llvm::dyn_cast<D>(R.getFoundDecl());
73inline clang::NamespaceDecl*
Namespace(clang::Sema* S,
const char* Name,
74 const clang::DeclContext* Within) {
75 clang::DeclarationName DName = &(S->Context.Idents.get(Name));
76 clang::LookupResult R(*S, DName, clang::SourceLocation(),
77 clang::Sema::LookupNestedNameSpecifierName);
78 R.suppressDiagnostics();
80 S->LookupName(R, S->TUScope);
82 if (
const clang::TagDecl* TD = llvm::dyn_cast<clang::TagDecl>(Within)) {
83 if (!TD->getDefinition()) {
88 S->LookupQualifiedName(R,
const_cast<clang::DeclContext*
>(Within));
96 return llvm::dyn_cast<clang::NamespaceDecl>(R.getFoundDecl());
99inline void Named(clang::Sema* S, clang::LookupResult& R,
100 const clang::DeclContext* Within =
nullptr) {
101 R.suppressDiagnostics();
103 S->LookupName(R, S->TUScope);
105 const clang::DeclContext* primaryWithin =
nullptr;
106 if (
const clang::TagDecl* TD = llvm::dyn_cast<clang::TagDecl>(Within)) {
108 llvm::dyn_cast_or_null<clang::DeclContext>(TD->getDefinition());
110 primaryWithin = Within->getPrimaryContext();
112 if (!primaryWithin) {
116 S->LookupQualifiedName(R,
const_cast<clang::DeclContext*
>(primaryWithin));
120inline clang::NamedDecl*
Named(clang::Sema* S,
121 const clang::DeclarationName& Name,
122 const clang::DeclContext* Within =
nullptr) {
123 clang::LookupResult R(*S, Name, clang::SourceLocation(),
124 clang::Sema::LookupOrdinaryName,
125 RedeclarationKind::ForVisibleRedeclaration);
127 return LookupResult2Decl<clang::NamedDecl>(R);
130inline clang::NamedDecl*
Named(clang::Sema* S, llvm::StringRef Name,
131 const clang::DeclContext* Within =
nullptr) {
132 clang::DeclarationName DName = &S->Context.Idents.get(Name);
133 return Named(S, DName, Within);
136inline clang::NamedDecl*
Named(clang::Sema* S,
const char* Name,
137 const clang::DeclContext* Within =
nullptr) {
138 return Named(S, llvm::StringRef(Name), Within);
173 static std::tuple<int, int, int>
174 initAndGetFileDescriptors(std::vector<const char*>& vargs,
181 if (!io_ctx.stdin_file || !io_ctx.stdout_file || !io_ctx.stderr_file) {
182 bool init = io_ctx.initializeTempFiles();
184 llvm::errs() <<
"Can't start out-of-process JIT execution.\n";
190 stdin_fd = fileno(io_ctx.stdin_file.get());
191 stdout_fd = fileno(io_ctx.stdout_file.get());
192 stderr_fd = fileno(io_ctx.stderr_file.get());
194 return std::make_tuple(stdin_fd, stdout_fd, stderr_fd);
197 std::unique_ptr<clang::Interpreter> inner;
198 std::unique_ptr<IOContext> io_context;
199 mutable std::unique_ptr<DynamicLibraryManager> sDLM;
200 mutable std::once_flag sDLMInit;
205 std::unique_ptr<IOContext> ctx =
nullptr,
bool oop =
false)
206 : inner(std::move(CI)), io_context(std::move(ctx)), outOfProcess(oop) {}
209 static std::unique_ptr<Interpreter>
210 create(
int argc,
const char*
const* argv,
const char* llvmdir =
nullptr,
211 const std::vector<std::shared_ptr<clang::ModuleFileExtension>>&
212 moduleExtensions = {},
213 void* extraLibHandle =
nullptr,
bool noRuntime =
true) {
214 std::vector<const char*> vargs(argv + 1, argv + argc);
219 auto io_ctx = std::make_unique<IOContext>();
220 bool outOfProcess =
false;
222#if LLVM_VERSION_MAJOR > 21 && !defined(_WIN32)
223 outOfProcess = std::any_of(vargs.begin(), vargs.end(), [](
const char* arg) {
224 return llvm::StringRef(arg).trim() ==
"--use-oop-jit";
229 std::tie(stdin_fd, stdout_fd, stderr_fd) =
230 initAndGetFileDescriptors(vargs, *io_ctx);
232 if (stdin_fd == -1 || stdout_fd == -1 || stderr_fd == -1) {
234 <<
"Redirection files creation failed for Out-Of-Process JIT\n";
244 llvm::errs() <<
"Interpreter creation failed\n";
248 return std::make_unique<Interpreter>(std::move(CI), std::move(io_ctx),
254 operator const clang::Interpreter&()
const {
return *inner; }
255 operator clang::Interpreter&() {
return *inner; }
269 return io_context->stdin_file.get();
270 case (STDOUT_FILENO):
271 return io_context->stdout_file.get();
272 case (STDERR_FILENO):
273 return io_context->stderr_file.get();
275 llvm::errs() <<
"No temp file for the FD\n";
287 return inner->getCompilerInstance();
294 llvm::Expected<clang::PartialTranslationUnit&>
Parse(llvm::StringRef Code) {
295 return inner->Parse(Code);
298 llvm::Error
Execute(clang::PartialTranslationUnit& T) {
299 return inner->Execute(T);
303 return inner->ParseAndExecute(Code, V);
309 static bool make_engine_once =
true;
310 if (make_engine_once) {
311 if (
auto Err = inner->ParseAndExecute(
""))
312 llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(),
"Error:");
313 make_engine_once =
false;
320 llvm::Expected<llvm::orc::ExecutorAddr>
324 if (llvm::Error Err = AddrOrErr.takeError())
325 return std::move(Err);
326 return llvm::orc::ExecutorAddr(*AddrOrErr);
330 llvm::Expected<llvm::orc::ExecutorAddr>
334 if (llvm::Error Err = AddrOrErr.takeError())
335 return std::move(Err);
336 return llvm::orc::ExecutorAddr(*AddrOrErr);
341 llvm::Expected<llvm::orc::ExecutorAddr>
344 if (llvm::Error Err = AddrOrErr.takeError())
345 return std::move(Err);
346 return llvm::orc::ExecutorAddr(*AddrOrErr);
351 clang::frontend::ParseSyntaxOnly;
358 return addressOrErr->toPtr<
void*>();
360 llvm::consumeError(addressOrErr.takeError());
371 return addressOrErr->toPtr<
void*>();
373 llvm::consumeError(addressOrErr.takeError());
378 clang::PartialTranslationUnit** PTU =
nullptr) {
379 return process(input,
nullptr, PTU);
386 clang::PartialTranslationUnit** PTU =
nullptr,
387 bool disableValuePrinting =
false) {
388 auto PTUOrErr =
Parse(input);
390 llvm::logAllUnhandledErrors(PTUOrErr.takeError(), llvm::errs(),
391 "Failed to parse via ::process:");
398 if (
auto Err =
Execute(*PTUOrErr)) {
399 llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(),
400 "Failed to execute via ::process:");
408 llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(),
409 "Failed to execute via ::evaluate:");
416 bool ifUnique,
bool withAccessControl) {
430 clang::LangOptions& LO =
432 bool SavedAccessControl = LO.AccessControl;
433 LO.AccessControl = withAccessControl;
436 LO.AccessControl = SavedAccessControl;
437 llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(),
438 "Failed to compileFunction: ");
442 LO.AccessControl = SavedAccessControl;
455 std::call_once(sDLMInit, [
this] {
456 sDLM = std::make_unique<DynamicLibraryManager>();
457 sDLM->initializeDyld([](llvm::StringRef) {
return false; });
474 clang::HeaderSearchOptions& HOpts =
475 const_cast<clang::HeaderSearchOptions&
>(CI->getHeaderSearchOpts());
478 size_t Idx = HOpts.UserEntries.size();
481 clang::Preprocessor& PP = CI->getPreprocessor();
482 clang::SourceManager& SM = PP.getSourceManager();
483 clang::FileManager& FM = SM.getFileManager();
484 clang::HeaderSearch& HSearch = PP.getHeaderSearchInfo();
485 const bool isFramework =
false;
488 for (
const size_t N = HOpts.UserEntries.size(); Idx < N; ++Idx) {
489 const clang::HeaderSearchOptions::Entry& E = HOpts.UserEntries[Idx];
490 if (
auto DE = FM.getOptionalDirectoryRef(E.Path))
491 HSearch.AddSearchPath(
492 clang::DirectoryLookup(*DE, clang::SrcMgr::C_User, isFramework),
493 E.Group == clang::frontend::Angled);
516 bool withSystem,
bool withFlags)
const {
518 incpaths, withSystem, withFlags);
523 if (triple.isWasm()) {
525 if (
auto Err = inner->LoadDynamicLibrary(filename.c_str())) {
526 llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(),
534 std::string canonicalLib;
538 const std::string& library = lookup ? canonicalLib : filename;
539 if (!library.empty()) {
546 assert(0 &&
"Cannot find library with existing canonical name!");
556 std::string
toString(
const char* type,
void* obj) {
557 assert(0 &&
"toString is not implemented!");
563 if (llvm::Error Err =
Undo(N)) {
564 llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(),
565 "Failed to undo via ::undo");
A helper class managing dynamic shared objects.
LoadLibResult loadLibrary(llvm::StringRef, bool permanent, bool resolved=false)
Loads a shared library.
@ kLoadLibNotFound
library was not found
@ kLoadLibAlreadyLoaded
library was already loaded
@ kLoadLibSuccess
library loaded successfully
std::string lookupLibrary(llvm::StringRef libStem, llvm::SmallVector< llvm::StringRef, 2 > RPath={}, llvm::SmallVector< llvm::StringRef, 2 > RunPath={}, llvm::StringRef libLoader="", bool variateLibStem=true) const
Looks up a library taking into account the current include paths and the system include paths.
FILE * getRedirectionFileForOutOfProcess(int FD)
llvm::Expected< clang::PartialTranslationUnit & > Parse(llvm::StringRef Code)
DynamicLibraryManager * getDynamicLibraryManager()
Interpreter(std::unique_ptr< clang::Interpreter > CI, std::unique_ptr< IOContext > ctx=nullptr, bool oop=false)
CompilationResult declare(const std::string &input, clang::PartialTranslationUnit **PTU=nullptr)
CompilationResult loadLibrary(const std::string &filename, bool lookup)
CompilationResult undo(unsigned N=1)
llvm::Expected< llvm::orc::ExecutorAddr > getSymbolAddress(clang::GlobalDecl GD) const
const clang::CompilerInstance * getCompilerInstance() const
llvm::Error Execute(clang::PartialTranslationUnit &T)
llvm::Error ParseAndExecute(llvm::StringRef Code, clang::Value *V=nullptr)
bool isInSyntaxOnlyMode() const
llvm::orc::LLJIT * getExecutionEngine() const
static std::unique_ptr< Interpreter > create(int argc, const char *const *argv, const char *llvmdir=nullptr, const std::vector< std::shared_ptr< clang::ModuleFileExtension > > &moduleExtensions={}, void *extraLibHandle=nullptr, bool noRuntime=true)
const DynamicLibraryManager * getDynamicLibraryManager() const
void makeEngineOnce() const
bool isOutOfProcess() const
llvm::Expected< llvm::orc::ExecutorAddr > getSymbolAddress(llvm::StringRef IRName) const
void AddIncludePath(llvm::StringRef PathsStr)
Adds a single include path (-I).
const clang::CompilerInstance * getCI() const
void * getAddressOfGlobal(const clang::GlobalDecl &GD) const
CompilationResult process(const std::string &input, clang::Value *V=0, clang::PartialTranslationUnit **PTU=nullptr, bool disableValuePrinting=false)
Maybe transform the input line to implement cint command line semantics (declarations are global) and...
llvm::Expected< llvm::orc::ExecutorAddr > getSymbolAddressFromLinkerName(llvm::StringRef LinkerName) const
CompilationResult
Describes the return result of the different routines that do the incremental compilation.
CompilationResult evaluate(const std::string &input, clang::Value &V)
std::string toString(const char *type, void *obj)
clang::Sema & getSema() const
void AddIncludePaths(llvm::StringRef PathsStr, const char *Delim=":")
Adds multiple include paths separated by a delimiter.
void GetIncludePaths(llvm::SmallVectorImpl< std::string > &incpaths, bool withSystem, bool withFlags) const
Get the current include paths that are used.
void * getAddressOfGlobal(llvm::StringRef SymName) const
void * compileFunction(llvm::StringRef name, llvm::StringRef code, bool ifUnique, bool withAccessControl)
llvm::Error Undo(unsigned N=1)
void Named(clang::Sema *S, clang::LookupResult &R, const clang::DeclContext *Within=nullptr)
clang::NamespaceDecl * Namespace(clang::Sema *S, const char *Name, const clang::DeclContext *Within)
void CopyIncludePaths(const clang::HeaderSearchOptions &Opts, llvm::SmallVectorImpl< std::string > &incpaths, bool withSystem, bool withFlags)
Copies the current include paths into the HeaderSearchOptions.
void AddIncludePaths(llvm::StringRef PathStr, clang::HeaderSearchOptions &HOpts, const char *Delim)
Adds multiple include paths separated by a delimiter into the given HeaderSearchOptions.
llvm::Expected< llvm::JITTargetAddress > getSymbolAddressFromLinkerName(clang::Interpreter &I, llvm::StringRef LinkerName)
llvm::Error Undo(clang::Interpreter &I, unsigned N=1)
std::unique_ptr< clang::Interpreter > createClangInterpreter(std::vector< const char * > &args, int stdin_fd=-1, int stdout_fd=-1, int stderr_fd=-1)
llvm::orc::LLJIT * getExecutionEngine(clang::Interpreter &I)
llvm::Expected< llvm::JITTargetAddress > getSymbolAddress(clang::Interpreter &I, llvm::StringRef IRName)
std::unique_ptr< FILE, FileDeleter > stdin_file
std::unique_ptr< FILE, FileDeleter > stderr_file
bool initializeTempFiles()
std::unique_ptr< FILE, FileDeleter > stdout_file