mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-10-02 06:09:51 +00:00
LibJS/Bytecode: Unroll the bytecode interpreter
This commit adds a HANDLE_INSTRUCTION macro that expands to everything needed to handle a single instruction (invoking the handler function, checking for exceptions, and advancing the program counter). This gives a ~15% speed-up on a for loop microbenchmark, and makes basically everything faster.
This commit is contained in:
parent
fae1527a18
commit
ce93000757
Notes:
sideshowbarker
2024-07-17 01:55:29 +09:00
Author: https://github.com/awesomekling
Commit: ce93000757
Pull-request: https://github.com/SerenityOS/serenity/pull/24240
Reviewed-by: https://github.com/Hendiadyoin1
Reviewed-by: https://github.com/trflynn89 ✅
3 changed files with 134 additions and 10 deletions
|
@ -128,6 +128,7 @@ namespace JS::Bytecode {
|
|||
class alignas(void*) Instruction {
|
||||
public:
|
||||
constexpr static bool IsTerminator = false;
|
||||
static constexpr bool IsVariableLength = false;
|
||||
|
||||
enum class Type {
|
||||
#define __BYTECODE_OP(op) \
|
||||
|
|
|
@ -330,10 +330,6 @@ Interpreter::HandleExceptionResponse Interpreter::handle_exception(size_t& progr
|
|||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
#define NEXT_INSTRUCTION() \
|
||||
program_counter += instruction.length(); \
|
||||
goto start;
|
||||
|
||||
void Interpreter::run_bytecode(size_t entry_point)
|
||||
{
|
||||
auto& running_execution_context = vm().running_execution_context();
|
||||
|
@ -357,10 +353,12 @@ void Interpreter::run_bytecode(size_t entry_point)
|
|||
switch (instruction.type()) {
|
||||
case Instruction::Type::SetLocal:
|
||||
locals[static_cast<Op::SetLocal const&>(instruction).index()] = get(static_cast<Op::SetLocal const&>(instruction).src());
|
||||
NEXT_INSTRUCTION();
|
||||
program_counter += sizeof(Op::SetLocal);
|
||||
goto start;
|
||||
case Instruction::Type::Mov:
|
||||
set(static_cast<Op::Mov const&>(instruction).dst(), get(static_cast<Op::Mov const&>(instruction).src()));
|
||||
NEXT_INSTRUCTION();
|
||||
program_counter += sizeof(Op::Mov);
|
||||
goto start;
|
||||
case Instruction::Type::End:
|
||||
accumulator = get(static_cast<Op::End const&>(instruction).value());
|
||||
return;
|
||||
|
@ -381,7 +379,8 @@ void Interpreter::run_bytecode(size_t entry_point)
|
|||
program_counter = jump.target().address();
|
||||
goto start;
|
||||
}
|
||||
NEXT_INSTRUCTION();
|
||||
program_counter += sizeof(Op::JumpTrue);
|
||||
goto start;
|
||||
}
|
||||
case Instruction::Type::JumpFalse: {
|
||||
auto& jump = static_cast<Op::JumpFalse const&>(instruction);
|
||||
|
@ -389,7 +388,8 @@ void Interpreter::run_bytecode(size_t entry_point)
|
|||
program_counter = jump.target().address();
|
||||
goto start;
|
||||
}
|
||||
NEXT_INSTRUCTION();
|
||||
program_counter += sizeof(Op::JumpFalse);
|
||||
goto start;
|
||||
}
|
||||
case Instruction::Type::JumpNullish: {
|
||||
auto& jump = static_cast<Op::JumpNullish const&>(instruction);
|
||||
|
@ -440,7 +440,121 @@ void Interpreter::run_bytecode(size_t entry_point)
|
|||
program_counter = finalizer.value();
|
||||
goto start;
|
||||
}
|
||||
default:
|
||||
|
||||
#define HANDLE_INSTRUCTION(name) \
|
||||
case Instruction::Type::name: { \
|
||||
auto& typed_instruction = static_cast<Op::name const&>(instruction); \
|
||||
auto result = typed_instruction.execute_impl(*this); \
|
||||
if (result.is_error()) { \
|
||||
if (handle_exception(program_counter, *result.throw_completion().value()) == HandleExceptionResponse::ExitFromExecutable) \
|
||||
return; \
|
||||
goto start; \
|
||||
} \
|
||||
if constexpr (Op::name::IsVariableLength) \
|
||||
program_counter += instruction.length(); \
|
||||
else \
|
||||
program_counter += sizeof(Op::name); \
|
||||
goto start; \
|
||||
}
|
||||
|
||||
HANDLE_INSTRUCTION(Add);
|
||||
HANDLE_INSTRUCTION(ArrayAppend);
|
||||
HANDLE_INSTRUCTION(AsyncIteratorClose);
|
||||
HANDLE_INSTRUCTION(BitwiseAnd);
|
||||
HANDLE_INSTRUCTION(BitwiseNot);
|
||||
HANDLE_INSTRUCTION(BitwiseOr);
|
||||
HANDLE_INSTRUCTION(BitwiseXor);
|
||||
HANDLE_INSTRUCTION(BlockDeclarationInstantiation);
|
||||
HANDLE_INSTRUCTION(Call);
|
||||
HANDLE_INSTRUCTION(CallWithArgumentArray);
|
||||
HANDLE_INSTRUCTION(Catch);
|
||||
HANDLE_INSTRUCTION(ConcatString);
|
||||
HANDLE_INSTRUCTION(CopyObjectExcludingProperties);
|
||||
HANDLE_INSTRUCTION(CreateLexicalEnvironment);
|
||||
HANDLE_INSTRUCTION(CreateVariable);
|
||||
HANDLE_INSTRUCTION(Decrement);
|
||||
HANDLE_INSTRUCTION(DeleteById);
|
||||
HANDLE_INSTRUCTION(DeleteByIdWithThis);
|
||||
HANDLE_INSTRUCTION(DeleteByValue);
|
||||
HANDLE_INSTRUCTION(DeleteByValueWithThis);
|
||||
HANDLE_INSTRUCTION(DeleteVariable);
|
||||
HANDLE_INSTRUCTION(Div);
|
||||
HANDLE_INSTRUCTION(Dump);
|
||||
HANDLE_INSTRUCTION(EnterObjectEnvironment);
|
||||
HANDLE_INSTRUCTION(Exp);
|
||||
HANDLE_INSTRUCTION(GetById);
|
||||
HANDLE_INSTRUCTION(GetByIdWithThis);
|
||||
HANDLE_INSTRUCTION(GetByValue);
|
||||
HANDLE_INSTRUCTION(GetByValueWithThis);
|
||||
HANDLE_INSTRUCTION(GetCalleeAndThisFromEnvironment);
|
||||
HANDLE_INSTRUCTION(GetGlobal);
|
||||
HANDLE_INSTRUCTION(GetImportMeta);
|
||||
HANDLE_INSTRUCTION(GetIterator);
|
||||
HANDLE_INSTRUCTION(GetMethod);
|
||||
HANDLE_INSTRUCTION(GetNewTarget);
|
||||
HANDLE_INSTRUCTION(GetNextMethodFromIteratorRecord);
|
||||
HANDLE_INSTRUCTION(GetObjectFromIteratorRecord);
|
||||
HANDLE_INSTRUCTION(GetObjectPropertyIterator);
|
||||
HANDLE_INSTRUCTION(GetPrivateById);
|
||||
HANDLE_INSTRUCTION(GetVariable);
|
||||
HANDLE_INSTRUCTION(GreaterThan);
|
||||
HANDLE_INSTRUCTION(GreaterThanEquals);
|
||||
HANDLE_INSTRUCTION(HasPrivateId);
|
||||
HANDLE_INSTRUCTION(ImportCall);
|
||||
HANDLE_INSTRUCTION(In);
|
||||
HANDLE_INSTRUCTION(Increment);
|
||||
HANDLE_INSTRUCTION(InstanceOf);
|
||||
HANDLE_INSTRUCTION(IteratorClose);
|
||||
HANDLE_INSTRUCTION(IteratorNext);
|
||||
HANDLE_INSTRUCTION(IteratorToArray);
|
||||
HANDLE_INSTRUCTION(LeaveFinally);
|
||||
HANDLE_INSTRUCTION(LeaveLexicalEnvironment);
|
||||
HANDLE_INSTRUCTION(LeaveUnwindContext);
|
||||
HANDLE_INSTRUCTION(LeftShift);
|
||||
HANDLE_INSTRUCTION(LessThan);
|
||||
HANDLE_INSTRUCTION(LessThanEquals);
|
||||
HANDLE_INSTRUCTION(LooselyEquals);
|
||||
HANDLE_INSTRUCTION(LooselyInequals);
|
||||
HANDLE_INSTRUCTION(Mod);
|
||||
HANDLE_INSTRUCTION(Mul);
|
||||
HANDLE_INSTRUCTION(NewArray);
|
||||
HANDLE_INSTRUCTION(NewClass);
|
||||
HANDLE_INSTRUCTION(NewFunction);
|
||||
HANDLE_INSTRUCTION(NewObject);
|
||||
HANDLE_INSTRUCTION(NewPrimitiveArray);
|
||||
HANDLE_INSTRUCTION(NewRegExp);
|
||||
HANDLE_INSTRUCTION(NewTypeError);
|
||||
HANDLE_INSTRUCTION(Not);
|
||||
HANDLE_INSTRUCTION(PostfixDecrement);
|
||||
HANDLE_INSTRUCTION(PostfixIncrement);
|
||||
HANDLE_INSTRUCTION(PutById);
|
||||
HANDLE_INSTRUCTION(PutByIdWithThis);
|
||||
HANDLE_INSTRUCTION(PutByValue);
|
||||
HANDLE_INSTRUCTION(PutByValueWithThis);
|
||||
HANDLE_INSTRUCTION(PutPrivateById);
|
||||
HANDLE_INSTRUCTION(ResolveSuperBase);
|
||||
HANDLE_INSTRUCTION(ResolveThisBinding);
|
||||
HANDLE_INSTRUCTION(RestoreScheduledJump);
|
||||
HANDLE_INSTRUCTION(RightShift);
|
||||
HANDLE_INSTRUCTION(SetVariable);
|
||||
HANDLE_INSTRUCTION(StrictlyEquals);
|
||||
HANDLE_INSTRUCTION(StrictlyInequals);
|
||||
HANDLE_INSTRUCTION(Sub);
|
||||
HANDLE_INSTRUCTION(SuperCallWithArgumentArray);
|
||||
HANDLE_INSTRUCTION(Throw);
|
||||
HANDLE_INSTRUCTION(ThrowIfNotObject);
|
||||
HANDLE_INSTRUCTION(ThrowIfNullish);
|
||||
HANDLE_INSTRUCTION(ThrowIfTDZ);
|
||||
HANDLE_INSTRUCTION(Typeof);
|
||||
HANDLE_INSTRUCTION(TypeofVariable);
|
||||
HANDLE_INSTRUCTION(UnaryMinus);
|
||||
HANDLE_INSTRUCTION(UnaryPlus);
|
||||
HANDLE_INSTRUCTION(UnsignedRightShift);
|
||||
|
||||
case Instruction::Type::Await:
|
||||
case Instruction::Type::Return:
|
||||
case Instruction::Type::Yield:
|
||||
// Handled delicately below.
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -466,7 +580,8 @@ void Interpreter::run_bytecode(size_t entry_point)
|
|||
break;
|
||||
}
|
||||
|
||||
NEXT_INSTRUCTION();
|
||||
program_counter += instruction.length();
|
||||
goto start;
|
||||
}
|
||||
|
||||
if (!will_yield) {
|
||||
|
|
|
@ -209,6 +209,8 @@ JS_ENUMERATE_NEW_BUILTIN_ERROR_OPS(JS_DECLARE_NEW_BUILTIN_ERROR_OP)
|
|||
// NOTE: This instruction is variable-width depending on the number of excluded names
|
||||
class CopyObjectExcludingProperties final : public Instruction {
|
||||
public:
|
||||
static constexpr bool IsVariableLength = true;
|
||||
|
||||
CopyObjectExcludingProperties(Operand dst, Operand from_object, Vector<Operand> const& excluded_names)
|
||||
: Instruction(Type::CopyObjectExcludingProperties, length_impl(excluded_names.size()))
|
||||
, m_dst(dst)
|
||||
|
@ -242,6 +244,8 @@ private:
|
|||
// NOTE: This instruction is variable-width depending on the number of elements!
|
||||
class NewArray final : public Instruction {
|
||||
public:
|
||||
static constexpr bool IsVariableLength = true;
|
||||
|
||||
explicit NewArray(Operand dst)
|
||||
: Instruction(Type::NewArray, length_impl(0))
|
||||
, m_dst(dst)
|
||||
|
@ -290,6 +294,8 @@ private:
|
|||
|
||||
class NewPrimitiveArray final : public Instruction {
|
||||
public:
|
||||
static constexpr bool IsVariableLength = true;
|
||||
|
||||
NewPrimitiveArray(Operand dst, ReadonlySpan<Value> elements)
|
||||
: Instruction(Type::NewPrimitiveArray, length_impl(elements.size()))
|
||||
, m_dst(dst)
|
||||
|
@ -1230,6 +1236,8 @@ enum class CallType {
|
|||
|
||||
class Call final : public Instruction {
|
||||
public:
|
||||
static constexpr bool IsVariableLength = true;
|
||||
|
||||
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)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue