From da45bd3fdea3adccc2139f673e49e0d7346b765c Mon Sep 17 00:00:00 2001 From: Evgeniy Baskov Date: Sat, 28 Oct 2023 19:43:55 +0300 Subject: [PATCH] LibJS/JIT: Implement static exception handling --- Userland/Libraries/LibJS/JIT/Compiler.cpp | 203 +++------------------- Userland/Libraries/LibJS/JIT/Compiler.h | 17 +- 2 files changed, 39 insertions(+), 181 deletions(-) diff --git a/Userland/Libraries/LibJS/JIT/Compiler.cpp b/Userland/Libraries/LibJS/JIT/Compiler.cpp index fe8c0e480d..407389d008 100644 --- a/Userland/Libraries/LibJS/JIT/Compiler.cpp +++ b/Userland/Libraries/LibJS/JIT/Compiler.cpp @@ -360,146 +360,41 @@ void Compiler::compile_decrement(Bytecode::Op::Decrement const&) void Compiler::check_exception() { - // if (!exception.is_empty()) goto m_exception_handler; load_vm_register(GPR0, Bytecode::Register::exception()); 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 (!unwind_context.valid) return; - 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 const& handler, Optional 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( + if (auto const* handler = current_block().handler(); handler) { + Assembler::Label no_exception; + m_assembler.jump_if( Assembler::Operand::Register(GPR0), - Assembler::Operand::Imm(0), - Assembler::Patchable::Yes); - block_data_for(finalizer.value().block()).absolute_references_to_here.append(m_assembler.m_output.size() - 8); - m_assembler.push(Assembler::Operand::Register(GPR0)); + Assembler::Condition::EqualTo, + Assembler::Operand::Register(GPR1), + no_exception); + 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 { - 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) { - push_unwind_context(true, op.handler_target(), op.finalizer_target()); - m_assembler.jump(label_for(op.entry_point().block())); } void Compiler::compile_leave_unwind_context(Bytecode::Op::LeaveUnwindContext const&) { - pop_unwind_context(); + /* Nothing */ } 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()); - // check for finalizer - // if (!unwind_context.valid) goto normal_return; - Assembler::Label normal_return {}; - m_assembler.mov( - Assembler::Operand::Register(GPR1), - Assembler::Operand::Mem64BaseAndOffset(UNWIND_CONTEXT_BASE, 0)); - 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(); + if (auto const* finalizer = current_block().finalizer(); finalizer) { + store_vm_register(Bytecode::Register::saved_return_value(), GPR0); + m_assembler.jump(label_for(*finalizer)); + } else { + store_vm_register(Bytecode::Register::return_value(), GPR0); + jump_to_exit(); + } } static Value cxx_new_string(VM& vm, DeprecatedString const& string) @@ -1800,10 +1673,9 @@ OwnPtr Compiler::compile(Bytecode::Executable& bytecode_execut Assembler::Operand::Register(LOCALS_ARRAY_BASE), Assembler::Operand::Register(ARG2)); - compiler.push_unwind_context(false, {}, {}); - for (auto& block : bytecode_executable.basic_blocks) { compiler.block_data_for(*block).start_offset = compiler.m_output.size(); + compiler.set_current_block(*block); auto it = Bytecode::InstructionStreamIterator(block->instruction_stream()); while (!it.at_end()) { auto const& op = *it; @@ -1831,11 +1703,6 @@ OwnPtr Compiler::compile(Bytecode::Executable& bytecode_execut compiler.m_exit_label.link(compiler.m_assembler); 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); if (executable_memory == MAP_FAILED) { dbgln("mmap: {}", strerror(errno)); @@ -1844,21 +1711,7 @@ OwnPtr Compiler::compile(Bytecode::Executable& bytecode_execut for (auto& block : bytecode_executable.basic_blocks) { auto& block_data = compiler.block_data_for(*block); - 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(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) { diff --git a/Userland/Libraries/LibJS/JIT/Compiler.h b/Userland/Libraries/LibJS/JIT/Compiler.h index b9bd929316..ccaa14e04c 100644 --- a/Userland/Libraries/LibJS/JIT/Compiler.h +++ b/Userland/Libraries/LibJS/JIT/Compiler.h @@ -37,7 +37,6 @@ private: static constexpr auto STACK_POINTER = Assembler::Reg::RSP; static constexpr auto REGISTER_ARRAY_BASE = Assembler::Reg::RBX; static constexpr auto LOCALS_ARRAY_BASE = Assembler::Reg::R14; - static constexpr auto UNWIND_CONTEXT_BASE = Assembler::Reg::R15; # endif # define JS_ENUMERATE_COMMON_BINARY_OPS_WITHOUT_FAST_PATH(O) \ @@ -159,9 +158,6 @@ private: void check_exception(); void handle_exception(); - void push_unwind_context(bool valid, Optional const& handler, Optional const& finalizer); - void pop_unwind_context(); - void jump_to_exit(); void native_call(void* function_address, Vector const& stack_arguments = {}); @@ -187,7 +183,6 @@ private: struct BasicBlockData { size_t start_offset { 0 }; Assembler::Label label; - Vector absolute_references_to_here; }; 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> m_basic_block_data; Vector m_output; Assembler m_assembler { m_output }; Assembler::Label m_exit_label; - Assembler::Label m_exception_handler; Bytecode::Executable& m_bytecode_executable; + Bytecode::BasicBlock const* m_current_block; }; }