mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-29 07:16:08 +00:00

This patch creates DWARFLinkerBase library, places DWARFLinker code into DWARFLinker\Classic, places DWARFLinkerParallel into DWARFLinker\Parallel. updates BOLT to use new library. This patch is NFC.
168 lines
4.9 KiB
C++
168 lines
4.9 KiB
C++
//===- ArrayList.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_LIB_DWARFLINKER_PARALLEL_ARRAYLIST_H
|
|
#define LLVM_LIB_DWARFLINKER_PARALLEL_ARRAYLIST_H
|
|
|
|
#include "llvm/Support/PerThreadBumpPtrAllocator.h"
|
|
#include <atomic>
|
|
|
|
namespace llvm {
|
|
namespace dwarf_linker {
|
|
namespace parallel {
|
|
|
|
/// This class is a simple list of T structures. It keeps elements as
|
|
/// pre-allocated groups to save memory for each element's next pointer.
|
|
/// It allocates internal data using specified per-thread BumpPtrAllocator.
|
|
/// Method add() can be called asynchronously.
|
|
template <typename T, size_t ItemsGroupSize = 512> class ArrayList {
|
|
public:
|
|
ArrayList(llvm::parallel::PerThreadBumpPtrAllocator *Allocator)
|
|
: Allocator(Allocator) {}
|
|
|
|
/// Add specified \p Item to the list.
|
|
T &add(const T &Item) {
|
|
assert(Allocator);
|
|
|
|
// Allocate head group if it is not allocated yet.
|
|
while (!LastGroup) {
|
|
if (allocateNewGroup(GroupsHead))
|
|
LastGroup = GroupsHead.load();
|
|
}
|
|
|
|
ItemsGroup *CurGroup;
|
|
size_t CurItemsCount;
|
|
do {
|
|
CurGroup = LastGroup;
|
|
CurItemsCount = CurGroup->ItemsCount.fetch_add(1);
|
|
|
|
// Check whether current group is full.
|
|
if (CurItemsCount < ItemsGroupSize)
|
|
break;
|
|
|
|
// Allocate next group if necessary.
|
|
if (!CurGroup->Next)
|
|
allocateNewGroup(CurGroup->Next);
|
|
|
|
LastGroup.compare_exchange_weak(CurGroup, CurGroup->Next);
|
|
} while (true);
|
|
|
|
// Store item into the current group.
|
|
CurGroup->Items[CurItemsCount] = Item;
|
|
return CurGroup->Items[CurItemsCount];
|
|
}
|
|
|
|
using ItemHandlerTy = function_ref<void(T &)>;
|
|
|
|
/// Enumerate all items and apply specified \p Handler to each.
|
|
void forEach(ItemHandlerTy Handler) {
|
|
for (ItemsGroup *CurGroup = GroupsHead; CurGroup;
|
|
CurGroup = CurGroup->Next) {
|
|
for (T &Item : *CurGroup)
|
|
Handler(Item);
|
|
}
|
|
}
|
|
|
|
/// Check whether list is empty.
|
|
bool empty() { return !GroupsHead; }
|
|
|
|
/// Erase list.
|
|
void erase() {
|
|
GroupsHead = nullptr;
|
|
LastGroup = nullptr;
|
|
}
|
|
|
|
void sort(function_ref<bool(const T &LHS, const T &RHS)> Comparator) {
|
|
SmallVector<T> SortedItems;
|
|
forEach([&](T &Item) { SortedItems.push_back(Item); });
|
|
|
|
if (SortedItems.size()) {
|
|
std::sort(SortedItems.begin(), SortedItems.end(), Comparator);
|
|
|
|
size_t SortedItemIdx = 0;
|
|
forEach([&](T &Item) { Item = SortedItems[SortedItemIdx++]; });
|
|
assert(SortedItemIdx == SortedItems.size());
|
|
}
|
|
}
|
|
|
|
size_t size() {
|
|
size_t Result = 0;
|
|
|
|
for (ItemsGroup *CurGroup = GroupsHead; CurGroup != nullptr;
|
|
CurGroup = CurGroup->Next)
|
|
Result += CurGroup->getItemsCount();
|
|
|
|
return Result;
|
|
}
|
|
|
|
protected:
|
|
struct ItemsGroup {
|
|
using ArrayTy = std::array<T, ItemsGroupSize>;
|
|
|
|
// Array of items kept by this group.
|
|
ArrayTy Items;
|
|
|
|
// Pointer to the next items group.
|
|
std::atomic<ItemsGroup *> Next = nullptr;
|
|
|
|
// Number of items in this group.
|
|
// NOTE: ItemsCount could be inaccurate as it might be incremented by
|
|
// several threads. Use getItemsCount() method to get real number of items
|
|
// inside ItemsGroup.
|
|
std::atomic<size_t> ItemsCount = 0;
|
|
|
|
size_t getItemsCount() const {
|
|
return std::min(ItemsCount.load(), ItemsGroupSize);
|
|
}
|
|
|
|
typename ArrayTy::iterator begin() { return Items.begin(); }
|
|
typename ArrayTy::iterator end() { return Items.begin() + getItemsCount(); }
|
|
};
|
|
|
|
// Allocate new group. Put allocated group into the \p AtomicGroup if
|
|
// it is empty. If \p AtomicGroup is filled by another thread then
|
|
// put allocated group into the end of groups list.
|
|
// \returns true if allocated group is put into the \p AtomicGroup.
|
|
bool allocateNewGroup(std::atomic<ItemsGroup *> &AtomicGroup) {
|
|
ItemsGroup *CurGroup = nullptr;
|
|
|
|
// Allocate new group.
|
|
ItemsGroup *NewGroup = Allocator->Allocate<ItemsGroup>();
|
|
NewGroup->ItemsCount = 0;
|
|
NewGroup->Next = nullptr;
|
|
|
|
// Try to replace current group with allocated one.
|
|
if (AtomicGroup.compare_exchange_weak(CurGroup, NewGroup))
|
|
return true;
|
|
|
|
// Put allocated group as last group.
|
|
while (CurGroup) {
|
|
ItemsGroup *NextGroup = CurGroup->Next;
|
|
|
|
if (!NextGroup) {
|
|
if (CurGroup->Next.compare_exchange_weak(NextGroup, NewGroup))
|
|
break;
|
|
}
|
|
|
|
CurGroup = NextGroup;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
std::atomic<ItemsGroup *> GroupsHead = nullptr;
|
|
std::atomic<ItemsGroup *> LastGroup = nullptr;
|
|
llvm::parallel::PerThreadBumpPtrAllocator *Allocator = nullptr;
|
|
};
|
|
|
|
} // end of namespace parallel
|
|
} // end of namespace dwarf_linker
|
|
} // end of namespace llvm
|
|
|
|
#endif // LLVM_LIB_DWARFLINKER_PARALLEL_ARRAYLIST_H
|