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.

This commit is contained in:
SarahW 2019-03-25 19:07:51 +00:00
commit 7e099c277e
9 changed files with 594 additions and 148 deletions

View file

@ -8,19 +8,71 @@
extern int has_ea; extern int has_ea;
static ir_data_t ir_block; static ir_data_t ir_block;
static int codegen_unroll_start, codegen_unroll_count;
ir_data_t *codegen_ir_init() ir_data_t *codegen_ir_init()
{ {
ir_block.wr_pos = 0; ir_block.wr_pos = 0;
// pclog("codegen_ir_init %04x:%04x\n", CS,cpu_state.pc); // pclog("codegen_ir_init %04x:%04x\n", CS,cpu_state.pc);
codegen_unroll_count = 0;
return &ir_block; 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) void codegen_ir_compile(ir_data_t *ir, codeblock_t *block)
{ {
int jump_target_at_end = -1; int jump_target_at_end = -1;
int c; 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_mark_as_required();
codegen_reg_process_dead_list(ir); codegen_reg_process_dead_list(ir);
block_write_data = codeblock_allocator_get_ptr(block->head_mem_block); block_write_data = codeblock_allocator_get_ptr(block->head_mem_block);

View file

@ -2,4 +2,5 @@
ir_data_t *codegen_ir_init(); 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); void codegen_ir_compile(ir_data_t *ir, codeblock_t *block);

View file

@ -336,6 +336,7 @@ typedef struct uop_t
int jump_dest_uop; int jump_dest_uop;
int jump_list_next; int jump_list_next;
void *jump_dest; void *jump_dest;
uint32_t pc;
} uop_t; } uop_t;
#define UOP_NR_MAX 4096 #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_b = invalid_ir_reg;
uop->src_reg_c = 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; uop->jump_list_next = -1;
if (uop_type & (UOP_TYPE_BARRIER | UOP_TYPE_ORDER_BARRIER)) if (uop_type & (UOP_TYPE_BARRIER | UOP_TYPE_ORDER_BARRIER))

View file

@ -19,7 +19,7 @@ static int VF_SET_01()
return VF_SET() ? 1 : 0; 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; 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: case FLAGS_ZN8: case FLAGS_ZN16: case FLAGS_ZN32:
/*Overflow is always zero*/ /*Overflow is always zero*/
return; return 0;
case FLAGS_SUB8: case FLAGS_DEC8: case FLAGS_SUB8: case FLAGS_DEC8:
jump_uop = uop_CMP_JNO_DEST(ir, IREG_flags_op1_B, IREG_flags_op2_B); 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_MOV_IMM(ir, IREG_pc, dest_addr);
uop_JMP(ir, codegen_exit_rout); uop_JMP(ir, codegen_exit_rout);
uop_set_jump_dest(ir, jump_uop); 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; 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*/ /*Overflow is always zero*/
uop_MOV_IMM(ir, IREG_pc, dest_addr); uop_MOV_IMM(ir, IREG_pc, dest_addr);
uop_JMP(ir, codegen_exit_rout); uop_JMP(ir, codegen_exit_rout);
return; return 0;
case FLAGS_SUB8: case FLAGS_DEC8: case FLAGS_SUB8: case FLAGS_DEC8:
jump_uop = uop_CMP_JO_DEST(ir, IREG_flags_op1_B, IREG_flags_op2_B); 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_MOV_IMM(ir, IREG_pc, dest_addr);
uop_JMP(ir, codegen_exit_rout); uop_JMP(ir, codegen_exit_rout);
uop_set_jump_dest(ir, jump_uop); 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 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) switch (codegen_flags_changed ? cpu_state.flags_op : FLAGS_UNKNOWN)
{ {
case FLAGS_ZN8: case FLAGS_ZN16: case FLAGS_ZN32: case FLAGS_ZN8: case FLAGS_ZN16: case FLAGS_ZN32:
/*Carry is always zero*/ /*Carry is always zero*/
return; return 0;
case FLAGS_SUB8: 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; break;
case FLAGS_SUB16: 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; break;
case FLAGS_SUB32: 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; break;
case FLAGS_UNKNOWN: case FLAGS_UNKNOWN:
default: default:
uop_CALL_FUNC_RESULT(ir, IREG_temp0, CF_SET); 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; 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_JMP(ir, codegen_exit_rout);
uop_set_jump_dest(ir, jump_uop); 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 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) 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*/ /*Carry is always zero*/
uop_MOV_IMM(ir, IREG_pc, dest_addr); uop_MOV_IMM(ir, IREG_pc, dest_addr);
uop_JMP(ir, codegen_exit_rout); uop_JMP(ir, codegen_exit_rout);
return; return 0;
case FLAGS_SUB8: 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; break;
case FLAGS_SUB16: 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; break;
case FLAGS_SUB32: 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; break;
case FLAGS_UNKNOWN: case FLAGS_UNKNOWN:
default: default:
uop_CALL_FUNC_RESULT(ir, IREG_temp0, CF_SET); 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; 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_JMP(ir, codegen_exit_rout);
uop_set_jump_dest(ir, jump_uop); 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; 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); if (!codegen_flags_changed || (cpu_state.flags_op == FLAGS_UNKNOWN))
jump_uop = uop_CMP_IMM_JZ_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
{
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 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); return 0;
uop_JMP(ir, codegen_exit_rout);
uop_set_jump_dest(ir, jump_uop);
} }
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; 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); if (!codegen_flags_changed || (cpu_state.flags_op == FLAGS_UNKNOWN))
jump_uop = 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
{
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 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); return 0;
uop_JMP(ir, codegen_exit_rout);
uop_set_jump_dest(ir, jump_uop);
} }
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 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) switch (codegen_flags_changed ? cpu_state.flags_op : FLAGS_UNKNOWN)
{ {
case FLAGS_ZN8: case FLAGS_ZN16: case FLAGS_ZN32: case FLAGS_ZN8: case FLAGS_ZN16: case FLAGS_ZN32:
/*Carry is always zero, so test zero only*/ /*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; break;
case FLAGS_SUB8: 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; break;
case FLAGS_SUB16: 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; break;
case FLAGS_SUB32: 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; break;
case FLAGS_UNKNOWN: case FLAGS_UNKNOWN:
default: default:
uop_CALL_FUNC_RESULT(ir, IREG_temp0, CF_SET); if (do_unroll)
jump_uop2 = uop_CMP_IMM_JNZ_DEST(ir, IREG_temp0, 0); {
uop_CALL_FUNC_RESULT(ir, IREG_temp0, ZF_SET); uop_CALL_FUNC_RESULT(ir, IREG_temp0, CF_SET);
jump_uop = uop_CMP_IMM_JZ_DEST(ir, IREG_temp0, 0); 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; break;
} }
if (jump_uop2 != -1) if (do_unroll)
uop_set_jump_dest(ir, jump_uop2); {
uop_MOV_IMM(ir, IREG_pc, dest_addr); uop_MOV_IMM(ir, IREG_pc, next_pc);
uop_JMP(ir, codegen_exit_rout); uop_JMP(ir, codegen_exit_rout);
uop_set_jump_dest(ir, jump_uop); 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 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) switch (codegen_flags_changed ? cpu_state.flags_op : FLAGS_UNKNOWN)
{ {
case FLAGS_ZN8: case FLAGS_ZN16: case FLAGS_ZN32: case FLAGS_ZN8: case FLAGS_ZN16: case FLAGS_ZN32:
/*Carry is always zero, so test zero only*/ /*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; break;
case FLAGS_SUB8: 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; break;
case FLAGS_SUB16: 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; break;
case FLAGS_SUB32: 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; break;
case FLAGS_UNKNOWN: case FLAGS_UNKNOWN:
default: default:
uop_CALL_FUNC_RESULT(ir, IREG_temp0, CF_SET); if (do_unroll)
jump_uop = uop_CMP_IMM_JNZ_DEST(ir, IREG_temp0, 0); {
uop_CALL_FUNC_RESULT(ir, IREG_temp0, ZF_SET); uop_CALL_FUNC_RESULT(ir, IREG_temp0, CF_SET);
jump_uop2 = uop_CMP_IMM_JNZ_DEST(ir, IREG_temp0, 0); 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; break;
} }
uop_MOV_IMM(ir, IREG_pc, dest_addr); if (do_unroll)
uop_JMP(ir, codegen_exit_rout); {
uop_set_jump_dest(ir, jump_uop); if (jump_uop2 != -1)
if (jump_uop2 != -1) uop_set_jump_dest(ir, jump_uop2);
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 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) 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_SAR8:
case FLAGS_INC8: case FLAGS_INC8:
case FLAGS_DEC8: 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; break;
case FLAGS_ZN16: 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_SAR16:
case FLAGS_INC16: case FLAGS_INC16:
case FLAGS_DEC16: 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; break;
case FLAGS_ZN32: 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_SAR32:
case FLAGS_INC32: case FLAGS_INC32:
case FLAGS_DEC32: 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; break;
case FLAGS_UNKNOWN: case FLAGS_UNKNOWN:
default: default:
uop_CALL_FUNC_RESULT(ir, IREG_temp0, NF_SET); 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; 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_JMP(ir, codegen_exit_rout);
uop_set_jump_dest(ir, jump_uop); 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 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) 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_SAR8:
case FLAGS_INC8: case FLAGS_INC8:
case FLAGS_DEC8: 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; break;
case FLAGS_ZN16: 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_SAR16:
case FLAGS_INC16: case FLAGS_INC16:
case FLAGS_DEC16: 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; break;
case FLAGS_ZN32: 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_SAR32:
case FLAGS_INC32: case FLAGS_INC32:
case FLAGS_DEC32: 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; break;
case FLAGS_UNKNOWN: case FLAGS_UNKNOWN:
default: default:
uop_CALL_FUNC_RESULT(ir, IREG_temp0, NF_SET); 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; 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_JMP(ir, codegen_exit_rout);
uop_set_jump_dest(ir, jump_uop); 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; 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_MOV_IMM(ir, IREG_pc, dest_addr);
uop_JMP(ir, codegen_exit_rout); uop_JMP(ir, codegen_exit_rout);
uop_set_jump_dest(ir, jump_uop); 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; 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_MOV_IMM(ir, IREG_pc, dest_addr);
uop_JMP(ir, codegen_exit_rout); uop_JMP(ir, codegen_exit_rout);
uop_set_jump_dest(ir, jump_uop); 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 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) switch (codegen_flags_changed ? cpu_state.flags_op : FLAGS_UNKNOWN)
{ {
case FLAGS_ZN8: case FLAGS_ZN8:
/*V flag is always clear. Condition is true if N is set*/ /*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; break;
case FLAGS_ZN16: 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; break;
case FLAGS_ZN32: 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; break;
case FLAGS_SUB8: case FLAGS_DEC8: 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; break;
case FLAGS_SUB16: case FLAGS_DEC16: 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; break;
case FLAGS_SUB32: case FLAGS_DEC32: 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; break;
case FLAGS_UNKNOWN: case FLAGS_UNKNOWN:
default: default:
uop_CALL_FUNC_RESULT(ir, IREG_temp0, NF_SET_01); uop_CALL_FUNC_RESULT(ir, IREG_temp0, NF_SET_01);
uop_CALL_FUNC_RESULT(ir, IREG_temp1, VF_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; 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_JMP(ir, codegen_exit_rout);
uop_set_jump_dest(ir, jump_uop); 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 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) switch (codegen_flags_changed ? cpu_state.flags_op : FLAGS_UNKNOWN)
{ {
case FLAGS_ZN8: case FLAGS_ZN8:
/*V flag is always clear. Condition is true if N is set*/ /*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; break;
case FLAGS_ZN16: 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; break;
case FLAGS_ZN32: 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; break;
case FLAGS_SUB8: case FLAGS_DEC8: 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; break;
case FLAGS_SUB16: case FLAGS_DEC16: 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; break;
case FLAGS_SUB32: case FLAGS_DEC32: 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; break;
case FLAGS_UNKNOWN: case FLAGS_UNKNOWN:
default: default:
uop_CALL_FUNC_RESULT(ir, IREG_temp0, NF_SET_01); uop_CALL_FUNC_RESULT(ir, IREG_temp0, NF_SET_01);
uop_CALL_FUNC_RESULT(ir, IREG_temp1, VF_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; 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_JMP(ir, codegen_exit_rout);
uop_set_jump_dest(ir, jump_uop); 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 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) switch (codegen_flags_changed ? cpu_state.flags_op : FLAGS_UNKNOWN)
{ {
case FLAGS_SUB8: case FLAGS_DEC8: 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; break;
case FLAGS_SUB16: case FLAGS_DEC16: 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; break;
case FLAGS_SUB32: case FLAGS_DEC32: 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; break;
case FLAGS_UNKNOWN: case FLAGS_UNKNOWN:
default: default:
uop_CALL_FUNC_RESULT(ir, IREG_temp0, ZF_SET); if (do_unroll)
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_temp0, ZF_SET);
uop_CALL_FUNC_RESULT(ir, IREG_temp1, VF_SET_01); jump_uop2 = uop_CMP_IMM_JNZ_DEST(ir, IREG_temp0, 0);
jump_uop = uop_CMP_JZ_DEST(ir, IREG_temp0, IREG_temp1); 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; break;
} }
if (jump_uop2 != -1) if (do_unroll)
uop_set_jump_dest(ir, jump_uop2); {
uop_MOV_IMM(ir, IREG_pc, dest_addr); uop_MOV_IMM(ir, IREG_pc, next_pc);
uop_JMP(ir, codegen_exit_rout); uop_JMP(ir, codegen_exit_rout);
uop_set_jump_dest(ir, jump_uop); 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 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) switch (codegen_flags_changed ? cpu_state.flags_op : FLAGS_UNKNOWN)
{ {
case FLAGS_SUB8: case FLAGS_DEC8: 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; break;
case FLAGS_SUB16: case FLAGS_DEC16: 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; break;
case FLAGS_SUB32: case FLAGS_DEC32: 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; break;
case FLAGS_UNKNOWN: case FLAGS_UNKNOWN:
default: default:
uop_CALL_FUNC_RESULT(ir, IREG_temp0, ZF_SET); if (do_unroll)
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_temp0, ZF_SET);
uop_CALL_FUNC_RESULT(ir, IREG_temp1, VF_SET_01); jump_uop2 = uop_CMP_IMM_JNZ_DEST(ir, IREG_temp0, 0);
jump_uop = uop_CMP_JNZ_DEST(ir, IREG_temp0, IREG_temp1); 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; break;
} }
uop_MOV_IMM(ir, IREG_pc, dest_addr); if (do_unroll)
uop_JMP(ir, codegen_exit_rout); {
uop_set_jump_dest(ir, jump_uop); if (jump_uop2 != -1)
if (jump_uop2 != -1) uop_set_jump_dest(ir, jump_uop2);
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) \ #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 offset = (int32_t)(int8_t)fastreadb(cs + op_pc); \
uint32_t dest_addr = op_pc + 1 + offset; \ uint32_t dest_addr = op_pc + 1 + offset; \
int ret; \
\ \
if (!(op_32 & 0x100)) \ if (!(op_32 & 0x100)) \
dest_addr &= 0xffff; \ 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); \ 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 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 offset = (int32_t)(int16_t)fastreadw(cs + op_pc); \
uint32_t dest_addr = (op_pc + 2 + offset) & 0xffff; \ 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); \ 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 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 offset = fastreadl(cs + op_pc); \
uint32_t dest_addr = op_pc + 4 + offset; \ 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); \ codegen_mark_code_present(block, cs+op_pc, 4); \
return op_pc+4; \ return ret ? dest_addr : (op_pc+4); \
} }
ropJ(O) 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 offset = (int32_t)(int8_t)fastreadb(cs + op_pc);
uint32_t dest_addr = op_pc + 1 + offset; uint32_t dest_addr = op_pc + 1 + offset;
uint32_t ret_addr;
int jump_uop; int jump_uop;
if (!(op_32 & 0x100)) if (!(op_32 & 0x100))
dest_addr &= 0xffff; 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); if (op_32 & 0x200)
jump_uop = uop_CMP_IMM_JZ_DEST(ir, IREG_ECX, 0); {
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 else
{ {
uop_SUB_IMM(ir, IREG_CX, IREG_CX, 1); if (op_32 & 0x200)
jump_uop = uop_CMP_IMM_JZ_DEST(ir, IREG_CX, 0); {
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_JMP(ir, codegen_exit_rout);
uop_set_jump_dest(ir, jump_uop); uop_set_jump_dest(ir, jump_uop);
codegen_mark_code_present(block, cs+op_pc, 1); 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) uint32_t ropLOOPE(codeblock_t *block, ir_data_t *ir, uint8_t opcode, uint32_t fetchdat, uint32_t op_32, uint32_t op_pc)

View file

@ -2,6 +2,7 @@
#include "x86.h" #include "x86.h"
#include "386_common.h" #include "386_common.h"
#include "codegen.h" #include "codegen.h"
#include "codegen_ir.h"
#include "codegen_ir_defs.h" #include "codegen_ir_defs.h"
#include "codegen_reg.h" #include "codegen_reg.h"
#include "codegen_ops_helpers.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_SHL_IMM(ir, IREG_temp3, IREG_temp3, (4 - (addr & 3)) * 8);
uop_OR(ir, dest_reg, dest_reg, IREG_temp3); 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;
}

View file

@ -109,3 +109,18 @@ static inline void LOAD_IMMEDIATE_FROM_RAM_32(codeblock_t *block, ir_data_t *ir,
else else
uop_MOV_REG_PTR(ir, dest_reg, get_ram_ptr(addr)); 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);
}

View file

@ -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)) if (!(op_32 & 0x100))
dest_addr &= 0xffff; 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); 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) 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; 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); 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 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 offset = fastreadl(cs + op_pc);
uint32_t dest_addr = op_pc+4+offset; 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); 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) 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)

View file

@ -4,6 +4,7 @@
#include "codegen_ir_defs.h" #include "codegen_ir_defs.h"
#include "codegen_reg.h" #include "codegen_reg.h"
int max_version_refcount;
uint16_t reg_dead_list = 0; uint16_t reg_dead_list = 0;
uint8_t reg_last_version[IREG_COUNT]; uint8_t reg_last_version[IREG_COUNT];
@ -249,11 +250,7 @@ void codegen_reg_reset()
} }
reg_dead_list = 0; reg_dead_list = 0;
} max_version_refcount = 0;
static inline int ir_reg_is_invalid(ir_reg_t ir_reg)
{
return (IREG_GET_REG(ir_reg.reg) == IREG_INVALID);
} }
static inline int ir_get_refcount(ir_reg_t ir_reg) static inline int ir_get_refcount(ir_reg_t ir_reg)

View file

@ -315,7 +315,10 @@ extern ir_reg_t invalid_ir_reg;
typedef uint16_t ir_host_reg_t; typedef uint16_t ir_host_reg_t;
extern int max_version_refcount;
#define REG_VERSION_MAX 250 #define REG_VERSION_MAX 250
#define REG_REFCOUNT_MAX 250
static inline ir_reg_t codegen_reg_read(int reg) 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++; version->refcount++;
if (!version->refcount) if (!version->refcount)
fatal("codegen_reg_read - refcount overflow\n"); fatal("codegen_reg_read - refcount overflow\n");
else if (version->refcount > REG_VERSION_MAX) else if (version->refcount > REG_REFCOUNT_MAX)
CPU_BLOCK_END(); 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]); // 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; 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"); fatal("codegen_reg_write - version overflow\n");
else if (reg_last_version[IREG_GET_REG(reg)] > REG_VERSION_MAX) else if (reg_last_version[IREG_GET_REG(reg)] > REG_VERSION_MAX)
CPU_BLOCK_END(); 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 = &reg_version[IREG_GET_REG(reg)][ireg.version]; version = &reg_version[IREG_GET_REG(reg)][ireg.version];
version->refcount = 0; version->refcount = 0;
version->flags = 0; version->flags = 0;
@ -367,6 +375,11 @@ static inline ir_reg_t codegen_reg_write(int reg, int uop_nr)
return ireg; 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; struct ir_data_t;
void codegen_reg_reset(); void codegen_reg_reset();