1 // Copyright (c) 2012- PPSSPP Project / Dolphin Project.
2
3 // This program is free software: you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation, version 2.0 or later versions.
6
7 // This program is distributed in the hope that it will be useful,
8 // but WITHOUT ANY WARRANTY; without even the implied warranty of
9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 // GNU General Public License 2.0 for more details.
11
12 // A copy of the GPL 2.0 should have been included with the program.
13 // If not, see http://www.gnu.org/licenses/
14
15 // Official git repository and contact information can be found at
16 // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
17
18 #include "ppsspp_config.h"
19 #include <cstddef>
20 #include <algorithm>
21
22 #include "Common.h"
23
24 #ifdef _WIN32
25 #include "Common/CommonWindows.h"
26 #endif
27
28 #include "Core/Core.h"
29 #include "Core/MemMap.h"
30 #include "Core/CoreTiming.h"
31 #include "Core/Reporting.h"
32
33 #include "Core/MIPS/MIPS.h"
34 #include "Core/MIPS/MIPSTables.h"
35 #include "Core/MIPS/MIPSAnalyst.h"
36
37 #include "Core/MIPS/JitCommon/JitBlockCache.h"
38 #include "Core/MIPS/JitCommon/JitCommon.h"
39
40 // #include "JitBase.h"
41
42 // Enable define below to enable oprofile integration. For this to work,
43 // it requires at least oprofile version 0.9.4, and changing the build
44 // system to link the Dolphin executable against libopagent. Since the
45 // dependency is a little inconvenient and this is possibly a slight
46 // performance hit, it's not enabled by default, but it's useful for
47 // locating performance issues.
48 #if defined USE_OPROFILE && USE_OPROFILE
49 #include <opagent.h>
50
51 op_agent_t agent;
52 #endif
53
54 #if defined USE_VTUNE
55 #include <jitprofiling.h>
56 #pragma comment(lib, "libittnotify.lib")
57 #pragma comment(lib, "jitprofiling.lib")
58 #endif
59
60 const u32 INVALID_EXIT = 0xFFFFFFFF;
61
JitBlockCache(MIPSState * mipsState,CodeBlockCommon * codeBlock)62 JitBlockCache::JitBlockCache(MIPSState *mipsState, CodeBlockCommon *codeBlock) :
63 codeBlock_(codeBlock), blocks_(nullptr), num_blocks_(0) {
64 }
65
~JitBlockCache()66 JitBlockCache::~JitBlockCache() {
67 Shutdown();
68 }
69
ContainsAddress(u32 em_address)70 bool JitBlock::ContainsAddress(u32 em_address) {
71 // WARNING - THIS DOES NOT WORK WITH JIT INLINING ENABLED.
72 // However, that doesn't exist yet so meh.
73 return (em_address >= originalAddress && em_address < originalAddress + 4 * originalSize);
74 }
75
IsFull() const76 bool JitBlockCache::IsFull() const {
77 return num_blocks_ >= MAX_NUM_BLOCKS - 1;
78 }
79
Init()80 void JitBlockCache::Init() {
81 #if defined USE_OPROFILE && USE_OPROFILE
82 agent = op_open_agent();
83 #endif
84 blocks_ = new JitBlock[MAX_NUM_BLOCKS];
85 Clear();
86 }
87
Shutdown()88 void JitBlockCache::Shutdown() {
89 Clear(); // Make sure proxy block links are deleted
90 delete [] blocks_;
91 blocks_ = 0;
92 num_blocks_ = 0;
93 #if defined USE_OPROFILE && USE_OPROFILE
94 op_close_agent(agent);
95 #endif
96
97 #ifdef USE_VTUNE
98 iJIT_NotifyEvent(iJVM_EVENT_TYPE_SHUTDOWN, NULL);
99 #endif
100 }
101
102 // This clears the JIT cache. It's called from JitCache.cpp when the JIT cache
103 // is full and when saving and loading states.
Clear()104 void JitBlockCache::Clear() {
105 block_map_.clear();
106 proxyBlockMap_.clear();
107 for (int i = 0; i < num_blocks_; i++)
108 DestroyBlock(i, DestroyType::CLEAR);
109 links_to_.clear();
110 num_blocks_ = 0;
111
112 blockMemRanges_[JITBLOCK_RANGE_SCRATCH] = std::make_pair(0xFFFFFFFF, 0x00000000);
113 blockMemRanges_[JITBLOCK_RANGE_RAMBOTTOM] = std::make_pair(0xFFFFFFFF, 0x00000000);
114 blockMemRanges_[JITBLOCK_RANGE_RAMTOP] = std::make_pair(0xFFFFFFFF, 0x00000000);
115 }
116
Reset()117 void JitBlockCache::Reset() {
118 Shutdown();
119 Init();
120 }
121
GetBlock(int no)122 JitBlock *JitBlockCache::GetBlock(int no) {
123 return &blocks_[no];
124 }
125
GetBlock(int no) const126 const JitBlock *JitBlockCache::GetBlock(int no) const {
127 return &blocks_[no];
128 }
129
AllocateBlock(u32 startAddress)130 int JitBlockCache::AllocateBlock(u32 startAddress) {
131 JitBlock &b = blocks_[num_blocks_];
132
133 b.proxyFor = 0;
134 // If there's an existing pure proxy block at the address, we need to ditch it and create a new one,
135 // taking over the proxied blocks.
136 int num = GetBlockNumberFromStartAddress(startAddress, false);
137 if (num >= 0) {
138 if (blocks_[num].IsPureProxy()) {
139 RemoveBlockMap(num);
140 blocks_[num].invalid = true;
141 b.proxyFor = new std::vector<u32>();
142 *b.proxyFor = *blocks_[num].proxyFor;
143 blocks_[num].proxyFor->clear();
144 delete blocks_[num].proxyFor;
145 blocks_[num].proxyFor = 0;
146 }
147 }
148
149 b.invalid = false;
150 b.originalAddress = startAddress;
151 for (int i = 0; i < MAX_JIT_BLOCK_EXITS; ++i) {
152 b.exitAddress[i] = INVALID_EXIT;
153 b.exitPtrs[i] = 0;
154 b.linkStatus[i] = false;
155 }
156 b.blockNum = num_blocks_;
157 num_blocks_++; //commit the current block
158 return num_blocks_ - 1;
159 }
160
ProxyBlock(u32 rootAddress,u32 startAddress,u32 size,const u8 * codePtr)161 void JitBlockCache::ProxyBlock(u32 rootAddress, u32 startAddress, u32 size, const u8 *codePtr) {
162 // If there's an existing block at the startAddress, add rootAddress as a proxy root of that block
163 // instead of creating a new block.
164 int num = GetBlockNumberFromStartAddress(startAddress, false);
165 if (num != -1) {
166 DEBUG_LOG(HLE, "Adding proxy root %08x to block at %08x", rootAddress, startAddress);
167 if (!blocks_[num].proxyFor) {
168 blocks_[num].proxyFor = new std::vector<u32>();
169 }
170 blocks_[num].proxyFor->push_back(rootAddress);
171 }
172
173 JitBlock &b = blocks_[num_blocks_];
174 b.invalid = false;
175 b.originalAddress = startAddress;
176 b.originalSize = size;
177 for (int i = 0; i < MAX_JIT_BLOCK_EXITS; ++i) {
178 b.exitAddress[i] = INVALID_EXIT;
179 b.exitPtrs[i] = 0;
180 b.linkStatus[i] = false;
181 }
182 b.exitAddress[0] = rootAddress;
183 b.blockNum = num_blocks_;
184 b.proxyFor = new std::vector<u32>();
185 b.SetPureProxy(); // flag as pure proxy block.
186
187 // Make binary searches and stuff work ok
188 b.normalEntry = codePtr;
189 b.checkedEntry = codePtr;
190 proxyBlockMap_.insert(std::make_pair(startAddress, num_blocks_));
191 AddBlockMap(num_blocks_);
192
193 num_blocks_++; //commit the current block
194 }
195
AddBlockMap(int block_num)196 void JitBlockCache::AddBlockMap(int block_num) {
197 const JitBlock &b = blocks_[block_num];
198 // Convert the logical address to a physical address for the block map
199 // Yeah, this'll work fine for PSP too I think.
200 u32 pAddr = b.originalAddress & 0x1FFFFFFF;
201 block_map_[std::make_pair(pAddr + 4 * b.originalSize, pAddr)] = block_num;
202 }
203
RemoveBlockMap(int block_num)204 void JitBlockCache::RemoveBlockMap(int block_num) {
205 const JitBlock &b = blocks_[block_num];
206 if (b.invalid) {
207 return;
208 }
209
210 const u32 pAddr = b.originalAddress & 0x1FFFFFFF;
211 auto it = block_map_.find(std::make_pair(pAddr + 4 * b.originalSize, pAddr));
212 if (it != block_map_.end() && it->second == (u32)block_num) {
213 block_map_.erase(it);
214 } else {
215 // It wasn't in there, or it has the wrong key. Let's search...
216 for (auto it = block_map_.begin(); it != block_map_.end(); ++it) {
217 if (it->second == (u32)block_num) {
218 block_map_.erase(it);
219 break;
220 }
221 }
222 }
223 }
224
ExpandRange(std::pair<u32,u32> & range,u32 newStart,u32 newEnd)225 static void ExpandRange(std::pair<u32, u32> &range, u32 newStart, u32 newEnd) {
226 range.first = std::min(range.first, newStart);
227 range.second = std::max(range.second, newEnd);
228 }
229
FinalizeBlock(int block_num,bool block_link)230 void JitBlockCache::FinalizeBlock(int block_num, bool block_link) {
231 JitBlock &b = blocks_[block_num];
232
233 b.originalFirstOpcode = Memory::Read_Opcode_JIT(b.originalAddress);
234 MIPSOpcode opcode = GetEmuHackOpForBlock(block_num);
235 Memory::Write_Opcode_JIT(b.originalAddress, opcode);
236
237 AddBlockMap(block_num);
238
239 if (block_link) {
240 for (int i = 0; i < MAX_JIT_BLOCK_EXITS; i++) {
241 if (b.exitAddress[i] != INVALID_EXIT) {
242 links_to_.insert(std::make_pair(b.exitAddress[i], block_num));
243 }
244 }
245
246 LinkBlock(block_num);
247 LinkBlockExits(block_num);
248 }
249
250 const u32 blockEnd = b.originalAddress + b.originalSize * 4 - 4;
251 if (Memory::IsScratchpadAddress(b.originalAddress)) {
252 ExpandRange(blockMemRanges_[JITBLOCK_RANGE_SCRATCH], b.originalAddress, blockEnd);
253 }
254 const u32 halfUserMemory = (PSP_GetUserMemoryEnd() - PSP_GetUserMemoryBase()) / 2;
255 if (b.originalAddress < PSP_GetUserMemoryBase() + halfUserMemory) {
256 ExpandRange(blockMemRanges_[JITBLOCK_RANGE_RAMBOTTOM], b.originalAddress, blockEnd);
257 }
258 if (blockEnd > PSP_GetUserMemoryBase() + halfUserMemory) {
259 ExpandRange(blockMemRanges_[JITBLOCK_RANGE_RAMTOP], b.originalAddress, blockEnd);
260 }
261
262 #if defined USE_OPROFILE && USE_OPROFILE
263 char buf[100];
264 sprintf(buf, "EmuCode%x", b.originalAddress);
265 const u8* blockStart = blocks_[block_num].checkedEntry;
266 op_write_native_code(agent, buf, (uint64_t)blockStart, blockStart, b.normalEntry + b.codeSize - b.checkedEntry);
267 #endif
268
269 #ifdef USE_VTUNE
270 sprintf(b.blockName, "EmuCode_0x%08x", b.originalAddress);
271
272 iJIT_Method_Load jmethod = {0};
273 jmethod.method_id = iJIT_GetNewMethodID();
274 jmethod.class_file_name = "";
275 jmethod.source_file_name = __FILE__;
276 jmethod.method_load_address = (void*)blocks_[block_num].checkedEntry;
277 jmethod.method_size = b.normalEntry + b.codeSize - b.checkedEntry;
278 jmethod.line_number_size = 0;
279 jmethod.method_name = b.blockName;
280 iJIT_NotifyEvent(iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED, (void*)&jmethod);
281 #endif
282 }
283
RangeMayHaveEmuHacks(u32 start,u32 end) const284 bool JitBlockCache::RangeMayHaveEmuHacks(u32 start, u32 end) const {
285 for (int i = 0; i < JITBLOCK_RANGE_COUNT; ++i) {
286 if (end >= blockMemRanges_[i].first && start <= blockMemRanges_[i].second) {
287 return true;
288 }
289 }
290 return false;
291 }
292
binary_search(JitBlock blocks_[],const u8 * baseoff,int imin,int imax)293 static int binary_search(JitBlock blocks_[], const u8 *baseoff, int imin, int imax) {
294 while (imin < imax) {
295 int imid = (imin + imax) / 2;
296 if (blocks_[imid].normalEntry < baseoff)
297 imin = imid + 1;
298 else
299 imax = imid;
300 }
301 if ((imax == imin) && (blocks_[imin].normalEntry == baseoff))
302 return imin;
303 else
304 return -1;
305 }
306
GetBlockNumberFromEmuHackOp(MIPSOpcode inst,bool ignoreBad) const307 int JitBlockCache::GetBlockNumberFromEmuHackOp(MIPSOpcode inst, bool ignoreBad) const {
308 if (!num_blocks_ || !MIPS_IS_EMUHACK(inst)) // definitely not a JIT block
309 return -1;
310 int off = (inst & MIPS_EMUHACK_VALUE_MASK);
311
312 const u8 *baseoff = codeBlock_->GetBasePtr() + off;
313 if (baseoff < codeBlock_->GetBasePtr() || baseoff >= codeBlock_->GetCodePtr()) {
314 if (!ignoreBad) {
315 ERROR_LOG(JIT, "JitBlockCache: Invalid Emuhack Op %08x", inst.encoding);
316 }
317 return -1;
318 }
319
320 int bl = binary_search(blocks_, baseoff, 0, num_blocks_ - 1);
321 if (bl >= 0 && blocks_[bl].invalid) {
322 return -1;
323 } else {
324 return bl;
325 }
326 }
327
GetEmuHackOpForBlock(int blockNum) const328 MIPSOpcode JitBlockCache::GetEmuHackOpForBlock(int blockNum) const {
329 int off = (int)(blocks_[blockNum].normalEntry - codeBlock_->GetBasePtr());
330 return MIPSOpcode(MIPS_EMUHACK_OPCODE | off);
331 }
332
GetBlockNumberFromStartAddress(u32 addr,bool realBlocksOnly) const333 int JitBlockCache::GetBlockNumberFromStartAddress(u32 addr, bool realBlocksOnly) const {
334 if (!blocks_ || !Memory::IsValidAddress(addr))
335 return -1;
336
337 MIPSOpcode inst = MIPSOpcode(Memory::Read_U32(addr));
338 int bl = GetBlockNumberFromEmuHackOp(inst);
339 if (bl < 0) {
340 if (!realBlocksOnly) {
341 // Wasn't an emu hack op, look through proxyBlockMap_.
342 auto range = proxyBlockMap_.equal_range(addr);
343 for (auto it = range.first; it != range.second; ++it) {
344 const int blockIndex = it->second;
345 if (blocks_[blockIndex].originalAddress == addr && !blocks_[blockIndex].proxyFor && !blocks_[blockIndex].invalid)
346 return blockIndex;
347 }
348 }
349 return -1;
350 }
351
352 if (blocks_[bl].originalAddress != addr)
353 return -1;
354
355 return bl;
356 }
357
GetBlockNumbersFromAddress(u32 em_address,std::vector<int> * block_numbers)358 void JitBlockCache::GetBlockNumbersFromAddress(u32 em_address, std::vector<int> *block_numbers) {
359 for (int i = 0; i < num_blocks_; i++)
360 if (blocks_[i].ContainsAddress(em_address))
361 block_numbers->push_back(i);
362 }
363
GetAddressFromBlockPtr(const u8 * ptr) const364 u32 JitBlockCache::GetAddressFromBlockPtr(const u8 *ptr) const {
365 if (!codeBlock_->IsInSpace(ptr))
366 return (u32)-1;
367
368 for (int i = 0; i < num_blocks_; ++i) {
369 const auto &b = blocks_[i];
370 if (!b.invalid && ptr >= b.checkedEntry && ptr < b.normalEntry + b.codeSize) {
371 return b.originalAddress;
372 }
373 }
374
375 // It's in jit somewhere, but we must have deleted it.
376 return 0;
377 }
378
GetOriginalFirstOp(int block_num)379 MIPSOpcode JitBlockCache::GetOriginalFirstOp(int block_num) {
380 if (block_num >= num_blocks_ || block_num < 0) {
381 return MIPSOpcode(block_num);
382 }
383 return blocks_[block_num].originalFirstOpcode;
384 }
385
LinkBlockExits(int i)386 void JitBlockCache::LinkBlockExits(int i) {
387 JitBlock &b = blocks_[i];
388 if (b.invalid) {
389 // This block is dead. Don't relink it.
390 return;
391 }
392 if (b.IsPureProxy()) {
393 // Pure proxies can't link, since they don't have code.
394 return;
395 }
396
397 for (int e = 0; e < MAX_JIT_BLOCK_EXITS; e++) {
398 if (b.exitAddress[e] != INVALID_EXIT && !b.linkStatus[e]) {
399 int destinationBlock = GetBlockNumberFromStartAddress(b.exitAddress[e], true);
400 if (destinationBlock == -1) {
401 continue;
402 }
403
404 JitBlock &eb = blocks_[destinationBlock];
405 // Make sure the destination is not invalid.
406 if (!eb.invalid) {
407 MIPSComp::jit->LinkBlock(b.exitPtrs[e], eb.checkedEntry);
408 b.linkStatus[e] = true;
409 }
410 }
411 }
412 }
413
LinkBlock(int i)414 void JitBlockCache::LinkBlock(int i) {
415 LinkBlockExits(i);
416 JitBlock &b = blocks_[i];
417 // equal_range(b) returns pair<iterator,iterator> representing the range
418 // of element with key b
419 auto ppp = links_to_.equal_range(b.originalAddress);
420 if (ppp.first == ppp.second)
421 return;
422 for (auto iter = ppp.first; iter != ppp.second; ++iter) {
423 // INFO_LOG(JIT, "Linking block %i to block %i", iter->second, i);
424 LinkBlockExits(iter->second);
425 }
426 }
427
UnlinkBlock(int i)428 void JitBlockCache::UnlinkBlock(int i) {
429 JitBlock &b = blocks_[i];
430 auto ppp = links_to_.equal_range(b.originalAddress);
431 if (ppp.first == ppp.second)
432 return;
433 for (auto iter = ppp.first; iter != ppp.second; ++iter) {
434 JitBlock &sourceBlock = blocks_[iter->second];
435 for (int e = 0; e < MAX_JIT_BLOCK_EXITS; e++) {
436 if (sourceBlock.exitAddress[e] == b.originalAddress)
437 sourceBlock.linkStatus[e] = false;
438 }
439 }
440 }
441
SaveAndClearEmuHackOps()442 std::vector<u32> JitBlockCache::SaveAndClearEmuHackOps() {
443 std::vector<u32> result;
444 result.resize(num_blocks_);
445
446 for (int block_num = 0; block_num < num_blocks_; ++block_num) {
447 JitBlock &b = blocks_[block_num];
448 if (b.invalid)
449 continue;
450
451 const u32 emuhack = GetEmuHackOpForBlock(block_num).encoding;
452 if (Memory::ReadUnchecked_U32(b.originalAddress) == emuhack)
453 {
454 result[block_num] = emuhack;
455 Memory::Write_Opcode_JIT(b.originalAddress, b.originalFirstOpcode);
456 }
457 else
458 result[block_num] = 0;
459 }
460
461 return result;
462 }
463
RestoreSavedEmuHackOps(std::vector<u32> saved)464 void JitBlockCache::RestoreSavedEmuHackOps(std::vector<u32> saved) {
465 if (num_blocks_ != (int)saved.size()) {
466 ERROR_LOG(JIT, "RestoreSavedEmuHackOps: Wrong saved block size.");
467 return;
468 }
469
470 for (int block_num = 0; block_num < num_blocks_; ++block_num) {
471 const JitBlock &b = blocks_[block_num];
472 if (b.invalid || saved[block_num] == 0)
473 continue;
474
475 // Only if we restored it, write it back.
476 if (Memory::ReadUnchecked_U32(b.originalAddress) == b.originalFirstOpcode.encoding)
477 Memory::Write_Opcode_JIT(b.originalAddress, MIPSOpcode(saved[block_num]));
478 }
479 }
480
DestroyBlock(int block_num,DestroyType type)481 void JitBlockCache::DestroyBlock(int block_num, DestroyType type) {
482 if (block_num < 0 || block_num >= num_blocks_) {
483 ERROR_LOG_REPORT(JIT, "DestroyBlock: Invalid block number %d", block_num);
484 return;
485 }
486 JitBlock *b = &blocks_[block_num];
487 // No point it being in there anymore.
488 RemoveBlockMap(block_num);
489
490 // Pure proxy blocks always point directly to a real block, there should be no chains of
491 // proxy-only blocks pointing to proxy-only blocks.
492 // Follow a block proxy chain.
493 // Destroy the block that transitively has this as a proxy. Likely the root block once inlined
494 // this block or its 'parent', so now that this block has changed, the root block must be destroyed.
495 if (b->proxyFor) {
496 for (size_t i = 0; i < b->proxyFor->size(); i++) {
497 int proxied_blocknum = GetBlockNumberFromStartAddress((*b->proxyFor)[i], false);
498 // If it was already cleared, we don't know which to destroy.
499 if (proxied_blocknum != -1) {
500 DestroyBlock(proxied_blocknum, type);
501 }
502 }
503 b->proxyFor->clear();
504 delete b->proxyFor;
505 b->proxyFor = 0;
506 }
507 auto range = proxyBlockMap_.equal_range(b->originalAddress);
508 for (auto it = range.first; it != range.second; ++it) {
509 if (it->second == block_num) {
510 // Found it. Delete and bail.
511 proxyBlockMap_.erase(it);
512 break;
513 }
514 }
515
516 // TODO: Handle the case when there's a proxy block and a regular JIT block at the same location.
517 // In this case we probably "leak" the proxy block currently (no memory leak but it'll stay enabled).
518
519 if (b->invalid) {
520 if (type == DestroyType::INVALIDATE)
521 ERROR_LOG(JIT, "Invalidating invalid block %d", block_num);
522 return;
523 }
524
525 b->invalid = true;
526 if (!b->IsPureProxy()) {
527 if (Memory::ReadUnchecked_U32(b->originalAddress) == GetEmuHackOpForBlock(block_num).encoding)
528 Memory::Write_Opcode_JIT(b->originalAddress, b->originalFirstOpcode);
529 }
530
531 // It's not safe to set normalEntry to 0 here, since we use a binary search
532 // that looks at that later to find blocks. Marking it invalid is enough.
533
534 UnlinkBlock(block_num);
535
536 // Don't change the jit code when invalidating a pure proxy block.
537 if (b->IsPureProxy()) {
538 return;
539 }
540
541 if (b->checkedEntry) {
542 // We can skip this if we're clearing anyway, which cuts down on protect back and forth on WX exclusive.
543 if (type != DestroyType::CLEAR) {
544 u8 *writableEntry = codeBlock_->GetWritablePtrFromCodePtr(b->checkedEntry);
545 MIPSComp::jit->UnlinkBlock(writableEntry, b->originalAddress);
546 }
547 } else {
548 ERROR_LOG(JIT, "Unlinking block with no entry: %08x (%d)", b->originalAddress, block_num);
549 }
550 }
551
InvalidateICache(u32 address,const u32 length)552 void JitBlockCache::InvalidateICache(u32 address, const u32 length) {
553 // Convert the logical address to a physical address for the block map
554 const u32 pAddr = address & 0x1FFFFFFF;
555 const u32 pEnd = pAddr + length;
556
557 if (pEnd < pAddr) {
558 ERROR_LOG(JIT, "Bad InvalidateICache: %08x with len=%d", address, length);
559 return;
560 }
561
562 if (pAddr == 0 && pEnd >= 0x1FFFFFFF) {
563 InvalidateChangedBlocks();
564 return;
565 }
566
567 // Blocks may start and end in overlapping ways, and destroying one invalidates iterators.
568 // So after destroying one, we start over.
569 do {
570 restart:
571 auto next = block_map_.lower_bound(std::make_pair(pAddr, 0));
572 auto last = block_map_.upper_bound(std::make_pair(pEnd + MAX_BLOCK_INSTRUCTIONS, 0));
573 // Note that if next is end(), last will be end() too (equal.)
574 for (; next != last; ++next) {
575 const u32 blockStart = next->first.second;
576 const u32 blockEnd = next->first.first;
577 if (blockStart < pEnd && blockEnd > pAddr) {
578 DestroyBlock(next->second, DestroyType::INVALIDATE);
579 // Our iterator is now invalid. Break and search again.
580 // Most of the time there shouldn't be a bunch of matching blocks.
581 goto restart;
582 }
583 }
584 // We got here - it wasn't in the map at all (or anymore.)
585 } while (false);
586 }
587
InvalidateChangedBlocks()588 void JitBlockCache::InvalidateChangedBlocks() {
589 // The primary goal of this is to make sure block linking is cleared up.
590 for (int block_num = 0; block_num < num_blocks_; ++block_num) {
591 JitBlock &b = blocks_[block_num];
592 if (b.invalid || b.IsPureProxy())
593 continue;
594
595 const u32 emuhack = GetEmuHackOpForBlock(block_num).encoding;
596 if (Memory::ReadUnchecked_U32(b.originalAddress) != emuhack) {
597 DEBUG_LOG(JIT, "Invalidating changed block at %08x", b.originalAddress);
598 DestroyBlock(block_num, DestroyType::INVALIDATE);
599 }
600 }
601 }
602
GetBlockExitSize()603 int JitBlockCache::GetBlockExitSize() {
604 #if PPSSPP_ARCH(ARM)
605 // Will depend on the sequence found to encode the destination address.
606 return 0;
607 #elif PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64)
608 return 15;
609 #elif PPSSPP_ARCH(ARM64)
610 // Will depend on the sequence found to encode the destination address.
611 return 0;
612 #else
613 #warning GetBlockExitSize unimplemented
614 return 0;
615 #endif
616 }
617
ComputeStats(BlockCacheStats & bcStats) const618 void JitBlockCache::ComputeStats(BlockCacheStats &bcStats) const {
619 double totalBloat = 0.0;
620 double maxBloat = 0.0;
621 double minBloat = 1000000000.0;
622 for (int i = 0; i < num_blocks_; i++) {
623 const JitBlock *b = GetBlock(i);
624 double codeSize = (double)b->codeSize;
625 if (codeSize == 0)
626 continue;
627 double origSize = (double)(4 * b->originalSize);
628 double bloat = codeSize / origSize;
629 if (bloat < minBloat) {
630 minBloat = bloat;
631 bcStats.minBloatBlock = b->originalAddress;
632 }
633 if (bloat > maxBloat) {
634 maxBloat = bloat;
635 bcStats.maxBloatBlock = b->originalAddress;
636 }
637 totalBloat += bloat;
638 bcStats.bloatMap[bloat] = b->originalAddress;
639 }
640 bcStats.numBlocks = num_blocks_;
641 bcStats.minBloat = minBloat;
642 bcStats.maxBloat = maxBloat;
643 bcStats.avgBloat = totalBloat / (double)num_blocks_;
644 }
645
GetBlockDebugInfo(int blockNum) const646 JitBlockDebugInfo JitBlockCache::GetBlockDebugInfo(int blockNum) const {
647 JitBlockDebugInfo debugInfo{};
648 const JitBlock *block = GetBlock(blockNum);
649 debugInfo.originalAddress = block->originalAddress;
650 for (u32 addr = block->originalAddress; addr <= block->originalAddress + block->originalSize * 4; addr += 4) {
651 char temp[256];
652 MIPSDisAsm(Memory::Read_Instruction(addr), addr, temp, true);
653 std::string mipsDis = temp;
654 debugInfo.origDisasm.push_back(mipsDis);
655 }
656
657 #if PPSSPP_ARCH(ARM)
658 debugInfo.targetDisasm = DisassembleArm2(block->normalEntry, block->codeSize);
659 #elif PPSSPP_ARCH(ARM64)
660 debugInfo.targetDisasm = DisassembleArm64(block->normalEntry, block->codeSize);
661 #elif PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64)
662 debugInfo.targetDisasm = DisassembleX86(block->normalEntry, block->codeSize);
663 #endif
664
665 return debugInfo;
666 }
667