Compiler.cpp 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. /*
  2. * Copyright (c) 2023, Andreas Kling <kling@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/OwnPtr.h>
  7. #include <LibJS/Bytecode/Instruction.h>
  8. #include <LibJS/JIT/Compiler.h>
  9. #include <LibJS/Runtime/ValueInlines.h>
  10. #include <sys/mman.h>
  11. namespace JS::JIT {
  12. void Compiler::store_vm_register(Bytecode::Register dst, Assembler::Reg src)
  13. {
  14. m_assembler.mov(
  15. Assembler::Operand::Mem64BaseAndOffset(REGISTER_ARRAY_BASE, dst.index() * sizeof(Value)),
  16. Assembler::Operand::Register(src));
  17. }
  18. void Compiler::load_vm_register(Assembler::Reg dst, Bytecode::Register src)
  19. {
  20. m_assembler.mov(
  21. Assembler::Operand::Register(dst),
  22. Assembler::Operand::Mem64BaseAndOffset(REGISTER_ARRAY_BASE, src.index() * sizeof(Value)));
  23. }
  24. void Compiler::store_vm_local(size_t dst, Assembler::Reg src)
  25. {
  26. m_assembler.mov(
  27. Assembler::Operand::Mem64BaseAndOffset(LOCALS_ARRAY_BASE, dst * sizeof(Value)),
  28. Assembler::Operand::Register(src));
  29. }
  30. void Compiler::load_vm_local(Assembler::Reg dst, size_t src)
  31. {
  32. m_assembler.mov(
  33. Assembler::Operand::Register(dst),
  34. Assembler::Operand::Mem64BaseAndOffset(LOCALS_ARRAY_BASE, src * sizeof(Value)));
  35. }
  36. void Compiler::compile_load_immediate(Bytecode::Op::LoadImmediate const& op)
  37. {
  38. m_assembler.mov(
  39. Assembler::Operand::Register(GPR0),
  40. Assembler::Operand::Imm64(op.value().encoded()));
  41. store_vm_register(Bytecode::Register::accumulator(), GPR0);
  42. }
  43. void Compiler::compile_load(Bytecode::Op::Load const& op)
  44. {
  45. load_vm_register(GPR0, op.src());
  46. store_vm_register(Bytecode::Register::accumulator(), GPR0);
  47. }
  48. void Compiler::compile_store(Bytecode::Op::Store const& op)
  49. {
  50. load_vm_register(GPR0, Bytecode::Register::accumulator());
  51. store_vm_register(op.dst(), GPR0);
  52. }
  53. void Compiler::compile_get_local(Bytecode::Op::GetLocal const& op)
  54. {
  55. load_vm_local(GPR0, op.index());
  56. store_vm_register(Bytecode::Register::accumulator(), GPR0);
  57. }
  58. void Compiler::compile_set_local(Bytecode::Op::SetLocal const& op)
  59. {
  60. load_vm_register(GPR0, Bytecode::Register::accumulator());
  61. store_vm_local(op.index(), GPR0);
  62. }
  63. void Compiler::compile_jump(Bytecode::Op::Jump const& op)
  64. {
  65. m_assembler.jump(const_cast<Bytecode::BasicBlock&>(op.true_target()->block()));
  66. }
  67. static bool cxx_to_boolean(VM&, Value value)
  68. {
  69. return value.to_boolean();
  70. }
  71. void Compiler::compile_to_boolean(Assembler::Reg dst, Assembler::Reg src)
  72. {
  73. // dst = src;
  74. m_assembler.mov(
  75. Assembler::Operand::Register(dst),
  76. Assembler::Operand::Register(src));
  77. // dst >>= 48;
  78. m_assembler.shift_right(
  79. Assembler::Operand::Register(dst),
  80. Assembler::Operand::Imm8(48));
  81. // if (dst != BOOLEAN_TAG) goto slow_case;
  82. auto slow_case = m_assembler.make_label();
  83. m_assembler.jump_if_not_equal(
  84. Assembler::Operand::Register(dst),
  85. Assembler::Operand::Imm32(BOOLEAN_TAG),
  86. slow_case);
  87. // Fast path for JS::Value booleans.
  88. // dst = src;
  89. m_assembler.mov(
  90. Assembler::Operand::Register(dst),
  91. Assembler::Operand::Register(src));
  92. // dst &= 1;
  93. m_assembler.bitwise_and(
  94. Assembler::Operand::Register(dst),
  95. Assembler::Operand::Imm32(1));
  96. // goto end;
  97. auto end = m_assembler.jump();
  98. // slow_case: // call C++ helper
  99. slow_case.link(m_assembler);
  100. m_assembler.mov(
  101. Assembler::Operand::Register(ARG1),
  102. Assembler::Operand::Register(src));
  103. m_assembler.native_call((void*)cxx_to_boolean);
  104. m_assembler.mov(
  105. Assembler::Operand::Register(dst),
  106. Assembler::Operand::Register(RET));
  107. // end:
  108. end.link(m_assembler);
  109. }
  110. void Compiler::compile_jump_conditional(Bytecode::Op::JumpConditional const& op)
  111. {
  112. load_vm_register(GPR1, Bytecode::Register::accumulator());
  113. compile_to_boolean(GPR0, GPR1);
  114. m_assembler.jump_conditional(GPR0,
  115. const_cast<Bytecode::BasicBlock&>(op.true_target()->block()),
  116. const_cast<Bytecode::BasicBlock&>(op.false_target()->block()));
  117. }
  118. [[maybe_unused]] static Value cxx_less_than(VM& vm, Value lhs, Value rhs)
  119. {
  120. // FIXME: Handle exceptions!
  121. return MUST(less_than(vm, lhs, rhs));
  122. }
  123. void Compiler::compile_less_than(Bytecode::Op::LessThan const& op)
  124. {
  125. load_vm_register(ARG1, op.lhs());
  126. load_vm_register(ARG2, Bytecode::Register::accumulator());
  127. m_assembler.native_call((void*)cxx_less_than);
  128. store_vm_register(Bytecode::Register::accumulator(), RET);
  129. }
  130. [[maybe_unused]] static Value cxx_increment(VM& vm, Value value)
  131. {
  132. // FIXME: Handle exceptions!
  133. auto old_value = MUST(value.to_numeric(vm));
  134. if (old_value.is_number())
  135. return Value(old_value.as_double() + 1);
  136. return BigInt::create(vm, old_value.as_bigint().big_integer().plus(Crypto::SignedBigInteger { 1 }));
  137. }
  138. void Compiler::compile_increment(Bytecode::Op::Increment const&)
  139. {
  140. load_vm_register(ARG1, Bytecode::Register::accumulator());
  141. m_assembler.native_call((void*)cxx_increment);
  142. store_vm_register(Bytecode::Register::accumulator(), RET);
  143. }
  144. OwnPtr<NativeExecutable> Compiler::compile(Bytecode::Executable const& bytecode_executable)
  145. {
  146. if (getenv("LIBJS_NO_JIT"))
  147. return nullptr;
  148. Compiler compiler;
  149. compiler.m_assembler.mov(
  150. Assembler::Operand::Register(REGISTER_ARRAY_BASE),
  151. Assembler::Operand::Register(ARG1));
  152. compiler.m_assembler.mov(
  153. Assembler::Operand::Register(LOCALS_ARRAY_BASE),
  154. Assembler::Operand::Register(ARG2));
  155. for (auto& block : bytecode_executable.basic_blocks) {
  156. block->offset = compiler.m_output.size();
  157. auto it = Bytecode::InstructionStreamIterator(block->instruction_stream());
  158. while (!it.at_end()) {
  159. auto const& op = *it;
  160. switch (op.type()) {
  161. case Bytecode::Instruction::Type::LoadImmediate:
  162. compiler.compile_load_immediate(static_cast<Bytecode::Op::LoadImmediate const&>(op));
  163. break;
  164. case Bytecode::Instruction::Type::Store:
  165. compiler.compile_store(static_cast<Bytecode::Op::Store const&>(op));
  166. break;
  167. case Bytecode::Instruction::Type::Load:
  168. compiler.compile_load(static_cast<Bytecode::Op::Load const&>(op));
  169. break;
  170. case Bytecode::Instruction::Type::GetLocal:
  171. compiler.compile_get_local(static_cast<Bytecode::Op::GetLocal const&>(op));
  172. break;
  173. case Bytecode::Instruction::Type::SetLocal:
  174. compiler.compile_set_local(static_cast<Bytecode::Op::SetLocal const&>(op));
  175. break;
  176. case Bytecode::Instruction::Type::Jump:
  177. compiler.compile_jump(static_cast<Bytecode::Op::Jump const&>(op));
  178. break;
  179. case Bytecode::Instruction::Type::JumpConditional:
  180. compiler.compile_jump_conditional(static_cast<Bytecode::Op::JumpConditional const&>(op));
  181. break;
  182. case Bytecode::Instruction::Type::LessThan:
  183. compiler.compile_less_than(static_cast<Bytecode::Op::LessThan const&>(op));
  184. break;
  185. case Bytecode::Instruction::Type::Increment:
  186. compiler.compile_increment(static_cast<Bytecode::Op::Increment const&>(op));
  187. break;
  188. default:
  189. dbgln("JIT compilation failed: {}", bytecode_executable.name);
  190. dbgln("Unsupported bytecode op: {}", op.to_deprecated_string(bytecode_executable));
  191. return nullptr;
  192. }
  193. ++it;
  194. }
  195. if (!block->is_terminated())
  196. compiler.m_assembler.exit();
  197. }
  198. // Patch up all the jumps
  199. for (auto& block : bytecode_executable.basic_blocks) {
  200. for (auto& jump : block->jumps_to_here) {
  201. auto offset = block->offset - jump - 4;
  202. compiler.m_output[jump + 0] = (offset >> 0) & 0xff;
  203. compiler.m_output[jump + 1] = (offset >> 8) & 0xff;
  204. compiler.m_output[jump + 2] = (offset >> 16) & 0xff;
  205. compiler.m_output[jump + 3] = (offset >> 24) & 0xff;
  206. }
  207. }
  208. write(STDOUT_FILENO, compiler.m_output.data(), compiler.m_output.size());
  209. auto* executable_memory = mmap(nullptr, compiler.m_output.size(), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
  210. if (executable_memory == MAP_FAILED) {
  211. perror("mmap");
  212. return nullptr;
  213. }
  214. memcpy(executable_memory, compiler.m_output.data(), compiler.m_output.size());
  215. mprotect(executable_memory, compiler.m_output.size(), PROT_READ | PROT_EXEC);
  216. return make<NativeExecutable>(executable_memory, compiler.m_output.size());
  217. }
  218. }