[llvm] Introduce CovMap in ObjectYAML (PR #127432)

James Henderson via llvm-commits llvm-commits at lists.llvm.org
Mon Feb 24 01:23:33 PST 2025


================
@@ -0,0 +1,984 @@
+//===- CovMap.cpp - ObjectYAML Interface for coverage map -----------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Implementations of CovMap, encoder, decoder.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ObjectYAML/CovMap.h"
+#include "llvm/ADT/MapVector.h"
+#include "llvm/ObjectYAML/ELFYAML.h"
+#include "llvm/ProfileData/Coverage/CoverageMapping.h"
+#include "llvm/ProfileData/Coverage/CoverageMappingReader.h"
+#include "llvm/ProfileData/Coverage/CoverageMappingWriter.h"
+#include "llvm/ProfileData/InstrProf.h"
+#include "llvm/Support/Alignment.h"
+#include "llvm/Support/DataExtractor.h"
+#include "llvm/Support/Endian.h"
+#include "llvm/Support/LEB128.h"
+#include "llvm/Support/MD5.h"
+#include "llvm/Support/YAMLTraits.h"
+#include <cstdint>
+
+#define COVMAP_V3
+
+using namespace llvm;
+using namespace llvm::coverage::yaml;
+using namespace llvm::covmap;
+
+bool Decoder::enabled;
+
+// DataExtractor w/ single Cursor
+struct coverage::yaml::DecoderContext : DataExtractor,
+                                        DataExtractor::Cursor,
+                                        DecoderParam {
+  uint64_t LineStart = 0;
+
+  DecoderContext(const ArrayRef<uint8_t> Content, const DecoderParam &Param,
+                 bool IsLE)
+      : DataExtractor(Content, IsLE, /*AddressSize=*/0),
+        DataExtractor::Cursor(0), DecoderParam(Param) {}
+
+  bool eof() { return DataExtractor::eof(*this); }
+  uint32_t getU32() { return DataExtractor::getU32(*this); }
+  uint64_t getU64() { return DataExtractor::getU64(*this); }
+  Expected<uint64_t> getULEB128() {
+    uint64_t Result = DataExtractor::getULEB128(*this);
+    if (!*this)
+      return takeError();
+    return Result;
+  }
+  StringRef getBytes(size_t sz) { return DataExtractor::getBytes(*this, sz); }
+};
+
+void CounterTy::encode(raw_ostream &OS) const {
+  std::pair<unsigned, uint64_t> C;
+  if (RefOpt)
+    C = {Ref, *RefOpt};
+  else if (SubOpt)
+    C = {Sub, *SubOpt};
+  else if (AddOpt)
+    C = {Add, *AddOpt};
+  else if (Tag && *Tag == Zero)
+    C = {Zero, 0u};
+  else if (Tag && Val)
+    C = {*Tag, *Val};
+  else
+    llvm_unreachable("Null value cannot be met");
+
+  encodeULEB128(C.first | (C.second << 2), OS);
+}
+
+Error CounterTy::decodeOrTag(DecoderContext &Data) {
+  auto COrErr = Data.getULEB128();
+  if (!COrErr)
+    return COrErr.takeError();
+  auto T = static_cast<TagTy>(*COrErr & 0x03);
+  auto V = (*COrErr >> 2);
+  if (T == Zero) {
+    if (V == 0)
+      Tag = Zero; // w/o Val
+    else
+      Val = V; // w/o Tag
+  } else {
+    if (Data.Raw) {
+      Tag = T;
+      Val = V;
+    } else {
+      switch (T) {
+      case Zero:
+        llvm_unreachable("Zero should be handled in advance");
+      case Ref:
+        RefOpt = V;
+        break;
+      case Sub:
+        SubOpt = V;
+        break;
+      case Add:
+        AddOpt = V;
+        break;
+      }
+    }
+  }
+
+  return Error::success();
+}
+
+Error CounterTy::decode(DecoderContext &Data) {
+  if (auto E = decodeOrTag(Data))
+    return E;
+  if (!this->Tag && this->Val)
+    return make_error<CoverageMapError>(
+        coveragemap_error::malformed,
+        "Counter::Zero shouldn't have the Val: 0x" +
+            Twine::utohexstr(*this->Val));
+  return Error::success();
+}
+
+void DecisionTy::encode(raw_ostream &OS) const {
+  encodeULEB128(BIdx, OS);
+  encodeULEB128(NC, OS);
+}
+
+Error DecisionTy::decode(DecoderContext &Data) {
+  auto BIdxOrErr = Data.getULEB128();
+  if (!BIdxOrErr)
+    return BIdxOrErr.takeError();
+  BIdx = *BIdxOrErr;
+
+  auto NCOrErr = Data.getULEB128();
+  if (!NCOrErr)
+    return NCOrErr.takeError();
+  NC = *NCOrErr;
+
+  return Error::success();
+}
+
+void RecTy::encode(uint64_t &StartLoc, raw_ostream &OS) const {
+  if (Expansion) {
+    encodeULEB128(4 + (*Expansion << 3), OS);
+  } else if (ExtTag && *ExtTag == Skip) {
+    encodeULEB128(2 << 3, OS);
+  } else if (DecisionOpt) {
+    assert(!ExtTag || *ExtTag == Decision);
+    encodeULEB128(5 << 3, OS);
+    DecisionOpt->encode(OS);
+  } else if (MCDC) {
+    assert(!ExtTag || *ExtTag == MCDCBranch);
+    assert(BranchOpt);
+    encodeULEB128(6 << 3, OS);
+    (*BranchOpt)[0].encode(OS);
+    (*BranchOpt)[1].encode(OS);
+    encodeULEB128((*MCDC)[0], OS);
+    encodeULEB128((*MCDC)[1], OS);
+    encodeULEB128((*MCDC)[2], OS);
+  } else if (BranchOpt) {
+    assert(!ExtTag || *ExtTag == Branch);
+    encodeULEB128(4 << 3, OS);
+    (*BranchOpt)[0].encode(OS);
+    (*BranchOpt)[1].encode(OS);
+  } else {
+    // Non-tag CounterTy
+    CounterTy::encode(OS);
+  }
+
+  assert((!isGap || *isGap) && "Don't set isGap=false");
+  uint32_t Gap = (isGap ? (1u << 31) : 0u);
+  if (Loc) {
+    encodeULEB128((*Loc)[0] - StartLoc, OS);
+    encodeULEB128((*Loc)[1], OS);
+    encodeULEB128((*Loc)[2] - (*Loc)[0], OS);
+    encodeULEB128((*Loc)[3] | Gap, OS);
+    StartLoc = (*Loc)[0];
+  } else {
+    encodeULEB128((*dLoc)[0], OS);
+    encodeULEB128((*dLoc)[1], OS);
+    encodeULEB128((*dLoc)[2], OS);
+    encodeULEB128((*dLoc)[3] | Gap, OS);
+  }
+}
+
+Error RecTy::decode(DecoderContext &Data) {
+  auto getU16 = [&]() -> Expected<uint16_t> {
+    auto ValOrErr = Data.getULEB128();
+    if (!ValOrErr)
+      return ValOrErr.takeError();
+    if (*ValOrErr > 0x7FFF + 1)
+      return make_error<CoverageMapError>(coveragemap_error::malformed,
+                                          "MC/DC index is out of range: 0x" +
+                                              Twine::utohexstr(*ValOrErr));
+    return static_cast<uint16_t>(*ValOrErr);
+  };
+
+  auto decodeBranch = [&]() -> Error {
+    auto &B = BranchOpt.emplace();
+    if (auto E = B[0].decode(Data))
+      return E;
+    if (auto E = B[1].decode(Data))
+      return E;
+    return Error::success();
+  };
+
+  // Decode tagged CounterTy
+  if (auto E = CounterTy::decodeOrTag(Data))
+    return E;
+  if (!this->Val || this->Tag) {
+    // Compatible to CounterTy
+  } else if (*this->Val & 1u) {
+    Expansion = (*this->Val >> 1);
+    this->Val.reset();
+  } else {
+    auto Tag = *this->Val >> 1;
+    this->Val.reset();
+    switch (Tag) {
+    case Skip:
+      ExtTag = Skip; // w/o Val
+      break;
+    case Decision:
+      if (auto E = DecisionOpt.emplace().decode(Data))
+        return E;
+      if (Data.Raw)
+        ExtTag = Decision;
+      break;
+    case Branch:
+      if (auto E = decodeBranch())
+        return E;
+      if (Data.Raw)
+        ExtTag = Branch;
+      break;
+    case MCDCBranch: {
+      if (auto E = decodeBranch())
+        return E;
+      auto I0OrErr = getU16();
+      if (!I0OrErr)
+        return I0OrErr.takeError();
+      auto I1OrErr = getU16();
+      if (!I1OrErr)
+        return I1OrErr.takeError();
+      auto I2OrErr = getU16();
+      if (!I2OrErr)
+        return I2OrErr.takeError();
+      MCDC = {*I0OrErr, *I1OrErr, *I2OrErr};
+      if (Data.Raw)
+        ExtTag = MCDCBranch;
+      break;
+    }
+    default:
+      return make_error<CoverageMapError>(
+          coveragemap_error::malformed,
+          "Record doesn't have an valid Tag: 0x" + Twine::utohexstr(Tag));
----------------
jh7370 wrote:

```suggestion
          "Record doesn't have a valid Tag: 0x" + Twine::utohexstr(Tag));
```

https://github.com/llvm/llvm-project/pull/127432


More information about the llvm-commits mailing list