as_callfunc_x64_gcc.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478
  1. /*
  2. AngelCode Scripting Library
  3. Copyright (c) 2003-2017 Andreas Jonsson
  4. This software is provided 'as-is', without any express or implied
  5. warranty. In no event will the authors be held liable for any
  6. damages arising from the use of this software.
  7. Permission is granted to anyone to use this software for any
  8. purpose, including commercial applications, and to alter it and
  9. redistribute it freely, subject to the following restrictions:
  10. 1. The origin of this software must not be misrepresented; you
  11. must not claim that you wrote the original software. If you use
  12. this software in a product, an acknowledgment in the product
  13. documentation would be appreciated but is not required.
  14. 2. Altered source versions must be plainly marked as such, and
  15. must not be misrepresented as being the original software.
  16. 3. This notice may not be removed or altered from any source
  17. distribution.
  18. The original version of this library can be located at:
  19. http://www.angelcode.com/angelscript/
  20. Andreas Jonsson
  21. andreas@angelcode.com
  22. */
  23. /*
  24. * Implements the AMD64 calling convention for gcc-based 64bit Unices
  25. *
  26. * Author: Ionut "gargltk" Leonte <ileonte@bitdefender.com>
  27. *
  28. * Initial author: niteice
  29. *
  30. * Added support for functor methods by Jordi Oliveras Rovira in April, 2014.
  31. */
  32. // Useful references for the System V AMD64 ABI:
  33. // http://eli.thegreenplace.net/2011/09/06/stack-frame-layout-on-x86-64/
  34. // http://math-atlas.sourceforge.net/devel/assembly/abi_sysV_amd64.pdf
  35. #include "as_config.h"
  36. #ifndef AS_MAX_PORTABILITY
  37. #ifdef AS_X64_GCC
  38. #include "as_scriptengine.h"
  39. #include "as_texts.h"
  40. #include "as_context.h"
  41. BEGIN_AS_NAMESPACE
  42. enum argTypes { x64INTARG = 0, x64FLOATARG = 1 };
  43. typedef asQWORD ( *funcptr_t )( void );
  44. #define X64_MAX_ARGS 32
  45. #define MAX_CALL_INT_REGISTERS 6
  46. #define MAX_CALL_SSE_REGISTERS 8
  47. #define X64_CALLSTACK_SIZE ( X64_MAX_ARGS + MAX_CALL_SSE_REGISTERS + 3 )
  48. // Note to self: Always remember to inform the used registers on the clobber line,
  49. // so that the gcc optimizer doesn't try to use them for other things
  50. static asQWORD __attribute__((noinline)) X64_CallFunction(const asQWORD *args, int cnt, funcptr_t func, asQWORD &retQW2, bool returnFloat)
  51. {
  52. // Need to flag the variable as volatile so the compiler doesn't optimize out the variable
  53. volatile asQWORD retQW1 = 0;
  54. // Reference: http://www.x86-64.org/documentation/abi.pdf
  55. __asm__ __volatile__ (
  56. " movq %0, %%rcx \n" // rcx = cnt
  57. " movq %1, %%r10 \n" // r10 = args
  58. " movq %2, %%r11 \n" // r11 = func
  59. // Backup stack pointer in R15 that is guaranteed to maintain its value over function calls
  60. " movq %%rsp, %%r15 \n"
  61. #ifdef __OPTIMIZE__
  62. // Make sure the stack unwind logic knows we've backed up the stack pointer in register r15
  63. // This should only be done if any optimization is done. If no optimization (-O0) is used,
  64. // then the compiler already backups the rsp before entering the inline assembler code
  65. " .cfi_def_cfa_register r15 \n"
  66. #endif
  67. // Skip the first 128 bytes on the stack frame, called "red zone",
  68. // that might be used by the compiler to store temporary values
  69. " sub $128, %%rsp \n"
  70. // Make sure the stack pointer will be aligned to 16 bytes when the function is called
  71. " movq %%rcx, %%rdx \n"
  72. " salq $3, %%rdx \n"
  73. " movq %%rsp, %%rax \n"
  74. " sub %%rdx, %%rax \n"
  75. " and $15, %%rax \n"
  76. " sub %%rax, %%rsp \n"
  77. // Push the stack parameters, i.e. the arguments that won't be loaded into registers
  78. " movq %%rcx, %%rsi \n"
  79. " testl %%esi, %%esi \n"
  80. " jle endstack \n"
  81. " subl $1, %%esi \n"
  82. " xorl %%edx, %%edx \n"
  83. " leaq 8(, %%rsi, 8), %%rcx \n"
  84. "loopstack: \n"
  85. " movq 112(%%r10, %%rdx), %%rax \n"
  86. " pushq %%rax \n"
  87. " addq $8, %%rdx \n"
  88. " cmpq %%rcx, %%rdx \n"
  89. " jne loopstack \n"
  90. "endstack: \n"
  91. // Populate integer and floating point parameters
  92. " movq %%r10, %%rax \n"
  93. " mov (%%rax), %%rdi \n"
  94. " mov 8(%%rax), %%rsi \n"
  95. " mov 16(%%rax), %%rdx \n"
  96. " mov 24(%%rax), %%rcx \n"
  97. " mov 32(%%rax), %%r8 \n"
  98. " mov 40(%%rax), %%r9 \n"
  99. " add $48, %%rax \n"
  100. " movsd (%%rax), %%xmm0 \n"
  101. " movsd 8(%%rax), %%xmm1 \n"
  102. " movsd 16(%%rax), %%xmm2 \n"
  103. " movsd 24(%%rax), %%xmm3 \n"
  104. " movsd 32(%%rax), %%xmm4 \n"
  105. " movsd 40(%%rax), %%xmm5 \n"
  106. " movsd 48(%%rax), %%xmm6 \n"
  107. " movsd 56(%%rax), %%xmm7 \n"
  108. // Call the function
  109. " call *%%r11 \n"
  110. // Restore stack pointer
  111. " mov %%r15, %%rsp \n"
  112. #ifdef __OPTIMIZE__
  113. // Inform the stack unwind logic that the stack pointer has been restored
  114. // This should only be done if any optimization is done. If no optimization (-O0) is used,
  115. // then the compiler already backups the rsp before entering the inline assembler code
  116. " .cfi_def_cfa_register rsp \n"
  117. #endif
  118. // Put return value in retQW1 and retQW2, using either RAX:RDX or XMM0:XMM1 depending on type of return value
  119. " movl %5, %%ecx \n"
  120. " testb %%cl, %%cl \n"
  121. " je intret \n"
  122. " lea %3, %%rax \n"
  123. " movq %%xmm0, (%%rax) \n"
  124. " lea %4, %%rdx \n"
  125. " movq %%xmm1, (%%rdx) \n"
  126. " jmp endcall \n"
  127. "intret: \n"
  128. " movq %%rax, %3 \n"
  129. " movq %%rdx, %4 \n"
  130. "endcall: \n"
  131. : : "g" ((asQWORD)cnt), "g" (args), "g" (func), "m" (retQW1), "m" (retQW2), "m" (returnFloat)
  132. : "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7",
  133. "%rdi", "%rsi", "%rax", "%rdx", "%rcx", "%r8", "%r9", "%r10", "%r11", "%r15");
  134. return retQW1;
  135. }
  136. // returns true if the given parameter is a 'variable argument'
  137. static inline bool IsVariableArgument( asCDataType type )
  138. {
  139. return ( type.GetTokenType() == ttQuestion ) ? true : false;
  140. }
  141. asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, void *obj, asDWORD *args, void *retPointer, asQWORD &retQW2, void *secondObject)
  142. {
  143. asCScriptEngine *engine = context->m_engine;
  144. asSSystemFunctionInterface *sysFunc = descr->sysFuncIntf;
  145. int callConv = sysFunc->callConv;
  146. asQWORD retQW = 0;
  147. asDWORD *stack_pointer = args;
  148. funcptr_t *vftable = NULL;
  149. int totalArgumentCount = 0;
  150. int n = 0;
  151. int param_post = 0;
  152. int argIndex = 0;
  153. funcptr_t func = (funcptr_t)sysFunc->func;
  154. if( sysFunc->hostReturnInMemory )
  155. {
  156. // The return is made in memory
  157. callConv++;
  158. }
  159. #ifdef AS_NO_THISCALL_FUNCTOR_METHOD
  160. // Determine the real function pointer in case of virtual method
  161. if ( obj && ( callConv == ICC_VIRTUAL_THISCALL || callConv == ICC_VIRTUAL_THISCALL_RETURNINMEM ) )
  162. #else
  163. if ( obj && ( callConv == ICC_VIRTUAL_THISCALL ||
  164. callConv == ICC_VIRTUAL_THISCALL_RETURNINMEM ||
  165. callConv == ICC_VIRTUAL_THISCALL_OBJFIRST ||
  166. callConv == ICC_VIRTUAL_THISCALL_OBJFIRST_RETURNINMEM ||
  167. callConv == ICC_VIRTUAL_THISCALL_OBJLAST ||
  168. callConv == ICC_VIRTUAL_THISCALL_OBJLAST_RETURNINMEM) )
  169. #endif
  170. {
  171. vftable = *((funcptr_t**)obj);
  172. func = vftable[FuncPtrToUInt(asFUNCTION_t(func)) >> 3];
  173. }
  174. // Determine the type of the arguments, and prepare the input array for the X64_CallFunction
  175. asQWORD paramBuffer[X64_CALLSTACK_SIZE] = { 0 };
  176. asBYTE argsType[X64_CALLSTACK_SIZE] = { 0 };
  177. switch ( callConv )
  178. {
  179. case ICC_CDECL_RETURNINMEM:
  180. case ICC_STDCALL_RETURNINMEM:
  181. {
  182. paramBuffer[0] = (asPWORD)retPointer;
  183. argsType[0] = x64INTARG;
  184. argIndex = 1;
  185. break;
  186. }
  187. #ifndef AS_NO_THISCALL_FUNCTOR_METHOD
  188. case ICC_THISCALL_OBJLAST:
  189. case ICC_VIRTUAL_THISCALL_OBJLAST:
  190. param_post = 2;
  191. #endif
  192. case ICC_THISCALL:
  193. case ICC_VIRTUAL_THISCALL:
  194. case ICC_CDECL_OBJFIRST:
  195. {
  196. paramBuffer[0] = (asPWORD)obj;
  197. argsType[0] = x64INTARG;
  198. argIndex = 1;
  199. break;
  200. }
  201. #ifndef AS_NO_THISCALL_FUNCTOR_METHOD
  202. case ICC_THISCALL_OBJLAST_RETURNINMEM:
  203. case ICC_VIRTUAL_THISCALL_OBJLAST_RETURNINMEM:
  204. param_post = 2;
  205. #endif
  206. case ICC_THISCALL_RETURNINMEM:
  207. case ICC_VIRTUAL_THISCALL_RETURNINMEM:
  208. case ICC_CDECL_OBJFIRST_RETURNINMEM:
  209. {
  210. paramBuffer[0] = (asPWORD)retPointer;
  211. paramBuffer[1] = (asPWORD)obj;
  212. argsType[0] = x64INTARG;
  213. argsType[1] = x64INTARG;
  214. argIndex = 2;
  215. break;
  216. }
  217. #ifndef AS_NO_THISCALL_FUNCTOR_METHOD
  218. case ICC_THISCALL_OBJFIRST:
  219. case ICC_VIRTUAL_THISCALL_OBJFIRST:
  220. {
  221. paramBuffer[0] = (asPWORD)obj;
  222. paramBuffer[1] = (asPWORD)secondObject;
  223. argsType[0] = x64INTARG;
  224. argsType[1] = x64INTARG;
  225. argIndex = 2;
  226. break;
  227. }
  228. case ICC_THISCALL_OBJFIRST_RETURNINMEM:
  229. case ICC_VIRTUAL_THISCALL_OBJFIRST_RETURNINMEM:
  230. {
  231. paramBuffer[0] = (asPWORD)retPointer;
  232. paramBuffer[1] = (asPWORD)obj;
  233. paramBuffer[2] = (asPWORD)secondObject;
  234. argsType[0] = x64INTARG;
  235. argsType[1] = x64INTARG;
  236. argsType[2] = x64INTARG;
  237. argIndex = 3;
  238. break;
  239. }
  240. #endif
  241. case ICC_CDECL_OBJLAST:
  242. param_post = 1;
  243. break;
  244. case ICC_CDECL_OBJLAST_RETURNINMEM:
  245. {
  246. paramBuffer[0] = (asPWORD)retPointer;
  247. argsType[0] = x64INTARG;
  248. argIndex = 1;
  249. param_post = 1;
  250. break;
  251. }
  252. }
  253. int argumentCount = ( int )descr->parameterTypes.GetLength();
  254. for( int a = 0; a < argumentCount; ++a )
  255. {
  256. const asCDataType &parmType = descr->parameterTypes[a];
  257. if( parmType.IsFloatType() && !parmType.IsReference() )
  258. {
  259. argsType[argIndex] = x64FLOATARG;
  260. memcpy(paramBuffer + argIndex, stack_pointer, sizeof(float));
  261. argIndex++;
  262. stack_pointer++;
  263. }
  264. else if( parmType.IsDoubleType() && !parmType.IsReference() )
  265. {
  266. argsType[argIndex] = x64FLOATARG;
  267. memcpy(paramBuffer + argIndex, stack_pointer, sizeof(double));
  268. argIndex++;
  269. stack_pointer += 2;
  270. }
  271. else if( IsVariableArgument( parmType ) )
  272. {
  273. // The variable args are really two, one pointer and one type id
  274. argsType[argIndex] = x64INTARG;
  275. argsType[argIndex+1] = x64INTARG;
  276. memcpy(paramBuffer + argIndex, stack_pointer, sizeof(void*));
  277. memcpy(paramBuffer + argIndex + 1, stack_pointer + 2, sizeof(asDWORD));
  278. argIndex += 2;
  279. stack_pointer += 3;
  280. }
  281. else if( parmType.IsPrimitive() ||
  282. parmType.IsReference() ||
  283. parmType.IsObjectHandle() )
  284. {
  285. argsType[argIndex] = x64INTARG;
  286. if( parmType.GetSizeOnStackDWords() == 1 )
  287. {
  288. memcpy(paramBuffer + argIndex, stack_pointer, sizeof(asDWORD));
  289. stack_pointer++;
  290. }
  291. else
  292. {
  293. memcpy(paramBuffer + argIndex, stack_pointer, sizeof(asQWORD));
  294. stack_pointer += 2;
  295. }
  296. argIndex++;
  297. }
  298. else
  299. {
  300. // An object is being passed by value
  301. if( (parmType.GetTypeInfo()->flags & COMPLEX_MASK) ||
  302. parmType.GetSizeInMemoryDWords() > 4 )
  303. {
  304. // Copy the address of the object
  305. argsType[argIndex] = x64INTARG;
  306. memcpy(paramBuffer + argIndex, stack_pointer, sizeof(asQWORD));
  307. argIndex++;
  308. }
  309. else if( (parmType.GetTypeInfo()->flags & asOBJ_APP_CLASS_ALLINTS) ||
  310. (parmType.GetTypeInfo()->flags & asOBJ_APP_PRIMITIVE) )
  311. {
  312. // Copy the value of the object
  313. if( parmType.GetSizeInMemoryDWords() > 2 )
  314. {
  315. argsType[argIndex] = x64INTARG;
  316. argsType[argIndex+1] = x64INTARG;
  317. memcpy(paramBuffer + argIndex, *(asDWORD**)stack_pointer, parmType.GetSizeInMemoryBytes());
  318. argIndex += 2;
  319. }
  320. else
  321. {
  322. argsType[argIndex] = x64INTARG;
  323. memcpy(paramBuffer + argIndex, *(asDWORD**)stack_pointer, parmType.GetSizeInMemoryBytes());
  324. argIndex++;
  325. }
  326. // Delete the original memory
  327. engine->CallFree(*(void**)stack_pointer);
  328. }
  329. else if( (parmType.GetTypeInfo()->flags & asOBJ_APP_CLASS_ALLFLOATS) ||
  330. (parmType.GetTypeInfo()->flags & asOBJ_APP_FLOAT) )
  331. {
  332. // Copy the value of the object
  333. if( parmType.GetSizeInMemoryDWords() > 2 )
  334. {
  335. argsType[argIndex] = x64FLOATARG;
  336. argsType[argIndex+1] = x64FLOATARG;
  337. memcpy(paramBuffer + argIndex, *(asDWORD**)stack_pointer, parmType.GetSizeInMemoryBytes());
  338. argIndex += 2;
  339. }
  340. else
  341. {
  342. argsType[argIndex] = x64FLOATARG;
  343. memcpy(paramBuffer + argIndex, *(asDWORD**)stack_pointer, parmType.GetSizeInMemoryBytes());
  344. argIndex++;
  345. }
  346. // Delete the original memory
  347. engine->CallFree(*(void**)stack_pointer);
  348. }
  349. stack_pointer += 2;
  350. }
  351. }
  352. // For the CDECL_OBJ_LAST calling convention we need to add the object pointer as the last argument
  353. if( param_post )
  354. {
  355. #ifdef AS_NO_THISCALL_FUNCTOR_METHOD
  356. paramBuffer[argIndex] = (asPWORD)obj;
  357. #else
  358. paramBuffer[argIndex] = (asPWORD)(param_post > 1 ? secondObject : obj);
  359. #endif
  360. argsType[argIndex] = x64INTARG;
  361. argIndex++;
  362. }
  363. totalArgumentCount = argIndex;
  364. /*
  365. * Q: WTF is going on here !?
  366. *
  367. * A: The idea is to pre-arange the parameters so that X64_CallFunction() can do
  368. * it's little magic which must work regardless of how the compiler decides to
  369. * allocate registers. Basically:
  370. * - the first MAX_CALL_INT_REGISTERS entries in tempBuff will
  371. * contain the values/types of the x64INTARG parameters - that is the ones who
  372. * go into the registers. If the function has less then MAX_CALL_INT_REGISTERS
  373. * integer parameters then the last entries will be set to 0
  374. * - the next MAX_CALL_SSE_REGISTERS entries will contain the float/double arguments
  375. * that go into the floating point registers. If the function has less than
  376. * MAX_CALL_SSE_REGISTERS floating point parameters then the last entries will
  377. * be set to 0
  378. * - index MAX_CALL_INT_REGISTERS + MAX_CALL_SSE_REGISTERS marks the start of the
  379. * parameters which will get passed on the stack. These are added to the array
  380. * in reverse order so that X64_CallFunction() can simply push them to the stack
  381. * without the need to perform further tests
  382. */
  383. asQWORD tempBuff[X64_CALLSTACK_SIZE] = { 0 };
  384. asBYTE argsSet[X64_CALLSTACK_SIZE] = { 0 };
  385. int used_int_regs = 0;
  386. int used_sse_regs = 0;
  387. int used_stack_args = 0;
  388. int idx = 0;
  389. for ( n = 0; ( n < totalArgumentCount ) && ( used_int_regs < MAX_CALL_INT_REGISTERS ); n++ )
  390. {
  391. if ( argsType[n] == x64INTARG )
  392. {
  393. argsSet[n] = 1;
  394. tempBuff[idx++] = paramBuffer[n];
  395. used_int_regs++;
  396. }
  397. }
  398. idx = MAX_CALL_INT_REGISTERS;
  399. for ( n = 0; ( n < totalArgumentCount ) && ( used_sse_regs < MAX_CALL_SSE_REGISTERS ); n++ )
  400. {
  401. if ( argsType[n] == x64FLOATARG )
  402. {
  403. argsSet[n] = 1;
  404. tempBuff[idx++] = paramBuffer[n];
  405. used_sse_regs++;
  406. }
  407. }
  408. idx = MAX_CALL_INT_REGISTERS + MAX_CALL_SSE_REGISTERS;
  409. for ( n = totalArgumentCount - 1; n >= 0; n-- )
  410. {
  411. if ( !argsSet[n] )
  412. {
  413. tempBuff[idx++] = paramBuffer[n];
  414. used_stack_args++;
  415. }
  416. }
  417. retQW = X64_CallFunction( tempBuff, used_stack_args, func, retQW2, sysFunc->hostReturnFloat );
  418. return retQW;
  419. }
  420. END_AS_NAMESPACE
  421. #endif // AS_X64_GCC
  422. #endif // AS_MAX_PORTABILITY