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/IR/IRNativeCommon.cpp
Views: 1401
// Copyright (c) 2023- PPSSPP 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 <atomic>18#include <climits>19#include <thread>20#include "Common/Profiler/Profiler.h"21#include "Common/StringUtils.h"22#include "Common/TimeUtil.h"23#include "Core/Core.h"24#include "Core/Debugger/SymbolMap.h"25#include "Core/MemMap.h"26#include "Core/MIPS/MIPSTables.h"27#include "Core/MIPS/IR/IRNativeCommon.h"2829using namespace MIPSComp;3031namespace MIPSComp {3233// Compile time flag to enable debug stats for not compiled ops.34static constexpr bool enableDebugStats = false;35// Compile time flag for enabling the simple IR jit profiler.36static constexpr bool enableDebugProfiler = false;3738// Used only for debugging when enableDebug is true above.39static std::map<uint8_t, int> debugSeenNotCompiledIR;40static std::map<const char *, int> debugSeenNotCompiled;41static std::map<std::pair<uint32_t, IRProfilerStatus>, int> debugSeenPCUsage;42static double lastDebugStatsLog = 0.0;43static constexpr double debugStatsFrequency = 5.0;4445static std::thread debugProfilerThread;46std::atomic<bool> debugProfilerThreadStatus = false;4748template <int N>49class IRProfilerTopValues {50public:51void Add(const std::pair<uint32_t, IRProfilerStatus> &v, int c) {52for (int i = 0; i < N; ++i) {53if (c > counts[i]) {54counts[i] = c;55values[i] = v;56return;57}58}59}6061int counts[N]{};62std::pair<uint32_t, IRProfilerStatus> values[N]{};63};6465const char *IRProfilerStatusToString(IRProfilerStatus s) {66switch (s) {67case IRProfilerStatus::NOT_RUNNING: return "NOT_RUNNING";68case IRProfilerStatus::IN_JIT: return "IN_JIT";69case IRProfilerStatus::TIMER_ADVANCE: return "TIMER_ADVANCE";70case IRProfilerStatus::COMPILING: return "COMPILING";71case IRProfilerStatus::MATH_HELPER: return "MATH_HELPER";72case IRProfilerStatus::REPLACEMENT: return "REPLACEMENT";73case IRProfilerStatus::SYSCALL: return "SYSCALL";74case IRProfilerStatus::INTERPRET: return "INTERPRET";75case IRProfilerStatus::IR_INTERPRET: return "IR_INTERPRET";76}77return "INVALID";78}7980static void LogDebugStats() {81if (!enableDebugStats && !enableDebugProfiler)82return;8384double now = time_now_d();85if (now < lastDebugStatsLog + debugStatsFrequency)86return;87lastDebugStatsLog = now;8889int worstIROp = -1;90int worstIRVal = 0;91for (auto it : debugSeenNotCompiledIR) {92if (it.second > worstIRVal) {93worstIRVal = it.second;94worstIROp = it.first;95}96}97debugSeenNotCompiledIR.clear();9899const char *worstName = nullptr;100int worstVal = 0;101for (auto it : debugSeenNotCompiled) {102if (it.second > worstVal) {103worstVal = it.second;104worstName = it.first;105}106}107debugSeenNotCompiled.clear();108109IRProfilerTopValues<4> slowestPCs;110int64_t totalCount = 0;111for (auto it : debugSeenPCUsage) {112slowestPCs.Add(it.first, it.second);113totalCount += it.second;114}115debugSeenPCUsage.clear();116117if (worstIROp != -1)118WARN_LOG(Log::JIT, "Most not compiled IR op: %s (%d)", GetIRMeta((IROp)worstIROp)->name, worstIRVal);119if (worstName != nullptr)120WARN_LOG(Log::JIT, "Most not compiled op: %s (%d)", worstName, worstVal);121if (slowestPCs.counts[0] != 0) {122for (int i = 0; i < 4; ++i) {123uint32_t pc = slowestPCs.values[i].first;124const char *status = IRProfilerStatusToString(slowestPCs.values[i].second);125const std::string label = g_symbolMap ? g_symbolMap->GetDescription(pc) : "";126WARN_LOG(Log::JIT, "Slowest sampled PC #%d: %08x (%s)/%s (%f%%)", i, pc, label.c_str(), status, 100.0 * (double)slowestPCs.counts[i] / (double)totalCount);127}128}129}130131bool IRNativeBackend::DebugStatsEnabled() const {132return enableDebugStats;133}134135bool IRNativeBackend::DebugProfilerEnabled() const {136return enableDebugProfiler;137}138139void IRNativeBackend::NotifyMIPSInterpret(const char *name) {140_assert_(enableDebugStats);141debugSeenNotCompiled[name]++;142}143144void IRNativeBackend::DoMIPSInst(uint32_t value) {145MIPSOpcode op;146memcpy(&op, &value, sizeof(op));147148if constexpr (enableDebugStats)149debugSeenNotCompiled[MIPSGetName(op)]++;150151MIPSInterpret(op);152}153154// This is called from IR->JIT implementation to fall back to the IR interpreter for missing ops.155// Not fast.156uint32_t IRNativeBackend::DoIRInst(uint64_t value) {157IRInst inst[2]{};158memcpy(&inst[0], &value, sizeof(value));159if constexpr (enableDebugStats)160debugSeenNotCompiledIR[(uint8_t)inst[0].op]++;161// Doesn't really matter what value it returns as PC.162inst[1].op = IROp::ExitToPC;163return IRInterpret(currentMIPS, &inst[0]);164}165166int IRNativeBackend::ReportBadAddress(uint32_t addr, uint32_t alignment, uint32_t isWrite) {167const auto toss = [&](MemoryExceptionType t) {168Core_MemoryException(addr, alignment, currentMIPS->pc, t);169return coreState != CORE_RUNNING ? 1 : 0;170};171172if (!Memory::IsValidRange(addr, alignment)) {173MemoryExceptionType t = isWrite == 1 ? MemoryExceptionType::WRITE_WORD : MemoryExceptionType::READ_WORD;174if (alignment > 4)175t = isWrite ? MemoryExceptionType::WRITE_BLOCK : MemoryExceptionType::READ_BLOCK;176return toss(t);177} else if (alignment > 1 && (addr & (alignment - 1)) != 0) {178return toss(MemoryExceptionType::ALIGNMENT);179}180return 0;181}182183IRNativeBackend::IRNativeBackend(IRBlockCache &blocks) : blocks_(blocks) {}184185IRNativeBackend::~IRNativeBackend() {186if (debugProfilerThreadStatus) {187debugProfilerThreadStatus = false;188debugProfilerThread.join();189}190}191192void IRNativeBackend::CompileIRInst(IRInst inst) {193switch (inst.op) {194case IROp::Nop:195break;196197case IROp::SetConst:198case IROp::SetConstF:199case IROp::Downcount:200case IROp::SetPC:201case IROp::SetPCConst:202CompIR_Basic(inst);203break;204205case IROp::Add:206case IROp::Sub:207case IROp::AddConst:208case IROp::SubConst:209case IROp::Neg:210CompIR_Arith(inst);211break;212213case IROp::And:214case IROp::Or:215case IROp::Xor:216case IROp::AndConst:217case IROp::OrConst:218case IROp::XorConst:219case IROp::Not:220CompIR_Logic(inst);221break;222223case IROp::Mov:224case IROp::Ext8to32:225case IROp::Ext16to32:226CompIR_Assign(inst);227break;228229case IROp::ReverseBits:230case IROp::BSwap16:231case IROp::BSwap32:232case IROp::Clz:233CompIR_Bits(inst);234break;235236case IROp::Shl:237case IROp::Shr:238case IROp::Sar:239case IROp::Ror:240case IROp::ShlImm:241case IROp::ShrImm:242case IROp::SarImm:243case IROp::RorImm:244CompIR_Shift(inst);245break;246247case IROp::Slt:248case IROp::SltConst:249case IROp::SltU:250case IROp::SltUConst:251CompIR_Compare(inst);252break;253254case IROp::MovZ:255case IROp::MovNZ:256case IROp::Max:257case IROp::Min:258CompIR_CondAssign(inst);259break;260261case IROp::MtLo:262case IROp::MtHi:263case IROp::MfLo:264case IROp::MfHi:265CompIR_HiLo(inst);266break;267268case IROp::Mult:269case IROp::MultU:270case IROp::Madd:271case IROp::MaddU:272case IROp::Msub:273case IROp::MsubU:274CompIR_Mult(inst);275break;276277case IROp::Div:278case IROp::DivU:279CompIR_Div(inst);280break;281282case IROp::Load8:283case IROp::Load8Ext:284case IROp::Load16:285case IROp::Load16Ext:286case IROp::Load32:287case IROp::Load32Linked:288CompIR_Load(inst);289break;290291case IROp::Load32Left:292case IROp::Load32Right:293CompIR_LoadShift(inst);294break;295296case IROp::LoadFloat:297CompIR_FLoad(inst);298break;299300case IROp::LoadVec4:301CompIR_VecLoad(inst);302break;303304case IROp::Store8:305case IROp::Store16:306case IROp::Store32:307CompIR_Store(inst);308break;309310case IROp::Store32Conditional:311CompIR_CondStore(inst);312break;313314case IROp::Store32Left:315case IROp::Store32Right:316CompIR_StoreShift(inst);317break;318319case IROp::StoreFloat:320CompIR_FStore(inst);321break;322323case IROp::StoreVec4:324CompIR_VecStore(inst);325break;326327case IROp::FAdd:328case IROp::FSub:329case IROp::FMul:330case IROp::FDiv:331case IROp::FSqrt:332case IROp::FNeg:333CompIR_FArith(inst);334break;335336case IROp::FMin:337case IROp::FMax:338CompIR_FCondAssign(inst);339break;340341case IROp::FMov:342case IROp::FAbs:343case IROp::FSign:344CompIR_FAssign(inst);345break;346347case IROp::FRound:348case IROp::FTrunc:349case IROp::FCeil:350case IROp::FFloor:351CompIR_FRound(inst);352break;353354case IROp::FCvtWS:355case IROp::FCvtSW:356case IROp::FCvtScaledWS:357case IROp::FCvtScaledSW:358CompIR_FCvt(inst);359break;360361case IROp::FSat0_1:362case IROp::FSatMinus1_1:363CompIR_FSat(inst);364break;365366case IROp::FCmp:367case IROp::FCmovVfpuCC:368case IROp::FCmpVfpuBit:369case IROp::FCmpVfpuAggregate:370CompIR_FCompare(inst);371break;372373case IROp::RestoreRoundingMode:374case IROp::ApplyRoundingMode:375case IROp::UpdateRoundingMode:376CompIR_RoundingMode(inst);377break;378379case IROp::SetCtrlVFPU:380case IROp::SetCtrlVFPUReg:381case IROp::SetCtrlVFPUFReg:382case IROp::FpCondFromReg:383case IROp::FpCondToReg:384case IROp::FpCtrlFromReg:385case IROp::FpCtrlToReg:386case IROp::VfpuCtrlToReg:387case IROp::FMovFromGPR:388case IROp::FMovToGPR:389CompIR_Transfer(inst);390break;391392case IROp::Vec4Init:393case IROp::Vec4Shuffle:394case IROp::Vec4Blend:395case IROp::Vec4Mov:396CompIR_VecAssign(inst);397break;398399case IROp::Vec4Add:400case IROp::Vec4Sub:401case IROp::Vec4Mul:402case IROp::Vec4Div:403case IROp::Vec4Scale:404case IROp::Vec4Neg:405case IROp::Vec4Abs:406CompIR_VecArith(inst);407break;408409case IROp::Vec4Dot:410CompIR_VecHoriz(inst);411break;412413case IROp::Vec2Unpack16To31:414case IROp::Vec2Unpack16To32:415case IROp::Vec4Unpack8To32:416case IROp::Vec4DuplicateUpperBitsAndShift1:417case IROp::Vec4Pack31To8:418case IROp::Vec4Pack32To8:419case IROp::Vec2Pack31To16:420case IROp::Vec2Pack32To16:421CompIR_VecPack(inst);422break;423424case IROp::Vec4ClampToZero:425case IROp::Vec2ClampToZero:426CompIR_VecClamp(inst);427break;428429case IROp::FSin:430case IROp::FCos:431case IROp::FRSqrt:432case IROp::FRecip:433case IROp::FAsin:434CompIR_FSpecial(inst);435break;436437case IROp::Interpret:438CompIR_Interpret(inst);439break;440441case IROp::Syscall:442case IROp::CallReplacement:443case IROp::Break:444CompIR_System(inst);445break;446447case IROp::Breakpoint:448case IROp::MemoryCheck:449CompIR_Breakpoint(inst);450break;451452case IROp::ValidateAddress8:453case IROp::ValidateAddress16:454case IROp::ValidateAddress32:455case IROp::ValidateAddress128:456CompIR_ValidateAddress(inst);457break;458459case IROp::ExitToConst:460case IROp::ExitToReg:461case IROp::ExitToPC:462CompIR_Exit(inst);463break;464465case IROp::ExitToConstIfEq:466case IROp::ExitToConstIfNeq:467case IROp::ExitToConstIfGtZ:468case IROp::ExitToConstIfGeZ:469case IROp::ExitToConstIfLtZ:470case IROp::ExitToConstIfLeZ:471case IROp::ExitToConstIfFpTrue:472case IROp::ExitToConstIfFpFalse:473CompIR_ExitIf(inst);474break;475476default:477_assert_msg_(false, "Unexpected IR op %d", (int)inst.op);478CompIR_Generic(inst);479break;480}481}482483IRNativeJit::IRNativeJit(MIPSState *mipsState)484: IRJit(mipsState, true), debugInterface_(blocks_) {}485486void IRNativeJit::Init(IRNativeBackend &backend) {487backend_ = &backend;488debugInterface_.Init(backend_);489backend_->GenerateFixedCode(mips_);490491// Wanted this to be a reference, but vtbls get in the way. Shouldn't change.492hooks_ = backend.GetNativeHooks();493494if (enableDebugProfiler && hooks_.profilerPC) {495debugProfilerThreadStatus = true;496debugProfilerThread = std::thread([&] {497// Spin, spin spin... maybe could at least hook into sleeps.498while (debugProfilerThreadStatus) {499IRProfilerStatus stat = *hooks_.profilerStatus;500uint32_t pc = *hooks_.profilerPC;501if (stat != IRProfilerStatus::NOT_RUNNING && stat != IRProfilerStatus::SYSCALL) {502debugSeenPCUsage[std::make_pair(pc, stat)]++;503}504}505});506}507}508509bool IRNativeJit::CompileNativeBlock(IRBlockCache *irblockCache, int block_num, bool preload) {510return backend_->CompileBlock(irblockCache, block_num, preload);511}512513void IRNativeJit::FinalizeNativeBlock(IRBlockCache *irblockCache, int block_num) {514backend_->FinalizeBlock(irblockCache, block_num, jo);515}516517void IRNativeJit::RunLoopUntil(u64 globalticks) {518if constexpr (enableDebugStats || enableDebugProfiler) {519LogDebugStats();520}521522PROFILE_THIS_SCOPE("jit");523hooks_.enterDispatcher();524}525526void IRNativeJit::ClearCache() {527IRJit::ClearCache();528backend_->ClearAllBlocks();529}530531bool IRNativeJit::DescribeCodePtr(const u8 *ptr, std::string &name) {532if (ptr != nullptr && backend_->DescribeCodePtr(ptr, name))533return true;534535int offset = backend_->OffsetFromCodePtr(ptr);536if (offset == -1)537return false;538539int block_num = -1;540int block_offset = INT_MAX;541for (int i = 0; i < blocks_.GetNumBlocks(); ++i) {542const auto &b = blocks_.GetBlock(i);543int b_start = b->GetNativeOffset();544if (b_start > offset)545continue;546547int b_end = backend_->GetNativeBlock(i)->checkedOffset;548int b_offset = offset - b_start;549if (b_end > b_start && b_end >= offset) {550// For sure within the block.551block_num = i;552block_offset = b_offset;553break;554}555556if (b_offset < block_offset) {557// Possibly within the block, unless in some other block...558block_num = i;559block_offset = b_offset;560}561}562563// Used by profiling tools that don't like spaces.564if (block_num == -1) {565name = "unknownOrDeletedBlock";566return true;567}568569const IRBlock *block = blocks_.GetBlock(block_num);570if (block) {571u32 start = 0, size = 0;572block->GetRange(&start, &size);573574// It helps to know which func this block is inside.575const std::string label = g_symbolMap ? g_symbolMap->GetDescription(start) : "";576if (!label.empty())577name = StringFromFormat("block%d_%08x_%s_0x%x", block_num, start, label.c_str(), block_offset);578else579name = StringFromFormat("block%d_%08x_0x%x", block_num, start, block_offset);580return true;581}582return false;583}584585bool IRNativeJit::CodeInRange(const u8 *ptr) const {586return backend_->CodeInRange(ptr);587}588589bool IRNativeJit::IsAtDispatchFetch(const u8 *ptr) const {590return ptr == backend_->GetNativeHooks().dispatchFetch;591}592593const u8 *IRNativeJit::GetDispatcher() const {594return backend_->GetNativeHooks().dispatcher;595}596597const u8 *IRNativeJit::GetCrashHandler() const {598return backend_->GetNativeHooks().crashHandler;599}600601void IRNativeJit::UpdateFCR31() {602backend_->UpdateFCR31(mips_);603}604605JitBlockCacheDebugInterface *IRNativeJit::GetBlockCacheDebugInterface() {606return &debugInterface_;607}608609bool IRNativeBackend::CodeInRange(const u8 *ptr) const {610return CodeBlock().IsInSpace(ptr);611}612613bool IRNativeBackend::DescribeCodePtr(const u8 *ptr, std::string &name) const {614if (!CodeBlock().IsInSpace(ptr))615return false;616617// Used in disassembly viewer.618if (ptr == (const uint8_t *)hooks_.enterDispatcher) {619name = "enterDispatcher";620} else if (ptr == hooks_.dispatcher) {621name = "dispatcher";622} else if (ptr == hooks_.dispatchFetch) {623name = "dispatchFetch";624} else if (ptr == hooks_.crashHandler) {625name = "crashHandler";626} else {627return false;628}629return true;630}631632int IRNativeBackend::OffsetFromCodePtr(const u8 *ptr) {633auto &codeBlock = CodeBlock();634if (!codeBlock.IsInSpace(ptr))635return -1;636return (int)codeBlock.GetOffset(ptr);637}638639void IRNativeBackend::FinalizeBlock(IRBlockCache *irBlockCache, int block_num, const JitOptions &jo) {640IRBlock *block = irBlockCache->GetBlock(block_num);641if (jo.enableBlocklink) {642uint32_t pc = block->GetOriginalStart();643644// First, link other blocks to this one now that it's finalized.645auto incoming = linksTo_.equal_range(pc);646for (auto it = incoming.first; it != incoming.second; ++it) {647auto &exits = nativeBlocks_[it->second].exits;648for (auto &blockExit : exits) {649if (blockExit.dest == pc)650OverwriteExit(blockExit.offset, blockExit.len, block_num);651}652}653654// And also any blocks from this one, in case we're finalizing it later.655auto &outgoing = nativeBlocks_[block_num].exits;656for (auto &blockExit : outgoing) {657int dstBlockNum = blocks_.GetBlockNumberFromStartAddress(blockExit.dest);658const IRNativeBlock *nativeBlock = GetNativeBlock(dstBlockNum);659if (nativeBlock)660OverwriteExit(blockExit.offset, blockExit.len, dstBlockNum);661}662}663}664665const IRNativeBlock *IRNativeBackend::GetNativeBlock(int block_num) const {666if (block_num < 0 || block_num >= (int)nativeBlocks_.size())667return nullptr;668return &nativeBlocks_[block_num];669}670671void IRNativeBackend::SetBlockCheckedOffset(int block_num, int offset) {672if (block_num >= (int)nativeBlocks_.size())673nativeBlocks_.resize(block_num + 1);674675nativeBlocks_[block_num].checkedOffset = offset;676}677678void IRNativeBackend::AddLinkableExit(int block_num, uint32_t pc, int exitStartOffset, int exitLen) {679linksTo_.emplace(pc, block_num);680681if (block_num >= (int)nativeBlocks_.size())682nativeBlocks_.resize(block_num + 1);683IRNativeBlockExit blockExit;684blockExit.offset = exitStartOffset;685blockExit.len = exitLen;686blockExit.dest = pc;687nativeBlocks_[block_num].exits.push_back(blockExit);688}689690void IRNativeBackend::EraseAllLinks(int block_num) {691if (block_num == -1) {692linksTo_.clear();693nativeBlocks_.clear();694} else {695linksTo_.erase(block_num);696if (block_num < (int)nativeBlocks_.size())697nativeBlocks_[block_num].exits.clear();698}699}700701IRNativeBlockCacheDebugInterface::IRNativeBlockCacheDebugInterface(const IRBlockCache &irBlocks)702: irBlocks_(irBlocks) {}703704void IRNativeBlockCacheDebugInterface::Init(const IRNativeBackend *backend) {705codeBlock_ = &backend->CodeBlock();706backend_ = backend;707}708709bool IRNativeBlockCacheDebugInterface::IsValidBlock(int blockNum) const {710return irBlocks_.IsValidBlock(blockNum);711}712713JitBlockMeta IRNativeBlockCacheDebugInterface::GetBlockMeta(int blockNum) const {714return irBlocks_.GetBlockMeta(blockNum);715}716717int IRNativeBlockCacheDebugInterface::GetNumBlocks() const {718return irBlocks_.GetNumBlocks();719}720721int IRNativeBlockCacheDebugInterface::GetBlockNumberFromStartAddress(u32 em_address, bool realBlocksOnly) const {722return irBlocks_.GetBlockNumberFromStartAddress(em_address, realBlocksOnly);723}724725JitBlockProfileStats IRNativeBlockCacheDebugInterface::GetBlockProfileStats(int blockNum) const {726return irBlocks_.GetBlockProfileStats(blockNum);727}728729void IRNativeBlockCacheDebugInterface::GetBlockCodeRange(int blockNum, int *startOffset, int *size) const {730int blockOffset = irBlocks_.GetBlock(blockNum)->GetNativeOffset();731int endOffset = backend_->GetNativeBlock(blockNum)->checkedOffset;732733// If endOffset is before, the checked entry is before the block start.734if (endOffset < blockOffset) {735// We assume linear allocation. Maybe a bit dangerous, should always be right.736if (blockNum + 1 >= GetNumBlocks()) {737// Last block, get from current code pointer.738endOffset = (int)codeBlock_->GetOffset(codeBlock_->GetCodePtr());739} else {740endOffset = irBlocks_.GetBlock(blockNum + 1)->GetNativeOffset();741_assert_msg_(endOffset >= blockOffset, "Next block not sequential, block=%d/%08x, next=%d/%08x", blockNum, blockOffset, blockNum + 1, endOffset);742}743}744745*startOffset = blockOffset;746*size = endOffset - blockOffset;747}748749JitBlockDebugInfo IRNativeBlockCacheDebugInterface::GetBlockDebugInfo(int blockNum) const {750JitBlockDebugInfo debugInfo = irBlocks_.GetBlockDebugInfo(blockNum);751752int blockOffset, codeSize;753GetBlockCodeRange(blockNum, &blockOffset, &codeSize);754755const u8 *blockStart = codeBlock_->GetBasePtr() + blockOffset;756#if PPSSPP_ARCH(ARM)757debugInfo.targetDisasm = DisassembleArm2(blockStart, codeSize);758#elif PPSSPP_ARCH(ARM64)759debugInfo.targetDisasm = DisassembleArm64(blockStart, codeSize);760#elif PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64)761debugInfo.targetDisasm = DisassembleX86(blockStart, codeSize);762#elif PPSSPP_ARCH(RISCV64)763debugInfo.targetDisasm = DisassembleRV64(blockStart, codeSize);764#endif765return debugInfo;766}767768void IRNativeBlockCacheDebugInterface::ComputeStats(BlockCacheStats &bcStats) const {769double totalBloat = 0.0;770double maxBloat = 0.0;771double minBloat = 1000000000.0;772int numBlocks = GetNumBlocks();773for (int i = 0; i < numBlocks; ++i) {774const IRBlock &b = *irBlocks_.GetBlock(i);775776// Native size, not IR size.777int blockOffset, codeSize;778GetBlockCodeRange(i, &blockOffset, &codeSize);779if (codeSize == 0)780continue;781782// MIPS (PSP) size.783u32 origAddr, origSize;784b.GetRange(&origAddr, &origSize);785786double bloat = (double)codeSize / (double)origSize;787if (bloat < minBloat) {788minBloat = bloat;789bcStats.minBloatBlock = origAddr;790}791if (bloat > maxBloat) {792maxBloat = bloat;793bcStats.maxBloatBlock = origAddr;794}795totalBloat += bloat;796}797bcStats.numBlocks = numBlocks;798bcStats.minBloat = (float)minBloat;799bcStats.maxBloat = (float)maxBloat;800bcStats.avgBloat = (float)(totalBloat / (double)numBlocks);801}802803} // namespace MIPSComp804805806