1
// Copyright (c) 2014- PPSSPP Project.
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.
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.
12
// A copy of the GPL 2.0 should have been included with the program.
13
// If not, see http://www.gnu.org/licenses/
15
// Official git repository and contact information can be found at
16
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
19
// Symbian can't build this due to an old gcc/lib combination, and doesn't need to.
20
// Kind programmer, if you want to translate this to a proper feature-detection
21
// define, please feel free to.
29
#include "MsgHandler.h"
38
A0 = 4, A1 = 5, A2 = 6, A3 = 7, A4 = 8, A5 = 9, A6 = 10, A7 = 11,
39
// Alternate names depending on ABI.
40
T0 = 8, T1 = 9, T2 = 10, T3 = 11,
43
S0, S1, S2, S3, S4, S5, S6, S7,
50
F0 = 32, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, F13, F14, F15,
51
F16, F17, F18, F19, F20, F21, F22, F23, F24, F25, F26, F27, F28, F29, F30, F31,
53
INVALID_REG = 0xFFFFFFFF
57
// All 32 except: ZERO, K0/K1 (kernel), RA. The rest are only convention.
58
NUMGPRs = 32 - 1 - 2 - 1,
62
enum FixupBranchType {
63
// 16-bit immediate jump/branch (to pc + (simm16 + 1 ops).)
65
// 26-bit immediate jump/branch (to pc's 4 high bits + imm * 4.)
69
// Beware of delay slots.
77
MIPSEmitter() : code_(0), lastCacheFlushEnd_(0) {
79
MIPSEmitter(u8 *code_ptr) : code_(code_ptr), lastCacheFlushEnd_(code_ptr) {
82
virtual ~MIPSEmitter() {
85
void SetCodePtr(u8 *ptr);
86
void ReserveCodeSpace(u32 bytes);
87
const u8 *AlignCode16();
88
const u8 *AlignCodePage();
89
const u8 *GetCodePtr() const;
90
u8 *GetWritableCodePtr();
92
void FlushIcacheSection(u8 *start, u8 *end);
94
// 20 bits valid in code.
98
SLL(R_ZERO, R_ZERO, 0);
101
// Note for all branches and jumps:
102
// MIPS has DELAY SLOTS. This emitter makes it so if you forget that, you'll be safe.
103
// If you want to run something inside a delay slot, emit the instruction inside a closure.
105
// Example: Translates to:
106
// J(&myFunc); J(&myFunc);
107
// ADDU(V0, V0, V1); NOP();
110
// J(&myFunc, [&] { J(&myFunc);
111
// ADDU(V0, V0, V1); ADDU(V0, V0, V1);
114
// This applies to all J*() and B*() functions (except BREAK(), which is not a branch func.)
116
FixupBranch J(std::function<void ()> delaySlot = nullptr);
117
void J(const void *func, std::function<void ()> delaySlot = nullptr);
118
FixupBranch JAL(std::function<void ()> delaySlot = nullptr);
119
void JAL(const void *func, std::function<void ()> delaySlot = nullptr);
120
void JR(MIPSReg rs, std::function<void ()> delaySlot = nullptr);
121
void JRRA(std::function<void ()> delaySlot = nullptr) {
124
void JALR(MIPSReg rd, MIPSReg rs, std::function<void ()> delaySlot = nullptr);
125
void JALR(MIPSReg rs, std::function<void ()> delaySlot = nullptr) {
126
JALR(R_RA, rs, delaySlot);
129
inline FixupBranch B(std::function<void ()> delaySlot = nullptr) {
130
return BEQ(R_ZERO, R_ZERO, delaySlot);
132
inline void B(const void *func, std::function<void ()> delaySlot = nullptr) {
133
return BEQ(R_ZERO, R_ZERO, func, delaySlot);
135
FixupBranch BLTZ(MIPSReg rs, std::function<void ()> delaySlot = nullptr);
136
void BLTZ(MIPSReg rs, const void *func, std::function<void ()> delaySlot = nullptr);
137
FixupBranch BEQ(MIPSReg rs, MIPSReg rt, std::function<void ()> delaySlot = nullptr);
138
void BEQ(MIPSReg rs, MIPSReg rt, const void *func, std::function<void ()> delaySlot = nullptr);
139
FixupBranch BNE(MIPSReg rs, MIPSReg rt, std::function<void ()> delaySlot = nullptr);
140
void BNE(MIPSReg rs, MIPSReg rt, const void *func, std::function<void ()> delaySlot = nullptr);
141
inline FixupBranch BEQZ(MIPSReg rs, std::function<void ()> delaySlot = nullptr) {
142
return BEQ(rs, R_ZERO, delaySlot);
144
inline void BEQZ(MIPSReg rs, const void *func, std::function<void ()> delaySlot = nullptr) {
145
return BEQ(rs, R_ZERO, func, delaySlot);
147
inline FixupBranch BNEZ(MIPSReg rs, std::function<void ()> delaySlot = nullptr) {
148
return BNE(rs, R_ZERO, delaySlot);
150
inline void BNEZ(MIPSReg rs, const void *func, std::function<void ()> delaySlot = nullptr) {
151
return BNE(rs, R_ZERO, func, delaySlot);
153
FixupBranch BLEZ(MIPSReg rs, std::function<void ()> delaySlot = nullptr);
154
void BLEZ(MIPSReg rs, const void *func, std::function<void ()> delaySlot = nullptr);
155
FixupBranch BGTZ(MIPSReg rs, std::function<void ()> delaySlot = nullptr);
156
void BGTZ(MIPSReg rs, const void *func, std::function<void ()> delaySlot = nullptr);
158
void SetJumpTarget(const FixupBranch &branch);
159
bool BInRange(const void *func);
160
bool JInRange(const void *func);
162
// R_AT is the stereotypical scratch reg, but it is not likely to be used.
163
void QuickCallFunction(MIPSReg scratchreg, const void *func);
164
template <typename T> void QuickCallFunction(MIPSReg scratchreg, T func) {
165
QuickCallFunction(scratchreg, (const void *)func);
168
void LB(MIPSReg dest, MIPSReg base, s16 offset);
169
void LH(MIPSReg dest, MIPSReg base, s16 offset);
170
void LW(MIPSReg dest, MIPSReg base, s16 offset);
171
void SB(MIPSReg value, MIPSReg base, s16 offset);
172
void SH(MIPSReg dest, MIPSReg base, s16 offset);
173
void SW(MIPSReg value, MIPSReg base, s16 offset);
175
// These exist for the sole purpose of making compilation fail if you try to load/store from R+R.
176
void LB(MIPSReg dest, MIPSReg base, MIPSReg invalid);
177
void LH(MIPSReg dest, MIPSReg base, MIPSReg invalid);
178
void LW(MIPSReg dest, MIPSReg base, MIPSReg invalid);
179
void SB(MIPSReg value, MIPSReg base, MIPSReg invalid);
180
void SH(MIPSReg dest, MIPSReg base, MIPSReg invalid);
181
void SW(MIPSReg value, MIPSReg base, MIPSReg invalid);
183
void SLL(MIPSReg rd, MIPSReg rt, u8 sa);
184
void SRL(MIPSReg rd, MIPSReg rt, u8 sa);
185
void SRA(MIPSReg rd, MIPSReg rt, u8 sa);
186
void SLLV(MIPSReg rd, MIPSReg rt, MIPSReg rs);
187
void SRLV(MIPSReg rd, MIPSReg rt, MIPSReg rs);
188
void SRAV(MIPSReg rd, MIPSReg rt, MIPSReg rs);
190
void SLT(MIPSReg rd, MIPSReg rt, MIPSReg rs);
191
void SLTU(MIPSReg rd, MIPSReg rt, MIPSReg rs);
192
void SLTI(MIPSReg rd, MIPSReg rt, s16 imm);
193
// Note: very importantly, *sign* extends imm before an unsigned compare.
194
void SLTIU(MIPSReg rt, MIPSReg rs, s16 imm);
196
// ADD/SUB/ADDI intentionally omitted. They are just versions that trap.
197
void ADDU(MIPSReg rd, MIPSReg rs, MIPSReg rt);
198
void SUBU(MIPSReg rd, MIPSReg rs, MIPSReg rt);
199
void ADDIU(MIPSReg rt, MIPSReg rs, s16 imm);
200
void SUBIU(MIPSReg rt, MIPSReg rs, s16 imm) {
204
void AND(MIPSReg rd, MIPSReg rs, MIPSReg rt);
205
void OR(MIPSReg rd, MIPSReg rs, MIPSReg rt);
206
void XOR(MIPSReg rd, MIPSReg rs, MIPSReg rt);
207
void ANDI(MIPSReg rt, MIPSReg rs, s16 imm);
208
void ORI(MIPSReg rt, MIPSReg rs, s16 imm);
209
void XORI(MIPSReg rt, MIPSReg rs, s16 imm);
211
// Clears the lower bits. On MIPS64, the result is sign extended.
212
void LUI(MIPSReg rt, s16 imm);
214
void INS(MIPSReg rt, MIPSReg rs, s8 pos, s8 size);
215
void EXT(MIPSReg rt, MIPSReg rs, s8 pos, s8 size);
217
// MIPS64 only. Transparently uses DSLL32 to shift 32-63 bits.
218
void DSLL(MIPSReg rd, MIPSReg rt, u8 sa);
220
void MOVI2R(MIPSReg reg, u64 val);
221
void MOVI2R(MIPSReg reg, s64 val) {
222
MOVI2R(reg, (u64)val);
224
void MOVI2R(MIPSReg reg, u32 val);
225
void MOVI2R(MIPSReg reg, s32 val) {
226
MOVI2R(reg, (u32)val);
228
template <class T> void MOVP2R(MIPSReg reg, T *val) {
229
if (sizeof(uintptr_t) > sizeof(u32)) {
230
MOVI2R(reg, (u64)(intptr_t)(const void *)val);
232
MOVI2R(reg, (u32)(intptr_t)(const void *)val);
237
inline void Write32(u32 value) {
242
inline void Write32Fields(u8 pos1, u32 v1) {
243
*code32_++ = (v1 << pos1);
245
inline void Write32Fields(u8 pos1, u32 v1, u8 pos2, u32 v2) {
246
*code32_++ = (v1 << pos1) | (v2 << pos2);
248
inline void Write32Fields(u8 pos1, u32 v1, u8 pos2, u32 v2, u8 pos3, u32 v3) {
249
*code32_++ = (v1 << pos1) | (v2 << pos2) | (v3 << pos3);
251
inline void Write32Fields(u8 pos1, u32 v1, u8 pos2, u32 v2, u8 pos3, u32 v3, u8 pos4, u32 v4) {
252
*code32_++ = (v1 << pos1) | (v2 << pos2) | (v3 << pos3) | (v4 << pos4);
254
inline void Write32Fields(u8 pos1, u32 v1, u8 pos2, u32 v2, u8 pos3, u32 v3, u8 pos4, u32 v4, u8 pos5, u32 v5) {
255
*code32_++ = (v1 << pos1) | (v2 << pos2) | (v3 << pos3) | (v4 << pos5) | (v5 << pos5);
257
inline void Write32Fields(u8 pos1, u32 v1, u8 pos2, u32 v2, u8 pos3, u32 v3, u8 pos4, u32 v4, u8 pos5, u32 v5, u8 pos6, u32 v6) {
258
*code32_++ = (v1 << pos1) | (v2 << pos2) | (v3 << pos3) | (v4 << pos5) | (v5 << pos5) | (v6 << pos6);
261
static void SetJumpTarget(const FixupBranch &branch, const void *dst);
262
static bool BInRange(const void *src, const void *dst);
263
static bool JInRange(const void *src, const void *dst);
264
FixupBranch MakeFixupBranch(FixupBranchType type);
265
void ApplyDelaySlot(std::function<void ()> delaySlot);
272
u8 *lastCacheFlushEnd_;
275
// Everything that needs to generate machine code should inherit from this.
276
// You get memory management for free, plus, you can use all the LUI etc functions without
277
// having to prefix them with gen-> or something similar.
278
class MIPSCodeBlock : public MIPSEmitter {
280
MIPSCodeBlock() : region(nullptr), region_size(0) {
282
virtual ~MIPSCodeBlock() {
288
// Call this before you generate any code.
289
void AllocCodeSpace(int size);
291
// Always clear code space with breakpoints, so that if someone accidentally executes
292
// uninitialized, it just breaks into the debugger.
293
void ClearCodeSpace();
295
// Call this when shutting down. Don't rely on the destructor, even though it'll do the job.
296
void FreeCodeSpace();
298
bool IsInSpace(const u8 *ptr) const {
299
return ptr >= region && ptr < region + region_size;
302
void ResetCodePtr() {
306
size_t GetSpaceLeft() const {
307
return region_size - (GetCodePtr() - region);
314
size_t GetOffset(const u8 *ptr) const {