//===- 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 // uintptr_t #include 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(NewStr); StringToId[Value].reset(NewStr); return ID; } return reinterpret_cast(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> StringToId; }; template 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(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 getStrings(const OwnerType *Owner); }; using GlobalString = StringID; template Ostream &operator<<(Ostream &Str, const StringID &Name) { return Str << Name.toString(); } template std::string operator+(const std::string &A, const StringID &B) { return A + B.toString(); } template std::string operator+(const StringID &A, const std::string &B) { return A.toString() + B; } } // end of namespace Ice namespace std { template struct hash> { size_t operator()(const Ice::StringID &Key) const { if (Key.hasStdString()) return hash()(Key.toString()); return hash()(Key.getID()); } }; } // end of namespace std #endif // SUBZERO_SRC_ICESTRINGPOOL_H