blob: 0a42dc36dab9937e4e85d407539d2088bc05c935 [file] [log] [blame]
//===- 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