as_callfunc_x86.cpp 35 KB

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