Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hrydgard
GitHub Repository: hrydgard/ppsspp
Path: blob/master/Core/MIPS/JitCommon/JitBlockCache.h
5686 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
#pragma once
19
20
#include <cstdint>
21
#include <map>
22
#include <unordered_map>
23
#include <vector>
24
#include <string>
25
26
#include "ppsspp_config.h"
27
#include "Common/CommonTypes.h"
28
#include "Common/CodeBlock.h"
29
#include "Core/MIPS/MIPS.h"
30
31
#if PPSSPP_ARCH(ARM) || PPSSPP_ARCH(ARM64)
32
const int MAX_JIT_BLOCK_EXITS = 4;
33
#else
34
const int MAX_JIT_BLOCK_EXITS = 8;
35
#endif
36
constexpr bool JIT_USE_COMPILEDHASH = true;
37
38
struct BlockCacheStats {
39
int numBlocks;
40
float avgBloat; // In code bytes, not instructions!
41
float minBloat;
42
u32 minBloatBlock;
43
float maxBloat;
44
u32 maxBloatBlock;
45
};
46
47
enum class DestroyType {
48
DESTROY,
49
INVALIDATE,
50
// Skips jit unlink, since it'll be poisoned anyway.
51
CLEAR,
52
};
53
54
// We should be careful not to access these block structures during runtime as they are large.
55
// Fine to mess with them at block compile time though.
56
struct JitBlock {
57
bool ContainsAddress(u32 em_address) const;
58
59
const u8 *checkedEntry; // const, we have to translate to writable.
60
const u8 *normalEntry;
61
62
u8 *exitPtrs[MAX_JIT_BLOCK_EXITS]; // to be able to rewrite the exit jump
63
u32 exitAddress[MAX_JIT_BLOCK_EXITS]; // 0xFFFFFFFF == unknown
64
65
u32 originalAddress;
66
MIPSOpcode originalFirstOpcode; //to be able to restore
67
uint64_t compiledHash;
68
u16 codeSize;
69
u16 originalSize;
70
u16 blockNum;
71
72
bool invalid;
73
bool linkStatus[MAX_JIT_BLOCK_EXITS];
74
75
// By having a pointer, we avoid a constructor/destructor being generated and dog slow
76
// performance in debug.
77
std::vector<u32> *proxyFor;
78
79
bool IsPureProxy() const {
80
return originalFirstOpcode.encoding == 0x68FF0000;
81
}
82
void SetPureProxy() {
83
// Magic number that won't be a real opcode.
84
originalFirstOpcode.encoding = 0x68FF0000;
85
}
86
};
87
88
typedef void (*CompiledCode)();
89
90
struct JitBlockDebugInfo {
91
uint32_t originalAddress;
92
std::vector<std::string> origDisasm;
93
std::vector<std::string> irDisasm; // if any
94
std::vector<std::string> targetDisasm;
95
};
96
97
struct JitBlockProfileStats {
98
int64_t executions;
99
int64_t totalNanos;
100
};
101
102
// Only used for debugging, so keeping it tight is not that important.
103
struct JitBlockMeta {
104
bool valid;
105
uint32_t addr;
106
uint32_t sizeInBytes;
107
};
108
109
class JitBlockCacheDebugInterface {
110
public:
111
virtual int GetNumBlocks() const = 0;
112
virtual int GetBlockNumberFromStartAddress(u32 em_address, bool realBlocksOnly = true) const = 0;
113
virtual JitBlockDebugInfo GetBlockDebugInfo(int blockNum) const = 0; // Expensive
114
virtual JitBlockMeta GetBlockMeta(int blockNum) const = 0;
115
virtual JitBlockProfileStats GetBlockProfileStats(int blockNum) const = 0;
116
virtual void ComputeStats(BlockCacheStats &bcStats) const = 0;
117
virtual bool IsValidBlock(int blockNum) const = 0;
118
virtual bool SupportsProfiling() const { return false; }
119
120
virtual ~JitBlockCacheDebugInterface() {}
121
};
122
123
class JitBlockCache : public JitBlockCacheDebugInterface {
124
public:
125
JitBlockCache(MIPSState *mipsState, CodeBlockCommon *codeBlock);
126
~JitBlockCache();
127
128
int AllocateBlock(u32 em_address);
129
// When a proxy block is invalidated, the block located at the rootAddress is invalidated too.
130
void ProxyBlock(u32 rootAddress, u32 startAddress, u32 size, const u8 *codePtr);
131
void FinalizeBlock(int block_num, bool block_link);
132
133
void Clear();
134
void Init();
135
void Shutdown();
136
void Reset();
137
138
bool IsFull() const;
139
void ComputeStats(BlockCacheStats &bcStats) const override;
140
141
// Code Cache
142
JitBlock *GetBlock(int block_num);
143
const JitBlock *GetBlock(int block_num) const;
144
145
// Fast way to get a block. Only works on the first source-cpu instruction of a block.
146
int GetBlockNumberFromStartAddress(u32 em_address, bool realBlocksOnly = true) const override;
147
148
// slower, but can get numbers from within blocks, not just the first instruction.
149
// WARNING! WILL NOT WORK WITH JIT INLINING ENABLED (not yet a feature but will be soon)
150
// Returns a list of block numbers - only one block can start at a particular address, but they CAN overlap.
151
// This one is slow so should only be used for one-shots from the debugger UI, not for anything during runtime.
152
void GetBlockNumbersFromAddress(u32 em_address, std::vector<int> *block_numbers);
153
// Similar to above, but only the first matching address.
154
int GetBlockNumberFromAddress(u32 em_address);
155
int GetBlockNumberFromEmuHackOp(MIPSOpcode inst, bool ignoreBad = false) const;
156
157
u32 GetAddressFromBlockPtr(const u8 *ptr) const;
158
159
MIPSOpcode GetOriginalFirstOp(int block_num);
160
161
bool RangeMayHaveEmuHacks(u32 start, u32 end) const;
162
163
// DOES NOT WORK CORRECTLY WITH JIT INLINING
164
void InvalidateICache(u32 address, const u32 length);
165
void InvalidateChangedBlocks();
166
void DestroyBlock(int block_num, DestroyType type);
167
168
// No jit operations may be run between these calls.
169
// Meant to be used to make memory safe for savestates, memcpy, etc.
170
std::vector<u32> SaveAndClearEmuHackOps();
171
void RestoreSavedEmuHackOps(const std::vector<u32> &saved);
172
173
int GetNumBlocks() const override { return num_blocks_; }
174
bool IsValidBlock(int blockNum) const override { return blockNum >= 0 && blockNum < num_blocks_ && !blocks_[blockNum].invalid; }
175
JitBlockMeta GetBlockMeta(int blockNum) const override {
176
JitBlockMeta meta{};
177
if (IsValidBlock(blockNum)) {
178
meta.valid = true;
179
meta.addr = blocks_[blockNum].originalAddress;
180
meta.sizeInBytes = blocks_[blockNum].originalSize;
181
}
182
return meta;
183
}
184
JitBlockProfileStats GetBlockProfileStats(int blockNum) const override {
185
return JitBlockProfileStats{};
186
}
187
188
static int GetBlockExitSize();
189
190
JitBlockDebugInfo GetBlockDebugInfo(int blockNum) const override;
191
192
enum {
193
MAX_BLOCK_INSTRUCTIONS = 0x4000,
194
};
195
196
private:
197
void LinkBlockExits(int i);
198
void LinkBlock(int i);
199
void UnlinkBlock(int i);
200
201
void AddBlockMap(int block_num);
202
void RemoveBlockMap(int block_num);
203
204
MIPSOpcode GetEmuHackOpForBlock(int block_num) const;
205
206
CodeBlockCommon *codeBlock_;
207
JitBlock *blocks_ = nullptr;
208
std::unordered_multimap<u32, int> proxyBlockMap_;
209
210
int num_blocks_ = 0;
211
std::unordered_multimap<u32, int> links_to_;
212
std::map<std::pair<u32,u32>, u32> block_map_; // (end_addr, start_addr) -> number
213
214
enum {
215
JITBLOCK_RANGE_SCRATCH = 0,
216
JITBLOCK_RANGE_RAMBOTTOM = 1,
217
JITBLOCK_RANGE_RAMTOP = 2,
218
JITBLOCK_RANGE_COUNT = 3,
219
};
220
std::pair<u32, u32> blockMemRanges_[3];
221
222
enum {
223
// Where does this number come from?
224
MAX_NUM_BLOCKS = 65536 * 4
225
};
226
};
227
228
229