2012-11-01 16:19:01 +01:00
|
|
|
// Copyright (c) 2012- PPSSPP Project / Dolphin Project.
|
|
|
|
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
|
|
|
// it under the terms of the GNU General Public License as published by
|
2012-11-04 23:01:49 +01:00
|
|
|
// the Free Software Foundation, version 2.0 or later versions.
|
2012-11-01 16:19:01 +01:00
|
|
|
|
|
|
|
// This program is distributed in the hope that it will be useful,
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
// GNU General Public License 2.0 for more details.
|
|
|
|
|
|
|
|
// A copy of the GPL 2.0 should have been included with the program.
|
|
|
|
// If not, see http://www.gnu.org/licenses/
|
|
|
|
|
|
|
|
// Official git repository and contact information can be found at
|
|
|
|
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
|
|
|
|
|
|
|
// Enable define below to enable oprofile integration. For this to work,
|
|
|
|
// it requires at least oprofile version 0.9.4, and changing the build
|
|
|
|
// system to link the Dolphin executable against libopagent. Since the
|
|
|
|
// dependency is a little inconvenient and this is possibly a slight
|
|
|
|
// performance hit, it's not enabled by default, but it's useful for
|
|
|
|
// locating performance issues.
|
|
|
|
|
2014-02-07 23:04:44 +01:00
|
|
|
#include <cstddef>
|
|
|
|
|
2012-11-01 16:19:01 +01:00
|
|
|
#include "Common.h"
|
|
|
|
|
|
|
|
#ifdef _WIN32
|
2013-07-28 20:43:25 -07:00
|
|
|
#include "Common/CommonWindows.h"
|
2012-11-01 16:19:01 +01:00
|
|
|
#endif
|
|
|
|
|
2013-04-27 19:35:42 +02:00
|
|
|
#include "Core/Core.h"
|
|
|
|
#include "Core/MemMap.h"
|
|
|
|
#include "Core/CoreTiming.h"
|
2013-09-01 00:19:41 -07:00
|
|
|
#include "Core/Reporting.h"
|
2012-11-01 16:19:01 +01:00
|
|
|
|
2013-04-27 19:35:42 +02:00
|
|
|
#include "Core/MIPS/MIPS.h"
|
|
|
|
#include "Core/MIPS/MIPSTables.h"
|
|
|
|
#include "Core/MIPS/MIPSAnalyst.h"
|
2012-11-01 16:19:01 +01:00
|
|
|
|
2013-04-27 19:35:42 +02:00
|
|
|
#include "Core/MIPS/JitCommon/JitBlockCache.h"
|
|
|
|
#include "Core/MIPS/JitCommon/JitCommon.h"
|
2013-04-26 23:58:20 +02:00
|
|
|
|
|
|
|
#if defined(ARM)
|
|
|
|
#include "Common/ArmEmitter.h"
|
|
|
|
#include "Core/MIPS/ARM/ArmAsm.h"
|
|
|
|
using namespace ArmGen;
|
|
|
|
#elif defined(_M_IX86) || defined(_M_X64)
|
|
|
|
#include "Common/x64Emitter.h"
|
|
|
|
#include "Common/x64Analyzer.h"
|
|
|
|
#include "Core/MIPS/x86/Asm.h"
|
|
|
|
using namespace Gen;
|
2013-08-10 16:39:28 +02:00
|
|
|
#elif defined(PPC)
|
|
|
|
#include "Common/ppcEmitter.h"
|
|
|
|
#include "Core/MIPS/MIPS.h"
|
|
|
|
using namespace PpcGen;
|
2013-04-27 00:22:18 +02:00
|
|
|
#else
|
|
|
|
#error "Unsupported arch!"
|
2013-04-26 23:58:20 +02:00
|
|
|
#endif
|
2012-11-01 16:19:01 +01:00
|
|
|
// #include "JitBase.h"
|
|
|
|
|
|
|
|
#if defined USE_OPROFILE && USE_OPROFILE
|
|
|
|
#include <opagent.h>
|
|
|
|
|
|
|
|
op_agent_t agent;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if defined USE_VTUNE
|
|
|
|
#include <jitprofiling.h>
|
|
|
|
#pragma comment(lib, "libittnotify.lib")
|
|
|
|
#pragma comment(lib, "jitprofiling.lib")
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
2013-09-29 13:51:09 -07:00
|
|
|
const u32 INVALID_EXIT = 0xFFFFFFFF;
|
|
|
|
const MIPSOpcode INVALID_ORIGINAL_OP = MIPSOpcode(0x00000001);
|
2012-11-01 16:19:01 +01:00
|
|
|
|
2013-12-19 13:34:06 +01:00
|
|
|
JitBlockCache::JitBlockCache(MIPSState *mips, CodeBlock *codeBlock) :
|
|
|
|
mips_(mips), codeBlock_(codeBlock), blocks_(0), num_blocks_(0) {
|
2013-04-27 00:22:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
JitBlockCache::~JitBlockCache() {
|
|
|
|
Shutdown();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool JitBlock::ContainsAddress(u32 em_address) {
|
2013-01-18 10:43:40 -08:00
|
|
|
// WARNING - THIS DOES NOT WORK WITH JIT INLINING ENABLED.
|
2013-04-27 00:22:18 +02:00
|
|
|
// However, that doesn't exist yet so meh.
|
2012-11-01 16:19:01 +01:00
|
|
|
return (em_address >= originalAddress && em_address < originalAddress + 4 * originalSize);
|
|
|
|
}
|
|
|
|
|
2013-12-19 13:34:06 +01:00
|
|
|
bool JitBlockCache::IsFull() const {
|
|
|
|
return num_blocks_ >= MAX_NUM_BLOCKS - 1;
|
2012-11-01 16:19:01 +01:00
|
|
|
}
|
|
|
|
|
2013-12-19 13:34:06 +01:00
|
|
|
void JitBlockCache::Init() {
|
2012-11-01 16:19:01 +01:00
|
|
|
#if defined USE_OPROFILE && USE_OPROFILE
|
|
|
|
agent = op_open_agent();
|
|
|
|
#endif
|
2013-12-19 13:34:06 +01:00
|
|
|
blocks_ = new JitBlock[MAX_NUM_BLOCKS];
|
2012-11-01 16:19:01 +01:00
|
|
|
Clear();
|
|
|
|
}
|
|
|
|
|
2013-12-19 13:34:06 +01:00
|
|
|
void JitBlockCache::Shutdown() {
|
|
|
|
delete [] blocks_;
|
|
|
|
blocks_ = 0;
|
|
|
|
num_blocks_ = 0;
|
2012-11-01 16:19:01 +01:00
|
|
|
#if defined USE_OPROFILE && USE_OPROFILE
|
|
|
|
op_close_agent(agent);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef USE_VTUNE
|
|
|
|
iJIT_NotifyEvent(iJVM_EVENT_TYPE_SHUTDOWN, NULL);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
// This clears the JIT cache. It's called from JitCache.cpp when the JIT cache
|
|
|
|
// is full and when saving and loading states.
|
2013-12-19 13:34:06 +01:00
|
|
|
void JitBlockCache::Clear() {
|
|
|
|
for (int i = 0; i < num_blocks_; i++)
|
2012-11-01 16:19:01 +01:00
|
|
|
DestroyBlock(i, false);
|
2013-12-19 13:34:06 +01:00
|
|
|
links_to_.clear();
|
|
|
|
block_map_.clear();
|
2013-12-19 00:39:49 +01:00
|
|
|
proxyBlockIndices_.clear();
|
2013-12-19 13:34:06 +01:00
|
|
|
num_blocks_ = 0;
|
2012-11-01 16:19:01 +01:00
|
|
|
}
|
|
|
|
|
2013-12-19 13:34:06 +01:00
|
|
|
void JitBlockCache::Reset() {
|
2012-11-01 16:19:01 +01:00
|
|
|
Shutdown();
|
|
|
|
Init();
|
|
|
|
}
|
|
|
|
|
2013-12-19 13:34:06 +01:00
|
|
|
JitBlock *JitBlockCache::GetBlock(int no) {
|
|
|
|
return &blocks_[no];
|
2012-11-01 16:19:01 +01:00
|
|
|
}
|
|
|
|
|
2013-12-19 13:34:06 +01:00
|
|
|
int JitBlockCache::AllocateBlock(u32 startAddress) {
|
|
|
|
JitBlock &b = blocks_[num_blocks_];
|
|
|
|
|
2014-03-29 22:21:38 +01:00
|
|
|
b.proxyFor = 0;
|
2013-12-19 13:31:13 +01:00
|
|
|
// If there's an existing pure proxy block at the address, we need to ditch it and create a new one,
|
|
|
|
// taking over the proxied blocks.
|
|
|
|
int num = GetBlockNumberFromStartAddress(startAddress, false);
|
|
|
|
if (num >= 0) {
|
2013-12-19 13:34:06 +01:00
|
|
|
if (blocks_[num].IsPureProxy()) {
|
|
|
|
blocks_[num].invalid = true;
|
2014-03-29 22:21:38 +01:00
|
|
|
b.proxyFor = new std::vector<u32>();
|
|
|
|
*b.proxyFor = *blocks_[num].proxyFor;
|
|
|
|
blocks_[num].proxyFor->clear();
|
|
|
|
delete blocks_[num].proxyFor;
|
|
|
|
blocks_[num].proxyFor = 0;
|
2013-12-19 13:31:13 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-11-01 16:19:01 +01:00
|
|
|
b.invalid = false;
|
2013-12-19 13:31:13 +01:00
|
|
|
b.originalAddress = startAddress;
|
2013-12-19 13:34:06 +01:00
|
|
|
for (int i = 0; i < MAX_JIT_BLOCK_EXITS; ++i) {
|
2013-08-15 23:23:39 -07:00
|
|
|
b.exitAddress[i] = INVALID_EXIT;
|
|
|
|
b.exitPtrs[i] = 0;
|
|
|
|
b.linkStatus[i] = false;
|
|
|
|
}
|
2013-12-19 13:34:06 +01:00
|
|
|
b.blockNum = num_blocks_;
|
|
|
|
num_blocks_++; //commit the current block
|
|
|
|
return num_blocks_ - 1;
|
2013-12-19 00:39:49 +01:00
|
|
|
}
|
|
|
|
|
2013-12-19 13:34:06 +01:00
|
|
|
void JitBlockCache::ProxyBlock(u32 rootAddress, u32 startAddress, u32 size, const u8 *codePtr) {
|
2013-12-19 13:31:13 +01:00
|
|
|
// If there's an existing block at the startAddress, add rootAddress as a proxy root of that block
|
|
|
|
// instead of creating a new block.
|
|
|
|
int num = GetBlockNumberFromStartAddress(startAddress, false);
|
|
|
|
if (num != -1) {
|
|
|
|
INFO_LOG(HLE, "Adding proxy root %08x to block at %08x", rootAddress, startAddress);
|
2014-03-29 22:21:38 +01:00
|
|
|
if (!blocks_[num].proxyFor) {
|
|
|
|
blocks_[num].proxyFor = new std::vector<u32>();
|
|
|
|
}
|
|
|
|
blocks_[num].proxyFor->push_back(rootAddress);
|
2013-12-19 13:31:13 +01:00
|
|
|
}
|
2013-12-19 11:45:39 +01:00
|
|
|
|
2013-12-19 13:34:06 +01:00
|
|
|
JitBlock &b = blocks_[num_blocks_];
|
2013-12-19 00:39:49 +01:00
|
|
|
b.invalid = false;
|
|
|
|
b.originalAddress = startAddress;
|
|
|
|
b.originalSize = size;
|
|
|
|
for (int i = 0; i < MAX_JIT_BLOCK_EXITS; ++i) {
|
|
|
|
b.exitAddress[i] = INVALID_EXIT;
|
|
|
|
b.exitPtrs[i] = 0;
|
|
|
|
b.linkStatus[i] = false;
|
|
|
|
}
|
|
|
|
b.exitAddress[0] = rootAddress;
|
2013-12-19 13:34:06 +01:00
|
|
|
b.blockNum = num_blocks_;
|
2014-04-20 21:44:10 -07:00
|
|
|
b.proxyFor = new std::vector<u32>();
|
2013-12-19 13:34:06 +01:00
|
|
|
b.SetPureProxy(); // flag as pure proxy block.
|
|
|
|
|
2013-12-19 00:39:49 +01:00
|
|
|
// Make binary searches and stuff work ok
|
|
|
|
b.normalEntry = codePtr;
|
|
|
|
b.checkedEntry = codePtr;
|
2013-12-19 13:34:06 +01:00
|
|
|
proxyBlockIndices_.push_back(num_blocks_);
|
|
|
|
num_blocks_++; //commit the current block
|
2012-11-01 16:19:01 +01:00
|
|
|
}
|
|
|
|
|
2013-12-19 13:34:06 +01:00
|
|
|
void JitBlockCache::FinalizeBlock(int block_num, bool block_link) {
|
|
|
|
JitBlock &b = blocks_[block_num];
|
2012-11-01 16:19:01 +01:00
|
|
|
|
|
|
|
b.originalFirstOpcode = Memory::Read_Opcode_JIT(b.originalAddress);
|
2013-08-24 14:43:49 -07:00
|
|
|
MIPSOpcode opcode = GetEmuHackOpForBlock(block_num);
|
2012-11-01 16:19:01 +01:00
|
|
|
Memory::Write_Opcode_JIT(b.originalAddress, opcode);
|
2013-12-19 13:31:13 +01:00
|
|
|
|
2012-11-01 16:19:01 +01:00
|
|
|
// Convert the logical address to a physical address for the block map
|
|
|
|
// Yeah, this'll work fine for PSP too I think.
|
|
|
|
u32 pAddr = b.originalAddress & 0x1FFFFFFF;
|
|
|
|
|
2013-12-19 13:34:06 +01:00
|
|
|
block_map_[std::make_pair(pAddr + 4 * b.originalSize - 1, pAddr)] = block_num;
|
|
|
|
if (block_link) {
|
|
|
|
for (int i = 0; i < MAX_JIT_BLOCK_EXITS; i++) {
|
|
|
|
if (b.exitAddress[i] != INVALID_EXIT)
|
|
|
|
links_to_.insert(std::pair<u32, int>(b.exitAddress[i], block_num));
|
2012-11-01 16:19:01 +01:00
|
|
|
}
|
2013-12-19 13:34:06 +01:00
|
|
|
|
2012-11-01 16:19:01 +01:00
|
|
|
LinkBlock(block_num);
|
|
|
|
LinkBlockExits(block_num);
|
|
|
|
}
|
|
|
|
|
|
|
|
#if defined USE_OPROFILE && USE_OPROFILE
|
|
|
|
char buf[100];
|
|
|
|
sprintf(buf, "EmuCode%x", b.originalAddress);
|
2013-12-19 13:34:06 +01:00
|
|
|
const u8* blockStart = blocks_[block_num].checkedEntry;
|
2013-12-01 02:34:37 -08:00
|
|
|
op_write_native_code(agent, buf, (uint64_t)blockStart, blockStart, b.normalEntry + b.codeSize - b.checkedEntry);
|
2012-11-01 16:19:01 +01:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef USE_VTUNE
|
|
|
|
sprintf(b.blockName, "EmuCode_0x%08x", b.originalAddress);
|
|
|
|
|
|
|
|
iJIT_Method_Load jmethod = {0};
|
|
|
|
jmethod.method_id = iJIT_GetNewMethodID();
|
|
|
|
jmethod.class_file_name = "";
|
|
|
|
jmethod.source_file_name = __FILE__;
|
2013-12-19 13:34:06 +01:00
|
|
|
jmethod.method_load_address = (void*)blocks_[block_num].checkedEntry;
|
2013-12-01 02:34:37 -08:00
|
|
|
jmethod.method_size = b.normalEntry + b.codeSize - b.checkedEntry;
|
2012-11-01 16:19:01 +01:00
|
|
|
jmethod.line_number_size = 0;
|
|
|
|
jmethod.method_name = b.blockName;
|
|
|
|
iJIT_NotifyEvent(iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED, (void*)&jmethod);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2013-12-19 13:34:06 +01:00
|
|
|
static int binary_search(JitBlock blocks_[], const u8 *baseoff, int imin, int imax) {
|
2013-12-19 00:39:49 +01:00
|
|
|
while (imin < imax) {
|
2013-04-27 23:40:17 +02:00
|
|
|
int imid = (imin + imax) / 2;
|
2013-12-19 13:34:06 +01:00
|
|
|
if (blocks_[imid].normalEntry < baseoff)
|
2013-04-27 23:40:17 +02:00
|
|
|
imin = imid + 1;
|
|
|
|
else
|
|
|
|
imax = imid;
|
|
|
|
}
|
2013-12-19 13:34:06 +01:00
|
|
|
if ((imax == imin) && (blocks_[imin].normalEntry == baseoff))
|
2013-04-27 23:40:17 +02:00
|
|
|
return imin;
|
|
|
|
else
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2013-12-10 13:06:57 +01:00
|
|
|
int JitBlockCache::GetBlockNumberFromEmuHackOp(MIPSOpcode inst, bool ignoreBad) const {
|
2013-12-19 13:34:06 +01:00
|
|
|
if (!num_blocks_ || !MIPS_IS_EMUHACK(inst)) // definitely not a JIT block
|
2013-04-27 00:22:18 +02:00
|
|
|
return -1;
|
|
|
|
int off = (inst & MIPS_EMUHACK_VALUE_MASK);
|
|
|
|
|
|
|
|
const u8 *baseoff = codeBlock_->GetBasePtr() + off;
|
2013-12-01 14:12:16 +01:00
|
|
|
if (baseoff < codeBlock_->GetBasePtr() || baseoff >= codeBlock_->GetCodePtr()) {
|
2013-12-10 13:06:57 +01:00
|
|
|
if (!ignoreBad) {
|
|
|
|
ERROR_LOG(JIT, "JitBlockCache: Invalid Emuhack Op %08x", inst.encoding);
|
|
|
|
}
|
2013-12-01 14:12:16 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2013-12-19 13:34:06 +01:00
|
|
|
int bl = binary_search(blocks_, baseoff, 0, num_blocks_ - 1);
|
2014-05-27 20:12:35 +02:00
|
|
|
if (bl >= 0 && blocks_[bl].invalid) {
|
2013-09-01 00:19:41 -07:00
|
|
|
return -1;
|
2014-05-27 20:12:35 +02:00
|
|
|
} else {
|
|
|
|
return bl;
|
|
|
|
}
|
2013-04-27 00:22:18 +02:00
|
|
|
}
|
|
|
|
|
2013-08-24 14:43:49 -07:00
|
|
|
MIPSOpcode JitBlockCache::GetEmuHackOpForBlock(int blockNum) const {
|
2013-12-19 13:34:06 +01:00
|
|
|
int off = (int)(blocks_[blockNum].normalEntry - codeBlock_->GetBasePtr());
|
2013-08-24 14:43:49 -07:00
|
|
|
return MIPSOpcode(MIPS_EMUHACK_OPCODE | off);
|
2012-11-01 16:19:01 +01:00
|
|
|
}
|
|
|
|
|
2013-12-19 00:39:49 +01:00
|
|
|
int JitBlockCache::GetBlockNumberFromStartAddress(u32 addr, bool realBlocksOnly) {
|
2013-12-19 13:34:06 +01:00
|
|
|
if (!blocks_)
|
2013-08-15 23:23:39 -07:00
|
|
|
return -1;
|
2013-12-19 00:39:49 +01:00
|
|
|
|
2013-08-24 14:43:49 -07:00
|
|
|
MIPSOpcode inst = MIPSOpcode(Memory::Read_U32(addr));
|
2013-04-27 00:22:18 +02:00
|
|
|
int bl = GetBlockNumberFromEmuHackOp(inst);
|
2013-12-19 00:39:49 +01:00
|
|
|
if (bl < 0) {
|
2013-12-19 13:31:13 +01:00
|
|
|
if (!realBlocksOnly) {
|
2013-12-19 13:34:06 +01:00
|
|
|
// Wasn't an emu hack op, look through proxyBlockIndices_.
|
2013-12-19 13:31:13 +01:00
|
|
|
for (size_t i = 0; i < proxyBlockIndices_.size(); i++) {
|
|
|
|
int blockIndex = proxyBlockIndices_[i];
|
2014-03-29 22:21:38 +01:00
|
|
|
if (blocks_[blockIndex].originalAddress == addr && !blocks_[blockIndex].proxyFor && !blocks_[blockIndex].invalid)
|
2013-12-19 13:31:13 +01:00
|
|
|
return blockIndex;
|
|
|
|
}
|
2013-12-19 00:39:49 +01:00
|
|
|
}
|
2012-11-01 16:19:01 +01:00
|
|
|
return -1;
|
2013-12-19 00:39:49 +01:00
|
|
|
}
|
|
|
|
|
2013-12-19 13:34:06 +01:00
|
|
|
if (blocks_[bl].originalAddress != addr)
|
2013-12-19 13:31:13 +01:00
|
|
|
return -1;
|
2013-12-19 00:39:49 +01:00
|
|
|
|
2012-11-01 16:19:01 +01:00
|
|
|
return bl;
|
|
|
|
}
|
|
|
|
|
2013-12-19 13:34:06 +01:00
|
|
|
void JitBlockCache::GetBlockNumbersFromAddress(u32 em_address, std::vector<int> *block_numbers) {
|
|
|
|
for (int i = 0; i < num_blocks_; i++)
|
|
|
|
if (blocks_[i].ContainsAddress(em_address))
|
2012-11-01 16:19:01 +01:00
|
|
|
block_numbers->push_back(i);
|
|
|
|
}
|
|
|
|
|
2013-11-30 18:21:47 -08:00
|
|
|
u32 JitBlockCache::GetAddressFromBlockPtr(const u8 *ptr) const {
|
|
|
|
if (!codeBlock_->IsInSpace(ptr))
|
|
|
|
return (u32)-1;
|
|
|
|
|
2013-12-19 13:34:06 +01:00
|
|
|
for (int i = 0; i < num_blocks_; ++i) {
|
|
|
|
const auto &b = blocks_[i];
|
2013-12-01 02:34:37 -08:00
|
|
|
if (!b.invalid && ptr >= b.checkedEntry && ptr < b.normalEntry + b.codeSize) {
|
2013-11-30 18:21:47 -08:00
|
|
|
return b.originalAddress;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-12-19 00:39:49 +01:00
|
|
|
// It's in jit somewhere, but we must have deleted it.
|
2013-11-30 18:21:47 -08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-12-19 13:34:06 +01:00
|
|
|
MIPSOpcode JitBlockCache::GetOriginalFirstOp(int block_num) {
|
|
|
|
if (block_num >= num_blocks_ || block_num < 0) {
|
2013-08-24 14:43:49 -07:00
|
|
|
return MIPSOpcode(block_num);
|
2012-11-01 16:19:01 +01:00
|
|
|
}
|
2013-12-19 13:34:06 +01:00
|
|
|
return blocks_[block_num].originalFirstOpcode;
|
2012-11-01 16:19:01 +01:00
|
|
|
}
|
|
|
|
|
2013-12-19 13:34:06 +01:00
|
|
|
void JitBlockCache::LinkBlockExits(int i) {
|
|
|
|
JitBlock &b = blocks_[i];
|
2013-04-27 19:35:42 +02:00
|
|
|
if (b.invalid) {
|
2012-11-01 16:19:01 +01:00
|
|
|
// This block is dead. Don't relink it.
|
|
|
|
return;
|
|
|
|
}
|
2013-12-19 00:39:49 +01:00
|
|
|
|
2013-08-15 23:23:39 -07:00
|
|
|
for (int e = 0; e < MAX_JIT_BLOCK_EXITS; e++) {
|
2013-04-27 19:35:42 +02:00
|
|
|
if (b.exitAddress[e] != INVALID_EXIT && !b.linkStatus[e]) {
|
2012-11-01 16:19:01 +01:00
|
|
|
int destinationBlock = GetBlockNumberFromStartAddress(b.exitAddress[e]);
|
2013-04-27 19:35:42 +02:00
|
|
|
if (destinationBlock != -1) {
|
2013-04-26 23:58:20 +02:00
|
|
|
#if defined(ARM)
|
|
|
|
ARMXEmitter emit(b.exitPtrs[e]);
|
2013-12-19 13:34:06 +01:00
|
|
|
emit.B(blocks_[destinationBlock].checkedEntry);
|
2013-04-26 23:58:20 +02:00
|
|
|
emit.FlushIcache();
|
|
|
|
|
|
|
|
#elif defined(_M_IX86) || defined(_M_X64)
|
2012-11-01 16:19:01 +01:00
|
|
|
XEmitter emit(b.exitPtrs[e]);
|
2013-12-19 13:34:06 +01:00
|
|
|
emit.JMP(blocks_[destinationBlock].checkedEntry, true);
|
2013-08-10 16:39:28 +02:00
|
|
|
#elif defined(PPC)
|
|
|
|
PPCXEmitter emit(b.exitPtrs[e]);
|
2013-12-19 13:34:06 +01:00
|
|
|
emit.B(blocks_[destinationBlock].checkedEntry);
|
2013-08-10 16:39:28 +02:00
|
|
|
emit.FlushIcache();
|
2013-04-26 23:58:20 +02:00
|
|
|
#endif
|
2012-11-01 16:19:01 +01:00
|
|
|
b.linkStatus[e] = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
|
2013-12-19 13:34:06 +01:00
|
|
|
void JitBlockCache::LinkBlock(int i) {
|
2012-11-01 16:19:01 +01:00
|
|
|
LinkBlockExits(i);
|
2013-12-19 13:34:06 +01:00
|
|
|
JitBlock &b = blocks_[i];
|
2012-11-01 16:19:01 +01:00
|
|
|
pair<multimap<u32, int>::iterator, multimap<u32, int>::iterator> ppp;
|
|
|
|
// equal_range(b) returns pair<iterator,iterator> representing the range
|
|
|
|
// of element with key b
|
2013-12-19 13:34:06 +01:00
|
|
|
ppp = links_to_.equal_range(b.originalAddress);
|
2012-11-01 16:19:01 +01:00
|
|
|
if (ppp.first == ppp.second)
|
|
|
|
return;
|
2013-02-04 08:49:58 -05:00
|
|
|
for (multimap<u32, int>::iterator iter = ppp.first; iter != ppp.second; ++iter) {
|
|
|
|
// PanicAlert("Linking block %i to block %i", iter->second, i);
|
|
|
|
LinkBlockExits(iter->second);
|
2012-11-01 16:19:01 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-12-19 13:34:06 +01:00
|
|
|
void JitBlockCache::UnlinkBlock(int i) {
|
|
|
|
JitBlock &b = blocks_[i];
|
2012-11-01 16:19:01 +01:00
|
|
|
pair<multimap<u32, int>::iterator, multimap<u32, int>::iterator> ppp;
|
2013-12-19 13:34:06 +01:00
|
|
|
ppp = links_to_.equal_range(b.originalAddress);
|
2012-11-01 16:19:01 +01:00
|
|
|
if (ppp.first == ppp.second)
|
|
|
|
return;
|
2013-02-04 08:49:58 -05:00
|
|
|
for (multimap<u32, int>::iterator iter = ppp.first; iter != ppp.second; ++iter) {
|
2013-12-19 13:34:06 +01:00
|
|
|
JitBlock &sourceBlock = blocks_[iter->second];
|
|
|
|
for (int e = 0; e < MAX_JIT_BLOCK_EXITS; e++) {
|
2012-11-01 16:19:01 +01:00
|
|
|
if (sourceBlock.exitAddress[e] == b.originalAddress)
|
|
|
|
sourceBlock.linkStatus[e] = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-12-19 13:34:06 +01:00
|
|
|
std::vector<u32> JitBlockCache::SaveAndClearEmuHackOps() {
|
2013-09-29 13:51:09 -07:00
|
|
|
std::vector<u32> result;
|
2013-12-19 13:34:06 +01:00
|
|
|
result.resize(num_blocks_);
|
2013-09-29 13:51:09 -07:00
|
|
|
|
2013-12-19 13:34:06 +01:00
|
|
|
for (int block_num = 0; block_num < num_blocks_; ++block_num) {
|
|
|
|
JitBlock &b = blocks_[block_num];
|
2013-09-29 13:51:09 -07:00
|
|
|
if (b.invalid)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
const u32 emuhack = GetEmuHackOpForBlock(block_num).encoding;
|
|
|
|
result[block_num] = emuhack;
|
|
|
|
// The goal here is to prevent restoring it if it did not match (in case originalFirstOpcode does match.)
|
|
|
|
if (Memory::ReadUnchecked_U32(b.originalAddress) != emuhack)
|
|
|
|
b.originalFirstOpcode = INVALID_ORIGINAL_OP;
|
|
|
|
else
|
|
|
|
Memory::Write_Opcode_JIT(b.originalAddress, b.originalFirstOpcode);
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2013-12-19 13:34:06 +01:00
|
|
|
void JitBlockCache::RestoreSavedEmuHackOps(std::vector<u32> saved) {
|
|
|
|
if (num_blocks_ != (int)saved.size()) {
|
2013-09-29 13:51:09 -07:00
|
|
|
ERROR_LOG(JIT, "RestoreSavedEmuHackOps: Wrong saved block size.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-12-19 13:34:06 +01:00
|
|
|
for (int block_num = 0; block_num < num_blocks_; ++block_num) {
|
|
|
|
const JitBlock &b = blocks_[block_num];
|
2013-09-29 13:51:09 -07:00
|
|
|
if (b.invalid)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// Only if we restored it, write it back.
|
|
|
|
if (Memory::ReadUnchecked_U32(b.originalAddress) == b.originalFirstOpcode.encoding)
|
|
|
|
Memory::Write_Opcode_JIT(b.originalAddress, MIPSOpcode(saved[block_num]));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-12-19 13:34:06 +01:00
|
|
|
void JitBlockCache::DestroyBlock(int block_num, bool invalidate) {
|
|
|
|
if (block_num < 0 || block_num >= num_blocks_) {
|
2013-09-01 00:19:41 -07:00
|
|
|
ERROR_LOG_REPORT(JIT, "DestroyBlock: Invalid block number %d", block_num);
|
2012-11-01 16:19:01 +01:00
|
|
|
return;
|
|
|
|
}
|
2013-12-19 13:34:06 +01:00
|
|
|
JitBlock *b = &blocks_[block_num];
|
2013-12-19 00:39:49 +01:00
|
|
|
|
2013-12-19 13:31:13 +01:00
|
|
|
// Pure proxy blocks always point directly to a real block, there should be no chains of
|
|
|
|
// proxy-only blocks pointing to proxy-only blocks.
|
2013-12-19 00:39:49 +01:00
|
|
|
// Follow a block proxy chain.
|
|
|
|
// Destroy the block that transitively has this as a proxy. Likely the root block once inlined
|
|
|
|
// this block or its 'parent', so now that this block has changed, the root block must be destroyed.
|
2014-03-29 22:21:38 +01:00
|
|
|
if (b->proxyFor) {
|
|
|
|
for (size_t i = 0; i < b->proxyFor->size(); i++) {
|
|
|
|
int proxied_blocknum = GetBlockNumberFromStartAddress((*b->proxyFor)[i], false);
|
2014-05-04 01:25:19 -07:00
|
|
|
// If it was already cleared, we don't know which to destroy.
|
|
|
|
if (proxied_blocknum != -1) {
|
|
|
|
DestroyBlock(proxied_blocknum, invalidate);
|
|
|
|
}
|
2014-03-29 22:21:38 +01:00
|
|
|
}
|
|
|
|
b->proxyFor->clear();
|
|
|
|
delete b->proxyFor;
|
|
|
|
b->proxyFor = 0;
|
2013-12-19 00:39:49 +01:00
|
|
|
}
|
2013-12-19 13:31:13 +01:00
|
|
|
// TODO: Remove from proxyBlockIndices_.
|
2013-12-19 00:39:49 +01:00
|
|
|
|
|
|
|
// TODO: Handle the case when there's a proxy block and a regular JIT block at the same location.
|
|
|
|
// In this case we probably "leak" the proxy block currently (no memory leak but it'll stay enabled).
|
|
|
|
|
|
|
|
if (b->invalid) {
|
2012-11-01 16:19:01 +01:00
|
|
|
if (invalidate)
|
|
|
|
ERROR_LOG(JIT, "Invalidating invalid block %d", block_num);
|
|
|
|
return;
|
|
|
|
}
|
2013-12-19 13:34:06 +01:00
|
|
|
|
2013-12-19 00:39:49 +01:00
|
|
|
b->invalid = true;
|
|
|
|
if (Memory::ReadUnchecked_U32(b->originalAddress) == GetEmuHackOpForBlock(block_num).encoding)
|
|
|
|
Memory::Write_Opcode_JIT(b->originalAddress, b->originalFirstOpcode);
|
2012-11-01 16:19:01 +01:00
|
|
|
|
2013-12-19 00:39:49 +01:00
|
|
|
// It's not safe to set normalEntry to 0 here, since we use a binary search
|
|
|
|
// that looks at that later to find blocks. Marking it invalid is enough.
|
2012-11-01 16:19:01 +01:00
|
|
|
|
2013-12-19 00:39:49 +01:00
|
|
|
UnlinkBlock(block_num);
|
2013-04-26 23:58:20 +02:00
|
|
|
|
|
|
|
#if defined(ARM)
|
|
|
|
|
|
|
|
// Send anyone who tries to run this block back to the dispatcher.
|
|
|
|
// Not entirely ideal, but .. pretty good.
|
|
|
|
// I hope there's enough space...
|
|
|
|
// checkedEntry is the only "linked" entrance so it's enough to overwrite that.
|
2013-12-19 00:39:49 +01:00
|
|
|
ARMXEmitter emit((u8 *)b->checkedEntry);
|
|
|
|
emit.MOVI2R(R0, b->originalAddress);
|
2013-04-26 23:58:20 +02:00
|
|
|
emit.STR(R0, CTXREG, offsetof(MIPSState, pc));
|
|
|
|
emit.B(MIPSComp::jit->dispatcher);
|
|
|
|
emit.FlushIcache();
|
|
|
|
|
|
|
|
#elif defined(_M_IX86) || defined(_M_X64)
|
|
|
|
|
2012-11-01 16:19:01 +01:00
|
|
|
// Send anyone who tries to run this block back to the dispatcher.
|
|
|
|
// Not entirely ideal, but .. pretty good.
|
|
|
|
// Spurious entrances from previously linked blocks can only come through checkedEntry
|
2013-12-19 00:39:49 +01:00
|
|
|
XEmitter emit((u8 *)b->checkedEntry);
|
2013-12-19 13:34:06 +01:00
|
|
|
emit.MOV(32, M(&mips_->pc), Imm32(b->originalAddress));
|
2012-11-01 16:19:01 +01:00
|
|
|
emit.JMP(MIPSComp::jit->Asm().dispatcher, true);
|
2013-08-10 16:39:28 +02:00
|
|
|
#elif defined(PPC)
|
2013-12-19 00:39:49 +01:00
|
|
|
PPCXEmitter emit((u8 *)b->checkedEntry);
|
|
|
|
emit.MOVI2R(R3, b->originalAddress);
|
2013-08-10 16:39:28 +02:00
|
|
|
emit.STW(R0, CTXREG, offsetof(MIPSState, pc));
|
|
|
|
emit.B(MIPSComp::jit->dispatcher);
|
|
|
|
emit.FlushIcache();
|
2013-04-26 23:58:20 +02:00
|
|
|
#endif
|
2012-11-01 16:19:01 +01:00
|
|
|
}
|
|
|
|
|
2013-12-19 13:34:06 +01:00
|
|
|
void JitBlockCache::InvalidateICache(u32 address, const u32 length) {
|
2012-11-01 16:19:01 +01:00
|
|
|
// Convert the logical address to a physical address for the block map
|
|
|
|
u32 pAddr = address & 0x1FFFFFFF;
|
|
|
|
|
|
|
|
// destroy JIT blocks
|
|
|
|
// !! this works correctly under assumption that any two overlapping blocks end at the same address
|
2013-09-01 00:19:41 -07:00
|
|
|
// TODO: This may not be a safe assumption with jit continuing enabled.
|
2013-12-19 13:34:06 +01:00
|
|
|
std::map<pair<u32,u32>, u32>::iterator it1 = block_map_.lower_bound(std::make_pair(pAddr, 0)), it2 = it1;
|
|
|
|
while (it2 != block_map_.end() && it2->first.second < pAddr + length) {
|
2012-11-01 16:19:01 +01:00
|
|
|
DestroyBlock(it2->second, true);
|
|
|
|
it2++;
|
|
|
|
}
|
2013-12-19 13:34:06 +01:00
|
|
|
|
2012-11-01 16:19:01 +01:00
|
|
|
if (it1 != it2)
|
2013-12-19 13:34:06 +01:00
|
|
|
block_map_.erase(it1, it2);
|
2012-11-01 16:19:01 +01:00
|
|
|
}
|