Author: Jun Zhang Date: 2023-05-19T13:40:44+08:00 New Revision: d71a4e02277a64a9dece591cdf2b34f15c3b19a0
URL: https://github.com/llvm/llvm-project/commit/d71a4e02277a64a9dece591cdf2b34f15c3b19a0 DIFF: https://github.com/llvm/llvm-project/commit/d71a4e02277a64a9dece591cdf2b34f15c3b19a0.diff LOG: Reland [clang-repl] Introduce Value to capture expression results This reverts commit 7158fd381a0bc0222195d6a07ebb42ea57957bda. * Fixes endianness issue on big endian machines like PowerPC-bl * Disable tests on platforms that having trouble to support JIT Signed-off-by: Jun Zhang <j...@junz.org> Added: clang/include/clang/Interpreter/Value.h clang/lib/Interpreter/InterpreterUtils.cpp clang/lib/Interpreter/InterpreterUtils.h clang/lib/Interpreter/Value.cpp Modified: clang/include/clang/Interpreter/Interpreter.h clang/lib/Interpreter/CMakeLists.txt clang/lib/Interpreter/IncrementalParser.cpp clang/lib/Interpreter/IncrementalParser.h clang/lib/Interpreter/Interpreter.cpp clang/tools/clang-repl/CMakeLists.txt clang/unittests/Interpreter/CMakeLists.txt clang/unittests/Interpreter/InterpreterTest.cpp Removed: ################################################################################ diff --git a/clang/include/clang/Interpreter/Interpreter.h b/clang/include/clang/Interpreter/Interpreter.h index b3d64458d777c..e680218452d1c 100644 --- a/clang/include/clang/Interpreter/Interpreter.h +++ b/clang/include/clang/Interpreter/Interpreter.h @@ -14,14 +14,15 @@ #ifndef LLVM_CLANG_INTERPRETER_INTERPRETER_H #define LLVM_CLANG_INTERPRETER_INTERPRETER_H -#include "clang/Interpreter/PartialTranslationUnit.h" - +#include "clang/AST/Decl.h" #include "clang/AST/GlobalDecl.h" +#include "clang/Interpreter/PartialTranslationUnit.h" +#include "clang/Interpreter/Value.h" +#include "llvm/ADT/DenseMap.h" #include "llvm/ExecutionEngine/JITSymbol.h" #include "llvm/ExecutionEngine/Orc/Shared/ExecutorAddress.h" #include "llvm/Support/Error.h" - #include <memory> #include <vector> @@ -54,24 +55,26 @@ class Interpreter { Interpreter(std::unique_ptr<CompilerInstance> CI, llvm::Error &Err); llvm::Error CreateExecutor(); + unsigned InitPTUSize = 0; + + // This member holds the last result of the value printing. It's a class + // member because we might want to access it after more inputs. If no value + // printing happens, it's in an invalid state. + Value LastValue; public: ~Interpreter(); static llvm::Expected<std::unique_ptr<Interpreter>> create(std::unique_ptr<CompilerInstance> CI); + const ASTContext &getASTContext() const; + ASTContext &getASTContext(); const CompilerInstance *getCompilerInstance() const; llvm::Expected<llvm::orc::LLJIT &> getExecutionEngine(); llvm::Expected<PartialTranslationUnit &> Parse(llvm::StringRef Code); llvm::Error Execute(PartialTranslationUnit &T); - llvm::Error ParseAndExecute(llvm::StringRef Code) { - auto PTU = Parse(Code); - if (!PTU) - return PTU.takeError(); - if (PTU->TheModule) - return Execute(*PTU); - return llvm::Error::success(); - } + llvm::Error ParseAndExecute(llvm::StringRef Code, Value *V = nullptr); + llvm::Expected<llvm::orc::ExecutorAddr> CompileDtorCall(CXXRecordDecl *CXXRD); /// Undo N previous incremental inputs. llvm::Error Undo(unsigned N = 1); @@ -92,6 +95,23 @@ class Interpreter { /// file. llvm::Expected<llvm::orc::ExecutorAddr> getSymbolAddressFromLinkerName(llvm::StringRef LinkerName) const; + + enum InterfaceKind { NoAlloc, WithAlloc, CopyArray }; + + const llvm::SmallVectorImpl<Expr *> &getValuePrintingInfo() const { + return ValuePrintingInfo; + } + + Expr *SynthesizeExpr(Expr *E); + +private: + size_t getEffectivePTUSize() const; + + bool FindRuntimeInterface(); + + llvm::DenseMap<CXXRecordDecl *, llvm::orc::ExecutorAddr> Dtors; + + llvm::SmallVector<Expr *, 3> ValuePrintingInfo; }; } // namespace clang diff --git a/clang/include/clang/Interpreter/Value.h b/clang/include/clang/Interpreter/Value.h new file mode 100644 index 0000000000000..e75a90505154a --- /dev/null +++ b/clang/include/clang/Interpreter/Value.h @@ -0,0 +1,200 @@ +//===--- Value.h - Definition of interpreter value --------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// Value is a lightweight struct that is used for carrying execution results in +// clang-repl. It's a special runtime that acts like a messager between compiled +// code and interpreted code. This makes it possible to exchange interesting +// information between the compiled & interpreted world. +// +// A typical usage is like the below: +// +// Value V; +// Interp.ParseAndExecute("int x = 42;"); +// Interp.ParseAndExecute("x", &V); +// V.getType(); // <-- Yields a clang::QualType. +// V.getInt(); // <-- Yields 42. +// +// The current design is still highly experimental and nobody should rely on the +// API being stable because we're hopefully going to make significant changes to +// it in the relatively near future. For example, Value also intends to be used +// as an exchange token for JIT support enabling remote execution on the embed +// devices where the JIT infrastructure cannot fit. To support that we will need +// to split the memory storage in a diff erent place and perhaps add a resource +// header is similar to intrinsics headers which have stricter performance +// constraints. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_INTERPRETER_VALUE_H +#define LLVM_CLANG_INTERPRETER_VALUE_H + +#include <cstdint> +// NOTE: Since the REPL itself could also include this runtime, extreme caution +// should be taken when MAKING CHANGES to this file, especially when INCLUDE NEW +// HEADERS, like <string>, <memory> and etc. (That pulls a large number of +// tokens and will impact the runtime performance of the REPL) + +namespace llvm { +class raw_ostream; + +} // namespace llvm + +namespace clang { + +class ASTContext; +class Interpreter; +class QualType; + +#if __has_attribute(visibility) && \ + (!(defined(_WIN32) || defined(__CYGWIN__)) || \ + (defined(__MINGW32__) && defined(__clang__))) +#if defined(LLVM_BUILD_LLVM_DYLIB) || defined(LLVM_BUILD_SHARED_LIBS) +#define REPL_EXTERNAL_VISIBILITY __attribute__((visibility("default"))) +#else +#define REPL_EXTERNAL_VISIBILITY +#endif +#else +#if defined(_WIN32) +#define REPL_EXTERNAL_VISIBILITY __declspec(dllexport) +#endif +#endif + +#define REPL_BUILTIN_TYPES \ + X(bool, Bool) \ + X(char, Char_S) \ + X(signed char, SChar) \ + X(unsigned char, UChar) \ + X(short, Short) \ + X(unsigned short, UShort) \ + X(int, Int) \ + X(unsigned int, UInt) \ + X(long, Long) \ + X(unsigned long, ULong) \ + X(long long, LongLong) \ + X(unsigned long long, ULongLong) \ + X(float, Float) \ + X(double, Double) \ + X(long double, LongDouble) + +class REPL_EXTERNAL_VISIBILITY Value { + union Storage { +#define X(type, name) type m_##name; + REPL_BUILTIN_TYPES +#undef X + void *m_Ptr; + }; + +public: + enum Kind { +#define X(type, name) K_##name, + REPL_BUILTIN_TYPES +#undef X + + K_Void, + K_PtrOrObj, + K_Unspecified + }; + + Value() = default; + Value(Interpreter *In, void *Ty); + Value(const Value &RHS); + Value(Value &&RHS) noexcept; + Value &operator=(const Value &RHS); + Value &operator=(Value &&RHS) noexcept; + ~Value(); + + void printType(llvm::raw_ostream &Out) const; + void printData(llvm::raw_ostream &Out) const; + void print(llvm::raw_ostream &Out) const; + void dump() const; + void clear(); + + ASTContext &getASTContext(); + const ASTContext &getASTContext() const; + Interpreter &getInterpreter(); + const Interpreter &getInterpreter() const; + QualType getType() const; + + bool isValid() const { return ValueKind != K_Unspecified; } + bool isVoid() const { return ValueKind == K_Void; } + bool hasValue() const { return isValid() && !isVoid(); } + bool isManuallyAlloc() const { return IsManuallyAlloc; } + Kind getKind() const { return ValueKind; } + void setKind(Kind K) { ValueKind = K; } + void setOpaqueType(void *Ty) { OpaqueType = Ty; } + + void *getPtr() const; + void setPtr(void *Ptr) { Data.m_Ptr = Ptr; } + +#define X(type, name) \ + void set##name(type Val) { Data.m_##name = Val; } \ + type get##name() const { return Data.m_##name; } + REPL_BUILTIN_TYPES +#undef X + + /// \brief Get the value with cast. + // + /// Get the value cast to T. This is similar to reinterpret_cast<T>(value), + /// casting the value of builtins (except void), enums and pointers. + /// Values referencing an object are treated as pointers to the object. + template <typename T> T convertTo() const { + return convertFwd<T>::cast(*this); + } + +protected: + bool isPointerOrObjectType() const { return ValueKind == K_PtrOrObj; } + + /// \brief Get to the value with type checking casting the underlying + /// stored value to T. + template <typename T> T as() const { + switch (ValueKind) { + default: + return T(); +#define X(type, name) \ + case Value::K_##name: \ + return (T)Data.m_##name; + REPL_BUILTIN_TYPES +#undef X + } + } + + // Allow convertTo to be partially specialized. + template <typename T> struct convertFwd { + static T cast(const Value &V) { + if (V.isPointerOrObjectType()) + return (T)(uintptr_t)V.as<void *>(); + if (!V.isValid() || V.isVoid()) { + return T(); + } + return V.as<T>(); + } + }; + + template <typename T> struct convertFwd<T *> { + static T *cast(const Value &V) { + if (V.isPointerOrObjectType()) + return (T *)(uintptr_t)V.as<void *>(); + return nullptr; + } + }; + + Interpreter *Interp = nullptr; + void *OpaqueType = nullptr; + Storage Data; + Kind ValueKind = K_Unspecified; + bool IsManuallyAlloc = false; +}; + +template <> inline void *Value::as() const { + if (isPointerOrObjectType()) + return Data.m_Ptr; + return (void *)as<uintptr_t>(); +} + +} // namespace clang +#endif diff --git a/clang/lib/Interpreter/CMakeLists.txt b/clang/lib/Interpreter/CMakeLists.txt index 721864c0cc1ea..0df0ba6b8b856 100644 --- a/clang/lib/Interpreter/CMakeLists.txt +++ b/clang/lib/Interpreter/CMakeLists.txt @@ -14,6 +14,8 @@ add_clang_library(clangInterpreter IncrementalExecutor.cpp IncrementalParser.cpp Interpreter.cpp + InterpreterUtils.cpp + Value.cpp DEPENDS intrinsics_gen diff --git a/clang/lib/Interpreter/IncrementalParser.cpp b/clang/lib/Interpreter/IncrementalParser.cpp index 2b932623a5fea..e431890712258 100644 --- a/clang/lib/Interpreter/IncrementalParser.cpp +++ b/clang/lib/Interpreter/IncrementalParser.cpp @@ -11,7 +11,6 @@ //===----------------------------------------------------------------------===// #include "IncrementalParser.h" - #include "clang/AST/DeclContextInternals.h" #include "clang/CodeGen/BackendUtil.h" #include "clang/CodeGen/CodeGenAction.h" @@ -19,9 +18,9 @@ #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/FrontendAction.h" #include "clang/FrontendTool/Utils.h" +#include "clang/Interpreter/Interpreter.h" #include "clang/Parse/Parser.h" #include "clang/Sema/Sema.h" - #include "llvm/Option/ArgList.h" #include "llvm/Support/CrashRecoveryContext.h" #include "llvm/Support/Error.h" @@ -31,6 +30,79 @@ namespace clang { +class IncrementalASTConsumer final : public ASTConsumer { + Interpreter &Interp; + std::unique_ptr<ASTConsumer> Consumer; + +public: + IncrementalASTConsumer(Interpreter &InterpRef, std::unique_ptr<ASTConsumer> C) + : Interp(InterpRef), Consumer(std::move(C)) {} + + bool HandleTopLevelDecl(DeclGroupRef DGR) override final { + if (DGR.isNull()) + return true; + if (!Consumer) + return true; + + for (Decl *D : DGR) + if (auto *TSD = llvm::dyn_cast<TopLevelStmtDecl>(D); + TSD && TSD->isSemiMissing()) + TSD->setStmt(Interp.SynthesizeExpr(cast<Expr>(TSD->getStmt()))); + + return Consumer->HandleTopLevelDecl(DGR); + } + void HandleTranslationUnit(ASTContext &Ctx) override final { + Consumer->HandleTranslationUnit(Ctx); + } + void HandleInlineFunctionDefinition(FunctionDecl *D) override final { + Consumer->HandleInlineFunctionDefinition(D); + } + void HandleInterestingDecl(DeclGroupRef D) override final { + Consumer->HandleInterestingDecl(D); + } + void HandleTagDeclDefinition(TagDecl *D) override final { + Consumer->HandleTagDeclDefinition(D); + } + void HandleTagDeclRequiredDefinition(const TagDecl *D) override final { + Consumer->HandleTagDeclRequiredDefinition(D); + } + void HandleCXXImplicitFunctionInstantiation(FunctionDecl *D) override final { + Consumer->HandleCXXImplicitFunctionInstantiation(D); + } + void HandleTopLevelDeclInObjCContainer(DeclGroupRef D) override final { + Consumer->HandleTopLevelDeclInObjCContainer(D); + } + void HandleImplicitImportDecl(ImportDecl *D) override final { + Consumer->HandleImplicitImportDecl(D); + } + void CompleteTentativeDefinition(VarDecl *D) override final { + Consumer->CompleteTentativeDefinition(D); + } + void CompleteExternalDeclaration(VarDecl *D) override final { + Consumer->CompleteExternalDeclaration(D); + } + void AssignInheritanceModel(CXXRecordDecl *RD) override final { + Consumer->AssignInheritanceModel(RD); + } + void HandleCXXStaticMemberVarInstantiation(VarDecl *D) override final { + Consumer->HandleCXXStaticMemberVarInstantiation(D); + } + void HandleVTable(CXXRecordDecl *RD) override final { + Consumer->HandleVTable(RD); + } + ASTMutationListener *GetASTMutationListener() override final { + return Consumer->GetASTMutationListener(); + } + ASTDeserializationListener *GetASTDeserializationListener() override final { + return Consumer->GetASTDeserializationListener(); + } + void PrintStats() override final { Consumer->PrintStats(); } + bool shouldSkipFunctionBody(Decl *D) override final { + return Consumer->shouldSkipFunctionBody(D); + } + static bool classof(const clang::ASTConsumer *) { return true; } +}; + /// A custom action enabling the incremental processing functionality. /// /// The usual \p FrontendAction expects one call to ExecuteAction and once it @@ -122,7 +194,8 @@ class IncrementalAction : public WrapperFrontendAction { } }; -IncrementalParser::IncrementalParser(std::unique_ptr<CompilerInstance> Instance, +IncrementalParser::IncrementalParser(Interpreter &Interp, + std::unique_ptr<CompilerInstance> Instance, llvm::LLVMContext &LLVMCtx, llvm::Error &Err) : CI(std::move(Instance)) { @@ -131,6 +204,9 @@ IncrementalParser::IncrementalParser(std::unique_ptr<CompilerInstance> Instance, if (Err) return; CI->ExecuteAction(*Act); + std::unique_ptr<ASTConsumer> IncrConsumer = + std::make_unique<IncrementalASTConsumer>(Interp, CI->takeASTConsumer()); + CI->setASTConsumer(std::move(IncrConsumer)); Consumer = &CI->getASTConsumer(); P.reset( new Parser(CI->getPreprocessor(), CI->getSema(), /*SkipBodies=*/false)); @@ -267,17 +343,22 @@ IncrementalParser::Parse(llvm::StringRef input) { "Lexer must be EOF when starting incremental parse!"); } - if (CodeGenerator *CG = getCodeGen(Act.get())) { - std::unique_ptr<llvm::Module> M(CG->ReleaseModule()); - CG->StartModule("incr_module_" + std::to_string(PTUs.size()), - M->getContext()); - + if (std::unique_ptr<llvm::Module> M = GenModule()) PTU->TheModule = std::move(M); - } return PTU; } +std::unique_ptr<llvm::Module> IncrementalParser::GenModule() { + static unsigned ID = 0; + if (CodeGenerator *CG = getCodeGen(Act.get())) { + std::unique_ptr<llvm::Module> M(CG->ReleaseModule()); + CG->StartModule("incr_module_" + std::to_string(ID++), M->getContext()); + return M; + } + return nullptr; +} + void IncrementalParser::CleanUpPTU(PartialTranslationUnit &PTU) { TranslationUnitDecl *MostRecentTU = PTU.TUPart; TranslationUnitDecl *FirstTU = MostRecentTU->getFirstDecl(); diff --git a/clang/lib/Interpreter/IncrementalParser.h b/clang/lib/Interpreter/IncrementalParser.h index 8e45d6b5931bc..99e37588df9db 100644 --- a/clang/lib/Interpreter/IncrementalParser.h +++ b/clang/lib/Interpreter/IncrementalParser.h @@ -16,7 +16,6 @@ #include "clang/Interpreter/PartialTranslationUnit.h" #include "clang/AST/GlobalDecl.h" - #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Error.h" @@ -31,8 +30,8 @@ namespace clang { class ASTConsumer; class CompilerInstance; class IncrementalAction; +class Interpreter; class Parser; - /// Provides support for incremental compilation. Keeps track of the state /// changes between the subsequent incremental input. /// @@ -57,7 +56,8 @@ class IncrementalParser { std::list<PartialTranslationUnit> PTUs; public: - IncrementalParser(std::unique_ptr<CompilerInstance> Instance, + IncrementalParser(Interpreter &Interp, + std::unique_ptr<CompilerInstance> Instance, llvm::LLVMContext &LLVMCtx, llvm::Error &Err); ~IncrementalParser(); @@ -76,6 +76,8 @@ class IncrementalParser { std::list<PartialTranslationUnit> &getPTUs() { return PTUs; } + std::unique_ptr<llvm::Module> GenModule(); + private: llvm::Expected<PartialTranslationUnit &> ParseOrWrapTopLevelDecl(); }; diff --git a/clang/lib/Interpreter/Interpreter.cpp b/clang/lib/Interpreter/Interpreter.cpp index 24fb9da69a8bc..bf0c98785ab79 100644 --- a/clang/lib/Interpreter/Interpreter.cpp +++ b/clang/lib/Interpreter/Interpreter.cpp @@ -16,7 +16,11 @@ #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/ModuleBuilder.h" #include "clang/CodeGen/ObjectFilePCHContainerOperations.h" @@ -27,13 +31,16 @@ #include "clang/Driver/Tool.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/TextDiagnosticBuffer.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 @@ -177,7 +184,7 @@ Interpreter::Interpreter(std::unique_ptr<CompilerInstance> CI, 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); } @@ -190,6 +197,29 @@ Interpreter::~Interpreter() { } } +// 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 <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)) 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); + } +)"; + llvm::Expected<std::unique_ptr<Interpreter>> Interpreter::create(std::unique_ptr<CompilerInstance> CI) { llvm::Error Err = llvm::Error::success(); @@ -197,6 +227,14 @@ Interpreter::create(std::unique_ptr<CompilerInstance> CI) { std::unique_ptr<Interpreter>(new Interpreter(std::move(CI), Err)); if (Err) return std::move(Err); + if (llvm::Error Err = Interp->ParseAndExecute(Runtimes)) + return std::move(Err); + + 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); } @@ -213,8 +251,26 @@ llvm::Expected<llvm::orc::LLJIT &> Interpreter::getExecutionEngine() { 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) { + // 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); } @@ -246,6 +302,25 @@ 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) @@ -279,7 +354,7 @@ Interpreter::getSymbolAddressFromLinkerName(llvm::StringRef Name) const { llvm::Error Interpreter::Undo(unsigned N) { std::list<PartialTranslationUnit> &PTUs = IncrParser->getPTUs(); - if (N > PTUs.size()) + if (N > getEffectivePTUSize()) return llvm::make_error<llvm::StringError>("Operation failed. " "Too many undos", std::error_code()); @@ -310,3 +385,342 @@ llvm::Error Interpreter::LoadDynamicLibrary(const char *name) { 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"}; + +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<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()}; + 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()); + } + } + } + + 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->getAs<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); +} diff --git a/clang/lib/Interpreter/InterpreterUtils.cpp b/clang/lib/Interpreter/InterpreterUtils.cpp new file mode 100644 index 0000000000000..c19cf6aa3156c --- /dev/null +++ b/clang/lib/Interpreter/InterpreterUtils.cpp @@ -0,0 +1,111 @@ +//===--- InterpreterUtils.cpp - Incremental Utils --------*- C++ -*-===// +// +// 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 some common utils used in the incremental library. +// +//===----------------------------------------------------------------------===// + +#include "InterpreterUtils.h" + +namespace clang { + +IntegerLiteral *IntegerLiteralExpr(ASTContext &C, uint64_t Val) { + return IntegerLiteral::Create(C, llvm::APSInt::getUnsigned(Val), + C.UnsignedLongLongTy, SourceLocation()); +} + +Expr *CStyleCastPtrExpr(Sema &S, QualType Ty, Expr *E) { + ASTContext &Ctx = S.getASTContext(); + if (!Ty->isPointerType()) + Ty = Ctx.getPointerType(Ty); + + TypeSourceInfo *TSI = Ctx.getTrivialTypeSourceInfo(Ty, SourceLocation()); + Expr *Result = + S.BuildCStyleCastExpr(SourceLocation(), TSI, SourceLocation(), E).get(); + assert(Result && "Cannot create CStyleCastPtrExpr"); + return Result; +} + +Expr *CStyleCastPtrExpr(Sema &S, QualType Ty, uintptr_t Ptr) { + ASTContext &Ctx = S.getASTContext(); + return CStyleCastPtrExpr(S, Ty, IntegerLiteralExpr(Ctx, (uint64_t)Ptr)); +} + +Sema::DeclGroupPtrTy CreateDGPtrFrom(Sema &S, Decl *D) { + SmallVector<Decl *, 1> DeclsInGroup; + DeclsInGroup.push_back(D); + Sema::DeclGroupPtrTy DeclGroupPtr = S.BuildDeclaratorGroup(DeclsInGroup); + return DeclGroupPtr; +} + +NamespaceDecl *LookupNamespace(Sema &S, llvm::StringRef Name, + const DeclContext *Within) { + DeclarationName DName = &S.Context.Idents.get(Name); + LookupResult R(S, DName, SourceLocation(), + Sema::LookupNestedNameSpecifierName); + R.suppressDiagnostics(); + if (!Within) + S.LookupName(R, S.TUScope); + else { + if (const auto *TD = dyn_cast<clang::TagDecl>(Within); + TD && !TD->getDefinition()) + // No definition, no lookup result. + return nullptr; + + S.LookupQualifiedName(R, const_cast<DeclContext *>(Within)); + } + + if (R.empty()) + return nullptr; + + R.resolveKind(); + + return dyn_cast<NamespaceDecl>(R.getFoundDecl()); +} + +NamedDecl *LookupNamed(Sema &S, llvm::StringRef Name, + const DeclContext *Within) { + DeclarationName DName = &S.Context.Idents.get(Name); + LookupResult R(S, DName, SourceLocation(), Sema::LookupOrdinaryName, + Sema::ForVisibleRedeclaration); + + R.suppressDiagnostics(); + + if (!Within) + S.LookupName(R, S.TUScope); + else { + const DeclContext *PrimaryWithin = nullptr; + if (const auto *TD = dyn_cast<TagDecl>(Within)) + PrimaryWithin = llvm::dyn_cast_or_null<DeclContext>(TD->getDefinition()); + else + PrimaryWithin = Within->getPrimaryContext(); + + // No definition, no lookup result. + if (!PrimaryWithin) + return nullptr; + + S.LookupQualifiedName(R, const_cast<DeclContext *>(PrimaryWithin)); + } + + if (R.empty()) + return nullptr; + R.resolveKind(); + + if (R.isSingleResult()) + return llvm::dyn_cast<NamedDecl>(R.getFoundDecl()); + + return nullptr; +} + +std::string GetFullTypeName(ASTContext &Ctx, QualType QT) { + PrintingPolicy Policy(Ctx.getPrintingPolicy()); + Policy.SuppressScope = false; + Policy.AnonymousTagLocations = false; + return QT.getAsString(Policy); +} +} // namespace clang diff --git a/clang/lib/Interpreter/InterpreterUtils.h b/clang/lib/Interpreter/InterpreterUtils.h new file mode 100644 index 0000000000000..8df158c17d491 --- /dev/null +++ b/clang/lib/Interpreter/InterpreterUtils.h @@ -0,0 +1,54 @@ +//===--- InterpreterUtils.h - Incremental Utils --------*- C++ -*-===// +// +// 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 some common utils used in the incremental library. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_INTERPRETER_UTILS_H +#define LLVM_CLANG_INTERPRETER_UTILS_H + +#include "clang/AST/ASTContext.h" +#include "clang/AST/Mangle.h" +#include "clang/AST/TypeVisitor.h" +#include "clang/Basic/TargetInfo.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/Lex/PreprocessorOptions.h" + +#include "clang/Sema/Lookup.h" +#include "llvm/IR/Module.h" +#include "llvm/Support/Errc.h" +#include "llvm/TargetParser/Host.h" + +namespace clang { +IntegerLiteral *IntegerLiteralExpr(ASTContext &C, uint64_t Val); + +Expr *CStyleCastPtrExpr(Sema &S, QualType Ty, Expr *E); + +Expr *CStyleCastPtrExpr(Sema &S, QualType Ty, uintptr_t Ptr); + +Sema::DeclGroupPtrTy CreateDGPtrFrom(Sema &S, Decl *D); + +NamespaceDecl *LookupNamespace(Sema &S, llvm::StringRef Name, + const DeclContext *Within = nullptr); + +NamedDecl *LookupNamed(Sema &S, llvm::StringRef Name, + const DeclContext *Within); + +std::string GetFullTypeName(ASTContext &Ctx, QualType QT); +} // namespace clang + +#endif diff --git a/clang/lib/Interpreter/Value.cpp b/clang/lib/Interpreter/Value.cpp new file mode 100644 index 0000000000000..fe37eebac5257 --- /dev/null +++ b/clang/lib/Interpreter/Value.cpp @@ -0,0 +1,266 @@ +//===--- Interpreter.h - Incremental Compiation and Execution---*- C++ -*-===// +// +// 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 defines the class that used to represent a value in incremental +// C++. +// +//===----------------------------------------------------------------------===// + +#include "clang/Interpreter/Value.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Type.h" +#include "clang/Interpreter/Interpreter.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/raw_os_ostream.h" +#include <cassert> +#include <cstdint> +#include <utility> + +using namespace clang; + +namespace { + +// This is internal buffer maintained by Value, used to hold temporaries. +class ValueStorage { +public: + using DtorFunc = void (*)(void *); + + static unsigned char *CreatePayload(void *DtorF, size_t AllocSize, + size_t ElementsSize) { + if (AllocSize < sizeof(Canary)) + AllocSize = sizeof(Canary); + unsigned char *Buf = + new unsigned char[ValueStorage::getPayloadOffset() + AllocSize]; + ValueStorage *VS = new (Buf) ValueStorage(DtorF, AllocSize, ElementsSize); + std::memcpy(VS->getPayload(), Canary, sizeof(Canary)); + return VS->getPayload(); + } + + unsigned char *getPayload() { return Storage; } + const unsigned char *getPayload() const { return Storage; } + + static unsigned getPayloadOffset() { + static ValueStorage Dummy(nullptr, 0, 0); + return Dummy.getPayload() - reinterpret_cast<unsigned char *>(&Dummy); + } + + static ValueStorage *getFromPayload(void *Payload) { + ValueStorage *R = reinterpret_cast<ValueStorage *>( + (unsigned char *)Payload - getPayloadOffset()); + return R; + } + + void Retain() { ++RefCnt; } + + void Release() { + assert(RefCnt > 0 && "Can't release if reference count is already zero"); + if (--RefCnt == 0) { + // We hace a non-trivial dtor. + if (Dtor && IsAlive()) { + assert(Elements && "We at least should have 1 element in Value"); + size_t Stride = AllocSize / Elements; + for (size_t Idx = 0; Idx < Elements; ++Idx) + (*Dtor)(getPayload() + Idx * Stride); + } + delete[] reinterpret_cast<unsigned char *>(this); + } + } + + // Check whether the storage is valid by validating the canary bits. + // If someone accidentally write some invalid bits in the storage, the canary + // will be changed first, and `IsAlive` will return false then. + bool IsAlive() const { + return std::memcmp(getPayload(), Canary, sizeof(Canary)) != 0; + } + +private: + ValueStorage(void *DtorF, size_t AllocSize, size_t ElementsNum) + : RefCnt(1), Dtor(reinterpret_cast<DtorFunc>(DtorF)), + AllocSize(AllocSize), Elements(ElementsNum) {} + + mutable unsigned RefCnt; + DtorFunc Dtor = nullptr; + size_t AllocSize = 0; + size_t Elements = 0; + unsigned char Storage[1]; + + // These are some canary bits that are used for protecting the storage been + // damaged. + static constexpr unsigned char Canary[8] = {0x4c, 0x37, 0xad, 0x8f, + 0x2d, 0x23, 0x95, 0x91}; +}; +} // namespace + +static Value::Kind ConvertQualTypeToKind(const ASTContext &Ctx, QualType QT) { + if (Ctx.hasSameType(QT, Ctx.VoidTy)) + return Value::K_Void; + + if (const auto *ET = QT->getAs<EnumType>()) + QT = ET->getDecl()->getIntegerType(); + + const auto *BT = QT->getAs<BuiltinType>(); + if (!BT || BT->isNullPtrType()) + return Value::K_PtrOrObj; + + switch (QT->getAs<BuiltinType>()->getKind()) { + default: + assert(false && "Type not supported"); + return Value::K_Unspecified; +#define X(type, name) \ + case BuiltinType::name: \ + return Value::K_##name; + REPL_BUILTIN_TYPES +#undef X + } +} + +Value::Value(Interpreter *In, void *Ty) : Interp(In), OpaqueType(Ty) { + setKind(ConvertQualTypeToKind(getASTContext(), getType())); + if (ValueKind == K_PtrOrObj) { + QualType Canon = getType().getCanonicalType(); + if ((Canon->isPointerType() || Canon->isObjectType() || + Canon->isReferenceType()) && + (Canon->isRecordType() || Canon->isConstantArrayType() || + Canon->isMemberPointerType())) { + IsManuallyAlloc = true; + // Compile dtor function. + Interpreter &Interp = getInterpreter(); + void *DtorF = nullptr; + size_t ElementsSize = 1; + QualType DtorTy = getType(); + + if (const auto *ArrTy = + llvm::dyn_cast<ConstantArrayType>(DtorTy.getTypePtr())) { + DtorTy = ArrTy->getElementType(); + llvm::APInt ArrSize(sizeof(size_t) * 8, 1); + do { + ArrSize *= ArrTy->getSize(); + ArrTy = llvm::dyn_cast<ConstantArrayType>( + ArrTy->getElementType().getTypePtr()); + } while (ArrTy); + ElementsSize = static_cast<size_t>(ArrSize.getZExtValue()); + } + if (const auto *RT = DtorTy->getAs<RecordType>()) { + if (CXXRecordDecl *CXXRD = + llvm::dyn_cast<CXXRecordDecl>(RT->getDecl())) { + if (llvm::Expected<llvm::orc::ExecutorAddr> Addr = + Interp.CompileDtorCall(CXXRD)) + DtorF = reinterpret_cast<void *>(Addr->getValue()); + else + llvm::logAllUnhandledErrors(Addr.takeError(), llvm::errs()); + } + } + + size_t AllocSize = + getASTContext().getTypeSizeInChars(getType()).getQuantity(); + unsigned char *Payload = + ValueStorage::CreatePayload(DtorF, AllocSize, ElementsSize); + setPtr((void *)Payload); + } + } +} + +Value::Value(const Value &RHS) + : Interp(RHS.Interp), OpaqueType(RHS.OpaqueType), Data(RHS.Data), + ValueKind(RHS.ValueKind), IsManuallyAlloc(RHS.IsManuallyAlloc) { + if (IsManuallyAlloc) + ValueStorage::getFromPayload(getPtr())->Retain(); +} + +Value::Value(Value &&RHS) noexcept { + Interp = std::exchange(RHS.Interp, nullptr); + OpaqueType = std::exchange(RHS.OpaqueType, nullptr); + Data = RHS.Data; + ValueKind = std::exchange(RHS.ValueKind, K_Unspecified); + IsManuallyAlloc = std::exchange(RHS.IsManuallyAlloc, false); + + if (IsManuallyAlloc) + ValueStorage::getFromPayload(getPtr())->Release(); +} + +Value &Value::operator=(const Value &RHS) { + if (IsManuallyAlloc) + ValueStorage::getFromPayload(getPtr())->Release(); + + Interp = RHS.Interp; + OpaqueType = RHS.OpaqueType; + Data = RHS.Data; + ValueKind = RHS.ValueKind; + IsManuallyAlloc = RHS.IsManuallyAlloc; + + if (IsManuallyAlloc) + ValueStorage::getFromPayload(getPtr())->Retain(); + + return *this; +} + +Value &Value::operator=(Value &&RHS) noexcept { + if (IsManuallyAlloc) + ValueStorage::getFromPayload(getPtr())->Release(); + + Interp = std::exchange(RHS.Interp, nullptr); + OpaqueType = std::exchange(RHS.OpaqueType, nullptr); + ValueKind = std::exchange(RHS.ValueKind, K_Unspecified); + IsManuallyAlloc = std::exchange(RHS.IsManuallyAlloc, false); + + Data = RHS.Data; + + return *this; +} + +void Value::clear() { + if (IsManuallyAlloc) + ValueStorage::getFromPayload(getPtr())->Release(); + ValueKind = K_Unspecified; + OpaqueType = nullptr; + Interp = nullptr; + IsManuallyAlloc = false; +} + +Value::~Value() { clear(); } + +void *Value::getPtr() const { + assert(ValueKind == K_PtrOrObj); + return Data.m_Ptr; +} + +QualType Value::getType() const { + return QualType::getFromOpaquePtr(OpaqueType); +} + +Interpreter &Value::getInterpreter() { + assert(Interp != nullptr && + "Can't get interpreter from a default constructed value"); + return *Interp; +} + +const Interpreter &Value::getInterpreter() const { + assert(Interp != nullptr && + "Can't get interpreter from a default constructed value"); + return *Interp; +} + +ASTContext &Value::getASTContext() { return getInterpreter().getASTContext(); } + +const ASTContext &Value::getASTContext() const { + return getInterpreter().getASTContext(); +} + +void Value::dump() const { print(llvm::outs()); } + +void Value::printType(llvm::raw_ostream &Out) const { + Out << "Not implement yet.\n"; +} +void Value::printData(llvm::raw_ostream &Out) const { + Out << "Not implement yet.\n"; +} +void Value::print(llvm::raw_ostream &Out) const { + assert(OpaqueType != nullptr && "Can't print default Value"); + Out << "Not implement yet.\n"; +} diff --git a/clang/tools/clang-repl/CMakeLists.txt b/clang/tools/clang-repl/CMakeLists.txt index 2ea0122a7b795..2ccbe292fd49e 100644 --- a/clang/tools/clang-repl/CMakeLists.txt +++ b/clang/tools/clang-repl/CMakeLists.txt @@ -12,6 +12,7 @@ add_clang_tool(clang-repl ) clang_target_link_libraries(clang-repl PRIVATE + clangAST clangBasic clangFrontend clangInterpreter diff --git a/clang/unittests/Interpreter/CMakeLists.txt b/clang/unittests/Interpreter/CMakeLists.txt index 1a099dbbfe59a..698494b9897f5 100644 --- a/clang/unittests/Interpreter/CMakeLists.txt +++ b/clang/unittests/Interpreter/CMakeLists.txt @@ -22,3 +22,5 @@ target_link_libraries(ClangReplInterpreterTests PUBLIC if(NOT WIN32) add_subdirectory(ExceptionTests) endif() + +export_executable_symbols(ClangReplInterpreterTests) diff --git a/clang/unittests/Interpreter/InterpreterTest.cpp b/clang/unittests/Interpreter/InterpreterTest.cpp index d555911a89451..330fd18ab0a43 100644 --- a/clang/unittests/Interpreter/InterpreterTest.cpp +++ b/clang/unittests/Interpreter/InterpreterTest.cpp @@ -17,6 +17,7 @@ #include "clang/AST/Mangle.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/TextDiagnosticPrinter.h" +#include "clang/Interpreter/Value.h" #include "clang/Sema/Lookup.h" #include "clang/Sema/Sema.h" @@ -33,6 +34,11 @@ using namespace clang; #define CLANG_INTERPRETER_NO_SUPPORT_EXEC #endif +int Global = 42; +// JIT reports symbol not found on Windows without the visibility attribute. +REPL_EXTERNAL_VISIBILITY int getGlobal() { return Global; } +REPL_EXTERNAL_VISIBILITY void setGlobal(int val) { Global = val; } + namespace { using Args = std::vector<const char *>; static std::unique_ptr<Interpreter> @@ -276,8 +282,7 @@ TEST(IncrementalProcessing, InstantiateTemplate) { std::vector<const char *> Args = {"-fno-delayed-template-parsing"}; std::unique_ptr<Interpreter> Interp = createInterpreter(Args); - llvm::cantFail(Interp->Parse("void* operator new(__SIZE_TYPE__, void* __p);" - "extern \"C\" int printf(const char*,...);" + llvm::cantFail(Interp->Parse("extern \"C\" int printf(const char*,...);" "class A {};" "struct B {" " template<typename T>" @@ -315,4 +320,103 @@ TEST(IncrementalProcessing, InstantiateTemplate) { free(NewA); } +#ifdef CLANG_INTERPRETER_NO_SUPPORT_EXEC +TEST(InterpreterTest, DISABLED_Value) { +#else +TEST(InterpreterTest, Value) { +#endif + // We cannot execute on the platform. + if (!HostSupportsJit()) + return; + + std::unique_ptr<Interpreter> Interp = createInterpreter(); + + Value V1; + llvm::cantFail(Interp->ParseAndExecute("int x = 42;")); + llvm::cantFail(Interp->ParseAndExecute("x", &V1)); + EXPECT_TRUE(V1.isValid()); + EXPECT_TRUE(V1.hasValue()); + EXPECT_EQ(V1.getInt(), 42); + EXPECT_EQ(V1.convertTo<int>(), 42); + EXPECT_TRUE(V1.getType()->isIntegerType()); + EXPECT_EQ(V1.getKind(), Value::K_Int); + EXPECT_FALSE(V1.isManuallyAlloc()); + + Value V2; + llvm::cantFail(Interp->ParseAndExecute("double y = 3.14;")); + llvm::cantFail(Interp->ParseAndExecute("y", &V2)); + EXPECT_TRUE(V2.isValid()); + EXPECT_TRUE(V2.hasValue()); + EXPECT_EQ(V2.getDouble(), 3.14); + EXPECT_EQ(V2.convertTo<double>(), 3.14); + EXPECT_TRUE(V2.getType()->isFloatingType()); + EXPECT_EQ(V2.getKind(), Value::K_Double); + EXPECT_FALSE(V2.isManuallyAlloc()); + + Value V3; + llvm::cantFail(Interp->ParseAndExecute( + "struct S { int* p; S() { p = new int(42); } ~S() { delete p; }};")); + llvm::cantFail(Interp->ParseAndExecute("S{}", &V3)); + EXPECT_TRUE(V3.isValid()); + EXPECT_TRUE(V3.hasValue()); + EXPECT_TRUE(V3.getType()->isRecordType()); + EXPECT_EQ(V3.getKind(), Value::K_PtrOrObj); + EXPECT_TRUE(V3.isManuallyAlloc()); + + Value V4; + llvm::cantFail(Interp->ParseAndExecute("int getGlobal();")); + llvm::cantFail(Interp->ParseAndExecute("void setGlobal(int);")); + llvm::cantFail(Interp->ParseAndExecute("getGlobal()", &V4)); + EXPECT_EQ(V4.getInt(), 42); + EXPECT_TRUE(V4.getType()->isIntegerType()); + + Value V5; + // Change the global from the compiled code. + setGlobal(43); + llvm::cantFail(Interp->ParseAndExecute("getGlobal()", &V5)); + EXPECT_EQ(V5.getInt(), 43); + EXPECT_TRUE(V5.getType()->isIntegerType()); + + // Change the global from the interpreted code. + llvm::cantFail(Interp->ParseAndExecute("setGlobal(44);")); + EXPECT_EQ(getGlobal(), 44); + + Value V6; + llvm::cantFail(Interp->ParseAndExecute("void foo() {}")); + llvm::cantFail(Interp->ParseAndExecute("foo()", &V6)); + EXPECT_TRUE(V6.isValid()); + EXPECT_FALSE(V6.hasValue()); + EXPECT_TRUE(V6.getType()->isVoidType()); + EXPECT_EQ(V6.getKind(), Value::K_Void); + EXPECT_FALSE(V2.isManuallyAlloc()); + + Value V7; + llvm::cantFail(Interp->ParseAndExecute("foo", &V7)); + EXPECT_TRUE(V7.isValid()); + EXPECT_TRUE(V7.hasValue()); + EXPECT_TRUE(V7.getType()->isFunctionProtoType()); + EXPECT_EQ(V7.getKind(), Value::K_PtrOrObj); + EXPECT_FALSE(V7.isManuallyAlloc()); + + Value V8; + llvm::cantFail(Interp->ParseAndExecute("struct SS{ void f() {} };")); + llvm::cantFail(Interp->ParseAndExecute("&SS::f", &V8)); + EXPECT_TRUE(V8.isValid()); + EXPECT_TRUE(V8.hasValue()); + EXPECT_TRUE(V8.getType()->isMemberFunctionPointerType()); + EXPECT_EQ(V8.getKind(), Value::K_PtrOrObj); + EXPECT_TRUE(V8.isManuallyAlloc()); + + Value V9; + llvm::cantFail(Interp->ParseAndExecute("struct A { virtual int f(); };")); + llvm::cantFail( + Interp->ParseAndExecute("struct B : A { int f() { return 42; }};")); + llvm::cantFail(Interp->ParseAndExecute("int (B::*ptr)() = &B::f;")); + llvm::cantFail(Interp->ParseAndExecute("ptr", &V9)); + EXPECT_TRUE(V9.isValid()); + EXPECT_TRUE(V9.hasValue()); + EXPECT_TRUE(V9.getType()->isMemberFunctionPointerType()); + EXPECT_EQ(V9.getKind(), Value::K_PtrOrObj); + EXPECT_TRUE(V9.isManuallyAlloc()); +} } // end anonymous namespace _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits