Remove dumb CodeBlock duplication in the emitters.

Fixes issue 6990.
This uses a bit of templating to remove the duplicate code that is the CodeBlocks in each emitter headers.
No actual functionality change in this.
This commit is contained in:
Ryan Houdek
2014-04-09 01:22:52 -05:00
parent 7d8604ac1c
commit 87d106d65c
15 changed files with 111 additions and 145 deletions

View File

@ -8,8 +8,8 @@
#include <vector>
#include "Common/CodeBlock.h"
#include "Common/Common.h"
#include "Common/MemoryUtil.h"
#if defined(__SYMBIAN32__) || defined(PANDORA)
#include <signal.h>
@ -700,78 +700,18 @@ public:
void VST1(u32 Size, ARMReg Vd, ARMReg Rn, NEONAlignment align = ALIGN_NONE, ARMReg Rm = _PC);
};
// Everything that needs to generate X86 code should inherit from this.
// You get memory management for free, plus, you can use all the MOV etc functions without
// having to prefix them with gen-> or something similar.
class ARMXCodeBlock : public ARMXEmitter
class ARMCodeBlock : public CodeBlock<ARMXEmitter>
{
protected:
u8 *region;
size_t region_size;
public:
ARMXCodeBlock() : region(nullptr), region_size(0) {}
virtual ~ARMXCodeBlock() { if (region) FreeCodeSpace(); }
// Call this before you generate any code.
void AllocCodeSpace(int size)
private:
void PoisonMemory() override
{
region_size = size;
region = (u8*)AllocateExecutableMemory(region_size);
SetCodePtr(region);
}
// Always clear code space with breakpoints, so that if someone accidentally executes
// uninitialized, it just breaks into the debugger.
void ClearCodeSpace()
{
// x86/64: 0xCC = breakpoint
memset(region, 0xCC, region_size);
ResetCodePtr();
}
// Call this when shutting down. Don't rely on the destructor, even though it'll do the job.
void FreeCodeSpace()
{
#ifndef __SYMBIAN32__
FreeMemoryPages(region, region_size);
region = nullptr;
#endif
region_size = 0;
}
bool IsInSpace(u8 *ptr)
{
return ptr >= region && ptr < region + region_size;
}
// Cannot currently be undone. Will write protect the entire code region.
// Start over if you need to change the code (call FreeCodeSpace(), AllocCodeSpace()).
void WriteProtect()
{
WriteProtectMemory(region, region_size, true);
}
void UnWriteProtect()
{
UnWriteProtectMemory(region, region_size, false);
}
void ResetCodePtr()
{
SetCodePtr(region);
}
size_t GetSpaceLeft() const
{
return region_size - (GetCodePtr() - region);
}
u8 *GetBasePtr() {
return region;
}
size_t GetOffset(u8 *ptr) {
return ptr - region;
u32* ptr = (u32*)region;
u32* maxptr = (u32*)region + region_size;
// If our memory isn't a multiple of u32 then this won't write the last remaining bytes with anything
// Less than optimal, but there would be nothing we could do but throw a runtime warning anyway.
// ARM: 0x01200070 = BKPT 0
while (ptr < maxptr)
*ptr++ = 0x01200070;
}
};

View File

@ -0,0 +1,76 @@
// Copyright 2013 Dolphin Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
#pragma once
#include "Common/Common.h"
#include "Common/MemoryUtil.h"
// Everything that needs to generate code should inherit from this.
// You get memory management for free, plus, you can use all emitter functions without
// having to prefix them with gen-> or something similar.
// Example implementation:
// class JIT : public CodeBlock<ARMXEmitter> {}
template<class T> class CodeBlock : public T, NonCopyable
{
private:
// A privately used function to set the executable RAM space to something invalid.
// For debugging usefulness it should be used to set the RAM to a host specific breakpoint instruction
virtual void PoisonMemory() = 0;
protected:
u8 *region;
size_t region_size;
public:
CodeBlock() : region(nullptr), region_size(0) {}
virtual ~CodeBlock() { if (region) FreeCodeSpace(); }
// Call this before you generate any code.
void AllocCodeSpace(int size)
{
region_size = size;
region = (u8*)AllocateExecutableMemory(region_size);
T::SetCodePtr(region);
}
// Always clear code space with breakpoints, so that if someone accidentally executes
// uninitialized, it just breaks into the debugger.
void ClearCodeSpace()
{
PoisonMemory();
ResetCodePtr();
}
// Call this when shutting down. Don't rely on the destructor, even though it'll do the job.
void FreeCodeSpace()
{
FreeMemoryPages(region, region_size);
region = nullptr;
region_size = 0;
}
bool IsInSpace(u8 *ptr)
{
return (ptr >= region) && (ptr < (region + region_size));
}
// Cannot currently be undone. Will write protect the entire code region.
// Start over if you need to change the code (call FreeCodeSpace(), AllocCodeSpace()).
void WriteProtect()
{
WriteProtectMemory(region, region_size, true);
}
void ResetCodePtr()
{
T::SetCodePtr(region);
}
size_t GetSpaceLeft() const
{
return region_size - (T::GetCodePtr() - region);
}
};

View File

@ -50,6 +50,7 @@
<ClInclude Include="BreakPoints.h" />
<ClInclude Include="CDUtils.h" />
<ClInclude Include="ChunkFile.h" />
<ClInclude Include="CodeBlock.h" />
<ClInclude Include="ColorUtil.h" />
<ClInclude Include="Common.h" />
<ClInclude Include="CommonFuncs.h" />

View File

@ -16,6 +16,7 @@
<ClInclude Include="BreakPoints.h" />
<ClInclude Include="CDUtils.h" />
<ClInclude Include="ChunkFile.h" />
<ClInclude Include="CodeBlock.h" />
<ClInclude Include="ColorUtil.h" />
<ClInclude Include="Common.h" />
<ClInclude Include="CommonFuncs.h" />

View File

@ -10,8 +10,8 @@
#include <cstring>
#include <functional>
#include "Common/CodeBlock.h"
#include "Common/Common.h"
#include "Common/MemoryUtil.h"
namespace Gen
{
@ -762,65 +762,13 @@ public:
}
}; // class XEmitter
// Everything that needs to generate X86 code should inherit from this.
// You get memory management for free, plus, you can use all the MOV etc functions without
// having to prefix them with gen-> or something similar.
class XCodeBlock : public XEmitter
class X64CodeBlock : public CodeBlock<XEmitter>
{
protected:
u8 *region;
size_t region_size;
public:
XCodeBlock() : region(nullptr), region_size(0) {}
virtual ~XCodeBlock() { if (region) FreeCodeSpace(); }
// Call this before you generate any code.
void AllocCodeSpace(int size)
{
region_size = size;
region = (u8*)AllocateExecutableMemory(region_size);
SetCodePtr(region);
}
// Always clear code space with breakpoints, so that if someone accidentally executes
// uninitialized, it just breaks into the debugger.
void ClearCodeSpace()
private:
void PoisonMemory() override
{
// x86/64: 0xCC = breakpoint
memset(region, 0xCC, region_size);
ResetCodePtr();
}
// Call this when shutting down. Don't rely on the destructor, even though it'll do the job.
void FreeCodeSpace()
{
FreeMemoryPages(region, region_size);
region = nullptr;
region_size = 0;
}
bool IsInSpace(u8 *ptr)
{
return ptr >= region && ptr < region + region_size;
}
// Cannot currently be undone. Will write protect the entire code region.
// Start over if you need to change the code (call FreeCodeSpace(), AllocCodeSpace()).
void WriteProtect()
{
WriteProtectMemory(region, region_size, true);
}
void ResetCodePtr()
{
SetCodePtr(region);
}
size_t GetSpaceLeft() const
{
return region_size - (GetCodePtr() - region);
}
};