#define ARGUMENT_SPILL_SIZE (8*10 + 8*16) /* Windows ARM64 Exception Handling * * Structured Exception Handling (SEH) on Windows ARM64 differs from the x64 * implementation. Functions consist of a single prologue and zero or more * epilogues. Instead of using offsets for the .seh* directives to manipulate the * stack frame, each directive corresponds to a single instruction. * * This presents a challenge for our objc_msgSend function, which only modifies * the stack when a slow lookup is needed (see label "5"). * * To address this, we move the directive marking the start of a function deep * into the msgSend body to prevent marking every instruction as ".seh_nop." * * For Windows: * - EH_START(x): Start of function (no effect on Windows) * - EH_END(x): End of function (no effect on Windows) * - EH_START_AT_OFFSET(x): Mark Start of function (Delayed) * - EH_END_AT_OFFSET(x): Mark End of function (Delayed) * - EH_END_PROLOGUE: End of function prologue * - EH_START_EPILOGUE: Start of function epilogue * - EH_END_EPILOGUE: End of function epilogue * - EH_SAVE_FP_LR(x): Save Frame Pointer and Link Register * - EH_STACK_ALLOC(x): Stack allocation (inside prologue) * - EH_ADD_FP(x): Add to Frame Pointer * - EH_NOP: Mark instruction with no unwinding relevance * * For non-64-bit Windows systems or other platforms, these macros have no effect and can be used without causing issues. */ #ifdef _WIN32 # define EH_START # define EH_END # define EH_START_AT_OFFSET .seh_proc objc_msgSend # define EH_END_AT_OFFSET .seh_endproc objc_msgSend # define EH_END_PROLOGUE .seh_endprologue # define EH_START_EPILOGUE .seh_startepilogue # define EH_END_EPILOGUE .seh_endepilogue # define EH_SAVE_FP_LR(x) .seh_save_fplr x # define EH_STACK_ALLOC(x) .seh_stackalloc x # define EH_ADD_FP(x) .seh_add_fp x # define EH_NOP .seh_nop #else // Marks the real start and end of the function # define EH_START .cfi_startproc # define EH_END .cfi_endproc // The following directives are either not // needed or not available with CFI # define EH_START_AT_OFFSET # define EH_END_AT_OFFSET # define EH_END_PROLOGUE # define EH_START_EPILOGUE # define EH_END_EPILOGUE # define EH_SAVE_FP_LR(x) # define EH_STACK_ALLOC(x) # define EH_ADD_FP(x) # define EH_NOP #endif .globl CDECL(objc_msgSend_fpret) TYPE_DIRECTIVE(CDECL(objc_msgSend_fpret), %function) .globl CDECL(objc_msgSend) TYPE_DIRECTIVE(CDECL(objc_msgSend), %function) .globl CDECL(objc_msgSend_stret) TYPE_DIRECTIVE(CDECL(objc_msgSend_stret), %function) CDECL(objc_msgSend): CDECL(objc_msgSend_fpret): CDECL(objc_msgSend_stret): EH_START cbz x0, 4f // Skip everything if the receiver is nil // Jump to 6: if this is a small object ubfx x9, x0, #0, #SMALLOBJ_BITS cbnz x9, 6f ldr x9, [x0] // Load class to x9 if not a small int 1: ldr x9, [x9, #DTABLE_OFFSET] // Dtable -> x9 ldr w10, [x1] // selector->index -> x10 ldr w11, [x9, #SHIFT_OFFSET] // dtable->shift -> x11 cmp x11, #8 // If this is a small dtable, jump to the // small dtable handlers b.eq 2f cbz x11, 3f ubfx x11, x10, #16, #8 // Put byte 3 of the sel id in x12 add x11, x9, x11, lsl #3 // x11 = dtable address + dtable data offset ldr x9, [x11, #DATA_OFFSET] // Load, adding in the data offset 2: // dtable16 ubfx x11, x10, #8, #8 // Put byte 2 of the sel id in x12 add x11, x9, x11, lsl #3 // x11 = dtable address + dtable data offset ldr x9, [x11, #DATA_OFFSET] // Load, adding in the data offset 3: // dtable8 ubfx x11, x10, #0, #8 // Put low byte of the sel id in x12 add x11, x9, x11, lsl #3 // x11 = dtable address + dtable data offset ldr x9, [x11, #DATA_OFFSET] // Load, adding in the data offset. // Slot pointer is now in x9 cbz x9, 5f // If the slot is nil, go to the C path ldr x9, [x9, #SLOT_OFFSET] // Load the method from the slot br x9 // Tail-call the method 4: // Nil receiver mov x0, #0 mov v0.d[0], x0 mov v0.d[1], x0 br lr 5: // Slow lookup EH_START_AT_OFFSET // Save anything that will be clobbered by // the call. // Note that we pre-index (see "!"), meaning // that we adjust the sp before storing the pair // of registers. stp x0, x1, [sp, #-(ARGUMENT_SPILL_SIZE)]! EH_STACK_ALLOC((ARGUMENT_SPILL_SIZE)) stp x2, x3, [sp, #16] EH_NOP // The following instructions can be ignored by SEH stp x4, x5, [sp, #32] EH_NOP stp x6, x7, [sp, #48] EH_NOP stp q0, q1, [sp, #64] EH_NOP stp q2, q3, [sp, #96] EH_NOP stp q4, q5, [sp, #128] EH_NOP stp q6, q7, [sp, #160] EH_NOP stp fp, lr, [sp, #192] // The order is arbitrary, except that EH_SAVE_FP_LR(192) // fp and lr must be spilled together add fp, sp, 192 // Adjust frame pointer EH_ADD_FP(192) stp x0, x8, [sp, #-16]! // it's convenient if x0 is spilled at sp EH_STACK_ALLOC(16) // stp performed pre-indexing by sp-16 EH_END_PROLOGUE #ifndef _WIN32 .cfi_def_cfa fp, 16 .cfi_offset fp, -16 .cfi_offset lr, -8 #endif // We now have all argument registers, the link // register and the receiver spilled on the // stack, with sp containing // the address of the receiver mov x0, sp // &self, _cmd in arguments mov x1, x1 bl CDECL(slowMsgLookup) // This is the only place where the EH directives // have to be accurate... mov x9, x0 // IMP -> x9 EH_START_EPILOGUE ldp x0, x1, [sp, #16] // Reload spilled argument registers EH_NOP ldp x2, x3, [sp, #32] EH_NOP ldp x4, x5, [sp, #48] EH_NOP ldp x6, x7, [sp, #64] EH_NOP ldp q0, q1, [sp, #80] EH_NOP ldp q2, q3, [sp, #112] EH_NOP ldp q4, q5, [sp, #144] EH_NOP ldp q6, q7, [sp, #176] EH_NOP ldp fp, lr, [sp, #208] EH_SAVE_FP_LR(208) // Post-increment sp += ARGUMENT_SPILL_SIZE +16 ldp x0, x8, [sp], #(ARGUMENT_SPILL_SIZE + 16) EH_STACK_ALLOC((ARGUMENT_SPILL_SIZE + 16)) EH_END_EPILOGUE EH_END_AT_OFFSET br x9 6: // Load 63:12 of SmallObjectClasses address // We use the CDECL macro as Windows prefixes // cdecl conforming symbols with "_". adrp x10, CDECL(SmallObjectClasses) // The macro handles this transparently. // Add lower 12-bits of SmallObjectClasses address to x10 add x10, x10, :lo12:CDECL(SmallObjectClasses) ldr x9, [x10, x9, lsl #3] b 1b EH_END #ifdef _WIN32 .text .def objc_msgSend; .scl 2; .type 32; .endef .def objc_msgSend_fpret; .scl 2; .type 32; .endef .def objc_msgSend_stret; .scl 2; .type 32; .endef .section .drectve,"yn" .ascii " /EXPORT:objc_msgSend" .ascii " /EXPORT:objc_msgSend_fpret" .ascii " /EXPORT:objc_msgSend_stret" #endif