aboutsummaryrefslogtreecommitdiff
path: root/contrib/llvm-project/clang/lib/Interpreter/Interpreter.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/llvm-project/clang/lib/Interpreter/Interpreter.cpp')
-rw-r--r--contrib/llvm-project/clang/lib/Interpreter/Interpreter.cpp688
1 files changed, 650 insertions, 38 deletions
diff --git a/contrib/llvm-project/clang/lib/Interpreter/Interpreter.cpp b/contrib/llvm-project/clang/lib/Interpreter/Interpreter.cpp
index 937504f34739..9f97a3c6b0be 100644
--- a/contrib/llvm-project/clang/lib/Interpreter/Interpreter.cpp
+++ b/contrib/llvm-project/clang/lib/Interpreter/Interpreter.cpp
@@ -11,13 +11,17 @@
//
//===----------------------------------------------------------------------===//
-#include "clang/Interpreter/Interpreter.h"
-
+#include "DeviceOffload.h"
#include "IncrementalExecutor.h"
#include "IncrementalParser.h"
+#include "InterpreterUtils.h"
#include "clang/AST/ASTContext.h"
+#include "clang/AST/Mangle.h"
+#include "clang/AST/TypeVisitor.h"
+#include "clang/Basic/DiagnosticSema.h"
#include "clang/Basic/TargetInfo.h"
+#include "clang/CodeGen/CodeGenAction.h"
#include "clang/CodeGen/ModuleBuilder.h"
#include "clang/CodeGen/ObjectFilePCHContainerOperations.h"
#include "clang/Driver/Compilation.h"
@@ -27,16 +31,21 @@
#include "clang/Driver/Tool.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/TextDiagnosticBuffer.h"
+#include "clang/Interpreter/Interpreter.h"
+#include "clang/Interpreter/Value.h"
#include "clang/Lex/PreprocessorOptions.h"
-
+#include "clang/Sema/Lookup.h"
+#include "llvm/ExecutionEngine/JITSymbol.h"
+#include "llvm/ExecutionEngine/Orc/LLJIT.h"
#include "llvm/IR/Module.h"
-#include "llvm/Support/Host.h"
-
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/TargetParser/Host.h"
using namespace clang;
// FIXME: Figure out how to unify with namespace init_convenience from
-// tools/clang-import-test/clang-import-test.cpp and
-// examples/clang-interpreter/main.cpp
+// tools/clang-import-test/clang-import-test.cpp
namespace {
/// Retrieves the clang CC1 specific flags out of the compilation's jobs.
/// \returns NULL on error.
@@ -47,14 +56,14 @@ GetCC1Arguments(DiagnosticsEngine *Diagnostics,
// failed. Extract that job from the Compilation.
const driver::JobList &Jobs = Compilation->getJobs();
if (!Jobs.size() || !isa<driver::Command>(*Jobs.begin()))
- return llvm::createStringError(std::errc::state_not_recoverable,
+ return llvm::createStringError(llvm::errc::not_supported,
"Driver initialization failed. "
"Unable to create a driver job");
// The one job we find should be to invoke clang again.
const driver::Command *Cmd = cast<driver::Command>(&(*Jobs.begin()));
if (llvm::StringRef(Cmd->getCreator().getName()) != "clang")
- return llvm::createStringError(std::errc::state_not_recoverable,
+ return llvm::createStringError(llvm::errc::not_supported,
"Driver initialization failed");
return &Cmd->getArguments();
@@ -77,8 +86,7 @@ CreateCI(const llvm::opt::ArgStringList &Argv) {
TextDiagnosticBuffer *DiagsBuffer = new TextDiagnosticBuffer;
DiagnosticsEngine Diags(DiagID, &*DiagOpts, DiagsBuffer);
bool Success = CompilerInvocation::CreateFromArgs(
- Clang->getInvocation(), llvm::makeArrayRef(Argv.begin(), Argv.size()),
- Diags);
+ Clang->getInvocation(), llvm::ArrayRef(Argv.begin(), Argv.size()), Diags);
// Infer the builtin include path if unspecified.
if (Clang->getHeaderSearchOpts().UseBuiltinIncludes &&
@@ -89,13 +97,13 @@ CreateCI(const llvm::opt::ArgStringList &Argv) {
// Create the actual diagnostics engine.
Clang->createDiagnostics();
if (!Clang->hasDiagnostics())
- return llvm::createStringError(std::errc::state_not_recoverable,
+ return llvm::createStringError(llvm::errc::not_supported,
"Initialization failed. "
"Unable to create diagnostics engine");
DiagsBuffer->FlushDiagnostics(Clang->getDiagnostics());
if (!Success)
- return llvm::createStringError(std::errc::state_not_recoverable,
+ return llvm::createStringError(llvm::errc::not_supported,
"Initialization failed. "
"Unable to flush diagnostics");
@@ -106,12 +114,18 @@ CreateCI(const llvm::opt::ArgStringList &Argv) {
Clang->setTarget(TargetInfo::CreateTargetInfo(
Clang->getDiagnostics(), Clang->getInvocation().TargetOpts));
if (!Clang->hasTarget())
- return llvm::createStringError(std::errc::state_not_recoverable,
+ return llvm::createStringError(llvm::errc::not_supported,
"Initialization failed. "
"Target is missing");
Clang->getTarget().adjust(Clang->getDiagnostics(), Clang->getLangOpts());
+ // Don't clear the AST before backend codegen since we do codegen multiple
+ // times, reusing the same AST.
+ Clang->getCodeGenOpts().ClearASTBeforeBackend = false;
+
+ Clang->getFrontendOpts().DisableFree = false;
+ Clang->getCodeGenOpts().DisableFree = false;
return std::move(Clang);
}
@@ -131,36 +145,27 @@ IncrementalCompilerBuilder::create(std::vector<const char *> &ClangArgv) {
// specified. By prepending we allow users to override the default
// action and use other actions in incremental mode.
// FIXME: Print proper driver diagnostics if the driver flags are wrong.
- ClangArgv.insert(ClangArgv.begin() + 1, "-c");
-
- if (!llvm::is_contained(ClangArgv, " -x")) {
- // We do C++ by default; append right after argv[0] if no "-x" given
- ClangArgv.push_back("-x");
- ClangArgv.push_back("c++");
- }
+ // We do C++ by default; append right after argv[0] if no "-x" given
+ ClangArgv.insert(ClangArgv.end(), "-Xclang");
+ ClangArgv.insert(ClangArgv.end(), "-fincremental-extensions");
+ ClangArgv.insert(ClangArgv.end(), "-c");
// Put a dummy C++ file on to ensure there's at least one compile job for the
// driver to construct.
ClangArgv.push_back("<<< inputs >>>");
- CompilerInvocation Invocation;
// Buffer diagnostics from argument parsing so that we can output them using a
// well formed diagnostic object.
IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
- IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
+ IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts =
+ CreateAndPopulateDiagOpts(ClangArgv);
TextDiagnosticBuffer *DiagsBuffer = new TextDiagnosticBuffer;
DiagnosticsEngine Diags(DiagID, &*DiagOpts, DiagsBuffer);
- unsigned MissingArgIndex, MissingArgCount;
- const llvm::opt::OptTable &Opts = driver::getDriverOptTable();
- llvm::opt::InputArgList ParsedArgs =
- Opts.ParseArgs(ArrayRef<const char *>(ClangArgv).slice(1),
- MissingArgIndex, MissingArgCount);
- ParseDiagnosticArgs(*DiagOpts, ParsedArgs, &Diags);
driver::Driver Driver(/*MainBinaryName=*/ClangArgv[0],
llvm::sys::getProcessTriple(), Diags);
Driver.setCheckInputsExist(false); // the input comes from mem buffers
- llvm::ArrayRef<const char *> RF = llvm::makeArrayRef(ClangArgv);
+ llvm::ArrayRef<const char *> RF = llvm::ArrayRef(ClangArgv);
std::unique_ptr<driver::Compilation> Compilation(Driver.BuildCompilation(RF));
if (Compilation->getArgs().hasArg(driver::options::OPT_v))
@@ -173,16 +178,97 @@ IncrementalCompilerBuilder::create(std::vector<const char *> &ClangArgv) {
return CreateCI(**ErrOrCC1Args);
}
+llvm::Expected<std::unique_ptr<CompilerInstance>>
+IncrementalCompilerBuilder::CreateCpp() {
+ std::vector<const char *> Argv;
+ Argv.reserve(5 + 1 + UserArgs.size());
+ Argv.push_back("-xc++");
+ Argv.insert(Argv.end(), UserArgs.begin(), UserArgs.end());
+
+ return IncrementalCompilerBuilder::create(Argv);
+}
+
+llvm::Expected<std::unique_ptr<CompilerInstance>>
+IncrementalCompilerBuilder::createCuda(bool device) {
+ std::vector<const char *> Argv;
+ Argv.reserve(5 + 4 + UserArgs.size());
+
+ Argv.push_back("-xcuda");
+ if (device)
+ Argv.push_back("--cuda-device-only");
+ else
+ Argv.push_back("--cuda-host-only");
+
+ std::string SDKPathArg = "--cuda-path=";
+ if (!CudaSDKPath.empty()) {
+ SDKPathArg += CudaSDKPath;
+ Argv.push_back(SDKPathArg.c_str());
+ }
+
+ std::string ArchArg = "--offload-arch=";
+ if (!OffloadArch.empty()) {
+ ArchArg += OffloadArch;
+ Argv.push_back(ArchArg.c_str());
+ }
+
+ Argv.insert(Argv.end(), UserArgs.begin(), UserArgs.end());
+
+ return IncrementalCompilerBuilder::create(Argv);
+}
+
+llvm::Expected<std::unique_ptr<CompilerInstance>>
+IncrementalCompilerBuilder::CreateCudaDevice() {
+ return IncrementalCompilerBuilder::createCuda(true);
+}
+
+llvm::Expected<std::unique_ptr<CompilerInstance>>
+IncrementalCompilerBuilder::CreateCudaHost() {
+ return IncrementalCompilerBuilder::createCuda(false);
+}
+
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),
+ IncrParser = std::make_unique<IncrementalParser>(*this, std::move(CI),
*TSCtx->getContext(), Err);
}
-Interpreter::~Interpreter() {}
+Interpreter::~Interpreter() {
+ if (IncrExecutor) {
+ if (llvm::Error Err = IncrExecutor->cleanUp())
+ llvm::report_fatal_error(
+ llvm::Twine("Failed to clean up IncrementalExecutor: ") +
+ toString(std::move(Err)));
+ }
+}
+
+// These better to put in a runtime header but we can't. This is because we
+// can't find the precise resource directory in unittests so we have to hard
+// code them.
+const char *const Runtimes = R"(
+#ifdef __cplusplus
+ void *__clang_Interpreter_SetValueWithAlloc(void*, void*, void*);
+ void __clang_Interpreter_SetValueNoAlloc(void*, void*, void*);
+ void __clang_Interpreter_SetValueNoAlloc(void*, void*, void*, void*);
+ void __clang_Interpreter_SetValueNoAlloc(void*, void*, void*, float);
+ void __clang_Interpreter_SetValueNoAlloc(void*, void*, void*, double);
+ void __clang_Interpreter_SetValueNoAlloc(void*, void*, void*, long double);
+ void __clang_Interpreter_SetValueNoAlloc(void*,void*,void*,unsigned long long);
+ struct __clang_Interpreter_NewTag{} __ci_newtag;
+ void* operator new(__SIZE_TYPE__, void* __p, __clang_Interpreter_NewTag) noexcept;
+ template <class T, class = T (*)() /*disable for arrays*/>
+ void __clang_Interpreter_SetValueCopyArr(T* Src, void* Placement, unsigned long Size) {
+ for (auto Idx = 0; Idx < Size; ++Idx)
+ new ((void*)(((T*)Placement) + Idx), __ci_newtag) T(Src[Idx]);
+ }
+ template <class T, unsigned long N>
+ void __clang_Interpreter_SetValueCopyArr(const T (*Src)[N], void* Placement, unsigned long Size) {
+ __clang_Interpreter_SetValueCopyArr(Src[0], Placement, Size);
+ }
+#endif // __cplusplus
+)";
llvm::Expected<std::unique_ptr<Interpreter>>
Interpreter::create(std::unique_ptr<CompilerInstance> CI) {
@@ -191,31 +277,115 @@ Interpreter::create(std::unique_ptr<CompilerInstance> CI) {
std::unique_ptr<Interpreter>(new Interpreter(std::move(CI), Err));
if (Err)
return std::move(Err);
+
+ auto PTU = Interp->Parse(Runtimes);
+ if (!PTU)
+ return PTU.takeError();
+
+ Interp->ValuePrintingInfo.resize(4);
+ // FIXME: This is a ugly hack. Undo command checks its availability by looking
+ // at the size of the PTU list. However we have parsed something in the
+ // beginning of the REPL so we have to mark them as 'Irrevocable'.
+ Interp->InitPTUSize = Interp->IncrParser->getPTUs().size();
return std::move(Interp);
}
+llvm::Expected<std::unique_ptr<Interpreter>>
+Interpreter::createWithCUDA(std::unique_ptr<CompilerInstance> CI,
+ std::unique_ptr<CompilerInstance> DCI) {
+ // avoid writing fat binary to disk using an in-memory virtual file system
+ llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> IMVFS =
+ std::make_unique<llvm::vfs::InMemoryFileSystem>();
+ llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayVFS =
+ std::make_unique<llvm::vfs::OverlayFileSystem>(
+ llvm::vfs::getRealFileSystem());
+ OverlayVFS->pushOverlay(IMVFS);
+ CI->createFileManager(OverlayVFS);
+
+ auto Interp = Interpreter::create(std::move(CI));
+ if (auto E = Interp.takeError())
+ return std::move(E);
+
+ llvm::Error Err = llvm::Error::success();
+ auto DeviceParser = std::make_unique<IncrementalCUDADeviceParser>(
+ **Interp, std::move(DCI), *(*Interp)->IncrParser.get(),
+ *(*Interp)->TSCtx->getContext(), IMVFS, Err);
+ if (Err)
+ return std::move(Err);
+
+ (*Interp)->DeviceParser = std::move(DeviceParser);
+
+ return Interp;
+}
+
const CompilerInstance *Interpreter::getCompilerInstance() const {
return IncrParser->getCI();
}
+CompilerInstance *Interpreter::getCompilerInstance() {
+ return IncrParser->getCI();
+}
+
+llvm::Expected<llvm::orc::LLJIT &> Interpreter::getExecutionEngine() {
+ if (!IncrExecutor) {
+ if (auto Err = CreateExecutor())
+ return std::move(Err);
+ }
+
+ return IncrExecutor->GetExecutionEngine();
+}
+
+ASTContext &Interpreter::getASTContext() {
+ return getCompilerInstance()->getASTContext();
+}
+
+const ASTContext &Interpreter::getASTContext() const {
+ return getCompilerInstance()->getASTContext();
+}
+
+size_t Interpreter::getEffectivePTUSize() const {
+ std::list<PartialTranslationUnit> &PTUs = IncrParser->getPTUs();
+ assert(PTUs.size() >= InitPTUSize && "empty PTU list?");
+ return PTUs.size() - InitPTUSize;
+}
+
llvm::Expected<PartialTranslationUnit &>
Interpreter::Parse(llvm::StringRef Code) {
+ // If we have a device parser, parse it first.
+ // The generated code will be included in the host compilation
+ if (DeviceParser) {
+ auto DevicePTU = DeviceParser->Parse(Code);
+ if (auto E = DevicePTU.takeError())
+ return std::move(E);
+ }
+
+ // Tell the interpreter sliently ignore unused expressions since value
+ // printing could cause it.
+ getCompilerInstance()->getDiagnostics().setSeverity(
+ clang::diag::warn_unused_expr, diag::Severity::Ignored, SourceLocation());
return IncrParser->Parse(Code);
}
+llvm::Error Interpreter::CreateExecutor() {
+ const clang::TargetInfo &TI =
+ getCompilerInstance()->getASTContext().getTargetInfo();
+ llvm::Error Err = llvm::Error::success();
+ auto Executor = std::make_unique<IncrementalExecutor>(*TSCtx, Err, TI);
+ if (!Err)
+ IncrExecutor = std::move(Executor);
+
+ return Err;
+}
+
llvm::Error Interpreter::Execute(PartialTranslationUnit &T) {
assert(T.TheModule);
if (!IncrExecutor) {
- const llvm::Triple &Triple =
- getCompilerInstance()->getASTContext().getTargetInfo().getTriple();
- llvm::Error Err = llvm::Error::success();
- IncrExecutor = std::make_unique<IncrementalExecutor>(*TSCtx, Err, Triple);
-
+ auto Err = CreateExecutor();
if (Err)
return Err;
}
// FIXME: Add a callback to retain the llvm::Module once the JIT is done.
- if (auto Err = IncrExecutor->addModule(std::move(T.TheModule)))
+ if (auto Err = IncrExecutor->addModule(T))
return Err;
if (auto Err = IncrExecutor->runCtors())
@@ -223,3 +393,445 @@ llvm::Error Interpreter::Execute(PartialTranslationUnit &T) {
return llvm::Error::success();
}
+
+llvm::Error Interpreter::ParseAndExecute(llvm::StringRef Code, Value *V) {
+
+ auto PTU = Parse(Code);
+ if (!PTU)
+ return PTU.takeError();
+ if (PTU->TheModule)
+ if (llvm::Error Err = Execute(*PTU))
+ return Err;
+
+ if (LastValue.isValid()) {
+ if (!V) {
+ LastValue.dump();
+ LastValue.clear();
+ } else
+ *V = std::move(LastValue);
+ }
+ return llvm::Error::success();
+}
+
+llvm::Expected<llvm::orc::ExecutorAddr>
+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::orc::ExecutorAddr>
+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::orc::ExecutorAddr>
+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 > getEffectivePTUSize())
+ 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();
+}
+
+llvm::Error Interpreter::LoadDynamicLibrary(const char *name) {
+ auto EE = getExecutionEngine();
+ if (!EE)
+ return EE.takeError();
+
+ auto &DL = EE->getDataLayout();
+
+ if (auto DLSG = llvm::orc::DynamicLibrarySearchGenerator::Load(
+ name, DL.getGlobalPrefix()))
+ EE->getMainJITDylib().addGenerator(std::move(*DLSG));
+ else
+ return DLSG.takeError();
+
+ return llvm::Error::success();
+}
+
+llvm::Expected<llvm::orc::ExecutorAddr>
+Interpreter::CompileDtorCall(CXXRecordDecl *CXXRD) {
+ assert(CXXRD && "Cannot compile a destructor for a nullptr");
+ if (auto Dtor = Dtors.find(CXXRD); Dtor != Dtors.end())
+ return Dtor->getSecond();
+
+ if (CXXRD->hasIrrelevantDestructor())
+ return llvm::orc::ExecutorAddr{};
+
+ CXXDestructorDecl *DtorRD =
+ getCompilerInstance()->getSema().LookupDestructor(CXXRD);
+
+ llvm::StringRef Name =
+ IncrParser->GetMangledName(GlobalDecl(DtorRD, Dtor_Base));
+ auto AddrOrErr = getSymbolAddress(Name);
+ if (!AddrOrErr)
+ return AddrOrErr.takeError();
+
+ Dtors[CXXRD] = *AddrOrErr;
+ return AddrOrErr;
+}
+
+static constexpr llvm::StringRef MagicRuntimeInterface[] = {
+ "__clang_Interpreter_SetValueNoAlloc",
+ "__clang_Interpreter_SetValueWithAlloc",
+ "__clang_Interpreter_SetValueCopyArr", "__ci_newtag"};
+
+bool Interpreter::FindRuntimeInterface() {
+ if (llvm::all_of(ValuePrintingInfo, [](Expr *E) { return E != nullptr; }))
+ return true;
+
+ Sema &S = getCompilerInstance()->getSema();
+ ASTContext &Ctx = S.getASTContext();
+
+ auto LookupInterface = [&](Expr *&Interface, llvm::StringRef Name) {
+ LookupResult R(S, &Ctx.Idents.get(Name), SourceLocation(),
+ Sema::LookupOrdinaryName, Sema::ForVisibleRedeclaration);
+ S.LookupQualifiedName(R, Ctx.getTranslationUnitDecl());
+ if (R.empty())
+ return false;
+
+ CXXScopeSpec CSS;
+ Interface = S.BuildDeclarationNameExpr(CSS, R, /*ADL=*/false).get();
+ return true;
+ };
+
+ if (!LookupInterface(ValuePrintingInfo[NoAlloc],
+ MagicRuntimeInterface[NoAlloc]))
+ return false;
+ if (!LookupInterface(ValuePrintingInfo[WithAlloc],
+ MagicRuntimeInterface[WithAlloc]))
+ return false;
+ if (!LookupInterface(ValuePrintingInfo[CopyArray],
+ MagicRuntimeInterface[CopyArray]))
+ return false;
+ if (!LookupInterface(ValuePrintingInfo[NewTag],
+ MagicRuntimeInterface[NewTag]))
+ return false;
+ return true;
+}
+
+namespace {
+
+class RuntimeInterfaceBuilder
+ : public TypeVisitor<RuntimeInterfaceBuilder, Interpreter::InterfaceKind> {
+ clang::Interpreter &Interp;
+ ASTContext &Ctx;
+ Sema &S;
+ Expr *E;
+ llvm::SmallVector<Expr *, 3> Args;
+
+public:
+ RuntimeInterfaceBuilder(clang::Interpreter &In, ASTContext &C, Sema &SemaRef,
+ Expr *VE, ArrayRef<Expr *> FixedArgs)
+ : Interp(In), Ctx(C), S(SemaRef), E(VE) {
+ // The Interpreter* parameter and the out parameter `OutVal`.
+ for (Expr *E : FixedArgs)
+ Args.push_back(E);
+
+ // Get rid of ExprWithCleanups.
+ if (auto *EWC = llvm::dyn_cast_if_present<ExprWithCleanups>(E))
+ E = EWC->getSubExpr();
+ }
+
+ ExprResult getCall() {
+ QualType Ty = E->getType();
+ QualType DesugaredTy = Ty.getDesugaredType(Ctx);
+
+ // For lvalue struct, we treat it as a reference.
+ if (DesugaredTy->isRecordType() && E->isLValue()) {
+ DesugaredTy = Ctx.getLValueReferenceType(DesugaredTy);
+ Ty = Ctx.getLValueReferenceType(Ty);
+ }
+
+ Expr *TypeArg =
+ CStyleCastPtrExpr(S, Ctx.VoidPtrTy, (uintptr_t)Ty.getAsOpaquePtr());
+ // The QualType parameter `OpaqueType`, represented as `void*`.
+ Args.push_back(TypeArg);
+
+ // We push the last parameter based on the type of the Expr. Note we need
+ // special care for rvalue struct.
+ Interpreter::InterfaceKind Kind = Visit(&*DesugaredTy);
+ switch (Kind) {
+ case Interpreter::InterfaceKind::WithAlloc:
+ case Interpreter::InterfaceKind::CopyArray: {
+ // __clang_Interpreter_SetValueWithAlloc.
+ ExprResult AllocCall = S.ActOnCallExpr(
+ /*Scope=*/nullptr,
+ Interp.getValuePrintingInfo()[Interpreter::InterfaceKind::WithAlloc],
+ E->getBeginLoc(), Args, E->getEndLoc());
+ assert(!AllocCall.isInvalid() && "Can't create runtime interface call!");
+
+ TypeSourceInfo *TSI = Ctx.getTrivialTypeSourceInfo(Ty, SourceLocation());
+
+ // Force CodeGen to emit destructor.
+ if (auto *RD = Ty->getAsCXXRecordDecl()) {
+ auto *Dtor = S.LookupDestructor(RD);
+ Dtor->addAttr(UsedAttr::CreateImplicit(Ctx));
+ Interp.getCompilerInstance()->getASTConsumer().HandleTopLevelDecl(
+ DeclGroupRef(Dtor));
+ }
+
+ // __clang_Interpreter_SetValueCopyArr.
+ if (Kind == Interpreter::InterfaceKind::CopyArray) {
+ const auto *ConstantArrTy =
+ cast<ConstantArrayType>(DesugaredTy.getTypePtr());
+ size_t ArrSize = Ctx.getConstantArrayElementCount(ConstantArrTy);
+ Expr *ArrSizeExpr = IntegerLiteralExpr(Ctx, ArrSize);
+ Expr *Args[] = {E, AllocCall.get(), ArrSizeExpr};
+ return S.ActOnCallExpr(
+ /*Scope *=*/nullptr,
+ Interp
+ .getValuePrintingInfo()[Interpreter::InterfaceKind::CopyArray],
+ SourceLocation(), Args, SourceLocation());
+ }
+ Expr *Args[] = {
+ AllocCall.get(),
+ Interp.getValuePrintingInfo()[Interpreter::InterfaceKind::NewTag]};
+ ExprResult CXXNewCall = S.BuildCXXNew(
+ E->getSourceRange(),
+ /*UseGlobal=*/true, /*PlacementLParen=*/SourceLocation(), Args,
+ /*PlacementRParen=*/SourceLocation(),
+ /*TypeIdParens=*/SourceRange(), TSI->getType(), TSI, std::nullopt,
+ E->getSourceRange(), E);
+
+ assert(!CXXNewCall.isInvalid() &&
+ "Can't create runtime placement new call!");
+
+ return S.ActOnFinishFullExpr(CXXNewCall.get(),
+ /*DiscardedValue=*/false);
+ }
+ // __clang_Interpreter_SetValueNoAlloc.
+ case Interpreter::InterfaceKind::NoAlloc: {
+ return S.ActOnCallExpr(
+ /*Scope=*/nullptr,
+ Interp.getValuePrintingInfo()[Interpreter::InterfaceKind::NoAlloc],
+ E->getBeginLoc(), Args, E->getEndLoc());
+ }
+ default:
+ llvm_unreachable("Unhandled Interpreter::InterfaceKind");
+ }
+ }
+
+ Interpreter::InterfaceKind VisitRecordType(const RecordType *Ty) {
+ return Interpreter::InterfaceKind::WithAlloc;
+ }
+
+ Interpreter::InterfaceKind
+ VisitMemberPointerType(const MemberPointerType *Ty) {
+ return Interpreter::InterfaceKind::WithAlloc;
+ }
+
+ Interpreter::InterfaceKind
+ VisitConstantArrayType(const ConstantArrayType *Ty) {
+ return Interpreter::InterfaceKind::CopyArray;
+ }
+
+ Interpreter::InterfaceKind
+ VisitFunctionProtoType(const FunctionProtoType *Ty) {
+ HandlePtrType(Ty);
+ return Interpreter::InterfaceKind::NoAlloc;
+ }
+
+ Interpreter::InterfaceKind VisitPointerType(const PointerType *Ty) {
+ HandlePtrType(Ty);
+ return Interpreter::InterfaceKind::NoAlloc;
+ }
+
+ Interpreter::InterfaceKind VisitReferenceType(const ReferenceType *Ty) {
+ ExprResult AddrOfE = S.CreateBuiltinUnaryOp(SourceLocation(), UO_AddrOf, E);
+ assert(!AddrOfE.isInvalid() && "Can not create unary expression");
+ Args.push_back(AddrOfE.get());
+ return Interpreter::InterfaceKind::NoAlloc;
+ }
+
+ Interpreter::InterfaceKind VisitBuiltinType(const BuiltinType *Ty) {
+ if (Ty->isNullPtrType())
+ Args.push_back(E);
+ else if (Ty->isFloatingType())
+ Args.push_back(E);
+ else if (Ty->isIntegralOrEnumerationType())
+ HandleIntegralOrEnumType(Ty);
+ else if (Ty->isVoidType()) {
+ // Do we need to still run `E`?
+ }
+
+ return Interpreter::InterfaceKind::NoAlloc;
+ }
+
+ Interpreter::InterfaceKind VisitEnumType(const EnumType *Ty) {
+ HandleIntegralOrEnumType(Ty);
+ return Interpreter::InterfaceKind::NoAlloc;
+ }
+
+private:
+ // Force cast these types to uint64 to reduce the number of overloads of
+ // `__clang_Interpreter_SetValueNoAlloc`.
+ void HandleIntegralOrEnumType(const Type *Ty) {
+ TypeSourceInfo *TSI = Ctx.getTrivialTypeSourceInfo(Ctx.UnsignedLongLongTy);
+ ExprResult CastedExpr =
+ S.BuildCStyleCastExpr(SourceLocation(), TSI, SourceLocation(), E);
+ assert(!CastedExpr.isInvalid() && "Cannot create cstyle cast expr");
+ Args.push_back(CastedExpr.get());
+ }
+
+ void HandlePtrType(const Type *Ty) {
+ TypeSourceInfo *TSI = Ctx.getTrivialTypeSourceInfo(Ctx.VoidPtrTy);
+ ExprResult CastedExpr =
+ S.BuildCStyleCastExpr(SourceLocation(), TSI, SourceLocation(), E);
+ assert(!CastedExpr.isInvalid() && "Can not create cstyle cast expression");
+ Args.push_back(CastedExpr.get());
+ }
+};
+} // namespace
+
+// This synthesizes a call expression to a speciall
+// function that is responsible for generating the Value.
+// In general, we transform:
+// clang-repl> x
+// To:
+// // 1. If x is a built-in type like int, float.
+// __clang_Interpreter_SetValueNoAlloc(ThisInterp, OpaqueValue, xQualType, x);
+// // 2. If x is a struct, and a lvalue.
+// __clang_Interpreter_SetValueNoAlloc(ThisInterp, OpaqueValue, xQualType,
+// &x);
+// // 3. If x is a struct, but a rvalue.
+// new (__clang_Interpreter_SetValueWithAlloc(ThisInterp, OpaqueValue,
+// xQualType)) (x);
+
+Expr *Interpreter::SynthesizeExpr(Expr *E) {
+ Sema &S = getCompilerInstance()->getSema();
+ ASTContext &Ctx = S.getASTContext();
+
+ if (!FindRuntimeInterface())
+ llvm_unreachable("We can't find the runtime iterface for pretty print!");
+
+ // Create parameter `ThisInterp`.
+ auto *ThisInterp = CStyleCastPtrExpr(S, Ctx.VoidPtrTy, (uintptr_t)this);
+
+ // Create parameter `OutVal`.
+ auto *OutValue = CStyleCastPtrExpr(S, Ctx.VoidPtrTy, (uintptr_t)&LastValue);
+
+ // Build `__clang_Interpreter_SetValue*` call.
+ RuntimeInterfaceBuilder Builder(*this, Ctx, S, E, {ThisInterp, OutValue});
+
+ ExprResult Result = Builder.getCall();
+ // It could fail, like printing an array type in C. (not supported)
+ if (Result.isInvalid())
+ return E;
+ return Result.get();
+}
+
+// Temporary rvalue struct that need special care.
+REPL_EXTERNAL_VISIBILITY void *
+__clang_Interpreter_SetValueWithAlloc(void *This, void *OutVal,
+ void *OpaqueType) {
+ Value &VRef = *(Value *)OutVal;
+ VRef = Value(static_cast<Interpreter *>(This), OpaqueType);
+ return VRef.getPtr();
+}
+
+// Pointers, lvalue struct that can take as a reference.
+REPL_EXTERNAL_VISIBILITY void
+__clang_Interpreter_SetValueNoAlloc(void *This, void *OutVal, void *OpaqueType,
+ void *Val) {
+ Value &VRef = *(Value *)OutVal;
+ VRef = Value(static_cast<Interpreter *>(This), OpaqueType);
+ VRef.setPtr(Val);
+}
+
+REPL_EXTERNAL_VISIBILITY void
+__clang_Interpreter_SetValueNoAlloc(void *This, void *OutVal,
+ void *OpaqueType) {
+ Value &VRef = *(Value *)OutVal;
+ VRef = Value(static_cast<Interpreter *>(This), OpaqueType);
+}
+
+static void SetValueDataBasedOnQualType(Value &V, unsigned long long Data) {
+ QualType QT = V.getType();
+ if (const auto *ET = QT->getAs<EnumType>())
+ QT = ET->getDecl()->getIntegerType();
+
+ switch (QT->castAs<BuiltinType>()->getKind()) {
+ default:
+ llvm_unreachable("unknown type kind!");
+#define X(type, name) \
+ case BuiltinType::name: \
+ V.set##name(Data); \
+ break;
+ REPL_BUILTIN_TYPES
+#undef X
+ }
+}
+
+REPL_EXTERNAL_VISIBILITY void
+__clang_Interpreter_SetValueNoAlloc(void *This, void *OutVal, void *OpaqueType,
+ unsigned long long Val) {
+ Value &VRef = *(Value *)OutVal;
+ VRef = Value(static_cast<Interpreter *>(This), OpaqueType);
+ SetValueDataBasedOnQualType(VRef, Val);
+}
+
+REPL_EXTERNAL_VISIBILITY void
+__clang_Interpreter_SetValueNoAlloc(void *This, void *OutVal, void *OpaqueType,
+ float Val) {
+ Value &VRef = *(Value *)OutVal;
+ VRef = Value(static_cast<Interpreter *>(This), OpaqueType);
+ VRef.setFloat(Val);
+}
+
+REPL_EXTERNAL_VISIBILITY void
+__clang_Interpreter_SetValueNoAlloc(void *This, void *OutVal, void *OpaqueType,
+ double Val) {
+ Value &VRef = *(Value *)OutVal;
+ VRef = Value(static_cast<Interpreter *>(This), OpaqueType);
+ VRef.setDouble(Val);
+}
+
+REPL_EXTERNAL_VISIBILITY void
+__clang_Interpreter_SetValueNoAlloc(void *This, void *OutVal, void *OpaqueType,
+ long double Val) {
+ Value &VRef = *(Value *)OutVal;
+ VRef = Value(static_cast<Interpreter *>(This), OpaqueType);
+ VRef.setLongDouble(Val);
+}
+
+// A trampoline to work around the fact that operator placement new cannot
+// really be forward declared due to libc++ and libstdc++ declaration mismatch.
+// FIXME: __clang_Interpreter_NewTag is ODR violation because we get the same
+// definition in the interpreter runtime. We should move it in a runtime header
+// which gets included by the interpreter and here.
+struct __clang_Interpreter_NewTag {};
+REPL_EXTERNAL_VISIBILITY void *
+operator new(size_t __sz, void *__p, __clang_Interpreter_NewTag) noexcept {
+ // Just forward to the standard operator placement new.
+ return operator new(__sz, __p);
+}