| //===- 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 |