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