3 * ===============================================================
4 * SourceMod, Copyright (C) 2004-2007 AlliedModders LLC.
6 * ===============================================================
8 * This file is part of the SourceMod/SourcePawn SDK. This file may only be
9 * used or modified under the Terms and Conditions of its License Agreement,
10 * which is found in public/licenses/LICENSE.txt. As of this notice, derivative
11 * works must be licensed under the GNU General Public License (version 2 or
12 * greater). A copy of the GPL is included under public/licenses/GPL.txt.
14 * To view the latest information, see: http://www.sourcemod.net/license.php
19 #include <sm_platform.h>
20 #include "extension.h"
21 #include <jit_helpers.h>
22 #include <x86_macros.h>
25 jit_uint32_t g_StackUsage = 0;
26 jit_uint32_t g_RegDecoder = 0;
32 inline jit_uint8_t _DecodeRegister3(jit_uint32_t val)
50 /* Should never happen */
58 inline void Write_Execution_Prologue(JitWriter *jit, bool is_void, bool has_params)
68 IA32_Push_Reg(jit, REG_EBP);
69 IA32_Mov_Reg_Rm(jit, REG_EBP, REG_ESP, MOD_REG);
72 IA32_Push_Reg(jit, REG_EDI);
73 IA32_Mov_Reg_Rm_Disp8(jit, REG_EDI, REG_EBP, 12);
77 IA32_Push_Reg(jit, REG_EBX);
78 IA32_Mov_Reg_Rm_Disp8(jit, REG_EBX, REG_EBP, 8);
82 inline void Write_Function_Epilogue(JitWriter *jit, bool is_void, bool has_params)
93 IA32_Pop_Reg(jit, REG_EBX);
97 IA32_Pop_Reg(jit, REG_EDI);
99 IA32_Mov_Reg_Rm(jit, REG_ESP, REG_EBP, MOD_REG);
100 IA32_Pop_Reg(jit, REG_EBP);
104 inline void Write_PushPOD(JitWriter *jit, const PassEncode *pEnc)
106 jit_uint8_t reg = _DecodeRegister3(g_RegDecoder++);
108 if (pEnc->info.flags & PASSFLAG_BYVAL)
110 switch (pEnc->info.size)
115 //mov reg, BYTE PTR [ebx+<offset>]
117 IA32_Xor_Reg_Rm(jit, reg, reg, MOD_REG);
118 if (pEnc->offset < SCHAR_MAX)
120 IA32_Mov_Reg8_Rm8_Disp8(jit, reg, REG_EBX, (jit_int8_t)pEnc->offset);
121 } else if (!pEnc->offset) {
122 IA32_Mov_Reg8_Rm8(jit, reg, REG_EBX, MOD_MEM_REG);
124 IA32_Mov_Reg8_Rm8_Disp32(jit, reg, REG_EBX, pEnc->offset);
126 IA32_Push_Reg(jit, reg);
134 //mov reg, WORD PTR [ebx+<offset>]
136 IA32_Xor_Reg_Rm(jit, reg, reg, MOD_REG);
137 jit->write_ubyte(IA32_16BIT_PREFIX);
138 if (pEnc->offset < SCHAR_MAX)
140 IA32_Mov_Reg_Rm_Disp8(jit, reg, REG_EBX, (jit_int8_t)pEnc->offset);
141 } else if (!pEnc->offset) {
142 IA32_Mov_Reg_Rm(jit, reg, REG_EBX, MOD_MEM_REG);
144 IA32_Mov_Reg_Rm_Disp32(jit, reg, REG_EBX, pEnc->offset);
146 IA32_Push_Reg(jit, reg);
153 //mov reg, DWORD PTR [ebx+<offset>]
155 if (pEnc->offset < SCHAR_MAX)
157 IA32_Mov_Reg_Rm_Disp8(jit, reg, REG_EBX, (jit_int8_t)pEnc->offset);
158 } else if (!pEnc->offset) {
159 IA32_Mov_Reg_Rm(jit, reg, REG_EBX, MOD_MEM_REG);
161 IA32_Mov_Reg_Rm_Disp32(jit, reg, REG_EBX, pEnc->offset);
163 IA32_Push_Reg(jit, reg);
170 //mov reg, DWORD PTR [ebx+<offset>+4]
171 //mov reg2, DWORD PTR [ebx+<offset>]
174 jit_uint8_t reg2 = _DecodeRegister3(g_RegDecoder++);
176 if (pEnc->offset+4 < SCHAR_MAX)
178 IA32_Mov_Reg_Rm_Disp8(jit, reg, REG_EBX, (jit_int8_t)(pEnc->offset+4));
180 IA32_Mov_Reg_Rm_Disp32(jit, reg, REG_EBX, pEnc->offset+4);
182 if (pEnc->offset < SCHAR_MAX)
184 IA32_Mov_Reg_Rm_Disp8(jit, reg2, REG_EBX, (jit_int8_t)pEnc->offset);
185 } else if (!pEnc->offset) {
186 IA32_Mov_Reg_Rm(jit, reg, REG_EBX, MOD_MEM_REG);
188 IA32_Mov_Reg_Rm_Disp32(jit, reg2, REG_EBX, pEnc->offset);
190 IA32_Push_Reg(jit, reg);
191 IA32_Push_Reg(jit, reg2);
197 } else if (pEnc->info.flags & PASSFLAG_BYREF) {
198 //lea reg, [ebx+<offset>]
202 IA32_Push_Reg(jit, REG_EBX);
206 if (pEnc->offset < SCHAR_MAX)
208 IA32_Lea_DispRegImm8(jit, reg, REG_EBX, (jit_int8_t)pEnc->offset);
210 IA32_Lea_DispRegImm32(jit, reg, REG_EBX, pEnc->offset);
212 IA32_Push_Reg(jit, reg);
218 inline void Write_PushFloat(JitWriter *jit, const PassEncode *pEnc)
220 if (pEnc->info.flags & PASSFLAG_BYVAL)
222 switch (pEnc->info.size)
226 //fld DWORD PTR [ebx+<offset>]
228 //fstp DWORD PTR [esp]
229 if (pEnc->offset < SCHAR_MAX)
231 IA32_Fld_Mem32_Disp8(jit, REG_EBX, (jit_int8_t)pEnc->offset);
233 IA32_Fld_Mem32_Disp32(jit, REG_EBX, pEnc->offset);
235 IA32_Push_Reg(jit, _DecodeRegister3(g_RegDecoder++));
236 IA32_Fstp_Mem32(jit, REG_ESP, REG_NOIDX, NOSCALE);
242 //fld QWORD PTR [ebx+<offset>]
244 //fstp QWORD PTR [esp]
245 if (pEnc->offset < SCHAR_MAX)
247 IA32_Fld_Mem64_Disp8(jit, REG_EBX, (jit_int8_t)pEnc->offset);
249 IA32_Fld_Mem64_Disp32(jit, REG_EBX, pEnc->offset);
251 IA32_Sub_Rm_Imm8(jit, REG_ESP, 8, MOD_REG);
252 IA32_Fstp_Mem64(jit, REG_ESP, REG_NOIDX, NOSCALE);
257 } else if (pEnc->info.flags & PASSFLAG_BYREF) {
258 //lea reg, [ebx+<offset>]
262 IA32_Push_Reg(jit, REG_EBX);
267 jit_uint8_t reg = _DecodeRegister3(g_RegDecoder++);
268 if (pEnc->offset < SCHAR_MAX)
270 IA32_Lea_DispRegImm8(jit, reg, REG_EBX, (jit_int8_t)pEnc->offset);
272 IA32_Lea_DispRegImm32(jit, reg, REG_EBX, pEnc->offset);
274 IA32_Push_Reg(jit, reg);
280 inline void Write_PushObject(JitWriter *jit, const PassEncode *pEnc)
282 if (pEnc->info.flags & PASSFLAG_BYVAL)
284 #ifdef PLATFORM_POSIX
285 if (pEnc->info.flags & PASSFLAG_ODTOR)
290 jit_uint32_t dwords = pEnc->info.size >> 2;
291 jit_uint32_t bytes = pEnc->info.size & 0x3;
298 //lea esi, [ebx+<offs>]
307 if (pEnc->info.size < SCHAR_MAX)
309 IA32_Sub_Rm_Imm8(jit, REG_ESP, (jit_int8_t)pEnc->info.size, MOD_REG);
311 IA32_Sub_Rm_Imm32(jit, REG_ESP, pEnc->info.size, MOD_REG);
314 IA32_Push_Reg(jit, REG_EDI);
315 IA32_Push_Reg(jit, REG_ESI);
316 IA32_Lea_Reg_DispRegMultImm8(jit, REG_EDI, REG_NOIDX, REG_ESP, NOSCALE, 8);
317 if (pEnc->offset < SCHAR_MAX)
319 IA32_Lea_DispRegImm8(jit, REG_ESI, REG_EBX, (jit_int8_t)pEnc->offset);
320 } else if (!pEnc->offset) {
321 IA32_Mov_Reg_Rm(jit, REG_ESI, REG_EBX, MOD_REG);
323 IA32_Lea_DispRegImm32(jit, REG_ESI, REG_EBX, pEnc->offset);
327 IA32_Mov_Reg_Imm32(jit, REG_ECX, dwords);
333 IA32_Mov_Reg_Imm32(jit, REG_ECX, bytes);
337 IA32_Pop_Reg(jit, REG_ESI);
338 IA32_Pop_Reg(jit, REG_EDI);
340 g_StackUsage += pEnc->info.size;
341 } else if (pEnc->info.flags & PASSFLAG_BYREF) {
342 #ifdef PLATFORM_POSIX
347 IA32_Push_Reg(jit, REG_EBX);
352 //lea reg, [ebx+<offset>]
354 jit_uint8_t reg = _DecodeRegister3(g_RegDecoder++);
355 if (pEnc->offset < SCHAR_MAX)
357 IA32_Lea_DispRegImm8(jit, reg, REG_EBX, (jit_int8_t)pEnc->offset);
359 IA32_Lea_DispRegImm32(jit, reg, REG_EBX, pEnc->offset);
361 IA32_Push_Reg(jit, reg);
367 inline void Write_PushThisPtr(JitWriter *jit)
369 #ifdef PLATFORM_POSIX
372 jit_uint8_t reg = _DecodeRegister3(g_RegDecoder++);
374 IA32_Mov_Reg_Rm(jit, reg, REG_EBX, MOD_MEM_REG);
375 IA32_Push_Reg(jit, reg);
378 #elif defined PLATFORM_WINDOWS
380 IA32_Mov_Reg_Rm(jit, REG_ECX, REG_EBX, MOD_MEM_REG);
384 inline void Write_PushRetBuffer(JitWriter *jit)
387 IA32_Push_Reg(jit, REG_EDI);
390 inline void Write_CallFunction(JitWriter *jit, FuncAddrMethod method, CallWrapper *pWrapper)
392 if (method == FuncAddr_Direct)
395 jitoffs_t call = IA32_Call_Imm32(jit, 0);
396 IA32_Write_Jump32_Abs(jit, call, pWrapper->m_Addrs[ADDR_CALLEE]);
397 } else if (method == FuncAddr_VTable) {
398 //*(this + thisOffs + vtblOffs)[vtblIdx]
400 //mov eax, [edx+<thisOffs>+<vtblOffs>]
401 //mov edx, [eax+<vtblIdx>*4]
403 jit_uint32_t total_offs = pWrapper->m_VtInfo.thisOffs + pWrapper->m_VtInfo.vtblOffs;
404 jit_uint32_t vfunc_pos = pWrapper->m_VtInfo.vtblIdx * 4;
406 IA32_Mov_Reg_Rm(jit, REG_EDX, REG_EBX, MOD_MEM_REG);
407 if (total_offs < SCHAR_MAX)
409 IA32_Mov_Reg_Rm_Disp8(jit, REG_EAX, REG_EDX, (jit_int8_t)total_offs);
410 } else if (!total_offs) {
411 IA32_Mov_Reg_Rm(jit, REG_EAX, REG_EDX, MOD_MEM_REG);
413 IA32_Mov_Reg_Rm_Disp32(jit, REG_EAX, REG_EDX, total_offs);
415 if (vfunc_pos < SCHAR_MAX)
417 IA32_Mov_Reg_Rm_Disp8(jit, REG_EDX, REG_EAX, (jit_int8_t)vfunc_pos);
418 } else if (!vfunc_pos) {
419 IA32_Mov_Reg_Rm(jit, REG_EDX, REG_EAX, MOD_MEM_REG);
421 IA32_Mov_Reg_Rm_Disp32(jit, REG_EDX, REG_EAX, vfunc_pos);
423 IA32_Call_Reg(jit, REG_EDX);
427 inline void Write_RectifyStack(JitWriter *jit, jit_uint32_t value)
430 if (value < SCHAR_MAX)
432 IA32_Add_Rm_Imm8(jit, REG_ESP, (jit_int8_t)value, MOD_REG);
434 IA32_Add_Rm_Imm32(jit, REG_ESP, value, MOD_REG);
438 inline void Write_MovRet2Buf(JitWriter *jit, const PassInfo *pRet)
440 if (pRet->type == PassType_Float)
446 //fstp DWORD PTR [edi]
447 IA32_Fstp_Mem32(jit, REG_EDI, REG_NOIDX, NOSCALE);
452 //fstp QWORD PTR [edi]
453 IA32_Fstp_Mem64(jit, REG_EDI, REG_NOIDX, NOSCALE);
464 //mov BYTE PTR [edi], al
465 IA32_Mov_Rm8_Reg8(jit, REG_EDI, REG_EAX, MOD_MEM_REG);
470 //mov WORD PTR [edi], ax
471 jit->write_ubyte(IA32_16BIT_PREFIX);
472 IA32_Mov_Rm_Reg(jit, REG_EDI, REG_EAX, MOD_MEM_REG);
477 //mov DWORD PTR [edi], eax
478 IA32_Mov_Rm_Reg(jit, REG_EDI, REG_EAX, MOD_MEM_REG);
483 //mov DWORD PTR [edi], eax
484 //mov DWORD PTR [edi+4], edx
485 IA32_Mov_Rm_Reg(jit, REG_EDI, REG_EAX, MOD_MEM_REG);
486 IA32_Mov_Rm_Reg_Disp8(jit, REG_EDI, REG_EDX, 4);
492 /******************************
493 * Assembly Compiler Function *
494 ******************************/
496 void JIT_Compile(CallWrapper *pWrapper, FuncAddrMethod method)
499 JitWriter *jit = &writer;
501 jit_uint32_t CodeSize = 0;
502 bool Needs_Retbuf = false;
503 CallConvention Convention = pWrapper->GetCallConvention();
504 jit_uint32_t ParamCount = pWrapper->GetParamCount();
505 const PassInfo *pRet = pWrapper->GetReturnInfo();
507 writer.outbase = NULL;
508 writer.outptr = NULL;
511 /* Write function prologue */
512 Write_Execution_Prologue(jit, (pRet) ? false : true, (ParamCount) ? true : false);
514 /* Write parameter push code */
515 for (jit_int32_t i=ParamCount-1; i>=0; i--)
517 const PassEncode *pEnc = pWrapper->GetParamInfo(i);
518 switch (pEnc->info.type)
522 Write_PushPOD(jit, pEnc);
527 Write_PushFloat(jit, pEnc);
530 case PassType_Object:
532 Write_PushObject(jit, pEnc);
538 /* Prepare the this ptr if applicable */
539 if (Convention == CallConv_ThisCall)
541 Write_PushThisPtr(jit);
544 /* Skip the return buffer stuff if this is a void function */
550 if ((pRet->type == PassType_Object) && (pRet->flags & PASSFLAG_BYVAL))
552 #ifdef PLATFORM_POSIX
554 #elif defined PLATFORM_WINDOWS
555 if ((Convention == CallConv_ThisCall) ||
556 ((Convention == CallConv_Cdecl) &&
557 ((pRet->size > 8) || (pRet->flags & PASSFLAG_ODTOR|PASSFLAG_OCTOR|PASSFLAG_OASSIGNOP))))
564 /* Prepare the return buffer in case we are returning objects by value. */
567 Write_PushRetBuffer(jit);
571 /* Write the calling code */
572 Write_CallFunction(jit, method, pWrapper);
574 /* Clean up the calling stack */
575 #ifdef PLATFORM_WINDOWS
576 if ((ParamCount || Needs_Retbuf) && (Convention == CallConv_Cdecl))
578 /* Pop all parameters from the stack + hidden return pointer */
579 jit_uint32_t total = (Needs_Retbuf) ? g_StackUsage + sizeof(void *) : g_StackUsage;
580 Write_RectifyStack(jit, total);
581 #elif defined PLATFORM_POSIX
584 /* Pop all parameters from the stack */
585 Write_RectifyStack(jit, g_StackUsage);
589 /* Copy the return type to the return buffer if the function is not void */
590 if (pRet && !Needs_Retbuf)
592 Write_MovRet2Buf(jit, pRet);
595 /* Write Function Epilogue */
596 Write_Function_Epilogue(jit, (pRet) ? false : true, (ParamCount) ? true : false);
598 if (writer.outbase == NULL)
600 CodeSize = writer.get_outputpos();
601 writer.outbase = (jitcode_t)g_SPEngine->ExecAlloc(CodeSize);
602 writer.outptr = writer.outbase;
603 pWrapper->m_Addrs[ADDR_CODEBASE] = writer.outbase;
606 Needs_Retbuf = false;