create_tests.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773
  1. #!/usr/bin/env node
  2. "use strict";
  3. // number of tests per instruction
  4. const NUMBER_TESTS = 5;
  5. // arithmetic tests
  6. const NUMBER_ARITH_TESTS = 100;
  7. const MAX_PARALLEL_PROCS = +process.env.MAX_PARALLEL_PROCS || 32;
  8. const FLAGS_IGNORE = 0xFFFF3200;
  9. const CF = 1 << 0;
  10. const PF = 1 << 2;
  11. const AF = 1 << 4;
  12. const ZF = 1 << 6;
  13. const SF = 1 << 7;
  14. const OF = 1 << 11;
  15. const BUILD_DIR = __dirname + "/build/";
  16. const LOG_VERBOSE = false;
  17. const assert = require("assert").strict;
  18. const fs = require("fs");
  19. const fse = require("fs/promises");
  20. const path = require("path");
  21. const encodings = require("../../gen/x86_table.js");
  22. const util = require("util");
  23. const execFile = util.promisify(require("child_process").execFile);
  24. const Rand = require("./rand.js");
  25. const header = fs.readFileSync(path.join(__dirname, "header.inc"));
  26. const footer = fs.readFileSync(path.join(__dirname, "footer.inc"));
  27. main();
  28. async function main()
  29. {
  30. try
  31. {
  32. fs.mkdirSync(BUILD_DIR);
  33. }
  34. catch(e)
  35. {
  36. if(e.code !== "EEXIST")
  37. {
  38. throw e;
  39. }
  40. }
  41. const tests = create_tests().reverse();
  42. const workers = [];
  43. for(let i = 0; i < MAX_PARALLEL_PROCS; i++)
  44. {
  45. workers.push(worker(make_test, tests));
  46. }
  47. await Promise.all(workers);
  48. }
  49. async function worker(f, work)
  50. {
  51. while(work.length)
  52. {
  53. await f(work.pop());
  54. }
  55. }
  56. async function make_test(test)
  57. {
  58. LOG_VERBOSE && console.log("Start", test.name || test.file);
  59. let asm_file;
  60. let img_file;
  61. let tmp_file;
  62. assert((test.asm && test.name) || test.file);
  63. if(test.asm)
  64. {
  65. asm_file = BUILD_DIR + test.name + ".asm";
  66. img_file = BUILD_DIR + test.name + ".img";
  67. tmp_file = "/tmp/" + test.name + ".o";
  68. let old_code = undefined;
  69. try
  70. {
  71. old_code = await fse.readFile(asm_file, { encoding: "ascii" });
  72. }
  73. catch(e)
  74. {
  75. }
  76. if(old_code === test.asm)
  77. {
  78. LOG_VERBOSE && console.log("Skip", test.name || test.file);
  79. return;
  80. }
  81. await fse.writeFile(asm_file, test.asm);
  82. }
  83. else
  84. {
  85. asm_file = path.join(__dirname, test.file);
  86. img_file = BUILD_DIR + test.file.replace(/\.asm$/, ".img");
  87. tmp_file = "/tmp/" + test.file + ".o";
  88. try
  89. {
  90. if((await fse.stat(asm_file)).mtime < (await fse.stat(img_file)).mtime)
  91. {
  92. return;
  93. }
  94. }
  95. catch(e)
  96. {
  97. if(e.code !== "ENOENT") throw e;
  98. }
  99. }
  100. const options = {
  101. cwd: __dirname,
  102. };
  103. LOG_VERBOSE && console.log("nasm", ["-w+error", "-felf32", "-o", tmp_file, asm_file].join(" "));
  104. await execFile("nasm", ["-w+error", "-felf32", "-o", tmp_file, asm_file], options);
  105. LOG_VERBOSE && console.log("ld", ["-g", tmp_file, "-m", "elf_i386", "--section-start=.bss=0x100000", "--section-start=.text=0x80000", "--section-start=.multiboot=0x20000", "-o", img_file].join(" "));
  106. await execFile("ld", ["-g", tmp_file, "-m", "elf_i386", "--section-start=.bss=0x100000", "--section-start=.text=0x80000", "--section-start=.multiboot=0x20000", "-o", img_file], options);
  107. await fse.unlink(tmp_file);
  108. console.log(test.name || test.file);
  109. }
  110. function create_tests()
  111. {
  112. const tests = [];
  113. const asm_files = fs.readdirSync(__dirname).filter(f => f.endsWith(".asm"));
  114. tests.push.apply(tests, asm_files.map(file => ({ file })));
  115. for(const op of encodings)
  116. {
  117. const configurations = [
  118. { mem: 0, size: 16, },
  119. { mem: 0, size: 32, },
  120. { mem: 1, size: 16, },
  121. { mem: 1, size: 32, },
  122. ];
  123. let i = 0;
  124. for(const config of configurations)
  125. {
  126. for(let nth_test = 0; nth_test < NUMBER_TESTS; nth_test++)
  127. {
  128. if(nth_test > 0 && op.opcode === 0x8D)
  129. {
  130. // is already tested exhaustively in first run
  131. continue;
  132. }
  133. for(const asm of create_instruction_test(op, config, nth_test))
  134. {
  135. tests.push({
  136. name: "gen_" + format_opcode(op.opcode) + "_" + (op.fixed_g || 0) + "_" + i,
  137. asm,
  138. });
  139. i++;
  140. }
  141. }
  142. }
  143. }
  144. for(let i = 0; i < NUMBER_ARITH_TESTS; i++)
  145. {
  146. tests.push(create_arith_test(i));
  147. }
  148. return tests;
  149. }
  150. function format_opcode(n)
  151. {
  152. let x = n.toString(16);
  153. return (x.length === 1 || x.length === 3) ? "0" + x : x;
  154. }
  155. function create_nasm_modrm_combinations_16()
  156. {
  157. let result = [];
  158. for(let modrm = 0; modrm < 0xC0; modrm++)
  159. {
  160. let mod = modrm >> 6;
  161. let rm = modrm & 7;
  162. let has_imm8 = mod === 1;
  163. let has_imm16 = mod === 2 || rm === 6 && mod === 0;
  164. assert(!has_imm8 || !has_imm16);
  165. let line = ["db " + modrm];
  166. if(has_imm8) line.push("db 9ah");
  167. if(has_imm16) line.push("dw 9a1fh");
  168. result.push(line);
  169. }
  170. return result;
  171. }
  172. function create_nasm_modrm_combinations_32()
  173. {
  174. let result = [];
  175. let sample_sib_bytes = [0x05, 0x65, 0xAD, 0xCD, 0x20, 0xFF];
  176. let exhaustive_sib_bytes = [];
  177. for(let sib = 0; sib < 0x100; sib++) exhaustive_sib_bytes.push(sib);
  178. for(let modrm = 0; modrm < 0xC0; modrm++)
  179. {
  180. let mod = modrm >> 6;
  181. let reg = modrm >> 3 & 7;
  182. let rm = modrm & 7;
  183. let has_imm8 = mod === 1;
  184. let has_imm32 = mod === 2 || rm === 5 && mod === 0;
  185. let has_sib = rm === 4;
  186. assert(!has_imm8 || !has_imm32);
  187. if(has_sib)
  188. {
  189. // avoid generating an excessive number of tests
  190. let sib_bytes = reg === 0 ? exhaustive_sib_bytes : sample_sib_bytes;
  191. for(let sib of sib_bytes)
  192. {
  193. let line = ["db " + modrm, "db " + sib];
  194. if(has_imm8) line.push("db 9ah");
  195. if(has_imm32 || mod === 0 && (sib & 7) === 5) line.push("dd 9a1fbcdeh");
  196. result.push(line);
  197. }
  198. }
  199. else
  200. {
  201. let line = ["db " + modrm];
  202. if(has_imm8) line.push("db 9ah");
  203. if(has_imm32) line.push("dd 9a1fbcdeh");
  204. result.push(line);
  205. }
  206. }
  207. return result;
  208. }
  209. function rand_reg_but_not_esp(rng)
  210. {
  211. let r = rng.int32() & 7;
  212. return r === 4 ? rand_reg_but_not_esp(rng) : r;
  213. }
  214. function interesting_immediate(rng)
  215. {
  216. if(rng.int32() & 1)
  217. {
  218. return rng.int32();
  219. }
  220. else
  221. {
  222. return rng.int32() << (rng.int32() & 31) >> (rng.int32() & 31);
  223. }
  224. }
  225. function create_instruction_test(op, config, nth_test)
  226. {
  227. if(op.prefix || op.skip)
  228. {
  229. return [];
  230. }
  231. if(config.mem ? op.skip_mem : op.skip_reg)
  232. {
  233. // Not supported by test
  234. return [];
  235. }
  236. if(!op.e)
  237. {
  238. if(config.mem)
  239. {
  240. // doesn't use memory, don't test both
  241. return [];
  242. }
  243. }
  244. if(!op.os)
  245. {
  246. if(config.size === 16)
  247. {
  248. // equivalent to 32-bit version, don't test both
  249. return [];
  250. }
  251. }
  252. const rng = new Rand(1283698341 ^ op.opcode + nth_test * 0x10000);
  253. const size = (op.os || op.opcode % 2 === 1) ? config.size : 8;
  254. const is_modrm = op.e || op.fixed_g !== undefined;
  255. const codes = [];
  256. for(let reg of ["eax", "ecx", "edx", "ebx", "ebp", "esi", "edi"])
  257. {
  258. let rand = rng.int32();
  259. codes.push("mov " + reg + ", " + rand);
  260. }
  261. if(!op.is_fpu) // generate random mmx registers
  262. {
  263. codes.push("sub esp, 8");
  264. for(let i = 0; i < 8; i++)
  265. {
  266. codes.push("mov dword [esp], " + rng.int32());
  267. codes.push("mov dword [esp + 4], " + rng.int32());
  268. codes.push("movq mm" + i + ", [esp]");
  269. }
  270. codes.push("add esp, 8");
  271. }
  272. else // generate random fpu registers
  273. {
  274. codes.push("finit");
  275. codes.push("sub esp, 8");
  276. for(let i = 0; i < 8; i++)
  277. {
  278. codes.push("mov dword [esp], " + rng.int32());
  279. codes.push("mov dword [esp + 4], " + rng.int32());
  280. codes.push("fld qword [esp]");
  281. }
  282. for(let i = 0; i < 4; i++) // half full stack
  283. {
  284. codes.push("fstp qword [esp]");
  285. }
  286. codes.push("add esp, 8");
  287. }
  288. if(true) // generate random xmm registers
  289. {
  290. codes.push("sub esp, 16");
  291. for(let i = 0; i < 8; i++)
  292. {
  293. codes.push("mov dword [esp], " + rng.int32());
  294. codes.push("mov dword [esp + 4], " + rng.int32());
  295. codes.push("mov dword [esp + 8], " + rng.int32());
  296. codes.push("mov dword [esp + 12], " + rng.int32());
  297. codes.push("movdqu xmm" + i + ", [esp]");
  298. }
  299. codes.push("add esp, 16");
  300. }
  301. if(true) // generate random stack memory
  302. {
  303. for(let i = 0; i < 8; i++)
  304. {
  305. codes.push("sub esp, 4");
  306. codes.push("mov dword [esp], " + rng.int32());
  307. }
  308. }
  309. codes.push("push dword " + (rng.int32() & ~(1 << 8 | 1 << 9)));
  310. codes.push("popf");
  311. if(rng.int32() & 1)
  312. {
  313. // generate random flags using arithmetic instruction
  314. // not well-distributed, but can trigger bugs in lazy flag calculation
  315. if(rng.int32() & 1)
  316. {
  317. // rarely sets zero flag, other flags mostly well-distributed
  318. codes.push("add al, ah");
  319. }
  320. else
  321. {
  322. // always sets zero flag
  323. codes.push("sub al, al");
  324. }
  325. }
  326. if(op.is_string)
  327. {
  328. codes.push("mov ecx, 3");
  329. codes.push("mov edi, (102000h-16)");
  330. codes.push("mov esi, (102000h-20)");
  331. }
  332. if(size === 16)
  333. {
  334. codes.push("db 66h ; 16 bit");
  335. }
  336. let opcode = op.opcode;
  337. if([0x0FA5, 0x0FAD].includes(op.opcode) && size === 16)
  338. {
  339. // shld/shrd: immediates larger than opsize are undefined behaviour,
  340. // but it's anded with 31 automatically, so only bit 4 needs to be cleared
  341. codes.push("and cl, ~16");
  342. }
  343. if(opcode === 0x8D)
  344. {
  345. // special case: lea: generate 16-bit addressing and all modrm combinations
  346. assert(is_modrm);
  347. codes.push([].concat(
  348. create_nasm_modrm_combinations_16().map(lines => ["db 67h", "db 8dh"].concat(lines).join("\n")),
  349. create_nasm_modrm_combinations_32().map(lines => ["db 8dh"].concat(lines).join("\n"))
  350. ));
  351. }
  352. else
  353. {
  354. assert(opcode < 0x1000000);
  355. if(opcode >= 0x10000)
  356. {
  357. let c = opcode >> 16;
  358. assert(c === 0x66 || c === 0xF3 || c === 0xF2);
  359. codes.push("db " + c);
  360. opcode &= ~0xFF0000;
  361. }
  362. if(opcode >= 0x100)
  363. {
  364. let c = opcode >> 8;
  365. assert(c === 0x0F || c === 0xF2 || c === 0xF3, "Expected 0F, F2, or F3 prefix, got " + c.toString(16));
  366. codes.push("db " + c);
  367. opcode &= ~0xFF00;
  368. }
  369. codes.push("db " + opcode);
  370. if(is_modrm)
  371. {
  372. let g = rand_reg_but_not_esp(rng);
  373. if(op.fixed_g !== undefined)
  374. {
  375. g = op.fixed_g;
  376. }
  377. if(config.mem)
  378. {
  379. const e = 0x04; // [esp]
  380. const sib = 0x24;
  381. codes.push("db " + (e | g << 3));
  382. codes.push("db " + sib);
  383. }
  384. else
  385. {
  386. const es =
  387. op.is_fpu ? [0, 1, 2, 3, 4, 5, 6, 7] : [
  388. rand_reg_but_not_esp(rng)
  389. ];
  390. const modrm_bytes = es.map(e => "db " + (0xC0 | g << 3 | e));
  391. codes.push(modrm_bytes);
  392. }
  393. }
  394. }
  395. if(op.opcode === 0xC8) // special case: enter
  396. {
  397. codes.push("dw 8h");
  398. codes.push("db 0h");
  399. }
  400. else if(op.imm8 || op.imm8s || op.imm16 || op.imm1632 || op.imm32 || op.immaddr)
  401. {
  402. if(op.imm8 || op.imm8s)
  403. {
  404. if([0x0FA4, 0x0FAC].includes(op.opcode))
  405. {
  406. // shld/shrd: immediates larger than opsize are undefined behaviour
  407. codes.push("db " + (rng.int32() & (size === 16 ? 15 : 31)));
  408. }
  409. else
  410. {
  411. codes.push("db " + (rng.int32() & 0xFF));
  412. }
  413. }
  414. else
  415. {
  416. if(op.immaddr)
  417. {
  418. // immaddr: depends on address size
  419. // generate valid pointer into bss section
  420. codes.push("dd (102000h-16)");
  421. }
  422. else
  423. {
  424. assert(op.imm1632 || op.imm16 || op.imm32);
  425. if(op.imm1632 && size === 16 || op.imm16)
  426. {
  427. codes.push("dw " + (rng.int32() & 0xFFFF));
  428. }
  429. else
  430. {
  431. assert(op.imm1632 && size === 32 || op.imm32);
  432. codes.push("dd " + rng.int32());
  433. }
  434. }
  435. }
  436. }
  437. if(op.mask_flags)
  438. {
  439. codes.push(
  440. "pushf",
  441. "and dword [esp], ~" + (op.mask_flags | FLAGS_IGNORE),
  442. "popf",
  443. "mov dword [esp-4], 0",
  444. );
  445. }
  446. if(op.opcode === 0x06 || op.opcode === 0x0E || op.opcode === 0x16 || op.opcode === 0x1E ||
  447. op.opcode === 0x0FA0 || op.opcode === 0x0FA8)
  448. {
  449. // push sreg: mask result
  450. if(size === 16)
  451. {
  452. codes.push("mov word [esp], 0");
  453. }
  454. else
  455. {
  456. // NOTE: upper word is undefined behaviour (unchanged on Intel, zero on AMD)
  457. codes.push("mov dword [esp], 0");
  458. }
  459. }
  460. return all_combinations(codes).map(c => {
  461. return header + c.join("\n") + "\n" + footer;
  462. });
  463. }
  464. function create_arith_test(i)
  465. {
  466. const rng = new Rand(916237867 ^ i);
  467. const registers_by_size = {
  468. 8: ["al", "ah", "cl", "ch", "dl", "dh", "bl", "bh"],
  469. 16: ["ax", "cx", "dx", "bx", "sp", "bp", "si", "di"],
  470. 32: ["eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi"],
  471. };
  472. const mask_by_size = {
  473. 8: 0xFF,
  474. 16: 0xFFFF,
  475. 32: -1,
  476. };
  477. const word_by_size = {
  478. 8: "byte",
  479. 16: "word",
  480. 32: "dword",
  481. };
  482. const two_operand_instructions = ["add", "sub", "adc", "sbb", "and", "or", "xor", "cmp", "test"];
  483. const one_operand_instructions = [
  484. "inc", "dec", "neg",
  485. "mul", //"idiv", "div", // technically also eax:edx, but are implied by assembler
  486. "imul", // handled specifically below to also generate 2-/3-operand form
  487. ];
  488. const shift_instructions = ["shl", "shr", "sar", "rol", "ror", "rcl", "rcr"];
  489. // TODO: cmpxchg, xadd, bsf, bsr, shrd/shld, popcnt, bt*
  490. const instructions = [two_operand_instructions, one_operand_instructions, shift_instructions].flat();
  491. const conditions = [
  492. // suffix flag
  493. ["o", OF],
  494. ["c", CF],
  495. ["z", ZF],
  496. ["p", PF],
  497. ["s", SF],
  498. ["be", CF | ZF],
  499. ["l", SF | OF],
  500. ["le", SF | OF | ZF],
  501. ];
  502. let c = [];
  503. let address = 0x100000;
  504. for(let reg of registers_by_size[32])
  505. {
  506. if(reg !== "esp")
  507. {
  508. c.push(`mov ${reg}, ${interesting_immediate(rng)}`);
  509. }
  510. }
  511. let undefined_flags = 0;
  512. for(let i = 0; i < 2000; i++)
  513. {
  514. const ins = instructions[rng.uint32() % instructions.length];
  515. const size = [8, 16, 32][rng.uint32() % 3];
  516. const size_word = word_by_size[size];
  517. const dst_is_mem = rng.int32() & 1;
  518. const dst = dst_is_mem ?
  519. `${size_word} [${nasm_hex(address)}]` :
  520. registers_by_size[size][rand_reg_but_not_esp(rng)];
  521. let src_is_mem = false;
  522. if(ins === "imul" && (rng.int32() & 1)) // other encodings handled in one_operand_instructions
  523. {
  524. // dst must be reg, no 8-bit
  525. const size_imul = [16, 32][rng.int32() & 1];
  526. const dst_imul = registers_by_size[size_imul][rand_reg_but_not_esp(rng)];
  527. const src1 = dst_is_mem ?
  528. `${word_by_size[size_imul]} [${nasm_hex(address)}]` :
  529. registers_by_size[size_imul][rand_reg_but_not_esp(rng)];
  530. if(rng.int32() & 1)
  531. {
  532. c.push(`${ins} ${dst_imul}, ${src1}`);
  533. }
  534. else
  535. {
  536. const src2 = nasm_hex(interesting_immediate(rng) & mask_by_size[size_imul]);
  537. c.push(`${ins} ${dst_imul}, ${src1}, ${src2}`);
  538. }
  539. }
  540. else if(one_operand_instructions.includes(ins))
  541. {
  542. c.push(`${ins} ${dst}`);
  543. }
  544. else if(two_operand_instructions.includes(ins))
  545. {
  546. src_is_mem = !dst_is_mem && (rng.int32() & 1);
  547. const src = src_is_mem ?
  548. `${size_word} [${nasm_hex(address)}]` :
  549. (rng.int32() & 1) ?
  550. registers_by_size[size][rand_reg_but_not_esp(rng)] :
  551. nasm_hex(interesting_immediate(rng) & mask_by_size[size]);
  552. c.push(`${ins} ${dst}, ${src}`);
  553. }
  554. else if(shift_instructions.includes(ins))
  555. {
  556. if(rng.int32() & 1)
  557. {
  558. // unknown CL
  559. undefined_flags |= AF | OF;
  560. c.push(`${ins} ${dst}, cl`);
  561. }
  562. else
  563. {
  564. const shift = interesting_immediate(rng) & 0xFF;
  565. // TODO: shift mod {8,9,16,17,32,33} depending on bitsize/rotate/with-carry, shifts can clear undefined_flags if shift is not zero
  566. undefined_flags |= shift === 1 ? AF : AF | OF;
  567. if(rng.int32() & 1)
  568. {
  569. // known CL
  570. c.push(`mov cl, ${nasm_hex(shift)}`);
  571. c.push(`${ins} ${dst}, cl`);
  572. }
  573. else
  574. {
  575. // immediate
  576. c.push(`${ins} ${dst}, ${nasm_hex(shift)}`);
  577. }
  578. }
  579. }
  580. if(dst_is_mem || src_is_mem)
  581. {
  582. if(rng.int32() & 1)
  583. {
  584. address += size / 8;
  585. // initialise next word
  586. c.push(`mov dword [${nasm_hex(address)}], ${nasm_hex(interesting_immediate(rng) & 0xFF)}`);
  587. }
  588. }
  589. if(ins === "imul" || ins === "mul" || ins === "idiv" || ins === "div")
  590. {
  591. undefined_flags = SF | ZF | AF | PF;
  592. }
  593. else if(!shift_instructions.includes(ins))
  594. {
  595. // adc/sbb/inc/dec read CF, but CF is never undefined
  596. undefined_flags = 0;
  597. }
  598. if(rng.int32() & 1)
  599. {
  600. // setcc
  601. const cond = random_pick(conditions.filter(([_, flag]) => 0 === (flag & undefined_flags)).map(([suffix]) => suffix), rng);
  602. assert(cond);
  603. const invert = (rng.int32() & 1) ? "n" : "";
  604. const ins2 = `set${invert}${cond}`;
  605. const dst2 = (rng.int32() & 1) ? `byte [${nasm_hex(address++)}]` : registers_by_size[8][rng.int32() & 7];
  606. c.push(`${ins2} ${dst2}`);
  607. }
  608. else if(rng.int32() & 1)
  609. {
  610. // cmovcc
  611. const cond = random_pick(conditions.filter(([_, flag]) => 0 === (flag & undefined_flags)).map(([suffix]) => suffix), rng);
  612. assert(cond);
  613. const invert = (rng.int32() & 1) ? "n" : "";
  614. const ins2 = `cmov${invert}${cond}`;
  615. const size = (rng.int32() & 1) ? 16 : 32;
  616. const src2 = registers_by_size[size][rng.int32() & 7];
  617. const dst2 = registers_by_size[size][rand_reg_but_not_esp(rng)];
  618. c.push(`${ins2} ${dst2}, ${src2}`);
  619. }
  620. else if(rng.int32() & 1)
  621. {
  622. c.push("pushf");
  623. c.push("and dword [esp], ~" + nasm_hex(FLAGS_IGNORE | undefined_flags));
  624. c.push(`pop ${registers_by_size[32][rand_reg_but_not_esp(rng)]}`);
  625. }
  626. else
  627. {
  628. // intentionally left blank
  629. }
  630. // TODO:
  631. // cmovcc
  632. // other random instructions (mov, etc.)
  633. }
  634. c.push("pushf");
  635. c.push("and dword [esp], ~" + nasm_hex(FLAGS_IGNORE | undefined_flags));
  636. c.push("popf");
  637. assert(address < 0x102000);
  638. const name = `arith_${i}`;
  639. const asm = header + c.join("\n") + "\n" + footer;
  640. return { name, asm };
  641. }
  642. function all_combinations(xs)
  643. {
  644. let result = [xs];
  645. for(let i = 0; i < xs.length; i++)
  646. {
  647. let x = xs[i];
  648. if(x instanceof Array)
  649. {
  650. let new_result = [];
  651. for(let r of result)
  652. {
  653. for(let x_ of x)
  654. {
  655. r = r.slice();
  656. r[i] = x_;
  657. new_result.push(r);
  658. }
  659. }
  660. result = new_result;
  661. }
  662. }
  663. return result;
  664. }
  665. function nasm_hex(x)
  666. {
  667. return `0${(x >>> 0).toString(16).toUpperCase()}h`;
  668. }
  669. function random_pick(xs, rng)
  670. {
  671. return xs[rng.uint32() % xs.length];
  672. }