1 //===- ChainedIncludesSource.cpp - Chained PCHs in Memory -------*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This file defines the ChainedIncludesSource class, which converts headers 10 // to chained PCHs in memory, mainly used for testing. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #include "clang/Basic/Builtins.h" 15 #include "clang/Basic/TargetInfo.h" 16 #include "clang/Frontend/ASTUnit.h" 17 #include "clang/Frontend/CompilerInstance.h" 18 #include "clang/Frontend/TextDiagnosticPrinter.h" 19 #include "clang/Lex/Preprocessor.h" 20 #include "clang/Lex/PreprocessorOptions.h" 21 #include "clang/Parse/ParseAST.h" 22 #include "clang/Sema/MultiplexExternalSemaSource.h" 23 #include "clang/Serialization/ASTReader.h" 24 #include "clang/Serialization/ASTWriter.h" 25 #include "llvm/Support/MemoryBuffer.h" 26 27 using namespace clang; 28 29 namespace { 30 class ChainedIncludesSource : public ExternalSemaSource { 31 public: 32 ChainedIncludesSource(std::vector<std::unique_ptr<CompilerInstance>> CIs) 33 : CIs(std::move(CIs)) {} 34 35 protected: 36 //===--------------------------------------------------------------------===// 37 // ExternalASTSource interface. 38 //===--------------------------------------------------------------------===// 39 40 /// Return the amount of memory used by memory buffers, breaking down 41 /// by heap-backed versus mmap'ed memory. 42 void getMemoryBufferSizes(MemoryBufferSizes &sizes) const override { 43 for (unsigned i = 0, e = CIs.size(); i != e; ++i) { 44 if (const ExternalASTSource *eSrc = 45 CIs[i]->getASTContext().getExternalSource()) { 46 eSrc->getMemoryBufferSizes(sizes); 47 } 48 } 49 } 50 51 private: 52 std::vector<std::unique_ptr<CompilerInstance>> CIs; 53 }; 54 } // end anonymous namespace 55 56 static ASTReader * 57 createASTReader(CompilerInstance &CI, StringRef pchFile, 58 SmallVectorImpl<std::unique_ptr<llvm::MemoryBuffer>> &MemBufs, 59 SmallVectorImpl<std::string> &bufNames, 60 ASTDeserializationListener *deserialListener = nullptr) { 61 Preprocessor &PP = CI.getPreprocessor(); 62 std::unique_ptr<ASTReader> Reader; 63 Reader.reset(new ASTReader( 64 PP, CI.getModuleCache(), &CI.getASTContext(), CI.getPCHContainerReader(), 65 /*Extensions=*/{}, 66 /*isysroot=*/"", DisableValidationForModuleKind::PCH)); 67 for (unsigned ti = 0; ti < bufNames.size(); ++ti) { 68 StringRef sr(bufNames[ti]); 69 Reader->addInMemoryBuffer(sr, std::move(MemBufs[ti])); 70 } 71 Reader->setDeserializationListener(deserialListener); 72 switch (Reader->ReadAST(pchFile, serialization::MK_PCH, SourceLocation(), 73 ASTReader::ARR_None)) { 74 case ASTReader::Success: 75 // Set the predefines buffer as suggested by the PCH reader. 76 PP.setPredefines(Reader->getSuggestedPredefines()); 77 return Reader.release(); 78 79 case ASTReader::Failure: 80 case ASTReader::Missing: 81 case ASTReader::OutOfDate: 82 case ASTReader::VersionMismatch: 83 case ASTReader::ConfigurationMismatch: 84 case ASTReader::HadErrors: 85 break; 86 } 87 return nullptr; 88 } 89 90 IntrusiveRefCntPtr<ExternalSemaSource> clang::createChainedIncludesSource( 91 CompilerInstance &CI, IntrusiveRefCntPtr<ExternalSemaSource> &Reader) { 92 93 std::vector<std::string> &includes = CI.getPreprocessorOpts().ChainedIncludes; 94 assert(!includes.empty() && "No '-chain-include' in options!"); 95 96 std::vector<std::unique_ptr<CompilerInstance>> CIs; 97 InputKind IK = CI.getFrontendOpts().Inputs[0].getKind(); 98 99 SmallVector<std::unique_ptr<llvm::MemoryBuffer>, 4> SerialBufs; 100 SmallVector<std::string, 4> serialBufNames; 101 102 for (unsigned i = 0, e = includes.size(); i != e; ++i) { 103 bool firstInclude = (i == 0); 104 std::unique_ptr<CompilerInvocation> CInvok; 105 CInvok.reset(new CompilerInvocation(CI.getInvocation())); 106 107 CInvok->getPreprocessorOpts().ChainedIncludes.clear(); 108 CInvok->getPreprocessorOpts().ImplicitPCHInclude.clear(); 109 CInvok->getPreprocessorOpts().DisablePCHOrModuleValidation = 110 DisableValidationForModuleKind::PCH; 111 CInvok->getPreprocessorOpts().Includes.clear(); 112 CInvok->getPreprocessorOpts().MacroIncludes.clear(); 113 CInvok->getPreprocessorOpts().Macros.clear(); 114 115 CInvok->getFrontendOpts().Inputs.clear(); 116 FrontendInputFile InputFile(includes[i], IK); 117 CInvok->getFrontendOpts().Inputs.push_back(InputFile); 118 119 TextDiagnosticPrinter *DiagClient = 120 new TextDiagnosticPrinter(llvm::errs(), CI.getDiagnosticOpts()); 121 IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); 122 IntrusiveRefCntPtr<DiagnosticsEngine> Diags( 123 new DiagnosticsEngine(DiagID, CI.getDiagnosticOpts(), DiagClient)); 124 125 auto Clang = std::make_unique<CompilerInstance>( 126 std::move(CInvok), CI.getPCHContainerOperations()); 127 Clang->setDiagnostics(Diags.get()); 128 Clang->setTarget(TargetInfo::CreateTargetInfo( 129 Clang->getDiagnostics(), Clang->getInvocation().getTargetOpts())); 130 Clang->createFileManager(); 131 Clang->createSourceManager(Clang->getFileManager()); 132 Clang->createPreprocessor(TU_Prefix); 133 Clang->getDiagnosticClient().BeginSourceFile(Clang->getLangOpts(), 134 &Clang->getPreprocessor()); 135 Clang->createASTContext(); 136 137 auto Buffer = std::make_shared<PCHBuffer>(); 138 ArrayRef<std::shared_ptr<ModuleFileExtension>> Extensions; 139 auto consumer = std::make_unique<PCHGenerator>( 140 Clang->getPreprocessor(), Clang->getModuleCache(), "-", /*isysroot=*/"", 141 Buffer, Extensions, /*AllowASTWithErrors=*/true); 142 Clang->getASTContext().setASTMutationListener( 143 consumer->GetASTMutationListener()); 144 Clang->setASTConsumer(std::move(consumer)); 145 Clang->createSema(TU_Prefix, nullptr); 146 147 if (firstInclude) { 148 Preprocessor &PP = Clang->getPreprocessor(); 149 PP.getBuiltinInfo().initializeBuiltins(PP.getIdentifierTable(), 150 PP.getLangOpts()); 151 } else { 152 assert(!SerialBufs.empty()); 153 SmallVector<std::unique_ptr<llvm::MemoryBuffer>, 4> Bufs; 154 // TODO: Pass through the existing MemoryBuffer instances instead of 155 // allocating new ones. 156 for (auto &SB : SerialBufs) 157 Bufs.push_back(llvm::MemoryBuffer::getMemBuffer(SB->getBuffer())); 158 std::string pchName = includes[i-1]; 159 llvm::raw_string_ostream os(pchName); 160 os << ".pch" << i-1; 161 serialBufNames.push_back(pchName); 162 163 IntrusiveRefCntPtr<ASTReader> Reader; 164 Reader = createASTReader( 165 *Clang, pchName, Bufs, serialBufNames, 166 Clang->getASTConsumer().GetASTDeserializationListener()); 167 if (!Reader) 168 return nullptr; 169 Clang->setASTReader(Reader); 170 Clang->getASTContext().setExternalSource(Reader); 171 } 172 173 if (!Clang->InitializeSourceManager(InputFile)) 174 return nullptr; 175 176 ParseAST(Clang->getSema()); 177 Clang->getDiagnosticClient().EndSourceFile(); 178 assert(Buffer->IsComplete && "serialization did not complete"); 179 auto &serialAST = Buffer->Data; 180 SerialBufs.push_back(llvm::MemoryBuffer::getMemBufferCopy( 181 StringRef(serialAST.data(), serialAST.size()))); 182 serialAST.clear(); 183 CIs.push_back(std::move(Clang)); 184 } 185 186 assert(!SerialBufs.empty()); 187 std::string pchName = includes.back() + ".pch-final"; 188 serialBufNames.push_back(pchName); 189 Reader = createASTReader(CI, pchName, SerialBufs, serialBufNames); 190 if (!Reader) 191 return nullptr; 192 193 auto ChainedSrc = 194 llvm::makeIntrusiveRefCnt<ChainedIncludesSource>(std::move(CIs)); 195 return llvm::makeIntrusiveRefCnt<MultiplexExternalSemaSource>( 196 ChainedSrc.get(), Reader.get()); 197 } 198