diff --git a/llvm/include/llvm/ADT/AddressRanges.h b/llvm/include/llvm/ADT/AddressRanges.h --- a/llvm/include/llvm/ADT/AddressRanges.h +++ b/llvm/include/llvm/ADT/AddressRanges.h @@ -28,7 +28,11 @@ 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 @@ 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 class AddressRangesBase { protected: - using Collection = SmallVector; + using Collection = SmallVector; 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 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 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 &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 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 { 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> - 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 operator[](size_t Idx) const { - return std::make_pair(Ranges[Idx], Values[Idx]); - } + AddressRange Range; + int64_t Value = 0; +}; -protected: - using ValuesCollection = SmallVector; - 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 { +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 --- a/llvm/include/llvm/DWARFLinker/DWARFLinkerCompileUnit.h +++ b/llvm/include/llvm/DWARFLinker/DWARFLinkerCompileUnit.h @@ -21,7 +21,7 @@ /// Mapped value in the address map is the offset to apply to the /// linked address. -using RangesTy = AddressRangesMap; +using RangesTy = AddressRangesMap; // FIXME: Delete this structure. struct PatchLocation { diff --git a/llvm/lib/DWARFLinker/DWARFLinker.cpp b/llvm/lib/DWARFLinker/DWARFLinker.cpp --- a/llvm/lib/DWARFLinker/DWARFLinker.cpp +++ b/llvm/lib/DWARFLinker/DWARFLinker.cpp @@ -1659,7 +1659,7 @@ DWARFDataExtractor RangeExtractor(OrigDwarf.getDWARFObj(), OrigDwarf.getDWARFObj().getRangesSection(), OrigDwarf.isLittleEndian(), AddressSize); - std::optional> CachedRange; + std::optional CachedRange; DWARFUnit &OrigUnit = Unit.getOrigUnit(); auto OrigUnitDie = OrigUnit.getUnitDIE(false); uint64_t UnitBaseAddress = @@ -1687,9 +1687,9 @@ } 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 @@ } 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 @@ // in NewRows. std::vector Seq; const auto &FunctionRanges = Unit.getFunctionRanges(); - std::optional> CurrRange; + std::optional 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 @@ // 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 @@ // for now do as dsymutil. // FIXME: Understand exactly what cases this addresses and // potentially remove it along with the Ranges map. - if (std::optional> Range = - Ranges.getRangeValueThatContains(Row.Address.Address)) - StopAddress = Row.Address.Address + (*Range).second; + if (std::optional Range = + Ranges.getRangeThatContains(Row.Address.Address)) + StopAddress = Row.Address.Address + (*Range).Value; } } if (StopAddress != -1ULL && !Seq.empty()) { @@ -1863,7 +1863,7 @@ 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 @@ // 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> Range = - Ranges.getRangeValueThatContains(Loc); + std::optional 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 @@ // 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 --- a/llvm/lib/DWARFLinker/DWARFStreamer.cpp +++ b/llvm/lib/DWARFLinker/DWARFStreamer.cpp @@ -402,10 +402,9 @@ // Linked addresses might end up in a different 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 --- 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 - -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 --- a/llvm/lib/Support/CMakeLists.txt +++ b/llvm/lib/Support/CMakeLists.txt @@ -117,7 +117,6 @@ 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 --- a/llvm/unittests/Support/AddressRangeTest.cpp +++ b/llvm/unittests/Support/AddressRangeTest.cpp @@ -149,8 +149,31 @@ 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(std::rand() % 1000); + uint64_t End = Start + static_cast(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 Ranges; + AddressRangesMap Ranges; EXPECT_EQ(Ranges.size(), 0u); EXPECT_TRUE(Ranges.empty()); @@ -162,73 +185,247 @@ 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(std::rand() % 1000); + uint64_t End = Start + static_cast(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(std::rand() % 1000); + uint64_t End = Start + static_cast(std::rand() % 1000); + int64_t Value = static_cast(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()); + } }