ladybird/Userland/Libraries/LibJS/Bytecode/Executable.h
Andreas Kling f6aee2b9e8 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. :^)
2024-05-07 09:15:40 +02:00

107 lines
3.1 KiB
C++

/*
* Copyright (c) 2021-2024, Andreas Kling <kling@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/DeprecatedFlyString.h>
#include <AK/HashMap.h>
#include <AK/NonnullOwnPtr.h>
#include <AK/OwnPtr.h>
#include <AK/WeakPtr.h>
#include <LibJS/Bytecode/IdentifierTable.h>
#include <LibJS/Bytecode/Label.h>
#include <LibJS/Bytecode/StringTable.h>
#include <LibJS/Forward.h>
#include <LibJS/Heap/Cell.h>
#include <LibJS/Heap/CellAllocator.h>
#include <LibJS/Runtime/EnvironmentCoordinate.h>
#include <LibJS/SourceRange.h>
namespace JS::Bytecode {
struct PropertyLookupCache {
WeakPtr<Shape> shape;
Optional<u32> property_offset;
WeakPtr<Object> prototype;
WeakPtr<PrototypeChainValidity> prototype_chain_validity;
};
struct GlobalVariableCache : public PropertyLookupCache {
u64 environment_serial_number { 0 };
};
using EnvironmentVariableCache = Optional<EnvironmentCoordinate>;
struct SourceRecord {
u32 source_start_offset {};
u32 source_end_offset {};
};
class Executable final : public Cell {
JS_CELL(Executable, Cell);
JS_DECLARE_ALLOCATOR(Executable);
public:
Executable(
Vector<u8> bytecode,
NonnullOwnPtr<IdentifierTable>,
NonnullOwnPtr<StringTable>,
NonnullOwnPtr<RegexTable>,
Vector<Value> constants,
NonnullRefPtr<SourceCode const>,
size_t number_of_property_lookup_caches,
size_t number_of_global_variable_caches,
size_t number_of_environment_variable_caches,
size_t number_of_registers,
bool is_strict_mode);
virtual ~Executable() override;
DeprecatedFlyString name;
Vector<u8> bytecode;
Vector<PropertyLookupCache> property_lookup_caches;
Vector<GlobalVariableCache> global_variable_caches;
Vector<EnvironmentVariableCache> environment_variable_caches;
NonnullOwnPtr<StringTable> string_table;
NonnullOwnPtr<IdentifierTable> identifier_table;
NonnullOwnPtr<RegexTable> regex_table;
Vector<Value> constants;
NonnullRefPtr<SourceCode const> source_code;
size_t number_of_registers { 0 };
bool is_strict_mode { false };
struct ExceptionHandlers {
size_t start_offset;
size_t end_offset;
Optional<size_t> handler_offset;
Optional<size_t> finalizer_offset;
};
Vector<ExceptionHandlers> exception_handlers;
Vector<size_t> basic_block_start_offsets;
ByteString const& get_string(StringTableIndex index) const { return string_table->get(index); }
DeprecatedFlyString const& get_identifier(IdentifierTableIndex index) const { return identifier_table->get(index); }
Optional<DeprecatedFlyString const&> get_identifier(Optional<IdentifierTableIndex> const& index) const
{
if (!index.has_value())
return {};
return get_identifier(*index);
}
[[nodiscard]] Optional<ExceptionHandlers const&> exception_handlers_for_offset(size_t offset) const;
[[nodiscard]] UnrealizedSourceRange source_range_at(size_t offset) const;
void dump() const;
private:
virtual void visit_edges(Visitor&) override;
};
}