Files
jak-project/game/kernel/common/codegen.h
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

113 lines
2.4 KiB
C

#pragma once
/*!
* @file codegen.h
* Helpers for the small machine code stubs the kernel writes at runtime.
*/
#include <cstring>
#include "common/arm64/encoding.h"
#include "common/common_types.h"
#include "game/runtime.h"
#ifdef __APPLE__
#include <libkern/OSCacheControl.h>
#endif
/*!
* Makes freshly written instructions visible to the CPU.
*/
inline void flush_icache(void* addr, int size) {
#ifdef __aarch64__
#ifdef __APPLE__
sys_icache_invalidate(addr, size);
#else
__builtin___clear_cache((char*)addr, (char*)addr + size);
#endif
#else
(void)addr;
(void)size;
#endif
}
/*!
* Makes EE instructions visible through their executable mapping.
*/
inline void flush_icache_goal(u32 goal_addr, u32 size) {
flush_icache(g_ee_main_mem_exec + goal_addr, (int)size);
}
#ifdef __aarch64__
/*!
* Emits a fixed movz/movk sequence for a 64-bit value.
*/
inline int emit_arm64_mov64(u8* dst, u32 reg, u64 val) {
u32 instr = arm64::encode_movz_64(reg, u16(val & 0xffff), 0);
memcpy(dst, &instr, 4);
int offset = 4;
for (u32 halfword = 1; halfword < 4; halfword++) {
instr = arm64::encode_movk_64(reg, u16((val >> (halfword * 16)) & 0xffff), halfword);
memcpy(dst + offset, &instr, 4);
offset += 4;
}
return offset;
}
/*!
* Emits a GOAL-to-C stub and returns its size in bytes.
*/
inline int emit_arm64_c_stub(u8* dst, u64 target, u64 trampoline, bool arg3_is_pp) {
int offset = emit_arm64_mov64(dst, 8, target);
if (arg3_is_pp) {
// pp goes from x20 to C argument x3
u32 instr = 0xaa0003e0 | (20 << 16) | 3;
memcpy(dst + offset, &instr, 4);
offset += 4;
}
offset += emit_arm64_mov64(dst + offset, 9, trampoline);
u32 br = 0xd61f0000 | (9 << 5); // br x9
memcpy(dst + offset, &br, 4);
offset += 4;
return offset;
}
#endif
/*!
* Emits the host return stub used by GOAL `nothing`.
*/
inline int emit_return_stub(u8* dst) {
#ifdef __aarch64__
const u32 instr = 0xd65f03c0; // ret
memcpy(dst, &instr, 4);
return 4;
#else
dst[0] = 0xc3; // ret
return 1;
#endif
}
/*!
* Emits a GOAL function that returns zero.
*/
inline int emit_zero_stub(u8* dst) {
#ifdef __aarch64__
// movz x0, #0 followed by ret
const u32 instrs[2] = {arm64::encode_movz_64(0, 0, 0), 0xd65f03c0};
memcpy(dst, instrs, 8);
return 8;
#else
dst[0] = 0x31; // xor eax, eax
dst[1] = 0xc0;
dst[2] = 0xc3; // ret
return 3;
#endif
}