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>
113 lines
2.4 KiB
C
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
|
|
}
|