diff --git a/llvm/include/llvm/DWARFLinkerParallel/StringPool.h b/llvm/include/llvm/DWARFLinkerParallel/StringPool.h --- a/llvm/include/llvm/DWARFLinkerParallel/StringPool.h +++ b/llvm/include/llvm/DWARFLinkerParallel/StringPool.h @@ -75,6 +75,8 @@ PerThreadStringAllocator, StringPoolEntryInfo>( Allocator, InitialSize) {} + PerThreadStringAllocator &getAllocatorRef() { return Allocator; } + private: PerThreadStringAllocator Allocator; }; diff --git a/llvm/include/llvm/DWARFLinkerParallel/StringTable.h b/llvm/include/llvm/DWARFLinkerParallel/StringTable.h new file mode 100644 --- /dev/null +++ b/llvm/include/llvm/DWARFLinkerParallel/StringTable.h @@ -0,0 +1,84 @@ +//===- StringTable.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_STRINGTABLE_H +#define LLVM_DWARFLINKERPARALLEL_STRINGTABLE_H + +#include "llvm/ADT/SmallVector.h" +#include "llvm/DWARFLinkerParallel/StringPool.h" + +namespace llvm { +namespace dwarflinker_parallel { + +using StringsVector = SmallVector; + +/// This class prepares strings for emission into .debug_str table: +/// translates string if necessary, assigns index and offset, keeps in order. +class StringTable { +public: + StringTable(StringPool &Strings, + std::function StringsTranslator) + : Strings(Strings), StringsTranslator(StringsTranslator) {} + ~StringTable() {} + + /// Add string to the vector of strings which should be emitted. + /// Translate input string if neccessary, assign index and offset. + /// \returns updated string entry. + StringEntry *add(StringEntry *String) { + // Translate string if necessary. + if (StringsTranslator) + String = Strings.insert(StringsTranslator(String->first())).first; + + // Store String for emission and assign index and offset. + if (String->getValue() == nullptr) { + DwarfStringPoolEntry *NewEntry = + Strings.getAllocatorRef().Allocate(); + + NewEntry->Symbol = nullptr; + NewEntry->Index = StringEntriesForEmission.size(); + + if (StringEntriesForEmission.empty()) + NewEntry->Offset = 0; + else { + StringEntry *PrevString = StringEntriesForEmission.back(); + NewEntry->Offset = + PrevString->getValue()->Offset + PrevString->getKeyLength() + 1; + } + + String->getValue() = NewEntry; + StringEntriesForEmission.push_back(String); + } + + return String; + } + + /// Erase contents of StringsForEmission. + void clear() { StringEntriesForEmission.clear(); } + + /// Enumerate all strings in sequential order and call \p Handler for each + /// string. + void forEach(function_ref Handler) const { + for (const StringEntry *Entry : StringEntriesForEmission) + Handler(*Entry); + } + +protected: + /// List of strings for emission. + StringsVector StringEntriesForEmission; + + /// String pool for the translated strings. + StringPool &Strings; + + /// Translator for the strings. + std::function StringsTranslator; +}; + +} // end of namespace dwarflinker_parallel +} // end namespace llvm + +#endif // LLVM_DWARFLINKERPARALLEL_STRINGTABLE_H diff --git a/llvm/unittests/DWARFLinkerParallel/CMakeLists.txt b/llvm/unittests/DWARFLinkerParallel/CMakeLists.txt --- a/llvm/unittests/DWARFLinkerParallel/CMakeLists.txt +++ b/llvm/unittests/DWARFLinkerParallel/CMakeLists.txt @@ -6,6 +6,7 @@ add_llvm_unittest(DWARFLinkerParallelTests DWARFLinkerTest.cpp StringPoolTest.cpp + StringTableTest.cpp ) target_link_libraries(DWARFLinkerParallelTests PRIVATE LLVMTestingSupport) diff --git a/llvm/unittests/DWARFLinkerParallel/StringTableTest.cpp b/llvm/unittests/DWARFLinkerParallel/StringTableTest.cpp new file mode 100644 --- /dev/null +++ b/llvm/unittests/DWARFLinkerParallel/StringTableTest.cpp @@ -0,0 +1,104 @@ +//===- llvm/unittest/DWARFLinkerParallel/StringTableTest.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/StringTable.h" +#include "llvm/Support/Parallel.h" +#include "gtest/gtest.h" +#include + +using namespace llvm; +using namespace dwarflinker_parallel; + +namespace { + +TEST(StringPoolTest, TestStringTable) { + struct StringDescription { + const char *Str = nullptr; + uint64_t Idx = 0; + uint64_t Offset = 0; + }; + + SmallVector InputStrings = { + {"first", 0, 0}, {"second", 1, 6}, {"third", 2, 13}}; + + StringPool Strings; + StringTable OutStrings(Strings, nullptr); + + // Check string insertion. + StringEntry *FirstPtr = Strings.insert(InputStrings[0].Str).first; + StringEntry *SecondPtr = Strings.insert(InputStrings[1].Str).first; + StringEntry *ThirdPtr = Strings.insert(InputStrings[2].Str).first; + + FirstPtr = OutStrings.add(FirstPtr); + SecondPtr = OutStrings.add(SecondPtr); + ThirdPtr = OutStrings.add(ThirdPtr); + + // Check fields of inserted strings. + EXPECT_TRUE(FirstPtr->getKey() == InputStrings[0].Str); + EXPECT_TRUE(FirstPtr->getValue()->Offset == InputStrings[0].Offset); + EXPECT_TRUE(FirstPtr->getValue()->Index == InputStrings[0].Idx); + + EXPECT_TRUE(SecondPtr->getKey() == InputStrings[1].Str); + EXPECT_TRUE(SecondPtr->getValue()->Offset == InputStrings[1].Offset); + EXPECT_TRUE(SecondPtr->getValue()->Index == InputStrings[1].Idx); + + EXPECT_TRUE(ThirdPtr->getKey() == InputStrings[2].Str); + EXPECT_TRUE(ThirdPtr->getValue()->Offset == InputStrings[2].Offset); + EXPECT_TRUE(ThirdPtr->getValue()->Index == InputStrings[2].Idx); + + // Check order enumerated strings. + uint64_t CurIdx = 0; + std::function checkStr = + [&](DwarfStringPoolEntryRef Entry) { + EXPECT_TRUE(Entry.getEntry().isIndexed()); + EXPECT_TRUE(Entry.getIndex() == CurIdx); + EXPECT_TRUE(Entry.getOffset() == InputStrings[CurIdx].Offset); + EXPECT_TRUE(Entry.getString() == InputStrings[CurIdx].Str); + + CurIdx++; + }; + + OutStrings.forEach(checkStr); +} + +TEST(StringPoolTest, TestStringTableWithTranslator) { + std::string Word; + std::function TranslatorFunc = + [&](StringRef InputString) -> StringRef { + Word.clear(); + for (auto Sym : InputString) + Word.insert(Word.begin(), Sym); + Word += '0'; + return Word; + }; + + StringPool Strings; + StringTable OutStrings(Strings, TranslatorFunc); + + StringEntry *FirstPtr = Strings.insert("first").first; + StringEntry *SecondPtr = Strings.insert("second").first; + StringEntry *ThirdPtr = Strings.insert("third").first; + + FirstPtr = OutStrings.add(FirstPtr); + SecondPtr = OutStrings.add(SecondPtr); + ThirdPtr = OutStrings.add(ThirdPtr); + + EXPECT_TRUE(FirstPtr->getKey() == "tsrif0"); + EXPECT_TRUE(FirstPtr->getValue()->Offset == 0); + EXPECT_TRUE(FirstPtr->getValue()->Index == 0); + + EXPECT_TRUE(SecondPtr->getKey() == "dnoces0"); + EXPECT_TRUE(SecondPtr->getValue()->Offset == 7); + EXPECT_TRUE(SecondPtr->getValue()->Index == 1); + + EXPECT_TRUE(ThirdPtr->getKey() == "driht0"); + EXPECT_TRUE(ThirdPtr->getValue()->Offset == 15); + EXPECT_TRUE(ThirdPtr->getValue()->Index == 2); +} + +} // anonymous namespace