[DWARFLinkerParallel] Add StringPool class.
authorAlexey Lapshin <a.v.lapshin@mail.ru>
Wed, 29 Mar 2023 10:58:21 +0000 (12:58 +0200)
committerAlexey Lapshin <a.v.lapshin@mail.ru>
Fri, 31 Mar 2023 10:11:43 +0000 (12:11 +0200)
This patch is extracted from D96035. It adds StringPool class.
StringPool allows to store strings in parallel. It also allows
to have string data associated with the concrete string.

Reviewed By: JDevlieghere

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

llvm/include/llvm/DWARFLinkerParallel/StringPool.h [new file with mode: 0644]
llvm/lib/DWARFLinkerParallel/CMakeLists.txt
llvm/lib/DWARFLinkerParallel/StringPool.cpp [new file with mode: 0644]
llvm/unittests/DWARFLinkerParallel/CMakeLists.txt
llvm/unittests/DWARFLinkerParallel/StringPoolTest.cpp [new file with mode: 0644]

diff --git a/llvm/include/llvm/DWARFLinkerParallel/StringPool.h b/llvm/include/llvm/DWARFLinkerParallel/StringPool.h
new file mode 100644 (file)
index 0000000..52fb64c
--- /dev/null
@@ -0,0 +1,85 @@
+//===- StringPool.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_DWARFLINKERPARALLEL_STRINGPOOL_H
+#define LLVM_DWARFLINKERPARALLEL_STRINGPOOL_H
+
+#include "llvm/ADT/ConcurrentHashtable.h"
+#include "llvm/CodeGen/DwarfStringPoolEntry.h"
+#include "llvm/Support/Allocator.h"
+#include <string>
+#include <string_view>
+
+namespace llvm {
+namespace dwarflinker_parallel {
+
+/// StringEntry keeps data of the string: the length, external offset
+/// and a string body which is placed right after StringEntry.
+using StringEntry = StringMapEntry<DwarfStringPoolEntry *>;
+
+class PerThreadStringAllocator
+    : public AllocatorBase<PerThreadStringAllocator> {
+public:
+  inline LLVM_ATTRIBUTE_RETURNS_NONNULL void *Allocate(size_t Size,
+                                                       size_t Alignment) {
+    return ThreadLocalAllocator.Allocate(Size, Align(Alignment));
+  }
+
+  // Pull in base class overloads.
+  using AllocatorBase<PerThreadStringAllocator>::Allocate;
+
+private:
+  static thread_local BumpPtrAllocator ThreadLocalAllocator;
+};
+
+class StringPoolEntryInfo {
+public:
+  /// \returns Hash value for the specified \p Key.
+  static inline uint64_t getHashValue(const StringRef &Key) {
+    return xxHash64(Key);
+  }
+
+  /// \returns true if both \p LHS and \p RHS are equal.
+  static inline bool isEqual(const StringRef &LHS, const StringRef &RHS) {
+    return LHS == RHS;
+  }
+
+  /// \returns key for the specified \p KeyData.
+  static inline StringRef getKey(const StringEntry &KeyData) {
+    return KeyData.getKey();
+  }
+
+  /// \returns newly created object of KeyDataTy type.
+  static inline StringEntry *create(const StringRef &Key,
+                                    PerThreadStringAllocator &Allocator) {
+    return StringEntry::create(Key, Allocator);
+  }
+};
+
+class StringPool : public ConcurrentHashTableByPtr<StringRef, StringEntry,
+                                                   PerThreadStringAllocator,
+                                                   StringPoolEntryInfo> {
+public:
+  StringPool()
+      : ConcurrentHashTableByPtr<StringRef, StringEntry,
+                                 PerThreadStringAllocator, StringPoolEntryInfo>(
+            Allocator) {}
+
+  StringPool(size_t InitialSize)
+      : ConcurrentHashTableByPtr<StringRef, StringEntry,
+                                 PerThreadStringAllocator, StringPoolEntryInfo>(
+            Allocator, InitialSize) {}
+
+private:
+  PerThreadStringAllocator Allocator;
+};
+
+} // end of namespace dwarflinker_parallel
+} // end namespace llvm
+
+#endif // LLVM_DWARFLINKERPARALLEL_STRINGPOOL_H
index 298fb98..a9bad6c 100644 (file)
@@ -1,5 +1,6 @@
 add_llvm_component_library(LLVMDWARFLinkerParallel
   DWARFLinker.cpp
+  StringPool.cpp
 
   ADDITIONAL_HEADER_DIRS
   ${LLVM_MAIN_INCLUDE_DIR}/llvm/DWARFLinkerParallel
diff --git a/llvm/lib/DWARFLinkerParallel/StringPool.cpp b/llvm/lib/DWARFLinkerParallel/StringPool.cpp
new file mode 100644 (file)
index 0000000..aa8bc61
--- /dev/null
@@ -0,0 +1,12 @@
+//=== StringPool.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/DWARFLinkerParallel/StringPool.h"
+
+thread_local llvm::BumpPtrAllocator
+    llvm::dwarflinker_parallel::PerThreadStringAllocator::ThreadLocalAllocator;
index e81e09c..ca2cb47 100644 (file)
@@ -5,6 +5,7 @@ set(LLVM_LINK_COMPONENTS
 
 add_llvm_unittest(DWARFLinkerParallelTests
   DWARFLinkerTest.cpp
+  StringPoolTest.cpp
   )
 
 target_link_libraries(DWARFLinkerParallelTests PRIVATE LLVMTestingSupport)
diff --git a/llvm/unittests/DWARFLinkerParallel/StringPoolTest.cpp b/llvm/unittests/DWARFLinkerParallel/StringPoolTest.cpp
new file mode 100644 (file)
index 0000000..2e36cde
--- /dev/null
@@ -0,0 +1,62 @@
+//===- llvm/unittest/DWARFLinkerParallel/StringPoolTest.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/DWARFLinkerParallel/StringPool.h"
+#include "llvm/Support/Parallel.h"
+#include "gtest/gtest.h"
+#include <cstdlib>
+
+using namespace llvm;
+using namespace dwarflinker_parallel;
+
+namespace {
+
+TEST(StringPoolTest, TestStringPool) {
+  StringPool Strings;
+
+  std::pair<StringEntry *, bool> Entry = Strings.insert("test");
+  EXPECT_TRUE(Entry.second);
+  EXPECT_TRUE(Entry.first->getKey() == "test");
+  EXPECT_TRUE(Entry.first->second == nullptr);
+
+  StringEntry *EntryPtr = Entry.first;
+
+  Entry = Strings.insert("test");
+  EXPECT_FALSE(Entry.second);
+  EXPECT_TRUE(Entry.first->getKey() == "test");
+  EXPECT_TRUE(Entry.first->second == nullptr);
+  EXPECT_TRUE(EntryPtr == Entry.first);
+
+  Entry = Strings.insert("test2");
+  EXPECT_TRUE(Entry.second);
+  EXPECT_TRUE(Entry.first->getKey() == "test2");
+  EXPECT_TRUE(Entry.first->second == nullptr);
+  EXPECT_TRUE(EntryPtr != Entry.first);
+}
+
+TEST(StringPoolTest, TestStringPoolParallel) {
+  StringPool Strings;
+
+  // Add data.
+  parallelFor(0, 1000, [&](size_t Idx) {
+    std::pair<StringEntry *, bool> Entry = Strings.insert(std::to_string(Idx));
+    EXPECT_TRUE(Entry.second);
+    EXPECT_TRUE(Entry.first->getKey() == std::to_string(Idx));
+    EXPECT_TRUE(Entry.first->second == nullptr);
+  });
+
+  // Check data.
+  parallelFor(0, 1000, [&](size_t Idx) {
+    std::pair<StringEntry *, bool> Entry = Strings.insert(std::to_string(Idx));
+    EXPECT_FALSE(Entry.second);
+    EXPECT_TRUE(Entry.first->getKey() == std::to_string(Idx));
+    EXPECT_TRUE(Entry.first->second == nullptr);
+  });
+}
+
+} // anonymous namespace