|
|
@@ -57,62 +57,50 @@ typedef asQWORD ( *funcptr_t )( void );
|
|
|
// 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
|
|
|
|
|
|
-#define PUSH_LONG( val ) \
|
|
|
- __asm__ __volatile__ ( \
|
|
|
- "movq %0, %%rax\n" \
|
|
|
- "pushq %%rax" \
|
|
|
- : \
|
|
|
- : "m" ( val ) \
|
|
|
- : "%rax" \
|
|
|
- )
|
|
|
-
|
|
|
-#define ASM_GET_REG( name, dest ) \
|
|
|
- __asm__ __volatile__ ( \
|
|
|
- "movq %" name ", %0\n" \
|
|
|
- : \
|
|
|
- : "m" ( dest ) \
|
|
|
- : name \
|
|
|
- )
|
|
|
-
|
|
|
-static void __attribute__((noinline)) GetReturnedXmm0Xmm1(asQWORD &a, asQWORD &b)
|
|
|
+static asQWORD __attribute__((noinline)) X64_CallFunction(const asQWORD *args, int cnt, funcptr_t func, asQWORD &retQW2, bool returnFloat)
|
|
|
{
|
|
|
- __asm__ __volatile__ (
|
|
|
- "lea %0, %%rax\n"
|
|
|
- "movq %%xmm0, (%%rax)\n"
|
|
|
- "lea %1, %%rdx\n"
|
|
|
- "movq %%xmm1, (%%rdx)\n"
|
|
|
- : // no optput
|
|
|
- : "m" (a), "m" (b)
|
|
|
- : "%rax", "%rdx", "%xmm0", "%xmm1"
|
|
|
- );
|
|
|
-}
|
|
|
+ asQWORD retQW1;
|
|
|
|
|
|
-static asQWORD __attribute__((noinline)) X64_CallFunction( const asQWORD *args, int cnt, funcptr_t func )
|
|
|
-{
|
|
|
- asQWORD retval;
|
|
|
- funcptr_t call = func;
|
|
|
- int i = 0;
|
|
|
+ // Reference: http://www.x86-64.org/documentation/abi.pdf
|
|
|
|
|
|
- // Backup the stack pointer and then align it to 16 bytes.
|
|
|
- // The R15 register is guaranteed to maintain its value over function
|
|
|
- // calls, so it is safe for us to keep the original stack pointer here.
|
|
|
__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"
|
|
|
+
|
|
|
+ // 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 %0, %%rax \n"
|
|
|
+ " sub %%rdx, %%rax \n"
|
|
|
" and $15, %%rax \n"
|
|
|
" sub %%rax, %%rsp \n"
|
|
|
- : : "r" ((asQWORD)cnt*8)
|
|
|
- // Tell the compiler that we're using the RAX and R15.
|
|
|
- // This will make sure these registers are backed up by the compiler.
|
|
|
- : "%rax", "%r15", "%rsp");
|
|
|
|
|
|
- // Push the stack parameters
|
|
|
- for( i = MAX_CALL_INT_REGISTERS + MAX_CALL_SSE_REGISTERS; cnt-- > 0; i++ )
|
|
|
- PUSH_LONG( args[i] );
|
|
|
+ // 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
|
|
|
- __asm__ __volatile__ (
|
|
|
+ " movq %%r10, %%rax \n"
|
|
|
" mov (%%rax), %%rdi \n"
|
|
|
" mov 8(%%rax), %%rsi \n"
|
|
|
" mov 16(%%rax), %%rdx \n"
|
|
|
@@ -128,21 +116,32 @@ static asQWORD __attribute__((noinline)) X64_CallFunction( const asQWORD *args,
|
|
|
" movsd 40(%%rax), %%xmm5 \n"
|
|
|
" movsd 48(%%rax), %%xmm6 \n"
|
|
|
" movsd 56(%%rax), %%xmm7 \n"
|
|
|
- :
|
|
|
- : "a" (args) // Pass args in rax
|
|
|
+
|
|
|
+ // Call the function
|
|
|
+ " call *%%r11 \n"
|
|
|
+
|
|
|
+ // Restore stack pointer
|
|
|
+ " mov %%r15, %%rsp \n"
|
|
|
+
|
|
|
+ // 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"
|
|
|
+
|
|
|
+ : : "r" ((asQWORD)cnt), "r" (args), "r" (func), "m" (retQW1), "m" (retQW2), "m" (returnFloat)
|
|
|
: "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7",
|
|
|
- "%rdi", "%rsi", "%rdx", "%rcx", "%r8", "%r9",
|
|
|
- // rsp and r15 is added too so the compiler doesn't try to use
|
|
|
- // them to store anything over this piece of assembly
|
|
|
- "%rsp", "%r15");
|
|
|
+ "%rdi", "%rsi", "%rax", "%rdx", "%rcx", "%r8", "%r9", "%r10", "%r11", "%r15");
|
|
|
|
|
|
- // call the function with the arguments
|
|
|
- retval = call();
|
|
|
-
|
|
|
- // Restore the stack pointer
|
|
|
- __asm__ __volatile__ (" mov %%r15, %%rsp \n" : : : "%r15", "%rsp");
|
|
|
-
|
|
|
- return retval;
|
|
|
+ return retQW1;
|
|
|
}
|
|
|
|
|
|
// returns true if the given parameter is a 'variable argument'
|
|
|
@@ -394,12 +393,7 @@ asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- retQW = X64_CallFunction( tempBuff, used_stack_args, func );
|
|
|
- ASM_GET_REG( "%rdx", retQW2 );
|
|
|
-
|
|
|
- // If the return is a float value we need to get the value from the FP register
|
|
|
- if( sysFunc->hostReturnFloat )
|
|
|
- GetReturnedXmm0Xmm1(retQW, retQW2);
|
|
|
+ retQW = X64_CallFunction( tempBuff, used_stack_args, func, retQW2, sysFunc->hostReturnFloat );
|
|
|
|
|
|
return retQW;
|
|
|
}
|