Files
jak-project/game/kernel/asm_funcs_arm64.s
T
Parker de25f39439 arm64: Apple Silicon support (#4390)
Got OpenGOAL building and running natively on Apple Silicon.

This is the rest of the port after the smaller arm64 emitter PRs. I was
told pushing one big PR was okay. This covers goalc, the runtime,
linker, kernel and the GOAL asm.

The new paths have native tests. I also found four more emitter bugs
while running it. They're in scalar sqrt, 128-bit stores, scalar max and
indexed stores above 4 GB.

Spent a while cleaning it up so it's easier to read. 4am gotta sleep lol

Closes #3841

---------

Co-authored-by: Tyler Wilding <xtvaser@gmail.com>
2026-08-24 23:22:47 -04:00

332 lines
8.7 KiB
ArmAsm
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
;; GOAL Runtime assembly functions. These exist only in the arm64 version of GOAL.
;; - https://developer.apple.com/documentation/xcode/writing-arm64-code-for-apple-platforms#Pass-arguments-to-functions-correctly
;; - https://en.wikipedia.org/wiki/Calling_convention#ARM_(A64)
;; - https://student.cs.uwaterloo.ca/~cs452/docs/rpi4b/aapcs64.pdf
;; - s16s31 (d8d15, q4q7) must be preserved
;; - s0s15 (d0d7, q0q3) and d16d31 (q8q15) do not need to be preserved
;; - https://devblogs.microsoft.com/oldnewthing/20220728-00/?p=106912
;; - ;; - https://courses.cs.washington.edu/courses/cse469/19wi/arm64.pdf
.text
;; Call C++ code on arm64 systems, from GOAL.
;; Following the macOS documentation which mostly aligns with standard arm64
.global _arg_call_arm64
.align 4
_arg_call_arm64:
stp x29, x30, [sp, #-16]!
mov x29, sp
; Putting an exclamation point after the close-bracket
; means that the calculated effective address is written back to the base register. (pre-indexing)
stp q15, q14, [sp, #-32]!
stp q13, q12, [sp, #-32]!
stp q11, q10, [sp, #-32]!
stp q9, q8, [sp, #-32]!
blr x8
;; restore the vector pairs in the same order they were saved
ldp q9, q8, [sp], #32
ldp q11, q10, [sp], #32
ldp q13, q12, [sp], #32
ldp q15, q14, [sp], #32
ldp x29, x30, [sp], #16
ret
;; Call C++ code on arm64 systems, from GOAL.
;;
;; Put arguments on the stack and put a pointer to this array in the first arg.
;; this function pushes all 8 OpenGOAL registers into a stack array.
;; it calls the function in x8 with a pointer to this array.
;; it returns the return value of the called function.
.global _stack_call_arm64
.align 4
_stack_call_arm64:
stp x29, x30, [sp, #-16]!
mov x29, sp
stp q15, q14, [sp, #-32]!
stp q13, q12, [sp, #-32]!
stp q11, q10, [sp, #-32]!
stp q9, q8, [sp, #-32]!
; create stack array of arguments
; arg 7 (R11 in x86)
; arg 6 (R10 in x86)
; arg 5 (R8 in x86)
; arg 4 (R8 in x86)
; arg 3 (RCX in x86)
; arg 2 (RDX in x86)
; arg 1 (RSI in x86)
; arg 0 (RDI in x86)
; put x0 at the lowest address and x7 at the highest
stp x6, x7, [sp, #-16]!
stp x4, x5, [sp, #-16]!
stp x2, x3, [sp, #-16]!
stp x0, x1, [sp, #-16]!
; set first argument
; pass the array address as the first argument
mov x0, sp
; call function
blr x8
; keep x0 because it holds the return value
ldr x1, [sp, #8]
ldp x2, x3, [sp, #16]
ldp x4, x5, [sp, #32]
ldp x6, x7, [sp, #48]
add sp, sp, #64
;; restore the vector pairs in the same order they were saved
ldp q9, q8, [sp], #32
ldp q11, q10, [sp], #32
ldp q13, q12, [sp], #32
ldp q15, q14, [sp], #32
ldp x29, x30, [sp], #16
; return!
ret
;; Call c++ code through mips2c.
;; GOAL will call a dynamically generated trampoline.
;; x9 holds the C function and x10 holds the fake GOAL stack size.
.global _mips2c_call_arm64
.align 4
_mips2c_call_arm64:
stp x29, x30, [sp, #-16]!
mov x29, sp
;; first, save quadword registers
;; save all 128 bits because AAPCS64 only preserves the low half
sub sp, sp, #128
stp q8, q9, [sp]
stp q10, q11, [sp, #32]
stp q12, q13, [sp, #64]
stp q14, q15, [sp, #96]
;; oof
;; 1280-byte MIPS register context
sub sp, sp, #1280
; arg 0 (RDI in x86) and
str x0, [sp, #64] ;; a0
; arg 1 (RSI in x86)
str x1, [sp, #80] ;; a1
; arg 2 (RDX in x86) and arg 3 (RCX in x86)
str x2, [sp, #96] ;; a2
; arg 2 (RDX in x86) and arg 3 (RCX in x86)
str x3, [sp, #112] ;; a3
; arg 4 (R8 in x86) and arg 5 (R8 in x86)
str x4, [sp, #128] ;; t0
; arg 4 (R8 in x86) and arg 5 (R8 in x86)
str x5, [sp, #144] ;; t1
; arg 6 (R10 in x86) and arg 7 (R11 in x86)
str x6, [sp, #160] ;; t2
; arg 6 (R10 in x86) and arg 7 (R11 in x86)
str x7, [sp, #176] ;; t3
;; s6 (pp) (R13 in x86) and s7 (st) (R14 in x86)
str x20, [sp, #352] ;; s6 (pp)
;; s6 (pp) (R13 in x86) and s7 (st) (R14 in x86)
str x21, [sp, #368] ;; s7 (st)
;; store the context as a GOAL pointer in the MIPS sp slot
mov x11, sp
sub x11, x11, x22
;; mip2c code's MIPS stack
str x11, [sp, #464] ;; r29 (sp)
; move the stack pointer to arg 0
mov x0, sp ;; pass the context as the sole argument
;; allocate space on the stack for GOAL fake stack
;; round the fake GOAL stack to 16 bytes
add x11, x10, #15
and x11, x11, #-16
sub sp, sp, x11
;; and remember this so we can find our way back
str x11, [sp, #-16]! ;; save the stack size across the C call
;; call!
blr x9
;; unallocate
ldr x11, [sp], #16
add sp, sp, x11 ;; restore sp to the context base
ldr x0, [sp, #32] ;; return the v0 slot written by mips2c
; reset the stackpointer back
add sp, sp, #1280
ldp q8, q9, [sp]
ldp q10, q11, [sp, #32]
ldp q12, q13, [sp, #64]
ldp q14, q15, [sp, #96]
add sp, sp, #128
ldp x29, x30, [sp], #16
ret
;; The _call_goal_asm function is used to call a GOAL function from C.
;; It calls on the parent stack, which is a bad idea if your stack is not already a GOAL stack.
;; It supports up to 3 arguments and a return value.
;; This should be called with the arguments:
;; - first goal arg
;; - second goal arg
;; - third goal arg
;; - address of function to call
;; - address of the symbol table
;; - GOAL memory space offset
.global _call_goal_asm_arm64
.align 4
_call_goal_asm_arm64:
stp x29, x30, [sp, #-16]!
mov x29, sp
;; saved registers we need to modify for GOAL should be preserved
; ARM64 requires 16-byte stack pointer alignment
stp x20, x21, [sp, #-16]!
stp x22, x27, [sp, #-16]!
sub sp, sp, #128
stp q8, q9, [sp]
stp q10, q11, [sp, #32]
stp q12, q13, [sp, #64]
stp q14, q15, [sp, #96]
;; x0 - first arg
;; x1 - second arg
;; x2 - third arg
;; x3 - function pointer
;; x4 - st (goes in x20 and x21)
;; x5 - off (goes in x22)
;; x6 holds the executable mapping base for x27
;; set GOAL process
mov x20, x4
;; symbol table
mov x21, x4
;; offset
mov x22, x5
mov x27, x6
;; call GOAL by function pointer
blr x3
;; restore saved registers.
ldp q8, q9, [sp]
ldp q10, q11, [sp, #32]
ldp q12, q13, [sp, #64]
ldp q14, q15, [sp, #96]
add sp, sp, #128
ldp x22, x27, [sp], #16
ldp x20, x21, [sp], #16
ldp x29, x30, [sp], #16
ret
.global _call_goal8_asm_arm64
.align 4
_call_goal8_asm_arm64:
stp x29, x30, [sp, #-16]!
mov x29, sp
;; saved registers we need to modify for GOAL should be preserved
; ARM64 requires 16-byte stack pointer alignment
stp x20, x21, [sp, #-16]!
stp x22, x27, [sp, #-16]!
sub sp, sp, #128
stp q8, q9, [sp]
stp q10, q11, [sp, #32]
stp q12, q13, [sp, #64]
stp q14, q15, [sp, #96]
;; x0 - first arg (func)
;; x1 - second arg (arg array)
;; x2 - third arg (0)
;; x3 - pp (goes in r13)
;; x4 - st (goes in r14)
;; x5 - off (goes in r15)
;; x6 holds the executable mapping base for x27
;; set GOAL function pointer
mov x20, x3
;; st
mov x21, x4
;; offset
mov x22, x5
;; save the executable mapping before loading x6 from the argument array
mov x27, x6
;; move function to temp
mov x8, x0
;; extract arguments
ldr x0, [x1] ;; 0
ldr x2, [x1, #+16] ;; 2
ldr x3, [x1, #+24] ;; 3
ldr x4, [x1, #+32] ;; 4
ldr x5, [x1, #+40] ;; 5
ldr x6, [x1, #+48] ;; 6
ldr x7, [x1, #+56] ;; 7
ldr x1, [x1, #+8] ;; 1 (do this last)
;; call GOAL by function pointer
blr x8
;; retore registers.
ldp q8, q9, [sp]
ldp q10, q11, [sp, #32]
ldp q12, q13, [sp, #64]
ldp q14, q15, [sp, #96]
add sp, sp, #128
ldp x22, x27, [sp], #16
ldp x20, x21, [sp], #16
ldp x29, x30, [sp], #16
ret
;; Call goal, but switch stacks.
.global _call_goal_on_stack_asm_arm64
.align 4
_call_goal_on_stack_asm_arm64:
stp x29, x30, [sp, #-16]!
mov x29, sp
;; x0 - stack pointer
;; x1 - unused
;; x2 - unused
;; x3 - function pointer
;; x4 - st (goes in x21 and x20)
;; x5 - offset (goes in x22)
;; x6 holds the executable mapping base for x27
;; saved registers we need to modify for GOAL should be preserved
; ARM64 requires 16-byte stack pointer alignment
stp x20, x21, [sp, #-16]!
stp x22, x27, [sp, #-16]!
sub sp, sp, #128
stp q8, q9, [sp]
stp q10, q11, [sp, #32]
stp q12, q13, [sp, #64]
stp q14, q15, [sp, #96]
;; also stash the current stack pointer on the stack
;; NOTE - you cannot directly store or load the `sp` register in arm64
;; save the native sp through x9 because str cannot use sp as data
mov x9, sp
;; switch to new stack
mov sp, x0
str x9, [sp, #-16]!
;; set GOAL function pointer
mov x20, x4 ;; set GOAL process
mov x21, x4 ;; symbol table
mov x22, x5 ;; offset
mov x27, x6
;; call GOAL by function pointer
blr x3
;; restore registers
ldr x9, [sp], #16
mov sp, x9
ldp q8, q9, [sp]
ldp q10, q11, [sp, #32]
ldp q12, q13, [sp, #64]
ldp q14, q15, [sp, #96]
add sp, sp, #128
ldp x22, x27, [sp], #16
ldp x20, x21, [sp], #16
ldp x29, x30, [sp], #16
ret