Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions clang/include/clang/Serialization/ASTWriter.h
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,9 @@ class ASTWriter : public ASTDeserializationListener,

/// Mapping from a source location entry to whether it is affecting or not.
llvm::BitVector IsSLocAffecting;
/// Mapping from a source location entry to whether it must be included as
/// input file.
llvm::BitVector IsSLocFileEntryAffecting;

/// Mapping from \c FileID to an index into the FileID adjustment table.
std::vector<FileID> NonAffectingFileIDs;
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/Serialization/ASTReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5862,6 +5862,12 @@ llvm::Error ASTReader::ReadSubmoduleBlock(ModuleFile &F,
}

CurrentModule->Kind = Kind;
// Note that we may be rewriting an existing location and it is important
// to keep doing that. In particular, we would like to prefer a
// `DefinitionLoc` loaded from the module file instead of the location
// created in the current source manager, because it allows the new
// location to be marked as "unaffecting" when writing and avoid creating
// duplicate locations for the same module map file.
CurrentModule->DefinitionLoc = DefinitionLoc;
CurrentModule->Signature = F.Signature;
CurrentModule->IsFromModuleFile = true;
Expand Down
41 changes: 31 additions & 10 deletions clang/lib/Serialization/ASTWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
#include "clang/AST/TypeLocVisitor.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/DiagnosticOptions.h"
#include "clang/Basic/FileEntry.h"
#include "clang/Basic/FileManager.h"
#include "clang/Basic/FileSystemOptions.h"
#include "clang/Basic/IdentifierTable.h"
Expand Down Expand Up @@ -81,6 +82,7 @@
#include "llvm/ADT/APSInt.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/Hashing.h"
#include "llvm/ADT/PointerIntPair.h"
#include "llvm/ADT/STLExtras.h"
Expand Down Expand Up @@ -166,18 +168,25 @@ static TypeCode getTypeCodeForTypeClass(Type::TypeClass id) {

namespace {

std::optional<std::set<const FileEntry *>>
struct AffectingModuleMaps {
llvm::DenseSet<FileID> DefinitionFileIDs;
llvm::DenseSet<const FileEntry *> DefinitionFiles;
};

std::optional<AffectingModuleMaps>
GetAffectingModuleMaps(const Preprocessor &PP, Module *RootModule) {
if (!PP.getHeaderSearchInfo()
.getHeaderSearchOpts()
.ModulesPruneNonAffectingModuleMaps)
return std::nullopt;

const HeaderSearch &HS = PP.getHeaderSearchInfo();
const SourceManager &SM = PP.getSourceManager();
const ModuleMap &MM = HS.getModuleMap();

std::set<const FileEntry *> ModuleMaps;
std::set<const Module *> ProcessedModules;
llvm::DenseSet<FileID> ModuleMaps;

llvm::DenseSet<const Module *> ProcessedModules;
auto CollectModuleMapsForHierarchy = [&](const Module *M) {
M = M->getTopLevelModule();

Expand All @@ -192,13 +201,13 @@ GetAffectingModuleMaps(const Preprocessor &PP, Module *RootModule) {

// The containing module map is affecting, because it's being pointed
// into by Module::DefinitionLoc.
if (auto FE = MM.getContainingModuleMapFile(Mod))
ModuleMaps.insert(*FE);
if (auto F = MM.getContainingModuleMapFileID(Mod); F.isValid())
ModuleMaps.insert(F);
// For inferred modules, the module map that allowed inferring is not
// related to the virtual containing module map file. It did affect the
// compilation, though.
if (auto FE = MM.getModuleMapFileForUniquing(Mod))
ModuleMaps.insert(*FE);
if (auto F = MM.getModuleMapFileIDForUniquing(Mod); F.isValid())
ModuleMaps.insert(F);

for (auto *SubM : Mod->submodules())
Q.push(SubM);
Expand Down Expand Up @@ -268,7 +277,16 @@ GetAffectingModuleMaps(const Preprocessor &PP, Module *RootModule) {
// just ban module map hierarchies where module map defining a (sub)module X
// includes a module map defining a module that's not a submodule of X.

return ModuleMaps;
llvm::DenseSet<const FileEntry *> ModuleFileEntries;
for (FileID MM : ModuleMaps) {
if (auto *FE = SM.getFileEntryForID(MM))
ModuleFileEntries.insert(FE);
}

AffectingModuleMaps R;
R.DefinitionFileIDs = std::move(ModuleMaps);
R.DefinitionFiles = std::move(ModuleFileEntries);
return std::move(R);
}

class ASTTypeWriter {
Expand Down Expand Up @@ -1772,7 +1790,7 @@ void ASTWriter::WriteInputFiles(SourceManager &SourceMgr,
continue;

// Do not emit input files that do not affect current module.
if (!IsSLocAffecting[I])
if (!IsSLocFileEntryAffecting[I])
continue;

InputFileEntry Entry(*Cache->OrigEntry);
Expand Down Expand Up @@ -4924,6 +4942,7 @@ void ASTWriter::computeNonAffectingInputFiles() {
unsigned N = SrcMgr.local_sloc_entry_size();

IsSLocAffecting.resize(N, true);
IsSLocFileEntryAffecting.resize(N, true);

if (!WritingModule)
return;
Expand Down Expand Up @@ -4960,10 +4979,12 @@ void ASTWriter::computeNonAffectingInputFiles() {
continue;

// Don't prune module maps that are affecting.
if (llvm::is_contained(*AffectingModuleMaps, *Cache->OrigEntry))
if (AffectingModuleMaps->DefinitionFileIDs.contains(FID))
continue;

IsSLocAffecting[I] = false;
IsSLocFileEntryAffecting[I] =
AffectingModuleMaps->DefinitionFiles.contains(*Cache->OrigEntry);

FileIDAdjustment += 1;
// Even empty files take up one element in the offset table.
Expand Down
2 changes: 1 addition & 1 deletion clang/test/ClangScanDeps/modules-extern-submodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ module third {}
// CHECK-NEXT: "command-line": [
// CHECK-NEXT: "-cc1",
// CHECK: "-fmodule-map-file=[[PREFIX]]/second/second/module.modulemap"
// CHECK-NOT: "-fmodule-map-file=[[PREFIX]]/second/second/sub.modulemap"
// CHECK-NEXT: "-fmodule-map-file=[[PREFIX]]/second/second/sub.modulemap"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's fairly important to not report those on command lines. On Darwin, we have one module map that includes lots of other module maps, so listing them all blows up the command line length (and is unnecessary).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for pointing this out, I did not realize this was important.

That would require digging into scan-deps a bit, let me try to see if it's possible to match the previous behavior.
In general, I feel that rewriting ModuleDepCollector in a way that walks over the module dependency graph rather than the InputFile is probably the right solution in the long run, but I'm not sure if it's an easy change to make. (I still don't have a good grasp on what outputs are generally expected here, relying on something as internal as InputFile in the serialized AST was a surprise).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've updated the code to keep the old behavior in that case, PTAL.

// CHECK-NOT: "-fmodule-map-file=[[PREFIX]]/third/module.modulemap"
// CHECK: "-fmodule-file=second=[[PREFIX]]/cache/{{.*}}/second-{{.*}}.pcm"
// CHECK: ],
Expand Down
85 changes: 85 additions & 0 deletions clang/test/Modules/prune-non-affecting-module-map-repeated.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// Check that the same module map file passed to -fmodule-map-file *and*
// available from one of the `-fmodule-file` does not allocate extra source
// location space. This optimization is important for using module maps in
// large codebases to avoid running out of source location space.

// RUN: rm -rf %t && mkdir %t
// RUN: split-file %s %t

// RUN: %clang_cc1 -xc++ -fmodules -fno-implicit-modules -fmodule-map-file=%t/base.map -fmodule-name=base -emit-module %t/base.map -o %t/base.pcm
// RUN: %clang_cc1 -xc++ -fmodules -fno-implicit-modules \
// RUN: -fmodule-map-file=%t/base.map -fmodule-map-file=%t/mod1.map \
// RUN: -fmodule-file=%t/base.pcm \
// RUN: -fmodule-name=mod1 -emit-module %t/mod1.map -o %t/mod1.pcm
// RUN: %clang_cc1 -xc++ -fmodules -fno-implicit-modules \
// RUN: -fmodule-map-file=%t/base.map -fmodule-map-file=%t/mod1.map -fmodule-map-file=%t/mod2.map \
// RUN: -fmodule-file=%t/mod1.pcm \
// RUN: -fmodule-name=mod2 -emit-module %t/mod2.map -o %t/mod2.pcm
// RUN: %clang_cc1 -xc++ -fmodules -fno-implicit-modules \
// RUN: -fmodule-map-file=%t/base.map -fmodule-map-file=%t/mod2.map -fmodule-map-file=%t/mod3.map \
// RUN: -fmodule-file=%t/mod2.pcm \
// RUN: -fmodule-name=mod3 -emit-module %t/mod3.map -o %t/mod3.pcm
// RUN: %clang_cc1 -xc++ -fmodules -fno-implicit-modules \
// RUN: -fmodule-map-file=%t/base.map -fmodule-map-file=%t/mod3.map -fmodule-map-file=%t/mod4.map \
// RUN: -fmodule-file=%t/mod3.pcm \
// RUN: -fmodule-name=mod4 -emit-module %t/mod4.map -o %t/mod4.pcm
// RUN: %clang_cc1 -xc++ -fmodules -fno-implicit-modules -fmodule-map-file=%t/base.map -fmodule-map-file=%t/mod4.map -fmodule-file=%t/mod4.pcm -fsyntax-only -verify %t/check_slocs.cc

//--- base.map
module base { header "vector.h" }
//--- mod1.map
module mod1 { header "mod1.h" }
//--- mod2.map
module mod2 { header "mod2.h" }
//--- mod3.map
module mod3 { header "mod3.h" }
//--- mod4.map
module mod4 { header "mod4.h" }
//--- check_slocs.cc
#include "mod4.h"
#pragma clang __debug sloc_usage // expected-remark {{source manager location address space usage}}
// expected-note@* {{% of available space}}

// Module map files files that were specified on the command line are entered twice (once when parsing command-line, once loaded from the .pcm)
// Those that not specified on the command line must be entered once.

// [email protected]:1 {{file entered 2 times}}
// [email protected]:1 {{file entered 2 times}}
// [email protected]:1 {{file entered 1 time}}
// [email protected]:1 {{file entered 1 time}}
// [email protected]:1 {{file entered 1 time}}
// expected-note@* + {{file entered}}


//--- vector.h
#ifndef VECTOR_H
#define VECTOR_H
#endif

//--- mod1.h
#ifndef MOD1
#define MOD1
#include "vector.h"
int mod1();
#endif
//--- mod2.h
#ifndef MOD2
#define MOD2
#include "vector.h"
#include "mod1.h"
int mod2();
#endif
//--- mod3.h
#ifndef MOD3
#define MOD3
#include "vector.h"
#include "mod2.h"
int mod3();
#endif
//--- mod4.h
#ifndef MOD4
#define MOD4
#include "vector.h"
#include "mod3.h"
int mod4();
#endif
Loading