sourcepawn/jit/x86/opcode_helpers.cpp
author David Anderson <dvander@alliedmods.net>
Mon Jan 01 03:23:57 2007 +0000 (2007-01-01)
changeset 241 5444b56b5ba6
parent 238 43c7cb6914a8
child 350 1e4e30f7d6ca
permissions -rw-r--r--
JIT now properly handles debug mode
     1 #include <limits.h>
     2 #include <string.h>
     3 #include <malloc.h>
     4 #include "jit_x86.h"
     5 #include "opcode_helpers.h"
     6 #include "x86_macros.h"
     7 
     8 #define NUM_INFO_PARAMS	5
     9 
    10 jitoffs_t Write_Execute_Function(JitWriter *jit)
    11 {
    12 	/** 
    13 	 * The variables we're passed in:
    14 	 *  sp_context_t *ctx, uint32_t code_idx, cell_t *result
    15 	 */
    16 
    17 	/**
    18 	 * !NOTE!
    19 	 * Currently, we do not accept ctx->frm as the new frame pointer.
    20 	 * Instead, we copy the frame from the stack pointer.
    21 	 * This is because we do not support resuming or sleeping!
    22 	 */
    23 
    24 	//push ebp
    25 	//mov ebp, esp
    26 	IA32_Push_Reg(jit, REG_EBP);
    27 	IA32_Mov_Reg_Rm(jit, REG_EBP, REG_ESP, MOD_REG);
    28 
    29 	//push esi
    30 	//push edi
    31 	//push ebx
    32 	IA32_Push_Reg(jit, REG_ESI);
    33 	IA32_Push_Reg(jit, REG_EDI);
    34 	IA32_Push_Reg(jit, REG_EBX);
    35 
    36 	//sub esp, 4*n	- reserve info array
    37 	//mov esi, esp	- save info pointer
    38 	IA32_Sub_Rm_Imm8(jit, REG_ESP, 4*NUM_INFO_PARAMS, MOD_REG);
    39 	IA32_Mov_Reg_Rm(jit, AMX_REG_INFO, REG_ESP, MOD_REG);
    40 
    41 	/* Initial memory setup */
    42 	//mov eax, [ebp+16]		- get result pointer
    43 	//mov [esi+8], eax		- store into info pointer
    44 	//mov eax, [ebp+8]		- get context
    45 	//mov [esi+12], eax		- store context into info pointer
    46 	//mov ecx, [eax+<offs>]	- get heap pointer
    47 	//mov [esi+4], ecx		- store heap into info pointer
    48 	//mov ebp, [eax+<offs>]	- get data pointer
    49 	IA32_Mov_Reg_Rm_Disp8(jit, REG_EAX, REG_EBP, 16);
    50 	IA32_Mov_Rm_Reg_Disp8(jit, AMX_REG_INFO, REG_EAX, AMX_INFO_RETVAL);
    51 	IA32_Mov_Reg_Rm_Disp8(jit, REG_EAX, REG_EBP, 8);
    52 	IA32_Mov_Rm_Reg_Disp8(jit, AMX_REG_INFO, REG_EAX, AMX_INFO_CONTEXT);
    53 	IA32_Mov_Reg_Rm_Disp8(jit, REG_ECX, REG_EAX, offsetof(sp_context_t, hp));
    54 	IA32_Mov_Rm_Reg_Disp8(jit, AMX_REG_INFO, REG_ECX, AMX_INFO_HEAP);
    55 	IA32_Mov_Reg_Rm_Disp8(jit, AMX_REG_DAT, REG_EAX, offsetof(sp_context_t, memory));
    56 
    57 	/* Frame setup */
    58 	//mov edi, [eax+<offs>]	- get stack pointer
    59 	//add edi, ebp			- relocate to data section
    60 	//mov ebx, edi			- copy sp to frm
    61 	IA32_Mov_Reg_Rm_Disp8(jit, AMX_REG_STK, REG_EAX, offsetof(sp_context_t, sp));
    62 	IA32_Add_Rm_Reg(jit, AMX_REG_STK, AMX_REG_DAT, MOD_REG);
    63 	IA32_Mov_Reg_Rm(jit, AMX_REG_FRM, AMX_REG_STK, MOD_REG);
    64 
    65 	/* Info memory setup */
    66 	//mov ecx, [eax+<offs>] - copy memsize to temp var
    67 	//add ecx, ebp			- relocate
    68 	//mov [esi+x], ecx		- store relocated
    69 	IA32_Mov_Reg_Rm_Disp8(jit, REG_ECX, REG_EAX, offsetof(sp_context_t, memory));
    70 	IA32_Add_Reg_Rm(jit, AMX_REG_TMP, AMX_REG_DAT, MOD_REG);
    71 	IA32_Mov_Rm_Reg_Disp8(jit, AMX_REG_INFO, REG_ECX, AMX_INFO_STACKTOP);
    72 
    73 	/* Remaining needed vars */
    74 	//mov ecx, [esp+(4*(NUM_INFO_PARAMS+3))+12] - get code index (normally esp+12, but we have another array on the stack)
    75 	//add ecx, [eax+<offs>]   - add code base to index
    76 	IA32_Mov_Reg_Esp_Disp8(jit, REG_ECX, 12+(4*(NUM_INFO_PARAMS+3)));
    77 	IA32_Add_Reg_Rm_Disp8(jit, REG_ECX, REG_EAX, offsetof(sp_context_t, codebase));
    78 
    79 	/* by now, everything is set up, so we can call into the plugin */
    80 	//call ecx
    81 	IA32_Call_Reg(jit, REG_ECX);
    82 
    83 	/* if the code flow gets to here, there was a normal return */
    84 	//mov ecx, [esi+8]		- get retval pointer
    85 	//mov [ecx], eax		- store retval from PRI
    86 	//mov eax, SP_ERROR_NONE	- set no error
    87 	IA32_Mov_Reg_Rm_Disp8(jit, REG_ECX, AMX_REG_INFO, AMX_INFO_RETVAL);
    88 	IA32_Mov_Rm_Reg(jit, REG_ECX, AMX_REG_PRI, MOD_MEM_REG);
    89 	IA32_Mov_Reg_Imm32(jit, REG_EAX, SP_ERROR_NONE);
    90 
    91 	/* save where error checking/halting functions should go to */
    92 	jitoffs_t offs_return = jit->get_outputpos();
    93 	//mov esp, esi			- restore stack pointer
    94 	IA32_Mov_Reg_Rm(jit, REG_ESP, REG_ESI, MOD_REG);
    95 
    96 	/* _FOR NOW_ ...
    97 	 * We are going to restore SP, HP, and FRM for now.  This is for 
    98      * debugging only, to check for alignment errors.  As such:
    99 	 * :TODO: probably remove this.
   100 	 */
   101 	//mov ecx, [esi+context]
   102 	//sub edi, ebp
   103 	//mov edx, [esi+heap]
   104 	//mov [ecx+sp], edi
   105 	//mov [ecx+hp], edx
   106 	IA32_Mov_Reg_Rm_Disp8(jit, REG_ECX, REG_ESI, AMX_INFO_CONTEXT);
   107 	IA32_Sub_Reg_Rm(jit, REG_EDI, REG_EBP, MOD_REG);
   108 	IA32_Mov_Reg_Rm_Disp8(jit, REG_EDX, REG_ESI, AMX_INFO_HEAP);
   109 	IA32_Mov_Rm_Reg_Disp8(jit, REG_ECX, REG_EDI, offsetof(sp_context_t, sp));
   110 	IA32_Mov_Rm_Reg_Disp8(jit, REG_ECX, REG_EDX, offsetof(sp_context_t, hp));
   111 
   112 	//add esp, 4*NUM_INFO_PARAMS
   113 	//pop ebx
   114 	//pop edi
   115 	//pop esi
   116 	//pop ebp
   117 	//ret
   118 	IA32_Add_Rm_Imm8(jit, REG_ESP, 4*NUM_INFO_PARAMS, MOD_REG);
   119 	IA32_Pop_Reg(jit, REG_EBX);
   120 	IA32_Pop_Reg(jit, REG_EDI);
   121 	IA32_Pop_Reg(jit, REG_ESI);
   122 	IA32_Pop_Reg(jit, REG_EBP);
   123 	IA32_Return(jit);
   124 
   125 	return offs_return;
   126 }
   127 
   128 void Write_BreakDebug(JitWriter *jit)
   129 {
   130 	//push edi
   131 	//mov edi, ecx
   132 	//mov ecx, [esi+ctx]
   133 	//cmp [ecx+dbreak], 0
   134 	//jnz :nocall
   135 	IA32_Push_Reg(jit, REG_EDI);
   136 	IA32_Mov_Reg_Rm(jit, REG_EDI, REG_ECX, MOD_REG);
   137 	IA32_Mov_Reg_Rm_Disp8(jit, AMX_REG_TMP, AMX_REG_INFO, AMX_INFO_CONTEXT);
   138 	IA32_Cmp_Rm_Disp8_Imm8(jit, AMX_REG_TMP, offsetof(sp_context_t, dbreak), 0);
   139 	jitoffs_t jmp = IA32_Jump_Cond_Imm8(jit, CC_Z, 0);
   140 
   141 	/* NOTE, Hack! PUSHAD pushes EDI last which still has the CIP */
   142 	//pushad
   143 	//push [esi+frm]
   144 	//push ctx
   145 	//mov ecx, [ecx+dbreak]
   146 	//call ecx
   147 	//add esp, 8
   148 	//popad
   149 	IA32_Pushad(jit);
   150 	IA32_Push_Rm_Disp8(jit, AMX_REG_INFO, AMX_INFO_FRAME); //:TODO: move to regs and push? and dont disp for 0
   151 	IA32_Push_Reg(jit, AMX_REG_TMP);
   152 	IA32_Mov_Reg_Rm_Disp8(jit, AMX_REG_TMP, AMX_REG_TMP, offsetof(sp_context_t, dbreak));
   153 	IA32_Call_Reg(jit, AMX_REG_TMP);
   154 	IA32_Add_Rm_Imm8(jit, REG_ESP, 4*2, MOD_REG);
   155 	IA32_Popad(jit);
   156 
   157 	//:nocall
   158 	//pop edi
   159 	//ret
   160 	IA32_Pop_Reg(jit, REG_EDI);
   161 	IA32_Send_Jump8_Here(jit, jmp);
   162 	IA32_Return(jit);
   163 }
   164 
   165 void Write_GetError(JitWriter *jit)
   166 {
   167 	CompData *data = (CompData *)jit->data;
   168 
   169 	//mov eax, [esi+info.context]
   170 	//mov eax, [eax+ctx.error]
   171 	//jmp [jit_return]
   172 	IA32_Mov_Reg_Rm_Disp8(jit, REG_EAX, AMX_REG_INFO, AMX_INFO_CONTEXT);
   173 	IA32_Mov_Reg_Rm_Disp8(jit, REG_EAX, REG_EAX, offsetof(sp_context_t, n_err));
   174 	IA32_Jump_Imm32_Abs(jit, data->jit_return);
   175 }
   176 
   177 void Write_SetError(JitWriter *jit, int error)
   178 {
   179 	CompData *data = (CompData *)jit->data;
   180 
   181 	//mov eax, <error>
   182 	//jmp [jit_return]
   183 	IA32_Mov_Reg_Imm32(jit, REG_EAX, error);
   184 	IA32_Jump_Imm32_Abs(jit, data->jit_return);
   185 }
   186 
   187 void Write_Check_DivZero(JitWriter *jit, jit_uint8_t reg)
   188 {
   189 	//test reg, reg
   190 	//jz :error
   191 	IA32_Test_Rm_Reg(jit, reg, reg, MOD_REG);
   192 	IA32_Jump_Cond_Imm32_Abs(jit, CC_Z, ((CompData *)jit->data)->jit_error_divzero);
   193 }
   194 
   195 void Write_CheckHeap_Min(JitWriter *jit)
   196 {
   197 	/* Check if the stack went beyond the heap low.
   198 	 * This usually means there was a compiler error.
   199 	 * NOTE: Special optimization here.
   200 	 * The heap low is always known ahead of time! :)
   201 	 */
   202 	CompData *data = (CompData *)jit->data;
   203 	//cmp [esi+info.heap], <heaplow>
   204 	//jb :error
   205 	IA32_Cmp_Rm_Imm32_Disp8(jit, AMX_REG_INFO, AMX_INFO_HEAP, data->plugin->data_size);
   206 	IA32_Jump_Cond_Imm32_Abs(jit, CC_B, data->jit_error_heapmin);
   207 }
   208 
   209 void Write_CheckHeap_Low(JitWriter *jit)
   210 {
   211 	/* Check if the heap is trying to grow beyond the stack.
   212 	 */
   213 	//mov ecx, [esi+info.heap]
   214 	//lea ecx, [ebp+ecx+STACK_MARGIN]
   215 	//cmp ecx, edi
   216 	//ja :error		; I think this is right
   217 	IA32_Mov_Reg_Rm_Disp8(jit, AMX_REG_TMP, AMX_REG_INFO, AMX_INFO_HEAP);
   218 	IA32_Lea_Reg_DispRegMultImm8(jit, AMX_REG_TMP, AMX_REG_DAT, AMX_REG_TMP, NOSCALE, STACK_MARGIN);
   219 	IA32_Cmp_Reg_Rm(jit, AMX_REG_TMP, AMX_REG_STK, MOD_REG);
   220 	IA32_Jump_Cond_Imm32_Abs(jit, CC_A, ((CompData *)jit->data)->jit_error_heaplow);
   221 }
   222 
   223 void Write_CheckStack_Min(JitWriter *jit)
   224 {
   225 	/* Check if the stack went beyond the stack top
   226 	 * This usually means there was a compiler error.
   227 	 */
   228 	//cmp edi, [esi+info.stacktop]
   229 	//jae :error
   230 	IA32_Cmp_Reg_Rm_Disp8(jit, AMX_REG_STK, AMX_REG_INFO, AMX_INFO_STACKTOP);
   231 	IA32_Jump_Cond_Imm32_Abs(jit, CC_AE, ((CompData *)jit->data)->jit_error_stackmin);
   232 }
   233 
   234 void Write_CheckStack_Low(JitWriter *jit)
   235 {
   236 	/* Check if the stack went beyond the heap boundary.
   237 	 * Unfortunately this one isn't as quick as the other check.
   238 	 * The stack margin check is important for sysreq.n having space.
   239 	 */
   240 	//mov ecx, [esi+info.heap]
   241 	//lea ecx, [ebp+ecx+STACK_MARGIN]
   242 	//cmp edi, ecx
   243 	//jb :error		; I think this is right
   244 	IA32_Mov_Reg_Rm_Disp8(jit, AMX_REG_TMP, AMX_REG_INFO, AMX_INFO_HEAP);
   245 	IA32_Lea_Reg_DispRegMultImm8(jit, AMX_REG_TMP, AMX_REG_DAT, AMX_REG_TMP, NOSCALE, STACK_MARGIN);
   246 	IA32_Cmp_Reg_Rm(jit, AMX_REG_STK, AMX_REG_TMP, MOD_REG);
   247 	IA32_Jump_Cond_Imm32_Abs(jit, CC_B, ((CompData *)jit->data)->jit_error_stacklow);
   248 }
   249 
   250 void Write_Check_VerifyAddr(JitWriter *jit, jit_uint8_t reg)
   251 {
   252 	CompData *data = (CompData *)jit->data;
   253 
   254 	/* :TODO: Should this be checking for below heaplow?
   255 	 * The old JIT did not.
   256 	 */
   257 
   258 	bool call = false;
   259 	if (!(data->inline_level & JIT_INLINE_ERRORCHECKS))
   260 	{
   261 		/* If we're not in the initial generation phase,
   262 		 * Write a call to the actual routine instead.
   263 		 */
   264 		if ((reg == REG_EAX) && data->jit_verify_addr_eax)
   265 		{
   266 			jitoffs_t call = IA32_Call_Imm32(jit, 0);
   267 			IA32_Write_Jump32(jit, call, data->jit_verify_addr_eax);
   268 			return;
   269 		} else if ((reg == REG_EDX) && data->jit_verify_addr_edx) {
   270 			jitoffs_t call = IA32_Call_Imm32(jit, 0);
   271 			IA32_Write_Jump32(jit, call, data->jit_verify_addr_edx);
   272 			return;
   273 		}
   274 		call = true;
   275 	}
   276 
   277 	/** 
   278 	 * :TODO: If we can't find a nicer way of doing this,
   279 	 *  then scrap it on high optimizations.  The second portion is not needed at all!
   280 	 */
   281 
   282 	/* Part 1: Check if we're in the memory bounds */
   283 	//cmp <reg>, <stpu>
   284 	//jae :error
   285 	IA32_Cmp_Rm_Imm32(jit, MOD_REG, reg, ((CompData *)jit->data)->plugin->memory);
   286 	IA32_Jump_Cond_Imm32_Abs(jit, CC_AE, ((CompData *)jit->data)->jit_error_memaccess);
   287 
   288 	/* Part 2: Check if we're in the invalid region between HP and SP */
   289 	jitoffs_t jmp;
   290 	//cmp <reg>, [esi+info.heap]
   291 	//jb :continue
   292 	//lea ecx, [ebp+<reg>]
   293 	//cmp edi, ecx
   294 	//jb :error
   295 	//:continue
   296 	IA32_Cmp_Reg_Rm_Disp8(jit, reg, AMX_REG_INFO, AMX_INFO_HEAP);
   297 	jmp = IA32_Jump_Cond_Imm8(jit, CC_B, 0);
   298 	IA32_Lea_Reg_DispRegMultImm8(jit, AMX_REG_TMP, AMX_REG_DAT, reg, NOSCALE, 0);
   299 	IA32_Cmp_Reg_Rm(jit, AMX_REG_TMP, AMX_REG_STK, MOD_REG);
   300 	IA32_Jump_Cond_Imm32_Abs(jit, CC_B, ((CompData *)jit->data)->jit_error_memaccess);
   301 	IA32_Send_Jump8_Here(jit, jmp);
   302 
   303 	if (call)
   304 	{
   305 		IA32_Return(jit);
   306 	}
   307 }
   308 
   309 void Macro_PushN_Addr(JitWriter *jit, int i)
   310 {
   311 	//push eax
   312 	//mov eax, [esi+frm]
   313 	//loop i times:
   314 	// lea ecx, [eax+<val>]
   315 	// mov [edi-4*i], ecx
   316 	//sub edi, 4*N
   317 	//pop eax
   318 
   319 	cell_t val;
   320 	int n = 1;
   321 	IA32_Push_Reg(jit, AMX_REG_PRI);
   322 	IA32_Mov_Reg_Rm(jit, AMX_REG_PRI, AMX_REG_INFO, MOD_MEM_REG);
   323 	do
   324 	{
   325 		val = jit->read_cell();
   326 		if (val < SCHAR_MAX && val > SCHAR_MIN)
   327 			IA32_Lea_DispRegImm8(jit, AMX_REG_TMP, AMX_REG_PRI, (jit_int8_t)val);
   328 		else
   329 			IA32_Lea_DispRegImm32(jit, AMX_REG_TMP, AMX_REG_PRI, val);
   330 		IA32_Mov_Rm_Reg_Disp8(jit, AMX_REG_STK, AMX_REG_TMP, -4*n);
   331 	} while (n++ < i);
   332 	IA32_Sub_Rm_Imm8(jit, AMX_REG_STK, 4*i, MOD_REG);
   333 	IA32_Pop_Reg(jit, AMX_REG_PRI);
   334 }
   335 
   336 void Macro_PushN_S(JitWriter *jit, int i)
   337 {
   338 	//loop i times:
   339 	// mov ecx, [ebx+<val>]
   340 	// mov [edi-4*i], ecx
   341 	//sub edi, 4*N
   342 
   343 	cell_t val;
   344 	int n = 1;
   345 	do 
   346 	{
   347 		val = jit->read_cell();
   348 		if (val < SCHAR_MAX && val > SCHAR_MIN)
   349 			IA32_Mov_Reg_Rm_Disp8(jit, AMX_REG_TMP, AMX_REG_FRM, (jit_int8_t)val);
   350 		else
   351 			IA32_Mov_Reg_Rm_Disp32(jit, AMX_REG_TMP, AMX_REG_FRM, val);
   352 		IA32_Mov_Rm_Reg_Disp8(jit, AMX_REG_STK, AMX_REG_TMP, -4*n);
   353 	} while (n++ < i);
   354 	IA32_Sub_Rm_Imm8(jit, AMX_REG_STK, 4*i, MOD_REG);
   355 }
   356 
   357 void Macro_PushN_C(JitWriter *jit, int i)
   358 {
   359 	//loop i times:
   360 	// mov [edi-4*i], <val>
   361 	//sub edi, 4*N
   362 
   363 	int n = 1;
   364 	do 
   365 	{
   366 		IA32_Mov_Rm_Imm32_Disp8(jit, AMX_REG_STK, jit->read_cell(), -4*n);
   367 	} while (n++ < i);
   368 	IA32_Sub_Rm_Imm8(jit, AMX_REG_STK, 4*i, MOD_REG);
   369 }
   370 
   371 void Macro_PushN(JitWriter *jit, int i)
   372 {
   373 	//loop i times:
   374 	// mov ecx, [ebp+<val>]
   375 	// mov [edi-4*i], ecx
   376 	//sub edi, 4*N
   377 
   378 	cell_t val;
   379 	int n = 1;
   380 	do 
   381 	{
   382 		val = jit->read_cell();
   383 		if (val < SCHAR_MAX && val > SCHAR_MIN)
   384 			IA32_Mov_Reg_Rm_Disp8(jit, AMX_REG_TMP, AMX_REG_DAT, (jit_int8_t)val);
   385 		else
   386 			IA32_Mov_Reg_Rm_Disp32(jit, AMX_REG_TMP, AMX_REG_DAT, val);
   387 		IA32_Mov_Rm_Reg_Disp8(jit, AMX_REG_STK, AMX_REG_TMP, -4*n);
   388 	} while (n++ < i);
   389 	IA32_Sub_Rm_Imm8(jit, AMX_REG_STK, 4*i, MOD_REG);
   390 }
   391 
   392 void WriteOp_Sysreq_C_Function(JitWriter *jit)
   393 {
   394 	/* The small daddy of the big daddy of opcodes.
   395 	 * ecx - native index
   396 	 */
   397 	CompData *data = (CompData *)jit->data;
   398 
   399 	/* save registers we will need */
   400 	//push edx
   401 	IA32_Push_Reg(jit, AMX_REG_ALT);
   402 
   403 	/* push some callback stuff */
   404 	//push edi		; stack
   405 	//push ecx		; native index
   406 	IA32_Push_Reg(jit, AMX_REG_STK);
   407 	IA32_Push_Reg(jit, REG_ECX);
   408 
   409 	/* Relocate stack, heap, frm information, then store back */
   410 	//mov eax, [esi+context]
   411 	//mov ecx, [esi+hea]
   412 	//sub edi, ebp
   413 	//mov [eax+hp], ecx
   414 	//mov ecx, [esi]
   415 	//mov [eax+sp], edi
   416 	//mov [eax+frm], ecx
   417 	IA32_Mov_Reg_Rm_Disp8(jit, REG_EAX, AMX_REG_INFO, AMX_INFO_CONTEXT);
   418 	IA32_Mov_Reg_Rm_Disp8(jit, AMX_REG_TMP, AMX_REG_INFO, AMX_INFO_HEAP);
   419 	IA32_Sub_Reg_Rm(jit, AMX_REG_STK, AMX_REG_DAT, MOD_REG);
   420 	IA32_Mov_Rm_Reg_Disp8(jit, REG_EAX, AMX_REG_TMP, offsetof(sp_context_t, hp));
   421 	IA32_Mov_Reg_Rm(jit, AMX_REG_TMP, AMX_INFO_FRM, MOD_MEM_REG);
   422 	IA32_Mov_Rm_Reg_Disp8(jit, REG_EAX, AMX_REG_STK, offsetof(sp_context_t, sp));
   423 	IA32_Mov_Rm_Reg_Disp8(jit, REG_EAX, AMX_REG_TMP, offsetof(sp_context_t, frm));
   424 
   425 	/* finally, push the last parameter and make the call */
   426 	//push eax		; context
   427 	//call NativeCallback
   428 	IA32_Push_Reg(jit, REG_EAX);
   429 	jitoffs_t call = IA32_Call_Imm32(jit, 0);
   430 	if (!data->debug)
   431 	{
   432 		IA32_Write_Jump32_Abs(jit, call, NativeCallback);
   433 	} else {
   434 		IA32_Write_Jump32_Abs(jit, call, NativeCallback_Debug);
   435 	}
   436 
   437 	/* Test for error */
   438 	//mov ecx, [esi+context]
   439 	//cmp [ecx+err], 0
   440 	//jnz :error
   441 	IA32_Mov_Reg_Rm_Disp8(jit, AMX_REG_TMP, AMX_REG_INFO, AMX_INFO_CONTEXT);
   442 	IA32_Cmp_Rm_Disp8_Imm8(jit, AMX_REG_TMP, offsetof(sp_context_t, n_err), 0);
   443 	IA32_Jump_Cond_Imm32_Abs(jit, CC_NZ, data->jit_extern_error);
   444 
   445 	/* restore what we damaged */
   446 	//add esp, 4*3
   447 	//add edi, ebp
   448 	//pop edx
   449 	IA32_Add_Rm_Imm8(jit, REG_ESP, 4*3, MOD_REG);
   450 	IA32_Add_Reg_Rm(jit, AMX_REG_STK, AMX_REG_DAT, MOD_REG);
   451 	IA32_Pop_Reg(jit, AMX_REG_ALT);
   452 
   453 	//ret
   454 	IA32_Return(jit);
   455 }
   456 
   457 void GenerateArrayIndirectionVectors(cell_t *arraybase, cell_t dims[], ucell_t _dimcount, bool autozero)
   458 {
   459 	cell_t vectors = 1;				/* we need one vector to start off with */
   460 	cell_t cur_offs = 0;
   461 	cell_t cur_write = 0;
   462 	cell_t dimcount = _dimcount;
   463 
   464 	/* Initialize rotation */
   465 	cur_write = dims[dimcount-1];
   466 	
   467 	while (--dimcount >= 1)
   468 	{
   469 		cell_t cur_dim = dims[dimcount];
   470 		cell_t sub_dim = dims[dimcount-1];
   471 		for (cell_t i=0; i<vectors; i++)
   472 		{
   473 			for (cell_t j=0; j<cur_dim; j++)
   474 			{
   475 				arraybase[cur_offs] = (cur_write - cur_offs)*sizeof(cell_t);
   476 				cur_offs++;
   477 				cur_write += sub_dim;
   478 			}
   479 		}
   480 		vectors = cur_dim;
   481 	}
   482 
   483 	/* everything after cur_offs can be zeroed */
   484 	if (autozero)
   485 	{
   486 		size_t size = 1;
   487 		for (ucell_t i=0; i<_dimcount; i++)
   488 		{
   489 			size *= dims[i];
   490 		}
   491 		memset(&arraybase[cur_offs], 0, size*sizeof(cell_t));
   492 	}
   493 
   494 	return;
   495 }
   496 
   497 /**
   498  * A few notes about this function.
   499  * I was more concerned about efficient use of registers here, rather than 
   500  *  fine-tuned optimization.  The reason is that the code is already complicated,
   501  *  and it is very easy to mess up.
   502  */
   503 void WriteIntrinsic_GenArray(JitWriter *jit)
   504 {
   505 	/**
   506 	 * save important values
   507 	 */
   508 	//push ebx
   509 	//push eax
   510 	//push edx
   511 	//push ecx				;value is referenced on stack
   512 	IA32_Push_Reg(jit, REG_EBX);
   513 	IA32_Push_Reg(jit, REG_EAX);
   514 	IA32_Push_Reg(jit, REG_EDX);
   515 	IA32_Push_Reg(jit, REG_ECX);
   516 
   517 	/**
   518 	 * Calculate how many cells will be needed.
   519 	 */
   520 	//mov edx, [edi]		;get last dimension's count
   521 	//mov eax, 1			;position at second to last dimension
   522 	//:loop
   523 	//cmp eax, [esp]		;compare to # of params
   524 	//jae :done				;end loop if done
   525 	//mov ecx, [edi+eax*4]	;get dimension size
   526 	//imul edx, ecx			;multiply by size
   527 	//add eax, 1			;increment
   528 	//add edx, ecx			;add size (indirection vector)
   529 	//jmp :loop				;jump back
   530 	//:done
   531 	IA32_Mov_Reg_Rm(jit, REG_EDX, AMX_REG_STK, MOD_MEM_REG);
   532 	IA32_Mov_Reg_Imm32(jit, REG_EAX, 1);
   533 	jitoffs_t loop1 = jit->get_outputpos();
   534 	IA32_Cmp_Reg_Rm_ESP(jit, REG_EAX);
   535 	jitoffs_t done1 = IA32_Jump_Cond_Imm8(jit, CC_AE, 0);
   536 	IA32_Mov_Reg_Rm_Disp_Reg(jit, REG_ECX, AMX_REG_STK, REG_EAX, SCALE4);
   537 	IA32_IMul_Reg_Rm(jit, REG_EDX, REG_ECX, MOD_REG);
   538 	IA32_Add_Rm_Imm8(jit, REG_EAX, 1, MOD_REG);
   539 	IA32_Add_Reg_Rm(jit, REG_EDX, REG_ECX, MOD_REG);
   540 	IA32_Write_Jump8(jit, IA32_Jump_Imm8(jit, loop1), loop1);
   541 	IA32_Send_Jump8_Here(jit, done1);
   542 
   543 	/* Test if we have heap space for this */
   544 	//mov eax, [esi+info.heap]	;get heap pointer
   545 	//lea eax, [eax+edx*4]		;new heap pointer
   546 	//cmp eax, <hlw>			;compare to heap low
   547 	//jbe :error				;die if we hit this (it should always be >)
   548 	//add eax, ebp				;relocate to stack
   549 	//cmp eax, edi				;die if above the stack pointer
   550 	//jae :error
   551 	IA32_Mov_Reg_Rm_Disp8(jit, REG_EAX, AMX_REG_INFO, AMX_INFO_HEAP);
   552 	IA32_Lea_Reg_DispRegMult(jit, REG_EAX, REG_EAX, REG_EDX, SCALE4);
   553 	IA32_Cmp_Rm_Imm32(jit, MOD_REG, REG_EAX, ((CompData *)jit->data)->plugin->data_size);
   554 	IA32_Jump_Cond_Imm32_Abs(jit, CC_BE, ((CompData *)jit->data)->jit_error_array_too_big);
   555 	IA32_Add_Reg_Rm(jit, REG_EAX, AMX_REG_DAT, MOD_REG);
   556 	IA32_Cmp_Reg_Rm(jit, REG_EAX, AMX_REG_STK, MOD_REG);
   557 	IA32_Jump_Cond_Imm32_Abs(jit, CC_AE, ((CompData *)jit->data)->jit_error_array_too_big);
   558 	
   559 	/* Prepare for indirection iteration */
   560 	//mov eax, [esi+info.heap]	;get heap pointer
   561 	//lea ebx, [eax+edx*4]		;new heap pointer
   562 	//mov [esi+info.heap], ebx	;store back
   563 	//push eax					;save heap pointer - we need it
   564 	IA32_Mov_Reg_Rm_Disp8(jit, REG_EAX, AMX_REG_INFO, AMX_INFO_HEAP);
   565 	IA32_Lea_Reg_DispRegMult(jit, REG_EBX, REG_EAX, REG_EDX, SCALE4);
   566 	IA32_Mov_Rm_Reg_Disp8(jit, AMX_REG_INFO, REG_EBX, AMX_INFO_HEAP);
   567 	IA32_Push_Reg(jit, REG_EAX);
   568 
   569 	WriteOp_Tracker_Push_Reg(jit, REG_EDX);
   570 	
   571 	/* This part is too messy to do in straight assembly.
   572 	 * I'm letting the compiler handle it and thus it's in C.
   573 	 */
   574 	//lea ebx, [ebp+eax]		;get base pointer
   575 	//push dword [esp-8]		;push autozero
   576 	//push dword [esp-8]		;push dimension count
   577 	//push edi					;push dim array
   578 	//push ebx
   579 	//call GenerateArrayIndirectionVectors
   580 	//add esp, 4*4
   581 	IA32_Lea_Reg_DispRegMult(jit, REG_EBX, REG_EAX, REG_EBP, NOSCALE);
   582 	IA32_Push_Rm_Disp8_ESP(jit, 8);
   583 	IA32_Push_Rm_Disp8_ESP(jit, 8);
   584 	IA32_Push_Reg(jit, REG_EDI);
   585 	IA32_Push_Reg(jit, REG_EBX);
   586 	IA32_Write_Jump32_Abs(jit, IA32_Call_Imm32(jit, 0), (void *)&GenerateArrayIndirectionVectors);
   587 	IA32_Add_Rm_Imm8(jit, REG_ESP, 4*4, MOD_REG);
   588 
   589 	/* Store the heap pointer back into the stack */
   590 	//pop eax					;restore heap pointer
   591 	//pop ecx					;restore param count
   592 	//lea edi, [edi+ecx*4-4]	;pop params-4 off the stack
   593 	//mov [edi], eax			;store back the heap pointer
   594 	IA32_Pop_Reg(jit, REG_EAX);
   595 	IA32_Pop_Reg(jit, REG_ECX);
   596 	IA32_Lea_Reg_DispRegMultImm8(jit, AMX_REG_STK, AMX_REG_STK, REG_ECX, SCALE4, -4);
   597 	IA32_Mov_Rm_Reg(jit, AMX_REG_STK, REG_EAX, MOD_MEM_REG);
   598 
   599 	/* Return to caller */
   600 	//pop edx
   601 	//pop eax
   602 	//pop ebx
   603 	//ret
   604 	IA32_Pop_Reg(jit, REG_ECX);
   605 	IA32_Pop_Reg(jit, REG_EAX);
   606 	IA32_Pop_Reg(jit, REG_EBX);
   607 	IA32_Return(jit);
   608 }
   609 
   610 void WriteOp_Sysreq_N_Function(JitWriter *jit)
   611 {
   612 	/* The big daddy of opcodes.
   613 	 * eax - num_params
   614 	 * ecx - native index
   615 	 */
   616 	CompData *data = (CompData *)jit->data;
   617 
   618 	/* store the number of parameters on the stack */
   619 	//mov [edi-4], eax
   620 	//sub edi, 4
   621 	IA32_Mov_Rm_Reg_Disp8(jit, AMX_REG_STK, REG_EAX, -4);
   622 	IA32_Sub_Rm_Imm8(jit, AMX_REG_STK, 4, MOD_REG);
   623 
   624 	/* save registers we will need */
   625 	//push eax	; num_params for stack popping
   626 	//push edx
   627 	IA32_Push_Reg(jit, REG_EAX);
   628 	IA32_Push_Reg(jit, AMX_REG_ALT);
   629 
   630 	/* push some callback stuff */
   631 	//push edi		; stack
   632 	//push ecx		; native index
   633 	IA32_Push_Reg(jit, AMX_REG_STK);
   634 	IA32_Push_Reg(jit, REG_ECX);
   635 
   636 	/* Relocate stack, heap, frm information, then store back */
   637 	//mov eax, [esi+context]
   638 	//mov ecx, [esi+hea]
   639 	//sub edi, ebp
   640 	//mov [eax+hp], ecx
   641 	//mov ecx, [esi]
   642 	//mov [eax+sp], edi
   643 	//mov [eax+frm], ecx
   644 	IA32_Mov_Reg_Rm_Disp8(jit, REG_EAX, AMX_REG_INFO, AMX_INFO_CONTEXT);
   645 	IA32_Mov_Reg_Rm_Disp8(jit, AMX_REG_TMP, AMX_REG_INFO, AMX_INFO_HEAP);
   646 	IA32_Sub_Reg_Rm(jit, AMX_REG_STK, AMX_REG_DAT, MOD_REG);
   647 	IA32_Mov_Rm_Reg_Disp8(jit, REG_EAX, AMX_REG_TMP, offsetof(sp_context_t, hp));
   648 	IA32_Mov_Reg_Rm(jit, AMX_REG_TMP, AMX_INFO_FRM, MOD_MEM_REG);
   649 	IA32_Mov_Rm_Reg_Disp8(jit, REG_EAX, AMX_REG_STK, offsetof(sp_context_t, sp));
   650 	IA32_Mov_Rm_Reg_Disp8(jit, REG_EAX, AMX_REG_TMP, offsetof(sp_context_t, frm));
   651 
   652 	/* finally, push the last parameter and make the call */
   653 	//push eax		; context
   654 	//call NativeCallback
   655 	IA32_Push_Reg(jit, REG_EAX);
   656 	jitoffs_t call = IA32_Call_Imm32(jit, 0);
   657 	if (!data->debug)
   658 	{
   659 		IA32_Write_Jump32_Abs(jit, call, NativeCallback);
   660 	} else {
   661 		IA32_Write_Jump32_Abs(jit, call, NativeCallback_Debug);
   662 	}
   663 
   664 	/* Test for error */
   665 	//mov ecx, [esi+context]
   666 	//cmp [ecx+err], 0
   667 	//jnz :error
   668 	IA32_Mov_Reg_Rm_Disp8(jit, AMX_REG_TMP, AMX_REG_INFO, AMX_INFO_CONTEXT);
   669 	IA32_Cmp_Rm_Disp8_Imm8(jit, AMX_REG_TMP, offsetof(sp_context_t, n_err), 0);
   670 	IA32_Jump_Cond_Imm32_Abs(jit, CC_NZ, data->jit_extern_error);
   671 
   672 	/* restore what we damaged */
   673 	//add esp, 4*3
   674 	//add edi, ebp
   675 	//pop edx
   676 	//pop ecx	; num_params
   677 	IA32_Add_Rm_Imm8(jit, REG_ESP, 4*3, MOD_REG);
   678 	IA32_Add_Reg_Rm(jit, AMX_REG_STK, AMX_REG_DAT, MOD_REG);
   679 	IA32_Pop_Reg(jit, AMX_REG_ALT);
   680 	IA32_Pop_Reg(jit, REG_ECX);
   681 
   682 	/* pop the AMX stack.  do not check the margins.
   683 	 * Note that this is not a true macro - we don't bother to
   684 	 * set ALT here because nothing will be using it.
   685 	 */
   686 	//lea edi, [edi+ecx*4+4]
   687 	IA32_Lea_Reg_DispRegMultImm8(jit, AMX_REG_STK, AMX_REG_STK, REG_ECX, SCALE4, 4);
   688 
   689 	//ret
   690 	IA32_Return(jit);
   691 }
   692 
   693 void WriteOp_Tracker_Push_Reg(JitWriter *jit, uint8_t reg)
   694 {
   695 	CompData *data = (CompData *)jit->data;
   696 
   697 	/* Save registers that may be damaged by the call */
   698 	//push eax
   699 	//push ecx
   700 	//push edi
   701 	//lea edi, [<reg>*4]		; we want the count in bytes not in cells
   702 	IA32_Push_Reg(jit, AMX_REG_PRI);
   703 	if (reg == REG_ECX)
   704 	{
   705 		IA32_Push_Reg(jit, AMX_REG_TMP);
   706 	}
   707 	IA32_Push_Reg(jit, AMX_REG_STK);
   708 	IA32_Lea_Reg_RegMultImm32(jit, REG_EDI, reg, SCALE4, 0);
   709 
   710 	/* Get the context ptr, push it and call the check */
   711 	//mov eax, [esi+context]
   712 	//push eax
   713 	//call JIT_VerifyOrAllocateTracker
   714 	IA32_Mov_Reg_Rm_Disp8(jit, REG_EAX, AMX_REG_INFO, AMX_INFO_CONTEXT);
   715 	IA32_Push_Reg(jit, REG_EAX);
   716 	jitoffs_t call = IA32_Call_Imm32(jit, 0);
   717 	IA32_Write_Jump32_Abs(jit, call, JIT_VerifyOrAllocateTracker);
   718 
   719 	/* Check for errors */
   720 	//cmp eax, 0
   721 	//jnz :error
   722 	IA32_Cmp_Rm_Imm8(jit, MOD_REG, REG_EAX, 0);
   723 	IA32_Jump_Cond_Imm32_Abs(jit, CC_NZ, data->jit_return);
   724 
   725 	/* Restore */
   726 	//pop eax
   727 	IA32_Pop_Reg(jit, REG_EAX);
   728 	
   729 	/* Push the register into the stack and increment pCur */
   730 	//mov edx, [eax+vm[]]
   731 	//mov eax, [edx+pcur]
   732 	//add [edx+pcur], 4
   733 	//mov [eax], edi
   734 	IA32_Mov_Reg_Rm_Disp8(jit, REG_EDX, REG_EAX, offsetof(sp_context_t, vm[JITVARS_TRACKER]));
   735 	IA32_Mov_Reg_Rm_Disp8(jit, REG_EAX, REG_EDX, offsetof(tracker_t, pCur));
   736 	IA32_Add_Rm_Imm8_Disp8(jit, REG_EDX, 4, offsetof(tracker_t, pCur));
   737 	IA32_Mov_Rm_Reg(jit, REG_EAX, REG_EDI, MOD_MEM_REG);
   738 
   739 	/* Restore PRI, ALT and STK */
   740 	//pop edi
   741 	//pop ecx
   742 	//pop eax
   743 	IA32_Pop_Reg(jit, AMX_REG_STK);
   744 	if (reg == REG_ECX)
   745 	{
   746 		IA32_Pop_Reg(jit, AMX_REG_TMP);
   747 	}
   748 	IA32_Pop_Reg(jit, AMX_REG_PRI);
   749 }
   750 
   751 int JIT_VerifyOrAllocateTracker(sp_context_t *ctx)
   752 {
   753 	tracker_t *trk = (tracker_t *)(ctx->vm[JITVARS_TRACKER]);
   754 
   755 	if ((size_t)(trk->pCur - trk->pBase) >= trk->size)
   756 	{
   757 		return SP_ERROR_TRACKER_BOUNDS;
   758 	}
   759 
   760 	if (trk->pCur+1 - (trk->pBase + trk->size) == 0)
   761 	{
   762 		size_t disp = trk->size - 1;
   763 		trk->size *= 2;
   764 		trk->pBase = (ucell_t *)realloc(trk->pBase, trk->size * sizeof(cell_t));
   765 
   766 		if (!trk->pBase)
   767 		{
   768 			return SP_ERROR_TRACKER_BOUNDS;
   769 		}
   770 
   771 		trk->pCur = trk->pBase + disp;
   772 	}
   773 
   774 	return SP_ERROR_NONE;
   775 }
   776 
   777 int JIT_VerifyLowBoundTracker(sp_context_t *ctx)
   778 {
   779 	tracker_t *trk = (tracker_t *)(ctx->vm[JITVARS_TRACKER]);
   780 
   781 	if (trk->pCur <= trk->pBase)
   782 	{
   783 		return SP_ERROR_TRACKER_BOUNDS;
   784 	}
   785 
   786 	return SP_ERROR_NONE;
   787 }