arm64jit: Track writable and non-writable pointers.

Switch uses different memory regions.  We can handle this, might as well
cleanup some const abuse.
This commit is contained in:
Unknown W. Brackets 2020-05-16 22:12:35 -07:00
parent bab907f792
commit 7910b4029a
17 changed files with 81 additions and 47 deletions

View File

@ -275,9 +275,14 @@ static int EncodeSize(int size) {
}
}
void ARM64XEmitter::SetCodePointer(u8* ptr)
ARM64XEmitter::ARM64XEmitter(const u8 *ptr, u8 *writePtr) {
SetCodePointer(ptr, writePtr);
}
void ARM64XEmitter::SetCodePointer(const u8 *ptr, u8 *writePtr)
{
m_code = ptr;
m_writable = writePtr;
m_lastCacheFlushEnd = ptr;
}
@ -288,7 +293,7 @@ const u8* ARM64XEmitter::GetCodePointer() const
u8* ARM64XEmitter::GetWritableCodePtr()
{
return m_code;
return m_writable;
}
void ARM64XEmitter::ReserveCodeSpace(u32 bytes)
@ -320,11 +325,11 @@ void ARM64XEmitter::FlushIcache()
m_lastCacheFlushEnd = m_code;
}
void ARM64XEmitter::FlushIcacheSection(u8* start, u8* end)
void ARM64XEmitter::FlushIcacheSection(const u8 *start, const u8 *end)
{
#if defined(IOS)
// Header file says this is equivalent to: sys_icache_invalidate(start, end - start);
sys_cache_control(kCacheFunctionPrepareForExecution, start, end - start);
sys_cache_control(kCacheFunctionPrepareForExecution, (void *)start, end - start);
#elif PPSSPP_PLATFORM(WINDOWS)
FlushInstructionCache(GetCurrentProcess(), start, end - start);
#elif PPSSPP_ARCH(ARM64)
@ -919,7 +924,9 @@ void ARM64XEmitter::SetJumpTarget(FixupBranch const& branch)
inst = (0x25 << 26) | MaskImm26(distance);
break;
}
*(u32*)branch.ptr = inst;
ptrdiff_t writable = m_writable - m_code;
*(u32 *)(branch.ptr + writable) = inst;
}
FixupBranch ARM64XEmitter::CBZ(ARM64Reg Rt)
@ -3923,8 +3930,11 @@ void ARM64XEmitter::SUBSI2R(ARM64Reg Rd, ARM64Reg Rn, u64 imm, ARM64Reg scratch)
}
void ARM64CodeBlock::PoisonMemory(int offset) {
u32* ptr = (u32*)(region + offset);
u32* maxptr = (u32*)(region + region_size - offset);
// So we can adjust region to writable space. Might be zero.
ptrdiff_t writable = m_writable - m_code;
u32 *ptr = (u32 *)(region + offset + writable);
u32 *maxptr = (u32 *)(region + region_size - offset + writable);
// 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.
// AArch64: 0xD4200000 = BRK 0

View File

@ -162,7 +162,8 @@ enum RoundingMode {
struct FixupBranch
{
u8* ptr;
// Pointer to executable code address.
const u8 *ptr;
// Type defines
// 0 = CBZ (32bit)
// 1 = CBNZ (32bit)
@ -338,10 +339,12 @@ public:
class ARM64XEmitter
{
friend class ARM64FloatEmitter;
friend class ARM64CodeBlock;
private:
u8 *m_code = nullptr;
u8 *m_lastCacheFlushEnd = nullptr;
const u8 *m_code = nullptr;
u8 *m_writable = nullptr;
const u8 *m_lastCacheFlushEnd = nullptr;
void EncodeCompareBranchInst(u32 op, ARM64Reg Rt, const void* ptr);
void EncodeTestBranchInst(u32 op, ARM64Reg Rt, u8 bits, const void* ptr);
@ -375,8 +378,9 @@ private:
protected:
inline void Write32(u32 value)
{
*(u32*)m_code = value;
*(u32 *)m_writable = value;
m_code += 4;
m_writable += 4;
}
public:
@ -384,23 +388,20 @@ public:
{
}
ARM64XEmitter(u8 *code_ptr) {
m_code = code_ptr;
m_lastCacheFlushEnd = code_ptr;
}
ARM64XEmitter(const u8 *codePtr, u8 *writablePtr);
virtual ~ARM64XEmitter()
{
}
void SetCodePointer(u8* ptr);
void SetCodePointer(const u8 *ptr, u8 *writePtr);
const u8* GetCodePointer() const;
void ReserveCodeSpace(u32 bytes);
const u8* AlignCode16();
const u8* AlignCodePage();
void FlushIcache();
void FlushIcacheSection(u8* start, u8* end);
void FlushIcacheSection(const u8* start, const u8* end);
u8* GetWritableCodePtr();
// FixupBranch branching

View File

@ -586,7 +586,7 @@ void ARMXEmitter::QuickCallFunction(ARMReg reg, const void *func) {
}
}
void ARMXEmitter::SetCodePointer(u8 *ptr)
void ARMXEmitter::SetCodePointer(u8 *ptr, u8 *writePtr)
{
code = ptr;
startcode = code;

View File

@ -443,7 +443,7 @@ public:
}
virtual ~ARMXEmitter() {}
void SetCodePointer(u8 *ptr);
void SetCodePointer(u8 *ptr, u8 *writePtr);
const u8 *GetCodePointer() const;
void ReserveCodeSpace(u32 bytes);

View File

@ -4,6 +4,7 @@
#pragma once
#include <cstddef>
#include "Common.h"
#include "MemoryUtil.h"
@ -22,7 +23,6 @@ public:
return (ptr >= region) && (ptr < (region + region_size));
}
virtual void SetCodePtr(u8 *ptr) = 0;
virtual const u8 *GetCodePtr() const = 0;
u8 *GetBasePtr() {
@ -33,7 +33,13 @@ public:
return ptr - region;
}
virtual const u8 *GetCodePtrFromWritablePtr(u8 *ptr) = 0;
virtual u8 *GetWritablePtrFromCodePtr(const u8 *ptr) = 0;
protected:
virtual void SetCodePtr(u8 *ptr) = 0;
// Note: this should be the readable/executable side if writable is a different pointer.
u8 *region = nullptr;
size_t region_size = 0;
};
@ -56,7 +62,8 @@ public:
region_size = size;
// The protection will be set to RW if PlatformIsWXExclusive.
region = (u8 *)AllocateExecutableMemory(region_size);
T::SetCodePointer(region);
writableRegion = region;
T::SetCodePointer(region, writableRegion);
}
// Always clear code space with breakpoints, so that if someone accidentally executes
@ -104,27 +111,42 @@ public:
ProtectMemoryPages(region, region_size, MEM_PROT_READ | MEM_PROT_WRITE);
FreeMemoryPages(region, region_size);
region = nullptr;
writableRegion = nullptr;
region_size = 0;
}
void SetCodePtr(u8 *ptr) override {
T::SetCodePointer(ptr);
}
const u8 *GetCodePtr() const override {
return T::GetCodePointer();
}
void ResetCodePtr(int offset) {
T::SetCodePointer(region + offset);
void ResetCodePtr(size_t offset) {
T::SetCodePointer(region + offset, writableRegion + offset);
}
size_t GetSpaceLeft() const {
return region_size - (T::GetCodePointer() - region);
}
const u8 *GetCodePtrFromWritablePtr(u8 *ptr) override {
// So we can adjust region to writable space. Might be zero.
ptrdiff_t writable = T::GetWritableCodePtr() - T::GetCodePointer();
return ptr - writable;
}
u8 *GetWritablePtrFromCodePtr(const u8 *ptr) override {
// So we can adjust region to writable space. Might be zero.
ptrdiff_t writable = T::GetWritableCodePtr() - T::GetCodePointer();
return (u8 *)ptr + writable;
}
protected:
void SetCodePtr(u8 *ptr) override {
T::SetCodePointer(ptr, GetWritablePtrFromCodePtr(ptr));
}
private:
// Note: this is a readable pointer.
const uint8_t *writeStart_ = nullptr;
uint8_t *writableRegion = nullptr;
};

View File

@ -96,7 +96,7 @@ enum NormalSSEOps
};
void XEmitter::SetCodePointer(u8 *ptr)
void XEmitter::SetCodePointer(u8 *ptr, u8 *writePtr)
{
code = ptr;
}

View File

@ -372,7 +372,7 @@ public:
void WriteModRM(int mod, int rm, int reg);
void WriteSIB(int scale, int index, int base);
void SetCodePointer(u8 *ptr);
void SetCodePointer(u8 *ptr, u8 *writePtr);
const u8 *GetCodePointer() const;
void ReserveCodeSpace(int bytes);

View File

@ -304,17 +304,17 @@ const u8 *ArmJit::DoJit(u32 em_address, JitBlock *b)
JumpTarget backJump = GetCodePtr();
gpr.SetRegImm(R0, js.blockStart);
B((const void *)outerLoopPCInR0);
b->checkedEntry = (u8 *)GetCodePtr();
b->checkedEntry = GetCodePtr();
SetCC(CC_LT);
B(backJump);
SetCC(CC_AL);
} else if (jo.useForwardJump) {
b->checkedEntry = (u8 *)GetCodePtr();
b->checkedEntry = GetCodePtr();
SetCC(CC_LT);
bail = B();
SetCC(CC_AL);
} else {
b->checkedEntry = (u8 *)GetCodePtr();
b->checkedEntry = GetCodePtr();
SetCC(CC_LT);
gpr.SetRegImm(R0, js.blockStart);
B((const void *)outerLoopPCInR0);

View File

@ -300,13 +300,13 @@ const u8 *Arm64Jit::DoJit(u32 em_address, JitBlock *b) {
const u8 *backJump = GetCodePtr();
MOVI2R(SCRATCH1, js.blockStart);
B((const void *)outerLoopPCInSCRATCH1);
b->checkedEntry = (u8 *)GetCodePtr();
b->checkedEntry = GetCodePtr();
B(CC_LT, backJump);
} else if (jo.useForwardJump) {
b->checkedEntry = (u8 *)GetCodePtr();
b->checkedEntry = GetCodePtr();
bail = B(CC_LT);
} else if (jo.enableBlocklink) {
b->checkedEntry = (u8 *)GetCodePtr();
b->checkedEntry = GetCodePtr();
MOVI2R(SCRATCH1, js.blockStart);
FixupBranch skip = B(CC_GE);
B((const void *)outerLoopPCInSCRATCH1);
@ -442,7 +442,7 @@ void Arm64Jit::LinkBlock(u8 *exitPoint, const u8 *checkedEntry) {
if (PlatformIsWXExclusive()) {
ProtectMemoryPages(exitPoint, 32, MEM_PROT_READ | MEM_PROT_WRITE);
}
ARM64XEmitter emit(exitPoint);
ARM64XEmitter emit(GetCodePtrFromWritablePtr(exitPoint), exitPoint);
emit.B(checkedEntry);
// TODO: Write stuff after, convering up the now-unused instructions.
emit.FlushIcache();
@ -459,7 +459,7 @@ void Arm64Jit::UnlinkBlock(u8 *checkedEntry, u32 originalAddress) {
ProtectMemoryPages(checkedEntry, 16, MEM_PROT_READ | MEM_PROT_WRITE);
}
ARM64XEmitter emit(checkedEntry);
ARM64XEmitter emit(GetCodePtrFromWritablePtr(checkedEntry), checkedEntry);
emit.MOVI2R(SCRATCH1, originalAddress);
emit.STR(INDEX_UNSIGNED, SCRATCH1, CTXREG, offsetof(MIPSState, pc));
emit.B(MIPSComp::jit->GetDispatcher());

View File

@ -186,7 +186,7 @@ void JitBlockCache::ProxyBlock(u32 rootAddress, u32 startAddress, u32 size, cons
// Make binary searches and stuff work ok
b.normalEntry = codePtr;
b.checkedEntry = (u8 *)codePtr; // Ugh, casting away const..
b.checkedEntry = codePtr;
proxyBlockMap_.insert(std::make_pair(startAddress, num_blocks_));
AddBlockMap(num_blocks_);
@ -541,7 +541,8 @@ void JitBlockCache::DestroyBlock(int block_num, DestroyType type) {
if (b->checkedEntry) {
// We can skip this if we're clearing anyway, which cuts down on protect back and forth on WX exclusive.
if (type != DestroyType::CLEAR) {
MIPSComp::jit->UnlinkBlock(b->checkedEntry, b->originalAddress);
u8 *writableEntry = codeBlock_->GetWritablePtrFromCodePtr(b->checkedEntry);
MIPSComp::jit->UnlinkBlock(writableEntry, b->originalAddress);
}
} else {
ERROR_LOG(JIT, "Unlinking block with no entry: %08x (%d)", b->originalAddress, block_num);

View File

@ -59,7 +59,7 @@ enum class DestroyType {
struct JitBlock {
bool ContainsAddress(u32 em_address);
u8 *checkedEntry; // not const, may need to write through this to unlink
const u8 *checkedEntry; // const, we have to translate to writable.
const u8 *normalEntry;
u8 *exitPtrs[MAX_JIT_BLOCK_EXITS]; // to be able to rewrite the exit jump

View File

@ -335,7 +335,7 @@ const u8 *Jit::DoJit(u32 em_address, JitBlock *b) {
js.PrefixStart();
// We add a check before the block, used when entering from a linked block.
b->checkedEntry = (u8 *)GetCodePtr();
b->checkedEntry = GetCodePtr();
// Downcount flag check. The last block decremented downcounter, and the flag should still be available.
FixupBranch skip = J_CC(CC_NS);
MOV(32, MIPSSTATE_VAR(pc), Imm32(js.blockStart));

View File

@ -285,7 +285,7 @@ JittedVertexDecoder VertexDecoderJitCache::Compile(const VertexDecoder &dec, int
if (!CompileStep(dec, i)) {
EndWrite();
// Reset the code ptr and return zero to indicate that we failed.
SetCodePtr(const_cast<u8 *>(start));
ResetCodePtr(GetOffset(start));
char temp[1024] = {0};
dec.ToString(temp);
INFO_LOG(G3D, "Could not compile vertex decoder: %s", temp);

View File

@ -248,7 +248,7 @@ JittedVertexDecoder VertexDecoderJitCache::Compile(const VertexDecoder &dec, int
if (!CompileStep(dec, i)) {
EndWrite();
// Reset the code ptr (effectively undoing what we generated) and return zero to indicate that we failed.
SetCodePtr(const_cast<u8 *>(start));
ResetCodePtr(GetOffset(start));
char temp[1024] = {0};
dec.ToString(temp);
ERROR_LOG(G3D, "Could not compile vertex decoder, failed at step %d: %s", i, temp);

View File

@ -251,7 +251,7 @@ JittedVertexDecoder VertexDecoderJitCache::Compile(const VertexDecoder &dec, int
if (!CompileStep(dec, i)) {
EndWrite();
// Reset the code ptr and return zero to indicate that we failed.
SetCodePtr(const_cast<u8 *>(start));
ResetCodePtr(GetOffset(start));
return 0;
}
}

View File

@ -79,7 +79,7 @@ NearestFunc SamplerJitCache::Compile(const SamplerID &id) {
if (!Jit_ReadTextureFormat(id)) {
EndWrite();
SetCodePtr(const_cast<u8 *>(start));
ResetCodePtr(GetOffset(start));
return nullptr;
}
@ -106,7 +106,7 @@ LinearFunc SamplerJitCache::CompileLinear(const SamplerID &id) {
if (!Jit_ReadTextureFormat(id)) {
EndWrite();
SetCodePtr(const_cast<u8 *>(nearest));
ResetCodePtr(GetOffset(nearest));
return nullptr;
}

View File

@ -38,7 +38,7 @@ bool TestArm64Emitter() {
u32 code[512];
ARM64XEmitter emitter((u8 *)code);
ARM64XEmitter emitter((u8 *)code, (u8 *)code);
ARM64FloatEmitter fp(&emitter);
emitter.MOVfromSP(X3);