Skip to content

Commit b12cd6a

Browse files
[lldb] Use batched memory reads in ClassDescriptorV2::relative_list_entry_t (#201284)
This reduces the number of memory reads performed when reading Objective C classes metadata. Note: these addresses are indeed sequential (with a small offset between them), but there are so many of them that they would not fit into a single Process::ReadMemory cache line, so this is still a win, and it also puts the code into the right shape for vectorizing the next read in the same loop, which will see the biggest savings.
1 parent b659948 commit b12cd6a

2 files changed

Lines changed: 38 additions & 34 deletions

File tree

lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCClassDescriptorV2.cpp

Lines changed: 34 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -393,29 +393,27 @@ ClassDescriptorV2::ivar_t::Read(Process *process, lldb::addr_t addr) {
393393
return result;
394394
}
395395

396-
llvm::Expected<ClassDescriptorV2::relative_list_entry_t>
397-
ClassDescriptorV2::relative_list_entry_t::Read(Process *process,
398-
lldb::addr_t addr) {
396+
llvm::Expected<llvm::SmallVector<ClassDescriptorV2::relative_list_entry_t>>
397+
ClassDescriptorV2::ReadRelativeListEntries(Process &process,
398+
llvm::ArrayRef<lldb::addr_t> addrs) {
399399
size_t size = sizeof(uint64_t); // m_image_index : 16
400400
// m_list_offset : 48
401401

402-
DataBufferHeap buffer(size, '\0');
403-
Status error;
404-
405-
process->ReadMemory(addr, buffer.GetBytes(), size, error);
406-
if (error.Fail())
407-
return llvm::joinErrors(
408-
error.takeError(),
409-
llvm::createStringErrorV(
410-
"Failed to read relative_list_entry_t at address {0:x}", addr));
411-
412-
DataExtractor extractor(buffer.GetBytes(), size, process->GetByteOrder(),
413-
process->GetAddressByteSize());
414-
lldb::offset_t cursor = 0;
415-
uint64_t raw_entry = extractor.GetU64_unchecked(&cursor);
416-
uint16_t image_index = raw_entry & 0xFFFF;
417-
int64_t list_offset = llvm::SignExtend64<48>(raw_entry >> 16);
418-
return relative_list_entry_t{image_index, list_offset};
402+
llvm::SmallVector<std::optional<uint64_t>> raw_entries =
403+
process.ReadUnsignedIntegersFromMemory(addrs, size);
404+
405+
llvm::SmallVector<relative_list_entry_t> results;
406+
results.reserve(addrs.size());
407+
for (auto [addr, maybe_raw] : llvm::zip(addrs, raw_entries)) {
408+
if (!maybe_raw)
409+
return llvm::createStringErrorV(
410+
"Failed to read relative_list_entry_t at address {0:x}", addr);
411+
uint64_t raw = *maybe_raw;
412+
uint16_t image_index = raw & 0xFFFF;
413+
int64_t list_offset = llvm::SignExtend64<48>(raw >> 16);
414+
results.push_back(relative_list_entry_t{image_index, list_offset});
415+
}
416+
return results;
419417
}
420418

421419
llvm::Expected<ClassDescriptorV2::relative_list_list_t>
@@ -505,18 +503,23 @@ llvm::Error ClassDescriptorV2::ProcessRelativeMethodLists(
505503
if (!relative_method_lists)
506504
return relative_method_lists.takeError();
507505

508-
for (uint32_t i = 0; i < relative_method_lists->m_count; i++) {
509-
// 2. Extract the image index and the list offset from the
510-
// relative_list_entry_t
511-
const lldb::addr_t entry_addr = relative_method_lists->m_first_ptr +
512-
(i * relative_method_lists->m_entsize);
513-
auto entry = relative_list_entry_t::Read(process, entry_addr);
514-
if (!entry)
515-
return entry.takeError();
506+
// 2. Compute the address of every relative_list_entry_t and read them all in
507+
// a single batched memory read.
508+
auto to_entry_addr = [&](uint64_t idx) {
509+
return relative_method_lists->m_first_ptr +
510+
(idx * relative_method_lists->m_entsize);
511+
};
512+
auto entry_addrs = llvm::to_vector(llvm::map_range(
513+
llvm::seq<uint64_t>(relative_method_lists->m_count), to_entry_addr));
514+
515+
auto entries = ReadRelativeListEntries(*process, entry_addrs);
516+
if (!entries)
517+
return entries.takeError();
516518

519+
for (auto [entry_addr, entry] : llvm::zip(entry_addrs, *entries)) {
517520
// 3. Calculate the pointer to the method_list_t from the
518521
// relative_list_entry_t
519-
const lldb::addr_t method_list_addr = entry_addr + entry->m_list_offset;
522+
const lldb::addr_t method_list_addr = entry_addr + entry.m_list_offset;
520523

521524
// 4. Get the method_list_t from the pointer
522525
llvm::Expected<method_list_t> method_list =
@@ -525,10 +528,10 @@ llvm::Error ClassDescriptorV2::ProcessRelativeMethodLists(
525528
return method_list.takeError();
526529

527530
// 5. Cache the result so we don't need to reconstruct it later.
528-
m_image_to_method_lists[entry->m_image_index].emplace_back(*method_list);
531+
m_image_to_method_lists[entry.m_image_index].emplace_back(*method_list);
529532

530533
// 6. If the relevant image is loaded, add the methods to the Decl
531-
if (!m_runtime.IsSharedCacheImageLoaded(entry->m_image_index))
534+
if (!m_runtime.IsSharedCacheImageLoaded(entry.m_image_index))
532535
continue;
533536

534537
ProcessMethodList(instance_method_func, *method_list);

lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCClassDescriptorV2.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -210,11 +210,12 @@ class ClassDescriptorV2 : public ObjCLanguageRuntime::ClassDescriptor {
210210
struct relative_list_entry_t {
211211
uint16_t m_image_index;
212212
int64_t m_list_offset;
213-
214-
static llvm::Expected<relative_list_entry_t> Read(Process *process,
215-
lldb::addr_t addr);
216213
};
217214

215+
static llvm::Expected<
216+
llvm::SmallVector<ClassDescriptorV2::relative_list_entry_t>>
217+
ReadRelativeListEntries(Process &process, llvm::ArrayRef<lldb::addr_t> addrs);
218+
218219
struct relative_list_list_t {
219220
uint32_t m_entsize;
220221
uint32_t m_count;

0 commit comments

Comments
 (0)