[llvm] 1e72920 - [dsymutil] dsymutil produces broken lines info (probably) with LTO on mac

Alexey Lapshin via llvm-commits llvm-commits at lists.llvm.org
Fri Feb 3 02:12:04 PST 2023


Author: Alexey Lapshin
Date: 2023-02-03T11:09:50+01:00
New Revision: 1e72920c8859771c03177623ad7519543d78eb7d

URL: https://github.com/llvm/llvm-project/commit/1e72920c8859771c03177623ad7519543d78eb7d
DIFF: https://github.com/llvm/llvm-project/commit/1e72920c8859771c03177623ad7519543d78eb7d.diff

LOG: [dsymutil] dsymutil produces broken lines info (probably) with LTO on mac

This patch fixes #60307 issue. The 8bb4451 introduces the possibility
to unite overlapped or adjacent address ranges to keep address ranges
in an unambiguous state. The AddressRangesMap is used to normalize
address ranges. The AddressRangesMap keeps address ranges and the value
of the relocated address. For intersected range, it creates a united
range that keeps the last inserted mapping value. The same for adjusted ranges.
While it is OK to use the last inserted mapping value for intersected ranges
(as there is no way how to resolve ambiguity) It is not OK to use the
last inserted value for adjacent address ranges. Currently, two following
address ranges are united into a single one:

{0,24,17e685c} {24,d8,55afe20} -> {0,d8,55afe20}

To avoid the problem, the AddressRangesMap should not unite adjacent address ranges
with different relocated addresses. Instead, it should leave adjacent address ranges
as separate ranges. So, the ranges should look like this:

{0,24,17e685c} {24,d8,55afe20}

Differential Revision: https://reviews.llvm.org/D142936

Added: 
    

Modified: 
    llvm/include/llvm/ADT/AddressRanges.h
    llvm/include/llvm/DWARFLinker/DWARFLinkerCompileUnit.h
    llvm/lib/DWARFLinker/DWARFLinker.cpp
    llvm/lib/DWARFLinker/DWARFStreamer.cpp
    llvm/lib/Support/CMakeLists.txt
    llvm/unittests/Support/AddressRangeTest.cpp

Removed: 
    llvm/lib/Support/AddressRanges.cpp


################################################################################
diff  --git a/llvm/include/llvm/ADT/AddressRanges.h b/llvm/include/llvm/ADT/AddressRanges.h
index f2052d82e7c1f..415d30bbb5cf9 100644
--- a/llvm/include/llvm/ADT/AddressRanges.h
+++ b/llvm/include/llvm/ADT/AddressRanges.h
@@ -28,7 +28,11 @@ class AddressRange {
   uint64_t start() const { return Start; }
   uint64_t end() const { return End; }
   uint64_t size() const { return End - Start; }
+  uint64_t empty() const { return size() == 0; }
   bool contains(uint64_t Addr) const { return Start <= Addr && Addr < End; }
+  bool contains(const AddressRange &R) const {
+    return Start <= R.Start && R.End <= End;
+  }
   bool intersects(const AddressRange &R) const {
     return Start < R.End && R.Start < End;
   }
@@ -45,101 +49,163 @@ class AddressRange {
   uint64_t End = 0;
 };
 
-/// The AddressRanges class helps normalize address range collections.
-/// This class keeps a sorted vector of AddressRange objects and can perform
-/// insertions and searches efficiently. The address ranges are always sorted
-/// and never contain any invalid or empty address ranges.
-/// Intersecting([100,200), [150,300)) and adjacent([100,200), [200,300))
-/// address ranges are combined during insertion.
-class AddressRanges {
+/// The AddressRangesBase class presents the base functionality for the
+/// normalized address ranges collection. This class keeps a sorted vector
+/// of AddressRange-like objects and can perform searches efficiently.
+/// The address ranges are always sorted and never contain any invalid,
+/// empty or intersected address ranges.
+
+template <typename T> class AddressRangesBase {
 protected:
-  using Collection = SmallVector<AddressRange>;
+  using Collection = SmallVector<T>;
   Collection Ranges;
 
 public:
   void clear() { Ranges.clear(); }
   bool empty() const { return Ranges.empty(); }
-  bool contains(uint64_t Addr) const { return find(Addr) != Ranges.end(); }
+  bool contains(uint64_t Addr) const {
+    return find(Addr, Addr + 1) != Ranges.end();
+  }
   bool contains(AddressRange Range) const {
-    return find(Range) != Ranges.end();
+    return find(Range.start(), Range.end()) != Ranges.end();
   }
-  std::optional<AddressRange> getRangeThatContains(uint64_t Addr) const {
-    Collection::const_iterator It = find(Addr);
+  void reserve(size_t Capacity) { Ranges.reserve(Capacity); }
+  size_t size() const { return Ranges.size(); }
+
+  std::optional<T> getRangeThatContains(uint64_t Addr) const {
+    typename Collection::const_iterator It = find(Addr, Addr + 1);
     if (It == Ranges.end())
       return std::nullopt;
 
     return *It;
   }
-  Collection::const_iterator insert(AddressRange Range);
-  void reserve(size_t Capacity) { Ranges.reserve(Capacity); }
-  size_t size() const { return Ranges.size(); }
-  bool operator==(const AddressRanges &RHS) const {
-    return Ranges == RHS.Ranges;
-  }
-  const AddressRange &operator[](size_t i) const {
+
+  typename Collection::const_iterator begin() const { return Ranges.begin(); }
+  typename Collection::const_iterator end() const { return Ranges.end(); }
+
+  const T &operator[](size_t i) const {
     assert(i < Ranges.size());
     return Ranges[i];
   }
-  Collection::const_iterator begin() const { return Ranges.begin(); }
-  Collection::const_iterator end() const { return Ranges.end(); }
+
+  bool operator==(const AddressRangesBase<T> &RHS) const {
+    return Ranges == RHS.Ranges;
+  }
 
 protected:
-  Collection::const_iterator find(uint64_t Addr) const;
-  Collection::const_iterator find(AddressRange Range) const;
+  typename Collection::const_iterator find(uint64_t Start, uint64_t End) const {
+    if (Start >= End)
+      return Ranges.end();
+
+    auto It =
+        std::partition_point(Ranges.begin(), Ranges.end(), [=](const T &R) {
+          return AddressRange(R).start() <= Start;
+        });
+
+    if (It == Ranges.begin())
+      return Ranges.end();
+
+    --It;
+    if (End > AddressRange(*It).end())
+      return Ranges.end();
+
+    return It;
+  }
 };
 
-/// AddressRangesMap class maps values to the address ranges.
-/// It keeps address ranges and corresponding values. If ranges
-/// are combined during insertion, then combined range keeps
-/// newly inserted value.
-template <typename T> class AddressRangesMap : protected AddressRanges {
+/// The AddressRanges class helps normalize address range collections.
+/// This class keeps a sorted vector of AddressRange objects and can perform
+/// insertions and searches efficiently. Intersecting([100,200), [150,300))
+/// and adjacent([100,200), [200,300)) address ranges are combined during
+/// insertion.
+class AddressRanges : public AddressRangesBase<AddressRange> {
 public:
-  void clear() {
-    Ranges.clear();
-    Values.clear();
+  Collection::const_iterator insert(AddressRange Range) {
+    if (Range.empty())
+      return Ranges.end();
+
+    auto It = llvm::upper_bound(Ranges, Range);
+    auto It2 = It;
+    while (It2 != Ranges.end() && It2->start() <= Range.end())
+      ++It2;
+    if (It != It2) {
+      Range = {Range.start(), std::max(Range.end(), std::prev(It2)->end())};
+      It = Ranges.erase(It, It2);
+    }
+    if (It != Ranges.begin() && Range.start() <= std::prev(It)->end()) {
+      --It;
+      *It = {It->start(), std::max(It->end(), Range.end())};
+      return It;
+    }
+
+    return Ranges.insert(It, Range);
   }
-  bool empty() const { return AddressRanges::empty(); }
-  bool contains(uint64_t Addr) const { return AddressRanges::contains(Addr); }
-  bool contains(AddressRange Range) const {
-    return AddressRanges::contains(Range);
-  }
-  void insert(AddressRange Range, T Value) {
-    size_t InputSize = Ranges.size();
-    Collection::const_iterator RangesIt = AddressRanges::insert(Range);
-    if (RangesIt == Ranges.end())
-      return;
+};
 
-    // make Values match to Ranges.
-    size_t Idx = RangesIt - Ranges.begin();
-    typename ValuesCollection::iterator ValuesIt = Values.begin() + Idx;
-    if (InputSize < Ranges.size())
-      Values.insert(ValuesIt, T());
-    else if (InputSize > Ranges.size())
-      Values.erase(ValuesIt, ValuesIt + InputSize - Ranges.size());
-    assert(Ranges.size() == Values.size());
-
-    // set value to the inserted or combined range.
-    Values[Idx] = Value;
-  }
-  size_t size() const {
-    assert(Ranges.size() == Values.size());
-    return AddressRanges::size();
-  }
-  std::optional<std::pair<AddressRange, T>>
-  getRangeValueThatContains(uint64_t Addr) const {
-    Collection::const_iterator It = find(Addr);
-    if (It == Ranges.end())
-      return std::nullopt;
+class AddressRangeValuePair {
+public:
+  operator AddressRange() const { return Range; }
 
-    return std::make_pair(*It, Values[It - Ranges.begin()]);
-  }
-  std::pair<AddressRange, T> operator[](size_t Idx) const {
-    return std::make_pair(Ranges[Idx], Values[Idx]);
-  }
+  AddressRange Range;
+  int64_t Value = 0;
+};
 
-protected:
-  using ValuesCollection = SmallVector<T>;
-  ValuesCollection Values;
+inline bool operator==(const AddressRangeValuePair &LHS,
+                       const AddressRangeValuePair &RHS) {
+  return LHS.Range == RHS.Range && LHS.Value == RHS.Value;
+}
+
+/// AddressRangesMap class maps values to the address ranges.
+/// It keeps normalized address ranges and corresponding values.
+/// This class keeps a sorted vector of AddressRangeValuePair objects
+/// and can perform insertions and searches efficiently.
+/// Intersecting([100,200), [150,300)) ranges splitted into non-conflicting
+/// parts([100,200), [200,300)). Adjacent([100,200), [200,300)) address
+/// ranges are not combined during insertion.
+class AddressRangesMap : public AddressRangesBase<AddressRangeValuePair> {
+public:
+  void insert(AddressRange Range, int64_t Value) {
+    if (Range.empty())
+      return;
+
+    // Search for range which is less than or equal incoming Range.
+    auto It = std::partition_point(Ranges.begin(), Ranges.end(),
+                                   [=](const AddressRangeValuePair &R) {
+                                     return R.Range.start() <= Range.start();
+                                   });
+
+    if (It != Ranges.begin())
+      It--;
+
+    while (!Range.empty()) {
+      // Inserted range does not overlap with any range.
+      // Store it into the Ranges collection.
+      if (It == Ranges.end() || Range.end() <= It->Range.start()) {
+        Ranges.insert(It, {Range, Value});
+        return;
+      }
+
+      // Inserted range partially overlaps with current range.
+      // Store not overlapped part of inserted range.
+      if (Range.start() < It->Range.start()) {
+        It = Ranges.insert(It, {{Range.start(), It->Range.start()}, Value});
+        It++;
+        Range = {It->Range.start(), Range.end()};
+        continue;
+      }
+
+      // Inserted range fully overlaps with current range.
+      if (Range.end() <= It->Range.end())
+        return;
+
+      // Inserted range partially overlaps with current range.
+      // Remove overlapped part from the inserted range.
+      if (Range.start() < It->Range.end())
+        Range = {It->Range.end(), Range.end()};
+
+      It++;
+    }
+  }
 };
 
 } // namespace llvm

diff  --git a/llvm/include/llvm/DWARFLinker/DWARFLinkerCompileUnit.h b/llvm/include/llvm/DWARFLinker/DWARFLinkerCompileUnit.h
index 5b0ea339c4d6f..9c7f24e69d48d 100644
--- a/llvm/include/llvm/DWARFLinker/DWARFLinkerCompileUnit.h
+++ b/llvm/include/llvm/DWARFLinker/DWARFLinkerCompileUnit.h
@@ -21,7 +21,7 @@ class DeclContext;
 
 /// Mapped value in the address map is the offset to apply to the
 /// linked address.
-using RangesTy = AddressRangesMap<int64_t>;
+using RangesTy = AddressRangesMap;
 
 // FIXME: Delete this structure.
 struct PatchLocation {

diff  --git a/llvm/lib/DWARFLinker/DWARFLinker.cpp b/llvm/lib/DWARFLinker/DWARFLinker.cpp
index 9f6e54377edeb..d302d61894fa6 100644
--- a/llvm/lib/DWARFLinker/DWARFLinker.cpp
+++ b/llvm/lib/DWARFLinker/DWARFLinker.cpp
@@ -1659,7 +1659,7 @@ void DWARFLinker::patchRangesForUnit(const CompileUnit &Unit,
   DWARFDataExtractor RangeExtractor(OrigDwarf.getDWARFObj(),
                                     OrigDwarf.getDWARFObj().getRangesSection(),
                                     OrigDwarf.isLittleEndian(), AddressSize);
-  std::optional<std::pair<AddressRange, int64_t>> CachedRange;
+  std::optional<AddressRangeValuePair> CachedRange;
   DWARFUnit &OrigUnit = Unit.getOrigUnit();
   auto OrigUnitDie = OrigUnit.getUnitDIE(false);
   uint64_t UnitBaseAddress =
@@ -1687,9 +1687,9 @@ void DWARFLinker::patchRangesForUnit(const CompileUnit &Unit,
         }
 
         if (!CachedRange ||
-            !CachedRange->first.contains(Range.StartAddress + BaseAddress))
-          CachedRange = FunctionRanges.getRangeValueThatContains(
-              Range.StartAddress + BaseAddress);
+            !CachedRange->Range.contains(Range.StartAddress + BaseAddress))
+          CachedRange = FunctionRanges.getRangeThatContains(Range.StartAddress +
+                                                            BaseAddress);
 
         // All range entries should lie in the function range.
         if (!CachedRange) {
@@ -1698,8 +1698,8 @@ void DWARFLinker::patchRangesForUnit(const CompileUnit &Unit,
         }
 
         LinkedRanges.insert(
-            {Range.StartAddress + BaseAddress + CachedRange->second,
-             Range.EndAddress + BaseAddress + CachedRange->second});
+            {Range.StartAddress + BaseAddress + CachedRange->Value,
+             Range.EndAddress + BaseAddress + CachedRange->Value});
       }
     }
 
@@ -1802,7 +1802,7 @@ void DWARFLinker::patchLineTableForUnit(CompileUnit &Unit,
   // in NewRows.
   std::vector<DWARFDebugLine::Row> Seq;
   const auto &FunctionRanges = Unit.getFunctionRanges();
-  std::optional<std::pair<AddressRange, int64_t>> CurrRange;
+  std::optional<AddressRangeValuePair> CurrRange;
 
   // FIXME: This logic is meant to generate exactly the same output as
   // Darwin's classic dsymutil. There is a nicer way to implement this
@@ -1821,13 +1821,13 @@ void DWARFLinker::patchLineTableForUnit(CompileUnit &Unit,
     // it is marked as end_sequence in the input (because in that
     // case, the relocation offset is accurate and that entry won't
     // serve as the start of another function).
-    if (!CurrRange || !CurrRange->first.contains(Row.Address.Address) ||
-        (Row.Address.Address == CurrRange->first.end() && !Row.EndSequence)) {
+    if (!CurrRange || !CurrRange->Range.contains(Row.Address.Address) ||
+        (Row.Address.Address == CurrRange->Range.end() && !Row.EndSequence)) {
       // We just stepped out of a known range. Insert a end_sequence
       // corresponding to the end of the range.
       uint64_t StopAddress =
-          CurrRange ? CurrRange->first.end() + CurrRange->second : -1ULL;
-      CurrRange = FunctionRanges.getRangeValueThatContains(Row.Address.Address);
+          CurrRange ? CurrRange->Range.end() + CurrRange->Value : -1ULL;
+      CurrRange = FunctionRanges.getRangeThatContains(Row.Address.Address);
       if (!CurrRange) {
         if (StopAddress != -1ULL) {
           // Try harder by looking in the Address ranges map.
@@ -1836,9 +1836,9 @@ void DWARFLinker::patchLineTableForUnit(CompileUnit &Unit,
           // for now do as dsymutil.
           // FIXME: Understand exactly what cases this addresses and
           // potentially remove it along with the Ranges map.
-          if (std::optional<std::pair<AddressRange, int64_t>> Range =
-                  Ranges.getRangeValueThatContains(Row.Address.Address))
-            StopAddress = Row.Address.Address + (*Range).second;
+          if (std::optional<AddressRangeValuePair> Range =
+                  Ranges.getRangeThatContains(Row.Address.Address))
+            StopAddress = Row.Address.Address + (*Range).Value;
         }
       }
       if (StopAddress != -1ULL && !Seq.empty()) {
@@ -1863,7 +1863,7 @@ void DWARFLinker::patchLineTableForUnit(CompileUnit &Unit,
       continue;
 
     // Relocate row address and add it to the current sequence.
-    Row.Address.Address += CurrRange->second;
+    Row.Address.Address += CurrRange->Value;
     Seq.emplace_back(Row);
 
     if (Row.EndSequence)
@@ -2002,8 +2002,8 @@ void DWARFLinker::patchFrameInfoForObject(const DWARFFile &File,
     // the function entry point, thus we can't just lookup the address
     // in the debug map. Use the AddressInfo's range map to see if the FDE
     // describes something that we can relocate.
-    std::optional<std::pair<AddressRange, int64_t>> Range =
-        Ranges.getRangeValueThatContains(Loc);
+    std::optional<AddressRangeValuePair> Range =
+        Ranges.getRangeThatContains(Loc);
     if (!Range) {
       // The +4 is to account for the size of the InitialLength field itself.
       InputOffset = EntryOffset + InitialLength + 4;
@@ -2032,7 +2032,7 @@ void DWARFLinker::patchFrameInfoForObject(const DWARFFile &File,
     // fields that will get reconstructed by emitFDE().
     unsigned FDERemainingBytes = InitialLength - (4 + AddrSize);
     TheDwarfEmitter->emitFDE(IteratorInserted.first->getValue(), AddrSize,
-                             Loc + Range->second,
+                             Loc + Range->Value,
                              FrameData.substr(InputOffset, FDERemainingBytes));
     InputOffset += FDERemainingBytes;
   }

diff  --git a/llvm/lib/DWARFLinker/DWARFStreamer.cpp b/llvm/lib/DWARFLinker/DWARFStreamer.cpp
index 5cad267fd8456..ae79e8cb90660 100644
--- a/llvm/lib/DWARFLinker/DWARFStreamer.cpp
+++ b/llvm/lib/DWARFLinker/DWARFStreamer.cpp
@@ -402,10 +402,9 @@ void DwarfStreamer::emitUnitRangesEntries(CompileUnit &Unit,
   // Linked addresses might end up in a 
diff erent order.
   // Build linked address ranges.
   AddressRanges LinkedRanges;
-  for (size_t Idx = 0; Idx < FunctionRanges.size(); Idx++)
+  for (const AddressRangeValuePair &Range : FunctionRanges)
     LinkedRanges.insert(
-        {FunctionRanges[Idx].first.start() + FunctionRanges[Idx].second,
-         FunctionRanges[Idx].first.end() + FunctionRanges[Idx].second});
+        {Range.Range.start() + Range.Value, Range.Range.end() + Range.Value});
 
   if (!FunctionRanges.empty())
     emitDwarfDebugArangesTable(Unit, LinkedRanges);

diff  --git a/llvm/lib/Support/AddressRanges.cpp b/llvm/lib/Support/AddressRanges.cpp
deleted file mode 100644
index 187d5be00daed..0000000000000
--- a/llvm/lib/Support/AddressRanges.cpp
+++ /dev/null
@@ -1,70 +0,0 @@
-//===- AddressRanges.cpp ----------------------------------------*- C++ -*-===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#include "llvm/ADT/AddressRanges.h"
-#include "llvm/ADT/STLExtras.h"
-#include <inttypes.h>
-
-using namespace llvm;
-
-AddressRanges::Collection::const_iterator
-AddressRanges::insert(AddressRange Range) {
-  if (Range.size() == 0)
-    return Ranges.end();
-
-  auto It = llvm::upper_bound(Ranges, Range);
-  auto It2 = It;
-  while (It2 != Ranges.end() && It2->start() <= Range.end())
-    ++It2;
-  if (It != It2) {
-    Range = {Range.start(), std::max(Range.end(), std::prev(It2)->end())};
-    It = Ranges.erase(It, It2);
-  }
-  if (It != Ranges.begin() && Range.start() <= std::prev(It)->end()) {
-    --It;
-    *It = {It->start(), std::max(It->end(), Range.end())};
-    return It;
-  }
-
-  return Ranges.insert(It, Range);
-}
-
-AddressRanges::Collection::const_iterator
-AddressRanges::find(uint64_t Addr) const {
-  auto It = std::partition_point(
-      Ranges.begin(), Ranges.end(),
-      [=](const AddressRange &R) { return R.start() <= Addr; });
-
-  if (It == Ranges.begin())
-    return Ranges.end();
-
-  --It;
-  if (Addr >= It->end())
-    return Ranges.end();
-
-  return It;
-}
-
-AddressRanges::Collection::const_iterator
-AddressRanges::find(AddressRange Range) const {
-  if (Range.size() == 0)
-    return Ranges.end();
-
-  auto It = std::partition_point(
-      Ranges.begin(), Ranges.end(),
-      [=](const AddressRange &R) { return R.start() <= Range.start(); });
-
-  if (It == Ranges.begin())
-    return Ranges.end();
-
-  --It;
-  if (Range.end() > It->end())
-    return Ranges.end();
-
-  return It;
-}

diff  --git a/llvm/lib/Support/CMakeLists.txt b/llvm/lib/Support/CMakeLists.txt
index fdb2a5dcaa1a5..7fb66216ba731 100644
--- a/llvm/lib/Support/CMakeLists.txt
+++ b/llvm/lib/Support/CMakeLists.txt
@@ -117,7 +117,6 @@ endif()
 add_subdirectory(BLAKE3)
 
 add_llvm_component_library(LLVMSupport
-  AddressRanges.cpp
   ABIBreak.cpp
   AMDGPUMetadata.cpp
   APFixedPoint.cpp

diff  --git a/llvm/unittests/Support/AddressRangeTest.cpp b/llvm/unittests/Support/AddressRangeTest.cpp
index 468f1e22ffa88..06b326678402d 100644
--- a/llvm/unittests/Support/AddressRangeTest.cpp
+++ b/llvm/unittests/Support/AddressRangeTest.cpp
@@ -149,8 +149,31 @@ TEST(AddressRangeTest, TestRanges) {
   EXPECT_EQ(Ranges[0], AddressRange(0x1000, 0x5000));
 }
 
+TEST(AddressRangeTest, TestRangesRandom) {
+  AddressRanges Ranges;
+  size_t NumElements = 100;
+
+  std::srand(std::time(nullptr));
+
+  // Fill ranges.
+  for (size_t Idx = 0; Idx < NumElements; Idx++) {
+    uint64_t Start = static_cast<uint64_t>(std::rand() % 1000);
+    uint64_t End = Start + static_cast<uint64_t>(std::rand() % 1000);
+    Ranges.insert({Start, End});
+  }
+
+  // Check ranges.
+  for (size_t Idx = 0; Idx + 1 < Ranges.size(); Idx++) {
+    // Check that ranges are not intersected.
+    EXPECT_FALSE(Ranges[Idx].intersects(Ranges[Idx + 1]));
+
+    // Check that ranges are sorted and not adjusted.
+    EXPECT_TRUE(Ranges[Idx].end() < Ranges[Idx + 1].start());
+  }
+}
+
 TEST(AddressRangeTest, TestRangesMap) {
-  AddressRangesMap<int> Ranges;
+  AddressRangesMap Ranges;
 
   EXPECT_EQ(Ranges.size(), 0u);
   EXPECT_TRUE(Ranges.empty());
@@ -162,73 +185,247 @@ TEST(AddressRangeTest, TestRangesMap) {
   EXPECT_TRUE(Ranges.contains(0x1500));
   EXPECT_TRUE(Ranges.contains(AddressRange(0x1000, 0x2000)));
 
+  ///////////////////////////////////////
+  /// Check ranges with the same mapped value.
+
+  // Clear ranges.
+  Ranges.clear();
+  EXPECT_EQ(Ranges.size(), 0u);
+  EXPECT_TRUE(Ranges.empty());
+
+  // Add range and check mapped value.
+  Ranges.insert(AddressRange(0x1000, 0x2000), 0x11);
+  EXPECT_EQ(Ranges.size(), 1u);
+  EXPECT_EQ(Ranges.getRangeThatContains(0x1000)->Value, 0x11);
+
+  // Add adjacent range and check mapped value.
+  Ranges.insert(AddressRange(0x2000, 0x3000), 0x11);
+  EXPECT_EQ(Ranges.size(), 2u);
+  EXPECT_EQ(Ranges.getRangeThatContains(0x1000)->Value, 0x11);
+  EXPECT_EQ(Ranges.getRangeThatContains(0x2000)->Value, 0x11);
+  EXPECT_EQ(Ranges.getRangeThatContains(0x2900)->Value, 0x11);
+  EXPECT_FALSE(Ranges.getRangeThatContains(0x3000));
+
+  // Add intersecting range and check mapped value.
+  Ranges.insert(AddressRange(0x1000, 0x3000), 0x11);
+  EXPECT_EQ(Ranges.size(), 2u);
+  EXPECT_EQ(Ranges.getRangeThatContains(0x1000)->Value, 0x11);
+
+  // Add second range and check mapped values.
+  Ranges.insert(AddressRange(0x4000, 0x5000), 0x11);
+  EXPECT_EQ(Ranges.size(), 3u);
+  EXPECT_EQ(Ranges[0].Range, AddressRange(0x1000, 0x2000));
+  EXPECT_EQ(Ranges[0].Value, 0x11);
+  EXPECT_EQ(Ranges[1].Range, AddressRange(0x2000, 0x3000));
+  EXPECT_EQ(Ranges[1].Value, 0x11);
+  EXPECT_EQ(Ranges[2].Range, AddressRange(0x4000, 0x5000));
+  EXPECT_EQ(Ranges[2].Value, 0x11);
+  EXPECT_EQ(Ranges.getRangeThatContains(0x1000)->Value, 0x11);
+  EXPECT_EQ(Ranges.getRangeThatContains(0x4000)->Value, 0x11);
+
+  // Add intersecting range and check mapped value.
+  Ranges.insert(AddressRange(0x0, 0x6000), 0x11);
+  EXPECT_EQ(Ranges.size(), 6u);
+  EXPECT_EQ(Ranges.getRangeThatContains(0x1000)->Value, 0x11);
+
+  // Check that mapped values are correctly preserved for combined ranges.
+  Ranges.clear();
+  Ranges.insert(AddressRange(0x0, 0xff), 0x11);
+  Ranges.insert(AddressRange(0x100, 0x1ff), 0x11);
+  Ranges.insert(AddressRange(0x200, 0x2ff), 0x11);
+  Ranges.insert(AddressRange(0x500, 0x5ff), 0x11);
+  Ranges.insert(AddressRange(0x300, 0x3ff), 0x11);
+  Ranges.insert(AddressRange(0x400, 0x4ff), 0x11);
+  Ranges.insert(AddressRange(0x600, 0x6ff), 0x11);
+  EXPECT_EQ(Ranges.size(), 7u);
+
+  Ranges.insert(AddressRange(0x150, 0x350), 0x11);
+  EXPECT_EQ(Ranges.size(), 9u);
+  EXPECT_EQ(Ranges[0].Range, AddressRange(0x0, 0xff));
+  EXPECT_EQ(Ranges[0].Value, 0x11);
+  EXPECT_EQ(Ranges[1].Range, AddressRange(0x100, 0x1ff));
+  EXPECT_EQ(Ranges[1].Value, 0x11);
+  EXPECT_EQ(Ranges[2].Range, AddressRange(0x1ff, 0x200));
+  EXPECT_EQ(Ranges[2].Value, 0x11);
+  EXPECT_EQ(Ranges[3].Range, AddressRange(0x200, 0x2ff));
+  EXPECT_EQ(Ranges[3].Value, 0x11);
+  EXPECT_EQ(Ranges[4].Range, AddressRange(0x2ff, 0x300));
+  EXPECT_EQ(Ranges[4].Value, 0x11);
+  EXPECT_EQ(Ranges[5].Range, AddressRange(0x300, 0x3ff));
+  EXPECT_EQ(Ranges[5].Value, 0x11);
+  EXPECT_EQ(Ranges[6].Range, AddressRange(0x400, 0x4ff));
+  EXPECT_EQ(Ranges[6].Value, 0x11);
+  EXPECT_EQ(Ranges[7].Range, AddressRange(0x500, 0x5ff));
+  EXPECT_EQ(Ranges[7].Value, 0x11);
+  EXPECT_EQ(Ranges[8].Range, AddressRange(0x600, 0x6ff));
+  EXPECT_EQ(Ranges[8].Value, 0x11);
+
+  Ranges.insert(AddressRange(0x3ff, 0x400), 0x11);
+  EXPECT_EQ(Ranges.size(), 10u);
+  EXPECT_EQ(Ranges[0].Range, AddressRange(0x0, 0xff));
+  EXPECT_EQ(Ranges[0].Value, 0x11);
+  EXPECT_EQ(Ranges[1].Range, AddressRange(0x100, 0x1ff));
+  EXPECT_EQ(Ranges[1].Value, 0x11);
+  EXPECT_EQ(Ranges[2].Range, AddressRange(0x1ff, 0x200));
+  EXPECT_EQ(Ranges[2].Value, 0x11);
+  EXPECT_EQ(Ranges[3].Range, AddressRange(0x200, 0x2ff));
+  EXPECT_EQ(Ranges[3].Value, 0x11);
+  EXPECT_EQ(Ranges[4].Range, AddressRange(0x2ff, 0x300));
+  EXPECT_EQ(Ranges[4].Value, 0x11);
+  EXPECT_EQ(Ranges[5].Range, AddressRange(0x300, 0x3ff));
+  EXPECT_EQ(Ranges[5].Value, 0x11);
+  EXPECT_EQ(Ranges[6].Range, AddressRange(0x3ff, 0x400));
+  EXPECT_EQ(Ranges[6].Value, 0x11);
+  EXPECT_EQ(Ranges[7].Range, AddressRange(0x400, 0x4ff));
+  EXPECT_EQ(Ranges[7].Value, 0x11);
+  EXPECT_EQ(Ranges[8].Range, AddressRange(0x500, 0x5ff));
+  EXPECT_EQ(Ranges[8].Value, 0x11);
+  EXPECT_EQ(Ranges[9].Range, AddressRange(0x600, 0x6ff));
+  EXPECT_EQ(Ranges[9].Value, 0x11);
+
+  /////////////////////////////////////////////
+  /// Check ranges with various mapped values.
+
   // Clear ranges.
   Ranges.clear();
   EXPECT_EQ(Ranges.size(), 0u);
   EXPECT_TRUE(Ranges.empty());
 
-  // Add range and check value.
+  // Add range and check mapped value.
   Ranges.insert(AddressRange(0x1000, 0x2000), 0xfe);
   EXPECT_EQ(Ranges.size(), 1u);
-  EXPECT_EQ(Ranges.getRangeValueThatContains(0x1000)->second, 0xfe);
+  EXPECT_EQ(Ranges.getRangeThatContains(0x1000)->Value, 0xfe);
 
-  // Add adjacent range and check value.
+  // Add adjacent range and check mapped value.
   Ranges.insert(AddressRange(0x2000, 0x3000), 0xfc);
-  EXPECT_EQ(Ranges.size(), 1u);
-  EXPECT_EQ(Ranges.getRangeValueThatContains(0x1000)->second, 0xfc);
-  EXPECT_EQ(Ranges.getRangeValueThatContains(0x2000)->second, 0xfc);
-  EXPECT_EQ(Ranges.getRangeValueThatContains(0x2900)->second, 0xfc);
-  EXPECT_FALSE(Ranges.getRangeValueThatContains(0x3000));
+  EXPECT_EQ(Ranges.size(), 2u);
+  EXPECT_EQ(Ranges.getRangeThatContains(0x1000)->Value, 0xfe);
+  EXPECT_EQ(Ranges.getRangeThatContains(0x2000)->Value, 0xfc);
+  EXPECT_EQ(Ranges.getRangeThatContains(0x2900)->Value, 0xfc);
+  EXPECT_FALSE(Ranges.getRangeThatContains(0x3000));
 
-  // Add intersecting range and check value.
-  Ranges.insert(AddressRange(0x2000, 0x3000), 0xff);
-  EXPECT_EQ(Ranges.size(), 1u);
-  EXPECT_EQ(Ranges.getRangeValueThatContains(0x1000)->second, 0xff);
+  // Add intersecting range and check mapped value.
+  Ranges.insert(AddressRange(0x1000, 0x3000), 0xff);
+  EXPECT_EQ(Ranges.size(), 2u);
+  EXPECT_EQ(Ranges.getRangeThatContains(0x1000)->Value, 0xfe);
 
-  // Add second range and check values.
+  // Add one more range and check mapped values.
   Ranges.insert(AddressRange(0x4000, 0x5000), 0x0);
-  EXPECT_EQ(Ranges.size(), 2u);
-  EXPECT_EQ(Ranges[0].second, 0xff);
-  EXPECT_EQ(Ranges[1].second, 0x0);
-  EXPECT_EQ(Ranges.getRangeValueThatContains(0x1000)->second, 0xff);
-  EXPECT_EQ(Ranges.getRangeValueThatContains(0x4000)->second, 0x0);
+  EXPECT_EQ(Ranges.size(), 3u);
+  EXPECT_EQ(Ranges[0].Value, 0xfe);
+  EXPECT_EQ(Ranges[1].Value, 0xfc);
+  EXPECT_EQ(Ranges[2].Value, 0x0);
+  EXPECT_EQ(Ranges.getRangeThatContains(0x1000)->Value, 0xfe);
+  EXPECT_EQ(Ranges.getRangeThatContains(0x4000)->Value, 0x0);
 
-  // Add intersecting range and check value.
+  // Add intersecting range and check mapped value.
   Ranges.insert(AddressRange(0x0, 0x6000), 0x1);
-  EXPECT_EQ(Ranges.size(), 1u);
-  EXPECT_EQ(Ranges.getRangeValueThatContains(0x1000)->second, 0x1);
+  EXPECT_EQ(Ranges.size(), 6u);
+  EXPECT_EQ(Ranges[0].Value, 0x1);
+  EXPECT_EQ(Ranges[1].Value, 0xfe);
+  EXPECT_EQ(Ranges[2].Value, 0xfc);
+  EXPECT_EQ(Ranges[3].Value, 0x1);
+  EXPECT_EQ(Ranges[4].Value, 0x0);
+  EXPECT_EQ(Ranges[5].Value, 0x1);
+  EXPECT_EQ(Ranges.getRangeThatContains(0x1000)->Value, 0xfe);
 
-  // Check that values are correctly preserved for combined ranges.
+  // Check that mapped values are correctly preserved for combined ranges.
   Ranges.clear();
   Ranges.insert(AddressRange(0x0, 0xff), 0x1);
   Ranges.insert(AddressRange(0x100, 0x1ff), 0x2);
   Ranges.insert(AddressRange(0x200, 0x2ff), 0x3);
   Ranges.insert(AddressRange(0x300, 0x3ff), 0x4);
-  Ranges.insert(AddressRange(0x400, 0x4ff), 0x5);
   Ranges.insert(AddressRange(0x500, 0x5ff), 0x6);
+  Ranges.insert(AddressRange(0x400, 0x4ff), 0x5);
   Ranges.insert(AddressRange(0x600, 0x6ff), 0x7);
+  EXPECT_EQ(Ranges.size(), 7u);
 
   Ranges.insert(AddressRange(0x150, 0x350), 0xff);
-  EXPECT_EQ(Ranges.size(), 5u);
-  EXPECT_EQ(Ranges[0].first, AddressRange(0x0, 0xff));
-  EXPECT_EQ(Ranges[0].second, 0x1);
-  EXPECT_EQ(Ranges[1].first, AddressRange(0x100, 0x3ff));
-  EXPECT_EQ(Ranges[1].second, 0xff);
-  EXPECT_EQ(Ranges[2].first, AddressRange(0x400, 0x4ff));
-  EXPECT_EQ(Ranges[2].second, 0x5);
-  EXPECT_EQ(Ranges[3].first, AddressRange(0x500, 0x5ff));
-  EXPECT_EQ(Ranges[3].second, 0x6);
-  EXPECT_EQ(Ranges[4].first, AddressRange(0x600, 0x6ff));
-  EXPECT_EQ(Ranges[4].second, 0x7);
+  EXPECT_EQ(Ranges.size(), 9u);
+  EXPECT_EQ(Ranges[0].Range, AddressRange(0x0, 0xff));
+  EXPECT_EQ(Ranges[0].Value, 0x1);
+  EXPECT_EQ(Ranges[1].Range, AddressRange(0x100, 0x1ff));
+  EXPECT_EQ(Ranges[1].Value, 0x2);
+  EXPECT_EQ(Ranges[2].Range, AddressRange(0x1ff, 0x200));
+  EXPECT_EQ(Ranges[2].Value, 0xff);
+  EXPECT_EQ(Ranges[3].Range, AddressRange(0x200, 0x2ff));
+  EXPECT_EQ(Ranges[3].Value, 0x3);
+  EXPECT_EQ(Ranges[4].Range, AddressRange(0x2ff, 0x300));
+  EXPECT_EQ(Ranges[4].Value, 0xff);
+  EXPECT_EQ(Ranges[5].Range, AddressRange(0x300, 0x3ff));
+  EXPECT_EQ(Ranges[5].Value, 0x4);
+  EXPECT_EQ(Ranges[6].Range, AddressRange(0x400, 0x4ff));
+  EXPECT_EQ(Ranges[6].Value, 0x5);
+  EXPECT_EQ(Ranges[7].Range, AddressRange(0x500, 0x5ff));
+  EXPECT_EQ(Ranges[7].Value, 0x6);
+  EXPECT_EQ(Ranges[8].Range, AddressRange(0x600, 0x6ff));
+  EXPECT_EQ(Ranges[8].Value, 0x7);
 
+  Ranges.insert(AddressRange(0x650, 0x700), 0x8);
   Ranges.insert(AddressRange(0x3ff, 0x400), 0x5);
-  EXPECT_EQ(Ranges.size(), 4u);
-  EXPECT_EQ(Ranges[0].first, AddressRange(0x0, 0xff));
-  EXPECT_EQ(Ranges[0].second, 0x1);
-  EXPECT_EQ(Ranges[1].first, AddressRange(0x100, 0x4ff));
-  EXPECT_EQ(Ranges[1].second, 0x5);
-  EXPECT_EQ(Ranges[2].first, AddressRange(0x500, 0x5ff));
-  EXPECT_EQ(Ranges[2].second, 0x6);
-  EXPECT_EQ(Ranges[3].first, AddressRange(0x600, 0x6ff));
-  EXPECT_EQ(Ranges[3].second, 0x7);
+  Ranges.insert(AddressRange(0x0, 0x40), 0xee);
+  EXPECT_EQ(Ranges.size(), 11u);
+  EXPECT_EQ(Ranges[0].Range, AddressRange(0x0, 0xff));
+  EXPECT_EQ(Ranges[0].Value, 0x1);
+  EXPECT_EQ(Ranges[1].Range, AddressRange(0x100, 0x1ff));
+  EXPECT_EQ(Ranges[1].Value, 0x2);
+  EXPECT_EQ(Ranges[2].Range, AddressRange(0x1ff, 0x200));
+  EXPECT_EQ(Ranges[2].Value, 0xff);
+  EXPECT_EQ(Ranges[3].Range, AddressRange(0x200, 0x2ff));
+  EXPECT_EQ(Ranges[3].Value, 0x3);
+  EXPECT_EQ(Ranges[4].Range, AddressRange(0x2ff, 0x300));
+  EXPECT_EQ(Ranges[4].Value, 0xff);
+  EXPECT_EQ(Ranges[5].Range, AddressRange(0x300, 0x3ff));
+  EXPECT_EQ(Ranges[5].Value, 0x4);
+  EXPECT_EQ(Ranges[6].Range, AddressRange(0x3ff, 0x400));
+  EXPECT_EQ(Ranges[6].Value, 0x5);
+  EXPECT_EQ(Ranges[7].Range, AddressRange(0x400, 0x4ff));
+  EXPECT_EQ(Ranges[7].Value, 0x5);
+  EXPECT_EQ(Ranges[8].Range, AddressRange(0x500, 0x5ff));
+  EXPECT_EQ(Ranges[8].Value, 0x6);
+  EXPECT_EQ(Ranges[9].Range, AddressRange(0x600, 0x6ff));
+  EXPECT_EQ(Ranges[9].Value, 0x7);
+  EXPECT_EQ(Ranges[10].Range, AddressRange(0x6ff, 0x700));
+  EXPECT_EQ(Ranges[10].Value, 0x8);
+}
+
+TEST(AddressRangeTest, TestRangesMapRandom) {
+  AddressRangesMap Ranges;
+  size_t NumElements = 100;
+
+  std::srand(std::time(nullptr));
+
+  // Fill ranges. Use the same mapped value.
+  for (size_t Idx = 0; Idx < NumElements; Idx++) {
+    uint64_t Start = static_cast<uint64_t>(std::rand() % 1000);
+    uint64_t End = Start + static_cast<uint64_t>(std::rand() % 1000);
+    Ranges.insert({Start, End}, 0xffLL);
+  }
+
+  // Check ranges.
+  for (size_t Idx = 0; Idx + 1 < Ranges.size(); Idx++) {
+    // Check that ranges are not intersected.
+    EXPECT_FALSE(Ranges[Idx].Range.intersects(Ranges[Idx + 1].Range));
+
+    // Check that ranges are sorted and not adjusted.
+    EXPECT_TRUE(Ranges[Idx].Range.end() <= Ranges[Idx + 1].Range.start());
+  }
+
+  Ranges.clear();
+  // Fill ranges. Use the various mapped value.
+  for (size_t Idx = 0; Idx < NumElements; Idx++) {
+    uint64_t Start = static_cast<uint64_t>(std::rand() % 1000);
+    uint64_t End = Start + static_cast<uint64_t>(std::rand() % 1000);
+    int64_t Value = static_cast<int64_t>(std::rand() % 10);
+    Ranges.insert({Start, End}, Value);
+  }
+
+  // Check ranges.
+  for (size_t Idx = 0; Idx + 1 < Ranges.size(); Idx++) {
+    // Check that ranges are not intersected.
+    EXPECT_FALSE(Ranges[Idx].Range.intersects(Ranges[Idx + 1].Range));
+
+    // Check that ranges are sorted and not adjusted.
+    EXPECT_TRUE(Ranges[Idx].Range.end() <= Ranges[Idx + 1].Range.start());
+  }
 }


        


More information about the llvm-commits mailing list