xref: /freebsd-src/contrib/llvm-project/llvm/lib/DWARFLinker/Parallel/ArrayList.h (revision 1db9f3b21e39176dd5b67cf8ac378633b172463e)
1*1db9f3b2SDimitry Andric //===- ArrayList.h ----------------------------------------------*- C++ -*-===//
2*1db9f3b2SDimitry Andric //
3*1db9f3b2SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*1db9f3b2SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*1db9f3b2SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*1db9f3b2SDimitry Andric //
7*1db9f3b2SDimitry Andric //===----------------------------------------------------------------------===//
8*1db9f3b2SDimitry Andric 
9*1db9f3b2SDimitry Andric #ifndef LLVM_LIB_DWARFLINKER_PARALLEL_ARRAYLIST_H
10*1db9f3b2SDimitry Andric #define LLVM_LIB_DWARFLINKER_PARALLEL_ARRAYLIST_H
11*1db9f3b2SDimitry Andric 
12*1db9f3b2SDimitry Andric #include "llvm/Support/PerThreadBumpPtrAllocator.h"
13*1db9f3b2SDimitry Andric #include <atomic>
14*1db9f3b2SDimitry Andric 
15*1db9f3b2SDimitry Andric namespace llvm {
16*1db9f3b2SDimitry Andric namespace dwarf_linker {
17*1db9f3b2SDimitry Andric namespace parallel {
18*1db9f3b2SDimitry Andric 
19*1db9f3b2SDimitry Andric /// This class is a simple list of T structures. It keeps elements as
20*1db9f3b2SDimitry Andric /// pre-allocated groups to save memory for each element's next pointer.
21*1db9f3b2SDimitry Andric /// It allocates internal data using specified per-thread BumpPtrAllocator.
22*1db9f3b2SDimitry Andric /// Method add() can be called asynchronously.
23*1db9f3b2SDimitry Andric template <typename T, size_t ItemsGroupSize = 512> class ArrayList {
24*1db9f3b2SDimitry Andric public:
ArrayList(llvm::parallel::PerThreadBumpPtrAllocator * Allocator)25*1db9f3b2SDimitry Andric   ArrayList(llvm::parallel::PerThreadBumpPtrAllocator *Allocator)
26*1db9f3b2SDimitry Andric       : Allocator(Allocator) {}
27*1db9f3b2SDimitry Andric 
28*1db9f3b2SDimitry Andric   /// Add specified \p Item to the list.
add(const T & Item)29*1db9f3b2SDimitry Andric   T &add(const T &Item) {
30*1db9f3b2SDimitry Andric     assert(Allocator);
31*1db9f3b2SDimitry Andric 
32*1db9f3b2SDimitry Andric     // Allocate head group if it is not allocated yet.
33*1db9f3b2SDimitry Andric     while (!LastGroup) {
34*1db9f3b2SDimitry Andric       if (allocateNewGroup(GroupsHead))
35*1db9f3b2SDimitry Andric         LastGroup = GroupsHead.load();
36*1db9f3b2SDimitry Andric     }
37*1db9f3b2SDimitry Andric 
38*1db9f3b2SDimitry Andric     ItemsGroup *CurGroup;
39*1db9f3b2SDimitry Andric     size_t CurItemsCount;
40*1db9f3b2SDimitry Andric     do {
41*1db9f3b2SDimitry Andric       CurGroup = LastGroup;
42*1db9f3b2SDimitry Andric       CurItemsCount = CurGroup->ItemsCount.fetch_add(1);
43*1db9f3b2SDimitry Andric 
44*1db9f3b2SDimitry Andric       // Check whether current group is full.
45*1db9f3b2SDimitry Andric       if (CurItemsCount < ItemsGroupSize)
46*1db9f3b2SDimitry Andric         break;
47*1db9f3b2SDimitry Andric 
48*1db9f3b2SDimitry Andric       // Allocate next group if necessary.
49*1db9f3b2SDimitry Andric       if (!CurGroup->Next)
50*1db9f3b2SDimitry Andric         allocateNewGroup(CurGroup->Next);
51*1db9f3b2SDimitry Andric 
52*1db9f3b2SDimitry Andric       LastGroup.compare_exchange_weak(CurGroup, CurGroup->Next);
53*1db9f3b2SDimitry Andric     } while (true);
54*1db9f3b2SDimitry Andric 
55*1db9f3b2SDimitry Andric     // Store item into the current group.
56*1db9f3b2SDimitry Andric     CurGroup->Items[CurItemsCount] = Item;
57*1db9f3b2SDimitry Andric     return CurGroup->Items[CurItemsCount];
58*1db9f3b2SDimitry Andric   }
59*1db9f3b2SDimitry Andric 
60*1db9f3b2SDimitry Andric   using ItemHandlerTy = function_ref<void(T &)>;
61*1db9f3b2SDimitry Andric 
62*1db9f3b2SDimitry Andric   /// Enumerate all items and apply specified \p Handler to each.
forEach(ItemHandlerTy Handler)63*1db9f3b2SDimitry Andric   void forEach(ItemHandlerTy Handler) {
64*1db9f3b2SDimitry Andric     for (ItemsGroup *CurGroup = GroupsHead; CurGroup;
65*1db9f3b2SDimitry Andric          CurGroup = CurGroup->Next) {
66*1db9f3b2SDimitry Andric       for (T &Item : *CurGroup)
67*1db9f3b2SDimitry Andric         Handler(Item);
68*1db9f3b2SDimitry Andric     }
69*1db9f3b2SDimitry Andric   }
70*1db9f3b2SDimitry Andric 
71*1db9f3b2SDimitry Andric   /// Check whether list is empty.
empty()72*1db9f3b2SDimitry Andric   bool empty() { return !GroupsHead; }
73*1db9f3b2SDimitry Andric 
74*1db9f3b2SDimitry Andric   /// Erase list.
erase()75*1db9f3b2SDimitry Andric   void erase() {
76*1db9f3b2SDimitry Andric     GroupsHead = nullptr;
77*1db9f3b2SDimitry Andric     LastGroup = nullptr;
78*1db9f3b2SDimitry Andric   }
79*1db9f3b2SDimitry Andric 
sort(function_ref<bool (const T & LHS,const T & RHS)> Comparator)80*1db9f3b2SDimitry Andric   void sort(function_ref<bool(const T &LHS, const T &RHS)> Comparator) {
81*1db9f3b2SDimitry Andric     SmallVector<T> SortedItems;
82*1db9f3b2SDimitry Andric     forEach([&](T &Item) { SortedItems.push_back(Item); });
83*1db9f3b2SDimitry Andric 
84*1db9f3b2SDimitry Andric     if (SortedItems.size()) {
85*1db9f3b2SDimitry Andric       std::sort(SortedItems.begin(), SortedItems.end(), Comparator);
86*1db9f3b2SDimitry Andric 
87*1db9f3b2SDimitry Andric       size_t SortedItemIdx = 0;
88*1db9f3b2SDimitry Andric       forEach([&](T &Item) { Item = SortedItems[SortedItemIdx++]; });
89*1db9f3b2SDimitry Andric       assert(SortedItemIdx == SortedItems.size());
90*1db9f3b2SDimitry Andric     }
91*1db9f3b2SDimitry Andric   }
92*1db9f3b2SDimitry Andric 
size()93*1db9f3b2SDimitry Andric   size_t size() {
94*1db9f3b2SDimitry Andric     size_t Result = 0;
95*1db9f3b2SDimitry Andric 
96*1db9f3b2SDimitry Andric     for (ItemsGroup *CurGroup = GroupsHead; CurGroup != nullptr;
97*1db9f3b2SDimitry Andric          CurGroup = CurGroup->Next)
98*1db9f3b2SDimitry Andric       Result += CurGroup->getItemsCount();
99*1db9f3b2SDimitry Andric 
100*1db9f3b2SDimitry Andric     return Result;
101*1db9f3b2SDimitry Andric   }
102*1db9f3b2SDimitry Andric 
103*1db9f3b2SDimitry Andric protected:
104*1db9f3b2SDimitry Andric   struct ItemsGroup {
105*1db9f3b2SDimitry Andric     using ArrayTy = std::array<T, ItemsGroupSize>;
106*1db9f3b2SDimitry Andric 
107*1db9f3b2SDimitry Andric     // Array of items kept by this group.
108*1db9f3b2SDimitry Andric     ArrayTy Items;
109*1db9f3b2SDimitry Andric 
110*1db9f3b2SDimitry Andric     // Pointer to the next items group.
111*1db9f3b2SDimitry Andric     std::atomic<ItemsGroup *> Next = nullptr;
112*1db9f3b2SDimitry Andric 
113*1db9f3b2SDimitry Andric     // Number of items in this group.
114*1db9f3b2SDimitry Andric     // NOTE: ItemsCount could be inaccurate as it might be incremented by
115*1db9f3b2SDimitry Andric     // several threads. Use getItemsCount() method to get real number of items
116*1db9f3b2SDimitry Andric     // inside ItemsGroup.
117*1db9f3b2SDimitry Andric     std::atomic<size_t> ItemsCount = 0;
118*1db9f3b2SDimitry Andric 
getItemsCountItemsGroup119*1db9f3b2SDimitry Andric     size_t getItemsCount() const {
120*1db9f3b2SDimitry Andric       return std::min(ItemsCount.load(), ItemsGroupSize);
121*1db9f3b2SDimitry Andric     }
122*1db9f3b2SDimitry Andric 
beginItemsGroup123*1db9f3b2SDimitry Andric     typename ArrayTy::iterator begin() { return Items.begin(); }
endItemsGroup124*1db9f3b2SDimitry Andric     typename ArrayTy::iterator end() { return Items.begin() + getItemsCount(); }
125*1db9f3b2SDimitry Andric   };
126*1db9f3b2SDimitry Andric 
127*1db9f3b2SDimitry Andric   // Allocate new group. Put allocated group into the \p AtomicGroup if
128*1db9f3b2SDimitry Andric   // it is empty. If \p AtomicGroup is filled by another thread then
129*1db9f3b2SDimitry Andric   // put allocated group into the end of groups list.
130*1db9f3b2SDimitry Andric   // \returns true if allocated group is put into the \p AtomicGroup.
allocateNewGroup(std::atomic<ItemsGroup * > & AtomicGroup)131*1db9f3b2SDimitry Andric   bool allocateNewGroup(std::atomic<ItemsGroup *> &AtomicGroup) {
132*1db9f3b2SDimitry Andric     ItemsGroup *CurGroup = nullptr;
133*1db9f3b2SDimitry Andric 
134*1db9f3b2SDimitry Andric     // Allocate new group.
135*1db9f3b2SDimitry Andric     ItemsGroup *NewGroup = Allocator->Allocate<ItemsGroup>();
136*1db9f3b2SDimitry Andric     NewGroup->ItemsCount = 0;
137*1db9f3b2SDimitry Andric     NewGroup->Next = nullptr;
138*1db9f3b2SDimitry Andric 
139*1db9f3b2SDimitry Andric     // Try to replace current group with allocated one.
140*1db9f3b2SDimitry Andric     if (AtomicGroup.compare_exchange_weak(CurGroup, NewGroup))
141*1db9f3b2SDimitry Andric       return true;
142*1db9f3b2SDimitry Andric 
143*1db9f3b2SDimitry Andric     // Put allocated group as last group.
144*1db9f3b2SDimitry Andric     while (CurGroup) {
145*1db9f3b2SDimitry Andric       ItemsGroup *NextGroup = CurGroup->Next;
146*1db9f3b2SDimitry Andric 
147*1db9f3b2SDimitry Andric       if (!NextGroup) {
148*1db9f3b2SDimitry Andric         if (CurGroup->Next.compare_exchange_weak(NextGroup, NewGroup))
149*1db9f3b2SDimitry Andric           break;
150*1db9f3b2SDimitry Andric       }
151*1db9f3b2SDimitry Andric 
152*1db9f3b2SDimitry Andric       CurGroup = NextGroup;
153*1db9f3b2SDimitry Andric     }
154*1db9f3b2SDimitry Andric 
155*1db9f3b2SDimitry Andric     return false;
156*1db9f3b2SDimitry Andric   }
157*1db9f3b2SDimitry Andric 
158*1db9f3b2SDimitry Andric   std::atomic<ItemsGroup *> GroupsHead = nullptr;
159*1db9f3b2SDimitry Andric   std::atomic<ItemsGroup *> LastGroup = nullptr;
160*1db9f3b2SDimitry Andric   llvm::parallel::PerThreadBumpPtrAllocator *Allocator = nullptr;
161*1db9f3b2SDimitry Andric };
162*1db9f3b2SDimitry Andric 
163*1db9f3b2SDimitry Andric } // end of namespace parallel
164*1db9f3b2SDimitry Andric } // end of namespace dwarf_linker
165*1db9f3b2SDimitry Andric } // end of namespace llvm
166*1db9f3b2SDimitry Andric 
167*1db9f3b2SDimitry Andric #endif // LLVM_LIB_DWARFLINKER_PARALLEL_ARRAYLIST_H
168