SandboXP

Форк
0
/
generate_jit.js 
563 строки · 15.9 Кб
1
#!/usr/bin/env node
2
"use strict";
3

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");
10

11
const OUT_DIR = path.join(__dirname, "..", "src/rust/gen/");
12

13
mkdirpSync(OUT_DIR);
14

15
const table_arg = get_switch_value("--table");
16
const gen_all = get_switch_exist("--all");
17
const to_generate = {
18
    jit: gen_all || table_arg === "jit",
19
    jit0f: gen_all || table_arg === "jit0f",
20
};
21

22
assert(
23
    Object.keys(to_generate).some(k => to_generate[k]),
24
    "Pass --table [jit|jit0f] or --all to pick which tables to generate"
25
);
26

27
gen_table();
28

29
function gen_read_imm_call(op, size_variant)
30
{
31
    let size = (op.os || op.opcode % 2 === 1) ? size_variant : 8;
32

33
    if(op.imm8 || op.imm8s || op.imm16 || op.imm1632 || op.imm32 || op.immaddr)
34
    {
35
        if(op.imm8)
36
        {
37
            return "ctx.cpu.read_imm8()";
38
        }
39
        else if(op.imm8s)
40
        {
41
            return "ctx.cpu.read_imm8s()";
42
        }
43
        else
44
        {
45
            if(op.immaddr)
46
            {
47
                // immaddr: depends on address size
48
                return "ctx.cpu.read_moffs()";
49
            }
50
            else
51
            {
52
                assert(op.imm1632 || op.imm16 || op.imm32);
53

54
                if(op.imm1632 && size === 16 || op.imm16)
55
                {
56
                    return "ctx.cpu.read_imm16()";
57
                }
58
                else
59
                {
60
                    assert(op.imm1632 && size === 32 || op.imm32);
61
                    return "ctx.cpu.read_imm32()";
62
                }
63
            }
64
        }
65
    }
66
    else
67
    {
68
        return undefined;
69
    }
70
}
71

72
function gen_call(name, args)
73
{
74
    args = args || [];
75
    return `${name}(${args.join(", ")});`;
76
}
77

78
/*
79
 * Current naming scheme:
80
 * instr(16|32|)_(66|F2|F3)?0F?[0-9a-f]{2}(_[0-7])?(_mem|_reg|)
81
 */
82
function make_instruction_name(encoding, size)
83
{
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}`;
89

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");
92

93
    return `instr${suffix}_${second_prefix}${first_prefix}${opcode_hex}${fixed_g_suffix}`;
94
}
95

96
function gen_instruction_body(encodings, size)
97
{
98
    const encoding = encodings[0];
99

100
    let has_66 = [];
101
    let has_F2 = [];
102
    let has_F3 = [];
103
    let no_prefix = [];
104

105
    for(let e of encodings)
106
    {
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);
111
    }
112

113
    if(has_F2.length || has_F3.length)
114
    {
115
        assert((encoding.opcode & 0xFF0000) === 0 || (encoding.opcode & 0xFF00) === 0x0F00);
116
    }
117

118
    if(has_66.length)
119
    {
120
        assert((encoding.opcode & 0xFF00) === 0x0F00);
121
    }
122

123
    const code = [];
124

125
    if(encoding.e)
126
    {
127
        code.push("let modrm_byte = ctx.cpu.read_imm8();");
128
    }
129

130
    if(has_66.length || has_F2.length || has_F3.length)
131
    {
132
        const if_blocks = [];
133

134
        if(has_66.length) {
135
            const body = gen_instruction_body_after_prefix(has_66, size);
136
            if_blocks.push({ condition: "ctx.cpu.prefixes & ::prefix::PREFIX_66 != 0", body, });
137
        }
138
        if(has_F2.length) {
139
            const body = gen_instruction_body_after_prefix(has_F2, size);
140
            if_blocks.push({ condition: "ctx.cpu.prefixes & ::prefix::PREFIX_F2 != 0", body, });
141
        }
142
        if(has_F3.length) {
143
            const body = gen_instruction_body_after_prefix(has_F3, size);
144
            if_blocks.push({ condition: "ctx.cpu.prefixes & ::prefix::PREFIX_F3 != 0", body, });
145
        }
146

147
        const else_block = {
148
            body: gen_instruction_body_after_prefix(no_prefix, size),
149
        };
150

151
        return [].concat(
152
            code,
153
            {
154
                type: "if-else",
155
                if_blocks,
156
                else_block,
157
            }
158
        );
159
    }
160
    else {
161
        return [].concat(
162
            code,
163
            gen_instruction_body_after_prefix(encodings, size)
164
        );
165
    }
166
}
167

168
function gen_instruction_body_after_prefix(encodings, size)
169
{
170
    const encoding = encodings[0];
171

172
    if(encoding.fixed_g !== undefined)
173
    {
174
        assert(encoding.e);
175

176
        // instruction with modrm byte where the middle 3 bits encode the instruction
177

178
        // group by opcode without prefix plus middle bits of modrm byte
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);
185

186
        return [
187
            {
188
                type: "switch",
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);
193

194
                    return {
195
                        conditions: [fixed_g],
196
                        body,
197
                    };
198
                }),
199

200
                default_case: {
201
                    body: [].concat(
202
                        gen_call(`::codegen::gen_trigger_ud`, ["ctx"]),
203
                        "*instr_flags |= ::jit::JIT_INSTR_BLOCK_BOUNDARY_FLAG;"
204
                    ),
205
                }
206
            },
207
        ];
208
    }
209
    else {
210
        assert(encodings.length === 1);
211
        return gen_instruction_body_after_fixed_g(encodings[0], size);
212
    }
213
}
214

215
function gen_instruction_body_after_fixed_g(encoding, size)
216
{
217
    const instruction_postfix = [];
218

219
    if(encoding.block_boundary || (!encoding.custom && encoding.e))
220
    {
221
        instruction_postfix.push("*instr_flags |= ::jit::JIT_INSTR_BLOCK_BOUNDARY_FLAG;");
222
    }
223

224
    const instruction_prefix = [];
225

226
    if(encoding.task_switch_test || encoding.sse)
227
    {
228
        instruction_prefix.push(
229
            gen_call(encoding.sse ? "::codegen::gen_task_switch_test_mmx" : "::codegen::gen_task_switch_test", ["ctx"])
230
        );
231
    }
232

233
    const imm_read = gen_read_imm_call(encoding, size);
234
    const imm_read_bindings = [];
235
    if(imm_read)
236
    {
237
        imm_read_bindings.push(`let imm = ${imm_read} as u32;`);
238
    }
239

240
    const instruction_name = make_instruction_name(encoding, size);
241

242
    if(!encoding.prefix)
243
    {
244
        if(encoding.custom)
245
        {
246
        }
247
        else
248
        {
249
            instruction_prefix.push(
250
                gen_call("::codegen::gen_move_registers_from_locals_to_memory", ["ctx"])
251
            );
252
            instruction_postfix.push(
253
                gen_call("::codegen::gen_move_registers_from_memory_to_locals", ["ctx"])
254
            );
255
        }
256
    }
257

258
    if(encoding.e)
259
    {
260
        const reg_postfix = [];
261
        const mem_postfix = [];
262

263
        if(encoding.mem_ud)
264
        {
265
            mem_postfix.push(
266
                "*instr_flags |= ::jit::JIT_INSTR_BLOCK_BOUNDARY_FLAG;"
267
            );
268
        }
269

270
        if(encoding.reg_ud)
271
        {
272
            reg_postfix.push(
273
                "*instr_flags |= ::jit::JIT_INSTR_BLOCK_BOUNDARY_FLAG;"
274
            );
275
        }
276

277
        if(encoding.ignore_mod)
278
        {
279
            assert(!imm_read, "Unexpected instruction (ignore mod with immediate value)");
280

281
            // Has modrm byte, but the 2 mod bits are ignored and both
282
            // operands are always registers (0f20-0f24)
283
            const args = ["ctx.builder", `"${instruction_name}"`, "(modrm_byte & 7) as u32", "(modrm_byte >> 3 & 7) as u32"];
284

285
            return [].concat(
286
                instruction_prefix,
287
                gen_call(`::codegen::gen_fn${args.length - 2}_const`, args),
288
                reg_postfix,
289
                instruction_postfix
290
            );
291
        }
292
        else if(encoding.custom)
293
        {
294
            const mem_args = ["ctx", "addr"];
295
            const reg_args = ["ctx", "(modrm_byte & 7) as u32"];
296

297
            if(encoding.fixed_g === undefined)
298
            {
299
                mem_args.push("(modrm_byte >> 3 & 7) as u32");
300
                reg_args.push("(modrm_byte >> 3 & 7) as u32");
301
            }
302

303
            if(imm_read)
304
            {
305
                mem_args.push("imm");
306
                reg_args.push("imm");
307
            }
308

309
            return [].concat(
310
                instruction_prefix,
311
                {
312
                    type: "if-else",
313
                    if_blocks: [{
314
                        condition: "modrm_byte < 0xC0",
315
                        body: [].concat(
316
                            "let addr = ::modrm::decode(ctx.cpu, modrm_byte);",
317
                            imm_read_bindings,
318
                            gen_call(`::jit_instructions::${instruction_name}_mem_jit`, mem_args),
319
                            mem_postfix
320
                        ),
321
                    }],
322
                    else_block: {
323
                        body: [].concat(
324
                            imm_read_bindings,
325
                            gen_call(`::jit_instructions::${instruction_name}_reg_jit`, reg_args),
326
                            reg_postfix
327
                        ),
328
                    },
329
                },
330
                instruction_postfix
331
            );
332
        }
333
        else
334
        {
335
            const mem_args = ["ctx.builder", `"${instruction_name}_mem"`];
336
            const reg_args = ["ctx.builder", `"${instruction_name}_reg"`, "(modrm_byte & 7) as u32"];
337

338
            if(encoding.fixed_g === undefined)
339
            {
340
                mem_args.push("(modrm_byte >> 3 & 7) as u32");
341
                reg_args.push("(modrm_byte >> 3 & 7) as u32");
342
            }
343

344
            if(imm_read)
345
            {
346
                mem_args.push("imm");
347
                reg_args.push("imm");
348
            }
349

350
            return [].concat(
351
                instruction_prefix,
352
                {
353
                    type: "if-else",
354
                    if_blocks: [{
355
                        condition: "modrm_byte < 0xC0",
356
                        body: [].concat(
357
                            "let addr = ::modrm::decode(ctx.cpu, modrm_byte);",
358
                            gen_call(`::codegen::gen_modrm_resolve`, ["ctx", "addr"]),
359
                            imm_read_bindings,
360
                            gen_call(`::codegen::gen_modrm_fn${mem_args.length - 2}`, mem_args),
361
                            mem_postfix
362
                        ),
363
                    }],
364
                    else_block: {
365
                        body: [].concat(
366
                            imm_read_bindings,
367
                            gen_call(`::codegen::gen_fn${reg_args.length - 2}_const`, reg_args),
368
                            reg_postfix
369
                        ),
370
                    },
371
                },
372
                instruction_postfix
373
            );
374
        }
375
    }
376
    else if(encoding.prefix || encoding.custom)
377
    {
378
        // custom, but not modrm
379

380
        const args = ["ctx"];
381

382
        if(imm_read)
383
        {
384
            args.push("imm");
385
        }
386

387
        if(encoding.prefix)
388
        {
389
            args.push("instr_flags");
390
        }
391

392
        return [].concat(
393
            instruction_prefix,
394
            imm_read_bindings,
395
            gen_call(`::jit_instructions::${instruction_name}_jit`, args),
396
            instruction_postfix
397
        );
398
    }
399
    else
400
    {
401
        // instruction without modrm byte or prefix
402

403
        const args = ["ctx.builder", `"${instruction_name}"`];
404

405
        if(imm_read)
406
        {
407
            args.push("imm");
408
        }
409

410
        if(encoding.extra_imm16)
411
        {
412
            assert(imm_read);
413
            imm_read_bindings.push(`let imm2 = ctx.cpu.read_imm16() as u32;`);
414
            args.push("imm2");
415
        }
416
        else if(encoding.extra_imm8)
417
        {
418
            assert(imm_read);
419
            imm_read_bindings.push(`let imm2 = ctx.cpu.read_imm8() as u32;`);
420
            args.push("imm2");
421
        }
422

423
        return [].concat(
424
            instruction_prefix,
425
            imm_read_bindings,
426
            gen_call(`::codegen::gen_fn${args.length - 2}_const`, args),
427
            instruction_postfix
428
        );
429
    }
430
}
431

432
function gen_table()
433
{
434
    let by_opcode = Object.create(null);
435
    let by_opcode0f = Object.create(null);
436

437
    for(let o of x86_table)
438
    {
439
        let opcode = o.opcode;
440

441
        if((opcode & 0xFF00) === 0x0F00)
442
        {
443
            opcode &= 0xFF;
444
            by_opcode0f[opcode] = by_opcode0f[opcode] || [];
445
            by_opcode0f[opcode].push(o);
446
        }
447
        else
448
        {
449
            opcode &= 0xFF;
450
            by_opcode[opcode] = by_opcode[opcode] || [];
451
            by_opcode[opcode].push(o);
452
        }
453
    }
454

455
    let cases = [];
456
    for(let opcode = 0; opcode < 0x100; opcode++)
457
    {
458
        let encoding = by_opcode[opcode];
459
        assert(encoding && encoding.length);
460

461
        let opcode_hex = hex(opcode, 2);
462
        let opcode_high_hex = hex(opcode | 0x100, 2);
463

464
        if(encoding[0].os)
465
        {
466
            cases.push({
467
                conditions: [`0x${opcode_hex}`],
468
                body: gen_instruction_body(encoding, 16),
469
            });
470
            cases.push({
471
                conditions: [`0x${opcode_high_hex}`],
472
                body: gen_instruction_body(encoding, 32),
473
            });
474
        }
475
        else
476
        {
477
            cases.push({
478
                conditions: [`0x${opcode_hex}`, `0x${opcode_high_hex}`],
479
                body: gen_instruction_body(encoding, undefined),
480
            });
481
        }
482
    }
483
    const table = {
484
        type: "switch",
485
        condition: "opcode",
486
        cases,
487
        default_case: {
488
            body: ["assert!(false);"]
489
        },
490
    };
491

492
    if(to_generate.jit)
493
    {
494
        const code = [
495
            "#[cfg_attr(rustfmt, rustfmt_skip)]",
496
            "pub fn jit(opcode: u32, ctx: &mut ::jit::JitContext, instr_flags: &mut u32) {",
497
            table,
498
            "}",
499
        ];
500

501
        finalize_table_rust(
502
            OUT_DIR,
503
            "jit.rs",
504
            rust_ast.print_syntax_tree([].concat(code)).join("\n") + "\n"
505
        );
506
    }
507

508
    const cases0f = [];
509
    for(let opcode = 0; opcode < 0x100; opcode++)
510
    {
511
        let encoding = by_opcode0f[opcode];
512

513
        assert(encoding && encoding.length);
514

515
        let opcode_hex = hex(opcode, 2);
516
        let opcode_high_hex = hex(opcode | 0x100, 2);
517

518
        if(encoding[0].os)
519
        {
520
            cases0f.push({
521
                conditions: [`0x${opcode_hex}`],
522
                body: gen_instruction_body(encoding, 16),
523
            });
524
            cases0f.push({
525
                conditions: [`0x${opcode_high_hex}`],
526
                body: gen_instruction_body(encoding, 32),
527
            });
528
        }
529
        else
530
        {
531
            let block = {
532
                conditions: [`0x${opcode_hex}`, `0x${opcode_high_hex}`],
533
                body: gen_instruction_body(encoding, undefined),
534
            };
535
            cases0f.push(block);
536
        }
537
    }
538

539
    const table0f = {
540
        type: "switch",
541
        condition: "opcode",
542
        cases: cases0f,
543
        default_case: {
544
            body: ["assert!(false);"]
545
        },
546
    };
547

548
    if(to_generate.jit0f)
549
    {
550
        const code = [
551
            "#[cfg_attr(rustfmt, rustfmt_skip)]",
552
            "pub fn jit(opcode: u32, ctx: &mut ::jit::JitContext, instr_flags: &mut u32) {",
553
            table0f,
554
            "}",
555
        ];
556

557
        finalize_table_rust(
558
            OUT_DIR,
559
            "jit0f.rs",
560
            rust_ast.print_syntax_tree([].concat(code)).join("\n") + "\n"
561
        );
562
    }
563
}
564

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.