[clang] [clang-repl] Simplify the value printing logic to enable out-of-process. (PR #107737)
Vassil Vassilev via cfe-commits
cfe-commits at lists.llvm.org
Mon Sep 9 10:26:26 PDT 2024
https://github.com/vgvassilev updated https://github.com/llvm/llvm-project/pull/107737
>From 2aa7527b52656d064c39aec94c9f1001ed10f7d8 Mon Sep 17 00:00:00 2001
From: Vassil Vassilev <v.g.vassilev at gmail.com>
Date: Fri, 6 Sep 2024 09:52:36 +0000
Subject: [PATCH 1/2] [clang-repl] Simplify the value printing logic to enable
out-of-process.
This patch improves the design of the IncrementalParser and Interpreter classes.
Now the incremental parser is only responsible for building the partial
translation unit declaration and the AST, while the Interpreter fills in the
lower level llvm::Module and other JIT-related infrastructure. Finally the
Interpreter class now orchestrates the AST and the LLVM IR with the
IncrementalParser and IncrementalExecutor classes.
The design improvement allows us to rework some of the logic that extracts an
interpreter value into the clang::Value object. The new implementation
simplifies use-cases which are used for out-of-process execution by allowing
interpreter to be inherited or customized with an clang::ASTConsumer.
This change will enable completing the pretty printing work which is in
llvm/llvm-project#84769
---
.../clang/Frontend/MultiplexConsumer.h | 3 +-
clang/include/clang/Interpreter/Interpreter.h | 53 +-
clang/lib/Frontend/MultiplexConsumer.cpp | 7 +
clang/lib/Interpreter/CMakeLists.txt | 1 +
clang/lib/Interpreter/DeviceOffload.cpp | 10 +-
clang/lib/Interpreter/DeviceOffload.h | 14 +-
clang/lib/Interpreter/IncrementalExecutor.cpp | 2 +-
clang/lib/Interpreter/IncrementalParser.cpp | 253 +------
clang/lib/Interpreter/IncrementalParser.h | 45 +-
clang/lib/Interpreter/Interpreter.cpp | 648 ++++++------------
.../Interpreter/InterpreterValuePrinter.cpp | 400 +++++++++++
.../Interpreter/CodeCompletionTest.cpp | 2 +-
.../Interpreter/InterpreterExtensionsTest.cpp | 64 +-
13 files changed, 707 insertions(+), 795 deletions(-)
create mode 100644 clang/lib/Interpreter/InterpreterValuePrinter.cpp
diff --git a/clang/include/clang/Frontend/MultiplexConsumer.h b/clang/include/clang/Frontend/MultiplexConsumer.h
index 3a7670d7a51aa6..b190750bb29fb8 100644
--- a/clang/include/clang/Frontend/MultiplexConsumer.h
+++ b/clang/include/clang/Frontend/MultiplexConsumer.h
@@ -53,6 +53,7 @@ class MultiplexConsumer : public SemaConsumer {
public:
// Takes ownership of the pointers in C.
MultiplexConsumer(std::vector<std::unique_ptr<ASTConsumer>> C);
+ MultiplexConsumer(std::unique_ptr<ASTConsumer> C);
~MultiplexConsumer() override;
// ASTConsumer
@@ -80,7 +81,7 @@ class MultiplexConsumer : public SemaConsumer {
void InitializeSema(Sema &S) override;
void ForgetSema() override;
-private:
+protected:
std::vector<std::unique_ptr<ASTConsumer>> Consumers; // Owns these.
std::unique_ptr<MultiplexASTMutationListener> MutationListener;
std::unique_ptr<MultiplexASTDeserializationListener> DeserializationListener;
diff --git a/clang/include/clang/Interpreter/Interpreter.h b/clang/include/clang/Interpreter/Interpreter.h
index 1234608bb58647..cbb1cfd4ab02a8 100644
--- a/clang/include/clang/Interpreter/Interpreter.h
+++ b/clang/include/clang/Interpreter/Interpreter.h
@@ -14,11 +14,9 @@
#ifndef LLVM_CLANG_INTERPRETER_INTERPRETER_H
#define LLVM_CLANG_INTERPRETER_INTERPRETER_H
-#include "clang/AST/Decl.h"
#include "clang/AST/GlobalDecl.h"
#include "clang/Interpreter/PartialTranslationUnit.h"
#include "clang/Interpreter/Value.h"
-#include "clang/Sema/Ownership.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ExecutionEngine/JITSymbol.h"
@@ -38,6 +36,9 @@ class ThreadSafeContext;
namespace clang {
class CompilerInstance;
+class CodeGenerator;
+class CXXRecordDecl;
+class Decl;
class IncrementalExecutor;
class IncrementalParser;
@@ -77,26 +78,27 @@ class IncrementalCompilerBuilder {
llvm::StringRef CudaSDKPath;
};
-/// Generate glue code between the Interpreter's built-in runtime and user code.
-class RuntimeInterfaceBuilder {
-public:
- virtual ~RuntimeInterfaceBuilder() = default;
-
- using TransformExprFunction = ExprResult(RuntimeInterfaceBuilder *Builder,
- Expr *, ArrayRef<Expr *>);
- virtual TransformExprFunction *getPrintValueTransformer() = 0;
-};
+class IncrementalAction;
+class InProcessPrintingASTConsumer;
/// Provides top-level interfaces for incremental compilation and execution.
class Interpreter {
+ friend class Value;
+ friend InProcessPrintingASTConsumer;
+
std::unique_ptr<llvm::orc::ThreadSafeContext> TSCtx;
+ /// Long-lived, incremental parsing action.
+ std::unique_ptr<IncrementalAction> Act;
std::unique_ptr<IncrementalParser> IncrParser;
std::unique_ptr<IncrementalExecutor> IncrExecutor;
- std::unique_ptr<RuntimeInterfaceBuilder> RuntimeIB;
// An optional parser for CUDA offloading
std::unique_ptr<IncrementalParser> DeviceParser;
+ /// List containing every information about every incrementally parsed piece
+ /// of code.
+ std::list<PartialTranslationUnit> PTUs;
+
unsigned InitPTUSize = 0;
// This member holds the last result of the value printing. It's a class
@@ -104,15 +106,15 @@ class Interpreter {
// printing happens, it's in an invalid state.
Value LastValue;
- // Add a call to an Expr to report its result. We query the function from
- // RuntimeInterfaceBuilder once and store it as a function pointer to avoid
- // frequent virtual function calls.
- RuntimeInterfaceBuilder::TransformExprFunction *AddPrintValueCall = nullptr;
+ /// When CodeGen is created the first llvm::Module gets cached in many places
+ /// and we must keep it alive.
+ std::unique_ptr<llvm::Module> CachedInCodeGenModule;
protected:
// Derived classes can use an extended interface of the Interpreter.
Interpreter(std::unique_ptr<CompilerInstance> CI, llvm::Error &Err,
- std::unique_ptr<llvm::orc::LLJITBuilder> JITBuilder = nullptr);
+ std::unique_ptr<llvm::orc::LLJITBuilder> JITBuilder = nullptr,
+ std::unique_ptr<clang::ASTConsumer> Consumer = nullptr);
// Create the internal IncrementalExecutor, or re-create it after calling
// ResetExecutor().
@@ -122,15 +124,8 @@ class Interpreter {
// JIT engine. In particular, it doesn't run cleanup or destructors.
void ResetExecutor();
- // Lazily construct the RuntimeInterfaceBuilder. The provided instance will be
- // used for the entire lifetime of the interpreter. The default implementation
- // targets the in-process __clang_Interpreter runtime. Override this to use a
- // custom runtime.
- virtual std::unique_ptr<RuntimeInterfaceBuilder> FindRuntimeInterface();
-
public:
virtual ~Interpreter();
-
static llvm::Expected<std::unique_ptr<Interpreter>>
create(std::unique_ptr<CompilerInstance> CI);
static llvm::Expected<std::unique_ptr<Interpreter>>
@@ -145,7 +140,6 @@ class Interpreter {
llvm::Expected<PartialTranslationUnit &> Parse(llvm::StringRef Code);
llvm::Error Execute(PartialTranslationUnit &T);
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);
@@ -167,8 +161,6 @@ class Interpreter {
llvm::Expected<llvm::orc::ExecutorAddr>
getSymbolAddressFromLinkerName(llvm::StringRef LinkerName) const;
- enum InterfaceKind { NoAlloc, WithAlloc, CopyArray, NewTag };
-
const llvm::SmallVectorImpl<Expr *> &getValuePrintingInfo() const {
return ValuePrintingInfo;
}
@@ -178,7 +170,14 @@ class Interpreter {
private:
size_t getEffectivePTUSize() const;
void markUserCodeStart();
+ llvm::Expected<Expr *> AttachValuePrinting(Expr *E);
+ llvm::Expected<llvm::orc::ExecutorAddr> CompileDtorCall(CXXRecordDecl *CXXRD);
+
+ CodeGenerator *getCodeGen() const;
+ std::unique_ptr<llvm::Module> GenModule();
+ // A cache for the compiled destructors used to for de-allocation of managed
+ // clang::Values.
llvm::DenseMap<CXXRecordDecl *, llvm::orc::ExecutorAddr> Dtors;
llvm::SmallVector<Expr *, 4> ValuePrintingInfo;
diff --git a/clang/lib/Frontend/MultiplexConsumer.cpp b/clang/lib/Frontend/MultiplexConsumer.cpp
index 2158d176d18929..3fd3c9bd69037a 100644
--- a/clang/lib/Frontend/MultiplexConsumer.cpp
+++ b/clang/lib/Frontend/MultiplexConsumer.cpp
@@ -298,6 +298,13 @@ MultiplexConsumer::MultiplexConsumer(
}
}
+MultiplexConsumer::MultiplexConsumer(std::unique_ptr<ASTConsumer> C)
+ : MultiplexConsumer([](std::unique_ptr<ASTConsumer> Consumer) {
+ std::vector<std::unique_ptr<ASTConsumer>> Consumers;
+ Consumers.push_back(std::move(Consumer));
+ return Consumers;
+ }(std::move(C))) {}
+
MultiplexConsumer::~MultiplexConsumer() {}
void MultiplexConsumer::Initialize(ASTContext &Context) {
diff --git a/clang/lib/Interpreter/CMakeLists.txt b/clang/lib/Interpreter/CMakeLists.txt
index 6a069659ebb8db..2cc7c59b61d318 100644
--- a/clang/lib/Interpreter/CMakeLists.txt
+++ b/clang/lib/Interpreter/CMakeLists.txt
@@ -22,6 +22,7 @@ add_clang_library(clangInterpreter
IncrementalExecutor.cpp
IncrementalParser.cpp
Interpreter.cpp
+ InterpreterValuePrinter.cpp
InterpreterUtils.cpp
Value.cpp
${WASM_SRC}
diff --git a/clang/lib/Interpreter/DeviceOffload.cpp b/clang/lib/Interpreter/DeviceOffload.cpp
index 07c9e3005e5fd3..4bf9ed28096893 100644
--- a/clang/lib/Interpreter/DeviceOffload.cpp
+++ b/clang/lib/Interpreter/DeviceOffload.cpp
@@ -15,6 +15,7 @@
#include "clang/Basic/TargetOptions.h"
#include "clang/CodeGen/ModuleBuilder.h"
#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Interpreter/PartialTranslationUnit.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/IR/Module.h"
@@ -24,11 +25,10 @@
namespace clang {
IncrementalCUDADeviceParser::IncrementalCUDADeviceParser(
- Interpreter &Interp, std::unique_ptr<CompilerInstance> Instance,
- IncrementalParser &HostParser, llvm::LLVMContext &LLVMCtx,
+ std::unique_ptr<CompilerInstance> Instance, IncrementalParser &HostParser,
llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> FS,
- llvm::Error &Err)
- : IncrementalParser(Interp, std::move(Instance), LLVMCtx, Err),
+ llvm::Error &Err, const std::list<PartialTranslationUnit> &PTUs)
+ : IncrementalParser(std::move(Instance), Err), PTUs(PTUs),
HostParser(HostParser), VFS(FS) {
if (Err)
return;
@@ -41,7 +41,7 @@ IncrementalCUDADeviceParser::IncrementalCUDADeviceParser(
}
}
-llvm::Expected<PartialTranslationUnit &>
+llvm::Expected<TranslationUnitDecl *>
IncrementalCUDADeviceParser::Parse(llvm::StringRef Input) {
auto PTU = IncrementalParser::Parse(Input);
if (!PTU)
diff --git a/clang/lib/Interpreter/DeviceOffload.h b/clang/lib/Interpreter/DeviceOffload.h
index ce4f218c94c79d..b84870474841a5 100644
--- a/clang/lib/Interpreter/DeviceOffload.h
+++ b/clang/lib/Interpreter/DeviceOffload.h
@@ -18,19 +18,19 @@
#include "llvm/Support/VirtualFileSystem.h"
namespace clang {
-
+struct PartialTranslationUnit;
class IncrementalCUDADeviceParser : public IncrementalParser {
+ const std::list<PartialTranslationUnit> &PTUs;
+
public:
IncrementalCUDADeviceParser(
- Interpreter &Interp, std::unique_ptr<CompilerInstance> Instance,
- IncrementalParser &HostParser, llvm::LLVMContext &LLVMCtx,
+ std::unique_ptr<CompilerInstance> Instance, IncrementalParser &HostParser,
llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> VFS,
- llvm::Error &Err);
+ llvm::Error &Err, const std::list<PartialTranslationUnit> &PTUs);
- llvm::Expected<PartialTranslationUnit &>
- Parse(llvm::StringRef Input) override;
+ llvm::Expected<TranslationUnitDecl *> Parse(llvm::StringRef Input) override;
- // Generate PTX for the last PTU
+ // Generate PTX for the last PTU.
llvm::Expected<llvm::StringRef> GeneratePTX();
// Generate fatbinary contents in memory
diff --git a/clang/lib/Interpreter/IncrementalExecutor.cpp b/clang/lib/Interpreter/IncrementalExecutor.cpp
index 1824a5b4570a93..4d2adecaafce74 100644
--- a/clang/lib/Interpreter/IncrementalExecutor.cpp
+++ b/clang/lib/Interpreter/IncrementalExecutor.cpp
@@ -118,4 +118,4 @@ IncrementalExecutor::getSymbolAddress(llvm::StringRef Name,
return SymOrErr->getAddress();
}
-} // end namespace clang
+} // namespace clang
diff --git a/clang/lib/Interpreter/IncrementalParser.cpp b/clang/lib/Interpreter/IncrementalParser.cpp
index b7c809c45098ca..615f61e9aec1bc 100644
--- a/clang/lib/Interpreter/IncrementalParser.cpp
+++ b/clang/lib/Interpreter/IncrementalParser.cpp
@@ -13,233 +13,33 @@
#include "IncrementalParser.h"
#include "clang/AST/DeclContextInternals.h"
-#include "clang/CodeGen/BackendUtil.h"
-#include "clang/CodeGen/CodeGenAction.h"
-#include "clang/CodeGen/ModuleBuilder.h"
#include "clang/Frontend/CompilerInstance.h"
-#include "clang/Frontend/FrontendAction.h"
-#include "clang/FrontendTool/Utils.h"
-#include "clang/Interpreter/Interpreter.h"
+#include "clang/Interpreter/PartialTranslationUnit.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"
-#include "llvm/Support/Timer.h"
#include <sstream>
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(DeclaratorDecl *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
-/// sees a call to \p EndSourceFile it deletes some of the important objects
-/// such as \p Preprocessor and \p Sema assuming no further input will come.
-///
-/// \p IncrementalAction ensures it keep its underlying action's objects alive
-/// as long as the \p IncrementalParser needs them.
-///
-class IncrementalAction : public WrapperFrontendAction {
-private:
- bool IsTerminating = false;
-
-public:
- IncrementalAction(CompilerInstance &CI, llvm::LLVMContext &LLVMCtx,
- llvm::Error &Err)
- : WrapperFrontendAction([&]() {
- llvm::ErrorAsOutParameter EAO(&Err);
- std::unique_ptr<FrontendAction> Act;
- switch (CI.getFrontendOpts().ProgramAction) {
- default:
- Err = llvm::createStringError(
- std::errc::state_not_recoverable,
- "Driver initialization failed. "
- "Incremental mode for action %d is not supported",
- CI.getFrontendOpts().ProgramAction);
- return Act;
- case frontend::ASTDump:
- [[fallthrough]];
- case frontend::ASTPrint:
- [[fallthrough]];
- case frontend::ParseSyntaxOnly:
- Act = CreateFrontendAction(CI);
- break;
- case frontend::PluginAction:
- [[fallthrough]];
- case frontend::EmitAssembly:
- [[fallthrough]];
- case frontend::EmitBC:
- [[fallthrough]];
- case frontend::EmitObj:
- [[fallthrough]];
- case frontend::PrintPreprocessedInput:
- [[fallthrough]];
- case frontend::EmitLLVMOnly:
- Act.reset(new EmitLLVMOnlyAction(&LLVMCtx));
- break;
- }
- return Act;
- }()) {}
- FrontendAction *getWrapped() const { return WrappedAction.get(); }
- TranslationUnitKind getTranslationUnitKind() override {
- return TU_Incremental;
- }
-
- void ExecuteAction() override {
- CompilerInstance &CI = getCompilerInstance();
- assert(CI.hasPreprocessor() && "No PP!");
-
- // Use a code completion consumer?
- CodeCompleteConsumer *CompletionConsumer = nullptr;
- if (CI.hasCodeCompletionConsumer())
- CompletionConsumer = &CI.getCodeCompletionConsumer();
-
- Preprocessor &PP = CI.getPreprocessor();
- PP.EnterMainSourceFile();
-
- if (!CI.hasSema())
- CI.createSema(getTranslationUnitKind(), CompletionConsumer);
- }
-
- // Do not terminate after processing the input. This allows us to keep various
- // clang objects alive and to incrementally grow the current TU.
- void EndSourceFile() override {
- // The WrappedAction can be nullptr if we issued an error in the ctor.
- if (IsTerminating && getWrapped())
- WrapperFrontendAction::EndSourceFile();
- }
-
- void FinalizeAction() {
- assert(!IsTerminating && "Already finalized!");
- IsTerminating = true;
- EndSourceFile();
- }
-};
-
-CodeGenerator *IncrementalParser::getCodeGen() const {
- FrontendAction *WrappedAct = Act->getWrapped();
- if (!WrappedAct->hasIRSupport())
- return nullptr;
- return static_cast<CodeGenAction *>(WrappedAct)->getCodeGenerator();
-}
-
IncrementalParser::IncrementalParser() {}
-IncrementalParser::IncrementalParser(Interpreter &Interp,
- std::unique_ptr<CompilerInstance> Instance,
- llvm::LLVMContext &LLVMCtx,
+IncrementalParser::IncrementalParser(std::unique_ptr<CompilerInstance> Instance,
llvm::Error &Err)
: CI(std::move(Instance)) {
llvm::ErrorAsOutParameter EAO(&Err);
- Act = std::make_unique<IncrementalAction>(*CI, LLVMCtx, Err);
- if (Err)
- return;
- CI->ExecuteAction(*Act);
- if (getCodeGen())
- CachedInCodeGenModule = GenModule();
-
- 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));
P->Initialize();
-
- // An initial PTU is needed as CUDA includes some headers automatically
- auto PTU = ParseOrWrapTopLevelDecl();
- if (auto E = PTU.takeError()) {
- consumeError(std::move(E)); // FIXME
- return; // PTU.takeError();
- }
-
- if (getCodeGen()) {
- PTU->TheModule = GenModule();
- assert(PTU->TheModule && "Failed to create initial PTU");
- }
}
-IncrementalParser::~IncrementalParser() {
- P.reset();
- Act->FinalizeAction();
-}
+IncrementalParser::~IncrementalParser() { P.reset(); }
-llvm::Expected<PartialTranslationUnit &>
+llvm::Expected<TranslationUnitDecl *>
IncrementalParser::ParseOrWrapTopLevelDecl() {
// Recover resources if we crash before exiting this method.
Sema &S = CI->getSema();
@@ -247,12 +47,9 @@ IncrementalParser::ParseOrWrapTopLevelDecl() {
Sema::GlobalEagerInstantiationScope GlobalInstantiations(S, /*Enabled=*/true);
Sema::LocalEagerInstantiationScope LocalInstantiations(S);
- PTUs.emplace_back(PartialTranslationUnit());
- PartialTranslationUnit &LastPTU = PTUs.back();
// Add a new PTU.
ASTContext &C = S.getASTContext();
C.addTranslationUnitDecl();
- LastPTU.TUPart = C.getTranslationUnitDecl();
// Skip previous eof due to last incremental input.
if (P->getCurToken().is(tok::annot_repl_input_end)) {
@@ -278,9 +75,7 @@ IncrementalParser::ParseOrWrapTopLevelDecl() {
DiagnosticsEngine &Diags = getCI()->getDiagnostics();
if (Diags.hasErrorOccurred()) {
- PartialTranslationUnit MostRecentPTU = {C.getTranslationUnitDecl(),
- nullptr};
- CleanUpPTU(MostRecentPTU);
+ CleanUpPTU(C.getTranslationUnitDecl());
Diags.Reset(/*soft=*/true);
Diags.getClient()->clear();
@@ -299,10 +94,10 @@ IncrementalParser::ParseOrWrapTopLevelDecl() {
Consumer->HandleTranslationUnit(C);
- return LastPTU;
+ return C.getTranslationUnitDecl();
}
-llvm::Expected<PartialTranslationUnit &>
+llvm::Expected<TranslationUnitDecl *>
IncrementalParser::Parse(llvm::StringRef input) {
Preprocessor &PP = CI->getPreprocessor();
assert(PP.isIncrementalProcessingEnabled() && "Not in incremental mode!?");
@@ -356,37 +151,10 @@ IncrementalParser::Parse(llvm::StringRef input) {
"Lexer must be EOF when starting incremental parse!");
}
- 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()) {
- // Clang's CodeGen is designed to work with a single llvm::Module. In many
- // cases for convenience various CodeGen parts have a reference to the
- // llvm::Module (TheModule or Module) which does not change when a new
- // module is pushed. However, the execution engine wants to take ownership
- // of the module which does not map well to CodeGen's design. To work this
- // around we created an empty module to make CodeGen happy. We should make
- // sure it always stays empty.
- assert((!CachedInCodeGenModule ||
- (CachedInCodeGenModule->empty() &&
- CachedInCodeGenModule->global_empty() &&
- CachedInCodeGenModule->alias_empty() &&
- CachedInCodeGenModule->ifunc_empty())) &&
- "CodeGen wrote to a readonly module");
- 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;
+void IncrementalParser::CleanUpPTU(TranslationUnitDecl *MostRecentTU) {
if (StoredDeclsMap *Map = MostRecentTU->getPrimaryContext()->getLookupPtr()) {
for (auto &&[Key, List] : *Map) {
DeclContextLookupResult R = List.getLookupResult();
@@ -419,9 +187,4 @@ void IncrementalParser::CleanUpPTU(PartialTranslationUnit &PTU) {
}
}
-llvm::StringRef IncrementalParser::GetMangledName(GlobalDecl GD) const {
- CodeGenerator *CG = getCodeGen();
- assert(CG);
- return CG->GetMangledName(GD);
-}
} // end namespace clang
diff --git a/clang/lib/Interpreter/IncrementalParser.h b/clang/lib/Interpreter/IncrementalParser.h
index f63bce50acd3b9..4ce361143b581b 100644
--- a/clang/lib/Interpreter/IncrementalParser.h
+++ b/clang/lib/Interpreter/IncrementalParser.h
@@ -13,35 +13,24 @@
#ifndef LLVM_CLANG_LIB_INTERPRETER_INCREMENTALPARSER_H
#define LLVM_CLANG_LIB_INTERPRETER_INCREMENTALPARSER_H
-#include "clang/AST/GlobalDecl.h"
-#include "clang/Interpreter/PartialTranslationUnit.h"
-
-#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Error.h"
#include <list>
#include <memory>
-namespace llvm {
-class LLVMContext;
-class Module;
-} // namespace llvm
namespace clang {
class ASTConsumer;
class CodeGenerator;
class CompilerInstance;
-class IncrementalAction;
-class Interpreter;
class Parser;
+class TranslationUnitDecl;
+
/// Provides support for incremental compilation. Keeps track of the state
/// changes between the subsequent incremental input.
///
class IncrementalParser {
protected:
- /// Long-lived, incremental parsing action.
- std::unique_ptr<IncrementalAction> Act;
-
/// Compiler instance performing the incremental compilation.
std::unique_ptr<CompilerInstance> CI;
@@ -54,42 +43,24 @@ class IncrementalParser {
/// Counts the number of direct user input lines that have been parsed.
unsigned InputCount = 0;
- /// List containing every information about every incrementally parsed piece
- /// of code.
- std::list<PartialTranslationUnit> PTUs;
-
- /// When CodeGen is created the first llvm::Module gets cached in many places
- /// and we must keep it alive.
- std::unique_ptr<llvm::Module> CachedInCodeGenModule;
-
IncrementalParser();
public:
- IncrementalParser(Interpreter &Interp,
- std::unique_ptr<CompilerInstance> Instance,
- llvm::LLVMContext &LLVMCtx, llvm::Error &Err);
+ IncrementalParser(std::unique_ptr<CompilerInstance> Instance,
+ llvm::Error &Err);
virtual ~IncrementalParser();
CompilerInstance *getCI() { return CI.get(); }
- CodeGenerator *getCodeGen() const;
/// Parses incremental input by creating an in-memory file.
///\returns a \c PartialTranslationUnit which holds information about the
- /// \c TranslationUnitDecl and \c llvm::Module corresponding to the input.
- virtual llvm::Expected<PartialTranslationUnit &> Parse(llvm::StringRef Input);
-
- /// Uses the CodeGenModule mangled name cache and avoids recomputing.
- ///\returns the mangled name of a \c GD.
- llvm::StringRef GetMangledName(GlobalDecl GD) const;
-
- void CleanUpPTU(PartialTranslationUnit &PTU);
-
- std::list<PartialTranslationUnit> &getPTUs() { return PTUs; }
+ /// \c TranslationUnitDecl.
+ virtual llvm::Expected<TranslationUnitDecl *> Parse(llvm::StringRef Input);
- std::unique_ptr<llvm::Module> GenModule();
+ void CleanUpPTU(TranslationUnitDecl *MostRecentTU);
private:
- llvm::Expected<PartialTranslationUnit &> ParseOrWrapTopLevelDecl();
+ llvm::Expected<TranslationUnitDecl *> ParseOrWrapTopLevelDecl();
};
} // end namespace clang
diff --git a/clang/lib/Interpreter/Interpreter.cpp b/clang/lib/Interpreter/Interpreter.cpp
index 7209a33272ef22..e1135c2cf88fe3 100644
--- a/clang/lib/Interpreter/Interpreter.cpp
+++ b/clang/lib/Interpreter/Interpreter.cpp
@@ -19,6 +19,7 @@
#include "Wasm.h"
#endif // __EMSCRIPTEN__
+#include "clang/AST/ASTConsumer.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Mangle.h"
#include "clang/AST/TypeVisitor.h"
@@ -33,7 +34,10 @@
#include "clang/Driver/Options.h"
#include "clang/Driver/Tool.h"
#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/FrontendAction.h"
+#include "clang/Frontend/MultiplexConsumer.h"
#include "clang/Frontend/TextDiagnosticBuffer.h"
+#include "clang/FrontendTool/Utils.h"
#include "clang/Interpreter/Interpreter.h"
#include "clang/Interpreter/Value.h"
#include "clang/Lex/PreprocessorOptions.h"
@@ -47,10 +51,7 @@
#include "llvm/Support/raw_ostream.h"
#include "llvm/TargetParser/Host.h"
-#include <cstdarg>
-
using namespace clang;
-
// FIXME: Figure out how to unify with namespace init_convenience from
// tools/clang-import-test/clang-import-test.cpp
namespace {
@@ -138,6 +139,8 @@ CreateCI(const llvm::opt::ArgStringList &Argv) {
} // anonymous namespace
+namespace clang {
+
llvm::Expected<std::unique_ptr<CompilerInstance>>
IncrementalCompilerBuilder::create(std::string TT,
std::vector<const char *> &ClangArgv) {
@@ -241,20 +244,177 @@ IncrementalCompilerBuilder::CreateCudaHost() {
return IncrementalCompilerBuilder::createCuda(false);
}
+class InProcessPrintingASTConsumer final : public MultiplexConsumer {
+ Interpreter &Interp;
+
+public:
+ InProcessPrintingASTConsumer(std::unique_ptr<ASTConsumer> C, Interpreter &I)
+ : MultiplexConsumer(std::move(C)), Interp(I) {}
+ bool HandleTopLevelDecl(DeclGroupRef DGR) override final {
+ if (DGR.isNull())
+ return true;
+ // if (!Consumer)
+ // return true;
+
+ for (Decl *D : DGR)
+ if (auto *TLSD = llvm::dyn_cast<TopLevelStmtDecl>(D))
+ if (TLSD && TLSD->isSemiMissing()) {
+ auto ExprOrErr =
+ Interp.AttachValuePrinting(cast<Expr>(TLSD->getStmt()));
+ if (llvm::Error E = ExprOrErr.takeError()) {
+ llvm::logAllUnhandledErrors(std::move(E), llvm::errs(),
+ "Value printing failed: ");
+ return false; // abort parsing
+ }
+ TLSD->setStmt(*ExprOrErr);
+ }
+
+ return MultiplexConsumer::HandleTopLevelDecl(DGR);
+ }
+};
+
+/// A custom action enabling the incremental processing functionality.
+///
+/// The usual \p FrontendAction expects one call to ExecuteAction and once it
+/// sees a call to \p EndSourceFile it deletes some of the important objects
+/// such as \p Preprocessor and \p Sema assuming no further input will come.
+///
+/// \p IncrementalAction ensures it keep its underlying action's objects alive
+/// as long as the \p IncrementalParser needs them.
+///
+class IncrementalAction : public WrapperFrontendAction {
+private:
+ bool IsTerminating = false;
+ Interpreter &Interp;
+ std::unique_ptr<ASTConsumer> Consumer;
+
+public:
+ IncrementalAction(CompilerInstance &CI, llvm::LLVMContext &LLVMCtx,
+ llvm::Error &Err, Interpreter &I,
+ std::unique_ptr<ASTConsumer> Consumer = nullptr)
+ : WrapperFrontendAction([&]() {
+ llvm::ErrorAsOutParameter EAO(&Err);
+ std::unique_ptr<FrontendAction> Act;
+ switch (CI.getFrontendOpts().ProgramAction) {
+ default:
+ Err = llvm::createStringError(
+ std::errc::state_not_recoverable,
+ "Driver initialization failed. "
+ "Incremental mode for action %d is not supported",
+ CI.getFrontendOpts().ProgramAction);
+ return Act;
+ case frontend::ASTDump:
+ [[fallthrough]];
+ case frontend::ASTPrint:
+ [[fallthrough]];
+ case frontend::ParseSyntaxOnly:
+ Act = CreateFrontendAction(CI);
+ break;
+ case frontend::PluginAction:
+ [[fallthrough]];
+ case frontend::EmitAssembly:
+ [[fallthrough]];
+ case frontend::EmitBC:
+ [[fallthrough]];
+ case frontend::EmitObj:
+ [[fallthrough]];
+ case frontend::PrintPreprocessedInput:
+ [[fallthrough]];
+ case frontend::EmitLLVMOnly:
+ Act.reset(new EmitLLVMOnlyAction(&LLVMCtx));
+ break;
+ }
+ return Act;
+ }()),
+ Interp(I), Consumer(std::move(Consumer)) {}
+ FrontendAction *getWrapped() const { return WrappedAction.get(); }
+ TranslationUnitKind getTranslationUnitKind() override {
+ return TU_Incremental;
+ }
+
+ std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
+ StringRef InFile) override {
+ std::unique_ptr<ASTConsumer> C =
+ WrapperFrontendAction::CreateASTConsumer(CI, InFile);
+
+ if (Consumer) {
+ std::vector<std::unique_ptr<ASTConsumer>> Cs;
+ Cs.push_back(std::move(Consumer));
+ Cs.push_back(std::move(C));
+ return std::make_unique<MultiplexConsumer>(std::move(Cs));
+ }
+
+ return std::make_unique<InProcessPrintingASTConsumer>(std::move(C), Interp);
+ }
+
+ void ExecuteAction() override {
+ CompilerInstance &CI = getCompilerInstance();
+ assert(CI.hasPreprocessor() && "No PP!");
+
+ // Use a code completion consumer?
+ CodeCompleteConsumer *CompletionConsumer = nullptr;
+ if (CI.hasCodeCompletionConsumer())
+ CompletionConsumer = &CI.getCodeCompletionConsumer();
+
+ Preprocessor &PP = CI.getPreprocessor();
+ PP.EnterMainSourceFile();
+
+ if (!CI.hasSema())
+ CI.createSema(getTranslationUnitKind(), CompletionConsumer);
+ }
+
+ // Do not terminate after processing the input. This allows us to keep various
+ // clang objects alive and to incrementally grow the current TU.
+ void EndSourceFile() override {
+ // The WrappedAction can be nullptr if we issued an error in the ctor.
+ if (IsTerminating && getWrapped())
+ WrapperFrontendAction::EndSourceFile();
+ }
+
+ void FinalizeAction() {
+ assert(!IsTerminating && "Already finalized!");
+ IsTerminating = true;
+ EndSourceFile();
+ }
+};
+
Interpreter::Interpreter(std::unique_ptr<CompilerInstance> CI,
llvm::Error &ErrOut,
- std::unique_ptr<llvm::orc::LLJITBuilder> JITBuilder)
+ std::unique_ptr<llvm::orc::LLJITBuilder> JITBuilder,
+ std::unique_ptr<clang::ASTConsumer> Consumer)
: JITBuilder(std::move(JITBuilder)) {
llvm::ErrorAsOutParameter EAO(&ErrOut);
auto LLVMCtx = std::make_unique<llvm::LLVMContext>();
TSCtx = std::make_unique<llvm::orc::ThreadSafeContext>(std::move(LLVMCtx));
- IncrParser = std::make_unique<IncrementalParser>(
- *this, std::move(CI), *TSCtx->getContext(), ErrOut);
+
+ Act = std::make_unique<IncrementalAction>(*CI, *TSCtx->getContext(), ErrOut,
+ *this, std::move(Consumer));
+ if (ErrOut)
+ return;
+ CI->ExecuteAction(*Act);
+
+ if (getCodeGen())
+ CachedInCodeGenModule = GenModule();
+
+ IncrParser = std::make_unique<IncrementalParser>(std::move(CI), ErrOut);
+
+ // An initial PTU is needed as CUDA includes some headers automatically.
+ auto PTU = Parse("");
+ if (auto E = PTU.takeError()) {
+ ErrOut = joinErrors(std::move(ErrOut), std::move(E));
+ return;
+ }
+
+ if (getCodeGen()) {
+ PTU->TheModule = GenModule();
+ assert(PTU->TheModule && "Failed to create initial PTU");
+ }
+
if (ErrOut)
return;
// Not all frontends support code-generation, e.g. ast-dump actions don't
- if (IncrParser->getCodeGen()) {
+ if (getCodeGen()) {
if (llvm::Error Err = CreateExecutor()) {
ErrOut = joinErrors(std::move(ErrOut), std::move(Err));
return;
@@ -262,7 +422,7 @@ Interpreter::Interpreter(std::unique_ptr<CompilerInstance> CI,
// Process the PTUs that came from initialization. For example -include will
// give us a header that's processed at initialization of the preprocessor.
- for (PartialTranslationUnit &PTU : IncrParser->getPTUs())
+ for (PartialTranslationUnit &PTU : PTUs)
if (llvm::Error Err = Execute(PTU)) {
ErrOut = joinErrors(std::move(ErrOut), std::move(Err));
return;
@@ -271,6 +431,8 @@ Interpreter::Interpreter(std::unique_ptr<CompilerInstance> CI,
}
Interpreter::~Interpreter() {
+ Act->FinalizeAction();
+ IncrParser.reset();
if (IncrExecutor) {
if (llvm::Error Err = IncrExecutor->cleanUp())
llvm::report_fatal_error(
@@ -342,8 +504,8 @@ Interpreter::createWithCUDA(std::unique_ptr<CompilerInstance> CI,
llvm::Error Err = llvm::Error::success();
auto DeviceParser = std::make_unique<IncrementalCUDADeviceParser>(
- **Interp, std::move(DCI), *(*Interp)->IncrParser.get(),
- *(*Interp)->TSCtx->getContext(), IMVFS, Err);
+ std::move(DCI), *(*Interp)->IncrParser.get(), IMVFS, Err,
+ (*Interp)->PTUs);
if (Err)
return std::move(Err);
@@ -379,22 +541,21 @@ const ASTContext &Interpreter::getASTContext() const {
void Interpreter::markUserCodeStart() {
assert(!InitPTUSize && "We only do this once");
- InitPTUSize = IncrParser->getPTUs().size();
+ InitPTUSize = PTUs.size();
}
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) {
- // If we have a device parser, parse it first.
- // The generated code will be included in the host compilation
+ // If we have a device parser, parse it first. The generated code will be
+ // included in the host compilation
if (DeviceParser) {
- auto DevicePTU = DeviceParser->Parse(Code);
- if (auto E = DevicePTU.takeError())
+ llvm::Expected<TranslationUnitDecl *> DeviceTU = DeviceParser->Parse(Code);
+ if (auto E = DeviceTU.takeError())
return std::move(E);
}
@@ -402,7 +563,19 @@ Interpreter::Parse(llvm::StringRef Code) {
// printing could cause it.
getCompilerInstance()->getDiagnostics().setSeverity(
clang::diag::warn_unused_expr, diag::Severity::Ignored, SourceLocation());
- return IncrParser->Parse(Code);
+
+ llvm::Expected<TranslationUnitDecl *> TuOrErr = IncrParser->Parse(Code);
+ if (!TuOrErr)
+ return TuOrErr.takeError();
+
+ PTUs.emplace_back(PartialTranslationUnit());
+ PartialTranslationUnit &LastPTU = PTUs.back();
+ LastPTU.TUPart = *TuOrErr;
+
+ if (std::unique_ptr<llvm::Module> M = GenModule())
+ LastPTU.TheModule = std::move(M);
+
+ return LastPTU;
}
static llvm::Expected<llvm::orc::JITTargetMachineBuilder>
@@ -420,7 +593,7 @@ llvm::Error Interpreter::CreateExecutor() {
return llvm::make_error<llvm::StringError>("Operation failed. "
"Execution engine exists",
std::error_code());
- if (!IncrParser->getCodeGen())
+ if (!getCodeGen())
return llvm::make_error<llvm::StringError>("Operation failed. "
"No code generator available",
std::error_code());
@@ -492,7 +665,7 @@ Interpreter::getSymbolAddress(GlobalDecl GD) const {
return llvm::make_error<llvm::StringError>("Operation failed. "
"No execution engine",
std::error_code());
- llvm::StringRef MangledName = IncrParser->GetMangledName(GD);
+ llvm::StringRef MangledName = getCodeGen()->GetMangledName(GD);
return getSymbolAddress(MangledName);
}
@@ -518,7 +691,6 @@ Interpreter::getSymbolAddressFromLinkerName(llvm::StringRef Name) const {
llvm::Error Interpreter::Undo(unsigned N) {
- std::list<PartialTranslationUnit> &PTUs = IncrParser->getPTUs();
if (N > getEffectivePTUSize())
return llvm::make_error<llvm::StringError>("Operation failed. "
"Too many undos",
@@ -529,7 +701,7 @@ llvm::Error Interpreter::Undo(unsigned N) {
return Err;
}
- IncrParser->CleanUpPTU(PTUs.back());
+ IncrParser->CleanUpPTU(PTUs.back().TUPart);
PTUs.pop_back();
}
return llvm::Error::success();
@@ -551,416 +723,32 @@ 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", "__ci_newtag"};
-
-static std::unique_ptr<RuntimeInterfaceBuilder>
-createInProcessRuntimeInterfaceBuilder(Interpreter &Interp, ASTContext &Ctx,
- Sema &S);
-
-std::unique_ptr<RuntimeInterfaceBuilder> Interpreter::FindRuntimeInterface() {
- if (llvm::all_of(ValuePrintingInfo, [](Expr *E) { return E != nullptr; }))
- return nullptr;
-
- 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,
- RedeclarationKind::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 nullptr;
- if (Ctx.getLangOpts().CPlusPlus) {
- if (!LookupInterface(ValuePrintingInfo[WithAlloc],
- MagicRuntimeInterface[WithAlloc]))
- return nullptr;
- if (!LookupInterface(ValuePrintingInfo[CopyArray],
- MagicRuntimeInterface[CopyArray]))
- return nullptr;
- if (!LookupInterface(ValuePrintingInfo[NewTag],
- MagicRuntimeInterface[NewTag]))
- return nullptr;
+std::unique_ptr<llvm::Module> Interpreter::GenModule() {
+ static unsigned ID = 0;
+ if (CodeGenerator *CG = getCodeGen()) {
+ // Clang's CodeGen is designed to work with a single llvm::Module. In many
+ // cases for convenience various CodeGen parts have a reference to the
+ // llvm::Module (TheModule or Module) which does not change when a new
+ // module is pushed. However, the execution engine wants to take ownership
+ // of the module which does not map well to CodeGen's design. To work this
+ // around we created an empty module to make CodeGen happy. We should make
+ // sure it always stays empty.
+ assert((!CachedInCodeGenModule || (CachedInCodeGenModule->empty() &&
+ CachedInCodeGenModule->global_empty() &&
+ CachedInCodeGenModule->alias_empty() &&
+ CachedInCodeGenModule->ifunc_empty())) &&
+ "CodeGen wrote to a readonly module");
+ std::unique_ptr<llvm::Module> M(CG->ReleaseModule());
+ CG->StartModule("incr_module_" + std::to_string(ID++), M->getContext());
+ return M;
}
-
- return createInProcessRuntimeInterfaceBuilder(*this, Ctx, S);
+ return nullptr;
}
-namespace {
-
-class InterfaceKindVisitor
- : public TypeVisitor<InterfaceKindVisitor, Interpreter::InterfaceKind> {
- friend class InProcessRuntimeInterfaceBuilder;
-
- ASTContext &Ctx;
- Sema &S;
- Expr *E;
- llvm::SmallVector<Expr *, 3> Args;
-
-public:
- InterfaceKindVisitor(ASTContext &Ctx, Sema &S, Expr *E)
- : Ctx(Ctx), S(S), E(E) {}
-
- 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 the uint that fits the register size. That way we
- // reduce the number of overloads of `__clang_Interpreter_SetValueNoAlloc`.
- void HandleIntegralOrEnumType(const Type *Ty) {
- uint64_t PtrBits = Ctx.getTypeSize(Ctx.VoidPtrTy);
- QualType UIntTy = Ctx.getBitIntType(/*Unsigned=*/true, PtrBits);
- TypeSourceInfo *TSI = Ctx.getTrivialTypeSourceInfo(UIntTy);
- 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());
- }
-};
-
-class InProcessRuntimeInterfaceBuilder : public RuntimeInterfaceBuilder {
- Interpreter &Interp;
- ASTContext &Ctx;
- Sema &S;
-
-public:
- InProcessRuntimeInterfaceBuilder(Interpreter &Interp, ASTContext &C, Sema &S)
- : Interp(Interp), Ctx(C), S(S) {}
-
- TransformExprFunction *getPrintValueTransformer() override {
- return &transformForValuePrinting;
- }
-
-private:
- static ExprResult transformForValuePrinting(RuntimeInterfaceBuilder *Builder,
- Expr *E,
- ArrayRef<Expr *> FixedArgs) {
- auto *B = static_cast<InProcessRuntimeInterfaceBuilder *>(Builder);
-
- // Get rid of ExprWithCleanups.
- if (auto *EWC = llvm::dyn_cast_if_present<ExprWithCleanups>(E))
- E = EWC->getSubExpr();
-
- InterfaceKindVisitor Visitor(B->Ctx, B->S, E);
-
- // The Interpreter* parameter and the out parameter `OutVal`.
- for (Expr *E : FixedArgs)
- Visitor.Args.push_back(E);
-
- QualType Ty = E->getType();
- QualType DesugaredTy = Ty.getDesugaredType(B->Ctx);
-
- // For lvalue struct, we treat it as a reference.
- if (DesugaredTy->isRecordType() && E->isLValue()) {
- DesugaredTy = B->Ctx.getLValueReferenceType(DesugaredTy);
- Ty = B->Ctx.getLValueReferenceType(Ty);
- }
-
- Expr *TypeArg = CStyleCastPtrExpr(B->S, B->Ctx.VoidPtrTy,
- (uintptr_t)Ty.getAsOpaquePtr());
- // The QualType parameter `OpaqueType`, represented as `void*`.
- Visitor.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 = Visitor.Visit(&*DesugaredTy);
- switch (Kind) {
- case Interpreter::InterfaceKind::WithAlloc:
- case Interpreter::InterfaceKind::CopyArray: {
- // __clang_Interpreter_SetValueWithAlloc.
- ExprResult AllocCall = B->S.ActOnCallExpr(
- /*Scope=*/nullptr,
- B->Interp
- .getValuePrintingInfo()[Interpreter::InterfaceKind::WithAlloc],
- E->getBeginLoc(), Visitor.Args, E->getEndLoc());
- assert(!AllocCall.isInvalid() && "Can't create runtime interface call!");
-
- TypeSourceInfo *TSI =
- B->Ctx.getTrivialTypeSourceInfo(Ty, SourceLocation());
-
- // Force CodeGen to emit destructor.
- if (auto *RD = Ty->getAsCXXRecordDecl()) {
- auto *Dtor = B->S.LookupDestructor(RD);
- Dtor->addAttr(UsedAttr::CreateImplicit(B->Ctx));
- B->Interp.getCompilerInstance()->getASTConsumer().HandleTopLevelDecl(
- DeclGroupRef(Dtor));
- }
-
- // __clang_Interpreter_SetValueCopyArr.
- if (Kind == Interpreter::InterfaceKind::CopyArray) {
- const auto *ConstantArrTy =
- cast<ConstantArrayType>(DesugaredTy.getTypePtr());
- size_t ArrSize = B->Ctx.getConstantArrayElementCount(ConstantArrTy);
- Expr *ArrSizeExpr = IntegerLiteralExpr(B->Ctx, ArrSize);
- Expr *Args[] = {E, AllocCall.get(), ArrSizeExpr};
- return B->S.ActOnCallExpr(
- /*Scope *=*/nullptr,
- B->Interp
- .getValuePrintingInfo()[Interpreter::InterfaceKind::CopyArray],
- SourceLocation(), Args, SourceLocation());
- }
- Expr *Args[] = {
- AllocCall.get(),
- B->Interp.getValuePrintingInfo()[Interpreter::InterfaceKind::NewTag]};
- ExprResult CXXNewCall = B->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 B->S.ActOnFinishFullExpr(CXXNewCall.get(),
- /*DiscardedValue=*/false);
- }
- // __clang_Interpreter_SetValueNoAlloc.
- case Interpreter::InterfaceKind::NoAlloc: {
- return B->S.ActOnCallExpr(
- /*Scope=*/nullptr,
- B->Interp.getValuePrintingInfo()[Interpreter::InterfaceKind::NoAlloc],
- E->getBeginLoc(), Visitor.Args, E->getEndLoc());
- }
- default:
- llvm_unreachable("Unhandled Interpreter::InterfaceKind");
- }
- }
-};
-} // namespace
-
-static std::unique_ptr<RuntimeInterfaceBuilder>
-createInProcessRuntimeInterfaceBuilder(Interpreter &Interp, ASTContext &Ctx,
- Sema &S) {
- return std::make_unique<InProcessRuntimeInterfaceBuilder>(Interp, Ctx, S);
-}
-
-// 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 (!RuntimeIB) {
- RuntimeIB = FindRuntimeInterface();
- AddPrintValueCall = RuntimeIB->getPrintValueTransformer();
- }
-
- assert(AddPrintValueCall &&
- "We don't have a runtime interface 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.
- ExprResult Result =
- AddPrintValueCall(RuntimeIB.get(), E, {ThisInterp, OutValue});
-
- // 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();
-}
-
-extern "C" void REPL_EXTERNAL_VISIBILITY __clang_Interpreter_SetValueNoAlloc(
- void *This, void *OutVal, void *OpaqueType, ...) {
- Value &VRef = *(Value *)OutVal;
- Interpreter *I = static_cast<Interpreter *>(This);
- VRef = Value(I, OpaqueType);
- if (VRef.isVoid())
- return;
-
- va_list args;
- va_start(args, /*last named param*/ OpaqueType);
-
- QualType QT = VRef.getType();
- if (VRef.getKind() == Value::K_PtrOrObj) {
- VRef.setPtr(va_arg(args, void *));
- } else {
- if (const auto *ET = QT->getAs<EnumType>())
- QT = ET->getDecl()->getIntegerType();
- switch (QT->castAs<BuiltinType>()->getKind()) {
- default:
- llvm_unreachable("unknown type kind!");
- break;
- // Types shorter than int are resolved as int, else va_arg has UB.
- case BuiltinType::Bool:
- VRef.setBool(va_arg(args, int));
- break;
- case BuiltinType::Char_S:
- VRef.setChar_S(va_arg(args, int));
- break;
- case BuiltinType::SChar:
- VRef.setSChar(va_arg(args, int));
- break;
- case BuiltinType::Char_U:
- VRef.setChar_U(va_arg(args, unsigned));
- break;
- case BuiltinType::UChar:
- VRef.setUChar(va_arg(args, unsigned));
- break;
- case BuiltinType::Short:
- VRef.setShort(va_arg(args, int));
- break;
- case BuiltinType::UShort:
- VRef.setUShort(va_arg(args, unsigned));
- break;
- case BuiltinType::Int:
- VRef.setInt(va_arg(args, int));
- break;
- case BuiltinType::UInt:
- VRef.setUInt(va_arg(args, unsigned));
- break;
- case BuiltinType::Long:
- VRef.setLong(va_arg(args, long));
- break;
- case BuiltinType::ULong:
- VRef.setULong(va_arg(args, unsigned long));
- break;
- case BuiltinType::LongLong:
- VRef.setLongLong(va_arg(args, long long));
- break;
- case BuiltinType::ULongLong:
- VRef.setULongLong(va_arg(args, unsigned long long));
- break;
- // Types shorter than double are resolved as double, else va_arg has UB.
- case BuiltinType::Float:
- VRef.setFloat(va_arg(args, double));
- break;
- case BuiltinType::Double:
- VRef.setDouble(va_arg(args, double));
- break;
- case BuiltinType::LongDouble:
- VRef.setLongDouble(va_arg(args, long double));
- break;
- // See REPL_BUILTIN_TYPES.
- }
- }
- va_end(args);
-}
-
-// A trampoline to work around the fact that operator placement new cannot
-// really be forward declared due to libc++ and libstdc++ declaration mismatch.
-// FIXME: __clang_Interpreter_NewTag is ODR violation because we get the same
-// definition in the interpreter runtime. We should move it in a runtime header
-// which gets included by the interpreter and here.
-struct __clang_Interpreter_NewTag {};
-REPL_EXTERNAL_VISIBILITY void *
-operator new(size_t __sz, void *__p, __clang_Interpreter_NewTag) noexcept {
- // Just forward to the standard operator placement new.
- return operator new(__sz, __p);
+CodeGenerator *Interpreter::getCodeGen() const {
+ FrontendAction *WrappedAct = Act->getWrapped();
+ if (!WrappedAct->hasIRSupport())
+ return nullptr;
+ return static_cast<CodeGenAction *>(WrappedAct)->getCodeGenerator();
}
+} // namespace clang
diff --git a/clang/lib/Interpreter/InterpreterValuePrinter.cpp b/clang/lib/Interpreter/InterpreterValuePrinter.cpp
new file mode 100644
index 00000000000000..6673c2f773785d
--- /dev/null
+++ b/clang/lib/Interpreter/InterpreterValuePrinter.cpp
@@ -0,0 +1,400 @@
+//===--- InterpreterValuePrinter.cpp - Value printing 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 routines for in-process value printing in clang-repl.
+//
+//===----------------------------------------------------------------------===//
+
+#include "IncrementalParser.h"
+#include "InterpreterUtils.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/PrettyPrinter.h"
+#include "clang/AST/Type.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Interpreter/Interpreter.h"
+#include "clang/Interpreter/Value.h"
+#include "clang/Lex/Preprocessor.h"
+#include "clang/Sema/Lookup.h"
+#include "clang/Sema/Sema.h"
+
+#include "llvm/Support/Error.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <cassert>
+#include <string>
+
+#include <cstdarg>
+
+namespace clang {
+
+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 =
+ getCodeGen()->GetMangledName(GlobalDecl(DtorRD, Dtor_Base));
+ auto AddrOrErr = getSymbolAddress(Name);
+ if (!AddrOrErr)
+ return AddrOrErr.takeError();
+
+ Dtors[CXXRD] = *AddrOrErr;
+ return AddrOrErr;
+}
+
+enum InterfaceKind { NoAlloc, WithAlloc, CopyArray, NewTag };
+
+class InterfaceKindVisitor
+ : public TypeVisitor<InterfaceKindVisitor, InterfaceKind> {
+
+ Sema &S;
+ Expr *E;
+ llvm::SmallVectorImpl<Expr *> &Args;
+
+public:
+ InterfaceKindVisitor(Sema &S, Expr *E, llvm::SmallVectorImpl<Expr *> &Args)
+ : S(S), E(E), Args(Args) {}
+
+ InterfaceKind computeInterfaceKind(QualType Ty) {
+ return Visit(Ty.getTypePtr());
+ }
+
+ InterfaceKind VisitRecordType(const RecordType *Ty) {
+ return InterfaceKind::WithAlloc;
+ }
+
+ InterfaceKind VisitMemberPointerType(const MemberPointerType *Ty) {
+ return InterfaceKind::WithAlloc;
+ }
+
+ InterfaceKind VisitConstantArrayType(const ConstantArrayType *Ty) {
+ return InterfaceKind::CopyArray;
+ }
+
+ InterfaceKind VisitFunctionProtoType(const FunctionProtoType *Ty) {
+ HandlePtrType(Ty);
+ return InterfaceKind::NoAlloc;
+ }
+
+ InterfaceKind VisitPointerType(const PointerType *Ty) {
+ HandlePtrType(Ty);
+ return InterfaceKind::NoAlloc;
+ }
+
+ 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 InterfaceKind::NoAlloc;
+ }
+
+ 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 InterfaceKind::NoAlloc;
+ }
+
+ InterfaceKind VisitEnumType(const EnumType *Ty) {
+ HandleIntegralOrEnumType(Ty);
+ return InterfaceKind::NoAlloc;
+ }
+
+private:
+ // Force cast these types to the uint that fits the register size. That way we
+ // reduce the number of overloads of `__clang_Interpreter_SetValueNoAlloc`.
+ void HandleIntegralOrEnumType(const Type *Ty) {
+ ASTContext &Ctx = S.getASTContext();
+ uint64_t PtrBits = Ctx.getTypeSize(Ctx.VoidPtrTy);
+ QualType UIntTy = Ctx.getBitIntType(/*Unsigned=*/true, PtrBits);
+ TypeSourceInfo *TSI = Ctx.getTrivialTypeSourceInfo(UIntTy);
+ 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) {
+ ASTContext &Ctx = S.getASTContext();
+ 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());
+ }
+};
+
+// 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);
+llvm::Expected<Expr *> Interpreter::AttachValuePrinting(Expr *E) {
+ Sema &S = getCompilerInstance()->getSema();
+ ASTContext &Ctx = S.getASTContext();
+
+ // Find the value printing builtins.
+ if (!ValuePrintingInfo[0]) {
+ assert(llvm::all_of(ValuePrintingInfo, [](Expr *E) { return !E; }));
+
+ auto LookupInterface = [&](Expr *&Interface,
+ llvm::StringRef Name) -> llvm::Error {
+ LookupResult R(S, &Ctx.Idents.get(Name), SourceLocation(),
+ Sema::LookupOrdinaryName,
+ RedeclarationKind::ForVisibleRedeclaration);
+ S.LookupQualifiedName(R, Ctx.getTranslationUnitDecl());
+ if (R.empty())
+ return llvm::make_error<llvm::StringError>(
+ Name + " not found!", llvm::inconvertibleErrorCode());
+
+ CXXScopeSpec CSS;
+ Interface = S.BuildDeclarationNameExpr(CSS, R, /*ADL=*/false).get();
+ return llvm::Error::success();
+ };
+ static constexpr llvm::StringRef Builtin[] = {
+ "__clang_Interpreter_SetValueNoAlloc",
+ "__clang_Interpreter_SetValueWithAlloc",
+ "__clang_Interpreter_SetValueCopyArr", "__ci_newtag"};
+ if (llvm::Error Err =
+ LookupInterface(ValuePrintingInfo[NoAlloc], Builtin[NoAlloc]))
+ return std::move(Err);
+
+ if (Ctx.getLangOpts().CPlusPlus) {
+ if (llvm::Error Err =
+ LookupInterface(ValuePrintingInfo[WithAlloc], Builtin[WithAlloc]))
+ return std::move(Err);
+ if (llvm::Error Err =
+ LookupInterface(ValuePrintingInfo[CopyArray], Builtin[CopyArray]))
+ return std::move(Err);
+ if (llvm::Error Err =
+ LookupInterface(ValuePrintingInfo[NewTag], Builtin[NewTag]))
+ return std::move(Err);
+ }
+ }
+
+ llvm::SmallVector<Expr *, 4> AdjustedArgs;
+ // Create parameter `ThisInterp`.
+ AdjustedArgs.push_back(CStyleCastPtrExpr(S, Ctx.VoidPtrTy, (uintptr_t)this));
+
+ // Create parameter `OutVal`.
+ AdjustedArgs.push_back(
+ CStyleCastPtrExpr(S, Ctx.VoidPtrTy, (uintptr_t)&LastValue));
+
+ // Build `__clang_Interpreter_SetValue*` call.
+
+ // Get rid of ExprWithCleanups.
+ if (auto *EWC = llvm::dyn_cast_if_present<ExprWithCleanups>(E))
+ E = EWC->getSubExpr();
+
+ 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*`.
+ AdjustedArgs.push_back(TypeArg);
+
+ // We push the last parameter based on the type of the Expr. Note we need
+ // special care for rvalue struct.
+ InterfaceKindVisitor V(S, E, AdjustedArgs);
+ Scope *Scope = nullptr;
+ ExprResult SetValueE;
+ InterfaceKind Kind = V.computeInterfaceKind(DesugaredTy);
+ switch (Kind) {
+ case InterfaceKind::WithAlloc:
+ LLVM_FALLTHROUGH;
+ case InterfaceKind::CopyArray: {
+ // __clang_Interpreter_SetValueWithAlloc.
+ ExprResult AllocCall =
+ S.ActOnCallExpr(Scope, ValuePrintingInfo[InterfaceKind::WithAlloc],
+ E->getBeginLoc(), AdjustedArgs, 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));
+ getCompilerInstance()->getASTConsumer().HandleTopLevelDecl(
+ DeclGroupRef(Dtor));
+ }
+
+ // __clang_Interpreter_SetValueCopyArr.
+ if (Kind == 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};
+ SetValueE =
+ S.ActOnCallExpr(Scope, ValuePrintingInfo[InterfaceKind::CopyArray],
+ SourceLocation(), Args, SourceLocation());
+ }
+ Expr *Args[] = {AllocCall.get(), ValuePrintingInfo[InterfaceKind::NewTag]};
+ 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!");
+
+ SetValueE = S.ActOnFinishFullExpr(CXXNewCall.get(),
+ /*DiscardedValue=*/false);
+ break;
+ }
+ // __clang_Interpreter_SetValueNoAlloc.
+ case InterfaceKind::NoAlloc: {
+ SetValueE =
+ S.ActOnCallExpr(Scope, ValuePrintingInfo[InterfaceKind::NoAlloc],
+ E->getBeginLoc(), AdjustedArgs, E->getEndLoc());
+ break;
+ }
+ default:
+ llvm_unreachable("Unhandled InterfaceKind");
+ }
+
+ // It could fail, like printing an array type in C. (not supported)
+ if (SetValueE.isInvalid())
+ return E;
+
+ return SetValueE.get();
+}
+
+} // namespace clang
+
+using namespace clang;
+
+// 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();
+}
+
+extern "C" void REPL_EXTERNAL_VISIBILITY __clang_Interpreter_SetValueNoAlloc(
+ void *This, void *OutVal, void *OpaqueType, ...) {
+ Value &VRef = *(Value *)OutVal;
+ Interpreter *I = static_cast<Interpreter *>(This);
+ VRef = Value(I, OpaqueType);
+ if (VRef.isVoid())
+ return;
+
+ va_list args;
+ va_start(args, /*last named param*/ OpaqueType);
+
+ QualType QT = VRef.getType();
+ if (VRef.getKind() == Value::K_PtrOrObj) {
+ VRef.setPtr(va_arg(args, void *));
+ } else {
+ if (const auto *ET = QT->getAs<EnumType>())
+ QT = ET->getDecl()->getIntegerType();
+ switch (QT->castAs<BuiltinType>()->getKind()) {
+ default:
+ llvm_unreachable("unknown type kind!");
+ break;
+ // Types shorter than int are resolved as int, else va_arg has UB.
+ case BuiltinType::Bool:
+ VRef.setBool(va_arg(args, int));
+ break;
+ case BuiltinType::Char_S:
+ VRef.setChar_S(va_arg(args, int));
+ break;
+ case BuiltinType::SChar:
+ VRef.setSChar(va_arg(args, int));
+ break;
+ case BuiltinType::Char_U:
+ VRef.setChar_U(va_arg(args, unsigned));
+ break;
+ case BuiltinType::UChar:
+ VRef.setUChar(va_arg(args, unsigned));
+ break;
+ case BuiltinType::Short:
+ VRef.setShort(va_arg(args, int));
+ break;
+ case BuiltinType::UShort:
+ VRef.setUShort(va_arg(args, unsigned));
+ break;
+ case BuiltinType::Int:
+ VRef.setInt(va_arg(args, int));
+ break;
+ case BuiltinType::UInt:
+ VRef.setUInt(va_arg(args, unsigned));
+ break;
+ case BuiltinType::Long:
+ VRef.setLong(va_arg(args, long));
+ break;
+ case BuiltinType::ULong:
+ VRef.setULong(va_arg(args, unsigned long));
+ break;
+ case BuiltinType::LongLong:
+ VRef.setLongLong(va_arg(args, long long));
+ break;
+ case BuiltinType::ULongLong:
+ VRef.setULongLong(va_arg(args, unsigned long long));
+ break;
+ // Types shorter than double are resolved as double, else va_arg has UB.
+ case BuiltinType::Float:
+ VRef.setFloat(va_arg(args, double));
+ break;
+ case BuiltinType::Double:
+ VRef.setDouble(va_arg(args, double));
+ break;
+ case BuiltinType::LongDouble:
+ VRef.setLongDouble(va_arg(args, long double));
+ break;
+ // See REPL_BUILTIN_TYPES.
+ }
+ }
+ va_end(args);
+}
+
+// A trampoline to work around the fact that operator placement new cannot
+// really be forward declared due to libc++ and libstdc++ declaration mismatch.
+// FIXME: __clang_Interpreter_NewTag is ODR violation because we get the same
+// definition in the interpreter runtime. We should move it in a runtime header
+// which gets included by the interpreter and here.
+struct __clang_Interpreter_NewTag {};
+REPL_EXTERNAL_VISIBILITY void *
+operator new(size_t __sz, void *__p, __clang_Interpreter_NewTag) noexcept {
+ // Just forward to the standard operator placement new.
+ return operator new(__sz, __p);
+}
diff --git a/clang/unittests/Interpreter/CodeCompletionTest.cpp b/clang/unittests/Interpreter/CodeCompletionTest.cpp
index 72fcce76a1029d..23cfc469695d28 100644
--- a/clang/unittests/Interpreter/CodeCompletionTest.cpp
+++ b/clang/unittests/Interpreter/CodeCompletionTest.cpp
@@ -26,7 +26,7 @@ auto CB = clang::IncrementalCompilerBuilder();
class CodeCompletionTest : public InterpreterTestBase {
public:
- std::unique_ptr<Interpreter> Interp;
+ std::unique_ptr<clang::Interpreter> Interp;
void SetUp() override {
if (!HostSupportsJIT())
diff --git a/clang/unittests/Interpreter/InterpreterExtensionsTest.cpp b/clang/unittests/Interpreter/InterpreterExtensionsTest.cpp
index 5f1f29cebab148..29af464dbcebb9 100644
--- a/clang/unittests/Interpreter/InterpreterExtensionsTest.cpp
+++ b/clang/unittests/Interpreter/InterpreterExtensionsTest.cpp
@@ -65,41 +65,13 @@ class InterpreterExtensionsTest : public InterpreterTestBase {
}
};
-class RecordRuntimeIBMetrics : public Interpreter {
- struct NoopRuntimeInterfaceBuilder : public RuntimeInterfaceBuilder {
- NoopRuntimeInterfaceBuilder(Sema &S) : S(S) {}
-
- TransformExprFunction *getPrintValueTransformer() override {
- TransformerQueries += 1;
- return &noop;
- }
-
- static ExprResult noop(RuntimeInterfaceBuilder *Builder, Expr *E,
- ArrayRef<Expr *> FixedArgs) {
- auto *B = static_cast<NoopRuntimeInterfaceBuilder *>(Builder);
- B->TransformedExprs += 1;
- return B->S.ActOnFinishFullExpr(E, /*DiscardedValue=*/false);
- }
-
- Sema &S;
- size_t TransformedExprs = 0;
- size_t TransformerQueries = 0;
- };
-
-public:
- // Inherit with using wouldn't make it public
- RecordRuntimeIBMetrics(std::unique_ptr<CompilerInstance> CI, llvm::Error &Err)
- : Interpreter(std::move(CI), Err) {}
-
- std::unique_ptr<RuntimeInterfaceBuilder> FindRuntimeInterface() override {
- assert(RuntimeIBPtr == nullptr && "We create the builder only once");
- Sema &S = getCompilerInstance()->getSema();
- auto RuntimeIB = std::make_unique<NoopRuntimeInterfaceBuilder>(S);
- RuntimeIBPtr = RuntimeIB.get();
- return RuntimeIB;
- }
-
- NoopRuntimeInterfaceBuilder *RuntimeIBPtr = nullptr;
+struct OutOfProcInterpreter : public Interpreter {
+ OutOfProcInterpreter(
+ std::unique_ptr<CompilerInstance> CI, llvm::Error &ErrOut,
+ std::unique_ptr<clang::ASTConsumer> Consumer,
+ std::unique_ptr<llvm::orc::LLJITBuilder> JITBuilder = nullptr)
+ : Interpreter(std::move(CI), ErrOut, std::move(JITBuilder),
+ std::move(Consumer)) {}
};
TEST_F(InterpreterExtensionsTest, FindRuntimeInterface) {
@@ -108,13 +80,23 @@ TEST_F(InterpreterExtensionsTest, FindRuntimeInterface) {
clang::IncrementalCompilerBuilder CB;
llvm::Error ErrOut = llvm::Error::success();
- RecordRuntimeIBMetrics Interp(cantFail(CB.CreateCpp()), ErrOut);
+ auto CI = cantFail(CB.CreateCpp());
+ // Do not attach the default consumer which is specialized for in-process.
+ class NoopConsumer : public ASTConsumer {};
+ std::unique_ptr<ASTConsumer> C = std::make_unique<NoopConsumer>();
+ OutOfProcInterpreter I(std::move(CI), ErrOut, std::move(C),
+ /*JITBuilder=*/nullptr);
cantFail(std::move(ErrOut));
- cantFail(Interp.Parse("int a = 1; a"));
- cantFail(Interp.Parse("int b = 2; b"));
- cantFail(Interp.Parse("int c = 3; c"));
- EXPECT_EQ(3U, Interp.RuntimeIBPtr->TransformedExprs);
- EXPECT_EQ(1U, Interp.RuntimeIBPtr->TransformerQueries);
+ cantFail(I.Parse("int a = 1; a"));
+ cantFail(I.Parse("int b = 2; b"));
+ cantFail(I.Parse("int c = 3; c"));
+
+ // Make sure no clang::Value logic is attached by the Interpreter.
+ Value V1;
+ llvm::cantFail(I.ParseAndExecute("int x = 42;"));
+ llvm::cantFail(I.ParseAndExecute("x", &V1));
+ EXPECT_FALSE(V1.isValid());
+ EXPECT_FALSE(V1.hasValue());
}
class CustomJBInterpreter : public Interpreter {
>From a4071505e30f24cd507615a1999c782f4fbe39bb Mon Sep 17 00:00:00 2001
From: Vassil Vassilev <v.g.vassilev at gmail.com>
Date: Mon, 9 Sep 2024 15:48:22 +0000
Subject: [PATCH 2/2] Tell the jit about -include files
---
clang/include/clang/Interpreter/Interpreter.h | 1 +
clang/lib/Interpreter/Interpreter.cpp | 46 +++++++++----------
2 files changed, 24 insertions(+), 23 deletions(-)
diff --git a/clang/include/clang/Interpreter/Interpreter.h b/clang/include/clang/Interpreter/Interpreter.h
index cbb1cfd4ab02a8..55e60d28a11cf1 100644
--- a/clang/include/clang/Interpreter/Interpreter.h
+++ b/clang/include/clang/Interpreter/Interpreter.h
@@ -175,6 +175,7 @@ class Interpreter {
CodeGenerator *getCodeGen() const;
std::unique_ptr<llvm::Module> GenModule();
+ PartialTranslationUnit &RegisterPTU(TranslationUnitDecl *TU);
// A cache for the compiled destructors used to for de-allocation of managed
// clang::Values.
diff --git a/clang/lib/Interpreter/Interpreter.cpp b/clang/lib/Interpreter/Interpreter.cpp
index e1135c2cf88fe3..93af355c9466be 100644
--- a/clang/lib/Interpreter/Interpreter.cpp
+++ b/clang/lib/Interpreter/Interpreter.cpp
@@ -393,33 +393,29 @@ Interpreter::Interpreter(std::unique_ptr<CompilerInstance> CI,
return;
CI->ExecuteAction(*Act);
- if (getCodeGen())
- CachedInCodeGenModule = GenModule();
+ ASTContext &C = CI->getASTContext();
IncrParser = std::make_unique<IncrementalParser>(std::move(CI), ErrOut);
- // An initial PTU is needed as CUDA includes some headers automatically.
- auto PTU = Parse("");
- if (auto E = PTU.takeError()) {
- ErrOut = joinErrors(std::move(ErrOut), std::move(E));
- return;
- }
-
- if (getCodeGen()) {
- PTU->TheModule = GenModule();
- assert(PTU->TheModule && "Failed to create initial PTU");
- }
-
if (ErrOut)
return;
- // Not all frontends support code-generation, e.g. ast-dump actions don't
if (getCodeGen()) {
+ CachedInCodeGenModule = GenModule();
if (llvm::Error Err = CreateExecutor()) {
ErrOut = joinErrors(std::move(ErrOut), std::move(Err));
return;
}
+ }
+
+ // The initial PTU is filled by `-include` or by CUDA includes automatically.
+ RegisterPTU(C.getTranslationUnitDecl());
+
+ // Prepare the IncrParser for input.
+ llvm::cantFail(Parse(""));
+ // Not all frontends support code-generation, e.g. ast-dump actions don't
+ if (getCodeGen()) {
// Process the PTUs that came from initialization. For example -include will
// give us a header that's processed at initialization of the preprocessor.
for (PartialTranslationUnit &PTU : PTUs)
@@ -549,6 +545,17 @@ size_t Interpreter::getEffectivePTUSize() const {
return PTUs.size() - InitPTUSize;
}
+PartialTranslationUnit &Interpreter::RegisterPTU(TranslationUnitDecl *TU) {
+ PTUs.emplace_back(PartialTranslationUnit());
+ PartialTranslationUnit &LastPTU = PTUs.back();
+ LastPTU.TUPart = TU;
+
+ if (std::unique_ptr<llvm::Module> M = GenModule())
+ LastPTU.TheModule = std::move(M);
+
+ return LastPTU;
+}
+
llvm::Expected<PartialTranslationUnit &>
Interpreter::Parse(llvm::StringRef Code) {
// If we have a device parser, parse it first. The generated code will be
@@ -568,14 +575,7 @@ Interpreter::Parse(llvm::StringRef Code) {
if (!TuOrErr)
return TuOrErr.takeError();
- PTUs.emplace_back(PartialTranslationUnit());
- PartialTranslationUnit &LastPTU = PTUs.back();
- LastPTU.TUPart = *TuOrErr;
-
- if (std::unique_ptr<llvm::Module> M = GenModule())
- LastPTU.TheModule = std::move(M);
-
- return LastPTU;
+ return RegisterPTU(*TuOrErr);
}
static llvm::Expected<llvm::orc::JITTargetMachineBuilder>
More information about the cfe-commits
mailing list