Assembler.h 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437
  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(Operand op)
  179. {
  180. if (op.type == Operand::Type::Reg) {
  181. if (to_underlying(op.reg) >= 8)
  182. emit8(0x41);
  183. emit8(0xff);
  184. emit8(0xe0 | encode_reg(op.reg));
  185. } else {
  186. VERIFY_NOT_REACHED();
  187. }
  188. }
  189. void verify_not_reached()
  190. {
  191. // ud2
  192. emit8(0x0f);
  193. emit8(0x0b);
  194. }
  195. void jump(Bytecode::BasicBlock& target)
  196. {
  197. // jmp target (RIP-relative 32-bit offset)
  198. emit8(0xe9);
  199. target.jumps_to_here.append(m_output.size());
  200. emit32(0xdeadbeef);
  201. }
  202. void jump_conditional(Reg reg, Bytecode::BasicBlock& true_target, Bytecode::BasicBlock& false_target)
  203. {
  204. // if (reg & 1) is 0, jump to false_target, else jump to true_target
  205. // test reg, 1
  206. emit8(0x48 | ((to_underlying(reg) >= 8) ? 1 << 2 : 0));
  207. emit8(0xf7);
  208. emit8(0xc0 | encode_reg(reg));
  209. emit32(0x01);
  210. // jz false_target (RIP-relative 32-bit offset)
  211. emit8(0x0f);
  212. emit8(0x84);
  213. false_target.jumps_to_here.append(m_output.size());
  214. emit32(0xdeadbeef);
  215. // jmp true_target (RIP-relative 32-bit offset)
  216. jump(true_target);
  217. }
  218. void cmp(Operand lhs, Operand rhs)
  219. {
  220. if (lhs.type == Operand::Type::Reg && rhs.type == Operand::Type::Reg) {
  221. emit8(0x48
  222. | ((to_underlying(lhs.reg) >= 8) ? 1 << 2 : 0)
  223. | ((to_underlying(rhs.reg) >= 8) ? 1 << 0 : 0));
  224. emit8(0x39);
  225. emit8(0xc0 | (encode_reg(lhs.reg) << 3) | encode_reg(rhs.reg));
  226. } else if (lhs.type == Operand::Type::Reg && rhs.type == Operand::Type::Imm32) {
  227. emit8(0x48 | ((to_underlying(lhs.reg) >= 8) ? 1 << 0 : 0));
  228. emit8(0x81);
  229. emit8(0xf8 | encode_reg(lhs.reg));
  230. emit32(rhs.offset_or_immediate);
  231. } else {
  232. VERIFY_NOT_REACHED();
  233. }
  234. }
  235. void jump_if_equal(Operand lhs, Operand rhs, Label& label)
  236. {
  237. cmp(lhs, rhs);
  238. // je label (RIP-relative 32-bit offset)
  239. emit8(0x0f);
  240. emit8(0x84);
  241. emit32(0xdeadbeef);
  242. label.offset_in_instruction_stream = m_output.size();
  243. }
  244. void jump_if_not_equal(Operand lhs, Operand rhs, Label& label)
  245. {
  246. cmp(lhs, rhs);
  247. // jne label (RIP-relative 32-bit offset)
  248. emit8(0x0f);
  249. emit8(0x85);
  250. emit32(0xdeadbeef);
  251. label.offset_in_instruction_stream = m_output.size();
  252. }
  253. void bitwise_and(Operand dst, Operand src)
  254. {
  255. // and dst,src
  256. if (dst.type == Operand::Type::Reg && src.type == Operand::Type::Reg) {
  257. emit8(0x48
  258. | ((to_underlying(dst.reg) >= 8) ? 1 << 2 : 0)
  259. | ((to_underlying(src.reg) >= 8) ? 1 << 0 : 0));
  260. emit8(0x21);
  261. emit8(0xc0 | (encode_reg(dst.reg) << 3) | encode_reg(src.reg));
  262. } else if (dst.type == Operand::Type::Reg && src.type == Operand::Type::Imm32) {
  263. emit8(0x48 | ((to_underlying(dst.reg) >= 8) ? 1 << 0 : 0));
  264. emit8(0x81);
  265. emit8(0xe0 | encode_reg(dst.reg));
  266. emit32(src.offset_or_immediate);
  267. } else {
  268. VERIFY_NOT_REACHED();
  269. }
  270. }
  271. void enter()
  272. {
  273. push(Operand::Register(Reg::RBP));
  274. mov(Operand::Register(Reg::RBP), Operand::Register(Reg::RSP));
  275. sub(Operand::Register(Reg::RSP), Operand::Imm8(8));
  276. }
  277. void exit()
  278. {
  279. // leave
  280. emit8(0xc9);
  281. // ret
  282. emit8(0xc3);
  283. }
  284. void push(Operand op)
  285. {
  286. if (op.type == Operand::Type::Reg) {
  287. if (to_underlying(op.reg) >= 8)
  288. emit8(0x49);
  289. emit8(0x50 | encode_reg(op.reg));
  290. } else if (op.type == Operand::Type::Imm32) {
  291. emit8(0x68);
  292. emit32(op.offset_or_immediate);
  293. } else {
  294. VERIFY_NOT_REACHED();
  295. }
  296. }
  297. void pop(Operand op)
  298. {
  299. if (op.type == Operand::Type::Reg) {
  300. if (to_underlying(op.reg) >= 8)
  301. emit8(0x49);
  302. emit8(0x58 | encode_reg(op.reg));
  303. } else {
  304. VERIFY_NOT_REACHED();
  305. }
  306. }
  307. void add(Operand dst, Operand src)
  308. {
  309. if (dst.type == Operand::Type::Reg && src.type == Operand::Type::Reg) {
  310. emit8(0x48
  311. | ((to_underlying(dst.reg) >= 8) ? 1 << 2 : 0)
  312. | ((to_underlying(src.reg) >= 8) ? 1 << 0 : 0));
  313. emit8(0x01);
  314. emit8(0xc0 | (encode_reg(dst.reg) << 3) | encode_reg(src.reg));
  315. } else if (dst.type == Operand::Type::Reg && src.type == Operand::Type::Imm32) {
  316. emit8(0x48 | ((to_underlying(dst.reg) >= 8) ? 1 << 0 : 0));
  317. emit8(0x81);
  318. emit8(0xc0 | encode_reg(dst.reg));
  319. emit32(src.offset_or_immediate);
  320. } else if (dst.type == Operand::Type::Reg && src.type == Operand::Type::Imm8) {
  321. emit8(0x48 | ((to_underlying(dst.reg) >= 8) ? 1 << 0 : 0));
  322. emit8(0x83);
  323. emit8(0xc0 | encode_reg(dst.reg));
  324. emit8(src.offset_or_immediate);
  325. } else {
  326. VERIFY_NOT_REACHED();
  327. }
  328. }
  329. void sub(Operand dst, Operand src)
  330. {
  331. if (dst.type == Operand::Type::Reg && src.type == Operand::Type::Reg) {
  332. emit8(0x48
  333. | ((to_underlying(dst.reg) >= 8) ? 1 << 2 : 0)
  334. | ((to_underlying(src.reg) >= 8) ? 1 << 0 : 0));
  335. emit8(0x29);
  336. emit8(0xc0 | (encode_reg(dst.reg) << 3) | encode_reg(src.reg));
  337. } else if (dst.type == Operand::Type::Reg && src.type == Operand::Type::Imm32) {
  338. emit8(0x48 | ((to_underlying(dst.reg) >= 8) ? 1 << 0 : 0));
  339. emit8(0x81);
  340. emit8(0xe8 | encode_reg(dst.reg));
  341. emit32(src.offset_or_immediate);
  342. } else if (dst.type == Operand::Type::Reg && src.type == Operand::Type::Imm8) {
  343. emit8(0x48 | ((to_underlying(dst.reg) >= 8) ? 1 << 0 : 0));
  344. emit8(0x83);
  345. emit8(0xe8 | encode_reg(dst.reg));
  346. emit8(src.offset_or_immediate);
  347. } else {
  348. VERIFY_NOT_REACHED();
  349. }
  350. }
  351. void native_call(void* callee)
  352. {
  353. // push caller-saved registers on the stack
  354. // (callee-saved registers: RBX, RSP, RBP, and R12–R15)
  355. push(Operand::Register(Reg::RCX));
  356. push(Operand::Register(Reg::RDX));
  357. push(Operand::Register(Reg::RSI));
  358. push(Operand::Register(Reg::RDI));
  359. push(Operand::Register(Reg::R8));
  360. push(Operand::Register(Reg::R9));
  361. push(Operand::Register(Reg::R10));
  362. push(Operand::Register(Reg::R11));
  363. // align the stack to 16-byte boundary
  364. sub(Operand::Register(Reg::RSP), Operand::Imm8(8));
  365. // load callee into RAX
  366. mov(Operand::Register(Reg::RAX), Operand::Imm64(bit_cast<u64>(callee)));
  367. // call RAX
  368. emit8(0xff);
  369. emit8(0xd0);
  370. // adjust stack pointer
  371. add(Operand::Register(Reg::RSP), Operand::Imm8(8));
  372. // restore caller-saved registers from the stack
  373. pop(Operand::Register(Reg::R11));
  374. pop(Operand::Register(Reg::R10));
  375. pop(Operand::Register(Reg::R9));
  376. pop(Operand::Register(Reg::R8));
  377. pop(Operand::Register(Reg::RDI));
  378. pop(Operand::Register(Reg::RSI));
  379. pop(Operand::Register(Reg::RDX));
  380. pop(Operand::Register(Reg::RCX));
  381. }
  382. };
  383. }