mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 20:07:35 +00:00
LibJS/Bytecode: Put arguments directly in the Call instruction
Instead of having Call refer to a range of VM registers, it now has a trailing list of argument operands as part of the instruction. This means we no longer have to shuffle every argument value into a register before making a call, making bytecode smaller & faster. :^)
This commit is contained in:
parent
da107ec9fb
commit
9a0a5a79f4
3 changed files with 70 additions and 53 deletions
|
@ -1627,19 +1627,19 @@ Bytecode::CodeGenerationErrorOr<Optional<Bytecode::Operand>> CallExpression::gen
|
|||
auto arguments = TRY(arguments_to_array_for_call(generator, this->arguments())).value();
|
||||
generator.emit<Bytecode::Op::CallWithArgumentArray>(call_type, dst, callee, this_value, arguments, expression_string_index);
|
||||
} else {
|
||||
Optional<Bytecode::Register> first_argument_reg {};
|
||||
for (size_t i = 0; i < arguments().size(); ++i) {
|
||||
auto reg = generator.allocate_register();
|
||||
if (!first_argument_reg.has_value())
|
||||
first_argument_reg = reg;
|
||||
}
|
||||
u32 register_offset = 0;
|
||||
Vector<Bytecode::Operand> argument_operands;
|
||||
for (auto const& argument : arguments()) {
|
||||
auto value = TRY(argument.value->generate_bytecode(generator)).value();
|
||||
generator.emit<Bytecode::Op::Mov>(Bytecode::Operand(Bytecode::Register(first_argument_reg.value().index() + register_offset)), value);
|
||||
register_offset += 1;
|
||||
argument_operands.append(TRY(argument.value->generate_bytecode(generator)).value());
|
||||
}
|
||||
generator.emit<Bytecode::Op::Call>(call_type, dst, callee, this_value, first_argument_reg.value_or(Bytecode::Register { 0 }), arguments().size(), expression_string_index, builtin);
|
||||
generator.emit_with_extra_operand_slots<Bytecode::Op::Call>(
|
||||
argument_operands.size(),
|
||||
call_type,
|
||||
dst,
|
||||
callee,
|
||||
this_value,
|
||||
argument_operands,
|
||||
expression_string_index,
|
||||
builtin);
|
||||
}
|
||||
|
||||
return dst;
|
||||
|
|
|
@ -42,6 +42,7 @@ bool g_dump_bytecode = false;
|
|||
static ByteString format_operand(StringView name, Operand operand, Bytecode::Executable const& executable)
|
||||
{
|
||||
StringBuilder builder;
|
||||
if (!name.is_empty())
|
||||
builder.appendff("\033[32m{}\033[0m:", name);
|
||||
switch (operand.type()) {
|
||||
case Operand::Type::Register:
|
||||
|
@ -77,6 +78,20 @@ static ByteString format_operand(StringView name, Operand operand, Bytecode::Exe
|
|||
return builder.to_byte_string();
|
||||
}
|
||||
|
||||
static ByteString format_operand_list(StringView name, ReadonlySpan<Operand> operands, Bytecode::Executable const& executable)
|
||||
{
|
||||
StringBuilder builder;
|
||||
if (!name.is_empty())
|
||||
builder.appendff(", \033[32{}\033[0m:[", name);
|
||||
for (size_t i = 0; i < operands.size(); ++i) {
|
||||
if (i != 0)
|
||||
builder.append(", "sv);
|
||||
builder.appendff("{}", format_operand(""sv, operands[i], executable));
|
||||
}
|
||||
builder.append("]"sv);
|
||||
return builder.to_byte_string();
|
||||
}
|
||||
|
||||
NonnullOwnPtr<CallFrame> CallFrame::create(size_t register_count)
|
||||
{
|
||||
size_t allocation_size = sizeof(CallFrame) + sizeof(Value) * register_count;
|
||||
|
@ -1202,27 +1217,25 @@ ThrowCompletionOr<void> Mov::execute_impl(Bytecode::Interpreter&) const
|
|||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
static ThrowCompletionOr<Value> dispatch_builtin_call(Bytecode::Interpreter& interpreter, Bytecode::Builtin builtin, Register first_argument)
|
||||
static ThrowCompletionOr<Value> dispatch_builtin_call(Bytecode::Interpreter& interpreter, Bytecode::Builtin builtin, ReadonlySpan<Operand> arguments)
|
||||
{
|
||||
switch (builtin) {
|
||||
case Builtin::MathAbs:
|
||||
return TRY(MathObject::abs_impl(interpreter.vm(), interpreter.reg(first_argument)));
|
||||
return TRY(MathObject::abs_impl(interpreter.vm(), interpreter.get(arguments[0])));
|
||||
case Builtin::MathLog:
|
||||
return TRY(MathObject::log_impl(interpreter.vm(), interpreter.reg(first_argument)));
|
||||
case Builtin::MathPow: {
|
||||
auto exponent = interpreter.reg(Register { first_argument.index() + 1 });
|
||||
return TRY(MathObject::pow_impl(interpreter.vm(), interpreter.reg(first_argument), exponent));
|
||||
}
|
||||
return TRY(MathObject::log_impl(interpreter.vm(), interpreter.get(arguments[0])));
|
||||
case Builtin::MathPow:
|
||||
return TRY(MathObject::pow_impl(interpreter.vm(), interpreter.get(arguments[0]), interpreter.get(arguments[1])));
|
||||
case Builtin::MathExp:
|
||||
return TRY(MathObject::exp_impl(interpreter.vm(), interpreter.reg(first_argument)));
|
||||
return TRY(MathObject::exp_impl(interpreter.vm(), interpreter.get(arguments[0])));
|
||||
case Builtin::MathCeil:
|
||||
return TRY(MathObject::ceil_impl(interpreter.vm(), interpreter.reg(first_argument)));
|
||||
return TRY(MathObject::ceil_impl(interpreter.vm(), interpreter.get(arguments[0])));
|
||||
case Builtin::MathFloor:
|
||||
return TRY(MathObject::floor_impl(interpreter.vm(), interpreter.reg(first_argument)));
|
||||
return TRY(MathObject::floor_impl(interpreter.vm(), interpreter.get(arguments[0])));
|
||||
case Builtin::MathRound:
|
||||
return TRY(MathObject::round_impl(interpreter.vm(), interpreter.reg(first_argument)));
|
||||
return TRY(MathObject::round_impl(interpreter.vm(), interpreter.get(arguments[0])));
|
||||
case Builtin::MathSqrt:
|
||||
return TRY(MathObject::sqrt_impl(interpreter.vm(), interpreter.reg(first_argument)));
|
||||
return TRY(MathObject::sqrt_impl(interpreter.vm(), interpreter.get(arguments[0])));
|
||||
case Bytecode::Builtin::__Count:
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
@ -1236,11 +1249,15 @@ ThrowCompletionOr<void> Call::execute_impl(Bytecode::Interpreter& interpreter) c
|
|||
TRY(throw_if_needed_for_call(interpreter, callee, call_type(), expression_string()));
|
||||
|
||||
if (m_builtin.has_value() && m_argument_count == Bytecode::builtin_argument_count(m_builtin.value()) && interpreter.realm().get_builtin_value(m_builtin.value()) == callee) {
|
||||
interpreter.set(dst(), TRY(dispatch_builtin_call(interpreter, m_builtin.value(), m_first_argument)));
|
||||
interpreter.set(dst(), TRY(dispatch_builtin_call(interpreter, m_builtin.value(), { m_arguments, m_argument_count })));
|
||||
return {};
|
||||
}
|
||||
|
||||
interpreter.set(dst(), TRY(perform_call(interpreter, interpreter.get(m_this_value), call_type(), callee, interpreter.registers().slice(m_first_argument.index(), m_argument_count))));
|
||||
Vector<Value> argument_values;
|
||||
argument_values.ensure_capacity(m_argument_count);
|
||||
for (size_t i = 0; i < m_argument_count; ++i)
|
||||
argument_values.unchecked_append(interpreter.get(m_arguments[i]));
|
||||
interpreter.set(dst(), TRY(perform_call(interpreter, interpreter.get(m_this_value), call_type(), callee, argument_values)));
|
||||
return {};
|
||||
}
|
||||
|
||||
|
@ -1893,30 +1910,25 @@ static StringView call_type_to_string(CallType type)
|
|||
ByteString Call::to_byte_string_impl(Bytecode::Executable const& executable) const
|
||||
{
|
||||
auto type = call_type_to_string(m_type);
|
||||
|
||||
StringBuilder builder;
|
||||
builder.appendff("Call{} {}, {}, {}"sv,
|
||||
type,
|
||||
format_operand("dst"sv, m_dst, executable),
|
||||
format_operand("callee"sv, m_callee, executable),
|
||||
format_operand("this"sv, m_this_value, executable));
|
||||
|
||||
builder.append(format_operand_list("args"sv, { m_arguments, m_argument_count }, executable));
|
||||
|
||||
if (m_builtin.has_value()) {
|
||||
return ByteString::formatted("Call{} {}, {}, {}, first_arg:{} (builtin {})",
|
||||
type,
|
||||
format_operand("dst"sv, m_dst, executable),
|
||||
format_operand("callee"sv, m_callee, executable),
|
||||
format_operand("this"sv, m_this_value, executable),
|
||||
m_first_argument,
|
||||
m_builtin.value());
|
||||
builder.appendff(", (builtin:{})", m_builtin.value());
|
||||
}
|
||||
|
||||
if (m_expression_string.has_value()) {
|
||||
return ByteString::formatted("Call{} {}, {}, {}, first_arg:{} ({})",
|
||||
type,
|
||||
format_operand("dst"sv, m_dst, executable),
|
||||
format_operand("callee"sv, m_callee, executable),
|
||||
format_operand("this"sv, m_this_value, executable),
|
||||
m_first_argument,
|
||||
executable.get_string(m_expression_string.value()));
|
||||
builder.appendff(", `{}`", executable.get_string(m_expression_string.value()));
|
||||
}
|
||||
return ByteString::formatted("Call{} {}, {}, {}, first_arg:{}",
|
||||
type,
|
||||
format_operand("dst"sv, m_dst, executable),
|
||||
format_operand("callee"sv, m_callee, executable),
|
||||
format_operand("this"sv, m_this_value, executable),
|
||||
m_first_argument);
|
||||
|
||||
return builder.to_byte_string();
|
||||
}
|
||||
|
||||
ByteString CallWithArgumentArray::to_byte_string_impl(Bytecode::Executable const& executable) const
|
||||
|
|
|
@ -1152,17 +1152,23 @@ enum class CallType {
|
|||
|
||||
class Call final : public Instruction {
|
||||
public:
|
||||
Call(CallType type, Operand dst, Operand callee, Operand this_value, Register first_argument, u32 argument_count, Optional<StringTableIndex> expression_string = {}, Optional<Builtin> builtin = {})
|
||||
: Instruction(Type::Call, sizeof(*this))
|
||||
Call(CallType type, Operand dst, Operand callee, Operand this_value, ReadonlySpan<Operand> arguments, Optional<StringTableIndex> expression_string = {}, Optional<Builtin> builtin = {})
|
||||
: Instruction(Type::Call, length_impl(arguments.size()))
|
||||
, m_dst(dst)
|
||||
, m_callee(callee)
|
||||
, m_this_value(this_value)
|
||||
, m_first_argument(first_argument)
|
||||
, m_argument_count(argument_count)
|
||||
, m_argument_count(arguments.size())
|
||||
, m_type(type)
|
||||
, m_expression_string(expression_string)
|
||||
, m_builtin(builtin)
|
||||
{
|
||||
for (size_t i = 0; i < arguments.size(); ++i)
|
||||
m_arguments[i] = arguments[i];
|
||||
}
|
||||
|
||||
size_t length_impl(size_t argument_count) const
|
||||
{
|
||||
return round_up_to_power_of_two(alignof(void*), sizeof(*this) + sizeof(Operand) * argument_count);
|
||||
}
|
||||
|
||||
CallType call_type() const { return m_type; }
|
||||
|
@ -1171,7 +1177,6 @@ public:
|
|||
Operand this_value() const { return m_this_value; }
|
||||
Optional<StringTableIndex> const& expression_string() const { return m_expression_string; }
|
||||
|
||||
Register first_argument() const { return m_first_argument; }
|
||||
u32 argument_count() const { return m_argument_count; }
|
||||
|
||||
Optional<Builtin> const& builtin() const { return m_builtin; }
|
||||
|
@ -1183,11 +1188,11 @@ private:
|
|||
Operand m_dst;
|
||||
Operand m_callee;
|
||||
Operand m_this_value;
|
||||
Register m_first_argument;
|
||||
u32 m_argument_count { 0 };
|
||||
CallType m_type;
|
||||
Optional<StringTableIndex> m_expression_string;
|
||||
Optional<Builtin> m_builtin;
|
||||
Operand m_arguments[];
|
||||
};
|
||||
|
||||
class CallWithArgumentArray final : public Instruction {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue