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