mirror of
https://github.com/fergalmoran/ladybird.git
synced 2025-12-29 12:49:08 +00:00
LibJS/Bytecode: Flatten bytecode to a contiguous representation
Instead of keeping bytecode as a set of disjoint basic blocks on the
malloc heap, bytecode is now a contiguous sequence of bytes(!)
The transformation happens at the end of Bytecode::Generator::generate()
and the only really hairy part is rerouting jump labels.
This required solving a few problems:
- The interpreter execution loop had to change quite a bit, since we
were storing BasicBlock pointers all over the place, and control
transfer was done by redirecting the interpreter's current block.
- Exception handlers & finalizers are now stored per-bytecode-range
in a side table in Executable.
- The interpreter now has a plain program counter instead of a stream
iterator. This actually makes error stack generation a bit nicer
since we just have to deal with a number instead of reaching into
the iterator.
This yields a 25% performance improvement on this microbenchmark:
for (let i = 0; i < 1_000_000; ++i) { }
But basically everything gets faster. :^)
This commit is contained in:
@@ -1,11 +1,12 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
|
||||
* Copyright (c) 2021-2024, Andreas Kling <kling@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibJS/Bytecode/BasicBlock.h>
|
||||
#include <LibJS/Bytecode/Executable.h>
|
||||
#include <LibJS/Bytecode/Instruction.h>
|
||||
#include <LibJS/Bytecode/RegexTable.h>
|
||||
#include <LibJS/SourceCode.h>
|
||||
|
||||
@@ -14,6 +15,7 @@ namespace JS::Bytecode {
|
||||
JS_DEFINE_ALLOCATOR(Executable);
|
||||
|
||||
Executable::Executable(
|
||||
Vector<u8> bytecode,
|
||||
NonnullOwnPtr<IdentifierTable> identifier_table,
|
||||
NonnullOwnPtr<StringTable> string_table,
|
||||
NonnullOwnPtr<RegexTable> regex_table,
|
||||
@@ -23,9 +25,8 @@ Executable::Executable(
|
||||
size_t number_of_global_variable_caches,
|
||||
size_t number_of_environment_variable_caches,
|
||||
size_t number_of_registers,
|
||||
Vector<NonnullOwnPtr<BasicBlock>> basic_blocks,
|
||||
bool is_strict_mode)
|
||||
: basic_blocks(move(basic_blocks))
|
||||
: bytecode(move(bytecode))
|
||||
, string_table(move(string_table))
|
||||
, identifier_table(move(identifier_table))
|
||||
, regex_table(move(regex_table))
|
||||
@@ -44,8 +45,42 @@ Executable::~Executable() = default;
|
||||
void Executable::dump() const
|
||||
{
|
||||
warnln("\033[37;1mJS bytecode executable\033[0m \"{}\"", name);
|
||||
for (auto& block : basic_blocks)
|
||||
block->dump(*this);
|
||||
InstructionStreamIterator it(bytecode, this);
|
||||
|
||||
size_t basic_block_offset_index = 0;
|
||||
|
||||
while (!it.at_end()) {
|
||||
bool print_basic_block_marker = false;
|
||||
if (basic_block_offset_index < basic_block_start_offsets.size()
|
||||
&& it.offset() == basic_block_start_offsets[basic_block_offset_index]) {
|
||||
++basic_block_offset_index;
|
||||
print_basic_block_marker = true;
|
||||
}
|
||||
|
||||
StringBuilder builder;
|
||||
builder.appendff("[{:4x}] ", it.offset());
|
||||
if (print_basic_block_marker)
|
||||
builder.appendff("{:4}: ", basic_block_offset_index - 1);
|
||||
else
|
||||
builder.append(" "sv);
|
||||
builder.append((*it).to_byte_string(*this));
|
||||
|
||||
warnln("{}", builder.string_view());
|
||||
|
||||
++it;
|
||||
}
|
||||
|
||||
if (!exception_handlers.is_empty()) {
|
||||
warnln("");
|
||||
warnln("Exception handlers:");
|
||||
for (auto& handlers : exception_handlers) {
|
||||
warnln(" from {:4x} to {:4x} handler {:4x} finalizer {:4x}",
|
||||
handlers.start_offset,
|
||||
handlers.end_offset,
|
||||
handlers.handler_offset.value_or(0),
|
||||
handlers.finalizer_offset.value_or(0));
|
||||
}
|
||||
}
|
||||
|
||||
warnln("");
|
||||
}
|
||||
@@ -56,4 +91,27 @@ void Executable::visit_edges(Visitor& visitor)
|
||||
visitor.visit(constants);
|
||||
}
|
||||
|
||||
Optional<Executable::ExceptionHandlers const&> Executable::exception_handlers_for_offset(size_t offset) const
|
||||
{
|
||||
for (auto& handlers : exception_handlers) {
|
||||
if (handlers.start_offset <= offset && offset < handlers.end_offset)
|
||||
return handlers;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
UnrealizedSourceRange Executable::source_range_at(size_t offset) const
|
||||
{
|
||||
if (offset >= bytecode.size())
|
||||
return {};
|
||||
auto it = InstructionStreamIterator(bytecode.span().slice(offset), this);
|
||||
VERIFY(!it.at_end());
|
||||
auto& instruction = *it;
|
||||
return UnrealizedSourceRange {
|
||||
.source_code = source_code,
|
||||
.start_offset = instruction.source_record().source_start_offset,
|
||||
.end_offset = instruction.source_record().source_end_offset,
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user