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