1 #pragma once
2
3 #include <vector>
4 #include <utility>
5
6 #include "Common/CommonTypes.h"
7 #include "Core/MIPS/MIPS.h"
8
9 // Basic IR
10 //
11 // This IR refers implicitly to the MIPS register set and is simple to interpret.
12 // To do real compiler things with it and do full-function compilation, it probably
13 // needs to be lifted to a higher IR first, before being lowered onto each target.
14 // But this gets rid of a lot of MIPS idiosyncrasies that makes it tricky, like
15 // delay slots, and is very suitable for translation into other IRs. Can of course
16 // even be directly JIT-ed, but the gains will probably be tiny over our older direct
17 // MIPS->target JITs.
18
19 enum class IROp : u8 {
20 Nop,
21
22 SetConst,
23 SetConstF,
24
25 Mov,
26
27 Add,
28 Sub,
29 Neg,
30 Not,
31
32 And,
33 Or,
34 Xor,
35
36 AddConst,
37 SubConst,
38
39 AndConst,
40 OrConst,
41 XorConst,
42
43 Shl,
44 Shr,
45 Sar,
46 Ror,
47
48 // The shift is stored directly, not in the const table, so Imm instead of Const
49 ShlImm,
50 ShrImm,
51 SarImm,
52 RorImm,
53
54 Slt,
55 SltConst,
56 SltU,
57 SltUConst,
58
59 Clz,
60
61 // Conditional moves
62 MovZ,
63 MovNZ,
64
65 Max,
66 Min,
67
68 // Byte swaps. All CPUs have native ones so worth keeping.
69 BSwap16, // Swaps both the high and low byte pairs.
70 BSwap32,
71
72 // Weird Hi/Lo semantics preserved. Too annoying to do something more generic.
73 MtLo,
74 MtHi,
75 MfLo,
76 MfHi,
77 Mult,
78 MultU,
79 Madd,
80 MaddU,
81 Msub,
82 MsubU,
83 Div,
84 DivU,
85
86 // These take a constant from the pool as an offset.
87 // Loads from a constant address can be represented by using r0.
88 Load8,
89 Load8Ext,
90 Load16,
91 Load16Ext,
92 Load32,
93 Load32Left,
94 Load32Right,
95 LoadFloat,
96 LoadVec4,
97
98 Store8,
99 Store16,
100 Store32,
101 Store32Left,
102 Store32Right,
103 StoreFloat,
104 StoreVec4,
105
106 Ext8to32,
107 Ext16to32,
108 ReverseBits,
109
110 FAdd,
111 FSub,
112 FMul,
113 FDiv,
114 FMin,
115 FMax,
116
117 FMov,
118 FSqrt,
119 FNeg,
120 FAbs,
121 FSign,
122
123 FRound,
124 FTrunc,
125 FCeil,
126 FFloor,
127
128 FCvtWS,
129 FCvtSW,
130
131 FMovFromGPR,
132 FMovToGPR,
133
134 FSat0_1,
135 FSatMinus1_1,
136
137 FpCondToReg,
138 VfpuCtrlToReg,
139
140 ZeroFpCond,
141 FCmp,
142
143 FCmovVfpuCC,
144 FCmpVfpuBit,
145 FCmpVfpuAggregate,
146
147 // Rounding Mode
148 RestoreRoundingMode,
149 ApplyRoundingMode,
150 UpdateRoundingMode,
151
152 SetCtrlVFPU,
153 SetCtrlVFPUReg,
154 SetCtrlVFPUFReg,
155
156 // 4-wide instructions to assist SIMD.
157 // Can of course add a pass to break them up if a target does not
158 // support SIMD.
159 Vec4Init,
160 Vec4Shuffle,
161 Vec4Mov,
162 Vec4Add,
163 Vec4Sub,
164 Vec4Mul,
165 Vec4Div,
166 Vec4Scale,
167 Vec4Dot,
168 Vec4Neg,
169 Vec4Abs,
170
171 // vx2i
172 Vec2Unpack16To31, // Note that the result is shifted down by 1, hence 31
173 Vec2Unpack16To32,
174 Vec4Unpack8To32,
175 Vec4DuplicateUpperBitsAndShift1, // Bizarro vuc2i behaviour, in an instruction. Split?
176 Vec4ClampToZero,
177 Vec2ClampToZero,
178 Vec4Pack31To8,
179 Vec4Pack32To8,
180 Vec2Pack31To16,
181 Vec2Pack32To16,
182
183 // Slow special functions. Used on singles.
184 FSin,
185 FCos,
186 FRSqrt,
187 FRecip,
188 FAsin,
189
190 // Fake/System instructions
191 Interpret,
192
193 // Emit this before you exit. Semantic is to set the downcount
194 // that will be used at the actual exit.
195 Downcount, // src1 + (src2<<8)
196
197 // End-of-basic-block.
198 ExitToConst, // 0, const, downcount
199 ExitToReg,
200 ExitToConstIfEq, // const, reg1, reg2
201 ExitToConstIfNeq, // const, reg1, reg2
202 ExitToConstIfGtZ, // const, reg1, 0
203 ExitToConstIfGeZ, // const, reg1, 0
204 ExitToConstIfLtZ, // const, reg1, 0
205 ExitToConstIfLeZ, // const, reg1, 0
206
207 ExitToConstIfFpTrue,
208 ExitToConstIfFpFalse,
209 ExitToPC, // Used after a syscall to give us a way to do things before returning.
210
211 Syscall,
212 SetPC, // hack to make syscall returns work
213 SetPCConst, // hack to make replacement know PC
214 CallReplacement,
215 Break,
216 Breakpoint,
217 MemoryCheck,
218 };
219
220 enum IRComparison {
221 Greater,
222 GreaterEqual,
223 Less,
224 LessEqual,
225 Equal,
226 NotEqual,
227 Bad,
228 };
229
230 // Some common vec4 constants.
231 enum class Vec4Init {
232 AllZERO,
233 AllONE,
234 AllMinusONE,
235 Set_1000,
236 Set_0100,
237 Set_0010,
238 Set_0001,
239 };
240
241 // Hm, unused
Invert(IRComparison comp)242 inline IRComparison Invert(IRComparison comp) {
243 switch (comp) {
244 case IRComparison::Equal: return IRComparison::NotEqual;
245 case IRComparison::NotEqual: return IRComparison::Equal;
246 case IRComparison::Greater: return IRComparison::LessEqual;
247 case IRComparison::GreaterEqual: return IRComparison::Less;
248 case IRComparison::Less: return IRComparison::GreaterEqual;
249 case IRComparison::LessEqual: return IRComparison::Greater;
250 default:
251 return IRComparison::Bad;
252 }
253 }
254
ComparisonToExit(IRComparison comp)255 inline IROp ComparisonToExit(IRComparison comp) {
256 switch (comp) {
257 case IRComparison::Equal: return IROp::ExitToConstIfEq;
258 case IRComparison::NotEqual: return IROp::ExitToConstIfNeq;
259 case IRComparison::Greater: return IROp::ExitToConstIfGtZ;
260 case IRComparison::GreaterEqual: return IROp::ExitToConstIfGeZ;
261 case IRComparison::Less: return IROp::ExitToConstIfLtZ;
262 case IRComparison::LessEqual: return IROp::ExitToConstIfLeZ;
263 default:
264 return IROp::Break;
265 }
266 }
267
268 enum IRFpCompareMode {
269 False = 0,
270 EitherUnordered,
271 EqualOrdered, // eq, seq (equal, ordered)
272 EqualUnordered, // ueq, ngl (equal, unordered)
273 LessOrdered, // olt, lt (less than, ordered)
274 LessUnordered, // ult, nge (less than, unordered)
275 LessEqualOrdered, // ole, le (less equal, ordered)
276 LessEqualUnordered, // ule, ngt (less equal, unordered)
277 };
278
279 enum {
280 IRTEMP_0 = 192,
281 IRTEMP_1,
282 IRTEMP_2,
283 IRTEMP_3,
284 IRTEMP_LHS, // Reserved for use in branches
285 IRTEMP_RHS, // Reserved for use in branches
286 IRTEMP_LR_ADDR, // Reserved for left/right loads and stores.
287 IRTEMP_LR_VALUE, // Reserved for left/right loads and stores.
288 IRTEMP_LR_MASK, // Reserved for left/right loads and stores.
289 IRTEMP_LR_SHIFT, // Reserved for left/right loads and stores.
290
291 IRVTEMP_PFX_S = 224 - 32, // Relative to the FP regs
292 IRVTEMP_PFX_T = 228 - 32,
293 IRVTEMP_PFX_D = 232 - 32,
294 IRVTEMP_0 = 236 - 32,
295
296 // 16 float temps for vector S and T prefixes and things like that.
297 // IRVTEMP_0 = 208 - 64, // -64 to be relative to v[0]
298
299 // Hacky way to get to other state
300 IRREG_VFPU_CTRL_BASE = 208,
301 IRREG_VFPU_CC = 211,
302 IRREG_LO = 242, // offset of lo in MIPSState / 4
303 IRREG_HI = 243,
304 IRREG_FCR31 = 244,
305 IRREG_FPCOND = 245,
306 };
307
308 enum IRFlags {
309 // Uses src3, not dest.
310 IRFLAG_SRC3 = 0x0001,
311 // Uses src3 AND dest (i.e. mutates dest.)
312 IRFLAG_SRC3DST = 0x0002,
313 // Exit instruction (maybe conditional.)
314 IRFLAG_EXIT = 0x0004,
315 };
316
317 struct IRMeta {
318 IROp op;
319 const char *name;
320 const char types[4]; // GGG
321 u32 flags;
322 };
323
324 // 64 bits.
325 struct IRInst {
326 IROp op;
327 union {
328 u8 dest;
329 u8 src3;
330 };
331 u8 src1;
332 u8 src2;
333 u32 constant;
334 };
335
336 // Returns the new PC.
337 u32 IRInterpret(MIPSState *ms, const IRInst *inst, int count);
338
339 // Each IR block gets a constant pool.
340 class IRWriter {
341 public:
342 IRWriter &operator =(const IRWriter &w) {
343 insts_ = w.insts_;
344 return *this;
345 }
346 IRWriter &operator =(IRWriter &&w) {
347 insts_ = std::move(w.insts_);
348 return *this;
349 }
350
351 void Write(IROp op, u8 dst = 0, u8 src1 = 0, u8 src2 = 0);
Write(IRInst inst)352 void Write(IRInst inst) {
353 insts_.push_back(inst);
354 }
355 void WriteSetConstant(u8 dst, u32 value);
356
357 int AddConstant(u32 value);
358 int AddConstantFloat(float value);
359
Clear()360 void Clear() {
361 insts_.clear();
362 }
363
GetInstructions()364 const std::vector<IRInst> &GetInstructions() const { return insts_; }
365
366 private:
367 std::vector<IRInst> insts_;
368 u32 nextConst_ = 0;
369 };
370
371 struct IROptions {
372 uint32_t disableFlags;
373 bool unalignedLoadStore;
374 };
375
376 const IRMeta *GetIRMeta(IROp op);
377 void DisassembleIR(char *buf, size_t bufsize, IRInst inst);
378 void InitIR();
379