From 17a4208fe54bea02e16fb6a4d05fe8d73b78b4e3 Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Fri, 19 Dec 2014 21:08:25 -0600 Subject: [PATCH] [AArch64] Adds backpatching routines. Currently supports only integer loadstores. Floating point loadstores will come later. This system is semi based on the ARMv7 backpatching routine, where we need to initialize our backpatch routine sizes prior to actually using them so we know we won't be overwriting any memory. --- Source/Core/Core/CMakeLists.txt | 1 + Source/Core/Core/PowerPC/JitArm64/Jit.cpp | 2 + Source/Core/Core/PowerPC/JitArm64/Jit.h | 15 +- .../PowerPC/JitArm64/JitArm64_BackPatch.cpp | 453 ++++++++++++++++++ 4 files changed, 468 insertions(+), 3 deletions(-) create mode 100644 Source/Core/Core/PowerPC/JitArm64/JitArm64_BackPatch.cpp diff --git a/Source/Core/Core/CMakeLists.txt b/Source/Core/Core/CMakeLists.txt index 4824bc8b7e..c241a1c7ba 100644 --- a/Source/Core/Core/CMakeLists.txt +++ b/Source/Core/Core/CMakeLists.txt @@ -223,6 +223,7 @@ elseif(_M_ARM_64) PowerPC/JitArm64/JitAsm.cpp PowerPC/JitArm64/JitArm64Cache.cpp PowerPC/JitArm64/JitArm64_RegCache.cpp + PowerPC/JitArm64/JitArm64_BackPatch.cpp PowerPC/JitArm64/JitArm64_Branch.cpp PowerPC/JitArm64/JitArm64_Integer.cpp PowerPC/JitArm64/JitArm64_LoadStore.cpp diff --git a/Source/Core/Core/PowerPC/JitArm64/Jit.cpp b/Source/Core/Core/PowerPC/JitArm64/Jit.cpp index acb772d422..22d029030b 100644 --- a/Source/Core/Core/PowerPC/JitArm64/Jit.cpp +++ b/Source/Core/Core/PowerPC/JitArm64/Jit.cpp @@ -25,6 +25,7 @@ void JitArm64::Init() code_block.m_stats = &js.st; code_block.m_gpa = &js.gpa; code_block.m_fpa = &js.fpa; + InitBackpatch(); } void JitArm64::ClearCache() @@ -278,6 +279,7 @@ const u8* JitArm64::DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buf, JitB js.next_inst = ops[i + 1].inst; js.next_compilerPC = ops[i + 1].address; } + if (!ops[i].skip) { if (js.memcheck && (opinfo->flags & FL_USE_FPU)) diff --git a/Source/Core/Core/PowerPC/JitArm64/Jit.h b/Source/Core/Core/PowerPC/JitArm64/Jit.h index d8bde7dcfc..b0b207dea3 100644 --- a/Source/Core/Core/PowerPC/JitArm64/Jit.h +++ b/Source/Core/Core/PowerPC/JitArm64/Jit.h @@ -4,6 +4,8 @@ #pragma once +#include + #include "Common/Arm64Emitter.h" #include "Core/PowerPC/CPUCoreBase.h" @@ -11,6 +13,7 @@ #include "Core/PowerPC/JitArm64/JitArm64_RegCache.h" #include "Core/PowerPC/JitArm64/JitArm64Cache.h" #include "Core/PowerPC/JitArm64/JitAsm.h" +#include "Core/PowerPC/JitArmCommon/BackPatch.h" #include "Core/PowerPC/JitCommon/JitBase.h" #define PPCSTATE_OFF(elem) ((s64)&PowerPC::ppcState.elem - (s64)&PowerPC::ppcState) @@ -31,11 +34,9 @@ public: JitBaseBlockCache *GetBlockCache() { return &blocks; } - const u8 *BackPatch(u8 *codePtr, u32 em_address, void *ctx) { return nullptr; } - bool IsInCodeSpace(u8 *ptr) { return IsInSpace(ptr); } - bool HandleFault(uintptr_t access_address, SContext* ctx) override { return false; } + bool HandleFault(uintptr_t access_address, SContext* ctx) override; void ClearCache(); @@ -106,6 +107,14 @@ private: PPCAnalyst::CodeBuffer code_buffer; + // The key is the backpatch flags + std::map m_backpatch_info; + + // Backpatching routines + bool DisasmLoadStore(const u8* ptr, u32* flags, Arm64Gen::ARM64Reg* reg); + void InitBackpatch(); + u32 EmitBackpatchRoutine(ARM64XEmitter* emit, u32 flags, bool fastmem, bool do_padding, Arm64Gen::ARM64Reg RS, Arm64Gen::ARM64Reg addr); + const u8* DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buf, JitBlock *b); void DoDownCount(); diff --git a/Source/Core/Core/PowerPC/JitArm64/JitArm64_BackPatch.cpp b/Source/Core/Core/PowerPC/JitArm64/JitArm64_BackPatch.cpp new file mode 100644 index 0000000000..0b813e4b72 --- /dev/null +++ b/Source/Core/Core/PowerPC/JitArm64/JitArm64_BackPatch.cpp @@ -0,0 +1,453 @@ +// Copyright 2014 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#include + +#include "Common/CommonTypes.h" +#include "Common/StringUtil.h" + +#include "Core/HW/Memmap.h" +#include "Core/PowerPC/JitArm64/Jit.h" +#include "Core/PowerPC/JitArmCommon/BackPatch.h" + +using namespace Arm64Gen; + +static void DoBacktrace(uintptr_t access_address, SContext* ctx) +{ + for (int i = 0; i < 30; i += 2) + ERROR_LOG(DYNA_REC, "R%d: 0x%016llx\tR%d: 0x%016llx", i, ctx->CTX_REG(i), i + 1, ctx->CTX_REG(i + 1)); + + ERROR_LOG(DYNA_REC, "R30: 0x%016llx\tSP: 0x%016llx", ctx->CTX_REG(30), ctx->CTX_SP); + + ERROR_LOG(DYNA_REC, "Access Address: 0x%016lx", access_address); + ERROR_LOG(DYNA_REC, "PC: 0x%016llx", ctx->CTX_PC); + + ERROR_LOG(DYNA_REC, "Memory Around PC"); + + std::string pc_memory = ""; + for (u64 pc = (ctx->CTX_PC - 32); pc < (ctx->CTX_PC + 32); pc += 16) + { + pc_memory += StringFromFormat("%08x%08x%08x%08x", + *(u32*)pc, *(u32*)(pc + 4), *(u32*)(pc + 8), *(u32*)(pc + 12)); + + ERROR_LOG(DYNA_REC, "0x%016lx: %08x %08x %08x %08x", + pc, *(u32*)pc, *(u32*)(pc + 4), *(u32*)(pc + 8), *(u32*)(pc + 12)); + } + + ERROR_LOG(DYNA_REC, "Full block: %s", pc_memory.c_str()); +} + +bool JitArm64::DisasmLoadStore(const u8* ptr, u32* flags, ARM64Reg* reg) +{ + u32 inst = *(u32*)ptr; + u32 prev_inst = *(u32*)(ptr - 4); + u32 next_inst = *(u32*)(ptr + 4); + + u8 op = (inst >> 22) & 0xFF; + u8 size = (inst >> 30) & 0x3; + + if (size == 0) // 8-bit + *flags |= BackPatchInfo::FLAG_SIZE_8; + else if (size == 1) // 16-bit + *flags |= BackPatchInfo::FLAG_SIZE_16; + else // 32-bit + *flags |= BackPatchInfo::FLAG_SIZE_32; + + if (op == 0xE5) // Load + { + *flags |= BackPatchInfo::FLAG_LOAD; + *reg = (ARM64Reg)(inst & 0x1F); + if ((next_inst & 0x7FFFF000) != 0x5AC00000) // REV + *flags |= BackPatchInfo::FLAG_REVERSE; + if ((next_inst & 0x7F800000) == 0x13000000) // SXTH + *flags |= BackPatchInfo::FLAG_EXTEND; + return true; + } + else if (op == 0xE4) // Store + { + *flags |= BackPatchInfo::FLAG_STORE; + + if (size == 0) // 8-bit + *reg = (ARM64Reg)(inst & 0x1F); + else // 16-bit/32-bit register is in previous REV instruction + *reg = (ARM64Reg)((prev_inst >> 5) & 0x1F); + return true; + } + + return false; +} + +u32 JitArm64::EmitBackpatchRoutine(ARM64XEmitter* emit, u32 flags, bool fastmem, bool do_padding, ARM64Reg RS, ARM64Reg addr) +{ + u32 trouble_offset = 0; + const u8* code_base = emit->GetCodePtr(); + + if (fastmem) + { + MOVK(addr, ((u64)Memory::base >> 32) & 0xFFFF, SHIFT_32); + + if (flags & BackPatchInfo::FLAG_STORE && + flags & (BackPatchInfo::FLAG_SIZE_F32 | BackPatchInfo::FLAG_SIZE_F64)) + { + } + else if (flags & BackPatchInfo::FLAG_LOAD && + flags & (BackPatchInfo::FLAG_SIZE_F32 | BackPatchInfo::FLAG_SIZE_F64)) + { + } + else if (flags & BackPatchInfo::FLAG_STORE) + { + ARM64Reg temp = W0; + if (flags & BackPatchInfo::FLAG_SIZE_32) + emit->REV32(temp, RS); + else if (flags & BackPatchInfo::FLAG_SIZE_16) + emit->REV16(temp, RS); + + trouble_offset = (emit->GetCodePtr() - code_base) / 4; + + if (flags & BackPatchInfo::FLAG_SIZE_32) + emit->STR(INDEX_UNSIGNED, temp, addr, 0); + else if (flags & BackPatchInfo::FLAG_SIZE_16) + emit->STRH(INDEX_UNSIGNED, temp, addr, 0); + else + { + emit->STRB(INDEX_UNSIGNED, RS, addr, 0); + emit->HINT(HINT_NOP); + } + } + else + { + trouble_offset = (emit->GetCodePtr() - code_base) / 4; + + if (flags & BackPatchInfo::FLAG_SIZE_32) + emit->LDR(INDEX_UNSIGNED, RS, addr, 0); + else if (flags & BackPatchInfo::FLAG_SIZE_16) + emit->LDRH(INDEX_UNSIGNED, RS, addr, 0); + else if (flags & BackPatchInfo::FLAG_SIZE_8) + emit->LDRB(INDEX_UNSIGNED, RS, addr, 0); + + if (!(flags & BackPatchInfo::FLAG_REVERSE)) + { + if (flags & BackPatchInfo::FLAG_SIZE_32) + emit->REV32(RS, RS); + else if (flags & BackPatchInfo::FLAG_SIZE_16) + emit->REV16(RS, RS); + } + + if (flags & BackPatchInfo::FLAG_EXTEND) + emit->SXTH(RS, RS); + } + } + else + { + if (flags & BackPatchInfo::FLAG_STORE && + flags & (BackPatchInfo::FLAG_SIZE_F32 | BackPatchInfo::FLAG_SIZE_F64)) + { + } + else if (flags & BackPatchInfo::FLAG_LOAD && + flags & (BackPatchInfo::FLAG_SIZE_F32 | BackPatchInfo::FLAG_SIZE_F64)) + { + } + else if (flags & BackPatchInfo::FLAG_STORE) + { + emit->MOV(W0, RS); + + if (flags & BackPatchInfo::FLAG_SIZE_32) + emit->MOVI2R(X30, (u64)&Memory::Write_U32); + else if (flags & BackPatchInfo::FLAG_SIZE_16) + emit->MOVI2R(X30, (u64)&Memory::Write_U16); + else + emit->MOVI2R(X30, (u64)&Memory::Write_U8); + + emit->BLR(X30); + } + else + { + if (flags & BackPatchInfo::FLAG_SIZE_32) + emit->MOVI2R(X30, (u64)&Memory::Read_U32); + else if (flags & BackPatchInfo::FLAG_SIZE_16) + emit->MOVI2R(X30, (u64)&Memory::Read_U16); + else if (flags & BackPatchInfo::FLAG_SIZE_8) + emit->MOVI2R(X30, (u64)&Memory::Read_U8); + + emit->BLR(X30); + + if (!(flags & BackPatchInfo::FLAG_REVERSE)) + { + emit->MOV(RS, W0); + } + else + { + if (flags & BackPatchInfo::FLAG_SIZE_32) + emit->REV32(RS, W0); + else if (flags & BackPatchInfo::FLAG_SIZE_16) + emit->REV16(RS, W0); + } + + if (flags & BackPatchInfo::FLAG_EXTEND) + emit->SXTH(RS, RS); + } + } + + if (do_padding) + { + BackPatchInfo& info = m_backpatch_info[flags]; + u32 num_insts_max = std::max(info.m_fastmem_size, info.m_slowmem_size); + + u32 code_size = emit->GetCodePtr() - code_base; + code_size /= 4; + + for (u32 i = 0; i < (num_insts_max - code_size); ++i) + emit->HINT(HINT_NOP); + } + + return trouble_offset; +} + +bool JitArm64::HandleFault(uintptr_t access_address, SContext* ctx) +{ + if (access_address < (uintptr_t)Memory::base) + { + ERROR_LOG(DYNA_REC, "Exception handler - access below memory space. PC: 0x%016llx 0x%016lx < 0x%016lx", ctx->CTX_PC, access_address, (uintptr_t)Memory::base); + + DoBacktrace(access_address, ctx); + return false; + } + + if (!IsInSpace((u8*)ctx->CTX_PC)) + { + ERROR_LOG(DYNA_REC, "Backpatch location not within codespace 0x%016llx(0x%08x)", ctx->CTX_PC, Common::swap32(*(u32*)ctx->CTX_PC)); + + DoBacktrace(access_address, ctx); + return false; + } + + ARM64Reg reg = INVALID_REG; + u32 flags = 0; + + if (!DisasmLoadStore((const u8*)ctx->CTX_PC, &flags, ®)) + { + ERROR_LOG(DYNA_REC, "Error disassembling address 0x%016llx(0x%08x)", ctx->CTX_PC, Common::swap32(*(u32*)ctx->CTX_PC)); + + DoBacktrace(access_address, ctx); + return false; + } + + BackPatchInfo& info = m_backpatch_info[flags]; + ARM64XEmitter emitter((u8*)(ctx->CTX_PC - info.m_fastmem_trouble_inst_offset * 4)); + u64 new_pc = (u64)emitter.GetCodePtr(); + + // Slowmem routine doesn't need the address location + // It is already in the correct location + EmitBackpatchRoutine(&emitter, flags, false, true, reg, INVALID_REG); + + emitter.FlushIcache(); + ctx->CTX_PC = new_pc; + + // Wipe the top bits of the addr_register + if (flags & BackPatchInfo::FLAG_STORE) + ctx->CTX_REG(1) &= 0xFFFFFFFFUll; + else + ctx->CTX_REG(0) &= 0xFFFFFFFFUll; + return true; +} + +void JitArm64::InitBackpatch() +{ + u32 flags = 0; + BackPatchInfo info; + u8* code_base = GetWritableCodePtr(); + u8* code_end; + + // Loads + { + // 8bit + { + flags = + BackPatchInfo::FLAG_LOAD | + BackPatchInfo::FLAG_SIZE_8; + EmitBackpatchRoutine(this, flags, false, false, W0, X1); + code_end = GetWritableCodePtr(); + info.m_slowmem_size = (code_end - code_base) / 4; + + SetCodePtr(code_base); + + info.m_fastmem_trouble_inst_offset = + EmitBackpatchRoutine(this, flags, true, false, W0, X1); + code_end = GetWritableCodePtr(); + info.m_fastmem_size = (code_end - code_base) / 4; + + SetCodePtr(code_base); + + m_backpatch_info[flags] = info; + } + // 16bit + { + flags = + BackPatchInfo::FLAG_LOAD | + BackPatchInfo::FLAG_SIZE_16; + EmitBackpatchRoutine(this, flags, false, false, W0, X1); + code_end = GetWritableCodePtr(); + info.m_slowmem_size = (code_end - code_base) / 4; + + SetCodePtr(code_base); + + info.m_fastmem_trouble_inst_offset = + EmitBackpatchRoutine(this, flags, true, false, W0, X1); + code_end = GetWritableCodePtr(); + info.m_fastmem_size = (code_end - code_base) / 4; + + SetCodePtr(code_base); + + m_backpatch_info[flags] = info; + } + // 32bit + { + flags = + BackPatchInfo::FLAG_LOAD | + BackPatchInfo::FLAG_SIZE_32; + EmitBackpatchRoutine(this, flags, false, false, W0, X1); + code_end = GetWritableCodePtr(); + info.m_slowmem_size = (code_end - code_base) / 4; + + SetCodePtr(code_base); + + info.m_fastmem_trouble_inst_offset = + EmitBackpatchRoutine(this, flags, true, false, W0, X1); + code_end = GetWritableCodePtr(); + info.m_fastmem_size = (code_end - code_base) / 4; + + SetCodePtr(code_base); + + m_backpatch_info[flags] = info; + } + // 16bit - Extend + { + flags = + BackPatchInfo::FLAG_LOAD | + BackPatchInfo::FLAG_SIZE_16 | + BackPatchInfo::FLAG_EXTEND; + EmitBackpatchRoutine(this, flags, false, false, W0, X1); + code_end = GetWritableCodePtr(); + info.m_slowmem_size = (code_end - code_base) / 4; + + SetCodePtr(code_base); + + info.m_fastmem_trouble_inst_offset = + EmitBackpatchRoutine(this, flags, true, false, W0, X1); + code_end = GetWritableCodePtr(); + info.m_fastmem_size = (code_end - code_base) / 4; + + SetCodePtr(code_base); + + m_backpatch_info[flags] = info; + } + // 16bit - Reverse + { + flags = + BackPatchInfo::FLAG_LOAD | + BackPatchInfo::FLAG_SIZE_16 | + BackPatchInfo::FLAG_REVERSE; + EmitBackpatchRoutine(this, flags, false, false, W0, X1); + code_end = GetWritableCodePtr(); + info.m_slowmem_size = (code_end - code_base) / 4; + + SetCodePtr(code_base); + + info.m_fastmem_trouble_inst_offset = + EmitBackpatchRoutine(this, flags, true, false, W0, X1); + code_end = GetWritableCodePtr(); + info.m_fastmem_size = (code_end - code_base) / 4; + + SetCodePtr(code_base); + + m_backpatch_info[flags] = info; + } + // 32bit - Reverse + { + flags = + BackPatchInfo::FLAG_LOAD | + BackPatchInfo::FLAG_SIZE_32 | + BackPatchInfo::FLAG_REVERSE; + EmitBackpatchRoutine(this, flags, false, false, W0, X1); + code_end = GetWritableCodePtr(); + info.m_slowmem_size = (code_end - code_base) / 4; + + SetCodePtr(code_base); + + info.m_fastmem_trouble_inst_offset = + EmitBackpatchRoutine(this, flags, true, false, W0, X1); + code_end = GetWritableCodePtr(); + info.m_fastmem_size = (code_end - code_base) / 4; + + SetCodePtr(code_base); + + m_backpatch_info[flags] = info; + } + } + + // Stores + { + // 8bit + { + flags = + BackPatchInfo::FLAG_STORE | + BackPatchInfo::FLAG_SIZE_8; + EmitBackpatchRoutine(this, flags, false, false, W0, X1); + code_end = GetWritableCodePtr(); + info.m_slowmem_size = (code_end - code_base) / 4; + + SetCodePtr(code_base); + + info.m_fastmem_trouble_inst_offset = + EmitBackpatchRoutine(this, flags, true, false, W0, X1); + code_end = GetWritableCodePtr(); + info.m_fastmem_size = (code_end - code_base) / 4; + + SetCodePtr(code_base); + + m_backpatch_info[flags] = info; + } + // 16bit + { + flags = + BackPatchInfo::FLAG_STORE | + BackPatchInfo::FLAG_SIZE_16; + EmitBackpatchRoutine(this, flags, false, false, W0, X1); + code_end = GetWritableCodePtr(); + info.m_slowmem_size = (code_end - code_base) / 4; + + SetCodePtr(code_base); + + info.m_fastmem_trouble_inst_offset = + EmitBackpatchRoutine(this, flags, true, false, W0, X1); + code_end = GetWritableCodePtr(); + info.m_fastmem_size = (code_end - code_base) / 4; + + SetCodePtr(code_base); + + m_backpatch_info[flags] = info; + } + // 32bit + { + flags = + BackPatchInfo::FLAG_STORE | + BackPatchInfo::FLAG_SIZE_32; + EmitBackpatchRoutine(this, flags, false, false, W0, X1); + code_end = GetWritableCodePtr(); + info.m_slowmem_size = (code_end - code_base) / 4; + + SetCodePtr(code_base); + + info.m_fastmem_trouble_inst_offset = + EmitBackpatchRoutine(this, flags, true, false, W0, X1); + code_end = GetWritableCodePtr(); + info.m_fastmem_size = (code_end - code_base) / 4; + + SetCodePtr(code_base); + + m_backpatch_info[flags] = info; + } + } +} +