diff --git a/llvm/include/llvm/DWARFLinkerNext/List.h b/llvm/include/llvm/DWARFLinkerNext/List.h new file mode 100644 --- /dev/null +++ b/llvm/include/llvm/DWARFLinkerNext/List.h @@ -0,0 +1,113 @@ +//===- List.h ---------------------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DWARFLINKERNEXT_LIST_H +#define LLVM_DWARFLINKERNEXT_LIST_H + +#include "llvm/ADT/STLFunctionalExtras.h" +#include +#include +#include +#include + +namespace llvm { +namespace dwarflinker { + +/// Mixin adding "Next" field for the list entry. +template class ListItemMixin { +public: + T *Next = nullptr; +}; + +/// Simple list with lock free insertions. Other methods are not thread-safe. +/// ASSUMPTION: nodes are allocated and deallocated outside the list. +template class List { +public: + List() { + static_assert(std::is_base_of, T>::value, + "type parameter of List must derive from ListItemMixin"); + } + + /// \returns current head of the list. + T *getHead() { return Head; } + const T *getHead() const { return Head; } + + /// \returns true if the list is empty. + bool isEmpty() const { return Head == nullptr; } + + /// Adds \p NewItem into the list. Item added at front of the list. + void addAtFront(T *NewItem) { + T *CurHead = nullptr; + do { + CurHead = Head; + NewItem->Next = CurHead; + } while (!Head.compare_exchange_weak(CurHead, NewItem)); + } + + /// Enumerates the items in the list and calls the \p Handler for each item. + void enumerateItems(function_ref Handler) { + T *CurItem = Head; + while (CurItem != nullptr) { + T *NextItem = CurItem->Next; + Handler(*CurItem); + + CurItem = NextItem; + } + } + + /// Enumerates the items in the list and calls the \p handler for each item. + void enumerateItems(function_ref Handler) const { + T *CurItem = Head; + while (CurItem != nullptr) { + T *NextItem = CurItem->Next; + Handler(*CurItem); + + CurItem = NextItem; + } + } + + /// Sets list head to null. + void erase() { Head = nullptr; } + + /// Sorts list items according to the \p Comparator. + void sortItems(function_ref Comparator) { + // TODO: It might be beneficial to implement sorting of items + // using merge sort. + std::vector Types; + enumerateItems([&](T &Type) { Types.push_back(&Type); }); + + if (Types.size()) { + std::sort(Types.begin(), Types.end(), Comparator); + + Head = *Types.begin(); + Types.back()->Next = nullptr; + + for (size_t CurIdx = 1; CurIdx < Types.size(); CurIdx++) { + Types[CurIdx - 1]->Next = Types[CurIdx]; + } + } + } + + /// Reverse list items. + void reverseItems() { + T *nextItem = nullptr; + enumerateItems([&](T &Type) { + Type.Next = nextItem; + nextItem = &Type; + }); + Head = nextItem; + } + +protected: + std::atomic Head = {nullptr}; +}; + +} // end of namespace dwarflinker +} // end namespace llvm + +#endif // LLVM_DWARFLINKERNEXT_LIST_H diff --git a/llvm/unittests/CMakeLists.txt b/llvm/unittests/CMakeLists.txt --- a/llvm/unittests/CMakeLists.txt +++ b/llvm/unittests/CMakeLists.txt @@ -24,6 +24,7 @@ add_subdirectory(DebugInfo) add_subdirectory(Debuginfod) add_subdirectory(Demangle) +add_subdirectory(DWARFLinkerNext) add_subdirectory(ExecutionEngine) add_subdirectory(FileCheck) add_subdirectory(Frontend) diff --git a/llvm/unittests/DWARFLinkerNext/CMakeLists.txt b/llvm/unittests/DWARFLinkerNext/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/llvm/unittests/DWARFLinkerNext/CMakeLists.txt @@ -0,0 +1,11 @@ +set(LLVM_LINK_COMPONENTS + Support + ) + +add_llvm_unittest(DWARFLinkerNextTests + ListTest.cpp + ) + +target_link_libraries(DWARFLinkerNextTests PRIVATE LLVMTestingSupport) + +add_dependencies(DWARFLinkerNextTests intrinsics_gen) diff --git a/llvm/unittests/DWARFLinkerNext/ListTest.cpp b/llvm/unittests/DWARFLinkerNext/ListTest.cpp new file mode 100644 --- /dev/null +++ b/llvm/unittests/DWARFLinkerNext/ListTest.cpp @@ -0,0 +1,113 @@ +//===- llvm/unittest/DWARFLinkerNext/ListTest.cpp -------------------------===// +// +// 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/DWARFLinkerNext/List.h" +#include "llvm/Support/Parallel.h" +#include "gtest/gtest.h" +#include + +using namespace llvm; +using namespace dwarflinker; + +namespace { + +struct Number : public ListItemMixin { + int Num = 0; +}; + +inline bool operator==(const Number &LHS, const Number &RHS) { + return LHS.Num == RHS.Num; +} + +TEST(ListTest, TestList) { + + Number N1; + N1.Num = 1; + + List L; + + EXPECT_TRUE(L.isEmpty()); + EXPECT_TRUE(L.getHead() == nullptr); + + // Add first item. + L.addAtFront(&N1); + EXPECT_FALSE(L.isEmpty()); + EXPECT_TRUE(L.getHead() == &N1); + + Number N2; + N2.Num = 2; + + // Add second item. + L.addAtFront(&N2); + EXPECT_FALSE(L.isEmpty()); + EXPECT_TRUE(L.getHead() == &N2); + EXPECT_TRUE(L.getHead()->Next == &N1); + + Number N3; + N3.Num = 3; + + // Add third item. + L.addAtFront(&N3); + EXPECT_FALSE(L.isEmpty()); + EXPECT_TRUE(L.getHead() == &N3); + EXPECT_TRUE(L.getHead()->Next == &N2); + EXPECT_TRUE(L.getHead()->Next->Next == &N1); + + // Check sorting. + L.sortItems( + [](Number *LHS, Number *RHS) -> bool { return LHS->Num < RHS->Num; }); + + EXPECT_TRUE(L.getHead()->Num == 1); + EXPECT_TRUE(L.getHead()->Next->Num == 2); + EXPECT_TRUE(L.getHead()->Next->Next->Num == 3); + + // Check reverse items. + L.reverseItems(); + + EXPECT_TRUE(L.getHead()->Num == 3); + EXPECT_TRUE(L.getHead()->Next->Num == 2); + EXPECT_TRUE(L.getHead()->Next->Next->Num == 1); + + // Check items enumerations. + L.enumerateItems( + [](Number &N) { EXPECT_TRUE(N.Num == 1 || N.Num == 2 || N.Num == 3); }); + + // Check cleanup. + L.erase(); + EXPECT_TRUE(L.isEmpty()); + EXPECT_TRUE(L.getHead() == nullptr); +} + +TEST(ListTest, TestListParallel) { + std::vector Data; + + // Create data. + for (int Idx = 10; Idx < 1000; Idx++) { + Number N; + N.Num = Idx; + Data.push_back(N); + } + + List L; + + // Add data into the list. + parallelFor(0, Data.size(), [&](size_t Idx) { L.addAtFront(&Data[Idx]); }); + + EXPECT_FALSE(L.isEmpty()); + + // Check that list contains exactly source data. + size_t NumElements = 0; + L.enumerateItems([&](const Number &N) { + NumElements++; + + EXPECT_TRUE(std::find(Data.begin(), Data.end(), N) != Data.end()); + }); + EXPECT_TRUE(NumElements == Data.size()); +} + +} // anonymous namespace