//===------ Interpreter.cpp - Incremental Compilation and Execution -------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file implements the component which performs incremental code // compilation and execution. // //===----------------------------------------------------------------------===// #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" #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/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/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 namespace { /// Retrieves the clang CC1 specific flags out of the compilation's jobs. /// \returns NULL on error. static llvm::Expected GetCC1Arguments(DiagnosticsEngine *Diagnostics, driver::Compilation *Compilation) { // We expect to get back exactly one Command job, if we didn't something // failed. Extract that job from the Compilation. const driver::JobList &Jobs = Compilation->getJobs(); if (!Jobs.size() || !isa(*Jobs.begin())) 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(&(*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> CreateCI(const llvm::opt::ArgStringList &Argv) { std::unique_ptr Clang(new CompilerInstance()); IntrusiveRefCntPtr DiagID(new DiagnosticIDs()); // Register the support for object-file-wrapped Clang modules. // FIXME: Clang should register these container operations automatically. auto PCHOps = Clang->getPCHContainerOperations(); PCHOps->registerWriter(std::make_unique()); PCHOps->registerReader(std::make_unique()); // Buffer diagnostics from argument parsing so that we can output them using // a well formed diagnostic object. IntrusiveRefCntPtr DiagOpts = new DiagnosticOptions(); TextDiagnosticBuffer *DiagsBuffer = new TextDiagnosticBuffer; DiagnosticsEngine Diags(DiagID, &*DiagOpts, DiagsBuffer); bool Success = CompilerInvocation::CreateFromArgs( Clang->getInvocation(), llvm::ArrayRef(Argv.begin(), Argv.size()), Diags); // Infer the builtin include path if unspecified. if (Clang->getHeaderSearchOpts().UseBuiltinIncludes && Clang->getHeaderSearchOpts().ResourceDir.empty()) Clang->getHeaderSearchOpts().ResourceDir = CompilerInvocation::GetResourcesPath(Argv[0], nullptr); // Create the actual diagnostics engine. 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"); // FIXME: Merge with CompilerInstance::ExecuteAction. 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()); // 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); } } // anonymous namespace llvm::Expected> IncrementalCompilerBuilder::create(std::vector &ClangArgv) { // If we don't know ClangArgv0 or the address of main() at this point, try // to guess it anyway (it's possible on some platforms). std::string MainExecutableName = llvm::sys::fs::getMainExecutable(nullptr, nullptr); ClangArgv.insert(ClangArgv.begin(), MainExecutableName.c_str()); // Prepending -c to force the driver to do something if no action was // 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. // 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 >>>"); // Buffer diagnostics from argument parsing so that we can output them using a // well formed diagnostic object. IntrusiveRefCntPtr DiagID(new DiagnosticIDs()); IntrusiveRefCntPtr DiagOpts = CreateAndPopulateDiagOpts(ClangArgv); TextDiagnosticBuffer *DiagsBuffer = new TextDiagnosticBuffer; DiagnosticsEngine Diags(DiagID, &*DiagOpts, DiagsBuffer); driver::Driver Driver(/*MainBinaryName=*/ClangArgv[0], llvm::sys::getProcessTriple(), Diags); Driver.setCheckInputsExist(false); // the input comes from mem buffers llvm::ArrayRef RF = llvm::ArrayRef(ClangArgv); std::unique_ptr Compilation(Driver.BuildCompilation(RF)); if (Compilation->getArgs().hasArg(driver::options::OPT_v)) Compilation->getJobs().Print(llvm::errs(), "\n", /*Quote=*/false); auto ErrOrCC1Args = GetCC1Arguments(&Diags, Compilation.get()); if (auto Err = ErrOrCC1Args.takeError()) return std::move(Err); return CreateCI(**ErrOrCC1Args); } llvm::Expected> IncrementalCompilerBuilder::CreateCpp() { std::vector 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> IncrementalCompilerBuilder::createCuda(bool device) { std::vector 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> IncrementalCompilerBuilder::CreateCudaDevice() { return IncrementalCompilerBuilder::createCuda(true); } llvm::Expected> IncrementalCompilerBuilder::CreateCudaHost() { return IncrementalCompilerBuilder::createCuda(false); } Interpreter::Interpreter(std::unique_ptr CI, llvm::Error &Err) { llvm::ErrorAsOutParameter EAO(&Err); auto LLVMCtx = std::make_unique(); TSCtx = std::make_unique(std::move(LLVMCtx)); IncrParser = std::make_unique(*this, std::move(CI), *TSCtx->getContext(), Err); } 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"( void* operator new(__SIZE_TYPE__, void* __p) noexcept; 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); template void __clang_Interpreter_SetValueCopyArr(T* Src, void* Placement, unsigned long Size) { for (auto Idx = 0; Idx < Size; ++Idx) new ((void*)(((T*)Placement) + Idx)) T(Src[Idx]); } template void __clang_Interpreter_SetValueCopyArr(const T (*Src)[N], void* Placement, unsigned long Size) { __clang_Interpreter_SetValueCopyArr(Src[0], Placement, Size); } )"; llvm::Expected> Interpreter::create(std::unique_ptr CI) { llvm::Error Err = llvm::Error::success(); auto Interp = std::unique_ptr(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(3); // 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> Interpreter::createWithCUDA(std::unique_ptr CI, std::unique_ptr DCI) { // avoid writing fat binary to disk using an in-memory virtual file system llvm::IntrusiveRefCntPtr IMVFS = std::make_unique(); llvm::IntrusiveRefCntPtr OverlayVFS = std::make_unique( 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( **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 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 &PTUs = IncrParser->getPTUs(); assert(PTUs.size() >= InitPTUSize && "empty PTU list?"); return PTUs.size() - InitPTUSize; } llvm::Expected 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(*TSCtx, Err, TI); if (!Err) IncrExecutor = std::move(Executor); return Err; } llvm::Error Interpreter::Execute(PartialTranslationUnit &T) { assert(T.TheModule); if (!IncrExecutor) { 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(T)) return Err; if (auto Err = IncrExecutor->runCtors()) return Err; 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 Interpreter::getSymbolAddress(GlobalDecl GD) const { if (!IncrExecutor) return llvm::make_error("Operation failed. " "No execution engine", std::error_code()); llvm::StringRef MangledName = IncrParser->GetMangledName(GD); return getSymbolAddress(MangledName); } llvm::Expected Interpreter::getSymbolAddress(llvm::StringRef IRName) const { if (!IncrExecutor) return llvm::make_error("Operation failed. " "No execution engine", std::error_code()); return IncrExecutor->getSymbolAddress(IRName, IncrementalExecutor::IRName); } llvm::Expected Interpreter::getSymbolAddressFromLinkerName(llvm::StringRef Name) const { if (!IncrExecutor) return llvm::make_error("Operation failed. " "No execution engine", std::error_code()); return IncrExecutor->getSymbolAddress(Name, IncrementalExecutor::LinkerName); } llvm::Error Interpreter::Undo(unsigned N) { std::list &PTUs = IncrParser->getPTUs(); if (N > getEffectivePTUSize()) return llvm::make_error("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 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"}; 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; return true; } namespace { class RuntimeInterfaceBuilder : public TypeVisitor { clang::Interpreter &Interp; ASTContext &Ctx; Sema &S; Expr *E; llvm::SmallVector Args; public: RuntimeInterfaceBuilder(clang::Interpreter &In, ASTContext &C, Sema &SemaRef, Expr *VE, ArrayRef 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(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(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()}; 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()); } } 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(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(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(This), OpaqueType); } static void SetValueDataBasedOnQualType(Value &V, unsigned long long Data) { QualType QT = V.getType(); if (const auto *ET = QT->getAs()) QT = ET->getDecl()->getIntegerType(); switch (QT->castAs()->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(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(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(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(This), OpaqueType); VRef.setLongDouble(Val); }