//===--- RequestCache.h - Per-request caching ----------------- -*- C++ -*-===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// // // This file defines data structures to efficiently support the request // evaluator's per-request caching and dependency tracking maps. // //===----------------------------------------------------------------------===// #include "swift/AST/DependencyCollector.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/raw_ostream.h" #ifndef SWIFT_AST_REQUEST_CACHE_H #define SWIFT_AST_REQUEST_CACHE_H namespace swift { namespace evaluator { namespace detail { // Remove this when the compiler bumps to C++17. template using void_t = void; template> struct TupleHasDenseMapInfo {}; template struct TupleHasDenseMapInfo< std::tuple, void_t::getEmptyKey)...>> { using type = void_t<>; }; } // end namespace detail namespace { /// Wrapper for a request with additional empty and tombstone states. template> class RequestKey { friend struct llvm::DenseMapInfo; union { char Empty; Request Req; }; enum class StorageKind : uint8_t { Normal, Empty, Tombstone }; StorageKind Kind; static RequestKey getEmpty() { return RequestKey(StorageKind::Empty); } static RequestKey getTombstone() { return RequestKey(StorageKind::Tombstone); } RequestKey(StorageKind kind) : Empty(), Kind(kind) { assert(kind != StorageKind::Normal); } public: explicit RequestKey(Request req) : Req(std::move(req)), Kind(StorageKind::Normal) {} RequestKey(const RequestKey &other) : Empty(), Kind(other.Kind) { if (Kind == StorageKind::Normal) new (&Req) Request(other.Req); } RequestKey(RequestKey &&other) : Empty(), Kind(other.Kind) { if (Kind == StorageKind::Normal) new (&Req) Request(std::move(other.Req)); } RequestKey &operator=(const RequestKey &other) { if (&other != this) { this->~RequestKey(); new (this) RequestKey(other); } return *this; } RequestKey &operator=(RequestKey &&other) { if (&other != this) { this->~RequestKey(); new (this) RequestKey(std::move(other)); } return *this; } ~RequestKey() { if (Kind == StorageKind::Normal) Req.~Request(); } bool isStorageEqual(const Request &req) const { if (Kind != StorageKind::Normal) return false; return Req == req; } friend bool operator==(const RequestKey &lhs, const RequestKey &rhs) { if (lhs.Kind == StorageKind::Normal && rhs.Kind == StorageKind::Normal) { return lhs.Req == rhs.Req; } else { return lhs.Kind == rhs.Kind; } } friend bool operator!=(const RequestKey &lhs, const RequestKey &rhs) { return !(lhs == rhs); } friend llvm::hash_code hash_value(const RequestKey &key) { if (key.Kind != StorageKind::Normal) return 1; return hash_value(key.Req); } }; template class RequestKey::type> { friend struct llvm::DenseMapInfo; using Info = llvm::DenseMapInfo; Request Req; static RequestKey getEmpty() { return RequestKey(Request(Info::getEmptyKey())); } static RequestKey getTombstone() { return RequestKey(Request(Info::getTombstoneKey())); } public: explicit RequestKey(Request req) : Req(std::move(req)) {} bool isStorageEqual(const Request &req) const { return Req == req; } friend bool operator==(const RequestKey &lhs, const RequestKey &rhs) { return lhs.Req == rhs.Req; } friend bool operator!=(const RequestKey &lhs, const RequestKey &rhs) { return !(lhs == rhs); } friend llvm::hash_code hash_value(const RequestKey &key) { return hash_value(key.Req); } }; } // end namespace /// Type-erased wrapper for caching results of a single type of request. class PerRequestCache { void *Storage; std::function Deleter; std::function Dumper; PerRequestCache(void *storage, std::function deleter, std::function dumper) : Storage(storage), Deleter(deleter), Dumper(dumper) {} public: PerRequestCache() : Storage(nullptr), Deleter([](void *) {}), Dumper([](llvm::raw_ostream &, void *) {}) {} PerRequestCache(PerRequestCache &&other) : Storage(other.Storage), Deleter(std::move(other.Deleter)), Dumper(std::move(other.Dumper)) { other.Storage = nullptr; } PerRequestCache &operator=(PerRequestCache &&other) { if (&other != this) { this->~PerRequestCache(); new (this) PerRequestCache(std::move(other)); } return *this; } PerRequestCache(const PerRequestCache &) = delete; PerRequestCache &operator=(const PerRequestCache &) = delete; template static PerRequestCache makeEmpty() { using Map = llvm::DenseMap, typename Request::OutputType>; return PerRequestCache(new Map(), [](void *ptr) { delete static_cast(ptr); }, [](llvm::raw_ostream &out, void *storage) { out << TypeID::getName() << "\t"; if (auto *map = static_cast(storage)) { out << map->size() << "\t" << llvm::capacity_in_bytes(*map); } else { out << "0\t0"; } out << "\n"; }); } template llvm::DenseMap, typename Request::OutputType> * get() const { using Map = llvm::DenseMap, typename Request::OutputType>; assert(Storage); return static_cast(Storage); } template std::pair size() const { using Map = llvm::DenseMap, typename Request::OutputType>; if (!Storage) return std::make_pair(0, 0); auto map = static_cast(Storage); return std::make_pair(map.size(), llvm::capacity_in_bytes(map)); } bool isNull() const { return !Storage; } ~PerRequestCache() { if (Storage) Deleter(Storage); } void dump(llvm::raw_ostream &out) { Dumper(out, Storage); } }; /// Data structure for caching results of requests. Sharded by the type ID /// zone and request kind, with a PerRequestCache for each request kind. /// /// Conceptually equivalent to DenseMap, but without /// type erasure overhead for keys and values. class RequestCache { #define SWIFT_TYPEID_ZONE(Name, Id) \ std::vector Name##ZoneCache; \ \ template < \ typename Request, typename ZoneTypes = TypeIDZoneTypes, \ typename std::enable_if::zone == Zone::Name>::type * = \ nullptr> \ llvm::DenseMap, \ typename Request::OutputType> * \ getCache() { \ auto &caches = Name##ZoneCache; \ if (caches.empty()) { \ caches.resize(ZoneTypes::Count); \ } \ auto idx = TypeID::localID; \ if (caches[idx].isNull()) { \ caches[idx] = PerRequestCache::makeEmpty(); \ } \ return caches[idx].template get(); \ } #include "swift/Basic/TypeIDZones.def" #undef SWIFT_TYPEID_ZONE public: template typename llvm::DenseMap, typename Request::OutputType>::const_iterator find_as(const Request &req) { auto *cache = getCache(); return cache->find_as(req); } template typename llvm::DenseMap, typename Request::OutputType>::const_iterator end() { auto *cache = getCache(); return cache->end(); } template bool insert(Request req, typename Request::OutputType val) { auto *cache = getCache(); auto result = cache->insert({RequestKey(std::move(req)), std::move(val)}); return result.second; } template void erase(Request req) { auto *cache = getCache(); cache->erase(RequestKey(std::move(req))); } void clear() { #define SWIFT_TYPEID_ZONE(Name, Id) Name##ZoneCache.clear(); #include "swift/Basic/TypeIDZones.def" #undef SWIFT_TYPEID_ZONE } void dump(llvm::raw_ostream &out) { #define SWIFT_TYPEID_ZONE(Name, Id) \ for (auto &entry : Name##ZoneCache) { \ entry.dump(out); \ } #include "swift/Basic/TypeIDZones.def" #undef SWIFT_TYPEID_ZONE } }; /// Type-erased wrapper for caching dependencies from a single type of request. class PerRequestReferences { void *Storage; std::function Deleter; PerRequestReferences(void *storage, std::function deleter) : Storage(storage), Deleter(deleter) {} public: PerRequestReferences() : Storage(nullptr), Deleter([](void *) {}) {} PerRequestReferences(PerRequestReferences &&other) : Storage(other.Storage), Deleter(std::move(other.Deleter)) { other.Storage = nullptr; } PerRequestReferences &operator=(PerRequestReferences &&other) { if (&other != this) { this->~PerRequestReferences(); new (this) PerRequestReferences(std::move(other)); } return *this; } PerRequestReferences(const PerRequestReferences &) = delete; PerRequestReferences &operator=(const PerRequestReferences &) = delete; template static PerRequestReferences makeEmpty() { using Map = llvm::DenseMap, std::vector>; return PerRequestReferences(new Map(), [](void *ptr) { delete static_cast(ptr); }); } template llvm::DenseMap, std::vector> * get() const { using Map = llvm::DenseMap, std::vector>; assert(Storage); return static_cast(Storage); } bool isNull() const { return !Storage; } ~PerRequestReferences() { if (Storage) Deleter(Storage); } }; /// Data structure for caching dependencies from requests. Sharded by the /// type ID zone and request kind, with a PerRequestReferences for each /// request kind. /// /// Conceptually equivalent to DenseMap>, but /// without type erasure overhead for keys. class RequestReferences { #define SWIFT_TYPEID_ZONE(Name, Id) \ std::vector Name##ZoneRefs; \ \ template < \ typename Request, typename ZoneTypes = TypeIDZoneTypes, \ typename std::enable_if::zone == Zone::Name>::type * = \ nullptr> \ llvm::DenseMap, \ std::vector> * \ getRefs() { \ auto &refs = Name##ZoneRefs; \ if (refs.empty()) { \ refs.resize(ZoneTypes::Count); \ } \ auto idx = TypeID::localID; \ if (refs[idx].isNull()) { \ refs[idx] = PerRequestReferences::makeEmpty(); \ } \ return refs[idx].template get(); \ } #include "swift/Basic/TypeIDZones.def" #undef SWIFT_TYPEID_ZONE public: template typename llvm::DenseMap, std::vector>::const_iterator find_as(const Request &req) { auto *refs = getRefs(); return refs->find_as(req); } template typename llvm::DenseMap, std::vector>::const_iterator end() { auto *refs = getRefs(); return refs->end(); } template void insert(Request req, std::vector val) { auto *refs = getRefs(); refs->insert({RequestKey(std::move(req)), std::move(val)}); } template void erase(Request req) { auto *refs = getRefs(); refs->erase(RequestKey(std::move(req))); } void clear() { #define SWIFT_TYPEID_ZONE(Name, Id) Name##ZoneRefs.clear(); #include "swift/Basic/TypeIDZones.def" #undef SWIFT_TYPEID_ZONE } }; } // end namespace evaluator } // end namespace swift namespace llvm { template struct DenseMapInfo> { using RequestKey = swift::evaluator::RequestKey; static inline RequestKey getEmptyKey() { return RequestKey::getEmpty(); } static inline RequestKey getTombstoneKey() { return RequestKey::getTombstone(); } static unsigned getHashValue(const RequestKey &key) { return hash_value(key); } static unsigned getHashValue(const Request &request) { return hash_value(request); } static bool isEqual(const RequestKey &lhs, const RequestKey &rhs) { return lhs == rhs; } static bool isEqual(const Request &lhs, const RequestKey &rhs) { return rhs.isStorageEqual(lhs); } }; } // end namespace llvm #endif // SWIFT_AST_REQUEST_CACHE_H