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