4
const assert = require("assert").strict;
5
const fs = require("fs");
6
const path = require("path");
7
const x86_table = require("./x86_table");
8
const rust_ast = require("./rust_ast");
9
const { hex, mkdirpSync, get_switch_value, get_switch_exist, finalize_table_rust } = require("./util");
11
const OUT_DIR = path.join(__dirname, "..", "src/rust/gen/");
15
const table_arg = get_switch_value("--table");
16
const gen_all = get_switch_exist("--all");
18
jit: gen_all || table_arg === "jit",
19
jit0f: gen_all || table_arg === "jit0f",
23
Object.keys(to_generate).some(k => to_generate[k]),
24
"Pass --table [jit|jit0f] or --all to pick which tables to generate"
29
function gen_read_imm_call(op, size_variant)
31
let size = (op.os || op.opcode % 2 === 1) ? size_variant : 8;
33
if(op.imm8 || op.imm8s || op.imm16 || op.imm1632 || op.imm32 || op.immaddr)
37
return "ctx.cpu.read_imm8()";
41
return "ctx.cpu.read_imm8s()";
48
return "ctx.cpu.read_moffs()";
52
assert(op.imm1632 || op.imm16 || op.imm32);
54
if(op.imm1632 && size === 16 || op.imm16)
56
return "ctx.cpu.read_imm16()";
60
assert(op.imm1632 && size === 32 || op.imm32);
61
return "ctx.cpu.read_imm32()";
72
function gen_call(name, args)
75
return `${name}(${args.join(", ")});`;
82
function make_instruction_name(encoding, size)
84
const suffix = encoding.os ? String(size) : "";
85
const opcode_hex = hex(encoding.opcode & 0xFF, 2);
86
const first_prefix = (encoding.opcode & 0xFF00) === 0 ? "" : hex(encoding.opcode >> 8 & 0xFF, 2);
87
const second_prefix = (encoding.opcode & 0xFF0000) === 0 ? "" : hex(encoding.opcode >> 16 & 0xFF, 2);
88
const fixed_g_suffix = encoding.fixed_g === undefined ? "" : `_${encoding.fixed_g}`;
90
assert(first_prefix === "" || first_prefix === "0F" || first_prefix === "F2" || first_prefix === "F3");
91
assert(second_prefix === "" || second_prefix === "66" || second_prefix === "F2" || second_prefix === "F3");
93
return `instr${suffix}_${second_prefix}${first_prefix}${opcode_hex}${fixed_g_suffix}`;
96
function gen_instruction_body(encodings, size)
98
const encoding = encodings[0];
105
for(let e of encodings)
107
if((e.opcode >>> 16) === 0x66) has_66.push(e);
108
else if((e.opcode >>> 8 & 0xFF) === 0xF2 || (e.opcode >>> 16) === 0xF2) has_F2.push(e);
109
else if((e.opcode >>> 8 & 0xFF) === 0xF3 || (e.opcode >>> 16) === 0xF3) has_F3.push(e);
110
else no_prefix.push(e);
113
if(has_F2.length || has_F3.length)
115
assert((encoding.opcode & 0xFF0000) === 0 || (encoding.opcode & 0xFF00) === 0x0F00);
120
assert((encoding.opcode & 0xFF00) === 0x0F00);
127
code.push("let modrm_byte = ctx.cpu.read_imm8();");
130
if(has_66.length || has_F2.length || has_F3.length)
132
const if_blocks = [];
135
const body = gen_instruction_body_after_prefix(has_66, size);
136
if_blocks.push({ condition: "ctx.cpu.prefixes & ::prefix::PREFIX_66 != 0", body, });
139
const body = gen_instruction_body_after_prefix(has_F2, size);
140
if_blocks.push({ condition: "ctx.cpu.prefixes & ::prefix::PREFIX_F2 != 0", body, });
143
const body = gen_instruction_body_after_prefix(has_F3, size);
144
if_blocks.push({ condition: "ctx.cpu.prefixes & ::prefix::PREFIX_F3 != 0", body, });
148
body: gen_instruction_body_after_prefix(no_prefix, size),
163
gen_instruction_body_after_prefix(encodings, size)
168
function gen_instruction_body_after_prefix(encodings, size)
170
const encoding = encodings[0];
172
if(encoding.fixed_g !== undefined)
179
let cases = encodings.reduce((cases_by_opcode, case_) => {
180
assert(typeof case_.fixed_g === "number");
181
cases_by_opcode[case_.opcode & 0xFFFF | case_.fixed_g << 16] = case_;
182
return cases_by_opcode;
183
}, Object.create(null));
184
cases = Object.values(cases).sort((e1, e2) => e1.fixed_g - e2.fixed_g);
189
condition: "modrm_byte >> 3 & 7",
190
cases: cases.map(case_ => {
191
const fixed_g = case_.fixed_g;
192
const body = gen_instruction_body_after_fixed_g(case_, size);
195
conditions: [fixed_g],
202
gen_call(`::codegen::gen_trigger_ud`, ["ctx"]),
203
"*instr_flags |= ::jit::JIT_INSTR_BLOCK_BOUNDARY_FLAG;"
210
assert(encodings.length === 1);
211
return gen_instruction_body_after_fixed_g(encodings[0], size);
215
function gen_instruction_body_after_fixed_g(encoding, size)
217
const instruction_postfix = [];
219
if(encoding.block_boundary || (!encoding.custom && encoding.e))
221
instruction_postfix.push("*instr_flags |= ::jit::JIT_INSTR_BLOCK_BOUNDARY_FLAG;");
224
const instruction_prefix = [];
226
if(encoding.task_switch_test || encoding.sse)
228
instruction_prefix.push(
229
gen_call(encoding.sse ? "::codegen::gen_task_switch_test_mmx" : "::codegen::gen_task_switch_test", ["ctx"])
233
const imm_read = gen_read_imm_call(encoding, size);
234
const imm_read_bindings = [];
237
imm_read_bindings.push(`let imm = ${imm_read} as u32;`);
240
const instruction_name = make_instruction_name(encoding, size);
249
instruction_prefix.push(
250
gen_call("::codegen::gen_move_registers_from_locals_to_memory", ["ctx"])
252
instruction_postfix.push(
253
gen_call("::codegen::gen_move_registers_from_memory_to_locals", ["ctx"])
260
const reg_postfix = [];
261
const mem_postfix = [];
266
"*instr_flags |= ::jit::JIT_INSTR_BLOCK_BOUNDARY_FLAG;"
273
"*instr_flags |= ::jit::JIT_INSTR_BLOCK_BOUNDARY_FLAG;"
277
if(encoding.ignore_mod)
279
assert(!imm_read, "Unexpected instruction (ignore mod with immediate value)");
283
const args = ["ctx.builder", `"${instruction_name}"`, "(modrm_byte & 7) as u32", "(modrm_byte >> 3 & 7) as u32"];
287
gen_call(`::codegen::gen_fn${args.length - 2}_const`, args),
292
else if(encoding.custom)
294
const mem_args = ["ctx", "addr"];
295
const reg_args = ["ctx", "(modrm_byte & 7) as u32"];
297
if(encoding.fixed_g === undefined)
299
mem_args.push("(modrm_byte >> 3 & 7) as u32");
300
reg_args.push("(modrm_byte >> 3 & 7) as u32");
305
mem_args.push("imm");
306
reg_args.push("imm");
314
condition: "modrm_byte < 0xC0",
316
"let addr = ::modrm::decode(ctx.cpu, modrm_byte);",
318
gen_call(`::jit_instructions::${instruction_name}_mem_jit`, mem_args),
325
gen_call(`::jit_instructions::${instruction_name}_reg_jit`, reg_args),
335
const mem_args = ["ctx.builder", `"${instruction_name}_mem"`];
336
const reg_args = ["ctx.builder", `"${instruction_name}_reg"`, "(modrm_byte & 7) as u32"];
338
if(encoding.fixed_g === undefined)
340
mem_args.push("(modrm_byte >> 3 & 7) as u32");
341
reg_args.push("(modrm_byte >> 3 & 7) as u32");
346
mem_args.push("imm");
347
reg_args.push("imm");
355
condition: "modrm_byte < 0xC0",
357
"let addr = ::modrm::decode(ctx.cpu, modrm_byte);",
358
gen_call(`::codegen::gen_modrm_resolve`, ["ctx", "addr"]),
360
gen_call(`::codegen::gen_modrm_fn${mem_args.length - 2}`, mem_args),
367
gen_call(`::codegen::gen_fn${reg_args.length - 2}_const`, reg_args),
376
else if(encoding.prefix || encoding.custom)
380
const args = ["ctx"];
389
args.push("instr_flags");
395
gen_call(`::jit_instructions::${instruction_name}_jit`, args),
403
const args = ["ctx.builder", `"${instruction_name}"`];
410
if(encoding.extra_imm16)
413
imm_read_bindings.push(`let imm2 = ctx.cpu.read_imm16() as u32;`);
416
else if(encoding.extra_imm8)
419
imm_read_bindings.push(`let imm2 = ctx.cpu.read_imm8() as u32;`);
426
gen_call(`::codegen::gen_fn${args.length - 2}_const`, args),
434
let by_opcode = Object.create(null);
435
let by_opcode0f = Object.create(null);
437
for(let o of x86_table)
439
let opcode = o.opcode;
441
if((opcode & 0xFF00) === 0x0F00)
444
by_opcode0f[opcode] = by_opcode0f[opcode] || [];
445
by_opcode0f[opcode].push(o);
450
by_opcode[opcode] = by_opcode[opcode] || [];
451
by_opcode[opcode].push(o);
456
for(let opcode = 0; opcode < 0x100; opcode++)
458
let encoding = by_opcode[opcode];
459
assert(encoding && encoding.length);
461
let opcode_hex = hex(opcode, 2);
462
let opcode_high_hex = hex(opcode | 0x100, 2);
467
conditions: [`0x${opcode_hex}`],
468
body: gen_instruction_body(encoding, 16),
471
conditions: [`0x${opcode_high_hex}`],
472
body: gen_instruction_body(encoding, 32),
478
conditions: [`0x${opcode_hex}`, `0x${opcode_high_hex}`],
479
body: gen_instruction_body(encoding, undefined),
488
body: ["assert!(false);"]
495
"#[cfg_attr(rustfmt, rustfmt_skip)]",
496
"pub fn jit(opcode: u32, ctx: &mut ::jit::JitContext, instr_flags: &mut u32) {",
504
rust_ast.print_syntax_tree([].concat(code)).join("\n") + "\n"
509
for(let opcode = 0; opcode < 0x100; opcode++)
511
let encoding = by_opcode0f[opcode];
513
assert(encoding && encoding.length);
515
let opcode_hex = hex(opcode, 2);
516
let opcode_high_hex = hex(opcode | 0x100, 2);
521
conditions: [`0x${opcode_hex}`],
522
body: gen_instruction_body(encoding, 16),
525
conditions: [`0x${opcode_high_hex}`],
526
body: gen_instruction_body(encoding, 32),
532
conditions: [`0x${opcode_hex}`, `0x${opcode_high_hex}`],
533
body: gen_instruction_body(encoding, undefined),
544
body: ["assert!(false);"]
548
if(to_generate.jit0f)
551
"#[cfg_attr(rustfmt, rustfmt_skip)]",
552
"pub fn jit(opcode: u32, ctx: &mut ::jit::JitContext, instr_flags: &mut u32) {",
560
rust_ast.print_syntax_tree([].concat(code)).join("\n") + "\n"