diff --git a/sourcepawn/include/sp_vm_types.h b/sourcepawn/include/sp_vm_types.h index 3866174a..f89a5be4 100644 --- a/sourcepawn/include/sp_vm_types.h +++ b/sourcepawn/include/sp_vm_types.h @@ -25,6 +25,7 @@ typedef int32_t cell_t; #define SP_ERR_STACKMIN 13 /* Stack went beyond its minimum value */ #define SP_ERR_HEAPMIN 14 /* Heap went beyond its minimum value */ #define SP_ERR_DIVIDE_BY_ZERO 15 /* Division by zero */ +#define SP_ERR_ARRAY_BOUNDS 16 /* Array index is out of bounds */ /********************************************** *** The following structures are reference structures. diff --git a/sourcepawn/vm/jit/x86/jit_x86.cpp b/sourcepawn/vm/jit/x86/jit_x86.cpp index e9577a60..932e74e4 100644 --- a/sourcepawn/vm/jit/x86/jit_x86.cpp +++ b/sourcepawn/vm/jit/x86/jit_x86.cpp @@ -930,6 +930,37 @@ inline void WriteOp_Movs(JitWriter *jit) IA32_Pop_Reg(jit, REG_ESI); } +inline void WriteOp_Cmps(JitWriter *jit) +{ + //push edi + //push esi + //lea esi, [edi+edx] + //lea edi, [edi+eax] + IA32_Push_Reg(jit, REG_EDI); + IA32_Push_Reg(jit, REG_ESI); + IA32_Lea_Reg_DispRegMult(jit, REG_ESI, AMX_REG_DAT, AMX_REG_ALT, NOSCALE); + IA32_Lea_Reg_DispRegMult(jit, REG_EDI, AMX_REG_DAT, AMX_REG_PRI, NOSCALE); + + //xor eax, eax + //repe cmpsb + //je :cmps1 + IA32_Xor_Rm_Reg(jit, REG_EAX, REG_EAX, MOD_REG); + IA32_Cmpsb(jit); + jitoffs_t jmp = IA32_Jump_Cond_Imm8(jit, CC_E, 0); + + //sbb eax, eax + //sbb eax, -1 + IA32_Sbb_Rm_Reg(jit, REG_EAX, REG_EAX, MOD_REG); + IA32_Sbb_Eax_Imm32(jit, -1); + + //:cmps1 + //pop esi + //pop edi + IA32_Send_Jump8_Here(jit, jmp); + IA32_Pop_Reg(jit, REG_ESI); + IA32_Pop_Reg(jit, REG_EDI); +} + inline void WriteOp_Fill(JitWriter *jit) { //add edi, edx @@ -1322,6 +1353,36 @@ inline void WriteOp_Retn(JitWriter *jit) IA32_Jump_Reg(jit, AMX_REG_TMP); } +inline void WriteOp_Bounds(JitWriter *jit) +{ + Write_BoundsCheck(jit); +} + +inline void WriteOp_Halt(JitWriter *jit) +{ + //mov ecx, [esi+ret] + //mov [ecx], eax + IA32_Mov_Reg_Rm_Disp8(jit, AMX_REG_TMP, AMX_REG_INFO, AMX_INFO_RETVAL); + IA32_Mov_Rm_Reg(jit, AMX_REG_TMP, AMX_REG_PRI, MOD_MEM_REG); + + /* :TODO: + * We don't support sleeping or halting with weird values. + * So we're omitting the mov eax that was here. + */ + jit->read_cell(); + + CompData *data = (CompData *)jit->data; + jitoffs_t reloc; + if (data->inline_level & JIT_INLINE_ERRORCHECKS) + { + reloc = IA32_Jump_Imm32(jit, 0); + } else { + reloc = IA32_Call_Imm32(jit, 0); + } + IA32_Write_Jump32(jit, reloc, data->jit_return); +} + + /************************************************* ************************************************* * JIT PROPER ************************************ @@ -1409,6 +1470,7 @@ IPluginContext *JITX86::CompileToContext(ICompilation *co, int *err) data->jit_chkmargin_heap = 0; data->jit_verify_addr_eax = 0; data->jit_verify_addr_edx = 0; + data->jit_bounds = 0; /* Start writing the actual code */ data->jit_return = Write_Execute_Function(jit); @@ -1426,8 +1488,11 @@ IPluginContext *JITX86::CompileToContext(ICompilation *co, int *err) Write_CheckMargin_Heap(jit); data->jit_chkmargin_heap = jitpos; - /* Begin opcode browsing */ + jitpos = jit->jit_curpos(); + Write_BoundsCheck(jit); + data->jit_bounds = jitpos; + /* Begin opcode browsing */ for (; writer.inptr <= endptr;) { op = (OPCODE)writer.read_cell(); @@ -2073,6 +2138,21 @@ IPluginContext *JITX86::CompileToContext(ICompilation *co, int *err) WriteOp_Retn(jit); break; } + case OP_CMPS: + { + WriteOp_Cmps(jit); + break; + } + case OP_BOUNDS: + { + WriteOp_Bounds(jit); + break; + } + case OP_HALT: + { + WriteOp_Halt(jit); + break; + } default: { AbortCompilation(co); diff --git a/sourcepawn/vm/jit/x86/jit_x86.h b/sourcepawn/vm/jit/x86/jit_x86.h index 704367c9..edcb212f 100644 --- a/sourcepawn/vm/jit/x86/jit_x86.h +++ b/sourcepawn/vm/jit/x86/jit_x86.h @@ -24,6 +24,7 @@ public: jitoffs_t jit_verify_addr_eax; jitoffs_t jit_verify_addr_edx; jitoffs_t jit_chkmargin_heap; + jitoffs_t jit_bounds; int inline_level; bool checks; bool debug; diff --git a/sourcepawn/vm/jit/x86/opcode_helpers.cpp b/sourcepawn/vm/jit/x86/opcode_helpers.cpp index 026b6611..18ffa95d 100644 --- a/sourcepawn/vm/jit/x86/opcode_helpers.cpp +++ b/sourcepawn/vm/jit/x86/opcode_helpers.cpp @@ -159,7 +159,7 @@ void Write_Check_DivZero(JitWriter *jit, jit_uint8_t reg) //sub esp, 4 - correct stack for returning to non-inlined JIT IA32_Sub_Rm_Imm8(jit, REG_ESP, 4, MOD_REG); } - Write_Error(jit, SP_ERR_DIVZERO); + Write_Error(jit, SP_ERR_DIVIDE_BY_ZERO); //continue: IA32_Send_Jump8_Here(jit, jmp); @@ -227,13 +227,66 @@ void Write_Check_VerifyAddr(JitWriter *jit, jit_uint8_t reg, bool firstcall) } } +void Write_BoundsCheck(JitWriter *jit) +{ + CompData *data = (CompData *)jit->data; + bool always_inline = ((data->inline_level & JIT_INLINE_ERRORCHECKS) == JIT_INLINE_ERRORCHECKS); + + /* :TODO: break out on high -O level? */ + + if (!always_inline) + { + if (data->jit_bounds) + { + /* just generate the call */ + //mov ecx, + //call + IA32_Mov_Reg_Imm32(jit, AMX_REG_TMP, jit->read_cell()); + jitoffs_t call = IA32_Call_Imm32(jit, 0); + IA32_Write_Jump32(jit, call, data->jit_bounds); + } else { + //cmp eax, 0 + //jl :err_bounds + //cmp eax, ecx + //jg :err_bounds + //ret + IA32_Cmp_Rm_Imm32(jit, MOD_REG, AMX_REG_PRI, 0); + jitoffs_t jmp1 = IA32_Jump_Cond_Imm8(jit, CC_L, 0); + //:TODO: make sure this is right order + IA32_Cmp_Rm_Reg(jit, AMX_REG_PRI, AMX_REG_TMP, MOD_REG); + jitoffs_t jmp2 = IA32_Jump_Cond_Imm8(jit, CC_G, 0); + IA32_Return(jit); + IA32_Send_Jump8_Here(jit, jmp1); + IA32_Send_Jump8_Here(jit, jmp2); + Write_Error(jit, SP_ERR_ARRAY_BOUNDS); + } + } else { + //cmp eax, 0 + //jl :err_bounds + IA32_Cmp_Rm_Imm32(jit, MOD_REG, AMX_REG_PRI, 0); + jitoffs_t jmp1 = IA32_Jump_Cond_Imm8(jit, CC_L, 0); + //cmp eax, + //jg :err_bounds + IA32_Cmp_Rm_Imm32(jit, MOD_REG, AMX_REG_PRI, jit->read_cell()); + jitoffs_t jmp2 = IA32_Jump_Cond_Imm8(jit, CC_G, 0); + //jmp :continue + jitoffs_t cont = IA32_Jump_Imm8(jit, 0); + //:err_bounds + IA32_Send_Jump8_Here(jit, jmp1); + IA32_Send_Jump8_Here(jit, jmp2); + Write_Error(jit, SP_ERR_ARRAY_BOUNDS); + //:continue + IA32_Send_Jump8_Here(jit, cont); + } +} + void Write_CheckMargin_Heap(JitWriter *jit) { CompData *data = (CompData *)jit->data; bool always_inline = ((data->inline_level & JIT_INLINE_ERRORCHECKS) == JIT_INLINE_ERRORCHECKS); - if (always_inline && data->jit_chkmargin_heap) + if (!always_inline && data->jit_chkmargin_heap) { /* just generate the call */ jitoffs_t call = IA32_Call_Imm32(jit, 0); diff --git a/sourcepawn/vm/jit/x86/opcode_helpers.h b/sourcepawn/vm/jit/x86/opcode_helpers.h index 0629a032..23f66eab 100644 --- a/sourcepawn/vm/jit/x86/opcode_helpers.h +++ b/sourcepawn/vm/jit/x86/opcode_helpers.h @@ -32,11 +32,16 @@ void Write_CheckMargin_Stack(JitWriter *jit); * Verifies heap margins. */ void Write_CheckMargin_Heap(JitWriter *jit); + /** -* Checks for division by zero. -*/ + * Checks for division by zero. + */ void Write_Check_DivZero(JitWriter *jit, jit_uint8_t reg); +/** + * Writes a bounds check. + */ +void Write_BoundsCheck(JitWriter *jit); /** * These are for writing the PushN opcodes. @@ -166,10 +171,10 @@ typedef enum OP_DEC_S, //DONE OP_DEC_I, //DONE OP_MOVS, //DONE - OP_CMPS, + OP_CMPS, //DONE OP_FILL, //DONE - OP_HALT, - OP_BOUNDS, + OP_HALT, //DONE + OP_BOUNDS, //DONE OP_SYSREQ_PRI, OP_SYSREQ_C, OP_FILE, //DEPRECATED diff --git a/sourcepawn/vm/jit/x86/x86_macros.h b/sourcepawn/vm/jit/x86/x86_macros.h index b5fcc8f1..bf62a38b 100644 --- a/sourcepawn/vm/jit/x86/x86_macros.h +++ b/sourcepawn/vm/jit/x86/x86_macros.h @@ -63,6 +63,8 @@ #define IA32_SUB_REG_RM 0x2B // encoding is /r #define IA32_SUB_RM_IMM8 0x83 // encoding is /5 #define IA32_SUB_RM_IMM32 0x81 // encoding is /5 +#define IA32_SBB_RM_REG 0x19 // encoding is /r +#define IA32_SBB_EAX_IMM32 0x1D // encoding is #define IA32_JMP_IMM32 0xE9 // encoding is imm32 #define IA32_JMP_IMM8 0xEB // encoding is imm8 #define IA32_JMP_RM 0xFF // encoding is /4 @@ -76,6 +78,7 @@ #define IA32_CMP_RM_IMM32 0x81 // encoding is /7 #define IA32_CMP_RM_IMM8 0x83 // encoding is /7 #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 #define IA32_JCC_IMM 0x70 // encoding is +cc #define IA32_JCC_IMM32_1 0x0F // opcode part 1 @@ -342,6 +345,12 @@ inline void IA32_Sub_Rm_Reg(JitWriter *jit, jit_uint8_t dest, jit_uint8_t src, j jit->write_ubyte(ia32_modrm(mode, src, dest)); } +inline void IA32_Sbb_Rm_Reg(JitWriter *jit, jit_uint8_t dest, jit_uint8_t src, jit_uint8_t mode) +{ + jit->write_ubyte(IA32_SBB_RM_REG); + jit->write_ubyte(ia32_modrm(mode, src, dest)); +} + inline void IA32_Sub_Reg_Rm(JitWriter *jit, jit_uint8_t dest, jit_uint8_t src, jit_uint8_t mode) { jit->write_ubyte(IA32_SUB_REG_RM); @@ -362,6 +371,12 @@ inline void IA32_Sub_Rm_Imm32(JitWriter *jit, jit_uint8_t reg, jit_int32_t val, jit->write_int32(val); } +inline void IA32_Sbb_Eax_Imm32(JitWriter *jit, jit_int32_t value) +{ + jit->write_ubyte(IA32_SBB_EAX_IMM32); + jit->write_int32(value); +} + inline void IA32_Div_Rm(JitWriter *jit, jit_uint8_t reg, jit_uint8_t mode) { jit->write_ubyte(IA32_DIV_RM); @@ -859,6 +874,11 @@ inline void IA32_SetCC_Rm8(JitWriter *jit, jit_uint8_t reg, jit_uint8_t cond) jit->write_ubyte(ia32_modrm(MOD_REG, 0, reg)); } +inline void IA32_Cmpsb(JitWriter *jit) +{ + jit->write_ubyte(IA32_CMPSB); +} + inline void IA32_Rep(JitWriter *jit) { jit->write_ubyte(IA32_REP);