Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
136 changes: 136 additions & 0 deletions lldb/source/Plugins/TypeSystem/Swift/LRUCache.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
//===-- LRUCache.h ----------------------------------------------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2024 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
//
//===----------------------------------------------------------------------===//

#ifndef liblldb_LRUCache_h_
#define liblldb_LRUCache_h_

#include "lldb/Utility/ConstString.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/STLFunctionalExtras.h"
#include <list>
#include <mutex>

namespace lldb_private {
namespace swift_demangle {

/// A thread-safe Least Recently Used (LRU) cache implementation.
///
/// This class template implements an LRU cache with a fixed capacity, which
/// supports insertion, retrieval, and a factory-based retrieval mechanism. It
/// ensures thread safety through the use of a mutex lock. Keys are always
/// `ConstString`
template <typename Value>
class LRUCache {
public:
/// Constructs an instance with the specified capacity.
LRUCache(size_t capacity) : m_capacity(capacity) {}

/// Retrieves a value from the cache for the given key.
/// If the key is found in the cache, the corresponding value is returned and
/// the element is moved to the front of the LRU list, indicating it was
/// recently used. If the key is not found, std::nullopt is returned
std::optional<Value> Get(ConstString key) {
std::lock_guard lock{m_mutex};
auto map_it = m_map.find(key);
if (map_it == m_map.end())
return std::nullopt;
const auto &map_value = map_it->second;
MoveListElementToFront(map_value.list_it);
return map_value.value;
}

/// Inserts a key-value pair into the cache.
/// If the key already exists in the cache, its value is updated and the
/// element is moved to the front of the LRU list. If the key does not exist,
/// it is inserted into the cache. If the cache is at full capacity, the least
/// recently used element is evicted to make space for the new element.
void Put(ConstString key, const Value &value) {
if (m_capacity == 0)
return;
std::lock_guard lock{m_mutex};
auto map_it = m_map.find(key);
if (map_it != m_map.end()) {
auto &map_value = map_it->second;
map_value.value = value;
MoveListElementToFront(map_value.list_it);
} else {
InsertElement(key, value);
}
}

/// Retrieves a value from the cache or creates it if not present.
/// If the key is found in the cache, the corresponding value is returned and
/// the element is moved to the front of the LRU list. If the key is not
/// found, the given factory function is called to create a new value, which
/// is then inserted into the cache before being returned.
Value GetOrCreate(ConstString key, llvm::function_ref<Value()> factory) {
if (m_capacity == 0)
return factory();

{
std::lock_guard lock{m_mutex};
if (auto map_it = m_map.find(key); map_it != m_map.end()) {
const auto &map_value = map_it->second;
MoveListElementToFront(map_value.list_it);
return map_value.value;
}
}

// Call `factory` with `m_mutex` unlocked
const auto value = factory();

{
std::lock_guard lock{m_mutex};
InsertElement(key, value);
}

return value;
}

private:
using List = std::list<ConstString>;
struct MapValue {
List::iterator list_it;
Value value;
};
using Map = llvm::DenseMap<ConstString, MapValue>;

void InsertElement(ConstString key, const Value &value) {
assert(m_capacity > 0);

if (m_list.size() >= m_capacity) {
// Evict the least recent element
auto key = m_list.back();
m_list.pop_back();
m_map.erase(key);
}

m_list.emplace_front(key);
std::pair<typename Map::iterator, bool> emplace_result =
m_map.try_emplace(key, MapValue{m_list.begin(), value});
assert(emplace_result.second && "Element with this key already exists");
}

void MoveListElementToFront(typename List::iterator it) {
m_list.splice(m_list.begin(), m_list, it);
}

size_t m_capacity;
List m_list;
Map m_map;
std::mutex m_mutex;
};

} // namespace swift_demangle
} // namespace lldb_private

#endif
14 changes: 12 additions & 2 deletions lldb/source/Plugins/TypeSystem/Swift/TypeSystemSwiftTypeRef.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2951,7 +2951,11 @@ TypeSystemSwiftTypeRef::GetCanonicalType(opaque_compiler_type_t type) {
auto impl = [&]() {
using namespace swift::Demangle;
Demangler dem;
NodePointer canonical = GetCanonicalDemangleTree(dem, AsMangledName(type));
auto original_mangled_name = ConstString(AsMangledName(type));
if (auto cached = m_canonical_types_cache.Get(original_mangled_name))
return *cached;

NodePointer canonical = GetCanonicalDemangleTree(dem, original_mangled_name.GetStringRef());
if (ContainsUnresolvedTypeAlias(canonical)) {
// If this is a typealias defined in the expression evaluator,
// then we don't have debug info to resolve it from.
Expand All @@ -2963,7 +2967,13 @@ TypeSystemSwiftTypeRef::GetCanonicalType(opaque_compiler_type_t type) {
if (!mangling.isSuccess())
return CompilerType();
ConstString mangled(mangling.result());
return GetTypeFromMangledTypename(mangled);
auto result = GetTypeFromMangledTypename(mangled);
// Cache the result only when:
// 1) the type is fully resolved. Types with unresolved typealias will be
// processed by SwiftASTContext which has its own cache.
// 2) the type succesfully mangled.
m_canonical_types_cache.Put(original_mangled_name, result);
return result;
};
VALIDATE_AND_RETURN(impl, GetCanonicalType, type, g_no_exe_ctx,
(ReconstructType(type)), (ReconstructType(type)));
Expand Down
4 changes: 4 additions & 0 deletions lldb/source/Plugins/TypeSystem/Swift/TypeSystemSwiftTypeRef.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#ifndef liblldb_TypeSystemSwiftTypeRef_h_
#define liblldb_TypeSystemSwiftTypeRef_h_

#include "Plugins/TypeSystem/Swift/LRUCache.h"
#include "Plugins/TypeSystem/Swift/TypeSystemSwift.h"
#include "lldb/Core/SwiftForward.h"
#include "lldb/Utility/ThreadSafeDenseMap.h"
Expand Down Expand Up @@ -500,6 +501,9 @@ class TypeSystemSwiftTypeRef : public TypeSystemSwift {

/// All lldb::Type pointers produced by DWARFASTParser Swift go here.
ThreadSafeDenseMap<const char *, lldb::TypeSP> m_swift_type_map;

/// Fully resolved types produced by `GetCanonicalType` go here.
swift_demangle::LRUCache<CompilerType> m_canonical_types_cache{10};
};

/// This one owns a SwiftASTContextForExpressions.
Expand Down