LibJS: Replace source map HashMap with sorted Vector
Bytecode source map entries are always added in order of increasing bytecode offset, and lookups only happen during error handling (a cold path). This makes a sorted vector with binary search a better fit than a hash map. This change reduces memory overhead and speeds up bytecode generation by avoiding hash table operations during compilation. Lookups remain fast via binary search, and since source_range_at() is only called when generating stack traces, the O(log n) lookup is acceptable.
This commit is contained in:
parent
d488f9f12f
commit
81bee185e6
4 changed files with 23 additions and 11 deletions
|
|
@ -56,7 +56,7 @@ public:
|
|||
BasicBlock const* finalizer() const { return m_finalizer; }
|
||||
|
||||
auto const& source_map() const { return m_source_map; }
|
||||
void add_source_map_entry(u32 bytecode_offset, SourceRecord const& source_record) { m_source_map.set(bytecode_offset, source_record); }
|
||||
void add_source_map_entry(u32 bytecode_offset, SourceRecord const& source_record) { m_source_map.append({ bytecode_offset, source_record }); }
|
||||
|
||||
[[nodiscard]] bool has_resolved_this() const { return m_has_resolved_this; }
|
||||
void set_has_resolved_this() { m_has_resolved_this = true; }
|
||||
|
|
@ -75,7 +75,7 @@ private:
|
|||
bool m_terminated { false };
|
||||
bool m_has_resolved_this { false };
|
||||
|
||||
HashMap<size_t, SourceRecord> m_source_map;
|
||||
Vector<SourceMapEntry> m_source_map;
|
||||
|
||||
size_t m_last_instruction_start_offset { 0 };
|
||||
};
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/BinarySearch.h>
|
||||
#include <LibJS/Bytecode/BasicBlock.h>
|
||||
#include <LibJS/Bytecode/Executable.h>
|
||||
#include <LibJS/Bytecode/Instruction.h>
|
||||
|
|
@ -115,13 +116,19 @@ UnrealizedSourceRange Executable::source_range_at(size_t offset) const
|
|||
return {};
|
||||
auto it = InstructionStreamIterator(bytecode.span().slice(offset), this);
|
||||
VERIFY(!it.at_end());
|
||||
auto mapping = source_map.get(offset);
|
||||
if (!mapping.has_value())
|
||||
auto* entry = binary_search(source_map, offset, nullptr, [](size_t needle, SourceMapEntry const& entry) -> int {
|
||||
if (needle < entry.bytecode_offset)
|
||||
return -1;
|
||||
if (needle > entry.bytecode_offset)
|
||||
return 1;
|
||||
return 0;
|
||||
});
|
||||
if (!entry)
|
||||
return {};
|
||||
return UnrealizedSourceRange {
|
||||
.source_code = source_code,
|
||||
.start_offset = mapping->source_start_offset,
|
||||
.end_offset = mapping->source_end_offset,
|
||||
.start_offset = entry->source_record.source_start_offset,
|
||||
.end_offset = entry->source_record.source_end_offset,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <AK/HashMap.h>
|
||||
#include <AK/NonnullOwnPtr.h>
|
||||
#include <AK/OwnPtr.h>
|
||||
#include <AK/Utf16FlyString.h>
|
||||
|
|
@ -92,6 +91,11 @@ struct SourceRecord {
|
|||
u32 source_end_offset {};
|
||||
};
|
||||
|
||||
struct SourceMapEntry {
|
||||
u32 bytecode_offset {};
|
||||
SourceRecord source_record {};
|
||||
};
|
||||
|
||||
class JS_API Executable final : public Cell {
|
||||
GC_CELL(Executable, Cell);
|
||||
GC_DECLARE_ALLOCATOR(Executable);
|
||||
|
|
@ -143,7 +147,7 @@ public:
|
|||
Vector<ExceptionHandlers> exception_handlers;
|
||||
Vector<size_t> basic_block_start_offsets;
|
||||
|
||||
HashMap<size_t, SourceRecord> source_map;
|
||||
Vector<SourceMapEntry> source_map;
|
||||
|
||||
Vector<LocalVariable> local_variable_names;
|
||||
u32 local_index_base { 0 };
|
||||
|
|
|
|||
|
|
@ -293,7 +293,7 @@ CodeGenerationErrorOr<GC::Ref<Executable>> Generator::compile(VM& vm, ASTNode co
|
|||
};
|
||||
Vector<UnlinkedExceptionHandlers> unlinked_exception_handlers;
|
||||
|
||||
HashMap<size_t, SourceRecord> source_map;
|
||||
Vector<SourceMapEntry> source_map;
|
||||
|
||||
Optional<ScopedOperand> undefined_constant;
|
||||
|
||||
|
|
@ -360,8 +360,9 @@ CodeGenerationErrorOr<GC::Ref<Executable>> Generator::compile(VM& vm, ASTNode co
|
|||
|
||||
block_offsets.set(block.ptr(), bytecode.size());
|
||||
|
||||
for (auto& [offset, source_record] : block->source_map()) {
|
||||
source_map.set(bytecode.size() + offset, source_record);
|
||||
for (auto const& entry : block->source_map()) {
|
||||
VERIFY(bytecode.size() <= NumericLimits<u32>::max());
|
||||
source_map.append({ static_cast<u32>(bytecode.size()) + entry.bytecode_offset, entry.source_record });
|
||||
}
|
||||
|
||||
Bytecode::InstructionStreamIterator it(block->instruction_stream());
|
||||
|
|
|
|||
Loading…
Reference in a new issue