Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hrydgard
GitHub Repository: hrydgard/ppsspp
Path: blob/master/Core/MIPS/x86/CompBranch.cpp
5679 views
1
// Copyright (c) 2012- 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
#include "ppsspp_config.h"
19
#if PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64)
20
21
#include "Common/Data/Convert/SmallDataConvert.h"
22
#include "Common/Profiler/Profiler.h"
23
24
#include "Core/Config.h"
25
#include "Core/Core.h"
26
#include "Core/Reporting.h"
27
#include "Core/HLE/HLE.h"
28
#include "Core/HLE/HLETables.h"
29
#include "Core/MemMap.h"
30
31
#include "Core/MIPS/MIPS.h"
32
#include "Core/MIPS/MIPSCodeUtils.h"
33
#include "Core/MIPS/MIPSAnalyst.h"
34
#include "Core/MIPS/MIPSTables.h"
35
36
#include "Core/MIPS/x86/Jit.h"
37
#include "Core/MIPS/x86/RegCache.h"
38
#include "Core/MIPS/JitCommon/JitBlockCache.h"
39
40
#define _RS MIPS_GET_RS(op)
41
#define _RT MIPS_GET_RT(op)
42
#define _RD MIPS_GET_RD(op)
43
#define _FS MIPS_GET_FS(op)
44
#define _FT MIPS_GET_FT(op)
45
#define _FD MIPS_GET_FD(op)
46
#define _SA MIPS_GET_SA(op)
47
#define _POS ((op>> 6) & 0x1F)
48
#define _SIZE ((op>>11) & 0x1F)
49
#define _IMM26 (op & 0x03FFFFFF)
50
#define TARGET16 ((int)(SignExtend16ToU32(op) << 2))
51
#define TARGET26 (_IMM26 << 2)
52
53
#define LOOPOPTIMIZATION 0
54
55
using namespace MIPSAnalyst;
56
57
// NOTE: Can't use CONDITIONAL_DISABLE in this file, branches are so special
58
// that they cannot be interpreted in the context of the Jit.
59
60
// But we can at least log and compare.
61
// #define DO_CONDITIONAL_LOG 1
62
#define DO_CONDITIONAL_LOG 0
63
64
// We can also disable nice delay slots.
65
// #define CONDITIONAL_NICE_DELAYSLOT branchInfo.delaySlotIsNice = false;
66
#define CONDITIONAL_NICE_DELAYSLOT ;
67
68
#if DO_CONDITIONAL_LOG
69
#define CONDITIONAL_LOG BranchLog(op);
70
#define CONDITIONAL_LOG_EXIT(addr) BranchLogExit(op, addr, false);
71
#define CONDITIONAL_LOG_EXIT_EAX() BranchLogExit(op, 0, true);
72
#else
73
#define CONDITIONAL_LOG ;
74
#define CONDITIONAL_LOG_EXIT(addr) ;
75
#define CONDITIONAL_LOG_EXIT_EAX() ;
76
#endif
77
78
namespace MIPSComp
79
{
80
using namespace Gen;
81
82
static void JitBranchLog(MIPSOpcode op, u32 pc) {
83
currentMIPS->pc = pc;
84
currentMIPS->inDelaySlot = false;
85
86
MIPSInterpretFunc func = MIPSGetInterpretFunc(op);
87
MIPSInfo info = MIPSGetInfo(op);
88
func(op);
89
90
// Branch taken, use nextPC.
91
if (currentMIPS->inDelaySlot)
92
currentMIPS->intBranchExit = currentMIPS->nextPC;
93
else
94
{
95
// Branch not taken, likely delay slot skipped.
96
if (info & LIKELY)
97
currentMIPS->intBranchExit = currentMIPS->pc;
98
// Branch not taken, so increment over delay slot.
99
else
100
currentMIPS->intBranchExit = currentMIPS->pc + 4;
101
}
102
103
currentMIPS->pc = pc;
104
currentMIPS->inDelaySlot = false;
105
}
106
107
static void JitBranchLogMismatch(MIPSOpcode op, u32 pc)
108
{
109
char temp[256];
110
MIPSDisAsm(op, pc, temp, sizeof(temp), true);
111
ERROR_LOG(Log::JIT, "Bad jump: %s - int:%08x jit:%08x", temp, currentMIPS->intBranchExit, currentMIPS->jitBranchExit);
112
Core_Break(BreakReason::JitBranchDebug, pc);
113
}
114
115
void Jit::BranchLog(MIPSOpcode op)
116
{
117
FlushAll();
118
ABI_CallFunctionCC(thunks.ProtectFunction(&JitBranchLog), op.encoding, GetCompilerPC());
119
}
120
121
void Jit::BranchLogExit(MIPSOpcode op, u32 dest, bool useEAX)
122
{
123
OpArg destArg = useEAX ? R(EAX) : Imm32(dest);
124
125
CMP(32, MIPSSTATE_VAR(intBranchExit), destArg);
126
FixupBranch skip = J_CC(CC_E);
127
128
MOV(32, MIPSSTATE_VAR(jitBranchExit), destArg);
129
ABI_CallFunctionCC(thunks.ProtectFunction(&JitBranchLogMismatch), op.encoding, GetCompilerPC());
130
// Restore EAX, we probably ruined it.
131
if (useEAX)
132
MOV(32, R(EAX), MIPSSTATE_VAR(jitBranchExit));
133
134
SetJumpTarget(skip);
135
}
136
137
CCFlags Jit::FlipCCFlag(CCFlags flag)
138
{
139
switch (flag)
140
{
141
case CC_O: return CC_NO;
142
case CC_NO: return CC_O;
143
case CC_B: return CC_NB;
144
case CC_NB: return CC_B;
145
case CC_Z: return CC_NZ;
146
case CC_NZ: return CC_Z;
147
case CC_BE: return CC_NBE;
148
case CC_NBE: return CC_BE;
149
case CC_S: return CC_NS;
150
case CC_NS: return CC_S;
151
case CC_P: return CC_NP;
152
case CC_NP: return CC_P;
153
case CC_L: return CC_NL;
154
case CC_NL: return CC_L;
155
case CC_LE: return CC_NLE;
156
case CC_NLE: return CC_LE;
157
}
158
ERROR_LOG_REPORT(Log::JIT, "FlipCCFlag: Unexpected CC flag: %d", flag);
159
return CC_O;
160
}
161
162
CCFlags Jit::SwapCCFlag(CCFlags flag)
163
{
164
// This swaps the comparison for an lhs/rhs swap, but doesn't flip/invert the logic.
165
switch (flag)
166
{
167
case CC_O: return CC_O;
168
case CC_NO: return CC_NO;
169
case CC_B: return CC_A;
170
case CC_NB: return CC_NA;
171
case CC_Z: return CC_Z;
172
case CC_NZ: return CC_NZ;
173
case CC_BE: return CC_AE;
174
case CC_NBE: return CC_NAE;
175
case CC_S: return CC_S;
176
case CC_NS: return CC_NS;
177
case CC_P: return CC_P;
178
case CC_NP: return CC_NP;
179
case CC_L: return CC_G;
180
case CC_NL: return CC_NG;
181
case CC_LE: return CC_GE;
182
case CC_NLE: return CC_NGE;
183
}
184
ERROR_LOG_REPORT(Log::JIT, "SwapCCFlag: Unexpected CC flag: %d", flag);
185
return CC_O;
186
}
187
188
bool Jit::PredictTakeBranch(u32 targetAddr, bool likely) {
189
// If it's likely, it's... probably likely, right?
190
if (likely)
191
return true;
192
193
// TODO: Normal branch prediction would be to take branches going upward to lower addresses.
194
// However, this results in worse performance as of this comment's writing.
195
// The reverse check generally gives better or same performance.
196
return targetAddr > GetCompilerPC();
197
}
198
199
void Jit::CompBranchExits(CCFlags cc, u32 targetAddr, u32 notTakenAddr, const BranchInfo &branchInfo) {
200
if (branchInfo.andLink)
201
gpr.SetImm(MIPS_REG_RA, GetCompilerPC() + 8);
202
203
// We may want to try to continue along this branch a little while, to reduce reg flushing.
204
bool predictTakeBranch = PredictTakeBranch(targetAddr, branchInfo.likely);
205
206
Gen::FixupBranch ptr;
207
if (!branchInfo.likely) {
208
if (!branchInfo.delaySlotIsNice && !branchInfo.delaySlotIsBranch)
209
CompileDelaySlot(DELAYSLOT_SAFE_FLUSH);
210
else
211
FlushAll();
212
ptr = J_CC(cc, true);
213
} else {
214
FlushAll();
215
ptr = J_CC(cc, true);
216
if (!branchInfo.delaySlotIsBranch)
217
CompileDelaySlot(DELAYSLOT_FLUSH);
218
}
219
220
// Handle the linkage of a delay slot, even when we're taking the branch.
221
if (branchInfo.delaySlotIsBranch) {
222
// We still link when the branch is taken (targetAddr case.)
223
// Remember, it's from the perspective of the delay slot, so +12.
224
if ((branchInfo.delaySlotInfo & OUT_RA) != 0)
225
gpr.SetImm(MIPS_REG_RA, GetCompilerPC() + 12);
226
if ((branchInfo.delaySlotInfo & OUT_RD) != 0)
227
gpr.SetImm(MIPS_GET_RD(branchInfo.delaySlotOp), GetCompilerPC() + 12);
228
FlushAll();
229
}
230
231
// Take the branch
232
CONDITIONAL_LOG_EXIT(targetAddr);
233
WriteExit(targetAddr, js.nextExit++);
234
235
// Not taken
236
SetJumpTarget(ptr);
237
CONDITIONAL_LOG_EXIT(notTakenAddr);
238
WriteExit(notTakenAddr, js.nextExit++);
239
js.compiling = false;
240
}
241
242
void Jit::CompBranchExit(bool taken, u32 targetAddr, u32 notTakenAddr, const BranchInfo &branchInfo) {
243
// Continuing is handled in the imm branch case... TODO: move it here?
244
if (branchInfo.andLink)
245
gpr.SetImm(MIPS_REG_RA, GetCompilerPC() + 8);
246
if (branchInfo.delaySlotIsBranch) {
247
if (taken) {
248
// We still link when the branch is taken (targetAddr case.)
249
// Remember, it's from the perspective of the delay slot, so +12.
250
if ((branchInfo.delaySlotInfo & OUT_RA) != 0)
251
gpr.SetImm(MIPS_REG_RA, GetCompilerPC() + 12);
252
if ((branchInfo.delaySlotInfo & OUT_RD) != 0)
253
gpr.SetImm(MIPS_GET_RD(branchInfo.delaySlotOp), GetCompilerPC() + 12);
254
}
255
FlushAll();
256
} else if (taken || !branchInfo.likely) {
257
CompileDelaySlot(DELAYSLOT_FLUSH);
258
} else {
259
FlushAll();
260
}
261
262
const u32 destAddr = taken ? targetAddr : notTakenAddr;
263
CONDITIONAL_LOG_EXIT(destAddr);
264
WriteExit(destAddr, js.nextExit++);
265
js.compiling = false;
266
}
267
268
void Jit::BranchRSRTComp(MIPSOpcode op, Gen::CCFlags cc, bool likely)
269
{
270
CONDITIONAL_LOG;
271
if (js.inDelaySlot) {
272
ERROR_LOG_REPORT(Log::JIT, "Branch in RSRTComp delay slot at %08x in block starting at %08x", GetCompilerPC(), js.blockStart);
273
return;
274
}
275
int offset = TARGET16;
276
MIPSGPReg rt = _RT;
277
MIPSGPReg rs = _RS;
278
u32 targetAddr = GetCompilerPC() + offset + 4;
279
280
BranchInfo branchInfo(GetCompilerPC(), op, GetOffsetInstruction(1), false, likely);
281
branchInfo.delaySlotIsNice = IsDelaySlotNiceReg(op, branchInfo.delaySlotOp, rt, rs);
282
CONDITIONAL_NICE_DELAYSLOT;
283
284
bool immBranch = false;
285
bool immBranchTaken = false;
286
if (gpr.IsImm(rs) && gpr.IsImm(rt) && !branchInfo.delaySlotIsBranch) {
287
// The cc flags are opposites: when NOT to take the branch.
288
bool immBranchNotTaken;
289
s32 rsImm = (s32)gpr.GetImm(rs);
290
s32 rtImm = (s32)gpr.GetImm(rt);
291
292
switch (cc)
293
{
294
case CC_E: immBranchNotTaken = rsImm == rtImm; break;
295
case CC_NE: immBranchNotTaken = rsImm != rtImm; break;
296
default: immBranchNotTaken = false; _dbg_assert_msg_(false, "Bad cc flag in BranchRSRTComp().");
297
}
298
immBranch = true;
299
immBranchTaken = !immBranchNotTaken;
300
}
301
302
if (jo.immBranches && immBranch && js.numInstructions < jo.continueMaxInstructions)
303
{
304
if (!immBranchTaken)
305
{
306
// Skip the delay slot if likely, otherwise it'll be the next instruction.
307
if (likely)
308
js.compilerPC += 4;
309
return;
310
}
311
312
// Branch taken. Always compile the delay slot, and then go to dest.
313
CompileDelaySlot(DELAYSLOT_NICE);
314
AddContinuedBlock(targetAddr);
315
// Account for the increment in the loop.
316
js.compilerPC = targetAddr - 4;
317
// In case the delay slot was a break or something.
318
js.compiling = true;
319
return;
320
}
321
322
js.downcountAmount += MIPSGetInstructionCycleEstimate(branchInfo.delaySlotOp);
323
324
u32 notTakenTarget = ResolveNotTakenTarget(branchInfo);
325
if (immBranch)
326
CompBranchExit(immBranchTaken, targetAddr, notTakenTarget, branchInfo);
327
else
328
{
329
if (!likely && branchInfo.delaySlotIsNice && !branchInfo.delaySlotIsBranch)
330
CompileDelaySlot(DELAYSLOT_NICE);
331
332
if (gpr.IsImm(rt) && gpr.GetImm(rt) == 0)
333
{
334
gpr.KillImmediate(rs, true, false);
335
CMP(32, gpr.R(rs), Imm32(0));
336
}
337
else
338
{
339
gpr.MapReg(rs, true, false);
340
CMP(32, gpr.R(rs), gpr.R(rt));
341
}
342
343
CompBranchExits(cc, targetAddr, notTakenTarget, branchInfo);
344
}
345
}
346
347
void Jit::BranchRSZeroComp(MIPSOpcode op, Gen::CCFlags cc, bool andLink, bool likely)
348
{
349
CONDITIONAL_LOG;
350
if (js.inDelaySlot) {
351
ERROR_LOG_REPORT(Log::JIT, "Branch in RSZeroComp delay slot at %08x in block starting at %08x", GetCompilerPC(), js.blockStart);
352
return;
353
}
354
int offset = TARGET16;
355
MIPSGPReg rs = _RS;
356
u32 targetAddr = GetCompilerPC() + offset + 4;
357
358
BranchInfo branchInfo(GetCompilerPC(), op, GetOffsetInstruction(1), andLink, likely);
359
branchInfo.delaySlotIsNice = IsDelaySlotNiceReg(op, branchInfo.delaySlotOp, rs);
360
js.downcountAmount += MIPSGetInstructionCycleEstimate(branchInfo.delaySlotOp);
361
CONDITIONAL_NICE_DELAYSLOT;
362
363
bool immBranch = false;
364
bool immBranchTaken = false;
365
if (gpr.IsImm(rs) && !branchInfo.delaySlotIsBranch) {
366
// The cc flags are opposites: when NOT to take the branch.
367
bool immBranchNotTaken;
368
s32 imm = (s32)gpr.GetImm(rs);
369
370
switch (cc)
371
{
372
case CC_G: immBranchNotTaken = imm > 0; break;
373
case CC_GE: immBranchNotTaken = imm >= 0; break;
374
case CC_L: immBranchNotTaken = imm < 0; break;
375
case CC_LE: immBranchNotTaken = imm <= 0; break;
376
default: immBranchNotTaken = false; _dbg_assert_msg_(false, "Bad cc flag in BranchRSZeroComp().");
377
}
378
immBranch = true;
379
immBranchTaken = !immBranchNotTaken;
380
}
381
382
if (jo.immBranches && immBranch && js.numInstructions < jo.continueMaxInstructions)
383
{
384
if (!immBranchTaken)
385
{
386
// Skip the delay slot if likely, otherwise it'll be the next instruction.
387
if (andLink)
388
gpr.SetImm(MIPS_REG_RA, GetCompilerPC() + 8);
389
if (likely)
390
js.compilerPC += 4;
391
return;
392
}
393
394
// Branch taken. Always compile the delay slot, and then go to dest.
395
if (andLink)
396
gpr.SetImm(MIPS_REG_RA, GetCompilerPC() + 8);
397
CompileDelaySlot(DELAYSLOT_NICE);
398
399
AddContinuedBlock(targetAddr);
400
// Account for the increment in the loop.
401
js.compilerPC = targetAddr - 4;
402
// In case the delay slot was a break or something.
403
js.compiling = true;
404
return;
405
}
406
407
js.downcountAmount += MIPSGetInstructionCycleEstimate(branchInfo.delaySlotOp);
408
409
u32 notTakenTarget = ResolveNotTakenTarget(branchInfo);
410
if (immBranch)
411
CompBranchExit(immBranchTaken, targetAddr, notTakenTarget, branchInfo);
412
else
413
{
414
if (!likely && branchInfo.delaySlotIsNice && !branchInfo.delaySlotIsBranch)
415
CompileDelaySlot(DELAYSLOT_NICE);
416
417
gpr.MapReg(rs, true, false);
418
CMP(32, gpr.R(rs), Imm32(0));
419
420
CompBranchExits(cc, targetAddr, notTakenTarget, branchInfo);
421
}
422
}
423
424
425
void Jit::Comp_RelBranch(MIPSOpcode op)
426
{
427
switch (op>>26)
428
{
429
case 4: BranchRSRTComp(op, CC_NZ, false); break;//beq
430
case 5: BranchRSRTComp(op, CC_Z, false); break;//bne
431
432
case 6: BranchRSZeroComp(op, CC_G, false, false); break;//blez
433
case 7: BranchRSZeroComp(op, CC_LE, false, false); break;//bgtz
434
435
case 20: BranchRSRTComp(op, CC_NZ, true); break;//beql
436
case 21: BranchRSRTComp(op, CC_Z, true); break;//bnel
437
438
case 22: BranchRSZeroComp(op, CC_G, false, true); break;//blezl
439
case 23: BranchRSZeroComp(op, CC_LE, false, true); break;//bgtzl
440
441
default:
442
_dbg_assert_msg_(false,"Trying to compile instruction that can't be compiled");
443
break;
444
}
445
}
446
447
void Jit::Comp_RelBranchRI(MIPSOpcode op)
448
{
449
switch ((op >> 16) & 0x1F)
450
{
451
case 0: BranchRSZeroComp(op, CC_GE, false, false); break; //if ((s32)R(rs) < 0) DelayBranchTo(addr); else PC += 4; break;//bltz
452
case 1: BranchRSZeroComp(op, CC_L, false, false); break; //if ((s32)R(rs) >= 0) DelayBranchTo(addr); else PC += 4; break;//bgez
453
case 2: BranchRSZeroComp(op, CC_GE, false, true); break; //if ((s32)R(rs) < 0) DelayBranchTo(addr); else PC += 8; break;//bltzl
454
case 3: BranchRSZeroComp(op, CC_L, false, true); break; //if ((s32)R(rs) >= 0) DelayBranchTo(addr); else PC += 8; break;//bgezl
455
case 16: BranchRSZeroComp(op, CC_GE, true, false); break; //R(MIPS_REG_RA) = PC + 8; if ((s32)R(rs) < 0) DelayBranchTo(addr); else PC += 4; break;//bltzal
456
case 17: BranchRSZeroComp(op, CC_L, true, false); break; //R(MIPS_REG_RA) = PC + 8; if ((s32)R(rs) >= 0) DelayBranchTo(addr); else PC += 4; break;//bgezal
457
case 18: BranchRSZeroComp(op, CC_GE, true, true); break; //R(MIPS_REG_RA) = PC + 8; if ((s32)R(rs) < 0) DelayBranchTo(addr); else SkipLikely(); break;//bltzall
458
case 19: BranchRSZeroComp(op, CC_L, true, true); break; //R(MIPS_REG_RA) = PC + 8; if ((s32)R(rs) >= 0) DelayBranchTo(addr); else SkipLikely(); break;//bgezall
459
default:
460
_dbg_assert_msg_(false,"Trying to compile instruction that can't be compiled");
461
break;
462
}
463
}
464
465
466
// If likely is set, discard the branch slot if NOT taken.
467
void Jit::BranchFPFlag(MIPSOpcode op, Gen::CCFlags cc, bool likely)
468
{
469
CONDITIONAL_LOG;
470
if (js.inDelaySlot) {
471
ERROR_LOG_REPORT(Log::JIT, "Branch in FPFlag delay slot at %08x in block starting at %08x", GetCompilerPC(), js.blockStart);
472
return;
473
}
474
int offset = TARGET16;
475
u32 targetAddr = GetCompilerPC() + offset + 4;
476
477
BranchInfo branchInfo(GetCompilerPC(), op, GetOffsetInstruction(1), false, likely);
478
branchInfo.delaySlotIsNice = IsDelaySlotNiceFPU(op, branchInfo.delaySlotOp);
479
CONDITIONAL_NICE_DELAYSLOT;
480
481
js.downcountAmount += MIPSGetInstructionCycleEstimate(branchInfo.delaySlotOp);
482
if (!likely && branchInfo.delaySlotIsNice && !branchInfo.delaySlotIsBranch)
483
CompileDelaySlot(DELAYSLOT_NICE);
484
485
gpr.KillImmediate(MIPS_REG_FPCOND, true, false);
486
TEST(32, gpr.R(MIPS_REG_FPCOND), Imm32(1));
487
488
u32 notTakenTarget = ResolveNotTakenTarget(branchInfo);
489
CompBranchExits(cc, targetAddr, notTakenTarget, branchInfo);
490
}
491
492
493
void Jit::Comp_FPUBranch(MIPSOpcode op)
494
{
495
switch((op >> 16) & 0x1f)
496
{
497
case 0: BranchFPFlag(op, CC_NZ, false); break; //bc1f
498
case 1: BranchFPFlag(op, CC_Z, false); break; //bc1t
499
case 2: BranchFPFlag(op, CC_NZ, true); break; //bc1fl
500
case 3: BranchFPFlag(op, CC_Z, true); break; //bc1tl
501
default:
502
_dbg_assert_msg_(false,"Trying to interpret instruction that can't be interpreted");
503
break;
504
}
505
}
506
507
// If likely is set, discard the branch slot if NOT taken.
508
void Jit::BranchVFPUFlag(MIPSOpcode op, Gen::CCFlags cc, bool likely)
509
{
510
CONDITIONAL_LOG;
511
if (js.inDelaySlot) {
512
// I think we can safely just warn-log this without reporting, it's pretty clear that this type
513
// of branch is ignored.
514
WARN_LOG(Log::JIT, "Branch in VFPU delay slot at %08x in block starting at %08x", GetCompilerPC(), js.blockStart);
515
return;
516
}
517
int offset = TARGET16;
518
u32 targetAddr = GetCompilerPC() + offset + 4;
519
520
BranchInfo branchInfo(GetCompilerPC(), op, GetOffsetInstruction(1), false, likely);
521
// Sometimes there's a VFPU branch in a delay slot (Disgaea 2: Dark Hero Days, Zettai Hero Project, La Pucelle)
522
// The behavior is undefined - the CPU may take the second branch even if the first one passes.
523
// However, it does consistently try each branch, which these games seem to expect.
524
branchInfo.delaySlotIsNice = IsDelaySlotNiceVFPU(op, branchInfo.delaySlotOp);
525
CONDITIONAL_NICE_DELAYSLOT;
526
527
js.downcountAmount += MIPSGetInstructionCycleEstimate(branchInfo.delaySlotOp);
528
if (!likely && branchInfo.delaySlotIsNice && !branchInfo.delaySlotIsBranch)
529
CompileDelaySlot(DELAYSLOT_NICE);
530
531
// THE CONDITION
532
int imm3 = (op >> 18) & 7;
533
534
gpr.KillImmediate(MIPS_REG_VFPUCC, true, false);
535
TEST(32, gpr.R(MIPS_REG_VFPUCC), Imm32(1 << imm3));
536
537
u32 notTakenTarget = ResolveNotTakenTarget(branchInfo);
538
CompBranchExits(cc, targetAddr, notTakenTarget, branchInfo);
539
}
540
541
542
void Jit::Comp_VBranch(MIPSOpcode op)
543
{
544
switch ((op >> 16) & 3)
545
{
546
case 0: BranchVFPUFlag(op, CC_NZ, false); break; //bvf
547
case 1: BranchVFPUFlag(op, CC_Z, false); break; //bvt
548
case 2: BranchVFPUFlag(op, CC_NZ, true); break; //bvfl
549
case 3: BranchVFPUFlag(op, CC_Z, true); break; //bvtl
550
default:
551
_dbg_assert_msg_(false,"Comp_VBranch: Invalid instruction");
552
break;
553
}
554
}
555
556
static void HitInvalidJump(uint32_t dest) {
557
Core_ExecException(dest, currentMIPS->pc - 8, ExecExceptionType::JUMP);
558
}
559
560
void Jit::Comp_Jump(MIPSOpcode op) {
561
CONDITIONAL_LOG;
562
if (js.inDelaySlot) {
563
ERROR_LOG_REPORT(Log::JIT, "Branch in Jump delay slot at %08x in block starting at %08x", GetCompilerPC(), js.blockStart);
564
return;
565
}
566
u32 off = TARGET26;
567
u32 targetAddr = (GetCompilerPC() & 0xF0000000) | off;
568
569
// Might be a stubbed address or something?
570
if (!Memory::IsValidAddress(targetAddr) || (targetAddr & 3) != 0) {
571
if (js.nextExit == 0) {
572
ERROR_LOG(Log::JIT, "Jump to invalid address: %08x PC %08x LR %08x", targetAddr, GetCompilerPC(), currentMIPS->r[MIPS_REG_RA]);
573
} else {
574
js.compiling = false;
575
}
576
// TODO: Mark this block dirty or something? May be indication it will be changed by imports.
577
578
CompileDelaySlot(DELAYSLOT_NICE);
579
FlushAll();
580
MOV(32, MIPSSTATE_VAR(pc), Imm32(GetCompilerPC() + 8));
581
ABI_CallFunctionC(&HitInvalidJump, targetAddr);
582
WriteSyscallExit();
583
return;
584
}
585
586
switch (op >> 26) {
587
case 2: //j
588
CompileDelaySlot(DELAYSLOT_NICE);
589
if (CanContinueJump(targetAddr))
590
{
591
AddContinuedBlock(targetAddr);
592
// Account for the increment in the loop.
593
js.compilerPC = targetAddr - 4;
594
// In case the delay slot was a break or something.
595
js.compiling = true;
596
return;
597
}
598
FlushAll();
599
CONDITIONAL_LOG_EXIT(targetAddr);
600
WriteExit(targetAddr, js.nextExit++);
601
break;
602
603
case 3: //jal
604
// Special case for branches to "replace functions":
605
if (ReplaceJalTo(targetAddr))
606
return;
607
608
// Check for small function inlining (future)
609
610
611
// Save return address - might be overwritten by delay slot.
612
gpr.SetImm(MIPS_REG_RA, GetCompilerPC() + 8);
613
CompileDelaySlot(DELAYSLOT_NICE);
614
if (CanContinueJump(targetAddr))
615
{
616
AddContinuedBlock(targetAddr);
617
// Account for the increment in the loop.
618
js.compilerPC = targetAddr - 4;
619
// In case the delay slot was a break or something.
620
js.compiling = true;
621
return;
622
}
623
FlushAll();
624
CONDITIONAL_LOG_EXIT(targetAddr);
625
WriteExit(targetAddr, js.nextExit++);
626
break;
627
628
default:
629
_dbg_assert_msg_(false,"Trying to compile instruction that can't be compiled");
630
break;
631
}
632
js.compiling = false;
633
}
634
635
void Jit::Comp_JumpReg(MIPSOpcode op)
636
{
637
CONDITIONAL_LOG;
638
if (js.inDelaySlot) {
639
ERROR_LOG_REPORT(Log::JIT, "Branch in JumpReg delay slot at %08x in block starting at %08x", GetCompilerPC(), js.blockStart);
640
return;
641
}
642
MIPSGPReg rs = _RS;
643
MIPSGPReg rd = _RD;
644
bool andLink = (op & 0x3f) == 9 && rd != MIPS_REG_ZERO;
645
646
MIPSOpcode delaySlotOp = GetOffsetInstruction(1);
647
js.downcountAmount += MIPSGetInstructionCycleEstimate(delaySlotOp);
648
bool delaySlotIsNice = IsDelaySlotNiceReg(op, delaySlotOp, rs);
649
if (andLink && rs == rd)
650
delaySlotIsNice = false;
651
CONDITIONAL_NICE_DELAYSLOT;
652
653
X64Reg destReg = EAX;
654
if (IsSyscall(delaySlotOp))
655
{
656
// If this is a syscall, write the pc (for thread switching and other good reasons.)
657
gpr.MapReg(rs, true, false);
658
MOV(32, MIPSSTATE_VAR(pc), gpr.R(rs));
659
if (andLink)
660
gpr.SetImm(rd, GetCompilerPC() + 8);
661
CompileDelaySlot(DELAYSLOT_FLUSH);
662
663
// Syscalls write the exit code for us.
664
_dbg_assert_msg_(!js.compiling, "Expected syscall to write an exit code.");
665
return;
666
}
667
else if (delaySlotIsNice)
668
{
669
if (andLink)
670
gpr.SetImm(rd, GetCompilerPC() + 8);
671
CompileDelaySlot(DELAYSLOT_NICE);
672
673
if (!andLink && rs == MIPS_REG_RA && g_Config.bDiscardRegsOnJRRA) {
674
// According to the MIPS ABI, there are some regs we don't need to preserve.
675
// Let's discard them so we don't need to write them back.
676
// NOTE: Not all games follow the MIPS ABI! Tekken 6, for example, will crash
677
// with this enabled.
678
gpr.DiscardRegContentsIfCached(MIPS_REG_COMPILER_SCRATCH);
679
for (int i = MIPS_REG_A0; i <= MIPS_REG_T7; i++)
680
gpr.DiscardRegContentsIfCached((MIPSGPReg)i);
681
gpr.DiscardRegContentsIfCached(MIPS_REG_T8);
682
gpr.DiscardRegContentsIfCached(MIPS_REG_T9);
683
}
684
685
if (gpr.IsImm(rs) && CanContinueJump(gpr.GetImm(rs)))
686
{
687
AddContinuedBlock(gpr.GetImm(rs));
688
// Account for the increment in the loop.
689
js.compilerPC = gpr.GetImm(rs) - 4;
690
// In case the delay slot was a break or something.
691
js.compiling = true;
692
return;
693
}
694
695
if (gpr.R(rs).IsSimpleReg()) {
696
destReg = gpr.R(rs).GetSimpleReg();
697
} else {
698
MOV(32, R(EAX), gpr.R(rs));
699
}
700
FlushAll();
701
} else {
702
// Latch destination now - save it in memory.
703
gpr.MapReg(rs, true, false);
704
MOV(32, MIPSSTATE_VAR(savedPC), gpr.R(rs));
705
if (andLink)
706
gpr.SetImm(rd, GetCompilerPC() + 8);
707
CompileDelaySlot(DELAYSLOT_NICE);
708
MOV(32, R(EAX), MIPSSTATE_VAR(savedPC));
709
FlushAll();
710
}
711
712
switch (op & 0x3f) {
713
case 8: //jr
714
break;
715
case 9: //jalr
716
break;
717
default:
718
_dbg_assert_msg_(false,"Trying to compile instruction that can't be compiled");
719
break;
720
}
721
722
CONDITIONAL_LOG_EXIT_EAX();
723
WriteExitDestInReg(destReg);
724
js.compiling = false;
725
}
726
727
void Jit::Comp_Syscall(MIPSOpcode op)
728
{
729
if (op.encoding == 0x03FFFFcc) {
730
WARN_LOG(Log::JIT, "Encountered bad syscall instruction at %08x (%08x)", js.compilerPC, op.encoding);
731
}
732
733
if (!g_Config.bSkipDeadbeefFilling)
734
{
735
// All of these will be overwritten with DEADBEEF anyway.
736
gpr.DiscardR(MIPS_REG_COMPILER_SCRATCH);
737
// We need to keep A0 - T3, which are used for args.
738
gpr.DiscardR(MIPS_REG_T4);
739
gpr.DiscardR(MIPS_REG_T5);
740
gpr.DiscardR(MIPS_REG_T6);
741
gpr.DiscardR(MIPS_REG_T7);
742
gpr.DiscardR(MIPS_REG_T8);
743
gpr.DiscardR(MIPS_REG_T9);
744
745
gpr.DiscardR(MIPS_REG_HI);
746
gpr.DiscardR(MIPS_REG_LO);
747
}
748
FlushAll();
749
750
// If we're in a delay slot, this is off by one.
751
const int offset = js.inDelaySlot ? -1 : 0;
752
WriteDowncount(offset);
753
RestoreRoundingMode();
754
js.downcountAmount = -offset;
755
756
if (!js.inDelaySlot) {
757
MOV(32, MIPSSTATE_VAR(pc), Imm32(GetCompilerPC() + 4));
758
}
759
760
#ifdef USE_PROFILER
761
// When profiling, we can't skip CallSyscall, since it times syscalls.
762
ABI_CallFunctionC(&CallSyscall, op.encoding);
763
#else
764
// Skip the CallSyscall where possible.
765
void *quickFunc = GetQuickSyscallFunc(op);
766
if (quickFunc)
767
ABI_CallFunctionP(quickFunc, (void *)GetSyscallFuncPointer(op));
768
else
769
ABI_CallFunctionC(&CallSyscall, op.encoding);
770
#endif
771
772
ApplyRoundingMode();
773
WriteSyscallExit();
774
js.compiling = false;
775
}
776
777
void Jit::Comp_Break(MIPSOpcode op)
778
{
779
Comp_Generic(op);
780
WriteSyscallExit();
781
js.compiling = false;
782
}
783
784
} // namespace Mipscomp
785
786
#endif // PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64)
787
788