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

View file

@ -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);

View file

@ -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))

View file

@ -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)

View file

@ -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;
}

View file

@ -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);
}

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))
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)

View file

@ -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)

View file

@ -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 = &reg_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();