[clang] [clang-repl] : Fix clang-repl crash with --cuda flag (PR #136404)
Anutosh Bhat via cfe-commits
cfe-commits at lists.llvm.org
Fri Apr 25 04:20:26 PDT 2025
https://github.com/anutosh491 updated https://github.com/llvm/llvm-project/pull/136404
>From d10dab46a0d95ad5e3d44ac826efd18c8aa645ee Mon Sep 17 00:00:00 2001
From: anutosh491 <andersonbhat491 at gmail.com>
Date: Fri, 18 Apr 2025 18:45:00 +0530
Subject: [PATCH 1/4] Fix cuda flag with clang-repl
---
clang/include/clang/Interpreter/Interpreter.h | 13 ++--
clang/lib/Interpreter/DeviceOffload.cpp | 45 +++++---------
clang/lib/Interpreter/DeviceOffload.h | 2 -
clang/lib/Interpreter/Interpreter.cpp | 59 ++++++++++++++-----
4 files changed, 68 insertions(+), 51 deletions(-)
diff --git a/clang/include/clang/Interpreter/Interpreter.h b/clang/include/clang/Interpreter/Interpreter.h
index b1b63aedf86ab..56213f88b9e30 100644
--- a/clang/include/clang/Interpreter/Interpreter.h
+++ b/clang/include/clang/Interpreter/Interpreter.h
@@ -41,6 +41,7 @@ class CXXRecordDecl;
class Decl;
class IncrementalExecutor;
class IncrementalParser;
+class IncrementalCUDADeviceParser;
/// Create a pre-configured \c CompilerInstance for incremental processing.
class IncrementalCompilerBuilder {
@@ -93,7 +94,10 @@ class Interpreter {
std::unique_ptr<IncrementalExecutor> IncrExecutor;
// An optional parser for CUDA offloading
- std::unique_ptr<IncrementalParser> DeviceParser;
+ std::unique_ptr<IncrementalCUDADeviceParser> DeviceParser;
+
+ // An optional action for CUDA offloading
+ std::unique_ptr<IncrementalAction> DeviceAct;
/// List containing information about each incrementally parsed piece of code.
std::list<PartialTranslationUnit> PTUs;
@@ -175,10 +179,11 @@ class Interpreter {
llvm::Expected<Expr *> ExtractValueFromExpr(Expr *E);
llvm::Expected<llvm::orc::ExecutorAddr> CompileDtorCall(CXXRecordDecl *CXXRD);
- CodeGenerator *getCodeGen() const;
- std::unique_ptr<llvm::Module> GenModule();
+ CodeGenerator *getCodeGen(IncrementalAction *Action = nullptr) const;
+ std::unique_ptr<llvm::Module> GenModule(IncrementalAction *Action = nullptr);
PartialTranslationUnit &RegisterPTU(TranslationUnitDecl *TU,
- std::unique_ptr<llvm::Module> M = {});
+ std::unique_ptr<llvm::Module> M = {},
+ IncrementalAction *Action = nullptr);
// A cache for the compiled destructors used to for de-allocation of managed
// clang::Values.
diff --git a/clang/lib/Interpreter/DeviceOffload.cpp b/clang/lib/Interpreter/DeviceOffload.cpp
index 1999d63d1aa04..7d0125403ea52 100644
--- a/clang/lib/Interpreter/DeviceOffload.cpp
+++ b/clang/lib/Interpreter/DeviceOffload.cpp
@@ -31,10 +31,9 @@ IncrementalCUDADeviceParser::IncrementalCUDADeviceParser(
llvm::Error &Err, const std::list<PartialTranslationUnit> &PTUs)
: IncrementalParser(*DeviceInstance, Err), PTUs(PTUs), VFS(FS),
CodeGenOpts(HostInstance.getCodeGenOpts()),
- TargetOpts(HostInstance.getTargetOpts()) {
+ TargetOpts(DeviceInstance->getTargetOpts()) {
if (Err)
return;
- DeviceCI = std::move(DeviceInstance);
StringRef Arch = TargetOpts.CPU;
if (!Arch.starts_with("sm_") || Arch.substr(3).getAsInteger(10, SMVersion)) {
Err = llvm::joinErrors(std::move(Err), llvm::make_error<llvm::StringError>(
@@ -42,34 +41,7 @@ IncrementalCUDADeviceParser::IncrementalCUDADeviceParser(
llvm::inconvertibleErrorCode()));
return;
}
-}
-
-llvm::Expected<TranslationUnitDecl *>
-IncrementalCUDADeviceParser::Parse(llvm::StringRef Input) {
- auto PTU = IncrementalParser::Parse(Input);
- if (!PTU)
- return PTU.takeError();
-
- auto PTX = GeneratePTX();
- if (!PTX)
- return PTX.takeError();
-
- auto Err = GenerateFatbinary();
- if (Err)
- return std::move(Err);
-
- std::string FatbinFileName =
- "/incr_module_" + std::to_string(PTUs.size()) + ".fatbin";
- VFS->addFile(FatbinFileName, 0,
- llvm::MemoryBuffer::getMemBuffer(
- llvm::StringRef(FatbinContent.data(), FatbinContent.size()),
- "", false));
-
- CodeGenOpts.CudaGpuBinaryFileName = FatbinFileName;
-
- FatbinContent.clear();
-
- return PTU;
+ DeviceCI = std::move(DeviceInstance);
}
llvm::Expected<llvm::StringRef> IncrementalCUDADeviceParser::GeneratePTX() {
@@ -172,6 +144,19 @@ llvm::Error IncrementalCUDADeviceParser::GenerateFatbinary() {
FatbinContent.append(PTXCode.begin(), PTXCode.end());
+ const PartialTranslationUnit &PTU = PTUs.back();
+
+ std::string FatbinFileName = "/" + PTU.TheModule->getName().str() + ".fatbin";
+
+ VFS->addFile(FatbinFileName, 0,
+ llvm::MemoryBuffer::getMemBuffer(
+ llvm::StringRef(FatbinContent.data(), FatbinContent.size()),
+ "", false));
+
+ CodeGenOpts.CudaGpuBinaryFileName = FatbinFileName;
+
+ FatbinContent.clear();
+
return llvm::Error::success();
}
diff --git a/clang/lib/Interpreter/DeviceOffload.h b/clang/lib/Interpreter/DeviceOffload.h
index b9a1acab004c3..43645033c4840 100644
--- a/clang/lib/Interpreter/DeviceOffload.h
+++ b/clang/lib/Interpreter/DeviceOffload.h
@@ -33,8 +33,6 @@ class IncrementalCUDADeviceParser : public IncrementalParser {
llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> VFS,
llvm::Error &Err, const std::list<PartialTranslationUnit> &PTUs);
- llvm::Expected<TranslationUnitDecl *> Parse(llvm::StringRef Input) override;
-
// Generate PTX for the last PTU.
llvm::Expected<llvm::StringRef> GeneratePTX();
diff --git a/clang/lib/Interpreter/Interpreter.cpp b/clang/lib/Interpreter/Interpreter.cpp
index f8c8d0a425659..f91563dd0378c 100644
--- a/clang/lib/Interpreter/Interpreter.cpp
+++ b/clang/lib/Interpreter/Interpreter.cpp
@@ -481,20 +481,34 @@ Interpreter::createWithCUDA(std::unique_ptr<CompilerInstance> CI,
OverlayVFS->pushOverlay(IMVFS);
CI->createFileManager(OverlayVFS);
- auto Interp = Interpreter::create(std::move(CI));
- if (auto E = Interp.takeError())
- return std::move(E);
+ llvm::Expected<std::unique_ptr<Interpreter>> InterpOrErr =
+ Interpreter::create(std::move(CI));
+ if (!InterpOrErr)
+ return InterpOrErr;
+
+ std::unique_ptr<Interpreter> Interp = std::move(*InterpOrErr);
llvm::Error Err = llvm::Error::success();
- auto DeviceParser = std::make_unique<IncrementalCUDADeviceParser>(
- std::move(DCI), *(*Interp)->getCompilerInstance(), IMVFS, Err,
- (*Interp)->PTUs);
+ llvm::LLVMContext &LLVMCtx = *Interp->TSCtx->getContext();
+
+ auto DeviceAct =
+ std::make_unique<IncrementalAction>(*DCI, LLVMCtx, Err, *Interp);
+
if (Err)
return std::move(Err);
- (*Interp)->DeviceParser = std::move(DeviceParser);
+ Interp->DeviceAct = std::move(DeviceAct);
+
+ DCI->ExecuteAction(*Interp->DeviceAct);
+
+ auto DeviceParser = std::make_unique<IncrementalCUDADeviceParser>(
+ std::move(DCI), *Interp->getCompilerInstance(), IMVFS, Err, Interp->PTUs);
+
+ if (Err)
+ return std::move(Err);
- return Interp;
+ Interp->DeviceParser = std::move(DeviceParser);
+ return std::move(Interp);
}
const CompilerInstance *Interpreter::getCompilerInstance() const {
@@ -532,15 +546,17 @@ size_t Interpreter::getEffectivePTUSize() const {
PartialTranslationUnit &
Interpreter::RegisterPTU(TranslationUnitDecl *TU,
- std::unique_ptr<llvm::Module> M /*={}*/) {
+ std::unique_ptr<llvm::Module> M /*={}*/,
+ IncrementalAction *Action) {
PTUs.emplace_back(PartialTranslationUnit());
PartialTranslationUnit &LastPTU = PTUs.back();
LastPTU.TUPart = TU;
if (!M)
- M = GenModule();
+ M = GenModule(Action);
- assert((!getCodeGen() || M) && "Must have a llvm::Module at this point");
+ assert((!getCodeGen(Action) || M) &&
+ "Must have a llvm::Module at this point");
LastPTU.TheModule = std::move(M);
LLVM_DEBUG(llvm::dbgs() << "compile-ptu " << PTUs.size() - 1
@@ -560,6 +576,16 @@ Interpreter::Parse(llvm::StringRef Code) {
llvm::Expected<TranslationUnitDecl *> DeviceTU = DeviceParser->Parse(Code);
if (auto E = DeviceTU.takeError())
return std::move(E);
+
+ RegisterPTU(*DeviceTU, nullptr, DeviceAct.get());
+
+ llvm::Expected<llvm::StringRef> PTX = DeviceParser->GeneratePTX();
+ if (!PTX)
+ return PTX.takeError();
+
+ llvm::Error Err = DeviceParser->GenerateFatbinary();
+ if (Err)
+ return std::move(Err);
}
// Tell the interpreter sliently ignore unused expressions since value
@@ -736,9 +762,10 @@ llvm::Error Interpreter::LoadDynamicLibrary(const char *name) {
return llvm::Error::success();
}
-std::unique_ptr<llvm::Module> Interpreter::GenModule() {
+std::unique_ptr<llvm::Module>
+Interpreter::GenModule(IncrementalAction *Action) {
static unsigned ID = 0;
- if (CodeGenerator *CG = getCodeGen()) {
+ if (CodeGenerator *CG = getCodeGen(Action)) {
// 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
@@ -760,8 +787,10 @@ std::unique_ptr<llvm::Module> Interpreter::GenModule() {
return nullptr;
}
-CodeGenerator *Interpreter::getCodeGen() const {
- FrontendAction *WrappedAct = Act->getWrapped();
+CodeGenerator *Interpreter::getCodeGen(IncrementalAction *Action) const {
+ if (!Action)
+ Action = Act.get();
+ FrontendAction *WrappedAct = Action->getWrapped();
if (!WrappedAct->hasIRSupport())
return nullptr;
return static_cast<CodeGenAction *>(WrappedAct)->getCodeGenerator();
>From e7b2d19940179d9c89a88a966f3b28fa7f956d6b Mon Sep 17 00:00:00 2001
From: anutosh491 <andersonbhat491 at gmail.com>
Date: Fri, 25 Apr 2025 15:07:46 +0530
Subject: [PATCH 2/4] Delegate Codegen to IncrementalParser
---
clang/include/clang/Interpreter/Interpreter.h | 11 -
clang/lib/Interpreter/CMakeLists.txt | 1 +
clang/lib/Interpreter/DeviceOffload.cpp | 6 +-
clang/lib/Interpreter/DeviceOffload.h | 6 +-
clang/lib/Interpreter/IncrementalAction.cpp | 110 ++++++++++
clang/lib/Interpreter/IncrementalAction.h | 68 ++++++
clang/lib/Interpreter/IncrementalParser.cpp | 65 +++++-
clang/lib/Interpreter/IncrementalParser.h | 28 ++-
clang/lib/Interpreter/Interpreter.cpp | 198 ++----------------
.../Interpreter/InterpreterValuePrinter.cpp | 2 +-
10 files changed, 288 insertions(+), 207 deletions(-)
create mode 100644 clang/lib/Interpreter/IncrementalAction.cpp
create mode 100644 clang/lib/Interpreter/IncrementalAction.h
diff --git a/clang/include/clang/Interpreter/Interpreter.h b/clang/include/clang/Interpreter/Interpreter.h
index 56213f88b9e30..145ed409b7807 100644
--- a/clang/include/clang/Interpreter/Interpreter.h
+++ b/clang/include/clang/Interpreter/Interpreter.h
@@ -36,7 +36,6 @@ class ThreadSafeContext;
namespace clang {
class CompilerInstance;
-class CodeGenerator;
class CXXRecordDecl;
class Decl;
class IncrementalExecutor;
@@ -109,10 +108,6 @@ class Interpreter {
// printing happens, it's in an invalid state.
Value LastValue;
- /// 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;
-
/// Compiler instance performing the incremental compilation.
std::unique_ptr<CompilerInstance> CI;
@@ -179,12 +174,6 @@ class Interpreter {
llvm::Expected<Expr *> ExtractValueFromExpr(Expr *E);
llvm::Expected<llvm::orc::ExecutorAddr> CompileDtorCall(CXXRecordDecl *CXXRD);
- CodeGenerator *getCodeGen(IncrementalAction *Action = nullptr) const;
- std::unique_ptr<llvm::Module> GenModule(IncrementalAction *Action = nullptr);
- PartialTranslationUnit &RegisterPTU(TranslationUnitDecl *TU,
- std::unique_ptr<llvm::Module> M = {},
- IncrementalAction *Action = nullptr);
-
// A cache for the compiled destructors used to for de-allocation of managed
// clang::Values.
llvm::DenseMap<CXXRecordDecl *, llvm::orc::ExecutorAddr> Dtors;
diff --git a/clang/lib/Interpreter/CMakeLists.txt b/clang/lib/Interpreter/CMakeLists.txt
index bf70cdfbee01e..79b8369371db6 100644
--- a/clang/lib/Interpreter/CMakeLists.txt
+++ b/clang/lib/Interpreter/CMakeLists.txt
@@ -22,6 +22,7 @@ endif()
add_clang_library(clangInterpreter
DeviceOffload.cpp
CodeCompletion.cpp
+ IncrementalAction.cpp
IncrementalExecutor.cpp
IncrementalParser.cpp
Interpreter.cpp
diff --git a/clang/lib/Interpreter/DeviceOffload.cpp b/clang/lib/Interpreter/DeviceOffload.cpp
index 7d0125403ea52..5415697d24daf 100644
--- a/clang/lib/Interpreter/DeviceOffload.cpp
+++ b/clang/lib/Interpreter/DeviceOffload.cpp
@@ -26,10 +26,10 @@ namespace clang {
IncrementalCUDADeviceParser::IncrementalCUDADeviceParser(
std::unique_ptr<CompilerInstance> DeviceInstance,
- CompilerInstance &HostInstance,
+ CompilerInstance &HostInstance, IncrementalAction *DeviceAct,
llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> FS,
- llvm::Error &Err, const std::list<PartialTranslationUnit> &PTUs)
- : IncrementalParser(*DeviceInstance, Err), PTUs(PTUs), VFS(FS),
+ llvm::Error &Err, std::list<PartialTranslationUnit> &PTUs)
+ : IncrementalParser(*DeviceInstance, DeviceAct, Err, PTUs), VFS(FS),
CodeGenOpts(HostInstance.getCodeGenOpts()),
TargetOpts(DeviceInstance->getTargetOpts()) {
if (Err)
diff --git a/clang/lib/Interpreter/DeviceOffload.h b/clang/lib/Interpreter/DeviceOffload.h
index 43645033c4840..e4cfd26aa4871 100644
--- a/clang/lib/Interpreter/DeviceOffload.h
+++ b/clang/lib/Interpreter/DeviceOffload.h
@@ -22,16 +22,16 @@ struct PartialTranslationUnit;
class CompilerInstance;
class CodeGenOptions;
class TargetOptions;
+class IncrementalAction;
class IncrementalCUDADeviceParser : public IncrementalParser {
- const std::list<PartialTranslationUnit> &PTUs;
public:
IncrementalCUDADeviceParser(
std::unique_ptr<CompilerInstance> DeviceInstance,
- CompilerInstance &HostInstance,
+ CompilerInstance &HostInstance, IncrementalAction *DeviceAct,
llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> VFS,
- llvm::Error &Err, const std::list<PartialTranslationUnit> &PTUs);
+ llvm::Error &Err, std::list<PartialTranslationUnit> &PTUs);
// Generate PTX for the last PTU.
llvm::Expected<llvm::StringRef> GeneratePTX();
diff --git a/clang/lib/Interpreter/IncrementalAction.cpp b/clang/lib/Interpreter/IncrementalAction.cpp
new file mode 100644
index 0000000000000..683925859c645
--- /dev/null
+++ b/clang/lib/Interpreter/IncrementalAction.cpp
@@ -0,0 +1,110 @@
+//===--- IncrementalAction.h - Incremental Frontend Action -*- 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "IncrementalAction.h"
+
+#include "clang/AST/ASTConsumer.h"
+#include "clang/CodeGen/CodeGenAction.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/FrontendOptions.h"
+#include "clang/FrontendTool/Utils.h"
+#include "clang/Interpreter/Interpreter.h"
+#include "clang/Sema/Sema.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/ErrorHandling.h"
+
+namespace clang {
+IncrementalAction::IncrementalAction(CompilerInstance &CI,
+ llvm::LLVMContext &LLVMCtx,
+ llvm::Error &Err, Interpreter &I,
+ std::unique_ptr<ASTConsumer> Consumer)
+ : 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:
+ case frontend::ASTPrint:
+ case frontend::ParseSyntaxOnly:
+ Act = CreateFrontendAction(CI);
+ break;
+ case frontend::PluginAction:
+ case frontend::EmitAssembly:
+ case frontend::EmitBC:
+ case frontend::EmitObj:
+ case frontend::PrintPreprocessedInput:
+ case frontend::EmitLLVMOnly:
+ Act.reset(new EmitLLVMOnlyAction(&LLVMCtx));
+ break;
+ }
+ return Act;
+ }()),
+ Interp(I), Consumer(std::move(Consumer)) {}
+
+std::unique_ptr<ASTConsumer>
+IncrementalAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
+ 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 IncrementalAction::ExecuteAction() {
+ WrapperFrontendAction::ExecuteAction();
+ getCompilerInstance().getSema().CurContext = nullptr;
+}
+
+void IncrementalAction::EndSourceFile() {
+ if (IsTerminating && getWrapped())
+ WrapperFrontendAction::EndSourceFile();
+}
+
+void IncrementalAction::FinalizeAction() {
+ assert(!IsTerminating && "Already finalized!");
+ IsTerminating = true;
+ EndSourceFile();
+}
+
+InProcessPrintingASTConsumer::InProcessPrintingASTConsumer(
+ std::unique_ptr<ASTConsumer> C, Interpreter &I)
+ : MultiplexConsumer(std::move(C)), Interp(I) {}
+
+bool InProcessPrintingASTConsumer::HandleTopLevelDecl(DeclGroupRef DGR) {
+ if (DGR.isNull())
+ return true;
+
+ for (Decl *D : DGR)
+ if (auto *TLSD = llvm::dyn_cast<TopLevelStmtDecl>(D))
+ if (TLSD && TLSD->isSemiMissing()) {
+ auto ExprOrErr =
+ Interp.ExtractValueFromExpr(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);
+}
+
+} // namespace clang
\ No newline at end of file
diff --git a/clang/lib/Interpreter/IncrementalAction.h b/clang/lib/Interpreter/IncrementalAction.h
new file mode 100644
index 0000000000000..08d090a0513fe
--- /dev/null
+++ b/clang/lib/Interpreter/IncrementalAction.h
@@ -0,0 +1,68 @@
+//===--- IncrementalAction.h - Incremental Frontend Action -*- 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_INTERPRETER_INCREMENTALACTION_H
+#define LLVM_CLANG_INTERPRETER_INCREMENTALACTION_H
+
+#include "clang/Frontend/FrontendActions.h"
+#include "clang/Frontend/MultiplexConsumer.h"
+
+namespace clang {
+
+class Interpreter;
+
+/// 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);
+
+ FrontendAction *getWrapped() const { return WrappedAction.get(); }
+
+ TranslationUnitKind getTranslationUnitKind() override {
+ return TU_Incremental;
+ }
+
+ std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
+ StringRef InFile) override;
+
+ void ExecuteAction() override;
+
+ // 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;
+
+ void FinalizeAction();
+};
+
+class InProcessPrintingASTConsumer final : public MultiplexConsumer {
+ Interpreter &Interp;
+
+public:
+ InProcessPrintingASTConsumer(std::unique_ptr<ASTConsumer> C, Interpreter &I);
+
+ bool HandleTopLevelDecl(DeclGroupRef DGR) override;
+};
+
+} // end namespace clang
+
+#endif // LLVM_CLANG_INTERPRETER_INCREMENTALACTION_H
diff --git a/clang/lib/Interpreter/IncrementalParser.cpp b/clang/lib/Interpreter/IncrementalParser.cpp
index 41d6304bd5f65..ee3e6e595f1ce 100644
--- a/clang/lib/Interpreter/IncrementalParser.cpp
+++ b/clang/lib/Interpreter/IncrementalParser.cpp
@@ -11,12 +11,17 @@
//===----------------------------------------------------------------------===//
#include "IncrementalParser.h"
+#include "IncrementalAction.h"
#include "clang/AST/DeclContextInternals.h"
+#include "clang/CodeGen/CodeGenAction.h"
+#include "clang/CodeGen/ModuleBuilder.h"
#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/FrontendAction.h"
#include "clang/Interpreter/PartialTranslationUnit.h"
#include "clang/Parse/Parser.h"
#include "clang/Sema/Sema.h"
+#include "llvm/IR/Module.h"
#include "llvm/Support/CrashRecoveryContext.h"
#include "llvm/Support/Error.h"
@@ -27,11 +32,12 @@ namespace clang {
// IncrementalParser::IncrementalParser() {}
IncrementalParser::IncrementalParser(CompilerInstance &Instance,
- llvm::Error &Err)
- : S(Instance.getSema()) {
+ IncrementalAction *Act, llvm::Error &Err,
+ std::list<PartialTranslationUnit> &PTUs)
+ : S(Instance.getSema()), Act(Act), PTUs(PTUs) {
llvm::ErrorAsOutParameter EAO(&Err);
Consumer = &S.getASTConsumer();
- P.reset(new Parser(S.getPreprocessor(), S, /*SkipBodies=*/false));
+ P = std::make_unique<Parser>(S.getPreprocessor(), S, /*SkipBodies=*/false);
P->Initialize();
}
@@ -185,4 +191,57 @@ void IncrementalParser::CleanUpPTU(TranslationUnitDecl *MostRecentTU) {
}
}
+PartialTranslationUnit &
+IncrementalParser::RegisterPTU(TranslationUnitDecl *TU,
+ std::unique_ptr<llvm::Module> M /*={}*/) {
+ PTUs.emplace_back(PartialTranslationUnit());
+ PartialTranslationUnit &LastPTU = PTUs.back();
+ LastPTU.TUPart = TU;
+
+ if (!M)
+ M = GenModule();
+
+ assert((!getCodeGen() || M) && "Must have a llvm::Module at this point");
+
+ LastPTU.TheModule = std::move(M);
+ LLVM_DEBUG(llvm::dbgs() << "compile-ptu " << PTUs.size() - 1
+ << ": [TU=" << LastPTU.TUPart);
+ if (LastPTU.TheModule)
+ LLVM_DEBUG(llvm::dbgs() << ", M=" << LastPTU.TheModule.get() << " ("
+ << LastPTU.TheModule->getName() << ")");
+ LLVM_DEBUG(llvm::dbgs() << "]\n");
+ return LastPTU;
+}
+
+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 ||
+ !getCompilerInstance()->getPreprocessorOpts().Includes.empty()) ||
+ (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;
+}
+
+CodeGenerator *IncrementalParser::getCodeGen() const {
+ FrontendAction *WrappedAct = Act->getWrapped();
+ if (!WrappedAct->hasIRSupport())
+ return nullptr;
+ return static_cast<CodeGenAction *>(WrappedAct)->getCodeGenerator();
+}
+
} // end namespace clang
diff --git a/clang/lib/Interpreter/IncrementalParser.h b/clang/lib/Interpreter/IncrementalParser.h
index 4fdde749a2e75..2dadc086f18cf 100644
--- a/clang/lib/Interpreter/IncrementalParser.h
+++ b/clang/lib/Interpreter/IncrementalParser.h
@@ -19,6 +19,10 @@
#include <list>
#include <memory>
+namespace llvm {
+class Module;
+}
+
namespace clang {
class ASTConsumer;
class CodeGenerator;
@@ -26,6 +30,8 @@ class CompilerInstance;
class Parser;
class Sema;
class TranslationUnitDecl;
+class IncrementalAction;
+struct PartialTranslationUnit;
/// Provides support for incremental compilation. Keeps track of the state
/// changes between the subsequent incremental input.
@@ -44,12 +50,20 @@ class IncrementalParser {
/// Counts the number of direct user input lines that have been parsed.
unsigned InputCount = 0;
- // IncrementalParser();
+ /// The FrontendAction used during incremental parsing.
+ IncrementalAction *Act = nullptr;
+
+ std::list<PartialTranslationUnit> &PTUs;
public:
- IncrementalParser(CompilerInstance &Instance, llvm::Error &Err);
+ IncrementalParser(CompilerInstance &Instance, IncrementalAction *Act,
+ llvm::Error &Err, std::list<PartialTranslationUnit> &PTUs);
virtual ~IncrementalParser();
+ /// 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;
+
/// Parses incremental input by creating an in-memory file.
///\returns a \c PartialTranslationUnit which holds information about the
/// \c TranslationUnitDecl.
@@ -57,6 +71,16 @@ class IncrementalParser {
void CleanUpPTU(TranslationUnitDecl *MostRecentTU);
+ /// Access the current code generator.
+ CodeGenerator *getCodeGen() const;
+
+ /// Generate an LLVM module for the most recent parsed input.
+ std::unique_ptr<llvm::Module> GenModule();
+
+ /// Register a PTU produced by Parse.
+ PartialTranslationUnit &RegisterPTU(TranslationUnitDecl *TU,
+ std::unique_ptr<llvm::Module> M = {});
+
private:
llvm::Expected<TranslationUnitDecl *> ParseOrWrapTopLevelDecl();
};
diff --git a/clang/lib/Interpreter/Interpreter.cpp b/clang/lib/Interpreter/Interpreter.cpp
index f91563dd0378c..c18e232ed1974 100644
--- a/clang/lib/Interpreter/Interpreter.cpp
+++ b/clang/lib/Interpreter/Interpreter.cpp
@@ -12,6 +12,7 @@
//===----------------------------------------------------------------------===//
#include "DeviceOffload.h"
+#include "IncrementalAction.h"
#include "IncrementalExecutor.h"
#include "IncrementalParser.h"
#include "InterpreterUtils.h"
@@ -28,7 +29,6 @@
#include "clang/Basic/DiagnosticSema.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/CodeGen/CodeGenAction.h"
-#include "clang/CodeGen/ModuleBuilder.h"
#include "clang/CodeGen/ObjectFilePCHContainerWriter.h"
#include "clang/Driver/Compilation.h"
#include "clang/Driver/Driver.h"
@@ -248,120 +248,6 @@ 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;
-
- for (Decl *D : DGR)
- if (auto *TLSD = llvm::dyn_cast<TopLevelStmtDecl>(D))
- if (TLSD && TLSD->isSemiMissing()) {
- auto ExprOrErr =
- Interp.ExtractValueFromExpr(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:
- case frontend::ASTPrint:
- case frontend::ParseSyntaxOnly:
- Act = CreateFrontendAction(CI);
- break;
- case frontend::PluginAction:
- case frontend::EmitAssembly:
- case frontend::EmitBC:
- case frontend::EmitObj:
- case frontend::PrintPreprocessedInput:
- 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 {
- WrapperFrontendAction::ExecuteAction();
- getCompilerInstance().getSema().CurContext = nullptr;
- }
-
- // 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> Instance,
llvm::Error &ErrOut,
std::unique_ptr<llvm::orc::LLJITBuilder> JITBuilder,
@@ -378,22 +264,23 @@ Interpreter::Interpreter(std::unique_ptr<CompilerInstance> Instance,
return;
CI->ExecuteAction(*Act);
- IncrParser = std::make_unique<IncrementalParser>(*CI, ErrOut);
+ IncrParser =
+ std::make_unique<IncrementalParser>(*CI, Act.get(), ErrOut, PTUs);
if (ErrOut)
return;
- if (getCodeGen()) {
- CachedInCodeGenModule = GenModule();
+ if (IncrParser->getCodeGen()) {
+ IncrParser->CachedInCodeGenModule = IncrParser->GenModule();
// The initial PTU is filled by `-include` or by CUDA includes
// automatically.
if (!CI->getPreprocessorOpts().Includes.empty()) {
// We can't really directly pass the CachedInCodeGenModule to the Jit
// because it will steal it, causing dangling references as explained in
// Interpreter::Execute
- auto M = llvm::CloneModule(*CachedInCodeGenModule);
+ auto M = llvm::CloneModule(*IncrParser->CachedInCodeGenModule);
ASTContext &C = CI->getASTContext();
- RegisterPTU(C.getTranslationUnitDecl(), std::move(M));
+ IncrParser->RegisterPTU(C.getTranslationUnitDecl(), std::move(M));
}
if (llvm::Error Err = CreateExecutor()) {
ErrOut = joinErrors(std::move(ErrOut), std::move(Err));
@@ -402,7 +289,7 @@ Interpreter::Interpreter(std::unique_ptr<CompilerInstance> Instance,
}
// Not all frontends support code-generation, e.g. ast-dump actions don't
- if (getCodeGen()) {
+ if (IncrParser->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)
@@ -502,7 +389,8 @@ Interpreter::createWithCUDA(std::unique_ptr<CompilerInstance> CI,
DCI->ExecuteAction(*Interp->DeviceAct);
auto DeviceParser = std::make_unique<IncrementalCUDADeviceParser>(
- std::move(DCI), *Interp->getCompilerInstance(), IMVFS, Err, Interp->PTUs);
+ std::move(DCI), *Interp->getCompilerInstance(), Interp->DeviceAct.get(),
+ IMVFS, Err, Interp->PTUs);
if (Err)
return std::move(Err);
@@ -544,30 +432,6 @@ size_t Interpreter::getEffectivePTUSize() const {
return PTUs.size() - InitPTUSize;
}
-PartialTranslationUnit &
-Interpreter::RegisterPTU(TranslationUnitDecl *TU,
- std::unique_ptr<llvm::Module> M /*={}*/,
- IncrementalAction *Action) {
- PTUs.emplace_back(PartialTranslationUnit());
- PartialTranslationUnit &LastPTU = PTUs.back();
- LastPTU.TUPart = TU;
-
- if (!M)
- M = GenModule(Action);
-
- assert((!getCodeGen(Action) || M) &&
- "Must have a llvm::Module at this point");
-
- LastPTU.TheModule = std::move(M);
- LLVM_DEBUG(llvm::dbgs() << "compile-ptu " << PTUs.size() - 1
- << ": [TU=" << LastPTU.TUPart);
- if (LastPTU.TheModule)
- LLVM_DEBUG(llvm::dbgs() << ", M=" << LastPTU.TheModule.get() << " ("
- << LastPTU.TheModule->getName() << ")");
- LLVM_DEBUG(llvm::dbgs() << "]\n");
- return LastPTU;
-}
-
llvm::Expected<PartialTranslationUnit &>
Interpreter::Parse(llvm::StringRef Code) {
// If we have a device parser, parse it first. The generated code will be
@@ -577,7 +441,7 @@ Interpreter::Parse(llvm::StringRef Code) {
if (auto E = DeviceTU.takeError())
return std::move(E);
- RegisterPTU(*DeviceTU, nullptr, DeviceAct.get());
+ DeviceParser->RegisterPTU(*DeviceTU);
llvm::Expected<llvm::StringRef> PTX = DeviceParser->GeneratePTX();
if (!PTX)
@@ -597,7 +461,7 @@ Interpreter::Parse(llvm::StringRef Code) {
if (!TuOrErr)
return TuOrErr.takeError();
- return RegisterPTU(*TuOrErr);
+ return IncrParser->RegisterPTU(*TuOrErr);
}
static llvm::Expected<llvm::orc::JITTargetMachineBuilder>
@@ -615,7 +479,7 @@ llvm::Error Interpreter::CreateExecutor() {
return llvm::make_error<llvm::StringError>("Operation failed. "
"Execution engine exists",
std::error_code());
- if (!getCodeGen())
+ if (!IncrParser->getCodeGen())
return llvm::make_error<llvm::StringError>("Operation failed. "
"No code generator available",
std::error_code());
@@ -695,7 +559,7 @@ Interpreter::getSymbolAddress(GlobalDecl GD) const {
return llvm::make_error<llvm::StringError>("Operation failed. "
"No execution engine",
std::error_code());
- llvm::StringRef MangledName = getCodeGen()->GetMangledName(GD);
+ llvm::StringRef MangledName = IncrParser->getCodeGen()->GetMangledName(GD);
return getSymbolAddress(MangledName);
}
@@ -761,38 +625,4 @@ llvm::Error Interpreter::LoadDynamicLibrary(const char *name) {
return llvm::Error::success();
}
-
-std::unique_ptr<llvm::Module>
-Interpreter::GenModule(IncrementalAction *Action) {
- static unsigned ID = 0;
- if (CodeGenerator *CG = getCodeGen(Action)) {
- // 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 ||
- !getCompilerInstance()->getPreprocessorOpts().Includes.empty()) ||
- (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;
-}
-
-CodeGenerator *Interpreter::getCodeGen(IncrementalAction *Action) const {
- if (!Action)
- Action = Act.get();
- FrontendAction *WrappedAct = Action->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
index 3e3fbfd172caa..f21bdbf55a549 100644
--- a/clang/lib/Interpreter/InterpreterValuePrinter.cpp
+++ b/clang/lib/Interpreter/InterpreterValuePrinter.cpp
@@ -45,7 +45,7 @@ Interpreter::CompileDtorCall(CXXRecordDecl *CXXRD) {
getCompilerInstance()->getSema().LookupDestructor(CXXRD);
llvm::StringRef Name =
- getCodeGen()->GetMangledName(GlobalDecl(DtorRD, Dtor_Base));
+ IncrParser->getCodeGen()->GetMangledName(GlobalDecl(DtorRD, Dtor_Base));
auto AddrOrErr = getSymbolAddress(Name);
if (!AddrOrErr)
return AddrOrErr.takeError();
>From 3fee45b28834e2d4ebe5ccde1efa250ba2bd741d Mon Sep 17 00:00:00 2001
From: anutosh491 <andersonbhat491 at gmail.com>
Date: Fri, 25 Apr 2025 16:36:32 +0530
Subject: [PATCH 3/4] Fix ci
---
clang/lib/Interpreter/IncrementalParser.cpp | 6 ++++--
clang/lib/Interpreter/IncrementalParser.h | 3 +++
2 files changed, 7 insertions(+), 2 deletions(-)
diff --git a/clang/lib/Interpreter/IncrementalParser.cpp b/clang/lib/Interpreter/IncrementalParser.cpp
index ee3e6e595f1ce..b52cb3263bb7f 100644
--- a/clang/lib/Interpreter/IncrementalParser.cpp
+++ b/clang/lib/Interpreter/IncrementalParser.cpp
@@ -27,6 +27,8 @@
#include <sstream>
+#define DEBUG_TYPE "clang-repl"
+
namespace clang {
// IncrementalParser::IncrementalParser() {}
@@ -34,7 +36,7 @@ namespace clang {
IncrementalParser::IncrementalParser(CompilerInstance &Instance,
IncrementalAction *Act, llvm::Error &Err,
std::list<PartialTranslationUnit> &PTUs)
- : S(Instance.getSema()), Act(Act), PTUs(PTUs) {
+ : S(Instance.getSema()), CI(Instance), Act(Act), PTUs(PTUs) {
llvm::ErrorAsOutParameter EAO(&Err);
Consumer = &S.getASTConsumer();
P = std::make_unique<Parser>(S.getPreprocessor(), S, /*SkipBodies=*/false);
@@ -224,7 +226,7 @@ std::unique_ptr<llvm::Module> IncrementalParser::GenModule() {
// around we created an empty module to make CodeGen happy. We should make
// sure it always stays empty.
assert(((!CachedInCodeGenModule ||
- !getCompilerInstance()->getPreprocessorOpts().Includes.empty()) ||
+ !CI.getPreprocessorOpts().Includes.empty()) ||
(CachedInCodeGenModule->empty() &&
CachedInCodeGenModule->global_empty() &&
CachedInCodeGenModule->alias_empty() &&
diff --git a/clang/lib/Interpreter/IncrementalParser.h b/clang/lib/Interpreter/IncrementalParser.h
index 2dadc086f18cf..c3d7cd615d9b0 100644
--- a/clang/lib/Interpreter/IncrementalParser.h
+++ b/clang/lib/Interpreter/IncrementalParser.h
@@ -50,6 +50,9 @@ class IncrementalParser {
/// Counts the number of direct user input lines that have been parsed.
unsigned InputCount = 0;
+ /// The CompilerInstance used during parsing.
+ CompilerInstance &CI;
+
/// The FrontendAction used during incremental parsing.
IncrementalAction *Act = nullptr;
>From ec717bf7c8b034114ac3b6adb51b5ae1808a44a1 Mon Sep 17 00:00:00 2001
From: anutosh491 <andersonbhat491 at gmail.com>
Date: Fri, 25 Apr 2025 16:50:12 +0530
Subject: [PATCH 4/4] fix ci 2
---
clang/lib/Interpreter/IncrementalParser.cpp | 1 +
1 file changed, 1 insertion(+)
diff --git a/clang/lib/Interpreter/IncrementalParser.cpp b/clang/lib/Interpreter/IncrementalParser.cpp
index b52cb3263bb7f..9691673be06dd 100644
--- a/clang/lib/Interpreter/IncrementalParser.cpp
+++ b/clang/lib/Interpreter/IncrementalParser.cpp
@@ -19,6 +19,7 @@
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendAction.h"
#include "clang/Interpreter/PartialTranslationUnit.h"
+#include "clang/Lex/PreprocessorOptions.h"
#include "clang/Parse/Parser.h"
#include "clang/Sema/Sema.h"
#include "llvm/IR/Module.h"
More information about the cfe-commits
mailing list