mirror of
https://github.com/hrydgard/ppsspp.git
synced 2025-01-31 02:23:51 +00:00
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:
parent
bab907f792
commit
7910b4029a
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -96,7 +96,7 @@ enum NormalSSEOps
|
||||
};
|
||||
|
||||
|
||||
void XEmitter::SetCodePointer(u8 *ptr)
|
||||
void XEmitter::SetCodePointer(u8 *ptr, u8 *writePtr)
|
||||
{
|
||||
code = ptr;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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());
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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));
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user