3 * =============================================================================
5 * Copyright (C)2004-2007 AlliedModders LLC. All rights reserved.
6 * =============================================================================
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
20 #include "opcode_helpers.h"
21 #include "x86_macros.h"
23 #define NUM_INFO_PARAMS 5
25 jitoffs_t Write_Execute_Function(JitWriter *jit)
28 * The variables we're passed in:
29 * sp_context_t *ctx, uint32_t code_idx, cell_t *result
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!
41 IA32_Push_Reg(jit, REG_EBP);
42 IA32_Mov_Reg_Rm(jit, REG_EBP, REG_ESP, MOD_REG);
47 IA32_Push_Reg(jit, REG_ESI);
48 IA32_Push_Reg(jit, REG_EDI);
49 IA32_Push_Reg(jit, REG_EBX);
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);
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));
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);
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);
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));
94 /* by now, everything is set up, so we can call into the plugin */
96 IA32_Call_Reg(jit, REG_ECX);
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);
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);
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.
116 //mov ecx, [esi+context]
118 //mov edx, [esi+heap]
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));
127 //add esp, 4*NUM_INFO_PARAMS
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);
143 void Write_BreakDebug(JitWriter *jit)
148 //cmp [ecx+dbreak], 0
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);
156 //:TODO: align the stack to 16bytes like in sysreq.x
157 /* NOTE, Hack! PUSHAD pushes EDI last which still has the CIP */
161 //mov ecx, [ecx+dbreak]
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);
176 IA32_Pop_Reg(jit, REG_EDI);
177 IA32_Send_Jump8_Here(jit, jmp);
181 void Write_GetError(JitWriter *jit)
183 CompData *data = (CompData *)jit->data;
185 //mov eax, [esi+info.context]
186 //mov eax, [eax+ctx.error]
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);
193 void Write_SetError(JitWriter *jit, int error)
195 CompData *data = (CompData *)jit->data;
199 IA32_Mov_Reg_Imm32(jit, REG_EAX, error);
200 IA32_Jump_Imm32_Abs(jit, data->jit_return);
203 void Write_Check_DivZero(JitWriter *jit, jit_uint8_t reg)
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);
211 void Write_CheckHeap_Min(JitWriter *jit)
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! :)
218 CompData *data = (CompData *)jit->data;
219 //cmp [esi+info.heap], <heaplow>
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);
225 void Write_CheckHeap_Low(JitWriter *jit)
227 /* Check if the heap is trying to grow beyond the stack.
229 //mov ecx, [esi+info.heap]
230 //lea ecx, [ebp+ecx+STACK_MARGIN]
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);
239 void Write_CheckStack_Min(JitWriter *jit)
241 /* Check if the stack went beyond the stack top
242 * This usually means there was a compiler error.
244 //cmp edi, [esi+info.stacktop]
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);
250 void Write_CheckStack_Low(JitWriter *jit)
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.
256 //mov ecx, [esi+info.heap]
257 //lea ecx, [ebp+ecx+STACK_MARGIN]
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);
266 void Write_Check_VerifyAddr(JitWriter *jit, jit_uint8_t reg)
268 CompData *data = (CompData *)jit->data;
270 /* :TODO: Should this be checking for below heaplow?
271 * The old JIT did not.
275 if (!(data->inline_level & JIT_INLINE_ERRORCHECKS))
277 /* If we're not in the initial generation phase,
278 * Write a call to the actual routine instead.
280 if ((reg == REG_EAX) && data->jit_verify_addr_eax)
282 jitoffs_t call = IA32_Call_Imm32(jit, 0);
283 IA32_Write_Jump32(jit, call, data->jit_verify_addr_eax);
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);
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!
298 /* Part 1: Check if we're in the memory bounds */
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);
304 /* Part 2: Check if we're in the invalid region between HP and SP */
306 //cmp <reg>, [esi+info.heap]
308 //lea ecx, [ebp+<reg>]
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);
325 void Macro_PushN_Addr(JitWriter *jit, int i)
330 // lea ecx, [eax+<val>]
331 // mov [edi-4*i], ecx
337 IA32_Push_Reg(jit, AMX_REG_PRI);
338 IA32_Mov_Reg_Rm(jit, AMX_REG_PRI, AMX_REG_INFO, MOD_MEM_REG);
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);
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);
348 IA32_Sub_Rm_Imm8(jit, AMX_REG_STK, 4*i, MOD_REG);
349 IA32_Pop_Reg(jit, AMX_REG_PRI);
352 void Macro_PushN_S(JitWriter *jit, int i)
355 // mov ecx, [ebx+<val>]
356 // mov [edi-4*i], ecx
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);
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);
370 IA32_Sub_Rm_Imm8(jit, AMX_REG_STK, 4*i, MOD_REG);
373 void Macro_PushN_C(JitWriter *jit, int i)
376 // mov [edi-4*i], <val>
382 IA32_Mov_Rm_Imm32_Disp8(jit, AMX_REG_STK, jit->read_cell(), -4*n);
384 IA32_Sub_Rm_Imm8(jit, AMX_REG_STK, 4*i, MOD_REG);
387 void Macro_PushN(JitWriter *jit, int i)
390 // mov ecx, [ebp+<val>]
391 // mov [edi-4*i], ecx
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);
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);
405 IA32_Sub_Rm_Imm8(jit, AMX_REG_STK, 4*i, MOD_REG);
408 void WriteOp_Sysreq_C_Function(JitWriter *jit)
410 /* The small daddy of the big daddy of opcodes.
413 CompData *data = (CompData *)jit->data;
415 /* save registers we will need */
417 IA32_Push_Reg(jit, AMX_REG_ALT);
419 /* Align the stack to 16 bytes */
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);
429 /* push some callback stuff */
431 //push ecx ; native index
432 IA32_Push_Reg(jit, AMX_REG_STK);
433 IA32_Push_Reg(jit, REG_ECX);
435 /* Relocate stack, heap, frm information, then store back */
436 //mov eax, [esi+context]
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));
451 /* finally, push the last parameter and make the call */
453 //call NativeCallback
454 IA32_Push_Reg(jit, REG_EAX);
455 jitoffs_t call = IA32_Call_Imm32(jit, 0);
458 IA32_Write_Jump32_Abs(jit, call, (void *)NativeCallback);
460 IA32_Write_Jump32_Abs(jit, call, (void *)NativeCallback_Debug);
464 //mov ecx, [esi+context]
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);
471 /* restore what we damaged */
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);
485 typedef struct array_creation_s
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 */
493 cell_t _GenerateArrayIndirectionVectors(array_creation_t *ar, int dim, cell_t cur_offs)
495 cell_t write_offs = cur_offs;
496 cell_t *data_offs = ar->data_offs;
498 cur_offs += ar->dim_list[dim];
501 * Dimension n-x where x > 2 will have sub-vectors.
502 * Otherwise, we just need to reference the data section.
504 if (ar->dim_count > 2 && dim < ar->dim_count - 2)
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.
511 for (int i = 0; i < ar->dim_list[dim]; i++)
513 ar->base[write_offs] = (cur_offs - write_offs) * sizeof(cell_t);
515 cur_offs = _GenerateArrayIndirectionVectors(ar, dim + 1, cur_offs);
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.
524 for (int i = 0; i < ar->dim_list[dim]; i++)
526 ar->base[write_offs] = (*data_offs - write_offs) * sizeof(cell_t);
528 *data_offs = *data_offs + ar->dim_list[dim + 1];
535 static cell_t calc_indirection(const array_creation_t *ar, cell_t dim)
537 cell_t size = ar->dim_list[dim];
539 if (dim < ar->dim_count - 2)
541 size += ar->dim_list[dim] * calc_indirection(ar, dim + 1);
547 void GenerateArrayIndirectionVectors(cell_t *arraybase, cell_t dims[], cell_t _dimcount, bool autozero)
552 /* Reverse the dimensions */
553 cell_t dim_list[sDIMEN_MAX];
555 for (int i = _dimcount - 1; i >= 0; i--)
557 dim_list[cur_dim++] = dims[i];
561 ar.dim_list = dim_list;
562 ar.dim_count = _dimcount;
563 ar.data_offs = &data_offs;
565 data_offs = calc_indirection(&ar, 0);
567 _GenerateArrayIndirectionVectors(&ar, 0, 0);
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.
576 void WriteIntrinsic_GenArray(JitWriter *jit)
579 * save important values
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);
591 * Calculate how many cells will be needed.
593 //mov edx, [edi] ;get last dimension's count
594 //mov eax, 1 ;position at second to last dimension
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
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);
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
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);
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);
642 WriteOp_Tracker_Push_Reg(jit, REG_EDX);
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.
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
652 //call GenerateArrayIndirectionVectors
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);
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);
672 /* Return to caller */
677 IA32_Pop_Reg(jit, REG_ECX);
678 IA32_Pop_Reg(jit, REG_EAX);
679 IA32_Pop_Reg(jit, REG_EBX);
683 void WriteOp_Sysreq_N_Function(JitWriter *jit)
685 /* The big daddy of opcodes.
689 CompData *data = (CompData *)jit->data;
691 /* store the number of parameters on the stack */
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);
697 /* save registers we will need */
698 //push eax ; num_params for stack popping
700 IA32_Push_Reg(jit, REG_EAX);
701 IA32_Push_Reg(jit, AMX_REG_ALT);
703 /* Align the stack to 16 bytes */
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);
713 /* push some callback stuff */
715 //push ecx ; native index
716 IA32_Push_Reg(jit, AMX_REG_STK);
717 IA32_Push_Reg(jit, REG_ECX);
719 /* Relocate stack, heap, frm information, then store back */
720 //mov eax, [esi+context]
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));
735 /* finally, push the last parameter and make the call */
737 //call NativeCallback
738 IA32_Push_Reg(jit, REG_EAX);
739 jitoffs_t call = IA32_Call_Imm32(jit, 0);
742 IA32_Write_Jump32_Abs(jit, call, (void *)NativeCallback);
744 IA32_Write_Jump32_Abs(jit, call, (void *)NativeCallback_Debug);
748 //mov ecx, [esi+context]
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);
755 /* restore what we damaged */
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);
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.
771 //lea edi, [edi+ecx*4+4]
772 IA32_Lea_Reg_DispRegMultImm8(jit, AMX_REG_STK, AMX_REG_STK, REG_ECX, SCALE4, 4);
778 void WriteOp_Tracker_Push_Reg(JitWriter *jit, uint8_t reg)
780 CompData *data = (CompData *)jit->data;
782 /* Save registers that may be damaged by the call */
786 //lea edi, [<reg>*4] ; we want the count in bytes not in cells
787 IA32_Push_Reg(jit, AMX_REG_PRI);
790 IA32_Push_Reg(jit, AMX_REG_TMP);
792 IA32_Push_Reg(jit, AMX_REG_STK);
793 IA32_Lea_Reg_RegMultImm32(jit, REG_EDI, reg, SCALE4, 0);
795 /* Get the context ptr, push it and call the check */
796 //mov eax, [esi+context]
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);
804 /* Check for errors */
807 IA32_Cmp_Rm_Imm8(jit, MOD_REG, REG_EAX, 0);
808 IA32_Jump_Cond_Imm32_Abs(jit, CC_NZ, data->jit_return);
812 IA32_Pop_Reg(jit, REG_EAX);
814 /* Push the register into the stack and increment pCur */
815 //mov edx, [eax+vm[]]
816 //mov eax, [edx+pcur]
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);
824 /* Restore PRI, ALT and STK */
828 IA32_Pop_Reg(jit, AMX_REG_STK);
831 IA32_Pop_Reg(jit, AMX_REG_TMP);
833 IA32_Pop_Reg(jit, AMX_REG_PRI);
836 int JIT_VerifyOrAllocateTracker(sp_context_t *ctx)
838 tracker_t *trk = (tracker_t *)(ctx->vm[JITVARS_TRACKER]);
840 if ((size_t)(trk->pCur - trk->pBase) >= trk->size)
842 return SP_ERROR_TRACKER_BOUNDS;
845 if (trk->pCur+1 - (trk->pBase + trk->size) == 0)
847 size_t disp = trk->size - 1;
849 trk->pBase = (ucell_t *)realloc(trk->pBase, trk->size * sizeof(cell_t));
853 return SP_ERROR_TRACKER_BOUNDS;
856 trk->pCur = trk->pBase + disp;
859 return SP_ERROR_NONE;
862 int JIT_VerifyLowBoundTracker(sp_context_t *ctx)
864 tracker_t *trk = (tracker_t *)(ctx->vm[JITVARS_TRACKER]);
866 if (trk->pCur <= trk->pBase)
868 return SP_ERROR_TRACKER_BOUNDS;
871 return SP_ERROR_NONE;
874 void Write_RoundingTable(JitWriter *jit)
876 jit->write_int32(-1);
881 void AlignMe(JitWriter *jit)
883 jitoffs_t cur_offs = jit->get_outputpos();
884 jitoffs_t offset = ((cur_offs & 0xFFFFFFF0) + 16) - cur_offs;
888 for (jit_uint32_t i=0; i<offset; i++)
890 jit->write_ubyte(IA32_INT3);