1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-26 05:17:34 +00:00

LibJS: Add basic monomorphic caching for PutById property access

This patch makes it possible for JS::Object::internal_set() to populate
a CacheablePropertyMetadata, and uses this to implement a basic
monomorphic cache for the most common form of property write access.
This commit is contained in:
Andreas Kling 2023-11-08 20:51:26 +01:00
parent 28118623f5
commit b1b2ca1485
28 changed files with 99 additions and 54 deletions

View file

@ -482,9 +482,9 @@ Bytecode::CodeGenerationErrorOr<void> AssignmentExpression::generate_bytecode(By
} else if (expression.property().is_identifier()) {
auto identifier_table_ref = generator.intern_identifier(verify_cast<Identifier>(expression.property()).string());
if (!lhs_is_super_expression)
generator.emit<Bytecode::Op::PutById>(*base_object_register, identifier_table_ref);
generator.emit<Bytecode::Op::PutById>(*base_object_register, identifier_table_ref, Bytecode::Op::PropertyKind::KeyValue, generator.next_property_lookup_cache());
else
generator.emit<Bytecode::Op::PutByIdWithThis>(*base_object_register, *this_value_register, identifier_table_ref);
generator.emit<Bytecode::Op::PutByIdWithThis>(*base_object_register, *this_value_register, identifier_table_ref, Bytecode::Op::PropertyKind::KeyValue, generator.next_property_lookup_cache());
} else if (expression.property().is_private_identifier()) {
auto identifier_table_ref = generator.intern_identifier(verify_cast<PrivateIdentifier>(expression.property()).string());
generator.emit<Bytecode::Op::PutPrivateById>(*base_object_register, identifier_table_ref);
@ -985,7 +985,7 @@ Bytecode::CodeGenerationErrorOr<void> ObjectExpression::generate_bytecode(Byteco
TRY(generator.emit_named_evaluation_if_anonymous_function(property->value(), name));
}
generator.emit<Bytecode::Op::PutById>(object_reg, key_name, property_kind);
generator.emit<Bytecode::Op::PutById>(object_reg, key_name, property_kind, generator.next_property_lookup_cache());
} else {
TRY(property->key().generate_bytecode(generator));
auto property_reg = generator.allocate_register();
@ -1695,7 +1695,7 @@ static void generate_yield(Bytecode::Generator& generator, Bytecode::Label conti
// 5. Return Completion Record { [[Type]]: return, [[Value]]: awaited.[[Value]], [[Target]]: empty }.
generator.emit<Bytecode::Op::LoadImmediate>(Value(to_underlying(Completion::Type::Return)));
generator.emit<Bytecode::Op::PutById>(received_completion_register, type_identifier);
generator.emit<Bytecode::Op::PutById>(received_completion_register, type_identifier, Bytecode::Op::PropertyKind::KeyValue, generator.next_property_lookup_cache());
generator.emit<Bytecode::Op::Jump>(Bytecode::Label { load_completion_and_jump_to_continuation_label_block });
generator.switch_to_basic_block(load_completion_and_jump_to_continuation_label_block);
@ -2240,7 +2240,7 @@ Bytecode::CodeGenerationErrorOr<void> TaggedTemplateLiteral::generate_bytecode(B
auto raw_strings_reg = generator.allocate_register();
generator.emit<Bytecode::Op::Store>(raw_strings_reg);
generator.emit<Bytecode::Op::PutById>(strings_reg, generator.intern_identifier("raw"));
generator.emit<Bytecode::Op::PutById>(strings_reg, generator.intern_identifier("raw"), Bytecode::Op::PropertyKind::KeyValue, generator.next_property_lookup_cache());
generator.emit<Bytecode::Op::LoadImmediate>(js_undefined());
auto this_reg = generator.allocate_register();

View file

@ -137,7 +137,7 @@ ThrowCompletionOr<Value> get_global(Bytecode::Interpreter& interpreter, Deprecat
return vm.throw_completion<ReferenceError>(ErrorType::UnknownIdentifier, identifier);
}
ThrowCompletionOr<void> put_by_property_key(VM& vm, Value base, Value this_value, Value value, PropertyKey name, Op::PropertyKind kind)
ThrowCompletionOr<void> put_by_property_key(VM& vm, Value base, Value this_value, Value value, PropertyKey name, Op::PropertyKind kind, PropertyLookupCache* cache)
{
// Better error message than to_object would give
if (vm.in_strict_mode() && base.is_nullish())
@ -165,7 +165,22 @@ ThrowCompletionOr<void> put_by_property_key(VM& vm, Value base, Value this_value
break;
}
case Op::PropertyKind::KeyValue: {
bool succeeded = TRY(object->internal_set(name, value, this_value));
if (cache
&& cache->shape == &object->shape()
&& (!object->shape().is_unique() || object->shape().unique_shape_serial_number() == cache->unique_shape_serial_number)) {
object->put_direct(*cache->property_offset, value);
return {};
}
CacheablePropertyMetadata cacheable_metadata;
bool succeeded = TRY(object->internal_set(name, value, this_value, &cacheable_metadata));
if (succeeded && cache && cacheable_metadata.type == CacheablePropertyMetadata::Type::OwnProperty) {
cache->shape = object->shape();
cache->property_offset = cacheable_metadata.property_offset.value();
cache->unique_shape_serial_number = object->shape().unique_shape_serial_number();
}
if (!succeeded && vm.in_strict_mode()) {
if (base.is_object())
return vm.throw_completion<TypeError>(ErrorType::ReferenceNullishSetProperty, name, base.to_string_without_side_effects());

View file

@ -16,7 +16,7 @@ ThrowCompletionOr<NonnullGCPtr<Object>> base_object_for_get(VM&, Value base_valu
ThrowCompletionOr<Value> get_by_id(VM&, DeprecatedFlyString const& property, Value base_value, Value this_value, PropertyLookupCache&);
ThrowCompletionOr<Value> get_by_value(VM&, Value base_value, Value property_key_value);
ThrowCompletionOr<Value> get_global(Bytecode::Interpreter&, DeprecatedFlyString const& identifier, GlobalVariableCache&);
ThrowCompletionOr<void> put_by_property_key(VM&, Value base, Value this_value, Value value, PropertyKey name, Op::PropertyKind kind);
ThrowCompletionOr<void> put_by_property_key(VM&, Value base, Value this_value, Value value, PropertyKey name, Op::PropertyKind kind, PropertyLookupCache* = nullptr);
ThrowCompletionOr<Value> perform_call(Interpreter&, Value this_value, Op::CallType, Value callee, MarkedVector<Value> argument_values);
ThrowCompletionOr<void> throw_if_needed_for_call(Interpreter&, Value callee, Op::CallType, Optional<StringTableIndex> const& expression_string);
ThrowCompletionOr<Value> typeof_variable(VM&, DeprecatedFlyString const&);

View file

@ -297,7 +297,7 @@ CodeGenerationErrorOr<void> Generator::emit_store_to_reference(JS::ASTNode const
} else {
// 3. Let propertyKey be StringValue of IdentifierName.
auto identifier_table_ref = intern_identifier(verify_cast<Identifier>(expression.property()).string());
emit<Bytecode::Op::PutByIdWithThis>(super_reference.base, super_reference.this_value, identifier_table_ref);
emit<Bytecode::Op::PutByIdWithThis>(super_reference.base, super_reference.this_value, identifier_table_ref, Bytecode::Op::PropertyKind::KeyValue, next_property_lookup_cache());
}
} else {
TRY(expression.object().generate_bytecode(*this));
@ -314,7 +314,7 @@ CodeGenerationErrorOr<void> Generator::emit_store_to_reference(JS::ASTNode const
} else if (expression.property().is_identifier()) {
emit<Bytecode::Op::Load>(value_reg);
auto identifier_table_ref = intern_identifier(verify_cast<Identifier>(expression.property()).string());
emit<Bytecode::Op::PutById>(object_reg, identifier_table_ref);
emit<Bytecode::Op::PutById>(object_reg, identifier_table_ref, Bytecode::Op::PropertyKind::KeyValue, next_property_lookup_cache());
} else if (expression.property().is_private_identifier()) {
emit<Bytecode::Op::Load>(value_reg);
auto identifier_table_ref = intern_identifier(verify_cast<PrivateIdentifier>(expression.property()).string());

View file

@ -236,6 +236,7 @@ public:
[[nodiscard]] size_t next_global_variable_cache() { return m_next_global_variable_cache++; }
[[nodiscard]] size_t next_environment_variable_cache() { return m_next_environment_variable_cache++; }
[[nodiscard]] size_t next_property_lookup_cache() { return m_next_property_lookup_cache++; }
private:
enum class JumpType {

View file

@ -811,7 +811,8 @@ ThrowCompletionOr<void> PutById::execute_impl(Bytecode::Interpreter& interpreter
auto value = interpreter.accumulator();
auto base = interpreter.reg(m_base);
PropertyKey name = interpreter.current_executable().get_identifier(m_property);
TRY(put_by_property_key(vm, base, base, value, name, m_kind));
auto& cache = interpreter.current_executable().property_lookup_caches[m_cache_index];
TRY(put_by_property_key(vm, base, base, value, name, m_kind, &cache));
interpreter.accumulator() = value;
return {};
}
@ -823,7 +824,8 @@ ThrowCompletionOr<void> PutByIdWithThis::execute_impl(Bytecode::Interpreter& int
auto value = interpreter.accumulator();
auto base = interpreter.reg(m_base);
PropertyKey name = interpreter.current_executable().get_identifier(m_property);
TRY(put_by_property_key(vm, base, interpreter.reg(m_this_value), value, name, m_kind));
auto& cache = interpreter.current_executable().property_lookup_caches[m_cache_index];
TRY(put_by_property_key(vm, base, interpreter.reg(m_this_value), value, name, m_kind, &cache));
interpreter.accumulator() = value;
return {};
}

View file

@ -668,11 +668,12 @@ enum class PropertyKind {
class PutById final : public Instruction {
public:
explicit PutById(Register base, IdentifierTableIndex property, PropertyKind kind = PropertyKind::KeyValue)
explicit PutById(Register base, IdentifierTableIndex property, PropertyKind kind, u32 cache_index)
: Instruction(Type::PutById, sizeof(*this))
, m_base(base)
, m_property(property)
, m_kind(kind)
, m_cache_index(cache_index)
{
}
@ -682,21 +683,24 @@ public:
Register base() const { return m_base; }
IdentifierTableIndex property() const { return m_property; }
PropertyKind kind() const { return m_kind; }
u32 cache_index() const { return m_cache_index; }
private:
Register m_base;
IdentifierTableIndex m_property;
PropertyKind m_kind;
u32 m_cache_index { 0 };
};
class PutByIdWithThis final : public Instruction {
public:
PutByIdWithThis(Register base, Register this_value, IdentifierTableIndex property, PropertyKind kind = PropertyKind::KeyValue)
PutByIdWithThis(Register base, Register this_value, IdentifierTableIndex property, PropertyKind kind, u32 cache_index)
: Instruction(Type::PutByIdWithThis, sizeof(*this))
, m_base(base)
, m_this_value(this_value)
, m_property(property)
, m_kind(kind)
, m_cache_index(cache_index)
{
}
@ -707,12 +711,14 @@ public:
Register this_value() const { return m_this_value; }
IdentifierTableIndex property() const { return m_property; }
PropertyKind kind() const { return m_kind; }
u32 cache_index() const { return m_cache_index; }
private:
Register m_base;
Register m_this_value;
IdentifierTableIndex m_property;
PropertyKind m_kind;
u32 m_cache_index { 0 };
};
class PutPrivateById final : public Instruction {