mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 19:37: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();
|
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);
|
generator.emit<Bytecode::Op::CallWithArgumentArray>(call_type, dst, callee, this_value, arguments, expression_string_index);
|
||||||
} else {
|
} else {
|
||||||
Optional<Bytecode::Register> first_argument_reg {};
|
Vector<Bytecode::Operand> argument_operands;
|
||||||
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;
|
|
||||||
for (auto const& argument : arguments()) {
|
for (auto const& argument : arguments()) {
|
||||||
auto value = TRY(argument.value->generate_bytecode(generator)).value();
|
argument_operands.append(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;
|
|
||||||
}
|
}
|
||||||
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;
|
return dst;
|
||||||
|
|
|
@ -42,7 +42,8 @@ bool g_dump_bytecode = false;
|
||||||
static ByteString format_operand(StringView name, Operand operand, Bytecode::Executable const& executable)
|
static ByteString format_operand(StringView name, Operand operand, Bytecode::Executable const& executable)
|
||||||
{
|
{
|
||||||
StringBuilder builder;
|
StringBuilder builder;
|
||||||
builder.appendff("\033[32m{}\033[0m:", name);
|
if (!name.is_empty())
|
||||||
|
builder.appendff("\033[32m{}\033[0m:", name);
|
||||||
switch (operand.type()) {
|
switch (operand.type()) {
|
||||||
case Operand::Type::Register:
|
case Operand::Type::Register:
|
||||||
builder.appendff("\033[33mreg{}\033[0m", operand.index());
|
builder.appendff("\033[33mreg{}\033[0m", operand.index());
|
||||||
|
@ -77,6 +78,20 @@ static ByteString format_operand(StringView name, Operand operand, Bytecode::Exe
|
||||||
return builder.to_byte_string();
|
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)
|
NonnullOwnPtr<CallFrame> CallFrame::create(size_t register_count)
|
||||||
{
|
{
|
||||||
size_t allocation_size = sizeof(CallFrame) + sizeof(Value) * 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();
|
__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) {
|
switch (builtin) {
|
||||||
case Builtin::MathAbs:
|
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:
|
case Builtin::MathLog:
|
||||||
return TRY(MathObject::log_impl(interpreter.vm(), interpreter.reg(first_argument)));
|
return TRY(MathObject::log_impl(interpreter.vm(), interpreter.get(arguments[0])));
|
||||||
case Builtin::MathPow: {
|
case Builtin::MathPow:
|
||||||
auto exponent = interpreter.reg(Register { first_argument.index() + 1 });
|
return TRY(MathObject::pow_impl(interpreter.vm(), interpreter.get(arguments[0]), interpreter.get(arguments[1])));
|
||||||
return TRY(MathObject::pow_impl(interpreter.vm(), interpreter.reg(first_argument), exponent));
|
|
||||||
}
|
|
||||||
case Builtin::MathExp:
|
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:
|
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:
|
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:
|
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:
|
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:
|
case Bytecode::Builtin::__Count:
|
||||||
VERIFY_NOT_REACHED();
|
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()));
|
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) {
|
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 {};
|
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 {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1893,30 +1910,25 @@ static StringView call_type_to_string(CallType type)
|
||||||
ByteString Call::to_byte_string_impl(Bytecode::Executable const& executable) const
|
ByteString Call::to_byte_string_impl(Bytecode::Executable const& executable) const
|
||||||
{
|
{
|
||||||
auto type = call_type_to_string(m_type);
|
auto type = call_type_to_string(m_type);
|
||||||
if (m_builtin.has_value()) {
|
|
||||||
return ByteString::formatted("Call{} {}, {}, {}, first_arg:{} (builtin {})",
|
StringBuilder builder;
|
||||||
type,
|
builder.appendff("Call{} {}, {}, {}"sv,
|
||||||
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());
|
|
||||||
}
|
|
||||||
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()));
|
|
||||||
}
|
|
||||||
return ByteString::formatted("Call{} {}, {}, {}, first_arg:{}",
|
|
||||||
type,
|
type,
|
||||||
format_operand("dst"sv, m_dst, executable),
|
format_operand("dst"sv, m_dst, executable),
|
||||||
format_operand("callee"sv, m_callee, executable),
|
format_operand("callee"sv, m_callee, executable),
|
||||||
format_operand("this"sv, m_this_value, executable),
|
format_operand("this"sv, m_this_value, executable));
|
||||||
m_first_argument);
|
|
||||||
|
builder.append(format_operand_list("args"sv, { m_arguments, m_argument_count }, executable));
|
||||||
|
|
||||||
|
if (m_builtin.has_value()) {
|
||||||
|
builder.appendff(", (builtin:{})", m_builtin.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_expression_string.has_value()) {
|
||||||
|
builder.appendff(", `{}`", executable.get_string(m_expression_string.value()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.to_byte_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
ByteString CallWithArgumentArray::to_byte_string_impl(Bytecode::Executable const& executable) const
|
ByteString CallWithArgumentArray::to_byte_string_impl(Bytecode::Executable const& executable) const
|
||||||
|
|
|
@ -1152,17 +1152,23 @@ enum class CallType {
|
||||||
|
|
||||||
class Call final : public Instruction {
|
class Call final : public Instruction {
|
||||||
public:
|
public:
|
||||||
Call(CallType type, Operand dst, Operand callee, Operand this_value, Register first_argument, u32 argument_count, Optional<StringTableIndex> expression_string = {}, Optional<Builtin> builtin = {})
|
Call(CallType type, Operand dst, Operand callee, Operand this_value, ReadonlySpan<Operand> arguments, Optional<StringTableIndex> expression_string = {}, Optional<Builtin> builtin = {})
|
||||||
: Instruction(Type::Call, sizeof(*this))
|
: Instruction(Type::Call, length_impl(arguments.size()))
|
||||||
, m_dst(dst)
|
, m_dst(dst)
|
||||||
, m_callee(callee)
|
, m_callee(callee)
|
||||||
, m_this_value(this_value)
|
, m_this_value(this_value)
|
||||||
, m_first_argument(first_argument)
|
, m_argument_count(arguments.size())
|
||||||
, m_argument_count(argument_count)
|
|
||||||
, m_type(type)
|
, m_type(type)
|
||||||
, m_expression_string(expression_string)
|
, m_expression_string(expression_string)
|
||||||
, m_builtin(builtin)
|
, 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; }
|
CallType call_type() const { return m_type; }
|
||||||
|
@ -1171,7 +1177,6 @@ public:
|
||||||
Operand this_value() const { return m_this_value; }
|
Operand this_value() const { return m_this_value; }
|
||||||
Optional<StringTableIndex> const& expression_string() const { return m_expression_string; }
|
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; }
|
u32 argument_count() const { return m_argument_count; }
|
||||||
|
|
||||||
Optional<Builtin> const& builtin() const { return m_builtin; }
|
Optional<Builtin> const& builtin() const { return m_builtin; }
|
||||||
|
@ -1183,11 +1188,11 @@ private:
|
||||||
Operand m_dst;
|
Operand m_dst;
|
||||||
Operand m_callee;
|
Operand m_callee;
|
||||||
Operand m_this_value;
|
Operand m_this_value;
|
||||||
Register m_first_argument;
|
|
||||||
u32 m_argument_count { 0 };
|
u32 m_argument_count { 0 };
|
||||||
CallType m_type;
|
CallType m_type;
|
||||||
Optional<StringTableIndex> m_expression_string;
|
Optional<StringTableIndex> m_expression_string;
|
||||||
Optional<Builtin> m_builtin;
|
Optional<Builtin> m_builtin;
|
||||||
|
Operand m_arguments[];
|
||||||
};
|
};
|
||||||
|
|
||||||
class CallWithArgumentArray final : public Instruction {
|
class CallWithArgumentArray final : public Instruction {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue