diff --git a/Userland/Libraries/LibJS/JIT/Assembler.h b/Userland/Libraries/LibJS/JIT/Assembler.h index b1a4832708..752c9eaf2a 100644 --- a/Userland/Libraries/LibJS/JIT/Assembler.h +++ b/Userland/Libraries/LibJS/JIT/Assembler.h @@ -37,6 +37,8 @@ struct Assembler { struct Operand { enum class Type { Reg, + Imm8, + Imm32, Imm64, Mem64BaseAndOffset, }; @@ -54,6 +56,22 @@ struct Assembler { return operand; } + static Operand Imm8(u8 imm8) + { + Operand operand; + operand.type = Type::Imm8; + operand.offset_or_immediate = imm8; + return operand; + } + + static Operand Imm32(u32 imm32) + { + Operand operand; + operand.type = Type::Imm32; + operand.offset_or_immediate = imm32; + return operand; + } + static Operand Imm64(u64 imm64) { Operand operand; @@ -77,6 +95,16 @@ struct Assembler { return to_underlying(reg) & 0x7; } + void shift_right(Operand dst, Operand count) + { + VERIFY(dst.type == Operand::Type::Reg); + VERIFY(count.type == Operand::Type::Imm8); + emit8(0x48 | ((to_underlying(dst.reg) >= 8) ? 1 << 0 : 0)); + emit8(0xc1); + emit8(0xe8 | encode_reg(dst.reg)); + emit8(count.offset_or_immediate); + } + void mov(Operand dst, Operand src) { if (dst.type == Operand::Type::Reg && src.type == Operand::Type::Reg) { @@ -176,6 +204,33 @@ struct Assembler { emit8(0xc0 | (to_underlying(dst) << 3) | to_underlying(dst)); } + struct Label { + size_t offset_in_instruction_stream { 0 }; + + void link(Assembler& assembler) + { + auto offset = assembler.m_output.size() - offset_in_instruction_stream; + auto jump_slot = offset_in_instruction_stream - 4; + assembler.m_output[jump_slot + 0] = (offset >> 0) & 0xff; + assembler.m_output[jump_slot + 1] = (offset >> 8) & 0xff; + assembler.m_output[jump_slot + 2] = (offset >> 16) & 0xff; + assembler.m_output[jump_slot + 3] = (offset >> 24) & 0xff; + } + }; + + [[nodiscard]] Label make_label() + { + return { .offset_in_instruction_stream = m_output.size() }; + } + + [[nodiscard]] Label jump() + { + // jmp target (RIP-relative 32-bit offset) + emit8(0xe9); + emit32(0xdeadbeef); + return make_label(); + } + void jump(Bytecode::BasicBlock& target) { // jmp target (RIP-relative 32-bit offset) @@ -203,6 +258,50 @@ struct Assembler { jump(true_target); } + void jump_if_not_equal(Operand lhs, Operand rhs, Label& label) + { + // cmp lhs, rhs + if (lhs.type == Operand::Type::Reg && rhs.type == Operand::Type::Reg) { + emit8(0x48 + | ((to_underlying(lhs.reg) >= 8) ? 1 << 2 : 0) + | ((to_underlying(rhs.reg) >= 8) ? 1 << 0 : 0)); + emit8(0x39); + emit8(0xc0 | (encode_reg(lhs.reg) << 3) | encode_reg(rhs.reg)); + } else if (lhs.type == Operand::Type::Reg && rhs.type == Operand::Type::Imm32) { + emit8(0x48 | ((to_underlying(lhs.reg) >= 8) ? 1 << 0 : 0)); + emit8(0x81); + emit8(0xf8 | encode_reg(lhs.reg)); + emit32(rhs.offset_or_immediate); + } else { + VERIFY_NOT_REACHED(); + } + + // jne label (RIP-relative 32-bit offset) + emit8(0x0f); + emit8(0x85); + emit32(0xdeadbeef); + label.offset_in_instruction_stream = m_output.size(); + } + + void bitwise_and(Operand dst, Operand src) + { + // and dst,src + if (dst.type == Operand::Type::Reg && src.type == Operand::Type::Reg) { + emit8(0x48 + | ((to_underlying(dst.reg) >= 8) ? 1 << 2 : 0) + | ((to_underlying(src.reg) >= 8) ? 1 << 0 : 0)); + emit8(0x21); + emit8(0xc0 | (encode_reg(dst.reg) << 3) | encode_reg(src.reg)); + } else if (dst.type == Operand::Type::Reg && src.type == Operand::Type::Imm32) { + emit8(0x48 | ((to_underlying(dst.reg) >= 8) ? 1 << 0 : 0)); + emit8(0x81); + emit8(0xe0 | encode_reg(dst.reg)); + emit32(src.offset_or_immediate); + } else { + VERIFY_NOT_REACHED(); + } + } + void exit() { // ret diff --git a/Userland/Libraries/LibJS/JIT/Compiler.cpp b/Userland/Libraries/LibJS/JIT/Compiler.cpp index 6066b06971..31e10d5ff2 100644 --- a/Userland/Libraries/LibJS/JIT/Compiler.cpp +++ b/Userland/Libraries/LibJS/JIT/Compiler.cpp @@ -82,22 +82,59 @@ static bool cxx_to_boolean(VM&, Value value) return value.to_boolean(); } -void Compiler::compile_to_boolean(Assembler::Reg reg) +void Compiler::compile_to_boolean(Assembler::Reg dst, Assembler::Reg src) { + // dst = src; + m_assembler.mov( + Assembler::Operand::Register(dst), + Assembler::Operand::Register(src)); + + // dst >>= 48; + m_assembler.shift_right( + Assembler::Operand::Register(dst), + Assembler::Operand::Imm8(48)); + + // if (dst != BOOLEAN_TAG) goto slow_case; + auto slow_case = m_assembler.make_label(); + m_assembler.jump_if_not_equal( + Assembler::Operand::Register(dst), + Assembler::Operand::Imm32(BOOLEAN_TAG), + slow_case); + + // Fast path for JS::Value booleans. + + // dst = src; + m_assembler.mov( + Assembler::Operand::Register(dst), + Assembler::Operand::Register(src)); + + // dst &= 1; + m_assembler.bitwise_and( + Assembler::Operand::Register(dst), + Assembler::Operand::Imm32(1)); + + // goto end; + auto end = m_assembler.jump(); + + // slow_case: // call C++ helper + slow_case.link(m_assembler); m_assembler.mov( Assembler::Operand::Register(Assembler::Reg::Arg1), - Assembler::Operand::Register(reg)); + Assembler::Operand::Register(src)); m_assembler.native_call((void*)cxx_to_boolean); m_assembler.mov( - Assembler::Operand::Register(reg), + Assembler::Operand::Register(dst), Assembler::Operand::Register(Assembler::Reg::Ret)); + + // end: + end.link(m_assembler); } void Compiler::compile_jump_conditional(Bytecode::Op::JumpConditional const& op) { - load_vm_register(Assembler::Reg::GPR0, Bytecode::Register::accumulator()); + load_vm_register(Assembler::Reg::GPR1, Bytecode::Register::accumulator()); - compile_to_boolean(Assembler::Reg::GPR0); + compile_to_boolean(Assembler::Reg::GPR0, Assembler::Reg::GPR1); m_assembler.jump_conditional(Assembler::Reg::GPR0, const_cast(op.true_target()->block()), diff --git a/Userland/Libraries/LibJS/JIT/Compiler.h b/Userland/Libraries/LibJS/JIT/Compiler.h index 9139ff3b0e..5840df8b42 100644 --- a/Userland/Libraries/LibJS/JIT/Compiler.h +++ b/Userland/Libraries/LibJS/JIT/Compiler.h @@ -34,7 +34,7 @@ private: void store_vm_local(size_t, Assembler::Reg); void load_vm_local(Assembler::Reg, size_t); - void compile_to_boolean(Assembler::Reg); + void compile_to_boolean(Assembler::Reg dst, Assembler::Reg src); Vector m_output; Assembler m_assembler { m_output };