Path: blob/main/contrib/llvm-project/clang/lib/Interpreter/Interpreter.cpp
35232 views
//===------ Interpreter.cpp - Incremental Compilation and Execution -------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This file implements the component which performs incremental code9// compilation and execution.10//11//===----------------------------------------------------------------------===//1213#include "DeviceOffload.h"14#include "IncrementalExecutor.h"15#include "IncrementalParser.h"16#include "InterpreterUtils.h"17#ifdef __EMSCRIPTEN__18#include "Wasm.h"19#endif // __EMSCRIPTEN__2021#include "clang/AST/ASTContext.h"22#include "clang/AST/Mangle.h"23#include "clang/AST/TypeVisitor.h"24#include "clang/Basic/DiagnosticSema.h"25#include "clang/Basic/TargetInfo.h"26#include "clang/CodeGen/CodeGenAction.h"27#include "clang/CodeGen/ModuleBuilder.h"28#include "clang/CodeGen/ObjectFilePCHContainerOperations.h"29#include "clang/Driver/Compilation.h"30#include "clang/Driver/Driver.h"31#include "clang/Driver/Job.h"32#include "clang/Driver/Options.h"33#include "clang/Driver/Tool.h"34#include "clang/Frontend/CompilerInstance.h"35#include "clang/Frontend/TextDiagnosticBuffer.h"36#include "clang/Interpreter/Interpreter.h"37#include "clang/Interpreter/Value.h"38#include "clang/Lex/PreprocessorOptions.h"39#include "clang/Sema/Lookup.h"40#include "llvm/ExecutionEngine/JITSymbol.h"41#include "llvm/ExecutionEngine/Orc/LLJIT.h"42#include "llvm/IR/Module.h"43#include "llvm/Support/Errc.h"44#include "llvm/Support/ErrorHandling.h"45#include "llvm/Support/raw_ostream.h"46#include "llvm/TargetParser/Host.h"4748#include <cstdarg>4950using namespace clang;5152// FIXME: Figure out how to unify with namespace init_convenience from53// tools/clang-import-test/clang-import-test.cpp54namespace {55/// Retrieves the clang CC1 specific flags out of the compilation's jobs.56/// \returns NULL on error.57static llvm::Expected<const llvm::opt::ArgStringList *>58GetCC1Arguments(DiagnosticsEngine *Diagnostics,59driver::Compilation *Compilation) {60// We expect to get back exactly one Command job, if we didn't something61// failed. Extract that job from the Compilation.62const driver::JobList &Jobs = Compilation->getJobs();63if (!Jobs.size() || !isa<driver::Command>(*Jobs.begin()))64return llvm::createStringError(llvm::errc::not_supported,65"Driver initialization failed. "66"Unable to create a driver job");6768// The one job we find should be to invoke clang again.69const driver::Command *Cmd = cast<driver::Command>(&(*Jobs.begin()));70if (llvm::StringRef(Cmd->getCreator().getName()) != "clang")71return llvm::createStringError(llvm::errc::not_supported,72"Driver initialization failed");7374return &Cmd->getArguments();75}7677static llvm::Expected<std::unique_ptr<CompilerInstance>>78CreateCI(const llvm::opt::ArgStringList &Argv) {79std::unique_ptr<CompilerInstance> Clang(new CompilerInstance());80IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());8182// Register the support for object-file-wrapped Clang modules.83// FIXME: Clang should register these container operations automatically.84auto PCHOps = Clang->getPCHContainerOperations();85PCHOps->registerWriter(std::make_unique<ObjectFilePCHContainerWriter>());86PCHOps->registerReader(std::make_unique<ObjectFilePCHContainerReader>());8788// Buffer diagnostics from argument parsing so that we can output them using89// a well formed diagnostic object.90IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();91TextDiagnosticBuffer *DiagsBuffer = new TextDiagnosticBuffer;92DiagnosticsEngine Diags(DiagID, &*DiagOpts, DiagsBuffer);93bool Success = CompilerInvocation::CreateFromArgs(94Clang->getInvocation(), llvm::ArrayRef(Argv.begin(), Argv.size()), Diags);9596// Infer the builtin include path if unspecified.97if (Clang->getHeaderSearchOpts().UseBuiltinIncludes &&98Clang->getHeaderSearchOpts().ResourceDir.empty())99Clang->getHeaderSearchOpts().ResourceDir =100CompilerInvocation::GetResourcesPath(Argv[0], nullptr);101102// Create the actual diagnostics engine.103Clang->createDiagnostics();104if (!Clang->hasDiagnostics())105return llvm::createStringError(llvm::errc::not_supported,106"Initialization failed. "107"Unable to create diagnostics engine");108109DiagsBuffer->FlushDiagnostics(Clang->getDiagnostics());110if (!Success)111return llvm::createStringError(llvm::errc::not_supported,112"Initialization failed. "113"Unable to flush diagnostics");114115// FIXME: Merge with CompilerInstance::ExecuteAction.116llvm::MemoryBuffer *MB = llvm::MemoryBuffer::getMemBuffer("").release();117Clang->getPreprocessorOpts().addRemappedFile("<<< inputs >>>", MB);118119Clang->setTarget(TargetInfo::CreateTargetInfo(120Clang->getDiagnostics(), Clang->getInvocation().TargetOpts));121if (!Clang->hasTarget())122return llvm::createStringError(llvm::errc::not_supported,123"Initialization failed. "124"Target is missing");125126Clang->getTarget().adjust(Clang->getDiagnostics(), Clang->getLangOpts());127128// Don't clear the AST before backend codegen since we do codegen multiple129// times, reusing the same AST.130Clang->getCodeGenOpts().ClearASTBeforeBackend = false;131132Clang->getFrontendOpts().DisableFree = false;133Clang->getCodeGenOpts().DisableFree = false;134return std::move(Clang);135}136137} // anonymous namespace138139llvm::Expected<std::unique_ptr<CompilerInstance>>140IncrementalCompilerBuilder::create(std::string TT,141std::vector<const char *> &ClangArgv) {142143// If we don't know ClangArgv0 or the address of main() at this point, try144// to guess it anyway (it's possible on some platforms).145std::string MainExecutableName =146llvm::sys::fs::getMainExecutable(nullptr, nullptr);147148ClangArgv.insert(ClangArgv.begin(), MainExecutableName.c_str());149150// Prepending -c to force the driver to do something if no action was151// specified. By prepending we allow users to override the default152// action and use other actions in incremental mode.153// FIXME: Print proper driver diagnostics if the driver flags are wrong.154// We do C++ by default; append right after argv[0] if no "-x" given155ClangArgv.insert(ClangArgv.end(), "-Xclang");156ClangArgv.insert(ClangArgv.end(), "-fincremental-extensions");157ClangArgv.insert(ClangArgv.end(), "-c");158159// Put a dummy C++ file on to ensure there's at least one compile job for the160// driver to construct.161ClangArgv.push_back("<<< inputs >>>");162163// Buffer diagnostics from argument parsing so that we can output them using a164// well formed diagnostic object.165IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());166IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts =167CreateAndPopulateDiagOpts(ClangArgv);168TextDiagnosticBuffer *DiagsBuffer = new TextDiagnosticBuffer;169DiagnosticsEngine Diags(DiagID, &*DiagOpts, DiagsBuffer);170171driver::Driver Driver(/*MainBinaryName=*/ClangArgv[0], TT, Diags);172Driver.setCheckInputsExist(false); // the input comes from mem buffers173llvm::ArrayRef<const char *> RF = llvm::ArrayRef(ClangArgv);174std::unique_ptr<driver::Compilation> Compilation(Driver.BuildCompilation(RF));175176if (Compilation->getArgs().hasArg(driver::options::OPT_v))177Compilation->getJobs().Print(llvm::errs(), "\n", /*Quote=*/false);178179auto ErrOrCC1Args = GetCC1Arguments(&Diags, Compilation.get());180if (auto Err = ErrOrCC1Args.takeError())181return std::move(Err);182183return CreateCI(**ErrOrCC1Args);184}185186llvm::Expected<std::unique_ptr<CompilerInstance>>187IncrementalCompilerBuilder::CreateCpp() {188std::vector<const char *> Argv;189Argv.reserve(5 + 1 + UserArgs.size());190Argv.push_back("-xc++");191#ifdef __EMSCRIPTEN__192Argv.push_back("-target");193Argv.push_back("wasm32-unknown-emscripten");194Argv.push_back("-shared");195Argv.push_back("-fvisibility=default");196#endif197Argv.insert(Argv.end(), UserArgs.begin(), UserArgs.end());198199std::string TT = TargetTriple ? *TargetTriple : llvm::sys::getProcessTriple();200return IncrementalCompilerBuilder::create(TT, Argv);201}202203llvm::Expected<std::unique_ptr<CompilerInstance>>204IncrementalCompilerBuilder::createCuda(bool device) {205std::vector<const char *> Argv;206Argv.reserve(5 + 4 + UserArgs.size());207208Argv.push_back("-xcuda");209if (device)210Argv.push_back("--cuda-device-only");211else212Argv.push_back("--cuda-host-only");213214std::string SDKPathArg = "--cuda-path=";215if (!CudaSDKPath.empty()) {216SDKPathArg += CudaSDKPath;217Argv.push_back(SDKPathArg.c_str());218}219220std::string ArchArg = "--offload-arch=";221if (!OffloadArch.empty()) {222ArchArg += OffloadArch;223Argv.push_back(ArchArg.c_str());224}225226Argv.insert(Argv.end(), UserArgs.begin(), UserArgs.end());227228std::string TT = TargetTriple ? *TargetTriple : llvm::sys::getProcessTriple();229return IncrementalCompilerBuilder::create(TT, Argv);230}231232llvm::Expected<std::unique_ptr<CompilerInstance>>233IncrementalCompilerBuilder::CreateCudaDevice() {234return IncrementalCompilerBuilder::createCuda(true);235}236237llvm::Expected<std::unique_ptr<CompilerInstance>>238IncrementalCompilerBuilder::CreateCudaHost() {239return IncrementalCompilerBuilder::createCuda(false);240}241242Interpreter::Interpreter(std::unique_ptr<CompilerInstance> CI,243llvm::Error &ErrOut,244std::unique_ptr<llvm::orc::LLJITBuilder> JITBuilder)245: JITBuilder(std::move(JITBuilder)) {246llvm::ErrorAsOutParameter EAO(&ErrOut);247auto LLVMCtx = std::make_unique<llvm::LLVMContext>();248TSCtx = std::make_unique<llvm::orc::ThreadSafeContext>(std::move(LLVMCtx));249IncrParser = std::make_unique<IncrementalParser>(250*this, std::move(CI), *TSCtx->getContext(), ErrOut);251if (ErrOut)252return;253254// Not all frontends support code-generation, e.g. ast-dump actions don't255if (IncrParser->getCodeGen()) {256if (llvm::Error Err = CreateExecutor()) {257ErrOut = joinErrors(std::move(ErrOut), std::move(Err));258return;259}260261// Process the PTUs that came from initialization. For example -include will262// give us a header that's processed at initialization of the preprocessor.263for (PartialTranslationUnit &PTU : IncrParser->getPTUs())264if (llvm::Error Err = Execute(PTU)) {265ErrOut = joinErrors(std::move(ErrOut), std::move(Err));266return;267}268}269}270271Interpreter::~Interpreter() {272if (IncrExecutor) {273if (llvm::Error Err = IncrExecutor->cleanUp())274llvm::report_fatal_error(275llvm::Twine("Failed to clean up IncrementalExecutor: ") +276toString(std::move(Err)));277}278}279280// These better to put in a runtime header but we can't. This is because we281// can't find the precise resource directory in unittests so we have to hard282// code them.283const char *const Runtimes = R"(284#define __CLANG_REPL__ 1285#ifdef __cplusplus286#define EXTERN_C extern "C"287void *__clang_Interpreter_SetValueWithAlloc(void*, void*, void*);288struct __clang_Interpreter_NewTag{} __ci_newtag;289void* operator new(__SIZE_TYPE__, void* __p, __clang_Interpreter_NewTag) noexcept;290template <class T, class = T (*)() /*disable for arrays*/>291void __clang_Interpreter_SetValueCopyArr(T* Src, void* Placement, unsigned long Size) {292for (auto Idx = 0; Idx < Size; ++Idx)293new ((void*)(((T*)Placement) + Idx), __ci_newtag) T(Src[Idx]);294}295template <class T, unsigned long N>296void __clang_Interpreter_SetValueCopyArr(const T (*Src)[N], void* Placement, unsigned long Size) {297__clang_Interpreter_SetValueCopyArr(Src[0], Placement, Size);298}299#else300#define EXTERN_C extern301#endif // __cplusplus302303EXTERN_C void __clang_Interpreter_SetValueNoAlloc(void *This, void *OutVal, void *OpaqueType, ...);304)";305306llvm::Expected<std::unique_ptr<Interpreter>>307Interpreter::create(std::unique_ptr<CompilerInstance> CI) {308llvm::Error Err = llvm::Error::success();309auto Interp =310std::unique_ptr<Interpreter>(new Interpreter(std::move(CI), Err));311if (Err)312return std::move(Err);313314// Add runtime code and set a marker to hide it from user code. Undo will not315// go through that.316auto PTU = Interp->Parse(Runtimes);317if (!PTU)318return PTU.takeError();319Interp->markUserCodeStart();320321Interp->ValuePrintingInfo.resize(4);322return std::move(Interp);323}324325llvm::Expected<std::unique_ptr<Interpreter>>326Interpreter::createWithCUDA(std::unique_ptr<CompilerInstance> CI,327std::unique_ptr<CompilerInstance> DCI) {328// avoid writing fat binary to disk using an in-memory virtual file system329llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> IMVFS =330std::make_unique<llvm::vfs::InMemoryFileSystem>();331llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayVFS =332std::make_unique<llvm::vfs::OverlayFileSystem>(333llvm::vfs::getRealFileSystem());334OverlayVFS->pushOverlay(IMVFS);335CI->createFileManager(OverlayVFS);336337auto Interp = Interpreter::create(std::move(CI));338if (auto E = Interp.takeError())339return std::move(E);340341llvm::Error Err = llvm::Error::success();342auto DeviceParser = std::make_unique<IncrementalCUDADeviceParser>(343**Interp, std::move(DCI), *(*Interp)->IncrParser.get(),344*(*Interp)->TSCtx->getContext(), IMVFS, Err);345if (Err)346return std::move(Err);347348(*Interp)->DeviceParser = std::move(DeviceParser);349350return Interp;351}352353const CompilerInstance *Interpreter::getCompilerInstance() const {354return IncrParser->getCI();355}356357CompilerInstance *Interpreter::getCompilerInstance() {358return IncrParser->getCI();359}360361llvm::Expected<llvm::orc::LLJIT &> Interpreter::getExecutionEngine() {362if (!IncrExecutor) {363if (auto Err = CreateExecutor())364return std::move(Err);365}366367return IncrExecutor->GetExecutionEngine();368}369370ASTContext &Interpreter::getASTContext() {371return getCompilerInstance()->getASTContext();372}373374const ASTContext &Interpreter::getASTContext() const {375return getCompilerInstance()->getASTContext();376}377378void Interpreter::markUserCodeStart() {379assert(!InitPTUSize && "We only do this once");380InitPTUSize = IncrParser->getPTUs().size();381}382383size_t Interpreter::getEffectivePTUSize() const {384std::list<PartialTranslationUnit> &PTUs = IncrParser->getPTUs();385assert(PTUs.size() >= InitPTUSize && "empty PTU list?");386return PTUs.size() - InitPTUSize;387}388389llvm::Expected<PartialTranslationUnit &>390Interpreter::Parse(llvm::StringRef Code) {391// If we have a device parser, parse it first.392// The generated code will be included in the host compilation393if (DeviceParser) {394auto DevicePTU = DeviceParser->Parse(Code);395if (auto E = DevicePTU.takeError())396return std::move(E);397}398399// Tell the interpreter sliently ignore unused expressions since value400// printing could cause it.401getCompilerInstance()->getDiagnostics().setSeverity(402clang::diag::warn_unused_expr, diag::Severity::Ignored, SourceLocation());403return IncrParser->Parse(Code);404}405406static llvm::Expected<llvm::orc::JITTargetMachineBuilder>407createJITTargetMachineBuilder(const std::string &TT) {408if (TT == llvm::sys::getProcessTriple())409// This fails immediately if the target backend is not registered410return llvm::orc::JITTargetMachineBuilder::detectHost();411412// If the target backend is not registered, LLJITBuilder::create() will fail413return llvm::orc::JITTargetMachineBuilder(llvm::Triple(TT));414}415416llvm::Error Interpreter::CreateExecutor() {417if (IncrExecutor)418return llvm::make_error<llvm::StringError>("Operation failed. "419"Execution engine exists",420std::error_code());421if (!IncrParser->getCodeGen())422return llvm::make_error<llvm::StringError>("Operation failed. "423"No code generator available",424std::error_code());425if (!JITBuilder) {426const std::string &TT = getCompilerInstance()->getTargetOpts().Triple;427auto JTMB = createJITTargetMachineBuilder(TT);428if (!JTMB)429return JTMB.takeError();430auto JB = IncrementalExecutor::createDefaultJITBuilder(std::move(*JTMB));431if (!JB)432return JB.takeError();433JITBuilder = std::move(*JB);434}435436llvm::Error Err = llvm::Error::success();437#ifdef __EMSCRIPTEN__438auto Executor = std::make_unique<WasmIncrementalExecutor>(*TSCtx);439#else440auto Executor =441std::make_unique<IncrementalExecutor>(*TSCtx, *JITBuilder, Err);442#endif443if (!Err)444IncrExecutor = std::move(Executor);445446return Err;447}448449void Interpreter::ResetExecutor() { IncrExecutor.reset(); }450451llvm::Error Interpreter::Execute(PartialTranslationUnit &T) {452assert(T.TheModule);453if (!IncrExecutor) {454auto Err = CreateExecutor();455if (Err)456return Err;457}458// FIXME: Add a callback to retain the llvm::Module once the JIT is done.459if (auto Err = IncrExecutor->addModule(T))460return Err;461462if (auto Err = IncrExecutor->runCtors())463return Err;464465return llvm::Error::success();466}467468llvm::Error Interpreter::ParseAndExecute(llvm::StringRef Code, Value *V) {469470auto PTU = Parse(Code);471if (!PTU)472return PTU.takeError();473if (PTU->TheModule)474if (llvm::Error Err = Execute(*PTU))475return Err;476477if (LastValue.isValid()) {478if (!V) {479LastValue.dump();480LastValue.clear();481} else482*V = std::move(LastValue);483}484return llvm::Error::success();485}486487llvm::Expected<llvm::orc::ExecutorAddr>488Interpreter::getSymbolAddress(GlobalDecl GD) const {489if (!IncrExecutor)490return llvm::make_error<llvm::StringError>("Operation failed. "491"No execution engine",492std::error_code());493llvm::StringRef MangledName = IncrParser->GetMangledName(GD);494return getSymbolAddress(MangledName);495}496497llvm::Expected<llvm::orc::ExecutorAddr>498Interpreter::getSymbolAddress(llvm::StringRef IRName) const {499if (!IncrExecutor)500return llvm::make_error<llvm::StringError>("Operation failed. "501"No execution engine",502std::error_code());503504return IncrExecutor->getSymbolAddress(IRName, IncrementalExecutor::IRName);505}506507llvm::Expected<llvm::orc::ExecutorAddr>508Interpreter::getSymbolAddressFromLinkerName(llvm::StringRef Name) const {509if (!IncrExecutor)510return llvm::make_error<llvm::StringError>("Operation failed. "511"No execution engine",512std::error_code());513514return IncrExecutor->getSymbolAddress(Name, IncrementalExecutor::LinkerName);515}516517llvm::Error Interpreter::Undo(unsigned N) {518519std::list<PartialTranslationUnit> &PTUs = IncrParser->getPTUs();520if (N > getEffectivePTUSize())521return llvm::make_error<llvm::StringError>("Operation failed. "522"Too many undos",523std::error_code());524for (unsigned I = 0; I < N; I++) {525if (IncrExecutor) {526if (llvm::Error Err = IncrExecutor->removeModule(PTUs.back()))527return Err;528}529530IncrParser->CleanUpPTU(PTUs.back());531PTUs.pop_back();532}533return llvm::Error::success();534}535536llvm::Error Interpreter::LoadDynamicLibrary(const char *name) {537auto EE = getExecutionEngine();538if (!EE)539return EE.takeError();540541auto &DL = EE->getDataLayout();542543if (auto DLSG = llvm::orc::DynamicLibrarySearchGenerator::Load(544name, DL.getGlobalPrefix()))545EE->getMainJITDylib().addGenerator(std::move(*DLSG));546else547return DLSG.takeError();548549return llvm::Error::success();550}551552llvm::Expected<llvm::orc::ExecutorAddr>553Interpreter::CompileDtorCall(CXXRecordDecl *CXXRD) {554assert(CXXRD && "Cannot compile a destructor for a nullptr");555if (auto Dtor = Dtors.find(CXXRD); Dtor != Dtors.end())556return Dtor->getSecond();557558if (CXXRD->hasIrrelevantDestructor())559return llvm::orc::ExecutorAddr{};560561CXXDestructorDecl *DtorRD =562getCompilerInstance()->getSema().LookupDestructor(CXXRD);563564llvm::StringRef Name =565IncrParser->GetMangledName(GlobalDecl(DtorRD, Dtor_Base));566auto AddrOrErr = getSymbolAddress(Name);567if (!AddrOrErr)568return AddrOrErr.takeError();569570Dtors[CXXRD] = *AddrOrErr;571return AddrOrErr;572}573574static constexpr llvm::StringRef MagicRuntimeInterface[] = {575"__clang_Interpreter_SetValueNoAlloc",576"__clang_Interpreter_SetValueWithAlloc",577"__clang_Interpreter_SetValueCopyArr", "__ci_newtag"};578579static std::unique_ptr<RuntimeInterfaceBuilder>580createInProcessRuntimeInterfaceBuilder(Interpreter &Interp, ASTContext &Ctx,581Sema &S);582583std::unique_ptr<RuntimeInterfaceBuilder> Interpreter::FindRuntimeInterface() {584if (llvm::all_of(ValuePrintingInfo, [](Expr *E) { return E != nullptr; }))585return nullptr;586587Sema &S = getCompilerInstance()->getSema();588ASTContext &Ctx = S.getASTContext();589590auto LookupInterface = [&](Expr *&Interface, llvm::StringRef Name) {591LookupResult R(S, &Ctx.Idents.get(Name), SourceLocation(),592Sema::LookupOrdinaryName,593RedeclarationKind::ForVisibleRedeclaration);594S.LookupQualifiedName(R, Ctx.getTranslationUnitDecl());595if (R.empty())596return false;597598CXXScopeSpec CSS;599Interface = S.BuildDeclarationNameExpr(CSS, R, /*ADL=*/false).get();600return true;601};602603if (!LookupInterface(ValuePrintingInfo[NoAlloc],604MagicRuntimeInterface[NoAlloc]))605return nullptr;606if (Ctx.getLangOpts().CPlusPlus) {607if (!LookupInterface(ValuePrintingInfo[WithAlloc],608MagicRuntimeInterface[WithAlloc]))609return nullptr;610if (!LookupInterface(ValuePrintingInfo[CopyArray],611MagicRuntimeInterface[CopyArray]))612return nullptr;613if (!LookupInterface(ValuePrintingInfo[NewTag],614MagicRuntimeInterface[NewTag]))615return nullptr;616}617618return createInProcessRuntimeInterfaceBuilder(*this, Ctx, S);619}620621namespace {622623class InterfaceKindVisitor624: public TypeVisitor<InterfaceKindVisitor, Interpreter::InterfaceKind> {625friend class InProcessRuntimeInterfaceBuilder;626627ASTContext &Ctx;628Sema &S;629Expr *E;630llvm::SmallVector<Expr *, 3> Args;631632public:633InterfaceKindVisitor(ASTContext &Ctx, Sema &S, Expr *E)634: Ctx(Ctx), S(S), E(E) {}635636Interpreter::InterfaceKind VisitRecordType(const RecordType *Ty) {637return Interpreter::InterfaceKind::WithAlloc;638}639640Interpreter::InterfaceKind641VisitMemberPointerType(const MemberPointerType *Ty) {642return Interpreter::InterfaceKind::WithAlloc;643}644645Interpreter::InterfaceKind646VisitConstantArrayType(const ConstantArrayType *Ty) {647return Interpreter::InterfaceKind::CopyArray;648}649650Interpreter::InterfaceKind651VisitFunctionProtoType(const FunctionProtoType *Ty) {652HandlePtrType(Ty);653return Interpreter::InterfaceKind::NoAlloc;654}655656Interpreter::InterfaceKind VisitPointerType(const PointerType *Ty) {657HandlePtrType(Ty);658return Interpreter::InterfaceKind::NoAlloc;659}660661Interpreter::InterfaceKind VisitReferenceType(const ReferenceType *Ty) {662ExprResult AddrOfE = S.CreateBuiltinUnaryOp(SourceLocation(), UO_AddrOf, E);663assert(!AddrOfE.isInvalid() && "Can not create unary expression");664Args.push_back(AddrOfE.get());665return Interpreter::InterfaceKind::NoAlloc;666}667668Interpreter::InterfaceKind VisitBuiltinType(const BuiltinType *Ty) {669if (Ty->isNullPtrType())670Args.push_back(E);671else if (Ty->isFloatingType())672Args.push_back(E);673else if (Ty->isIntegralOrEnumerationType())674HandleIntegralOrEnumType(Ty);675else if (Ty->isVoidType()) {676// Do we need to still run `E`?677}678679return Interpreter::InterfaceKind::NoAlloc;680}681682Interpreter::InterfaceKind VisitEnumType(const EnumType *Ty) {683HandleIntegralOrEnumType(Ty);684return Interpreter::InterfaceKind::NoAlloc;685}686687private:688// Force cast these types to the uint that fits the register size. That way we689// reduce the number of overloads of `__clang_Interpreter_SetValueNoAlloc`.690void HandleIntegralOrEnumType(const Type *Ty) {691uint64_t PtrBits = Ctx.getTypeSize(Ctx.VoidPtrTy);692QualType UIntTy = Ctx.getBitIntType(/*Unsigned=*/true, PtrBits);693TypeSourceInfo *TSI = Ctx.getTrivialTypeSourceInfo(UIntTy);694ExprResult CastedExpr =695S.BuildCStyleCastExpr(SourceLocation(), TSI, SourceLocation(), E);696assert(!CastedExpr.isInvalid() && "Cannot create cstyle cast expr");697Args.push_back(CastedExpr.get());698}699700void HandlePtrType(const Type *Ty) {701TypeSourceInfo *TSI = Ctx.getTrivialTypeSourceInfo(Ctx.VoidPtrTy);702ExprResult CastedExpr =703S.BuildCStyleCastExpr(SourceLocation(), TSI, SourceLocation(), E);704assert(!CastedExpr.isInvalid() && "Can not create cstyle cast expression");705Args.push_back(CastedExpr.get());706}707};708709class InProcessRuntimeInterfaceBuilder : public RuntimeInterfaceBuilder {710Interpreter &Interp;711ASTContext &Ctx;712Sema &S;713714public:715InProcessRuntimeInterfaceBuilder(Interpreter &Interp, ASTContext &C, Sema &S)716: Interp(Interp), Ctx(C), S(S) {}717718TransformExprFunction *getPrintValueTransformer() override {719return &transformForValuePrinting;720}721722private:723static ExprResult transformForValuePrinting(RuntimeInterfaceBuilder *Builder,724Expr *E,725ArrayRef<Expr *> FixedArgs) {726auto *B = static_cast<InProcessRuntimeInterfaceBuilder *>(Builder);727728// Get rid of ExprWithCleanups.729if (auto *EWC = llvm::dyn_cast_if_present<ExprWithCleanups>(E))730E = EWC->getSubExpr();731732InterfaceKindVisitor Visitor(B->Ctx, B->S, E);733734// The Interpreter* parameter and the out parameter `OutVal`.735for (Expr *E : FixedArgs)736Visitor.Args.push_back(E);737738QualType Ty = E->getType();739QualType DesugaredTy = Ty.getDesugaredType(B->Ctx);740741// For lvalue struct, we treat it as a reference.742if (DesugaredTy->isRecordType() && E->isLValue()) {743DesugaredTy = B->Ctx.getLValueReferenceType(DesugaredTy);744Ty = B->Ctx.getLValueReferenceType(Ty);745}746747Expr *TypeArg = CStyleCastPtrExpr(B->S, B->Ctx.VoidPtrTy,748(uintptr_t)Ty.getAsOpaquePtr());749// The QualType parameter `OpaqueType`, represented as `void*`.750Visitor.Args.push_back(TypeArg);751752// We push the last parameter based on the type of the Expr. Note we need753// special care for rvalue struct.754Interpreter::InterfaceKind Kind = Visitor.Visit(&*DesugaredTy);755switch (Kind) {756case Interpreter::InterfaceKind::WithAlloc:757case Interpreter::InterfaceKind::CopyArray: {758// __clang_Interpreter_SetValueWithAlloc.759ExprResult AllocCall = B->S.ActOnCallExpr(760/*Scope=*/nullptr,761B->Interp762.getValuePrintingInfo()[Interpreter::InterfaceKind::WithAlloc],763E->getBeginLoc(), Visitor.Args, E->getEndLoc());764assert(!AllocCall.isInvalid() && "Can't create runtime interface call!");765766TypeSourceInfo *TSI =767B->Ctx.getTrivialTypeSourceInfo(Ty, SourceLocation());768769// Force CodeGen to emit destructor.770if (auto *RD = Ty->getAsCXXRecordDecl()) {771auto *Dtor = B->S.LookupDestructor(RD);772Dtor->addAttr(UsedAttr::CreateImplicit(B->Ctx));773B->Interp.getCompilerInstance()->getASTConsumer().HandleTopLevelDecl(774DeclGroupRef(Dtor));775}776777// __clang_Interpreter_SetValueCopyArr.778if (Kind == Interpreter::InterfaceKind::CopyArray) {779const auto *ConstantArrTy =780cast<ConstantArrayType>(DesugaredTy.getTypePtr());781size_t ArrSize = B->Ctx.getConstantArrayElementCount(ConstantArrTy);782Expr *ArrSizeExpr = IntegerLiteralExpr(B->Ctx, ArrSize);783Expr *Args[] = {E, AllocCall.get(), ArrSizeExpr};784return B->S.ActOnCallExpr(785/*Scope *=*/nullptr,786B->Interp787.getValuePrintingInfo()[Interpreter::InterfaceKind::CopyArray],788SourceLocation(), Args, SourceLocation());789}790Expr *Args[] = {791AllocCall.get(),792B->Interp.getValuePrintingInfo()[Interpreter::InterfaceKind::NewTag]};793ExprResult CXXNewCall = B->S.BuildCXXNew(794E->getSourceRange(),795/*UseGlobal=*/true, /*PlacementLParen=*/SourceLocation(), Args,796/*PlacementRParen=*/SourceLocation(),797/*TypeIdParens=*/SourceRange(), TSI->getType(), TSI, std::nullopt,798E->getSourceRange(), E);799800assert(!CXXNewCall.isInvalid() &&801"Can't create runtime placement new call!");802803return B->S.ActOnFinishFullExpr(CXXNewCall.get(),804/*DiscardedValue=*/false);805}806// __clang_Interpreter_SetValueNoAlloc.807case Interpreter::InterfaceKind::NoAlloc: {808return B->S.ActOnCallExpr(809/*Scope=*/nullptr,810B->Interp.getValuePrintingInfo()[Interpreter::InterfaceKind::NoAlloc],811E->getBeginLoc(), Visitor.Args, E->getEndLoc());812}813default:814llvm_unreachable("Unhandled Interpreter::InterfaceKind");815}816}817};818} // namespace819820static std::unique_ptr<RuntimeInterfaceBuilder>821createInProcessRuntimeInterfaceBuilder(Interpreter &Interp, ASTContext &Ctx,822Sema &S) {823return std::make_unique<InProcessRuntimeInterfaceBuilder>(Interp, Ctx, S);824}825826// This synthesizes a call expression to a speciall827// function that is responsible for generating the Value.828// In general, we transform:829// clang-repl> x830// To:831// // 1. If x is a built-in type like int, float.832// __clang_Interpreter_SetValueNoAlloc(ThisInterp, OpaqueValue, xQualType, x);833// // 2. If x is a struct, and a lvalue.834// __clang_Interpreter_SetValueNoAlloc(ThisInterp, OpaqueValue, xQualType,835// &x);836// // 3. If x is a struct, but a rvalue.837// new (__clang_Interpreter_SetValueWithAlloc(ThisInterp, OpaqueValue,838// xQualType)) (x);839840Expr *Interpreter::SynthesizeExpr(Expr *E) {841Sema &S = getCompilerInstance()->getSema();842ASTContext &Ctx = S.getASTContext();843844if (!RuntimeIB) {845RuntimeIB = FindRuntimeInterface();846AddPrintValueCall = RuntimeIB->getPrintValueTransformer();847}848849assert(AddPrintValueCall &&850"We don't have a runtime interface for pretty print!");851852// Create parameter `ThisInterp`.853auto *ThisInterp = CStyleCastPtrExpr(S, Ctx.VoidPtrTy, (uintptr_t)this);854855// Create parameter `OutVal`.856auto *OutValue = CStyleCastPtrExpr(S, Ctx.VoidPtrTy, (uintptr_t)&LastValue);857858// Build `__clang_Interpreter_SetValue*` call.859ExprResult Result =860AddPrintValueCall(RuntimeIB.get(), E, {ThisInterp, OutValue});861862// It could fail, like printing an array type in C. (not supported)863if (Result.isInvalid())864return E;865return Result.get();866}867868// Temporary rvalue struct that need special care.869REPL_EXTERNAL_VISIBILITY void *870__clang_Interpreter_SetValueWithAlloc(void *This, void *OutVal,871void *OpaqueType) {872Value &VRef = *(Value *)OutVal;873VRef = Value(static_cast<Interpreter *>(This), OpaqueType);874return VRef.getPtr();875}876877extern "C" void REPL_EXTERNAL_VISIBILITY __clang_Interpreter_SetValueNoAlloc(878void *This, void *OutVal, void *OpaqueType, ...) {879Value &VRef = *(Value *)OutVal;880Interpreter *I = static_cast<Interpreter *>(This);881VRef = Value(I, OpaqueType);882if (VRef.isVoid())883return;884885va_list args;886va_start(args, /*last named param*/ OpaqueType);887888QualType QT = VRef.getType();889if (VRef.getKind() == Value::K_PtrOrObj) {890VRef.setPtr(va_arg(args, void *));891} else {892if (const auto *ET = QT->getAs<EnumType>())893QT = ET->getDecl()->getIntegerType();894switch (QT->castAs<BuiltinType>()->getKind()) {895default:896llvm_unreachable("unknown type kind!");897break;898// Types shorter than int are resolved as int, else va_arg has UB.899case BuiltinType::Bool:900VRef.setBool(va_arg(args, int));901break;902case BuiltinType::Char_S:903VRef.setChar_S(va_arg(args, int));904break;905case BuiltinType::SChar:906VRef.setSChar(va_arg(args, int));907break;908case BuiltinType::Char_U:909VRef.setChar_U(va_arg(args, unsigned));910break;911case BuiltinType::UChar:912VRef.setUChar(va_arg(args, unsigned));913break;914case BuiltinType::Short:915VRef.setShort(va_arg(args, int));916break;917case BuiltinType::UShort:918VRef.setUShort(va_arg(args, unsigned));919break;920case BuiltinType::Int:921VRef.setInt(va_arg(args, int));922break;923case BuiltinType::UInt:924VRef.setUInt(va_arg(args, unsigned));925break;926case BuiltinType::Long:927VRef.setLong(va_arg(args, long));928break;929case BuiltinType::ULong:930VRef.setULong(va_arg(args, unsigned long));931break;932case BuiltinType::LongLong:933VRef.setLongLong(va_arg(args, long long));934break;935case BuiltinType::ULongLong:936VRef.setULongLong(va_arg(args, unsigned long long));937break;938// Types shorter than double are resolved as double, else va_arg has UB.939case BuiltinType::Float:940VRef.setFloat(va_arg(args, double));941break;942case BuiltinType::Double:943VRef.setDouble(va_arg(args, double));944break;945case BuiltinType::LongDouble:946VRef.setLongDouble(va_arg(args, long double));947break;948// See REPL_BUILTIN_TYPES.949}950}951va_end(args);952}953954// A trampoline to work around the fact that operator placement new cannot955// really be forward declared due to libc++ and libstdc++ declaration mismatch.956// FIXME: __clang_Interpreter_NewTag is ODR violation because we get the same957// definition in the interpreter runtime. We should move it in a runtime header958// which gets included by the interpreter and here.959struct __clang_Interpreter_NewTag {};960REPL_EXTERNAL_VISIBILITY void *961operator new(size_t __sz, void *__p, __clang_Interpreter_NewTag) noexcept {962// Just forward to the standard operator placement new.963return operator new(__sz, __p);964}965966967