~ppsspp/ppsspp/ppsspp_1.3.0

« back to all changes in this revision

Viewing changes to Common/MipsEmitter.h

  • Committer: Sérgio Benjamim
  • Date: 2017-01-02 00:12:05 UTC
  • Revision ID: sergio_br2@yahoo.com.br-20170102001205-cxbta9za203nmjwm
1.3.0 source (from ppsspp_1.3.0-r160.p5.l1762.a165.t83~56~ubuntu16.04.1.tar.xz).

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Copyright (c) 2014- PPSSPP 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
// 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.
 
22
#ifndef __SYMBIAN32__
 
23
 
 
24
#include <functional>
 
25
#include <vector>
 
26
#include <stdint.h>
 
27
 
 
28
#include "Common.h"
 
29
#include "MsgHandler.h"
 
30
 
 
31
namespace MIPSGen {
 
32
 
 
33
enum MIPSReg {
 
34
        R_ZERO = 0,
 
35
        R_AT,
 
36
        V0, V1,
 
37
 
 
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,
 
41
 
 
42
        T4, T5, T6, T7,
 
43
        S0, S1, S2, S3, S4, S5, S6, S7,
 
44
        T8, T9,
 
45
        K0, K1,
 
46
        R_GP, R_SP, R_FP,
 
47
        R_RA,
 
48
 
 
49
        F_BASE = 32,
 
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,
 
52
 
 
53
        INVALID_REG = 0xFFFFFFFF
 
54
};
 
55
 
 
56
enum {
 
57
        // All 32 except: ZERO, K0/K1 (kernel), RA.  The rest are only convention.
 
58
        NUMGPRs = 32 - 1 - 2 - 1,
 
59
        NUMFPRs = 32,
 
60
};
 
61
 
 
62
enum FixupBranchType {
 
63
        // 16-bit immediate jump/branch (to pc + (simm16 + 1 ops).)
 
64
        BRANCH_16,
 
65
        // 26-bit immediate jump/branch (to pc's 4 high bits + imm * 4.)
 
66
        BRANCH_26,
 
67
};
 
68
 
 
69
// Beware of delay slots.
 
70
struct FixupBranch {
 
71
        u8 *ptr;
 
72
        FixupBranchType type;
 
73
};
 
74
 
 
75
class MIPSEmitter {
 
76
public:
 
77
        MIPSEmitter() : code_(0), lastCacheFlushEnd_(0) {
 
78
        }
 
79
        MIPSEmitter(u8 *code_ptr) : code_(code_ptr), lastCacheFlushEnd_(code_ptr) {
 
80
                SetCodePtr(code_ptr);
 
81
        }
 
82
        virtual ~MIPSEmitter() {
 
83
        }
 
84
 
 
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();
 
91
        void FlushIcache();
 
92
        void FlushIcacheSection(u8 *start, u8 *end);
 
93
 
 
94
        // 20 bits valid in code.
 
95
        void BREAK(u32 code);
 
96
 
 
97
        void NOP() {
 
98
                SLL(R_ZERO, R_ZERO, 0);
 
99
        }
 
100
 
 
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.
 
104
        //
 
105
        // Example:                        Translates to:
 
106
        //    J(&myFunc);                  J(&myFunc);
 
107
        //    ADDU(V0, V0, V1);            NOP();
 
108
        //                                 ADDU(V0, V0, V1);
 
109
        //
 
110
        //    J(&myFunc, [&] {             J(&myFunc);
 
111
        //        ADDU(V0, V0, V1);        ADDU(V0, V0, V1);
 
112
        //    });
 
113
        //
 
114
        // This applies to all J*() and B*() functions (except BREAK(), which is not a branch func.)
 
115
 
 
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) {
 
122
                JR(R_RA, delaySlot);
 
123
        }
 
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);
 
127
        }
 
128
 
 
129
        inline FixupBranch B(std::function<void ()> delaySlot = nullptr) {
 
130
                return BEQ(R_ZERO, R_ZERO, delaySlot);
 
131
        }
 
132
        inline void B(const void *func, std::function<void ()> delaySlot = nullptr) {
 
133
                return BEQ(R_ZERO, R_ZERO, func, delaySlot);
 
134
        }
 
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);
 
143
        }
 
144
        inline void BEQZ(MIPSReg rs, const void *func, std::function<void ()> delaySlot = nullptr) {
 
145
                return BEQ(rs, R_ZERO, func, delaySlot);
 
146
        }
 
147
        inline FixupBranch BNEZ(MIPSReg rs, std::function<void ()> delaySlot = nullptr) {
 
148
                return BNE(rs, R_ZERO, delaySlot);
 
149
        }
 
150
        inline void BNEZ(MIPSReg rs, const void *func, std::function<void ()> delaySlot = nullptr) {
 
151
                return BNE(rs, R_ZERO, func, delaySlot);
 
152
        }
 
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);
 
157
 
 
158
        void SetJumpTarget(const FixupBranch &branch);
 
159
        bool BInRange(const void *func);
 
160
        bool JInRange(const void *func);
 
161
 
 
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);
 
166
        }
 
167
 
 
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);
 
174
 
 
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);
 
182
 
 
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);
 
189
 
 
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);
 
195
 
 
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) {
 
201
                ADDIU(rt, rs, -imm);
 
202
        }
 
203
 
 
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);
 
210
 
 
211
        // Clears the lower bits.  On MIPS64, the result is sign extended.
 
212
        void LUI(MIPSReg rt, s16 imm);
 
213
 
 
214
        void INS(MIPSReg rt, MIPSReg rs, s8 pos, s8 size);
 
215
        void EXT(MIPSReg rt, MIPSReg rs, s8 pos, s8 size);
 
216
 
 
217
        // MIPS64 only.  Transparently uses DSLL32 to shift 32-63 bits.
 
218
        void DSLL(MIPSReg rd, MIPSReg rt, u8 sa);
 
219
 
 
220
        void MOVI2R(MIPSReg reg, u64 val);
 
221
        void MOVI2R(MIPSReg reg, s64 val) {
 
222
                MOVI2R(reg, (u64)val);
 
223
        }
 
224
        void MOVI2R(MIPSReg reg, u32 val);
 
225
        void MOVI2R(MIPSReg reg, s32 val) {
 
226
                MOVI2R(reg, (u32)val);
 
227
        }
 
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);
 
231
                } else {
 
232
                        MOVI2R(reg, (u32)(intptr_t)(const void *)val);
 
233
                }
 
234
        }
 
235
 
 
236
protected:
 
237
        inline void Write32(u32 value) {
 
238
                *code32_++ = value;
 
239
        }
 
240
 
 
241
        // Less parenthesis.
 
242
        inline void Write32Fields(u8 pos1, u32 v1) {
 
243
                *code32_++ = (v1 << pos1);
 
244
        }
 
245
        inline void Write32Fields(u8 pos1, u32 v1, u8 pos2, u32 v2) {
 
246
                *code32_++ = (v1 << pos1) | (v2 << pos2);
 
247
        }
 
248
        inline void Write32Fields(u8 pos1, u32 v1, u8 pos2, u32 v2, u8 pos3, u32 v3) {
 
249
                *code32_++ = (v1 << pos1) | (v2 << pos2) | (v3 << pos3);
 
250
        }
 
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);
 
253
        }
 
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);
 
256
        }
 
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);
 
259
        }
 
260
 
 
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);
 
266
 
 
267
private:
 
268
        union {
 
269
                u8 *code_;
 
270
                u32 *code32_;
 
271
        };
 
272
        u8 *lastCacheFlushEnd_;
 
273
};
 
274
 
 
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 {
 
279
public:
 
280
        MIPSCodeBlock() : region(nullptr), region_size(0) {
 
281
        }
 
282
        virtual ~MIPSCodeBlock() {
 
283
                if (region) {
 
284
                        FreeCodeSpace();
 
285
                }
 
286
        }
 
287
 
 
288
        // Call this before you generate any code.
 
289
        void AllocCodeSpace(int size);
 
290
 
 
291
        // Always clear code space with breakpoints, so that if someone accidentally executes
 
292
        // uninitialized, it just breaks into the debugger.
 
293
        void ClearCodeSpace();
 
294
 
 
295
        // Call this when shutting down. Don't rely on the destructor, even though it'll do the job.
 
296
        void FreeCodeSpace();
 
297
 
 
298
        bool IsInSpace(const u8 *ptr) const {
 
299
                return ptr >= region && ptr < region + region_size;
 
300
        }
 
301
 
 
302
        void ResetCodePtr() {
 
303
                SetCodePtr(region);
 
304
        }
 
305
 
 
306
        size_t GetSpaceLeft() const {
 
307
                return region_size - (GetCodePtr() - region);
 
308
        }
 
309
 
 
310
        u8 *GetBasePtr() {
 
311
                return region;
 
312
        }
 
313
 
 
314
        size_t GetOffset(const u8 *ptr) const {
 
315
                return ptr - region;
 
316
        }
 
317
 
 
318
protected:
 
319
        u8 *region;
 
320
        size_t region_size;
 
321
};
 
322
 
 
323
};
 
324
 
 
325
#endif