junaire updated this revision to Diff 508072.
junaire added a comment.
Update comments
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D141215/new/
https://reviews.llvm.org/D141215
Files:
clang/include/clang/Basic/TokenKinds.def
clang/include/clang/Interpreter/Interpreter.h
clang/include/clang/Interpreter/Value.h
clang/include/clang/Parse/Parser.h
clang/lib/Frontend/PrintPreprocessedOutput.cpp
clang/lib/Interpreter/ASTHelpers.cpp
clang/lib/Interpreter/ASTHelpers.h
clang/lib/Interpreter/CMakeLists.txt
clang/lib/Interpreter/IncrementalParser.cpp
clang/lib/Interpreter/IncrementalParser.h
clang/lib/Interpreter/Interpreter.cpp
clang/lib/Interpreter/Value.cpp
clang/lib/Lex/PPLexerChange.cpp
clang/lib/Parse/ParseCXXInlineMethods.cpp
clang/lib/Parse/ParseDecl.cpp
clang/lib/Parse/ParseStmt.cpp
clang/lib/Parse/Parser.cpp
clang/tools/clang-repl/CMakeLists.txt
clang/unittests/Interpreter/CMakeLists.txt
clang/unittests/Interpreter/InterpreterTest.cpp
Index: clang/unittests/Interpreter/InterpreterTest.cpp
===================================================================
--- clang/unittests/Interpreter/InterpreterTest.cpp
+++ 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,10 @@
#define CLANG_INTERPRETER_NO_SUPPORT_EXEC
#endif
+int Global = 42;
+int getGlobal() { return Global; }
+void setGlobal(int val) { Global = val; }
+
namespace {
using Args = std::vector<const char *>;
static std::unique_ptr<Interpreter>
@@ -139,7 +144,6 @@
auto Interp = createInterpreter(ExtraArgs, DiagPrinter.get());
- // Fail to undo.
auto Err1 = Interp->Undo();
EXPECT_EQ("Operation failed. Too many undos",
llvm::toString(std::move(Err1)));
@@ -276,8 +280,7 @@
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>"
@@ -314,4 +317,31 @@
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_EQ(V1.getInt(), 42);
+ EXPECT_TRUE(V1.getType()->isIntegerType());
+
+ llvm::cantFail(Interp->ParseAndExecute("int getGlobal();"));
+ llvm::cantFail(Interp->ParseAndExecute("void setGlobal(int);"));
+ Value V2;
+ llvm::cantFail(Interp->ParseAndExecute("getGlobal()", &V2));
+ EXPECT_EQ(V2.getInt(), 42);
+ EXPECT_TRUE(V2.getType()->isIntegerType());
+
+ // Change the global from the compiled code.
+ setGlobal(43);
+ Value V3;
+ llvm::cantFail(Interp->ParseAndExecute("getGlobal()", &V3));
+ EXPECT_EQ(V3.getInt(), 43);
+ EXPECT_TRUE(V3.getType()->isIntegerType());
+
+ // Change the global from the interpreted code.
+ llvm::cantFail(Interp->ParseAndExecute("setGlobal(44);"));
+ EXPECT_EQ(getGlobal(), 44);
+}
} // end anonymous namespace
Index: clang/unittests/Interpreter/CMakeLists.txt
===================================================================
--- clang/unittests/Interpreter/CMakeLists.txt
+++ clang/unittests/Interpreter/CMakeLists.txt
@@ -22,3 +22,5 @@
if(NOT WIN32)
add_subdirectory(ExceptionTests)
endif()
+
+export_executable_symbols(ClangReplInterpreterTests)
Index: clang/tools/clang-repl/CMakeLists.txt
===================================================================
--- clang/tools/clang-repl/CMakeLists.txt
+++ clang/tools/clang-repl/CMakeLists.txt
@@ -12,6 +12,7 @@
)
clang_target_link_libraries(clang-repl PRIVATE
+ clangAST
clangBasic
clangFrontend
clangInterpreter
Index: clang/lib/Parse/Parser.cpp
===================================================================
--- clang/lib/Parse/Parser.cpp
+++ clang/lib/Parse/Parser.cpp
@@ -320,6 +320,7 @@
case tok::annot_module_begin:
case tok::annot_module_end:
case tok::annot_module_include:
+ case tok::annot_input_end:
// Stop before we change submodules. They generally indicate a "good"
// place to pick up parsing again (except in the special case where
// we're trying to skip to EOF).
@@ -616,8 +617,8 @@
// Skip over the EOF token, flagging end of previous input for incremental
// processing
- if (PP.isIncrementalProcessingEnabled() && Tok.is(tok::eof))
- ConsumeToken();
+ if (PP.isIncrementalProcessingEnabled() && Tok.is(tok::annot_input_end))
+ ConsumeAnyToken();
Result = nullptr;
switch (Tok.getKind()) {
@@ -697,6 +698,7 @@
return false;
case tok::eof:
+ case tok::annot_input_end:
// Check whether -fmax-tokens= was reached.
if (PP.getMaxTokens() != 0 && PP.getTokenCount() > PP.getMaxTokens()) {
PP.Diag(Tok.getLocation(), diag::warn_max_tokens_total)
Index: clang/lib/Parse/ParseStmt.cpp
===================================================================
--- clang/lib/Parse/ParseStmt.cpp
+++ clang/lib/Parse/ParseStmt.cpp
@@ -543,8 +543,16 @@
return ParseCaseStatement(StmtCtx, /*MissingCase=*/true, Expr);
}
- // Otherwise, eat the semicolon.
- ExpectAndConsumeSemi(diag::err_expected_semi_after_expr);
+ if (PP.isIncrementalProcessingEnabled() && Tok.is(tok::annot_input_end)) {
+ // If we're parsing an ExprStmt and the last semicolon is missing and the
+ // incremental externsion is enabled and we're reaching the end, consider we
+ // want to do value printing.
+ ConsumeAnyToken();
+ setPrettyPrintMode();
+ } else {
+ // Otherwise, eat the semicolon.
+ ExpectAndConsumeSemi(diag::err_expected_semi_after_expr);
+ }
return handleExprStmt(Expr, StmtCtx);
}
Index: clang/lib/Parse/ParseDecl.cpp
===================================================================
--- clang/lib/Parse/ParseDecl.cpp
+++ clang/lib/Parse/ParseDecl.cpp
@@ -2077,6 +2077,7 @@
case tok::annot_module_begin:
case tok::annot_module_end:
case tok::annot_module_include:
+ case tok::annot_input_end:
return;
default:
Index: clang/lib/Parse/ParseCXXInlineMethods.cpp
===================================================================
--- clang/lib/Parse/ParseCXXInlineMethods.cpp
+++ clang/lib/Parse/ParseCXXInlineMethods.cpp
@@ -836,6 +836,7 @@
case tok::annot_module_begin:
case tok::annot_module_end:
case tok::annot_module_include:
+ case tok::annot_input_end:
// Ran out of tokens.
return false;
@@ -1242,6 +1243,7 @@
case tok::annot_module_begin:
case tok::annot_module_end:
case tok::annot_module_include:
+ case tok::annot_input_end:
// Ran out of tokens.
return false;
Index: clang/lib/Lex/PPLexerChange.cpp
===================================================================
--- clang/lib/Lex/PPLexerChange.cpp
+++ clang/lib/Lex/PPLexerChange.cpp
@@ -535,13 +535,19 @@
return LeavingSubmodule;
}
}
-
// If this is the end of the main file, form an EOF token.
assert(CurLexer && "Got EOF but no current lexer set!");
const char *EndPos = getCurLexerEndPos();
Result.startToken();
CurLexer->BufferPtr = EndPos;
- CurLexer->FormTokenWithChars(Result, EndPos, tok::eof);
+
+ if (isIncrementalProcessingEnabled()) {
+ CurLexer->FormTokenWithChars(Result, EndPos, tok::annot_input_end);
+ Result.setAnnotationEndLoc(Result.getLocation());
+ Result.setAnnotationValue(nullptr);
+ } else {
+ CurLexer->FormTokenWithChars(Result, EndPos, tok::eof);
+ }
if (isCodeCompletionEnabled()) {
// Inserting the code-completion point increases the source buffer by 1,
Index: clang/lib/Interpreter/Value.cpp
===================================================================
--- /dev/null
+++ clang/lib/Interpreter/Value.cpp
@@ -0,0 +1,189 @@
+#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) {
+ unsigned char *Buf = new unsigned char[1024];
+ ValueStorage *VS = new (Buf) ValueStorage(DtorF, AllocSize, ElementsSize);
+ 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) {
+ return reinterpret_cast<ValueStorage *>((unsigned char *)Payload -
+ getPayloadOffset());
+ }
+
+ 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) {
+ size_t Stride = AllocSize / Elements;
+ for (size_t Idx = 0; Idx < Elements; ++Idx)
+ (*Dtor)(getPayload() + Idx * Stride);
+ }
+ delete[] reinterpret_cast<unsigned char *>(this);
+ }
+ }
+
+private:
+ ValueStorage(void *DtorF, size_t AllocSize, size_t ElementsNum)
+ : RefCnt(1), AllocSize(AllocSize), Elements(ElementsNum),
+ Dtor(reinterpret_cast<DtorFunc>(DtorF)) {}
+
+ mutable unsigned RefCnt;
+ size_t AllocSize = 0;
+ size_t Elements = 0;
+ unsigned char Storage[1];
+ DtorFunc Dtor = nullptr;
+};
+} // namespace
+
+static Value::Kind ConvertQualTypeToKind(ASTContext &Ctx, QualType QT) {
+ if (Ctx.hasSameType(QT, Ctx.VoidTy))
+ return Value::K_Void;
+
+ if (const auto *ET = dyn_cast<EnumType>(QT.getTypePtr()))
+ QT = ET->getDecl()->getIntegerType();
+
+ if (!QT->isBuiltinType() || QT->castAs<BuiltinType>()->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(void *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())) {
+ // Compile dtor function.
+ Interpreter &Interp = getInterpreter();
+ void *DtorF = nullptr;
+ // FIXME: Arrays
+ if (const auto *RT = getType()->getAs<RecordType>())
+ // FIXME: How to handle errors in a constructor?
+ DtorF = llvm::cantFail(Interp.CompileDtorCall(RT->getDecl()));
+
+ size_t AllocSize =
+ getASTContext().getTypeSizeInChars(getType()).getQuantity();
+ size_t ElementsSize = 1;
+ setPtr(ValueStorage::CreatePayload(DtorF, AllocSize, ElementsSize));
+ }
+ }
+}
+
+Value::Value(const Value &RHS)
+ : Interp(RHS.Interp), OpaqueType(RHS.OpaqueType), 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.Interp, nullptr);
+ ValueKind = std::exchange(RHS.ValueKind, K_Unspecified);
+
+ IsManuallyAlloc = RHS.IsManuallyAlloc;
+ if (IsManuallyAlloc)
+ ValueStorage::getFromPayload(getPtr())->Release();
+}
+
+Value &Value::operator=(const Value &RHS) {
+ if (IsManuallyAlloc)
+ ValueStorage::getFromPayload(getPtr())->Release();
+
+ Interp = RHS.Interp;
+ OpaqueType = RHS.Interp;
+ 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 = RHS.IsManuallyAlloc;
+
+ return *this;
+}
+
+Value::~Value() {
+ if (IsManuallyAlloc)
+ ValueStorage::getFromPayload(getPtr())->Release();
+}
+
+QualType Value::getType() const {
+ return QualType::getFromOpaquePtr(OpaqueType);
+}
+
+Interpreter &Value::getInterpreter() const {
+ assert(Interp != nullptr &&
+ "Can't get interpreter from a default constructed value");
+ return *reinterpret_cast<Interpreter *>(Interp);
+}
+
+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";
+}
Index: clang/lib/Interpreter/Interpreter.cpp
===================================================================
--- clang/lib/Interpreter/Interpreter.cpp
+++ clang/lib/Interpreter/Interpreter.cpp
@@ -17,6 +17,9 @@
#include "IncrementalParser.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"
@@ -29,10 +32,11 @@
#include "clang/Frontend/TextDiagnosticBuffer.h"
#include "clang/Lex/PreprocessorOptions.h"
+#include "ASTHelpers.h"
+#include "clang/Sema/Lookup.h"
#include "llvm/IR/Module.h"
#include "llvm/Support/Errc.h"
#include "llvm/TargetParser/Host.h"
-
using namespace clang;
// FIXME: Figure out how to unify with namespace init_convenience from
@@ -189,6 +193,18 @@
}
}
+// These better to put in a runtime header but we can't. This is because we
+// can't find the percise resource directory in unittests so we have to hard
+// code them.
+std::vector<std::string> Runtimes = {
+ "#include <new>",
+ "void *__InterpreterSetValueWithAlloc(void*, void*, void*);",
+ "void __InterpreterSetValueNoAlloc(void*, void*, void*, void*);",
+ "void __InterpreterSetValueNoAlloc(void*, void*, void*, float);",
+ "void __InterpreterSetValueNoAlloc(void*, void*, void*, double);",
+ "void __InterpreterSetValueNoAlloc(void*, void*, void*, long double);",
+ "void __InterpreterSetValueNoAlloc(void*,void*,void*,unsigned long long);"};
+
llvm::Expected<std::unique_ptr<Interpreter>>
Interpreter::create(std::unique_ptr<CompilerInstance> CI) {
llvm::Error Err = llvm::Error::success();
@@ -196,6 +212,12 @@
std::unique_ptr<Interpreter>(new Interpreter(std::move(CI), Err));
if (Err)
return std::move(Err);
+ for (const std::string &R : Runtimes)
+ llvm::cantFail(Interp->ParseAndExecute(R));
+ // 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);
}
@@ -203,22 +225,38 @@
return IncrParser->getCI();
}
+ASTContext &Interpreter::getASTContext() const {
+ return getCompilerInstance()->getASTContext();
+}
+
+Parser &Interpreter::getParser() const { return IncrParser->getParser(); }
+
const llvm::orc::LLJIT *Interpreter::getExecutionEngine() const {
if (IncrExecutor)
return IncrExecutor->getExecutionEngine();
return nullptr;
}
+std::string Interpreter::CreateUniqName(std::string Base) {
+ static size_t I = 0;
+ Base += std::to_string(I);
+ I += 1;
+ return Base;
+}
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);
}
llvm::Error Interpreter::Execute(PartialTranslationUnit &T) {
assert(T.TheModule);
if (!IncrExecutor) {
- const clang::TargetInfo &TI =
- getCompilerInstance()->getASTContext().getTargetInfo();
+ const clang::TargetInfo &TI = getASTContext().getTargetInfo();
llvm::Error Err = llvm::Error::success();
IncrExecutor = std::make_unique<IncrementalExecutor>(*TSCtx, Err, TI);
@@ -235,6 +273,33 @@
return llvm::Error::success();
}
+llvm::Error Interpreter::ParseAndExecute(llvm::StringRef Code, Value *V) {
+ auto PTU = Parse(Code);
+ if (!PTU)
+ return PTU.takeError();
+ if (IncrParser->isPrettyPrintMode()) {
+ IncrParser->setPrettyPrintMode(false);
+
+ // The user ask for a value, return it directly.
+ if (V) {
+ if (llvm::Error Err = GenerateValue(*PTU->TUPart, V))
+ return Err;
+ return llvm::Error::success();
+ }
+
+ // Presumbly we need to perform pretty print.
+ Value DefaultValue;
+ if (llvm::Error Err = GenerateValue(*PTU->TUPart, &DefaultValue))
+ return Err;
+ DefaultValue.dump();
+ return llvm::Error::success();
+ }
+
+ if (PTU->TheModule)
+ return Execute(*PTU);
+ return llvm::Error::success();
+}
+
llvm::Expected<llvm::JITTargetAddress>
Interpreter::getSymbolAddress(GlobalDecl GD) const {
if (!IncrExecutor)
@@ -268,7 +333,7 @@
llvm::Error Interpreter::Undo(unsigned N) {
std::list<PartialTranslationUnit> &PTUs = IncrParser->getPTUs();
- if (N > PTUs.size())
+ if (N + InitPTUSize > PTUs.size())
return llvm::make_error<llvm::StringError>("Operation failed. "
"Too many undos",
std::error_code());
@@ -283,3 +348,432 @@
}
return llvm::Error::success();
}
+
+llvm::Expected<llvm::JITTargetAddress> Interpreter::CompileDecl(Decl *D) {
+ assert(D && "The decl being compiled can't be null");
+ Sema &S = getCompilerInstance()->getSema();
+ llvm::Expected<PartialTranslationUnit &> PTUOrErr =
+ IncrParser->Parse(CreateDGPtrFrom(S, D));
+ if (!PTUOrErr)
+ return PTUOrErr.takeError();
+
+ if (PTUOrErr->TheModule)
+ if (llvm::Error Err = Execute(*PTUOrErr))
+ return Err;
+
+ ASTNameGenerator ASTNameGen(getASTContext());
+ llvm::Expected<llvm::JITTargetAddress> AddrOrErr =
+ getSymbolAddressFromLinkerName(ASTNameGen.getName(D));
+
+ return AddrOrErr;
+}
+
+llvm::Error Interpreter::GenerateValue(TranslationUnitDecl &TU, Value *V) {
+ // Capture the expression we want to print.
+ auto ExprOrErr = CaptureExpr(TU);
+ if (!ExprOrErr)
+ return ExprOrErr.takeError();
+
+ // Synthesize the wrapper function that is used to get the value.
+ FunctionDecl *ValueGetter = SynthesizeValueGetter(*ExprOrErr);
+
+ // Compile the function.
+ llvm::Expected<llvm::JITTargetAddress> AddrOrErr = CompileDecl(ValueGetter);
+
+ if (!AddrOrErr)
+ return AddrOrErr.takeError();
+
+ if (auto *Main = llvm::jitTargetAddressToPointer<void (*)(void *, void *)>(
+ AddrOrErr.get()))
+ (*Main)((void *)this, (void *)V);
+ return llvm::Error::success();
+}
+
+// Capture the last expression in the interpreter. We assume the user only
+// inputted a top level statement.
+llvm::Expected<Expr *> Interpreter::CaptureExpr(TranslationUnitDecl &TU) {
+ assert(std::distance(TU.decls_begin(), TU.decls_end()) > 0 &&
+ "Cannot capture Expr* in empty TranslationUnitDecl");
+
+ auto Size = std::distance(TU.decls_begin(), TU.decls_end());
+ auto Last = TU.decls_begin();
+ while (Size-- != 1)
+ Last++;
+
+ if (const auto *S = llvm::dyn_cast<clang::TopLevelStmtDecl>(*Last))
+ if (const auto *E = llvm::dyn_cast<clang::Expr>(S->getStmt()))
+ return const_cast<Expr *>(E);
+
+ return llvm::make_error<llvm::StringError>("Can not capture any Expr*!",
+ std::error_code());
+}
+
+llvm::Expected<void *> Interpreter::CompileDtorCall(const RecordDecl *RD) {
+ if (auto Dtor = Dtors.find(RD); Dtor != Dtors.end())
+ return Dtor->getSecond();
+
+ // No need to generate the destructor if it has no semantic effects.
+ if (const CXXRecordDecl *CXXRD = llvm::dyn_cast<CXXRecordDecl>(RD);
+ CXXRD->hasIrrelevantDestructor())
+ return nullptr;
+
+ // Generate AST.
+ //
+ // extern "C" void __InterpreterDtorT(void* obj) {
+ // ((T*)obj)->~T();
+ // }
+ //
+ QualType QT(RD->getTypeForDecl(), 0);
+
+ std::string DtorName;
+ llvm::raw_string_ostream SS(DtorName);
+ SS << "__InterpreterDtor" << RD;
+
+ std::string TypeName = GetFullTypeName(getASTContext(), QT);
+
+ const char *CodeTemplate = R"(
+ extern "C" void {0}(void* obj) {
+ (({1}*)obj)->~{1}();
+ }
+ )";
+ std::string Code = llvm::formatv(CodeTemplate, DtorName, TypeName);
+ llvm::Expected<PartialTranslationUnit &> PTUOrErr = Parse(Code);
+ // FIXME: Can't we just synthesize the AST directly?
+#if 0
+ Sema &S = getCompilerInstance()->getSema();
+ ASTContext &Ctx = S.getASTContext();
+
+ QualType QT(RD->getTypeForDecl(), 0);
+ std::string DtorName = "__InterpreterDtor" + QT.getAsString();
+
+ QualType RetTy = Ctx.VoidTy;
+ QualType ArgTy = Ctx.VoidPtrTy;
+
+ QualType FunctionTy = Ctx.getFunctionType(RetTy, {ArgTy}, {});
+ IdentifierInfo *II = &Ctx.Idents.get(DtorName);
+ // Extern "C" ?
+ auto *FD = FunctionDecl::Create(
+ S.getASTContext(), Ctx.getTranslationUnitDecl(), SourceLocation(),
+ SourceLocation(), II, FunctionTy, /*TInfo=*/nullptr, SC_Extern);
+ auto *ObjParm = ParmVarDecl::Create(
+ Ctx, FD, SourceLocation(), SourceLocation(), /*Id=*/nullptr, ArgTy,
+ Ctx.getTrivialTypeSourceInfo(ArgTy, SourceLocation()), SC_None,
+ /*DefArg=*/nullptr);
+ FD->setParams({ObjParm});
+
+ // The function Body.
+ auto *Obj = DeclRefExpr::Create(Ctx, NestedNameSpecifierLoc(),
+ SourceLocation(), ObjParm,
+ /*RefersToEnclosingVariableOrCapture=*/false,
+ SourceLocation(), ArgTy, VK_PRValue);
+ // Force cast it to the right type.
+ Expr *CastedExpr = CStyleCastPtrExpr(S, QT, Obj);
+
+ ExprResult DtorCall = nullptr;
+
+ assert(!DtorCall.isInvalid() && "Can't create the destructor call!");
+
+ FD->setBody(DtorCall.get());
+ // Compile it.
+ llvm::Expected<PartialTranslationUnit &> PTUOrErr =
+ IncrParser->Parse(CreateDGPtrFrom(S, FD));
+#endif
+ if (!PTUOrErr)
+ return PTUOrErr.takeError();
+ if (PTUOrErr->TheModule)
+ if (llvm::Error Err = Execute(*PTUOrErr))
+ return Err;
+
+ // Look up the function address in the JIT.
+ if (llvm::Expected<llvm::JITTargetAddress> AddrOrErr =
+ getSymbolAddressFromLinkerName(DtorName)) {
+ void *Dtor = (void *)*AddrOrErr;
+ Dtors[RD] = Dtor;
+ return Dtor;
+ }
+ return nullptr;
+}
+
+static constexpr llvm::StringRef MagicRuntimeInterface[] = {
+ "__InterpreterSetValueNoAlloc", "__InterpreterSetValueWithAlloc"};
+
+bool Interpreter::FindRuntimeInterface() {
+ if (RuntimeAllocInterface && RuntimeNoAllocInterface)
+ 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(RuntimeNoAllocInterface, MagicRuntimeInterface[0]))
+ return false;
+ if (!LookupInterface(RuntimeAllocInterface, MagicRuntimeInterface[1]))
+ return false;
+ return true;
+}
+
+namespace {
+
+enum class RuntimeInterfaceKind { NoAlloc, WithAlloc, CopyArray };
+
+class RuntimeInterfaceBuilder
+ : public TypeVisitor<RuntimeInterfaceBuilder, RuntimeInterfaceKind> {
+ 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.
+ switch (Visit(&*DesugaredTy)) {
+ // Synthesize a CallExpr for __InterpreterSetValueWithAlloc
+ case RuntimeInterfaceKind::WithAlloc: {
+ ExprResult AllocCall =
+ S.ActOnCallExpr(/*Scope=*/nullptr, Interp.RuntimeAllocInterface,
+ E->getBeginLoc(), Args, E->getEndLoc());
+ assert(!AllocCall.isInvalid() && "Can't create runtime interface call!");
+
+ TypeSourceInfo *TSI = Ctx.getTrivialTypeSourceInfo(Ty, SourceLocation());
+
+ Expr *Args2[] = {AllocCall.get()};
+
+ ExprResult CXXNewCall = S.BuildCXXNew(
+ E->getSourceRange(),
+ /*UseGlobal=*/true, /*PlacementLParen=*/SourceLocation(), Args2,
+ /*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);
+ }
+ // Synthesize a CallExpr for __InterpreterSetValueNoAlloc
+ case RuntimeInterfaceKind::NoAlloc: {
+ return S.ActOnCallExpr(/*Scope=*/nullptr, Interp.RuntimeNoAllocInterface,
+ E->getBeginLoc(), Args, E->getEndLoc());
+ }
+ case RuntimeInterfaceKind::CopyArray:
+ llvm_unreachable("Not implemented yet!");
+ }
+ }
+
+ RuntimeInterfaceKind VisitRecordType(const RecordType *Ty) {
+ return RuntimeInterfaceKind::WithAlloc;
+ }
+
+ RuntimeInterfaceKind VisitMemberPointerType(const MemberPointerType *Ty) {
+ llvm_unreachable("Not implemented yet");
+ }
+ RuntimeInterfaceKind VisitConstantArrayType(const ConstantArrayType *Ty) {
+ llvm_unreachable("Not implemented yet");
+ return RuntimeInterfaceKind::CopyArray;
+ }
+
+ RuntimeInterfaceKind VisitPointerType(const PointerType *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());
+ return RuntimeInterfaceKind::NoAlloc;
+ }
+
+ RuntimeInterfaceKind 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 RuntimeInterfaceKind::NoAlloc;
+ }
+
+ RuntimeInterfaceKind VisitBuiltinType(const BuiltinType *Ty) {
+ if (Ty->isNullPtrType())
+ Args.push_back(E);
+ else if (Ty->isFloatingType())
+ Args.push_back(E);
+ else if (Ty->isVoidType()) {
+ llvm_unreachable("Not implemented yet");
+ } else if (Ty->isIntegralOrEnumerationType()) {
+ HandleIntegralOrEnumType(Ty);
+ }
+ return RuntimeInterfaceKind::NoAlloc;
+ }
+
+ RuntimeInterfaceKind VisitEnumType(const EnumType *Ty) {
+ HandleIntegralOrEnumType(Ty);
+ return RuntimeInterfaceKind::NoAlloc;
+ }
+
+private:
+ // Force cast these types to uint64 to reduce the number of overloads of
+ // `__InterpreterSetValueNoAlloc`.
+ 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());
+ }
+};
+} // namespace
+
+static constexpr const char *const ValueGetter = "__InterpreterValueGetter";
+
+// This synthesizes a wrapper function that used in passing the Value object to
+// the interpreter. Inside the wrapper we synthesize another call to a speciall
+// function that is responsible for generating the Value.
+// In general, we transform:
+// clang-repl> x
+// To:
+// void __InterpreterValueGetter(void* ThisInterp, void* OpaqueValue) {
+// // 1. If x is a built-in type like int, float.
+// __InterpreterSetValueNoAlloc(ThisInterp, OpaqueValue, xQualType, x);
+// // 2. If x is a struct, and a lvalue.
+// __InterpreterSetValueNoAlloc(ThisInterp, OpaqueValue, xQualType, &x);
+// // 3. If x is a struct, but a rvalue.
+// new (__InterpreterSetValueWithAlloc(ThisInterp, OpaqueValue, xQualType))
+// (x);
+// }
+FunctionDecl *Interpreter::SynthesizeValueGetter(clang::Expr *E) {
+ Sema &S = getCompilerInstance()->getSema();
+ ASTContext &Ctx = S.getASTContext();
+
+ QualType Void = Ctx.VoidTy;
+ QualType VoidPtr = Ctx.VoidPtrTy;
+
+ // Synthesize function `__InterpreterValueGetter`.
+ QualType FunctionTy = Ctx.getFunctionType(Void, {VoidPtr, VoidPtr}, {});
+ IdentifierInfo *II = &Ctx.Idents.get(CreateUniqName(ValueGetter));
+ auto *FD = FunctionDecl::Create(
+ S.getASTContext(), Ctx.getTranslationUnitDecl(), SourceLocation(),
+ SourceLocation(), II, FunctionTy, /*TInfo=*/nullptr, SC_None);
+
+ // The two parameters:
+ // 1. void* OpaqueInterp
+ // 2. void* OpaqueValue
+ auto *OpaqueInterp = ParmVarDecl::Create(
+ Ctx, FD, SourceLocation(), SourceLocation(), /*Id=*/nullptr, VoidPtr,
+ Ctx.getTrivialTypeSourceInfo(VoidPtr, SourceLocation()), SC_None,
+ /*DefArg=*/nullptr);
+ auto *OpaqueValue = ParmVarDecl::Create(
+ Ctx, FD, SourceLocation(), SourceLocation(), /*Id=*/nullptr, VoidPtr,
+ Ctx.getTrivialTypeSourceInfo(VoidPtr, SourceLocation()), SC_None,
+ /*DefArg=*/nullptr);
+ FD->setParams({OpaqueInterp, OpaqueValue});
+
+ if (!FindRuntimeInterface())
+ llvm_unreachable("We can't find the runtime iterface for pretty print!");
+
+ // Create parameter `ThisInterp` for `__InterpreterSetValue*`
+ auto *ThisInterp = DeclRefExpr::Create(
+ Ctx, NestedNameSpecifierLoc(), SourceLocation(), OpaqueInterp,
+ /*RefersToEnclosingVariableOrCapture=*/false, SourceLocation(),
+ Ctx.VoidPtrTy, VK_PRValue);
+
+ // Create parameter `OutVal` for `__InterpreterSetValue*`
+ auto *OutValue = DeclRefExpr::Create(
+ Ctx, NestedNameSpecifierLoc(), SourceLocation(), OpaqueValue,
+ /*RefersToEnclosingVariableOrCapture=*/false, SourceLocation(),
+ Ctx.VoidPtrTy, VK_PRValue);
+
+ // Build `__InterpreterSetValue*` call.
+ RuntimeInterfaceBuilder Builder(*this, Ctx, S, E, {ThisInterp, OutValue});
+
+ ExprResult Result = Builder.getCall();
+ assert(!Result.isInvalid() && "Failed to generate the CallExpr!");
+ FD->setBody(Result.get());
+ return FD;
+}
+
+// Temporary rvalue struct that need special care.
+REPL_EXTERNAL_VISIBILITY void *
+__InterpreterSetValueWithAlloc(void *This, void *OutVal, void *OpaqueType) {
+ Value &VRef = *(Value *)OutVal;
+ VRef = Value(This, OpaqueType);
+ return VRef.getPtr();
+}
+
+// Pointers, lvalue struct that can take as a reference.
+REPL_EXTERNAL_VISIBILITY void __InterpreterSetValueNoAlloc(void *This,
+ void *OutVal,
+ void *OpaqueType,
+ void *Val) {
+ Value &VRef = *(Value *)OutVal;
+ VRef = Value(This, OpaqueType);
+ VRef.setPtr(Val);
+}
+
+REPL_EXTERNAL_VISIBILITY void
+__InterpreterSetValueNoAlloc(void *This, void *OutVal, void *OpaqueType,
+ unsigned long long Val) {
+ Value &VRef = *(Value *)OutVal;
+ VRef = Value(This, OpaqueType);
+ VRef.setULongLong(Val);
+}
+
+REPL_EXTERNAL_VISIBILITY void __InterpreterSetValueNoAlloc(void *This,
+ void *OutVal,
+ void *OpaqueType,
+ float Val) {
+ Value &VRef = *(Value *)OutVal;
+ VRef = Value(This, OpaqueType);
+ VRef.setFloat(Val);
+}
+
+REPL_EXTERNAL_VISIBILITY void __InterpreterSetValueNoAlloc(void *This,
+ void *OutVal,
+ void *OpaqueType,
+ double Val) {
+ Value &VRef = *(Value *)OutVal;
+ VRef = Value(This, OpaqueType);
+ VRef.setDouble(Val);
+}
+
+REPL_EXTERNAL_VISIBILITY void __InterpreterSetValueNoAlloc(void *This,
+ void *OutVal,
+ void *OpaqueType,
+ long double Val) {
+ Value &VRef = *(Value *)OutVal;
+ VRef = Value(This, OpaqueType);
+ VRef.setLongDouble(Val);
+}
Index: clang/lib/Interpreter/IncrementalParser.h
===================================================================
--- clang/lib/Interpreter/IncrementalParser.h
+++ clang/lib/Interpreter/IncrementalParser.h
@@ -16,7 +16,7 @@
#include "clang/Interpreter/PartialTranslationUnit.h"
#include "clang/AST/GlobalDecl.h"
-
+#include "clang/Parse/Parser.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Error.h"
@@ -32,7 +32,7 @@
class CompilerInstance;
class IncrementalAction;
class Parser;
-
+class Interpreter;
/// Provides support for incremental compilation. Keeps track of the state
/// changes between the subsequent incremental input.
///
@@ -68,6 +68,8 @@
/// \c TranslationUnitDecl and \c llvm::Module corresponding to the input.
llvm::Expected<PartialTranslationUnit &> Parse(llvm::StringRef Input);
+ llvm::Expected<PartialTranslationUnit &> Parse(Parser::DeclGroupPtrTy D);
+
/// Uses the CodeGenModule mangled name cache and avoids recomputing.
///\returns the mangled name of a \c GD.
llvm::StringRef GetMangledName(GlobalDecl GD) const;
@@ -76,8 +78,13 @@
std::list<PartialTranslationUnit> &getPTUs() { return PTUs; }
-private:
- llvm::Expected<PartialTranslationUnit &> ParseOrWrapTopLevelDecl();
+ Parser &getParser() const { return *P; }
+
+ llvm::Expected<PartialTranslationUnit &>
+ ParseOrWrapTopLevelDecl(Parser::DeclGroupPtrTy SynthesizedDecl = nullptr);
+
+ bool isPrettyPrintMode() const { return P->isPrettyPrintMode(); }
+ void setPrettyPrintMode(bool Mode = true) { P->setPrettyPrintMode(Mode); }
};
} // end namespace clang
Index: clang/lib/Interpreter/IncrementalParser.cpp
===================================================================
--- clang/lib/Interpreter/IncrementalParser.cpp
+++ clang/lib/Interpreter/IncrementalParser.cpp
@@ -19,9 +19,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"
@@ -143,7 +143,8 @@
}
llvm::Expected<PartialTranslationUnit &>
-IncrementalParser::ParseOrWrapTopLevelDecl() {
+IncrementalParser::ParseOrWrapTopLevelDecl(
+ Parser::DeclGroupPtrTy SynthesizedDecl) {
// Recover resources if we crash before exiting this method.
Sema &S = CI->getSema();
llvm::CrashRecoveryContextCleanupRegistrar<Sema> CleanupSema(&S);
@@ -158,8 +159,8 @@
LastPTU.TUPart = C.getTranslationUnitDecl();
// Skip previous eof due to last incremental input.
- if (P->getCurToken().is(tok::eof)) {
- P->ConsumeToken();
+ if (P->getCurToken().is(tok::annot_input_end)) {
+ P->ConsumeAnyToken();
// FIXME: Clang does not call ExitScope on finalizing the regular TU, we
// might want to do that around HandleEndOfTranslationUnit.
P->ExitScope();
@@ -169,14 +170,24 @@
S.ActOnTranslationUnitScope(P->getCurScope());
}
- Parser::DeclGroupPtrTy ADecl;
- Sema::ModuleImportState ImportState;
- for (bool AtEOF = P->ParseFirstTopLevelDecl(ADecl, ImportState); !AtEOF;
- AtEOF = P->ParseTopLevelDecl(ADecl, ImportState)) {
- if (ADecl && !Consumer->HandleTopLevelDecl(ADecl.get()))
- return llvm::make_error<llvm::StringError>("Parsing failed. "
- "The consumer rejected a decl",
- std::error_code());
+ if (SynthesizedDecl) {
+ if (!Consumer->HandleTopLevelDecl(SynthesizedDecl.get())) {
+ return llvm::make_error<llvm::StringError>(
+ "Parsing failed. "
+ "The consumer rejected the synthesized decl",
+ std::error_code());
+ }
+ } else {
+ Parser::DeclGroupPtrTy ADecl;
+ Sema::ModuleImportState ImportState;
+ for (bool AtEOF = P->ParseFirstTopLevelDecl(ADecl, ImportState); !AtEOF;
+ AtEOF = P->ParseTopLevelDecl(ADecl, ImportState)) {
+ if (ADecl && !Consumer->HandleTopLevelDecl(ADecl.get()))
+ return llvm::make_error<llvm::StringError>(
+ "Parsing failed. "
+ "The consumer rejected a decl",
+ std::error_code());
+ }
}
DiagnosticsEngine &Diags = getCI()->getDiagnostics();
@@ -259,12 +270,12 @@
Token Tok;
do {
PP.Lex(Tok);
- } while (Tok.isNot(tok::eof));
+ } while (Tok.isNot(tok::annot_input_end));
}
Token AssertTok;
PP.Lex(AssertTok);
- assert(AssertTok.is(tok::eof) &&
+ assert(AssertTok.is(tok::annot_input_end) &&
"Lexer must be EOF when starting incremental parse!");
if (CodeGenerator *CG = getCodeGen(Act.get())) {
@@ -278,6 +289,24 @@
return PTU;
}
+llvm::Expected<PartialTranslationUnit &>
+IncrementalParser::Parse(Parser::DeclGroupPtrTy D) {
+ llvm::Expected<PartialTranslationUnit &> PTUOrErr =
+ ParseOrWrapTopLevelDecl(D);
+ if (!PTUOrErr) {
+ return PTUOrErr.takeError();
+ }
+ 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());
+
+ PTUOrErr.get().TheModule = std::move(M);
+ }
+
+ return PTUOrErr;
+}
+
void IncrementalParser::CleanUpPTU(PartialTranslationUnit &PTU) {
TranslationUnitDecl *MostRecentTU = PTU.TUPart;
TranslationUnitDecl *FirstTU = MostRecentTU->getFirstDecl();
Index: clang/lib/Interpreter/CMakeLists.txt
===================================================================
--- clang/lib/Interpreter/CMakeLists.txt
+++ clang/lib/Interpreter/CMakeLists.txt
@@ -12,6 +12,8 @@
IncrementalExecutor.cpp
IncrementalParser.cpp
Interpreter.cpp
+ Value.cpp
+ ASTHelpers.cpp
DEPENDS
intrinsics_gen
Index: clang/lib/Interpreter/ASTHelpers.h
===================================================================
--- /dev/null
+++ clang/lib/Interpreter/ASTHelpers.h
@@ -0,0 +1,43 @@
+#ifndef LLVM_CLANG_INTERPRETER_AST_HELPERS_H
+#define LLVM_CLANG_INTERPRETER_AST_HELPERS_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"
+
+// TODO: create a sub namespace `repl`.
+namespace clang {
+IntegerLiteral *IntegerLiteralExpr(ASTContext &C, uintptr_t Ptr);
+
+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
Index: clang/lib/Interpreter/ASTHelpers.cpp
===================================================================
--- /dev/null
+++ clang/lib/Interpreter/ASTHelpers.cpp
@@ -0,0 +1,99 @@
+#include "ASTHelpers.h"
+
+namespace clang {
+
+IntegerLiteral *IntegerLiteralExpr(ASTContext &C, uintptr_t Ptr) {
+ const llvm::APInt Addr(8 * sizeof(void *), Ptr);
+ return IntegerLiteral::Create(C, Addr, C.getUIntPtrType(), 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, 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 clang::TagDecl *TD = dyn_cast<clang::TagDecl>(Within))
+ if (!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
Index: clang/lib/Frontend/PrintPreprocessedOutput.cpp
===================================================================
--- clang/lib/Frontend/PrintPreprocessedOutput.cpp
+++ clang/lib/Frontend/PrintPreprocessedOutput.cpp
@@ -663,7 +663,8 @@
// them.
if (Tok.is(tok::eof) ||
(Tok.isAnnotation() && !Tok.is(tok::annot_header_unit) &&
- !Tok.is(tok::annot_module_begin) && !Tok.is(tok::annot_module_end)))
+ !Tok.is(tok::annot_module_begin) && !Tok.is(tok::annot_module_end) &&
+ !Tok.is(tok::annot_input_end)))
return;
// EmittedDirectiveOnThisLine takes priority over RequireSameLine.
@@ -819,6 +820,9 @@
// -traditional-cpp the lexer keeps /all/ whitespace, including comments.
PP.Lex(Tok);
continue;
+ } else if (Tok.is(tok::annot_input_end)) {
+ PP.Lex(Tok);
+ continue;
} else if (Tok.is(tok::eod)) {
// Don't print end of directive tokens, since they are typically newlines
// that mess up our line tracking. These come from unknown pre-processor
Index: clang/include/clang/Parse/Parser.h
===================================================================
--- clang/include/clang/Parse/Parser.h
+++ clang/include/clang/Parse/Parser.h
@@ -439,6 +439,10 @@
/// a statement expression and builds a suitable expression statement.
StmtResult handleExprStmt(ExprResult E, ParsedStmtContext StmtCtx);
+ /// A flag that indicate if we run into something that need to pretty print.
+ /// Note this should only be used in incremental C++ (clang-repl).
+ bool PrettyPrintMode = false;
+
public:
Parser(Preprocessor &PP, Sema &Actions, bool SkipFunctionBodies);
~Parser() override;
@@ -459,6 +463,17 @@
return Actions.getObjCDeclContext();
}
+ bool isPrettyPrintMode() const {
+ assert(PP.isIncrementalProcessingEnabled() &&
+ "This should only be used in incremental C++");
+ return PrettyPrintMode;
+ }
+ void setPrettyPrintMode(bool Mode = true) {
+ assert(PP.isIncrementalProcessingEnabled() &&
+ "This should only be used in incremental C++");
+ PrettyPrintMode = Mode;
+ }
+
// Type forwarding. All of these are statically 'void*', but they may all be
// different actual classes based on the actions in place.
typedef OpaquePtr<DeclGroupRef> DeclGroupPtrTy;
@@ -692,7 +707,8 @@
bool isEofOrEom() {
tok::TokenKind Kind = Tok.getKind();
return Kind == tok::eof || Kind == tok::annot_module_begin ||
- Kind == tok::annot_module_end || Kind == tok::annot_module_include;
+ Kind == tok::annot_module_end || Kind == tok::annot_module_include ||
+ Kind == tok::annot_input_end;
}
/// Checks if the \p Level is valid for use in a fold expression.
Index: clang/include/clang/Interpreter/Value.h
===================================================================
--- /dev/null
+++ clang/include/clang/Interpreter/Value.h
@@ -0,0 +1,142 @@
+//===--- 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++.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_INTERPRETER_VALUE_H
+#define LLVM_CLANG_INTERPRETER_VALUE_H
+
+#include "llvm/Support/Compiler.h"
+#include <cassert>
+
+namespace llvm {
+class raw_ostream;
+
+} // namespace llvm
+
+namespace clang {
+
+class ASTContext;
+class Interpreter;
+
+#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
+
+class Interpreter;
+class QualType;
+
+#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(void /*Interpreter*/ *In, void /*QualType*/ *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;
+
+ ASTContext &getASTContext() const;
+ QualType getType() const;
+ Interpreter &getInterpreter() const;
+
+ bool isValid() const { return ValueKind != K_Unspecified; }
+ bool isVoid() const { return ValueKind == K_Void; }
+ bool isManuallyAlloc() const { return IsManuallyAlloc; }
+ Kind getKind() const { return ValueKind; }
+ void setKind(Kind K) { ValueKind = K; }
+ void setOpaqueType(void *Ty) { OpaqueType = Ty; }
+
+ void setPtr(void *Ptr) { Data.m_Ptr = Ptr; }
+
+ void *getPtr() const {
+ assert(ValueKind == K_PtrOrObj);
+ return Data.m_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
+
+ template <typename T> T getAs();
+
+// Allows us to have overloads for each builtin type, plus one that goes via
+// void*
+#define X(type, name) \
+ template <> type getAs() { \
+ assert(ValueKind == K_##name); \
+ return (type)Data.m_##name; \
+ }
+ REPL_BUILTIN_TYPES
+#undef X
+
+private:
+ // Interpreter, QualType are stored as void* to reduce dependencies.
+ void *Interp = nullptr;
+ void *OpaqueType = nullptr;
+ Storage Data;
+ Kind ValueKind = K_Unspecified;
+ bool IsManuallyAlloc = false;
+};
+
+} // namespace clang
+#endif
Index: clang/include/clang/Interpreter/Interpreter.h
===================================================================
--- clang/include/clang/Interpreter/Interpreter.h
+++ clang/include/clang/Interpreter/Interpreter.h
@@ -14,10 +14,12 @@
#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/Support/Error.h"
@@ -28,11 +30,12 @@
namespace orc {
class LLJIT;
class ThreadSafeContext;
-}
+} // namespace orc
} // namespace llvm
namespace clang {
+class Parser;
class CompilerInstance;
class IncrementalExecutor;
class IncrementalParser;
@@ -52,22 +55,19 @@
Interpreter(std::unique_ptr<CompilerInstance> CI, llvm::Error &Err);
+ unsigned InitPTUSize = 0;
+
public:
~Interpreter();
static llvm::Expected<std::unique_ptr<Interpreter>>
create(std::unique_ptr<CompilerInstance> CI);
+ ASTContext &getASTContext() const;
const CompilerInstance *getCompilerInstance() const;
const llvm::orc::LLJIT *getExecutionEngine() const;
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<void *> CompileDtorCall(const RecordDecl *RD);
/// Undo N previous incremental inputs.
llvm::Error Undo(unsigned N = 1);
@@ -85,6 +85,28 @@
/// file.
llvm::Expected<llvm::JITTargetAddress>
getSymbolAddressFromLinkerName(llvm::StringRef LinkerName) const;
+
+ /// Compiles the synthesized Decl and returns the JITTargetAddress.
+ llvm::Expected<llvm::JITTargetAddress> CompileDecl(Decl *FD);
+
+ std::string CreateUniqName(std::string Base);
+
+ Parser &getParser() const;
+
+ // FIXME: Make it private.
+ Expr *RuntimeNoAllocInterface = nullptr;
+ Expr *RuntimeAllocInterface = nullptr;
+
+private:
+ llvm::Expected<Expr *> CaptureExpr(TranslationUnitDecl &TU);
+
+ bool FindRuntimeInterface();
+
+ FunctionDecl *SynthesizeValueGetter(clang::Expr *E);
+
+ llvm::Error GenerateValue(TranslationUnitDecl &TU, Value *V);
+
+ llvm::DenseMap<const RecordDecl *, void *> Dtors;
};
} // namespace clang
Index: clang/include/clang/Basic/TokenKinds.def
===================================================================
--- clang/include/clang/Basic/TokenKinds.def
+++ clang/include/clang/Basic/TokenKinds.def
@@ -941,6 +941,9 @@
// into the name of a header unit.
ANNOTATION(header_unit)
+// Annotation for end of input in clang-repl.
+ANNOTATION(input_end)
+
#undef PRAGMA_ANNOTATION
#undef ANNOTATION
#undef TESTING_KEYWORD
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits