Skip to content

Commit ab75597

Browse files
committed
Rather than rejecting attempts to run preprocessor-only actions on AST files,
replay the steps taken to create the AST file with the preprocessor-only action installed to produce preprocessed output. This can be used to produce the preprocessed text for an existing .pch or .pcm file. llvm-svn: 304726
1 parent ca5e153 commit ab75597

File tree

9 files changed

+147
-26
lines changed

9 files changed

+147
-26
lines changed

clang/include/clang/Basic/LangOptions.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ class LangOptions : public LangOptionsBase {
5858
SOB_Trapping // -ftrapv
5959
};
6060

61+
// FIXME: Unify with TUKind.
6162
enum CompilingModuleKind {
6263
CMK_None, ///< Not compiling a module interface at all.
6364
CMK_ModuleMap, ///< Compiling a module from a module map.

clang/include/clang/Basic/SourceManager.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -722,6 +722,10 @@ class SourceManager : public RefCountedBase<SourceManager> {
722722

723723
void clearIDTables();
724724

725+
/// Initialize this source manager suitably to replay the compilation
726+
/// described by \p Old. Requires that \p Old outlive \p *this.
727+
void initializeForReplay(const SourceManager &Old);
728+
725729
DiagnosticsEngine &getDiagnostics() const { return Diag; }
726730

727731
FileManager &getFileManager() const { return FileMgr; }

clang/include/clang/Frontend/ASTUnit.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ class DiagnosticsEngine;
5151
class FileEntry;
5252
class FileManager;
5353
class HeaderSearch;
54+
class InputKind;
5455
class MemoryBufferCache;
5556
class Preprocessor;
5657
class PCHContainerOperations;
@@ -305,9 +306,6 @@ class ASTUnit : public ModuleLoader {
305306
/// (likely to change while trying to use them).
306307
bool UserFilesAreVolatile : 1;
307308

308-
/// \brief The language options used when we load an AST file.
309-
LangOptions ASTFileLangOpts;
310-
311309
static void ConfigureDiags(IntrusiveRefCntPtr<DiagnosticsEngine> Diags,
312310
ASTUnit &AST, bool CaptureDiagnostics);
313311

@@ -702,6 +700,9 @@ class ASTUnit : public ModuleLoader {
702700
/// \brief Determine what kind of translation unit this AST represents.
703701
TranslationUnitKind getTranslationUnitKind() const { return TUKind; }
704702

703+
/// \brief Determine the input kind this AST unit represents.
704+
InputKind getInputKind() const;
705+
705706
/// \brief A mapping from a file name to the memory buffer that stores the
706707
/// remapped contents of that file.
707708
typedef std::pair<std::string, llvm::MemoryBuffer *> RemappedFile;

clang/include/clang/Frontend/FrontendAction.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -176,10 +176,10 @@ class FrontendAction {
176176
virtual TranslationUnitKind getTranslationUnitKind() { return TU_Complete; }
177177

178178
/// \brief Does this action support use with PCH?
179-
virtual bool hasPCHSupport() const { return !usesPreprocessorOnly(); }
179+
virtual bool hasPCHSupport() const { return true; }
180180

181181
/// \brief Does this action support use with AST files?
182-
virtual bool hasASTFileSupport() const { return !usesPreprocessorOnly(); }
182+
virtual bool hasASTFileSupport() const { return true; }
183183

184184
/// \brief Does this action support use with IR files?
185185
virtual bool hasIRSupport() const { return false; }

clang/lib/Basic/SourceManager.cpp

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,41 @@ void SourceManager::clearIDTables() {
345345
createExpansionLoc(SourceLocation(),SourceLocation(),SourceLocation(), 1);
346346
}
347347

348+
void SourceManager::initializeForReplay(const SourceManager &Old) {
349+
assert(MainFileID.isInvalid() && "expected uninitialized SourceManager");
350+
351+
auto CloneContentCache = [&](const ContentCache *Cache) -> ContentCache * {
352+
auto *Clone = new (ContentCacheAlloc.Allocate<ContentCache>()) ContentCache;
353+
Clone->OrigEntry = Cache->OrigEntry;
354+
Clone->ContentsEntry = Cache->ContentsEntry;
355+
Clone->BufferOverridden = Cache->BufferOverridden;
356+
Clone->IsSystemFile = Cache->IsSystemFile;
357+
Clone->IsTransient = Cache->IsTransient;
358+
Clone->replaceBuffer(Cache->getRawBuffer(), /*DoNotFree*/true);
359+
return Clone;
360+
};
361+
362+
// Set up our main file ID as a copy of the old source manager's main file.
363+
const SLocEntry &OldMainFile = Old.getSLocEntry(Old.getMainFileID());
364+
assert(OldMainFile.isFile() && "main file is macro expansion?");
365+
setMainFileID(createFileID(
366+
CloneContentCache(OldMainFile.getFile().getContentCache()),
367+
SourceLocation(), OldMainFile.getFile().getFileCharacteristic(), 0, 0));
368+
369+
// Ensure all SLocEntries are loaded from the external source.
370+
for (unsigned I = 0, N = Old.LoadedSLocEntryTable.size(); I != N; ++I)
371+
if (!Old.SLocEntryLoaded[I])
372+
Old.loadSLocEntry(I, nullptr);
373+
374+
// Inherit any content cache data from the old source manager.
375+
for (auto &FileInfo : Old.FileInfos) {
376+
SrcMgr::ContentCache *&Slot = FileInfos[FileInfo.first];
377+
if (Slot)
378+
continue;
379+
Slot = CloneContentCache(FileInfo.second);
380+
}
381+
}
382+
348383
/// getOrCreateContentCache - Create or return a cached ContentCache for the
349384
/// specified file.
350385
const ContentCache *

clang/lib/Frontend/ASTUnit.cpp

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -667,6 +667,7 @@ std::unique_ptr<ASTUnit> ASTUnit::LoadFromASTFile(
667667

668668
ConfigureDiags(Diags, *AST, CaptureDiagnostics);
669669

670+
AST->LangOpts = std::make_shared<LangOptions>();
670671
AST->OnlyLocalDecls = OnlyLocalDecls;
671672
AST->CaptureDiagnostics = CaptureDiagnostics;
672673
AST->Diagnostics = Diags;
@@ -682,7 +683,7 @@ std::unique_ptr<ASTUnit> ASTUnit::LoadFromASTFile(
682683
AST->HeaderInfo.reset(new HeaderSearch(AST->HSOpts,
683684
AST->getSourceManager(),
684685
AST->getDiagnostics(),
685-
AST->ASTFileLangOpts,
686+
AST->getLangOpts(),
686687
/*Target=*/nullptr));
687688

688689
auto PPOpts = std::make_shared<PreprocessorOptions>();
@@ -696,13 +697,13 @@ std::unique_ptr<ASTUnit> ASTUnit::LoadFromASTFile(
696697
unsigned Counter;
697698

698699
AST->PP = std::make_shared<Preprocessor>(
699-
std::move(PPOpts), AST->getDiagnostics(), AST->ASTFileLangOpts,
700+
std::move(PPOpts), AST->getDiagnostics(), *AST->LangOpts,
700701
AST->getSourceManager(), *AST->PCMCache, HeaderInfo, *AST,
701702
/*IILookup=*/nullptr,
702703
/*OwnsHeaderSearch=*/false);
703704
Preprocessor &PP = *AST->PP;
704705

705-
AST->Ctx = new ASTContext(AST->ASTFileLangOpts, AST->getSourceManager(),
706+
AST->Ctx = new ASTContext(*AST->LangOpts, AST->getSourceManager(),
706707
PP.getIdentifierTable(), PP.getSelectorTable(),
707708
PP.getBuiltinInfo());
708709
ASTContext &Context = *AST->Ctx;
@@ -716,7 +717,7 @@ std::unique_ptr<ASTUnit> ASTUnit::LoadFromASTFile(
716717
AllowPCHWithCompilerErrors);
717718

718719
AST->Reader->setListener(llvm::make_unique<ASTInfoCollector>(
719-
*AST->PP, Context, AST->ASTFileLangOpts, AST->TargetOpts, AST->Target,
720+
*AST->PP, Context, *AST->LangOpts, AST->TargetOpts, AST->Target,
720721
Counter));
721722

722723
// Attach the AST reader to the AST context as an external AST
@@ -2879,7 +2880,32 @@ const FileEntry *ASTUnit::getPCHFile() {
28792880
}
28802881

28812882
bool ASTUnit::isModuleFile() {
2882-
return isMainFileAST() && ASTFileLangOpts.isCompilingModule();
2883+
return isMainFileAST() && getLangOpts().isCompilingModule();
2884+
}
2885+
2886+
InputKind ASTUnit::getInputKind() const {
2887+
auto &LangOpts = getLangOpts();
2888+
2889+
InputKind::Language Lang;
2890+
if (LangOpts.OpenCL)
2891+
Lang = InputKind::OpenCL;
2892+
else if (LangOpts.CUDA)
2893+
Lang = InputKind::CUDA;
2894+
else if (LangOpts.RenderScript)
2895+
Lang = InputKind::RenderScript;
2896+
else if (LangOpts.CPlusPlus)
2897+
Lang = LangOpts.ObjC1 ? InputKind::ObjCXX : InputKind::CXX;
2898+
else
2899+
Lang = LangOpts.ObjC1 ? InputKind::ObjC : InputKind::C;
2900+
2901+
InputKind::Format Fmt = InputKind::Source;
2902+
if (LangOpts.getCompilingModule() == LangOptions::CMK_ModuleMap)
2903+
Fmt = InputKind::ModuleMap;
2904+
2905+
// We don't know if input was preprocessed. Assume not.
2906+
bool PP = false;
2907+
2908+
return InputKind(Lang, Fmt, PP);
28832909
}
28842910

28852911
void ASTUnit::PreambleData::countLines() const {

clang/lib/Frontend/FrontendAction.cpp

Lines changed: 63 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -387,8 +387,7 @@ static std::error_code collectModuleHeaderIncludes(
387387
return std::error_code();
388388
}
389389

390-
static bool loadModuleMapForModuleBuild(CompilerInstance &CI,
391-
StringRef Filename, bool IsSystem,
390+
static bool loadModuleMapForModuleBuild(CompilerInstance &CI, bool IsSystem,
392391
bool IsPreprocessed,
393392
std::string &PresumedModuleMapFile,
394393
unsigned &Offset) {
@@ -523,23 +522,78 @@ getInputBufferForModule(CompilerInstance &CI, Module *M) {
523522
}
524523

525524
bool FrontendAction::BeginSourceFile(CompilerInstance &CI,
526-
const FrontendInputFile &Input) {
525+
const FrontendInputFile &RealInput) {
526+
FrontendInputFile Input(RealInput);
527527
assert(!Instance && "Already processing a source file!");
528528
assert(!Input.isEmpty() && "Unexpected empty filename!");
529529
setCurrentInput(Input);
530530
setCompilerInstance(&CI);
531531

532532
StringRef InputFile = Input.getFile();
533533
bool HasBegunSourceFile = false;
534+
bool ReplayASTFile = Input.getKind().getFormat() == InputKind::Precompiled &&
535+
usesPreprocessorOnly();
534536
if (!BeginInvocation(CI))
535537
goto failure;
536538

539+
// If we're replaying the build of an AST file, import it and set up
540+
// the initial state from its build.
541+
if (ReplayASTFile) {
542+
IntrusiveRefCntPtr<DiagnosticsEngine> Diags(&CI.getDiagnostics());
543+
544+
// The AST unit populates its own diagnostics engine rather than ours.
545+
IntrusiveRefCntPtr<DiagnosticsEngine> ASTDiags(
546+
new DiagnosticsEngine(Diags->getDiagnosticIDs(),
547+
&Diags->getDiagnosticOptions()));
548+
ASTDiags->setClient(Diags->getClient(), /*OwnsClient*/false);
549+
550+
std::unique_ptr<ASTUnit> AST = ASTUnit::LoadFromASTFile(
551+
InputFile, CI.getPCHContainerReader(), ASTDiags, CI.getFileSystemOpts(),
552+
CI.getCodeGenOpts().DebugTypeExtRefs);
553+
if (!AST)
554+
goto failure;
555+
556+
// Options relating to how we treat the input (but not what we do with it)
557+
// are inherited from the AST unit.
558+
CI.getLangOpts() = AST->getLangOpts();
559+
560+
// Preload all the module files loaded transitively by the AST unit.
561+
if (auto ASTReader = AST->getASTReader()) {
562+
auto &MM = ASTReader->getModuleManager();
563+
for (ModuleFile &MF : MM)
564+
if (&MF != &MM.getPrimaryModule())
565+
CI.getFrontendOpts().ModuleFiles.push_back(MF.FileName);
566+
}
567+
568+
// Set the shared objects, these are reset when we finish processing the
569+
// file, otherwise the CompilerInstance will happily destroy them.
570+
CI.setFileManager(&AST->getFileManager());
571+
CI.createSourceManager(CI.getFileManager());
572+
CI.getSourceManager().initializeForReplay(AST->getSourceManager());
573+
CI.createPreprocessor(getTranslationUnitKind());
574+
575+
// Set up the input file for replay purposes.
576+
auto Kind = AST->getInputKind();
577+
if (Kind.getFormat() == InputKind::ModuleMap) {
578+
Module *ASTModule =
579+
AST->getPreprocessor().getHeaderSearchInfo().lookupModule(
580+
AST->getLangOpts().CurrentModule, /*AllowSearch*/ false);
581+
Input = FrontendInputFile(ASTModule->PresumedModuleMapFile, Kind);
582+
} else {
583+
auto &SM = CI.getSourceManager();
584+
FileID ID = SM.getMainFileID();
585+
if (auto *File = SM.getFileEntryForID(ID))
586+
Input = FrontendInputFile(File->getName(), Kind);
587+
else
588+
Input = FrontendInputFile(SM.getBuffer(ID), Kind);
589+
}
590+
setCurrentInput(Input, std::move(AST));
591+
}
592+
537593
// AST files follow a very different path, since they share objects via the
538594
// AST unit.
539595
if (Input.getKind().getFormat() == InputKind::Precompiled) {
540-
// FIXME: We should not be asserting on bad command-line arguments.
541-
assert(!usesPreprocessorOnly() &&
542-
"Attempt to pass AST file to preprocessor only action!");
596+
assert(!usesPreprocessorOnly() && "this case was handled above");
543597
assert(hasASTFileSupport() &&
544598
"This action does not have AST file support!");
545599

@@ -680,7 +734,7 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI,
680734

681735
std::string PresumedModuleMapFile;
682736
unsigned OffsetToContents;
683-
if (loadModuleMapForModuleBuild(CI, Input.getFile(), Input.isSystem(),
737+
if (loadModuleMapForModuleBuild(CI, Input.isSystem(),
684738
Input.isPreprocessed(),
685739
PresumedModuleMapFile, OffsetToContents))
686740
goto failure;
@@ -829,14 +883,7 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI,
829883

830884
// If we failed, reset state since the client will not end up calling the
831885
// matching EndSourceFile().
832-
failure:
833-
if (isCurrentFileAST()) {
834-
CI.setASTContext(nullptr);
835-
CI.setPreprocessor(nullptr);
836-
CI.setSourceManager(nullptr);
837-
CI.setFileManager(nullptr);
838-
}
839-
886+
failure:
840887
if (HasBegunSourceFile)
841888
CI.getDiagnosticClient().EndSourceFile();
842889
CI.clearOutputFiles(/*EraseFiles=*/true);
@@ -914,6 +961,7 @@ void FrontendAction::EndSourceFile() {
914961
CI.resetAndLeakPreprocessor();
915962
CI.resetAndLeakSourceManager();
916963
CI.resetAndLeakFileManager();
964+
BuryPointer(CurrentASTUnit.release());
917965
} else {
918966
CI.setPreprocessor(nullptr);
919967
CI.setSourceManager(nullptr);

clang/lib/Serialization/ASTReader.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4918,6 +4918,7 @@ ASTReader::ReadSubmoduleBlock(ModuleFile &F, unsigned ClientLoadCapabilities) {
49184918
}
49194919

49204920
CurrentModule->setASTFile(F.File);
4921+
CurrentModule->PresumedModuleMapFile = F.ModuleMapPath;
49214922
}
49224923

49234924
CurrentModule->Kind = ModuleKind;

clang/test/Modules/preprocess-module.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,15 @@
3434
// RUN: rm %t/fwd.h %t/file.h %t/file2.h %t/module.modulemap
3535
// RUN: %clang_cc1 -fmodules -fmodule-name=file -fmodule-file=%t/fwd.pcm -x c++-module-map-cpp-output %t/copy.ii -emit-module -o %t/copy.pcm
3636

37-
// Finally, check that our module contains correct mapping information for the headers.
37+
// Check that our module contains correct mapping information for the headers.
3838
// RUN: cp %S/Inputs/preprocess/fwd.h %S/Inputs/preprocess/file.h %S/Inputs/preprocess/file2.h %S/Inputs/preprocess/module.modulemap %t
3939
// RUN: %clang_cc1 -fmodules -fmodule-file=%t/copy.pcm %s -I%t -verify -fno-modules-error-recovery -DCOPY -DINCLUDE
4040

41+
// Check that we can preprocess from a .pcm file and that we get the same result as preprocessing from the original sources.
42+
// RUN: %clang_cc1 -fmodules -fmodule-name=file -fmodule-file=%t/fwd.pcm -I%S/Inputs/preprocess -x c++-module-map %S/Inputs/preprocess/module.modulemap -emit-module -o %t/file.pcm
43+
// RUN: %clang_cc1 -fmodules -fmodule-name=file -fmodule-file=%t/fwd.pcm -I%S/Inputs/preprocess %t/file.pcm -E -frewrite-includes -o %t/file.rewrite.ii
44+
// RUN: cmp %t/rewrite.ii %t/file.rewrite.ii
45+
4146
// == module map
4247
// CHECK: # 1 "{{.*}}module.modulemap"
4348
// CHECK: module file {

0 commit comments

Comments
 (0)