diff --git a/sourcepawn/vm/jit/x86/jit_x86.cpp b/sourcepawn/vm/jit/x86/jit_x86.cpp index 96f753f6..d6d473ce 100644 --- a/sourcepawn/vm/jit/x86/jit_x86.cpp +++ b/sourcepawn/vm/jit/x86/jit_x86.cpp @@ -1128,7 +1128,6 @@ inline void WriteOp_Jzer(JitWriter *jit) //jz cell_t target = jit->read_cell(); IA32_Test_Rm_Reg(jit, AMX_REG_PRI, AMX_REG_PRI, MOD_REG); - IA32_Jump_Imm32_Abs(jit, RelocLookup(jit, target, false)); IA32_Jump_Cond_Imm32_Abs(jit, CC_Z, RelocLookup(jit, target, false)); } @@ -1195,6 +1194,124 @@ inline void WriteOp_JsGeq(JitWriter *jit) IA32_Jump_Cond_Imm32(jit, CC_GE, RelocLookup(jit, target, false)); } +inline void WriteOp_Switch(JitWriter *jit) +{ + cell_t offs = jit->read_cell(); + cell_t *tbl = (cell_t *)((char *)jit->inbase + offs); + + struct casetbl + { + cell_t val; + cell_t offs; + }; + + /* Read the number of cases, then advance by one */ + cell_t num_cases = *tbl++; + if (!num_cases) + { + /* Special treatment for 0 cases */ + } else { + /* Check if the case layout is fully sequential */ + casetbl *iter = (casetbl *)(tbl + 1); + casetbl *cases = iter; + cell_t first = iter[0].val; + cell_t last = first; + bool sequential = true; + for (cell_t i=1; i HIGH) + * This check is valid for both sequential and non-sequential. + */ + cell_t low_bound = cases[0].val; + if (low_bound != 0) + { + /* negate it so we'll get a lower bound of 0 */ + //lea ecx, [eax-] + low_bound = -low_bound; + if (low_bound > SCHAR_MIN && low_bound < SCHAR_MAX) + { + IA32_Lea_DispRegImm8(jit, AMX_REG_TMP, AMX_REG_PRI, low_bound); + } else { + IA32_Lea_DispRegImm32(jit, AMX_REG_TMP, AMX_REG_PRI, low_bound); + } + } + cell_t high_bound = abs(cases[0].val - cases[num_cases-1].val); + //cmp ecx, + if (high_bound > SCHAR_MIN && high_bound < SCHAR_MAX) + { + IA32_Cmp_Rm_Imm8(jit, MOD_REG, AMX_REG_TMP, high_bound); + } else { + IA32_Cmp_Rm_Imm32(jit, MOD_REG, AMX_REG_TMP, high_bound); + } + //ja + IA32_Jump_Cond_Imm32_Abs(jit, CC_A, RelocLookup(jit, *tbl, false)); + + /** + * Now we've taken the default case out of the way, it's time to do the + * full check, which is different for sequential vs. non-sequential. + */ + if (sequential) + { + /* we're now theoretically guaranteed to be jumping to a correct offset. + * ECX still has the correctly bound offset in it, luckily! + * thus, we simply need to relocate ECX and store the cases. + */ + //shr ecx, 2 + //add ecx, + IA32_Shr_Rm_Imm8(jit, AMX_REG_TMP, 2, MOD_REG); + jitoffs_t tbl_offs = IA32_Add_Rm_Imm32_Later(jit, AMX_REG_TMP, MOD_REG); + IA32_Jump_Rm(jit, AMX_REG_TMP, MOD_MEM_REG); + /* The case table starts here. Go back and write the output pointer. */ + jitoffs_t cur_pos = jit->jit_curpos(); + jit->setpos(tbl_offs); + jit->write_uint32((jit_uint32_t)(jit->outbase + cur_pos)); + jit->setpos(cur_pos); + //now we can write the case table, finally! + jit_uint32_t base = (jit_uint32_t)jit->outbase; + for (cell_t i=0; iwrite_uint32(base + RelocLookup(jit, cases[i].offs, false)); + } + } else { + /* The slow version. Go through each case and generate a check. + * In the future we should replace case tables of more than ~8 cases with a + * hash table lookup. + */ + cell_t val; + for (cell_t i=0; i OR cmp al, + if (val > SCHAR_MIN && val < SCHAR_MAX) + { + IA32_Cmp_Al_Imm8(jit, val); + } else { + IA32_Cmp_Eax_Imm32(jit, cases[i].val); + } + IA32_Jump_Cond_Imm32_Abs(jit, CC_E, RelocLookup(jit, cases[i].offs, false)); + } + } + } +} + +inline void WriteOp_Casetbl(JitWriter *jit) +{ + /* do nothing here, switch does all ze work */ + cell_t num_cases = jit->read_cell(); + + /* Two cells per case, one extra case for the default jump */ + num_cases = (num_cases * 2) + 1; + jit->inptr += num_cases; +} + + /************************************************* ************************************************* * JIT PROPER ************************************ @@ -1255,9 +1372,6 @@ sp_context_t *JITX86::CompileToContext(ICompilation *co, int *err) AbortCompilation(co); *err = SP_ERR_INVALID_INSTRUCTION; return NULL; - } else if (op_c == -2) { - /* :TODO: get rid of this block */ - cip += sizeof(cell_t); } else if (op_c == -1) { switch (op) { diff --git a/sourcepawn/vm/jit/x86/opcode_helpers.cpp b/sourcepawn/vm/jit/x86/opcode_helpers.cpp index 30dfafba..1bd33902 100644 --- a/sourcepawn/vm/jit/x86/opcode_helpers.cpp +++ b/sourcepawn/vm/jit/x86/opcode_helpers.cpp @@ -546,6 +546,17 @@ JITX86::JITX86() OpAdvTable[OP_PUSH_ADR] = sizeof(cell_t); OpAdvTable[OP_PUSH_HEAP_C] = sizeof(cell_t); OpAdvTable[OP_SYSREQ_C] = sizeof(cell_t); + OpAdvTable[OP_CALL] = sizeof(cell_t); + OpAdvTable[OP_JUMP] = sizeof(cell_t); + OpAdvTable[OP_JZER] = sizeof(cell_t); + OpAdvTable[OP_JNZ] = sizeof(cell_t); + OpAdvTable[OP_JEQ] = sizeof(cell_t); + OpAdvTable[OP_JNEQ] = sizeof(cell_t); + OpAdvTable[OP_JSLESS] = sizeof(cell_t); + OpAdvTable[OP_JSLEQ] = sizeof(cell_t); + OpAdvTable[OP_JSGRTR] = sizeof(cell_t); + OpAdvTable[OP_JSGEQ] = sizeof(cell_t); + OpAdvTable[OP_SWITCH] = sizeof(cell_t); /* instructions with 0 parameters */ OpAdvTable[OP_LOAD_I] = 0; @@ -603,19 +614,6 @@ JITX86::JITX86() OpAdvTable[OP_POP_HEAP_PRI] = 0; OpAdvTable[OP_SYSREQ_PRI] = 0; - /* opcodes that need relocation */ - OpAdvTable[OP_CALL] = -2; - OpAdvTable[OP_JUMP] = -2; - OpAdvTable[OP_JZER] = -2; - OpAdvTable[OP_JNZ] = -2; - OpAdvTable[OP_JEQ] = -2; - OpAdvTable[OP_JNEQ] = -2; - OpAdvTable[OP_JSLESS] = -2; - OpAdvTable[OP_JSLEQ] = -2; - OpAdvTable[OP_JSGRTR] = -2; - OpAdvTable[OP_JSGEQ] = -2; - OpAdvTable[OP_SWITCH] = -2; - /* opcodes that are totally invalid */ /* :TODO: make an alternate table if USE_UNGEN_OPCODES is on? */ OpAdvTable[OP_FILE] = -3; diff --git a/sourcepawn/vm/jit/x86/opcode_helpers.h b/sourcepawn/vm/jit/x86/opcode_helpers.h index 20388dd2..7df128bc 100644 --- a/sourcepawn/vm/jit/x86/opcode_helpers.h +++ b/sourcepawn/vm/jit/x86/opcode_helpers.h @@ -187,8 +187,8 @@ typedef enum OP_SYMBOL, // !GEN DEPRECATED OP_SRANGE, // !GEN DEPRECATED OP_JUMP_PRI, // !GEN - OP_SWITCH, - OP_CASETBL, + OP_SWITCH, //DONE + OP_CASETBL, //DONE OP_SWAP_PRI, //DONE OP_SWAP_ALT, //DONE OP_PUSH_ADR, //DONE diff --git a/sourcepawn/vm/jit/x86/opcode_switch.inc b/sourcepawn/vm/jit/x86/opcode_switch.inc index 4ea20788..e3551f5c 100644 --- a/sourcepawn/vm/jit/x86/opcode_switch.inc +++ b/sourcepawn/vm/jit/x86/opcode_switch.inc @@ -618,6 +618,16 @@ WriteOp_Jsleq(jit); break; } + case OP_SWITCH: + { + WriteOp_Switch(jit); + break; + } + case OP_CASETBL: + { + WriteOp_Casetbl(jit); + break; + } #if defined USE_UNGEN_OPCODES #include "ungen_opcode_switch.inc" #endif diff --git a/sourcepawn/vm/jit/x86/x86_macros.h b/sourcepawn/vm/jit/x86/x86_macros.h index ea2977b7..e423b596 100644 --- a/sourcepawn/vm/jit/x86/x86_macros.h +++ b/sourcepawn/vm/jit/x86/x86_macros.h @@ -78,6 +78,7 @@ #define IA32_MOV_RM_IMM32 0xC7 // encoding is /0 #define IA32_CMP_RM_IMM32 0x81 // encoding is /7 #define IA32_CMP_RM_IMM8 0x83 // encoding is /7 +#define IA32_CMP_AL_IMM32 0x3C // no extra encoding #define IA32_CMP_EAX_IMM32 0x3D // no extra encoding #define IA32_CMPSB 0xA6 // no extra encoding #define IA32_TEST_RM_REG 0x85 // encoding is /r @@ -468,6 +469,17 @@ inline void IA32_Add_Rm_Imm32_Disp8(JitWriter *jit, jit->write_int32(val); } +inline jitoffs_t IA32_Add_Rm_Imm32_Later(JitWriter *jit, + jit_uint8_t dest, + jit_uint8_t mode) +{ + jit->write_ubyte(IA32_ADD_RM_IMM32); + jit->write_ubyte(ia32_modrm(mode, 0, dest)); + jitoffs_t ptr = jit->jit_curpos(); + jit->write_int32(0); + return ptr; +} + inline void IA32_Add_Rm_Imm8_Disp32(JitWriter *jit, jit_uint8_t dest, jit_int8_t val, @@ -814,6 +826,12 @@ inline void IA32_Jump_Reg(JitWriter *jit, jit_uint8_t reg) jit->write_ubyte(ia32_modrm(MOD_REG, 4, reg)); } +inline void IA32_Jump_Rm(JitWriter *jit, jit_uint8_t reg, jit_uint8_t mode) +{ + jit->write_ubyte(IA32_JMP_RM); + jit->write_ubyte(ia32_modrm(mode, 4, reg)); +} + inline jitoffs_t IA32_Call_Imm32(JitWriter *jit, jit_int32_t disp) { jitoffs_t ptr; @@ -945,6 +963,12 @@ inline void IA32_Cmp_Rm_Disp8_Imm8(JitWriter *jit, jit_uint8_t reg, jit_int8_t d jit->write_byte(imm8); } +inline void IA32_Cmp_Al_Imm8(JitWriter *jit, jit_int8_t value) +{ + jit->write_ubyte(IA32_CMP_AL_IMM32); + jit->write_byte(value); +} + inline void IA32_Cmp_Eax_Imm32(JitWriter *jit, jit_int32_t value) { jit->write_ubyte(IA32_CMP_EAX_IMM32);