LibJS: Preserve runtime caches across executable swaps

Allow a freshly materialized executable to inherit compatible runtime
caches from the executable it replaces. Store template object caches as
GC cells so they can be shared safely between executable instances.
This commit is contained in:
Andreas Kling 2026-05-21 21:49:25 +02:00 committed by Andreas Kling
parent 76a7091acb
commit fafba20b1a
3 changed files with 64 additions and 6 deletions

View file

@ -25,6 +25,7 @@
namespace JS::Bytecode {
GC_DEFINE_ALLOCATOR(Executable);
GC_DEFINE_ALLOCATOR(TemplateObjectCache);
GC_DEFINE_ALLOCATOR(ObjectPropertyIteratorCacheData);
InstructionStream::InstructionStream(Vector<u8> bytecode)
@ -179,6 +180,17 @@ size_t PropertyLookupCache::external_memory_size() const
return 0;
}
void PropertyLookupCache::copy_from(PropertyLookupCache const& other)
{
clear();
if (auto* data = other.monomorphic_data()) {
set_monomorphic_data(new MonomorphicData(*data));
return;
}
if (auto* data = other.polymorphic_data())
set_polymorphic_data(new PolymorphicData(*data));
}
void PropertyLookupCache::clear()
{
if (auto* data = monomorphic_data()) {
@ -252,6 +264,12 @@ size_t ObjectPropertyIteratorCacheData::external_memory_size() const
return size;
}
void TemplateObjectCache::visit_edges(Visitor& visitor)
{
Base::visit_edges(visitor);
visitor.visit(cached_template_object);
}
Executable::Executable(
InstructionStream bytecode,
NonnullOwnPtr<IdentifierTable> identifier_table,
@ -282,7 +300,9 @@ Executable::Executable(
property_lookup_caches.resize(number_of_property_lookup_caches);
global_variable_caches.resize(number_of_global_variable_caches);
environment_coordinate_caches.resize(number_of_environment_coordinate_caches);
template_object_caches.resize(number_of_template_object_caches);
template_object_caches.ensure_capacity(number_of_template_object_caches);
for (size_t i = 0; i < number_of_template_object_caches; ++i)
template_object_caches.append(heap().allocate<TemplateObjectCache>());
object_shape_caches.resize(number_of_object_shape_caches);
object_property_iterator_caches.resize(number_of_object_property_iterator_caches);
asm_constants_size = this->constants.size();
@ -541,8 +561,7 @@ void Executable::visit_edges(Visitor& visitor)
{
Base::visit_edges(visitor);
visitor.visit(constants);
for (auto& cache : template_object_caches)
visitor.visit(cache.cached_template_object);
visitor.visit(template_object_caches);
for (auto& cache : object_property_iterator_caches)
visitor.visit(cache.data);
for (auto& cache : object_property_iterator_caches)
@ -558,6 +577,34 @@ void Executable::visit_edges(Visitor& visitor)
property_key_table->visit_edges(visitor);
}
void Executable::copy_runtime_caches_from(Executable const& other)
{
if (this == &other)
return;
if (property_lookup_caches.size() == other.property_lookup_caches.size()) {
for (size_t i = 0; i < property_lookup_caches.size(); ++i)
property_lookup_caches[i].copy_from(other.property_lookup_caches[i]);
}
if (global_variable_caches.size() == other.global_variable_caches.size())
global_variable_caches = other.global_variable_caches;
if (environment_coordinate_caches.size() == other.environment_coordinate_caches.size())
environment_coordinate_caches = other.environment_coordinate_caches;
if (template_object_caches.size() == other.template_object_caches.size())
template_object_caches = other.template_object_caches;
if (object_shape_caches.size() == other.object_shape_caches.size())
object_shape_caches = other.object_shape_caches;
if (object_property_iterator_caches.size() == other.object_property_iterator_caches.size()) {
for (size_t i = 0; i < object_property_iterator_caches.size(); ++i)
object_property_iterator_caches[i].data = other.object_property_iterator_caches[i].data;
}
}
size_t Executable::external_memory_size() const
{
size_t size = bytecode.external_memory_size();

View file

@ -98,6 +98,7 @@ struct PropertyLookupCache {
[[nodiscard]] Span<Entry> entries();
[[nodiscard]] ReadonlySpan<Entry> entries() const;
[[nodiscard]] size_t external_memory_size() const;
void copy_from(PropertyLookupCache const&);
void update(Entry::Type type, auto callback)
{
@ -194,8 +195,17 @@ struct GlobalVariableCache {
// https://tc39.es/ecma262/#sec-gettemplateobject
// Template objects are cached at the call site.
struct TemplateObjectCache {
class JS_API TemplateObjectCache final : public Cell {
GC_CELL(TemplateObjectCache, Cell);
GC_DECLARE_ALLOCATOR(TemplateObjectCache);
public:
virtual ~TemplateObjectCache() override = default;
GC::Ptr<Array> cached_template_object;
private:
virtual void visit_edges(Visitor&) override;
};
// Cache for object literal shapes.
@ -288,7 +298,7 @@ public:
Vector<PropertyLookupCache> property_lookup_caches;
Vector<GlobalVariableCache> global_variable_caches;
Vector<EnvironmentCoordinate> environment_coordinate_caches;
Vector<TemplateObjectCache> template_object_caches;
Vector<GC::Ref<TemplateObjectCache>> template_object_caches;
Vector<ObjectShapeCache> object_shape_caches;
Vector<ObjectPropertyIteratorCache> object_property_iterator_caches;
NonnullOwnPtr<StringTable> string_table;
@ -338,6 +348,7 @@ public:
}
[[nodiscard]] COLD Optional<size_t> basic_block_index_for_offset(size_t offset) const;
void copy_runtime_caches_from(Executable const&);
[[nodiscard]] COLD Optional<ExceptionHandlers const&> exception_handlers_for_offset(size_t offset) const;
[[nodiscard]] Optional<SourceRange> source_range_at(size_t offset) const;

View file

@ -2143,7 +2143,7 @@ void NewPrimitiveArray::execute_impl(VM& vm) const
// 13.2.8.4 GetTemplateObject ( templateLiteral ), https://tc39.es/ecma262/#sec-gettemplateobject
void GetTemplateObject::execute_impl(VM& vm) const
{
auto& cache = vm.current_executable().template_object_caches[m_cache];
auto& cache = *vm.current_executable().template_object_caches[m_cache];
// 1. Let realm be the current Realm Record.
auto& realm = *vm.current_realm();