mirror of
https://github.com/SerenityOS/serenity
synced 2024-09-18 15:32:56 +00:00
LibJS: Instantiate primitive array expressions using a single operation
This will not meaningfully affect short array literals, but it does give us a bit of extra perf when evaluating huge array expressions like in Kraken/imaging-darkroom.js
This commit is contained in:
parent
5e3a799e97
commit
f19349e1b6
|
@ -1133,10 +1133,21 @@ private:
|
|||
Vector<NonnullRefPtr<Expression const>> m_expressions;
|
||||
};
|
||||
|
||||
class BooleanLiteral final : public Expression {
|
||||
class PrimitiveLiteral : public Expression {
|
||||
public:
|
||||
virtual Value value() const = 0;
|
||||
|
||||
protected:
|
||||
explicit PrimitiveLiteral(SourceRange source_range)
|
||||
: Expression(move(source_range))
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class BooleanLiteral final : public PrimitiveLiteral {
|
||||
public:
|
||||
explicit BooleanLiteral(SourceRange source_range, bool value)
|
||||
: Expression(move(source_range))
|
||||
: PrimitiveLiteral(move(source_range))
|
||||
, m_value(value)
|
||||
{
|
||||
}
|
||||
|
@ -1144,14 +1155,16 @@ public:
|
|||
virtual void dump(int indent) const override;
|
||||
virtual Bytecode::CodeGenerationErrorOr<void> generate_bytecode(Bytecode::Generator&) const override;
|
||||
|
||||
virtual Value value() const override { return Value(m_value); }
|
||||
|
||||
private:
|
||||
bool m_value { false };
|
||||
};
|
||||
|
||||
class NumericLiteral final : public Expression {
|
||||
class NumericLiteral final : public PrimitiveLiteral {
|
||||
public:
|
||||
explicit NumericLiteral(SourceRange source_range, double value)
|
||||
: Expression(move(source_range))
|
||||
: PrimitiveLiteral(move(source_range))
|
||||
, m_value(value)
|
||||
{
|
||||
}
|
||||
|
@ -1159,6 +1172,8 @@ public:
|
|||
virtual void dump(int indent) const override;
|
||||
virtual Bytecode::CodeGenerationErrorOr<void> generate_bytecode(Bytecode::Generator&) const override;
|
||||
|
||||
virtual Value value() const override { return m_value; }
|
||||
|
||||
private:
|
||||
Value m_value;
|
||||
};
|
||||
|
@ -1197,15 +1212,17 @@ private:
|
|||
DeprecatedString m_value;
|
||||
};
|
||||
|
||||
class NullLiteral final : public Expression {
|
||||
class NullLiteral final : public PrimitiveLiteral {
|
||||
public:
|
||||
explicit NullLiteral(SourceRange source_range)
|
||||
: Expression(move(source_range))
|
||||
: PrimitiveLiteral(move(source_range))
|
||||
{
|
||||
}
|
||||
|
||||
virtual void dump(int indent) const override;
|
||||
virtual Bytecode::CodeGenerationErrorOr<void> generate_bytecode(Bytecode::Generator&) const override;
|
||||
|
||||
virtual Value value() const override { return js_null(); }
|
||||
};
|
||||
|
||||
class RegExpLiteral final : public Expression {
|
||||
|
|
|
@ -1012,6 +1012,19 @@ Bytecode::CodeGenerationErrorOr<void> ArrayExpression::generate_bytecode(Bytecod
|
|||
return {};
|
||||
}
|
||||
|
||||
if (all_of(m_elements, [](auto element) { return !element || is<PrimitiveLiteral>(*element); })) {
|
||||
// If all elements are constant primitives, we can just emit a single instruction to initialize the array,
|
||||
// instead of emitting instructions to manually evaluate them one-by-one
|
||||
auto values = MUST(FixedArray<Value>::create(m_elements.size()));
|
||||
for (auto i = 0u; i < m_elements.size(); ++i) {
|
||||
if (!m_elements[i])
|
||||
continue;
|
||||
values[i] = static_cast<PrimitiveLiteral const&>(*m_elements[i]).value();
|
||||
}
|
||||
generator.emit<Bytecode::Op::NewPrimitiveArray>(move(values));
|
||||
return {};
|
||||
}
|
||||
|
||||
auto first_spread = find_if(m_elements.begin(), m_elements.end(), [](auto el) { return el && is<SpreadExpression>(*el); });
|
||||
|
||||
Bytecode::Register args_start_reg { 0 };
|
||||
|
|
|
@ -86,6 +86,7 @@
|
|||
O(NewClass) \
|
||||
O(NewFunction) \
|
||||
O(NewObject) \
|
||||
O(NewPrimitiveArray) \
|
||||
O(NewRegExp) \
|
||||
O(NewString) \
|
||||
O(NewTypeError) \
|
||||
|
|
|
@ -605,6 +605,15 @@ ThrowCompletionOr<void> NewArray::execute_impl(Bytecode::Interpreter& interprete
|
|||
return {};
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> NewPrimitiveArray::execute_impl(Bytecode::Interpreter& interpreter) const
|
||||
{
|
||||
auto array = MUST(Array::create(interpreter.realm(), 0));
|
||||
for (size_t i = 0; i < m_values.size(); i++)
|
||||
array->indexed_properties().put(i, m_values[i], default_attributes);
|
||||
interpreter.accumulator() = array;
|
||||
return {};
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> Append::execute_impl(Bytecode::Interpreter& interpreter) const
|
||||
{
|
||||
return append(interpreter.vm(), interpreter.reg(m_lhs), interpreter.accumulator(), m_is_spread);
|
||||
|
@ -1306,6 +1315,11 @@ DeprecatedString NewArray::to_deprecated_string_impl(Bytecode::Executable const&
|
|||
return builder.to_deprecated_string();
|
||||
}
|
||||
|
||||
DeprecatedString NewPrimitiveArray::to_deprecated_string_impl(Bytecode::Executable const&) const
|
||||
{
|
||||
return DeprecatedString::formatted("NewPrimitiveArray {}"sv, m_values.span());
|
||||
}
|
||||
|
||||
DeprecatedString Append::to_deprecated_string_impl(Bytecode::Executable const&) const
|
||||
{
|
||||
if (m_is_spread)
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <AK/FixedArray.h>
|
||||
#include <AK/StdLibExtras.h>
|
||||
#include <LibCrypto/BigInt/SignedBigInteger.h>
|
||||
#include <LibJS/Bytecode/Builtins.h>
|
||||
|
@ -313,6 +314,23 @@ private:
|
|||
Register m_elements[];
|
||||
};
|
||||
|
||||
class NewPrimitiveArray final : public Instruction {
|
||||
public:
|
||||
explicit NewPrimitiveArray(FixedArray<Value> values)
|
||||
: Instruction(Type::NewPrimitiveArray, sizeof(*this))
|
||||
, m_values(move(values))
|
||||
{
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
|
||||
DeprecatedString to_deprecated_string_impl(Bytecode::Executable const&) const;
|
||||
|
||||
ReadonlySpan<Value> values() const { return m_values.span(); }
|
||||
|
||||
private:
|
||||
FixedArray<Value> m_values;
|
||||
};
|
||||
|
||||
class Append final : public Instruction {
|
||||
public:
|
||||
Append(Register lhs, bool is_spread)
|
||||
|
|
|
@ -1528,6 +1528,28 @@ void Compiler::compile_new_array(Bytecode::Op::NewArray const& op)
|
|||
store_accumulator(RET);
|
||||
}
|
||||
|
||||
static Value cxx_new_primitive_array(VM& vm, Value* values, size_t element_count)
|
||||
{
|
||||
auto& realm = *vm.current_realm();
|
||||
auto array = MUST(Array::create(realm, 0));
|
||||
for (size_t i = 0; i < element_count; ++i) {
|
||||
array->indexed_properties().put(i, values[i], default_attributes);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
void Compiler::compile_new_primitive_array(Bytecode::Op::NewPrimitiveArray const& op)
|
||||
{
|
||||
m_assembler.mov(
|
||||
Assembler::Operand::Register(ARG1),
|
||||
Assembler::Operand::Imm(bit_cast<u64>(op.values().data())));
|
||||
m_assembler.mov(
|
||||
Assembler::Operand::Register(ARG2),
|
||||
Assembler::Operand::Imm(op.values().size()));
|
||||
native_call((void*)cxx_new_primitive_array);
|
||||
store_accumulator(RET);
|
||||
}
|
||||
|
||||
void Compiler::compile_new_function(Bytecode::Op::NewFunction const& op)
|
||||
{
|
||||
m_assembler.mov(
|
||||
|
|
|
@ -92,6 +92,7 @@ private:
|
|||
O(NewString, new_string) \
|
||||
O(NewObject, new_object) \
|
||||
O(NewArray, new_array) \
|
||||
O(NewPrimitiveArray, new_primitive_array) \
|
||||
O(NewFunction, new_function) \
|
||||
O(NewRegExp, new_regexp) \
|
||||
O(NewBigInt, new_bigint) \
|
||||
|
|
Loading…
Reference in a new issue