167 lines
4.7 KiB
ArmAsm
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)
|