mirror of
https://github.com/open-goal/jak-project
synced 2026-08-31 17:43:06 -04:00
de25f39439
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>
332 lines
8.7 KiB
ArmAsm
332 lines
8.7 KiB
ArmAsm
;; 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
|
||
;; - s16–s31 (d8–d15, q4–q7) must be preserved
|
||
;; - s0–s15 (d0–d7, q0–q3) and d16–d31 (q8–q15) 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
|