fixed a compat regression, forgot msvc does funky things with overloaded virtual calls
3 * =============================================================================
4 * SourceMod BinTools Extension
5 * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
6 * =============================================================================
8 * This program is free software; you can redistribute it and/or modify it under
9 * the terms of the GNU General Public License, version 3.0, as published by the
10 * Free Software Foundation.
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
17 * You should have received a copy of the GNU General Public License along with
18 * this program. If not, see <http://www.gnu.org/licenses/>.
20 * As a special exception, AlliedModders LLC gives you permission to link the
21 * code of this program (as well as its derivative works) to "Half-Life 2," the
22 * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
23 * by the Valve Corporation. You must obey the GNU General Public License in
24 * all respects for all other code used. Additionally, AlliedModders LLC grants
25 * this exception to all derivative works. AlliedModders LLC defines further
26 * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
27 * or <http://www.sourcemod.net/license.php>.
32 #include <sm_platform.h>
33 #include "extension.h"
34 #include <jit_helpers.h>
35 #include <x86_macros.h>
38 jit_uint32_t g_StackUsage = 0;
39 jit_uint32_t g_StackAlign = 0;
40 jit_uint32_t g_RegDecoder = 0;
46 inline jit_uint8_t _DecodeRegister3(jit_uint32_t val)
64 /* Should never happen */
72 inline void Write_Execution_Prologue(JitWriter *jit, bool is_void, bool has_params)
85 //sub esp, <alignment>
86 IA32_Push_Reg(jit, REG_EBP);
87 IA32_Mov_Reg_Rm(jit, REG_EBP, REG_ESP, MOD_REG);
90 IA32_Push_Reg(jit, REG_EDI);
91 IA32_Mov_Reg_Rm_Disp8(jit, REG_EDI, REG_EBP, 12);
95 IA32_Push_Reg(jit, REG_EBX);
96 IA32_Mov_Reg_Rm_Disp8(jit, REG_EBX, REG_EBP, 8);
98 IA32_Push_Reg(jit, REG_ESI);
99 IA32_Mov_Reg_Rm(jit, REG_ESI, REG_ESP, MOD_REG);
100 IA32_And_Rm_Imm8(jit, REG_ESP, MOD_REG, -16);
104 /* Alloc this instruction before knowing the real stack usage */
105 IA32_Sub_Rm_Imm32(jit, REG_ESP, 1337, MOD_REG);
109 IA32_Sub_Rm_Imm32(jit, REG_ESP, g_StackAlign, MOD_REG);
114 inline void Write_Function_Epilogue(JitWriter *jit, bool is_void, bool has_params)
125 IA32_Mov_Reg_Rm(jit, REG_ESP, REG_ESI, MOD_REG);
126 IA32_Pop_Reg(jit, REG_ESI);
129 IA32_Pop_Reg(jit, REG_EBX);
133 IA32_Pop_Reg(jit, REG_EDI);
135 IA32_Mov_Reg_Rm(jit, REG_ESP, REG_EBP, MOD_REG);
136 IA32_Pop_Reg(jit, REG_EBP);
140 inline void Write_PushPOD(JitWriter *jit, const PassEncode *pEnc)
142 jit_uint8_t reg = _DecodeRegister3(g_RegDecoder++);
144 if (pEnc->info.flags & PASSFLAG_BYVAL)
146 switch (pEnc->info.size)
150 //movzx reg, BYTE PTR [ebx+<offset>]
152 if (pEnc->offset < SCHAR_MAX)
154 IA32_Movzx_Reg32_Rm8_Disp8(jit, reg, REG_EBX, (jit_int8_t)pEnc->offset);
155 } else if (!pEnc->offset) {
156 IA32_Movzx_Reg32_Rm8(jit, reg, REG_EBX, MOD_MEM_REG);
158 IA32_Movzx_Reg32_Rm8_Disp32(jit, reg, REG_EBX, pEnc->offset);
160 IA32_Push_Reg(jit, reg);
167 //movzx reg, WORD PTR [ebx+<offset>]
169 jit->write_ubyte(IA32_16BIT_PREFIX);
170 if (pEnc->offset < SCHAR_MAX)
172 IA32_Movzx_Reg32_Rm16_Disp8(jit, reg, REG_EBX, (jit_int8_t)pEnc->offset);
173 } else if (!pEnc->offset) {
174 IA32_Movzx_Reg32_Rm16(jit, reg, REG_EBX, MOD_MEM_REG);
176 IA32_Movzx_Reg32_Rm16_Disp32(jit, reg, REG_EBX, pEnc->offset);
178 IA32_Push_Reg(jit, reg);
185 //mov reg, DWORD PTR [ebx+<offset>]
187 if (pEnc->offset < SCHAR_MAX)
189 IA32_Mov_Reg_Rm_Disp8(jit, reg, REG_EBX, (jit_int8_t)pEnc->offset);
190 } else if (!pEnc->offset) {
191 IA32_Mov_Reg_Rm(jit, reg, REG_EBX, MOD_MEM_REG);
193 IA32_Mov_Reg_Rm_Disp32(jit, reg, REG_EBX, pEnc->offset);
195 IA32_Push_Reg(jit, reg);
202 //mov reg, DWORD PTR [ebx+<offset>+4]
203 //mov reg2, DWORD PTR [ebx+<offset>]
206 jit_uint8_t reg2 = _DecodeRegister3(g_RegDecoder++);
208 if (pEnc->offset+4 < SCHAR_MAX)
210 IA32_Mov_Reg_Rm_Disp8(jit, reg, REG_EBX, (jit_int8_t)(pEnc->offset+4));
212 IA32_Mov_Reg_Rm_Disp32(jit, reg, REG_EBX, pEnc->offset+4);
214 if (pEnc->offset < SCHAR_MAX)
216 IA32_Mov_Reg_Rm_Disp8(jit, reg2, REG_EBX, (jit_int8_t)pEnc->offset);
217 } else if (!pEnc->offset) {
218 IA32_Mov_Reg_Rm(jit, reg2, REG_EBX, MOD_MEM_REG);
220 IA32_Mov_Reg_Rm_Disp32(jit, reg2, REG_EBX, pEnc->offset);
222 IA32_Push_Reg(jit, reg);
223 IA32_Push_Reg(jit, reg2);
229 } else if (pEnc->info.flags & PASSFLAG_BYREF) {
230 //lea reg, [ebx+<offset>]
234 IA32_Push_Reg(jit, REG_EBX);
238 if (pEnc->offset < SCHAR_MAX)
240 IA32_Lea_DispRegImm8(jit, reg, REG_EBX, (jit_int8_t)pEnc->offset);
242 IA32_Lea_DispRegImm32(jit, reg, REG_EBX, pEnc->offset);
244 IA32_Push_Reg(jit, reg);
250 inline void Write_PushFloat(JitWriter *jit, const PassEncode *pEnc)
252 if (pEnc->info.flags & PASSFLAG_BYVAL)
254 switch (pEnc->info.size)
258 //fld DWORD PTR [ebx+<offset>]
260 //fstp DWORD PTR [esp]
261 if (pEnc->offset < SCHAR_MAX)
263 IA32_Fld_Mem32_Disp8(jit, REG_EBX, (jit_int8_t)pEnc->offset);
264 } else if (!pEnc->offset) {
265 IA32_Fld_Mem32(jit, REG_EBX);
267 IA32_Fld_Mem32_Disp32(jit, REG_EBX, pEnc->offset);
269 IA32_Push_Reg(jit, _DecodeRegister3(g_RegDecoder++));
270 IA32_Fstp_Mem32_ESP(jit);
276 //fld QWORD PTR [ebx+<offset>]
278 //fstp QWORD PTR [esp]
279 if (pEnc->offset < SCHAR_MAX)
281 IA32_Fld_Mem64_Disp8(jit, REG_EBX, (jit_int8_t)pEnc->offset);
282 } else if (!pEnc->offset) {
283 IA32_Fld_Mem64(jit, REG_EBX);
285 IA32_Fld_Mem64_Disp32(jit, REG_EBX, pEnc->offset);
287 IA32_Sub_Rm_Imm8(jit, REG_ESP, 8, MOD_REG);
288 IA32_Fstp_Mem64_ESP(jit);
293 } else if (pEnc->info.flags & PASSFLAG_BYREF) {
294 //lea reg, [ebx+<offset>]
298 IA32_Push_Reg(jit, REG_EBX);
303 jit_uint8_t reg = _DecodeRegister3(g_RegDecoder++);
304 if (pEnc->offset < SCHAR_MAX)
306 IA32_Lea_DispRegImm8(jit, reg, REG_EBX, (jit_int8_t)pEnc->offset);
308 IA32_Lea_DispRegImm32(jit, reg, REG_EBX, pEnc->offset);
310 IA32_Push_Reg(jit, reg);
316 inline void Write_PushObject(JitWriter *jit, const PassEncode *pEnc)
318 if (pEnc->info.flags & PASSFLAG_BYVAL)
320 #ifdef PLATFORM_POSIX
321 if (pEnc->info.flags & PASSFLAG_ODTOR)
326 jit_uint32_t dwords = pEnc->info.size >> 2;
327 jit_uint32_t bytes = pEnc->info.size & 0x3;
334 //lea esi, [ebx+<offs>]
343 if (pEnc->info.size < SCHAR_MAX)
345 IA32_Sub_Rm_Imm8(jit, REG_ESP, (jit_int8_t)pEnc->info.size, MOD_REG);
347 IA32_Sub_Rm_Imm32(jit, REG_ESP, pEnc->info.size, MOD_REG);
350 IA32_Push_Reg(jit, REG_EDI);
351 IA32_Push_Reg(jit, REG_ESI);
352 IA32_Lea_Reg_DispRegMultImm8(jit, REG_EDI, REG_NOIDX, REG_ESP, NOSCALE, 8);
353 if (pEnc->offset < SCHAR_MAX)
355 IA32_Lea_DispRegImm8(jit, REG_ESI, REG_EBX, (jit_int8_t)pEnc->offset);
356 } else if (!pEnc->offset) {
357 IA32_Mov_Reg_Rm(jit, REG_ESI, REG_EBX, MOD_REG);
359 IA32_Lea_DispRegImm32(jit, REG_ESI, REG_EBX, pEnc->offset);
363 IA32_Mov_Reg_Imm32(jit, REG_ECX, dwords);
369 IA32_Mov_Reg_Imm32(jit, REG_ECX, bytes);
373 IA32_Pop_Reg(jit, REG_ESI);
374 IA32_Pop_Reg(jit, REG_EDI);
376 g_StackUsage += pEnc->info.size;
377 } else if (pEnc->info.flags & PASSFLAG_BYREF) {
378 #ifdef PLATFORM_POSIX
383 IA32_Push_Reg(jit, REG_EBX);
388 //lea reg, [ebx+<offset>]
390 jit_uint8_t reg = _DecodeRegister3(g_RegDecoder++);
391 if (pEnc->offset < SCHAR_MAX)
393 IA32_Lea_DispRegImm8(jit, reg, REG_EBX, (jit_int8_t)pEnc->offset);
395 IA32_Lea_DispRegImm32(jit, reg, REG_EBX, pEnc->offset);
397 IA32_Push_Reg(jit, reg);
403 inline void Write_PushThisPtr(JitWriter *jit)
405 #ifdef PLATFORM_POSIX
408 jit_uint8_t reg = _DecodeRegister3(g_RegDecoder++);
410 IA32_Mov_Reg_Rm(jit, reg, REG_EBX, MOD_MEM_REG);
411 IA32_Push_Reg(jit, reg);
414 #elif defined PLATFORM_WINDOWS
416 IA32_Mov_Reg_Rm(jit, REG_ECX, REG_EBX, MOD_MEM_REG);
420 inline void Write_PushRetBuffer(JitWriter *jit)
423 IA32_Push_Reg(jit, REG_EDI);
426 inline void Write_CallFunction(JitWriter *jit, FuncAddrMethod method, CallWrapper *pWrapper)
428 if (method == FuncAddr_Direct)
431 jitoffs_t call = IA32_Call_Imm32(jit, 0);
432 IA32_Write_Jump32_Abs(jit, call, pWrapper->m_Addrs[ADDR_CALLEE]);
433 } else if (method == FuncAddr_VTable) {
434 //*(this + thisOffs + vtblOffs)[vtblIdx]
436 //mov eax, [edx+<thisOffs>+<vtblOffs>]
437 //mov edx, [eax+<vtblIdx>*4]
439 jit_uint32_t total_offs = pWrapper->m_VtInfo.thisOffs + pWrapper->m_VtInfo.vtblOffs;
440 jit_uint32_t vfunc_pos = pWrapper->m_VtInfo.vtblIdx * 4;
442 IA32_Mov_Reg_Rm(jit, REG_EDX, REG_EBX, MOD_MEM_REG);
443 if (total_offs < SCHAR_MAX)
445 IA32_Mov_Reg_Rm_Disp8(jit, REG_EAX, REG_EDX, (jit_int8_t)total_offs);
446 } else if (!total_offs) {
447 IA32_Mov_Reg_Rm(jit, REG_EAX, REG_EDX, MOD_MEM_REG);
449 IA32_Mov_Reg_Rm_Disp32(jit, REG_EAX, REG_EDX, total_offs);
451 if (vfunc_pos < SCHAR_MAX)
453 IA32_Mov_Reg_Rm_Disp8(jit, REG_EDX, REG_EAX, (jit_int8_t)vfunc_pos);
454 } else if (!vfunc_pos) {
455 IA32_Mov_Reg_Rm(jit, REG_EDX, REG_EAX, MOD_MEM_REG);
457 IA32_Mov_Reg_Rm_Disp32(jit, REG_EDX, REG_EAX, vfunc_pos);
459 IA32_Call_Reg(jit, REG_EDX);
463 inline void Write_RectifyStack(JitWriter *jit, jit_uint32_t value)
466 if (value < SCHAR_MAX)
468 IA32_Add_Rm_Imm8(jit, REG_ESP, (jit_int8_t)value, MOD_REG);
470 IA32_Add_Rm_Imm32(jit, REG_ESP, value, MOD_REG);
474 inline void Write_MovRet2Buf(JitWriter *jit, const PassInfo *pRet)
476 if (pRet->type == PassType_Float)
482 //fstp DWORD PTR [edi]
483 IA32_Fstp_Mem32(jit, REG_EDI);
488 //fstp QWORD PTR [edi]
489 IA32_Fstp_Mem64(jit, REG_EDI);
500 //mov BYTE PTR [edi], al
501 IA32_Mov_Rm8_Reg8(jit, REG_EDI, REG_EAX, MOD_MEM_REG);
506 //mov WORD PTR [edi], ax
507 jit->write_ubyte(IA32_16BIT_PREFIX);
508 IA32_Mov_Rm_Reg(jit, REG_EDI, REG_EAX, MOD_MEM_REG);
513 //mov DWORD PTR [edi], eax
514 IA32_Mov_Rm_Reg(jit, REG_EDI, REG_EAX, MOD_MEM_REG);
519 //mov DWORD PTR [edi], eax
520 //mov DWORD PTR [edi+4], edx
521 IA32_Mov_Rm_Reg(jit, REG_EDI, REG_EAX, MOD_MEM_REG);
522 IA32_Mov_Rm_Reg_Disp8(jit, REG_EDI, REG_EDX, 4);
528 /******************************
529 * Assembly Compiler Function *
530 ******************************/
532 void JIT_Compile(CallWrapper *pWrapper, FuncAddrMethod method)
535 JitWriter *jit = &writer;
537 jit_uint32_t CodeSize = 0;
538 bool Needs_Retbuf = false;
539 CallConvention Convention = pWrapper->GetCallConvention();
540 jit_uint32_t ParamCount = pWrapper->GetParamCount();
541 const PassInfo *pRet = pWrapper->GetReturnInfo();
542 bool hasParams = (ParamCount || Convention == CallConv_ThisCall);
546 writer.outbase = NULL;
547 writer.outptr = NULL;
550 /* Write function prologue */
551 Write_Execution_Prologue(jit, (pRet) ? false : true, hasParams);
553 /* Write parameter push code */
554 for (jit_int32_t i=ParamCount-1; i>=0; i--)
556 const PassEncode *pEnc = pWrapper->GetParamInfo(i);
557 switch (pEnc->info.type)
561 Write_PushPOD(jit, pEnc);
566 Write_PushFloat(jit, pEnc);
569 case PassType_Object:
571 Write_PushObject(jit, pEnc);
577 /* Prepare the this ptr if applicable */
578 if (Convention == CallConv_ThisCall)
580 Write_PushThisPtr(jit);
583 /* Skip the return buffer stuff if this is a void function */
589 if ((pRet->type == PassType_Object) && (pRet->flags & PASSFLAG_BYVAL))
591 #ifdef PLATFORM_POSIX
593 #elif defined PLATFORM_WINDOWS
594 if ((Convention == CallConv_ThisCall) ||
595 ((Convention == CallConv_Cdecl) &&
596 ((pRet->size > 8) || (pRet->flags & PASSFLAG_ODTOR|PASSFLAG_OCTOR|PASSFLAG_OASSIGNOP))))
603 /* Prepare the return buffer in case we are returning objects by value. */
606 Write_PushRetBuffer(jit);
610 /* Write the calling code */
611 Write_CallFunction(jit, method, pWrapper);
613 /* Clean up the calling stack */
614 #ifdef PLATFORM_WINDOWS
615 if ((ParamCount || Needs_Retbuf) && (Convention == CallConv_Cdecl))
617 /* Pop all parameters from the stack + hidden return pointer */
618 jit_uint32_t total = (Needs_Retbuf) ? g_StackUsage + sizeof(void *) : g_StackUsage;
619 Write_RectifyStack(jit, total);
620 #elif defined PLATFORM_POSIX
623 /* Pop all parameters from the stack */
624 Write_RectifyStack(jit, g_StackUsage);
628 /* Copy the return type to the return buffer if the function is not void */
629 if (pRet && !Needs_Retbuf)
631 Write_MovRet2Buf(jit, pRet);
634 /* Write Function Epilogue */
635 Write_Function_Epilogue(jit, (pRet) ? false : true, hasParams);
637 if (writer.outbase == NULL)
639 CodeSize = writer.get_outputpos();
640 writer.outbase = (jitcode_t)g_SPEngine->AllocatePageMemory(CodeSize);
641 g_SPEngine->SetReadWrite(writer.outbase);
642 writer.outptr = writer.outbase;
643 pWrapper->m_Addrs[ADDR_CODEBASE] = writer.outbase;
644 g_StackAlign = (g_StackUsage) ? ((g_StackUsage & 0xFFFFFFF0) + 16) - g_StackUsage : 0;
647 Needs_Retbuf = false;
650 g_SPEngine->SetReadExecute(writer.outbase);