Assembler.h 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405
  1. /*
  2. * Copyright (c) 2023, Andreas Kling <kling@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #pragma once
  7. #include <AK/Vector.h>
  8. #include <LibJS/Bytecode/BasicBlock.h>
  9. namespace JS::JIT {
  10. struct Assembler {
  11. Assembler(Vector<u8>& output)
  12. : m_output(output)
  13. {
  14. }
  15. Vector<u8>& m_output;
  16. enum class Reg {
  17. RAX = 0,
  18. RCX = 1,
  19. RDX = 2,
  20. RBX = 3,
  21. RSP = 4,
  22. RBP = 5,
  23. RSI = 6,
  24. RDI = 7,
  25. R8 = 8,
  26. R9 = 9,
  27. R10 = 10,
  28. R11 = 11,
  29. R12 = 12,
  30. R13 = 13,
  31. R14 = 14,
  32. R15 = 15,
  33. };
  34. struct Operand {
  35. enum class Type {
  36. Reg,
  37. Imm8,
  38. Imm32,
  39. Imm64,
  40. Mem64BaseAndOffset,
  41. };
  42. Type type {};
  43. Reg reg {};
  44. u64 offset_or_immediate { 0 };
  45. static Operand Register(Reg reg)
  46. {
  47. Operand operand;
  48. operand.type = Type::Reg;
  49. operand.reg = reg;
  50. return operand;
  51. }
  52. static Operand Imm8(u8 imm8)
  53. {
  54. Operand operand;
  55. operand.type = Type::Imm8;
  56. operand.offset_or_immediate = imm8;
  57. return operand;
  58. }
  59. static Operand Imm32(u32 imm32)
  60. {
  61. Operand operand;
  62. operand.type = Type::Imm32;
  63. operand.offset_or_immediate = imm32;
  64. return operand;
  65. }
  66. static Operand Imm64(u64 imm64)
  67. {
  68. Operand operand;
  69. operand.type = Type::Imm64;
  70. operand.offset_or_immediate = imm64;
  71. return operand;
  72. }
  73. static Operand Mem64BaseAndOffset(Reg base, u64 offset)
  74. {
  75. Operand operand;
  76. operand.type = Type::Mem64BaseAndOffset;
  77. operand.reg = base;
  78. operand.offset_or_immediate = offset;
  79. return operand;
  80. }
  81. };
  82. static constexpr u8 encode_reg(Reg reg)
  83. {
  84. return to_underlying(reg) & 0x7;
  85. }
  86. void shift_right(Operand dst, Operand count)
  87. {
  88. VERIFY(dst.type == Operand::Type::Reg);
  89. VERIFY(count.type == Operand::Type::Imm8);
  90. emit8(0x48 | ((to_underlying(dst.reg) >= 8) ? 1 << 0 : 0));
  91. emit8(0xc1);
  92. emit8(0xe8 | encode_reg(dst.reg));
  93. emit8(count.offset_or_immediate);
  94. }
  95. void mov(Operand dst, Operand src)
  96. {
  97. if (dst.type == Operand::Type::Reg && src.type == Operand::Type::Reg) {
  98. if (src.reg == dst.reg)
  99. return;
  100. emit8(0x48
  101. | ((to_underlying(src.reg) >= 8) ? 1 << 2 : 0)
  102. | ((to_underlying(dst.reg) >= 8) ? 1 << 0 : 0));
  103. emit8(0x89);
  104. emit8(0xc0 | (encode_reg(src.reg) << 3) | encode_reg(dst.reg));
  105. return;
  106. }
  107. if (dst.type == Operand::Type::Reg && src.type == Operand::Type::Imm64) {
  108. emit8(0x48 | ((to_underlying(dst.reg) >= 8) ? 1 << 0 : 0));
  109. emit8(0xb8 | encode_reg(dst.reg));
  110. emit64(src.offset_or_immediate);
  111. return;
  112. }
  113. if (dst.type == Operand::Type::Mem64BaseAndOffset && src.type == Operand::Type::Reg) {
  114. emit8(0x48
  115. | ((to_underlying(src.reg) >= 8) ? 1 << 2 : 0)
  116. | ((to_underlying(dst.reg) >= 8) ? 1 << 0 : 0));
  117. emit8(0x89);
  118. emit8(0x80 | (encode_reg(src.reg) << 3) | encode_reg(dst.reg));
  119. emit32(dst.offset_or_immediate);
  120. return;
  121. }
  122. if (dst.type == Operand::Type::Reg && src.type == Operand::Type::Mem64BaseAndOffset) {
  123. emit8(0x48
  124. | ((to_underlying(dst.reg) >= 8) ? 1 << 2 : 0)
  125. | ((to_underlying(src.reg) >= 8) ? 1 << 0 : 0));
  126. emit8(0x8b);
  127. emit8(0x80 | (encode_reg(dst.reg) << 3) | encode_reg(src.reg));
  128. emit32(src.offset_or_immediate);
  129. return;
  130. }
  131. VERIFY_NOT_REACHED();
  132. }
  133. void emit8(u8 value)
  134. {
  135. m_output.append(value);
  136. }
  137. void emit32(u32 value)
  138. {
  139. m_output.append((value >> 0) & 0xff);
  140. m_output.append((value >> 8) & 0xff);
  141. m_output.append((value >> 16) & 0xff);
  142. m_output.append((value >> 24) & 0xff);
  143. }
  144. void emit64(u64 value)
  145. {
  146. m_output.append((value >> 0) & 0xff);
  147. m_output.append((value >> 8) & 0xff);
  148. m_output.append((value >> 16) & 0xff);
  149. m_output.append((value >> 24) & 0xff);
  150. m_output.append((value >> 32) & 0xff);
  151. m_output.append((value >> 40) & 0xff);
  152. m_output.append((value >> 48) & 0xff);
  153. m_output.append((value >> 56) & 0xff);
  154. }
  155. struct Label {
  156. size_t offset_in_instruction_stream { 0 };
  157. void link(Assembler& assembler)
  158. {
  159. auto offset = assembler.m_output.size() - offset_in_instruction_stream;
  160. auto jump_slot = offset_in_instruction_stream - 4;
  161. assembler.m_output[jump_slot + 0] = (offset >> 0) & 0xff;
  162. assembler.m_output[jump_slot + 1] = (offset >> 8) & 0xff;
  163. assembler.m_output[jump_slot + 2] = (offset >> 16) & 0xff;
  164. assembler.m_output[jump_slot + 3] = (offset >> 24) & 0xff;
  165. }
  166. };
  167. [[nodiscard]] Label make_label()
  168. {
  169. return { .offset_in_instruction_stream = m_output.size() };
  170. }
  171. [[nodiscard]] Label jump()
  172. {
  173. // jmp target (RIP-relative 32-bit offset)
  174. emit8(0xe9);
  175. emit32(0xdeadbeef);
  176. return make_label();
  177. }
  178. void jump(Bytecode::BasicBlock& target)
  179. {
  180. // jmp target (RIP-relative 32-bit offset)
  181. emit8(0xe9);
  182. target.jumps_to_here.append(m_output.size());
  183. emit32(0xdeadbeef);
  184. }
  185. void jump_conditional(Reg reg, Bytecode::BasicBlock& true_target, Bytecode::BasicBlock& false_target)
  186. {
  187. // if (reg & 1) is 0, jump to false_target, else jump to true_target
  188. // test reg, 1
  189. emit8(0x48 | ((to_underlying(reg) >= 8) ? 1 << 2 : 0));
  190. emit8(0xf7);
  191. emit8(0xc0 | encode_reg(reg));
  192. emit32(0x01);
  193. // jz false_target (RIP-relative 32-bit offset)
  194. emit8(0x0f);
  195. emit8(0x84);
  196. false_target.jumps_to_here.append(m_output.size());
  197. emit32(0xdeadbeef);
  198. // jmp true_target (RIP-relative 32-bit offset)
  199. jump(true_target);
  200. }
  201. void cmp(Operand lhs, Operand rhs)
  202. {
  203. if (lhs.type == Operand::Type::Reg && rhs.type == Operand::Type::Reg) {
  204. emit8(0x48
  205. | ((to_underlying(lhs.reg) >= 8) ? 1 << 2 : 0)
  206. | ((to_underlying(rhs.reg) >= 8) ? 1 << 0 : 0));
  207. emit8(0x39);
  208. emit8(0xc0 | (encode_reg(lhs.reg) << 3) | encode_reg(rhs.reg));
  209. } else if (lhs.type == Operand::Type::Reg && rhs.type == Operand::Type::Imm32) {
  210. emit8(0x48 | ((to_underlying(lhs.reg) >= 8) ? 1 << 0 : 0));
  211. emit8(0x81);
  212. emit8(0xf8 | encode_reg(lhs.reg));
  213. emit32(rhs.offset_or_immediate);
  214. } else {
  215. VERIFY_NOT_REACHED();
  216. }
  217. }
  218. void jump_if_equal(Operand lhs, Operand rhs, Label& label)
  219. {
  220. cmp(lhs, rhs);
  221. // je label (RIP-relative 32-bit offset)
  222. emit8(0x0f);
  223. emit8(0x84);
  224. emit32(0xdeadbeef);
  225. label.offset_in_instruction_stream = m_output.size();
  226. }
  227. void jump_if_not_equal(Operand lhs, Operand rhs, Label& label)
  228. {
  229. cmp(lhs, rhs);
  230. // jne label (RIP-relative 32-bit offset)
  231. emit8(0x0f);
  232. emit8(0x85);
  233. emit32(0xdeadbeef);
  234. label.offset_in_instruction_stream = m_output.size();
  235. }
  236. void bitwise_and(Operand dst, Operand src)
  237. {
  238. // and dst,src
  239. if (dst.type == Operand::Type::Reg && src.type == Operand::Type::Reg) {
  240. emit8(0x48
  241. | ((to_underlying(dst.reg) >= 8) ? 1 << 2 : 0)
  242. | ((to_underlying(src.reg) >= 8) ? 1 << 0 : 0));
  243. emit8(0x21);
  244. emit8(0xc0 | (encode_reg(dst.reg) << 3) | encode_reg(src.reg));
  245. } else if (dst.type == Operand::Type::Reg && src.type == Operand::Type::Imm32) {
  246. emit8(0x48 | ((to_underlying(dst.reg) >= 8) ? 1 << 0 : 0));
  247. emit8(0x81);
  248. emit8(0xe0 | encode_reg(dst.reg));
  249. emit32(src.offset_or_immediate);
  250. } else {
  251. VERIFY_NOT_REACHED();
  252. }
  253. }
  254. void exit()
  255. {
  256. // ret
  257. emit8(0xc3);
  258. }
  259. void push(Operand op)
  260. {
  261. if (op.type == Operand::Type::Reg) {
  262. if (to_underlying(op.reg) >= 8)
  263. emit8(0x49);
  264. emit8(0x50 | encode_reg(op.reg));
  265. } else {
  266. VERIFY_NOT_REACHED();
  267. }
  268. }
  269. void pop(Operand op)
  270. {
  271. if (op.type == Operand::Type::Reg) {
  272. if (to_underlying(op.reg) >= 8)
  273. emit8(0x49);
  274. emit8(0x58 | encode_reg(op.reg));
  275. } else {
  276. VERIFY_NOT_REACHED();
  277. }
  278. }
  279. void add(Operand dst, Operand src)
  280. {
  281. if (dst.type == Operand::Type::Reg && src.type == Operand::Type::Reg) {
  282. emit8(0x48
  283. | ((to_underlying(dst.reg) >= 8) ? 1 << 2 : 0)
  284. | ((to_underlying(src.reg) >= 8) ? 1 << 0 : 0));
  285. emit8(0x01);
  286. emit8(0xc0 | (encode_reg(dst.reg) << 3) | encode_reg(src.reg));
  287. } else if (dst.type == Operand::Type::Reg && src.type == Operand::Type::Imm32) {
  288. emit8(0x48 | ((to_underlying(dst.reg) >= 8) ? 1 << 0 : 0));
  289. emit8(0x81);
  290. emit8(0xc0 | encode_reg(dst.reg));
  291. emit32(src.offset_or_immediate);
  292. } else if (dst.type == Operand::Type::Reg && src.type == Operand::Type::Imm8) {
  293. emit8(0x48 | ((to_underlying(dst.reg) >= 8) ? 1 << 0 : 0));
  294. emit8(0x83);
  295. emit8(0xc0 | encode_reg(dst.reg));
  296. emit8(src.offset_or_immediate);
  297. } else {
  298. VERIFY_NOT_REACHED();
  299. }
  300. }
  301. void sub(Operand dst, Operand src)
  302. {
  303. if (dst.type == Operand::Type::Reg && src.type == Operand::Type::Reg) {
  304. emit8(0x48
  305. | ((to_underlying(dst.reg) >= 8) ? 1 << 2 : 0)
  306. | ((to_underlying(src.reg) >= 8) ? 1 << 0 : 0));
  307. emit8(0x29);
  308. emit8(0xc0 | (encode_reg(dst.reg) << 3) | encode_reg(src.reg));
  309. } else if (dst.type == Operand::Type::Reg && src.type == Operand::Type::Imm32) {
  310. emit8(0x48 | ((to_underlying(dst.reg) >= 8) ? 1 << 0 : 0));
  311. emit8(0x81);
  312. emit8(0xe8 | encode_reg(dst.reg));
  313. emit32(src.offset_or_immediate);
  314. } else if (dst.type == Operand::Type::Reg && src.type == Operand::Type::Imm8) {
  315. emit8(0x48 | ((to_underlying(dst.reg) >= 8) ? 1 << 0 : 0));
  316. emit8(0x83);
  317. emit8(0xe8 | encode_reg(dst.reg));
  318. emit8(src.offset_or_immediate);
  319. } else {
  320. VERIFY_NOT_REACHED();
  321. }
  322. }
  323. void native_call(void* callee)
  324. {
  325. // push caller-saved registers on the stack
  326. // (callee-saved registers: RBX, RSP, RBP, and R12–R15)
  327. push(Operand::Register(Reg::RCX));
  328. push(Operand::Register(Reg::RDX));
  329. push(Operand::Register(Reg::RSI));
  330. push(Operand::Register(Reg::RDI));
  331. push(Operand::Register(Reg::R8));
  332. push(Operand::Register(Reg::R9));
  333. push(Operand::Register(Reg::R10));
  334. push(Operand::Register(Reg::R11));
  335. // align the stack to 16-byte boundary
  336. sub(Operand::Register(Reg::RSP), Operand::Imm8(8));
  337. // load callee into RAX
  338. mov(Operand::Register(Reg::RAX), Operand::Imm64(bit_cast<u64>(callee)));
  339. // call RAX
  340. emit8(0xff);
  341. emit8(0xd0);
  342. // adjust stack pointer
  343. add(Operand::Register(Reg::RSP), Operand::Imm8(8));
  344. // restore caller-saved registers from the stack
  345. pop(Operand::Register(Reg::R11));
  346. pop(Operand::Register(Reg::R10));
  347. pop(Operand::Register(Reg::R9));
  348. pop(Operand::Register(Reg::R8));
  349. pop(Operand::Register(Reg::RDI));
  350. pop(Operand::Register(Reg::RSI));
  351. pop(Operand::Register(Reg::RDX));
  352. pop(Operand::Register(Reg::RCX));
  353. }
  354. };
  355. }