as_callfunc_x86.cpp 32 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159
  1. /*
  2. AngelCode Scripting Library
  3. Copyright (c) 2003-2011 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. [email protected]
  22. */
  23. //
  24. // as_callfunc_x86.cpp
  25. //
  26. // These functions handle the actual calling of system functions
  27. //
  28. #include "as_config.h"
  29. #ifndef AS_MAX_PORTABILITY
  30. #ifdef AS_X86
  31. #include "as_callfunc.h"
  32. #include "as_scriptengine.h"
  33. #include "as_texts.h"
  34. #include "as_tokendef.h"
  35. BEGIN_AS_NAMESPACE
  36. //
  37. // With some compile level optimizations the functions don't clear the FPU
  38. // stack themselves. So we have to do it as part of calling the native functions,
  39. // as the compiler will not be able to predict when it is supposed to do it by
  40. // itself due to the dynamic nature of scripts
  41. //
  42. // - fninit clears the FPU stack and the FPU control word
  43. // - emms only clears the FPU stack, while preserving the FPU control word
  44. //
  45. // By default I use fninit as it seems to be what works for most people,
  46. // but some may find it necessary to define this as emms instead.
  47. //
  48. // TODO: Figure out when one or the other must be used, and a way to
  49. // configure this automatically in as_config.h
  50. //
  51. #ifndef CLEAR_FPU_STACK
  52. #define CLEAR_FPU_STACK fninit
  53. #endif
  54. // These macros are just to allow me to use the above macro in the GNUC style inline assembly
  55. #define _S(x) _TOSTRING(x)
  56. #define _TOSTRING(x) #x
  57. typedef asQWORD (*t_CallCDeclQW)(const asDWORD *, int, size_t);
  58. typedef asQWORD (*t_CallCDeclQWObj)(void *obj, const asDWORD *, int, size_t);
  59. typedef asDWORD (*t_CallCDeclRetByRef)(const asDWORD *, int, size_t, void *);
  60. typedef asDWORD (*t_CallCDeclObjRetByRef)(void *obj, const asDWORD *, int, size_t, void *);
  61. typedef asQWORD (*t_CallSTDCallQW)(const asDWORD *, int, size_t);
  62. typedef asQWORD (*t_CallThisCallQW)(const void *, const asDWORD *, int, size_t);
  63. typedef asDWORD (*t_CallThisCallRetByRef)(const void *, const asDWORD *, int, size_t, void *);
  64. // Prototypes
  65. void CallCDeclFunction(const asDWORD *args, int paramSize, size_t func);
  66. void CallCDeclFunctionObjLast(const void *obj, const asDWORD *args, int paramSize, size_t func);
  67. void CallCDeclFunctionObjFirst(const void *obj, const asDWORD *args, int paramSize, size_t func);
  68. void CallCDeclFunctionRetByRef_impl(const asDWORD *args, int paramSize, size_t func, void *retPtr);
  69. void CallCDeclFunctionRetByRefObjLast_impl(const void *obj, const asDWORD *args, int paramSize, size_t func, void *retPtr);
  70. void CallCDeclFunctionRetByRefObjFirst_impl(const void *obj, const asDWORD *args, int paramSize, size_t func, void *retPtr);
  71. void CallSTDCallFunction(const asDWORD *args, int paramSize, size_t func);
  72. void CallThisCallFunction(const void *obj, const asDWORD *args, int paramSize, size_t func);
  73. void CallThisCallFunctionRetByRef_impl(const void *, const asDWORD *, int, size_t, void *retPtr);
  74. // Initialize function pointers
  75. const t_CallCDeclQW CallCDeclFunctionQWord = (t_CallCDeclQW)CallCDeclFunction;
  76. const t_CallCDeclQWObj CallCDeclFunctionQWordObjLast = (t_CallCDeclQWObj)CallCDeclFunctionObjLast;
  77. const t_CallCDeclQWObj CallCDeclFunctionQWordObjFirst = (t_CallCDeclQWObj)CallCDeclFunctionObjFirst;
  78. const t_CallCDeclRetByRef CallCDeclFunctionRetByRef = (t_CallCDeclRetByRef)CallCDeclFunctionRetByRef_impl;
  79. const t_CallCDeclObjRetByRef CallCDeclFunctionRetByRefObjLast = (t_CallCDeclObjRetByRef)CallCDeclFunctionRetByRefObjLast_impl;
  80. const t_CallCDeclObjRetByRef CallCDeclFunctionRetByRefObjFirst = (t_CallCDeclObjRetByRef)CallCDeclFunctionRetByRefObjFirst_impl;
  81. const t_CallSTDCallQW CallSTDCallFunctionQWord = (t_CallSTDCallQW)CallSTDCallFunction;
  82. const t_CallThisCallQW CallThisCallFunctionQWord = (t_CallThisCallQW)CallThisCallFunction;
  83. const t_CallThisCallRetByRef CallThisCallFunctionRetByRef = (t_CallThisCallRetByRef)CallThisCallFunctionRetByRef_impl;
  84. asDWORD GetReturnedFloat();
  85. asQWORD GetReturnedDouble();
  86. asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, void *obj, asDWORD *args, void *retPointer, asQWORD &/*retQW2*/)
  87. {
  88. asCScriptEngine *engine = context->engine;
  89. asSSystemFunctionInterface *sysFunc = descr->sysFuncIntf;
  90. asQWORD retQW;
  91. // Prepare the parameters
  92. int paramSize = sysFunc->paramSize;
  93. asDWORD paramBuffer[64];
  94. if( sysFunc->takesObjByVal )
  95. {
  96. paramSize = 0;
  97. int spos = 0;
  98. int dpos = 1;
  99. for( asUINT n = 0; n < descr->parameterTypes.GetLength(); n++ )
  100. {
  101. if( descr->parameterTypes[n].IsObject() && !descr->parameterTypes[n].IsObjectHandle() && !descr->parameterTypes[n].IsReference() )
  102. {
  103. #ifdef COMPLEX_OBJS_PASSED_BY_REF
  104. if( descr->parameterTypes[n].GetObjectType()->flags & COMPLEX_MASK )
  105. {
  106. paramBuffer[dpos++] = args[spos++];
  107. paramSize++;
  108. }
  109. else
  110. #endif
  111. {
  112. // Copy the object's memory to the buffer
  113. memcpy(&paramBuffer[dpos], *(void**)(args+spos), descr->parameterTypes[n].GetSizeInMemoryBytes());
  114. // Delete the original memory
  115. engine->CallFree(*(char**)(args+spos));
  116. spos++;
  117. dpos += descr->parameterTypes[n].GetSizeInMemoryDWords();
  118. paramSize += descr->parameterTypes[n].GetSizeInMemoryDWords();
  119. }
  120. }
  121. else
  122. {
  123. // Copy the value directly
  124. paramBuffer[dpos++] = args[spos++];
  125. if( descr->parameterTypes[n].GetSizeOnStackDWords() > 1 )
  126. paramBuffer[dpos++] = args[spos++];
  127. paramSize += descr->parameterTypes[n].GetSizeOnStackDWords();
  128. }
  129. }
  130. // Keep a free location at the beginning
  131. args = &paramBuffer[1];
  132. }
  133. // Make the actual call
  134. void *func = (void*)sysFunc->func;
  135. int callConv = sysFunc->callConv;
  136. if( sysFunc->hostReturnInMemory )
  137. callConv++;
  138. asDWORD *vftable;
  139. context->isCallingSystemFunction = true;
  140. switch( callConv )
  141. {
  142. case ICC_CDECL:
  143. retQW = CallCDeclFunctionQWord(args, paramSize<<2, (size_t)func);
  144. break;
  145. case ICC_CDECL_RETURNINMEM:
  146. retQW = CallCDeclFunctionRetByRef(args, paramSize<<2, (size_t)func, retPointer);
  147. break;
  148. case ICC_STDCALL:
  149. retQW = CallSTDCallFunctionQWord(args, paramSize<<2, (size_t)func);
  150. break;
  151. case ICC_STDCALL_RETURNINMEM:
  152. // Push the return pointer on the stack
  153. paramSize++;
  154. args--;
  155. *(size_t*)args = (size_t)retPointer;
  156. retQW = CallSTDCallFunctionQWord(args, paramSize<<2, (size_t)func);
  157. break;
  158. case ICC_THISCALL:
  159. retQW = CallThisCallFunctionQWord(obj, args, paramSize<<2, (size_t)func);
  160. break;
  161. case ICC_THISCALL_RETURNINMEM:
  162. retQW = CallThisCallFunctionRetByRef(obj, args, paramSize<<2, (size_t)func, retPointer);
  163. break;
  164. case ICC_VIRTUAL_THISCALL:
  165. // Get virtual function table from the object pointer
  166. vftable = *(asDWORD**)obj;
  167. retQW = CallThisCallFunctionQWord(obj, args, paramSize<<2, vftable[size_t(func)>>2]);
  168. break;
  169. case ICC_VIRTUAL_THISCALL_RETURNINMEM:
  170. // Get virtual function table from the object pointer
  171. vftable = *(asDWORD**)obj;
  172. retQW = CallThisCallFunctionRetByRef(obj, args, paramSize<<2, vftable[size_t(func)>>2], retPointer);
  173. break;
  174. case ICC_CDECL_OBJLAST:
  175. retQW = CallCDeclFunctionQWordObjLast(obj, args, paramSize<<2, (size_t)func);
  176. break;
  177. case ICC_CDECL_OBJLAST_RETURNINMEM:
  178. // Call the system object method as a cdecl with the obj ref as the last parameter
  179. retQW = CallCDeclFunctionRetByRefObjLast(obj, args, paramSize<<2, (size_t)func, retPointer);
  180. break;
  181. case ICC_CDECL_OBJFIRST:
  182. // Call the system object method as a cdecl with the obj ref as the first parameter
  183. retQW = CallCDeclFunctionQWordObjFirst(obj, args, paramSize<<2, (size_t)func);
  184. break;
  185. case ICC_CDECL_OBJFIRST_RETURNINMEM:
  186. // Call the system object method as a cdecl with the obj ref as the first parameter
  187. retQW = CallCDeclFunctionRetByRefObjFirst(obj, args, paramSize<<2, (size_t)func, retPointer);
  188. break;
  189. default:
  190. context->SetInternalException(TXT_INVALID_CALLING_CONVENTION);
  191. }
  192. context->isCallingSystemFunction = false;
  193. // If the return is a float value we need to get the value from the FP register
  194. if( sysFunc->hostReturnFloat )
  195. {
  196. if( sysFunc->hostReturnSize == 1 )
  197. *(asDWORD*)&retQW = GetReturnedFloat();
  198. else
  199. retQW = GetReturnedDouble();
  200. }
  201. return retQW;
  202. }
  203. // On GCC we need to prevent the compiler from inlining these assembler routines when
  204. // optimizing for speed (-O3), as the loop labels get duplicated which cause compile errors.
  205. #ifdef __GNUC__
  206. #define NOINLINE __attribute ((__noinline__))
  207. #else
  208. #define NOINLINE
  209. #endif
  210. void NOINLINE CallCDeclFunction(const asDWORD *args, int paramSize, size_t func)
  211. {
  212. #if defined ASM_INTEL
  213. // Copy the data to the real stack. If we fail to do
  214. // this we may run into trouble in case of exceptions.
  215. __asm
  216. {
  217. // We must save registers that are used
  218. push ecx
  219. // Clear the FPU stack, in case the called function doesn't do it by itself
  220. CLEAR_FPU_STACK
  221. // Copy arguments from script
  222. // stack to application stack
  223. mov ecx, paramSize
  224. mov eax, args
  225. add eax, ecx
  226. cmp ecx, 0
  227. je endcopy
  228. copyloop:
  229. sub eax, 4
  230. push dword ptr [eax]
  231. sub ecx, 4
  232. jne copyloop
  233. endcopy:
  234. // Call function
  235. call [func]
  236. // Pop arguments from stack
  237. add esp, paramSize
  238. // Restore registers
  239. pop ecx
  240. // return value in EAX or EAX:EDX
  241. }
  242. #elif defined ASM_AT_N_T
  243. UNUSED_VAR(args);
  244. UNUSED_VAR(paramSize);
  245. UNUSED_VAR(func);
  246. asm("pushl %ecx \n"
  247. _S(CLEAR_FPU_STACK) "\n"
  248. // Need to align the stack pointer so that it is aligned to 16 bytes when making the function call.
  249. // It is assumed that when entering this function, the stack pointer is already aligned, so we need
  250. // to calculate how much we will put on the stack during this call.
  251. "movl 12(%ebp), %eax \n" // paramSize
  252. "addl $4, %eax \n" // counting esp that we will push on the stack
  253. "movl %esp, %ecx \n"
  254. "subl %eax, %ecx \n"
  255. "andl $15, %ecx \n"
  256. "movl %esp, %eax \n"
  257. "subl %ecx, %esp \n"
  258. "pushl %eax \n" // Store the original stack pointer
  259. "movl 12(%ebp), %ecx \n" // paramSize
  260. "movl 8(%ebp), %eax \n" // args
  261. "addl %ecx, %eax \n" // push arguments on the stack
  262. "cmp $0, %ecx \n"
  263. "je endcopy \n"
  264. "copyloop: \n"
  265. "subl $4, %eax \n"
  266. "pushl (%eax) \n"
  267. "subl $4, %ecx \n"
  268. "jne copyloop \n"
  269. "endcopy: \n"
  270. "call *16(%ebp) \n"
  271. "addl 12(%ebp), %esp \n" // pop arguments
  272. // Pop the alignment bytes
  273. "popl %esp \n"
  274. "popl %ecx \n");
  275. #endif
  276. }
  277. void NOINLINE CallCDeclFunctionObjLast(const void *obj, const asDWORD *args, int paramSize, size_t func)
  278. {
  279. #if defined ASM_INTEL
  280. // Copy the data to the real stack. If we fail to do
  281. // this we may run into trouble in case of exceptions.
  282. __asm
  283. {
  284. // We must save registers that are used
  285. push ecx
  286. // Clear the FPU stack, in case the called function doesn't do it by itself
  287. CLEAR_FPU_STACK
  288. // Push the object pointer as the last argument to the function
  289. push obj
  290. // Copy arguments from script
  291. // stack to application stack
  292. mov ecx, paramSize
  293. mov eax, args
  294. add eax, ecx
  295. cmp ecx, 0
  296. je endcopy
  297. copyloop:
  298. sub eax, 4
  299. push dword ptr [eax]
  300. sub ecx, 4
  301. jne copyloop
  302. endcopy:
  303. // Call function
  304. call [func]
  305. // Pop arguments from stack
  306. add esp, paramSize
  307. add esp, 4
  308. // Restore registers
  309. pop ecx
  310. // return value in EAX or EAX:EDX
  311. }
  312. #elif defined ASM_AT_N_T
  313. UNUSED_VAR(obj);
  314. UNUSED_VAR(args);
  315. UNUSED_VAR(paramSize);
  316. UNUSED_VAR(func);
  317. asm("pushl %ecx \n"
  318. _S(CLEAR_FPU_STACK) "\n"
  319. // Need to align the stack pointer so that it is aligned to 16 bytes when making the function call.
  320. // It is assumed that when entering this function, the stack pointer is already aligned, so we need
  321. // to calculate how much we will put on the stack during this call.
  322. "movl 16(%ebp), %eax \n" // paramSize
  323. "addl $8, %eax \n" // counting esp that we will push on the stack
  324. "movl %esp, %ecx \n"
  325. "subl %eax, %ecx \n"
  326. "andl $15, %ecx \n"
  327. "movl %esp, %eax \n"
  328. "subl %ecx, %esp \n"
  329. "pushl %eax \n" // Store the original stack pointer
  330. "pushl 8(%ebp) \n"
  331. "movl 16(%ebp), %ecx \n" // paramSize
  332. "movl 12(%ebp), %eax \n" // args
  333. "addl %ecx, %eax \n" // push arguments on the stack
  334. "cmp $0, %ecx \n"
  335. "je endcopy8 \n"
  336. "copyloop8: \n"
  337. "subl $4, %eax \n"
  338. "pushl (%eax) \n"
  339. "subl $4, %ecx \n"
  340. "jne copyloop8 \n"
  341. "endcopy8: \n"
  342. "call *20(%ebp) \n"
  343. "addl 16(%ebp), %esp \n" // pop arguments
  344. "addl $4, %esp \n"
  345. // Pop the alignment bytes
  346. "popl %esp \n"
  347. "popl %ecx \n");
  348. #endif
  349. }
  350. void NOINLINE CallCDeclFunctionObjFirst(const void *obj, const asDWORD *args, int paramSize, size_t func)
  351. {
  352. #if defined ASM_INTEL
  353. // Copy the data to the real stack. If we fail to do
  354. // this we may run into trouble in case of exceptions.
  355. __asm
  356. {
  357. // We must save registers that are used
  358. push ecx
  359. // Clear the FPU stack, in case the called function doesn't do it by itself
  360. CLEAR_FPU_STACK
  361. // Copy arguments from script
  362. // stack to application stack
  363. mov ecx, paramSize
  364. mov eax, args
  365. add eax, ecx
  366. cmp ecx, 0
  367. je endcopy
  368. copyloop:
  369. sub eax, 4
  370. push dword ptr [eax]
  371. sub ecx, 4
  372. jne copyloop
  373. endcopy:
  374. // push object as first parameter
  375. push obj
  376. // Call function
  377. call [func]
  378. // Pop arguments from stack
  379. add esp, paramSize
  380. add esp, 4
  381. // Restore registers
  382. pop ecx
  383. // return value in EAX or EAX:EDX
  384. }
  385. #elif defined ASM_AT_N_T
  386. UNUSED_VAR(obj);
  387. UNUSED_VAR(args);
  388. UNUSED_VAR(paramSize);
  389. UNUSED_VAR(func);
  390. asm("pushl %ecx \n"
  391. _S(CLEAR_FPU_STACK) "\n"
  392. // Need to align the stack pointer so that it is aligned to 16 bytes when making the function call.
  393. // It is assumed that when entering this function, the stack pointer is already aligned, so we need
  394. // to calculate how much we will put on the stack during this call.
  395. "movl 16(%ebp), %eax \n" // paramSize
  396. "addl $8, %eax \n" // counting esp that we will push on the stack
  397. "movl %esp, %ecx \n"
  398. "subl %eax, %ecx \n"
  399. "andl $15, %ecx \n"
  400. "movl %esp, %eax \n"
  401. "subl %ecx, %esp \n"
  402. "pushl %eax \n" // Store the original stack pointer
  403. "movl 16(%ebp), %ecx \n" // paramSize
  404. "movl 12(%ebp), %eax \n" // args
  405. "addl %ecx, %eax \n" // push arguments on the stack
  406. "cmp $0, %ecx \n"
  407. "je endcopy6 \n"
  408. "copyloop6: \n"
  409. "subl $4, %eax \n"
  410. "pushl (%eax) \n"
  411. "subl $4, %ecx \n"
  412. "jne copyloop6 \n"
  413. "endcopy6: \n"
  414. "pushl 8(%ebp) \n" // push obj
  415. "call *20(%ebp) \n"
  416. "addl 16(%ebp), %esp \n" // pop arguments
  417. "addl $4, %esp \n"
  418. // Pop the alignment bytes
  419. "popl %esp \n"
  420. "popl %ecx \n");
  421. #endif
  422. }
  423. void NOINLINE CallCDeclFunctionRetByRefObjFirst_impl(const void *obj, const asDWORD *args, int paramSize, size_t func, void *retPtr)
  424. {
  425. #if defined ASM_INTEL
  426. // Copy the data to the real stack. If we fail to do
  427. // this we may run into trouble in case of exceptions.
  428. __asm
  429. {
  430. // We must save registers that are used
  431. push ecx
  432. // Clear the FPU stack, in case the called function doesn't do it by itself
  433. CLEAR_FPU_STACK
  434. // Copy arguments from script
  435. // stack to application stack
  436. mov ecx, paramSize
  437. mov eax, args
  438. add eax, ecx
  439. cmp ecx, 0
  440. je endcopy
  441. copyloop:
  442. sub eax, 4
  443. push dword ptr [eax]
  444. sub ecx, 4
  445. jne copyloop
  446. endcopy:
  447. // Push the object pointer
  448. push obj
  449. // Push the return pointer
  450. push retPtr;
  451. // Call function
  452. call [func]
  453. // Pop arguments from stack
  454. add esp, paramSize
  455. #ifndef CALLEE_POPS_HIDDEN_RETURN_POINTER
  456. // Pop the return pointer
  457. add esp, 8
  458. #else
  459. add esp, 4
  460. #endif
  461. // Restore registers
  462. pop ecx
  463. // return value in EAX or EAX:EDX
  464. }
  465. #elif defined ASM_AT_N_T
  466. UNUSED_VAR(obj);
  467. UNUSED_VAR(args);
  468. UNUSED_VAR(paramSize);
  469. UNUSED_VAR(func);
  470. UNUSED_VAR(retPtr);
  471. asm("pushl %ecx \n"
  472. _S(CLEAR_FPU_STACK) "\n"
  473. // Need to align the stack pointer so that it is aligned to 16 bytes when making the function call.
  474. // It is assumed that when entering this function, the stack pointer is already aligned, so we need
  475. // to calculate how much we will put on the stack during this call.
  476. "movl 16(%ebp), %eax \n" // paramSize
  477. "addl $12, %eax \n" // counting esp that we will push on the stack
  478. "movl %esp, %ecx \n"
  479. "subl %eax, %ecx \n"
  480. "andl $15, %ecx \n"
  481. "movl %esp, %eax \n"
  482. "subl %ecx, %esp \n"
  483. "pushl %eax \n" // Store the original stack pointer
  484. "movl 16(%ebp), %ecx \n" // paramSize
  485. "movl 12(%ebp), %eax \n" // args
  486. "addl %ecx, %eax \n" // push arguments on the stack
  487. "cmp $0, %ecx \n"
  488. "je endcopy5 \n"
  489. "copyloop5: \n"
  490. "subl $4, %eax \n"
  491. "pushl (%eax) \n"
  492. "subl $4, %ecx \n"
  493. "jne copyloop5 \n"
  494. "endcopy5: \n"
  495. "pushl 8(%ebp) \n" // push object first
  496. "pushl 24(%ebp) \n" // retPtr
  497. "call *20(%ebp) \n" // func
  498. "addl 16(%ebp), %esp \n" // pop arguments
  499. #ifndef CALLEE_POPS_HIDDEN_RETURN_POINTER
  500. "addl $8, %esp \n" // Pop the return pointer and object pointer
  501. #else
  502. "addl $4, %esp \n" // Pop the object pointer
  503. #endif
  504. // Pop the alignment bytes
  505. "popl %esp \n"
  506. "popl %ecx \n");
  507. #endif
  508. }
  509. void NOINLINE CallCDeclFunctionRetByRef_impl(const asDWORD *args, int paramSize, size_t func, void *retPtr)
  510. {
  511. #if defined ASM_INTEL
  512. // Copy the data to the real stack. If we fail to do
  513. // this we may run into trouble in case of exceptions.
  514. __asm
  515. {
  516. // We must save registers that are used
  517. push ecx
  518. // Clear the FPU stack, in case the called function doesn't do it by itself
  519. CLEAR_FPU_STACK
  520. // Copy arguments from script
  521. // stack to application stack
  522. mov ecx, paramSize
  523. mov eax, args
  524. add eax, ecx
  525. cmp ecx, 0
  526. je endcopy
  527. copyloop:
  528. sub eax, 4
  529. push dword ptr [eax]
  530. sub ecx, 4
  531. jne copyloop
  532. endcopy:
  533. // Push the return pointer
  534. push retPtr;
  535. // Call function
  536. call [func]
  537. // Pop arguments from stack
  538. add esp, paramSize
  539. #ifndef CALLEE_POPS_HIDDEN_RETURN_POINTER
  540. // Pop the return pointer
  541. add esp, 4
  542. #endif
  543. // Restore registers
  544. pop ecx
  545. // return value in EAX or EAX:EDX
  546. }
  547. #elif defined ASM_AT_N_T
  548. UNUSED_VAR(args);
  549. UNUSED_VAR(paramSize);
  550. UNUSED_VAR(func);
  551. UNUSED_VAR(retPtr);
  552. asm("pushl %ecx \n"
  553. _S(CLEAR_FPU_STACK) "\n"
  554. // Need to align the stack pointer so that it is aligned to 16 bytes when making the function call.
  555. // It is assumed that when entering this function, the stack pointer is already aligned, so we need
  556. // to calculate how much we will put on the stack during this call.
  557. "movl 12(%ebp), %eax \n" // paramSize
  558. "addl $8, %eax \n" // counting esp that we will push on the stack
  559. "movl %esp, %ecx \n"
  560. "subl %eax, %ecx \n"
  561. "andl $15, %ecx \n"
  562. "movl %esp, %eax \n"
  563. "subl %ecx, %esp \n"
  564. "pushl %eax \n" // Store the original stack pointer
  565. "movl 12(%ebp), %ecx \n" // paramSize
  566. "movl 8(%ebp), %eax \n" // args
  567. "addl %ecx, %eax \n" // push arguments on the stack
  568. "cmp $0, %ecx \n"
  569. "je endcopy7 \n"
  570. "copyloop7: \n"
  571. "subl $4, %eax \n"
  572. "pushl (%eax) \n"
  573. "subl $4, %ecx \n"
  574. "jne copyloop7 \n"
  575. "endcopy7: \n"
  576. "pushl 20(%ebp) \n" // retPtr
  577. "call *16(%ebp) \n" // func
  578. "addl 12(%ebp), %esp \n" // pop arguments
  579. #ifndef CALLEE_POPS_HIDDEN_RETURN_POINTER
  580. "addl $4, %esp \n" // Pop the return pointer
  581. #endif
  582. // Pop the alignment bytes
  583. "popl %esp \n"
  584. "popl %ecx \n");
  585. #endif
  586. }
  587. void NOINLINE CallCDeclFunctionRetByRefObjLast_impl(const void *obj, const asDWORD *args, int paramSize, size_t func, void *retPtr)
  588. {
  589. #if defined ASM_INTEL
  590. // Copy the data to the real stack. If we fail to do
  591. // this we may run into trouble in case of exceptions.
  592. __asm
  593. {
  594. // We must save registers that are used
  595. push ecx
  596. // Clear the FPU stack, in case the called function doesn't do it by itself
  597. CLEAR_FPU_STACK
  598. push obj
  599. // Copy arguments from script
  600. // stack to application stack
  601. mov ecx, paramSize
  602. mov eax, args
  603. add eax, ecx
  604. cmp ecx, 0
  605. je endcopy
  606. copyloop:
  607. sub eax, 4
  608. push dword ptr [eax]
  609. sub ecx, 4
  610. jne copyloop
  611. endcopy:
  612. // Push the return pointer
  613. push retPtr;
  614. // Call function
  615. call [func]
  616. // Pop arguments from stack
  617. add esp, paramSize
  618. add esp, 4
  619. #ifndef CALLEE_POPS_HIDDEN_RETURN_POINTER
  620. // Pop the return pointer
  621. add esp, 4
  622. #endif
  623. // Restore registers
  624. pop ecx
  625. // return value in EAX or EAX:EDX
  626. }
  627. #elif defined ASM_AT_N_T
  628. UNUSED_VAR(obj);
  629. UNUSED_VAR(args);
  630. UNUSED_VAR(paramSize);
  631. UNUSED_VAR(func);
  632. UNUSED_VAR(retPtr);
  633. asm("pushl %ecx \n"
  634. _S(CLEAR_FPU_STACK) "\n"
  635. // Need to align the stack pointer so that it is aligned to 16 bytes when making the function call.
  636. // It is assumed that when entering this function, the stack pointer is already aligned, so we need
  637. // to calculate how much we will put on the stack during this call.
  638. "movl 16(%ebp), %eax \n" // paramSize
  639. "addl $12, %eax \n" // counting esp that we will push on the stack
  640. "movl %esp, %ecx \n"
  641. "subl %eax, %ecx \n"
  642. "andl $15, %ecx \n"
  643. "movl %esp, %eax \n"
  644. "subl %ecx, %esp \n"
  645. "pushl %eax \n" // Store the original stack pointer
  646. "pushl 8(%ebp) \n"
  647. "movl 16(%ebp), %ecx \n" // paramSize
  648. "movl 12(%ebp), %eax \n" // args
  649. "addl %ecx, %eax \n" // push arguments on the stack
  650. "cmp $0, %ecx \n"
  651. "je endcopy4 \n"
  652. "copyloop4: \n"
  653. "subl $4, %eax \n"
  654. "pushl (%eax) \n"
  655. "subl $4, %ecx \n"
  656. "jne copyloop4 \n"
  657. "endcopy4: \n"
  658. "pushl 24(%ebp) \n" // retPtr
  659. "call *20(%ebp) \n" // func
  660. "addl 16(%ebp), %esp \n" // pop arguments
  661. #ifndef CALLEE_POPS_HIDDEN_RETURN_POINTER
  662. "addl $8, %esp \n" // Pop the return pointer
  663. #else
  664. "addl $4, %esp \n" // Pop the return pointer
  665. #endif
  666. // Pop the alignment bytes
  667. "popl %esp \n"
  668. "popl %ecx \n");
  669. #endif
  670. }
  671. void NOINLINE CallSTDCallFunction(const asDWORD *args, int paramSize, size_t func)
  672. {
  673. #if defined ASM_INTEL
  674. // Copy the data to the real stack. If we fail to do
  675. // this we may run into trouble in case of exceptions.
  676. __asm
  677. {
  678. // We must save registers that are used
  679. push ecx
  680. // Clear the FPU stack, in case the called function doesn't do it by itself
  681. CLEAR_FPU_STACK
  682. // Copy arguments from script
  683. // stack to application stack
  684. mov ecx, paramSize
  685. mov eax, args
  686. add eax, ecx
  687. cmp ecx, 0
  688. je endcopy
  689. copyloop:
  690. sub eax, 4
  691. push dword ptr [eax]
  692. sub ecx, 4
  693. jne copyloop
  694. endcopy:
  695. // Call function
  696. call [func]
  697. // The callee already removed parameters from the stack
  698. // Restore registers
  699. pop ecx
  700. // return value in EAX or EAX:EDX
  701. }
  702. #elif defined ASM_AT_N_T
  703. UNUSED_VAR(args);
  704. UNUSED_VAR(paramSize);
  705. UNUSED_VAR(func);
  706. asm("pushl %ecx \n"
  707. _S(CLEAR_FPU_STACK) "\n"
  708. // Need to align the stack pointer so that it is aligned to 16 bytes when making the function call.
  709. // It is assumed that when entering this function, the stack pointer is already aligned, so we need
  710. // to calculate how much we will put on the stack during this call.
  711. "movl 12(%ebp), %eax \n" // paramSize
  712. "addl $4, %eax \n" // counting esp that we will push on the stack
  713. "movl %esp, %ecx \n"
  714. "subl %eax, %ecx \n"
  715. "andl $15, %ecx \n"
  716. "movl %esp, %eax \n"
  717. "subl %ecx, %esp \n"
  718. "pushl %eax \n" // Store the original stack pointer
  719. "movl 12(%ebp), %ecx \n" // paramSize
  720. "movl 8(%ebp), %eax \n" // args
  721. "addl %ecx, %eax \n" // push arguments on the stack
  722. "cmp $0, %ecx \n"
  723. "je endcopy2 \n"
  724. "copyloop2: \n"
  725. "subl $4, %eax \n"
  726. "pushl (%eax) \n"
  727. "subl $4, %ecx \n"
  728. "jne copyloop2 \n"
  729. "endcopy2: \n"
  730. "call *16(%ebp) \n" // callee pops the arguments
  731. // Pop the alignment bytes
  732. "popl %esp \n"
  733. "popl %ecx \n");
  734. #endif
  735. }
  736. void NOINLINE CallThisCallFunction(const void *obj, const asDWORD *args, int paramSize, size_t func)
  737. {
  738. #if defined ASM_INTEL
  739. // Copy the data to the real stack. If we fail to do
  740. // this we may run into trouble in case of exceptions.
  741. __asm
  742. {
  743. // We must save registers that are used
  744. push ecx
  745. // Clear the FPU stack, in case the called function doesn't do it by itself
  746. CLEAR_FPU_STACK
  747. // Copy arguments from script
  748. // stack to application stack
  749. mov ecx, paramSize
  750. mov eax, args
  751. add eax, ecx
  752. cmp ecx, 0
  753. je endcopy
  754. copyloop:
  755. sub eax, 4
  756. push dword ptr [eax]
  757. sub ecx, 4
  758. jne copyloop
  759. endcopy:
  760. #ifdef THISCALL_PASS_OBJECT_POINTER_ON_THE_STACK
  761. // Push the object pointer on the stack
  762. push obj
  763. #else
  764. // Move object pointer to ECX
  765. mov ecx, obj
  766. #endif
  767. // Call function
  768. call [func]
  769. #ifndef THISCALL_CALLEE_POPS_ARGUMENTS
  770. // Pop arguments
  771. add esp, paramSize
  772. #ifdef THISCALL_PASS_OBJECT_POINTER_ON_THE_STACK
  773. // Pop object pointer
  774. add esp, 4
  775. #endif
  776. #endif
  777. // Restore registers
  778. pop ecx
  779. // Return value in EAX or EAX:EDX
  780. }
  781. #elif defined ASM_AT_N_T
  782. UNUSED_VAR(obj);
  783. UNUSED_VAR(args);
  784. UNUSED_VAR(paramSize);
  785. UNUSED_VAR(func);
  786. asm("pushl %ecx \n"
  787. _S(CLEAR_FPU_STACK) "\n"
  788. // Need to align the stack pointer so that it is aligned to 16 bytes when making the function call.
  789. // It is assumed that when entering this function, the stack pointer is already aligned, so we need
  790. // to calculate how much we will put on the stack during this call.
  791. "movl 16(%ebp), %eax \n" // paramSize
  792. "addl $8, %eax \n" // counting esp that we will push on the stack
  793. "movl %esp, %ecx \n"
  794. "subl %eax, %ecx \n"
  795. "andl $15, %ecx \n"
  796. "movl %esp, %eax \n"
  797. "subl %ecx, %esp \n"
  798. "pushl %eax \n" // Store the original stack pointer
  799. "movl 16(%ebp), %ecx \n" // paramSize
  800. "movl 12(%ebp), %eax \n" // args
  801. "addl %ecx, %eax \n" // push all arguments on the stack
  802. "cmp $0, %ecx \n"
  803. "je endcopy1 \n"
  804. "copyloop1: \n"
  805. "subl $4, %eax \n"
  806. "pushl (%eax) \n"
  807. "subl $4, %ecx \n"
  808. "jne copyloop1 \n"
  809. "endcopy1: \n"
  810. "movl 8(%ebp), %ecx \n" // move obj into ECX
  811. "pushl 8(%ebp) \n" // push obj on the stack
  812. "call *20(%ebp) \n"
  813. "addl 16(%ebp), %esp \n" // pop arguments
  814. "addl $4, %esp \n" // pop obj
  815. // Pop the alignment bytes
  816. "popl %esp \n"
  817. "popl %ecx \n");
  818. #endif
  819. }
  820. void NOINLINE CallThisCallFunctionRetByRef_impl(const void *obj, const asDWORD *args, int paramSize, size_t func, void *retPtr)
  821. {
  822. #if defined ASM_INTEL
  823. // Copy the data to the real stack. If we fail to do
  824. // this we may run into trouble in case of exceptions.
  825. __asm
  826. {
  827. // We must save registers that are used
  828. push ecx
  829. // Clear the FPU stack, in case the called function doesn't do it by itself
  830. CLEAR_FPU_STACK
  831. // Copy arguments from script
  832. // stack to application stack
  833. mov ecx, paramSize
  834. mov eax, args
  835. add eax, ecx
  836. cmp ecx, 0
  837. je endcopy
  838. copyloop:
  839. sub eax, 4
  840. push dword ptr [eax]
  841. sub ecx, 4
  842. jne copyloop
  843. endcopy:
  844. #ifdef THISCALL_PASS_OBJECT_POINTER_ON_THE_STACK
  845. // Push the object pointer on the stack
  846. push obj
  847. #else
  848. // Move object pointer to ECX
  849. mov ecx, obj
  850. #endif
  851. // Push the return pointer
  852. push retPtr
  853. // Call function
  854. call [func]
  855. #ifndef THISCALL_CALLEE_POPS_HIDDEN_RETURN_POINTER
  856. // Pop the return pointer
  857. add esp, 4
  858. #endif
  859. #ifndef THISCALL_CALLEE_POPS_ARGUMENTS
  860. // Pop arguments
  861. add esp, paramSize
  862. #ifdef THISCALL_PASS_OBJECT_POINTER_ON_THE_STACK
  863. // Pop object pointer
  864. add esp, 4
  865. #endif
  866. #endif
  867. // Restore registers
  868. pop ecx
  869. // Return value in EAX or EAX:EDX
  870. }
  871. #elif defined ASM_AT_N_T
  872. UNUSED_VAR(obj);
  873. UNUSED_VAR(args);
  874. UNUSED_VAR(paramSize);
  875. UNUSED_VAR(func);
  876. UNUSED_VAR(retPtr);
  877. asm("pushl %ecx \n"
  878. _S(CLEAR_FPU_STACK) "\n"
  879. // Need to align the stack pointer so that it is aligned to 16 bytes when making the function call.
  880. // It is assumed that when entering this function, the stack pointer is already aligned, so we need
  881. // to calculate how much we will put on the stack during this call.
  882. "movl 16(%ebp), %eax \n" // paramSize
  883. "addl $12, %eax \n" // counting esp that we will push on the stack
  884. "movl %esp, %ecx \n"
  885. "subl %eax, %ecx \n"
  886. "andl $15, %ecx \n"
  887. "movl %esp, %eax \n"
  888. "subl %ecx, %esp \n"
  889. "pushl %eax \n" // Store the original stack pointer
  890. "movl 16(%ebp), %ecx \n" // paramSize
  891. "movl 12(%ebp), %eax \n" // args
  892. "addl %ecx, %eax \n" // push all arguments to the stack
  893. "cmp $0, %ecx \n"
  894. "je endcopy3 \n"
  895. "copyloop3: \n"
  896. "subl $4, %eax \n"
  897. "pushl (%eax) \n"
  898. "subl $4, %ecx \n"
  899. "jne copyloop3 \n"
  900. "endcopy3: \n"
  901. "movl 8(%ebp), %ecx \n" // move obj into ECX
  902. "pushl 8(%ebp) \n" // push obj on the stack
  903. "pushl 24(%ebp) \n" // push retPtr on the stack
  904. "call *20(%ebp) \n"
  905. #ifndef THISCALL_CALLEE_POPS_HIDDEN_RETURN_POINTER
  906. "addl $4, %esp \n" // pop return pointer
  907. #endif
  908. "addl 16(%ebp), %esp \n" // pop arguments
  909. "addl $4, %esp \n" // pop the object pointer
  910. // the return pointer was popped by the callee
  911. // Pop the alignment bytes
  912. "popl %esp \n"
  913. "popl %ecx \n");
  914. #endif
  915. }
  916. asDWORD GetReturnedFloat()
  917. {
  918. asDWORD f;
  919. #if defined ASM_INTEL
  920. // Get the float value from ST0
  921. __asm fstp dword ptr [f]
  922. #elif defined ASM_AT_N_T
  923. asm("fstps %0 \n" : "=m" (f));
  924. #endif
  925. return f;
  926. }
  927. asQWORD GetReturnedDouble()
  928. {
  929. asQWORD d;
  930. #if defined ASM_INTEL
  931. // Get the double value from ST0
  932. __asm fstp qword ptr [d]
  933. #elif defined ASM_AT_N_T
  934. asm("fstpl %0 \n" : "=m" (d));
  935. #endif
  936. return d;
  937. }
  938. END_AS_NAMESPACE
  939. #endif // AS_X86
  940. #endif // AS_MAX_PORTABILITY