CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
Path: blob/master/Core/MIPS/JitCommon/JitBlockCache.cpp
Views: 1401
// 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// -10 to safely leave space for some proxy blocks, which we don't check before we allocate (not ideal, but should work).94return num_blocks_ >= MAX_NUM_BLOCKS - 10;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() {122block_map_.clear();123proxyBlockMap_.clear();124for (int i = 0; i < num_blocks_; i++)125DestroyBlock(i, DestroyType::CLEAR);126links_to_.clear();127num_blocks_ = 0;128129blockMemRanges_[JITBLOCK_RANGE_SCRATCH] = std::make_pair(0xFFFFFFFF, 0x00000000);130blockMemRanges_[JITBLOCK_RANGE_RAMBOTTOM] = std::make_pair(0xFFFFFFFF, 0x00000000);131blockMemRanges_[JITBLOCK_RANGE_RAMTOP] = std::make_pair(0xFFFFFFFF, 0x00000000);132}133134void JitBlockCache::Reset() {135Shutdown();136Init();137}138139JitBlock *JitBlockCache::GetBlock(int no) {140return &blocks_[no];141}142143const JitBlock *JitBlockCache::GetBlock(int no) const {144return &blocks_[no];145}146147int JitBlockCache::AllocateBlock(u32 startAddress) {148JitBlock &b = blocks_[num_blocks_];149150b.proxyFor = 0;151// If there's an existing pure proxy block at the address, we need to ditch it and create a new one,152// taking over the proxied blocks.153int num = GetBlockNumberFromStartAddress(startAddress, false);154if (num >= 0) {155if (blocks_[num].IsPureProxy()) {156RemoveBlockMap(num);157blocks_[num].invalid = true;158b.proxyFor = new std::vector<u32>();159*b.proxyFor = *blocks_[num].proxyFor;160blocks_[num].proxyFor->clear();161delete blocks_[num].proxyFor;162blocks_[num].proxyFor = 0;163}164}165166b.invalid = false;167b.originalAddress = startAddress;168for (int i = 0; i < MAX_JIT_BLOCK_EXITS; ++i) {169b.exitAddress[i] = INVALID_EXIT;170b.exitPtrs[i] = 0;171b.linkStatus[i] = false;172}173b.blockNum = num_blocks_;174num_blocks_++; //commit the current block175return num_blocks_ - 1;176}177178void JitBlockCache::ProxyBlock(u32 rootAddress, u32 startAddress, u32 size, const u8 *codePtr) {179// If there's an existing block at the startAddress, add rootAddress as a proxy root of that block180// instead of creating a new block.181int num = GetBlockNumberFromStartAddress(startAddress, false);182if (num != -1) {183DEBUG_LOG(Log::HLE, "Adding proxy root %08x to block at %08x", rootAddress, startAddress);184if (!blocks_[num].proxyFor) {185blocks_[num].proxyFor = new std::vector<u32>();186}187blocks_[num].proxyFor->push_back(rootAddress);188}189190JitBlock &b = blocks_[num_blocks_];191b.invalid = false;192b.originalAddress = startAddress;193b.originalSize = size;194for (int i = 0; i < MAX_JIT_BLOCK_EXITS; ++i) {195b.exitAddress[i] = INVALID_EXIT;196b.exitPtrs[i] = 0;197b.linkStatus[i] = false;198}199b.exitAddress[0] = rootAddress;200b.blockNum = num_blocks_;201b.proxyFor = new std::vector<u32>();202b.SetPureProxy(); // flag as pure proxy block.203204// Make binary searches and stuff work ok205b.normalEntry = codePtr;206b.checkedEntry = codePtr;207proxyBlockMap_.emplace(startAddress, num_blocks_);208AddBlockMap(num_blocks_);209210num_blocks_++; //commit the current block211}212213void JitBlockCache::AddBlockMap(int block_num) {214const JitBlock &b = blocks_[block_num];215// Convert the logical address to a physical address for the block map216// Yeah, this'll work fine for PSP too I think.217u32 pAddr = b.originalAddress & 0x1FFFFFFF;218block_map_[std::make_pair(pAddr + 4 * b.originalSize, pAddr)] = block_num;219}220221void JitBlockCache::RemoveBlockMap(int block_num) {222const JitBlock &b = blocks_[block_num];223if (b.invalid) {224return;225}226227const u32 pAddr = b.originalAddress & 0x1FFFFFFF;228auto it = block_map_.find(std::make_pair(pAddr + 4 * b.originalSize, pAddr));229if (it != block_map_.end() && it->second == (u32)block_num) {230block_map_.erase(it);231} else {232// It wasn't in there, or it has the wrong key. Let's search...233for (auto it = block_map_.begin(); it != block_map_.end(); ++it) {234if (it->second == (u32)block_num) {235block_map_.erase(it);236break;237}238}239}240}241242static void ExpandRange(std::pair<u32, u32> &range, u32 newStart, u32 newEnd) {243range.first = std::min(range.first, newStart);244range.second = std::max(range.second, newEnd);245}246247void JitBlockCache::FinalizeBlock(int block_num, bool block_link) {248JitBlock &b = blocks_[block_num];249_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);250251b.originalFirstOpcode = Memory::Read_Opcode_JIT(b.originalAddress);252MIPSOpcode opcode = GetEmuHackOpForBlock(block_num);253Memory::Write_Opcode_JIT(b.originalAddress, opcode);254255// Note that this hashes the emuhack too, which is intentional.256b.compiledHash = HashJitBlock(b);257258AddBlockMap(block_num);259260if (block_link) {261for (int i = 0; i < MAX_JIT_BLOCK_EXITS; i++) {262if (b.exitAddress[i] != INVALID_EXIT) {263links_to_.emplace(b.exitAddress[i], block_num);264}265}266267LinkBlock(block_num);268LinkBlockExits(block_num);269}270271const u32 blockEnd = b.originalAddress + b.originalSize * 4 - 4;272if (Memory::IsScratchpadAddress(b.originalAddress)) {273ExpandRange(blockMemRanges_[JITBLOCK_RANGE_SCRATCH], b.originalAddress, blockEnd);274}275const u32 halfUserMemory = (PSP_GetUserMemoryEnd() - PSP_GetUserMemoryBase()) / 2;276if (b.originalAddress < PSP_GetUserMemoryBase() + halfUserMemory) {277ExpandRange(blockMemRanges_[JITBLOCK_RANGE_RAMBOTTOM], b.originalAddress, blockEnd);278}279if (blockEnd > PSP_GetUserMemoryBase() + halfUserMemory) {280ExpandRange(blockMemRanges_[JITBLOCK_RANGE_RAMTOP], b.originalAddress, blockEnd);281}282283#if defined USE_OPROFILE && USE_OPROFILE284char buf[100];285snprintf(buf, sizeof(buf), "EmuCode%x", b.originalAddress);286const u8* blockStart = blocks_[block_num].checkedEntry;287op_write_native_code(agent, buf, (uint64_t)blockStart, blockStart, b.normalEntry + b.codeSize - b.checkedEntry);288#endif289290#ifdef USE_VTUNE291snprintf(b.blockName, sizeof(b.blockName), "EmuCode_0x%08x", b.originalAddress);292293iJIT_Method_Load jmethod = {0};294jmethod.method_id = iJIT_GetNewMethodID();295jmethod.class_file_name = "";296jmethod.source_file_name = __FILE__;297jmethod.method_load_address = (void*)blocks_[block_num].checkedEntry;298jmethod.method_size = b.normalEntry + b.codeSize - b.checkedEntry;299jmethod.line_number_size = 0;300jmethod.method_name = b.blockName;301iJIT_NotifyEvent(iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED, (void*)&jmethod);302#endif303}304305bool JitBlockCache::RangeMayHaveEmuHacks(u32 start, u32 end) const {306for (int i = 0; i < JITBLOCK_RANGE_COUNT; ++i) {307if (end >= blockMemRanges_[i].first && start <= blockMemRanges_[i].second) {308return true;309}310}311return false;312}313314static int binary_search(const JitBlock blocks_[], const u8 *baseoff, int imin, int imax) {315while (imin < imax) {316int imid = (imin + imax) / 2;317if (blocks_[imid].normalEntry < baseoff)318imin = imid + 1;319else320imax = imid;321}322if ((imax == imin) && (blocks_[imin].normalEntry == baseoff))323return imin;324else325return -1;326}327328int JitBlockCache::GetBlockNumberFromEmuHackOp(MIPSOpcode inst, bool ignoreBad) const {329if (!num_blocks_ || !MIPS_IS_EMUHACK(inst)) // definitely not a JIT block330return -1;331int off = (inst & MIPS_EMUHACK_VALUE_MASK);332333const u8 *baseoff = codeBlock_->GetBasePtr() + off;334if (baseoff < codeBlock_->GetBasePtr() || baseoff >= codeBlock_->GetCodePtr()) {335if (!ignoreBad) {336ERROR_LOG(Log::JIT, "JitBlockCache: Invalid Emuhack Op %08x", inst.encoding);337}338return -1;339}340341int bl = binary_search(blocks_, baseoff, 0, num_blocks_ - 1);342if (bl >= 0 && blocks_[bl].invalid) {343return -1;344} else {345return bl;346}347}348349MIPSOpcode JitBlockCache::GetEmuHackOpForBlock(int blockNum) const {350int off = (int)(blocks_[blockNum].normalEntry - codeBlock_->GetBasePtr());351return MIPSOpcode(MIPS_EMUHACK_OPCODE | off);352}353354int JitBlockCache::GetBlockNumberFromStartAddress(u32 addr, bool realBlocksOnly) const {355if (!blocks_ || !Memory::IsValidAddress(addr))356return -1;357358MIPSOpcode inst = MIPSOpcode(Memory::Read_U32(addr));359int bl = GetBlockNumberFromEmuHackOp(inst);360if (bl < 0) {361if (!realBlocksOnly) {362// Wasn't an emu hack op, look through proxyBlockMap_.363auto range = proxyBlockMap_.equal_range(addr);364for (auto it = range.first; it != range.second; ++it) {365const int blockIndex = it->second;366if (blocks_[blockIndex].originalAddress == addr && !blocks_[blockIndex].proxyFor && !blocks_[blockIndex].invalid)367return blockIndex;368}369}370return -1;371}372373if (blocks_[bl].originalAddress != addr)374return -1;375376return bl;377}378379void JitBlockCache::GetBlockNumbersFromAddress(u32 em_address, std::vector<int> *block_numbers) {380for (int i = 0; i < num_blocks_; i++)381if (blocks_[i].ContainsAddress(em_address))382block_numbers->push_back(i);383}384385int JitBlockCache::GetBlockNumberFromAddress(u32 em_address) {386for (int i = 0; i < num_blocks_; i++) {387if (blocks_[i].ContainsAddress(em_address))388return i;389}390391return -1;392}393394u32 JitBlockCache::GetAddressFromBlockPtr(const u8 *ptr) const {395if (!codeBlock_->IsInSpace(ptr))396return (u32)-1;397398for (int i = 0; i < num_blocks_; ++i) {399const auto &b = blocks_[i];400if (!b.invalid && ptr >= b.checkedEntry && ptr < b.normalEntry + b.codeSize) {401return b.originalAddress;402}403}404405// It's in jit somewhere, but we must have deleted it.406return 0;407}408409MIPSOpcode JitBlockCache::GetOriginalFirstOp(int block_num) {410if (block_num >= num_blocks_ || block_num < 0) {411return MIPSOpcode(block_num);412}413return blocks_[block_num].originalFirstOpcode;414}415416void JitBlockCache::LinkBlockExits(int i) {417JitBlock &b = blocks_[i];418if (b.invalid) {419// This block is dead. Don't relink it.420return;421}422if (b.IsPureProxy()) {423// Pure proxies can't link, since they don't have code.424return;425}426427for (int e = 0; e < MAX_JIT_BLOCK_EXITS; e++) {428if (b.exitAddress[e] != INVALID_EXIT && !b.linkStatus[e]) {429int destinationBlock = GetBlockNumberFromStartAddress(b.exitAddress[e], true);430if (destinationBlock == -1) {431continue;432}433434JitBlock &eb = blocks_[destinationBlock];435// Make sure the destination is not invalid.436if (!eb.invalid) {437MIPSComp::jit->LinkBlock(b.exitPtrs[e], eb.checkedEntry);438b.linkStatus[e] = true;439}440}441}442}443444void JitBlockCache::LinkBlock(int i) {445LinkBlockExits(i);446JitBlock &b = blocks_[i];447// equal_range(b) returns pair<iterator,iterator> representing the range448// of element with key b449auto ppp = links_to_.equal_range(b.originalAddress);450if (ppp.first == ppp.second)451return;452for (auto iter = ppp.first; iter != ppp.second; ++iter) {453// INFO_LOG(Log::JIT, "Linking block %i to block %i", iter->second, i);454LinkBlockExits(iter->second);455}456}457458void JitBlockCache::UnlinkBlock(int i) {459JitBlock &b = blocks_[i];460auto ppp = links_to_.equal_range(b.originalAddress);461if (ppp.first == ppp.second)462return;463for (auto iter = ppp.first; iter != ppp.second; ++iter) {464if ((size_t)iter->second >= num_blocks_) {465// Something probably went very wrong. Try to stumble along nevertheless.466ERROR_LOG(Log::JIT, "UnlinkBlock: Invalid block number %d", iter->second);467continue;468}469JitBlock &sourceBlock = blocks_[iter->second];470for (int e = 0; e < MAX_JIT_BLOCK_EXITS; e++) {471if (sourceBlock.exitAddress[e] == b.originalAddress)472sourceBlock.linkStatus[e] = false;473}474}475}476477std::vector<u32> JitBlockCache::SaveAndClearEmuHackOps() {478std::vector<u32> result;479result.resize(num_blocks_);480481for (int block_num = 0; block_num < num_blocks_; ++block_num) {482JitBlock &b = blocks_[block_num];483if (b.invalid)484continue;485486const u32 emuhack = GetEmuHackOpForBlock(block_num).encoding;487if (Memory::ReadUnchecked_U32(b.originalAddress) == emuhack)488{489result[block_num] = emuhack;490Memory::Write_Opcode_JIT(b.originalAddress, b.originalFirstOpcode);491}492else493result[block_num] = 0;494}495496return result;497}498499void JitBlockCache::RestoreSavedEmuHackOps(const std::vector<u32> &saved) {500if (num_blocks_ != (int)saved.size()) {501ERROR_LOG(Log::JIT, "RestoreSavedEmuHackOps: Wrong saved block size.");502return;503}504505for (int block_num = 0; block_num < num_blocks_; ++block_num) {506const JitBlock &b = blocks_[block_num];507if (b.invalid || saved[block_num] == 0)508continue;509510// Only if we restored it, write it back.511if (Memory::ReadUnchecked_U32(b.originalAddress) == b.originalFirstOpcode.encoding)512Memory::Write_Opcode_JIT(b.originalAddress, MIPSOpcode(saved[block_num]));513}514}515516void JitBlockCache::DestroyBlock(int block_num, DestroyType type) {517if (block_num < 0 || block_num >= num_blocks_) {518ERROR_LOG_REPORT(Log::JIT, "DestroyBlock: Invalid block number %d", block_num);519return;520}521JitBlock *b = &blocks_[block_num];522// No point it being in there anymore.523RemoveBlockMap(block_num);524525// Pure proxy blocks always point directly to a real block, there should be no chains of526// proxy-only blocks pointing to proxy-only blocks.527// Follow a block proxy chain.528// Destroy the block that transitively has this as a proxy. Likely the root block once inlined529// this block or its 'parent', so now that this block has changed, the root block must be destroyed.530if (b->proxyFor) {531for (size_t i = 0; i < b->proxyFor->size(); i++) {532int proxied_blocknum = GetBlockNumberFromStartAddress((*b->proxyFor)[i], false);533// If it was already cleared, we don't know which to destroy.534if (proxied_blocknum != -1) {535DestroyBlock(proxied_blocknum, type);536}537}538b->proxyFor->clear();539delete b->proxyFor;540b->proxyFor = 0;541}542auto range = proxyBlockMap_.equal_range(b->originalAddress);543for (auto it = range.first; it != range.second; ++it) {544if (it->second == block_num) {545// Found it. Delete and bail.546proxyBlockMap_.erase(it);547break;548}549}550551// TODO: Handle the case when there's a proxy block and a regular JIT block at the same location.552// In this case we probably "leak" the proxy block currently (no memory leak but it'll stay enabled).553554if (b->invalid) {555if (type == DestroyType::INVALIDATE)556ERROR_LOG(Log::JIT, "Invalidating invalid block %d", block_num);557return;558}559560b->invalid = true;561if (!b->IsPureProxy()) {562if (Memory::ReadUnchecked_U32(b->originalAddress) == GetEmuHackOpForBlock(block_num).encoding)563Memory::Write_Opcode_JIT(b->originalAddress, b->originalFirstOpcode);564}565566// It's not safe to set normalEntry to 0 here, since we use a binary search567// that looks at that later to find blocks. Marking it invalid is enough.568569UnlinkBlock(block_num);570571// Don't change the jit code when invalidating a pure proxy block.572if (b->IsPureProxy()) {573return;574}575576if (b->checkedEntry) {577// We can skip this if we're clearing anyway, which cuts down on protect back and forth on WX exclusive.578if (type != DestroyType::CLEAR) {579u8 *writableEntry = codeBlock_->GetWritablePtrFromCodePtr(b->checkedEntry);580MIPSComp::jit->UnlinkBlock(writableEntry, b->originalAddress);581}582} else {583ERROR_LOG(Log::JIT, "Unlinking block with no entry: %08x (%d)", b->originalAddress, block_num);584}585}586587void JitBlockCache::InvalidateICache(u32 address, const u32 length) {588// Convert the logical address to a physical address for the block map589const u32 pAddr = address & 0x1FFFFFFF;590const u32 pEnd = pAddr + length;591592if (pEnd < pAddr) {593ERROR_LOG(Log::JIT, "Bad InvalidateICache: %08x with len=%d", address, length);594return;595}596597if (pAddr == 0 && pEnd >= 0x1FFFFFFF) {598InvalidateChangedBlocks();599return;600}601602// Blocks may start and end in overlapping ways, and destroying one invalidates iterators.603// So after destroying one, we start over.604do {605restart:606auto next = block_map_.lower_bound(std::make_pair(pAddr, 0));607auto last = block_map_.upper_bound(std::make_pair(pEnd + MAX_BLOCK_INSTRUCTIONS, 0));608// Note that if next is end(), last will be end() too (equal.)609for (; next != last; ++next) {610const u32 blockStart = next->first.second;611const u32 blockEnd = next->first.first;612if (blockStart < pEnd && blockEnd > pAddr) {613DestroyBlock(next->second, DestroyType::INVALIDATE);614// Our iterator is now invalid. Break and search again.615// Most of the time there shouldn't be a bunch of matching blocks.616goto restart;617}618}619// We got here - it wasn't in the map at all (or anymore.)620} while (false);621}622623void JitBlockCache::InvalidateChangedBlocks() {624// The primary goal of this is to make sure block linking is cleared up.625for (int block_num = 0; block_num < num_blocks_; ++block_num) {626JitBlock &b = blocks_[block_num];627if (b.invalid || b.IsPureProxy())628continue;629630bool changed = false;631if (JIT_USE_COMPILEDHASH) {632changed = b.compiledHash != HashJitBlock(b);633} else {634const u32 emuhack = GetEmuHackOpForBlock(block_num).encoding;635changed = Memory::ReadUnchecked_U32(b.originalAddress) != emuhack;636}637638if (changed) {639DEBUG_LOG(Log::JIT, "Invalidating changed block at %08x", b.originalAddress);640DestroyBlock(block_num, DestroyType::INVALIDATE);641}642}643}644645int JitBlockCache::GetBlockExitSize() {646#if PPSSPP_ARCH(ARM)647// Will depend on the sequence found to encode the destination address.648return 0;649#elif PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64)650return 15;651#elif PPSSPP_ARCH(ARM64)652// Will depend on the sequence found to encode the destination address.653return 0;654#elif PPSSPP_ARCH(RISCV64)655// Will depend on the sequence found to encode the destination address.656return 0;657#else658#warning GetBlockExitSize unimplemented659return 0;660#endif661}662663void JitBlockCache::ComputeStats(BlockCacheStats &bcStats) const {664double totalBloat = 0.0;665double maxBloat = 0.0;666double minBloat = 1000000000.0;667for (int i = 0; i < num_blocks_; i++) {668const JitBlock *b = GetBlock(i);669double codeSize = (double)b->codeSize;670if (codeSize == 0)671continue;672double origSize = (double)(4 * b->originalSize);673double bloat = codeSize / origSize;674if (bloat < minBloat) {675minBloat = bloat;676bcStats.minBloatBlock = b->originalAddress;677}678if (bloat > maxBloat) {679maxBloat = bloat;680bcStats.maxBloatBlock = b->originalAddress;681}682totalBloat += bloat;683}684bcStats.numBlocks = num_blocks_;685bcStats.minBloat = (float)minBloat;686bcStats.maxBloat = (float)maxBloat;687bcStats.avgBloat = (float)(totalBloat / (double)num_blocks_);688}689690JitBlockDebugInfo JitBlockCache::GetBlockDebugInfo(int blockNum) const {691JitBlockDebugInfo debugInfo{};692const JitBlock *block = GetBlock(blockNum);693debugInfo.originalAddress = block->originalAddress;694debugInfo.origDisasm.reserve(((block->originalAddress + block->originalSize * 4) - block->originalAddress) / 4);695for (u32 addr = block->originalAddress; addr <= block->originalAddress + block->originalSize * 4; addr += 4) {696char temp[256];697MIPSDisAsm(Memory::Read_Instruction(addr), addr, temp, sizeof(temp), true);698std::string mipsDis = temp;699debugInfo.origDisasm.push_back(mipsDis);700}701702#if PPSSPP_ARCH(ARM)703debugInfo.targetDisasm = DisassembleArm2(block->normalEntry, block->codeSize);704#elif PPSSPP_ARCH(ARM64)705debugInfo.targetDisasm = DisassembleArm64(block->normalEntry, block->codeSize);706#elif PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64)707debugInfo.targetDisasm = DisassembleX86(block->normalEntry, block->codeSize);708#elif PPSSPP_ARCH(RISCV64)709debugInfo.targetDisasm = DisassembleRV64(block->normalEntry, block->codeSize);710#endif711return debugInfo;712}713714715