[clang] a423b7f - [clang-repl] Introduce Value to capture expression results
Jun Zhang via cfe-commits
cfe-commits at lists.llvm.org
Tue May 16 05:11:20 PDT 2023
Author: Jun Zhang
Date: 2023-05-16T20:10:49+08:00
New Revision: a423b7f1d7ca8b263af85944f57a69aa08fc942c
URL: https://github.com/llvm/llvm-project/commit/a423b7f1d7ca8b263af85944f57a69aa08fc942c
DIFF: https://github.com/llvm/llvm-project/commit/a423b7f1d7ca8b263af85944f57a69aa08fc942c.diff
LOG: [clang-repl] Introduce Value to capture expression results
This is the second part of the below RFC:
https://discourse.llvm.org/t/rfc-handle-execution-results-in-clang-repl/68493
This patch implements a Value class that can be used to carry expression
results in clang-repl. In other words, when we see a top expression
without semi, it will be captured and stored to a Value object. You can
explicitly specify where you want to store the object, like:
```
Value V;
llvm::cantFail(Interp->ParseAndExecute("int x = 42;"));
llvm::cantFail(Interp->ParseAndExecute("x", &V));
```
`V` now stores some useful infomation about `x`, you can get its real
value (42), it's `clang::QualType` or anything interesting.
However, if you don't specify the optional argument, it will be captured
to a local variable, and automatically called `Value::dump`, which is
not implemented yet in this patch.
Signed-off-by: Jun Zhang <jun at 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..90a0097e5cc37
--- /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..82eaa8539c140 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,15 @@
#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/raw_ostream.h"
#include "llvm/TargetParser/Host.h"
-
using namespace clang;
// FIXME: Figure out how to unify with namespace init_convenience from
@@ -177,7 +183,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 +196,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 +226,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 +250,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 +301,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 +353,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 +384,325 @@ 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);
+}
+
+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);
+ VRef.setULongLong(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..1662de406e40f 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,95 @@ TEST(IncrementalProcessing, InstantiateTemplate) {
free(NewA);
}
+TEST(InterpreterTest, Value) {
+ 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
More information about the cfe-commits
mailing list