CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
hrydgard

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

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