Modules hide macro definitions by default, so that silly things like

include guards don't show up as macro definitions in every translation
unit that imports a module. Macro definitions can, however, be
exported with the intentionally-ugly #__export_macro__
directive. Implement this feature by not even bothering to serialize
non-exported macros to a module, because clients of that module need
not (should not) know that these macros even exist.

llvm-svn: 138943
This commit is contained in:
Douglas Gregor 2011-09-01 17:04:32 +00:00
parent 0274487d97
commit 4a69c2e6c5
15 changed files with 184 additions and 49 deletions

View File

@ -324,4 +324,6 @@ def err_pp_linemarker_invalid_pop : Error<
def ext_pp_line_too_big : Extension<
"C requires #line number to be less than %0, allowed as extension">;
def err_pp_export_non_macro : Error<"no macro named %0 to export">;
}

View File

@ -89,6 +89,9 @@ PPKEYWORD(sccs)
PPKEYWORD(assert)
PPKEYWORD(unassert)
// Clang extensions
PPKEYWORD(__export_macro__)
//===----------------------------------------------------------------------===//
// Language keywords.
//===----------------------------------------------------------------------===//

View File

@ -39,6 +39,11 @@ class MacroInfo {
IdentifierInfo **ArgumentList;
unsigned NumArguments;
/// \brief The location at which this macro was exported from its module.
///
/// If invalid, this macro has not been explicitly exported.
SourceLocation ExportLocation;
/// ReplacementTokens - This is the list of tokens that the macro is defined
/// to.
SmallVector<Token, 8> ReplacementTokens;
@ -68,6 +73,9 @@ class MacroInfo {
/// IsFromAST - True if this macro was loaded from an AST file.
bool IsFromAST : 1;
/// \brief Whether this macro changed after it was loaded from an AST file.
bool ChangedAfterLoad : 1;
private:
//===--------------------------------------------------------------------===//
// State that changes as the macro is used.
@ -209,6 +217,14 @@ public:
/// setIsFromAST - Set whether this macro was loaded from an AST file.
void setIsFromAST(bool FromAST = true) { IsFromAST = FromAST; }
/// \brief Determine whether this macro has changed since it was loaded from
/// an AST file.
bool hasChangedAfterLoad() const { return ChangedAfterLoad; }
/// \brief Note whether this macro has changed after it was loaded from an
/// AST file.
void setChangedAfterLoad(bool CAL = true) { ChangedAfterLoad = CAL; }
/// isUsed - Return false if this macro is defined in the main file and has
/// not yet been used.
bool isUsed() const { return IsUsed; }
@ -262,6 +278,19 @@ public:
IsDisabled = true;
}
/// \brief Set the export location for this macro.
void setExportLocation(SourceLocation ExportLoc) {
ExportLocation = ExportLoc;
}
/// \brief Determine whether this macro was explicitly exported from its
/// module.
bool isExported() const { return ExportLocation.isValid(); }
/// \brief Determine the location where this macro was explicitly exported
/// from its module.
SourceLocation getExportLocation() { return ExportLocation; }
private:
unsigned getDefinitionLengthSlow(SourceManager &SM) const;
};

View File

@ -1117,7 +1117,8 @@ private:
void HandleDigitDirective(Token &Tok);
void HandleUserDiagnosticDirective(Token &Tok, bool isWarning);
void HandleIdentSCCSDirective(Token &Tok);
void HandleMacroExportDirective(Token &Tok);
// File inclusion.
void HandleIncludeDirective(SourceLocation HashLoc,
Token &Tok,

View File

@ -348,7 +348,7 @@ private:
void WriteSourceManagerBlock(SourceManager &SourceMgr,
const Preprocessor &PP,
StringRef isysroot);
void WritePreprocessor(const Preprocessor &PP);
void WritePreprocessor(const Preprocessor &PP, bool IsModule);
void WriteHeaderSearch(HeaderSearch &HS, StringRef isysroot);
void WritePreprocessorDetail(PreprocessingRecord &PPRec);
void WritePragmaDiagnosticMappings(const Diagnostic &Diag);
@ -359,7 +359,7 @@ private:
void WriteTypeDeclOffsets();
void WriteSelectors(Sema &SemaRef);
void WriteReferencedSelectorsPool(Sema &SemaRef);
void WriteIdentifierTable(Preprocessor &PP);
void WriteIdentifierTable(Preprocessor &PP, bool IsModule);
void WriteAttributes(const AttrVec &Attrs, RecordDataImpl &Record);
void WriteDeclUpdatesBlocks();
void WriteDeclReplacementsBlock();
@ -386,7 +386,8 @@ private:
void WriteDecl(ASTContext &Context, Decl *D);
void WriteASTCore(Sema &SemaRef, MemorizeStatCalls *StatCalls,
StringRef isysroot, const std::string &OutputFile);
StringRef isysroot, const std::string &OutputFile,
bool IsModule);
public:
/// \brief Create a new precompiled header writer that outputs to
@ -407,11 +408,14 @@ public:
/// \param StatCalls the object that cached all of the stat() calls made while
/// searching for source files and headers.
///
/// \param isysroot if non-empty, write a relocatable PCH file whose headers
/// \param IsModule Whether we're writing a module (otherwise, we're writing a
/// precompiled header).
///
/// \param isysroot if non-empty, write a relocatable file whose headers
/// are relative to the given system root.
void WriteAST(Sema &SemaRef, MemorizeStatCalls *StatCalls,
const std::string &OutputFile,
StringRef isysroot);
bool IsModule, StringRef isysroot);
/// \brief Emit a source location.
void AddSourceLocation(SourceLocation Loc, RecordDataImpl &Record);
@ -643,6 +647,7 @@ public:
class PCHGenerator : public SemaConsumer {
const Preprocessor &PP;
std::string OutputFile;
bool IsModule;
std::string isysroot;
raw_ostream *Out;
Sema *SemaPtr;
@ -657,6 +662,7 @@ protected:
public:
PCHGenerator(const Preprocessor &PP, StringRef OutputFile,
bool IsModule,
StringRef isysroot, raw_ostream *Out);
~PCHGenerator();
virtual void InitializeSema(Sema &S) { SemaPtr = &S; }

View File

@ -217,6 +217,7 @@ tok::PPKeywordKind IdentifierInfo::getPPKeywordID() const {
CASE(12, 'i', 'c', include_next);
CASE(16, '_', 'i', __include_macros);
CASE(16, '_', 'e', __export_macro__);
#undef CASE
#undef HASH
}

View File

@ -771,7 +771,7 @@ class PrecompilePreambleConsumer : public PCHGenerator,
public:
PrecompilePreambleConsumer(ASTUnit &Unit, const Preprocessor &PP,
StringRef isysroot, raw_ostream *Out)
: PCHGenerator(PP, "", isysroot, Out), Unit(Unit),
: PCHGenerator(PP, "", /*IsModule=*/false, isysroot, Out), Unit(Unit),
Hash(Unit.getCurrentTopLevelHashValue()) {
Hash = 0;
}
@ -2324,7 +2324,8 @@ bool ASTUnit::serialize(raw_ostream &OS) {
std::vector<unsigned char> Buffer;
llvm::BitstreamWriter Stream(Buffer);
ASTWriter Writer(Stream);
Writer.WriteAST(getSema(), 0, std::string(), "");
// FIXME: Handle modules
Writer.WriteAST(getSema(), 0, std::string(), /*IsModule=*/false, "");
// Write the generated bitstream to "Out".
if (!Buffer.empty())

View File

@ -83,7 +83,8 @@ ASTConsumer *GeneratePCHAction::CreateASTConsumer(CompilerInstance &CI,
if (!CI.getFrontendOpts().RelocatablePCH)
Sysroot.clear();
return new PCHGenerator(CI.getPreprocessor(), OutputFile, Sysroot, OS);
return new PCHGenerator(CI.getPreprocessor(), OutputFile, MakeModule,
Sysroot, OS);
}
bool GeneratePCHAction::ComputeASTConsumerArguments(CompilerInstance &CI,

View File

@ -21,6 +21,7 @@ MacroInfo::MacroInfo(SourceLocation DefLoc) : Location(DefLoc) {
IsGNUVarargs = false;
IsBuiltinMacro = false;
IsFromAST = false;
ChangedAfterLoad = false;
IsDisabled = false;
IsUsed = false;
IsAllowRedefinitionsWithoutWarning = false;
@ -40,6 +41,7 @@ MacroInfo::MacroInfo(const MacroInfo &MI, llvm::BumpPtrAllocator &PPAllocator) {
IsGNUVarargs = MI.IsGNUVarargs;
IsBuiltinMacro = MI.IsBuiltinMacro;
IsFromAST = MI.IsFromAST;
ChangedAfterLoad = MI.ChangedAfterLoad;
IsDisabled = MI.IsDisabled;
IsUsed = MI.IsUsed;
IsAllowRedefinitionsWithoutWarning = MI.IsAllowRedefinitionsWithoutWarning;

View File

@ -652,6 +652,9 @@ TryAgain:
case tok::pp_unassert:
//isExtension = true; // FIXME: implement #unassert
break;
case tok::pp___export_macro__:
return HandleMacroExportDirective(Result);
}
break;
}
@ -1000,6 +1003,37 @@ void Preprocessor::HandleIdentSCCSDirective(Token &Tok) {
}
}
/// \brief Handle a #__export_macro__ directive.
void Preprocessor::HandleMacroExportDirective(Token &Tok) {
Token MacroNameTok;
ReadMacroName(MacroNameTok, 2);
// Error reading macro name? If so, diagnostic already issued.
if (MacroNameTok.is(tok::eod))
return;
// Check to see if this is the last token on the #__export_macro__ line.
CheckEndOfDirective("__export_macro__");
// Okay, we finally have a valid identifier to undef.
MacroInfo *MI = getMacroInfo(MacroNameTok.getIdentifierInfo());
// If the macro is not defined, this is an error.
if (MI == 0) {
Diag(MacroNameTok, diag::err_pp_export_non_macro)
<< MacroNameTok.getIdentifierInfo();
return;
}
// Note that this macro has now been exported.
MI->setExportLocation(MacroNameTok.getLocation());
// If this macro definition came from a PCH file, mark it
// as having changed since serialization.
if (MI->isFromAST())
MI->setChangedAfterLoad();
}
//===----------------------------------------------------------------------===//
// Preprocessor Include Directive Handling.
//===----------------------------------------------------------------------===//

View File

@ -1352,15 +1352,16 @@ void ASTReader::ReadMacroRecord(Module &F, uint64_t Offset) {
MI->setIsFromAST();
unsigned NextIndex = 3;
MI->setExportLocation(ReadSourceLocation(F, Record, NextIndex));
if (RecType == PP_MACRO_FUNCTION_LIKE) {
// Decode function-like macro info.
bool isC99VarArgs = Record[3];
bool isGNUVarArgs = Record[4];
bool isC99VarArgs = Record[NextIndex++];
bool isGNUVarArgs = Record[NextIndex++];
MacroArgs.clear();
unsigned NumArgs = Record[5];
NextIndex = 6 + NumArgs;
unsigned NumArgs = Record[NextIndex++];
for (unsigned i = 0; i != NumArgs; ++i)
MacroArgs.push_back(getLocalIdentifier(F, Record[6+i]));
MacroArgs.push_back(getLocalIdentifier(F, Record[NextIndex++]));
// Install function-like macro info.
MI->setIsFunctionLike();

View File

@ -1667,7 +1667,7 @@ static int compareMacroDefinitions(const void *XPtr, const void *YPtr) {
/// \brief Writes the block containing the serialized form of the
/// preprocessor.
///
void ASTWriter::WritePreprocessor(const Preprocessor &PP) {
void ASTWriter::WritePreprocessor(const Preprocessor &PP, bool IsModule) {
RecordData Record;
// If the preprocessor __COUNTER__ value has been bumped, remember it.
@ -1697,8 +1697,10 @@ void ASTWriter::WritePreprocessor(const Preprocessor &PP) {
for (Preprocessor::macro_iterator I = PP.macro_begin(Chain == 0),
E = PP.macro_end(Chain == 0);
I != E; ++I) {
MacroDefinitionsSeen.insert(I->first);
MacrosToEmit.push_back(std::make_pair(I->first, I->second));
if (!IsModule || I->second->isExported()) {
MacroDefinitionsSeen.insert(I->first);
MacrosToEmit.push_back(std::make_pair(I->first, I->second));
}
}
// Sort the set of macro definitions that need to be serialized by the
@ -1730,14 +1732,15 @@ void ASTWriter::WritePreprocessor(const Preprocessor &PP) {
// chained PCH, by storing the offset into the original PCH rather than
// writing the macro definition a second time.
if (MI->isBuiltinMacro() ||
(Chain && Name->isFromAST() && MI->isFromAST()))
(Chain && Name->isFromAST() && MI->isFromAST() &&
!MI->hasChangedAfterLoad()))
continue;
AddIdentifierRef(Name, Record);
MacroOffsets[Name] = Stream.GetCurrentBitNo();
Record.push_back(MI->getDefinitionLoc().getRawEncoding());
Record.push_back(MI->isUsed());
AddSourceLocation(MI->getExportLocation(), Record);
unsigned Code;
if (MI->isObjectLike()) {
Code = PP_MACRO_OBJECT_LIKE;
@ -2293,38 +2296,52 @@ namespace {
class ASTIdentifierTableTrait {
ASTWriter &Writer;
Preprocessor &PP;
bool IsModule;
/// \brief Determines whether this is an "interesting" identifier
/// that needs a full IdentifierInfo structure written into the hash
/// table.
static bool isInterestingIdentifier(const IdentifierInfo *II) {
return II->isPoisoned() ||
II->isExtensionToken() ||
II->hasMacroDefinition() ||
II->getObjCOrBuiltinID() ||
II->getFETokenInfo<void>();
bool isInterestingIdentifier(IdentifierInfo *II, MacroInfo *&Macro) {
Macro = 0;
if (II->isPoisoned() ||
II->isExtensionToken() ||
II->getObjCOrBuiltinID() ||
II->getFETokenInfo<void>())
return true;
if (!II->hasMacroDefinition())
return false;
if (!IsModule)
return true;
if ((Macro = PP.getMacroInfo(II)))
return Macro->isExported();
return false;
}
public:
typedef const IdentifierInfo* key_type;
typedef IdentifierInfo* key_type;
typedef key_type key_type_ref;
typedef IdentID data_type;
typedef data_type data_type_ref;
ASTIdentifierTableTrait(ASTWriter &Writer, Preprocessor &PP)
: Writer(Writer), PP(PP) { }
ASTIdentifierTableTrait(ASTWriter &Writer, Preprocessor &PP, bool IsModule)
: Writer(Writer), PP(PP), IsModule(IsModule) { }
static unsigned ComputeHash(const IdentifierInfo* II) {
return llvm::HashString(II->getName());
}
std::pair<unsigned,unsigned>
EmitKeyDataLength(raw_ostream& Out, const IdentifierInfo* II,
IdentID ID) {
EmitKeyDataLength(raw_ostream& Out, IdentifierInfo* II, IdentID ID) {
unsigned KeyLen = II->getLength() + 1;
unsigned DataLen = 4; // 4 bytes for the persistent ID << 1
if (isInterestingIdentifier(II)) {
MacroInfo *Macro;
if (isInterestingIdentifier(II, Macro)) {
DataLen += 2; // 2 bytes for builtin ID, flags
if (II->hasMacroDefinition() &&
!PP.getMacroInfo(const_cast<IdentifierInfo *>(II))->isBuiltinMacro())
@ -2350,18 +2367,19 @@ public:
Out.write(II->getNameStart(), KeyLen);
}
void EmitData(raw_ostream& Out, const IdentifierInfo* II,
void EmitData(raw_ostream& Out, IdentifierInfo* II,
IdentID ID, unsigned) {
if (!isInterestingIdentifier(II)) {
MacroInfo *Macro;
if (!isInterestingIdentifier(II, Macro)) {
clang::io::Emit32(Out, ID << 1);
return;
}
clang::io::Emit32(Out, (ID << 1) | 0x01);
uint32_t Bits = 0;
bool hasMacroDefinition =
II->hasMacroDefinition() &&
!PP.getMacroInfo(const_cast<IdentifierInfo *>(II))->isBuiltinMacro();
bool hasMacroDefinition
= II->hasMacroDefinition() &&
(Macro || (Macro = PP.getMacroInfo(II))) && !Macro->isBuiltinMacro();
Bits = (uint32_t)II->getObjCOrBuiltinID();
Bits = (Bits << 1) | unsigned(hasMacroDefinition);
Bits = (Bits << 1) | unsigned(II->isExtensionToken());
@ -2395,14 +2413,14 @@ public:
/// The identifier table consists of a blob containing string data
/// (the actual identifiers themselves) and a separate "offsets" index
/// that maps identifier IDs to locations within the blob.
void ASTWriter::WriteIdentifierTable(Preprocessor &PP) {
void ASTWriter::WriteIdentifierTable(Preprocessor &PP, bool IsModule) {
using namespace llvm;
// Create and write out the blob that contains the identifier
// strings.
{
OnDiskChainedHashTableGenerator<ASTIdentifierTableTrait> Generator;
ASTIdentifierTableTrait Trait(*this, PP);
ASTIdentifierTableTrait Trait(*this, PP, IsModule);
// Look for any identifiers that were named while processing the
// headers, but are otherwise not needed. We add these to the hash
@ -2422,14 +2440,15 @@ void ASTWriter::WriteIdentifierTable(Preprocessor &PP) {
ID != IDEnd; ++ID) {
assert(ID->first && "NULL identifier in identifier table");
if (!Chain || !ID->first->isFromAST())
Generator.insert(ID->first, ID->second, Trait);
Generator.insert(const_cast<IdentifierInfo *>(ID->first), ID->second,
Trait);
}
// Create the on-disk hash table in a buffer.
llvm::SmallString<4096> IdentifierTable;
uint32_t BucketOffset;
{
ASTIdentifierTableTrait Trait(*this, PP);
ASTIdentifierTableTrait Trait(*this, PP, IsModule);
llvm::raw_svector_ostream Out(IdentifierTable);
// Make sure that no bucket is at offset 0
clang::io::Emit32(Out, 0);
@ -2818,7 +2837,7 @@ ASTWriter::ASTWriter(llvm::BitstreamWriter &Stream)
void ASTWriter::WriteAST(Sema &SemaRef, MemorizeStatCalls *StatCalls,
const std::string &OutputFile,
StringRef isysroot) {
bool IsModule, StringRef isysroot) {
// Emit the file header.
Stream.Emit((unsigned)'C', 8);
Stream.Emit((unsigned)'P', 8);
@ -2828,7 +2847,7 @@ void ASTWriter::WriteAST(Sema &SemaRef, MemorizeStatCalls *StatCalls,
WriteBlockInfoBlock();
Context = &SemaRef.Context;
WriteASTCore(SemaRef, StatCalls, isysroot, OutputFile);
WriteASTCore(SemaRef, StatCalls, isysroot, OutputFile, IsModule);
Context = 0;
}
@ -2843,7 +2862,7 @@ static void AddLazyVectorDecls(ASTWriter &Writer, Vector &Vec,
void ASTWriter::WriteASTCore(Sema &SemaRef, MemorizeStatCalls *StatCalls,
StringRef isysroot,
const std::string &OutputFile) {
const std::string &OutputFile, bool IsModule) {
using namespace llvm;
ASTContext &Context = SemaRef.Context;
@ -3095,11 +3114,11 @@ void ASTWriter::WriteASTCore(Sema &SemaRef, MemorizeStatCalls *StatCalls,
}
Stream.ExitBlock();
WritePreprocessor(PP);
WritePreprocessor(PP, IsModule);
WriteHeaderSearch(PP.getHeaderSearchInfo(), isysroot);
WriteSelectors(SemaRef);
WriteReferencedSelectorsPool(SemaRef);
WriteIdentifierTable(PP);
WriteIdentifierTable(PP, IsModule);
WriteFPPragmaOptions(SemaRef.getFPOptions());
WriteOpenCLExtensions(SemaRef);

View File

@ -106,7 +106,7 @@ ChainedIncludesSource *ChainedIncludesSource::create(CompilerInstance &CI) {
llvm::raw_svector_ostream OS(serialAST);
llvm::OwningPtr<ASTConsumer> consumer;
consumer.reset(new PCHGenerator(Clang->getPreprocessor(), "-",
/*isysroot=*/"", &OS));
/*IsModule=*/false, /*isysroot=*/"", &OS));
Clang->getASTContext().setASTMutationListener(
consumer->GetASTMutationListener());
Clang->setASTConsumer(consumer.take());

View File

@ -28,9 +28,11 @@ using namespace clang;
PCHGenerator::PCHGenerator(const Preprocessor &PP,
StringRef OutputFile,
bool IsModule,
StringRef isysroot,
raw_ostream *OS)
: PP(PP), OutputFile(OutputFile), isysroot(isysroot.str()), Out(OS),
: PP(PP), OutputFile(OutputFile), IsModule(IsModule),
isysroot(isysroot.str()), Out(OS),
SemaPtr(0), StatCalls(0), Stream(Buffer), Writer(Stream) {
// Install a stat() listener to keep track of all of the stat()
// calls.
@ -50,7 +52,7 @@ void PCHGenerator::HandleTranslationUnit(ASTContext &Ctx) {
// Emit the PCH file
assert(SemaPtr && "No Sema?");
Writer.WriteAST(*SemaPtr, StatCalls, OutputFile, isysroot);
Writer.WriteAST(*SemaPtr, StatCalls, OutputFile, IsModule, isysroot);
// Write the generated bitstream to "Out".
Out->write((char *)&Buffer.front(), Buffer.size());

View File

@ -0,0 +1,33 @@
// RUN: %clang_cc1 -emit-module -o %t/macros.pcm -DMODULE %s
// RUN: %clang_cc1 -verify -I %t %s
#if defined(MODULE)
#define INTEGER(X) int
#define FLOAT float
#define DOUBLE double
#__export_macro__ INTEGER
#__export_macro__ DOUBLE
#else
__import_module__ macros;
#ifndef INTEGER
# error INTEGER macro should be visible
#endif
#ifdef FLOAT
# error FLOAT macro should not be visible
#endif
#ifdef MODULE
# error MODULE macro should not be visible
#endif
double d;
DOUBLE *dp = &d;
#__export_macro__ WIBBLE // expected-error{{no macro named 'WIBBLE' to export}}
#endif