LibJS: Split PutBy* instructions into specialized per-kind variants

This allows the compiler to fold away lots of unused code for each kind.

1.10x speed-up on MicroBench/pic-add-own.js :^)
This commit is contained in:
Andreas Kling 2025-10-10 12:09:34 +02:00 committed by Andreas Kling
commit b47f8f94fe
Notes: github-actions[bot] 2025-10-11 18:10:13 +00:00
5 changed files with 439 additions and 305 deletions

View file

@ -653,7 +653,7 @@ Bytecode::CodeGenerationErrorOr<Optional<ScopedOperand>> AssignmentExpression::g
if (!lhs_is_super_expression) if (!lhs_is_super_expression)
generator.emit_put_by_id(*base, identifier_table_ref, rval, Bytecode::PutKind::Normal, generator.next_property_lookup_cache(), move(base_identifier)); generator.emit_put_by_id(*base, identifier_table_ref, rval, Bytecode::PutKind::Normal, generator.next_property_lookup_cache(), move(base_identifier));
else else
generator.emit<Bytecode::Op::PutByIdWithThis>(*base, *this_value, identifier_table_ref, rval, Bytecode::PutKind::Normal, generator.next_property_lookup_cache()); generator.emit<Bytecode::Op::PutNormalByIdWithThis>(*base, *this_value, identifier_table_ref, rval, generator.next_property_lookup_cache());
} else if (expression.property().is_private_identifier()) { } else if (expression.property().is_private_identifier()) {
auto identifier_table_ref = generator.intern_identifier(as<PrivateIdentifier>(expression.property()).string()); auto identifier_table_ref = generator.intern_identifier(as<PrivateIdentifier>(expression.property()).string());
generator.emit<Bytecode::Op::PutPrivateById>(*base, identifier_table_ref, rval); generator.emit<Bytecode::Op::PutPrivateById>(*base, identifier_table_ref, rval);

View file

@ -765,7 +765,7 @@ CodeGenerationErrorOr<void> Generator::emit_store_to_reference(JS::ASTNode const
} else { } else {
// 3. Let propertyKey be StringValue of IdentifierName. // 3. Let propertyKey be StringValue of IdentifierName.
auto identifier_table_ref = intern_identifier(as<Identifier>(expression.property()).string()); auto identifier_table_ref = intern_identifier(as<Identifier>(expression.property()).string());
emit<Bytecode::Op::PutByIdWithThis>(*super_reference.base, *super_reference.this_value, identifier_table_ref, value, Bytecode::PutKind::Normal, next_property_lookup_cache()); emit<Bytecode::Op::PutNormalByIdWithThis>(*super_reference.base, *super_reference.this_value, identifier_table_ref, value, next_property_lookup_cache());
} }
} else { } else {
auto object = TRY(expression.object().generate_bytecode(*this)).value(); auto object = TRY(expression.object().generate_bytecode(*this)).value();
@ -806,7 +806,7 @@ CodeGenerationErrorOr<void> Generator::emit_store_to_reference(ReferenceOperands
if (reference.base == reference.this_value) if (reference.base == reference.this_value)
emit_put_by_id(*reference.base, *reference.referenced_identifier, value, Bytecode::PutKind::Normal, next_property_lookup_cache()); emit_put_by_id(*reference.base, *reference.referenced_identifier, value, Bytecode::PutKind::Normal, next_property_lookup_cache());
else else
emit<Bytecode::Op::PutByIdWithThis>(*reference.base, *reference.this_value, *reference.referenced_identifier, value, Bytecode::PutKind::Normal, next_property_lookup_cache()); emit<Bytecode::Op::PutNormalByIdWithThis>(*reference.base, *reference.this_value, *reference.referenced_identifier, value, next_property_lookup_cache());
return {}; return {};
} }
if (reference.base == reference.this_value) if (reference.base == reference.this_value)
@ -1159,11 +1159,29 @@ void Generator::emit_put_by_id(Operand base, IdentifierTableIndex property, Oper
if (!string.is_empty() && !(string.code_unit_at(0) == '0' && string.length_in_code_units() > 1)) { if (!string.is_empty() && !(string.code_unit_at(0) == '0' && string.length_in_code_units() > 1)) {
auto property_index = string.to_number<u32>(TrimWhitespace::No); auto property_index = string.to_number<u32>(TrimWhitespace::No);
if (property_index.has_value() && property_index.value() < NumericLimits<u32>::max()) { if (property_index.has_value() && property_index.value() < NumericLimits<u32>::max()) {
emit<Op::PutByNumericId>(base, property_index.release_value(), src, kind, cache_index, move(base_identifier)); #define EMIT_PUT_BY_NUMERIC_ID(kind) \
case PutKind::kind: \
emit<Op::Put##kind##ByNumericId>(base, property_index.release_value(), src, cache_index, move(base_identifier)); \
break;
switch (kind) {
JS_ENUMERATE_PUT_KINDS(EMIT_PUT_BY_NUMERIC_ID)
default:
VERIFY_NOT_REACHED();
}
#undef EMIT_PUT_BY_NUMERIC_ID
return; return;
} }
} }
emit<Op::PutById>(base, property, src, kind, cache_index, move(base_identifier)); #define EMIT_PUT_BY_ID(kind) \
case PutKind::kind: \
emit<Op::Put##kind##ById>(base, property, src, cache_index, move(base_identifier)); \
break;
switch (kind) {
JS_ENUMERATE_PUT_KINDS(EMIT_PUT_BY_ID)
default:
VERIFY_NOT_REACHED();
}
#undef EMIT_PUT_BY_ID
} }
void Generator::emit_put_by_value(ScopedOperand base, ScopedOperand property, ScopedOperand src, Bytecode::PutKind kind, Optional<IdentifierTableIndex> base_identifier) void Generator::emit_put_by_value(ScopedOperand base, ScopedOperand property, ScopedOperand src, Bytecode::PutKind kind, Optional<IdentifierTableIndex> base_identifier)
@ -1175,7 +1193,16 @@ void Generator::emit_put_by_value(ScopedOperand base, ScopedOperand property, Sc
return; return;
} }
} }
emit<Op::PutByValue>(base, property, src, kind, base_identifier); #define EMIT_PUT_BY_VALUE(kind) \
case PutKind::kind: \
emit<Op::Put##kind##ByValue>(base, property, src, move(base_identifier)); \
break;
switch (kind) {
JS_ENUMERATE_PUT_KINDS(EMIT_PUT_BY_VALUE)
default:
VERIFY_NOT_REACHED();
}
#undef EMIT_PUT_BY_VALUE
} }
void Generator::emit_put_by_value_with_this(ScopedOperand base, ScopedOperand property, ScopedOperand this_value, ScopedOperand src, Bytecode::PutKind kind) void Generator::emit_put_by_value_with_this(ScopedOperand base, ScopedOperand property, ScopedOperand this_value, ScopedOperand src, Bytecode::PutKind kind)
@ -1183,11 +1210,29 @@ void Generator::emit_put_by_value_with_this(ScopedOperand base, ScopedOperand pr
if (property.operand().is_constant() && get_constant(property).is_string()) { if (property.operand().is_constant() && get_constant(property).is_string()) {
auto property_key = MUST(get_constant(property).to_property_key(vm())); auto property_key = MUST(get_constant(property).to_property_key(vm()));
if (property_key.is_string()) { if (property_key.is_string()) {
emit<Op::PutByIdWithThis>(base, this_value, intern_identifier(property_key.as_string()), src, kind, m_next_property_lookup_cache++); #define EMIT_PUT_BY_ID_WITH_THIS(kind) \
case PutKind::kind: \
emit<Op::Put##kind##ByIdWithThis>(base, this_value, intern_identifier(property_key.as_string()), src, m_next_property_lookup_cache++); \
break;
switch (kind) {
JS_ENUMERATE_PUT_KINDS(EMIT_PUT_BY_ID_WITH_THIS)
default:
VERIFY_NOT_REACHED();
}
#undef EMIT_PUT_BY_ID_WITH_THIS
return; return;
} }
} }
emit<Bytecode::Op::PutByValueWithThis>(base, property, this_value, src, kind); #define EMIT_PUT_BY_VALUE_WITH_THIS(kind) \
case PutKind::kind: \
emit<Op::Put##kind##ByValueWithThis>(base, property, this_value, src); \
break;
switch (kind) {
JS_ENUMERATE_PUT_KINDS(EMIT_PUT_BY_VALUE_WITH_THIS)
default:
VERIFY_NOT_REACHED();
}
#undef EMIT_PUT_BY_VALUE_WITH_THIS
} }
void Generator::emit_iterator_value(ScopedOperand dst, ScopedOperand result) void Generator::emit_iterator_value(ScopedOperand dst, ScopedOperand result)

View file

@ -123,12 +123,37 @@
O(PrepareYield) \ O(PrepareYield) \
O(PostfixDecrement) \ O(PostfixDecrement) \
O(PostfixIncrement) \ O(PostfixIncrement) \
O(PutById) \ O(PutNormalById) \
O(PutByNumericId) \ O(PutOwnById) \
O(PutByIdWithThis) \ O(PutGetterById) \
O(PutSetterById) \
O(PutPrototypeById) \
O(PutNormalByNumericId) \
O(PutOwnByNumericId) \
O(PutGetterByNumericId) \
O(PutSetterByNumericId) \
O(PutPrototypeByNumericId) \
O(PutNormalByIdWithThis) \
O(PutOwnByIdWithThis) \
O(PutGetterByIdWithThis) \
O(PutSetterByIdWithThis) \
O(PutPrototypeByIdWithThis) \
O(PutNormalByNumericIdWithThis) \
O(PutOwnByNumericIdWithThis) \
O(PutGetterByNumericIdWithThis) \
O(PutSetterByNumericIdWithThis) \
O(PutPrototypeByNumericIdWithThis) \
O(PutBySpread) \ O(PutBySpread) \
O(PutByValue) \ O(PutNormalByValue) \
O(PutByValueWithThis) \ O(PutOwnByValue) \
O(PutGetterByValue) \
O(PutSetterByValue) \
O(PutPrototypeByValue) \
O(PutNormalByValueWithThis) \
O(PutOwnByValueWithThis) \
O(PutGetterByValueWithThis) \
O(PutSetterByValueWithThis) \
O(PutPrototypeByValueWithThis) \
O(PutPrivateById) \ O(PutPrivateById) \
O(ResolveSuperBase) \ O(ResolveSuperBase) \
O(ResolveThisBinding) \ O(ResolveThisBinding) \

View file

@ -638,12 +638,22 @@ FLATTEN_ON_CLANG void Interpreter::run_bytecode(size_t entry_point)
HANDLE_INSTRUCTION_WITHOUT_EXCEPTION_CHECK(PrepareYield); HANDLE_INSTRUCTION_WITHOUT_EXCEPTION_CHECK(PrepareYield);
HANDLE_INSTRUCTION(PostfixDecrement); HANDLE_INSTRUCTION(PostfixDecrement);
HANDLE_INSTRUCTION(PostfixIncrement); HANDLE_INSTRUCTION(PostfixIncrement);
HANDLE_INSTRUCTION(PutById);
HANDLE_INSTRUCTION(PutByNumericId); #define HANDLE_PUT_KIND_BY_ID(kind) HANDLE_INSTRUCTION(Put##kind##ById);
HANDLE_INSTRUCTION(PutByIdWithThis); #define HANDLE_PUT_KIND_BY_NUMERIC_ID(kind) HANDLE_INSTRUCTION(Put##kind##ByNumericId);
#define HANDLE_PUT_KIND_BY_VALUE(kind) HANDLE_INSTRUCTION(Put##kind##ByValue);
#define HANDLE_PUT_KIND_BY_VALUE_WITH_THIS(kind) HANDLE_INSTRUCTION(Put##kind##ByValueWithThis);
#define HANDLE_PUT_KIND_BY_ID_WITH_THIS(kind) HANDLE_INSTRUCTION(Put##kind##ByIdWithThis);
#define HANDLE_PUT_KIND_BY_NUMERIC_ID_WITH_THIS(kind) HANDLE_INSTRUCTION(Put##kind##ByNumericIdWithThis);
JS_ENUMERATE_PUT_KINDS(HANDLE_PUT_KIND_BY_ID)
JS_ENUMERATE_PUT_KINDS(HANDLE_PUT_KIND_BY_NUMERIC_ID)
JS_ENUMERATE_PUT_KINDS(HANDLE_PUT_KIND_BY_ID_WITH_THIS)
JS_ENUMERATE_PUT_KINDS(HANDLE_PUT_KIND_BY_NUMERIC_ID_WITH_THIS)
JS_ENUMERATE_PUT_KINDS(HANDLE_PUT_KIND_BY_VALUE)
JS_ENUMERATE_PUT_KINDS(HANDLE_PUT_KIND_BY_VALUE_WITH_THIS)
HANDLE_INSTRUCTION(PutBySpread); HANDLE_INSTRUCTION(PutBySpread);
HANDLE_INSTRUCTION(PutByValue);
HANDLE_INSTRUCTION(PutByValueWithThis);
HANDLE_INSTRUCTION(PutPrivateById); HANDLE_INSTRUCTION(PutPrivateById);
HANDLE_INSTRUCTION(ResolveSuperBase); HANDLE_INSTRUCTION(ResolveSuperBase);
HANDLE_INSTRUCTION(ResolveThisBinding); HANDLE_INSTRUCTION(ResolveThisBinding);
@ -1188,7 +1198,8 @@ inline ThrowCompletionOr<Value> get_global(Interpreter& interpreter, IdentifierT
return vm.throw_completion<ReferenceError>(ErrorType::UnknownIdentifier, identifier); return vm.throw_completion<ReferenceError>(ErrorType::UnknownIdentifier, identifier);
} }
inline ThrowCompletionOr<void> put_by_property_key(VM& vm, Value base, Value this_value, Value value, Optional<Utf16FlyString const&> const& base_identifier, PropertyKey name, PutKind kind, PropertyLookupCache* caches = nullptr) template<PutKind kind>
ThrowCompletionOr<void> put_by_property_key(VM& vm, Value base, Value this_value, Value value, Optional<Utf16FlyString const&> const& base_identifier, PropertyKey name, PropertyLookupCache* caches = nullptr)
{ {
// Better error message than to_object would give // Better error message than to_object would give
if (vm.in_strict_mode() && base.is_nullish()) if (vm.in_strict_mode() && base.is_nullish())
@ -1200,7 +1211,7 @@ inline ThrowCompletionOr<void> put_by_property_key(VM& vm, Value base, Value thi
return throw_null_or_undefined_property_access(vm, base, base_identifier, name); return throw_null_or_undefined_property_access(vm, base, base_identifier, name);
auto object = maybe_object.release_value(); auto object = maybe_object.release_value();
if (kind == PutKind::Getter || kind == PutKind::Setter) { if constexpr (kind == PutKind::Getter || kind == PutKind::Setter) {
// The generator should only pass us functions for getters and setters. // The generator should only pass us functions for getters and setters.
VERIFY(value.is_function()); VERIFY(value.is_function());
} }
@ -1243,7 +1254,7 @@ inline ThrowCompletionOr<void> put_by_property_key(VM& vm, Value base, Value thi
if (can_use_cache) { if (can_use_cache) {
auto value_in_prototype = cache.prototype->get_direct(cache.property_offset.value()); auto value_in_prototype = cache.prototype->get_direct(cache.property_offset.value());
if (value_in_prototype.is_accessor()) { if (value_in_prototype.is_accessor()) {
TRY(call(vm, value_in_prototype.as_accessor().setter(), this_value, value)); (void)TRY(call(vm, value_in_prototype.as_accessor().setter(), this_value, value));
return {}; return {};
} }
} }
@ -1254,7 +1265,7 @@ inline ThrowCompletionOr<void> put_by_property_key(VM& vm, Value base, Value thi
break; break;
auto value_in_object = object->get_direct(cache.property_offset.value()); auto value_in_object = object->get_direct(cache.property_offset.value());
if (value_in_object.is_accessor()) { if (value_in_object.is_accessor()) {
TRY(call(vm, value_in_object.as_accessor().setter(), this_value, value)); (void)TRY(call(vm, value_in_object.as_accessor().setter(), this_value, value));
} else { } else {
object->put_direct(*cache.property_offset, value); object->put_direct(*cache.property_offset, value);
} }
@ -1414,10 +1425,11 @@ inline Value new_function(VM& vm, FunctionNode const& function_node, Optional<Id
return value; return value;
} }
inline ThrowCompletionOr<void> put_by_value(VM& vm, Value base, Optional<Utf16FlyString const&> const& base_identifier, Value property_key_value, Value value, PutKind kind) template<PutKind kind>
inline ThrowCompletionOr<void> put_by_value(VM& vm, Value base, Optional<Utf16FlyString const&> const& base_identifier, Value property_key_value, Value value)
{ {
// OPTIMIZATION: Fast path for simple Int32 indexes in array-like objects. // OPTIMIZATION: Fast path for simple Int32 indexes in array-like objects.
if ((kind == PutKind::Normal || kind == PutKind::Own) if (kind == PutKind::Normal
&& base.is_object() && property_key_value.is_int32() && property_key_value.as_i32() >= 0) { && base.is_object() && property_key_value.is_int32() && property_key_value.as_i32() >= 0) {
auto& object = base.as_object(); auto& object = base.as_object();
auto* storage = object.indexed_properties().storage(); auto* storage = object.indexed_properties().storage();
@ -1526,7 +1538,7 @@ inline ThrowCompletionOr<void> put_by_value(VM& vm, Value base, Optional<Utf16Fl
} }
auto property_key = TRY(property_key_value.to_property_key(vm)); auto property_key = TRY(property_key_value.to_property_key(vm));
TRY(put_by_property_key(vm, base, base, value, base_identifier, property_key, kind)); TRY(put_by_property_key<kind>(vm, base, base, value, base_identifier, property_key));
return {}; return {};
} }
@ -2682,40 +2694,93 @@ ThrowCompletionOr<void> PutBySpread::execute_impl(Bytecode::Interpreter& interpr
return {}; return {};
} }
ThrowCompletionOr<void> PutById::execute_impl(Bytecode::Interpreter& interpreter) const #define DEFINE_PUT_KIND_BY_ID(kind) \
{ ThrowCompletionOr<void> Put##kind##ById::execute_impl(Bytecode::Interpreter& interpreter) const \
auto& vm = interpreter.vm(); { \
auto value = interpreter.get(m_src); auto& vm = interpreter.vm(); \
auto base = interpreter.get(m_base); auto value = interpreter.get(m_src); \
auto base_identifier = interpreter.get_identifier(m_base_identifier); auto base = interpreter.get(m_base); \
PropertyKey name { interpreter.get_identifier(m_property), PropertyKey::StringMayBeNumber::No }; auto base_identifier = interpreter.get_identifier(m_base_identifier); \
auto& cache = interpreter.current_executable().property_lookup_caches[m_cache_index]; PropertyKey name { interpreter.get_identifier(m_property), PropertyKey::StringMayBeNumber::No }; \
TRY(put_by_property_key(vm, base, base, value, base_identifier, name, m_kind, &cache)); auto& cache = interpreter.current_executable().property_lookup_caches[m_cache_index]; \
return {}; TRY(put_by_property_key<PutKind::kind>(vm, base, base, value, base_identifier, name, &cache)); \
} return {}; \
} \
ByteString Put##kind##ById::to_byte_string_impl(Bytecode::Executable const& executable) const \
{ \
return ByteString::formatted("Put" #kind "ById {}, {}, {}", \
format_operand("base"sv, m_base, executable), \
executable.identifier_table->get(m_property), \
format_operand("src"sv, m_src, executable)); \
}
ThrowCompletionOr<void> PutByNumericId::execute_impl(Bytecode::Interpreter& interpreter) const JS_ENUMERATE_PUT_KINDS(DEFINE_PUT_KIND_BY_ID)
{
auto& vm = interpreter.vm();
auto value = interpreter.get(m_src);
auto base = interpreter.get(m_base);
auto base_identifier = interpreter.get_identifier(m_base_identifier);
PropertyKey name { m_property_index };
auto& cache = interpreter.current_executable().property_lookup_caches[m_cache_index];
TRY(put_by_property_key(vm, base, base, value, base_identifier, name, m_kind, &cache));
return {};
}
ThrowCompletionOr<void> PutByIdWithThis::execute_impl(Bytecode::Interpreter& interpreter) const #define DEFINE_PUT_KIND_BY_NUMERIC_ID(kind) \
{ ThrowCompletionOr<void> Put##kind##ByNumericId::execute_impl(Bytecode::Interpreter& interpreter) const \
auto& vm = interpreter.vm(); { \
auto value = interpreter.get(m_src); auto& vm = interpreter.vm(); \
auto base = interpreter.get(m_base); auto value = interpreter.get(m_src); \
PropertyKey name { interpreter.get_identifier(m_property), PropertyKey::StringMayBeNumber::No }; auto base = interpreter.get(m_base); \
auto& cache = interpreter.current_executable().property_lookup_caches[m_cache_index]; auto base_identifier = interpreter.get_identifier(m_base_identifier); \
TRY(put_by_property_key(vm, base, interpreter.get(m_this_value), value, {}, name, m_kind, &cache)); PropertyKey name { m_property }; \
return {}; auto& cache = interpreter.current_executable().property_lookup_caches[m_cache_index]; \
} TRY(put_by_property_key<PutKind::kind>(vm, base, base, value, base_identifier, name, &cache)); \
return {}; \
} \
ByteString Put##kind##ByNumericId::to_byte_string_impl(Bytecode::Executable const& executable) const \
{ \
return ByteString::formatted("Put" #kind "ByNumericId {}, {}, {}", \
format_operand("base"sv, m_base, executable), \
m_property, \
format_operand("src"sv, m_src, executable)); \
}
JS_ENUMERATE_PUT_KINDS(DEFINE_PUT_KIND_BY_NUMERIC_ID)
#define DEFINE_PUT_KIND_BY_NUMERIC_ID_WITH_THIS(kind) \
ThrowCompletionOr<void> Put##kind##ByNumericIdWithThis::execute_impl(Bytecode::Interpreter& interpreter) const \
{ \
auto& vm = interpreter.vm(); \
auto value = interpreter.get(m_src); \
auto base = interpreter.get(m_base); \
PropertyKey name { m_property }; \
auto& cache = interpreter.current_executable().property_lookup_caches[m_cache_index]; \
TRY(put_by_property_key<PutKind::kind>(vm, base, interpreter.get(m_this_value), value, {}, name, &cache)); \
return {}; \
} \
ByteString Put##kind##ByNumericIdWithThis::to_byte_string_impl(Bytecode::Executable const& executable) const \
{ \
return ByteString::formatted("Put" #kind "ByNumericIdWithThis {}, {}, {}, {}", \
format_operand("base"sv, m_base, executable), \
m_property, \
format_operand("src"sv, m_src, executable), \
format_operand("this"sv, m_this_value, executable)); \
}
JS_ENUMERATE_PUT_KINDS(DEFINE_PUT_KIND_BY_NUMERIC_ID_WITH_THIS)
#define DEFINE_PUT_KIND_BY_ID_WITH_THIS(kind) \
ThrowCompletionOr<void> Put##kind##ByIdWithThis::execute_impl(Bytecode::Interpreter& interpreter) const \
{ \
auto& vm = interpreter.vm(); \
auto value = interpreter.get(m_src); \
auto base = interpreter.get(m_base); \
PropertyKey name { interpreter.get_identifier(m_property), PropertyKey::StringMayBeNumber::No }; \
auto& cache = interpreter.current_executable().property_lookup_caches[m_cache_index]; \
TRY(put_by_property_key<PutKind::kind>(vm, base, interpreter.get(m_this_value), value, {}, name, &cache)); \
return {}; \
} \
ByteString Put##kind##ByIdWithThis::to_byte_string_impl(Bytecode::Executable const& executable) const \
{ \
return ByteString::formatted("Put" #kind "ByIdWithThis {}, {}, {}, {}", \
format_operand("base"sv, m_base, executable), \
executable.identifier_table->get(m_property), \
format_operand("src"sv, m_src, executable), \
format_operand("this"sv, m_this_value, executable)); \
}
JS_ENUMERATE_PUT_KINDS(DEFINE_PUT_KIND_BY_ID_WITH_THIS)
ThrowCompletionOr<void> PutPrivateById::execute_impl(Bytecode::Interpreter& interpreter) const ThrowCompletionOr<void> PutPrivateById::execute_impl(Bytecode::Interpreter& interpreter) const
{ {
@ -3218,24 +3283,48 @@ ThrowCompletionOr<void> GetByValueWithThis::execute_impl(Bytecode::Interpreter&
return {}; return {};
} }
ThrowCompletionOr<void> PutByValue::execute_impl(Bytecode::Interpreter& interpreter) const #define DEFINE_PUT_KIND_BY_VALUE(kind) \
{ ThrowCompletionOr<void> Put##kind##ByValue::execute_impl(Bytecode::Interpreter& interpreter) const \
auto& vm = interpreter.vm(); { \
auto value = interpreter.get(m_src); auto& vm = interpreter.vm(); \
auto base_identifier = interpreter.get_identifier(m_base_identifier); auto value = interpreter.get(m_src); \
TRY(put_by_value(vm, interpreter.get(m_base), base_identifier, interpreter.get(m_property), value, m_kind)); auto base = interpreter.get(m_base); \
return {}; auto base_identifier = interpreter.get_identifier(m_base_identifier); \
} auto property = interpreter.get(m_property); \
TRY(put_by_value<PutKind::kind>(vm, base, base_identifier, property, value)); \
return {}; \
} \
ByteString Put##kind##ByValue::to_byte_string_impl(Bytecode::Executable const& executable) const \
{ \
return ByteString::formatted("Put" #kind "ByValue {}, {}, {}", \
format_operand("base"sv, m_base, executable), \
format_operand("property"sv, m_property, executable), \
format_operand("src"sv, m_src, executable)); \
}
ThrowCompletionOr<void> PutByValueWithThis::execute_impl(Bytecode::Interpreter& interpreter) const JS_ENUMERATE_PUT_KINDS(DEFINE_PUT_KIND_BY_VALUE)
{
auto& vm = interpreter.vm(); #define DEFINE_PUT_KIND_BY_VALUE_WITH_THIS(kind) \
auto value = interpreter.get(m_src); ThrowCompletionOr<void> Put##kind##ByValueWithThis::execute_impl(Bytecode::Interpreter& interpreter) const \
auto base = interpreter.get(m_base); { \
auto property_key = TRY(interpreter.get(m_property).to_property_key(vm)); auto& vm = interpreter.vm(); \
TRY(put_by_property_key(vm, base, interpreter.get(m_this_value), value, {}, property_key, m_kind)); auto value = interpreter.get(m_src); \
return {}; auto base = interpreter.get(m_base); \
} auto this_value = interpreter.get(m_this_value); \
auto property_key = TRY(interpreter.get(m_property).to_property_key(vm)); \
TRY(put_by_property_key<PutKind::kind>(vm, base, this_value, value, {}, property_key)); \
return {}; \
} \
ByteString Put##kind##ByValueWithThis::to_byte_string_impl(Bytecode::Executable const& executable) const \
{ \
return ByteString::formatted("Put" #kind "ByValueWithThis {}, {}, {}, {}", \
format_operand("base"sv, m_base, executable), \
format_operand("property"sv, m_property, executable), \
format_operand("src"sv, m_src, executable), \
format_operand("this"sv, m_this_value, executable)); \
}
JS_ENUMERATE_PUT_KINDS(DEFINE_PUT_KIND_BY_VALUE_WITH_THIS)
ThrowCompletionOr<void> DeleteByValue::execute_impl(Bytecode::Interpreter& interpreter) const ThrowCompletionOr<void> DeleteByValue::execute_impl(Bytecode::Interpreter& interpreter) const
{ {
@ -3591,23 +3680,6 @@ ByteString SetVariableBinding::to_byte_string_impl(Bytecode::Executable const& e
format_operand("src"sv, src(), executable)); format_operand("src"sv, src(), executable));
} }
static StringView property_kind_to_string(PutKind kind)
{
switch (kind) {
case PutKind::Getter:
return "getter"sv;
case PutKind::Setter:
return "setter"sv;
case PutKind::Normal:
return "key-value"sv;
case PutKind::Own:
return "direct-key-value"sv;
case PutKind::Prototype:
return "proto-setter"sv;
}
VERIFY_NOT_REACHED();
}
ByteString PutBySpread::to_byte_string_impl(Bytecode::Executable const& executable) const ByteString PutBySpread::to_byte_string_impl(Bytecode::Executable const& executable) const
{ {
return ByteString::formatted("PutBySpread {}, {}", return ByteString::formatted("PutBySpread {}, {}",
@ -3615,37 +3687,6 @@ ByteString PutBySpread::to_byte_string_impl(Bytecode::Executable const& executab
format_operand("src"sv, m_src, executable)); format_operand("src"sv, m_src, executable));
} }
ByteString PutById::to_byte_string_impl(Bytecode::Executable const& executable) const
{
auto kind = property_kind_to_string(m_kind);
return ByteString::formatted("PutById {}, {}, {}, kind:{}",
format_operand("base"sv, m_base, executable),
executable.identifier_table->get(m_property),
format_operand("src"sv, m_src, executable),
kind);
}
ByteString PutByNumericId::to_byte_string_impl(Bytecode::Executable const& executable) const
{
auto kind = property_kind_to_string(m_kind);
return ByteString::formatted("PutByNumericId {}, {}, {}, kind:{}",
format_operand("base"sv, m_base, executable),
m_property_index,
format_operand("src"sv, m_src, executable),
kind);
}
ByteString PutByIdWithThis::to_byte_string_impl(Bytecode::Executable const& executable) const
{
auto kind = property_kind_to_string(m_kind);
return ByteString::formatted("PutByIdWithThis {}, {}, {}, {}, kind:{}",
format_operand("base"sv, m_base, executable),
executable.identifier_table->get(m_property),
format_operand("src"sv, m_src, executable),
format_operand("this"sv, m_this_value, executable),
kind);
}
ByteString PutPrivateById::to_byte_string_impl(Bytecode::Executable const& executable) const ByteString PutPrivateById::to_byte_string_impl(Bytecode::Executable const& executable) const
{ {
return ByteString::formatted( return ByteString::formatted(
@ -4045,27 +4086,6 @@ ByteString GetByValueWithThis::to_byte_string_impl(Bytecode::Executable const& e
format_operand("property"sv, m_property, executable)); format_operand("property"sv, m_property, executable));
} }
ByteString PutByValue::to_byte_string_impl(Bytecode::Executable const& executable) const
{
auto kind = property_kind_to_string(m_kind);
return ByteString::formatted("PutByValue {}, {}, {}, kind:{}",
format_operand("base"sv, m_base, executable),
format_operand("property"sv, m_property, executable),
format_operand("src"sv, m_src, executable),
kind);
}
ByteString PutByValueWithThis::to_byte_string_impl(Bytecode::Executable const& executable) const
{
auto kind = property_kind_to_string(m_kind);
return ByteString::formatted("PutByValueWithThis {}, {}, {}, {}, kind:{}",
format_operand("base"sv, m_base, executable),
format_operand("property"sv, m_property, executable),
format_operand("src"sv, m_src, executable),
format_operand("this"sv, m_this_value, executable),
kind);
}
ByteString DeleteByValue::to_byte_string_impl(Bytecode::Executable const& executable) const ByteString DeleteByValue::to_byte_string_impl(Bytecode::Executable const& executable) const
{ {
return ByteString::formatted("DeleteByValue {}, {}, {}", return ByteString::formatted("DeleteByValue {}, {}, {}",

View file

@ -1205,109 +1205,153 @@ private:
Operand m_src; Operand m_src;
}; };
class PutById final : public Instruction { #define DECLARE_PUT_KIND_BY_ID(kind) \
public: class Put##kind##ById final : public Instruction { \
explicit PutById(Operand base, IdentifierTableIndex property, Operand src, PutKind kind, u32 cache_index, Optional<IdentifierTableIndex> base_identifier = {}) public: \
: Instruction(Type::PutById) explicit Put##kind##ById(Operand base, IdentifierTableIndex property, Operand src, u32 cache_index, Optional<IdentifierTableIndex> base_identifier = {}) \
, m_base(base) : Instruction(Type::Put##kind##ById) \
, m_property(property) , m_base(base) \
, m_src(src) , m_property(property) \
, m_kind(kind) , m_src(src) \
, m_cache_index(cache_index) , m_cache_index(cache_index) \
, m_base_identifier(move(base_identifier)) , m_base_identifier(move(base_identifier)) \
{ { \
} } \
\
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const; \
ByteString to_byte_string_impl(Bytecode::Executable const&) const; \
void visit_operands_impl(Function<void(Operand&)> visitor) \
{ \
visitor(m_base); \
visitor(m_src); \
} \
\
Operand base() const { return m_base; } \
IdentifierTableIndex property() const { return m_property; } \
Operand src() const { return m_src; } \
u32 cache_index() const { return m_cache_index; } \
\
private: \
Operand m_base; \
IdentifierTableIndex m_property; \
Operand m_src; \
u32 m_cache_index { 0 }; \
Optional<IdentifierTableIndex> m_base_identifier {}; \
};
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const; JS_ENUMERATE_PUT_KINDS(DECLARE_PUT_KIND_BY_ID)
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_base);
visitor(m_src);
}
Operand base() const { return m_base; } #define DECLARE_PUT_KIND_BY_NUMERIC_ID(kind) \
IdentifierTableIndex property() const { return m_property; } class Put##kind##ByNumericId final : public Instruction { \
Operand src() const { return m_src; } public: \
PutKind kind() const { return m_kind; } explicit Put##kind##ByNumericId(Operand base, u32 property, Operand src, u32 cache_index, Optional<IdentifierTableIndex> base_identifier = {}) \
u32 cache_index() const { return m_cache_index; } : Instruction(Type::Put##kind##ByNumericId) \
, m_base(base) \
, m_property(property) \
, m_src(src) \
, m_cache_index(cache_index) \
, m_base_identifier(move(base_identifier)) \
{ \
} \
\
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const; \
ByteString to_byte_string_impl(Bytecode::Executable const&) const; \
void visit_operands_impl(Function<void(Operand&)> visitor) \
{ \
visitor(m_base); \
visitor(m_src); \
} \
\
Operand base() const { return m_base; } \
u32 property() const { return m_property; } \
Operand src() const { return m_src; } \
u32 cache_index() const { return m_cache_index; } \
\
private: \
Operand m_base; \
u32 m_property; \
Operand m_src; \
u32 m_cache_index { 0 }; \
Optional<IdentifierTableIndex> m_base_identifier {}; \
};
private: JS_ENUMERATE_PUT_KINDS(DECLARE_PUT_KIND_BY_NUMERIC_ID)
Operand m_base;
IdentifierTableIndex m_property;
Operand m_src;
PutKind m_kind;
u32 m_cache_index { 0 };
Optional<IdentifierTableIndex> m_base_identifier {};
};
class PutByNumericId final : public Instruction { #define DECLARE_PUT_KIND_BY_ID_WITH_THIS(kind) \
public: class Put##kind##ByIdWithThis final : public Instruction { \
explicit PutByNumericId(Operand base, u64 property_index, Operand src, PutKind kind, u32 cache_index, Optional<IdentifierTableIndex> base_identifier = {}) public: \
: Instruction(Type::PutByNumericId) Put##kind##ByIdWithThis(Operand base, Operand this_value, IdentifierTableIndex property, Operand src, u32 cache_index) \
, m_base(base) : Instruction(Type::Put##kind##ByIdWithThis) \
, m_property_index(property_index) , m_base(base) \
, m_src(src) , m_this_value(this_value) \
, m_kind(kind) , m_property(property) \
, m_cache_index(cache_index) , m_src(src) \
, m_base_identifier(move(base_identifier)) , m_cache_index(cache_index) \
{ { \
} } \
\
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const; \
ByteString to_byte_string_impl(Bytecode::Executable const&) const; \
void visit_operands_impl(Function<void(Operand&)> visitor) \
{ \
visitor(m_base); \
visitor(m_this_value); \
visitor(m_src); \
} \
\
Operand base() const { return m_base; } \
Operand this_value() const { return m_this_value; } \
IdentifierTableIndex property() const { return m_property; } \
Operand src() const { return m_src; } \
u32 cache_index() const { return m_cache_index; } \
\
private: \
Operand m_base; \
Operand m_this_value; \
IdentifierTableIndex m_property; \
Operand m_src; \
u32 m_cache_index { 0 }; \
};
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const; JS_ENUMERATE_PUT_KINDS(DECLARE_PUT_KIND_BY_ID_WITH_THIS)
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_base);
visitor(m_src);
}
private: #define DECLARE_PUT_KIND_BY_NUMERIC_ID_WITH_THIS(kind) \
Operand m_base; class Put##kind##ByNumericIdWithThis final : public Instruction { \
u64 m_property_index; public: \
Operand m_src; Put##kind##ByNumericIdWithThis(Operand base, Operand this_value, u32 property, Operand src, u32 cache_index) \
PutKind m_kind; : Instruction(Type::Put##kind##ByNumericIdWithThis) \
u32 m_cache_index { 0 }; , m_base(base) \
Optional<IdentifierTableIndex> m_base_identifier {}; , m_this_value(this_value) \
}; , m_property(property) \
, m_src(src) \
, m_cache_index(cache_index) \
{ \
} \
\
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const; \
ByteString to_byte_string_impl(Bytecode::Executable const&) const; \
void visit_operands_impl(Function<void(Operand&)> visitor) \
{ \
visitor(m_base); \
visitor(m_this_value); \
visitor(m_src); \
} \
\
Operand base() const { return m_base; } \
Operand this_value() const { return m_this_value; } \
u32 property() const { return m_property; } \
Operand src() const { return m_src; } \
u32 cache_index() const { return m_cache_index; } \
\
private: \
Operand m_base; \
Operand m_this_value; \
u32 m_property; \
Operand m_src; \
u32 m_cache_index { 0 }; \
};
class PutByIdWithThis final : public Instruction { JS_ENUMERATE_PUT_KINDS(DECLARE_PUT_KIND_BY_NUMERIC_ID_WITH_THIS)
public:
PutByIdWithThis(Operand base, Operand this_value, IdentifierTableIndex property, Operand src, PutKind kind, u32 cache_index)
: Instruction(Type::PutByIdWithThis)
, m_base(base)
, m_this_value(this_value)
, m_property(property)
, m_src(src)
, m_kind(kind)
, m_cache_index(cache_index)
{
}
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_base);
visitor(m_this_value);
visitor(m_src);
}
Operand base() const { return m_base; }
Operand this_value() const { return m_this_value; }
IdentifierTableIndex property() const { return m_property; }
Operand src() const { return m_src; }
PutKind kind() const { return m_kind; }
u32 cache_index() const { return m_cache_index; }
private:
Operand m_base;
Operand m_this_value;
IdentifierTableIndex m_property;
Operand m_src;
PutKind m_kind;
u32 m_cache_index { 0 };
};
class PutPrivateById final : public Instruction { class PutPrivateById final : public Instruction {
public: public:
@ -1461,75 +1505,75 @@ private:
Operand m_this_value; Operand m_this_value;
}; };
class PutByValue final : public Instruction { #define DECLARE_PUT_KIND_BY_VALUE(kind) \
public: class Put##kind##ByValue final : public Instruction { \
PutByValue(Operand base, Operand property, Operand src, PutKind kind = PutKind::Normal, Optional<IdentifierTableIndex> base_identifier = {}) public: \
: Instruction(Type::PutByValue) Put##kind##ByValue(Operand base, Operand property, Operand src, Optional<IdentifierTableIndex> base_identifier = {}) \
, m_base(base) : Instruction(Type::Put##kind##ByValue) \
, m_property(property) , m_base(base) \
, m_src(src) , m_property(property) \
, m_kind(kind) , m_src(src) \
, m_base_identifier(move(base_identifier)) , m_base_identifier(move(base_identifier)) \
{ { \
} } \
\
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const; \
ByteString to_byte_string_impl(Bytecode::Executable const&) const; \
void visit_operands_impl(Function<void(Operand&)> visitor) \
{ \
visitor(m_base); \
visitor(m_property); \
visitor(m_src); \
} \
\
Operand base() const { return m_base; } \
Operand property() const { return m_property; } \
Operand src() const { return m_src; } \
\
private: \
Operand m_base; \
Operand m_property; \
Operand m_src; \
Optional<IdentifierTableIndex> m_base_identifier; \
};
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const; JS_ENUMERATE_PUT_KINDS(DECLARE_PUT_KIND_BY_VALUE)
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_base);
visitor(m_property);
visitor(m_src);
}
Operand base() const { return m_base; } #define DECLARE_PUT_KIND_BY_VALUE_WITH_THIS(kind) \
Operand property() const { return m_property; } class Put##kind##ByValueWithThis final : public Instruction { \
Operand src() const { return m_src; } public: \
PutKind kind() const { return m_kind; } Put##kind##ByValueWithThis(Operand base, Operand property, Operand this_value, Operand src) \
: Instruction(Type::Put##kind##ByValueWithThis) \
, m_base(base) \
, m_property(property) \
, m_this_value(this_value) \
, m_src(src) \
{ \
} \
\
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const; \
ByteString to_byte_string_impl(Bytecode::Executable const&) const; \
void visit_operands_impl(Function<void(Operand&)> visitor) \
{ \
visitor(m_base); \
visitor(m_property); \
visitor(m_this_value); \
visitor(m_src); \
} \
\
Operand base() const { return m_base; } \
Operand property() const { return m_property; } \
Operand this_value() const { return m_this_value; } \
Operand src() const { return m_src; } \
\
private: \
Operand m_base; \
Operand m_property; \
Operand m_this_value; \
Operand m_src; \
};
private: JS_ENUMERATE_PUT_KINDS(DECLARE_PUT_KIND_BY_VALUE_WITH_THIS)
Operand m_base;
Operand m_property;
Operand m_src;
PutKind m_kind;
Optional<IdentifierTableIndex> m_base_identifier;
};
class PutByValueWithThis final : public Instruction {
public:
PutByValueWithThis(Operand base, Operand property, Operand this_value, Operand src, PutKind kind = PutKind::Normal)
: Instruction(Type::PutByValueWithThis)
, m_base(base)
, m_property(property)
, m_this_value(this_value)
, m_src(src)
, m_kind(kind)
{
}
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_base);
visitor(m_property);
visitor(m_this_value);
visitor(m_src);
}
Operand base() const { return m_base; }
Operand property() const { return m_property; }
Operand this_value() const { return m_this_value; }
Operand src() const { return m_src; }
PutKind kind() const { return m_kind; }
private:
Operand m_base;
Operand m_property;
Operand m_this_value;
Operand m_src;
PutKind m_kind;
};
class DeleteByValue final : public Instruction { class DeleteByValue final : public Instruction {
public: public: