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

@ -765,7 +765,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(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 {
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)
emit_put_by_id(*reference.base, *reference.referenced_identifier, value, Bytecode::PutKind::Normal, next_property_lookup_cache());
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 {};
}
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)) {
auto property_index = string.to_number<u32>(TrimWhitespace::No);
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;
}
}
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)
@ -1175,7 +1193,16 @@ void Generator::emit_put_by_value(ScopedOperand base, ScopedOperand property, Sc
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)
@ -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()) {
auto property_key = MUST(get_constant(property).to_property_key(vm()));
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;
}
}
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)