sourcepawn/jit/x86/opcode_helpers.cpp
author Borja Ferrer <faluco@alliedmods.net>
Wed Jul 25 05:02:58 2007 +0000 (2007-07-25)
changeset 1166 a0aca246b033
parent 853 0bca7912df0e
child 1330 ab94316bb52b
permissions -rw-r--r--
fixed divine bug amb657, having a misaligned stack would crash on SSE instructions
     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 	//:TODO: align the stack to 16bytes like in sysreq.x
   156 	/* NOTE, Hack! PUSHAD pushes EDI last which still has the CIP */
   157 	//pushad
   158 	//push [esi+frm]
   159 	//push ctx
   160 	//mov ecx, [ecx+dbreak]
   161 	//call ecx
   162 	//add esp, 8
   163 	//popad
   164 	IA32_Pushad(jit);
   165 	IA32_Push_Rm_Disp8(jit, AMX_REG_INFO, AMX_INFO_FRAME); //:TODO: move to regs and push? and dont disp for 0
   166 	IA32_Push_Reg(jit, AMX_REG_TMP);
   167 	IA32_Mov_Reg_Rm_Disp8(jit, AMX_REG_TMP, AMX_REG_TMP, offsetof(sp_context_t, dbreak));
   168 	IA32_Call_Reg(jit, AMX_REG_TMP);
   169 	IA32_Add_Rm_Imm8(jit, REG_ESP, 4*2, MOD_REG);
   170 	IA32_Popad(jit);
   171 
   172 	//:nocall
   173 	//pop edi
   174 	//ret
   175 	IA32_Pop_Reg(jit, REG_EDI);
   176 	IA32_Send_Jump8_Here(jit, jmp);
   177 	IA32_Return(jit);
   178 }
   179 
   180 void Write_GetError(JitWriter *jit)
   181 {
   182 	CompData *data = (CompData *)jit->data;
   183 
   184 	//mov eax, [esi+info.context]
   185 	//mov eax, [eax+ctx.error]
   186 	//jmp [jit_return]
   187 	IA32_Mov_Reg_Rm_Disp8(jit, REG_EAX, AMX_REG_INFO, AMX_INFO_CONTEXT);
   188 	IA32_Mov_Reg_Rm_Disp8(jit, REG_EAX, REG_EAX, offsetof(sp_context_t, n_err));
   189 	IA32_Jump_Imm32_Abs(jit, data->jit_return);
   190 }
   191 
   192 void Write_SetError(JitWriter *jit, int error)
   193 {
   194 	CompData *data = (CompData *)jit->data;
   195 
   196 	//mov eax, <error>
   197 	//jmp [jit_return]
   198 	IA32_Mov_Reg_Imm32(jit, REG_EAX, error);
   199 	IA32_Jump_Imm32_Abs(jit, data->jit_return);
   200 }
   201 
   202 void Write_Check_DivZero(JitWriter *jit, jit_uint8_t reg)
   203 {
   204 	//test reg, reg
   205 	//jz :error
   206 	IA32_Test_Rm_Reg(jit, reg, reg, MOD_REG);
   207 	IA32_Jump_Cond_Imm32_Abs(jit, CC_Z, ((CompData *)jit->data)->jit_error_divzero);
   208 }
   209 
   210 void Write_CheckHeap_Min(JitWriter *jit)
   211 {
   212 	/* Check if the stack went beyond the heap low.
   213 	 * This usually means there was a compiler error.
   214 	 * NOTE: Special optimization here.
   215 	 * The heap low is always known ahead of time! :)
   216 	 */
   217 	CompData *data = (CompData *)jit->data;
   218 	//cmp [esi+info.heap], <heaplow>
   219 	//jb :error
   220 	IA32_Cmp_Rm_Imm32_Disp8(jit, AMX_REG_INFO, AMX_INFO_HEAP, data->plugin->data_size);
   221 	IA32_Jump_Cond_Imm32_Abs(jit, CC_B, data->jit_error_heapmin);
   222 }
   223 
   224 void Write_CheckHeap_Low(JitWriter *jit)
   225 {
   226 	/* Check if the heap is trying to grow beyond the stack.
   227 	 */
   228 	//mov ecx, [esi+info.heap]
   229 	//lea ecx, [ebp+ecx+STACK_MARGIN]
   230 	//cmp ecx, edi
   231 	//ja :error		; I think this is right
   232 	IA32_Mov_Reg_Rm_Disp8(jit, AMX_REG_TMP, AMX_REG_INFO, AMX_INFO_HEAP);
   233 	IA32_Lea_Reg_DispRegMultImm8(jit, AMX_REG_TMP, AMX_REG_DAT, AMX_REG_TMP, NOSCALE, STACK_MARGIN);
   234 	IA32_Cmp_Reg_Rm(jit, AMX_REG_TMP, AMX_REG_STK, MOD_REG);
   235 	IA32_Jump_Cond_Imm32_Abs(jit, CC_A, ((CompData *)jit->data)->jit_error_heaplow);
   236 }
   237 
   238 void Write_CheckStack_Min(JitWriter *jit)
   239 {
   240 	/* Check if the stack went beyond the stack top
   241 	 * This usually means there was a compiler error.
   242 	 */
   243 	//cmp edi, [esi+info.stacktop]
   244 	//jae :error
   245 	IA32_Cmp_Reg_Rm_Disp8(jit, AMX_REG_STK, AMX_REG_INFO, AMX_INFO_STACKTOP);
   246 	IA32_Jump_Cond_Imm32_Abs(jit, CC_AE, ((CompData *)jit->data)->jit_error_stackmin);
   247 }
   248 
   249 void Write_CheckStack_Low(JitWriter *jit)
   250 {
   251 	/* Check if the stack went beyond the heap boundary.
   252 	 * Unfortunately this one isn't as quick as the other check.
   253 	 * The stack margin check is important for sysreq.n having space.
   254 	 */
   255 	//mov ecx, [esi+info.heap]
   256 	//lea ecx, [ebp+ecx+STACK_MARGIN]
   257 	//cmp edi, ecx
   258 	//jb :error		; I think this is right
   259 	IA32_Mov_Reg_Rm_Disp8(jit, AMX_REG_TMP, AMX_REG_INFO, AMX_INFO_HEAP);
   260 	IA32_Lea_Reg_DispRegMultImm8(jit, AMX_REG_TMP, AMX_REG_DAT, AMX_REG_TMP, NOSCALE, STACK_MARGIN);
   261 	IA32_Cmp_Reg_Rm(jit, AMX_REG_STK, AMX_REG_TMP, MOD_REG);
   262 	IA32_Jump_Cond_Imm32_Abs(jit, CC_B, ((CompData *)jit->data)->jit_error_stacklow);
   263 }
   264 
   265 void Write_Check_VerifyAddr(JitWriter *jit, jit_uint8_t reg)
   266 {
   267 	CompData *data = (CompData *)jit->data;
   268 
   269 	/* :TODO: Should this be checking for below heaplow?
   270 	 * The old JIT did not.
   271 	 */
   272 
   273 	bool call = false;
   274 	if (!(data->inline_level & JIT_INLINE_ERRORCHECKS))
   275 	{
   276 		/* If we're not in the initial generation phase,
   277 		 * Write a call to the actual routine instead.
   278 		 */
   279 		if ((reg == REG_EAX) && data->jit_verify_addr_eax)
   280 		{
   281 			jitoffs_t call = IA32_Call_Imm32(jit, 0);
   282 			IA32_Write_Jump32(jit, call, data->jit_verify_addr_eax);
   283 			return;
   284 		} else if ((reg == REG_EDX) && data->jit_verify_addr_edx) {
   285 			jitoffs_t call = IA32_Call_Imm32(jit, 0);
   286 			IA32_Write_Jump32(jit, call, data->jit_verify_addr_edx);
   287 			return;
   288 		}
   289 		call = true;
   290 	}
   291 
   292 	/** 
   293 	 * :TODO: If we can't find a nicer way of doing this,
   294 	 *  then scrap it on high optimizations.  The second portion is not needed at all!
   295 	 */
   296 
   297 	/* Part 1: Check if we're in the memory bounds */
   298 	//cmp <reg>, <stpu>
   299 	//jae :error
   300 	IA32_Cmp_Rm_Imm32(jit, MOD_REG, reg, ((CompData *)jit->data)->plugin->memory);
   301 	IA32_Jump_Cond_Imm32_Abs(jit, CC_AE, ((CompData *)jit->data)->jit_error_memaccess);
   302 
   303 	/* Part 2: Check if we're in the invalid region between HP and SP */
   304 	jitoffs_t jmp;
   305 	//cmp <reg>, [esi+info.heap]
   306 	//jb :continue
   307 	//lea ecx, [ebp+<reg>]
   308 	//cmp edi, ecx
   309 	//jb :error
   310 	//:continue
   311 	IA32_Cmp_Reg_Rm_Disp8(jit, reg, AMX_REG_INFO, AMX_INFO_HEAP);
   312 	jmp = IA32_Jump_Cond_Imm8(jit, CC_B, 0);
   313 	IA32_Lea_Reg_DispRegMultImm8(jit, AMX_REG_TMP, AMX_REG_DAT, reg, NOSCALE, 0);
   314 	IA32_Cmp_Reg_Rm(jit, AMX_REG_TMP, AMX_REG_STK, MOD_REG);
   315 	IA32_Jump_Cond_Imm32_Abs(jit, CC_B, ((CompData *)jit->data)->jit_error_memaccess);
   316 	IA32_Send_Jump8_Here(jit, jmp);
   317 
   318 	if (call)
   319 	{
   320 		IA32_Return(jit);
   321 	}
   322 }
   323 
   324 void Macro_PushN_Addr(JitWriter *jit, int i)
   325 {
   326 	//push eax
   327 	//mov eax, [esi+frm]
   328 	//loop i times:
   329 	// lea ecx, [eax+<val>]
   330 	// mov [edi-4*i], ecx
   331 	//sub edi, 4*N
   332 	//pop eax
   333 
   334 	cell_t val;
   335 	int n = 1;
   336 	IA32_Push_Reg(jit, AMX_REG_PRI);
   337 	IA32_Mov_Reg_Rm(jit, AMX_REG_PRI, AMX_REG_INFO, MOD_MEM_REG);
   338 	do
   339 	{
   340 		val = jit->read_cell();
   341 		if (val < SCHAR_MAX && val > SCHAR_MIN)
   342 			IA32_Lea_DispRegImm8(jit, AMX_REG_TMP, AMX_REG_PRI, (jit_int8_t)val);
   343 		else
   344 			IA32_Lea_DispRegImm32(jit, AMX_REG_TMP, AMX_REG_PRI, val);
   345 		IA32_Mov_Rm_Reg_Disp8(jit, AMX_REG_STK, AMX_REG_TMP, -4*n);
   346 	} while (n++ < i);
   347 	IA32_Sub_Rm_Imm8(jit, AMX_REG_STK, 4*i, MOD_REG);
   348 	IA32_Pop_Reg(jit, AMX_REG_PRI);
   349 }
   350 
   351 void Macro_PushN_S(JitWriter *jit, int i)
   352 {
   353 	//loop i times:
   354 	// mov ecx, [ebx+<val>]
   355 	// mov [edi-4*i], ecx
   356 	//sub edi, 4*N
   357 
   358 	cell_t val;
   359 	int n = 1;
   360 	do 
   361 	{
   362 		val = jit->read_cell();
   363 		if (val < SCHAR_MAX && val > SCHAR_MIN)
   364 			IA32_Mov_Reg_Rm_Disp8(jit, AMX_REG_TMP, AMX_REG_FRM, (jit_int8_t)val);
   365 		else
   366 			IA32_Mov_Reg_Rm_Disp32(jit, AMX_REG_TMP, AMX_REG_FRM, val);
   367 		IA32_Mov_Rm_Reg_Disp8(jit, AMX_REG_STK, AMX_REG_TMP, -4*n);
   368 	} while (n++ < i);
   369 	IA32_Sub_Rm_Imm8(jit, AMX_REG_STK, 4*i, MOD_REG);
   370 }
   371 
   372 void Macro_PushN_C(JitWriter *jit, int i)
   373 {
   374 	//loop i times:
   375 	// mov [edi-4*i], <val>
   376 	//sub edi, 4*N
   377 
   378 	int n = 1;
   379 	do 
   380 	{
   381 		IA32_Mov_Rm_Imm32_Disp8(jit, AMX_REG_STK, jit->read_cell(), -4*n);
   382 	} while (n++ < i);
   383 	IA32_Sub_Rm_Imm8(jit, AMX_REG_STK, 4*i, MOD_REG);
   384 }
   385 
   386 void Macro_PushN(JitWriter *jit, int i)
   387 {
   388 	//loop i times:
   389 	// mov ecx, [ebp+<val>]
   390 	// mov [edi-4*i], ecx
   391 	//sub edi, 4*N
   392 
   393 	cell_t val;
   394 	int n = 1;
   395 	do 
   396 	{
   397 		val = jit->read_cell();
   398 		if (val < SCHAR_MAX && val > SCHAR_MIN)
   399 			IA32_Mov_Reg_Rm_Disp8(jit, AMX_REG_TMP, AMX_REG_DAT, (jit_int8_t)val);
   400 		else
   401 			IA32_Mov_Reg_Rm_Disp32(jit, AMX_REG_TMP, AMX_REG_DAT, val);
   402 		IA32_Mov_Rm_Reg_Disp8(jit, AMX_REG_STK, AMX_REG_TMP, -4*n);
   403 	} while (n++ < i);
   404 	IA32_Sub_Rm_Imm8(jit, AMX_REG_STK, 4*i, MOD_REG);
   405 }
   406 
   407 void WriteOp_Sysreq_C_Function(JitWriter *jit)
   408 {
   409 	/* The small daddy of the big daddy of opcodes.
   410 	 * ecx - native index
   411 	 */
   412 	CompData *data = (CompData *)jit->data;
   413 
   414 	/* save registers we will need */
   415 	//push edx
   416 	IA32_Push_Reg(jit, AMX_REG_ALT);
   417 
   418 	/* Align the stack to 16 bytes */
   419 	//push ebx
   420 	//mov ebx, esp
   421 	//and esp, 0xFFFFFF0
   422 	//sub esp, 4
   423 	IA32_Push_Reg(jit, REG_EBX);
   424 	IA32_Mov_Reg_Rm(jit, REG_EBX, REG_ESP, MOD_REG);
   425 	IA32_And_Rm_Imm8(jit, REG_ESP, MOD_REG, -16);
   426 	IA32_Sub_Rm_Imm8(jit, REG_ESP, 4, MOD_REG);
   427 
   428 	/* push some callback stuff */
   429 	//push edi		; stack
   430 	//push ecx		; native index
   431 	IA32_Push_Reg(jit, AMX_REG_STK);
   432 	IA32_Push_Reg(jit, REG_ECX);
   433 
   434 	/* Relocate stack, heap, frm information, then store back */
   435 	//mov eax, [esi+context]
   436 	//mov ecx, [esi+hea]
   437 	//sub edi, ebp
   438 	//mov [eax+hp], ecx
   439 	//mov ecx, [esi]
   440 	//mov [eax+sp], edi
   441 	//mov [eax+frm], ecx
   442 	IA32_Mov_Reg_Rm_Disp8(jit, REG_EAX, AMX_REG_INFO, AMX_INFO_CONTEXT);
   443 	IA32_Mov_Reg_Rm_Disp8(jit, AMX_REG_TMP, AMX_REG_INFO, AMX_INFO_HEAP);
   444 	IA32_Sub_Reg_Rm(jit, AMX_REG_STK, AMX_REG_DAT, MOD_REG);
   445 	IA32_Mov_Rm_Reg_Disp8(jit, REG_EAX, AMX_REG_TMP, offsetof(sp_context_t, hp));
   446 	IA32_Mov_Reg_Rm(jit, AMX_REG_TMP, AMX_INFO_FRM, MOD_MEM_REG);
   447 	IA32_Mov_Rm_Reg_Disp8(jit, REG_EAX, AMX_REG_STK, offsetof(sp_context_t, sp));
   448 	IA32_Mov_Rm_Reg_Disp8(jit, REG_EAX, AMX_REG_TMP, offsetof(sp_context_t, frm));
   449 
   450 	/* finally, push the last parameter and make the call */
   451 	//push eax		; context
   452 	//call NativeCallback
   453 	IA32_Push_Reg(jit, REG_EAX);
   454 	jitoffs_t call = IA32_Call_Imm32(jit, 0);
   455 	if (!data->debug)
   456 	{
   457 		IA32_Write_Jump32_Abs(jit, call, (void *)NativeCallback);
   458 	} else {
   459 		IA32_Write_Jump32_Abs(jit, call, (void *)NativeCallback_Debug);
   460 	}
   461 
   462 	/* Test for error */
   463 	//mov ecx, [esi+context]
   464 	//cmp [ecx+err], 0
   465 	//jnz :error
   466 	IA32_Mov_Reg_Rm_Disp8(jit, AMX_REG_TMP, AMX_REG_INFO, AMX_INFO_CONTEXT);
   467 	IA32_Cmp_Rm_Disp8_Imm8(jit, AMX_REG_TMP, offsetof(sp_context_t, n_err), 0);
   468 	IA32_Jump_Cond_Imm32_Abs(jit, CC_NZ, data->jit_extern_error);
   469 
   470 	/* restore what we damaged */
   471 	//mov esp, ebx
   472 	//pop ebx
   473 	//add edi, ebp
   474 	//pop edx
   475 	IA32_Mov_Reg_Rm(jit, REG_ESP, REG_EBX, MOD_REG);
   476 	IA32_Pop_Reg(jit, REG_EBX);
   477 	IA32_Add_Reg_Rm(jit, AMX_REG_STK, AMX_REG_DAT, MOD_REG);
   478 	IA32_Pop_Reg(jit, AMX_REG_ALT);
   479 
   480 	//ret
   481 	IA32_Return(jit);
   482 }
   483 
   484 void GenerateArrayIndirectionVectors(cell_t *arraybase, cell_t dims[], ucell_t _dimcount, bool autozero)
   485 {
   486 	cell_t vectors = 1;				/* we need one vector to start off with */
   487 	cell_t cur_offs = 0;
   488 	cell_t cur_write = 0;
   489 	cell_t dimcount = _dimcount;
   490 
   491 	/* Initialize rotation */
   492 	cur_write = dims[dimcount-1];
   493 	
   494 	while (--dimcount >= 1)
   495 	{
   496 		cell_t cur_dim = dims[dimcount];
   497 		cell_t sub_dim = dims[dimcount-1];
   498 		for (cell_t i=0; i<vectors; i++)
   499 		{
   500 			for (cell_t j=0; j<cur_dim; j++)
   501 			{
   502 				arraybase[cur_offs] = (cur_write - cur_offs)*sizeof(cell_t);
   503 				cur_offs++;
   504 				cur_write += sub_dim;
   505 			}
   506 		}
   507 		vectors = cur_dim;
   508 	}
   509 
   510 	/* everything after cur_offs can be zeroed */
   511 	if (autozero)
   512 	{
   513 		size_t size = 1;
   514 		for (ucell_t i=0; i<_dimcount; i++)
   515 		{
   516 			size *= dims[i];
   517 		}
   518 		memset(&arraybase[cur_offs], 0, size*sizeof(cell_t));
   519 	}
   520 
   521 	return;
   522 }
   523 
   524 /**
   525  * A few notes about this function.
   526  * I was more concerned about efficient use of registers here, rather than 
   527  *  fine-tuned optimization.  The reason is that the code is already complicated,
   528  *  and it is very easy to mess up.
   529  */
   530 void WriteIntrinsic_GenArray(JitWriter *jit)
   531 {
   532 	/**
   533 	 * save important values
   534 	 */
   535 	//push ebx
   536 	//push eax
   537 	//push edx
   538 	//push ecx				;value is referenced on stack
   539 	IA32_Push_Reg(jit, REG_EBX);
   540 	IA32_Push_Reg(jit, REG_EAX);
   541 	IA32_Push_Reg(jit, REG_EDX);
   542 	IA32_Push_Reg(jit, REG_ECX);
   543 
   544 	/**
   545 	 * Calculate how many cells will be needed.
   546 	 */
   547 	//mov edx, [edi]		;get last dimension's count
   548 	//mov eax, 1			;position at second to last dimension
   549 	//:loop
   550 	//cmp eax, [esp]		;compare to # of params
   551 	//jae :done				;end loop if done
   552 	//mov ecx, [edi+eax*4]	;get dimension size
   553 	//imul edx, ecx			;multiply by size
   554 	//add eax, 1			;increment
   555 	//add edx, ecx			;add size (indirection vector)
   556 	//jmp :loop				;jump back
   557 	//:done
   558 	IA32_Mov_Reg_Rm(jit, REG_EDX, AMX_REG_STK, MOD_MEM_REG);
   559 	IA32_Mov_Reg_Imm32(jit, REG_EAX, 1);
   560 	jitoffs_t loop1 = jit->get_outputpos();
   561 	IA32_Cmp_Reg_Rm_ESP(jit, REG_EAX);
   562 	jitoffs_t done1 = IA32_Jump_Cond_Imm8(jit, CC_AE, 0);
   563 	IA32_Mov_Reg_Rm_Disp_Reg(jit, REG_ECX, AMX_REG_STK, REG_EAX, SCALE4);
   564 	IA32_IMul_Reg_Rm(jit, REG_EDX, REG_ECX, MOD_REG);
   565 	IA32_Add_Rm_Imm8(jit, REG_EAX, 1, MOD_REG);
   566 	IA32_Add_Reg_Rm(jit, REG_EDX, REG_ECX, MOD_REG);
   567 	IA32_Write_Jump8(jit, IA32_Jump_Imm8(jit, loop1), loop1);
   568 	IA32_Send_Jump8_Here(jit, done1);
   569 
   570 	/* Test if we have heap space for this */
   571 	//mov eax, [esi+info.heap]	;get heap pointer
   572 	//lea eax, [eax+edx*4]		;new heap pointer
   573 	//cmp eax, <hlw>			;compare to heap low
   574 	//jbe :error				;die if we hit this (it should always be >)
   575 	//add eax, ebp				;relocate to stack
   576 	//cmp eax, edi				;die if above the stack pointer
   577 	//jae :error
   578 	IA32_Mov_Reg_Rm_Disp8(jit, REG_EAX, AMX_REG_INFO, AMX_INFO_HEAP);
   579 	IA32_Lea_Reg_DispRegMult(jit, REG_EAX, REG_EAX, REG_EDX, SCALE4);
   580 	IA32_Cmp_Rm_Imm32(jit, MOD_REG, REG_EAX, ((CompData *)jit->data)->plugin->data_size);
   581 	IA32_Jump_Cond_Imm32_Abs(jit, CC_BE, ((CompData *)jit->data)->jit_error_array_too_big);
   582 	IA32_Add_Reg_Rm(jit, REG_EAX, AMX_REG_DAT, MOD_REG);
   583 	IA32_Cmp_Reg_Rm(jit, REG_EAX, AMX_REG_STK, MOD_REG);
   584 	IA32_Jump_Cond_Imm32_Abs(jit, CC_AE, ((CompData *)jit->data)->jit_error_array_too_big);
   585 	
   586 	/* Prepare for indirection iteration */
   587 	//mov eax, [esi+info.heap]	;get heap pointer
   588 	//lea ebx, [eax+edx*4]		;new heap pointer
   589 	//mov [esi+info.heap], ebx	;store back
   590 	//push eax					;save heap pointer - we need it
   591 	IA32_Mov_Reg_Rm_Disp8(jit, REG_EAX, AMX_REG_INFO, AMX_INFO_HEAP);
   592 	IA32_Lea_Reg_DispRegMult(jit, REG_EBX, REG_EAX, REG_EDX, SCALE4);
   593 	IA32_Mov_Rm_Reg_Disp8(jit, AMX_REG_INFO, REG_EBX, AMX_INFO_HEAP);
   594 	IA32_Push_Reg(jit, REG_EAX);
   595 
   596 	WriteOp_Tracker_Push_Reg(jit, REG_EDX);
   597 	
   598 	/* This part is too messy to do in straight assembly.
   599 	 * I'm letting the compiler handle it and thus it's in C.
   600 	 */
   601 	//lea ebx, [ebp+eax]		;get base pointer
   602 	//push dword [esp-8]		;push autozero
   603 	//push dword [esp-8]		;push dimension count
   604 	//push edi					;push dim array
   605 	//push ebx
   606 	//call GenerateArrayIndirectionVectors
   607 	//add esp, 4*4
   608 	IA32_Lea_Reg_DispRegMult(jit, REG_EBX, REG_EAX, REG_EBP, NOSCALE);
   609 	IA32_Push_Rm_Disp8_ESP(jit, 8);
   610 	IA32_Push_Rm_Disp8_ESP(jit, 8);
   611 	IA32_Push_Reg(jit, REG_EDI);
   612 	IA32_Push_Reg(jit, REG_EBX);
   613 	IA32_Write_Jump32_Abs(jit, IA32_Call_Imm32(jit, 0), (void *)&GenerateArrayIndirectionVectors);
   614 	IA32_Add_Rm_Imm8(jit, REG_ESP, 4*4, MOD_REG);
   615 
   616 	/* Store the heap pointer back into the stack */
   617 	//pop eax					;restore heap pointer
   618 	//pop ecx					;restore param count
   619 	//lea edi, [edi+ecx*4-4]	;pop params-4 off the stack
   620 	//mov [edi], eax			;store back the heap pointer
   621 	IA32_Pop_Reg(jit, REG_EAX);
   622 	IA32_Pop_Reg(jit, REG_ECX);
   623 	IA32_Lea_Reg_DispRegMultImm8(jit, AMX_REG_STK, AMX_REG_STK, REG_ECX, SCALE4, -4);
   624 	IA32_Mov_Rm_Reg(jit, AMX_REG_STK, REG_EAX, MOD_MEM_REG);
   625 
   626 	/* Return to caller */
   627 	//pop edx
   628 	//pop eax
   629 	//pop ebx
   630 	//ret
   631 	IA32_Pop_Reg(jit, REG_ECX);
   632 	IA32_Pop_Reg(jit, REG_EAX);
   633 	IA32_Pop_Reg(jit, REG_EBX);
   634 	IA32_Return(jit);
   635 }
   636 
   637 void WriteOp_Sysreq_N_Function(JitWriter *jit)
   638 {
   639 	/* The big daddy of opcodes.
   640 	 * eax - num_params
   641 	 * ecx - native index
   642 	 */
   643 	CompData *data = (CompData *)jit->data;
   644 
   645 	/* store the number of parameters on the stack */
   646 	//mov [edi-4], eax
   647 	//sub edi, 4
   648 	IA32_Mov_Rm_Reg_Disp8(jit, AMX_REG_STK, REG_EAX, -4);
   649 	IA32_Sub_Rm_Imm8(jit, AMX_REG_STK, 4, MOD_REG);
   650 
   651 	/* save registers we will need */
   652 	//push eax	; num_params for stack popping
   653 	//push edx
   654 	IA32_Push_Reg(jit, REG_EAX);
   655 	IA32_Push_Reg(jit, AMX_REG_ALT);
   656 
   657 	/* Align the stack to 16 bytes */
   658 	//push ebx
   659 	//mov ebx, esp
   660 	//and esp, 0xFFFFFF0
   661 	//sub esp, 4
   662 	IA32_Push_Reg(jit, REG_EBX);
   663 	IA32_Mov_Reg_Rm(jit, REG_EBX, REG_ESP, MOD_REG);
   664 	IA32_And_Rm_Imm8(jit, REG_ESP, MOD_REG, -16);
   665 	IA32_Sub_Rm_Imm8(jit, REG_ESP, 4, MOD_REG);
   666 
   667 	/* push some callback stuff */
   668 	//push edi		; stack
   669 	//push ecx		; native index
   670 	IA32_Push_Reg(jit, AMX_REG_STK);
   671 	IA32_Push_Reg(jit, REG_ECX);
   672 
   673 	/* Relocate stack, heap, frm information, then store back */
   674 	//mov eax, [esi+context]
   675 	//mov ecx, [esi+hea]
   676 	//sub edi, ebp
   677 	//mov [eax+hp], ecx
   678 	//mov ecx, [esi]
   679 	//mov [eax+sp], edi
   680 	//mov [eax+frm], ecx
   681 	IA32_Mov_Reg_Rm_Disp8(jit, REG_EAX, AMX_REG_INFO, AMX_INFO_CONTEXT);
   682 	IA32_Mov_Reg_Rm_Disp8(jit, AMX_REG_TMP, AMX_REG_INFO, AMX_INFO_HEAP);
   683 	IA32_Sub_Reg_Rm(jit, AMX_REG_STK, AMX_REG_DAT, MOD_REG);
   684 	IA32_Mov_Rm_Reg_Disp8(jit, REG_EAX, AMX_REG_TMP, offsetof(sp_context_t, hp));
   685 	IA32_Mov_Reg_Rm(jit, AMX_REG_TMP, AMX_INFO_FRM, MOD_MEM_REG);
   686 	IA32_Mov_Rm_Reg_Disp8(jit, REG_EAX, AMX_REG_STK, offsetof(sp_context_t, sp));
   687 	IA32_Mov_Rm_Reg_Disp8(jit, REG_EAX, AMX_REG_TMP, offsetof(sp_context_t, frm));
   688 
   689 	/* finally, push the last parameter and make the call */
   690 	//push eax		; context
   691 	//call NativeCallback
   692 	IA32_Push_Reg(jit, REG_EAX);
   693 	jitoffs_t call = IA32_Call_Imm32(jit, 0);
   694 	if (!data->debug)
   695 	{
   696 		IA32_Write_Jump32_Abs(jit, call, (void *)NativeCallback);
   697 	} else {
   698 		IA32_Write_Jump32_Abs(jit, call, (void *)NativeCallback_Debug);
   699 	}
   700 
   701 	/* Test for error */
   702 	//mov ecx, [esi+context]
   703 	//cmp [ecx+err], 0
   704 	//jnz :error
   705 	IA32_Mov_Reg_Rm_Disp8(jit, AMX_REG_TMP, AMX_REG_INFO, AMX_INFO_CONTEXT);
   706 	IA32_Cmp_Rm_Disp8_Imm8(jit, AMX_REG_TMP, offsetof(sp_context_t, n_err), 0);
   707 	IA32_Jump_Cond_Imm32_Abs(jit, CC_NZ, data->jit_extern_error);
   708 
   709 	/* restore what we damaged */
   710 	//mov esp, ebx
   711 	//pop ebx
   712 	//add edi, ebp
   713 	//pop edx
   714 	//pop ecx	; num_params
   715 	IA32_Mov_Reg_Rm(jit, REG_ESP, REG_EBX, MOD_REG);
   716 	IA32_Pop_Reg(jit, REG_EBX);
   717 	IA32_Add_Reg_Rm(jit, AMX_REG_STK, AMX_REG_DAT, MOD_REG);
   718 	IA32_Pop_Reg(jit, AMX_REG_ALT);
   719 	IA32_Pop_Reg(jit, REG_ECX);
   720 
   721 	/* pop the AMX stack.  do not check the margins.
   722 	 * Note that this is not a true macro - we don't bother to
   723 	 * set ALT here because nothing will be using it.
   724 	 */
   725 	//lea edi, [edi+ecx*4+4]
   726 	IA32_Lea_Reg_DispRegMultImm8(jit, AMX_REG_STK, AMX_REG_STK, REG_ECX, SCALE4, 4);
   727 
   728 	//ret
   729 	IA32_Return(jit);
   730 }
   731 
   732 void WriteOp_Tracker_Push_Reg(JitWriter *jit, uint8_t reg)
   733 {
   734 	CompData *data = (CompData *)jit->data;
   735 
   736 	/* Save registers that may be damaged by the call */
   737 	//push eax
   738 	//push ecx
   739 	//push edi
   740 	//lea edi, [<reg>*4]		; we want the count in bytes not in cells
   741 	IA32_Push_Reg(jit, AMX_REG_PRI);
   742 	if (reg == REG_ECX)
   743 	{
   744 		IA32_Push_Reg(jit, AMX_REG_TMP);
   745 	}
   746 	IA32_Push_Reg(jit, AMX_REG_STK);
   747 	IA32_Lea_Reg_RegMultImm32(jit, REG_EDI, reg, SCALE4, 0);
   748 
   749 	/* Get the context ptr, push it and call the check */
   750 	//mov eax, [esi+context]
   751 	//push eax
   752 	//call JIT_VerifyOrAllocateTracker
   753 	IA32_Mov_Reg_Rm_Disp8(jit, REG_EAX, AMX_REG_INFO, AMX_INFO_CONTEXT);
   754 	IA32_Push_Reg(jit, REG_EAX);
   755 	jitoffs_t call = IA32_Call_Imm32(jit, 0);
   756 	IA32_Write_Jump32_Abs(jit, call, (void *)JIT_VerifyOrAllocateTracker);
   757 
   758 	/* Check for errors */
   759 	//cmp eax, 0
   760 	//jnz :error
   761 	IA32_Cmp_Rm_Imm8(jit, MOD_REG, REG_EAX, 0);
   762 	IA32_Jump_Cond_Imm32_Abs(jit, CC_NZ, data->jit_return);
   763 
   764 	/* Restore */
   765 	//pop eax
   766 	IA32_Pop_Reg(jit, REG_EAX);
   767 	
   768 	/* Push the register into the stack and increment pCur */
   769 	//mov edx, [eax+vm[]]
   770 	//mov eax, [edx+pcur]
   771 	//add [edx+pcur], 4
   772 	//mov [eax], edi
   773 	IA32_Mov_Reg_Rm_Disp8(jit, REG_EDX, REG_EAX, offsetof(sp_context_t, vm[JITVARS_TRACKER]));
   774 	IA32_Mov_Reg_Rm_Disp8(jit, REG_EAX, REG_EDX, offsetof(tracker_t, pCur));
   775 	IA32_Add_Rm_Imm8_Disp8(jit, REG_EDX, 4, offsetof(tracker_t, pCur));
   776 	IA32_Mov_Rm_Reg(jit, REG_EAX, REG_EDI, MOD_MEM_REG);
   777 
   778 	/* Restore PRI, ALT and STK */
   779 	//pop edi
   780 	//pop ecx
   781 	//pop eax
   782 	IA32_Pop_Reg(jit, AMX_REG_STK);
   783 	if (reg == REG_ECX)
   784 	{
   785 		IA32_Pop_Reg(jit, AMX_REG_TMP);
   786 	}
   787 	IA32_Pop_Reg(jit, AMX_REG_PRI);
   788 }
   789 
   790 int JIT_VerifyOrAllocateTracker(sp_context_t *ctx)
   791 {
   792 	tracker_t *trk = (tracker_t *)(ctx->vm[JITVARS_TRACKER]);
   793 
   794 	if ((size_t)(trk->pCur - trk->pBase) >= trk->size)
   795 	{
   796 		return SP_ERROR_TRACKER_BOUNDS;
   797 	}
   798 
   799 	if (trk->pCur+1 - (trk->pBase + trk->size) == 0)
   800 	{
   801 		size_t disp = trk->size - 1;
   802 		trk->size *= 2;
   803 		trk->pBase = (ucell_t *)realloc(trk->pBase, trk->size * sizeof(cell_t));
   804 
   805 		if (!trk->pBase)
   806 		{
   807 			return SP_ERROR_TRACKER_BOUNDS;
   808 		}
   809 
   810 		trk->pCur = trk->pBase + disp;
   811 	}
   812 
   813 	return SP_ERROR_NONE;
   814 }
   815 
   816 int JIT_VerifyLowBoundTracker(sp_context_t *ctx)
   817 {
   818 	tracker_t *trk = (tracker_t *)(ctx->vm[JITVARS_TRACKER]);
   819 
   820 	if (trk->pCur <= trk->pBase)
   821 	{
   822 		return SP_ERROR_TRACKER_BOUNDS;
   823 	}
   824 
   825 	return SP_ERROR_NONE;
   826 }
   827 
   828 void Write_RoundingTable(JitWriter *jit)
   829 {
   830 	jit->write_int32(-1);
   831 	jit->write_int32(0);
   832 	jit->write_int32(1);
   833 }
   834 
   835 void AlignMe(JitWriter *jit)
   836 {
   837 	jitoffs_t cur_offs = jit->get_outputpos();
   838 	jitoffs_t offset = ((cur_offs & 0xFFFFFFF0) + 16) - cur_offs;
   839 
   840 	if (offset)
   841 	{
   842 		for (jit_uint32_t i=0; i<offset; i++)
   843 		{
   844 			jit->write_ubyte(IA32_INT3);
   845 		}
   846 	}
   847 }