mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 06:47:35 +00:00
LibJS/JIT: Implement static exception handling
This commit is contained in:
parent
1341f4438d
commit
da45bd3fde
2 changed files with 39 additions and 181 deletions
|
@ -360,146 +360,41 @@ void Compiler::compile_decrement(Bytecode::Op::Decrement const&)
|
||||||
|
|
||||||
void Compiler::check_exception()
|
void Compiler::check_exception()
|
||||||
{
|
{
|
||||||
// if (!exception.is_empty()) goto m_exception_handler;
|
|
||||||
load_vm_register(GPR0, Bytecode::Register::exception());
|
load_vm_register(GPR0, Bytecode::Register::exception());
|
||||||
m_assembler.mov(Assembler::Operand::Register(GPR1), Assembler::Operand::Imm(Value().encoded()));
|
m_assembler.mov(Assembler::Operand::Register(GPR1), Assembler::Operand::Imm(Value().encoded()));
|
||||||
m_assembler.jump_if(
|
|
||||||
Assembler::Operand::Register(GPR0),
|
|
||||||
Assembler::Condition::NotEqualTo,
|
|
||||||
Assembler::Operand::Register(GPR1),
|
|
||||||
m_exception_handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Compiler::handle_exception()
|
if (auto const* handler = current_block().handler(); handler) {
|
||||||
{
|
Assembler::Label no_exception;
|
||||||
// if (!unwind_context.valid) return;
|
m_assembler.jump_if(
|
||||||
Assembler::Label handle_exception {};
|
|
||||||
m_assembler.mov(
|
|
||||||
Assembler::Operand::Register(GPR0),
|
|
||||||
Assembler::Operand::Mem64BaseAndOffset(UNWIND_CONTEXT_BASE, 0));
|
|
||||||
m_assembler.jump_if(
|
|
||||||
Assembler::Operand::Register(GPR0),
|
|
||||||
Assembler::Condition::NotEqualTo,
|
|
||||||
Assembler::Operand::Imm(0),
|
|
||||||
handle_exception);
|
|
||||||
|
|
||||||
jump_to_exit();
|
|
||||||
|
|
||||||
// handle_exception:
|
|
||||||
handle_exception.link(m_assembler);
|
|
||||||
|
|
||||||
// if (unwind_context.handler) {
|
|
||||||
Assembler::Label no_handler {};
|
|
||||||
m_assembler.mov(
|
|
||||||
Assembler::Operand::Register(GPR0),
|
|
||||||
Assembler::Operand::Mem64BaseAndOffset(UNWIND_CONTEXT_BASE, 8));
|
|
||||||
m_assembler.jump_if(
|
|
||||||
Assembler::Operand::Register(GPR0),
|
|
||||||
Assembler::Condition::EqualTo,
|
|
||||||
Assembler::Operand::Imm(0),
|
|
||||||
no_handler);
|
|
||||||
// accumulator = exception;
|
|
||||||
load_vm_register(GPR1, Bytecode::Register::exception());
|
|
||||||
store_vm_register(Bytecode::Register::accumulator(), GPR1);
|
|
||||||
// exception = Value();
|
|
||||||
m_assembler.mov(
|
|
||||||
Assembler::Operand::Register(GPR1),
|
|
||||||
Assembler::Operand::Imm(Value().encoded()));
|
|
||||||
store_vm_register(Bytecode::Register::exception(), GPR1);
|
|
||||||
// unwind_context.handler = nullptr;
|
|
||||||
m_assembler.mov(
|
|
||||||
Assembler::Operand::Register(GPR1),
|
|
||||||
Assembler::Operand::Imm(0));
|
|
||||||
m_assembler.mov(
|
|
||||||
Assembler::Operand::Mem64BaseAndOffset(UNWIND_CONTEXT_BASE, 8),
|
|
||||||
Assembler::Operand::Register(GPR1));
|
|
||||||
// goto handler;
|
|
||||||
m_assembler.jump(Assembler::Operand::Register(GPR0));
|
|
||||||
// }
|
|
||||||
|
|
||||||
// no_handler:
|
|
||||||
no_handler.link(m_assembler);
|
|
||||||
|
|
||||||
// if (unwind_context.finalizer) goto finalizer;
|
|
||||||
Assembler::Label no_finalizer {};
|
|
||||||
m_assembler.mov(
|
|
||||||
Assembler::Operand::Register(GPR0),
|
|
||||||
Assembler::Operand::Mem64BaseAndOffset(UNWIND_CONTEXT_BASE, 16));
|
|
||||||
m_assembler.jump_if(
|
|
||||||
Assembler::Operand::Register(GPR0),
|
|
||||||
Assembler::Condition::EqualTo,
|
|
||||||
Assembler::Operand::Imm(0),
|
|
||||||
no_finalizer);
|
|
||||||
|
|
||||||
m_assembler.jump(Assembler::Operand::Register(GPR0));
|
|
||||||
|
|
||||||
// no_finalizer:
|
|
||||||
// NOTE: No catch and no finally!? Crash.
|
|
||||||
no_finalizer.link(m_assembler);
|
|
||||||
m_assembler.verify_not_reached();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Compiler::push_unwind_context(bool valid, Optional<Bytecode::Label> const& handler, Optional<Bytecode::Label> const& finalizer)
|
|
||||||
{
|
|
||||||
// Put this on the stack, and then point UNWIND_CONTEXT_BASE at it.
|
|
||||||
// struct {
|
|
||||||
// u64 valid;
|
|
||||||
// u64 handler;
|
|
||||||
// u64 finalizer;
|
|
||||||
// };
|
|
||||||
|
|
||||||
if (finalizer.has_value()) {
|
|
||||||
// push finalizer (patched later)
|
|
||||||
m_assembler.mov(
|
|
||||||
Assembler::Operand::Register(GPR0),
|
Assembler::Operand::Register(GPR0),
|
||||||
Assembler::Operand::Imm(0),
|
Assembler::Condition::EqualTo,
|
||||||
Assembler::Patchable::Yes);
|
Assembler::Operand::Register(GPR1),
|
||||||
block_data_for(finalizer.value().block()).absolute_references_to_here.append(m_assembler.m_output.size() - 8);
|
no_exception);
|
||||||
m_assembler.push(Assembler::Operand::Register(GPR0));
|
store_vm_register(Bytecode::Register::accumulator(), GPR0);
|
||||||
|
store_vm_register(Bytecode::Register::exception(), GPR1);
|
||||||
|
m_assembler.jump(label_for(*handler));
|
||||||
|
no_exception.link(m_assembler);
|
||||||
|
} else if (auto const* finalizer = current_block().finalizer(); finalizer) {
|
||||||
|
m_assembler.jump_if(Assembler::Operand::Register(GPR0),
|
||||||
|
Assembler::Condition::NotEqualTo,
|
||||||
|
Assembler::Operand::Register(GPR1),
|
||||||
|
label_for(*finalizer));
|
||||||
} else {
|
} else {
|
||||||
m_assembler.push(Assembler::Operand::Imm(0));
|
m_assembler.jump_if(Assembler::Operand::Register(GPR0),
|
||||||
|
Assembler::Condition::NotEqualTo,
|
||||||
|
Assembler::Operand::Register(GPR1),
|
||||||
|
m_exit_label);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (handler.has_value()) {
|
|
||||||
// push handler (patched later)
|
|
||||||
m_assembler.mov(
|
|
||||||
Assembler::Operand::Register(GPR0),
|
|
||||||
Assembler::Operand::Imm(0),
|
|
||||||
Assembler::Patchable::Yes);
|
|
||||||
block_data_for(handler.value().block()).absolute_references_to_here.append(m_assembler.m_output.size() - 8);
|
|
||||||
m_assembler.push(Assembler::Operand::Register(GPR0));
|
|
||||||
} else {
|
|
||||||
m_assembler.push(Assembler::Operand::Imm(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
// push valid
|
|
||||||
m_assembler.push(Assembler::Operand::Imm(valid));
|
|
||||||
|
|
||||||
// UNWIND_CONTEXT_BASE = STACK_POINTER
|
|
||||||
m_assembler.mov(
|
|
||||||
Assembler::Operand::Register(UNWIND_CONTEXT_BASE),
|
|
||||||
Assembler::Operand::Register(STACK_POINTER));
|
|
||||||
|
|
||||||
// align stack pointer
|
|
||||||
m_assembler.sub(Assembler::Operand::Register(STACK_POINTER), Assembler::Operand::Imm(8));
|
|
||||||
}
|
|
||||||
|
|
||||||
void Compiler::pop_unwind_context()
|
|
||||||
{
|
|
||||||
m_assembler.add(Assembler::Operand::Register(STACK_POINTER), Assembler::Operand::Imm(32));
|
|
||||||
m_assembler.add(Assembler::Operand::Register(UNWIND_CONTEXT_BASE), Assembler::Operand::Imm(32));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Compiler::compile_enter_unwind_context(Bytecode::Op::EnterUnwindContext const& op)
|
void Compiler::compile_enter_unwind_context(Bytecode::Op::EnterUnwindContext const& op)
|
||||||
{
|
{
|
||||||
push_unwind_context(true, op.handler_target(), op.finalizer_target());
|
|
||||||
|
|
||||||
m_assembler.jump(label_for(op.entry_point().block()));
|
m_assembler.jump(label_for(op.entry_point().block()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Compiler::compile_leave_unwind_context(Bytecode::Op::LeaveUnwindContext const&)
|
void Compiler::compile_leave_unwind_context(Bytecode::Op::LeaveUnwindContext const&)
|
||||||
{
|
{
|
||||||
pop_unwind_context();
|
/* Nothing */
|
||||||
}
|
}
|
||||||
|
|
||||||
void Compiler::compile_throw(Bytecode::Op::Throw const&)
|
void Compiler::compile_throw(Bytecode::Op::Throw const&)
|
||||||
|
@ -668,35 +563,13 @@ void Compiler::compile_return(Bytecode::Op::Return const&)
|
||||||
{
|
{
|
||||||
load_vm_register(GPR0, Bytecode::Register::accumulator());
|
load_vm_register(GPR0, Bytecode::Register::accumulator());
|
||||||
|
|
||||||
// check for finalizer
|
if (auto const* finalizer = current_block().finalizer(); finalizer) {
|
||||||
// if (!unwind_context.valid) goto normal_return;
|
store_vm_register(Bytecode::Register::saved_return_value(), GPR0);
|
||||||
Assembler::Label normal_return {};
|
m_assembler.jump(label_for(*finalizer));
|
||||||
m_assembler.mov(
|
} else {
|
||||||
Assembler::Operand::Register(GPR1),
|
store_vm_register(Bytecode::Register::return_value(), GPR0);
|
||||||
Assembler::Operand::Mem64BaseAndOffset(UNWIND_CONTEXT_BASE, 0));
|
jump_to_exit();
|
||||||
m_assembler.jump_if(
|
}
|
||||||
Assembler::Operand::Register(GPR1),
|
|
||||||
Assembler::Condition::EqualTo,
|
|
||||||
Assembler::Operand::Imm(0),
|
|
||||||
normal_return);
|
|
||||||
|
|
||||||
// if (!unwind_context.finalizer) goto normal_return;
|
|
||||||
m_assembler.mov(
|
|
||||||
Assembler::Operand::Register(GPR1),
|
|
||||||
Assembler::Operand::Mem64BaseAndOffset(UNWIND_CONTEXT_BASE, 16));
|
|
||||||
m_assembler.jump_if(
|
|
||||||
Assembler::Operand::Register(GPR1),
|
|
||||||
Assembler::Condition::EqualTo,
|
|
||||||
Assembler::Operand::Imm(0),
|
|
||||||
normal_return);
|
|
||||||
|
|
||||||
store_vm_register(Bytecode::Register::saved_return_value(), GPR0);
|
|
||||||
m_assembler.jump(Assembler::Operand::Register(GPR1));
|
|
||||||
|
|
||||||
// normal_return:
|
|
||||||
normal_return.link(m_assembler);
|
|
||||||
store_vm_register(Bytecode::Register::return_value(), GPR0);
|
|
||||||
jump_to_exit();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static Value cxx_new_string(VM& vm, DeprecatedString const& string)
|
static Value cxx_new_string(VM& vm, DeprecatedString const& string)
|
||||||
|
@ -1800,10 +1673,9 @@ OwnPtr<NativeExecutable> Compiler::compile(Bytecode::Executable& bytecode_execut
|
||||||
Assembler::Operand::Register(LOCALS_ARRAY_BASE),
|
Assembler::Operand::Register(LOCALS_ARRAY_BASE),
|
||||||
Assembler::Operand::Register(ARG2));
|
Assembler::Operand::Register(ARG2));
|
||||||
|
|
||||||
compiler.push_unwind_context(false, {}, {});
|
|
||||||
|
|
||||||
for (auto& block : bytecode_executable.basic_blocks) {
|
for (auto& block : bytecode_executable.basic_blocks) {
|
||||||
compiler.block_data_for(*block).start_offset = compiler.m_output.size();
|
compiler.block_data_for(*block).start_offset = compiler.m_output.size();
|
||||||
|
compiler.set_current_block(*block);
|
||||||
auto it = Bytecode::InstructionStreamIterator(block->instruction_stream());
|
auto it = Bytecode::InstructionStreamIterator(block->instruction_stream());
|
||||||
while (!it.at_end()) {
|
while (!it.at_end()) {
|
||||||
auto const& op = *it;
|
auto const& op = *it;
|
||||||
|
@ -1831,11 +1703,6 @@ OwnPtr<NativeExecutable> Compiler::compile(Bytecode::Executable& bytecode_execut
|
||||||
compiler.m_exit_label.link(compiler.m_assembler);
|
compiler.m_exit_label.link(compiler.m_assembler);
|
||||||
compiler.m_assembler.exit();
|
compiler.m_assembler.exit();
|
||||||
|
|
||||||
if (!compiler.m_exception_handler.jump_slot_offsets_in_instruction_stream.is_empty()) {
|
|
||||||
compiler.m_exception_handler.link(compiler.m_assembler);
|
|
||||||
compiler.handle_exception();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto* executable_memory = mmap(nullptr, compiler.m_output.size(), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
|
auto* executable_memory = mmap(nullptr, compiler.m_output.size(), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
|
||||||
if (executable_memory == MAP_FAILED) {
|
if (executable_memory == MAP_FAILED) {
|
||||||
dbgln("mmap: {}", strerror(errno));
|
dbgln("mmap: {}", strerror(errno));
|
||||||
|
@ -1844,21 +1711,7 @@ OwnPtr<NativeExecutable> Compiler::compile(Bytecode::Executable& bytecode_execut
|
||||||
|
|
||||||
for (auto& block : bytecode_executable.basic_blocks) {
|
for (auto& block : bytecode_executable.basic_blocks) {
|
||||||
auto& block_data = compiler.block_data_for(*block);
|
auto& block_data = compiler.block_data_for(*block);
|
||||||
|
|
||||||
block_data.label.link_to(compiler.m_assembler, block_data.start_offset);
|
block_data.label.link_to(compiler.m_assembler, block_data.start_offset);
|
||||||
|
|
||||||
// Patch up all the absolute references
|
|
||||||
for (auto& absolute_reference : block_data.absolute_references_to_here) {
|
|
||||||
auto offset = bit_cast<u64>(executable_memory) + block_data.start_offset;
|
|
||||||
compiler.m_output[absolute_reference + 0] = (offset >> 0) & 0xff;
|
|
||||||
compiler.m_output[absolute_reference + 1] = (offset >> 8) & 0xff;
|
|
||||||
compiler.m_output[absolute_reference + 2] = (offset >> 16) & 0xff;
|
|
||||||
compiler.m_output[absolute_reference + 3] = (offset >> 24) & 0xff;
|
|
||||||
compiler.m_output[absolute_reference + 4] = (offset >> 32) & 0xff;
|
|
||||||
compiler.m_output[absolute_reference + 5] = (offset >> 40) & 0xff;
|
|
||||||
compiler.m_output[absolute_reference + 6] = (offset >> 48) & 0xff;
|
|
||||||
compiler.m_output[absolute_reference + 7] = (offset >> 56) & 0xff;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if constexpr (DUMP_JIT_MACHINE_CODE_TO_STDOUT) {
|
if constexpr (DUMP_JIT_MACHINE_CODE_TO_STDOUT) {
|
||||||
|
|
|
@ -37,7 +37,6 @@ private:
|
||||||
static constexpr auto STACK_POINTER = Assembler::Reg::RSP;
|
static constexpr auto STACK_POINTER = Assembler::Reg::RSP;
|
||||||
static constexpr auto REGISTER_ARRAY_BASE = Assembler::Reg::RBX;
|
static constexpr auto REGISTER_ARRAY_BASE = Assembler::Reg::RBX;
|
||||||
static constexpr auto LOCALS_ARRAY_BASE = Assembler::Reg::R14;
|
static constexpr auto LOCALS_ARRAY_BASE = Assembler::Reg::R14;
|
||||||
static constexpr auto UNWIND_CONTEXT_BASE = Assembler::Reg::R15;
|
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
# define JS_ENUMERATE_COMMON_BINARY_OPS_WITHOUT_FAST_PATH(O) \
|
# define JS_ENUMERATE_COMMON_BINARY_OPS_WITHOUT_FAST_PATH(O) \
|
||||||
|
@ -159,9 +158,6 @@ private:
|
||||||
void check_exception();
|
void check_exception();
|
||||||
void handle_exception();
|
void handle_exception();
|
||||||
|
|
||||||
void push_unwind_context(bool valid, Optional<Bytecode::Label> const& handler, Optional<Bytecode::Label> const& finalizer);
|
|
||||||
void pop_unwind_context();
|
|
||||||
|
|
||||||
void jump_to_exit();
|
void jump_to_exit();
|
||||||
|
|
||||||
void native_call(void* function_address, Vector<Assembler::Operand> const& stack_arguments = {});
|
void native_call(void* function_address, Vector<Assembler::Operand> const& stack_arguments = {});
|
||||||
|
@ -187,7 +183,6 @@ private:
|
||||||
struct BasicBlockData {
|
struct BasicBlockData {
|
||||||
size_t start_offset { 0 };
|
size_t start_offset { 0 };
|
||||||
Assembler::Label label;
|
Assembler::Label label;
|
||||||
Vector<size_t> absolute_references_to_here;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
BasicBlockData& block_data_for(Bytecode::BasicBlock const& block)
|
BasicBlockData& block_data_for(Bytecode::BasicBlock const& block)
|
||||||
|
@ -197,13 +192,23 @@ private:
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void set_current_block(Bytecode::BasicBlock const& block)
|
||||||
|
{
|
||||||
|
m_current_block = █
|
||||||
|
}
|
||||||
|
|
||||||
|
Bytecode::BasicBlock const& current_block()
|
||||||
|
{
|
||||||
|
return *m_current_block;
|
||||||
|
}
|
||||||
|
|
||||||
HashMap<Bytecode::BasicBlock const*, NonnullOwnPtr<BasicBlockData>> m_basic_block_data;
|
HashMap<Bytecode::BasicBlock const*, NonnullOwnPtr<BasicBlockData>> m_basic_block_data;
|
||||||
|
|
||||||
Vector<u8> m_output;
|
Vector<u8> m_output;
|
||||||
Assembler m_assembler { m_output };
|
Assembler m_assembler { m_output };
|
||||||
Assembler::Label m_exit_label;
|
Assembler::Label m_exit_label;
|
||||||
Assembler::Label m_exception_handler;
|
|
||||||
Bytecode::Executable& m_bytecode_executable;
|
Bytecode::Executable& m_bytecode_executable;
|
||||||
|
Bytecode::BasicBlock const* m_current_block;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue