@@ -235,21 +235,38 @@ static _PyClassLoader_StaticCallReturn return_to_native_typecode(
235235 return ret ;
236236}
237237
238+ // Number of native arguments passed via registers (excluding the state arg).
239+ // On x86-64: rsi, rdx, rcx, r8, r9 = 5 register args.
240+ // On ARM64: x1-x7 = 7 register args.
241+ #if defined(__aarch64__ ) || defined(_M_ARM64 )
242+ #define NATIVE_REG_ARG_COUNT 7
243+ // The stack arg pointer points directly to the first stack arg.
244+ #define NATIVE_STACK_ARG_OFFSET 0
245+ #elif defined(_M_X64 ) || defined(_M_AMD64 ) || defined(__x86_64__ )
246+ #define NATIVE_REG_ARG_COUNT 5
247+ // The stack arg pointer points to the saved frame pointer, so we skip
248+ // the frame pointer and the return address to reach the first stack arg.
249+ #define NATIVE_STACK_ARG_OFFSET 2
250+ #endif
251+
238252int _PyClassLoader_HydrateArgsFromSig (
239253 _PyClassLoader_ThunkSignature * sig ,
240254 Py_ssize_t arg_count ,
241255 void * * args ,
242256 PyObject * * call_args ,
243257 PyObject * * free_args ) {
244- PyObject * * extra_args = (PyObject * * )args [5 ];
258+ PyObject * * extra_args = (PyObject * * )args [NATIVE_REG_ARG_COUNT ];
245259 for (Py_ssize_t i = 0 ; i < arg_count ; i ++ ) {
246260 void * original ;
247- if (i < 5 ) {
261+ if (i < NATIVE_REG_ARG_COUNT ) {
248262 original = args [i ]; // skip the v-table state
249263 } else {
250- // The original args came in on the stack, so we have to skip the frame
251- // pointer, the return address and then add one more.
252- original = extra_args [i - 3 ];
264+ // The original args came in on the stack. On x86-64 the stack pointer
265+ // saved in the args array points to the frame pointer, so we have to
266+ // skip over it and the return address. On ARM64 the pointer is directly
267+ // to the first stack arg.
268+ original =
269+ extra_args [(i - NATIVE_REG_ARG_COUNT ) + NATIVE_STACK_ARG_OFFSET ];
253270 }
254271
255272 if (sig -> ta_has_primitives && sig -> ta_argtype [i ] != TYPED_OBJECT ) {
@@ -704,10 +721,43 @@ PyObject* _PyVTable_native_entry(PyObject* state, void** args) {
704721 "leave\n"
705722 "ret\n" );
706723}
724+ #elif defined(__aarch64__ ) || defined(_M_ARM64 )
725+ __attribute__((naked ))
726+ PyObject * _PyVTable_native_entry (PyObject * state , void * * args ) {
727+ __asm__(
728+ /* Save frame pointer and link register */
729+ "stp x29, x30, [sp, #-16]!\n"
730+ "mov x29, sp\n"
731+ /* We want to push the arguments passed natively onto the stack */
732+ /* so that we can recover them in hydrate_args. So we store them */
733+ /* in a stack-allocated array and pass the address as the 2nd */
734+ /* argument. Note we don't need to save x0 as it's the state */
735+ /* argument which we're passing in anyway. */
736+ /* Calculate pointer to stack overflow args (original entry sp) */
737+ "add x9, x29, #16\n"
738+ /* Store x1-x7 and the stack arg pointer into the array */
739+ "stp x1, x2, [sp, #-64]!\n"
740+ "stp x3, x4, [sp, #16]\n"
741+ "stp x5, x6, [sp, #32]\n"
742+ "stp x7, x9, [sp, #48]\n"
743+ /* Set x1 to point to the args array */
744+ "mov x1, sp\n"
745+ "bl _PyVTable_thunk_native\n"
746+ /* We don't know if we're returning a floating point value or not */
747+ /* so we assume we are, and always populate the FP registers */
748+ /* even if we don't need to */
749+ "fmov d0, x0\n"
750+ "fmov d1, x1\n"
751+ /* Restore frame and return */
752+ "mov sp, x29\n"
753+ "ldp x29, x30, [sp], #16\n"
754+ "ret\n" );
755+ }
707756#else
708757PyObject * _PyVTable_native_entry (PyObject * state , void * * args ) {
709758 PyErr_SetString (
710- PyExc_RuntimeError , "native entry points not available on non x-64" );
759+ PyExc_RuntimeError ,
760+ "native entry points not available on this architecture" );
711761 return NULL ;
712762}
713763#endif
0 commit comments