106f32e7eSjoerg //===- CoverageMappingWriter.cpp - Code coverage mapping writer -----------===//
206f32e7eSjoerg //
306f32e7eSjoerg // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
406f32e7eSjoerg // See https://llvm.org/LICENSE.txt for license information.
506f32e7eSjoerg // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
606f32e7eSjoerg //
706f32e7eSjoerg //===----------------------------------------------------------------------===//
806f32e7eSjoerg //
906f32e7eSjoerg // This file contains support for writing coverage mapping data for
1006f32e7eSjoerg // instrumentation based coverage.
1106f32e7eSjoerg //
1206f32e7eSjoerg //===----------------------------------------------------------------------===//
1306f32e7eSjoerg 
14*da58b97aSjoerg #include "llvm/ProfileData/InstrProf.h"
1506f32e7eSjoerg #include "llvm/ProfileData/Coverage/CoverageMappingWriter.h"
1606f32e7eSjoerg #include "llvm/ADT/ArrayRef.h"
1706f32e7eSjoerg #include "llvm/ADT/SmallVector.h"
18*da58b97aSjoerg #include "llvm/Support/Compression.h"
1906f32e7eSjoerg #include "llvm/Support/LEB128.h"
2006f32e7eSjoerg #include "llvm/Support/raw_ostream.h"
2106f32e7eSjoerg #include <algorithm>
2206f32e7eSjoerg #include <cassert>
2306f32e7eSjoerg #include <limits>
2406f32e7eSjoerg #include <vector>
2506f32e7eSjoerg 
2606f32e7eSjoerg using namespace llvm;
2706f32e7eSjoerg using namespace coverage;
2806f32e7eSjoerg 
CoverageFilenamesSectionWriter(ArrayRef<std::string> Filenames)2906f32e7eSjoerg CoverageFilenamesSectionWriter::CoverageFilenamesSectionWriter(
30*da58b97aSjoerg     ArrayRef<std::string> Filenames)
3106f32e7eSjoerg     : Filenames(Filenames) {
3206f32e7eSjoerg #ifndef NDEBUG
3306f32e7eSjoerg   StringSet<> NameSet;
3406f32e7eSjoerg   for (StringRef Name : Filenames)
3506f32e7eSjoerg     assert(NameSet.insert(Name).second && "Duplicate filename");
3606f32e7eSjoerg #endif
3706f32e7eSjoerg }
3806f32e7eSjoerg 
write(raw_ostream & OS,bool Compress)39*da58b97aSjoerg void CoverageFilenamesSectionWriter::write(raw_ostream &OS, bool Compress) {
40*da58b97aSjoerg   std::string FilenamesStr;
41*da58b97aSjoerg   {
42*da58b97aSjoerg     raw_string_ostream FilenamesOS{FilenamesStr};
4306f32e7eSjoerg     for (const auto &Filename : Filenames) {
44*da58b97aSjoerg       encodeULEB128(Filename.size(), FilenamesOS);
45*da58b97aSjoerg       FilenamesOS << Filename;
4606f32e7eSjoerg     }
4706f32e7eSjoerg   }
4806f32e7eSjoerg 
49*da58b97aSjoerg   SmallString<128> CompressedStr;
50*da58b97aSjoerg   bool doCompression =
51*da58b97aSjoerg       Compress && zlib::isAvailable() && DoInstrProfNameCompression;
52*da58b97aSjoerg   if (doCompression) {
53*da58b97aSjoerg     auto E =
54*da58b97aSjoerg         zlib::compress(FilenamesStr, CompressedStr, zlib::BestSizeCompression);
55*da58b97aSjoerg     if (E)
56*da58b97aSjoerg       report_bad_alloc_error("Failed to zlib compress coverage data");
57*da58b97aSjoerg   }
58*da58b97aSjoerg 
59*da58b97aSjoerg   // ::= <num-filenames>
60*da58b97aSjoerg   //     <uncompressed-len>
61*da58b97aSjoerg   //     <compressed-len-or-zero>
62*da58b97aSjoerg   //     (<compressed-filenames> | <uncompressed-filenames>)
63*da58b97aSjoerg   encodeULEB128(Filenames.size(), OS);
64*da58b97aSjoerg   encodeULEB128(FilenamesStr.size(), OS);
65*da58b97aSjoerg   encodeULEB128(doCompression ? CompressedStr.size() : 0U, OS);
66*da58b97aSjoerg   OS << (doCompression ? StringRef(CompressedStr) : StringRef(FilenamesStr));
67*da58b97aSjoerg }
68*da58b97aSjoerg 
6906f32e7eSjoerg namespace {
7006f32e7eSjoerg 
7106f32e7eSjoerg /// Gather only the expressions that are used by the mapping
7206f32e7eSjoerg /// regions in this function.
7306f32e7eSjoerg class CounterExpressionsMinimizer {
7406f32e7eSjoerg   ArrayRef<CounterExpression> Expressions;
7506f32e7eSjoerg   SmallVector<CounterExpression, 16> UsedExpressions;
7606f32e7eSjoerg   std::vector<unsigned> AdjustedExpressionIDs;
7706f32e7eSjoerg 
7806f32e7eSjoerg public:
CounterExpressionsMinimizer(ArrayRef<CounterExpression> Expressions,ArrayRef<CounterMappingRegion> MappingRegions)7906f32e7eSjoerg   CounterExpressionsMinimizer(ArrayRef<CounterExpression> Expressions,
8006f32e7eSjoerg                               ArrayRef<CounterMappingRegion> MappingRegions)
8106f32e7eSjoerg       : Expressions(Expressions) {
8206f32e7eSjoerg     AdjustedExpressionIDs.resize(Expressions.size(), 0);
83*da58b97aSjoerg     for (const auto &I : MappingRegions) {
8406f32e7eSjoerg       mark(I.Count);
85*da58b97aSjoerg       mark(I.FalseCount);
86*da58b97aSjoerg     }
87*da58b97aSjoerg     for (const auto &I : MappingRegions) {
8806f32e7eSjoerg       gatherUsed(I.Count);
89*da58b97aSjoerg       gatherUsed(I.FalseCount);
90*da58b97aSjoerg     }
9106f32e7eSjoerg   }
9206f32e7eSjoerg 
mark(Counter C)9306f32e7eSjoerg   void mark(Counter C) {
9406f32e7eSjoerg     if (!C.isExpression())
9506f32e7eSjoerg       return;
9606f32e7eSjoerg     unsigned ID = C.getExpressionID();
9706f32e7eSjoerg     AdjustedExpressionIDs[ID] = 1;
9806f32e7eSjoerg     mark(Expressions[ID].LHS);
9906f32e7eSjoerg     mark(Expressions[ID].RHS);
10006f32e7eSjoerg   }
10106f32e7eSjoerg 
gatherUsed(Counter C)10206f32e7eSjoerg   void gatherUsed(Counter C) {
10306f32e7eSjoerg     if (!C.isExpression() || !AdjustedExpressionIDs[C.getExpressionID()])
10406f32e7eSjoerg       return;
10506f32e7eSjoerg     AdjustedExpressionIDs[C.getExpressionID()] = UsedExpressions.size();
10606f32e7eSjoerg     const auto &E = Expressions[C.getExpressionID()];
10706f32e7eSjoerg     UsedExpressions.push_back(E);
10806f32e7eSjoerg     gatherUsed(E.LHS);
10906f32e7eSjoerg     gatherUsed(E.RHS);
11006f32e7eSjoerg   }
11106f32e7eSjoerg 
getExpressions() const11206f32e7eSjoerg   ArrayRef<CounterExpression> getExpressions() const { return UsedExpressions; }
11306f32e7eSjoerg 
11406f32e7eSjoerg   /// Adjust the given counter to correctly transition from the old
11506f32e7eSjoerg   /// expression ids to the new expression ids.
adjust(Counter C) const11606f32e7eSjoerg   Counter adjust(Counter C) const {
11706f32e7eSjoerg     if (C.isExpression())
11806f32e7eSjoerg       C = Counter::getExpression(AdjustedExpressionIDs[C.getExpressionID()]);
11906f32e7eSjoerg     return C;
12006f32e7eSjoerg   }
12106f32e7eSjoerg };
12206f32e7eSjoerg 
12306f32e7eSjoerg } // end anonymous namespace
12406f32e7eSjoerg 
12506f32e7eSjoerg /// Encode the counter.
12606f32e7eSjoerg ///
12706f32e7eSjoerg /// The encoding uses the following format:
12806f32e7eSjoerg /// Low 2 bits - Tag:
12906f32e7eSjoerg ///   Counter::Zero(0) - A Counter with kind Counter::Zero
13006f32e7eSjoerg ///   Counter::CounterValueReference(1) - A counter with kind
13106f32e7eSjoerg ///     Counter::CounterValueReference
13206f32e7eSjoerg ///   Counter::Expression(2) + CounterExpression::Subtract(0) -
13306f32e7eSjoerg ///     A counter with kind Counter::Expression and an expression
13406f32e7eSjoerg ///     with kind CounterExpression::Subtract
13506f32e7eSjoerg ///   Counter::Expression(2) + CounterExpression::Add(1) -
13606f32e7eSjoerg ///     A counter with kind Counter::Expression and an expression
13706f32e7eSjoerg ///     with kind CounterExpression::Add
13806f32e7eSjoerg /// Remaining bits - Counter/Expression ID.
encodeCounter(ArrayRef<CounterExpression> Expressions,Counter C)13906f32e7eSjoerg static unsigned encodeCounter(ArrayRef<CounterExpression> Expressions,
14006f32e7eSjoerg                               Counter C) {
14106f32e7eSjoerg   unsigned Tag = unsigned(C.getKind());
14206f32e7eSjoerg   if (C.isExpression())
14306f32e7eSjoerg     Tag += Expressions[C.getExpressionID()].Kind;
14406f32e7eSjoerg   unsigned ID = C.getCounterID();
14506f32e7eSjoerg   assert(ID <=
14606f32e7eSjoerg          (std::numeric_limits<unsigned>::max() >> Counter::EncodingTagBits));
14706f32e7eSjoerg   return Tag | (ID << Counter::EncodingTagBits);
14806f32e7eSjoerg }
14906f32e7eSjoerg 
writeCounter(ArrayRef<CounterExpression> Expressions,Counter C,raw_ostream & OS)15006f32e7eSjoerg static void writeCounter(ArrayRef<CounterExpression> Expressions, Counter C,
15106f32e7eSjoerg                          raw_ostream &OS) {
15206f32e7eSjoerg   encodeULEB128(encodeCounter(Expressions, C), OS);
15306f32e7eSjoerg }
15406f32e7eSjoerg 
write(raw_ostream & OS)15506f32e7eSjoerg void CoverageMappingWriter::write(raw_ostream &OS) {
15606f32e7eSjoerg   // Check that we don't have any bogus regions.
15706f32e7eSjoerg   assert(all_of(MappingRegions,
15806f32e7eSjoerg                 [](const CounterMappingRegion &CMR) {
15906f32e7eSjoerg                   return CMR.startLoc() <= CMR.endLoc();
16006f32e7eSjoerg                 }) &&
16106f32e7eSjoerg          "Source region does not begin before it ends");
16206f32e7eSjoerg 
16306f32e7eSjoerg   // Sort the regions in an ascending order by the file id and the starting
16406f32e7eSjoerg   // location. Sort by region kinds to ensure stable order for tests.
16506f32e7eSjoerg   llvm::stable_sort(MappingRegions, [](const CounterMappingRegion &LHS,
16606f32e7eSjoerg                                        const CounterMappingRegion &RHS) {
16706f32e7eSjoerg     if (LHS.FileID != RHS.FileID)
16806f32e7eSjoerg       return LHS.FileID < RHS.FileID;
16906f32e7eSjoerg     if (LHS.startLoc() != RHS.startLoc())
17006f32e7eSjoerg       return LHS.startLoc() < RHS.startLoc();
17106f32e7eSjoerg     return LHS.Kind < RHS.Kind;
17206f32e7eSjoerg   });
17306f32e7eSjoerg 
17406f32e7eSjoerg   // Write out the fileid -> filename mapping.
17506f32e7eSjoerg   encodeULEB128(VirtualFileMapping.size(), OS);
17606f32e7eSjoerg   for (const auto &FileID : VirtualFileMapping)
17706f32e7eSjoerg     encodeULEB128(FileID, OS);
17806f32e7eSjoerg 
17906f32e7eSjoerg   // Write out the expressions.
18006f32e7eSjoerg   CounterExpressionsMinimizer Minimizer(Expressions, MappingRegions);
18106f32e7eSjoerg   auto MinExpressions = Minimizer.getExpressions();
18206f32e7eSjoerg   encodeULEB128(MinExpressions.size(), OS);
18306f32e7eSjoerg   for (const auto &E : MinExpressions) {
18406f32e7eSjoerg     writeCounter(MinExpressions, Minimizer.adjust(E.LHS), OS);
18506f32e7eSjoerg     writeCounter(MinExpressions, Minimizer.adjust(E.RHS), OS);
18606f32e7eSjoerg   }
18706f32e7eSjoerg 
18806f32e7eSjoerg   // Write out the mapping regions.
18906f32e7eSjoerg   // Split the regions into subarrays where each region in a
19006f32e7eSjoerg   // subarray has a fileID which is the index of that subarray.
19106f32e7eSjoerg   unsigned PrevLineStart = 0;
19206f32e7eSjoerg   unsigned CurrentFileID = ~0U;
19306f32e7eSjoerg   for (auto I = MappingRegions.begin(), E = MappingRegions.end(); I != E; ++I) {
19406f32e7eSjoerg     if (I->FileID != CurrentFileID) {
19506f32e7eSjoerg       // Ensure that all file ids have at least one mapping region.
19606f32e7eSjoerg       assert(I->FileID == (CurrentFileID + 1));
19706f32e7eSjoerg       // Find the number of regions with this file id.
19806f32e7eSjoerg       unsigned RegionCount = 1;
19906f32e7eSjoerg       for (auto J = I + 1; J != E && I->FileID == J->FileID; ++J)
20006f32e7eSjoerg         ++RegionCount;
20106f32e7eSjoerg       // Start a new region sub-array.
20206f32e7eSjoerg       encodeULEB128(RegionCount, OS);
20306f32e7eSjoerg 
20406f32e7eSjoerg       CurrentFileID = I->FileID;
20506f32e7eSjoerg       PrevLineStart = 0;
20606f32e7eSjoerg     }
20706f32e7eSjoerg     Counter Count = Minimizer.adjust(I->Count);
208*da58b97aSjoerg     Counter FalseCount = Minimizer.adjust(I->FalseCount);
20906f32e7eSjoerg     switch (I->Kind) {
21006f32e7eSjoerg     case CounterMappingRegion::CodeRegion:
21106f32e7eSjoerg     case CounterMappingRegion::GapRegion:
21206f32e7eSjoerg       writeCounter(MinExpressions, Count, OS);
21306f32e7eSjoerg       break;
21406f32e7eSjoerg     case CounterMappingRegion::ExpansionRegion: {
21506f32e7eSjoerg       assert(Count.isZero());
21606f32e7eSjoerg       assert(I->ExpandedFileID <=
21706f32e7eSjoerg              (std::numeric_limits<unsigned>::max() >>
21806f32e7eSjoerg               Counter::EncodingCounterTagAndExpansionRegionTagBits));
21906f32e7eSjoerg       // Mark an expansion region with a set bit that follows the counter tag,
22006f32e7eSjoerg       // and pack the expanded file id into the remaining bits.
22106f32e7eSjoerg       unsigned EncodedTagExpandedFileID =
22206f32e7eSjoerg           (1 << Counter::EncodingTagBits) |
22306f32e7eSjoerg           (I->ExpandedFileID
22406f32e7eSjoerg            << Counter::EncodingCounterTagAndExpansionRegionTagBits);
22506f32e7eSjoerg       encodeULEB128(EncodedTagExpandedFileID, OS);
22606f32e7eSjoerg       break;
22706f32e7eSjoerg     }
22806f32e7eSjoerg     case CounterMappingRegion::SkippedRegion:
22906f32e7eSjoerg       assert(Count.isZero());
23006f32e7eSjoerg       encodeULEB128(unsigned(I->Kind)
23106f32e7eSjoerg                         << Counter::EncodingCounterTagAndExpansionRegionTagBits,
23206f32e7eSjoerg                     OS);
23306f32e7eSjoerg       break;
234*da58b97aSjoerg     case CounterMappingRegion::BranchRegion:
235*da58b97aSjoerg       encodeULEB128(unsigned(I->Kind)
236*da58b97aSjoerg                         << Counter::EncodingCounterTagAndExpansionRegionTagBits,
237*da58b97aSjoerg                     OS);
238*da58b97aSjoerg       writeCounter(MinExpressions, Count, OS);
239*da58b97aSjoerg       writeCounter(MinExpressions, FalseCount, OS);
240*da58b97aSjoerg       break;
24106f32e7eSjoerg     }
24206f32e7eSjoerg     assert(I->LineStart >= PrevLineStart);
24306f32e7eSjoerg     encodeULEB128(I->LineStart - PrevLineStart, OS);
24406f32e7eSjoerg     encodeULEB128(I->ColumnStart, OS);
24506f32e7eSjoerg     assert(I->LineEnd >= I->LineStart);
24606f32e7eSjoerg     encodeULEB128(I->LineEnd - I->LineStart, OS);
24706f32e7eSjoerg     encodeULEB128(I->ColumnEnd, OS);
24806f32e7eSjoerg     PrevLineStart = I->LineStart;
24906f32e7eSjoerg   }
25006f32e7eSjoerg   // Ensure that all file ids have at least one mapping region.
25106f32e7eSjoerg   assert(CurrentFileID == (VirtualFileMapping.size() - 1));
25206f32e7eSjoerg }
253