Files
kjol/tools/tsgo/internal/fswatch/fsevents_darwin_ffi_arm64.s
2026-07-09 16:50:43 -04:00

167 lines
4.7 KiB
ArmAsm

//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)