diff options
Diffstat (limited to 'clang/lib/Lex/PreprocessingRecord.cpp')
| -rw-r--r-- | clang/lib/Lex/PreprocessingRecord.cpp | 516 | 
1 files changed, 516 insertions, 0 deletions
diff --git a/clang/lib/Lex/PreprocessingRecord.cpp b/clang/lib/Lex/PreprocessingRecord.cpp new file mode 100644 index 000000000000..115256db4809 --- /dev/null +++ b/clang/lib/Lex/PreprocessingRecord.cpp @@ -0,0 +1,516 @@ +//===- PreprocessingRecord.cpp - Record of Preprocessing ------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +//  This file implements the PreprocessingRecord class, which maintains a record +//  of what occurred during preprocessing, and its helpers. +// +//===----------------------------------------------------------------------===// + +#include "clang/Lex/PreprocessingRecord.h" +#include "clang/Basic/IdentifierTable.h" +#include "clang/Basic/LLVM.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/TokenKinds.h" +#include "clang/Lex/MacroInfo.h" +#include "clang/Lex/Token.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/iterator_range.h" +#include "llvm/Support/Capacity.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/ErrorHandling.h" +#include <algorithm> +#include <cassert> +#include <cstddef> +#include <cstring> +#include <iterator> +#include <utility> +#include <vector> + +using namespace clang; + +ExternalPreprocessingRecordSource::~ExternalPreprocessingRecordSource() = +    default; + +InclusionDirective::InclusionDirective(PreprocessingRecord &PPRec, +                                       InclusionKind Kind, StringRef FileName, +                                       bool InQuotes, bool ImportedModule, +                                       const FileEntry *File, SourceRange Range) +    : PreprocessingDirective(InclusionDirectiveKind, Range), InQuotes(InQuotes), +      Kind(Kind), ImportedModule(ImportedModule), File(File) { +  char *Memory = (char *)PPRec.Allocate(FileName.size() + 1, alignof(char)); +  memcpy(Memory, FileName.data(), FileName.size()); +  Memory[FileName.size()] = 0; +  this->FileName = StringRef(Memory, FileName.size()); +} + +PreprocessingRecord::PreprocessingRecord(SourceManager &SM) : SourceMgr(SM) {} + +/// Returns a pair of [Begin, End) iterators of preprocessed entities +/// that source range \p Range encompasses. +llvm::iterator_range<PreprocessingRecord::iterator> +PreprocessingRecord::getPreprocessedEntitiesInRange(SourceRange Range) { +  if (Range.isInvalid()) +    return llvm::make_range(iterator(), iterator()); + +  if (CachedRangeQuery.Range == Range) { +    return llvm::make_range(iterator(this, CachedRangeQuery.Result.first), +                            iterator(this, CachedRangeQuery.Result.second)); +  } + +  std::pair<int, int> Res = getPreprocessedEntitiesInRangeSlow(Range); + +  CachedRangeQuery.Range = Range; +  CachedRangeQuery.Result = Res; + +  return llvm::make_range(iterator(this, Res.first), +                          iterator(this, Res.second)); +} + +static bool isPreprocessedEntityIfInFileID(PreprocessedEntity *PPE, FileID FID, +                                           SourceManager &SM) { +  assert(FID.isValid()); +  if (!PPE) +    return false; + +  SourceLocation Loc = PPE->getSourceRange().getBegin(); +  if (Loc.isInvalid()) +    return false; + +  return SM.isInFileID(SM.getFileLoc(Loc), FID); +} + +/// Returns true if the preprocessed entity that \arg PPEI iterator +/// points to is coming from the file \arg FID. +/// +/// Can be used to avoid implicit deserializations of preallocated +/// preprocessed entities if we only care about entities of a specific file +/// and not from files \#included in the range given at +/// \see getPreprocessedEntitiesInRange. +bool PreprocessingRecord::isEntityInFileID(iterator PPEI, FileID FID) { +  if (FID.isInvalid()) +    return false; + +  int Pos = std::distance(iterator(this, 0), PPEI); +  if (Pos < 0) { +    if (unsigned(-Pos-1) >= LoadedPreprocessedEntities.size()) { +      assert(0 && "Out-of bounds loaded preprocessed entity"); +      return false; +    } +    assert(ExternalSource && "No external source to load from"); +    unsigned LoadedIndex = LoadedPreprocessedEntities.size()+Pos; +    if (PreprocessedEntity *PPE = LoadedPreprocessedEntities[LoadedIndex]) +      return isPreprocessedEntityIfInFileID(PPE, FID, SourceMgr); + +    // See if the external source can see if the entity is in the file without +    // deserializing it. +    Optional<bool> IsInFile = +        ExternalSource->isPreprocessedEntityInFileID(LoadedIndex, FID); +    if (IsInFile.hasValue()) +      return IsInFile.getValue(); + +    // The external source did not provide a definite answer, go and deserialize +    // the entity to check it. +    return isPreprocessedEntityIfInFileID( +                                       getLoadedPreprocessedEntity(LoadedIndex), +                                          FID, SourceMgr); +  } + +  if (unsigned(Pos) >= PreprocessedEntities.size()) { +    assert(0 && "Out-of bounds local preprocessed entity"); +    return false; +  } +  return isPreprocessedEntityIfInFileID(PreprocessedEntities[Pos], +                                        FID, SourceMgr); +} + +/// Returns a pair of [Begin, End) iterators of preprocessed entities +/// that source range \arg R encompasses. +std::pair<int, int> +PreprocessingRecord::getPreprocessedEntitiesInRangeSlow(SourceRange Range) { +  assert(Range.isValid()); +  assert(!SourceMgr.isBeforeInTranslationUnit(Range.getEnd(),Range.getBegin())); + +  std::pair<unsigned, unsigned> +    Local = findLocalPreprocessedEntitiesInRange(Range); + +  // Check if range spans local entities. +  if (!ExternalSource || SourceMgr.isLocalSourceLocation(Range.getBegin())) +    return std::make_pair(Local.first, Local.second); + +  std::pair<unsigned, unsigned> +    Loaded = ExternalSource->findPreprocessedEntitiesInRange(Range); + +  // Check if range spans local entities. +  if (Loaded.first == Loaded.second) +    return std::make_pair(Local.first, Local.second); + +  unsigned TotalLoaded = LoadedPreprocessedEntities.size(); + +  // Check if range spans loaded entities. +  if (Local.first == Local.second) +    return std::make_pair(int(Loaded.first)-TotalLoaded, +                          int(Loaded.second)-TotalLoaded); + +  // Range spands loaded and local entities. +  return std::make_pair(int(Loaded.first)-TotalLoaded, Local.second); +} + +std::pair<unsigned, unsigned> +PreprocessingRecord::findLocalPreprocessedEntitiesInRange( +                                                      SourceRange Range) const { +  if (Range.isInvalid()) +    return std::make_pair(0,0); +  assert(!SourceMgr.isBeforeInTranslationUnit(Range.getEnd(),Range.getBegin())); + +  unsigned Begin = findBeginLocalPreprocessedEntity(Range.getBegin()); +  unsigned End = findEndLocalPreprocessedEntity(Range.getEnd()); +  return std::make_pair(Begin, End); +} + +namespace { + +template <SourceLocation (SourceRange::*getRangeLoc)() const> +struct PPEntityComp { +  const SourceManager &SM; + +  explicit PPEntityComp(const SourceManager &SM) : SM(SM) {} + +  bool operator()(PreprocessedEntity *L, PreprocessedEntity *R) const { +    SourceLocation LHS = getLoc(L); +    SourceLocation RHS = getLoc(R); +    return SM.isBeforeInTranslationUnit(LHS, RHS); +  } + +  bool operator()(PreprocessedEntity *L, SourceLocation RHS) const { +    SourceLocation LHS = getLoc(L); +    return SM.isBeforeInTranslationUnit(LHS, RHS); +  } + +  bool operator()(SourceLocation LHS, PreprocessedEntity *R) const { +    SourceLocation RHS = getLoc(R); +    return SM.isBeforeInTranslationUnit(LHS, RHS); +  } + +  SourceLocation getLoc(PreprocessedEntity *PPE) const { +    SourceRange Range = PPE->getSourceRange(); +    return (Range.*getRangeLoc)(); +  } +}; + +} // namespace + +unsigned PreprocessingRecord::findBeginLocalPreprocessedEntity( +                                                     SourceLocation Loc) const { +  if (SourceMgr.isLoadedSourceLocation(Loc)) +    return 0; + +  size_t Count = PreprocessedEntities.size(); +  size_t Half; +  std::vector<PreprocessedEntity *>::const_iterator +    First = PreprocessedEntities.begin(); +  std::vector<PreprocessedEntity *>::const_iterator I; + +  // Do a binary search manually instead of using std::lower_bound because +  // The end locations of entities may be unordered (when a macro expansion +  // is inside another macro argument), but for this case it is not important +  // whether we get the first macro expansion or its containing macro. +  while (Count > 0) { +    Half = Count/2; +    I = First; +    std::advance(I, Half); +    if (SourceMgr.isBeforeInTranslationUnit((*I)->getSourceRange().getEnd(), +                                            Loc)){ +      First = I; +      ++First; +      Count = Count - Half - 1; +    } else +      Count = Half; +  } + +  return First - PreprocessedEntities.begin(); +} + +unsigned +PreprocessingRecord::findEndLocalPreprocessedEntity(SourceLocation Loc) const { +  if (SourceMgr.isLoadedSourceLocation(Loc)) +    return 0; + +  auto I = llvm::upper_bound(PreprocessedEntities, Loc, +                             PPEntityComp<&SourceRange::getBegin>(SourceMgr)); +  return I - PreprocessedEntities.begin(); +} + +PreprocessingRecord::PPEntityID +PreprocessingRecord::addPreprocessedEntity(PreprocessedEntity *Entity) { +  assert(Entity); +  SourceLocation BeginLoc = Entity->getSourceRange().getBegin(); + +  if (isa<MacroDefinitionRecord>(Entity)) { +    assert((PreprocessedEntities.empty() || +            !SourceMgr.isBeforeInTranslationUnit( +                BeginLoc, +                PreprocessedEntities.back()->getSourceRange().getBegin())) && +           "a macro definition was encountered out-of-order"); +    PreprocessedEntities.push_back(Entity); +    return getPPEntityID(PreprocessedEntities.size()-1, /*isLoaded=*/false); +  } + +  // Check normal case, this entity begin location is after the previous one. +  if (PreprocessedEntities.empty() || +      !SourceMgr.isBeforeInTranslationUnit(BeginLoc, +                   PreprocessedEntities.back()->getSourceRange().getBegin())) { +    PreprocessedEntities.push_back(Entity); +    return getPPEntityID(PreprocessedEntities.size()-1, /*isLoaded=*/false); +  } + +  // The entity's location is not after the previous one; this can happen with +  // include directives that form the filename using macros, e.g: +  // "#include MACRO(STUFF)" +  // or with macro expansions inside macro arguments where the arguments are +  // not expanded in the same order as listed, e.g: +  // \code +  //  #define M1 1 +  //  #define M2 2 +  //  #define FM(x,y) y x +  //  FM(M1, M2) +  // \endcode + +  using pp_iter = std::vector<PreprocessedEntity *>::iterator; + +  // Usually there are few macro expansions when defining the filename, do a +  // linear search for a few entities. +  unsigned count = 0; +  for (pp_iter RI    = PreprocessedEntities.end(), +               Begin = PreprocessedEntities.begin(); +       RI != Begin && count < 4; --RI, ++count) { +    pp_iter I = RI; +    --I; +    if (!SourceMgr.isBeforeInTranslationUnit(BeginLoc, +                                           (*I)->getSourceRange().getBegin())) { +      pp_iter insertI = PreprocessedEntities.insert(RI, Entity); +      return getPPEntityID(insertI - PreprocessedEntities.begin(), +                           /*isLoaded=*/false); +    } +  } + +  // Linear search unsuccessful. Do a binary search. +  pp_iter I = +      llvm::upper_bound(PreprocessedEntities, BeginLoc, +                        PPEntityComp<&SourceRange::getBegin>(SourceMgr)); +  pp_iter insertI = PreprocessedEntities.insert(I, Entity); +  return getPPEntityID(insertI - PreprocessedEntities.begin(), +                       /*isLoaded=*/false); +} + +void PreprocessingRecord::SetExternalSource( +                                    ExternalPreprocessingRecordSource &Source) { +  assert(!ExternalSource && +         "Preprocessing record already has an external source"); +  ExternalSource = &Source; +} + +unsigned PreprocessingRecord::allocateLoadedEntities(unsigned NumEntities) { +  unsigned Result = LoadedPreprocessedEntities.size(); +  LoadedPreprocessedEntities.resize(LoadedPreprocessedEntities.size() +                                    + NumEntities); +  return Result; +} + +unsigned PreprocessingRecord::allocateSkippedRanges(unsigned NumRanges) { +  unsigned Result = SkippedRanges.size(); +  SkippedRanges.resize(SkippedRanges.size() + NumRanges); +  SkippedRangesAllLoaded = false; +  return Result; +} + +void PreprocessingRecord::ensureSkippedRangesLoaded() { +  if (SkippedRangesAllLoaded || !ExternalSource) +    return; +  for (unsigned Index = 0; Index != SkippedRanges.size(); ++Index) { +    if (SkippedRanges[Index].isInvalid()) +      SkippedRanges[Index] = ExternalSource->ReadSkippedRange(Index); +  } +  SkippedRangesAllLoaded = true; +} + +void PreprocessingRecord::RegisterMacroDefinition(MacroInfo *Macro, +                                                  MacroDefinitionRecord *Def) { +  MacroDefinitions[Macro] = Def; +} + +/// Retrieve the preprocessed entity at the given ID. +PreprocessedEntity *PreprocessingRecord::getPreprocessedEntity(PPEntityID PPID){ +  if (PPID.ID < 0) { +    unsigned Index = -PPID.ID - 1; +    assert(Index < LoadedPreprocessedEntities.size() && +           "Out-of bounds loaded preprocessed entity"); +    return getLoadedPreprocessedEntity(Index); +  } + +  if (PPID.ID == 0) +    return nullptr; +  unsigned Index = PPID.ID - 1; +  assert(Index < PreprocessedEntities.size() && +         "Out-of bounds local preprocessed entity"); +  return PreprocessedEntities[Index]; +} + +/// Retrieve the loaded preprocessed entity at the given index. +PreprocessedEntity * +PreprocessingRecord::getLoadedPreprocessedEntity(unsigned Index) { +  assert(Index < LoadedPreprocessedEntities.size() && +         "Out-of bounds loaded preprocessed entity"); +  assert(ExternalSource && "No external source to load from"); +  PreprocessedEntity *&Entity = LoadedPreprocessedEntities[Index]; +  if (!Entity) { +    Entity = ExternalSource->ReadPreprocessedEntity(Index); +    if (!Entity) // Failed to load. +      Entity = new (*this) +         PreprocessedEntity(PreprocessedEntity::InvalidKind, SourceRange()); +  } +  return Entity; +} + +MacroDefinitionRecord * +PreprocessingRecord::findMacroDefinition(const MacroInfo *MI) { +  llvm::DenseMap<const MacroInfo *, MacroDefinitionRecord *>::iterator Pos = +      MacroDefinitions.find(MI); +  if (Pos == MacroDefinitions.end()) +    return nullptr; + +  return Pos->second; +} + +void PreprocessingRecord::addMacroExpansion(const Token &Id, +                                            const MacroInfo *MI, +                                            SourceRange Range) { +  // We don't record nested macro expansions. +  if (Id.getLocation().isMacroID()) +    return; + +  if (MI->isBuiltinMacro()) +    addPreprocessedEntity(new (*this) +                              MacroExpansion(Id.getIdentifierInfo(), Range)); +  else if (MacroDefinitionRecord *Def = findMacroDefinition(MI)) +    addPreprocessedEntity(new (*this) MacroExpansion(Def, Range)); +} + +void PreprocessingRecord::Ifdef(SourceLocation Loc, const Token &MacroNameTok, +                                const MacroDefinition &MD) { +  // This is not actually a macro expansion but record it as a macro reference. +  if (MD) +    addMacroExpansion(MacroNameTok, MD.getMacroInfo(), +                      MacroNameTok.getLocation()); +} + +void PreprocessingRecord::Ifndef(SourceLocation Loc, const Token &MacroNameTok, +                                 const MacroDefinition &MD) { +  // This is not actually a macro expansion but record it as a macro reference. +  if (MD) +    addMacroExpansion(MacroNameTok, MD.getMacroInfo(), +                      MacroNameTok.getLocation()); +} + +void PreprocessingRecord::Defined(const Token &MacroNameTok, +                                  const MacroDefinition &MD, +                                  SourceRange Range) { +  // This is not actually a macro expansion but record it as a macro reference. +  if (MD) +    addMacroExpansion(MacroNameTok, MD.getMacroInfo(), +                      MacroNameTok.getLocation()); +} + +void PreprocessingRecord::SourceRangeSkipped(SourceRange Range, +                                             SourceLocation EndifLoc) { +  assert(Range.isValid()); +  SkippedRanges.emplace_back(Range.getBegin(), EndifLoc); +} + +void PreprocessingRecord::MacroExpands(const Token &Id, +                                       const MacroDefinition &MD, +                                       SourceRange Range, +                                       const MacroArgs *Args) { +  addMacroExpansion(Id, MD.getMacroInfo(), Range); +} + +void PreprocessingRecord::MacroDefined(const Token &Id, +                                       const MacroDirective *MD) { +  const MacroInfo *MI = MD->getMacroInfo(); +  SourceRange R(MI->getDefinitionLoc(), MI->getDefinitionEndLoc()); +  MacroDefinitionRecord *Def = +      new (*this) MacroDefinitionRecord(Id.getIdentifierInfo(), R); +  addPreprocessedEntity(Def); +  MacroDefinitions[MI] = Def; +} + +void PreprocessingRecord::MacroUndefined(const Token &Id, +                                         const MacroDefinition &MD, +                                         const MacroDirective *Undef) { +  MD.forAllDefinitions([&](MacroInfo *MI) { MacroDefinitions.erase(MI); }); +} + +void PreprocessingRecord::InclusionDirective( +    SourceLocation HashLoc, +    const Token &IncludeTok, +    StringRef FileName, +    bool IsAngled, +    CharSourceRange FilenameRange, +    const FileEntry *File, +    StringRef SearchPath, +    StringRef RelativePath, +    const Module *Imported, +    SrcMgr::CharacteristicKind FileType) { +  InclusionDirective::InclusionKind Kind = InclusionDirective::Include; + +  switch (IncludeTok.getIdentifierInfo()->getPPKeywordID()) { +  case tok::pp_include: +    Kind = InclusionDirective::Include; +    break; + +  case tok::pp_import: +    Kind = InclusionDirective::Import; +    break; + +  case tok::pp_include_next: +    Kind = InclusionDirective::IncludeNext; +    break; + +  case tok::pp___include_macros: +    Kind = InclusionDirective::IncludeMacros; +    break; + +  default: +    llvm_unreachable("Unknown include directive kind"); +  } + +  SourceLocation EndLoc; +  if (!IsAngled) { +    EndLoc = FilenameRange.getBegin(); +  } else { +    EndLoc = FilenameRange.getEnd(); +    if (FilenameRange.isCharRange()) +      EndLoc = EndLoc.getLocWithOffset(-1); // the InclusionDirective expects +                                            // a token range. +  } +  clang::InclusionDirective *ID = +      new (*this) clang::InclusionDirective(*this, Kind, FileName, !IsAngled, +                                            (bool)Imported, File, +                                            SourceRange(HashLoc, EndLoc)); +  addPreprocessedEntity(ID); +} + +size_t PreprocessingRecord::getTotalMemory() const { +  return BumpAlloc.getTotalMemory() +    + llvm::capacity_in_bytes(MacroDefinitions) +    + llvm::capacity_in_bytes(PreprocessedEntities) +    + llvm::capacity_in_bytes(LoadedPreprocessedEntities) +    + llvm::capacity_in_bytes(SkippedRanges); +}  | 
