123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478 |
- /*
- AngelCode Scripting Library
- Copyright (c) 2003-2017 Andreas Jonsson
- This software is provided 'as-is', without any express or implied
- warranty. In no event will the authors be held liable for any
- damages arising from the use of this software.
- Permission is granted to anyone to use this software for any
- purpose, including commercial applications, and to alter it and
- redistribute it freely, subject to the following restrictions:
- 1. The origin of this software must not be misrepresented; you
- must not claim that you wrote the original software. If you use
- this software in a product, an acknowledgment in the product
- documentation would be appreciated but is not required.
- 2. Altered source versions must be plainly marked as such, and
- must not be misrepresented as being the original software.
- 3. This notice may not be removed or altered from any source
- distribution.
- The original version of this library can be located at:
- http://www.angelcode.com/angelscript/
- Andreas Jonsson
- andreas@angelcode.com
- */
- /*
- * Implements the AMD64 calling convention for gcc-based 64bit Unices
- *
- * Author: Ionut "gargltk" Leonte <ileonte@bitdefender.com>
- *
- * Initial author: niteice
- *
- * Added support for functor methods by Jordi Oliveras Rovira in April, 2014.
- */
- // Useful references for the System V AMD64 ABI:
- // http://eli.thegreenplace.net/2011/09/06/stack-frame-layout-on-x86-64/
- // http://math-atlas.sourceforge.net/devel/assembly/abi_sysV_amd64.pdf
-
- #include "as_config.h"
- #ifndef AS_MAX_PORTABILITY
- #ifdef AS_X64_GCC
- #include "as_scriptengine.h"
- #include "as_texts.h"
- #include "as_context.h"
- BEGIN_AS_NAMESPACE
- enum argTypes { x64INTARG = 0, x64FLOATARG = 1 };
- typedef asQWORD ( *funcptr_t )( void );
- #define X64_MAX_ARGS 32
- #define MAX_CALL_INT_REGISTERS 6
- #define MAX_CALL_SSE_REGISTERS 8
- #define X64_CALLSTACK_SIZE ( X64_MAX_ARGS + MAX_CALL_SSE_REGISTERS + 3 )
- // Note to self: Always remember to inform the used registers on the clobber line,
- // so that the gcc optimizer doesn't try to use them for other things
- static asQWORD __attribute__((noinline)) X64_CallFunction(const asQWORD *args, int cnt, funcptr_t func, asQWORD &retQW2, bool returnFloat)
- {
- // Need to flag the variable as volatile so the compiler doesn't optimize out the variable
- volatile asQWORD retQW1 = 0;
- // Reference: http://www.x86-64.org/documentation/abi.pdf
- __asm__ __volatile__ (
- " movq %0, %%rcx \n" // rcx = cnt
- " movq %1, %%r10 \n" // r10 = args
- " movq %2, %%r11 \n" // r11 = func
- // Backup stack pointer in R15 that is guaranteed to maintain its value over function calls
- " movq %%rsp, %%r15 \n"
- #ifdef __OPTIMIZE__
- // Make sure the stack unwind logic knows we've backed up the stack pointer in register r15
- // This should only be done if any optimization is done. If no optimization (-O0) is used,
- // then the compiler already backups the rsp before entering the inline assembler code
- " .cfi_def_cfa_register r15 \n"
- #endif
- // Skip the first 128 bytes on the stack frame, called "red zone",
- // that might be used by the compiler to store temporary values
- " sub $128, %%rsp \n"
- // Make sure the stack pointer will be aligned to 16 bytes when the function is called
- " movq %%rcx, %%rdx \n"
- " salq $3, %%rdx \n"
- " movq %%rsp, %%rax \n"
- " sub %%rdx, %%rax \n"
- " and $15, %%rax \n"
- " sub %%rax, %%rsp \n"
- // Push the stack parameters, i.e. the arguments that won't be loaded into registers
- " movq %%rcx, %%rsi \n"
- " testl %%esi, %%esi \n"
- " jle endstack \n"
- " subl $1, %%esi \n"
- " xorl %%edx, %%edx \n"
- " leaq 8(, %%rsi, 8), %%rcx \n"
- "loopstack: \n"
- " movq 112(%%r10, %%rdx), %%rax \n"
- " pushq %%rax \n"
- " addq $8, %%rdx \n"
- " cmpq %%rcx, %%rdx \n"
- " jne loopstack \n"
- "endstack: \n"
- // Populate integer and floating point parameters
- " movq %%r10, %%rax \n"
- " mov (%%rax), %%rdi \n"
- " mov 8(%%rax), %%rsi \n"
- " mov 16(%%rax), %%rdx \n"
- " mov 24(%%rax), %%rcx \n"
- " mov 32(%%rax), %%r8 \n"
- " mov 40(%%rax), %%r9 \n"
- " add $48, %%rax \n"
- " movsd (%%rax), %%xmm0 \n"
- " movsd 8(%%rax), %%xmm1 \n"
- " movsd 16(%%rax), %%xmm2 \n"
- " movsd 24(%%rax), %%xmm3 \n"
- " movsd 32(%%rax), %%xmm4 \n"
- " movsd 40(%%rax), %%xmm5 \n"
- " movsd 48(%%rax), %%xmm6 \n"
- " movsd 56(%%rax), %%xmm7 \n"
- // Call the function
- " call *%%r11 \n"
- // Restore stack pointer
- " mov %%r15, %%rsp \n"
- #ifdef __OPTIMIZE__
- // Inform the stack unwind logic that the stack pointer has been restored
- // This should only be done if any optimization is done. If no optimization (-O0) is used,
- // then the compiler already backups the rsp before entering the inline assembler code
- " .cfi_def_cfa_register rsp \n"
- #endif
- // Put return value in retQW1 and retQW2, using either RAX:RDX or XMM0:XMM1 depending on type of return value
- " movl %5, %%ecx \n"
- " testb %%cl, %%cl \n"
- " je intret \n"
- " lea %3, %%rax \n"
- " movq %%xmm0, (%%rax) \n"
- " lea %4, %%rdx \n"
- " movq %%xmm1, (%%rdx) \n"
- " jmp endcall \n"
- "intret: \n"
- " movq %%rax, %3 \n"
- " movq %%rdx, %4 \n"
- "endcall: \n"
- : : "g" ((asQWORD)cnt), "g" (args), "g" (func), "m" (retQW1), "m" (retQW2), "m" (returnFloat)
- : "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7",
- "%rdi", "%rsi", "%rax", "%rdx", "%rcx", "%r8", "%r9", "%r10", "%r11", "%r15");
-
- return retQW1;
- }
- // returns true if the given parameter is a 'variable argument'
- static inline bool IsVariableArgument( asCDataType type )
- {
- return ( type.GetTokenType() == ttQuestion ) ? true : false;
- }
- asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, void *obj, asDWORD *args, void *retPointer, asQWORD &retQW2, void *secondObject)
- {
- asCScriptEngine *engine = context->m_engine;
- asSSystemFunctionInterface *sysFunc = descr->sysFuncIntf;
- int callConv = sysFunc->callConv;
- asQWORD retQW = 0;
- asDWORD *stack_pointer = args;
- funcptr_t *vftable = NULL;
- int totalArgumentCount = 0;
- int n = 0;
- int param_post = 0;
- int argIndex = 0;
- funcptr_t func = (funcptr_t)sysFunc->func;
- if( sysFunc->hostReturnInMemory )
- {
- // The return is made in memory
- callConv++;
- }
- #ifdef AS_NO_THISCALL_FUNCTOR_METHOD
- // Determine the real function pointer in case of virtual method
- if ( obj && ( callConv == ICC_VIRTUAL_THISCALL || callConv == ICC_VIRTUAL_THISCALL_RETURNINMEM ) )
- #else
- if ( obj && ( callConv == ICC_VIRTUAL_THISCALL ||
- callConv == ICC_VIRTUAL_THISCALL_RETURNINMEM ||
- callConv == ICC_VIRTUAL_THISCALL_OBJFIRST ||
- callConv == ICC_VIRTUAL_THISCALL_OBJFIRST_RETURNINMEM ||
- callConv == ICC_VIRTUAL_THISCALL_OBJLAST ||
- callConv == ICC_VIRTUAL_THISCALL_OBJLAST_RETURNINMEM) )
- #endif
- {
- vftable = *((funcptr_t**)obj);
- func = vftable[FuncPtrToUInt(asFUNCTION_t(func)) >> 3];
- }
- // Determine the type of the arguments, and prepare the input array for the X64_CallFunction
- asQWORD paramBuffer[X64_CALLSTACK_SIZE] = { 0 };
- asBYTE argsType[X64_CALLSTACK_SIZE] = { 0 };
- switch ( callConv )
- {
- case ICC_CDECL_RETURNINMEM:
- case ICC_STDCALL_RETURNINMEM:
- {
- paramBuffer[0] = (asPWORD)retPointer;
- argsType[0] = x64INTARG;
- argIndex = 1;
- break;
- }
- #ifndef AS_NO_THISCALL_FUNCTOR_METHOD
- case ICC_THISCALL_OBJLAST:
- case ICC_VIRTUAL_THISCALL_OBJLAST:
- param_post = 2;
- #endif
- case ICC_THISCALL:
- case ICC_VIRTUAL_THISCALL:
- case ICC_CDECL_OBJFIRST:
- {
- paramBuffer[0] = (asPWORD)obj;
- argsType[0] = x64INTARG;
- argIndex = 1;
- break;
- }
- #ifndef AS_NO_THISCALL_FUNCTOR_METHOD
- case ICC_THISCALL_OBJLAST_RETURNINMEM:
- case ICC_VIRTUAL_THISCALL_OBJLAST_RETURNINMEM:
- param_post = 2;
- #endif
- case ICC_THISCALL_RETURNINMEM:
- case ICC_VIRTUAL_THISCALL_RETURNINMEM:
- case ICC_CDECL_OBJFIRST_RETURNINMEM:
- {
- paramBuffer[0] = (asPWORD)retPointer;
- paramBuffer[1] = (asPWORD)obj;
- argsType[0] = x64INTARG;
- argsType[1] = x64INTARG;
- argIndex = 2;
- break;
- }
- #ifndef AS_NO_THISCALL_FUNCTOR_METHOD
- case ICC_THISCALL_OBJFIRST:
- case ICC_VIRTUAL_THISCALL_OBJFIRST:
- {
- paramBuffer[0] = (asPWORD)obj;
- paramBuffer[1] = (asPWORD)secondObject;
- argsType[0] = x64INTARG;
- argsType[1] = x64INTARG;
- argIndex = 2;
- break;
- }
- case ICC_THISCALL_OBJFIRST_RETURNINMEM:
- case ICC_VIRTUAL_THISCALL_OBJFIRST_RETURNINMEM:
- {
- paramBuffer[0] = (asPWORD)retPointer;
- paramBuffer[1] = (asPWORD)obj;
- paramBuffer[2] = (asPWORD)secondObject;
- argsType[0] = x64INTARG;
- argsType[1] = x64INTARG;
- argsType[2] = x64INTARG;
- argIndex = 3;
- break;
- }
- #endif
- case ICC_CDECL_OBJLAST:
- param_post = 1;
- break;
- case ICC_CDECL_OBJLAST_RETURNINMEM:
- {
- paramBuffer[0] = (asPWORD)retPointer;
- argsType[0] = x64INTARG;
- argIndex = 1;
- param_post = 1;
- break;
- }
- }
- int argumentCount = ( int )descr->parameterTypes.GetLength();
- for( int a = 0; a < argumentCount; ++a )
- {
- const asCDataType &parmType = descr->parameterTypes[a];
- if( parmType.IsFloatType() && !parmType.IsReference() )
- {
- argsType[argIndex] = x64FLOATARG;
- memcpy(paramBuffer + argIndex, stack_pointer, sizeof(float));
- argIndex++;
- stack_pointer++;
- }
- else if( parmType.IsDoubleType() && !parmType.IsReference() )
- {
- argsType[argIndex] = x64FLOATARG;
- memcpy(paramBuffer + argIndex, stack_pointer, sizeof(double));
- argIndex++;
- stack_pointer += 2;
- }
- else if( IsVariableArgument( parmType ) )
- {
- // The variable args are really two, one pointer and one type id
- argsType[argIndex] = x64INTARG;
- argsType[argIndex+1] = x64INTARG;
- memcpy(paramBuffer + argIndex, stack_pointer, sizeof(void*));
- memcpy(paramBuffer + argIndex + 1, stack_pointer + 2, sizeof(asDWORD));
- argIndex += 2;
- stack_pointer += 3;
- }
- else if( parmType.IsPrimitive() ||
- parmType.IsReference() ||
- parmType.IsObjectHandle() )
- {
- argsType[argIndex] = x64INTARG;
- if( parmType.GetSizeOnStackDWords() == 1 )
- {
- memcpy(paramBuffer + argIndex, stack_pointer, sizeof(asDWORD));
- stack_pointer++;
- }
- else
- {
- memcpy(paramBuffer + argIndex, stack_pointer, sizeof(asQWORD));
- stack_pointer += 2;
- }
- argIndex++;
- }
- else
- {
- // An object is being passed by value
- if( (parmType.GetTypeInfo()->flags & COMPLEX_MASK) ||
- parmType.GetSizeInMemoryDWords() > 4 )
- {
- // Copy the address of the object
- argsType[argIndex] = x64INTARG;
- memcpy(paramBuffer + argIndex, stack_pointer, sizeof(asQWORD));
- argIndex++;
- }
- else if( (parmType.GetTypeInfo()->flags & asOBJ_APP_CLASS_ALLINTS) ||
- (parmType.GetTypeInfo()->flags & asOBJ_APP_PRIMITIVE) )
- {
- // Copy the value of the object
- if( parmType.GetSizeInMemoryDWords() > 2 )
- {
- argsType[argIndex] = x64INTARG;
- argsType[argIndex+1] = x64INTARG;
- memcpy(paramBuffer + argIndex, *(asDWORD**)stack_pointer, parmType.GetSizeInMemoryBytes());
- argIndex += 2;
- }
- else
- {
- argsType[argIndex] = x64INTARG;
- memcpy(paramBuffer + argIndex, *(asDWORD**)stack_pointer, parmType.GetSizeInMemoryBytes());
- argIndex++;
- }
- // Delete the original memory
- engine->CallFree(*(void**)stack_pointer);
- }
- else if( (parmType.GetTypeInfo()->flags & asOBJ_APP_CLASS_ALLFLOATS) ||
- (parmType.GetTypeInfo()->flags & asOBJ_APP_FLOAT) )
- {
- // Copy the value of the object
- if( parmType.GetSizeInMemoryDWords() > 2 )
- {
- argsType[argIndex] = x64FLOATARG;
- argsType[argIndex+1] = x64FLOATARG;
- memcpy(paramBuffer + argIndex, *(asDWORD**)stack_pointer, parmType.GetSizeInMemoryBytes());
- argIndex += 2;
- }
- else
- {
- argsType[argIndex] = x64FLOATARG;
- memcpy(paramBuffer + argIndex, *(asDWORD**)stack_pointer, parmType.GetSizeInMemoryBytes());
- argIndex++;
- }
- // Delete the original memory
- engine->CallFree(*(void**)stack_pointer);
- }
- stack_pointer += 2;
- }
- }
- // For the CDECL_OBJ_LAST calling convention we need to add the object pointer as the last argument
- if( param_post )
- {
- #ifdef AS_NO_THISCALL_FUNCTOR_METHOD
- paramBuffer[argIndex] = (asPWORD)obj;
- #else
- paramBuffer[argIndex] = (asPWORD)(param_post > 1 ? secondObject : obj);
- #endif
- argsType[argIndex] = x64INTARG;
- argIndex++;
- }
- totalArgumentCount = argIndex;
- /*
- * Q: WTF is going on here !?
- *
- * A: The idea is to pre-arange the parameters so that X64_CallFunction() can do
- * it's little magic which must work regardless of how the compiler decides to
- * allocate registers. Basically:
- * - the first MAX_CALL_INT_REGISTERS entries in tempBuff will
- * contain the values/types of the x64INTARG parameters - that is the ones who
- * go into the registers. If the function has less then MAX_CALL_INT_REGISTERS
- * integer parameters then the last entries will be set to 0
- * - the next MAX_CALL_SSE_REGISTERS entries will contain the float/double arguments
- * that go into the floating point registers. If the function has less than
- * MAX_CALL_SSE_REGISTERS floating point parameters then the last entries will
- * be set to 0
- * - index MAX_CALL_INT_REGISTERS + MAX_CALL_SSE_REGISTERS marks the start of the
- * parameters which will get passed on the stack. These are added to the array
- * in reverse order so that X64_CallFunction() can simply push them to the stack
- * without the need to perform further tests
- */
- asQWORD tempBuff[X64_CALLSTACK_SIZE] = { 0 };
- asBYTE argsSet[X64_CALLSTACK_SIZE] = { 0 };
- int used_int_regs = 0;
- int used_sse_regs = 0;
- int used_stack_args = 0;
- int idx = 0;
- for ( n = 0; ( n < totalArgumentCount ) && ( used_int_regs < MAX_CALL_INT_REGISTERS ); n++ )
- {
- if ( argsType[n] == x64INTARG )
- {
- argsSet[n] = 1;
- tempBuff[idx++] = paramBuffer[n];
- used_int_regs++;
- }
- }
- idx = MAX_CALL_INT_REGISTERS;
- for ( n = 0; ( n < totalArgumentCount ) && ( used_sse_regs < MAX_CALL_SSE_REGISTERS ); n++ )
- {
- if ( argsType[n] == x64FLOATARG )
- {
- argsSet[n] = 1;
- tempBuff[idx++] = paramBuffer[n];
- used_sse_regs++;
- }
- }
- idx = MAX_CALL_INT_REGISTERS + MAX_CALL_SSE_REGISTERS;
- for ( n = totalArgumentCount - 1; n >= 0; n-- )
- {
- if ( !argsSet[n] )
- {
- tempBuff[idx++] = paramBuffer[n];
- used_stack_args++;
- }
- }
- retQW = X64_CallFunction( tempBuff, used_stack_args, func, retQW2, sysFunc->hostReturnFloat );
- return retQW;
- }
- END_AS_NAMESPACE
- #endif // AS_X64_GCC
- #endif // AS_MAX_PORTABILITY
|