I'm proud to present the first non-backpatched error checking routine!
authorDavid Anderson <dvander@alliedmods.net>
Wed Sep 20 08:44:21 2006 +0000 (2006-09-20)
changeset 7555682fa202ea
parent 74 935d64738275
child 76 0af1d530c4d3
I'm proud to present the first non-backpatched error checking routine!
all of this is untested
sourcepawn/vm/jit/jit_helpers.h
sourcepawn/vm/jit/x86/jit_x86.cpp
sourcepawn/vm/jit/x86/jit_x86.h
sourcepawn/vm/jit/x86/opcode_helpers.cpp
sourcepawn/vm/jit/x86/opcode_helpers.h
sourcepawn/vm/jit/x86/x86_macros.h
     1.1 --- a/sourcepawn/vm/jit/jit_helpers.h	Wed Sep 20 07:07:49 2006 +0000
     1.2 +++ b/sourcepawn/vm/jit/jit_helpers.h	Wed Sep 20 08:44:21 2006 +0000
     1.3 @@ -2,6 +2,7 @@
     1.4  #define _INCLUDE_SOURCEPAWN_JIT_HELPERS_H_
     1.5  
     1.6  #include <sp_vm_types.h>
     1.7 +#include <sp_vm_api.h>
     1.8  
     1.9  #if defined HAVE_STDINT_H && !defined WIN32
    1.10  #include <stdint.h>
    1.11 @@ -70,6 +71,7 @@
    1.12  	cell_t *inptr;		/* input pointer */
    1.13  	jitcode_t outbase;	/* output pointer */
    1.14  	jitcode_t outptr;	/* output base */
    1.15 +	SourcePawn::ICompilation *data;	/* compiler live info */
    1.16  };
    1.17  
    1.18  #endif //_INCLUDE_SOURCEPAWN_JIT_HELPERS_H_
     2.1 --- a/sourcepawn/vm/jit/x86/jit_x86.cpp	Wed Sep 20 07:07:49 2006 +0000
     2.2 +++ b/sourcepawn/vm/jit/x86/jit_x86.cpp	Wed Sep 20 08:44:21 2006 +0000
     2.3 @@ -1,7 +1,6 @@
     2.4  #include <string.h>
     2.5  #include <stdlib.h>
     2.6  #include "jit_x86.h"
     2.7 -#include "..\jit_helpers.h"
     2.8  #include "opcode_helpers.h"
     2.9  #include "x86_macros.h"
    2.10  
    2.11 @@ -1001,6 +1000,13 @@
    2.12  	}
    2.13  }
    2.14  
    2.15 +inline void WriteOp_Load_I(JitWriter *jit)
    2.16 +{
    2.17 +	//mov eax, [edi+eax]
    2.18 +	Write_Check_VerifyAddr(jit, REG_EAX, false);
    2.19 +	IA32_Mov_Reg_Rm_Disp_Reg(jit, AMX_REG_PRI, AMX_REG_DAT, AMX_REG_PRI, NOSCALE);
    2.20 +}
    2.21 +
    2.22  
    2.23  /*************************************************
    2.24   *************************************************
    2.25 @@ -1074,7 +1080,6 @@
    2.26  	JitWriter writer;
    2.27  	JitWriter *jit = &writer;
    2.28  	cell_t *endptr = (cell_t *)(end_cip);
    2.29 -	jitoffs_t jit_return;
    2.30  
    2.31  	/* Initial code is written "blank,"
    2.32  	 * so we can check the exact memory usage.
    2.33 @@ -1085,16 +1090,17 @@
    2.34  
    2.35  	/* Get inlining level */
    2.36  	int inline_level = data->inline_level;
    2.37 -	bool never_inline = false;
    2.38 -
    2.39 -	if (inline_level == 0)
    2.40 -	{
    2.41 -		never_inline = true;
    2.42 -	}
    2.43  
    2.44  //:TODO: Jump back here once finished!
    2.45 +
    2.46  	/* Start writing the actual code */
    2.47 -	jit_return = Write_Execute_Function(jit, never_inline);
    2.48 +	data->jit_return = Write_Execute_Function(jit);
    2.49 +
    2.50 +	/* Write error checking routines in case they are needed */
    2.51 +	data->jit_verify_addr_eax = jit->jit_curpos();
    2.52 +	Write_Check_VerifyAddr(jit, REG_EAX, true);
    2.53 +	data->jit_verify_addr_edx = jit->jit_curpos();
    2.54 +	Write_Check_VerifyAddr(jit, REG_EDX, true);
    2.55  
    2.56  	for (; writer.inptr <= endptr;)
    2.57  	{
    2.58 @@ -1656,6 +1662,11 @@
    2.59  				WriteOp_Const_S(jit);
    2.60  				break;
    2.61  			}
    2.62 +		case OP_LOAD_I:
    2.63 +			{
    2.64 +				WriteOp_Load_I(jit);
    2.65 +				break;
    2.66 +			}
    2.67  		default:
    2.68  			{
    2.69  				AbortCompilation(co);
     3.1 --- a/sourcepawn/vm/jit/x86/jit_x86.h	Wed Sep 20 07:07:49 2006 +0000
     3.2 +++ b/sourcepawn/vm/jit/x86/jit_x86.h	Wed Sep 20 08:44:21 2006 +0000
     3.3 @@ -3,17 +3,24 @@
     3.4  
     3.5  #include <sp_vm_types.h>
     3.6  #include <sp_vm_api.h>
     3.7 +#include "..\jit_helpers.h"
     3.8  
     3.9  using namespace SourcePawn;
    3.10  
    3.11 +#define JIT_INLINE_ERRORCHECKS		(1<<0)
    3.12 +#define JIT_INLINE_NATIVES			(1<<1)
    3.13 +
    3.14  class CompData : public ICompilation
    3.15  {
    3.16  public:
    3.17 -	CompData() : plugin(NULL), debug(false), inline_level(2)
    3.18 +	CompData() : plugin(NULL), debug(false), inline_level(3)
    3.19  	{
    3.20  	};
    3.21  public:
    3.22  	sp_plugin_t *plugin;
    3.23 +	jitoffs_t jit_return;
    3.24 +	jitoffs_t jit_verify_addr_eax;
    3.25 +	jitoffs_t jit_verify_addr_edx;
    3.26  	int inline_level;
    3.27  	bool debug;
    3.28  };
    3.29 @@ -44,5 +51,6 @@
    3.30  #define AMX_INFO_HEAP		4
    3.31  #define AMX_INFO_RETVAL		8
    3.32  #define AMX_INFO_CONTEXT	12
    3.33 +#define AMX_INFO_STACKTOP	16
    3.34  
    3.35  #endif //_INCLUDE_SOURCEPAWN_JIT_X86_H_
     4.1 --- a/sourcepawn/vm/jit/x86/opcode_helpers.cpp	Wed Sep 20 07:07:49 2006 +0000
     4.2 +++ b/sourcepawn/vm/jit/x86/opcode_helpers.cpp	Wed Sep 20 08:44:21 2006 +0000
     4.3 @@ -9,19 +9,6 @@
     4.4  jitoffs_t Write_Execute_Function(JitWriter *jit, bool never_inline)
     4.5  {
     4.6  	/** 
     4.7 -	 * The state expected by our plugin is:
     4.8 -	 * #define AMX_REG_PRI		REG_EAX		(done)
     4.9 -	   #define AMX_REG_ALT		REG_EDX		(done)
    4.10 -	   #define AMX_REG_STK		REG_EBP		(done)
    4.11 -	   #define AMX_REG_DAT		REG_EDI		(done)
    4.12 -	   #define AMX_REG_TMP		REG_ECX		(nothing)
    4.13 -	   #define AMX_REG_INFO		REG_ESI		(done)
    4.14 -	   #define AMX_REG_FRM		REG_EBX		(done)
    4.15 -	   #define AMX_INFO_FRM		AMX_REG_INFO	(done)
    4.16 -	   #define AMX_INFO_HEAP	4			(done)
    4.17 -	   #define AMX_INFO_RETVAL	8			(done)
    4.18 -	   #define AMX_INFO_CONTEXT	12			(done)
    4.19 -	 *
    4.20  	 * The variables we're passed in:
    4.21  	 *  sp_context_t *ctx, uint32_t code_idx, cell_t *result
    4.22  	 */
    4.23 @@ -45,7 +32,7 @@
    4.24  	IA32_Push_Reg(jit, REG_EDI);
    4.25  	IA32_Push_Reg(jit, REG_EBX);
    4.26  
    4.27 -	//sub esp, 4*4	- allocate info array
    4.28 +	//sub esp, 4*5	- allocate info array
    4.29  	//mov esi, esp	- save info pointer
    4.30  	IA32_Sub_Rm_Imm8(jit, REG_ESP, 4*4, MOD_REG);
    4.31  	IA32_Mov_Reg_Rm(jit, AMX_REG_INFO, REG_ESP, MOD_REG);
    4.32 @@ -69,13 +56,21 @@
    4.33  	//mov ebp, [eax+<offs>]	- get stack pointer
    4.34  	//add ebp, edi			- relocate to data section
    4.35  	//mov ebx, ebp			- copy sp to frm
    4.36 +	IA32_Mov_Reg_Rm_Disp8(jit, AMX_REG_STK, REG_EAX, offsetof(sp_context_t, sp));
    4.37 +	IA32_Add_Rm_Reg(jit, REG_EBP, AMX_REG_STK, AMX_REG_DAT);
    4.38 +	IA32_Mov_Reg_Rm(jit, AMX_REG_FRM, AMX_REG_STK, MOD_REG);
    4.39 +
    4.40 +	//mov ecx, edi			- copy base of data to temp var
    4.41 +	//add ecx, [eax+<offs>] - add memsize to get stack top
    4.42 +	//mov [esi+16], ecx		- store stack top into info pointer
    4.43 +	IA32_Mov_Reg_Rm(jit, REG_ECX, AMX_REG_DAT, MOD_REG);
    4.44 +	IA32_Add_Reg_Rm_Disp8(jit, REG_ECX, REG_EAX, offsetof(sp_context_t, memory));
    4.45 +	IA32_Mov_Rm_Reg_Disp8(jit, REG_ESI, REG_ECX, AMX_INFO_STACKTOP);
    4.46 +
    4.47  	//mov ecx, [ebp+12]		- get code index
    4.48  	//add ecx, [eax+<offs>] - add code base to index
    4.49  	//mov edx, [eax+<offs>]	- get alt
    4.50  	//mov eax, [eax+<offs>] - get pri
    4.51 -	IA32_Mov_Reg_Rm_Disp8(jit, AMX_REG_STK, REG_EAX, offsetof(sp_context_t, sp));
    4.52 -	IA32_Add_Rm_Reg(jit, REG_EBP, AMX_REG_STK, AMX_REG_DAT);
    4.53 -	IA32_Mov_Reg_Rm(jit, AMX_REG_FRM, AMX_REG_STK, MOD_REG);
    4.54  	IA32_Mov_Reg_Rm_Disp8(jit, REG_ECX, REG_EBP, 12);
    4.55  	IA32_Add_Reg_Rm_Disp8(jit, REG_ECX, REG_EAX, offsetof(sp_context_t, base));
    4.56  	IA32_Mov_Reg_Rm_Disp8(jit, AMX_REG_ALT, REG_EAX, offsetof(sp_context_t, alt));
    4.57 @@ -94,9 +89,10 @@
    4.58  	IA32_Mov_Rm_Reg(jit, REG_EBP, AMX_REG_PRI, MOD_MEM_REG);
    4.59  	IA32_Mov_Reg_Imm32(jit, REG_EAX, SP_ERR_NONE);
    4.60  
    4.61 -	/* where error checking/return functions should go to */
    4.62 +	/* save where error checking/halting functions should go to */
    4.63  	jitoffs_t offs_return;
    4.64 -	if (never_inline)
    4.65 +	CompData *data = (CompData *)jit->data;
    4.66 +	if (!(data->inline_level & JIT_INLINE_ERRORCHECKS))
    4.67  	{
    4.68  		/* We have to write code assume we're breaking out of a call */
    4.69  		//jmp [past the next instruction]
    4.70 @@ -121,7 +117,7 @@
    4.71  	//pop esi
    4.72  	//pop ebp
    4.73  	//ret
    4.74 -	IA32_Add_Rm_Imm8(jit, REG_ESP, 4*4, MOD_REG);
    4.75 +	IA32_Add_Rm_Imm8(jit, REG_ESP, 4*5, MOD_REG);
    4.76  	IA32_Pop_Reg(jit, REG_EBX);
    4.77  	IA32_Pop_Reg(jit, REG_EDI);
    4.78  	IA32_Pop_Reg(jit, REG_ESI);
    4.79 @@ -131,6 +127,75 @@
    4.80  	return offs_return;
    4.81  }
    4.82  
    4.83 +void Write_Error(JitWriter *jit, int error)
    4.84 +{
    4.85 +	CompData *data = (CompData *)jit->data;
    4.86 +
    4.87 +	/* These are so small that we always inline them! */
    4.88 +	//mov eax, <error>
    4.89 +	//jmp [...jit_return]
    4.90 +	IA32_Mov_Reg_Imm32(jit, REG_EAX, error);
    4.91 +	jitoffs_t jmp = IA32_Jump_Imm32(jit, 0);
    4.92 +	IA32_Write_Jump32(jit, jmp, data->jit_return);
    4.93 +}
    4.94 +
    4.95 +void Write_Check_VerifyAddr(JitWriter *jit, jit_uint8_t reg, bool firstcall)
    4.96 +{
    4.97 +	CompData *data = (CompData *)jit->data;
    4.98 +
    4.99 +	/* :TODO: Should this be checking for below heaplow?
   4.100 +	 * The old JIT did not.
   4.101 +	 */
   4.102 +
   4.103 +	bool call = false;
   4.104 +	if (!(data->inline_level & JIT_INLINE_ERRORCHECKS))
   4.105 +	{
   4.106 +		/* If we're not in the initial generation phase,
   4.107 +		 * Write a call to the actual routine instead.
   4.108 +		 */
   4.109 +		if (!firstcall)
   4.110 +		{
   4.111 +			jitoffs_t call = IA32_Call_Imm32(jit, 0);
   4.112 +			if (reg == REG_EAX)
   4.113 +			{
   4.114 +				IA32_Write_Jump32(jit, call, data->jit_verify_addr_eax);
   4.115 +			} else if (reg == REG_EDX) {
   4.116 +				IA32_Write_Jump32(jit, call, data->jit_verify_addr_edx);
   4.117 +			}
   4.118 +			return;
   4.119 +		}
   4.120 +		call = true;
   4.121 +	} else if (firstcall) {
   4.122 +		/* Inline + initial gen == no code */
   4.123 +		return;
   4.124 +	}
   4.125 +
   4.126 +	//cmp reg, [stp]
   4.127 +	//jae memaccess
   4.128 +	//cmp reg, [hea]
   4.129 +	//jb continue
   4.130 +	//lea ecx, [reg+edi]
   4.131 +	//cmp ecx, ebp
   4.132 +	//jae continue
   4.133 +	//memaccess: (write error)
   4.134 +	//continue:
   4.135 +	IA32_Cmp_Reg_Rm_Disp8(jit, reg, AMX_REG_INFO, AMX_INFO_STACKTOP);
   4.136 +	jitoffs_t jmp1 = IA32_Jump_Cond_Imm8(jit, CC_AE, 0);
   4.137 +	IA32_Cmp_Reg_Rm_Disp8(jit, reg, AMX_REG_INFO, AMX_INFO_HEAP);
   4.138 +	jitoffs_t jmp2 = IA32_Jump_Cond_Imm8(jit, CC_B, 0);
   4.139 +	IA32_Lea_DispRegReg(jit, REG_ECX, reg, REG_EDI);
   4.140 +	IA32_Cmp_Rm_Reg(jit, REG_ECX, AMX_REG_STK, MOD_REG);
   4.141 +	jitoffs_t jmp3 = IA32_Jump_Cond_Imm8(jit, CC_AE, 0);
   4.142 +	IA32_Send_Jump8_Here(jit, jmp1);
   4.143 +	Write_Error(jit, SP_ERR_MEMACCESS);
   4.144 +	IA32_Send_Jump8_Here(jit, jmp2);
   4.145 +	IA32_Send_Jump8_Here(jit, jmp3);
   4.146 +	if (call)
   4.147 +	{
   4.148 +		IA32_Return(jit);
   4.149 +	}
   4.150 +}
   4.151 +
   4.152  void Macro_PushN_Addr(JitWriter *jit, int i)
   4.153  {
   4.154  	//push eax
     5.1 --- a/sourcepawn/vm/jit/x86/opcode_helpers.h	Wed Sep 20 07:07:49 2006 +0000
     5.2 +++ b/sourcepawn/vm/jit/x86/opcode_helpers.h	Wed Sep 20 08:44:21 2006 +0000
     5.3 @@ -7,11 +7,20 @@
     5.4   * This outputs the execution function for a plugin.
     5.5   * It also returns the 'return' offset, which is used for 
     5.6   * breaking out of the JIT during runtime.
     5.7 - *
     5.8 - * If 'never_inline' is true, it outputs slightly different code used for
     5.9 - * inlining the error checking routines.
    5.10   */
    5.11 -jitoffs_t Write_Execute_Function(JitWriter *jit, bool never_inline);
    5.12 +jitoffs_t Write_Execute_Function(JitWriter *jit);
    5.13 +
    5.14 +/**
    5.15 + * Generates code to set an error state in the VM and return.
    5.16 + * Note that this should only be called from inside an error
    5.17 + * checking function.
    5.18 + */
    5.19 +void Write_Error(JitWriter *jit, int error);
    5.20 +
    5.21 +/**
    5.22 + * Verifies an address by register.
    5.23 + */
    5.24 +void Write_Check_VerifyAddr(JitWriter *jit, jit_uint8_t reg, bool firstcall);
    5.25  
    5.26  /** 
    5.27   * These are for writing the PushN opcodes.
    5.28 @@ -32,7 +41,7 @@
    5.29  	OP_LREF_ALT,			//DONE
    5.30  	OP_LREF_S_PRI,			//DONE
    5.31  	OP_LREF_S_ALT,			//DONE
    5.32 -	OP_LOAD_I,
    5.33 +	OP_LOAD_I,				//DONE
    5.34  	OP_LODB_I,
    5.35  	OP_CONST_PRI,			//DONE
    5.36  	OP_CONST_ALT,			//DONE
     6.1 --- a/sourcepawn/vm/jit/x86/x86_macros.h	Wed Sep 20 07:07:49 2006 +0000
     6.2 +++ b/sourcepawn/vm/jit/x86/x86_macros.h	Wed Sep 20 08:44:21 2006 +0000
     6.3 @@ -97,6 +97,7 @@
     6.4  #define IA32_SHL_RM_CL			0xD3	// encoding is /4
     6.5  #define IA32_SAR_RM_IMM8		0xC1	// encoding is /7 <ib>
     6.6  #define IA32_CMP_RM_REG			0x39	// encoding is /r
     6.7 +#define IA32_CMP_REG_RM			0x3B	// encoding is /r
     6.8  #define IA32_SETCC_RM8_1		0x0F	// opcode part 1
     6.9  #define IA32_SETCC_RM8_2		0x90	// encoding is +cc /0 (8bits)
    6.10  #define IA32_XCHG_EAX_REG		0x90	// encoding is +r
    6.11 @@ -498,8 +499,16 @@
    6.12  	jit->write_byte(val);
    6.13  }
    6.14  
    6.15 +inline void IA32_Lea_DispRegReg(JitWriter *jit, jit_uint8_t dest, jit_uint8_t src_base, jit_uint8_t dispreg)
    6.16 +{
    6.17 +	jit->write_ubyte(IA32_LEA_REG_MEM);
    6.18 +	jit->write_ubyte(ia32_modrm(MOD_MEM_REG, dest, REG_SIB));
    6.19 +	jit->write_ubyte(ia32_sib(NOSCALE, dispreg, src_base));
    6.20 +}
    6.21 +
    6.22  inline void IA32_Lea_DispRegImm8(JitWriter *jit, jit_uint8_t dest, jit_uint8_t src_base, jit_int8_t val)
    6.23  {
    6.24 +	/* :TODO: - why does this take in src_base? */
    6.25  	jit->write_ubyte(IA32_LEA_REG_MEM);
    6.26  	jit->write_ubyte(ia32_modrm(MOD_DISP8, dest, MOD_MEM_REG));
    6.27  	jit->write_byte(val);
    6.28 @@ -507,6 +516,7 @@
    6.29  
    6.30  inline void IA32_Lea_DispRegImm32(JitWriter *jit, jit_uint8_t dest, jit_uint8_t src_base, jit_int32_t val)
    6.31  {
    6.32 +	/* :TODO: - why does this take in src_base? */
    6.33  	jit->write_ubyte(IA32_LEA_REG_MEM);
    6.34  	jit->write_ubyte(ia32_modrm(MOD_DISP32, dest, MOD_MEM_REG));
    6.35  	jit->write_int32(val);
    6.36 @@ -709,12 +719,31 @@
    6.37  	jit->outptr = oldptr;
    6.38  }
    6.39  
    6.40 +inline void IA32_Write_Jump32(JitWriter *jit, jitoffs_t jmp, jitoffs_t target)
    6.41 +{
    6.42 +	//save old ptr
    6.43 +	jitcode_t oldptr = jit->outptr;
    6.44 +	//get relative difference
    6.45 +	jit_int32_t diff = (target - (jmp + 1));
    6.46 +	//overwrite old value
    6.47 +	jit->outptr = jit->outbase + jmp;
    6.48 +	jit->write_int32(diff);
    6.49 +	//restore old ptr
    6.50 +	jit->outptr = oldptr;
    6.51 +}
    6.52 +
    6.53  inline void IA32_Send_Jump8_Here(JitWriter *jit, jitoffs_t jmp)
    6.54  {
    6.55  	jitoffs_t curptr = jit->jit_curpos();
    6.56  	IA32_Write_Jump8(jit, jmp, curptr);
    6.57  }
    6.58  
    6.59 +inline void IA32_Send_Jump32_Here(JitWriter *jit, jitoffs_t jmp)
    6.60 +{
    6.61 +	jitoffs_t curptr = jit->jit_curpos();
    6.62 +	IA32_Write_Jump32(jit, jmp, curptr);
    6.63 +}
    6.64 +
    6.65  inline void IA32_Return(JitWriter *jit)
    6.66  {
    6.67  	jit->write_ubyte(IA32_RET);
    6.68 @@ -732,6 +761,13 @@
    6.69  	jit->write_ubyte(ia32_modrm(mode, src, dest));
    6.70  }
    6.71  
    6.72 +inline void IA32_Cmp_Reg_Rm_Disp8(JitWriter *jit, jit_uint8_t reg1, jit_uint8_t reg2, jit_int8_t disp8)
    6.73 +{
    6.74 +	jit->write_ubyte(IA32_CMP_REG_RM);
    6.75 +	jit->write_ubyte(ia32_modrm(MOD_DISP8, reg1, reg2));
    6.76 +	jit->write_byte(disp8);
    6.77 +}
    6.78 +
    6.79  inline void IA32_Cmp_Rm_Imm8(JitWriter *jit, jit_uint8_t mode, jit_uint8_t rm, jit_int8_t imm8)
    6.80  {
    6.81  	jit->write_ubyte(IA32_CMP_RM_IMM8);