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:
parent
28118623f5
commit
b1b2ca1485
28 changed files with 99 additions and 54 deletions
|
@ -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();
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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&);
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {};
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue