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"
44#if defined(_WIN32) && (defined(_M_IX86) || defined(__i386__))
45#include "llvm/ExecutionEngine/Orc/AbsoluteSymbols.h"
46#include "llvm/Support/DynamicLibrary.h"
58class CompilerInstance;
62template <
typename D>
static D* LookupResult2Decl(clang::LookupResult& R) {
68 if (R.isSingleResult())
69 return llvm::dyn_cast<D>(R.getFoundDecl());
78inline clang::NamespaceDecl*
Namespace(clang::Sema* S,
const char* Name,
79 const clang::DeclContext* Within) {
80 clang::DeclarationName DName = &(S->Context.Idents.get(Name));
81 clang::LookupResult R(*S, DName, clang::SourceLocation(),
82 clang::Sema::LookupNestedNameSpecifierName);
83 R.suppressDiagnostics();
85 S->LookupName(R, S->TUScope);
87 if (
const clang::TagDecl* TD = llvm::dyn_cast<clang::TagDecl>(Within)) {
88 if (!TD->getDefinition()) {
93 S->LookupQualifiedName(R,
const_cast<clang::DeclContext*
>(Within));
101 return llvm::dyn_cast<clang::NamespaceDecl>(R.getFoundDecl());
104inline void Named(clang::Sema* S, clang::LookupResult& R,
105 const clang::DeclContext* Within =
nullptr) {
106 R.suppressDiagnostics();
108 S->LookupName(R, S->TUScope);
110 const clang::DeclContext* primaryWithin =
nullptr;
111 if (
const clang::TagDecl* TD = llvm::dyn_cast<clang::TagDecl>(Within)) {
113 llvm::dyn_cast_or_null<clang::DeclContext>(TD->getDefinition());
115 primaryWithin = Within->getPrimaryContext();
117 if (!primaryWithin) {
121 S->LookupQualifiedName(R,
const_cast<clang::DeclContext*
>(primaryWithin));
125inline clang::NamedDecl*
Named(clang::Sema* S,
126 const clang::DeclarationName& Name,
127 const clang::DeclContext* Within =
nullptr) {
128 clang::LookupResult R(*S, Name, clang::SourceLocation(),
129 clang::Sema::LookupOrdinaryName,
130 RedeclarationKind::ForVisibleRedeclaration);
132 return LookupResult2Decl<clang::NamedDecl>(R);
135inline clang::NamedDecl*
Named(clang::Sema* S, llvm::StringRef Name,
136 const clang::DeclContext* Within =
nullptr) {
137 clang::DeclarationName DName = &S->Context.Idents.get(Name);
138 return Named(S, DName, Within);
141inline clang::NamedDecl*
Named(clang::Sema* S,
const char* Name,
142 const clang::DeclContext* Within =
nullptr) {
143 return Named(S, llvm::StringRef(Name), Within);
152#if defined(_WIN32) && (defined(_M_IX86) || defined(__i386__))
158class COFFi386SymbolGenerator :
public llvm::orc::DefinitionGenerator {
161 tryToGenerate(llvm::orc::LookupState& LS, llvm::orc::LookupKind K,
162 llvm::orc::JITDylib& JD,
163 llvm::orc::JITDylibLookupFlags JDLookupFlags,
164 const llvm::orc::SymbolLookupSet& LookupSet)
override {
165 llvm::orc::SymbolMap NewSymbols;
166 for (
const auto& KV : LookupSet) {
167 llvm::StringRef Name = *KV.first;
169 if (Name.empty() || Name.starts_with(
"?"))
171 if (
void* Addr = lookupDecorated(Name))
172 NewSymbols[KV.first] = {llvm::orc::ExecutorAddr::fromPtr(Addr),
173 llvm::JITSymbolFlags::Exported};
175 if (NewSymbols.empty())
176 return llvm::Error::success();
177 return JD.define(llvm::orc::absoluteSymbols(std::move(NewSymbols)));
181 void* lookupDecorated(llvm::StringRef Name) {
182 bool Dllimport = Name.consume_front(
"__imp_");
183 Name.consume_front(
"_");
184 std::string S = Name.str();
185 void* Addr = llvm::sys::DynamicLibrary::SearchForAddressOfSymbol(S.c_str());
188 size_t At = S.rfind(
'@');
189 if (At != std::string::npos && At + 1 < S.size() &&
190 S.find_first_not_of(
"0123456789", At + 1) == std::string::npos) {
192 Addr = llvm::sys::DynamicLibrary::SearchForAddressOfSymbol(S.c_str());
195 if (Addr && Dllimport) {
198 m_ImportSlots.push_back(Addr);
199 return &m_ImportSlots.back();
204 std::deque<void*> m_ImportSlots;
234 static std::tuple<int, int, int>
235 initAndGetFileDescriptors(std::vector<const char*>& vargs,
242 if (!io_ctx.stdin_file || !io_ctx.stdout_file || !io_ctx.stderr_file) {
243 bool init = io_ctx.initializeTempFiles();
245 llvm::errs() <<
"Can't start out-of-process JIT execution.\n";
251 stdin_fd = fileno(io_ctx.stdin_file.get());
252 stdout_fd = fileno(io_ctx.stdout_file.get());
253 stderr_fd = fileno(io_ctx.stderr_file.get());
255 return std::make_tuple(stdin_fd, stdout_fd, stderr_fd);
258 std::unique_ptr<clang::Interpreter> inner;
259 std::unique_ptr<IOContext> io_context;
260 mutable std::unique_ptr<DynamicLibraryManager> sDLM;
261 mutable std::once_flag sDLMInit;
266 std::unique_ptr<IOContext> ctx =
nullptr,
bool oop =
false)
267 : inner(std::move(CI)), io_context(std::move(ctx)), outOfProcess(oop) {}
270 static std::unique_ptr<Interpreter>
271 create(
int argc,
const char*
const* argv,
const char* llvmdir =
nullptr,
272 const std::vector<std::shared_ptr<clang::ModuleFileExtension>>&
273 moduleExtensions = {},
274 void* extraLibHandle =
nullptr,
bool noRuntime =
true) {
275 std::vector<const char*> vargs(argv + 1, argv + argc);
280 auto io_ctx = std::make_unique<IOContext>();
281 bool outOfProcess =
false;
283#if LLVM_VERSION_MAJOR > 21 && !defined(_WIN32)
284 outOfProcess = std::any_of(vargs.begin(), vargs.end(), [](
const char* arg) {
285 return llvm::StringRef(arg).trim() ==
"--use-oop-jit";
290 std::tie(stdin_fd, stdout_fd, stderr_fd) =
291 initAndGetFileDescriptors(vargs, *io_ctx);
293 if (stdin_fd == -1 || stdout_fd == -1 || stderr_fd == -1) {
295 <<
"Redirection files creation failed for Out-Of-Process JIT\n";
305 llvm::errs() <<
"Interpreter creation failed\n";
309#if defined(_WIN32) && (defined(_M_IX86) || defined(__i386__))
313 std::make_unique<COFFi386SymbolGenerator>());
316 return std::make_unique<Interpreter>(std::move(CI), std::move(io_ctx),
322 operator const clang::Interpreter&()
const {
return *inner; }
323 operator clang::Interpreter&() {
return *inner; }
337 return io_context->stdin_file.get();
338 case (STDOUT_FILENO):
339 return io_context->stdout_file.get();
340 case (STDERR_FILENO):
341 return io_context->stderr_file.get();
343 llvm::errs() <<
"No temp file for the FD\n";
355 return inner->getCompilerInstance();
362 llvm::Expected<clang::PartialTranslationUnit&>
Parse(llvm::StringRef Code) {
363 return inner->Parse(Code);
366 llvm::Error
Execute(clang::PartialTranslationUnit& T) {
367 return inner->Execute(T);
371 return inner->ParseAndExecute(Code, V);
377 static bool make_engine_once =
true;
378 if (make_engine_once) {
379 if (
auto Err = inner->ParseAndExecute(
""))
380 llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(),
"Error:");
381 make_engine_once =
false;
388 llvm::Expected<llvm::orc::ExecutorAddr>
392 if (llvm::Error Err = AddrOrErr.takeError())
393 return std::move(Err);
394 return llvm::orc::ExecutorAddr(*AddrOrErr);
398 llvm::Expected<llvm::orc::ExecutorAddr>
402 if (llvm::Error Err = AddrOrErr.takeError())
403 return std::move(Err);
404 return llvm::orc::ExecutorAddr(*AddrOrErr);
409 llvm::Expected<llvm::orc::ExecutorAddr>
412 if (llvm::Error Err = AddrOrErr.takeError())
413 return std::move(Err);
414 return llvm::orc::ExecutorAddr(*AddrOrErr);
419 clang::frontend::ParseSyntaxOnly;
426 return addressOrErr->toPtr<
void*>();
428 llvm::consumeError(addressOrErr.takeError());
439 return addressOrErr->toPtr<
void*>();
441 llvm::consumeError(addressOrErr.takeError());
446 clang::PartialTranslationUnit** PTU =
nullptr) {
447 return process(input,
nullptr, PTU);
454 clang::PartialTranslationUnit** PTU =
nullptr,
455 bool disableValuePrinting =
false) {
456 auto PTUOrErr =
Parse(input);
458 llvm::logAllUnhandledErrors(PTUOrErr.takeError(), llvm::errs(),
459 "Failed to parse via ::process:");
466 if (
auto Err =
Execute(*PTUOrErr)) {
467 llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(),
468 "Failed to execute via ::process:");
476 llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(),
477 "Failed to execute via ::evaluate:");
484 bool ifUnique,
bool withAccessControl) {
498 clang::LangOptions& LO =
500 bool SavedAccessControl = LO.AccessControl;
501 LO.AccessControl = withAccessControl;
504 LO.AccessControl = SavedAccessControl;
505 llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(),
506 "Failed to compileFunction: ");
510 LO.AccessControl = SavedAccessControl;
523 std::call_once(sDLMInit, [
this] {
524 sDLM = std::make_unique<DynamicLibraryManager>();
525 sDLM->initializeDyld([](llvm::StringRef) {
return false; });
542 clang::HeaderSearchOptions& HOpts =
543 const_cast<clang::HeaderSearchOptions&
>(CI->getHeaderSearchOpts());
546 size_t Idx = HOpts.UserEntries.size();
549 clang::Preprocessor& PP = CI->getPreprocessor();
550 clang::SourceManager& SM = PP.getSourceManager();
551 clang::FileManager& FM = SM.getFileManager();
552 clang::HeaderSearch& HSearch = PP.getHeaderSearchInfo();
553 const bool isFramework =
false;
556 for (
const size_t N = HOpts.UserEntries.size(); Idx < N; ++Idx) {
557 const clang::HeaderSearchOptions::Entry& E = HOpts.UserEntries[Idx];
558 if (
auto DE = FM.getOptionalDirectoryRef(E.Path))
559 HSearch.AddSearchPath(
560 clang::DirectoryLookup(*DE, clang::SrcMgr::C_User, isFramework),
561 E.Group == clang::frontend::Angled);
584 bool withSystem,
bool withFlags)
const {
586 incpaths, withSystem, withFlags);
591 if (triple.isWasm()) {
593 if (
auto Err = inner->LoadDynamicLibrary(filename.c_str())) {
594 llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(),
602 std::string canonicalLib;
606 const std::string& library = lookup ? canonicalLib : filename;
607 if (!library.empty()) {
614 assert(0 &&
"Cannot find library with existing canonical name!");
624 std::string
toString(
const char* type,
void* obj) {
625 assert(0 &&
"toString is not implemented!");
631 if (llvm::Error Err =
Undo(N)) {
632 llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(),
633 "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