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