| //===- subzero/src/IceStringPool.h - String pooling -------------*- C++ -*-===// |
| // |
| // The Subzero Code Generator |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| /// |
| /// \file |
| /// \brief Defines unique pooled strings with short unique IDs. This makes |
| /// hashing, equality testing, and ordered comparison faster, and avoids a lot |
| /// of memory allocation compared to directly using std::string. |
| /// |
| //===----------------------------------------------------------------------===// |
| |
| #ifndef SUBZERO_SRC_ICESTRINGPOOL_H |
| #define SUBZERO_SRC_ICESTRINGPOOL_H |
| |
| #include "IceDefs.h" // Ostream |
| |
| #include "llvm/Support/ErrorHandling.h" |
| |
| #include <cstdint> // uintptr_t |
| #include <string> |
| |
| namespace Ice { |
| |
| class StringPool { |
| StringPool(const StringPool &) = delete; |
| StringPool &operator=(const StringPool &) = delete; |
| |
| public: |
| using IDType = uintptr_t; |
| |
| StringPool() = default; |
| ~StringPool() = default; |
| IDType getNewID() { |
| // TODO(stichnot): Make it so that the GlobalString ctor doesn't have to |
| // grab the lock, and instead does an atomic increment of NextID. |
| auto NewID = NextID; |
| NextID += IDIncrement; |
| return NewID; |
| } |
| IDType getOrAddString(const std::string &Value) { |
| auto Iter = StringToId.find(Value); |
| if (Iter == StringToId.end()) { |
| auto *NewStr = new std::string(Value); |
| auto ID = reinterpret_cast<IDType>(NewStr); |
| StringToId[Value].reset(NewStr); |
| return ID; |
| } |
| return reinterpret_cast<IDType>(Iter->second.get()); |
| } |
| void dump(Ostream &Str) const { |
| if (StringToId.empty()) |
| return; |
| Str << "String pool (NumStrings=" << StringToId.size() |
| << " NumIDs=" << ((NextID - FirstID) / IDIncrement) << "):"; |
| for (const auto &Tuple : StringToId) { |
| Str << " " << Tuple.first; |
| } |
| Str << "\n"; |
| } |
| |
| private: |
| static constexpr IDType FirstID = 1; |
| static constexpr IDType IDIncrement = 2; |
| IDType NextID = FirstID; |
| std::unordered_map<std::string, std::unique_ptr<std::string>> StringToId; |
| }; |
| |
| template <typename Traits> class StringID { |
| public: |
| using IDType = StringPool::IDType; |
| |
| StringID() = default; // Create a default, invalid StringID. |
| StringID(const StringID &) = default; |
| StringID &operator=(const StringID &) = default; |
| |
| /// Create a unique StringID without an actual string, by grabbing the next |
| /// unique integral ID from the Owner. |
| static StringID createWithoutString(const typename Traits::OwnerType *Owner) { |
| return StringID(Owner); |
| } |
| /// Create a unique StringID that holds an actual string, by fetching or |
| /// adding the string from the Owner's pool. |
| static StringID createWithString(const typename Traits::OwnerType *Owner, |
| const std::string &Value) { |
| return StringID(Owner, Value); |
| } |
| |
| /// Tests whether the StringID was initialized with respect to an actual |
| /// std::string value, i.e. via StringID::createWithString(). |
| bool hasStdString() const { return isValid() && ((ID & 0x1) == 0); } |
| |
| IDType getID() const { |
| assert(isValid()); |
| return ID; |
| } |
| const std::string &toString() const { |
| if (!hasStdString()) |
| llvm::report_fatal_error( |
| "toString() called when hasStdString() is false"); |
| return *reinterpret_cast<std::string *>(ID); |
| } |
| std::string toStringOrEmpty() const { |
| if (hasStdString()) |
| return toString(); |
| return ""; |
| } |
| |
| bool operator==(const StringID &Other) const { return ID == Other.ID; } |
| bool operator!=(const StringID &Other) const { return !(*this == Other); } |
| bool operator<(const StringID &Other) const { |
| const bool ThisHasString = hasStdString(); |
| const bool OtherHasString = Other.hasStdString(); |
| // Do a normal string comparison if both have strings. |
| if (ThisHasString && OtherHasString) |
| return this->toString() < Other.toString(); |
| // Use the ID as a tiebreaker if neither has a string. |
| if (!ThisHasString && !OtherHasString) |
| return ID < Other.ID; |
| // If exactly one has a string, then that one comes first. |
| assert(ThisHasString != OtherHasString); |
| return ThisHasString; |
| } |
| |
| private: |
| static constexpr IDType InvalidID = 0; |
| IDType ID = InvalidID; |
| |
| explicit StringID(const typename Traits::OwnerType *Owner) |
| : ID(Traits::getStrings(Owner)->getNewID()) {} |
| StringID(const typename Traits::OwnerType *Owner, const std::string &Value) |
| : ID(Traits::getStrings(Owner)->getOrAddString(Value)) { |
| assert(hasStdString()); |
| } |
| bool isValid() const { return ID != InvalidID; } |
| }; |
| |
| // TODO(stichnot, jpp): Move GlobalStringPoolTraits definition into |
| // IceGlobalContext.h, once the include order issues are solved. |
| struct GlobalStringPoolTraits { |
| using OwnerType = GlobalContext; |
| static LockedPtr<StringPool> getStrings(const OwnerType *Owner); |
| }; |
| |
| using GlobalString = StringID<struct GlobalStringPoolTraits>; |
| |
| template <typename T> |
| Ostream &operator<<(Ostream &Str, const StringID<T> &Name) { |
| return Str << Name.toString(); |
| } |
| |
| template <typename T> |
| std::string operator+(const std::string &A, const StringID<T> &B) { |
| return A + B.toString(); |
| } |
| |
| template <typename T> |
| std::string operator+(const StringID<T> &A, const std::string &B) { |
| return A.toString() + B; |
| } |
| |
| } // end of namespace Ice |
| |
| namespace std { |
| template <typename T> struct hash<Ice::StringID<T>> { |
| size_t operator()(const Ice::StringID<T> &Key) const { |
| if (Key.hasStdString()) |
| return hash<std::string>()(Key.toString()); |
| return hash<Ice::StringPool::IDType>()(Key.getID()); |
| } |
| }; |
| } // end of namespace std |
| |
| #endif // SUBZERO_SRC_ICESTRINGPOOL_H |