Path: blob/master/Core/MIPS/JitCommon/JitBlockCache.cpp
3189 views
// Copyright (c) 2012- PPSSPP Project / Dolphin Project.12// This program is free software: you can redistribute it and/or modify3// it under the terms of the GNU General Public License as published by4// the Free Software Foundation, version 2.0 or later versions.56// This program is distributed in the hope that it will be useful,7// but WITHOUT ANY WARRANTY; without even the implied warranty of8// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the9// GNU General Public License 2.0 for more details.1011// A copy of the GPL 2.0 should have been included with the program.12// If not, see http://www.gnu.org/licenses/1314// Official git repository and contact information can be found at15// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.1617#include "ppsspp_config.h"18#include <cstddef>19#include <algorithm>2021#include "ext/xxhash.h"22#include "Common/CommonTypes.h"23#include "Common/Profiler/Profiler.h"2425#ifdef _WIN3226#include "Common/CommonWindows.h"27#endif2829#include "Core/Core.h"30#include "Core/MemMap.h"31#include "Core/CoreTiming.h"32#include "Core/Reporting.h"33#include "Core/Config.h"3435#include "Core/MIPS/MIPS.h"36#include "Core/MIPS/MIPSTables.h"37#include "Core/MIPS/MIPSAnalyst.h"3839#include "Core/MIPS/JitCommon/JitBlockCache.h"40#include "Core/MIPS/JitCommon/JitCommon.h"4142// #include "JitBase.h"4344// Enable define below to enable oprofile integration. For this to work,45// it requires at least oprofile version 0.9.4, and changing the build46// system to link the Dolphin executable against libopagent. Since the47// dependency is a little inconvenient and this is possibly a slight48// performance hit, it's not enabled by default, but it's useful for49// locating performance issues.50#if defined USE_OPROFILE && USE_OPROFILE51#include <opagent.h>5253op_agent_t agent;54#endif5556#if defined USE_VTUNE57#include <jitprofiling.h>58#pragma comment(lib, "libittnotify.lib")59#pragma comment(lib, "jitprofiling.lib")60#endif6162const u32 INVALID_EXIT = 0xFFFFFFFF;6364static uint64_t HashJitBlock(const JitBlock &b) {65PROFILE_THIS_SCOPE("jithash");66if (JIT_USE_COMPILEDHASH) {67// Includes the emuhack (or emuhacks) in memory.68if (Memory::IsValidRange(b.originalAddress, b.originalSize * 4)) {69return XXH3_64bits(Memory::GetPointerUnchecked(b.originalAddress), b.originalSize * 4);70} else {71// Hm, this would be bad.72return 0;73}74}75return 0;76}7778JitBlockCache::JitBlockCache(MIPSState *mipsState, CodeBlockCommon *codeBlock) :79codeBlock_(codeBlock) {80}8182JitBlockCache::~JitBlockCache() {83Shutdown();84}8586bool JitBlock::ContainsAddress(u32 em_address) const {87// WARNING - THIS DOES NOT WORK WITH JIT INLINING ENABLED.88// However, that doesn't exist yet so meh.89return em_address >= originalAddress && em_address < originalAddress + 4 * originalSize;90}9192bool JitBlockCache::IsFull() const {93// Subtract some amount to safely leave space for some proxy blocks, which we don't check before we allocate (not ideal, but should be enough).94return num_blocks_ >= MAX_NUM_BLOCKS - 512;95}9697void JitBlockCache::Init() {98#if defined USE_OPROFILE && USE_OPROFILE99agent = op_open_agent();100#endif101blocks_ = new JitBlock[MAX_NUM_BLOCKS];102Clear();103}104105void JitBlockCache::Shutdown() {106Clear(); // Make sure proxy block links are deleted107delete [] blocks_;108blocks_ = 0;109num_blocks_ = 0;110#if defined USE_OPROFILE && USE_OPROFILE111op_close_agent(agent);112#endif113114#ifdef USE_VTUNE115iJIT_NotifyEvent(iJVM_EVENT_TYPE_SHUTDOWN, NULL);116#endif117}118119// This clears the JIT cache. It's called from JitCache.cpp when the JIT cache120// is full and when saving and loading states.121void JitBlockCache::Clear() {122// Note: We intentionally clear the block_map_ first to avoid O(N^2) behavior in RemoveBlockMap123block_map_.clear();124for (int i = 0; i < num_blocks_; i++) {125DestroyBlock(i, DestroyType::CLEAR);126}127proxyBlockMap_.clear();128links_to_.clear();129num_blocks_ = 0;130131blockMemRanges_[JITBLOCK_RANGE_SCRATCH] = std::make_pair(0xFFFFFFFF, 0x00000000);132blockMemRanges_[JITBLOCK_RANGE_RAMBOTTOM] = std::make_pair(0xFFFFFFFF, 0x00000000);133blockMemRanges_[JITBLOCK_RANGE_RAMTOP] = std::make_pair(0xFFFFFFFF, 0x00000000);134}135136void JitBlockCache::Reset() {137Shutdown();138Init();139}140141JitBlock *JitBlockCache::GetBlock(int no) {142return &blocks_[no];143}144145const JitBlock *JitBlockCache::GetBlock(int no) const {146return &blocks_[no];147}148149int JitBlockCache::AllocateBlock(u32 startAddress) {150_assert_(num_blocks_ < MAX_NUM_BLOCKS);151152JitBlock &b = blocks_[num_blocks_];153154b.proxyFor = 0;155// If there's an existing pure proxy block at the address, we need to ditch it and create a new one,156// taking over the proxied blocks.157int num = GetBlockNumberFromStartAddress(startAddress, false);158if (num >= 0) {159if (blocks_[num].IsPureProxy()) {160RemoveBlockMap(num);161blocks_[num].invalid = true;162b.proxyFor = new std::vector<u32>();163*b.proxyFor = *blocks_[num].proxyFor;164blocks_[num].proxyFor->clear();165delete blocks_[num].proxyFor;166blocks_[num].proxyFor = 0;167}168}169170b.invalid = false;171b.originalAddress = startAddress;172for (int i = 0; i < MAX_JIT_BLOCK_EXITS; ++i) {173b.exitAddress[i] = INVALID_EXIT;174b.exitPtrs[i] = 0;175b.linkStatus[i] = false;176}177b.blockNum = num_blocks_;178num_blocks_++; //commit the current block179return num_blocks_ - 1;180}181182void JitBlockCache::ProxyBlock(u32 rootAddress, u32 startAddress, u32 size, const u8 *codePtr) {183_assert_(num_blocks_ < MAX_NUM_BLOCKS);184185// If there's an existing block at the startAddress, add rootAddress as a proxy root of that block186// instead of creating a new block.187int num = GetBlockNumberFromStartAddress(startAddress, false);188if (num != -1) {189DEBUG_LOG(Log::HLE, "Adding proxy root %08x to block at %08x", rootAddress, startAddress);190if (!blocks_[num].proxyFor) {191blocks_[num].proxyFor = new std::vector<u32>();192}193blocks_[num].proxyFor->push_back(rootAddress);194}195196JitBlock &b = blocks_[num_blocks_];197b.invalid = false;198b.originalAddress = startAddress;199b.originalSize = size;200for (int i = 0; i < MAX_JIT_BLOCK_EXITS; ++i) {201b.exitAddress[i] = INVALID_EXIT;202b.exitPtrs[i] = 0;203b.linkStatus[i] = false;204}205b.exitAddress[0] = rootAddress;206b.blockNum = num_blocks_;207b.proxyFor = new std::vector<u32>();208b.SetPureProxy(); // flag as pure proxy block.209210// Make binary searches and stuff work ok211b.normalEntry = codePtr;212b.checkedEntry = codePtr;213proxyBlockMap_.emplace(startAddress, num_blocks_);214AddBlockMap(num_blocks_);215216num_blocks_++; //commit the current block217}218219void JitBlockCache::AddBlockMap(int block_num) {220const JitBlock &b = blocks_[block_num];221// Convert the logical address to a physical address for the block map222u32 pAddr = b.originalAddress & 0x1FFFFFFF;223block_map_[std::make_pair(pAddr + 4 * b.originalSize, pAddr)] = block_num;224}225226void JitBlockCache::RemoveBlockMap(int block_num) {227const JitBlock &b = blocks_[block_num];228if (b.invalid) {229return;230}231232const u32 pAddr = b.originalAddress & 0x1FFFFFFF;233auto it = block_map_.find(std::make_pair(pAddr + 4 * b.originalSize, pAddr));234if (it != block_map_.end() && it->second == (u32)block_num) {235block_map_.erase(it);236} else {237// It wasn't in there, or it has the wrong key. Let's search...238// TODO: This is O(n), so O(n^2) when called for every block.239for (auto it = block_map_.begin(); it != block_map_.end(); ++it) {240if (it->second == (u32)block_num) {241_dbg_assert_(false);242block_map_.erase(it);243break;244}245}246}247}248249static void ExpandRange(std::pair<u32, u32> &range, u32 newStart, u32 newEnd) {250range.first = std::min(range.first, newStart);251range.second = std::max(range.second, newEnd);252}253254void JitBlockCache::FinalizeBlock(int block_num, bool block_link) {255JitBlock &b = blocks_[block_num];256_assert_msg_(Memory::IsValidAddress(b.originalAddress), "FinalizeBlock: Bad originalAddress %08x in block %d (b.num: %d) proxy: %s sz: %d", b.originalAddress, block_num, b.blockNum, b.proxyFor ? "y" : "n", b.codeSize);257258b.originalFirstOpcode = Memory::Read_Opcode_JIT(b.originalAddress);259MIPSOpcode opcode = GetEmuHackOpForBlock(block_num);260Memory::Write_Opcode_JIT(b.originalAddress, opcode);261262// Note that this hashes the emuhack too, which is intentional.263b.compiledHash = HashJitBlock(b);264265AddBlockMap(block_num);266267if (block_link) {268for (int i = 0; i < MAX_JIT_BLOCK_EXITS; i++) {269if (b.exitAddress[i] != INVALID_EXIT) {270links_to_.emplace(b.exitAddress[i], block_num);271}272}273274LinkBlock(block_num);275LinkBlockExits(block_num);276}277278const u32 blockEnd = b.originalAddress + b.originalSize * 4 - 4;279if (Memory::IsScratchpadAddress(b.originalAddress)) {280ExpandRange(blockMemRanges_[JITBLOCK_RANGE_SCRATCH], b.originalAddress, blockEnd);281}282const u32 halfUserMemory = (PSP_GetUserMemoryEnd() - PSP_GetUserMemoryBase()) / 2;283if (b.originalAddress < PSP_GetUserMemoryBase() + halfUserMemory) {284ExpandRange(blockMemRanges_[JITBLOCK_RANGE_RAMBOTTOM], b.originalAddress, blockEnd);285}286if (blockEnd > PSP_GetUserMemoryBase() + halfUserMemory) {287ExpandRange(blockMemRanges_[JITBLOCK_RANGE_RAMTOP], b.originalAddress, blockEnd);288}289290#if defined USE_OPROFILE && USE_OPROFILE291char buf[100];292snprintf(buf, sizeof(buf), "EmuCode%x", b.originalAddress);293const u8* blockStart = blocks_[block_num].checkedEntry;294op_write_native_code(agent, buf, (uint64_t)blockStart, blockStart, b.normalEntry + b.codeSize - b.checkedEntry);295#endif296297#ifdef USE_VTUNE298snprintf(b.blockName, sizeof(b.blockName), "EmuCode_0x%08x", b.originalAddress);299300iJIT_Method_Load jmethod = {0};301jmethod.method_id = iJIT_GetNewMethodID();302jmethod.class_file_name = "";303jmethod.source_file_name = __FILE__;304jmethod.method_load_address = (void*)blocks_[block_num].checkedEntry;305jmethod.method_size = b.normalEntry + b.codeSize - b.checkedEntry;306jmethod.line_number_size = 0;307jmethod.method_name = b.blockName;308iJIT_NotifyEvent(iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED, (void*)&jmethod);309#endif310}311312bool JitBlockCache::RangeMayHaveEmuHacks(u32 start, u32 end) const {313for (int i = 0; i < JITBLOCK_RANGE_COUNT; ++i) {314if (end >= blockMemRanges_[i].first && start <= blockMemRanges_[i].second) {315return true;316}317}318return false;319}320321static int binary_search(const JitBlock blocks_[], const u8 *baseoff, int imin, int imax) {322while (imin < imax) {323int imid = (imin + imax) >> 1;324if (blocks_[imid].normalEntry < baseoff)325imin = imid + 1;326else327imax = imid;328}329if ((imax == imin) && (blocks_[imin].normalEntry == baseoff))330return imin;331else332return -1;333}334335int JitBlockCache::GetBlockNumberFromEmuHackOp(MIPSOpcode inst, bool ignoreBad) const {336if (!num_blocks_ || !MIPS_IS_EMUHACK(inst)) // definitely not a JIT block337return -1;338int off = (inst & MIPS_EMUHACK_VALUE_MASK);339340const u8 *baseoff = codeBlock_->GetBasePtr() + off;341if (baseoff < codeBlock_->GetBasePtr() || baseoff >= codeBlock_->GetCodePtr()) {342if (!ignoreBad) {343ERROR_LOG(Log::JIT, "JitBlockCache: Invalid Emuhack Op %08x", inst.encoding);344}345return -1;346}347348int bl = binary_search(blocks_, baseoff, 0, num_blocks_ - 1);349if (bl >= 0 && blocks_[bl].invalid) {350return -1;351} else {352return bl;353}354}355356MIPSOpcode JitBlockCache::GetEmuHackOpForBlock(int blockNum) const {357int off = (int)(blocks_[blockNum].normalEntry - codeBlock_->GetBasePtr());358return MIPSOpcode(MIPS_EMUHACK_OPCODE | off);359}360361int JitBlockCache::GetBlockNumberFromStartAddress(u32 addr, bool realBlocksOnly) const {362if (!blocks_ || !Memory::IsValidAddress(addr))363return -1;364365MIPSOpcode inst = MIPSOpcode(Memory::Read_U32(addr));366int bl = GetBlockNumberFromEmuHackOp(inst);367if (bl < 0) {368if (!realBlocksOnly) {369// Wasn't an emu hack op, look through proxyBlockMap_.370auto range = proxyBlockMap_.equal_range(addr);371for (auto it = range.first; it != range.second; ++it) {372const int blockIndex = it->second;373if (blocks_[blockIndex].originalAddress == addr && !blocks_[blockIndex].proxyFor && !blocks_[blockIndex].invalid)374return blockIndex;375}376}377return -1;378}379380if (blocks_[bl].originalAddress != addr)381return -1;382383return bl;384}385386void JitBlockCache::GetBlockNumbersFromAddress(u32 em_address, std::vector<int> *block_numbers) {387for (int i = 0; i < num_blocks_; i++)388if (blocks_[i].ContainsAddress(em_address))389block_numbers->push_back(i);390}391392int JitBlockCache::GetBlockNumberFromAddress(u32 em_address) {393for (int i = 0; i < num_blocks_; i++) {394if (blocks_[i].ContainsAddress(em_address))395return i;396}397398return -1;399}400401u32 JitBlockCache::GetAddressFromBlockPtr(const u8 *ptr) const {402if (!codeBlock_->IsInSpace(ptr))403return (u32)-1;404405for (int i = 0; i < num_blocks_; ++i) {406const auto &b = blocks_[i];407if (!b.invalid && ptr >= b.checkedEntry && ptr < b.normalEntry + b.codeSize) {408return b.originalAddress;409}410}411412// It's in jit somewhere, but we must have deleted it.413return 0;414}415416MIPSOpcode JitBlockCache::GetOriginalFirstOp(int block_num) {417if (block_num >= num_blocks_ || block_num < 0) {418return MIPSOpcode(block_num);419}420return blocks_[block_num].originalFirstOpcode;421}422423void JitBlockCache::LinkBlockExits(int i) {424JitBlock &b = blocks_[i];425if (b.invalid) {426// This block is dead. Don't relink it.427return;428}429if (b.IsPureProxy()) {430// Pure proxies can't link, since they don't have code.431return;432}433434for (int e = 0; e < MAX_JIT_BLOCK_EXITS; e++) {435if (b.exitAddress[e] != INVALID_EXIT && !b.linkStatus[e]) {436int destinationBlock = GetBlockNumberFromStartAddress(b.exitAddress[e], true);437if (destinationBlock == -1) {438continue;439}440441JitBlock &eb = blocks_[destinationBlock];442// Make sure the destination is not invalid.443if (!eb.invalid) {444MIPSComp::jit->LinkBlock(b.exitPtrs[e], eb.checkedEntry);445b.linkStatus[e] = true;446}447}448}449}450451void JitBlockCache::LinkBlock(int i) {452LinkBlockExits(i);453JitBlock &b = blocks_[i];454// equal_range(b) returns pair<iterator,iterator> representing the range455// of element with key b456auto ppp = links_to_.equal_range(b.originalAddress);457if (ppp.first == ppp.second)458return;459for (auto iter = ppp.first; iter != ppp.second; ++iter) {460// INFO_LOG(Log::JIT, "Linking block %i to block %i", iter->second, i);461LinkBlockExits(iter->second);462}463}464465void JitBlockCache::UnlinkBlock(int i) {466JitBlock &b = blocks_[i];467auto ppp = links_to_.equal_range(b.originalAddress);468if (ppp.first == ppp.second)469return;470for (auto iter = ppp.first; iter != ppp.second; ++iter) {471if ((size_t)iter->second >= num_blocks_) {472// Something probably went very wrong. Try to stumble along nevertheless.473ERROR_LOG(Log::JIT, "UnlinkBlock: Invalid block number %d", iter->second);474continue;475}476JitBlock &sourceBlock = blocks_[iter->second];477for (int e = 0; e < MAX_JIT_BLOCK_EXITS; e++) {478if (sourceBlock.exitAddress[e] == b.originalAddress)479sourceBlock.linkStatus[e] = false;480}481}482}483484std::vector<u32> JitBlockCache::SaveAndClearEmuHackOps() {485std::vector<u32> result;486result.resize(num_blocks_);487488for (int block_num = 0; block_num < num_blocks_; ++block_num) {489JitBlock &b = blocks_[block_num];490if (b.invalid)491continue;492493const u32 emuhack = GetEmuHackOpForBlock(block_num).encoding;494if (Memory::ReadUnchecked_U32(b.originalAddress) == emuhack)495{496result[block_num] = emuhack;497Memory::Write_Opcode_JIT(b.originalAddress, b.originalFirstOpcode);498}499else500result[block_num] = 0;501}502503return result;504}505506void JitBlockCache::RestoreSavedEmuHackOps(const std::vector<u32> &saved) {507if (num_blocks_ != (int)saved.size()) {508ERROR_LOG(Log::JIT, "RestoreSavedEmuHackOps: Wrong saved block size.");509return;510}511512for (int block_num = 0; block_num < num_blocks_; ++block_num) {513const JitBlock &b = blocks_[block_num];514if (b.invalid || saved[block_num] == 0)515continue;516517// Only if we restored it, write it back.518if (Memory::ReadUnchecked_U32(b.originalAddress) == b.originalFirstOpcode.encoding)519Memory::Write_Opcode_JIT(b.originalAddress, MIPSOpcode(saved[block_num]));520}521}522523void JitBlockCache::DestroyBlock(int block_num, DestroyType type) {524if (block_num < 0 || block_num >= num_blocks_) {525ERROR_LOG_REPORT(Log::JIT, "DestroyBlock: Invalid block number %d", block_num);526return;527}528JitBlock *b = &blocks_[block_num];529// No point it being in there anymore.530RemoveBlockMap(block_num);531532// Pure proxy blocks always point directly to a real block, there should be no chains of533// proxy-only blocks pointing to proxy-only blocks.534// Follow a block proxy chain.535// Destroy the block that transitively has this as a proxy. Likely the root block once inlined536// this block or its 'parent', so now that this block has changed, the root block must be destroyed.537if (b->proxyFor) {538for (size_t i = 0; i < b->proxyFor->size(); i++) {539int proxied_blocknum = GetBlockNumberFromStartAddress((*b->proxyFor)[i], false);540// If it was already cleared, we don't know which to destroy.541if (proxied_blocknum != -1) {542DestroyBlock(proxied_blocknum, type);543}544}545b->proxyFor->clear();546delete b->proxyFor;547b->proxyFor = 0;548}549auto range = proxyBlockMap_.equal_range(b->originalAddress);550for (auto it = range.first; it != range.second; ++it) {551if (it->second == block_num) {552// Found it. Delete and bail.553proxyBlockMap_.erase(it);554break;555}556}557558// TODO: Handle the case when there's a proxy block and a regular JIT block at the same location.559// In this case we probably "leak" the proxy block currently (no memory leak but it'll stay enabled).560561if (b->invalid) {562if (type == DestroyType::INVALIDATE)563ERROR_LOG(Log::JIT, "Invalidating invalid block %d", block_num);564return;565}566567b->invalid = true;568if (!b->IsPureProxy()) {569if (Memory::ReadUnchecked_U32(b->originalAddress) == GetEmuHackOpForBlock(block_num).encoding)570Memory::Write_Opcode_JIT(b->originalAddress, b->originalFirstOpcode);571}572573// It's not safe to set normalEntry to 0 here, since we use a binary search574// that looks at that later to find blocks. Marking it invalid is enough.575576UnlinkBlock(block_num);577578// Don't change the jit code when invalidating a pure proxy block.579if (b->IsPureProxy()) {580return;581}582583if (b->checkedEntry) {584// We can skip this if we're clearing anyway, which cuts down on protect back and forth on WX exclusive.585if (type != DestroyType::CLEAR) {586u8 *writableEntry = codeBlock_->GetWritablePtrFromCodePtr(b->checkedEntry);587MIPSComp::jit->UnlinkBlock(writableEntry, b->originalAddress);588}589} else {590ERROR_LOG(Log::JIT, "Unlinking block with no entry: %08x (%d)", b->originalAddress, block_num);591}592}593594void JitBlockCache::InvalidateICache(u32 address, const u32 length) {595// Convert the logical address to a physical address for the block map596const u32 pAddr = address & 0x1FFFFFFF;597const u32 pEnd = pAddr + length;598599if (pEnd < pAddr) {600ERROR_LOG(Log::JIT, "Bad InvalidateICache: %08x with len=%d", address, length);601return;602}603604if (pAddr == 0 && pEnd >= 0x1FFFFFFF) {605InvalidateChangedBlocks();606return;607}608609// Blocks may start and end in overlapping ways, and destroying one invalidates iterators.610// So after destroying one, we start over.611do {612restart:613auto next = block_map_.lower_bound(std::make_pair(pAddr, 0));614auto last = block_map_.upper_bound(std::make_pair(pEnd + MAX_BLOCK_INSTRUCTIONS, 0));615// Note that if next is end(), last will be end() too (equal.)616for (; next != last; ++next) {617const u32 blockStart = next->first.second;618const u32 blockEnd = next->first.first;619if (blockStart < pEnd && blockEnd > pAddr) {620DestroyBlock(next->second, DestroyType::INVALIDATE);621// Our iterator is now invalid. Break and search again.622// Most of the time there shouldn't be a bunch of matching blocks.623goto restart;624}625}626// We got here - it wasn't in the map at all (or anymore.)627} while (false);628}629630void JitBlockCache::InvalidateChangedBlocks() {631// The primary goal of this is to make sure block linking is cleared up.632for (int block_num = 0; block_num < num_blocks_; ++block_num) {633JitBlock &b = blocks_[block_num];634if (b.invalid || b.IsPureProxy())635continue;636637bool changed = false;638if (JIT_USE_COMPILEDHASH) {639changed = b.compiledHash != HashJitBlock(b);640} else {641const u32 emuhack = GetEmuHackOpForBlock(block_num).encoding;642changed = Memory::ReadUnchecked_U32(b.originalAddress) != emuhack;643}644645if (changed) {646DEBUG_LOG(Log::JIT, "Invalidating changed block at %08x", b.originalAddress);647DestroyBlock(block_num, DestroyType::INVALIDATE);648}649}650}651652int JitBlockCache::GetBlockExitSize() {653#if PPSSPP_ARCH(ARM)654// Will depend on the sequence found to encode the destination address.655return 0;656#elif PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64)657return 15;658#elif PPSSPP_ARCH(ARM64)659// Will depend on the sequence found to encode the destination address.660return 0;661#elif PPSSPP_ARCH(RISCV64)662// Will depend on the sequence found to encode the destination address.663return 0;664#elif PPSSPP_ARCH(LOONGARCH64)665// Will depend on the sequence found to encode the destination address.666return 0;667#else668#warning GetBlockExitSize unimplemented669return 0;670#endif671}672673void JitBlockCache::ComputeStats(BlockCacheStats &bcStats) const {674double totalBloat = 0.0;675double maxBloat = 0.0;676double minBloat = 1000000000.0;677for (int i = 0; i < num_blocks_; i++) {678const JitBlock *b = GetBlock(i);679double codeSize = (double)b->codeSize;680if (codeSize == 0)681continue;682double origSize = (double)(4 * b->originalSize);683double bloat = codeSize / origSize;684if (bloat < minBloat) {685minBloat = bloat;686bcStats.minBloatBlock = b->originalAddress;687}688if (bloat > maxBloat) {689maxBloat = bloat;690bcStats.maxBloatBlock = b->originalAddress;691}692totalBloat += bloat;693}694bcStats.numBlocks = num_blocks_;695bcStats.minBloat = (float)minBloat;696bcStats.maxBloat = (float)maxBloat;697bcStats.avgBloat = (float)(totalBloat / (double)num_blocks_);698}699700JitBlockDebugInfo JitBlockCache::GetBlockDebugInfo(int blockNum) const {701JitBlockDebugInfo debugInfo{};702const JitBlock *block = GetBlock(blockNum);703debugInfo.originalAddress = block->originalAddress;704debugInfo.origDisasm.reserve(((block->originalAddress + block->originalSize * 4) - block->originalAddress) / 4);705for (u32 addr = block->originalAddress; addr <= block->originalAddress + block->originalSize * 4; addr += 4) {706char temp[256];707MIPSDisAsm(Memory::Read_Instruction(addr), addr, temp, sizeof(temp), true);708std::string mipsDis = temp;709debugInfo.origDisasm.push_back(mipsDis);710}711712#if PPSSPP_ARCH(ARM)713debugInfo.targetDisasm = DisassembleArm2(block->normalEntry, block->codeSize);714#elif PPSSPP_ARCH(ARM64)715debugInfo.targetDisasm = DisassembleArm64(block->normalEntry, block->codeSize);716#elif PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64)717debugInfo.targetDisasm = DisassembleX86(block->normalEntry, block->codeSize);718#elif PPSSPP_ARCH(RISCV64)719debugInfo.targetDisasm = DisassembleRV64(block->normalEntry, block->codeSize);720#elif PPSSPP_ARCH(LOONGARCH64)721debugInfo.targetDisasm = DisassembleLA64(block->normalEntry, block->codeSize);722#endif723return debugInfo;724}725726727