//go:build darwin && arm64 #include "textflag.h" // fsevents_darwin_ffi_arm64.s: arm64 assembly for the FSEvents backend // // Contains two functions: // // 1. FSEventStreamCreate trampoline: moves the float64 latency bits // from R5 (integer register, where syscall6 puts it) into F0 (the // AAPCS64 first float argument register), and hardcodes flags to // 0x11 (kFSEventStreamCreateFlagUseCFTypes | // kFSEventStreamCreateFlagFileEvents). // // 2. fsEventsCallbackASM: the C-convention callback invoked by FSEvents // on a GCD dispatch queue thread. Retains/copies callback data into a // payload, writes the payload pointer to eventPipe to wake the Go event-loop // goroutine, then returns. Never enters Go ABI. Uses only caller-saved // registers (R0-R17) to avoid // clobbering AAPCS64 callee-saved R19-R28 and platform-reserved R18. // See TestCallbackASMTouchesOnlySafeRegisters for the static check. // --------------------------------------------------------------------------- // FSEventStreamCreate trampoline: shuffles the float64 latency argument. // // The runtime's syscall6 trampoline loads 6 args into R0-R5: // R0=allocator R1=callback R2=ctx R3=paths // R4=sinceWhen R5=latency(bits) // // The C function expects latency in F0 (float register) and flags in R5. // flags is always 0x11 (kFSEventStreamCreateFlagUseCFTypes | // kFSEventStreamCreateFlagFileEvents), so we hardcode it. // --------------------------------------------------------------------------- TEXT fse_FSEventStreamCreate_trampoline<>(SB), NOSPLIT, $0-0 FMOVD R5, F0 MOVD $0x11, R5 JMP fse_FSEventStreamCreate(SB) GLOBL ·fse_FSEventStreamCreate_trampoline_addr(SB), RODATA, $8 DATA ·fse_FSEventStreamCreate_trampoline_addr(SB)/8, $fse_FSEventStreamCreate_trampoline<>(SB) // --------------------------------------------------------------------------- // FSEvents callback: called from a GCD dispatch queue with C convention. // R0=streamRef R1=info R2=numEvents R3=paths R4=flags R5=ids // // `info` (R1) is a pointer to a streamCallback struct: // offset 0: eventPipeWrite fd // // Because all memory accesses use offset addressing from R1 (a caller-saved // register), there are no global symbol loads and no REGTMP/R27 hazard. // // Frame layout (80 bytes, 16-byte aligned): // RSP+ 0: saved R29 (FP) ← R29 points here (C frame chain) // RSP+ 8: saved R30 (LR) // RSP+16: payload pointer bytes written to eventPipe // RSP+24: saved info pointer // RSP+32: saved numEvents // RSP+40: saved original flags pointer // RSP+48: retained CFArray paths // RSP+56: copied flags pointer // RSP+64: saved original IDs pointer // RSP+72: copied IDs pointer // --------------------------------------------------------------------------- TEXT fsEventsCallbackASM<>(SB), NOSPLIT|NOFRAME, $0 SUB $80, RSP MOVD R29, (RSP) MOVD R30, 8(RSP) MOVD RSP, R29 MOVD R1, 24(RSP) // info MOVD R2, 32(RSP) // numEvents MOVD R4, 40(RSP) // original flags MOVD R5, 64(RSP) // original IDs // Retain the CFArray paths because FSEvents owns the callback argument. MOVD R3, R0 BL fse_CFRetain(SB) CBZ R0, done MOVD R0, 48(RSP) // Copy the flags array into C heap memory owned by the Go event loop. MOVD 32(RSP), R0 LSL $2, R0, R0 BL fse_malloc(SB) CBZ R0, releasePaths MOVD R0, 56(RSP) MOVD R0, R0 MOVD 40(RSP), R1 MOVD 32(RSP), R2 LSL $2, R2, R2 BL fse_memcpy(SB) // Copy the event ID array into C heap memory owned by the Go event loop. MOVD 32(RSP), R0 LSL $3, R0, R0 BL fse_malloc(SB) CBZ R0, freeFlags MOVD R0, 72(RSP) MOVD R0, R0 MOVD 64(RSP), R1 MOVD 32(RSP), R2 LSL $3, R2, R2 BL fse_memcpy(SB) // Allocate and populate fsEventsCallbackPayload. MOVD $32, R0 BL fse_malloc(SB) CBZ R0, freeIDs MOVD R0, 16(RSP) MOVD 32(RSP), R6 MOVD R6, (0*8)(R0) MOVD 48(RSP), R6 MOVD R6, (1*8)(R0) MOVD 56(RSP), R6 MOVD R6, (2*8)(R0) MOVD 72(RSP), R6 MOVD R6, (3*8)(R0) // write(info->eventPipeWrite, &payload, sizeof(payload)). writeAgain: MOVD 24(RSP), R6 // reload info MOVD (0*8)(R6), R0 // eventPipeWrite ADD $16, RSP, R1 // buf (payload pointer) MOVD $8, R2 // count BL fse_write(SB) CMP $8, R0 BEQ done ADD $1, R0, R6 CBNZ R6, freePayload BL fse___error(SB) MOVW (R0), R0 CMPW $4, R0 // EINTR BEQ writeAgain B freePayload freePayload: MOVD 16(RSP), R0 BL fse_free(SB) freeIDs: MOVD 72(RSP), R0 BL fse_free(SB) freeFlags: MOVD 56(RSP), R0 BL fse_free(SB) releasePaths: MOVD 48(RSP), R0 BL fse_CFRelease(SB) // Return 0. done: MOVD $0, R0 MOVD (RSP), R29 MOVD 8(RSP), R30 ADD $80, RSP RET GLOBL ·fsEventsCallbackAsmAddr(SB), RODATA, $8 DATA ·fsEventsCallbackAsmAddr(SB)/8, $fsEventsCallbackASM<>(SB)