// Generated by rexglue recompile-tests #pragma once #ifndef REX_CONFIG_H_INCLUDED #define REX_CONFIG_H_INCLUDED #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Test code base address (matches -Ttext=0x82010000 linker option) #define REX_IMAGE_BASE 0x{{ image_base }}ull #define REX_IMAGE_SIZE 0x{{ image_size }}ull #define REX_CODE_BASE 0x{{ code_base }}ull #define REX_CODE_SIZE 0x{{ code_size }}ull #define REX_THUNK_RESERVE_SIZE 0x10000ull #endif // REX_CONFIG_H_INCLUDED // Prologue macro (codegen init header defines this with Tracy/alignment hints; // tests just need a no-op) #ifndef REX_FUNC_PROLOGUE #if defined(__clang__) #define REX_FUNC_PROLOGUE() __builtin_assume(((size_t)base & 0x1F) == 0) #elif defined(__GNUC__) #define REX_FUNC_PROLOGUE() \ do { \ if (((size_t)base & 0x1F) != 0) \ __builtin_unreachable(); \ } while (0) #else #define REX_FUNC_PROLOGUE() ((void)0) #endif #endif // Function definition macros (same as codegen init header) #ifndef DECLARE_REX_FUNC #define DECLARE_REX_FUNC(name) \ REX_EXTERN(name); \ REX_EXTERN(__imp__##name) #endif #ifndef DEFINE_REX_FUNC #if defined(__APPLE__) #define DEFINE_REX_FUNC(name) \ REX_EXTERN(__imp__##name); \ REX_WEAK_FUNC(name) { \ __imp__##name(ctx, base); \ } \ REX_EXTERN(__imp__##name) #else #define DEFINE_REX_FUNC(name) \ __attribute__((alias("__imp__" #name))) REX_WEAK_FUNC(name); \ REX_EXTERN(__imp__##name) #endif #endif #if REX_PLATFORM_WIN32 #define REX_PHYS_HOST_OFFSET(addr) (((u32)(addr) >= 0xE0000000u) ? 0x1000u : 0u) #else #define REX_PHYS_HOST_OFFSET(addr) 0u #endif #define REX_RAW_ADDR(x) (base + (u32)(x) + REX_PHYS_HOST_OFFSET(x)) #define REX_LOAD_U8(x) (*(volatile u8*)(base + (u32)(x) + REX_PHYS_HOST_OFFSET(x))) #define REX_LOAD_U16(x) __builtin_bswap16(*(volatile u16*)(base + (u32)(x) + REX_PHYS_HOST_OFFSET(x))) #define REX_LOAD_U32(x) __builtin_bswap32(*(volatile u32*)(base + (u32)(x) + REX_PHYS_HOST_OFFSET(x))) #define REX_LOAD_U64(x) __builtin_bswap64(*(volatile u64*)(base + (u32)(x) + REX_PHYS_HOST_OFFSET(x))) #define REX_LOAD_STRING(x, len) \ std::string_view(reinterpret_cast(base + (u32)(x) + REX_PHYS_HOST_OFFSET(x)), (len)) #define REX_STORE_U8(x, y) (*(volatile u8*)(base + (u32)(x) + REX_PHYS_HOST_OFFSET(x)) = (y)) #define REX_STORE_U16(x, y) (*(volatile u16*)(base + (u32)(x) + REX_PHYS_HOST_OFFSET(x)) = __builtin_bswap16(y)) #define REX_STORE_U32(x, y) (*(volatile u32*)(base + (u32)(x) + REX_PHYS_HOST_OFFSET(x)) = __builtin_bswap32(y)) #define REX_STORE_U64(x, y) (*(volatile u64*)(base + (u32)(x) + REX_PHYS_HOST_OFFSET(x)) = __builtin_bswap64(y)) #define REX_MEMORY_SIZE 0x100000000ull //============================================================================= // Function Dispatch //============================================================================= #define REX_CALL_FUNC(x) x(ctx, base) {% include "codegen/_indirect_call.inja" %} //============================================================================= // Flush Mode //============================================================================= #define REX_SET_FLUSH_MODE(enable) \ do { if (enable) ctx.fpscr.enableFlushMode(); else ctx.fpscr.disableFlushMode(); } while (0) //============================================================================= // Misc //============================================================================= #define REX_UNIMPLEMENTED(addr, opcode) \ throw std::runtime_error("Unimplemented PPC instruction: " opcode) #define REX_QUERY_TIMEBASE() rex::chrono::Clock::QueryGuestTickCount() //============================================================================= // Setjmp/Longjmp //============================================================================= inline std::unordered_map& get_jmp_buf_map() { static thread_local std::unordered_map map; return map; } #define ppc_setjmp(guest_buf_addr) (setjmp(::get_jmp_buf_map()[(guest_buf_addr)])) [[noreturn]] inline void ppc_longjmp(u32 guest_buf_addr, int val) { auto& map = get_jmp_buf_map(); auto it = map.find(guest_buf_addr); if (it != map.end()) { longjmp(it->second, val); } std::abort(); } //============================================================================= // Trap Handling //============================================================================= inline void ppc_trap(PPCContext& ctx, u8* base, u16 trap_type) { switch (trap_type) { case 20: case 26: { auto str = REX_LOAD_STRING(ctx.r3.u32, ctx.r4.u16); REXCPU_DEBUG("(service trap) {}", str); break; } case 0: case 22: REXCPU_WARN("tw/td trap hit (type {})", trap_type); break; case 25: break; default: REXCPU_WARN("Unknown trap type {}", trap_type); break; } } //============================================================================= // Interrupt Locks //============================================================================= inline std::atomic& ppc_global_lock_count_() { static std::atomic count{0}; return count; } #define REX_CHECK_GLOBAL_LOCK() \ ([&]() -> u64 { \ auto lock_ = rex::thread::global_critical_region::AcquireDirect(); \ return ppc_global_lock_count_().load() ? 0 : 0x8000; \ }()) #define REX_ENTER_GLOBAL_LOCK() \ do { \ rex::thread::global_critical_region::mutex().lock(); \ ppc_global_lock_count_().fetch_add(1); \ } while (0) #define REX_LEAVE_GLOBAL_LOCK() \ do { \ auto old_count_ = ppc_global_lock_count_().fetch_sub(1); \ assert(old_count_ >= 1 && "LeaveGlobalLock called without matching EnterGlobalLock"); \ rex::thread::global_critical_region::mutex().unlock(); \ } while (0)