diff options
27 files changed, 1151 insertions, 805 deletions
diff --git a/clang/include/clang/AST/Stmt.h b/clang/include/clang/AST/Stmt.h index eb6836f8869..465bceaa193 100644 --- a/clang/include/clang/AST/Stmt.h +++ b/clang/include/clang/AST/Stmt.h @@ -583,6 +583,7 @@ public: body_range body() { return body_range(body_begin(), body_end()); } body_iterator body_begin() { return Body; } body_iterator body_end() { return Body + size(); } + Stmt *body_front() { return !body_empty() ? Body[0] : nullptr; } Stmt *body_back() { return !body_empty() ? Body[size()-1] : nullptr; } void setLastStmt(Stmt *S) { @@ -598,6 +599,9 @@ public: } const_body_iterator body_begin() const { return Body; } const_body_iterator body_end() const { return Body + size(); } + const Stmt *body_front() const { + return !body_empty() ? Body[0] : nullptr; + } const Stmt *body_back() const { return !body_empty() ? Body[size() - 1] : nullptr; } diff --git a/clang/lib/CodeGen/CoverageMappingGen.cpp b/clang/lib/CodeGen/CoverageMappingGen.cpp index 748b50da9ed..ad4a9753bb1 100644 --- a/clang/lib/CodeGen/CoverageMappingGen.cpp +++ b/clang/lib/CodeGen/CoverageMappingGen.cpp @@ -15,6 +15,7 @@ #include "CodeGenFunction.h" #include "clang/AST/StmtVisitor.h" #include "clang/Lex/Lexer.h" +#include "llvm/ADT/Optional.h" #include "llvm/ProfileData/CoverageMapping.h" #include "llvm/ProfileData/CoverageMappingReader.h" #include "llvm/ProfileData/CoverageMappingWriter.h" @@ -33,87 +34,50 @@ namespace { /// \brief A region of source code that can be mapped to a counter. class SourceMappingRegion { -public: - enum RegionFlags { - /// \brief This region won't be emitted if it wasn't extended. - /// This is useful so that we won't emit source ranges for single tokens - /// that we don't really care that much about, like: - /// the '(' token in #define MACRO ( - IgnoreIfNotExtended = 0x0001, - }; - -private: - FileID File, MacroArgumentFile; - Counter Count; - /// \brief A statement that initiated the count of Zero. - /// - /// This initiator statement is useful to prevent merging of unreachable - /// regions with different statements that caused the counter to become - /// unreachable. - const Stmt *UnreachableInitiator; - - /// \brief A statement that separates certain mapping regions into groups. - /// - /// The group statement is sometimes useful when we are emitting the source - /// regions not in their correct lexical order, e.g. the regions for the - /// incrementation expression in the 'for' construct. By marking the regions - /// in the incrementation expression with the group statement, we avoid the - /// merging of the regions from the incrementation expression and the loop's - /// body. - const Stmt *Group; - /// \brief The region's starting location. - SourceLocation LocStart; + Optional<SourceLocation> LocStart; /// \brief The region's ending location. - SourceLocation LocEnd, AlternativeLocEnd; - unsigned Flags; + Optional<SourceLocation> LocEnd; public: - SourceMappingRegion(FileID File, FileID MacroArgumentFile, Counter Count, - const Stmt *UnreachableInitiator, const Stmt *Group, - SourceLocation LocStart, SourceLocation LocEnd, - unsigned Flags = 0) - : File(File), MacroArgumentFile(MacroArgumentFile), Count(Count), - UnreachableInitiator(UnreachableInitiator), Group(Group), - LocStart(LocStart), LocEnd(LocEnd), AlternativeLocEnd(LocStart), - Flags(Flags) {} + SourceMappingRegion(Counter Count, Optional<SourceLocation> LocStart, + Optional<SourceLocation> LocEnd) + : Count(Count), LocStart(LocStart), LocEnd(LocEnd) {} + + SourceMappingRegion(SourceMappingRegion &&Region) + : Count(std::move(Region.Count)), LocStart(std::move(Region.LocStart)), + LocEnd(std::move(Region.LocEnd)) {} - const FileID &getFile() const { return File; } + SourceMappingRegion &operator=(SourceMappingRegion &&RHS) { + Count = std::move(RHS.Count); + LocStart = std::move(RHS.LocStart); + LocEnd = std::move(RHS.LocEnd); + return *this; + } const Counter &getCounter() const { return Count; } - const SourceLocation &getStartLoc() const { return LocStart; } + void setCounter(Counter C) { Count = C; } - const SourceLocation &getEndLoc(const SourceManager &SM) const { - if (SM.getFileID(LocEnd) != File) - return AlternativeLocEnd; - return LocEnd; - } + bool hasStartLoc() const { return LocStart.hasValue(); } - bool hasFlag(RegionFlags Flag) const { return (Flags & Flag) != 0; } + void setStartLoc(SourceLocation Loc) { LocStart = Loc; } - void setFlag(RegionFlags Flag) { Flags |= Flag; } + const SourceLocation &getStartLoc() const { + assert(LocStart && "Region has no start location"); + return *LocStart; + } - void clearFlag(RegionFlags Flag) { Flags &= ~Flag; } + bool hasEndLoc() const { return LocEnd.hasValue(); } - /// \brief Return true if two regions can be merged together. - bool isMergeable(SourceMappingRegion &R) { - // FIXME: We allow merging regions with a gap in between them. Should we? - return File == R.File && MacroArgumentFile == R.MacroArgumentFile && - Count == R.Count && UnreachableInitiator == R.UnreachableInitiator && - Group == R.Group; - } + void setEndLoc(SourceLocation Loc) { LocEnd = Loc; } - /// \brief A comparison that sorts such that mergeable regions are adjacent. - friend bool operator<(const SourceMappingRegion &LHS, - const SourceMappingRegion &RHS) { - return std::tie(LHS.File, LHS.MacroArgumentFile, LHS.Count, - LHS.UnreachableInitiator, LHS.Group) < - std::tie(RHS.File, RHS.MacroArgumentFile, RHS.Count, - RHS.UnreachableInitiator, RHS.Group); + const SourceLocation &getEndLoc() const { + assert(LocEnd && "Region has no end location"); + return *LocEnd; } }; @@ -126,26 +90,11 @@ public: const LangOptions &LangOpts; private: - struct FileInfo { - /// \brief The file id that will be used by the coverage mapping system. - unsigned CovMappingFileID; - const FileEntry *Entry; - - FileInfo(unsigned CovMappingFileID, const FileEntry *Entry) - : CovMappingFileID(CovMappingFileID), Entry(Entry) {} - }; - - /// \brief This mapping maps clang's FileIDs to file ids used - /// by the coverage mapping system and clang's file entries. - llvm::SmallDenseMap<FileID, FileInfo, 8> FileIDMapping; + /// \brief Map of clang's FileIDs to IDs used for coverage mapping. + llvm::SmallDenseMap<FileID, std::pair<unsigned, SourceLocation>, 8> + FileIDMapping; public: - /// \brief The statement that corresponds to the current source group. - const Stmt *CurrentSourceGroup; - - /// \brief The statement the initiated the current unreachable region. - const Stmt *CurrentUnreachableRegionInitiator; - /// \brief The coverage mapping regions for this function llvm::SmallVector<CounterMappingRegion, 32> MappingRegions; /// \brief The source mapping regions for this function. @@ -153,56 +102,99 @@ public: CoverageMappingBuilder(CoverageMappingModuleGen &CVM, SourceManager &SM, const LangOptions &LangOpts) - : CVM(CVM), SM(SM), LangOpts(LangOpts), - CurrentSourceGroup(nullptr), - CurrentUnreachableRegionInitiator(nullptr) {} + : CVM(CVM), SM(SM), LangOpts(LangOpts) {} /// \brief Return the precise end location for the given token. SourceLocation getPreciseTokenLocEnd(SourceLocation Loc) { - return Lexer::getLocForEndOfToken(SM.getSpellingLoc(Loc), 0, SM, LangOpts); + // We avoid getLocForEndOfToken here, because it doesn't do what we want for + // macro locations, which we just treat as expanded files. + unsigned TokLen = + Lexer::MeasureTokenLength(SM.getSpellingLoc(Loc), SM, LangOpts); + return Loc.getLocWithOffset(TokLen); } - /// \brief Create the mapping that maps from the function's file ids to - /// the indices for the translation unit's filenames. - void createFileIDMapping(SmallVectorImpl<unsigned> &Mapping) { - Mapping.resize(FileIDMapping.size(), 0); - for (const auto &I : FileIDMapping) - Mapping[I.second.CovMappingFileID] = CVM.getFileID(I.second.Entry); + /// \brief Return the start location of an included file or expanded macro. + SourceLocation getStartOfFileOrMacro(SourceLocation Loc) { + if (Loc.isMacroID()) + return Loc.getLocWithOffset(-SM.getFileOffset(Loc)); + return SM.getLocForStartOfFile(SM.getFileID(Loc)); } - /// \brief Get the coverage mapping file id that corresponds to the given - /// clang file id. If such file id doesn't exist, it gets added to the - /// mapping that maps from clang's file ids to coverage mapping file ids. - /// Returns None if there was an error getting the coverage mapping file id. - /// An example of an when this function fails is when the region tries - /// to get a coverage file id for a location in a built-in macro. - Optional<unsigned> getCoverageFileID(SourceLocation LocStart, FileID File, - FileID SpellingFile) { - auto Mapping = FileIDMapping.find(File); - if (Mapping != FileIDMapping.end()) - return Mapping->second.CovMappingFileID; + /// \brief Return the end location of an included file or expanded macro. + SourceLocation getEndOfFileOrMacro(SourceLocation Loc) { + if (Loc.isMacroID()) + return Loc.getLocWithOffset(SM.getFileIDSize(SM.getFileID(Loc)) - + SM.getFileOffset(Loc) - 1); + return SM.getLocForEndOfFile(SM.getFileID(Loc)); + } + + /// \brief Find out where the current file is included or macro is expanded. + SourceLocation getIncludeOrExpansionLoc(SourceLocation Loc) { + return Loc.isMacroID() ? SM.getImmediateExpansionRange(Loc).first + : SM.getIncludeLoc(SM.getFileID(Loc)); + } + + /// \brief Get the start of \c S ignoring macro argument locations. + SourceLocation getStart(const Stmt *S) { + SourceLocation Loc = S->getLocStart(); + while (SM.isMacroArgExpansion(Loc)) + Loc = SM.getImmediateExpansionRange(Loc).first; + return Loc; + } + + /// \brief Get the end of \c S ignoring macro argument locations. + SourceLocation getEnd(const Stmt *S) { + SourceLocation Loc = S->getLocEnd(); + while (SM.isMacroArgExpansion(Loc)) + Loc = SM.getImmediateExpansionRange(Loc).first; + return Loc; + } - auto Entry = SM.getFileEntryForID(SpellingFile); - if (!Entry) - return None; + /// \brief Find the set of files we have regions for and assign IDs + /// + /// Fills \c Mapping with the virtual file mapping needed to write out + /// coverage and collects the necessary file information to emit source and + /// expansion regions. + void gatherFileIDs(SmallVectorImpl<unsigned> &Mapping) { + FileIDMapping.clear(); + + SmallVector<FileID, 8> Visited; + SmallVector<std::pair<SourceLocation, unsigned>, 8> FileLocs; + for (const auto &Region : SourceRegions) { + SourceLocation Loc = Region.getStartLoc(); + FileID File = SM.getFileID(Loc); + if (std::find(Visited.begin(), Visited.end(), File) != Visited.end()) + continue; + Visited.push_back(File); + + unsigned Depth = 0; + for (SourceLocation Parent = getIncludeOrExpansionLoc(Loc); + !Parent.isInvalid(); Parent = getIncludeOrExpansionLoc(Parent)) + ++Depth; + FileLocs.push_back(std::make_pair(Loc, Depth)); + } + std::stable_sort(FileLocs.begin(), FileLocs.end(), llvm::less_second()); - unsigned Result = FileIDMapping.size(); - FileIDMapping.insert(std::make_pair(File, FileInfo(Result, Entry))); - createFileExpansionRegion(LocStart, File); - return Result; + for (const auto &FL : FileLocs) { + SourceLocation Loc = FL.first; + FileID SpellingFile = SM.getDecomposedSpellingLoc(Loc).first; + auto Entry = SM.getFileEntryForID(SpellingFile); + if (!Entry) + continue; + + FileIDMapping[SM.getFileID(Loc)] = std::make_pair(Mapping.size(), Loc); + Mapping.push_back(CVM.getFileID(Entry)); + } } - /// \brief Get the coverage mapping file id that corresponds to the given - /// clang file id. - /// Returns None if there was an error getting the coverage mapping file id. - Optional<unsigned> getExistingCoverageFileID(FileID File) { - // Make sure that the file is valid. - if (File.isInvalid()) - return None; - auto Mapping = FileIDMapping.find(File); - if (Mapping == FileIDMapping.end()) - return None; - return Mapping->second.CovMappingFileID; + /// \brief Get the coverage mapping file ID for \c Loc. + /// + /// If such file id doesn't exist, return None. + Optional<unsigned> getCoverageFileID(SourceLocation Loc) { + auto Mapping = FileIDMapping.find(SM.getFileID(Loc)); + if (Mapping != FileIDMapping.end()) + return Mapping->second.first; + return None; } /// \brief Return true if the given clang's file id has a corresponding @@ -231,15 +223,10 @@ public: for (const auto &I : SkippedRanges) { auto LocStart = I.getBegin(); auto LocEnd = I.getEnd(); - auto FileStart = SM.getFileID(LocStart); - if (!hasExistingCoverageFileID(FileStart)) - continue; - auto ActualFileStart = SM.getDecomposedSpellingLoc(LocStart).first; - if (ActualFileStart != SM.getDecomposedSpellingLoc(LocEnd).first) - // Ignore regions that span across multiple files. - continue; + assert(SM.isWrittenInSameFile(LocStart, LocEnd) && + "region spans multiple files"); - auto CovFileID = getCoverageFileID(LocStart, FileStart, ActualFileStart); + auto CovFileID = getCoverageFileID(LocStart); if (!CovFileID) continue; unsigned LineStart = SM.getSpellingLineNumber(LocStart); @@ -256,125 +243,62 @@ public: } } - /// \brief Create a mapping region that correponds to an expansion of - /// a macro or an embedded include. - void createFileExpansionRegion(SourceLocation Loc, FileID ExpandedFile) { - SourceLocation LocStart; - if (Loc.isMacroID()) - LocStart = SM.getImmediateExpansionRange(Loc).first; - else { - LocStart = SM.getIncludeLoc(ExpandedFile); - if (LocStart.isInvalid()) - return; // This file has no expansion region. - } - - auto File = SM.getFileID(LocStart); - auto SpellingFile = SM.getDecomposedSpellingLoc(LocStart).first; - auto CovFileID = getCoverageFileID(LocStart, File, SpellingFile); - auto ExpandedFileID = getExistingCoverageFileID(ExpandedFile); - if (!CovFileID || !ExpandedFileID) - return; - unsigned LineStart = SM.getSpellingLineNumber(LocStart); - unsigned ColumnStart = SM.getSpellingColumnNumber(LocStart); - unsigned LineEnd = LineStart; - // Compute the end column manually as Lexer::getLocForEndOfToken doesn't - // give the correct result in all cases. - unsigned ColumnEnd = - ColumnStart + - Lexer::MeasureTokenLength(SM.getSpellingLoc(LocStart), SM, LangOpts); - - MappingRegions.push_back(CounterMappingRegion::makeExpansion( - *CovFileID, *ExpandedFileID, LineStart, ColumnStart, LineEnd, - ColumnEnd)); - } - - /// \brief Enter a source region group that is identified by the given - /// statement. - /// It's not possible to enter a group when there is already - /// another group present. - void beginSourceRegionGroup(const Stmt *Group) { - assert(!CurrentSourceGroup); - CurrentSourceGroup = Group; - } - - /// \brief Exit the current source region group. - void endSourceRegionGroup() { CurrentSourceGroup = nullptr; } - - /// \brief Associate a counter with a given source code range. - void mapSourceCodeRange(SourceLocation LocStart, SourceLocation LocEnd, - Counter Count, const Stmt *UnreachableInitiator, - const Stmt *SourceGroup, unsigned Flags = 0, - FileID MacroArgumentFile = FileID()) { - if (SM.isMacroArgExpansion(LocStart)) { - // Map the code range with the macro argument's value. - mapSourceCodeRange(SM.getImmediateSpellingLoc(LocStart), - SM.getImmediateSpellingLoc(LocEnd), Count, - UnreachableInitiator, SourceGroup, Flags, - SM.getFileID(LocStart)); - // Map the code range where the macro argument is referenced. - SourceLocation RefLocStart(SM.getImmediateExpansionRange(LocStart).first); - SourceLocation RefLocEnd(RefLocStart); - if (SM.isMacroArgExpansion(RefLocStart)) - mapSourceCodeRange(RefLocStart, RefLocEnd, Count, UnreachableInitiator, - SourceGroup, 0, SM.getFileID(RefLocStart)); - else - mapSourceCodeRange(RefLocStart, RefLocEnd, Count, UnreachableInitiator, - SourceGroup); - return; - } - auto File = SM.getFileID(LocStart); - // Make sure that the file id is valid. - if (File.isInvalid()) - return; - SourceRegions.emplace_back(File, MacroArgumentFile, Count, - UnreachableInitiator, SourceGroup, LocStart, - LocEnd, Flags); - } - - void mapSourceCodeRange(SourceLocation LocStart, SourceLocation LocEnd, - Counter Count, unsigned Flags = 0) { - mapSourceCodeRange(LocStart, LocEnd, Count, - CurrentUnreachableRegionInitiator, CurrentSourceGroup, - Flags); - } - /// \brief Generate the coverage counter mapping regions from collected /// source regions. void emitSourceRegions() { - std::sort(SourceRegions.begin(), SourceRegions.end()); - - for (auto I = SourceRegions.begin(), E = SourceRegions.end(); I != E; ++I) { - // Keep the original start location of this region. - SourceLocation LocStart = I->getStartLoc(); - SourceLocation LocEnd = I->getEndLoc(SM); - - bool Ignore = I->hasFlag(SourceMappingRegion::IgnoreIfNotExtended); - // We need to handle mergeable regions together. - for (auto Next = I + 1; Next != E && Next->isMergeable(*I); ++Next) { - ++I; - LocStart = std::min(LocStart, I->getStartLoc()); - LocEnd = std::max(LocEnd, I->getEndLoc(SM)); - // FIXME: Should we && together the Ignore flag of multiple regions? - Ignore = false; - } - if (Ignore) + for (const auto &Region : SourceRegions) { + assert(Region.hasEndLoc() && "incomplete region"); + + SourceLocation LocStart = Region.getStartLoc(); + assert(!SM.getFileID(LocStart).isInvalid() && "region in invalid file"); + + auto CovFileID = getCoverageFileID(LocStart); + // Ignore regions that don't have a file, such as builtin macros. + if (!CovFileID) continue; + SourceLocation LocEnd = getPreciseTokenLocEnd(Region.getEndLoc()); + assert(SM.isWrittenInSameFile(LocStart, LocEnd) && + "region spans multiple files"); + // Find the spilling locations for the mapping region. - LocEnd = getPreciseTokenLocEnd(LocEnd); unsigned LineStart = SM.getSpellingLineNumber(LocStart); unsigned ColumnStart = SM.getSpellingColumnNumber(LocStart); unsigned LineEnd = SM.getSpellingLineNumber(LocEnd); unsigned ColumnEnd = SM.getSpellingColumnNumber(LocEnd); - auto SpellingFile = SM.getDecomposedSpellingLoc(LocStart).first; - auto CovFileID = getCoverageFileID(LocStart, I->getFile(), SpellingFile); - if (!CovFileID) + assert(LineStart <= LineEnd && "region start and end out of order"); + MappingRegions.push_back(CounterMappingRegion::makeRegion( + Region.getCounter(), *CovFileID, LineStart, ColumnStart, LineEnd, + ColumnEnd)); + } + } + + /// \brief Generate expansion regions for each virtual file we've seen. + void emitExpansionRegions() { + for (const auto &FM : FileIDMapping) { + SourceLocation ExpandedLoc = FM.second.second; + SourceLocation ParentLoc = getIncludeOrExpansionLoc(ExpandedLoc); + if (ParentLoc.isInvalid()) continue; - assert(LineStart <= LineEnd); - MappingRegions.push_back(CounterMappingRegion::makeRegion( - I->getCounter(), *CovFileID, LineStart, ColumnStart, LineEnd, + auto ParentFileID = getCoverageFileID(ParentLoc); + if (!ParentFileID) + continue; + auto ExpandedFileID = getCoverageFileID(ExpandedLoc); + assert(ExpandedFileID && "expansion in uncovered file"); + + SourceLocation LocEnd = getPreciseTokenLocEnd(ParentLoc); + assert(SM.isWrittenInSameFile(ParentLoc, LocEnd) && + "region spans multiple files"); + + unsigned LineStart = SM.getSpellingLineNumber(ParentLoc); + unsigned ColumnStart = SM.getSpellingColumnNumber(ParentLoc); + unsigned LineEnd = SM.getSpellingLineNumber(LocEnd); + unsigned ColumnEnd = SM.getSpellingColumnNumber(LocEnd); + + MappingRegions.push_back(CounterMappingRegion::makeExpansion( + *ParentFileID, *ExpandedFileID, LineStart, ColumnStart, LineEnd, ColumnEnd)); } } @@ -391,14 +315,14 @@ struct EmptyCoverageMappingBuilder : public CoverageMappingBuilder { if (!D->hasBody()) return; auto Body = D->getBody(); - mapSourceCodeRange(Body->getLocStart(), Body->getLocEnd(), Counter()); + SourceRegions.emplace_back(Counter(), getStart(Body), getEnd(Body)); } /// \brief Write the mapping data to the output stream void write(llvm::raw_ostream &OS) { - emitSourceRegions(); SmallVector<unsigned, 16> FileIDMapping; - createFileIDMapping(FileIDMapping); + gatherFileIDs(FileIDMapping); + emitSourceRegions(); CoverageMappingWriter Writer(FileIDMapping, None, MappingRegions); Writer.write(OS); @@ -413,136 +337,212 @@ struct CounterCoverageMappingBuilder /// \brief The map of statements to count values. llvm::DenseMap<const Stmt *, unsigned> &CounterMap; - Counter CurrentRegionCount; + /// \brief A stack of currently live regions. + std::vector<SourceMappingRegion> RegionStack; CounterExpressionBuilder Builder; - /// \brief Return a counter that represents the - /// expression that subracts rhs from lhs. + /// \brief A location in the most recently visited file or macro. + /// + /// This is used to adjust the active source regions appropriately when + /// expressions cross file or macro boundaries. + SourceLocation MostRecentLocation; + + /// \brief Return a counter for the subtraction of \c RHS from \c LHS Counter subtractCounters(Counter LHS, Counter RHS) { return Builder.subtract(LHS, RHS); } - /// \brief Return a counter that represents the - /// the exression that adds lhs and rhs. + /// \brief Return a counter for the sum of \c LHS and \c RHS. Counter addCounters(Counter LHS, Counter RHS) { return Builder.add(LHS, RHS); } + Counter addCounters(Counter C1, Counter C2, Counter C3) { + return addCounters(addCounters(C1, C2), C3); + } + + Counter addCounters(Counter C1, Counter C2, Counter C3, Counter C4) { + return addCounters(addCounters(C1, C2, C3), C4); + } + /// \brief Return the region counter for the given statement. + /// /// This should only be called on statements that have a dedicated counter. - unsigned getRegionCounter(const Stmt *S) { return CounterMap[S]; } + Counter getRegionCounter(const Stmt *S) { + return Counter::getCounter(CounterMap[S]); + } + + /// \brief Push a region onto the stack. + /// + /// Returns the index on the stack where the region was pushed. This can be + /// used with popRegions to exit a "scope", ending the region that was pushed. + size_t pushRegion(Counter Count, Optional<SourceLocation> StartLoc = None, + Optional<SourceLocation> EndLoc = None) { + if (StartLoc) + MostRecentLocation = *StartLoc; + RegionStack.emplace_back(Count, StartLoc, EndLoc); - /// \brief Return the region count for the counter at the given index. - Counter getRegionCount(unsigned CounterId) { - return Counter::getCounter(CounterId); + return RegionStack.size() - 1; } - /// \brief Return the counter value of the current region. - Counter getCurrentRegionCount() { return CurrentRegionCount; } + /// \brief Pop regions from the stack into the function's list of regions. + /// + /// Adds all regions from \c ParentIndex to the top of the stack to the + /// function's \c SourceRegions. + void popRegions(size_t ParentIndex) { + assert(RegionStack.size() >= ParentIndex && "parent not in stack"); + while (RegionStack.size() > ParentIndex) { + SourceMappingRegion &Region = RegionStack.back(); + if (Region.hasStartLoc()) { + SourceLocation StartLoc = Region.getStartLoc(); + SourceLocation EndLoc = Region.hasEndLoc() + ? Region.getEndLoc() + : RegionStack[ParentIndex].getEndLoc(); + while (!SM.isWrittenInSameFile(StartLoc, EndLoc)) { + // The region ends in a nested file or macro expansion. Create a + // separate region for each expansion. + SourceLocation NestedLoc = getStartOfFileOrMacro(EndLoc); + assert(SM.isWrittenInSameFile(NestedLoc, EndLoc)); + + SourceRegions.emplace_back(Region.getCounter(), NestedLoc, EndLoc); + + EndLoc = getIncludeOrExpansionLoc(EndLoc); + assert(!EndLoc.isInvalid() && + "File exit was not handled before popRegions"); + } + Region.setEndLoc(EndLoc); + + MostRecentLocation = EndLoc; + // If this region happens to span an entire expansion, we need to make + // sure we don't overlap the parent region with it. + if (StartLoc == getStartOfFileOrMacro(StartLoc) && + EndLoc == getEndOfFileOrMacro(EndLoc)) + MostRecentLocation = getIncludeOrExpansionLoc(EndLoc); + + assert(SM.isWrittenInSameFile(Region.getStartLoc(), EndLoc)); + SourceRegions.push_back(std::move(Region)); + } + RegionStack.pop_back(); + } + } - /// \brief Set the counter value for the current region. - /// This is used to keep track of changes to the most recent counter - /// from control flow and non-local exits. - void setCurrentRegionCount(Counter Count) { - CurrentRegionCount = Count; - CurrentUnreachableRegionInitiator = nullptr; + /// \brief Return the currently active region. + SourceMappingRegion &getRegion() { + assert(!RegionStack.empty() && "statement has no region"); + return RegionStack.back(); } - /// \brief Indicate that the current region is never reached, - /// and thus should have a counter value of zero. - /// This is important so that subsequent regions can correctly track - /// their parent counts. - void setCurrentRegionUnreachable(const Stmt *Initiator) { - CurrentRegionCount = Counter::getZero(); - CurrentUnreachableRegionInitiator = Initiator; + /// \brief Propagate counts through the children of \c S. + Counter propagateCounts(Counter TopCount, const Stmt *S) { + size_t Index = pushRegion(TopCount, getStart(S), getEnd(S)); + Visit(S); + Counter ExitCount = getRegion().getCounter(); + popRegions(Index); + return ExitCount; } - /// \brief A counter for a particular region. - /// This is the primary interface through - /// which the coverage mapping builder manages counters and their values. - class RegionMapper { - CounterCoverageMappingBuilder &Mapping; - Counter Count; - Counter ParentCount; - Counter RegionCount; - Counter Adjust; - - public: - RegionMapper(CounterCoverageMappingBuilder *Mapper, const Stmt *S) - : Mapping(*Mapper), - Count(Mapper->getRegionCount(Mapper->getRegionCounter(S))), - ParentCount(Mapper->getCurrentRegionCount()) {} - - /// Get the value of the counter. In most cases this is the number of times - /// the region of the counter was entered, but for switch labels it's the - /// number of direct jumps to that label. - Counter getCount() const { return Count; } - - /// Get the value of the counter with adjustments applied. Adjustments occur - /// when control enters or leaves the region abnormally; i.e., if there is a - /// jump to a label within the region, or if the function can return from - /// within the region. The adjusted count, then, is the value of the counter - /// at the end of the region. - Counter getAdjustedCount() const { - return Mapping.addCounters(Count, Adjust); - } + /// \brief Adjust the most recently visited location to \c EndLoc. + /// + /// This should be used after visiting any statements in non-source order. + void adjustForOutOfOrderTraversal(SourceLocation EndLoc) { + MostRecentLocation = EndLoc; + if (MostRecentLocation == getEndOfFileOrMacro(MostRecentLocation)) + MostRecentLocation = getIncludeOrExpansionLoc(MostRecentLocation); + } - /// Get the value of the counter in this region's parent, i.e., the region - /// that was active when this region began. This is useful for deriving - /// counts in implicitly counted regions, like the false case of a condition - /// or the normal exits of a loop. - Counter getParentCount() const { return ParentCount; } - - /// Activate the counter by emitting an increment and starting to track - /// adjustments. If AddIncomingFallThrough is true, the current region count - /// will be added to the counter for the purposes of tracking the region. - void beginRegion(bool AddIncomingFallThrough = false) { - RegionCount = Count; - if (AddIncomingFallThrough) - RegionCount = - Mapping.addCounters(RegionCount, Mapping.getCurrentRegionCount()); - Mapping.setCurrentRegionCount(RegionCount); - } + /// \brief Check whether \c Loc is included or expanded from \c Parent. + bool isNestedIn(SourceLocation Loc, FileID Parent) { + do { + Loc = getIncludeOrExpansionLoc(Loc); + if (Loc.isInvalid()) + return false; + } while (!SM.isInFileID(Loc, Parent)); + return true; + } - /// For counters on boolean branches, begins tracking adjustments for the - /// uncounted path. - void beginElseRegion() { - RegionCount = Mapping.subtractCounters(ParentCount, Count); - Mapping.setCurrentRegionCount(RegionCount); - } + /// \brief Adjust regions and state when \c NewLoc exits a file. + /// + /// If moving from our most recently tracked location to \c NewLoc exits any + /// files, this adjusts our current region stack and creates the file regions + /// for the exited file. + void handleFileExit(SourceLocation NewLoc) { + if (SM.isWrittenInSameFile(MostRecentLocation, NewLoc)) + return; - /// Reset the current region count. - void setCurrentRegionCount(Counter CurrentCount) { - RegionCount = CurrentCount; - Mapping.setCurrentRegionCount(RegionCount); + // If NewLoc is not in a file that contains MostRecentLocation, walk up to + // find the common ancestor. + SourceLocation LCA = NewLoc; + FileID ParentFile = SM.getFileID(LCA); + while (!isNestedIn(MostRecentLocation, ParentFile)) { + LCA = getIncludeOrExpansionLoc(LCA); + if (LCA.isInvalid() || SM.isWrittenInSameFile(LCA, MostRecentLocation)) { + // Since there isn't a common ancestor, no file was exited. We just need + // to adjust our location to the new file. + MostRecentLocation = NewLoc; + return; + } + ParentFile = SM.getFileID(LCA); } - /// Adjust for non-local control flow after emitting a subexpression or - /// substatement. This must be called to account for constructs such as - /// gotos, - /// labels, and returns, so that we can ensure that our region's count is - /// correct in the code that follows. - void adjustForControlFlow() { - Adjust = Mapping.addCounters( - Adjust, Mapping.subtractCounters(Mapping.getCurrentRegionCount(), - RegionCount)); - // Reset the region count in case this is called again later. - RegionCount = Mapping.getCurrentRegionCount(); - } + llvm::SmallSet<SourceLocation, 8> StartLocs; + Optional<Counter> ParentCounter; + for (auto I = RegionStack.rbegin(), E = RegionStack.rend(); I != E; ++I) { + if (!I->hasStartLoc()) + continue; + SourceLocation Loc = I->getStartLoc(); + if (!isNestedIn(Loc, ParentFile)) { + ParentCounter = I->getCounter(); + break; + } - /// Commit all adjustments to the current region. If the region is a loop, - /// the LoopAdjust value should be the count of all the breaks and continues - /// from the loop, to compensate for those counts being deducted from the - /// adjustments for the body of the loop. - void applyAdjustmentsToRegion() { - Mapping.setCurrentRegionCount(Mapping.addCounters(ParentCount, Adjust)); + while (!SM.isInFileID(Loc, ParentFile)) { + // The most nested region for each start location is the one with the + // correct count. We avoid creating redundant regions by stopping once + // we've seen this region. + if (StartLocs.insert(Loc).second) + SourceRegions.emplace_back(I->getCounter(), Loc, + getEndOfFileOrMacro(Loc)); + Loc = getIncludeOrExpansionLoc(Loc); + } + I->setStartLoc(getPreciseTokenLocEnd(Loc)); } - void applyAdjustmentsToRegion(Counter LoopAdjust) { - Mapping.setCurrentRegionCount(Mapping.addCounters( - Mapping.addCounters(ParentCount, Adjust), LoopAdjust)); + + if (ParentCounter) { + // If the file is contained completely by another region and doesn't + // immediately start its own region, the whole file gets a region + // corresponding to the parent. + SourceLocation Loc = MostRecentLocation; + while (isNestedIn(Loc, ParentFile)) { + SourceLocation FileStart = getStartOfFileOrMacro(Loc); + if (StartLocs.insert(FileStart).second) + SourceRegions.emplace_back(*ParentCounter, FileStart, + getEndOfFileOrMacro(Loc)); + Loc = getIncludeOrExpansionLoc(Loc); + } } - }; + + MostRecentLocation = NewLoc; + } + + /// \brief Ensure that \c S is included in the current region. + void extendRegion(const Stmt *S) { + SourceMappingRegion &Region = getRegion(); + SourceLocation StartLoc = getStart(S); + + handleFileExit(StartLoc); + if (!Region.hasStartLoc()) + Region.setStartLoc(StartLoc); + } + + /// \brief Mark \c S as a terminator, starting a zero region. + void terminateRegion(const Stmt *S) { + extendRegion(S); + SourceMappingRegion &Region = getRegion(); + if (!Region.hasEndLoc()) + Region.setEndLoc(getEnd(S)); + pushRegion(Counter::getZero()); + } /// \brief Keep counts of breaks and continues inside loops. struct BreakContinue { @@ -559,9 +559,10 @@ struct CounterCoverageMappingBuilder /// \brief Write the mapping data to the output stream void write(llvm::raw_ostream &OS) { - emitSourceRegions(); llvm::SmallVector<unsigned, 8> VirtualFileMapping; - createFileIDMapping(VirtualFileMapping); + gatherFileIDs(VirtualFileMapping); + emitSourceRegions(); + emitExpansionRegions(); gatherSkippedRegions(); CoverageMappingWriter Writer(VirtualFileMapping, Builder.getExpressions(), @@ -569,427 +570,282 @@ struct CounterCoverageMappingBuilder Writer.write(OS); } - /// \brief Associate the source code range with the current region count. - void mapSourceCodeRange(SourceLocation LocStart, SourceLocation LocEnd, - unsigned Flags = 0) { - CoverageMappingBuilder::mapSourceCodeRange(LocStart, LocEnd, - CurrentRegionCount, Flags); - } - - void mapSourceCodeRange(SourceLocation LocStart) { - CoverageMappingBuilder::mapSourceCodeRange(LocStart, LocStart, - CurrentRegionCount); - } - - /// \brief Associate the source range of a token with the current region - /// count. - /// Ignore the source range for this token if it produces a distinct - /// mapping region with no other source ranges. - void mapToken(SourceLocation LocStart) { - CoverageMappingBuilder::mapSourceCodeRange( - LocStart, LocStart, CurrentRegionCount, - SourceMappingRegion::IgnoreIfNotExtended); - } - void VisitStmt(const Stmt *S) { - mapSourceCodeRange(S->getLocStart()); + if (!S->getLocStart().isInvalid()) + extendRegion(S); for (Stmt::const_child_range I = S->children(); I; ++I) { if (*I) this->Visit(*I); } + handleFileExit(getEnd(S)); } void VisitDecl(const Decl *D) { - if (!D->hasBody()) - return; - // Counter tracks entry to the function body. - auto Body = D->getBody(); - RegionMapper Cnt(this, Body); - Cnt.beginRegion(); - Visit(Body); - } - - void VisitDeclStmt(const DeclStmt *S) { - mapSourceCodeRange(S->getLocStart()); - for (Stmt::const_child_range I = static_cast<const Stmt *>(S)->children(); - I; ++I) { - if (*I) - this->Visit(*I); - } - } - - void VisitCompoundStmt(const CompoundStmt *S) { - mapSourceCodeRange(S->getLBracLoc()); - mapSourceCodeRange(S->getRBracLoc()); - for (Stmt::const_child_range I = S->children(); I; ++I) { - if (*I) - this->Visit(*I); - } + Stmt *Body = D->getBody(); + propagateCounts(getRegionCounter(Body), Body); } void VisitReturnStmt(const ReturnStmt *S) { - mapSourceCodeRange(S->getLocStart()); + extendRegion(S); if (S->getRetValue()) Visit(S->getRetValue()); - setCurrentRegionUnreachable(S); + terminateRegion(S); } - void VisitGotoStmt(const GotoStmt *S) { - mapSourceCodeRange(S->getLocStart()); - mapToken(S->getLabelLoc()); - setCurrentRegionUnreachable(S); - } + void VisitGotoStmt(const GotoStmt *S) { terminateRegion(S); } void VisitLabelStmt(const LabelStmt *S) { - // Counter tracks the block following the label. - RegionMapper Cnt(this, S); - Cnt.beginRegion(); - mapSourceCodeRange(S->getLocStart()); - // Can't map the ':' token as its location isn't known. + SourceLocation Start = getStart(S); + // We can't extendRegion here or we risk overlapping with our new region. + handleFileExit(Start); + pushRegion(getRegionCounter(S), Start); Visit(S->getSubStmt()); } void VisitBreakStmt(const BreakStmt *S) { - mapSourceCodeRange(S->getLocStart()); assert(!BreakContinueStack.empty() && "break not in a loop or switch!"); BreakContinueStack.back().BreakCount = addCounters( - BreakContinueStack.back().BreakCount, getCurrentRegionCount()); - setCurrentRegionUnreachable(S); + BreakContinueStack.back().BreakCount, getRegion().getCounter()); + terminateRegion(S); } void VisitContinueStmt(const ContinueStmt *S) { - mapSourceCodeRange(S->getLocStart()); assert(!BreakContinueStack.empty() && "continue stmt not in a loop!"); BreakContinueStack.back().ContinueCount = addCounters( - BreakContinueStack.back().ContinueCount, getCurrentRegionCount()); - setCurrentRegionUnreachable(S); + BreakContinueStack.back().ContinueCount, getRegion().getCounter()); + terminateRegion(S); } void VisitWhileStmt(const WhileStmt *S) { - mapSourceCodeRange(S->getLocStart()); - // Counter tracks the body of the loop. - RegionMapper Cnt(this, S); + extendRegion(S); + + Counter ParentCount = getRegion().getCounter(); + Counter BodyCount = getRegionCounter(S); + + // Handle the body first so that we can get the backedge count. BreakContinueStack.push_back(BreakContinue()); - // Visit the body region first so the break/continue adjustments can be - // included when visiting the condition. - Cnt.beginRegion(); - Visit(S->getBody()); - Cnt.adjustForControlFlow(); - - // ...then go back and propagate counts through the condition. The count - // at the start of the condition is the sum of the incoming edges, - // the backedge from the end of the loop body, and the edges from - // continue statements. + extendRegion(S->getBody()); + Counter BackedgeCount = propagateCounts(BodyCount, S->getBody()); BreakContinue BC = BreakContinueStack.pop_back_val(); - Cnt.setCurrentRegionCount( - addCounters(Cnt.getParentCount(), - addCounters(Cnt.getAdjustedCount(), BC.ContinueCount))); - beginSourceRegionGroup(S->getCond()); - Visit(S->getCond()); - endSourceRegionGroup(); - Cnt.adjustForControlFlow(); - Cnt.applyAdjustmentsToRegion(addCounters(BC.BreakCount, BC.ContinueCount)); + + // Go back to handle the condition. + Counter CondCount = + addCounters(ParentCount, BackedgeCount, BC.ContinueCount); + propagateCounts(CondCount, S->getCond()); + adjustForOutOfOrderTraversal(getEnd(S)); + + Counter OutCount = + addCounters(BC.BreakCount, subtractCounters(CondCount, BodyCount)); + if (OutCount != ParentCount) + pushRegion(OutCount); } void VisitDoStmt(const DoStmt *S) { - mapSourceCodeRange(S->getLocStart()); - // Counter tracks the body of the loop. - RegionMapper Cnt(this, S); - BreakContinueStack.push_back(BreakContinue()); - Cnt.beginRegion(/*AddIncomingFallThrough=*/true); - Visit(S->getBody()); - Cnt.adjustForControlFlow(); + extendRegion(S); + + Counter ParentCount = getRegion().getCounter(); + Counter BodyCount = getRegionCounter(S); + BreakContinueStack.push_back(BreakContinue()); + extendRegion(S->getBody()); + Counter BackedgeCount = + propagateCounts(addCounters(ParentCount, BodyCount), S->getBody()); BreakContinue BC = BreakContinueStack.pop_back_val(); - // The count at the start of the condition is equal to the count at the - // end of the body. The adjusted count does not include either the - // fall-through count coming into the loop or the continue count, so add - // both of those separately. This is coincidentally the same equation as - // with while loops but for different reasons. - Cnt.setCurrentRegionCount( - addCounters(Cnt.getParentCount(), - addCounters(Cnt.getAdjustedCount(), BC.ContinueCount))); - Visit(S->getCond()); - Cnt.adjustForControlFlow(); - Cnt.applyAdjustmentsToRegion(addCounters(BC.BreakCount, BC.ContinueCount)); + + Counter CondCount = addCounters(BackedgeCount, BC.ContinueCount); + propagateCounts(CondCount, S->getCond()); + + Counter OutCount = + addCounters(BC.BreakCount, subtractCounters(CondCount, BodyCount)); + if (OutCount != ParentCount) + pushRegion(OutCount); } void VisitForStmt(const ForStmt *S) { - mapSourceCodeRange(S->getLocStart()); + extendRegion(S); if (S->getInit()) Visit(S->getInit()); - // Counter tracks the body of the loop. - RegionMapper Cnt(this, S); + Counter ParentCount = getRegion().getCounter(); + Counter BodyCount = getRegionCounter(S); + + // Handle the body first so that we can get the backedge count. BreakContinueStack.push_back(BreakContinue()); - // Visit the body region first. (This is basically the same as a while - // loop; see further comments in VisitWhileStmt.) - Cnt.beginRegion(); - Visit(S->getBody()); - Cnt.adjustForControlFlow(); + extendRegion(S->getBody()); + Counter BackedgeCount = propagateCounts(BodyCount, S->getBody()); + BreakContinue BC = BreakContinueStack.pop_back_val(); // The increment is essentially part of the body but it needs to include // the count for all the continue statements. - if (S->getInc()) { - Cnt.setCurrentRegionCount(addCounters( - getCurrentRegionCount(), BreakContinueStack.back().ContinueCount)); - beginSourceRegionGroup(S->getInc()); - Visit(S->getInc()); - endSourceRegionGroup(); - Cnt.adjustForControlFlow(); + if (const Stmt *Inc = S->getInc()) + propagateCounts(addCounters(BackedgeCount, BC.ContinueCount), Inc); + + // Go back to handle the condition. + Counter CondCount = + addCounters(ParentCount, BackedgeCount, BC.ContinueCount); + if (const Expr *Cond = S->getCond()) { + propagateCounts(CondCount, Cond); + adjustForOutOfOrderTraversal(getEnd(S)); } - BreakContinue BC = BreakContinueStack.pop_back_val(); - - // ...then go back and propagate counts through the condition. - if (S->getCond()) { - Cnt.setCurrentRegionCount( - addCounters(addCounters(Cnt.getParentCount(), Cnt.getAdjustedCount()), - BC.ContinueCount)); - beginSourceRegionGroup(S->getCond()); - Visit(S->getCond()); - endSourceRegionGroup(); - Cnt.adjustForControlFlow(); - } - Cnt.applyAdjustmentsToRegion(addCounters(BC.BreakCount, BC.ContinueCount)); + Counter OutCount = + addCounters(BC.BreakCount, subtractCounters(CondCount, BodyCount)); + if (OutCount != ParentCount) + pushRegion(OutCount); } void VisitCXXForRangeStmt(const CXXForRangeStmt *S) { - mapSourceCodeRange(S->getLocStart()); + extendRegion(S); + Visit(S->getLoopVarStmt()); Visit(S->getRangeStmt()); - Visit(S->getBeginEndStmt()); - // Counter tracks the body of the loop. - RegionMapper Cnt(this, S); + + Counter ParentCount = getRegion().getCounter(); + Counter BodyCount = getRegionCounter(S); + BreakContinueStack.push_back(BreakContinue()); - // Visit the body region first. (This is basically the same as a while - // loop; see further comments in VisitWhileStmt.) - Cnt.beginRegion(); - Visit(S->getBody()); - Cnt.adjustForControlFlow(); + extendRegion(S->getBody()); + Counter BackedgeCount = propagateCounts(BodyCount, S->getBody()); BreakContinue BC = BreakContinueStack.pop_back_val(); - Cnt.applyAdjustmentsToRegion(addCounters(BC.BreakCount, BC.ContinueCount)); + + Counter OutCount = addCounters(ParentCount, BC.BreakCount, BC.ContinueCount, + subtractCounters(BodyCount, BackedgeCount)); + if (OutCount != ParentCount) + pushRegion(OutCount); } void VisitObjCForCollectionStmt(const ObjCForCollectionStmt *S) { - mapSourceCodeRange(S->getLocStart()); + extendRegion(S); Visit(S->getElement()); - // Counter tracks the body of the loop. - RegionMapper Cnt(this, S); + + Counter ParentCount = getRegion().getCounter(); + Counter BodyCount = getRegionCounter(S); + BreakContinueStack.push_back(BreakContinue()); - Cnt.beginRegion(); - Visit(S->getBody()); + extendRegion(S->getBody()); + Counter BackedgeCount = propagateCounts(BodyCount, S->getBody()); BreakContinue BC = BreakContinueStack.pop_back_val(); - Cnt.adjustForControlFlow(); - Cnt.applyAdjustmentsToRegion(addCounters(BC.BreakCount, BC.ContinueCount)); + + Counter OutCount = addCounters(ParentCount, BC.BreakCount, BC.ContinueCount, + subtractCounters(BodyCount, BackedgeCount)); + if (OutCount != ParentCount) + pushRegion(OutCount); } void VisitSwitchStmt(const SwitchStmt *S) { - mapSourceCodeRange(S->getLocStart()); + extendRegion(S); Visit(S->getCond()); + BreakContinueStack.push_back(BreakContinue()); - // Map the '}' for the body to have the same count as the regions after - // the switch. - SourceLocation RBracLoc; - if (const auto *CS = dyn_cast<CompoundStmt>(S->getBody())) { - mapSourceCodeRange(CS->getLBracLoc()); - setCurrentRegionUnreachable(S); - for (Stmt::const_child_range I = CS->children(); I; ++I) { - if (*I) - this->Visit(*I); + + const Stmt *Body = S->getBody(); + extendRegion(Body); + if (const auto *CS = dyn_cast<CompoundStmt>(Body)) { + if (!CS->body_empty()) { + // The body of the switch needs a zero region so that fallthrough counts + // behave correctly, but it would be misleading to include the braces of + // the compound statement in the zeroed area, so we need to handle this + // specially. + size_t Index = + pushRegion(Counter::getZero(), getStart(CS->body_front()), + getEnd(CS->body_back())); + for (const auto &Child : CS->children()) + Visit(Child); + popRegions(Index); } - RBracLoc = CS->getRBracLoc(); - } else { - setCurrentRegionUnreachable(S); - Visit(S->getBody()); - } - // If the switch is inside a loop, add the continue counts. + } else + propagateCounts(Counter::getZero(), Body); BreakContinue BC = BreakContinueStack.pop_back_val(); + if (!BreakContinueStack.empty()) BreakContinueStack.back().ContinueCount = addCounters( BreakContinueStack.back().ContinueCount, BC.ContinueCount); - // Counter tracks the exit block of the switch. - RegionMapper ExitCnt(this, S); - ExitCnt.beginRegion(); - if (RBracLoc.isValid()) - mapSourceCodeRange(RBracLoc); - } - void VisitCaseStmt(const CaseStmt *S) { - // Counter for this particular case. This counts only jumps from the - // switch header and does not include fallthrough from the case before - // this one. - RegionMapper Cnt(this, S); - Cnt.beginRegion(/*AddIncomingFallThrough=*/true); - mapSourceCodeRange(S->getLocStart()); - mapToken(S->getColonLoc()); - Visit(S->getSubStmt()); + Counter ExitCount = getRegionCounter(S); + pushRegion(ExitCount); } - void VisitDefaultStmt(const DefaultStmt *S) { - // Counter for this default case. This does not include fallthrough from - // the previous case. - RegionMapper Cnt(this, S); - Cnt.beginRegion(/*AddIncomingFallThrough=*/true); - mapSourceCodeRange(S->getLocStart()); - mapToken(S->getColonLoc()); + void VisitSwitchCase(const SwitchCase *S) { + extendRegion(S); + + SourceMappingRegion &Parent = getRegion(); + + Counter Count = addCounters(Parent.getCounter(), getRegionCounter(S)); + // Reuse the existing region if it starts at our label. This is typical of + // the first case in a switch. + if (Parent.hasStartLoc() && Parent.getStartLoc() == getStart(S)) + Parent.setCounter(Count); + else + pushRegion(Count, getStart(S)); + + if (const CaseStmt *CS = dyn_cast<CaseStmt>(S)) { + Visit(CS->getLHS()); + if (const Expr *RHS = CS->getRHS()) + Visit(RHS); + } Visit(S->getSubStmt()); } void VisitIfStmt(const IfStmt *S) { - mapSourceCodeRange(S->getLocStart()); + extendRegion(S); Visit(S->getCond()); - mapToken(S->getElseLoc()); - - // Counter tracks the "then" part of an if statement. The count for - // the "else" part, if it exists, will be calculated from this counter. - RegionMapper Cnt(this, S); - Cnt.beginRegion(); - Visit(S->getThen()); - Cnt.adjustForControlFlow(); - - if (S->getElse()) { - Cnt.beginElseRegion(); - Visit(S->getElse()); - Cnt.adjustForControlFlow(); - } - Cnt.applyAdjustmentsToRegion(); + + Counter ParentCount = getRegion().getCounter(); + Counter ThenCount = getRegionCounter(S); + + extendRegion(S->getThen()); + Counter OutCount = propagateCounts(ThenCount, S->getThen()); + + Counter ElseCount = subtractCounters(ParentCount, ThenCount); + if (const Stmt *Else = S->getElse()) { + extendRegion(S->getElse()); + OutCount = addCounters(OutCount, propagateCounts(ElseCount, Else)); + } else + OutCount = addCounters(OutCount, ElseCount); + + if (OutCount != ParentCount) + pushRegion(OutCount); } void VisitCXXTryStmt(const CXXTryStmt *S) { - mapSourceCodeRange(S->getLocStart()); + extendRegion(S); Visit(S->getTryBlock()); for (unsigned I = 0, E = S->getNumHandlers(); I < E; ++I) Visit(S->getHandler(I)); - // Counter tracks the continuation block of the try statement. - RegionMapper Cnt(this, S); - Cnt.beginRegion(); + + Counter ExitCount = getRegionCounter(S); + pushRegion(ExitCount); } void VisitCXXCatchStmt(const CXXCatchStmt *S) { - mapSourceCodeRange(S->getLocStart()); - // Counter tracks the catch statement's handler block. - RegionMapper Cnt(this, S); - Cnt.beginRegion(); - Visit(S->getHandlerBlock()); + extendRegion(S); + propagateCounts(getRegionCounter(S), S->getHandlerBlock()); } void VisitAbstractConditionalOperator(const AbstractConditionalOperator *E) { - Visit(E->getCond()); - mapToken(E->getQuestionLoc()); - mapToken(E->getColonLoc()); - - // Counter tracks the "true" part of a conditional operator. The - // count in the "false" part will be calculated from this counter. - RegionMapper Cnt(this, E); - Cnt.beginRegion(); - Visit(E->getTrueExpr()); - Cnt.adjustForControlFlow(); - - Cnt.beginElseRegion(); - Visit(E->getFalseExpr()); - Cnt.adjustForControlFlow(); - - Cnt.applyAdjustmentsToRegion(); - } + extendRegion(E); - void VisitBinLAnd(const BinaryOperator *E) { - Visit(E->getLHS()); - mapToken(E->getOperatorLoc()); - // Counter tracks the right hand side of a logical and operator. - RegionMapper Cnt(this, E); - Cnt.beginRegion(); - Visit(E->getRHS()); - Cnt.adjustForControlFlow(); - Cnt.applyAdjustmentsToRegion(); - } + Counter ParentCount = getRegion().getCounter(); + Counter TrueCount = getRegionCounter(E); - void VisitBinLOr(const BinaryOperator *E) { - Visit(E->getLHS()); - mapToken(E->getOperatorLoc()); - // Counter tracks the right hand side of a logical or operator. - RegionMapper Cnt(this, E); - Cnt.beginRegion(); - Visit(E->getRHS()); - Cnt.adjustForControlFlow(); - Cnt.applyAdjustmentsToRegion(); + propagateCounts(TrueCount, E->getTrueExpr()); + propagateCounts(subtractCounters(ParentCount, TrueCount), + E->getFalseExpr()); } - void VisitParenExpr(const ParenExpr *E) { - mapToken(E->getLParen()); - Visit(E->getSubExpr()); - mapToken(E->getRParen()); - } - - void VisitBinaryOperator(const BinaryOperator *E) { + void VisitBinLAnd(const BinaryOperator *E) { + extendRegion(E); Visit(E->getLHS()); - mapToken(E->getOperatorLoc()); - Visit(E->getRHS()); - } - - void VisitUnaryOperator(const UnaryOperator *E) { - bool Postfix = E->isPostfix(); - if (!Postfix) - mapToken(E->getOperatorLoc()); - Visit(E->getSubExpr()); - if (Postfix) - mapToken(E->getOperatorLoc()); - } - - void VisitMemberExpr(const MemberExpr *E) { - Visit(E->getBase()); - mapToken(E->getMemberLoc()); - } - void VisitCallExpr(const CallExpr *E) { - Visit(E->getCallee()); - for (const auto &Arg : E->arguments()) - Visit(Arg); - mapToken(E->getRParenLoc()); + extendRegion(E->getRHS()); + propagateCounts(getRegionCounter(E), E->getRHS()); } - void VisitArraySubscriptExpr(const ArraySubscriptExpr *E) { + void VisitBinLOr(const BinaryOperator *E) { + extendRegion(E); Visit(E->getLHS()); - Visit(E->getRHS()); - mapToken(E->getRBracketLoc()); - } - - void VisitCStyleCastExpr(const CStyleCastExpr *E) { - mapToken(E->getLParenLoc()); - mapToken(E->getRParenLoc()); - Visit(E->getSubExpr()); - } - - // Map literals as tokens so that the macros like #define PI 3.14 - // won't generate coverage mapping regions. - void VisitIntegerLiteral(const IntegerLiteral *E) { - mapToken(E->getLocStart()); - } - - void VisitFloatingLiteral(const FloatingLiteral *E) { - mapToken(E->getLocStart()); - } - - void VisitCharacterLiteral(const CharacterLiteral *E) { - mapToken(E->getLocStart()); - } - - void VisitStringLiteral(const StringLiteral *E) { - mapToken(E->getLocStart()); - } - - void VisitImaginaryLiteral(const ImaginaryLiteral *E) { - mapToken(E->getLocStart()); - } - - void VisitObjCMessageExpr(const ObjCMessageExpr *E) { - mapToken(E->getLeftLoc()); - for (Stmt::const_child_range I = static_cast<const Stmt *>(E)->children(); - I; ++I) { - if (*I) - this->Visit(*I); - } - mapToken(E->getRightLoc()); + extendRegion(E->getRHS()); + propagateCounts(getRegionCounter(E), E->getRHS()); } }; } diff --git a/clang/test/CoverageMapping/Inputs/code.h b/clang/test/CoverageMapping/Inputs/code.h index cd3cfb5d3fe..609f54a8f15 100644 --- a/clang/test/CoverageMapping/Inputs/code.h +++ b/clang/test/CoverageMapping/Inputs/code.h @@ -1,3 +1,5 @@ +// Some code for the middle of a program + x = x; if (x == 0) { x = 1; diff --git a/clang/test/CoverageMapping/Inputs/ends_a_scope b/clang/test/CoverageMapping/Inputs/ends_a_scope new file mode 100644 index 00000000000..f6ce61dc812 --- /dev/null +++ b/clang/test/CoverageMapping/Inputs/ends_a_scope @@ -0,0 +1,5 @@ +// This file closes a scope + +} + +if (true) x = x; diff --git a/clang/test/CoverageMapping/Inputs/md.def b/clang/test/CoverageMapping/Inputs/md.def new file mode 100644 index 00000000000..fa0fb6bf59b --- /dev/null +++ b/clang/test/CoverageMapping/Inputs/md.def @@ -0,0 +1,5 @@ + +HANDLE_MD(Val1) +HANDLE_MD(Val2) + +#undef HANDLE_MD diff --git a/clang/test/CoverageMapping/Inputs/starts_a_scope b/clang/test/CoverageMapping/Inputs/starts_a_scope new file mode 100644 index 00000000000..57c3087d6b7 --- /dev/null +++ b/clang/test/CoverageMapping/Inputs/starts_a_scope @@ -0,0 +1,4 @@ +// This file opens a new scope + +x = x; +for (int i = 0; i < 2; ++i) { diff --git a/clang/test/CoverageMapping/break.c b/clang/test/CoverageMapping/break.c index d05f2b8f55a..07f1d6bc90c 100644 --- a/clang/test/CoverageMapping/break.c +++ b/clang/test/CoverageMapping/break.c @@ -4,24 +4,24 @@ int main() { // CHECK: File 0, [[@LINE]]:12 -> [[@LINE+28]]:2 = #0 int cnt = 0; // CHECK-NEXT: File 0, [[@LINE+1]]:9 -> [[@LINE+1]]:18 = #0 while(cnt < 100) { // CHECK-NEXT: File 0, [[@LINE]]:20 -> [[@LINE+3]]:4 = #1 break; - ++cnt; // CHECK-NEXT: File 0, [[@LINE]]:5 -> [[@LINE]]:10 = 0 + ++cnt; // CHECK-NEXT: File 0, [[@LINE]]:5 -> [[@LINE+1]]:4 = 0 } // CHECK-NEXT: File 0, [[@LINE+1]]:9 -> [[@LINE+1]]:18 = #0 while(cnt < 100) { // CHECK-NEXT: File 0, [[@LINE]]:20 -> [[@LINE+6]]:4 = #2 { break; - ++cnt; // CHECK-NEXT: File 0, [[@LINE]]:7 -> [[@LINE+2]]:10 = 0 + ++cnt; // CHECK-NEXT: File 0, [[@LINE]]:7 -> [[@LINE+3]]:4 = 0 } ++cnt; } // CHECK-NEXT: File 0, [[@LINE+1]]:9 -> [[@LINE+1]]:18 = ((#0 + #3) - #4) while(cnt < 100) { // CHECK-NEXT: File 0, [[@LINE]]:20 -> [[@LINE+6]]:4 = #3 if(cnt == 0) { // CHECK-NEXT: File 0, [[@LINE]]:18 -> [[@LINE+3]]:6 = #4 break; - ++cnt; // CHECK-NEXT: File 0, [[@LINE]]:7 -> [[@LINE]]:12 = 0 + ++cnt; // CHECK-NEXT: File 0, [[@LINE]]:7 -> [[@LINE+1]]:6 = 0 } - ++cnt; // CHECK-NEXT: File 0, [[@LINE]]:5 -> [[@LINE]]:10 = (#3 - #4) + ++cnt; // CHECK-NEXT: File 0, [[@LINE]]:5 -> [[@LINE+1]]:4 = (#3 - #4) } // CHECK-NEXT: File 0, [[@LINE+1]]:9 -> [[@LINE+1]]:18 = (#0 + #6) while(cnt < 100) { // CHECK-NEXT: File 0, [[@LINE]]:20 -> [[@LINE+7]]:4 = #5 - if(cnt == 0) { // CHECK-NEXT: File 0, [[@LINE]]:18 -> [[@LINE+5]]:10 = #6 + if(cnt == 0) { // CHECK-NEXT: File 0, [[@LINE]]:18 -> [[@LINE+2]]:6 = #6 ++cnt; } else { // CHECK-NEXT: File 0, [[@LINE]]:12 -> [[@LINE+2]]:6 = (#5 - #6) break; diff --git a/clang/test/CoverageMapping/continue.c b/clang/test/CoverageMapping/continue.c index 10852e15890..f100dba00b0 100644 --- a/clang/test/CoverageMapping/continue.c +++ b/clang/test/CoverageMapping/continue.c @@ -7,18 +7,18 @@ int main() { // CHECK: File 0, [[@LINE]]:12 -> [[@LINE+21]]:2 if(i < 10) { // CHECK-NEXT: File 0, [[@LINE]]:16 -> [[@LINE+13]]:6 = #2 if(i < 5) { // CHECK-NEXT: File 0, [[@LINE]]:17 -> [[@LINE+3]]:8 = #3 continue; - j = 1; // CHECK-NEXT: File 0, [[@LINE]]:9 -> [[@LINE]]:14 = 0 - } else { // CHECK-NEXT: File 0, [[@LINE]]:14 -> [[@LINE+7]]:13 = (#2 - #3) + j = 1; // CHECK-NEXT: File 0, [[@LINE]]:9 -> [[@LINE+1]]:8 = 0 + } else { // CHECK-NEXT: File 0, [[@LINE]]:14 -> [[@LINE+2]]:8 = (#2 - #3) j = 2; } - j = 3; + j = 3; // CHECK-NEXT: File 0, [[@LINE]]:7 -> [[@LINE+6]]:6 = (#2 - #3) if(i < 7) { // CHECK-NEXT: File 0, [[@LINE]]:17 -> [[@LINE+3]]:8 = #4 continue; - j = 4; // CHECK-NEXT: File 0, [[@LINE]]:9 -> [[@LINE]]:14 = 0 - } else j = 5; // CHECK-NEXT: File 0, [[@LINE]]:14 -> [[@LINE+1]]:12 = ((#2 - #3) - #4) - j = 6; + j = 4; // CHECK-NEXT: File 0, [[@LINE]]:9 -> [[@LINE+1]]:8 = 0 + } else j = 5; // CHECK-NEXT: File 0, [[@LINE]]:14 -> [[@LINE]]:19 = ((#2 - #3) - #4) + j = 6; // CHECK-NEXT: File 0, [[@LINE]]:7 -> [[@LINE+1]]:6 = ((#2 - #3) - #4) } else // CHECK-NEXT: File 0, [[@LINE+1]]:7 -> [[@LINE+1]]:12 = (#1 - #2) j = 7; - j = 8; // CHECK-NEXT: File 0, [[@LINE]]:5 -> [[@LINE]]:10 = ((#1 - #3) - #4) + j = 8; // CHECK-NEXT: File 0, [[@LINE]]:5 -> [[@LINE+1]]:4 = ((#1 - #3) - #4) } } diff --git a/clang/test/CoverageMapping/header.cpp b/clang/test/CoverageMapping/header.cpp index 0cff9d8e0b6..e495d5a6285 100644 --- a/clang/test/CoverageMapping/header.cpp +++ b/clang/test/CoverageMapping/header.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -fprofile-instr-generate -fcoverage-mapping -dump-coverage-mapping -emit-llvm-only -main-file-name tu1.cpp %s > %tmapping +// RUN: %clang_cc1 -fprofile-instr-generate -fcoverage-mapping -dump-coverage-mapping -emit-llvm-only -main-file-name header.cpp %s > %tmapping // RUN: FileCheck -input-file %tmapping %s --check-prefix=CHECK-FUNC // RUN: FileCheck -input-file %tmapping %s --check-prefix=CHECK-STATIC-FUNC // RUN: FileCheck -input-file %tmapping %s --check-prefix=CHECK-STATIC-FUNC2 @@ -14,14 +14,11 @@ int main() { // CHECK-FUNC: File 0, 4:25 -> 11:2 = #0 // CHECK-FUNC: File 0, 6:15 -> 8:4 = #1 // CHECK-FUNC: File 0, 8:10 -> 10:4 = (#0 - #1) -// CHECK-FUNC: Expansion,File 1, 6:10 -> 6:28 = #0 (Expanded file = 0) // CHECK-STATIC-FUNC: static_func // CHECK-STATIC-FUNC: File 0, 12:32 -> 20:2 = #0 // CHECK-STATIC-FUNC: File 0, 14:15 -> 16:4 = #1 // CHECK-STATIC-FUNC: File 0, 16:10 -> 18:4 = (#0 - #1) -// CHECK-STATIC-FUNC: Expansion,File 1, 6:10 -> 6:28 = #0 (Expanded file = 0) // CHECK-STATIC-FUNC2: static_func2 // CHECK-STATIC-FUNC2: File 0, 21:33 -> 29:2 = 0 -// CHECK-STATIC-FUNC2: Expansion,File 1, 6:10 -> 6:28 = 0 (Expanded file = 0) diff --git a/clang/test/CoverageMapping/includehell.cpp b/clang/test/CoverageMapping/includehell.cpp index dcc2eee58d6..830bc4f4506 100644 --- a/clang/test/CoverageMapping/includehell.cpp +++ b/clang/test/CoverageMapping/includehell.cpp @@ -1,12 +1,72 @@ -// RUN: %clang_cc1 -fprofile-instr-generate -fcoverage-mapping -dump-coverage-mapping -emit-llvm-only -main-file-name includehell.cpp %s | FileCheck %s +// RUN: %clang_cc1 -fprofile-instr-generate -fcoverage-mapping -dump-coverage-mapping -emit-llvm-only -main-file-name includehell.cpp %s > %tmapping -int main() { // CHECK: File 0, [[@LINE]]:12 -> [[@LINE+4]]:2 = #0 +int main() { int x = 0; - #include "Inputs/code.h" // CHECK-NEXT: Expansion,File 0, [[@LINE]]:12 -> [[@LINE]]:27 = #0 (Expanded file = 1) + + #include "Inputs/starts_a_scope" + x = x; + #include "Inputs/code.h" + x = x; + #include "Inputs/ends_a_scope" + + #include "Inputs/starts_a_scope" + #include "Inputs/code.h" + #include "Inputs/ends_a_scope" + + #include "Inputs/starts_a_scope" + #include "Inputs/ends_a_scope" + return 0; } -// CHECK-NEXT: File 1, 1:1 -> 9:7 = #0 -// CHECK-NEXT: File 1, 2:13 -> 4:2 = #1 -// CHECK-NEXT: File 1, 4:8 -> 6:2 = (#0 - #1) -// CHECK-NEXT: File 1, 7:11 -> 9:2 = #2 -// CHECK-NEXT: File 1, 9:8 -> 11:2 = (#0 - #2) + +// RUN: FileCheck -input-file %tmapping %s --check-prefix=CHECK-MAIN +// RUN: FileCheck -input-file %tmapping %s --check-prefix=CHECK-START +// RUN: FileCheck -input-file %tmapping %s --check-prefix=CHECK-CODE +// RUN: FileCheck -input-file %tmapping %s --check-prefix=CHECK-END + +// CHECK-MAIN: File [[MAIN:[0-9]]], 3:12 -> 20:2 = #0 +// CHECK-MAIN-NEXT: Expansion,File [[MAIN]], 6:12 -> 6:35 = #0 +// CHECK-MAIN-NEXT: File [[MAIN]], 6:35 -> 10:33 = #1 +// CHECK-MAIN-NEXT: Expansion,File [[MAIN]], 8:14 -> 8:29 = #1 +// CHECK-MAIN-NEXT: Expansion,File [[MAIN]], 10:12 -> 10:33 = #1 +// CHECK-MAIN-NEXT: Expansion,File [[MAIN]], 12:12 -> 12:35 = #0 +// CHECK-MAIN-NEXT: File [[MAIN]], 12:35 -> 14:33 = #5 +// CHECK-MAIN-NEXT: Expansion,File [[MAIN]], 13:14 -> 13:29 = #5 +// CHECK-MAIN-NEXT: Expansion,File [[MAIN]], 14:12 -> 14:33 = #5 +// CHECK-MAIN-NEXT: Expansion,File [[MAIN]], 16:12 -> 16:35 = #0 +// CHECK-MAIN-NEXT: File [[MAIN]], 16:35 -> 17:33 = #9 +// CHECK-MAIN-NEXT: Expansion,File [[MAIN]], 17:12 -> 17:33 = #9 + +// CHECK-START: File [[START1:[0-9]]], 1:1 -> 5:1 = #0 +// CHECK-START-NEXT: File [[START1]], 4:17 -> 4:22 = (#0 + #1) +// CHECK-START-NEXT: File [[START1]], 4:24 -> 4:27 = #1 +// CHECK-START-NEXT: File [[START1]], 4:29 -> 5:1 = #1 +// CHECK-START: File [[START2:[0-9]]], 1:1 -> 5:1 = #0 +// CHECK-START-NEXT: File [[START2]], 4:17 -> 4:22 = (#0 + #5) +// CHECK-START-NEXT: File [[START2]], 4:24 -> 4:27 = #5 +// CHECK-START-NEXT: File [[START2]], 4:29 -> 5:1 = #5 +// CHECK-START: File [[START3:[0-9]]], 1:1 -> 5:1 = #0 +// CHECK-START-NEXT: File [[START3]], 4:17 -> 4:22 = (#0 + #9) +// CHECK-START-NEXT: File [[START3]], 4:24 -> 4:27 = #9 +// CHECK-START-NEXT: File [[START3]], 4:29 -> 5:1 = #9 + +// CHECK-CODE: File [[CODE1:[0-9]]], 1:1 -> 14:1 = #1 +// CHECK-CODE-NEXT: File [[CODE1]], 4:13 -> 6:2 = #2 +// CHECK-CODE-NEXT: File [[CODE1]], 6:8 -> 8:2 = (#1 - #2) +// CHECK-CODE-NEXT: File [[CODE1]], 9:11 -> 11:2 = #3 +// CHECK-CODE-NEXT: File [[CODE1]], 11:8 -> 13:2 = (#1 - #3) +// CHECK-CODE: File [[CODE2:[0-9]]], 1:1 -> 14:1 = #5 +// CHECK-CODE-NEXT: File [[CODE2]], 4:13 -> 6:2 = #6 +// CHECK-CODE-NEXT: File [[CODE2]], 6:8 -> 8:2 = (#5 - #6) +// CHECK-CODE-NEXT: File [[CODE2]], 9:11 -> 11:2 = #7 +// CHECK-CODE-NEXT: File [[CODE2]], 11:8 -> 13:2 = (#5 - #7) + +// CHECK-END: File [[END1:[0-9]]], 1:1 -> 3:2 = #1 +// CHECK-END-NEXT: File [[END1]], 1:1 -> 6:1 = #0 +// CHECK-END-NEXT: File [[END1]], 5:11 -> 5:16 = #4 +// CHECK-END: File [[END2:[0-9]]], 1:1 -> 3:2 = #5 +// CHECK-END-NEXT: File [[END2]], 1:1 -> 6:1 = #0 +// CHECK-END-NEXT: File [[END2]], 5:11 -> 5:16 = #8 +// CHECK-END: File [[END3:[0-9]]], 1:1 -> 3:2 = #9 +// CHECK-END-NEXT: File [[END3]], 1:1 -> 6:1 = #0 +// CHECK-END-NEXT: File [[END3]], 5:11 -> 5:16 = #10 diff --git a/clang/test/CoverageMapping/label.cpp b/clang/test/CoverageMapping/label.cpp index 207d18e3f38..2fa58b68c37 100644 --- a/clang/test/CoverageMapping/label.cpp +++ b/clang/test/CoverageMapping/label.cpp @@ -7,15 +7,15 @@ void func() { // CHECK-NEXT: File 0, [[@LINE]]:13 -> [[@LINE+18]] for(i = 0; i < 10; ++i) { // CHECK-NEXT: File 0, [[@LINE]]:27 -> [[@LINE+10]]:4 = #1 if(i < 5) { // CHECK-NEXT: File 0, [[@LINE]]:15 -> [[@LINE+6]]:6 = #2 { - x: // CHECK-NEXT: File 0, [[@LINE]]:9 -> [[@LINE+6]]:14 = #3 + x: // CHECK-NEXT: File 0, [[@LINE]]:9 -> [[@LINE+4]]:6 = #3 int j = 1; } int m = 2; } else goto x; // CHECK-NEXT: File 0, [[@LINE]]:7 -> [[@LINE]]:13 = (#1 - #2) - int k = 3; + int k = 3; // CHECK-NEXT: File 0, [[@LINE]]:5 -> [[@LINE+1]]:4 = #3 } - static int j = 0; // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+2]]:12 = ((#0 + #3) - #1) + static int j = 0; // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+4]]:2 = ((#0 + #3) - #1) ++j; if(j == 1) goto x; // CHECK-NEXT: File 0, [[@LINE]]:5 -> [[@LINE]]:11 = #4 @@ -26,8 +26,8 @@ void test1(int x) { // CHECK-NEXT: File 0, [[@LINE]]:19 -> [[@LINE+7]]: if(x == 0) goto a; // CHECK-NEXT: File 0, [[@LINE]]:5 -> [[@LINE]]:11 = #1 goto b; // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE]]:9 = (#0 - #1) -a: // CHECK-NEXT: File 0, [[@LINE]]:1 -> [[@LINE]]:2 = #2 -b: // CHECK-NEXT: File 0, [[@LINE]]:1 -> [[@LINE+1]]:12 = #3 +a: // CHECK-NEXT: File 0, [[@LINE]]:1 -> [[@LINE+3]]:2 = #2 +b: // CHECK-NEXT: File 0, [[@LINE]]:1 -> [[@LINE+2]]:2 = #3 x = x + 1; } @@ -35,10 +35,10 @@ b: // CHECK-NEXT: File 0, [[@LINE]]:1 -> [[@LINE+1]]:1 void test2(int x) { // CHECK-NEXT: File 0, [[@LINE]]:19 -> [[@LINE+8]]:2 = #0 if(x == 0) goto a; // CHECK-NEXT: File 0, [[@LINE]]:5 -> [[@LINE]]:11 = #1 - // CHECK-NEXT: File 0, [[@LINE+1]]:8 -> [[@LINE+1]]:17 = (#0 - #1) + // CHECK-NEXT: File 0, [[@LINE+1]]:8 -> [[@LINE+1]]:25 = (#0 - #1) else if(x == 1) goto b; // CHECK-NEXT: File 0, [[@LINE]]:19 -> [[@LINE]]:25 = #2 -a: // CHECK-NEXT: File 0, [[@LINE]]:1 -> [[@LINE]]:2 = #3 -b: // CHECK-NEXT: File 0, [[@LINE]]:1 -> [[@LINE+1]]:12 = #4 +a: // CHECK-NEXT: File 0, [[@LINE]]:1 -> [[@LINE+3]]:2 = #3 +b: // CHECK-NEXT: File 0, [[@LINE]]:1 -> [[@LINE+2]]:2 = #4 x = x + 1; } @@ -46,18 +46,18 @@ b: // CHECK-NEXT: File 0, [[@LINE]]:1 -> [[@LINE+1]]:1 int main() { // CHECK-NEXT: File 0, [[@LINE]]:12 -> [[@LINE+17]]:2 = #0 int j = 0; for(int i = 0; i < 10; ++i) { // CHECK: File 0, [[@LINE]]:31 -> [[@LINE+11]]:4 = #1 - a: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+1]]:13 = #2 + a: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+10]]:4 = #2 if(i < 3) goto e; // CHECK-NEXT: File 0, [[@LINE]]:7 -> [[@LINE]]:13 = #3 goto c; // CHECK-NEXT: File 0, [[@LINE]]:5 -> [[@LINE]]:11 = (#2 - #3) - b: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+1]]:10 = #4 + b: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+6]]:4 = #4 j = 2; - c: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+1]]:10 = #5 + c: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+4]]:4 = #5 j = 1; - // CHECK-NEXT: File 0, [[@LINE+1]]:3 -> [[@LINE+1]]:4 = #6 - e: f: ; // CHECK-NEXT: File 0, [[@LINE]]:6 -> [[@LINE]]:10 = #7 + // CHECK-NEXT: File 0, [[@LINE+1]]:3 -> [[@LINE+2]]:4 = #6 + e: f: ; // CHECK-NEXT: File 0, [[@LINE]]:6 -> [[@LINE+1]]:4 = #7 } - func(); // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+2]]:11 = ((#0 + #7) - #1) + func(); // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+3]]:2 = ((#0 + #7) - #1) test1(0); test2(2); } diff --git a/clang/test/CoverageMapping/loopmacro.c b/clang/test/CoverageMapping/loopmacro.c index 77aab1da72c..bbd0c45c11f 100644 --- a/clang/test/CoverageMapping/loopmacro.c +++ b/clang/test/CoverageMapping/loopmacro.c @@ -1,40 +1,47 @@ // RUN: %clang_cc1 -fprofile-instr-generate -fcoverage-mapping -dump-coverage-mapping -emit-llvm-only -main-file-name loopmacro.c %s | FileCheck %s -# define HASH_BITS 15 -#define MIN_MATCH 3 -#define H_SHIFT ((HASH_BITS+MIN_MATCH-1)/MIN_MATCH) -#define WMASK 0xFFFF -#define HASH_MASK 0xFFFF -#define UPDATE_HASH(h,c) (h = (((h)<<H_SHIFT) ^ (c)) & HASH_MASK) +// CHECK: main +// CHECK-NEXT: File 0, {{[0-9]+}}:12 -> {{[0-9]+}}:2 = #0 +// CHECK-NEXT: File 0, {{[0-9]+}}:6 -> {{[0-9]+}}:4 = (#0 + #1) +// CHECK-NEXT: Expansion,File 0, {{[0-9]+}}:7 -> {{[0-9]+}}:20 = (#0 + #1) +// CHECK-NEXT: File 0, {{[0-9]+}}:12 -> {{[0-9]+}}:30 = (#0 + #1) + +// CHECK-NEXT: File 1, [[@LINE+4]]:4 -> [[@LINE+6]]:23 = (#0 + #1) +// CHECK-NEXT: Expansion,File 1, [[@LINE+3]]:5 -> [[@LINE+3]]:16 = (#0 + #1) +// CHECK-NEXT: Expansion,File 1, [[@LINE+3]]:16 -> [[@LINE+3]]:21 = (#0 + #1) #define INSERT_STRING(s, match_head) \ (UPDATE_HASH(ins_h, window[(s) + MIN_MATCH-1]), \ prev[(s) & WMASK] = match_head = head[ins_h], \ head[ins_h] = (s)) +// CHECK-NEXT: File 2, [[@LINE+3]]:26 -> [[@LINE+3]]:66 = (#0 + #1) +// CHECK-NEXT: Expansion,File 2, [[@LINE+2]]:38 -> [[@LINE+2]]:45 = (#0 + #1) +// CHECK-NEXT: Expansion,File 2, [[@LINE+1]]:56 -> [[@LINE+1]]:65 = (#0 + #1) +#define UPDATE_HASH(h,c) (h = (((h)<<H_SHIFT) ^ (c)) & HASH_MASK) +// CHECK-NEXT: File 3, [[@LINE+1]]:15 -> [[@LINE+1]]:21 = (#0 + #1) +#define WMASK 0xFFFF +// CHECK-NEXT: File 4, [[@LINE+4]]:18 -> [[@LINE+4]]:53 = (#0 + #1) +// CHECK-NEXT: Expansion,File 4, [[@LINE+3]]:20 -> [[@LINE+3]]:29 = (#0 + #1) +// CHECK-NEXT: Expansion,File 4, [[@LINE+2]]:30 -> [[@LINE+2]]:39 = (#0 + #1) +// CHECK-NEXT: Expansion,File 4, [[@LINE+1]]:43 -> [[@LINE+1]]:52 = (#0 + #1) +#define H_SHIFT ((HASH_BITS+MIN_MATCH-1)/MIN_MATCH) +// CHECK-NEXT: File 5, [[@LINE+1]]:19 -> [[@LINE+1]]:25 = (#0 + #1) +#define HASH_MASK 0xFFFF +// CHECK-NEXT: File 6, [[@LINE+1]]:20 -> [[@LINE+1]]:22 = (#0 + #1) +#define HASH_BITS 15 +// CHECK-NEXT: File 7, [[@LINE+2]]:20 -> [[@LINE+2]]:21 = (#0 + #1) +// CHECK-NEXT: File 8, [[@LINE+1]]:20 -> [[@LINE+1]]:21 = (#0 + #1) +#define MIN_MATCH 3 -int main() { // CHECK: File 0, [[@LINE]]:12 -> [[@LINE+12]]:2 = #0 +int main() { int strstart = 0; int hash_head = 2; int prev_length = 5; int ins_h = 1; - int prev[32] = { 0 }; - int head[32] = { 0 }; + int prev[32<<10] = { 0 }; + int head[32<<10] = { 0 }; int window[1024] = { 0 }; - do { // CHECK-NEXT: File 0, [[@LINE]]:6 -> [[@LINE+3]]:30 = (#0 + #1) + do { strstart++; - INSERT_STRING(strstart, hash_head); // CHECK-NEXT: Expansion,File 0, [[@LINE]]:7 -> [[@LINE]]:20 = (#0 + #1) (Expanded file = 1) + INSERT_STRING(strstart, hash_head); } while (--prev_length != 0); } -// CHECK-NEXT: File 0, 24:21 -> 24:29 = (#0 + #1) -// CHECK-NEXT: File 0, 24:21 -> 24:29 = (#0 + #1) -// CHECK-NEXT: File 0, 24:21 -> 24:29 = (#0 + #1) -// CHECK-NEXT: File 0, 24:31 -> 24:40 = (#0 + #1) -// CHECK-NEXT: File 1, 10:4 -> 12:23 = (#0 + #1) -// CHECK-NEXT: Expansion,File 1, 10:5 -> 10:16 = (#0 + #1) (Expanded file = 2) -// CHECK-NEXT: File 1, 10:17 -> 10:22 = (#0 + #1) -// CHECK-NEXT: File 1, 10:17 -> 10:22 = (#0 + #1) -// CHECK-NEXT: File 1, 10:24 -> 10:32 = (#0 + #1) -// CHECK-NEXT: File 1, 10:33 -> 10:36 = (#0 + #1) -// CHECK-NEXT: File 1, 10:46 -> 10:49 = (#0 + #1) -// CHECK-NEXT: File 2, 8:26 -> 8:66 = (#0 + #1) -// CHECK-NEXT: Expansion,File 2, 8:38 -> 8:45 = (#0 + #1) (Expanded file = 3) -// CHECK-NEXT: File 3, 5:18 -> 5:53 = (#0 + #1) diff --git a/clang/test/CoverageMapping/loops.cpp b/clang/test/CoverageMapping/loops.cpp index af0381e9f28..d619879f37f 100644 --- a/clang/test/CoverageMapping/loops.cpp +++ b/clang/test/CoverageMapping/loops.cpp @@ -22,16 +22,16 @@ int main() { // CHECK-NEXT: File 0, [[@LINE]]:12 -> [[@LI } int j = 0; // CHECK-NEXT: File 0, [[@LINE+1]]:9 -> [[@LINE+1]]:14 = (#0 + #3) while(j < 5) ++j; // CHECK-NEXT: File 0, [[@LINE]]:16 -> [[@LINE]]:19 = #3 - do { // CHECK-NEXT: File 0, [[@LINE]]:6 -> [[@LINE+2]]:17 = (#0 + #4) + do { // CHECK-NEXT: File 0, [[@LINE]]:6 -> [[@LINE+2]]:4 = (#0 + #4) ++j; - } while(j < 10); + } while(j < 10); // CHECK-NEXT: File 0, [[@LINE]]:11 -> [[@LINE]]:17 = (#0 + #4) j = 0; while (j < 5) // CHECK-NEXT: File 0, [[@LINE]]:5 -> [[@LINE]]:10 = (#0 + #5) ++j; // CHECK-NEXT: File 0, [[@LINE]]:6 -> [[@LINE]]:9 = #5 do - ++j; // CHECK-NEXT: File 0, [[@LINE]]:5 -> [[@LINE+1]]:15 = (#0 + #6) - while(j < 10); + ++j; // CHECK-NEXT: File 0, [[@LINE]]:5 -> [[@LINE]]:8 = (#0 + #6) + while(j < 10); // CHECK-NEXT: File 0, [[@LINE]]:9 -> [[@LINE]]:15 = (#0 + #6) rangedFor(); return 0; } diff --git a/clang/test/CoverageMapping/macro-expansion.c b/clang/test/CoverageMapping/macro-expansion.c new file mode 100644 index 00000000000..099dd3fb3d0 --- /dev/null +++ b/clang/test/CoverageMapping/macro-expansion.c @@ -0,0 +1,54 @@ +// RUN: %clang_cc1 -fprofile-instr-generate -fcoverage-mapping -dump-coverage-mapping -emit-llvm-only -main-file-name macro-expansion.c %s | FileCheck %s + +// CHECK: func +// CHECK: File 1, [[@LINE+4]]:12 -> [[@LINE+4]]:38 = #0 +// CHECK-NEXT: File 1, [[@LINE+3]]:15 -> [[@LINE+3]]:28 = (#0 + #2) +// CHECK-NEXT: File 1, [[@LINE+2]]:24 -> [[@LINE+2]]:26 = #3 +// CHECK-NEXT: File 1, [[@LINE+1]]:36 -> [[@LINE+1]]:37 = (#0 + #2) +#define M1 do { if (0) {} } while (0) +// CHECK-NEXT: File 2, [[@LINE+8]]:15 -> [[@LINE+8]]:41 = #0 +// CHECK-NEXT: File 2, [[@LINE+7]]:18 -> [[@LINE+7]]:31 = (#0 + #4) +// CHECK-NEXT: File 2, [[@LINE+6]]:27 -> [[@LINE+6]]:29 = #5 +// CHECK-NEXT: File 2, [[@LINE+5]]:39 -> [[@LINE+5]]:40 = (#0 + #4) +// CHECK-NEXT: File 3, [[@LINE+4]]:15 -> [[@LINE+4]]:41 = #0 +// CHECK-NEXT: File 3, [[@LINE+3]]:18 -> [[@LINE+3]]:31 = (#0 + #6) +// CHECK-NEXT: File 3, [[@LINE+2]]:27 -> [[@LINE+2]]:29 = #7 +// CHECK-NEXT: File 3, [[@LINE+1]]:39 -> [[@LINE+1]]:40 = (#0 + #6) +#define M2(x) do { if (x) {} } while (0) +// CHECK-NEXT: File 4, [[@LINE+4]]:15 -> [[@LINE+4]]:38 = #0 +// CHECK-NEXT: File 4, [[@LINE+3]]:18 -> [[@LINE+3]]:28 = (#0 + #8) +// CHECK-NEXT: Expansion,File 4, [[@LINE+2]]:20 -> [[@LINE+2]]:22 = (#0 + #8) +// CHECK-NEXT: File 4, [[@LINE+1]]:36 -> [[@LINE+1]]:37 = (#0 + #8) +#define M3(x) do { M2(x); } while (0) +// CHECK-NEXT: File 5, [[@LINE+2]]:15 -> [[@LINE+2]]:27 = #0 +// CHECK-NEXT: File 5, [[@LINE+1]]:23 -> [[@LINE+1]]:26 = #12 +#define M4(x) ((x) && (x)) +// CHECK-NEXT: File 6, [[@LINE+2]]:15 -> [[@LINE+2]]:27 = #0 +// CHECK-NEXT: File 6, [[@LINE+1]]:23 -> [[@LINE+1]]:26 = #14 +#define M5(x) ((x) || (x)) +// CHECK-NEXT: File 7, [[@LINE+1]]:15 -> [[@LINE+1]]:26 = #0 +#define M6(x) ((x) + (x)) +// CHECK-NEXT: File 8, [[@LINE+1]]:15 -> [[@LINE+1]]:18 = #0 +#define M7(x) (x) + +// Check for the expansion of M2 within M3. +// CHECK-NEXT: File 9, {{[0-9]+}}:15 -> {{[0-9]+}}:41 = (#0 + #8) +// CHECK-NEXT: File 9, {{[0-9]+}}:18 -> {{[0-9]+}}:31 = ((#0 + #8) + #9) +// CHECK-NEXT: File 9, {{[0-9]+}}:27 -> {{[0-9]+}}:29 = #10 +// CHECK-NEXT: File 9, {{[0-9]+}}:39 -> {{[0-9]+}}:40 = ((#0 + #8) + #9) + +void func(int x) { + if (x) {} + M1; + M2(!x); + M2(x); + M3(x); + if (M4(x)) {} + if (M5(x)) {} + if (M6(x)) {} + if (M7(x)) {} +} + +int main(int argc, const char *argv[]) { + func(0); +} diff --git a/clang/test/CoverageMapping/macro-expressions.cpp b/clang/test/CoverageMapping/macro-expressions.cpp new file mode 100644 index 00000000000..b9782b0c127 --- /dev/null +++ b/clang/test/CoverageMapping/macro-expressions.cpp @@ -0,0 +1,75 @@ +// RUN: %clang_cc1 -std=c++11 -fprofile-instr-generate -fcoverage-mapping -dump-coverage-mapping -emit-llvm-only -main-file-name macro-expressions.cpp %s | FileCheck %s + +#define EXPR(x) (x) +#define NEXPR(x) (!x) +#define DECL(T, x) T x +#define ASSIGN(x, y) x = y +#define LT(x, y) x < y +#define INC(x) ++x +#define ARR(T, x, y, z) (T[3]){x, y, z} + +#define PRI_64_LENGTH_MODIFIER "ll" +#define PRIo64 PRI_64_LENGTH_MODIFIER "o" +#define PRIu64 PRI_64_LENGTH_MODIFIER "u" + +// CHECK: foo +// CHECK-NEXT: File 0, [[@LINE+1]]:17 -> {{[0-9]+}}:2 = #0 +void foo(int i) { + // CHECK-NEXT: File 0, [[@LINE+1]]:10 -> [[@LINE+1]]:12 = #1 + if (0) {} + + // CHECK-NEXT: Expansion,File 0, [[@LINE+2]]:7 -> [[@LINE+2]]:11 = #0 + // CHECK-NEXT: File 0, [[@LINE+1]]:16 -> [[@LINE+1]]:18 = #2 + if (EXPR(i)) {} + // CHECK-NEXT: Expansion,File 0, [[@LINE+2]]:9 -> [[@LINE+2]]:14 = (#0 + #3) + // CHECK-NEXT: File 0, [[@LINE+1]]:20 -> [[@LINE+1]]:22 = #3 + for (;NEXPR(i);) {} + // CHECK-NEXT: Expansion,File 0, [[@LINE+4]]:8 -> [[@LINE+4]]:14 = #0 + // CHECK-NEXT: Expansion,File 0, [[@LINE+3]]:33 -> [[@LINE+3]]:35 = (#0 + #4) + // CHECK-NEXT: Expansion,File 0, [[@LINE+2]]:43 -> [[@LINE+2]]:46 = #4 + // CHECK-NEXT: File 0, [[@LINE+1]]:51 -> [[@LINE+1]]:53 = #4 + for (ASSIGN(DECL(int, j), 0); LT(j, i); INC(j)) {} + // CHECK-NEXT: Expansion,File 0, [[@LINE+1]]:3 -> [[@LINE+1]]:9 = #0 + ASSIGN(DECL(int, k), 0); + // CHECK-NEXT: Expansion,File 0, [[@LINE+3]]:10 -> [[@LINE+3]]:12 = (#0 + #5) + // CHECK-NEXT: File 0, [[@LINE+2]]:20 -> [[@LINE+2]]:31 = #5 + // CHECK-NEXT: Expansion,File 0, [[@LINE+1]]:22 -> [[@LINE+1]]:25 = #5 + while (LT(k, i)) { INC(k); } + // CHECK-NEXT: File 0, [[@LINE+2]]:6 -> [[@LINE+2]]:8 = (#0 + #6) + // CHECK-NEXT: Expansion,File 0, [[@LINE+1]]:16 -> [[@LINE+1]]:21 = (#0 + #6) + do {} while (NEXPR(i)); + // CHECK-NEXT: Expansion,File 0, [[@LINE+3]]:8 -> [[@LINE+3]]:12 = #0 + // CHECK-NEXT: Expansion,File 0, [[@LINE+2]]:23 -> [[@LINE+2]]:26 = #0 + // CHECK-NEXT: File 0, [[@LINE+1]]:42 -> [[@LINE+1]]:44 = #7 + for (DECL(int, j) : ARR(int, 1, 2, 3)) {} + + // CHECK-NEXT: Expansion,File 0, [[@LINE+2]]:14 -> [[@LINE+2]]:20 = #8 + // CHECK-NEXT: Expansion,File 0, [[@LINE+1]]:23 -> [[@LINE+1]]:29 = #0 + (void)(i ? PRIo64 : PRIu64); +} + +// CHECK-NEXT: File {{[0-9]+}}, 3:17 -> 3:20 = #0 +// CHECK-NEXT: File {{[0-9]+}}, 4:18 -> 4:22 = (#0 + #3) +// CHECK-NEXT: File {{[0-9]+}}, 6:22 -> 6:27 = #0 +// CHECK-NEXT: File {{[0-9]+}}, 8:16 -> 8:19 = #4 +// CHECK-NEXT: File {{[0-9]+}}, 7:18 -> 7:23 = (#0 + #4) +// CHECK-NEXT: File {{[0-9]+}}, 6:22 -> 6:27 = #0 +// CHECK-NEXT: File {{[0-9]+}}, 8:16 -> 8:19 = #5 +// CHECK-NEXT: File {{[0-9]+}}, 7:18 -> 7:23 = (#0 + #5) +// CHECK-NEXT: File {{[0-9]+}}, 4:18 -> 4:22 = (#0 + #6) +// CHECK-NEXT: File {{[0-9]+}}, 5:20 -> 5:23 = #0 +// CHECK-NEXT: File {{[0-9]+}}, 9:25 -> 9:40 = #0 +// CHECK-NEXT: Expansion,File {{[0-9]+}}, 12:16 -> 12:38 = #8 +// CHECK-NEXT: File {{[0-9]+}}, 12:38 -> 12:42 = #8 +// CHECK-NEXT: File {{[0-9]+}}, 13:16 -> 13:42 = #0 +// CHECK-NEXT: Expansion,File {{[0-9]+}}, 13:16 -> 13:38 = (#0 - #8) +// CHECK-NEXT: File {{[0-9]+}}, 13:38 -> 13:42 = (#0 - #8) +// CHECK-NEXT: File {{[0-9]+}}, 11:32 -> 11:36 = #8 +// CHECK-NEXT: File {{[0-9]+}}, 11:32 -> 11:36 = (#0 - #8) + +// CHECK-NOT: File {{[0-9]+}}, +// CHECK: main + +int main(int argc, const char *argv[]) { + foo(10); +} diff --git a/clang/test/CoverageMapping/macroception.c b/clang/test/CoverageMapping/macroception.c index 186f063f9b8..bde38ff7732 100644 --- a/clang/test/CoverageMapping/macroception.c +++ b/clang/test/CoverageMapping/macroception.c @@ -6,35 +6,47 @@ #define M11 M22 // CHECK-LABEL: main: -int main() M1 // CHECK-NEXT: Expansion,File 0, [[@LINE]]:12 -> [[@LINE]]:14 = #0 (Expanded file = 2) - return 0; // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+1]]:2 = #0 +// CHECK-NEXT: Expansion,File 0, [[@LINE+2]]:12 -> [[@LINE+2]]:14 = #0 +// CHECK-NEXT: File 0, [[@LINE+1]]:14 -> [[@LINE+3]]:2 = #0 +int main() M1 + return 0; } -// CHECK-NEXT: File 1, 3:12 -> 3:13 = #0 -// CHECK-NEXT: Expansion,File 2, 4:12 -> 4:14 = #0 (Expanded file = 1) - +// CHECK-NEXT: File 1, 4:12 -> 4:14 = #0 +// CHECK-NEXT: Expansion,File 1, 4:12 -> 4:14 = #0 +// CHECK-NEXT: File 2, 3:12 -> 3:13 = #0 // CHECK-LABEL: func2: -void func2() { // CHECK-NEXT: File 0, [[@LINE]]:14 -> [[@LINE+1]]:12 = #0 +// CHECK-NEXT: File 0, [[@LINE+2]]:14 -> [[@LINE+4]]:4 = #0 +// CHECK-NEXT: Expansion,File 0, [[@LINE+3]]:1 -> [[@LINE+3]]:4 = #0 +void func2() { int x = 0; -M11 // CHECK-NEXT: Expansion,File 0, [[@LINE]]:1 -> [[@LINE]]:4 = #0 (Expanded file = 2) -// CHECK-NEXT: File 1, 5:13 -> 5:14 = #0 -// CHECK-NEXT: Expansion,File 2, 6:13 -> 6:16 = #0 (Expanded file = 1) +M11 +// CHECK-NEXT: File 1, 6:13 -> 6:16 = #0 +// CHECK-NEXT: Expansion,File 1, 6:13 -> 6:16 = #0 +// CHECK-NEXT: File 2, 5:13 -> 5:14 = #0 // CHECK-LABEL: func3: -void func3() M1 // CHECK-NEXT: Expansion,File 0, [[@LINE]]:14 -> [[@LINE]]:16 = #0 (Expanded file = 2) - int x = 0; // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE]]:12 = #0 -M11 // CHECK-NEXT: Expansion,File 0, [[@LINE]]:1 -> [[@LINE]]:4 = #0 (Expanded file = 4) - -// CHECK-NEXT: File 1, 3:12 -> 3:13 = #0 -// CHECK-NEXT: Expansion,File 2, 4:12 -> 4:14 = #0 (Expanded file = 1) -// CHECK-NEXT: File 3, 5:13 -> 5:14 = #0 -// CHECK-NEXT: Expansion,File 4, 6:13 -> 6:16 = #0 (Expanded file = 3) +// CHECK-NEXT: Expansion,File 0, [[@LINE+3]]:14 -> [[@LINE+3]]:16 = #0 +// CHECK-NEXT: File 0, [[@LINE+2]]:16 -> [[@LINE+4]]:4 = #0 +// CHECK-NEXT: Expansion,File 0, [[@LINE+3]]:1 -> [[@LINE+3]]:4 = #0 +void func3() M1 + int x = 0; +M11 +// CHECK-NEXT: File 1, 4:12 -> 4:14 = #0 +// CHECK-NEXT: Expansion,File 1, 4:12 -> 4:14 = #0 +// CHECK-NEXT: File 2, 6:13 -> 6:16 = #0 +// CHECK-NEXT: Expansion,File 2, 6:13 -> 6:16 = #0 +// CHECK-NEXT: File 3, 3:12 -> 3:13 = #0 +// CHECK-NEXT: File 4, 5:13 -> 5:14 = #0 // CHECK-LABEL: func4: -// CHECK-NEXT: File 0, 3:12 -> 3:13 = #0 -// CHECK-NEXT: Expansion,File 1, 4:12 -> 4:14 = #0 (Expanded file = 0) +// CHECK-NEXT: Expansion,File 0, [[@LINE+3]]:14 -> [[@LINE+3]]:16 = #0 +// CHECK-NEXT: File 0, [[@LINE+2]]:16 -> [[@LINE+2]]:20 = #0 +// CHECK-NEXT: Expansion,File 0, [[@LINE+1]]:17 -> [[@LINE+1]]:20 = #0 void func4() M1 M11 -// CHECK-NEXT: Expansion,File 2, [[@LINE-1]]:14 -> [[@LINE-1]]:16 = #0 (Expanded file = 1) -// CHECK-NEXT: Expansion,File 2, [[@LINE-2]]:17 -> [[@LINE-2]]:20 = #0 (Expanded file = 4) -// CHECK-NEXT: File 3, 5:13 -> 5:14 = #0 -// CHECK-NEXT: Expansion,File 4, 6:13 -> 6:16 = #0 (Expanded file = 3) +// CHECK-NEXT: File 1, 4:12 -> 4:14 = #0 +// CHECK-NEXT: Expansion,File 1, 4:12 -> 4:14 = #0 +// CHECK-NEXT: File 2, 6:13 -> 6:16 = #0 +// CHECK-NEXT: Expansion,File 2, 6:13 -> 6:16 = #0 +// CHECK-NEXT: File 3, 3:12 -> 3:13 = #0 +// CHECK-NEXT: File 4, 5:13 -> 5:14 = #0 diff --git a/clang/test/CoverageMapping/macroparams.c b/clang/test/CoverageMapping/macroparams.c index 6df7550ea25..d2c8e55e6a7 100644 --- a/clang/test/CoverageMapping/macroparams.c +++ b/clang/test/CoverageMapping/macroparams.c @@ -1,12 +1,17 @@ // RUN: %clang_cc1 -fprofile-instr-generate -fcoverage-mapping -dump-coverage-mapping -emit-llvm-only -main-file-name macroparams.c %s | FileCheck %s -#define MACRO2(X2) (X2 + 2) // CHECK-DAG: File 2, [[@LINE]]:20 -> [[@LINE]]:28 = #0 -#define MACRO(X) MACRO2(x) // CHECK-DAG: File 1, [[@LINE]]:25 -> [[@LINE]]:26 = #0 - // CHECK-DAG: Expansion,File 1, [[@LINE-1]]:18 -> [[@LINE-1]]:24 = #0 (Expanded file = 2) +// CHECK: main +// CHECK-NEXT: File 0, {{[0-9]+}}:12 -> {{[0-9]+}}:2 = #0 +// CHECK-NEXT: Expansion,File 0, {{[0-9]+}}:3 -> {{[0-9]+}}:8 = #0 +// CHECK-NEXT: File 1, [[@LINE+2]]:18 -> [[@LINE+2]]:27 = #0 +// CHECK-NEXT: Expansion,File 1, [[@LINE+1]]:18 -> [[@LINE+1]]:24 = #0 +#define MACRO(X) MACRO2(x) +// CHECK-NEXT: File 2, [[@LINE+1]]:20 -> [[@LINE+1]]:28 = #0 +#define MACRO2(X2) (X2 + 2) -int main() { // CHECK-DAG: File 0, [[@LINE]]:12 -> [[@LINE+4]]:2 = #0 +int main() { int x = 0; - MACRO(x); // CHECK-DAG: Expansion,File 0, [[@LINE]]:3 -> [[@LINE]]:8 = #0 (Expanded file = 1) + MACRO(x); return 0; } diff --git a/clang/test/CoverageMapping/macroparams2.c b/clang/test/CoverageMapping/macroparams2.c index 4df2dd468fb..fc156de7558 100644 --- a/clang/test/CoverageMapping/macroparams2.c +++ b/clang/test/CoverageMapping/macroparams2.c @@ -1,19 +1,21 @@ // RUN: %clang_cc1 -fprofile-instr-generate -fcoverage-mapping -dump-coverage-mapping -emit-llvm-only -main-file-name macroparams2.c %s | FileCheck %s -// A test case for when the first macro parameter is used after the second -// macro parameter. +#define MACRO(REFS, CALLS) (4 * (CALLS) < (REFS)) struct S { int i, j; }; -#define MACRO(REFS, CALLS) (4 * (CALLS) < (REFS)) - -int main() { // CHECK: File 0, [[@LINE]]:12 -> [[@LINE+7]]:2 = #0 - struct S arr[32] = { 0 }; // CHECK-NEXT: Expansion,File 0, [[@LINE+2]]:7 -> [[@LINE+2]]:12 = #0 (Expanded file = 1) - int n = 0; // CHECK-NEXT: File 0, [[@LINE+1]]:13 -> [[@LINE+1]]:21 = #0 - if (MACRO(arr[n].j, arr[n].i)) { // CHECK-NEXT: File 0, [[@LINE]]:23 -> [[@LINE]]:31 = #0 - n = 1; // CHECK-NEXT: File 0, [[@LINE-1]]:34 -> [[@LINE+1]]:4 = #1 +// CHECK: File 0, [[@LINE+1]]:12 -> [[@LINE+10]]:2 = #0 +int main() { + struct S arr[32] = { 0 }; + int n = 0; + // CHECK-NEXT: Expansion,File 0, [[@LINE+2]]:7 -> [[@LINE+2]]:12 = #0 + // CHECK-NEXT: File 0, [[@LINE+1]]:34 -> [[@LINE+3]]:4 = #1 + if (MACRO(arr[n].j, arr[n].i)) { + n = 1; } return n; -} // CHECK-NEXT: File 1, [[@LINE-9]]:29 -> [[@LINE-9]]:51 = #0 +} + +// CHECK: File 1, 3:29 -> 3:51 = #0 diff --git a/clang/test/CoverageMapping/macros.c b/clang/test/CoverageMapping/macros.c index 7b8a5436c38..02ecceba63f 100644 --- a/clang/test/CoverageMapping/macros.c +++ b/clang/test/CoverageMapping/macros.c @@ -1,26 +1,43 @@ // RUN: %clang_cc1 -fprofile-instr-generate -fcoverage-mapping -dump-coverage-mapping -emit-llvm-only -main-file-name macros.c %s | FileCheck %s -void bar(); #define MACRO return; bar() #define MACRO_2 bar() #define MACRO_1 return; MACRO_2 - // CHECK: func -void func() { // CHECK-NEXT: File 0, [[@LINE]]:13 -> [[@LINE+4]]:2 = #0 +void bar() {} + +// CHECK: func +void func() { // CHECK-NEXT: File 0, [[@LINE]]:13 -> [[@LINE+5]]:2 = #0 int i = 0; - MACRO; // CHECK-NEXT: Expansion,File 0, [[@LINE]]:3 -> [[@LINE]]:8 = #0 (Expanded file = 1) - i = 2; // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE]]:8 = 0 + // CHECK-NEXT: Expansion,File 0, [[@LINE+1]]:3 -> [[@LINE+1]]:8 = #0 + MACRO; // CHECK-NEXT: File 0, [[@LINE]]:8 -> [[@LINE+2]]:2 = 0 + i = 2; } -// CHECK-NEXT: File 1, 4:15 -> 4:21 = #0 -// CHECK-NEXT: File 1, 4:23 -> 4:28 = 0 +// CHECK-NEXT: File 1, 3:15 -> 3:28 = #0 +// CHECK-NEXT: File 1, 3:23 -> 3:28 = 0 - // CHECK-NEXT: func2 -void func2() { // CHECK-NEXT: File 0, [[@LINE]]:14 -> [[@LINE+4]]:2 = #0 +// CHECK-NEXT: func2 +void func2() { // CHECK-NEXT: File 0, [[@LINE]]:14 -> [[@LINE+5]]:2 = #0 int i = 0; - MACRO_1; // CHECK-NEXT: Expansion,File 0, [[@LINE]]:3 -> [[@LINE]]:10 = #0 (Expanded file = 1) - i = 2; // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE]]:8 = 0 + // CHECK-NEXT: Expansion,File 0, [[@LINE+1]]:3 -> [[@LINE+1]]:10 = #0 + MACRO_1; // CHECK-NEXT: File 0, [[@LINE]]:10 -> [[@LINE+2]]:2 = 0 + i = 2; +} +// CHECK-NEXT: File 1, 5:17 -> 5:32 = #0 +// CHECK-NEXT: File 1, 5:25 -> 5:32 = 0 +// CHECK-NEXT: Expansion,File 1, 5:25 -> 5:32 = 0 +// CHECK-NEXT: File 2, 4:17 -> 4:22 = 0 + +// CHECK-NEXT: func3 +void func3() { // CHECK-NEXT: File 0, [[@LINE]]:14 -> [[@LINE+3]]:2 = #0 + MACRO_2; // CHECK-NEXT: Expansion,File 0, [[@LINE]]:3 -> [[@LINE]]:10 = #0 + MACRO_2; // CHECK-NEXT: Expansion,File 0, [[@LINE]]:3 -> [[@LINE]]:10 = #0 } -// CHECK-NEXT: File 1, 6:17 -> 6:23 = #0 -// CHECK-NEXT: Expansion,File 1, 6:25 -> 6:32 = 0 (Expanded file = 2) -// CHECK-NEXT: File 2, 5:17 -> 5:22 = 0 +// CHECK-NEXT: File 1, 4:17 -> 4:22 = #0 +// CHECK-NEXT: File 2, 4:17 -> 4:22 = #0 +int main(int argc, const char *argv[]) { + func(); + func2(); + func3(); +} diff --git a/clang/test/CoverageMapping/macroscopes.cpp b/clang/test/CoverageMapping/macroscopes.cpp new file mode 100644 index 00000000000..2a6763e58fe --- /dev/null +++ b/clang/test/CoverageMapping/macroscopes.cpp @@ -0,0 +1,101 @@ +// RUN: %clang_cc1 -fprofile-instr-generate -fcoverage-mapping -dump-coverage-mapping -emit-llvm-only -main-file-name macroscopes.cpp %s | FileCheck %s + +#define starts_a_scope for (int i = 0; i < 2; ++i) { + +#define ends_a_scope \ + x = x; \ + } + +#define some_code \ + x = x; \ + if (x == 0) { \ + x = 1; \ + } else { \ + x = 2; \ + } \ + if (true) { \ + x = x; \ + } else { \ + x = x; \ + } + +#define starts_a_while while (x < 5) +#define simple_stmt ++x + +// CHECK: main +// CHECK-NEXT: File 0, [[@LINE+1]]:12 -> {{[0-9]+}}:2 = #0 +int main() { + int x = 0; + // CHECK-NEXT: Expansion,File 0, [[@LINE+2]]:3 -> [[@LINE+2]]:17 = #0 + // CHECK-NEXT: File 0, [[@LINE+1]]:17 -> [[@LINE+7]]:15 = #1 + starts_a_scope + x = x; + // CHECK-NEXT: Expansion,File 0, [[@LINE+1]]:5 -> [[@LINE+1]]:14 = #1 + some_code + x = x; + // CHECK-NEXT: Expansion,File 0, [[@LINE+1]]:3 -> [[@LINE+1]]:15 = #1 + ends_a_scope + + // CHECK-NEXT: Expansion,File 0, [[@LINE+4]]:3 -> [[@LINE+4]]:17 = #0 + // CHECK-NEXT: File 0, [[@LINE+3]]:17 -> [[@LINE+5]]:15 = #4 + // CHECK-NEXT: Expansion,File 0, [[@LINE+3]]:5 -> [[@LINE+3]]:14 = #4 + // CHECK-NEXT: Expansion,File 0, [[@LINE+3]]:3 -> [[@LINE+3]]:15 = #4 + starts_a_scope + some_code + ends_a_scope + + // CHECK-NEXT: Expansion,File 0, [[@LINE+3]]:3 -> [[@LINE+3]]:17 = #0 + // CHECK-NEXT: File 0, [[@LINE+2]]:17 -> [[@LINE+3]]:15 = #7 + // CHECK-NEXT: Expansion,File 0, [[@LINE+2]]:3 -> [[@LINE+2]]:15 = #7 + starts_a_scope + ends_a_scope + + // CHECK-NEXT: Expansion,File 0, [[@LINE+2]]:3 -> [[@LINE+2]]:17 = #0 + // CHECK-NEXT: Expansion,File 0, [[@LINE+2]]:5 -> [[@LINE+2]]:16 = #8 + starts_a_while + simple_stmt; + + x = 0; + // CHECK-NEXT: Expansion,File 0, [[@LINE+4]]:3 -> [[@LINE+4]]:17 = #0 + // CHECK-NEXT: File 0, [[@LINE+3]]:18 -> [[@LINE+5]]:15 = #9 + // CHECK-NEXT: Expansion,File 0, [[@LINE+3]]:5 -> [[@LINE+3]]:16 = #9 + // CHECK-NEXT: Expansion,File 0, [[@LINE+3]]:3 -> [[@LINE+3]]:15 = #9 + starts_a_while { + simple_stmt; + ends_a_scope + + return 0; +} + +// CHECK-NEXT: File 1, 3:24 -> 3:53 = #0 +// CHECK-NEXT: File 1, 3:40 -> 3:45 = (#0 + #1) +// CHECK-NEXT: File 1, 3:47 -> 3:50 = #1 +// CHECK-NEXT: File 1, 3:52 -> 3:53 = #1 +// CHECK-NEXT: File 2, 10:3 -> 20:4 = #1 +// CHECK-NEXT: File 2, 11:15 -> 13:4 = #2 +// CHECK-NEXT: File 2, 13:10 -> 15:4 = (#1 - #2) +// CHECK-NEXT: File 2, 16:13 -> 18:4 = #3 +// CHECK-NEXT: File 2, 18:10 -> 20:4 = (#1 - #3) +// CHECK-NEXT: File 3, 6:3 -> 7:4 = #1 +// CHECK-NEXT: File 4, 3:24 -> 3:53 = #0 +// CHECK-NEXT: File 4, 3:40 -> 3:45 = (#0 + #4) +// CHECK-NEXT: File 4, 3:47 -> 3:50 = #4 +// CHECK-NEXT: File 4, 3:52 -> 3:53 = #4 +// CHECK-NEXT: File 5, 10:3 -> 20:4 = #4 +// CHECK-NEXT: File 5, 11:15 -> 13:4 = #5 +// CHECK-NEXT: File 5, 13:10 -> 15:4 = (#4 - #5) +// CHECK-NEXT: File 5, 16:13 -> 18:4 = #6 +// CHECK-NEXT: File 5, 18:10 -> 20:4 = (#4 - #6) +// CHECK-NEXT: File 6, 6:3 -> 7:4 = #4 +// CHECK-NEXT: File 7, 3:24 -> 3:53 = #0 +// CHECK-NEXT: File 7, 3:40 -> 3:45 = (#0 + #7) +// CHECK-NEXT: File 7, 3:47 -> 3:50 = #7 +// CHECK-NEXT: File 7, 3:52 -> 3:53 = #7 +// CHECK-NEXT: File 8, 6:3 -> 7:4 = #7 +// CHECK-NEXT: File 9, 22:24 -> 22:37 = #0 +// CHECK-NEXT: File 9, 22:31 -> 22:36 = (#0 + #8) +// CHECK-NEXT: File 10, 23:21 -> 23:24 = #8 +// CHECK-NEXT: File 11, 22:24 -> 22:37 = #0 +// CHECK-NEXT: File 11, 22:31 -> 22:36 = (#0 + #9) +// CHECK-NEXT: File 12, 23:21 -> 23:24 = #9 +// CHECK-NEXT: File 13, 6:3 -> 7:4 = #9 diff --git a/clang/test/CoverageMapping/md.cpp b/clang/test/CoverageMapping/md.cpp new file mode 100644 index 00000000000..fff0df35265 --- /dev/null +++ b/clang/test/CoverageMapping/md.cpp @@ -0,0 +1,33 @@ +// RUN: %clang_cc1 -fprofile-instr-generate -fcoverage-mapping -dump-coverage-mapping -emit-llvm-only -std=c++11 %s | FileCheck %s + +#define BREAK break + +enum class MD { + Val1, + Val2 +}; + +void nop() {} + +// CHECK: foo +// CHECK-NEXT: File 0, [[@LINE+1]]:16 -> {{[0-9]+}}:2 = #0 +void foo(MD i) { + switch (i) { + #define HANDLE_MD(X) \ + case MD::X: \ + break; + #include "Inputs/md.def" + default: + BREAK; + } + + if (false) + nop(); + #define HANDLE_MD(X) else if (i == MD::X) { nop(); } + #include "Inputs/md.def" +} + +int main(int argc, const char *argv[]) { + foo(MD::Val1); + return 0; +} diff --git a/clang/test/CoverageMapping/moremacros.c b/clang/test/CoverageMapping/moremacros.c new file mode 100644 index 00000000000..b059a3c059b --- /dev/null +++ b/clang/test/CoverageMapping/moremacros.c @@ -0,0 +1,36 @@ +// RUN: %clang_cc1 -fprofile-instr-generate -fcoverage-mapping -dump-coverage-mapping -emit-llvm-only -main-file-name macro-expansion.c %s | FileCheck %s + +#define LBRAC { +#define RBRAC } + +// CHECK: main: +// CHECK-NEXT: File 0, [[@LINE+1]]:40 -> [[@LINE+24]]:2 = #0 +int main(int argc, const char *argv[]) { + if (!argc) {} // CHECK-NEXT: File 0, [[@LINE]]:14 -> [[@LINE]]:16 = #1 + + // CHECK-NEXT: Expansion,File 0, [[@LINE+2]]:14 -> [[@LINE+2]]:19 = #2 + // CHECK-NEXT: File 0, [[@LINE+1]]:19 -> [[@LINE+4]]:8 = #2 + if (!argc) LBRAC + return 0; + // CHECK-NEXT: Expansion,File 0, [[@LINE+1]]:3 -> [[@LINE+1]]:8 = #2 + RBRAC + + // CHECK-NEXT: File 0, [[@LINE+3]]:3 -> [[@LINE+13]]:2 = (#0 - #2) + // CHECK-NEXT: Expansion,File 0, [[@LINE+2]]:14 -> [[@LINE+2]]:19 = #3 + // CHECK-NEXT: File 0, [[@LINE+1]]:19 -> [[@LINE+3]]:4 = #3 + if (!argc) LBRAC + return 0; + } + + // CHECK-NEXT: File 0, [[@LINE+2]]:3 -> [[@LINE+6]]:2 = ((#0 - #2) - #3) + // CHECK-NEXT: File 0, [[@LINE+1]]:14 -> [[@LINE+4]]:8 = #4 + if (!argc) { + return 0; + // CHECK-NEXT: Expansion,File 0, [[@LINE+1]]:3 -> [[@LINE+1]]:8 = #4 + RBRAC +} + +// CHECK-NEXT: File 1, 3:15 -> 3:16 = #2 +// CHECK-NEXT: File 2, 4:15 -> 4:16 = #2 +// CHECK-NEXT: File 3, 3:15 -> 3:16 = #3 +// CHECK-NEXT: File 4, 4:15 -> 4:16 = #4 diff --git a/clang/test/CoverageMapping/return.c b/clang/test/CoverageMapping/return.c index b6d9e7c738a..a05695b7f76 100644 --- a/clang/test/CoverageMapping/return.c +++ b/clang/test/CoverageMapping/return.c @@ -3,7 +3,7 @@ // CHECK: func void func() { // CHECK: File 0, [[@LINE]]:13 -> [[@LINE+3]]:2 = #0 return; - int i = 0; // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE]]:12 = 0 + int i = 0; // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+1]]:2 = 0 } // CHECK-NEXT: func2 @@ -13,7 +13,7 @@ void func2() { // CHECK-NEXT: File 0, [[@LINE]]:14 -> [[@LINE+1 for(int i = 0; i < 10; ++i) { // CHECK-NEXT: File 0, [[@LINE]]:31 -> [[@LINE+9]]:4 = #1 if(i > 2) { // CHECK-NEXT: File 0, [[@LINE]]:15 -> [[@LINE+2]]:6 = #2 return; - } // CHECK-NEXT: File 0, [[@LINE+1]]:5 -> [[@LINE+3]]:11 = (#1 - #2) + } // CHECK-NEXT: File 0, [[@LINE+1]]:5 -> [[@LINE+6]]:4 = (#1 - #2) if(i == 3) { // CHECK-NEXT: File 0, [[@LINE]]:16 -> [[@LINE+2]]:6 = #3 int j = 1; } else { // CHECK-NEXT: File 0, [[@LINE]]:12 -> [[@LINE+2]]:6 = ((#1 - #2) - #3) @@ -27,9 +27,15 @@ void func3(int x) { // CHECK-NEXT: File 0, [[@LINE]]:19 -> [[@LINE+9] if(x > 5) { // CHECK-NEXT: File 0, [[@LINE]]:13 -> [[@LINE+6]]:4 = #1 while(x >= 9) { // CHECK-NEXT: File 0, [[@LINE]]:11 -> [[@LINE]]:17 = #1 return; // CHECK-NEXT: File 0, [[@LINE-1]]:19 -> [[@LINE+2]]:6 = #2 - --x; // CHECK-NEXT: File 0, [[@LINE]]:7 -> [[@LINE]]:10 = 0 + --x; // CHECK-NEXT: File 0, [[@LINE]]:7 -> [[@LINE+1]]:6 = 0 } - int i = 0; // CHECK-NEXT: File 0, [[@LINE]]:5 -> [[@LINE]]:14 = (#1 - #2) + int i = 0; // CHECK-NEXT: File 0, [[@LINE]]:5 -> [[@LINE+1]]:4 = (#1 - #2) } - int j = 0; // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE]]:12 = (#0 - #2) + int j = 0; // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+1]]:2 = (#0 - #2) +} + +int main(int argc, const char *argv[]) { + func(); + func2(); + func3(10); } diff --git a/clang/test/CoverageMapping/switch.c b/clang/test/CoverageMapping/switch.c index 84791004899..3c0b0323f69 100644 --- a/clang/test/CoverageMapping/switch.c +++ b/clang/test/CoverageMapping/switch.c @@ -2,19 +2,44 @@ // CHECK: foo void foo(int i) { // CHECK-NEXT: File 0, [[@LINE]]:17 -> [[@LINE+8]]:2 = #0 switch(i) { - case 1: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+1]]:11 = #2 + case 1: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+3]]:10 = #2 return; case 2: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+1]]:10 = #3 break; - } // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+1]]:12 = #1 - int x = 0; + } + int x = 0; // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+1]]:2 = #1 +} + +void nop() {} + + // CHECK: bar +void bar(int i) { // CHECK-NEXT: File 0, [[@LINE]]:17 -> [[@LINE+20]]:2 = #0 + switch (i) + ; // CHECK-NEXT: File 0, [[@LINE]]:5 -> [[@LINE]]:6 = 0 + + switch (i) { // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+16]]:2 = #1 + } + + switch (i) // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+13]]:2 = #2 + nop(); // CHECK-NEXT: File 0, [[@LINE]]:5 -> [[@LINE]]:10 = 0 + + switch (i) // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+10]]:2 = #3 + case 1: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+1]]:10 = #5 + nop(); + + switch (i) { // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+6]]:2 = #4 + nop(); // CHECK-NEXT: File 0, [[@LINE]]:5 -> [[@LINE+2]]:10 = 0 + case 1: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+1]]:10 = #7 + nop(); + } + nop(); // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+1]]:2 = #6 } // CHECK-NEXT: main int main() { // CHECK-NEXT: File 0, [[@LINE]]:12 -> [[@LINE+34]]:2 = #0 int i = 0; switch(i) { - case 0: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+2]]:10 = #2 + case 0: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+7]]:10 = #2 i = 1; break; case 1: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+2]]:10 = #3 @@ -22,27 +47,27 @@ int main() { // CHECK-NEXT: File 0, [[@LINE]]:12 -> [[@LINE+34]]:2 = #0 break; default: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+1]]:10 = #4 break; - } // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+1]]:14 = #1 - switch(i) { - case 0: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+2]]:10 = #6 + } + switch(i) { // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+22]]:2 = #1 + case 0: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+6]]:10 = #6 i = 1; break; - case 1: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+1]]:10 = #7 + case 1: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+3]]:10 = #7 i = 2; default: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+1]]:10 = (#7 + #8) break; - } // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+3]]:14 = #5 - + } - switch(i) { - case 1: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE]]:10 = #10 - case 2: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+1]]:11 = (#10 + #11) + switch(i) { // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+12]]:2 = #5 + case 1: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+5]]:11 = #10 + case 2: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+4]]:11 = (#10 + #11) i = 11; - case 3: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE]]:10 = ((#10 + #11) + #12) + case 3: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+2]]:11 = ((#10 + #11) + #12) case 4: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+1]]:11 = (((#10 + #11) + #12) + #13) i = 99; - } // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+3]]:11 = #9 + } - foo(1); + foo(1); // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+2]]:11 = #9 + bar(1); return 0; } diff --git a/clang/test/CoverageMapping/switchmacro.c b/clang/test/CoverageMapping/switchmacro.c new file mode 100644 index 00000000000..5be38573d92 --- /dev/null +++ b/clang/test/CoverageMapping/switchmacro.c @@ -0,0 +1,28 @@ +// RUN: %clang_cc1 -fprofile-instr-generate -fcoverage-mapping -dump-coverage-mapping -emit-llvm-only -main-file-name switchmacro.c %s | FileCheck %s + +#define FOO(x) (void)x + +// CHECK: foo +int foo(int i) { // CHECK-NEXT: File 0, [[@LINE]]:16 -> {{[0-9]+}}:2 = #0 + switch (i) { + default: // CHECK-NEXT: File 0, [[@LINE]]:3 -> {{[0-9]+}}:11 = #2 + if (i == 1) + return 0; // CHECK-NEXT: File 0, [[@LINE]]:7 -> [[@LINE]]:15 = #3 + // CHECK-NEXT: Expansion,File 0, [[@LINE+2]]:5 -> [[@LINE+2]]:8 = (#2 - #3) + // CHECK-NEXT: File 0, [[@LINE+1]]:8 -> {{[0-9]+}}:11 = (#2 - #3) + FOO(1); + case 0: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+1]]:13 = ((#2 + #4) - #3) + return 2; + + // CHECK-NEXT: Expansion,File 0, [[@LINE+2]]:3 -> [[@LINE+2]]:6 = 0 + // CHECK-NEXT: File 0, [[@LINE+1]]:6 -> {{[0-9]+}}:11 = 0 + FOO(1); + // CHECK-NEXT: File 0, [[@LINE+1]]:3 -> {{[0-9]+}}:11 = #5 + label: ; + } +} + +int main(int argc, const char *argv[]) { + foo(3); + return 0; +} diff --git a/clang/test/CoverageMapping/trycatch.cpp b/clang/test/CoverageMapping/trycatch.cpp index fac18ab7301..cd653341e71 100644 --- a/clang/test/CoverageMapping/trycatch.cpp +++ b/clang/test/CoverageMapping/trycatch.cpp @@ -12,9 +12,9 @@ class Warning { // CHECK: func void func(int i) { // CHECK-NEXT: File 0, [[@LINE]]:18 -> [[@LINE+5]]:2 = #0 if(i % 2) - throw Error(); // CHECK-NEXT: File 0, [[@LINE]]:5 -> [[@LINE]]:16 = #1 - else if(i == 8) // CHECK-NEXT: File 0, [[@LINE]]:8 -> [[@LINE]]:17 = (#0 - #1) - throw ImportantError(); // CHECK-NEXT: File 0, [[@LINE]]:5 -> [[@LINE]]:25 = #2 + throw Error(); // CHECK-NEXT: File 0, [[@LINE]]:5 -> [[@LINE]]:18 = #1 + else if(i == 8) // CHECK-NEXT: File 0, [[@LINE]]:8 -> [[@LINE+1]]:27 = (#0 - #1) + throw ImportantError(); // CHECK-NEXT: File 0, [[@LINE]]:5 -> [[@LINE]]:27 = #2 } // CHECK-NEXT: main @@ -22,9 +22,9 @@ int main() { // CHECK-NEXT: File 0, [[@LINE]]:12 -> [[@ int j = 0; try { func(j); - } catch(const Error &e) { // CHECK-NEXT: File 0, [[@LINE]]:27 -> [[@LINE+2]]:10 = #2 + } catch(const Error &e) { // CHECK-NEXT: File 0, [[@LINE]]:27 -> [[@LINE+2]]:4 = #2 j = 1; - } catch(const ImportantError &e) { // CHECK-NEXT: File 0, [[@LINE]]:36 -> [[@LINE+3]]:8 = #3 + } catch(const ImportantError &e) { // CHECK-NEXT: File 0, [[@LINE]]:36 -> [[@LINE+2]]:4 = #3 j = 11; } catch(const Warning &w) { // CHECK-NEXT: File 0, [[@LINE]]:27 -> [[@LINE+2]]:4 = #4 diff --git a/clang/test/CoverageMapping/while.c b/clang/test/CoverageMapping/while.c new file mode 100644 index 00000000000..a85957ff8ac --- /dev/null +++ b/clang/test/CoverageMapping/while.c @@ -0,0 +1,12 @@ +// RUN: %clang_cc1 -fprofile-instr-generate -fcoverage-mapping -dump-coverage-mapping -emit-llvm-only -main-file-name loops.cpp %s | FileCheck %s + + // CHECK: main +int main() { // CHECK-NEXT: File 0, [[@LINE]]:12 -> [[@LINE+8]]:2 = #0 + int j = 0; // CHECK-NEXT: File 0, [[@LINE+1]]:9 -> [[@LINE+1]]:14 = (#0 + #1) + while(j < 5) ++j; // CHECK-NEXT: File 0, [[@LINE]]:16 -> [[@LINE]]:19 = #1 + j = 0; + while + (j < 5) // CHECK-NEXT: File 0, [[@LINE]]:5 -> [[@LINE]]:10 = (#0 + #2) + ++j; // CHECK-NEXT: File 0, [[@LINE]]:6 -> [[@LINE]]:9 = #2 + return 0; +} |