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