From 615fcc621dcf823a25f104d97097f3bd7270ba12 Mon Sep 17 00:00:00 2001 From: MerryMage Date: Tue, 21 Mar 2017 18:10:25 +0000 Subject: [PATCH 1/3] CodeBlock: Add support for multiple children --- Source/Core/Common/CodeBlock.h | 53 +++++++++++++++++++++------------- 1 file changed, 33 insertions(+), 20 deletions(-) diff --git a/Source/Core/Common/CodeBlock.h b/Source/Core/Common/CodeBlock.h index 28a4919e66..9eb1732ec7 100644 --- a/Source/Core/Common/CodeBlock.h +++ b/Source/Core/Common/CodeBlock.h @@ -5,6 +5,7 @@ #pragma once #include +#include #include "Common/Assert.h" #include "Common/CommonTypes.h" @@ -27,12 +28,13 @@ private: protected: u8* region = nullptr; + // Size of region we can use. size_t region_size = 0; - size_t parent_region_size = 0; + // Original size of the region we allocated. + size_t total_region_size = 0; - bool m_has_child = false; bool m_is_child = false; - CodeBlock* m_child = nullptr; + std::vector m_children; public: virtual ~CodeBlock() @@ -45,7 +47,8 @@ public: virtual void AllocCodeSpace(size_t size, bool need_low = true) { region_size = size; - region = static_cast(Common::AllocateExecutableMemory(region_size, need_low)); + total_region_size = size; + region = static_cast(Common::AllocateExecutableMemory(total_region_size, need_low)); T::SetCodePtr(region); } @@ -60,14 +63,16 @@ public: // Call this when shutting down. Don't rely on the destructor, even though it'll do the job. void FreeCodeSpace() { - Common::FreeMemoryPages(region, region_size); + _assert_(!m_is_child); + Common::FreeMemoryPages(region, total_region_size); region = nullptr; region_size = 0; - parent_region_size = 0; - if (m_has_child) + total_region_size = 0; + for (CodeBlock* child : m_children) { - m_child->region = nullptr; - m_child->region_size = 0; + child->region = nullptr; + child->region_size = 0; + child->total_region_size = 0; } } @@ -78,7 +83,8 @@ public: void ResetCodePtr() { T::SetCodePtr(region); } size_t GetSpaceLeft() const { - return (m_has_child ? parent_region_size : region_size) - (T::GetCodePtr() - region); + _assert_(static_cast(T::GetCodePtr() - region) < region_size); + return region_size - (T::GetCodePtr() - region); } bool IsAlmostFull() const @@ -86,16 +92,23 @@ public: // This should be bigger than the biggest block ever. return GetSpaceLeft() < 0x10000; } - void AddChildCodeSpace(CodeBlock* child, size_t size) + + bool HasChildren() const { return region_size != total_region_size; } + u8* AllocChildCodeSpace(size_t child_size) { - _assert_msg_(DYNA_REC, !m_has_child, "Already have a child! Can't have another!"); - m_child = child; - m_has_child = true; - m_child->m_is_child = true; - u8* child_region = region + region_size - size; - m_child->region = child_region; - m_child->region_size = size; - m_child->ResetCodePtr(); - parent_region_size = region_size - size; + _assert_msg_(DYNA_REG, child_size < GetSpaceLeft(), "Insufficient space for child allocation."); + u8* child_region = region + region_size - child_size; + region_size -= child_size; + return child_region; + } + void AddChildCodeSpace(CodeBlock* child, size_t child_size) + { + u8* child_region = AllocChildCodeSpace(child_size); + child->m_is_child = true; + child->region = child_region; + child->region_size = child_size; + child->total_region_size = child_size; + child->ResetCodePtr(); + m_children.emplace_back(child); } }; From da434e1a1cf1afb17945a38b5fce7c022e8a8dae Mon Sep 17 00:00:00 2001 From: MerryMage Date: Tue, 21 Mar 2017 18:37:48 +0000 Subject: [PATCH 2/3] ConstantPool: Externalize memory allocation --- Source/Core/Common/CodeBlock.h | 4 +- Source/Core/Core/PowerPC/Jit64/Jit.cpp | 6 ++- .../Core/PowerPC/Jit64Common/ConstantPool.cpp | 40 +++++++++---------- .../Core/PowerPC/Jit64Common/ConstantPool.h | 16 ++++---- .../Core/PowerPC/Jit64Common/EmuCodeBlock.cpp | 12 ------ .../Core/PowerPC/Jit64Common/EmuCodeBlock.h | 5 +-- 6 files changed, 33 insertions(+), 50 deletions(-) diff --git a/Source/Core/Common/CodeBlock.h b/Source/Core/Common/CodeBlock.h index 9eb1732ec7..9c3da1058b 100644 --- a/Source/Core/Common/CodeBlock.h +++ b/Source/Core/Common/CodeBlock.h @@ -44,7 +44,7 @@ public: } // Call this before you generate any code. - virtual void AllocCodeSpace(size_t size, bool need_low = true) + void AllocCodeSpace(size_t size, bool need_low = true) { region_size = size; total_region_size = size; @@ -54,7 +54,7 @@ public: // Always clear code space with breakpoints, so that if someone accidentally executes // uninitialized, it just breaks into the debugger. - virtual void ClearCodeSpace() + void ClearCodeSpace() { PoisonMemory(); ResetCodePtr(); diff --git a/Source/Core/Core/PowerPC/Jit64/Jit.cpp b/Source/Core/Core/PowerPC/Jit64/Jit.cpp index 74ca3ffd96..02b64bf729 100644 --- a/Source/Core/Core/PowerPC/Jit64/Jit.cpp +++ b/Source/Core/Core/PowerPC/Jit64/Jit.cpp @@ -230,7 +230,9 @@ void Jit64::Init() fpr.SetEmitter(this); trampolines.Init(jo.memcheck ? TRAMPOLINE_CODE_SIZE_MMU : TRAMPOLINE_CODE_SIZE); - AllocCodeSpace(CODE_SIZE); + const size_t constpool_size = m_const_pool.CONST_POOL_SIZE; + AllocCodeSpace(CODE_SIZE + constpool_size); + m_const_pool.Init(AllocChildCodeSpace(constpool_size), constpool_size); // BLR optimization has the same consequences as block linking, as well as // depending on the fault handler to be safe in the event of excessive BL. @@ -262,6 +264,7 @@ void Jit64::ClearCache() blocks.Clear(); trampolines.ClearCodeSpace(); m_far_code.ClearCodeSpace(); + m_const_pool.Clear(); ClearCodeSpace(); Clear(); UpdateMemoryOptions(); @@ -276,6 +279,7 @@ void Jit64::Shutdown() trampolines.Shutdown(); asm_routines.Shutdown(); m_far_code.Shutdown(); + m_const_pool.Shutdown(); } void Jit64::FallBackToInterpreter(UGeckoInstruction inst) diff --git a/Source/Core/Core/PowerPC/Jit64Common/ConstantPool.cpp b/Source/Core/Core/PowerPC/Jit64Common/ConstantPool.cpp index 20c3f46802..cd8da238a0 100644 --- a/Source/Core/Core/PowerPC/Jit64Common/ConstantPool.cpp +++ b/Source/Core/Core/PowerPC/Jit64Common/ConstantPool.cpp @@ -10,21 +10,31 @@ #include "Common/x64Emitter.h" #include "Core/PowerPC/Jit64Common/ConstantPool.h" -ConstantPool::ConstantPool(Gen::X64CodeBlock* parent) : m_parent(parent) -{ -} +ConstantPool::ConstantPool() = default; ConstantPool::~ConstantPool() = default; -void ConstantPool::AllocCodeSpace() +void ConstantPool::Init(void* memory, size_t size) { - _assert_(!m_current_ptr); - Init(); + m_region = memory; + m_region_size = size; + Clear(); } -void ConstantPool::ClearCodeSpace() +void ConstantPool::Clear() { - Init(); + m_current_ptr = m_region; + m_remaining_size = m_region_size; + m_const_info.clear(); +} + +void ConstantPool::Shutdown() +{ + m_region = nullptr; + m_region_size = 0; + m_current_ptr = nullptr; + m_remaining_size = 0; + m_const_info.clear(); } Gen::OpArg ConstantPool::GetConstantOpArg(const void* value, size_t element_size, @@ -51,17 +61,3 @@ Gen::OpArg ConstantPool::GetConstantOpArg(const void* value, size_t element_size u8* location = static_cast(info.m_location); return Gen::M(location + element_size * index); } - -void ConstantPool::Init() -{ - // If execution happens to run to the start of the constant pool, halt. - m_parent->INT3(); - m_parent->AlignCode16(); - - // Reserve a block of memory CONST_POOL_SIZE in size. - m_current_ptr = m_parent->GetWritableCodePtr(); - m_parent->ReserveCodeSpace(CONST_POOL_SIZE); - - m_remaining_size = CONST_POOL_SIZE; - m_const_info.clear(); -} diff --git a/Source/Core/Core/PowerPC/Jit64Common/ConstantPool.h b/Source/Core/Core/PowerPC/Jit64Common/ConstantPool.h index eba5cbec0f..732a41af9d 100644 --- a/Source/Core/Core/PowerPC/Jit64Common/ConstantPool.h +++ b/Source/Core/Core/PowerPC/Jit64Common/ConstantPool.h @@ -22,13 +22,12 @@ public: static constexpr size_t CONST_POOL_SIZE = 1024 * 32; static constexpr size_t ALIGNMENT = 16; - explicit ConstantPool(Gen::X64CodeBlock* parent); + ConstantPool(); ~ConstantPool(); - // ConstantPool reserves CONST_POOL_SIZE bytes from parent, and uses - // that space to store its constants. - void AllocCodeSpace(); - void ClearCodeSpace(); + void Init(void* memory, size_t size); + void Clear(); + void Shutdown(); // Copies the value into the pool if it doesn't exist. Returns a pointer // to existing values if they were already copied. Pointer equality is @@ -37,16 +36,15 @@ public: size_t index); private: - void Init(); - struct ConstantInfo { void* m_location; size_t m_size; }; - Gen::X64CodeBlock* m_parent; + void* m_region = nullptr; + size_t m_region_size = 0; void* m_current_ptr = nullptr; - size_t m_remaining_size = CONST_POOL_SIZE; + size_t m_remaining_size = 0; std::map m_const_info; }; diff --git a/Source/Core/Core/PowerPC/Jit64Common/EmuCodeBlock.cpp b/Source/Core/Core/PowerPC/Jit64Common/EmuCodeBlock.cpp index 3a35c8dfa7..7408765a68 100644 --- a/Source/Core/Core/PowerPC/Jit64Common/EmuCodeBlock.cpp +++ b/Source/Core/Core/PowerPC/Jit64Common/EmuCodeBlock.cpp @@ -40,18 +40,6 @@ OpArg FixImmediate(int access_size, OpArg arg) } } // Anonymous namespace -void EmuCodeBlock::ClearCodeSpace() -{ - X64CodeBlock::ClearCodeSpace(); - m_const_pool.ClearCodeSpace(); -} - -void EmuCodeBlock::AllocCodeSpace(size_t size, bool need_low) -{ - X64CodeBlock::AllocCodeSpace(size + ConstantPool::CONST_POOL_SIZE, need_low); - m_const_pool.AllocCodeSpace(); -} - void EmuCodeBlock::MemoryExceptionCheck() { // TODO: We really should untangle the trampolines, exception handlers and diff --git a/Source/Core/Core/PowerPC/Jit64Common/EmuCodeBlock.h b/Source/Core/Core/PowerPC/Jit64Common/EmuCodeBlock.h index 08bafb49d7..244494a31e 100644 --- a/Source/Core/Core/PowerPC/Jit64Common/EmuCodeBlock.h +++ b/Source/Core/Core/PowerPC/Jit64Common/EmuCodeBlock.h @@ -23,9 +23,6 @@ class Mapping; class EmuCodeBlock : public Gen::X64CodeBlock { public: - void ClearCodeSpace() override; - void AllocCodeSpace(size_t size, bool need_low = true) override; - void MemoryExceptionCheck(); // Simple functions to switch between near and far code emitting @@ -121,7 +118,7 @@ public: void Clear(); protected: - ConstantPool m_const_pool{this}; + ConstantPool m_const_pool; FarCodeCache m_far_code; u8* m_near_code; // Backed up when we switch to far code. From cb7d9556bfb776316b0aa203e429770fcaf87976 Mon Sep 17 00:00:00 2001 From: MerryMage Date: Tue, 21 Mar 2017 18:51:07 +0000 Subject: [PATCH 3/3] Jit64: Merge memory allocations into a single allocation Instead of each component allocating their own memory, we instead allocate the memory once and divvy that up among the components as required. This ensures that relative memory offsets remain within architecture limits. --- Source/Core/Core/PowerPC/Jit64/Jit.cpp | 13 ++++++++----- Source/Core/Core/PowerPC/Jit64/JitAsm.cpp | 12 ++++++++++-- Source/Core/Core/PowerPC/Jit64/JitAsm.h | 15 +++++---------- .../Core/PowerPC/Jit64Common/FarCodeCache.cpp | 4 +--- .../Core/PowerPC/Jit64Common/FarCodeCache.h | 2 +- .../PowerPC/Jit64Common/TrampolineCache.cpp | 10 ---------- .../Core/PowerPC/Jit64Common/TrampolineCache.h | 2 -- Source/Core/Core/PowerPC/Jit64IL/JitIL.cpp | 17 +++++++++++------ 8 files changed, 36 insertions(+), 39 deletions(-) diff --git a/Source/Core/Core/PowerPC/Jit64/Jit.cpp b/Source/Core/Core/PowerPC/Jit64/Jit.cpp index 02b64bf729..6d506b3d06 100644 --- a/Source/Core/Core/PowerPC/Jit64/Jit.cpp +++ b/Source/Core/Core/PowerPC/Jit64/Jit.cpp @@ -229,9 +229,14 @@ void Jit64::Init() gpr.SetEmitter(this); fpr.SetEmitter(this); - trampolines.Init(jo.memcheck ? TRAMPOLINE_CODE_SIZE_MMU : TRAMPOLINE_CODE_SIZE); + const size_t routines_size = asm_routines.CODE_SIZE; + const size_t trampolines_size = jo.memcheck ? TRAMPOLINE_CODE_SIZE_MMU : TRAMPOLINE_CODE_SIZE; + const size_t farcode_size = jo.memcheck ? FARCODE_SIZE_MMU : FARCODE_SIZE; const size_t constpool_size = m_const_pool.CONST_POOL_SIZE; - AllocCodeSpace(CODE_SIZE + constpool_size); + AllocCodeSpace(CODE_SIZE + routines_size + trampolines_size + farcode_size + constpool_size); + AddChildCodeSpace(&asm_routines, routines_size); + AddChildCodeSpace(&trampolines, trampolines_size); + AddChildCodeSpace(&m_far_code, farcode_size); m_const_pool.Init(AllocChildCodeSpace(constpool_size), constpool_size); // BLR optimization has the same consequences as block linking, as well as @@ -250,7 +255,7 @@ void Jit64::Init() // important: do this *after* generating the global asm routines, because we can't use farcode in // them. // it'll crash because the farcode functions get cleared on JIT clears. - m_far_code.Init(jo.memcheck ? FARCODE_SIZE_MMU : FARCODE_SIZE); + m_far_code.Init(); Clear(); code_block.m_stats = &js.st; @@ -276,8 +281,6 @@ void Jit64::Shutdown() FreeCodeSpace(); blocks.Shutdown(); - trampolines.Shutdown(); - asm_routines.Shutdown(); m_far_code.Shutdown(); m_const_pool.Shutdown(); } diff --git a/Source/Core/Core/PowerPC/Jit64/JitAsm.cpp b/Source/Core/Core/PowerPC/Jit64/JitAsm.cpp index 1239570dbd..cb2d12156d 100644 --- a/Source/Core/Core/PowerPC/Jit64/JitAsm.cpp +++ b/Source/Core/Core/PowerPC/Jit64/JitAsm.cpp @@ -2,7 +2,7 @@ // Licensed under GPLv2+ // Refer to the license.txt file included. -#include "Core/PowerPC/Jit64/Jit.h" +#include "Core/PowerPC/Jit64/JitAsm.h" #include "Common/CommonTypes.h" #include "Common/JitRegister.h" #include "Common/x64ABI.h" @@ -11,7 +11,7 @@ #include "Core/CoreTiming.h" #include "Core/HW/CPU.h" #include "Core/HW/Memmap.h" -#include "Core/PowerPC/Jit64/JitAsm.h" +#include "Core/PowerPC/Jit64/Jit.h" #include "Core/PowerPC/Jit64Common/Jit64PowerPCState.h" #include "Core/PowerPC/PowerPC.h" @@ -20,6 +20,14 @@ using namespace Gen; // Not PowerPC state. Can't put in 'this' because it's out of range... static void* s_saved_rsp; +void Jit64AsmRoutineManager::Init(u8* stack_top) +{ + m_const_pool.Init(AllocChildCodeSpace(4096), 4096); + m_stack_top = stack_top; + Generate(); + WriteProtect(); +} + // PLAN: no more block numbers - crazy opcodes just contain offset within // dynarec buffer // At this offset - 4, there is an int specifying the block number. diff --git a/Source/Core/Core/PowerPC/Jit64/JitAsm.h b/Source/Core/Core/PowerPC/Jit64/JitAsm.h index 2cdac46b45..6aaad10031 100644 --- a/Source/Core/Core/PowerPC/Jit64/JitAsm.h +++ b/Source/Core/Core/PowerPC/Jit64/JitAsm.h @@ -34,16 +34,11 @@ private: u8* m_stack_top; public: - void Init(u8* stack_top) - { - m_stack_top = stack_top; - // NOTE: When making large additions to the AsmCommon code, you might - // want to ensure this number is big enough. - AllocCodeSpace(16384); - Generate(); - WriteProtect(); - } + // NOTE: When making large additions to the AsmCommon code, you might + // want to ensure this number is big enough. + static constexpr size_t CODE_SIZE = 16384; + + void Init(u8* stack_top); - void Shutdown() { FreeCodeSpace(); } void ResetStack(Gen::X64CodeBlock& emitter); }; diff --git a/Source/Core/Core/PowerPC/Jit64Common/FarCodeCache.cpp b/Source/Core/Core/PowerPC/Jit64Common/FarCodeCache.cpp index 72909f6a6a..7efdd6f331 100644 --- a/Source/Core/Core/PowerPC/Jit64Common/FarCodeCache.cpp +++ b/Source/Core/Core/PowerPC/Jit64Common/FarCodeCache.cpp @@ -4,15 +4,13 @@ #include "Core/PowerPC/Jit64Common/FarCodeCache.h" -void FarCodeCache::Init(size_t size) +void FarCodeCache::Init() { - AllocCodeSpace(size); m_enabled = true; } void FarCodeCache::Shutdown() { - FreeCodeSpace(); m_enabled = false; } diff --git a/Source/Core/Core/PowerPC/Jit64Common/FarCodeCache.h b/Source/Core/Core/PowerPC/Jit64Common/FarCodeCache.h index bc82045870..9cbbf2c052 100644 --- a/Source/Core/Core/PowerPC/Jit64Common/FarCodeCache.h +++ b/Source/Core/Core/PowerPC/Jit64Common/FarCodeCache.h @@ -17,7 +17,7 @@ constexpr size_t FARCODE_SIZE_MMU = 1024 * 1024 * 48; class FarCodeCache : public Gen::X64CodeBlock { public: - void Init(size_t size); + void Init(); void Shutdown(); bool Enabled() const; diff --git a/Source/Core/Core/PowerPC/Jit64Common/TrampolineCache.cpp b/Source/Core/Core/PowerPC/Jit64Common/TrampolineCache.cpp index 5fa7c2d911..25848a1a24 100644 --- a/Source/Core/Core/PowerPC/Jit64Common/TrampolineCache.cpp +++ b/Source/Core/Core/PowerPC/Jit64Common/TrampolineCache.cpp @@ -23,21 +23,11 @@ using namespace Gen; -void TrampolineCache::Init(size_t size) -{ - AllocCodeSpace(size); -} - void TrampolineCache::ClearCodeSpace() { X64CodeBlock::ClearCodeSpace(); } -void TrampolineCache::Shutdown() -{ - FreeCodeSpace(); -} - const u8* TrampolineCache::GenerateTrampoline(const TrampolineInfo& info) { if (info.read) diff --git a/Source/Core/Core/PowerPC/Jit64Common/TrampolineCache.h b/Source/Core/Core/PowerPC/Jit64Common/TrampolineCache.h index 769c4b4c84..9fc9a0b940 100644 --- a/Source/Core/Core/PowerPC/Jit64Common/TrampolineCache.h +++ b/Source/Core/Core/PowerPC/Jit64Common/TrampolineCache.h @@ -24,8 +24,6 @@ class TrampolineCache : public EmuCodeBlock const u8* GenerateWriteTrampoline(const TrampolineInfo& info); public: - void Init(size_t size); - void Shutdown(); const u8* GenerateTrampoline(const TrampolineInfo& info); void ClearCodeSpace(); }; diff --git a/Source/Core/Core/PowerPC/Jit64IL/JitIL.cpp b/Source/Core/Core/PowerPC/Jit64IL/JitIL.cpp index 9a9730efc0..c2200791f2 100644 --- a/Source/Core/Core/PowerPC/Jit64IL/JitIL.cpp +++ b/Source/Core/Core/PowerPC/Jit64IL/JitIL.cpp @@ -262,12 +262,19 @@ void JitIL::Init() jo.accurateSinglePrecision = false; UpdateMemoryOptions(); - trampolines.Init(jo.memcheck ? TRAMPOLINE_CODE_SIZE_MMU : TRAMPOLINE_CODE_SIZE); - AllocCodeSpace(CODE_SIZE); + const size_t routines_size = asm_routines.CODE_SIZE; + const size_t trampolines_size = jo.memcheck ? TRAMPOLINE_CODE_SIZE_MMU : TRAMPOLINE_CODE_SIZE; + const size_t farcode_size = jo.memcheck ? FARCODE_SIZE_MMU : FARCODE_SIZE; + const size_t constpool_size = m_const_pool.CONST_POOL_SIZE; + AllocCodeSpace(CODE_SIZE + routines_size + trampolines_size + farcode_size + constpool_size); + AddChildCodeSpace(&asm_routines, routines_size); + AddChildCodeSpace(&trampolines, trampolines_size); + AddChildCodeSpace(&m_far_code, farcode_size); + m_const_pool.Init(AllocChildCodeSpace(constpool_size), constpool_size); + blocks.Init(); asm_routines.Init(nullptr); - - m_far_code.Init(jo.memcheck ? FARCODE_SIZE_MMU : FARCODE_SIZE); + m_far_code.Init(); Clear(); code_block.m_stats = &js.st; @@ -299,8 +306,6 @@ void JitIL::Shutdown() FreeCodeSpace(); blocks.Shutdown(); - trampolines.Shutdown(); - asm_routines.Shutdown(); m_far_code.Shutdown(); }