// Copyright (c) 2026 Petr Balvín (https://petrbalvin.org) // SPDX-License-Identifier: BSD-3-Clause #include "textflag.h" // ABI0 JIT trampoline for arm64. // // enterJIT saves the Go SP and LR, switches to the prepared stack, and // branches to the JIT function. When the function RETs (BR LR), control // lands in leaveJIT, which restores the Go stack and returns. // // The prepared stack layout: // [0] leaveJIT address (loaded into LR before jumping to fn) // [8] ABI0 argument area // func enterJIT(fn uintptr, stack uintptr) TEXT ·enterJIT(SB), NOSPLIT, $0-16 MOVD fn+0(FP), R0 // target function address MOVD R30, savedLR(SB) // save link register MOVD R3, savedSP(SB) // save Go stack pointer MOVD stack+8(FP), R3 // load prepared stack pointer MOVD 0(R3), R30 // load leaveJIT address into LR MOVD R3, RSP // switch to the prepared stack: SP stays on // the leave slot, so the kernel reads its // first argument at SP+8 per the frame // convention (amd64 lays the stack out the // same way) JMP (R0) // branch to JIT function // func leaveJIT() TEXT ·leaveJIT(SB), NOSPLIT, $0-0 MOVD savedSP(SB), R3 // restore Go stack pointer MOVD R3, RSP MOVD savedLR(SB), R30 // restore link register RET // return to Go caller // leaveRawAddr holds the raw .abi0 address of leaveJIT, read by call_arm64.go // in preference to reflect.ValueOf(leaveJIT), which returns the address of the // ABIInternal wrapper the linker interposes: the wrapper's prologue clobbers // the saved-register window the JIT call depends on. GLOBL ·leaveRawAddr(SB), NOPTR, $8 DATA ·leaveRawAddr(SB)/8, $·leaveJIT(SB) // Package-level storage for saved registers. GLOBL savedLR(SB), NOPTR, $8 GLOBL savedSP(SB), NOPTR, $8