From 7e099c277e1e38ff4f56c4cd7a9eb9cf07a7b1d8 Mon Sep 17 00:00:00 2001 From: SarahW Date: Mon, 25 Mar 2019 19:07:51 +0000 Subject: [PATCH] Added basic loop unrolling. Blocks that end in a JMP, LOOP or conditional branch to a previous instruction in the block will be unrolled up to 10 times. --- src/codegen_ir.c | 52 ++++ src/codegen_ir.h | 1 + src/codegen_ir_defs.h | 4 + src/codegen_ops_branch.c | 588 +++++++++++++++++++++++++++++--------- src/codegen_ops_helpers.c | 45 +++ src/codegen_ops_helpers.h | 15 + src/codegen_ops_jump.c | 15 +- src/codegen_reg.c | 7 +- src/codegen_reg.h | 15 +- 9 files changed, 594 insertions(+), 148 deletions(-) diff --git a/src/codegen_ir.c b/src/codegen_ir.c index 56d58f7..e3a9377 100644 --- a/src/codegen_ir.c +++ b/src/codegen_ir.c @@ -8,19 +8,71 @@ extern int has_ea; static ir_data_t ir_block; +static int codegen_unroll_start, codegen_unroll_count; + ir_data_t *codegen_ir_init() { ir_block.wr_pos = 0; // pclog("codegen_ir_init %04x:%04x\n", CS,cpu_state.pc); + codegen_unroll_count = 0; + return &ir_block; } +void codegen_ir_set_unroll(int count, int start) +{ + codegen_unroll_count = count; + codegen_unroll_start = start; +} + +static void duplicate_uop(ir_data_t *ir, uop_t *uop, int offset) +{ + uop_t *new_uop = uop_alloc(ir, uop->type); + +// pclog(" uop type %08x\n", uop->type); + if (!ir_reg_is_invalid(uop->src_reg_a)) + new_uop->src_reg_a = codegen_reg_read(uop->src_reg_a.reg); + if (!ir_reg_is_invalid(uop->src_reg_b)) + new_uop->src_reg_b = codegen_reg_read(uop->src_reg_b.reg); + if (!ir_reg_is_invalid(uop->src_reg_c)) + new_uop->src_reg_c = codegen_reg_read(uop->src_reg_c.reg); + if (!ir_reg_is_invalid(uop->dest_reg_a)) + new_uop->dest_reg_a = codegen_reg_write(uop->dest_reg_a.reg, ir->wr_pos-1); + + new_uop->type = uop->type; + new_uop->imm_data = uop->imm_data; + new_uop->p = uop->p; + new_uop->pc = uop->pc; + + if (uop->jump_dest_uop != -1) + { + new_uop->jump_dest_uop = uop->jump_dest_uop + offset; + } +} + void codegen_ir_compile(ir_data_t *ir, codeblock_t *block) { int jump_target_at_end = -1; int c; + if (codegen_unroll_count) + { + int unroll_count; + int unroll_end = ir->wr_pos; + + for (unroll_count = 1; unroll_count < codegen_unroll_count; unroll_count++) + { + int offset = ir->wr_pos - codegen_unroll_start; +// pclog("Unroll from %i to %i, offset %i - iteration %i\n", codegen_unroll_start, ir->wr_pos, offset, unroll_count); + for (c = codegen_unroll_start; c < unroll_end; c++) + { +// pclog(" Duplicate uop %i\n", c); + duplicate_uop(ir, &ir->uops[c], offset); + } + } + } + codegen_reg_mark_as_required(); codegen_reg_process_dead_list(ir); block_write_data = codeblock_allocator_get_ptr(block->head_mem_block); diff --git a/src/codegen_ir.h b/src/codegen_ir.h index aafeca6..b560256 100644 --- a/src/codegen_ir.h +++ b/src/codegen_ir.h @@ -2,4 +2,5 @@ ir_data_t *codegen_ir_init(); +void codegen_ir_set_unroll(int count, int start); void codegen_ir_compile(ir_data_t *ir, codeblock_t *block); diff --git a/src/codegen_ir_defs.h b/src/codegen_ir_defs.h index 7880c7a..82d0af9 100644 --- a/src/codegen_ir_defs.h +++ b/src/codegen_ir_defs.h @@ -336,6 +336,7 @@ typedef struct uop_t int jump_dest_uop; int jump_list_next; void *jump_dest; + uint32_t pc; } uop_t; #define UOP_NR_MAX 4096 @@ -361,6 +362,9 @@ static inline uop_t *uop_alloc(ir_data_t *ir, uint32_t uop_type) uop->src_reg_b = invalid_ir_reg; uop->src_reg_c = invalid_ir_reg; + uop->pc = cpu_state.oldpc; + + uop->jump_dest_uop = -1; uop->jump_list_next = -1; if (uop_type & (UOP_TYPE_BARRIER | UOP_TYPE_ORDER_BARRIER)) diff --git a/src/codegen_ops_branch.c b/src/codegen_ops_branch.c index 35e5c46..d22093a 100644 --- a/src/codegen_ops_branch.c +++ b/src/codegen_ops_branch.c @@ -19,7 +19,7 @@ static int VF_SET_01() return VF_SET() ? 1 : 0; } -static void ropJO_common(codeblock_t *block, ir_data_t *ir, uint32_t dest_addr) +static int ropJO_common(codeblock_t *block, ir_data_t *ir, uint32_t dest_addr, uint32_t next_pc) { int jump_uop; @@ -27,7 +27,7 @@ static void ropJO_common(codeblock_t *block, ir_data_t *ir, uint32_t dest_addr) { case FLAGS_ZN8: case FLAGS_ZN16: case FLAGS_ZN32: /*Overflow is always zero*/ - return; + return 0; case FLAGS_SUB8: case FLAGS_DEC8: jump_uop = uop_CMP_JNO_DEST(ir, IREG_flags_op1_B, IREG_flags_op2_B); @@ -50,8 +50,9 @@ static void ropJO_common(codeblock_t *block, ir_data_t *ir, uint32_t dest_addr) uop_MOV_IMM(ir, IREG_pc, dest_addr); uop_JMP(ir, codegen_exit_rout); uop_set_jump_dest(ir, jump_uop); + return 0; } -static void ropJNO_common(codeblock_t *block, ir_data_t *ir, uint32_t dest_addr) +static int ropJNO_common(codeblock_t *block, ir_data_t *ir, uint32_t dest_addr, uint32_t next_pc) { int jump_uop; @@ -61,7 +62,7 @@ static void ropJNO_common(codeblock_t *block, ir_data_t *ir, uint32_t dest_addr) /*Overflow is always zero*/ uop_MOV_IMM(ir, IREG_pc, dest_addr); uop_JMP(ir, codegen_exit_rout); - return; + return 0; case FLAGS_SUB8: case FLAGS_DEC8: jump_uop = uop_CMP_JO_DEST(ir, IREG_flags_op1_B, IREG_flags_op2_B); @@ -84,43 +85,59 @@ static void ropJNO_common(codeblock_t *block, ir_data_t *ir, uint32_t dest_addr) uop_MOV_IMM(ir, IREG_pc, dest_addr); uop_JMP(ir, codegen_exit_rout); uop_set_jump_dest(ir, jump_uop); + return 0; } -static void ropJB_common(codeblock_t *block, ir_data_t *ir, uint32_t dest_addr) +static int ropJB_common(codeblock_t *block, ir_data_t *ir, uint32_t dest_addr, uint32_t next_pc) { int jump_uop; + int do_unroll = (CF_SET() && codegen_can_unroll(block, ir, next_pc, dest_addr)); switch (codegen_flags_changed ? cpu_state.flags_op : FLAGS_UNKNOWN) { case FLAGS_ZN8: case FLAGS_ZN16: case FLAGS_ZN32: /*Carry is always zero*/ - return; + return 0; case FLAGS_SUB8: - jump_uop = uop_CMP_JNB_DEST(ir, IREG_flags_op1_B, IREG_flags_op2_B); + if (do_unroll) + jump_uop = uop_CMP_JB_DEST(ir, IREG_flags_op1_B, IREG_flags_op2_B); + else + jump_uop = uop_CMP_JNB_DEST(ir, IREG_flags_op1_B, IREG_flags_op2_B); break; case FLAGS_SUB16: - jump_uop = uop_CMP_JNB_DEST(ir, IREG_flags_op1_W, IREG_flags_op2_W); + if (do_unroll) + jump_uop = uop_CMP_JB_DEST(ir, IREG_flags_op1_W, IREG_flags_op2_W); + else + jump_uop = uop_CMP_JNB_DEST(ir, IREG_flags_op1_W, IREG_flags_op2_W); break; case FLAGS_SUB32: - jump_uop = uop_CMP_JNB_DEST(ir, IREG_flags_op1, IREG_flags_op2); + if (do_unroll) + jump_uop = uop_CMP_JB_DEST(ir, IREG_flags_op1, IREG_flags_op2); + else + jump_uop = uop_CMP_JNB_DEST(ir, IREG_flags_op1, IREG_flags_op2); break; case FLAGS_UNKNOWN: default: uop_CALL_FUNC_RESULT(ir, IREG_temp0, CF_SET); - jump_uop = uop_CMP_IMM_JZ_DEST(ir, IREG_temp0, 0); + if (do_unroll) + jump_uop = uop_CMP_IMM_JNZ_DEST(ir, IREG_temp0, 0); + else + jump_uop = uop_CMP_IMM_JZ_DEST(ir, IREG_temp0, 0); break; } - uop_MOV_IMM(ir, IREG_pc, dest_addr); + uop_MOV_IMM(ir, IREG_pc, do_unroll ? next_pc : dest_addr); uop_JMP(ir, codegen_exit_rout); uop_set_jump_dest(ir, jump_uop); + return do_unroll ? 1 : 0; } -static void ropJNB_common(codeblock_t *block, ir_data_t *ir, uint32_t dest_addr) +static int ropJNB_common(codeblock_t *block, ir_data_t *ir, uint32_t dest_addr, uint32_t next_pc) { int jump_uop; + int do_unroll = (!CF_SET() && codegen_can_unroll(block, ir, next_pc, dest_addr)); switch (codegen_flags_changed ? cpu_state.flags_op : FLAGS_UNKNOWN) { @@ -128,140 +145,266 @@ static void ropJNB_common(codeblock_t *block, ir_data_t *ir, uint32_t dest_addr) /*Carry is always zero*/ uop_MOV_IMM(ir, IREG_pc, dest_addr); uop_JMP(ir, codegen_exit_rout); - return; + return 0; case FLAGS_SUB8: - jump_uop = uop_CMP_JB_DEST(ir, IREG_flags_op1_B, IREG_flags_op2_B); + if (do_unroll) + jump_uop = uop_CMP_JNB_DEST(ir, IREG_flags_op1_B, IREG_flags_op2_B); + else + jump_uop = uop_CMP_JB_DEST(ir, IREG_flags_op1_B, IREG_flags_op2_B); break; case FLAGS_SUB16: - jump_uop = uop_CMP_JB_DEST(ir, IREG_flags_op1_W, IREG_flags_op2_W); + if (do_unroll) + jump_uop = uop_CMP_JNB_DEST(ir, IREG_flags_op1_W, IREG_flags_op2_W); + else + jump_uop = uop_CMP_JB_DEST(ir, IREG_flags_op1_W, IREG_flags_op2_W); break; case FLAGS_SUB32: - jump_uop = uop_CMP_JB_DEST(ir, IREG_flags_op1, IREG_flags_op2); + if (do_unroll) + jump_uop = uop_CMP_JNB_DEST(ir, IREG_flags_op1, IREG_flags_op2); + else + jump_uop = uop_CMP_JB_DEST(ir, IREG_flags_op1, IREG_flags_op2); break; case FLAGS_UNKNOWN: default: uop_CALL_FUNC_RESULT(ir, IREG_temp0, CF_SET); - jump_uop = uop_CMP_IMM_JNZ_DEST(ir, IREG_temp0, 0); + if (do_unroll) + jump_uop = uop_CMP_IMM_JZ_DEST(ir, IREG_temp0, 0); + else + jump_uop = uop_CMP_IMM_JNZ_DEST(ir, IREG_temp0, 0); break; } - uop_MOV_IMM(ir, IREG_pc, dest_addr); + uop_MOV_IMM(ir, IREG_pc, do_unroll ? next_pc : dest_addr); uop_JMP(ir, codegen_exit_rout); uop_set_jump_dest(ir, jump_uop); + return do_unroll ? 1 : 0; } -static void ropJE_common(codeblock_t *block, ir_data_t *ir, uint32_t dest_addr) +static int ropJE_common(codeblock_t *block, ir_data_t *ir, uint32_t dest_addr, uint32_t next_pc) { int jump_uop; - if (!codegen_flags_changed || (cpu_state.flags_op == FLAGS_UNKNOWN)) + if (ZF_SET() && codegen_can_unroll(block, ir, next_pc, dest_addr)) { - uop_CALL_FUNC_RESULT(ir, IREG_temp0, ZF_SET); - jump_uop = uop_CMP_IMM_JZ_DEST(ir, IREG_temp0, 0); + if (!codegen_flags_changed || (cpu_state.flags_op == FLAGS_UNKNOWN)) + { + uop_CALL_FUNC_RESULT(ir, IREG_temp0, ZF_SET); + jump_uop = uop_CMP_IMM_JNZ_DEST(ir, IREG_temp0, 0); + } + else + { + jump_uop = uop_CMP_IMM_JZ_DEST(ir, IREG_flags_res, 0); + } + uop_MOV_IMM(ir, IREG_pc, next_pc); + uop_JMP(ir, codegen_exit_rout); + uop_set_jump_dest(ir, jump_uop); + return 1; } else { - jump_uop = uop_CMP_IMM_JNZ_DEST(ir, IREG_flags_res, 0); + if (!codegen_flags_changed || (cpu_state.flags_op == FLAGS_UNKNOWN)) + { + uop_CALL_FUNC_RESULT(ir, IREG_temp0, ZF_SET); + jump_uop = uop_CMP_IMM_JZ_DEST(ir, IREG_temp0, 0); + } + else + { + jump_uop = uop_CMP_IMM_JNZ_DEST(ir, IREG_flags_res, 0); + } + uop_MOV_IMM(ir, IREG_pc, dest_addr); + uop_JMP(ir, codegen_exit_rout); + uop_set_jump_dest(ir, jump_uop); } - uop_MOV_IMM(ir, IREG_pc, dest_addr); - uop_JMP(ir, codegen_exit_rout); - uop_set_jump_dest(ir, jump_uop); + return 0; } -void ropJNE_common(codeblock_t *block, ir_data_t *ir, uint32_t dest_addr) +int ropJNE_common(codeblock_t *block, ir_data_t *ir, uint32_t dest_addr, uint32_t next_pc) { int jump_uop; - if (!codegen_flags_changed || (cpu_state.flags_op == FLAGS_UNKNOWN)) + if (!ZF_SET() && codegen_can_unroll(block, ir, next_pc, dest_addr)) { - uop_CALL_FUNC_RESULT(ir, IREG_temp0, ZF_SET); - jump_uop = uop_CMP_IMM_JNZ_DEST(ir, IREG_temp0, 0); + if (!codegen_flags_changed || (cpu_state.flags_op == FLAGS_UNKNOWN)) + { + uop_CALL_FUNC_RESULT(ir, IREG_temp0, ZF_SET); + jump_uop = uop_CMP_IMM_JZ_DEST(ir, IREG_temp0, 0); + } + else + { + jump_uop = uop_CMP_IMM_JNZ_DEST(ir, IREG_flags_res, 0); + } + uop_MOV_IMM(ir, IREG_pc, next_pc); + uop_JMP(ir, codegen_exit_rout); + uop_set_jump_dest(ir, jump_uop); + return 1; } else { - jump_uop = uop_CMP_IMM_JZ_DEST(ir, IREG_flags_res, 0); + if (!codegen_flags_changed || (cpu_state.flags_op == FLAGS_UNKNOWN)) + { + uop_CALL_FUNC_RESULT(ir, IREG_temp0, ZF_SET); + jump_uop = uop_CMP_IMM_JNZ_DEST(ir, IREG_temp0, 0); + } + else + { + jump_uop = uop_CMP_IMM_JZ_DEST(ir, IREG_flags_res, 0); + } + uop_MOV_IMM(ir, IREG_pc, dest_addr); + uop_JMP(ir, codegen_exit_rout); + uop_set_jump_dest(ir, jump_uop); } - uop_MOV_IMM(ir, IREG_pc, dest_addr); - uop_JMP(ir, codegen_exit_rout); - uop_set_jump_dest(ir, jump_uop); + return 0; } -static void ropJBE_common(codeblock_t *block, ir_data_t *ir, uint32_t dest_addr) +static int ropJBE_common(codeblock_t *block, ir_data_t *ir, uint32_t dest_addr, uint32_t next_pc) { int jump_uop, jump_uop2 = -1; + int do_unroll = ((CF_SET() || ZF_SET()) && codegen_can_unroll(block, ir, next_pc, dest_addr)); switch (codegen_flags_changed ? cpu_state.flags_op : FLAGS_UNKNOWN) { case FLAGS_ZN8: case FLAGS_ZN16: case FLAGS_ZN32: /*Carry is always zero, so test zero only*/ - jump_uop = uop_CMP_IMM_JNZ_DEST(ir, IREG_flags_res, 0); + if (do_unroll) + jump_uop = uop_CMP_IMM_JZ_DEST(ir, IREG_flags_res, 0); + else + jump_uop = uop_CMP_IMM_JNZ_DEST(ir, IREG_flags_res, 0); break; case FLAGS_SUB8: - jump_uop = uop_CMP_JNBE_DEST(ir, IREG_flags_op1_B, IREG_flags_op2_B); + if (do_unroll) + jump_uop = uop_CMP_JBE_DEST(ir, IREG_flags_op1_B, IREG_flags_op2_B); + else + jump_uop = uop_CMP_JNBE_DEST(ir, IREG_flags_op1_B, IREG_flags_op2_B); break; case FLAGS_SUB16: - jump_uop = uop_CMP_JNBE_DEST(ir, IREG_flags_op1_W, IREG_flags_op2_W); + if (do_unroll) + jump_uop = uop_CMP_JBE_DEST(ir, IREG_flags_op1_W, IREG_flags_op2_W); + else + jump_uop = uop_CMP_JNBE_DEST(ir, IREG_flags_op1_W, IREG_flags_op2_W); break; case FLAGS_SUB32: - jump_uop = uop_CMP_JNBE_DEST(ir, IREG_flags_op1, IREG_flags_op2); + if (do_unroll) + jump_uop = uop_CMP_JBE_DEST(ir, IREG_flags_op1, IREG_flags_op2); + else + jump_uop = uop_CMP_JNBE_DEST(ir, IREG_flags_op1, IREG_flags_op2); break; case FLAGS_UNKNOWN: default: - uop_CALL_FUNC_RESULT(ir, IREG_temp0, CF_SET); - jump_uop2 = uop_CMP_IMM_JNZ_DEST(ir, IREG_temp0, 0); - uop_CALL_FUNC_RESULT(ir, IREG_temp0, ZF_SET); - jump_uop = uop_CMP_IMM_JZ_DEST(ir, IREG_temp0, 0); + if (do_unroll) + { + uop_CALL_FUNC_RESULT(ir, IREG_temp0, CF_SET); + jump_uop2 = uop_CMP_IMM_JNZ_DEST(ir, IREG_temp0, 0); + uop_CALL_FUNC_RESULT(ir, IREG_temp0, ZF_SET); + jump_uop = uop_CMP_IMM_JNZ_DEST(ir, IREG_temp0, 0); + } + else + { + uop_CALL_FUNC_RESULT(ir, IREG_temp0, CF_SET); + jump_uop2 = uop_CMP_IMM_JNZ_DEST(ir, IREG_temp0, 0); + uop_CALL_FUNC_RESULT(ir, IREG_temp0, ZF_SET); + jump_uop = uop_CMP_IMM_JZ_DEST(ir, IREG_temp0, 0); + } break; } - if (jump_uop2 != -1) - uop_set_jump_dest(ir, jump_uop2); - uop_MOV_IMM(ir, IREG_pc, dest_addr); - uop_JMP(ir, codegen_exit_rout); - uop_set_jump_dest(ir, jump_uop); + if (do_unroll) + { + uop_MOV_IMM(ir, IREG_pc, next_pc); + uop_JMP(ir, codegen_exit_rout); + uop_set_jump_dest(ir, jump_uop); + if (jump_uop2 != -1) + uop_set_jump_dest(ir, jump_uop2); + return 1; + } + else + { + if (jump_uop2 != -1) + uop_set_jump_dest(ir, jump_uop2); + uop_MOV_IMM(ir, IREG_pc, dest_addr); + uop_JMP(ir, codegen_exit_rout); + uop_set_jump_dest(ir, jump_uop); + return 0; + } } -static void ropJNBE_common(codeblock_t *block, ir_data_t *ir, uint32_t dest_addr) +static int ropJNBE_common(codeblock_t *block, ir_data_t *ir, uint32_t dest_addr, uint32_t next_pc) { int jump_uop, jump_uop2 = -1; + int do_unroll = ((!CF_SET() && !ZF_SET()) && codegen_can_unroll(block, ir, next_pc, dest_addr)); switch (codegen_flags_changed ? cpu_state.flags_op : FLAGS_UNKNOWN) { case FLAGS_ZN8: case FLAGS_ZN16: case FLAGS_ZN32: /*Carry is always zero, so test zero only*/ - jump_uop = uop_CMP_IMM_JZ_DEST(ir, IREG_flags_res, 0); + if (do_unroll) + jump_uop = uop_CMP_IMM_JNZ_DEST(ir, IREG_flags_res, 0); + else + jump_uop = uop_CMP_IMM_JZ_DEST(ir, IREG_flags_res, 0); break; case FLAGS_SUB8: - jump_uop = uop_CMP_JBE_DEST(ir, IREG_flags_op1_B, IREG_flags_op2_B); + if (do_unroll) + jump_uop = uop_CMP_JNBE_DEST(ir, IREG_flags_op1_B, IREG_flags_op2_B); + else + jump_uop = uop_CMP_JBE_DEST(ir, IREG_flags_op1_B, IREG_flags_op2_B); break; case FLAGS_SUB16: - jump_uop = uop_CMP_JBE_DEST(ir, IREG_flags_op1_W, IREG_flags_op2_W); + if (do_unroll) + jump_uop = uop_CMP_JNBE_DEST(ir, IREG_flags_op1_W, IREG_flags_op2_W); + else + jump_uop = uop_CMP_JBE_DEST(ir, IREG_flags_op1_W, IREG_flags_op2_W); break; case FLAGS_SUB32: - jump_uop = uop_CMP_JBE_DEST(ir, IREG_flags_op1, IREG_flags_op2); + if (do_unroll) + jump_uop = uop_CMP_JNBE_DEST(ir, IREG_flags_op1, IREG_flags_op2); + else + jump_uop = uop_CMP_JBE_DEST(ir, IREG_flags_op1, IREG_flags_op2); break; case FLAGS_UNKNOWN: default: - uop_CALL_FUNC_RESULT(ir, IREG_temp0, CF_SET); - jump_uop = uop_CMP_IMM_JNZ_DEST(ir, IREG_temp0, 0); - uop_CALL_FUNC_RESULT(ir, IREG_temp0, ZF_SET); - jump_uop2 = uop_CMP_IMM_JNZ_DEST(ir, IREG_temp0, 0); + if (do_unroll) + { + uop_CALL_FUNC_RESULT(ir, IREG_temp0, CF_SET); + jump_uop2 = uop_CMP_IMM_JNZ_DEST(ir, IREG_temp0, 0); + uop_CALL_FUNC_RESULT(ir, IREG_temp0, ZF_SET); + jump_uop = uop_CMP_IMM_JZ_DEST(ir, IREG_temp0, 0); + } + else + { + uop_CALL_FUNC_RESULT(ir, IREG_temp0, CF_SET); + jump_uop = uop_CMP_IMM_JNZ_DEST(ir, IREG_temp0, 0); + uop_CALL_FUNC_RESULT(ir, IREG_temp0, ZF_SET); + jump_uop2 = uop_CMP_IMM_JNZ_DEST(ir, IREG_temp0, 0); + } break; } - uop_MOV_IMM(ir, IREG_pc, dest_addr); - uop_JMP(ir, codegen_exit_rout); - uop_set_jump_dest(ir, jump_uop); - if (jump_uop2 != -1) - uop_set_jump_dest(ir, jump_uop2); + if (do_unroll) + { + if (jump_uop2 != -1) + uop_set_jump_dest(ir, jump_uop2); + uop_MOV_IMM(ir, IREG_pc, next_pc); + uop_JMP(ir, codegen_exit_rout); + uop_set_jump_dest(ir, jump_uop); + return 1; + } + else + { + uop_MOV_IMM(ir, IREG_pc, dest_addr); + uop_JMP(ir, codegen_exit_rout); + uop_set_jump_dest(ir, jump_uop); + if (jump_uop2 != -1) + uop_set_jump_dest(ir, jump_uop2); + return 0; + } } -static void ropJS_common(codeblock_t *block, ir_data_t *ir, uint32_t dest_addr) +static int ropJS_common(codeblock_t *block, ir_data_t *ir, uint32_t dest_addr, uint32_t next_pc) { int jump_uop; + int do_unroll = (NF_SET() && codegen_can_unroll(block, ir, next_pc, dest_addr)); switch (codegen_flags_changed ? cpu_state.flags_op : FLAGS_UNKNOWN) { @@ -273,7 +416,10 @@ static void ropJS_common(codeblock_t *block, ir_data_t *ir, uint32_t dest_addr) case FLAGS_SAR8: case FLAGS_INC8: case FLAGS_DEC8: - jump_uop = uop_TEST_JNS_DEST(ir, IREG_flags_res_B); + if (do_unroll) + jump_uop = uop_TEST_JS_DEST(ir, IREG_flags_res_B); + else + jump_uop = uop_TEST_JNS_DEST(ir, IREG_flags_res_B); break; case FLAGS_ZN16: @@ -284,7 +430,10 @@ static void ropJS_common(codeblock_t *block, ir_data_t *ir, uint32_t dest_addr) case FLAGS_SAR16: case FLAGS_INC16: case FLAGS_DEC16: - jump_uop = uop_TEST_JNS_DEST(ir, IREG_flags_res_W); + if (do_unroll) + jump_uop = uop_TEST_JS_DEST(ir, IREG_flags_res_W); + else + jump_uop = uop_TEST_JNS_DEST(ir, IREG_flags_res_W); break; case FLAGS_ZN32: @@ -295,22 +444,30 @@ static void ropJS_common(codeblock_t *block, ir_data_t *ir, uint32_t dest_addr) case FLAGS_SAR32: case FLAGS_INC32: case FLAGS_DEC32: - jump_uop = uop_TEST_JNS_DEST(ir, IREG_flags_res); + if (do_unroll) + jump_uop = uop_TEST_JS_DEST(ir, IREG_flags_res); + else + jump_uop = uop_TEST_JNS_DEST(ir, IREG_flags_res); break; case FLAGS_UNKNOWN: default: uop_CALL_FUNC_RESULT(ir, IREG_temp0, NF_SET); - jump_uop = uop_CMP_IMM_JZ_DEST(ir, IREG_temp0, 0); + if (do_unroll) + jump_uop = uop_CMP_IMM_JNZ_DEST(ir, IREG_temp0, 0); + else + jump_uop = uop_CMP_IMM_JZ_DEST(ir, IREG_temp0, 0); break; } - uop_MOV_IMM(ir, IREG_pc, dest_addr); + uop_MOV_IMM(ir, IREG_pc, do_unroll ? next_pc : dest_addr); uop_JMP(ir, codegen_exit_rout); uop_set_jump_dest(ir, jump_uop); + return do_unroll ? 1 : 0; } -static void ropJNS_common(codeblock_t *block, ir_data_t *ir, uint32_t dest_addr) +static int ropJNS_common(codeblock_t *block, ir_data_t *ir, uint32_t dest_addr, uint32_t next_pc) { int jump_uop; + int do_unroll = (!NF_SET() && codegen_can_unroll(block, ir, next_pc, dest_addr)); switch (codegen_flags_changed ? cpu_state.flags_op : FLAGS_UNKNOWN) { @@ -322,7 +479,10 @@ static void ropJNS_common(codeblock_t *block, ir_data_t *ir, uint32_t dest_addr) case FLAGS_SAR8: case FLAGS_INC8: case FLAGS_DEC8: - jump_uop = uop_TEST_JS_DEST(ir, IREG_flags_res_B); + if (do_unroll) + jump_uop = uop_TEST_JNS_DEST(ir, IREG_flags_res_B); + else + jump_uop = uop_TEST_JS_DEST(ir, IREG_flags_res_B); break; case FLAGS_ZN16: @@ -333,7 +493,10 @@ static void ropJNS_common(codeblock_t *block, ir_data_t *ir, uint32_t dest_addr) case FLAGS_SAR16: case FLAGS_INC16: case FLAGS_DEC16: - jump_uop = uop_TEST_JS_DEST(ir, IREG_flags_res_W); + if (do_unroll) + jump_uop = uop_TEST_JNS_DEST(ir, IREG_flags_res_W); + else + jump_uop = uop_TEST_JS_DEST(ir, IREG_flags_res_W); break; case FLAGS_ZN32: @@ -344,21 +507,28 @@ static void ropJNS_common(codeblock_t *block, ir_data_t *ir, uint32_t dest_addr) case FLAGS_SAR32: case FLAGS_INC32: case FLAGS_DEC32: - jump_uop = uop_TEST_JS_DEST(ir, IREG_flags_res); + if (do_unroll) + jump_uop = uop_TEST_JNS_DEST(ir, IREG_flags_res); + else + jump_uop = uop_TEST_JS_DEST(ir, IREG_flags_res); break; case FLAGS_UNKNOWN: default: uop_CALL_FUNC_RESULT(ir, IREG_temp0, NF_SET); - jump_uop = uop_CMP_IMM_JNZ_DEST(ir, IREG_temp0, 0); + if (do_unroll) + jump_uop = uop_CMP_IMM_JZ_DEST(ir, IREG_temp0, 0); + else + jump_uop = uop_CMP_IMM_JNZ_DEST(ir, IREG_temp0, 0); break; } - uop_MOV_IMM(ir, IREG_pc, dest_addr); + uop_MOV_IMM(ir, IREG_pc, do_unroll ? next_pc : dest_addr); uop_JMP(ir, codegen_exit_rout); uop_set_jump_dest(ir, jump_uop); + return do_unroll ? 1 : 0; } -static void ropJP_common(codeblock_t *block, ir_data_t *ir, uint32_t dest_addr) +static int ropJP_common(codeblock_t *block, ir_data_t *ir, uint32_t dest_addr, uint32_t next_pc) { int jump_uop; @@ -367,8 +537,9 @@ static void ropJP_common(codeblock_t *block, ir_data_t *ir, uint32_t dest_addr) uop_MOV_IMM(ir, IREG_pc, dest_addr); uop_JMP(ir, codegen_exit_rout); uop_set_jump_dest(ir, jump_uop); + return 0; } -static void ropJNP_common(codeblock_t *block, ir_data_t *ir, uint32_t dest_addr) +static int ropJNP_common(codeblock_t *block, ir_data_t *ir, uint32_t dest_addr, uint32_t next_pc) { int jump_uop; @@ -377,146 +548,267 @@ static void ropJNP_common(codeblock_t *block, ir_data_t *ir, uint32_t dest_addr) uop_MOV_IMM(ir, IREG_pc, dest_addr); uop_JMP(ir, codegen_exit_rout); uop_set_jump_dest(ir, jump_uop); + return 0; } -static void ropJL_common(codeblock_t *block, ir_data_t *ir, uint32_t dest_addr) +static int ropJL_common(codeblock_t *block, ir_data_t *ir, uint32_t dest_addr, uint32_t next_pc) { int jump_uop; + int do_unroll = ((NF_SET() ? 1 : 0) != (VF_SET() ? 1 : 0) && codegen_can_unroll(block, ir, next_pc, dest_addr)); switch (codegen_flags_changed ? cpu_state.flags_op : FLAGS_UNKNOWN) { case FLAGS_ZN8: /*V flag is always clear. Condition is true if N is set*/ - jump_uop = uop_TEST_JNS_DEST(ir, IREG_flags_res_B); + if (do_unroll) + jump_uop = uop_TEST_JS_DEST(ir, IREG_flags_res_B); + else + jump_uop = uop_TEST_JNS_DEST(ir, IREG_flags_res_B); break; case FLAGS_ZN16: - jump_uop = uop_TEST_JNS_DEST(ir, IREG_flags_res_W); + if (do_unroll) + jump_uop = uop_TEST_JS_DEST(ir, IREG_flags_res_W); + else + jump_uop = uop_TEST_JNS_DEST(ir, IREG_flags_res_W); break; case FLAGS_ZN32: - jump_uop = uop_TEST_JNS_DEST(ir, IREG_flags_res); + if (do_unroll) + jump_uop = uop_TEST_JS_DEST(ir, IREG_flags_res); + else + jump_uop = uop_TEST_JNS_DEST(ir, IREG_flags_res); break; case FLAGS_SUB8: case FLAGS_DEC8: - jump_uop = uop_CMP_JNL_DEST(ir, IREG_flags_op1_B, IREG_flags_op2_B); + if (do_unroll) + jump_uop = uop_CMP_JL_DEST(ir, IREG_flags_op1_B, IREG_flags_op2_B); + else + jump_uop = uop_CMP_JNL_DEST(ir, IREG_flags_op1_B, IREG_flags_op2_B); break; case FLAGS_SUB16: case FLAGS_DEC16: - jump_uop = uop_CMP_JNL_DEST(ir, IREG_flags_op1_W, IREG_flags_op2_W); + if (do_unroll) + jump_uop = uop_CMP_JL_DEST(ir, IREG_flags_op1_W, IREG_flags_op2_W); + else + jump_uop = uop_CMP_JNL_DEST(ir, IREG_flags_op1_W, IREG_flags_op2_W); break; case FLAGS_SUB32: case FLAGS_DEC32: - jump_uop = uop_CMP_JNL_DEST(ir, IREG_flags_op1, IREG_flags_op2); + if (do_unroll) + jump_uop = uop_CMP_JL_DEST(ir, IREG_flags_op1, IREG_flags_op2); + else + jump_uop = uop_CMP_JNL_DEST(ir, IREG_flags_op1, IREG_flags_op2); break; case FLAGS_UNKNOWN: default: uop_CALL_FUNC_RESULT(ir, IREG_temp0, NF_SET_01); uop_CALL_FUNC_RESULT(ir, IREG_temp1, VF_SET_01); - jump_uop = uop_CMP_JZ_DEST(ir, IREG_temp0, IREG_temp1); + if (do_unroll) + jump_uop = uop_CMP_JNZ_DEST(ir, IREG_temp0, IREG_temp1); + else + jump_uop = uop_CMP_JZ_DEST(ir, IREG_temp0, IREG_temp1); break; } - uop_MOV_IMM(ir, IREG_pc, dest_addr); + if (do_unroll) + uop_MOV_IMM(ir, IREG_pc, next_pc); + else + uop_MOV_IMM(ir, IREG_pc, dest_addr); uop_JMP(ir, codegen_exit_rout); uop_set_jump_dest(ir, jump_uop); + return do_unroll ? 1 : 0; } -static void ropJNL_common(codeblock_t *block, ir_data_t *ir, uint32_t dest_addr) +static int ropJNL_common(codeblock_t *block, ir_data_t *ir, uint32_t dest_addr, uint32_t next_pc) { int jump_uop; - + int do_unroll = ((NF_SET() ? 1 : 0) == (VF_SET() ? 1 : 0) && codegen_can_unroll(block, ir, next_pc, dest_addr)); + switch (codegen_flags_changed ? cpu_state.flags_op : FLAGS_UNKNOWN) { case FLAGS_ZN8: /*V flag is always clear. Condition is true if N is set*/ - jump_uop = uop_TEST_JS_DEST(ir, IREG_flags_res_B); + if (do_unroll) + jump_uop = uop_TEST_JNS_DEST(ir, IREG_flags_res_B); + else + jump_uop = uop_TEST_JS_DEST(ir, IREG_flags_res_B); break; case FLAGS_ZN16: - jump_uop = uop_TEST_JS_DEST(ir, IREG_flags_res_W); + if (do_unroll) + jump_uop = uop_TEST_JNS_DEST(ir, IREG_flags_res_W); + else + jump_uop = uop_TEST_JS_DEST(ir, IREG_flags_res_W); break; case FLAGS_ZN32: - jump_uop = uop_TEST_JS_DEST(ir, IREG_flags_res); + if (do_unroll) + jump_uop = uop_TEST_JNS_DEST(ir, IREG_flags_res); + else + jump_uop = uop_TEST_JS_DEST(ir, IREG_flags_res); break; case FLAGS_SUB8: case FLAGS_DEC8: - jump_uop = uop_CMP_JL_DEST(ir, IREG_flags_op1_B, IREG_flags_op2_B); + if (do_unroll) + jump_uop = uop_CMP_JNL_DEST(ir, IREG_flags_op1_B, IREG_flags_op2_B); + else + jump_uop = uop_CMP_JL_DEST(ir, IREG_flags_op1_B, IREG_flags_op2_B); break; case FLAGS_SUB16: case FLAGS_DEC16: - jump_uop = uop_CMP_JL_DEST(ir, IREG_flags_op1_W, IREG_flags_op2_W); + if (do_unroll) + jump_uop = uop_CMP_JNL_DEST(ir, IREG_flags_op1_W, IREG_flags_op2_W); + else + jump_uop = uop_CMP_JL_DEST(ir, IREG_flags_op1_W, IREG_flags_op2_W); break; case FLAGS_SUB32: case FLAGS_DEC32: - jump_uop = uop_CMP_JL_DEST(ir, IREG_flags_op1, IREG_flags_op2); + if (do_unroll) + jump_uop = uop_CMP_JNL_DEST(ir, IREG_flags_op1, IREG_flags_op2); + else + jump_uop = uop_CMP_JL_DEST(ir, IREG_flags_op1, IREG_flags_op2); break; case FLAGS_UNKNOWN: default: uop_CALL_FUNC_RESULT(ir, IREG_temp0, NF_SET_01); uop_CALL_FUNC_RESULT(ir, IREG_temp1, VF_SET_01); - jump_uop = uop_CMP_JNZ_DEST(ir, IREG_temp0, IREG_temp1); + if (do_unroll) + jump_uop = uop_CMP_JZ_DEST(ir, IREG_temp0, IREG_temp1); + else + jump_uop = uop_CMP_JNZ_DEST(ir, IREG_temp0, IREG_temp1); break; } - uop_MOV_IMM(ir, IREG_pc, dest_addr); + if (do_unroll) + uop_MOV_IMM(ir, IREG_pc, next_pc); + else + uop_MOV_IMM(ir, IREG_pc, dest_addr); uop_JMP(ir, codegen_exit_rout); uop_set_jump_dest(ir, jump_uop); + return do_unroll ? 1 : 0; } -static void ropJLE_common(codeblock_t *block, ir_data_t *ir, uint32_t dest_addr) +static int ropJLE_common(codeblock_t *block, ir_data_t *ir, uint32_t dest_addr, uint32_t next_pc) { int jump_uop, jump_uop2 = -1; + int do_unroll = (((NF_SET() ? 1 : 0) != (VF_SET() ? 1 : 0) || ZF_SET()) && codegen_can_unroll(block, ir, next_pc, dest_addr)); switch (codegen_flags_changed ? cpu_state.flags_op : FLAGS_UNKNOWN) { case FLAGS_SUB8: case FLAGS_DEC8: - jump_uop = uop_CMP_JNLE_DEST(ir, IREG_flags_op1_B, IREG_flags_op2_B); + if (do_unroll) + jump_uop = uop_CMP_JLE_DEST(ir, IREG_flags_op1_B, IREG_flags_op2_B); + else + jump_uop = uop_CMP_JNLE_DEST(ir, IREG_flags_op1_B, IREG_flags_op2_B); break; case FLAGS_SUB16: case FLAGS_DEC16: - jump_uop = uop_CMP_JNLE_DEST(ir, IREG_flags_op1_W, IREG_flags_op2_W); + if (do_unroll) + jump_uop = uop_CMP_JLE_DEST(ir, IREG_flags_op1_W, IREG_flags_op2_W); + else + jump_uop = uop_CMP_JNLE_DEST(ir, IREG_flags_op1_W, IREG_flags_op2_W); break; case FLAGS_SUB32: case FLAGS_DEC32: - jump_uop = uop_CMP_JNLE_DEST(ir, IREG_flags_op1, IREG_flags_op2); + if (do_unroll) + jump_uop = uop_CMP_JLE_DEST(ir, IREG_flags_op1, IREG_flags_op2); + else + jump_uop = uop_CMP_JNLE_DEST(ir, IREG_flags_op1, IREG_flags_op2); break; case FLAGS_UNKNOWN: default: - uop_CALL_FUNC_RESULT(ir, IREG_temp0, ZF_SET); - jump_uop2 = uop_CMP_IMM_JNZ_DEST(ir, IREG_temp0, 0); - uop_CALL_FUNC_RESULT(ir, IREG_temp0, NF_SET_01); - uop_CALL_FUNC_RESULT(ir, IREG_temp1, VF_SET_01); - jump_uop = uop_CMP_JZ_DEST(ir, IREG_temp0, IREG_temp1); + if (do_unroll) + { + uop_CALL_FUNC_RESULT(ir, IREG_temp0, ZF_SET); + jump_uop2 = uop_CMP_IMM_JNZ_DEST(ir, IREG_temp0, 0); + uop_CALL_FUNC_RESULT(ir, IREG_temp0, NF_SET_01); + uop_CALL_FUNC_RESULT(ir, IREG_temp1, VF_SET_01); + jump_uop = uop_CMP_JNZ_DEST(ir, IREG_temp0, IREG_temp1); + } + else + { + uop_CALL_FUNC_RESULT(ir, IREG_temp0, ZF_SET); + jump_uop2 = uop_CMP_IMM_JNZ_DEST(ir, IREG_temp0, 0); + uop_CALL_FUNC_RESULT(ir, IREG_temp0, NF_SET_01); + uop_CALL_FUNC_RESULT(ir, IREG_temp1, VF_SET_01); + jump_uop = uop_CMP_JZ_DEST(ir, IREG_temp0, IREG_temp1); + } break; } - if (jump_uop2 != -1) - uop_set_jump_dest(ir, jump_uop2); - uop_MOV_IMM(ir, IREG_pc, dest_addr); - uop_JMP(ir, codegen_exit_rout); - uop_set_jump_dest(ir, jump_uop); + if (do_unroll) + { + uop_MOV_IMM(ir, IREG_pc, next_pc); + uop_JMP(ir, codegen_exit_rout); + uop_set_jump_dest(ir, jump_uop); + if (jump_uop2 != -1) + uop_set_jump_dest(ir, jump_uop2); + return 1; + } + else + { + if (jump_uop2 != -1) + uop_set_jump_dest(ir, jump_uop2); + uop_MOV_IMM(ir, IREG_pc, dest_addr); + uop_JMP(ir, codegen_exit_rout); + uop_set_jump_dest(ir, jump_uop); + return 0; + } } -static void ropJNLE_common(codeblock_t *block, ir_data_t *ir, uint32_t dest_addr) +static int ropJNLE_common(codeblock_t *block, ir_data_t *ir, uint32_t dest_addr, uint32_t next_pc) { int jump_uop, jump_uop2 = -1; + int do_unroll = ((NF_SET() ? 1 : 0) == (VF_SET() ? 1 : 0) && !ZF_SET() && codegen_can_unroll(block, ir, next_pc, dest_addr)); switch (codegen_flags_changed ? cpu_state.flags_op : FLAGS_UNKNOWN) { case FLAGS_SUB8: case FLAGS_DEC8: - jump_uop = uop_CMP_JLE_DEST(ir, IREG_flags_op1_B, IREG_flags_op2_B); + if (do_unroll) + jump_uop = uop_CMP_JNLE_DEST(ir, IREG_flags_op1_B, IREG_flags_op2_B); + else + jump_uop = uop_CMP_JLE_DEST(ir, IREG_flags_op1_B, IREG_flags_op2_B); break; case FLAGS_SUB16: case FLAGS_DEC16: - jump_uop = uop_CMP_JLE_DEST(ir, IREG_flags_op1_W, IREG_flags_op2_W); + if (do_unroll) + jump_uop = uop_CMP_JNLE_DEST(ir, IREG_flags_op1_W, IREG_flags_op2_W); + else + jump_uop = uop_CMP_JLE_DEST(ir, IREG_flags_op1_W, IREG_flags_op2_W); break; case FLAGS_SUB32: case FLAGS_DEC32: - jump_uop = uop_CMP_JLE_DEST(ir, IREG_flags_op1, IREG_flags_op2); + if (do_unroll) + jump_uop = uop_CMP_JNLE_DEST(ir, IREG_flags_op1, IREG_flags_op2); + else + jump_uop = uop_CMP_JLE_DEST(ir, IREG_flags_op1, IREG_flags_op2); break; case FLAGS_UNKNOWN: default: - uop_CALL_FUNC_RESULT(ir, IREG_temp0, ZF_SET); - jump_uop2 = uop_CMP_IMM_JNZ_DEST(ir, IREG_temp0, 0); - uop_CALL_FUNC_RESULT(ir, IREG_temp0, NF_SET_01); - uop_CALL_FUNC_RESULT(ir, IREG_temp1, VF_SET_01); - jump_uop = uop_CMP_JNZ_DEST(ir, IREG_temp0, IREG_temp1); + if (do_unroll) + { + uop_CALL_FUNC_RESULT(ir, IREG_temp0, ZF_SET); + jump_uop2 = uop_CMP_IMM_JNZ_DEST(ir, IREG_temp0, 0); + uop_CALL_FUNC_RESULT(ir, IREG_temp0, NF_SET_01); + uop_CALL_FUNC_RESULT(ir, IREG_temp1, VF_SET_01); + jump_uop = uop_CMP_JZ_DEST(ir, IREG_temp0, IREG_temp1); + } + else + { + uop_CALL_FUNC_RESULT(ir, IREG_temp0, ZF_SET); + jump_uop2 = uop_CMP_IMM_JNZ_DEST(ir, IREG_temp0, 0); + uop_CALL_FUNC_RESULT(ir, IREG_temp0, NF_SET_01); + uop_CALL_FUNC_RESULT(ir, IREG_temp1, VF_SET_01); + jump_uop = uop_CMP_JNZ_DEST(ir, IREG_temp0, IREG_temp1); + } break; } - uop_MOV_IMM(ir, IREG_pc, dest_addr); - uop_JMP(ir, codegen_exit_rout); - uop_set_jump_dest(ir, jump_uop); - if (jump_uop2 != -1) - uop_set_jump_dest(ir, jump_uop2); + if (do_unroll) + { + if (jump_uop2 != -1) + uop_set_jump_dest(ir, jump_uop2); + uop_MOV_IMM(ir, IREG_pc, next_pc); + uop_JMP(ir, codegen_exit_rout); + uop_set_jump_dest(ir, jump_uop); + return 1; + } + else + { + uop_MOV_IMM(ir, IREG_pc, dest_addr); + uop_JMP(ir, codegen_exit_rout); + uop_set_jump_dest(ir, jump_uop); + if (jump_uop2 != -1) + uop_set_jump_dest(ir, jump_uop2); + return 0; + } } #define ropJ(cond) \ @@ -524,33 +816,36 @@ uint32_t ropJ ## cond ## _8(codeblock_t *block, ir_data_t *ir, uint8_t opcode, u { \ uint32_t offset = (int32_t)(int8_t)fastreadb(cs + op_pc); \ uint32_t dest_addr = op_pc + 1 + offset; \ + int ret; \ \ if (!(op_32 & 0x100)) \ dest_addr &= 0xffff; \ - ropJ ## cond ## _common(block, ir, dest_addr); \ + ret = ropJ ## cond ## _common(block, ir, dest_addr, op_pc+1); \ \ codegen_mark_code_present(block, cs+op_pc, 1); \ - return op_pc+1; \ + return ret ? dest_addr : (op_pc+1); \ } \ uint32_t ropJ ## cond ## _16(codeblock_t *block, ir_data_t *ir, uint8_t opcode, uint32_t fetchdat, uint32_t op_32, uint32_t op_pc) \ { \ uint32_t offset = (int32_t)(int16_t)fastreadw(cs + op_pc); \ uint32_t dest_addr = (op_pc + 2 + offset) & 0xffff; \ + int ret; \ \ - ropJ ## cond ## _common(block, ir, dest_addr); \ + ret = ropJ ## cond ## _common(block, ir, dest_addr, op_pc+2); \ \ codegen_mark_code_present(block, cs+op_pc, 2); \ - return op_pc+2; \ + return ret ? dest_addr : (op_pc+2); \ } \ uint32_t ropJ ## cond ## _32(codeblock_t *block, ir_data_t *ir, uint8_t opcode, uint32_t fetchdat, uint32_t op_32, uint32_t op_pc) \ { \ uint32_t offset = fastreadl(cs + op_pc); \ uint32_t dest_addr = op_pc + 4 + offset; \ + int ret; \ \ - ropJ ## cond ## _common(block, ir, dest_addr); \ + ret = ropJ ## cond ## _common(block, ir, dest_addr, op_pc+4); \ \ codegen_mark_code_present(block, cs+op_pc, 4); \ - return op_pc+4; \ + return ret ? dest_addr : (op_pc+4); \ } ropJ(O) @@ -596,27 +891,48 @@ uint32_t ropLOOP(codeblock_t *block, ir_data_t *ir, uint8_t opcode, uint32_t fet { uint32_t offset = (int32_t)(int8_t)fastreadb(cs + op_pc); uint32_t dest_addr = op_pc + 1 + offset; + uint32_t ret_addr; int jump_uop; if (!(op_32 & 0x100)) dest_addr &= 0xffff; - if (op_32 & 0x200) + if (((op_32 & 0x200) ? ECX : CX) != 1 && codegen_can_unroll(block, ir, op_pc+1, dest_addr)) { - uop_SUB_IMM(ir, IREG_ECX, IREG_ECX, 1); - jump_uop = uop_CMP_IMM_JZ_DEST(ir, IREG_ECX, 0); + if (op_32 & 0x200) + { + uop_SUB_IMM(ir, IREG_ECX, IREG_ECX, 1); + jump_uop = uop_CMP_IMM_JNZ_DEST(ir, IREG_ECX, 0); + } + else + { + uop_SUB_IMM(ir, IREG_CX, IREG_CX, 1); + jump_uop = uop_CMP_IMM_JNZ_DEST(ir, IREG_CX, 0); + } + uop_MOV_IMM(ir, IREG_pc, op_pc+1); + ret_addr = dest_addr; + CPU_BLOCK_END(); } else { - uop_SUB_IMM(ir, IREG_CX, IREG_CX, 1); - jump_uop = uop_CMP_IMM_JZ_DEST(ir, IREG_CX, 0); + if (op_32 & 0x200) + { + uop_SUB_IMM(ir, IREG_ECX, IREG_ECX, 1); + jump_uop = uop_CMP_IMM_JZ_DEST(ir, IREG_ECX, 0); + } + else + { + uop_SUB_IMM(ir, IREG_CX, IREG_CX, 1); + jump_uop = uop_CMP_IMM_JZ_DEST(ir, IREG_CX, 0); + } + uop_MOV_IMM(ir, IREG_pc, dest_addr); + ret_addr = op_pc+1; } - uop_MOV_IMM(ir, IREG_pc, dest_addr); uop_JMP(ir, codegen_exit_rout); uop_set_jump_dest(ir, jump_uop); codegen_mark_code_present(block, cs+op_pc, 1); - return op_pc+1; + return ret_addr; } uint32_t ropLOOPE(codeblock_t *block, ir_data_t *ir, uint8_t opcode, uint32_t fetchdat, uint32_t op_32, uint32_t op_pc) diff --git a/src/codegen_ops_helpers.c b/src/codegen_ops_helpers.c index 8ccd1fb..fdde53b 100644 --- a/src/codegen_ops_helpers.c +++ b/src/codegen_ops_helpers.c @@ -2,6 +2,7 @@ #include "x86.h" #include "386_common.h" #include "codegen.h" +#include "codegen_ir.h" #include "codegen_ir_defs.h" #include "codegen_reg.h" #include "codegen_ops_helpers.h" @@ -24,3 +25,47 @@ void LOAD_IMMEDIATE_FROM_RAM_32_unaligned(codeblock_t *block, ir_data_t *ir, int uop_SHL_IMM(ir, IREG_temp3, IREG_temp3, (4 - (addr & 3)) * 8); uop_OR(ir, dest_reg, dest_reg, IREG_temp3); } + +#define UNROLL_MAX_REG_REFERENCES 200 +#define UNROLL_MAX_UOPS 1000 +#define UNROLL_MAX_COUNT 10 +int codegen_can_unroll_full(codeblock_t *block, ir_data_t *ir, uint32_t next_pc, uint32_t dest_addr) +{ + int start; + int max_unroll; + + /*Check that dest instruction was actually compiled into block*/ + for (start = 0; start < ir->wr_pos; start++) + { +// pclog(" uOP %i %08x %08x\n", c, ir->uops[c].pc, dest_addr); + if (ir->uops[start].pc == dest_addr) + break; + if (ir->uops[start].pc > dest_addr) + { +// pclog("Went past dest_addr. start_pc=%08x end_pc=%08x dest_pc=%08x wr_pos=%i loop_size=%i\n", block->pc-cs, next_pc, dest_addr, ir->wr_pos, ir->wr_pos-start); + return 0; + } + } + /*Couldn't find any uOPs corresponding to the destination instruction*/ + if (start == ir->wr_pos) + { + /*Is instruction jumping to itself?*/ + if (dest_addr != cpu_state.oldpc) + { +// pclog("Couldn't find start. start_pc=%08x end_pc=%08x dest_pc=%08x wr_pos=%i loop_size=%i\n", block->pc-cs, next_pc, dest_addr, ir->wr_pos, ir->wr_pos-start); + return 0; + } + } + + max_unroll = UNROLL_MAX_UOPS / ((ir->wr_pos-start)+6); + if (max_unroll > (UNROLL_MAX_REG_REFERENCES / max_version_refcount)) + max_unroll = (UNROLL_MAX_REG_REFERENCES / max_version_refcount); + if (max_unroll > UNROLL_MAX_COUNT) + max_unroll = UNROLL_MAX_COUNT; + if (max_unroll <= 1) + return 0; + + codegen_ir_set_unroll(max_unroll, start); + + return 1; +} diff --git a/src/codegen_ops_helpers.h b/src/codegen_ops_helpers.h index e1e2bbc..ae3e5ed 100644 --- a/src/codegen_ops_helpers.h +++ b/src/codegen_ops_helpers.h @@ -109,3 +109,18 @@ static inline void LOAD_IMMEDIATE_FROM_RAM_32(codeblock_t *block, ir_data_t *ir, else uop_MOV_REG_PTR(ir, dest_reg, get_ram_ptr(addr)); } + +int codegen_can_unroll_full(codeblock_t *block, ir_data_t *ir, uint32_t next_pc, uint32_t dest_addr); +static inline int codegen_can_unroll(codeblock_t *block, ir_data_t *ir, uint32_t next_pc, uint32_t dest_addr) +{ + if (block->flags & CODEBLOCK_BYTE_MASK) + return 0; + + /*Is dest within block?*/ + if (dest_addr > next_pc) + return 0; + if ((cs+dest_addr) < block->pc) + return 0; + + return codegen_can_unroll_full(block, ir, next_pc, dest_addr); +} diff --git a/src/codegen_ops_jump.c b/src/codegen_ops_jump.c index dd417e4..5e4cda4 100644 --- a/src/codegen_ops_jump.c +++ b/src/codegen_ops_jump.c @@ -16,9 +16,10 @@ uint32_t ropJMP_r8(codeblock_t *block, ir_data_t *ir, uint8_t opcode, uint32_t f if (!(op_32 & 0x100)) dest_addr &= 0xffff; - uop_MOV_IMM(ir, IREG_pc, dest_addr); + if (offset < 0) + codegen_can_unroll(block, ir, op_pc+1, dest_addr); codegen_mark_code_present(block, cs+op_pc, 1); - return -1; + return dest_addr; } uint32_t ropJMP_r16(codeblock_t *block, ir_data_t *ir, uint8_t opcode, uint32_t fetchdat, uint32_t op_32, uint32_t op_pc) { @@ -27,18 +28,20 @@ uint32_t ropJMP_r16(codeblock_t *block, ir_data_t *ir, uint8_t opcode, uint32_t dest_addr &= 0xffff; - uop_MOV_IMM(ir, IREG_pc, dest_addr); + if (offset < 0) + codegen_can_unroll(block, ir, op_pc+1, dest_addr); codegen_mark_code_present(block, cs+op_pc, 2); - return -1; + return dest_addr; } uint32_t ropJMP_r32(codeblock_t *block, ir_data_t *ir, uint8_t opcode, uint32_t fetchdat, uint32_t op_32, uint32_t op_pc) { uint32_t offset = fastreadl(cs + op_pc); uint32_t dest_addr = op_pc+4+offset; - uop_MOV_IMM(ir, IREG_pc, dest_addr); + if (offset < 0) + codegen_can_unroll(block, ir, op_pc+1, dest_addr); codegen_mark_code_present(block, cs+op_pc, 4); - return -1; + return dest_addr; } uint32_t ropJMP_far_16(codeblock_t *block, ir_data_t *ir, uint8_t opcode, uint32_t fetchdat, uint32_t op_32, uint32_t op_pc) diff --git a/src/codegen_reg.c b/src/codegen_reg.c index dda256c..eb07cfb 100644 --- a/src/codegen_reg.c +++ b/src/codegen_reg.c @@ -4,6 +4,7 @@ #include "codegen_ir_defs.h" #include "codegen_reg.h" +int max_version_refcount; uint16_t reg_dead_list = 0; uint8_t reg_last_version[IREG_COUNT]; @@ -249,11 +250,7 @@ void codegen_reg_reset() } reg_dead_list = 0; -} - -static inline int ir_reg_is_invalid(ir_reg_t ir_reg) -{ - return (IREG_GET_REG(ir_reg.reg) == IREG_INVALID); + max_version_refcount = 0; } static inline int ir_get_refcount(ir_reg_t ir_reg) diff --git a/src/codegen_reg.h b/src/codegen_reg.h index 6be5cfe..bd790a9 100644 --- a/src/codegen_reg.h +++ b/src/codegen_reg.h @@ -315,7 +315,10 @@ extern ir_reg_t invalid_ir_reg; typedef uint16_t ir_host_reg_t; +extern int max_version_refcount; + #define REG_VERSION_MAX 250 +#define REG_REFCOUNT_MAX 250 static inline ir_reg_t codegen_reg_read(int reg) { @@ -332,8 +335,10 @@ static inline ir_reg_t codegen_reg_read(int reg) version->refcount++; if (!version->refcount) fatal("codegen_reg_read - refcount overflow\n"); - else if (version->refcount > REG_VERSION_MAX) + else if (version->refcount > REG_REFCOUNT_MAX) CPU_BLOCK_END(); + if (version->refcount > max_version_refcount) + max_version_refcount = version->refcount; // pclog("codegen_reg_read: %i %i %i\n", reg & IREG_REG_MASK, ireg.version, reg_version_refcount[IREG_GET_REG(ireg.reg)][ireg.version]); return ireg; } @@ -359,6 +364,9 @@ static inline ir_reg_t codegen_reg_write(int reg, int uop_nr) fatal("codegen_reg_write - version overflow\n"); else if (reg_last_version[IREG_GET_REG(reg)] > REG_VERSION_MAX) CPU_BLOCK_END(); + if (reg_last_version[IREG_GET_REG(reg)] > max_version_refcount) + max_version_refcount = reg_last_version[IREG_GET_REG(reg)]; + version = ®_version[IREG_GET_REG(reg)][ireg.version]; version->refcount = 0; version->flags = 0; @@ -367,6 +375,11 @@ static inline ir_reg_t codegen_reg_write(int reg, int uop_nr) return ireg; } +static inline int ir_reg_is_invalid(ir_reg_t ir_reg) +{ + return (IREG_GET_REG(ir_reg.reg) == IREG_INVALID); +} + struct ir_data_t; void codegen_reg_reset();