Skip to content

Commit 6e4090d

Browse files
committed
Add some basic extra support for C++ unity building
(Unity building here called "jumbo" because it is called that in the context of the Chromium project.) This adds an example plugin named `JumboSupport` that adds a pragma named `jumbo`, which signals that the main source file is a jumbo compilation unit that will include other source files. After each include into the main source file, the contents of all anonymous namespace declarations in the included source file are hidden, so that the anonymous namespace remains local to source files. It also undefines all macros defined in the included source file. Some (intentionally) minimal changes to clang is implemented to make it possible to implement this functionality in the plugin.
1 parent b76bc18 commit 6e4090d

File tree

6 files changed

+209
-0
lines changed

6 files changed

+209
-0
lines changed

clang/examples/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@ if(CLANG_PLUGIN_SUPPORT)
1111
add_subdirectory(CallSuperAttribute)
1212
add_subdirectory(PluginsOrder)
1313
endif()
14+
add_subdirectory(JumboSupport)
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
add_llvm_library(JumboSupport MODULE JumboSupport.cpp PLUGIN_TOOL clang)
2+
3+
if(LLVM_ENABLE_PLUGINS AND (WIN32 OR CYGWIN))
4+
set(LLVM_LINK_COMPONENTS
5+
Support
6+
)
7+
clang_target_link_libraries(JumboSupport PRIVATE
8+
clangAST
9+
clangBasic
10+
clangFrontend
11+
clangLex
12+
LLVMSupport
13+
)
14+
endif()
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
//===- ResetAnonymousNamespace.cpp ----------------------------------------===//
2+
//
3+
// Clang plugin that adds
4+
//
5+
// #pragma reset_anonymous_namespace
6+
//
7+
// which resets the anonymous namespace, as if a new translation unit was being
8+
// processed.
9+
//
10+
//===----------------------------------------------------------------------===//
11+
12+
#include <set>
13+
#include <string>
14+
15+
#include "clang/AST/AST.h"
16+
#include "clang/AST/ASTConsumer.h"
17+
#include "clang/AST/RecursiveASTVisitor.h"
18+
#include "clang/Frontend/FrontendPluginRegistry.h"
19+
#include "clang/Lex/LexDiagnostic.h"
20+
#include "clang/Lex/Preprocessor.h"
21+
22+
namespace {
23+
24+
bool IsLocationInFile(clang::SourceManager &SM, clang::SourceLocation Loc,
25+
const clang::FileEntry *File) {
26+
clang::FileID FID = SM.getFileID(SM.getSpellingLoc(Loc));
27+
return FID.isValid() && SM.getFileEntryForID(FID) == File;
28+
}
29+
30+
class DisableTaggedNamespaceDecls
31+
: public clang::RecursiveASTVisitor<DisableTaggedNamespaceDecls> {
32+
private:
33+
clang::SourceManager &SM;
34+
const clang::FileEntry *File;
35+
36+
public:
37+
DisableTaggedNamespaceDecls(clang::SourceManager &SM,
38+
const clang::FileEntry *File)
39+
: SM(SM), File(File) {}
40+
41+
bool VisitNamespaceDecl(clang::NamespaceDecl *NS) {
42+
if (NS->isAnonymousNamespace())
43+
if (IsLocationInFile(SM, NS->getBeginLoc(), File))
44+
NS->setDisabled();
45+
return true;
46+
}
47+
};
48+
49+
class ASTConsumer : public clang::ASTConsumer {
50+
clang::ASTContext *Context;
51+
52+
public:
53+
static ASTConsumer *Instance;
54+
55+
ASTConsumer() { Instance = this; }
56+
57+
void Initialize(clang::ASTContext &Ctx) override { Context = &Ctx; }
58+
59+
clang::ASTContext *getASTContext() { return Context; }
60+
};
61+
62+
ASTConsumer *ASTConsumer::Instance = nullptr;
63+
64+
class PPCallbacks : public clang::PPCallbacks {
65+
clang::Preprocessor &PP;
66+
clang::SourceManager &SM;
67+
68+
const clang::FileEntry *CurrentFile;
69+
70+
std::set<std::string> DefinedMacros;
71+
72+
public:
73+
PPCallbacks(clang::Preprocessor &PP) : PP(PP), SM(PP.getSourceManager()) {}
74+
75+
static void Register(clang::Preprocessor &PP) {
76+
PP.addPPCallbacks(std::make_unique<PPCallbacks>(PP));
77+
}
78+
79+
void
80+
InclusionDirective(clang::SourceLocation HashLoc,
81+
const clang::Token &IncludeTok, llvm::StringRef FileName,
82+
bool IsAngled, clang::CharSourceRange FilenameRange,
83+
clang::OptionalFileEntryRef File,
84+
llvm::StringRef SearchPath, llvm::StringRef RelativePath,
85+
const clang::Module *SuggestedModule, bool ModuleImported,
86+
clang::SrcMgr::CharacteristicKind FileType) override {
87+
if (SM.isInMainFile(HashLoc))
88+
CurrentFile = &File->getFileEntry();
89+
}
90+
91+
void FileChanged(clang::SourceLocation Loc, FileChangeReason Reason,
92+
clang::SrcMgr::CharacteristicKind,
93+
clang::FileID PrevFID) override {
94+
if (SM.isInMainFile(Loc)) {
95+
auto Context = ASTConsumer::Instance->getASTContext();
96+
97+
DisableTaggedNamespaceDecls Visitor(SM, CurrentFile);
98+
Visitor.TraverseDecl(Context->getTranslationUnitDecl());
99+
100+
llvm::BumpPtrAllocator &Allocator = PP.getPreprocessorAllocator();
101+
for (auto Name : DefinedMacros) {
102+
clang::IdentifierInfo *II = PP.getIdentifierInfo(Name);
103+
PP.appendMacroDirective(II, new (Allocator)
104+
clang::UndefMacroDirective(Loc));
105+
}
106+
107+
CurrentFile = nullptr;
108+
DefinedMacros.clear();
109+
}
110+
}
111+
112+
void MacroDefined(const clang::Token &Name,
113+
const clang::MacroDirective *MD) override {
114+
if (CurrentFile && IsLocationInFile(SM, Name.getLocation(), CurrentFile))
115+
DefinedMacros.emplace(Name.getIdentifierInfo()->getName().str());
116+
}
117+
118+
void MacroUndefined(const clang::Token &Name,
119+
const clang::MacroDefinition &MD,
120+
const clang::MacroDirective *Undef) override {
121+
if (CurrentFile && IsLocationInFile(SM, Name.getLocation(), CurrentFile))
122+
DefinedMacros.erase(Name.getIdentifierInfo()->getName().str());
123+
}
124+
};
125+
126+
class JumboFrontendAction : public clang::PluginASTAction {
127+
public:
128+
std::unique_ptr<clang::ASTConsumer>
129+
CreateASTConsumer(clang::CompilerInstance &CI, llvm::StringRef) override {
130+
return std::make_unique<ASTConsumer>();
131+
}
132+
133+
bool ParseArgs(const clang::CompilerInstance &CI,
134+
const std::vector<std::string> &args) override {
135+
return true;
136+
}
137+
};
138+
139+
class PragmaJumbo : public clang::PragmaHandler {
140+
public:
141+
PragmaJumbo() : clang::PragmaHandler("jumbo") {}
142+
143+
void HandlePragma(clang::Preprocessor &PP, clang::PragmaIntroducer Introducer,
144+
clang::Token &PragmaTok) override {
145+
clang::Token Tok;
146+
PP.LexUnexpandedToken(Tok);
147+
if (Tok.isNot(clang::tok::eod))
148+
PP.Diag(Tok, clang::diag::ext_pp_extra_tokens_at_eol) << "pragma unity";
149+
150+
if (!ASTConsumer::Instance) {
151+
// Plugin not enabled.
152+
return;
153+
}
154+
155+
PPCallbacks::Register(PP);
156+
}
157+
};
158+
159+
} // namespace
160+
161+
static clang::FrontendPluginRegistry::Add<JumboFrontendAction>
162+
X("jumbo-support", "jumbo compilation support tools");
163+
164+
static clang::PragmaHandlerRegistry::Add<PragmaJumbo>
165+
P1("jumbo", "begin treating top-level includes as jumbo includes");

clang/include/clang/AST/Decl.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -579,6 +579,10 @@ class NamespaceDecl : public NamedDecl,
579579
/// The unnamed namespace that inhabits this namespace, if any.
580580
NamespaceDecl *AnonymousNamespace = nullptr;
581581

582+
/// Names in this specific namespace declaration should not be included in
583+
/// lookups.
584+
bool IsDisabled = false;
585+
582586
NamespaceDecl(ASTContext &C, DeclContext *DC, bool Inline,
583587
SourceLocation StartLoc, SourceLocation IdLoc,
584588
IdentifierInfo *Id, NamespaceDecl *PrevDecl, bool Nested);
@@ -681,6 +685,9 @@ class NamespaceDecl : public NamedDecl,
681685
static NamespaceDecl *castFromDeclContext(const DeclContext *DC) {
682686
return static_cast<NamespaceDecl *>(const_cast<DeclContext*>(DC));
683687
}
688+
689+
bool isDisabled() const { return IsDisabled; }
690+
void setDisabled() { IsDisabled = true; }
684691
};
685692

686693
class VarDecl;

clang/lib/AST/ItaniumMangle.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "clang/AST/Expr.h"
2525
#include "clang/AST/ExprCXX.h"
2626
#include "clang/AST/ExprConcepts.h"
27+
#include "clang/Basic/SourceManager.h"
2728
#include "clang/AST/ExprObjC.h"
2829
#include "clang/AST/Mangle.h"
2930
#include "clang/AST/TypeLoc.h"
@@ -1578,8 +1579,23 @@ void CXXNameMangler::mangleUnqualifiedName(
15781579

15791580
if (const NamespaceDecl *NS = dyn_cast<NamespaceDecl>(ND)) {
15801581
if (NS->isAnonymousNamespace()) {
1582+
#if 0
15811583
// This is how gcc mangles these names.
15821584
Out << "12_GLOBAL__N_1";
1585+
#endif
1586+
1587+
#if 1
1588+
// Add a per-file unique key for the source file containing
1589+
// the declaration.
1590+
// FIXME: This should be controlled by a flag.
1591+
SourceManager &SM = Context.getASTContext().getSourceManager();
1592+
SourceLocation Loc = SM.getSpellingLoc(NS->getBeginLoc());
1593+
1594+
std::string Name("__anonymous_");
1595+
Name.append(std::to_string(SM.getFileID(Loc).getHashValue()));
1596+
1597+
Out << Name.size() << Name;
1598+
#endif
15831599
break;
15841600
}
15851601
}

clang/lib/Sema/SemaLookup.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1137,6 +1137,12 @@ static bool LookupDirect(Sema &S, LookupResult &R, const DeclContext *DC) {
11371137
// Perform lookup into this declaration context.
11381138
DeclContext::lookup_result DR = DC->lookup(R.getLookupName());
11391139
for (NamedDecl *D : DR) {
1140+
DeclContext *DCtx = D->getDeclContext();
1141+
if (EnumDecl *ED = dyn_cast<EnumDecl>(DCtx))
1142+
DCtx = ED->getDeclContext();
1143+
if (NamespaceDecl *NS = dyn_cast<NamespaceDecl>(DCtx))
1144+
if (NS->isDisabled())
1145+
continue;
11401146
if ((D = R.getAcceptableDecl(D))) {
11411147
R.addDecl(D);
11421148
Found = true;

0 commit comments

Comments
 (0)