You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
250 lines
8.3 KiB
ArmAsm
250 lines
8.3 KiB
ArmAsm
#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(x) .seh_proc x
|
|
# define EH_END_AT_OFFSET(x) .seh_endproc x
|
|
|
|
# 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(x)
|
|
# define EH_END_AT_OFFSET(x)
|
|
# 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
|
|
|
|
.macro MSGSEND fnname receiver, sel
|
|
EH_START
|
|
|
|
cbz \receiver, 4f // Skip everything if the receiver is nil
|
|
// Jump to 6: if this is a small object
|
|
ubfx x9, \receiver, #0, #SMALLOBJ_BITS
|
|
cbnz x9, 6f
|
|
|
|
ldr x9, [\receiver] // Load class to x9 if not a small int
|
|
1:
|
|
ldr x9, [x9, #DTABLE_OFFSET] // Dtable -> x9
|
|
ldr w10, [\sel] // 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 \receiver, #0
|
|
mov v0.d[0], \receiver
|
|
mov v0.d[1], \receiver
|
|
br lr
|
|
5: // Slow lookup
|
|
EH_START_AT_OFFSET(\fnname)
|
|
|
|
// 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 \receiver, x8, [sp, #-16]! // it's convenient if \receiver 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, \sel
|
|
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 \receiver, x8, [sp], #(ARGUMENT_SPILL_SIZE + 16)
|
|
EH_STACK_ALLOC((ARGUMENT_SPILL_SIZE + 16))
|
|
|
|
EH_END_EPILOGUE
|
|
EH_END_AT_OFFSET(\fnname)
|
|
|
|
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
|
|
.endm
|
|
|
|
.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):
|
|
MSGSEND objc_msgSend, x0, x1
|
|
|
|
/*
|
|
In AAPCS, an SRet is passed in x8, not x0 like a normal pointer parameter.
|
|
On Windows, this is only the case for POD (plain old data) types. Non trivial
|
|
types with constructors and destructors are passed in x0 on sret.
|
|
|
|
We thus need two objc_msgSend functions on Windows on ARM64 for Sret:
|
|
1. objc_msgSend_stret for POD Sret
|
|
2. objc_msgSend_stret2 for non-trivial Sret (like C++ class instances)
|
|
*/
|
|
#ifdef _WIN32
|
|
.globl CDECL(objc_msgSend_stret2)
|
|
TYPE_DIRECTIVE(CDECL(objc_msgSend_stret2), %function)
|
|
CDECL(objc_msgSend_stret2):
|
|
MSGSEND objc_msgSend_stret2, x1, x2
|
|
|
|
.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
|
|
.def objc_msgSend_stret2;
|
|
.scl 2;
|
|
.type 32;
|
|
.endef
|
|
|
|
.section .drectve,"yn"
|
|
.ascii " /EXPORT:objc_msgSend"
|
|
.ascii " /EXPORT:objc_msgSend_fpret"
|
|
.ascii " /EXPORT:objc_msgSend_stret"
|
|
.ascii " /EXPORT:objc_msgSend_stret2"
|
|
#endif |