Skip to content

Commit 3f98ce1

Browse files
committed
[C++20] [Modules] Introduce thin BMI
Close #71034 This patch introduces thin BMI, which doesn't contain the definitions of functions and variables if its definitions won't contribute to the ABI. Testing is a big part of the patch. We want to make sure the thin BMI contains the same behavior with the existing and relatively stable fatBMI. This is pretty helpful for further reduction. For user interfeaces, this patch introduces `-fthinBMI-output=` arguments to specify the position of thin BMI. This should be used when compiling a single module unit. The design is helpful to use thin BMI in two phase compilations too. With thin BMI, In two phase compilations, we'll generate 2 BMIs, one thin BMI for being used by consumers, one fat BMI for compiling itself to object files. Maybe it sounds confusing to have 2 BMIs for one module unit. But only the thin BMI will be the BMI we're talking about generally and the fat BMI is only visible by the module unit itself. With one phase compilation, we may find the behavior of `-fthinBMI-output=` is pretty similar with `-fmodule-output=`, except one generating thin BMI and the other generating fat BMI. The design here is based on 2 things: (1) The serialization of C++ is pretty complex. We can't be sure we're handling every detail correctly in the every beginning. (2) The fat BMI is relatively widely used and relatively stable. So it looks not good to replace the fat BMI immediately with thin BMI. But, of course, in the end of the day, we want the consumers to use the thin BMI only. When that day comes, the `-fmodule-output=` will be an alias to `-fthinBMI-output=`. Another design choice is to reuse `-fmodule-output=` and introduce a flag `-femit-thin-BMI`. Then `-femit-thin-BMI -fmodule-output=` will have the same effect with `-fthinBMI-output=` now. The flag `-femit-thin-BMI` should be opt-in now and opt-off later and finally deprecated. The roadmap for thin BMI in my mind is: (1) In clang18, release thin BMI and mark it as experimental. Also encourage users and build systems to try this new mode. (2) In clang19 or clang20 (based on the issue feedbacks), remove the experimental mark for thin BMI and mark fat BMI as deprecated to be used by consumers. (3) In clang21 or clang22, error out if we found the users are trying to import a fat BMI.
1 parent 6d74578 commit 3f98ce1

File tree

112 files changed

+1049
-83
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

112 files changed

+1049
-83
lines changed

clang/include/clang/Basic/DiagnosticDriverKinds.td

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,8 @@ def err_drv_invalid_output_with_multiple_archs : Error<
158158
def err_drv_no_input_files : Error<"no input files">;
159159
def err_drv_output_argument_with_multiple_files : Error<
160160
"cannot specify -o when generating multiple output files">;
161+
def err_drv_thin_bmi_output_argument_with_multiple_files : Error <
162+
"cannot specify -fthinBMI-output when generating multiple module files">;
161163
def err_drv_out_file_argument_with_multiple_sources : Error<
162164
"cannot specify '%0%1' when compiling multiple source files">;
163165
def err_no_external_assembler : Error<

clang/include/clang/Driver/Options.td

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2915,6 +2915,11 @@ def fmodule_output : Flag<["-"], "fmodule-output">, Flags<[NoXarchOption]>,
29152915
Visibility<[ClangOption, CC1Option]>,
29162916
HelpText<"Save intermediate module file results when compiling a standard C++ module unit.">;
29172917

2918+
def fthinBMI_output_EQ : Joined<["-"], "fthinBMI-output=">, Group<f_Group>,
2919+
HelpText<"Specify the output path for the thin BMI for C++20 Named modules">,
2920+
Visibility<[ClangOption, CC1Option, CLOption, DXCOption]>,
2921+
MarshallingInfoString<FrontendOpts<"ThinBMIPath">>;
2922+
29182923
def fmodules_prune_interval : Joined<["-"], "fmodules-prune-interval=">, Group<i_Group>,
29192924
Visibility<[ClangOption, CC1Option]>, MetaVarName<"<seconds>">,
29202925
HelpText<"Specify the interval (in seconds) between attempts to prune the module cache">,
@@ -7223,7 +7228,9 @@ def ast_view : Flag<["-"], "ast-view">,
72237228
def emit_module : Flag<["-"], "emit-module">,
72247229
HelpText<"Generate pre-compiled module file from a module map">;
72257230
def emit_module_interface : Flag<["-"], "emit-module-interface">,
7226-
HelpText<"Generate pre-compiled module file from a C++ module interface">;
7231+
HelpText<"Generate pre-compiled module file from a standard C++ module interface unit">;
7232+
def emit_thin_module_interface : Flag<["-"], "emit-thin-module-interface">,
7233+
HelpText<"Generate reduced prebuilt module interface from a standard C++ module interface unit">;
72277234
def emit_header_unit : Flag<["-"], "emit-header-unit">,
72287235
HelpText<"Generate C++20 header units from header files">;
72297236
def emit_pch : Flag<["-"], "emit-pch">,

clang/include/clang/Frontend/FrontendActions.h

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,9 @@ class GenerateModuleAction : public ASTFrontendAction {
118118
CreateOutputFile(CompilerInstance &CI, StringRef InFile) = 0;
119119

120120
protected:
121+
std::vector<std::unique_ptr<ASTConsumer>>
122+
CreateMultiplexConsumer(CompilerInstance &CI, StringRef InFile);
123+
121124
std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
122125
StringRef InFile) override;
123126

@@ -147,14 +150,27 @@ class GenerateModuleFromModuleMapAction : public GenerateModuleAction {
147150
CreateOutputFile(CompilerInstance &CI, StringRef InFile) override;
148151
};
149152

153+
/// Generates fatBMI (which contains full information to generate the object
154+
/// files) for C++20 Named Modules. Also generates the thin BMI (only contains
155+
/// necessary information for importers) if `-fthinBMI-output=`.
150156
class GenerateModuleInterfaceAction : public GenerateModuleAction {
151-
private:
157+
protected:
152158
bool BeginSourceFileAction(CompilerInstance &CI) override;
153159

160+
std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
161+
StringRef InFile) override;
162+
154163
std::unique_ptr<raw_pwrite_stream>
155164
CreateOutputFile(CompilerInstance &CI, StringRef InFile) override;
156165
};
157166

167+
/// Only generates the thin BMI. This action is mainly used by tests.
168+
class GenerateThinModuleInterfaceAction : public GenerateModuleInterfaceAction {
169+
private:
170+
std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
171+
StringRef InFile) override;
172+
};
173+
158174
class GenerateHeaderUnitAction : public GenerateModuleAction {
159175

160176
private:

clang/include/clang/Frontend/FrontendOptions.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,9 +85,13 @@ enum ActionKind {
8585
/// Generate pre-compiled module from a module map.
8686
GenerateModule,
8787

88-
/// Generate pre-compiled module from a C++ module interface file.
88+
/// Generate pre-compiled module from a standard C++ module interface unit.
8989
GenerateModuleInterface,
9090

91+
/// Generate reduced module interface for a standard C++ module interface
92+
/// unit.
93+
GenerateThinModuleInterface,
94+
9195
/// Generate a C++20 header unit module from a header file.
9296
GenerateHeaderUnit,
9397

@@ -549,6 +553,9 @@ class FrontendOptions {
549553
/// Path which stores the output files for -ftime-trace
550554
std::string TimeTracePath;
551555

556+
/// Path to the thin BMI for -fthinbmi-output=
557+
std::string ThinBMIPath;
558+
552559
public:
553560
FrontendOptions()
554561
: DisableFree(false), RelocatablePCH(false), ShowHelp(false),

clang/include/clang/Serialization/ASTWriter.h

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,10 @@ class ASTWriter : public ASTDeserializationListener,
166166
/// Indicates that the AST contained compiler errors.
167167
bool ASTHasCompilerErrors = false;
168168

169+
/// Indicates that we're going to generate the reduced BMI for C++20
170+
/// named modules.
171+
bool GeneratingThinBMI = false;
172+
169173
/// Mapping from input file entries to the index into the
170174
/// offset table where information about that input file is stored.
171175
llvm::DenseMap<const FileEntry *, uint32_t> InputFileIDs;
@@ -582,7 +586,8 @@ class ASTWriter : public ASTDeserializationListener,
582586
ASTWriter(llvm::BitstreamWriter &Stream, SmallVectorImpl<char> &Buffer,
583587
InMemoryModuleCache &ModuleCache,
584588
ArrayRef<std::shared_ptr<ModuleFileExtension>> Extensions,
585-
bool IncludeTimestamps = true, bool BuildingImplicitModule = false);
589+
bool IncludeTimestamps = true, bool BuildingImplicitModule = false,
590+
bool GeneratingThinBMI = false);
586591
~ASTWriter() override;
587592

588593
ASTContext &getASTContext() const {
@@ -813,14 +818,22 @@ class PCHGenerator : public SemaConsumer {
813818
const ASTWriter &getWriter() const { return Writer; }
814819
SmallVectorImpl<char> &getPCH() const { return Buffer->Data; }
815820

821+
bool isComplete() const { return Buffer->IsComplete; }
822+
PCHBuffer *getBufferPtr() { return Buffer.get(); }
823+
StringRef getOutputFile() const { return OutputFile; }
824+
DiagnosticsEngine &getDiagnostics() const {
825+
return SemaPtr->getDiagnostics();
826+
}
827+
816828
public:
817829
PCHGenerator(const Preprocessor &PP, InMemoryModuleCache &ModuleCache,
818830
StringRef OutputFile, StringRef isysroot,
819831
std::shared_ptr<PCHBuffer> Buffer,
820832
ArrayRef<std::shared_ptr<ModuleFileExtension>> Extensions,
821833
bool AllowASTWithErrors = false, bool IncludeTimestamps = true,
822834
bool BuildingImplicitModule = false,
823-
bool ShouldCacheASTInMemory = false);
835+
bool ShouldCacheASTInMemory = false,
836+
bool GeneratingThinBMI = false);
824837
~PCHGenerator() override;
825838

826839
void InitializeSema(Sema &S) override { SemaPtr = &S; }
@@ -830,6 +843,19 @@ class PCHGenerator : public SemaConsumer {
830843
bool hasEmittedPCH() const { return Buffer->IsComplete; }
831844
};
832845

846+
class ThinBMIGenerator : public PCHGenerator {
847+
public:
848+
ThinBMIGenerator(const Preprocessor &PP, InMemoryModuleCache &ModuleCache,
849+
StringRef OutputFile, std::shared_ptr<PCHBuffer> Buffer,
850+
bool IncludeTimestamps);
851+
852+
void HandleTranslationUnit(ASTContext &Ctx) override;
853+
};
854+
855+
/// If the definition may impact the ABI. If yes, we're allowed to eliminate
856+
/// the definition of D in thin BMI.
857+
bool MayDefAffectABI(const Decl *D);
858+
833859
/// A simple helper class to pack several bits in order into (a) 32 bit
834860
/// integer(s).
835861
class BitsPacker {

clang/lib/Driver/Driver.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4086,6 +4086,13 @@ void Driver::BuildActions(Compilation &C, DerivedArgList &Args,
40864086
}
40874087
}
40884088

4089+
// Diagnose misuse of -fthinBMI-output. It should be an error if we specify
4090+
// -fthinBMI-output with multiple precompilation jobs. Here we didn't check if
4091+
// there are multiple module units in the inputs.
4092+
if (C.getArgs().getLastArg(options::OPT_fthinBMI_output_EQ) &&
4093+
Inputs.size() > 1)
4094+
Diag(clang::diag::err_drv_thin_bmi_output_argument_with_multiple_files);
4095+
40894096
handleArguments(C, Args, Inputs, Actions);
40904097

40914098
bool UseNewOffloadingDriver =

clang/lib/Driver/ToolChains/Clang.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3940,6 +3940,8 @@ static bool RenderModulesOptions(Compilation &C, const Driver &D,
39403940
Args.ClaimAllArgs(options::OPT_fmodule_output);
39413941
Args.ClaimAllArgs(options::OPT_fmodule_output_EQ);
39423942

3943+
Args.AddLastArg(CmdArgs, options::OPT_fthinBMI_output_EQ);
3944+
39433945
return HaveModules;
39443946
}
39453947

clang/lib/Frontend/CompilerInvocation.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2554,6 +2554,7 @@ static const auto &getFrontendActionTable() {
25542554

25552555
{frontend::GenerateModule, OPT_emit_module},
25562556
{frontend::GenerateModuleInterface, OPT_emit_module_interface},
2557+
{frontend::GenerateThinModuleInterface, OPT_emit_thin_module_interface},
25572558
{frontend::GenerateHeaderUnit, OPT_emit_header_unit},
25582559
{frontend::GeneratePCH, OPT_emit_pch},
25592560
{frontend::GenerateInterfaceStubs, OPT_emit_interface_stubs},
@@ -4236,6 +4237,7 @@ static bool isStrictlyPreprocessorAction(frontend::ActionKind Action) {
42364237
case frontend::FixIt:
42374238
case frontend::GenerateModule:
42384239
case frontend::GenerateModuleInterface:
4240+
case frontend::GenerateThinModuleInterface:
42394241
case frontend::GenerateHeaderUnit:
42404242
case frontend::GeneratePCH:
42414243
case frontend::GenerateInterfaceStubs:

clang/lib/Frontend/FrontendActions.cpp

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -184,12 +184,12 @@ bool GeneratePCHAction::BeginSourceFileAction(CompilerInstance &CI) {
184184
return true;
185185
}
186186

187-
std::unique_ptr<ASTConsumer>
188-
GenerateModuleAction::CreateASTConsumer(CompilerInstance &CI,
189-
StringRef InFile) {
187+
std::vector<std::unique_ptr<ASTConsumer>>
188+
GenerateModuleAction::CreateMultiplexConsumer(CompilerInstance &CI,
189+
StringRef InFile) {
190190
std::unique_ptr<raw_pwrite_stream> OS = CreateOutputFile(CI, InFile);
191191
if (!OS)
192-
return nullptr;
192+
return {};
193193

194194
std::string OutputFile = CI.getFrontendOpts().OutputFile;
195195
std::string Sysroot;
@@ -210,6 +210,17 @@ GenerateModuleAction::CreateASTConsumer(CompilerInstance &CI,
210210
+CI.getFrontendOpts().BuildingImplicitModule));
211211
Consumers.push_back(CI.getPCHContainerWriter().CreatePCHContainerGenerator(
212212
CI, std::string(InFile), OutputFile, std::move(OS), Buffer));
213+
return std::move(Consumers);
214+
}
215+
216+
std::unique_ptr<ASTConsumer>
217+
GenerateModuleAction::CreateASTConsumer(CompilerInstance &CI,
218+
StringRef InFile) {
219+
std::vector<std::unique_ptr<ASTConsumer>> Consumers =
220+
CreateMultiplexConsumer(CI, InFile);
221+
if (Consumers.empty())
222+
return nullptr;
223+
213224
return std::make_unique<MultiplexConsumer>(std::move(Consumers));
214225
}
215226

@@ -264,6 +275,35 @@ GenerateModuleInterfaceAction::CreateOutputFile(CompilerInstance &CI,
264275
return CI.createDefaultOutputFile(/*Binary=*/true, InFile, "pcm");
265276
}
266277

278+
static std::unique_ptr<ASTConsumer>
279+
CreateThinBMIGenerator(CompilerInstance &CI, StringRef OutputFile) {
280+
auto Buffer = std::make_shared<PCHBuffer>();
281+
return std::make_unique<ThinBMIGenerator>(
282+
CI.getPreprocessor(), CI.getModuleCache(), OutputFile, Buffer,
283+
/*IncludeTimestamps=*/+CI.getFrontendOpts().IncludeTimestamps);
284+
}
285+
286+
std::unique_ptr<ASTConsumer>
287+
GenerateModuleInterfaceAction::CreateASTConsumer(CompilerInstance &CI,
288+
StringRef InFile) {
289+
std::vector<std::unique_ptr<ASTConsumer>> Consumers =
290+
CreateMultiplexConsumer(CI, InFile);
291+
if (Consumers.empty())
292+
return nullptr;
293+
294+
if (!CI.getFrontendOpts().ThinBMIPath.empty())
295+
Consumers.push_back(
296+
CreateThinBMIGenerator(CI, CI.getFrontendOpts().ThinBMIPath));
297+
298+
return std::make_unique<MultiplexConsumer>(std::move(Consumers));
299+
}
300+
301+
std::unique_ptr<ASTConsumer>
302+
GenerateThinModuleInterfaceAction::CreateASTConsumer(CompilerInstance &CI,
303+
StringRef InFile) {
304+
return CreateThinBMIGenerator(CI, CI.getFrontendOpts().OutputFile);
305+
}
306+
267307
bool GenerateHeaderUnitAction::BeginSourceFileAction(CompilerInstance &CI) {
268308
if (!CI.getLangOpts().CPlusPlusModules) {
269309
CI.getDiagnostics().Report(diag::err_module_interface_requires_cpp_modules);
@@ -830,7 +870,6 @@ void DumpModuleInfoAction::ExecuteAction() {
830870

831871
const LangOptions &LO = getCurrentASTUnit().getLangOpts();
832872
if (LO.CPlusPlusModules && !LO.CurrentModule.empty()) {
833-
834873
ASTReader *R = getCurrentASTUnit().getASTReader().get();
835874
unsigned SubModuleCount = R->getTotalNumSubmodules();
836875
serialization::ModuleFile &MF = R->getModuleManager().getPrimaryModule();

clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ CreateFrontendBaseAction(CompilerInstance &CI) {
6565
return std::make_unique<GenerateModuleFromModuleMapAction>();
6666
case GenerateModuleInterface:
6767
return std::make_unique<GenerateModuleInterfaceAction>();
68+
case GenerateThinModuleInterface:
69+
return std::make_unique<GenerateThinModuleInterfaceAction>();
6870
case GenerateHeaderUnit:
6971
return std::make_unique<GenerateHeaderUnitAction>();
7072
case GeneratePCH: return std::make_unique<GeneratePCHAction>();

clang/lib/Serialization/ASTWriter.cpp

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4593,10 +4593,12 @@ ASTWriter::ASTWriter(llvm::BitstreamWriter &Stream,
45934593
SmallVectorImpl<char> &Buffer,
45944594
InMemoryModuleCache &ModuleCache,
45954595
ArrayRef<std::shared_ptr<ModuleFileExtension>> Extensions,
4596-
bool IncludeTimestamps, bool BuildingImplicitModule)
4596+
bool IncludeTimestamps, bool BuildingImplicitModule,
4597+
bool GeneratingThinBMI)
45974598
: Stream(Stream), Buffer(Buffer), ModuleCache(ModuleCache),
45984599
IncludeTimestamps(IncludeTimestamps),
4599-
BuildingImplicitModule(BuildingImplicitModule) {
4600+
BuildingImplicitModule(BuildingImplicitModule),
4601+
GeneratingThinBMI(GeneratingThinBMI) {
46004602
for (const auto &Ext : Extensions) {
46014603
if (auto Writer = Ext->createExtensionWriter(*this))
46024604
ModuleFileExtensionWriters.push_back(std::move(Writer));
@@ -5403,18 +5405,20 @@ void ASTWriter::WriteDeclUpdatesBlocks(RecordDataImpl &OffsetsRecord) {
54035405

54045406
// Add a trailing update record, if any. These must go last because we
54055407
// lazily load their attached statement.
5406-
if (HasUpdatedBody) {
5407-
const auto *Def = cast<FunctionDecl>(D);
5408-
Record.push_back(UPD_CXX_ADDED_FUNCTION_DEFINITION);
5409-
Record.push_back(Def->isInlined());
5410-
Record.AddSourceLocation(Def->getInnerLocStart());
5411-
Record.AddFunctionDefinition(Def);
5412-
} else if (HasAddedVarDefinition) {
5413-
const auto *VD = cast<VarDecl>(D);
5414-
Record.push_back(UPD_CXX_ADDED_VAR_DEFINITION);
5415-
Record.push_back(VD->isInline());
5416-
Record.push_back(VD->isInlineSpecified());
5417-
Record.AddVarDeclInit(VD);
5408+
if (!GeneratingThinBMI || MayDefAffectABI(D)) {
5409+
if (HasUpdatedBody) {
5410+
const auto *Def = cast<FunctionDecl>(D);
5411+
Record.push_back(UPD_CXX_ADDED_FUNCTION_DEFINITION);
5412+
Record.push_back(Def->isInlined());
5413+
Record.AddSourceLocation(Def->getInnerLocStart());
5414+
Record.AddFunctionDefinition(Def);
5415+
} else if (HasAddedVarDefinition) {
5416+
const auto *VD = cast<VarDecl>(D);
5417+
Record.push_back(UPD_CXX_ADDED_VAR_DEFINITION);
5418+
Record.push_back(VD->isInline());
5419+
Record.push_back(VD->isInlineSpecified());
5420+
Record.AddVarDeclInit(VD);
5421+
}
54185422
}
54195423

54205424
OffsetsRecord.push_back(GetDeclRef(D));

0 commit comments

Comments
 (0)