From dac7f3406c6d53f13e6d42f746b52b0ab43aa880 Mon Sep 17 00:00:00 2001 From: SarahW Date: Fri, 16 Jun 2017 18:26:54 +0100 Subject: [PATCH] Pentium timing improvements - decoder & pairing limitations on instructions with immediate & displacement, 7 byte limitation on Pentium MMX decoding. --- src/codegen_timing_pentium.c | 186 +++++++++++++++++++++++++++++------ 1 file changed, 155 insertions(+), 31 deletions(-) diff --git a/src/codegen_timing_pentium.c b/src/codegen_timing_pentium.c index 0fbf5e6..05b0f91 100644 --- a/src/codegen_timing_pentium.c +++ b/src/codegen_timing_pentium.c @@ -42,6 +42,11 @@ static int pair_timings[4][4] = #define CYCLES_RMW (2 << 0) #define CYCLES_BRANCH (3 << 0) +/*Instruction has immediate data. Can only be used with PAIR_U/PAIR_V/PAIR_UV*/ +#define CYCLES_HASIMM (3 << 2) +#define CYCLES_IMM8 (1 << 2) +#define CYCLES_IMM1632 (2 << 2) + #define CYCLES_MASK ((1 << 7) - 1) /*Instruction is MMX shift or pack/unpack instruction*/ @@ -62,6 +67,8 @@ static int pair_timings[4][4] = /*Instruction is FXCH and only pairs in V pipe with FX pairable instruction*/ #define PAIR_FXCH (6 << 29) +#define PAIR_FPU (4 << 29) + #define PAIR_MASK (7 << 29) /*Instruction has input dependency on register in REG field*/ @@ -296,38 +303,38 @@ static uint32_t opcode_timings_mod3[256] = /* ADD ADD ADD ADD*/ /*00*/ PAIR_UV | CYCLES_REG | SRCDEP_REG | SRCDEP_RM | DSTDEP_REG, PAIR_UV | CYCLES_REG | SRCDEP_REG | SRCDEP_RM | DSTDEP_REG, PAIR_UV | CYCLES_REG | SRCDEP_REG | SRCDEP_RM | DSTDEP_REG, PAIR_UV | CYCLES_REG | SRCDEP_REG | SRCDEP_RM | DSTDEP_REG, /* ADD ADD PUSH ES POP ES*/ - PAIR_UV | CYCLES_REG | SRCDEP_EAX | SRCDEP_RM | DSTDEP_EAX, PAIR_UV | CYCLES_REG | SRCDEP_EAX | SRCDEP_RM | DSTDEP_EAX, PAIR_NP | CYCLES(1), PAIR_NP | CYCLES(3), + PAIR_UV | CYCLES_REG | SRCDEP_EAX | DSTDEP_EAX, PAIR_UV | CYCLES_REG | SRCDEP_EAX | DSTDEP_EAX, PAIR_NP | CYCLES(1), PAIR_NP | CYCLES(3), /* OR OR OR OR*/ PAIR_UV | CYCLES_REG | SRCDEP_REG | SRCDEP_RM | DSTDEP_REG, PAIR_UV | CYCLES_REG | SRCDEP_REG | SRCDEP_RM | DSTDEP_REG, PAIR_UV | CYCLES_REG | SRCDEP_REG | SRCDEP_RM | DSTDEP_REG, PAIR_UV | CYCLES_REG | SRCDEP_REG | SRCDEP_RM | DSTDEP_REG, /* OR OR PUSH CS */ - PAIR_UV | CYCLES_REG | SRCDEP_EAX | SRCDEP_RM | DSTDEP_EAX, PAIR_UV | CYCLES_REG | SRCDEP_EAX | SRCDEP_RM | DSTDEP_EAX, PAIR_NP | CYCLES(1), INVALID, + PAIR_UV | CYCLES_REG | SRCDEP_EAX | DSTDEP_EAX, PAIR_UV | CYCLES_REG | SRCDEP_EAX | DSTDEP_EAX, PAIR_NP | CYCLES(1), INVALID, /* ADC ADC ADC ADC*/ /*10*/ PAIR_U | CYCLES_REG | SRCDEP_REG | SRCDEP_RM | DSTDEP_REG, PAIR_U | CYCLES_REG | SRCDEP_REG | SRCDEP_RM | DSTDEP_REG, PAIR_U | CYCLES_REG | SRCDEP_REG | SRCDEP_RM | DSTDEP_REG, PAIR_U | CYCLES_REG | SRCDEP_REG | SRCDEP_RM | DSTDEP_REG, /* ADC ADC PUSH SS POP SS*/ - PAIR_U | CYCLES_REG | SRCDEP_EAX | SRCDEP_RM | DSTDEP_EAX, PAIR_U | CYCLES_REG | SRCDEP_EAX | SRCDEP_RM | DSTDEP_EAX, PAIR_NP | CYCLES(1), PAIR_NP | CYCLES(3), + PAIR_U | CYCLES_REG | SRCDEP_EAX | DSTDEP_EAX, PAIR_U | CYCLES_REG | SRCDEP_EAX | DSTDEP_EAX, PAIR_NP | CYCLES(1), PAIR_NP | CYCLES(3), /* SBB SBB SBB SBB*/ PAIR_U | CYCLES_REG | SRCDEP_REG | SRCDEP_RM | DSTDEP_REG, PAIR_U | CYCLES_REG | SRCDEP_REG | SRCDEP_RM | DSTDEP_REG, PAIR_U | CYCLES_REG | SRCDEP_REG | SRCDEP_RM | DSTDEP_REG, PAIR_U | CYCLES_REG | SRCDEP_REG | SRCDEP_RM | DSTDEP_REG, /* SBB SBB PUSH DS POP DS*/ - PAIR_U | CYCLES_REG | SRCDEP_EAX | SRCDEP_RM | DSTDEP_EAX, PAIR_U | CYCLES_REG | SRCDEP_EAX | SRCDEP_RM | DSTDEP_EAX, PAIR_NP | CYCLES(1), PAIR_NP | CYCLES(3), + PAIR_U | CYCLES_REG | SRCDEP_EAX | DSTDEP_EAX, PAIR_U | CYCLES_REG | SRCDEP_EAX | DSTDEP_EAX, PAIR_NP | CYCLES(1), PAIR_NP | CYCLES(3), /* AND AND AND AND*/ /*20*/ PAIR_UV | CYCLES_REG | SRCDEP_REG | SRCDEP_RM | DSTDEP_REG, PAIR_UV | CYCLES_REG | SRCDEP_REG | SRCDEP_RM | DSTDEP_REG, PAIR_UV | CYCLES_REG | SRCDEP_REG | SRCDEP_RM | DSTDEP_REG, PAIR_UV | CYCLES_REG | SRCDEP_REG | SRCDEP_RM | DSTDEP_REG, /* AND AND DAA*/ - PAIR_UV | CYCLES_REG | SRCDEP_EAX | SRCDEP_RM | DSTDEP_EAX, PAIR_UV | CYCLES_REG | SRCDEP_EAX | SRCDEP_RM | DSTDEP_EAX, INVALID, PAIR_NP | CYCLES(3), + PAIR_UV | CYCLES_REG | SRCDEP_EAX | DSTDEP_EAX, PAIR_UV | CYCLES_REG | SRCDEP_EAX | DSTDEP_EAX, INVALID, PAIR_NP | CYCLES(3), /* SUB SUB SUB SUB*/ PAIR_UV | CYCLES_REG | SRCDEP_REG | SRCDEP_RM | DSTDEP_REG, PAIR_UV | CYCLES_REG | SRCDEP_REG | SRCDEP_RM | DSTDEP_REG, PAIR_UV | CYCLES_REG | SRCDEP_REG | SRCDEP_RM | DSTDEP_REG, PAIR_UV | CYCLES_REG | SRCDEP_REG | SRCDEP_RM | DSTDEP_REG, /* SUB SUB DAS*/ - PAIR_UV | CYCLES_REG | SRCDEP_EAX | SRCDEP_RM | DSTDEP_EAX, PAIR_UV | CYCLES_REG | SRCDEP_EAX | SRCDEP_RM | DSTDEP_EAX, INVALID, PAIR_NP | CYCLES(3), + PAIR_UV | CYCLES_REG | SRCDEP_EAX | DSTDEP_EAX, PAIR_UV | CYCLES_REG | SRCDEP_EAX | DSTDEP_EAX, INVALID, PAIR_NP | CYCLES(3), /* XOR XOR XOR XOR*/ /*30*/ PAIR_UV | CYCLES_REG | SRCDEP_REG | SRCDEP_RM | DSTDEP_REG, PAIR_UV | CYCLES_REG | SRCDEP_REG | SRCDEP_RM | DSTDEP_REG, PAIR_UV | CYCLES_REG | SRCDEP_REG | SRCDEP_RM | DSTDEP_REG, PAIR_UV | CYCLES_REG | SRCDEP_REG | SRCDEP_RM | DSTDEP_REG, /* XOR XOR AAA*/ - PAIR_UV | CYCLES_REG | SRCDEP_EAX | SRCDEP_RM | DSTDEP_EAX, PAIR_UV | CYCLES_REG | SRCDEP_EAX | SRCDEP_RM | DSTDEP_EAX, INVALID, PAIR_NP | CYCLES(3), + PAIR_UV | CYCLES_REG | SRCDEP_EAX | DSTDEP_EAX, PAIR_UV | CYCLES_REG | SRCDEP_EAX | DSTDEP_EAX, INVALID, PAIR_NP | CYCLES(3), /* CMP CMP CMP CMP*/ PAIR_UV | CYCLES_REG | SRCDEP_REG | SRCDEP_RM, PAIR_UV | CYCLES_REG | SRCDEP_REG | SRCDEP_RM, PAIR_UV | CYCLES_REG | SRCDEP_REG | SRCDEP_RM, PAIR_UV | CYCLES_REG | SRCDEP_RM | SRCDEP_REG, /* CMP CMP AAS*/ - PAIR_UV | CYCLES_REG | SRCDEP_EAX | SRCDEP_RM, PAIR_UV | CYCLES_REG | SRCDEP_EAX, INVALID, PAIR_NP | CYCLES(3), + PAIR_UV | CYCLES_REG | SRCDEP_EAX, PAIR_UV | CYCLES_REG | SRCDEP_EAX, INVALID, PAIR_NP | CYCLES(3), /* INC EAX INC ECX INC EDX INC EBX*/ /*40*/ PAIR_UV | CYCLES_REG | SRCDEP_EAX | DSTDEP_EAX, PAIR_UV | CYCLES_REG | SRCDEP_ECX | DSTDEP_ECX, PAIR_UV | CYCLES_REG | SRCDEP_EDX | DSTDEP_EDX, PAIR_UV | CYCLES_REG | SRCDEP_EBX | DSTDEP_EBX, @@ -810,14 +817,20 @@ static uint32_t opcode_timings_df_mod3[8] = PAIR_NP | CYCLES(2), INVALID, INVALID, INVALID }; +static uint32_t opcode_timings_81[8] = +{ + PAIR_UV | CYCLES_RMW | SRCDEP_REG | CYCLES_IMM1632, PAIR_UV | CYCLES_RMW | SRCDEP_REG | CYCLES_IMM1632, PAIR_UV | CYCLES_RMW | SRCDEP_REG | CYCLES_IMM1632, PAIR_UV | CYCLES_RMW | SRCDEP_REG | CYCLES_IMM1632, + PAIR_UV | CYCLES_RMW | SRCDEP_REG | CYCLES_IMM1632, PAIR_UV | CYCLES_RMW | SRCDEP_REG | CYCLES_IMM1632, PAIR_UV | CYCLES_RMW | SRCDEP_REG | CYCLES_IMM1632, PAIR_UV | CYCLES_RM | SRCDEP_REG | CYCLES_IMM1632 +}; static uint32_t opcode_timings_8x[8] = { - PAIR_UV | CYCLES_RMW | SRCDEP_REG, PAIR_UV | CYCLES_RMW | SRCDEP_REG, PAIR_UV | CYCLES_RMW | SRCDEP_REG, PAIR_UV | CYCLES_RMW | SRCDEP_REG, - PAIR_UV | CYCLES_RMW | SRCDEP_REG, PAIR_UV | CYCLES_RMW | SRCDEP_REG, PAIR_UV | CYCLES_RMW | SRCDEP_REG, PAIR_UV | CYCLES_RM | SRCDEP_REG + PAIR_UV | CYCLES_RMW | SRCDEP_REG | CYCLES_IMM8, PAIR_UV | CYCLES_RMW | SRCDEP_REG | CYCLES_IMM8, PAIR_UV | CYCLES_RMW | SRCDEP_REG | CYCLES_IMM8, PAIR_UV | CYCLES_RMW | SRCDEP_REG | CYCLES_IMM8, + PAIR_UV | CYCLES_RMW | SRCDEP_REG | CYCLES_IMM8, PAIR_UV | CYCLES_RMW | SRCDEP_REG | CYCLES_IMM8, PAIR_UV | CYCLES_RMW | SRCDEP_REG | CYCLES_IMM8, PAIR_UV | CYCLES_RM | SRCDEP_REG | CYCLES_IMM8 }; static int decode_delay; static uint8_t last_prefix; +static int prefixes; static inline int COUNT(uint32_t c, int op_32) { @@ -831,6 +844,10 @@ static inline int COUNT(uint32_t c, int op_32) return c & 0xffff; if ((c & PAIR_MASK) == PAIR_FX) return c & 0xffff; + if ((c & PAIR_MASK) == PAIR_FXCH) + return c & 0xffff; + if ((c & PAIR_UV) && !(c & PAIR_FPU)) + c &= 3; switch (c & CYCLES_MASK) { case CYCLES_REG: @@ -848,6 +865,80 @@ static inline int COUNT(uint32_t c, int op_32) return c; } +static inline int codegen_timing_has_displacement(uint32_t fetchdat, int op_32) +{ + if (op_32 & 0x200) + { + if ((fetchdat & 7) == 4 && (fetchdat & 0xc0) != 0xc0) + { + /*Has SIB*/ + if ((fetchdat & 0xc0) == 0x40 || (fetchdat & 0xc0) == 0x80 || (fetchdat & 0x700) == 0x500) + return 1; + } + else + { + if ((fetchdat & 0xc0) == 0x40 || (fetchdat & 0xc0) == 0x80 || (fetchdat & 0xc7) == 0x05) + return 1; + } + } + else + { + if ((fetchdat & 0xc0) == 0x40 || (fetchdat & 0xc0) == 0x80 || (fetchdat & 0xc7) == 0x06) + return 1; + } + return 0; +} + +/*The instruction is only of interest here if it's longer than 7 bytes, as that's the + limit on Pentium MMX parallel decoding*/ +static inline int codegen_timing_instr_length(uint32_t timing, uint32_t fetchdat, int op_32) +{ + int len = prefixes; + if ((timing & CYCLES_MASK) == CYCLES_RM || (timing & CYCLES_MASK) == CYCLES_RMW) + { + len += 2; /*Opcode + ModR/M*/ + if ((timing & CYCLES_HASIMM) == CYCLES_IMM8) + len++; + if ((timing & CYCLES_HASIMM) == CYCLES_IMM1632) + len += (op_32 & 0x100) ? 4 : 2; + + if (op_32 & 0x200) + { + if ((fetchdat & 7) == 4 && (fetchdat & 0xc0) != 0xc0) + { + /* Has SIB*/ + len++; + if ((fetchdat & 0xc0) == 0x40) + len++; + else if ((fetchdat & 0xc0) == 0x80) + len += 4; + else if ((fetchdat & 0x700) == 0x500) + len += 4; + } + else + { + if ((fetchdat & 0xc0) == 0x40) + len++; + else if ((fetchdat & 0xc0) == 0x80) + len += 4; + else if ((fetchdat & 0xc7) == 0x05) + len += 4; + } + } + else + { + if ((fetchdat & 0xc0) == 0x40) + len++; + else if ((fetchdat & 0xc0) == 0x80) + len += 2; + else if ((fetchdat & 0xc7) == 0x06) + len += 2; + } + } + + return len; +} + void codegen_timing_pentium_block_start() { u_pipe_full = decode_delay = 0; @@ -857,10 +948,12 @@ void codegen_timing_pentium_start() { // decode_delay = 0; last_prefix = 0; + prefixes = 0; } void codegen_timing_pentium_prefix(uint8_t prefix, uint32_t fetchdat) { + prefixes++; if (cpu_hasMMX && prefix == 0x0f) { /*On Pentium MMX 0fh prefix is 'free'*/ @@ -943,12 +1036,18 @@ void codegen_timing_pentium_opcode(uint8_t opcode, uint32_t fetchdat, int op_32) default: switch (opcode) { - case 0x80: case 0x81: case 0x82: case 0x83: + case 0x80: case 0x82: case 0x83: timings = mod3 ? opcode_timings_mod3 : opcode_timings_8x; if (!mod3) opcode = (fetchdat >> 3) & 7; // pclog("timings 80 %p %p %p\n", (void *)timings, (void *)opcode_timings_mod3, (void *)opcode_timings_8x); break; + case 0x81: + timings = mod3 ? opcode_timings_mod3 : opcode_timings_81; + if (!mod3) + opcode = (fetchdat >> 3) & 7; +// pclog("timings 80 %p %p %p\n", (void *)timings, (void *)opcode_timings_mod3, (void *)opcode_timings_8x); + break; case 0xc0: case 0xc1: case 0xd0: case 0xd1: case 0xd2: case 0xd3: timings = mod3 ? opcode_timings_shift_mod3 : opcode_timings_shift; @@ -996,24 +1095,39 @@ void codegen_timing_pentium_opcode(uint8_t opcode, uint32_t fetchdat, int op_32) if ((timings[opcode] & PAIR_V) && !(u_pipe_regmask & regmask) && !decode_delay) { - int t1 = u_pipe_timings[u_pipe_opcode] & CYCLES_MASK; - int t2 = timings[opcode] & CYCLES_MASK; - int t_pair; + int has_displacement; - if (t1 < 0 || t2 < 0 || t1 > CYCLES_BRANCH || t2 > CYCLES_BRANCH) - fatal("Pair out of range\n"); + if (timings[opcode] & CYCLES_HASIMM) + has_displacement = codegen_timing_has_displacement(fetchdat, op_32); + else + has_displacement = 0; + + if (!has_displacement && (!cpu_hasMMX || codegen_timing_instr_length(timings[opcode], fetchdat, op_32) <= 7)) + { + int t1 = u_pipe_timings[u_pipe_opcode] & CYCLES_MASK; + int t2 = timings[opcode] & CYCLES_MASK; + int t_pair; - t_pair = pair_timings[t1][t2]; - if (t_pair < 1) - fatal("Illegal pair timings : t1=%i t2=%i u_opcode=%02x v_opcode=%02x\n", t1, t2, u_pipe_opcode, opcode); + if (!(u_pipe_timings[u_pipe_opcode] & PAIR_FPU)) + t1 &= 3; + if (!(timings[opcode] & PAIR_FPU)) + t2 &= 3; + + if (t1 < 0 || t2 < 0 || t1 > CYCLES_BRANCH || t2 > CYCLES_BRANCH) + fatal("Pair out of range\n"); + + t_pair = pair_timings[t1][t2]; + if (t_pair < 1) + fatal("Illegal pair timings : t1=%i t2=%i u_opcode=%02x v_opcode=%02x\n", t1, t2, u_pipe_opcode, opcode); // pclog("Paired timings : t_pair=%i t1=%i t2=%i u_opcode=%02x v_opcode=%02x %08x:%08x\n", t_pair, t1, t2, u_pipe_opcode, opcode, cs, pc); - codegen_block_cycles += t_pair; - decode_delay = (-t_pair) + 1; + codegen_block_cycles += t_pair; + decode_delay = (-t_pair) + 1; - /*Instruction can pair with previous*/ - u_pipe_full = 0; - return; + /*Instruction can pair with previous*/ + u_pipe_full = 0; + return; + } } nopair: /*Instruction can not pair with previous*/ @@ -1026,13 +1140,23 @@ nopair: if ((timings[opcode] & PAIR_U) && !decode_delay) { - /*Instruction might pair with next*/ - u_pipe_full = 1; - u_pipe_opcode = opcode; - u_pipe_timings = timings; - u_pipe_op_32 = op_32; - u_pipe_regmask = get_dstdep_mask(timings[opcode], fetchdat, bit8); - return; + int has_displacement; + + if (timings[opcode] & CYCLES_HASIMM) + has_displacement = codegen_timing_has_displacement(fetchdat, op_32); + else + has_displacement = 0; + + if ((!has_displacement || cpu_hasMMX) && (!cpu_hasMMX || codegen_timing_instr_length(timings[opcode], fetchdat, op_32) <= 7)) + { + /*Instruction might pair with next*/ + u_pipe_full = 1; + u_pipe_opcode = opcode; + u_pipe_timings = timings; + u_pipe_op_32 = op_32; + u_pipe_regmask = get_dstdep_mask(timings[opcode], fetchdat, bit8); + return; + } } /*Instruction can not pair and must run now*/