generate_jit.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576
  1. #!/usr/bin/env node
  2. "use strict";
  3. const fs = require("fs");
  4. const path = require("path");
  5. const x86_table = require("./x86_table");
  6. const rust_ast = require("./rust_ast");
  7. const { hex, mkdirpSync, get_switch_value, get_switch_exist, finalize_table_rust } = require("./util");
  8. const OUT_DIR = path.join(__dirname, "..", "src/rust/gen/");
  9. mkdirpSync(OUT_DIR);
  10. const table_arg = get_switch_value("--table");
  11. const gen_all = get_switch_exist("--all");
  12. const to_generate = {
  13. jit: gen_all || table_arg === "jit",
  14. jit0f_16: gen_all || table_arg === "jit0f_16",
  15. jit0f_32: gen_all || table_arg === "jit0f_32",
  16. };
  17. console.assert(
  18. Object.keys(to_generate).some(k => to_generate[k]),
  19. "Pass --table [jit|jit0f_16|jit0f_32] or --all to pick which tables to generate"
  20. );
  21. gen_table();
  22. function gen_read_imm_call(op, size_variant)
  23. {
  24. let size = (op.os || op.opcode % 2 === 1) ? size_variant : 8;
  25. if(op.imm8 || op.imm8s || op.imm16 || op.imm1632 || op.imm32 || op.immaddr)
  26. {
  27. if(op.imm8)
  28. {
  29. return "ctx.cpu.read_imm8()";
  30. }
  31. else if(op.imm8s)
  32. {
  33. return "ctx.cpu.read_imm8s()";
  34. }
  35. else
  36. {
  37. if(op.immaddr)
  38. {
  39. // immaddr: depends on address size
  40. return "ctx.cpu.read_moffs()";
  41. }
  42. else
  43. {
  44. console.assert(op.imm1632 || op.imm16 || op.imm32);
  45. if(op.imm1632 && size === 16 || op.imm16)
  46. {
  47. return "ctx.cpu.read_imm16()";
  48. }
  49. else
  50. {
  51. console.assert(op.imm1632 && size === 32 || op.imm32);
  52. return "ctx.cpu.read_imm32()";
  53. }
  54. }
  55. }
  56. }
  57. else
  58. {
  59. return undefined;
  60. }
  61. }
  62. function gen_call(name, args)
  63. {
  64. args = args || [];
  65. return `${name}(${args.join(", ")});`;
  66. }
  67. function gen_codegen_call(name, args)
  68. {
  69. const IMM_VAR_NAME = "imm";
  70. let imm_read;
  71. args = (args || []).map(arg => {
  72. if(arg.includes("read_imm"))
  73. {
  74. imm_read = arg;
  75. return IMM_VAR_NAME;
  76. }
  77. else
  78. {
  79. return arg;
  80. }
  81. });
  82. const args_count = args.length;
  83. args = [].concat(["ctx", `"${name}"`], args);
  84. return [].concat(
  85. imm_read ? `let ${IMM_VAR_NAME} = ${imm_read};` : [],
  86. gen_call(`::codegen::gen_fn${args_count}_const`, args)
  87. );
  88. }
  89. /*
  90. * Current naming scheme:
  91. * instr(16|32|)_((66|F2|F3)?0F)?[0-9a-f]{2}(_[0-7])?(_mem|_reg|)
  92. */
  93. function make_instruction_name(encoding, size)
  94. {
  95. const suffix = encoding.os ? String(size) : "";
  96. const opcode_hex = hex(encoding.opcode & 0xFF, 2);
  97. const prefix_0f = (encoding.opcode & 0xFF00) === 0x0F00 ? "0F" : "";
  98. const prefix = (encoding.opcode & 0xFF0000) === 0 ? "" : hex(encoding.opcode >> 16 & 0xFF, 2);
  99. const fixed_g_suffix = encoding.fixed_g === undefined ? "" : `_${encoding.fixed_g}`;
  100. return `instr${suffix}_${prefix}${prefix_0f}${opcode_hex}${fixed_g_suffix}`;
  101. }
  102. function gen_instruction_body(encodings, size)
  103. {
  104. const encoding = encodings[0];
  105. let has_66 = [];
  106. let has_F2 = [];
  107. let has_F3 = [];
  108. let no_prefix = [];
  109. for(let e of encodings)
  110. {
  111. if((e.opcode >>> 16) === 0x66) has_66.push(e);
  112. else if((e.opcode >>> 16) === 0xF2) has_F2.push(e);
  113. else if((e.opcode >>> 16) === 0xF3) has_F3.push(e);
  114. else no_prefix.push(e);
  115. }
  116. if(has_66.length || has_F2.length || has_F3.length)
  117. {
  118. console.assert((encoding.opcode & 0xFF00) === 0x0F00);
  119. }
  120. const code = [];
  121. if(encoding.e)
  122. {
  123. code.push("let modrm_byte = ctx.cpu.read_imm8();");
  124. }
  125. if(has_66.length || has_F2.length || has_F3.length)
  126. {
  127. const if_blocks = [];
  128. if(has_66.length) {
  129. const body = gen_instruction_body_after_prefix(has_66, size);
  130. if_blocks.push({ condition: "ctx.cpu.prefixes & ::prefix::PREFIX_66 != 0", body, });
  131. }
  132. if(has_F2.length) {
  133. const body = gen_instruction_body_after_prefix(has_F2, size);
  134. if_blocks.push({ condition: "ctx.cpu.prefixes & ::prefix::PREFIX_F2 != 0", body, });
  135. }
  136. if(has_F3.length) {
  137. const body = gen_instruction_body_after_prefix(has_F3, size);
  138. if_blocks.push({ condition: "ctx.cpu.prefixes & ::prefix::PREFIX_F3 != 0", body, });
  139. }
  140. const else_block = {
  141. body: gen_instruction_body_after_prefix(no_prefix, size),
  142. };
  143. return [].concat(
  144. code,
  145. {
  146. type: "if-else",
  147. if_blocks,
  148. else_block,
  149. }
  150. );
  151. }
  152. else {
  153. return [].concat(
  154. code,
  155. gen_instruction_body_after_prefix(encodings, size)
  156. );
  157. }
  158. }
  159. function gen_instruction_body_after_prefix(encodings, size)
  160. {
  161. const encoding = encodings[0];
  162. if(encoding.fixed_g !== undefined)
  163. {
  164. console.assert(encoding.e);
  165. // instruction with modrm byte where the middle 3 bits encode the instruction
  166. // group by opcode without prefix plus middle bits of modrm byte
  167. let cases = encodings.reduce((cases_by_opcode, case_) => {
  168. console.assert(typeof case_.fixed_g === "number");
  169. cases_by_opcode[case_.opcode & 0xFFFF | case_.fixed_g << 16] = case_;
  170. return cases_by_opcode;
  171. }, Object.create(null));
  172. cases = Object.values(cases).sort((e1, e2) => e1.fixed_g - e2.fixed_g);
  173. return [
  174. {
  175. type: "switch",
  176. condition: "modrm_byte >> 3 & 7",
  177. cases: cases.map(case_ => {
  178. const fixed_g = case_.fixed_g;
  179. const body = gen_instruction_body_after_fixed_g(case_, size);
  180. return {
  181. conditions: [fixed_g],
  182. body,
  183. };
  184. }),
  185. default_case: {
  186. body: [].concat(
  187. gen_call(`::codegen::gen_fn0_const`, ["ctx", '"trigger_ud"'])
  188. ),
  189. }
  190. },
  191. ];
  192. }
  193. else {
  194. console.assert(encodings.length === 1);
  195. return gen_instruction_body_after_fixed_g(encodings[0], size);
  196. }
  197. }
  198. function gen_instruction_body_after_fixed_g(encoding, size)
  199. {
  200. const instruction_postfix = [];
  201. if(encoding.block_boundary)
  202. {
  203. instruction_postfix.push("*instr_flags |= ::jit::JIT_INSTR_BLOCK_BOUNDARY_FLAG;");
  204. }
  205. const APPEND_NONFAULTING_FLAG = "*instr_flags |= ::jit::JIT_INSTR_NONFAULTING_FLAG;";
  206. const imm_read = gen_read_imm_call(encoding, size);
  207. const imm_read_bindings = [];
  208. if(imm_read)
  209. {
  210. imm_read_bindings.push(`let imm = ${imm_read} as u32;`);
  211. }
  212. const instruction_name = make_instruction_name(encoding, size);
  213. if(encoding.e)
  214. {
  215. const reg_postfix = encoding.nonfaulting ? [APPEND_NONFAULTING_FLAG] : [];
  216. const mem_postfix = encoding.memory_nonfaulting ? [APPEND_NONFAULTING_FLAG] : [];
  217. if(encoding.ignore_mod)
  218. {
  219. console.assert(!imm_read, "Unexpected instruction (ignore mod with immediate value)");
  220. // Has modrm byte, but the 2 mod bits are ignored and both
  221. // operands are always registers (0f20-0f24)
  222. const args = ["ctx", `"${instruction_name}"`, "(modrm_byte & 7) as u32", "(modrm_byte >> 3 & 7) as u32"];
  223. return [].concat(
  224. gen_call(`::codegen::gen_fn${args.length - 2}_const`, args),
  225. reg_postfix,
  226. instruction_postfix
  227. );
  228. }
  229. else if(encoding.custom)
  230. {
  231. const mem_args = ["ctx", "modrm_byte"];
  232. const reg_args = ["ctx", "(modrm_byte & 7) as u32"];
  233. if(encoding.fixed_g === undefined)
  234. {
  235. mem_args.push("(modrm_byte >> 3 & 7) as u32");
  236. reg_args.push("(modrm_byte >> 3 & 7) as u32");
  237. }
  238. if(imm_read)
  239. {
  240. mem_args.push("imm");
  241. reg_args.push("imm");
  242. }
  243. return [].concat(
  244. imm_read_bindings,
  245. {
  246. type: "if-else",
  247. if_blocks: [{
  248. condition: "modrm_byte < 0xC0",
  249. body: [].concat(
  250. gen_call(`::jit_instructions::${instruction_name}_mem_jit`, mem_args),
  251. mem_postfix
  252. ),
  253. }],
  254. else_block: {
  255. body: [].concat(
  256. gen_call(`::jit_instructions::${instruction_name}_reg_jit`, reg_args),
  257. reg_postfix
  258. ),
  259. },
  260. }
  261. );
  262. }
  263. else
  264. {
  265. const mem_args = ["ctx", `"${instruction_name}_mem"`];
  266. const reg_args = ["ctx", `"${instruction_name}_reg"`, "(modrm_byte & 7) as u32"];
  267. if(encoding.fixed_g === undefined)
  268. {
  269. mem_args.push("(modrm_byte >> 3 & 7) as u32");
  270. reg_args.push("(modrm_byte >> 3 & 7) as u32");
  271. }
  272. if(imm_read)
  273. {
  274. mem_args.push("imm");
  275. reg_args.push("imm");
  276. }
  277. return [].concat(
  278. {
  279. type: "if-else",
  280. if_blocks: [{
  281. condition: "modrm_byte < 0xC0",
  282. body: [].concat(
  283. gen_call(`::codegen::gen_modrm_resolve`, ["ctx", "modrm_byte"]),
  284. imm_read_bindings,
  285. gen_call(`::codegen::gen_modrm_fn${mem_args.length - 2}`, mem_args),
  286. mem_postfix
  287. ),
  288. }],
  289. else_block: {
  290. body: [].concat(
  291. imm_read_bindings,
  292. gen_call(`::codegen::gen_fn${reg_args.length - 2}_const`, reg_args),
  293. reg_postfix
  294. ),
  295. },
  296. },
  297. instruction_postfix
  298. );
  299. }
  300. }
  301. else if(encoding.prefix || encoding.custom)
  302. {
  303. // custom, but not modrm
  304. if(encoding.prefix)
  305. {
  306. console.assert(!encoding.nonfaulting, "Prefix instructions cannot be marked as nonfaulting.");
  307. }
  308. if(encoding.nonfaulting)
  309. {
  310. instruction_postfix.push(APPEND_NONFAULTING_FLAG);
  311. }
  312. const args = ["ctx"];
  313. if(imm_read)
  314. {
  315. args.push("imm");
  316. }
  317. if(encoding.prefix)
  318. {
  319. args.push("instr_flags");
  320. }
  321. return [].concat(
  322. imm_read_bindings,
  323. gen_call(`::jit_instructions::${instruction_name}_jit`, args),
  324. instruction_postfix
  325. );
  326. }
  327. else
  328. {
  329. // instruction without modrm byte or prefix
  330. if(encoding.nonfaulting)
  331. {
  332. instruction_postfix.push(APPEND_NONFAULTING_FLAG);
  333. }
  334. const args = ["ctx", `"${instruction_name}"`];
  335. if(imm_read)
  336. {
  337. args.push("imm");
  338. }
  339. if(encoding.extra_imm16)
  340. {
  341. console.assert(imm_read);
  342. imm_read_bindings.push(`let imm2 = ctx.cpu.read_imm16() as u32;`);
  343. args.push("imm2");
  344. }
  345. else if(encoding.extra_imm8)
  346. {
  347. console.assert(imm_read);
  348. imm_read_bindings.push(`let imm2 = ctx.cpu.read_imm8() as u32;`);
  349. args.push("imm2");
  350. }
  351. return [].concat(
  352. imm_read_bindings,
  353. gen_call(`::codegen::gen_fn${args.length - 2}_const`, args),
  354. instruction_postfix
  355. );
  356. }
  357. }
  358. function gen_table()
  359. {
  360. let by_opcode = Object.create(null);
  361. let by_opcode0f = Object.create(null);
  362. for(let o of x86_table)
  363. {
  364. let opcode = o.opcode;
  365. if(opcode >= 0x100)
  366. {
  367. if((opcode & 0xFF00) === 0x0F00)
  368. {
  369. opcode &= 0xFF;
  370. by_opcode0f[opcode] = by_opcode0f[opcode] || [];
  371. by_opcode0f[opcode].push(o);
  372. }
  373. }
  374. else
  375. {
  376. by_opcode[opcode] = by_opcode[opcode] || [];
  377. by_opcode[opcode].push(o);
  378. }
  379. }
  380. let cases = [];
  381. for(let opcode = 0; opcode < 0x100; opcode++)
  382. {
  383. let encoding = by_opcode[opcode];
  384. console.assert(encoding && encoding.length);
  385. let opcode_hex = hex(opcode, 2);
  386. let opcode_high_hex = hex(opcode | 0x100, 2);
  387. if(encoding[0].os)
  388. {
  389. cases.push({
  390. conditions: [`0x${opcode_hex}`],
  391. body: gen_instruction_body(encoding, 16),
  392. });
  393. cases.push({
  394. conditions: [`0x${opcode_high_hex}`],
  395. body: gen_instruction_body(encoding, 32),
  396. });
  397. }
  398. else
  399. {
  400. cases.push({
  401. conditions: [`0x${opcode_hex}`, `0x${opcode_high_hex}`],
  402. body: gen_instruction_body(encoding, undefined),
  403. });
  404. }
  405. }
  406. const table = {
  407. type: "switch",
  408. condition: "opcode",
  409. cases,
  410. default_case: {
  411. body: ["assert!(false);"]
  412. },
  413. };
  414. if(to_generate.jit)
  415. {
  416. const code = [
  417. "#[cfg_attr(rustfmt, rustfmt_skip)]",
  418. "pub fn jit(opcode: u32, ctx: &mut ::jit::JitContext, instr_flags: &mut u32) {",
  419. table,
  420. "}",
  421. ];
  422. finalize_table_rust(
  423. OUT_DIR,
  424. "jit.rs",
  425. rust_ast.print_syntax_tree([].concat(code)).join("\n") + "\n"
  426. );
  427. }
  428. const cases0f_16 = [];
  429. const cases0f_32 = [];
  430. for(let opcode = 0; opcode < 0x100; opcode++)
  431. {
  432. let encoding = by_opcode0f[opcode];
  433. console.assert(encoding && encoding.length);
  434. let opcode_hex = hex(opcode, 2);
  435. if(encoding[0].os)
  436. {
  437. cases0f_16.push({
  438. conditions: [`0x${opcode_hex}`],
  439. body: gen_instruction_body(encoding, 16),
  440. });
  441. cases0f_32.push({
  442. conditions: [`0x${opcode_hex}`],
  443. body: gen_instruction_body(encoding, 32),
  444. });
  445. }
  446. else
  447. {
  448. let block = {
  449. conditions: [`0x${opcode_hex}`],
  450. body: gen_instruction_body(encoding, undefined),
  451. };
  452. cases0f_16.push(block);
  453. cases0f_32.push(block);
  454. }
  455. }
  456. const table0f_16 = {
  457. type: "switch",
  458. condition: "opcode",
  459. cases: cases0f_16,
  460. default_case: {
  461. body: ["assert!(false);"]
  462. },
  463. };
  464. const table0f_32 = {
  465. type: "switch",
  466. condition: "opcode",
  467. cases: cases0f_32,
  468. default_case: {
  469. body: ["assert!(false);"]
  470. },
  471. };
  472. if(to_generate.jit0f_16)
  473. {
  474. const code = [
  475. "#[cfg_attr(rustfmt, rustfmt_skip)]",
  476. "pub fn jit(opcode: u8, ctx: &mut ::jit::JitContext, instr_flags: &mut u32) {",
  477. table0f_16,
  478. "}",
  479. ];
  480. finalize_table_rust(
  481. OUT_DIR,
  482. "jit0f_16.rs",
  483. rust_ast.print_syntax_tree([].concat(code)).join("\n") + "\n"
  484. );
  485. }
  486. if(to_generate.jit0f_32)
  487. {
  488. const code = [
  489. "#[cfg_attr(rustfmt, rustfmt_skip)]",
  490. "pub fn jit(opcode: u8, ctx: &mut ::jit::JitContext, instr_flags: &mut u32) {",
  491. table0f_32,
  492. "}",
  493. ];
  494. finalize_table_rust(
  495. OUT_DIR,
  496. "jit0f_32.rs",
  497. rust_ast.print_syntax_tree([].concat(code)).join("\n") + "\n"
  498. );
  499. }
  500. }