LibJS: Stash thrown exception in a register before executing finalizer

This kills 2 birds with one stone:
1. It makes sure generated check_exception() calls in the finalizer
   don't mis-read the pending exception as caused by their matching
   operation.
2. It implicitly ensures that terminated finally blocks (by a return
   statement) overwrite any pending exceptions, since they will never
   execute the ContinuePendingUnwind operation that restores the
   stashed exception.
This additional logic is required in the JIT (as opposed to the
interpreter), since the JIT uses the exception register to store and
check the possibly-exceptional results from each individual operation,
while the interpreter only modifies it when an operation has thrown an
exception.
This commit is contained in:
Idan Horowitz 2023-11-03 20:47:33 +02:00 committed by Andreas Kling
parent aaa81cd3b9
commit 58e2fe895c
Notes: sideshowbarker 2024-07-17 07:38:17 +09:00
2 changed files with 13 additions and 2 deletions

View file

@ -45,7 +45,13 @@ public:
return Register(return_value_index);
}
static constexpr u32 reserved_register_count = 5;
static constexpr Register saved_exception()
{
constexpr u32 saved_exception_index = 5;
return Register(saved_exception_index);
}
static constexpr u32 reserved_register_count = 6;
constexpr explicit Register(u32 index)
: m_index(index)

View file

@ -426,6 +426,8 @@ void Compiler::check_exception()
m_assembler.jump(label_for(*handler));
no_exception.link(m_assembler);
} else if (auto const* finalizer = current_block().finalizer(); finalizer) {
store_vm_register(Bytecode::Register::saved_exception(), GPR0);
store_vm_register(Bytecode::Register::exception(), GPR1);
m_assembler.jump_if(Assembler::Operand::Register(GPR0),
Assembler::Condition::NotEqualTo,
Assembler::Operand::Register(GPR1),
@ -1247,11 +1249,14 @@ void Compiler::compile_set_variable(Bytecode::Op::SetVariable const& op)
void Compiler::compile_continue_pending_unwind(Bytecode::Op::ContinuePendingUnwind const& op)
{
// re-throw the exception if we reached the end of the finally block and there was no catch block to handle it
load_vm_register(GPR0, Bytecode::Register::saved_exception());
store_vm_register(Bytecode::Register::exception(), GPR0);
m_assembler.mov(Assembler::Operand::Register(GPR1), Assembler::Operand::Imm(Value().encoded()));
store_vm_register(Bytecode::Register::saved_exception(), GPR1);
check_exception();
// if (saved_return_value.is_empty()) goto resume_block;
load_vm_register(GPR0, Bytecode::Register::saved_return_value());
m_assembler.mov(Assembler::Operand::Register(GPR1), Assembler::Operand::Imm(Value().encoded()));
m_assembler.jump_if(
Assembler::Operand::Register(GPR0),
Assembler::Condition::EqualTo,