Home | History | Annotate | Line # | Download | only in Frontend
      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 ChainedIncludesSourceImpl : public ExternalSemaSource {
     31 public:
     32   ChainedIncludesSourceImpl(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 
     55 /// Members of ChainedIncludesSource, factored out so we can initialize
     56 /// them before we initialize the ExternalSemaSource base class.
     57 struct ChainedIncludesSourceMembers {
     58   ChainedIncludesSourceMembers(
     59       std::vector<std::unique_ptr<CompilerInstance>> CIs,
     60       IntrusiveRefCntPtr<ExternalSemaSource> FinalReader)
     61       : Impl(std::move(CIs)), FinalReader(std::move(FinalReader)) {}
     62   ChainedIncludesSourceImpl Impl;
     63   IntrusiveRefCntPtr<ExternalSemaSource> FinalReader;
     64 };
     65 
     66 /// Use MultiplexExternalSemaSource to dispatch all ExternalSemaSource
     67 /// calls to the final reader.
     68 class ChainedIncludesSource
     69     : private ChainedIncludesSourceMembers,
     70       public MultiplexExternalSemaSource {
     71 public:
     72   ChainedIncludesSource(std::vector<std::unique_ptr<CompilerInstance>> CIs,
     73                         IntrusiveRefCntPtr<ExternalSemaSource> FinalReader)
     74       : ChainedIncludesSourceMembers(std::move(CIs), std::move(FinalReader)),
     75         MultiplexExternalSemaSource(Impl, *this->FinalReader) {}
     76 };
     77 }
     78 
     79 static ASTReader *
     80 createASTReader(CompilerInstance &CI, StringRef pchFile,
     81                 SmallVectorImpl<std::unique_ptr<llvm::MemoryBuffer>> &MemBufs,
     82                 SmallVectorImpl<std::string> &bufNames,
     83                 ASTDeserializationListener *deserialListener = nullptr) {
     84   Preprocessor &PP = CI.getPreprocessor();
     85   std::unique_ptr<ASTReader> Reader;
     86   Reader.reset(new ASTReader(
     87       PP, CI.getModuleCache(), &CI.getASTContext(), CI.getPCHContainerReader(),
     88       /*Extensions=*/{},
     89       /*isysroot=*/"", DisableValidationForModuleKind::PCH));
     90   for (unsigned ti = 0; ti < bufNames.size(); ++ti) {
     91     StringRef sr(bufNames[ti]);
     92     Reader->addInMemoryBuffer(sr, std::move(MemBufs[ti]));
     93   }
     94   Reader->setDeserializationListener(deserialListener);
     95   switch (Reader->ReadAST(pchFile, serialization::MK_PCH, SourceLocation(),
     96                           ASTReader::ARR_None)) {
     97   case ASTReader::Success:
     98     // Set the predefines buffer as suggested by the PCH reader.
     99     PP.setPredefines(Reader->getSuggestedPredefines());
    100     return Reader.release();
    101 
    102   case ASTReader::Failure:
    103   case ASTReader::Missing:
    104   case ASTReader::OutOfDate:
    105   case ASTReader::VersionMismatch:
    106   case ASTReader::ConfigurationMismatch:
    107   case ASTReader::HadErrors:
    108     break;
    109   }
    110   return nullptr;
    111 }
    112 
    113 IntrusiveRefCntPtr<ExternalSemaSource> clang::createChainedIncludesSource(
    114     CompilerInstance &CI, IntrusiveRefCntPtr<ExternalSemaSource> &Reader) {
    115 
    116   std::vector<std::string> &includes = CI.getPreprocessorOpts().ChainedIncludes;
    117   assert(!includes.empty() && "No '-chain-include' in options!");
    118 
    119   std::vector<std::unique_ptr<CompilerInstance>> CIs;
    120   InputKind IK = CI.getFrontendOpts().Inputs[0].getKind();
    121 
    122   SmallVector<std::unique_ptr<llvm::MemoryBuffer>, 4> SerialBufs;
    123   SmallVector<std::string, 4> serialBufNames;
    124 
    125   for (unsigned i = 0, e = includes.size(); i != e; ++i) {
    126     bool firstInclude = (i == 0);
    127     std::unique_ptr<CompilerInvocation> CInvok;
    128     CInvok.reset(new CompilerInvocation(CI.getInvocation()));
    129 
    130     CInvok->getPreprocessorOpts().ChainedIncludes.clear();
    131     CInvok->getPreprocessorOpts().ImplicitPCHInclude.clear();
    132     CInvok->getPreprocessorOpts().DisablePCHOrModuleValidation =
    133         DisableValidationForModuleKind::PCH;
    134     CInvok->getPreprocessorOpts().Includes.clear();
    135     CInvok->getPreprocessorOpts().MacroIncludes.clear();
    136     CInvok->getPreprocessorOpts().Macros.clear();
    137 
    138     CInvok->getFrontendOpts().Inputs.clear();
    139     FrontendInputFile InputFile(includes[i], IK);
    140     CInvok->getFrontendOpts().Inputs.push_back(InputFile);
    141 
    142     TextDiagnosticPrinter *DiagClient =
    143       new TextDiagnosticPrinter(llvm::errs(), new DiagnosticOptions());
    144     IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
    145     IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
    146         new DiagnosticsEngine(DiagID, &CI.getDiagnosticOpts(), DiagClient));
    147 
    148     std::unique_ptr<CompilerInstance> Clang(
    149         new CompilerInstance(CI.getPCHContainerOperations()));
    150     Clang->setInvocation(std::move(CInvok));
    151     Clang->setDiagnostics(Diags.get());
    152     Clang->setTarget(TargetInfo::CreateTargetInfo(
    153         Clang->getDiagnostics(), Clang->getInvocation().TargetOpts));
    154     Clang->createFileManager();
    155     Clang->createSourceManager(Clang->getFileManager());
    156     Clang->createPreprocessor(TU_Prefix);
    157     Clang->getDiagnosticClient().BeginSourceFile(Clang->getLangOpts(),
    158                                                  &Clang->getPreprocessor());
    159     Clang->createASTContext();
    160 
    161     auto Buffer = std::make_shared<PCHBuffer>();
    162     ArrayRef<std::shared_ptr<ModuleFileExtension>> Extensions;
    163     auto consumer = std::make_unique<PCHGenerator>(
    164         Clang->getPreprocessor(), Clang->getModuleCache(), "-", /*isysroot=*/"",
    165         Buffer, Extensions, /*AllowASTWithErrors=*/true);
    166     Clang->getASTContext().setASTMutationListener(
    167                                             consumer->GetASTMutationListener());
    168     Clang->setASTConsumer(std::move(consumer));
    169     Clang->createSema(TU_Prefix, nullptr);
    170 
    171     if (firstInclude) {
    172       Preprocessor &PP = Clang->getPreprocessor();
    173       PP.getBuiltinInfo().initializeBuiltins(PP.getIdentifierTable(),
    174                                              PP.getLangOpts());
    175     } else {
    176       assert(!SerialBufs.empty());
    177       SmallVector<std::unique_ptr<llvm::MemoryBuffer>, 4> Bufs;
    178       // TODO: Pass through the existing MemoryBuffer instances instead of
    179       // allocating new ones.
    180       for (auto &SB : SerialBufs)
    181         Bufs.push_back(llvm::MemoryBuffer::getMemBuffer(SB->getBuffer()));
    182       std::string pchName = includes[i-1];
    183       llvm::raw_string_ostream os(pchName);
    184       os << ".pch" << i-1;
    185       serialBufNames.push_back(os.str());
    186 
    187       IntrusiveRefCntPtr<ASTReader> Reader;
    188       Reader = createASTReader(
    189           *Clang, pchName, Bufs, serialBufNames,
    190           Clang->getASTConsumer().GetASTDeserializationListener());
    191       if (!Reader)
    192         return nullptr;
    193       Clang->setASTReader(Reader);
    194       Clang->getASTContext().setExternalSource(Reader);
    195     }
    196 
    197     if (!Clang->InitializeSourceManager(InputFile))
    198       return nullptr;
    199 
    200     ParseAST(Clang->getSema());
    201     Clang->getDiagnosticClient().EndSourceFile();
    202     assert(Buffer->IsComplete && "serialization did not complete");
    203     auto &serialAST = Buffer->Data;
    204     SerialBufs.push_back(llvm::MemoryBuffer::getMemBufferCopy(
    205         StringRef(serialAST.data(), serialAST.size())));
    206     serialAST.clear();
    207     CIs.push_back(std::move(Clang));
    208   }
    209 
    210   assert(!SerialBufs.empty());
    211   std::string pchName = includes.back() + ".pch-final";
    212   serialBufNames.push_back(pchName);
    213   Reader = createASTReader(CI, pchName, SerialBufs, serialBufNames);
    214   if (!Reader)
    215     return nullptr;
    216 
    217   return IntrusiveRefCntPtr<ChainedIncludesSource>(
    218       new ChainedIncludesSource(std::move(CIs), Reader));
    219 }
    220