Skip to content

Commit 2ab4c28

Browse files
authored
[clang-repl] Delegate CodeGen related operations for PTU to IncrementalParser (#137458)
Read discussion : #136404 (comment) and the following comments for context Motivation 1) `IncrementalAction` is designed to keep Frontend statealive across inputs. As per the docstring: “IncrementalAction ensures it keeps its underlying action's objects alive as long as the IncrementalParser needs them.” 2) To align responsibilities with that contract, the parser layer (host: `IncrementalParser`, device: `IncrementalCUDADeviceParser`) should manage PTU registration and module generation, while the interpreter orchestrates at a higher level. What this PR does 1) Moves CodeGen surfaces behind IncrementalAction: GenModule(), getCodeGen(), and the cached “first CodeGen module” now live in IncrementalAction. 2) Moves PTU ownership to the parser layer: Adds IncrementalParser::RegisterPTU(…) (and device counterpart) 3) Add device-side registration in IncrementalCUDADeviceParser. 4) Remove Interpreter::{getCodeGen, GenModule, RegisterPTU}.
1 parent a12d012 commit 2ab4c28

File tree

10 files changed

+308
-212
lines changed

10 files changed

+308
-212
lines changed

clang/include/clang/Interpreter/Interpreter.h

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ class ThreadSafeContext;
3737
namespace clang {
3838

3939
class CompilerInstance;
40-
class CodeGenerator;
4140
class CXXRecordDecl;
4241
class Decl;
4342
class IncrementalExecutor;
@@ -110,10 +109,6 @@ class Interpreter {
110109
// printing happens, it's in an invalid state.
111110
Value LastValue;
112111

113-
/// When CodeGen is created the first llvm::Module gets cached in many places
114-
/// and we must keep it alive.
115-
std::unique_ptr<llvm::Module> CachedInCodeGenModule;
116-
117112
/// Compiler instance performing the incremental compilation.
118113
std::unique_ptr<CompilerInstance> CI;
119114

@@ -175,15 +170,9 @@ class Interpreter {
175170
llvm::Expected<llvm::orc::ExecutorAddr>
176171
getSymbolAddressFromLinkerName(llvm::StringRef LinkerName) const;
177172

178-
std::unique_ptr<llvm::Module> GenModule(IncrementalAction *Action = nullptr);
179-
PartialTranslationUnit &RegisterPTU(TranslationUnitDecl *TU,
180-
std::unique_ptr<llvm::Module> M = {},
181-
IncrementalAction *Action = nullptr);
182-
183173
private:
184174
size_t getEffectivePTUSize() const;
185175
void markUserCodeStart();
186-
llvm::Expected<Expr *> ExtractValueFromExpr(Expr *E);
187176

188177
// A cache for the compiled destructors used to for de-allocation of managed
189178
// clang::Values.
@@ -206,11 +195,6 @@ class Interpreter {
206195
// This function forces emission of the needed dtor.
207196
llvm::Expected<llvm::orc::ExecutorAddr>
208197
CompileDtorCall(CXXRecordDecl *CXXRD) const;
209-
210-
/// @}
211-
/// @name Code generation
212-
/// @{
213-
CodeGenerator *getCodeGen(IncrementalAction *Action = nullptr) const;
214198
};
215199
} // namespace clang
216200

clang/lib/Interpreter/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ endif()
2222
add_clang_library(clangInterpreter
2323
DeviceOffload.cpp
2424
CodeCompletion.cpp
25+
IncrementalAction.cpp
2526
IncrementalExecutor.cpp
2627
IncrementalParser.cpp
2728
Interpreter.cpp

clang/lib/Interpreter/DeviceOffload.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,10 @@ namespace clang {
2626

2727
IncrementalCUDADeviceParser::IncrementalCUDADeviceParser(
2828
CompilerInstance &DeviceInstance, CompilerInstance &HostInstance,
29+
IncrementalAction *DeviceAct,
2930
llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> FS,
30-
llvm::Error &Err, const std::list<PartialTranslationUnit> &PTUs)
31-
: IncrementalParser(DeviceInstance, Err), PTUs(PTUs), VFS(FS),
31+
llvm::Error &Err, std::list<PartialTranslationUnit> &PTUs)
32+
: IncrementalParser(DeviceInstance, DeviceAct, Err, PTUs), VFS(FS),
3233
CodeGenOpts(HostInstance.getCodeGenOpts()),
3334
TargetOpts(DeviceInstance.getTargetOpts()) {
3435
if (Err)

clang/lib/Interpreter/DeviceOffload.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,16 @@ struct PartialTranslationUnit;
2222
class CompilerInstance;
2323
class CodeGenOptions;
2424
class TargetOptions;
25+
class IncrementalAction;
2526

2627
class IncrementalCUDADeviceParser : public IncrementalParser {
27-
const std::list<PartialTranslationUnit> &PTUs;
2828

2929
public:
3030
IncrementalCUDADeviceParser(
3131
CompilerInstance &DeviceInstance, CompilerInstance &HostInstance,
32+
IncrementalAction *DeviceAct,
3233
llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> VFS,
33-
llvm::Error &Err, const std::list<PartialTranslationUnit> &PTUs);
34+
llvm::Error &Err, std::list<PartialTranslationUnit> &PTUs);
3435

3536
// Generate PTX for the last PTU.
3637
llvm::Expected<llvm::StringRef> GeneratePTX();
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
//===--- IncrementalAction.h - Incremental Frontend Action -*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "IncrementalAction.h"
10+
11+
#include "clang/AST/ASTConsumer.h"
12+
#include "clang/CodeGen/CodeGenAction.h"
13+
#include "clang/CodeGen/ModuleBuilder.h"
14+
#include "clang/Frontend/CompilerInstance.h"
15+
#include "clang/Frontend/FrontendOptions.h"
16+
#include "clang/FrontendTool/Utils.h"
17+
#include "clang/Interpreter/Interpreter.h"
18+
#include "clang/Lex/PreprocessorOptions.h"
19+
#include "clang/Sema/Sema.h"
20+
#include "llvm/IR/Module.h"
21+
#include "llvm/Support/Error.h"
22+
#include "llvm/Support/ErrorHandling.h"
23+
24+
namespace clang {
25+
IncrementalAction::IncrementalAction(CompilerInstance &CI,
26+
llvm::LLVMContext &LLVMCtx,
27+
llvm::Error &Err, Interpreter &I,
28+
std::unique_ptr<ASTConsumer> Consumer)
29+
: WrapperFrontendAction([&]() {
30+
llvm::ErrorAsOutParameter EAO(&Err);
31+
std::unique_ptr<FrontendAction> Act;
32+
switch (CI.getFrontendOpts().ProgramAction) {
33+
default:
34+
Err = llvm::createStringError(
35+
std::errc::state_not_recoverable,
36+
"Driver initialization failed. "
37+
"Incremental mode for action %d is not supported",
38+
CI.getFrontendOpts().ProgramAction);
39+
return Act;
40+
case frontend::ASTDump:
41+
case frontend::ASTPrint:
42+
case frontend::ParseSyntaxOnly:
43+
Act = CreateFrontendAction(CI);
44+
break;
45+
case frontend::PluginAction:
46+
case frontend::EmitAssembly:
47+
case frontend::EmitBC:
48+
case frontend::EmitObj:
49+
case frontend::PrintPreprocessedInput:
50+
case frontend::EmitLLVMOnly:
51+
Act.reset(new EmitLLVMOnlyAction(&LLVMCtx));
52+
break;
53+
}
54+
return Act;
55+
}()),
56+
Interp(I), CI(CI), Consumer(std::move(Consumer)) {}
57+
58+
std::unique_ptr<ASTConsumer>
59+
IncrementalAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
60+
std::unique_ptr<ASTConsumer> C =
61+
WrapperFrontendAction::CreateASTConsumer(CI, InFile);
62+
63+
if (Consumer) {
64+
std::vector<std::unique_ptr<ASTConsumer>> Cs;
65+
Cs.push_back(std::move(Consumer));
66+
Cs.push_back(std::move(C));
67+
return std::make_unique<MultiplexConsumer>(std::move(Cs));
68+
}
69+
70+
return std::make_unique<InProcessPrintingASTConsumer>(std::move(C), Interp);
71+
}
72+
73+
void IncrementalAction::ExecuteAction() {
74+
WrapperFrontendAction::ExecuteAction();
75+
getCompilerInstance().getSema().CurContext = nullptr;
76+
}
77+
78+
void IncrementalAction::EndSourceFile() {
79+
if (IsTerminating && getWrapped())
80+
WrapperFrontendAction::EndSourceFile();
81+
}
82+
83+
void IncrementalAction::FinalizeAction() {
84+
assert(!IsTerminating && "Already finalized!");
85+
IsTerminating = true;
86+
EndSourceFile();
87+
}
88+
89+
void IncrementalAction::CacheCodeGenModule() {
90+
CachedInCodeGenModule = GenModule();
91+
}
92+
93+
llvm::Module *IncrementalAction::getCachedCodeGenModule() const {
94+
return CachedInCodeGenModule.get();
95+
}
96+
97+
std::unique_ptr<llvm::Module> IncrementalAction::GenModule() {
98+
static unsigned ID = 0;
99+
if (CodeGenerator *CG = getCodeGen()) {
100+
// Clang's CodeGen is designed to work with a single llvm::Module. In many
101+
// cases for convenience various CodeGen parts have a reference to the
102+
// llvm::Module (TheModule or Module) which does not change when a new
103+
// module is pushed. However, the execution engine wants to take ownership
104+
// of the module which does not map well to CodeGen's design. To work this
105+
// around we created an empty module to make CodeGen happy. We should make
106+
// sure it always stays empty.
107+
assert(((!CachedInCodeGenModule ||
108+
!CI.getPreprocessorOpts().Includes.empty()) ||
109+
(CachedInCodeGenModule->empty() &&
110+
CachedInCodeGenModule->global_empty() &&
111+
CachedInCodeGenModule->alias_empty() &&
112+
CachedInCodeGenModule->ifunc_empty())) &&
113+
"CodeGen wrote to a readonly module");
114+
std::unique_ptr<llvm::Module> M(CG->ReleaseModule());
115+
CG->StartModule("incr_module_" + std::to_string(ID++), M->getContext());
116+
return M;
117+
}
118+
return nullptr;
119+
}
120+
121+
CodeGenerator *IncrementalAction::getCodeGen() const {
122+
FrontendAction *WrappedAct = getWrapped();
123+
if (!WrappedAct || !WrappedAct->hasIRSupport())
124+
return nullptr;
125+
return static_cast<CodeGenAction *>(WrappedAct)->getCodeGenerator();
126+
}
127+
128+
InProcessPrintingASTConsumer::InProcessPrintingASTConsumer(
129+
std::unique_ptr<ASTConsumer> C, Interpreter &I)
130+
: MultiplexConsumer(std::move(C)), Interp(I) {}
131+
132+
bool InProcessPrintingASTConsumer::HandleTopLevelDecl(DeclGroupRef DGR) {
133+
if (DGR.isNull())
134+
return true;
135+
136+
for (Decl *D : DGR)
137+
if (auto *TLSD = llvm::dyn_cast<TopLevelStmtDecl>(D))
138+
if (TLSD && TLSD->isSemiMissing()) {
139+
auto ExprOrErr = Interp.convertExprToValue(cast<Expr>(TLSD->getStmt()));
140+
if (llvm::Error E = ExprOrErr.takeError()) {
141+
llvm::logAllUnhandledErrors(std::move(E), llvm::errs(),
142+
"Value printing failed: ");
143+
return false; // abort parsing
144+
}
145+
TLSD->setStmt(*ExprOrErr);
146+
}
147+
148+
return MultiplexConsumer::HandleTopLevelDecl(DGR);
149+
}
150+
151+
} // namespace clang
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
//===--- IncrementalAction.h - Incremental Frontend Action -*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_CLANG_INTERPRETER_INCREMENTALACTION_H
10+
#define LLVM_CLANG_INTERPRETER_INCREMENTALACTION_H
11+
12+
#include "clang/Frontend/FrontendActions.h"
13+
#include "clang/Frontend/MultiplexConsumer.h"
14+
15+
namespace llvm {
16+
class Module;
17+
}
18+
19+
namespace clang {
20+
21+
class Interpreter;
22+
class CodeGenerator;
23+
24+
/// A custom action enabling the incremental processing functionality.
25+
///
26+
/// The usual \p FrontendAction expects one call to ExecuteAction and once it
27+
/// sees a call to \p EndSourceFile it deletes some of the important objects
28+
/// such as \p Preprocessor and \p Sema assuming no further input will come.
29+
///
30+
/// \p IncrementalAction ensures it keep its underlying action's objects alive
31+
/// as long as the \p IncrementalParser needs them.
32+
///
33+
class IncrementalAction : public WrapperFrontendAction {
34+
private:
35+
bool IsTerminating = false;
36+
Interpreter &Interp;
37+
CompilerInstance &CI;
38+
std::unique_ptr<ASTConsumer> Consumer;
39+
40+
/// When CodeGen is created the first llvm::Module gets cached in many places
41+
/// and we must keep it alive.
42+
std::unique_ptr<llvm::Module> CachedInCodeGenModule;
43+
44+
public:
45+
IncrementalAction(CompilerInstance &CI, llvm::LLVMContext &LLVMCtx,
46+
llvm::Error &Err, Interpreter &I,
47+
std::unique_ptr<ASTConsumer> Consumer = nullptr);
48+
49+
FrontendAction *getWrapped() const { return WrappedAction.get(); }
50+
51+
TranslationUnitKind getTranslationUnitKind() override {
52+
return TU_Incremental;
53+
}
54+
55+
std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
56+
StringRef InFile) override;
57+
58+
void ExecuteAction() override;
59+
60+
// Do not terminate after processing the input. This allows us to keep various
61+
// clang objects alive and to incrementally grow the current TU.
62+
void EndSourceFile() override;
63+
64+
void FinalizeAction();
65+
66+
/// Cache the current CodeGen module to preserve internal references.
67+
void CacheCodeGenModule();
68+
69+
/// Access the cached CodeGen module.
70+
llvm::Module *getCachedCodeGenModule() const;
71+
72+
/// Access the current code generator.
73+
CodeGenerator *getCodeGen() const;
74+
75+
/// Generate an LLVM module for the most recent parsed input.
76+
std::unique_ptr<llvm::Module> GenModule();
77+
};
78+
79+
class InProcessPrintingASTConsumer final : public MultiplexConsumer {
80+
Interpreter &Interp;
81+
82+
public:
83+
InProcessPrintingASTConsumer(std::unique_ptr<ASTConsumer> C, Interpreter &I);
84+
85+
bool HandleTopLevelDecl(DeclGroupRef DGR) override;
86+
};
87+
88+
} // end namespace clang
89+
90+
#endif // LLVM_CLANG_INTERPRETER_INCREMENTALACTION_H

clang/lib/Interpreter/IncrementalParser.cpp

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,24 +11,29 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
#include "IncrementalParser.h"
14+
#include "IncrementalAction.h"
1415

1516
#include "clang/AST/DeclContextInternals.h"
1617
#include "clang/Frontend/CompilerInstance.h"
1718
#include "clang/Interpreter/PartialTranslationUnit.h"
1819
#include "clang/Parse/Parser.h"
1920
#include "clang/Sema/Sema.h"
21+
#include "llvm/IR/Module.h"
2022
#include "llvm/Support/CrashRecoveryContext.h"
2123
#include "llvm/Support/Error.h"
2224

2325
#include <sstream>
2426

27+
#define DEBUG_TYPE "clang-repl"
28+
2529
namespace clang {
2630

2731
// IncrementalParser::IncrementalParser() {}
2832

2933
IncrementalParser::IncrementalParser(CompilerInstance &Instance,
30-
llvm::Error &Err)
31-
: S(Instance.getSema()) {
34+
IncrementalAction *Act, llvm::Error &Err,
35+
std::list<PartialTranslationUnit> &PTUs)
36+
: S(Instance.getSema()), Act(Act), PTUs(PTUs) {
3237
llvm::ErrorAsOutParameter EAO(&Err);
3338
Consumer = &S.getASTConsumer();
3439
P.reset(new Parser(S.getPreprocessor(), S, /*SkipBodies=*/false));
@@ -185,4 +190,25 @@ void IncrementalParser::CleanUpPTU(TranslationUnitDecl *MostRecentTU) {
185190
}
186191
}
187192

193+
PartialTranslationUnit &
194+
IncrementalParser::RegisterPTU(TranslationUnitDecl *TU,
195+
std::unique_ptr<llvm::Module> M /*={}*/) {
196+
PTUs.emplace_back(PartialTranslationUnit());
197+
PartialTranslationUnit &LastPTU = PTUs.back();
198+
LastPTU.TUPart = TU;
199+
200+
if (!M)
201+
M = Act->GenModule();
202+
203+
assert((!Act->getCodeGen() || M) && "Must have a llvm::Module at this point");
204+
205+
LastPTU.TheModule = std::move(M);
206+
LLVM_DEBUG(llvm::dbgs() << "compile-ptu " << PTUs.size() - 1
207+
<< ": [TU=" << LastPTU.TUPart);
208+
if (LastPTU.TheModule)
209+
LLVM_DEBUG(llvm::dbgs() << ", M=" << LastPTU.TheModule.get() << " ("
210+
<< LastPTU.TheModule->getName() << ")");
211+
LLVM_DEBUG(llvm::dbgs() << "]\n");
212+
return LastPTU;
213+
}
188214
} // end namespace clang

0 commit comments

Comments
 (0)