as_callfunc_ppc_64.cpp 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774
  1. /*
  2. AngelCode Scripting Library
  3. Copyright (c) 2003-2016 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. // as_callfunc_ppc_64.cpp
  25. //
  26. // These functions handle the actual calling of system functions
  27. //
  28. // This version is 64 bit PPC specific
  29. //
  30. #include "as_config.h"
  31. #ifndef AS_MAX_PORTABILITY
  32. #ifdef AS_PPC_64
  33. #if AS_PTR_SIZE == 2
  34. // TODO: Add support for PPC 64bit platforms with 64bit pointers, for example Linux PPC64 (big endian) and PPC64 (little endian)
  35. #error This code has not been prepared for PPC with 64bit pointers. Most likely the ABI is different
  36. #else
  37. #include "as_callfunc.h"
  38. #include "as_scriptengine.h"
  39. #include "as_texts.h"
  40. #include "as_tokendef.h"
  41. #include "as_context.h"
  42. #include <stdio.h>
  43. #include <stdlib.h>
  44. #ifdef __SNC__
  45. #include "ppu_asm_intrinsics.h"
  46. #endif
  47. BEGIN_AS_NAMESPACE
  48. // This part was written and tested by Jeff Slutter
  49. // from Reactor Zero, Abril, 2007, for PlayStation 3, which
  50. // is a PowerPC 64bit based architecture. Even though it is
  51. // 64bit it seems the pointer size is still 32bit.
  52. // It still remains to be seen how well this code works
  53. // on other PPC platforms, such as XBox 360, GameCube.
  54. #define AS_PPC_MAX_ARGS 32
  55. // The array used to send values to the correct places.
  56. // Contains a byte of argTypes to indicate the register type to load
  57. // or zero if end of arguments
  58. // The +1 is for when CallThis (object methods) is used
  59. // Extra +1 when returning in memory
  60. // Extra +1 in ppcArgsType to ensure zero end-of-args marker
  61. // TODO: multithread: The global variables must be removed to make the code thread safe
  62. extern "C"
  63. {
  64. enum argTypes { ppcENDARG = 0, ppcINTARG = 1, ppcFLOATARG = 2, ppcDOUBLEARG = 3, ppcLONGARG = 4 };
  65. static asBYTE ppcArgsType[AS_PPC_MAX_ARGS + 1 + 1 + 1];
  66. static asDWORD ppcArgs[2*AS_PPC_MAX_ARGS + 1 + 1];
  67. }
  68. // NOTE: these values are for PowerPC 64 bit. I'm sure things are different for PowerPC 32bit, but I don't have one.
  69. // I'm pretty sure that PPC 32bit sets up a stack frame slightly different (only 24 bytes for linkage area for instance)
  70. #define PPC_LINKAGE_SIZE (0x30) // how big the PPC linkage area is in a stack frame
  71. #define PPC_NUM_REGSTORE (10) // how many registers of the PPC we need to store/restore for ppcFunc64()
  72. #define PPC_REGSTORE_SIZE (8*PPC_NUM_REGSTORE) // how many bytes are required for register store/restore
  73. #define EXTRA_STACK_SIZE (PPC_LINKAGE_SIZE + PPC_REGSTORE_SIZE) // memory required, not including parameters, for the stack frame
  74. #define PPC_STACK_SIZE(numParams) ( -(( ( (((numParams)<8)?8:(numParams))<<3) + EXTRA_STACK_SIZE + 15 ) & ~15) ) // calculates the total stack size needed for ppcFunc64, must pad to 16bytes
  75. // This is PowerPC 64 bit specific
  76. // Loads all data into the correct places and calls the function.
  77. // ppcArgsType is an array containing a byte type (enum argTypes) for each argument.
  78. // StackArgSizeInBytes is the size in bytes of the stack frame (takes into account linkage area, etc. must be multiple of 16)
  79. extern "C" asQWORD ppcFunc64(const asDWORD* argsPtr, int StackArgSizeInBytes, asDWORD func);
  80. asm(""
  81. ".text\n"
  82. ".align 4\n"
  83. ".p2align 4,,15\n"
  84. ".globl .ppcFunc64\n"
  85. ".ppcFunc64:\n"
  86. // function prolog
  87. "std %r22, -0x08(%r1)\n" // we need a register other than r0, to store the old stack pointer
  88. "mr %r22, %r1\n" // store the old stack pointer, for now (to make storing registers easier)
  89. "stdux %r1, %r1, %r4\n" // atomically store and update the stack pointer for the new stack frame (in case of a signal/interrupt)
  90. "mflr %r0\n" // get the caller's LR register
  91. "std %r0, 0x10(%r22)\n" // store the caller's LR register
  92. "std %r23, -0x10(%r22)\n" //
  93. "std %r24, -0x18(%r22)\n" //
  94. "std %r25, -0x20(%r22)\n" //
  95. "std %r26, -0x28(%r22)\n" //
  96. "std %r27, -0x30(%r22)\n" //
  97. "std %r28, -0x38(%r22)\n" //
  98. "std %r29, -0x40(%r22)\n" //
  99. "std %r30, -0x48(%r22)\n" //
  100. "std %r31, -0x50(%r22)\n" //
  101. "std %r3, 0x30(%r22)\n" // save our parameters
  102. "std %r4, 0x38(%r22)\n" //
  103. "std %r5, 0x40(%r22)\n" //
  104. "mr %r31, %r1\n" // functions tend to store the stack pointer here too
  105. // initial registers for the function
  106. "mr %r29, %r3\n" // (r29) args list
  107. "lwz %r27, 0(%r5)\n" // load the function pointer to call. func actually holds the pointer to our function
  108. "addi %r26, %r1, 0x30\n" // setup the pointer to the parameter area to the function we're going to call
  109. "sub %r0,%r0,%r0\n" // zero out r0
  110. "mr %r23,%r0\n" // zero out r23, which holds the number of used GPR registers
  111. "mr %r22,%r0\n" // zero our r22, which holds the number of used float registers
  112. // load the global ppcArgsType which holds the types of arguments for each argument
  113. "lis %r25, ppcArgsType@ha\n" // load the upper 16 bits of the address to r25
  114. "addi %r25, %r25, ppcArgsType@l\n" // load the lower 16 bits of the address to r25
  115. "subi %r25, %r25, 1\n" // since we increment r25 on its use, we'll pre-decrement it
  116. // loop through the arguments
  117. "ppcNextArg:\n"
  118. "addi %r25, %r25, 1\n" // increment r25, our arg type pointer
  119. // switch based on the current argument type (0:end, 1:int, 2:float 3:double)
  120. "lbz %r24, 0(%r25)\n" // load the current argument type (it's a byte)
  121. "mulli %r24, %r24, 4\n" // our jump table has 4 bytes per case (1 instruction)
  122. "lis %r30, ppcTypeSwitch@ha\n" // load the address of the jump table for the switch
  123. "addi %r30, %r30, ppcTypeSwitch@l\n"
  124. "add %r0, %r30, %r24\n" // offset by our argument type
  125. "mtctr %r0\n" // load the jump address into CTR
  126. "bctr\n" // jump into the jump table/switch
  127. "nop\n"
  128. // the jump table/switch based on the current argument type
  129. "ppcTypeSwitch:\n"
  130. "b ppcArgsEnd\n"
  131. "b ppcArgIsInteger\n"
  132. "b ppcArgIsFloat\n"
  133. "b ppcArgIsDouble\n"
  134. "b ppcArgIsLong\n"
  135. // when we get here we have finished processing all the arguments
  136. // everything is ready to go to call the function
  137. "ppcArgsEnd:\n"
  138. "mtctr %r27\n" // the function pointer is stored in r27, load that into CTR
  139. "bctrl\n" // call the function. We have to do it this way so that the LR gets the proper
  140. "nop\n" // return value (the next instruction below). So we have to branch from CTR instead of LR.
  141. // when we get here, the function has returned, this is the function epilog
  142. "ld %r11,0x00(%r1)\n" // load in the caller's stack pointer
  143. "ld %r0,0x10(%r11)\n" // load in the caller's LR
  144. "mtlr %r0\n" // restore the caller's LR
  145. "ld %r22, -0x08(%r11)\n" // load registers
  146. "ld %r23, -0x10(%r11)\n" //
  147. "ld %r24, -0x18(%r11)\n" //
  148. "ld %r25, -0x20(%r11)\n" //
  149. "ld %r26, -0x28(%r11)\n" //
  150. "ld %r27, -0x30(%r11)\n" //
  151. "ld %r28, -0x38(%r11)\n" //
  152. "ld %r29, -0x40(%r11)\n" //
  153. "ld %r30, -0x48(%r11)\n" //
  154. "ld %r31, -0x50(%r11)\n" //
  155. "mr %r1, %r11\n" // restore the caller's SP
  156. "blr\n" // return back to the caller
  157. "nop\n"
  158. // Integer argument (GPR register)
  159. "ppcArgIsInteger:\n"
  160. "lis %r30,ppcLoadIntReg@ha\n" // load the address to the jump table for integer registers
  161. "addi %r30, %r30, ppcLoadIntReg@l\n"
  162. "mulli %r0, %r23, 8\n" // each item in the jump table is 2 instructions (8 bytes)
  163. "add %r0, %r0, %r30\n" // calculate ppcLoadIntReg[numUsedGPRRegs]
  164. "lwz %r30,0(%r29)\n" // load the next argument from the argument list into r30
  165. "cmpwi %r23, 8\n" // we can only load GPR3 through GPR10 (8 registers)
  166. "bgt ppcLoadIntRegUpd\n" // if we're beyond 8 GPR registers, we're in the stack, go there
  167. "mtctr %r0\n" // load the address of our ppcLoadIntReg jump table (we're below 8 GPR registers)
  168. "bctr\n" // load the argument into a GPR register
  169. "nop\n"
  170. // jump table for GPR registers, for the first 8 GPR arguments
  171. "ppcLoadIntReg:\n"
  172. "mr %r3,%r30\n" // arg0 (to r3)
  173. "b ppcLoadIntRegUpd\n"
  174. "mr %r4,%r30\n" // arg1 (to r4)
  175. "b ppcLoadIntRegUpd\n"
  176. "mr %r5,%r30\n" // arg2 (to r5)
  177. "b ppcLoadIntRegUpd\n"
  178. "mr %r6,%r30\n" // arg3 (to r6)
  179. "b ppcLoadIntRegUpd\n"
  180. "mr %r7,%r30\n" // arg4 (to r7)
  181. "b ppcLoadIntRegUpd\n"
  182. "mr %r8,%r30\n" // arg5 (to r8)
  183. "b ppcLoadIntRegUpd\n"
  184. "mr %r9,%r30\n" // arg6 (to r9)
  185. "b ppcLoadIntRegUpd\n"
  186. "mr %r10,%r30\n" // arg7 (to r10)
  187. "b ppcLoadIntRegUpd\n"
  188. // all GPR arguments still go on the stack
  189. "ppcLoadIntRegUpd:\n"
  190. "std %r30,0(%r26)\n" // store the argument into the next slot on the stack's argument list
  191. "addi %r23, %r23, 1\n" // count a used GPR register
  192. "addi %r29, %r29, 4\n" // move to the next argument on the list
  193. "addi %r26, %r26, 8\n" // adjust our argument stack pointer for the next
  194. "b ppcNextArg\n" // next argument
  195. // single Float argument
  196. "ppcArgIsFloat:\n"
  197. "lis %r30,ppcLoadFloatReg@ha\n" // get the base address of the float register jump table
  198. "addi %r30, %r30, ppcLoadFloatReg@l\n"
  199. "mulli %r0, %r22 ,8\n" // each jump table entry is 8 bytes
  200. "add %r0, %r0, %r30\n" // calculate the offset to ppcLoadFloatReg[numUsedFloatReg]
  201. "lfs 0, 0(%r29)\n" // load the next argument as a float into f0
  202. "cmpwi %r22, 13\n" // can't load more than 13 float/double registers
  203. "bgt ppcLoadFloatRegUpd\n" // if we're beyond 13 registers, just fall to inserting into the stack
  204. "mtctr %r0\n" // jump into the float jump table
  205. "bctr\n"
  206. "nop\n"
  207. // jump table for float registers, for the first 13 float arguments
  208. "ppcLoadFloatReg:\n"
  209. "fmr 1,0\n" // arg0 (f1)
  210. "b ppcLoadFloatRegUpd\n"
  211. "fmr 2,0\n" // arg1 (f2)
  212. "b ppcLoadFloatRegUpd\n"
  213. "fmr 3,0\n" // arg2 (f3)
  214. "b ppcLoadFloatRegUpd\n"
  215. "fmr 4,0\n" // arg3 (f4)
  216. "b ppcLoadFloatRegUpd\n"
  217. "fmr 5,0\n" // arg4 (f5)
  218. "b ppcLoadFloatRegUpd\n"
  219. "fmr 6,0\n" // arg5 (f6)
  220. "b ppcLoadFloatRegUpd\n"
  221. "fmr 7,0\n" // arg6 (f7)
  222. "b ppcLoadFloatRegUpd\n"
  223. "fmr 8,0\n" // arg7 (f8)
  224. "b ppcLoadFloatRegUpd\n"
  225. "fmr 9,0\n" // arg8 (f9)
  226. "b ppcLoadFloatRegUpd\n"
  227. "fmr 10,0\n" // arg9 (f10)
  228. "b ppcLoadFloatRegUpd\n"
  229. "fmr 11,0\n" // arg10 (f11)
  230. "b ppcLoadFloatRegUpd\n"
  231. "fmr 12,0\n" // arg11 (f12)
  232. "b ppcLoadFloatRegUpd\n"
  233. "fmr 13,0\n" // arg12 (f13)
  234. "b ppcLoadFloatRegUpd\n"
  235. "nop\n"
  236. // all float arguments still go on the stack
  237. "ppcLoadFloatRegUpd:\n"
  238. "stfs 0, 0x04(%r26)\n" // store, as a single float, f0 (current argument) on to the stack argument list
  239. "addi %r23, %r23, 1\n" // a float register eats up a GPR register
  240. "addi %r22, %r22, 1\n" // ...and, of course, a float register
  241. "addi %r29, %r29, 4\n" // move to the next argument in the list
  242. "addi %r26, %r26, 8\n" // move to the next stack slot
  243. "b ppcNextArg\n" // on to the next argument
  244. "nop\n"
  245. // double Float argument
  246. "ppcArgIsDouble:\n"
  247. "lis %r30, ppcLoadDoubleReg@ha\n" // load the base address of the jump table for double registers
  248. "addi %r30, %r30, ppcLoadDoubleReg@l\n"
  249. "mulli %r0, %r22, 8\n" // each slot of the jump table is 8 bytes
  250. "add %r0, %r0, %r30\n" // calculate ppcLoadDoubleReg[numUsedFloatReg]
  251. "lfd 0, 0(%r29)\n" // load the next argument, as a double float, into f0
  252. "cmpwi %r22,13\n" // the first 13 floats must go into float registers also
  253. "bgt ppcLoadDoubleRegUpd\n" // if we're beyond 13, then just put on to the stack
  254. "mtctr %r0\n" // we're under 13, first load our register
  255. "bctr\n" // jump into the jump table
  256. "nop\n"
  257. // jump table for float registers, for the first 13 float arguments
  258. "ppcLoadDoubleReg:\n"
  259. "fmr 1,0\n" // arg0 (f1)
  260. "b ppcLoadDoubleRegUpd\n"
  261. "fmr 2,0\n" // arg1 (f2)
  262. "b ppcLoadDoubleRegUpd\n"
  263. "fmr 3,0\n" // arg2 (f3)
  264. "b ppcLoadDoubleRegUpd\n"
  265. "fmr 4,0\n" // arg3 (f4)
  266. "b ppcLoadDoubleRegUpd\n"
  267. "fmr 5,0\n" // arg4 (f5)
  268. "b ppcLoadDoubleRegUpd\n"
  269. "fmr 6,0\n" // arg5 (f6)
  270. "b ppcLoadDoubleRegUpd\n"
  271. "fmr 7,0\n" // arg6 (f7)
  272. "b ppcLoadDoubleRegUpd\n"
  273. "fmr 8,0\n" // arg7 (f8)
  274. "b ppcLoadDoubleRegUpd\n"
  275. "fmr 9,0\n" // arg8 (f9)
  276. "b ppcLoadDoubleRegUpd\n"
  277. "fmr 10,0\n" // arg9 (f10)
  278. "b ppcLoadDoubleRegUpd\n"
  279. "fmr 11,0\n" // arg10 (f11)
  280. "b ppcLoadDoubleRegUpd\n"
  281. "fmr 12,0\n" // arg11 (f12)
  282. "b ppcLoadDoubleRegUpd\n"
  283. "fmr 13,0\n" // arg12 (f13)
  284. "b ppcLoadDoubleRegUpd\n"
  285. "nop\n"
  286. // all float arguments still go on the stack
  287. "ppcLoadDoubleRegUpd:\n"
  288. "stfd 0,0(%r26)\n" // store f0, as a double, into the argument list on the stack
  289. "addi %r23, %r23, 1\n" // a double float eats up one GPR
  290. "addi %r22, %r22, 1\n" // ...and, of course, a float
  291. "addi %r29, %r29, 8\n" // increment to our next argument we need to process (8 bytes for the 64bit float)
  292. "addi %r26, %r26, 8\n" // increment to the next slot on the argument list on the stack (8 bytes)
  293. "b ppcNextArg\n" // on to the next argument
  294. "nop\n"
  295. // Long (64 bit int) argument
  296. "ppcArgIsLong:\n"
  297. "lis %r30,ppcLoadLongReg@ha\n" // load the address to the jump table for integer64
  298. "addi %r30, %r30, ppcLoadLongReg@l\n"
  299. "mulli %r0, %r23, 8\n" // each item in the jump table is 2 instructions (8 bytes)
  300. "add %r0, %r0, %r30\n" // calculate ppcLoadLongReg[numUsedGPRRegs]
  301. "ld %r30,0(%r29)\n" // load the next argument from the argument list into r30
  302. "cmpwi %r23, 8\n" // we can only load GPR3 through GPR10 (8 registers)
  303. "bgt ppcLoadLongRegUpd\n" // if we're beyond 8 GPR registers, we're in the stack, go there
  304. "mtctr %r0\n" // load the address of our ppcLoadLongReg jump table (we're below 8 GPR registers)
  305. "bctr\n" // load the argument into a GPR register
  306. "nop\n"
  307. // jump table for GPR registers, for the first 8 GPR arguments
  308. "ppcLoadLongReg:\n"
  309. "mr %r3,%r30\n" // arg0 (to r3)
  310. "b ppcLoadLongRegUpd\n"
  311. "mr %r4,%r30\n" // arg1 (to r4)
  312. "b ppcLoadLongRegUpd\n"
  313. "mr %r5,%r30\n" // arg2 (to r5)
  314. "b ppcLoadLongRegUpd\n"
  315. "mr %r6,%r30\n" // arg3 (to r6)
  316. "b ppcLoadLongRegUpd\n"
  317. "mr %r7,%r30\n" // arg4 (to r7)
  318. "b ppcLoadLongRegUpd\n"
  319. "mr %r8,%r30\n" // arg5 (to r8)
  320. "b ppcLoadLongRegUpd\n"
  321. "mr %r9,%r30\n" // arg6 (to r9)
  322. "b ppcLoadLongRegUpd\n"
  323. "mr %r10,%r30\n" // arg7 (to r10)
  324. "b ppcLoadLongRegUpd\n"
  325. // all GPR arguments still go on the stack
  326. "ppcLoadLongRegUpd:\n"
  327. "std %r30,0(%r26)\n" // store the argument into the next slot on the stack's argument list
  328. "addi %r23, %r23, 1\n" // count a used GPR register
  329. "addi %r29, %r29, 8\n" // move to the next argument on the list
  330. "addi %r26, %r26, 8\n" // adjust our argument stack pointer for the next
  331. "b ppcNextArg\n" // next argument
  332. );
  333. static asDWORD GetReturnedFloat(void)
  334. {
  335. asDWORD f;
  336. #ifdef __SNC__
  337. __stfs( __freg(1), 0, (void*)&f);
  338. #else
  339. asm(" stfs 1, %0\n" : "=m"(f));
  340. #endif
  341. return f;
  342. }
  343. static asQWORD GetReturnedDouble(void)
  344. {
  345. asQWORD f;
  346. #ifdef __SNC__
  347. __stfd( __freg(1), 0, (void*)&f);
  348. #else
  349. asm(" stfd 1, %0\n" : "=m"(f));
  350. #endif
  351. return f;
  352. }
  353. // puts the arguments in the correct place in the stack array. See comments above.
  354. static void stackArgs( const asDWORD *args, const asBYTE *argsType, int &numIntArgs, int &numFloatArgs, int &numDoubleArgs, int &numLongArgs )
  355. {
  356. // initialize our offset based on any already placed arguments
  357. int i;
  358. int argWordPos = numIntArgs + numFloatArgs + (numDoubleArgs*2) + (numLongArgs*2);
  359. int typeOffset = numIntArgs + numFloatArgs + numDoubleArgs + numLongArgs;
  360. int typeIndex;
  361. for( i = 0, typeIndex = 0; ; i++, typeIndex++ )
  362. {
  363. // store the type
  364. ppcArgsType[typeOffset++] = argsType[typeIndex];
  365. if( argsType[typeIndex] == ppcENDARG )
  366. break;
  367. switch( argsType[typeIndex] )
  368. {
  369. case ppcFLOATARG:
  370. {
  371. // stow float
  372. ppcArgs[argWordPos] = args[i]; // it's just a bit copy
  373. numFloatArgs++;
  374. argWordPos++; //add one word
  375. }
  376. break;
  377. case ppcDOUBLEARG:
  378. {
  379. // stow double
  380. memcpy( &ppcArgs[argWordPos], &args[i], sizeof(double) ); // we have to do this because of alignment
  381. numDoubleArgs++;
  382. argWordPos+=2; //add two words
  383. i++;//doubles take up 2 argument slots
  384. }
  385. break;
  386. case ppcINTARG:
  387. {
  388. // stow register
  389. ppcArgs[argWordPos] = args[i];
  390. numIntArgs++;
  391. argWordPos++;
  392. }
  393. break;
  394. case ppcLONGARG:
  395. {
  396. // stow long
  397. memcpy( &ppcArgs[argWordPos], &args[i], 8 ); // for alignment purposes, we use memcpy
  398. numLongArgs++;
  399. argWordPos += 2; // add two words
  400. i++; // longs take up 2 argument slots
  401. }
  402. break;
  403. }
  404. }
  405. // close off the argument list (if we have max args we won't close it off until here)
  406. ppcArgsType[typeOffset] = ppcENDARG;
  407. }
  408. static asQWORD CallCDeclFunction(const asDWORD* pArgs, const asBYTE *pArgsType, int argSize, asDWORD func, void *retInMemory)
  409. {
  410. int baseArgCount = 0;
  411. if( retInMemory )
  412. {
  413. // the first argument is the 'return in memory' pointer
  414. ppcArgs[0] = (asDWORD)retInMemory;
  415. ppcArgsType[0] = ppcINTARG;
  416. ppcArgsType[1] = ppcENDARG;
  417. baseArgCount = 1;
  418. }
  419. // put the arguments in the correct places in the ppcArgs array
  420. int numTotalArgs = baseArgCount;
  421. if( argSize > 0 )
  422. {
  423. int intArgs = baseArgCount, floatArgs = 0, doubleArgs = 0, longArgs = 0;
  424. stackArgs( pArgs, pArgsType, intArgs, floatArgs, doubleArgs, longArgs );
  425. numTotalArgs = intArgs + floatArgs + doubleArgs + longArgs;
  426. }
  427. else
  428. {
  429. // no arguments, cap the type list
  430. ppcArgsType[baseArgCount] = ppcENDARG;
  431. }
  432. // call the function with the arguments
  433. return ppcFunc64( ppcArgs, PPC_STACK_SIZE(numTotalArgs), func );
  434. }
  435. // This function is identical to CallCDeclFunction, with the only difference that
  436. // the value in the first parameter is the object (unless we are returning in memory)
  437. static asQWORD CallThisCallFunction(const void *obj, const asDWORD* pArgs, const asBYTE *pArgsType, int argSize, asDWORD func, void *retInMemory )
  438. {
  439. int baseArgCount = 0;
  440. if( retInMemory )
  441. {
  442. // the first argument is the 'return in memory' pointer
  443. ppcArgs[0] = (asDWORD)retInMemory;
  444. ppcArgsType[0] = ppcINTARG;
  445. ppcArgsType[1] = ppcENDARG;
  446. baseArgCount = 1;
  447. }
  448. // the first argument is the 'this' of the object
  449. ppcArgs[baseArgCount] = (asDWORD)obj;
  450. ppcArgsType[baseArgCount++] = ppcINTARG;
  451. ppcArgsType[baseArgCount] = ppcENDARG;
  452. // put the arguments in the correct places in the ppcArgs array
  453. int numTotalArgs = baseArgCount;
  454. if( argSize > 0 )
  455. {
  456. int intArgs = baseArgCount, floatArgs = 0, doubleArgs = 0, longArgs = 0;
  457. stackArgs( pArgs, pArgsType, intArgs, floatArgs, doubleArgs, longArgs );
  458. numTotalArgs = intArgs + floatArgs + doubleArgs + longArgs;
  459. }
  460. // call the function with the arguments
  461. return ppcFunc64( ppcArgs, PPC_STACK_SIZE(numTotalArgs), func);
  462. }
  463. // This function is identical to CallCDeclFunction, with the only difference that
  464. // the value in the last parameter is the object
  465. // NOTE: on PPC the order for the args is reversed
  466. static asQWORD CallThisCallFunction_objLast(const void *obj, const asDWORD* pArgs, const asBYTE *pArgsType, int argSize, asDWORD func, void *retInMemory)
  467. {
  468. UNUSED_VAR(argSize);
  469. int baseArgCount = 0;
  470. if( retInMemory )
  471. {
  472. // the first argument is the 'return in memory' pointer
  473. ppcArgs[0] = (asDWORD)retInMemory;
  474. ppcArgsType[0] = ppcINTARG;
  475. ppcArgsType[1] = ppcENDARG;
  476. baseArgCount = 1;
  477. }
  478. // stack any of the arguments
  479. int intArgs = baseArgCount, floatArgs = 0, doubleArgs = 0, longArgs = 0;
  480. stackArgs( pArgs, pArgsType, intArgs, floatArgs, doubleArgs, longArgs );
  481. int numTotalArgs = intArgs + floatArgs + doubleArgs;
  482. // can we fit the object in at the end?
  483. if( numTotalArgs < AS_PPC_MAX_ARGS )
  484. {
  485. // put the object pointer at the end
  486. int argPos = intArgs + floatArgs + (doubleArgs * 2) + (longArgs *2);
  487. ppcArgs[argPos] = (asDWORD)obj;
  488. ppcArgsType[numTotalArgs++] = ppcINTARG;
  489. ppcArgsType[numTotalArgs] = ppcENDARG;
  490. }
  491. // call the function with the arguments
  492. return ppcFunc64( ppcArgs, PPC_STACK_SIZE(numTotalArgs), func );
  493. }
  494. // returns true if the given parameter is a 'variable argument'
  495. inline bool IsVariableArgument( asCDataType type )
  496. {
  497. return (type.GetTokenType() == ttQuestion) ? true : false;
  498. }
  499. asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, void *obj, asDWORD *args, void *retPointer, asQWORD &/*retQW2*/, void */*secondObject*/)
  500. {
  501. // TODO: PPC 64 does not yet support THISCALL_OBJFIRST/LAST
  502. // use a working array of types, we'll configure the final one in stackArgs
  503. asBYTE argsType[AS_PPC_MAX_ARGS + 1 + 1 + 1];
  504. memset( argsType, 0, sizeof(argsType));
  505. asCScriptEngine *engine = context->m_engine;
  506. asSSystemFunctionInterface *sysFunc = descr->sysFuncIntf;
  507. int callConv = sysFunc->callConv;
  508. asQWORD retQW = 0;
  509. void *func = (void*)sysFunc->func;
  510. int paramSize = sysFunc->paramSize;
  511. asDWORD *vftable = NULL;
  512. int a;
  513. // convert the parameters that are < 4 bytes from little endian to big endian
  514. int argDwordOffset = 0;
  515. int totalArgumentCount = 0;
  516. for( a = 0; a < (int)descr->parameterTypes.GetLength(); ++a )
  517. {
  518. // get the size for the parameter
  519. int numBytes = descr->parameterTypes[a].GetSizeInMemoryBytes();
  520. ++totalArgumentCount;
  521. // is this a variable argument?
  522. // for variable arguments, the typeID will always follow...but we know it is 4 bytes
  523. // so we can skip that parameter automatically.
  524. bool isVarArg = IsVariableArgument( descr->parameterTypes[a] );
  525. if( isVarArg )
  526. {
  527. ++totalArgumentCount;
  528. }
  529. if( numBytes >= 4 || descr->parameterTypes[a].IsReference() || descr->parameterTypes[a].IsObjectHandle() )
  530. {
  531. // DWORD or larger parameter --- no flipping needed
  532. argDwordOffset += descr->parameterTypes[a].GetSizeOnStackDWords();
  533. }
  534. else
  535. {
  536. // flip
  537. asASSERT( numBytes == 1 || numBytes == 2 );
  538. switch( numBytes )
  539. {
  540. case 1:
  541. {
  542. volatile asBYTE *bPtr = (asBYTE*)ARG_DW(args[argDwordOffset]);
  543. asBYTE t = bPtr[0];
  544. bPtr[0] = bPtr[3];
  545. bPtr[3] = t;
  546. t = bPtr[1];
  547. bPtr[1] = bPtr[2];
  548. bPtr[2] = t;
  549. }
  550. break;
  551. case 2:
  552. {
  553. volatile asWORD *wPtr = (asWORD*)ARG_DW(args[argDwordOffset]);
  554. asWORD t = wPtr[0];
  555. wPtr[0] = wPtr[1];
  556. wPtr[1] = t;
  557. }
  558. break;
  559. }
  560. ++argDwordOffset;
  561. }
  562. if( isVarArg )
  563. {
  564. // skip the implicit typeID
  565. ++argDwordOffset;
  566. }
  567. }
  568. asASSERT( totalArgumentCount <= AS_PPC_MAX_ARGS );
  569. // mark all float/double/int arguments
  570. int argIndex = 0;
  571. for( a = 0; a < (int)descr->parameterTypes.GetLength(); ++a, ++argIndex )
  572. {
  573. // get the base type
  574. argsType[argIndex] = ppcINTARG;
  575. if( descr->parameterTypes[a].IsFloatType() && !descr->parameterTypes[a].IsReference() )
  576. {
  577. argsType[argIndex] = ppcFLOATARG;
  578. }
  579. if( descr->parameterTypes[a].IsDoubleType() && !descr->parameterTypes[a].IsReference() )
  580. {
  581. argsType[argIndex] = ppcDOUBLEARG;
  582. }
  583. if( descr->parameterTypes[a].GetSizeOnStackDWords() == 2 && !descr->parameterTypes[a].IsDoubleType() && !descr->parameterTypes[a].IsReference() )
  584. {
  585. argsType[argIndex] = ppcLONGARG;
  586. }
  587. // if it is a variable argument, account for the typeID
  588. if( IsVariableArgument(descr->parameterTypes[a]) )
  589. {
  590. // implicitly add another parameter (AFTER the parameter above), for the TypeID
  591. argsType[++argIndex] = ppcINTARG;
  592. }
  593. }
  594. asASSERT( argIndex == totalArgumentCount );
  595. asDWORD paramBuffer[64];
  596. if( sysFunc->takesObjByVal )
  597. {
  598. paramSize = 0;
  599. int spos = 0;
  600. int dpos = 1;
  601. for( asUINT n = 0; n < descr->parameterTypes.GetLength(); n++ )
  602. {
  603. if( descr->parameterTypes[n].IsObject() && !descr->parameterTypes[n].IsObjectHandle() && !descr->parameterTypes[n].IsReference() &&
  604. !(descr->parameterTypes[n].GetTypeInfo()->flags & asOBJ_APP_ARRAY) )
  605. {
  606. #ifdef COMPLEX_OBJS_PASSED_BY_REF
  607. if( descr->parameterTypes[n].GetTypeInfo()->flags & COMPLEX_MASK )
  608. {
  609. paramBuffer[dpos++] = args[spos++];
  610. ++paramSize;
  611. }
  612. else
  613. #endif
  614. {
  615. // NOTE: we may have to do endian flipping here
  616. // Copy the object's memory to the buffer
  617. memcpy( &paramBuffer[dpos], *(void**)(args+spos), descr->parameterTypes[n].GetSizeInMemoryBytes() );
  618. // Delete the original memory
  619. engine->CallFree( *(char**)(args+spos) );
  620. spos++;
  621. dpos += descr->parameterTypes[n].GetSizeInMemoryDWords();
  622. paramSize += descr->parameterTypes[n].GetSizeInMemoryDWords();
  623. }
  624. }
  625. else
  626. {
  627. // Copy the value directly
  628. paramBuffer[dpos++] = args[spos++];
  629. if( descr->parameterTypes[n].GetSizeOnStackDWords() > 1 )
  630. {
  631. paramBuffer[dpos++] = args[spos++];
  632. }
  633. paramSize += descr->parameterTypes[n].GetSizeOnStackDWords();
  634. }
  635. // if this was a variable argument parameter, then account for the implicit typeID
  636. if( IsVariableArgument( descr->parameterTypes[n] ) )
  637. {
  638. // the TypeID is just a DWORD
  639. paramBuffer[dpos++] = args[spos++];
  640. ++paramSize;
  641. }
  642. }
  643. // Keep a free location at the beginning
  644. args = &paramBuffer[1];
  645. }
  646. // one last verification to make sure things are how we expect
  647. switch( callConv )
  648. {
  649. case ICC_CDECL:
  650. case ICC_CDECL_RETURNINMEM:
  651. case ICC_STDCALL:
  652. case ICC_STDCALL_RETURNINMEM:
  653. retQW = CallCDeclFunction( args, argsType, paramSize, (asDWORD)func, retPointer );
  654. break;
  655. case ICC_THISCALL:
  656. case ICC_THISCALL_RETURNINMEM:
  657. retQW = CallThisCallFunction(obj, args, argsType, paramSize, (asDWORD)func, retPointer );
  658. break;
  659. case ICC_VIRTUAL_THISCALL:
  660. case ICC_VIRTUAL_THISCALL_RETURNINMEM:
  661. // Get virtual function table from the object pointer
  662. vftable = *(asDWORD**)obj;
  663. retQW = CallThisCallFunction( obj, args, argsType, paramSize, vftable[asDWORD(func)>>2], retPointer );
  664. break;
  665. case ICC_CDECL_OBJLAST:
  666. case ICC_CDECL_OBJLAST_RETURNINMEM:
  667. retQW = CallThisCallFunction_objLast( obj, args, argsType, paramSize, (asDWORD)func, retPointer );
  668. break;
  669. case ICC_CDECL_OBJFIRST:
  670. case ICC_CDECL_OBJFIRST_RETURNINMEM:
  671. retQW = CallThisCallFunction( obj, args, argsType, paramSize, (asDWORD)func, retPointer );
  672. break;
  673. default:
  674. context->SetInternalException(TXT_INVALID_CALLING_CONVENTION);
  675. }
  676. if( sysFunc->hostReturnFloat )
  677. {
  678. // If the return is a float value we need to get the value from the FP register
  679. if( sysFunc->hostReturnSize == 1 )
  680. *(asDWORD*)&retQW = GetReturnedFloat();
  681. else
  682. retQW = GetReturnedDouble();
  683. }
  684. else if( sysFunc->hostReturnSize == 1 )
  685. {
  686. // Move the bits to the higher value to compensate for the adjustment that the caller does
  687. retQW <<= 32;
  688. }
  689. return retQW;
  690. }
  691. END_AS_NAMESPACE
  692. #endif // AS_PTR_SIZE == 2
  693. #endif // AS_PPC_64
  694. #endif // AS_MAX_PORTABILITY