CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
hrydgard

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: hrydgard/ppsspp
Path: blob/master/Core/MIPS/x86/CompBranch.cpp
Views: 1401
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_EnableStepping(true, "jit.branchdebug", 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
if (!branchInfo.delaySlotIsBranch && CanContinueBranch(predictTakeBranch ? targetAddr : notTakenAddr))
206
{
207
if (predictTakeBranch)
208
cc = FlipCCFlag(cc);
209
210
Gen::FixupBranch ptr;
211
RegCacheState state;
212
if (!branchInfo.likely)
213
{
214
if (!branchInfo.delaySlotIsNice)
215
CompileDelaySlot(DELAYSLOT_SAFE);
216
ptr = J_CC(cc, true);
217
GetStateAndFlushAll(state);
218
}
219
else
220
{
221
ptr = J_CC(cc, true);
222
if (predictTakeBranch)
223
GetStateAndFlushAll(state);
224
else
225
{
226
// We need to get the state BEFORE the delay slot is compiled.
227
gpr.GetState(state.gpr);
228
fpr.GetState(state.fpr);
229
CompileDelaySlot(DELAYSLOT_FLUSH);
230
}
231
}
232
233
if (predictTakeBranch)
234
{
235
// We flipped the cc, the not taken case is first.
236
CONDITIONAL_LOG_EXIT(notTakenAddr);
237
WriteExit(notTakenAddr, js.nextExit++);
238
239
// Now our taken path. Bring the regs back, we didn't flush 'em after all.
240
SetJumpTarget(ptr);
241
RestoreState(state);
242
CONDITIONAL_LOG_EXIT(targetAddr);
243
244
// Don't forget to run the delay slot if likely.
245
if (branchInfo.likely)
246
CompileDelaySlot(DELAYSLOT_NICE);
247
248
AddContinuedBlock(targetAddr);
249
// Account for the increment in the loop.
250
js.compilerPC = targetAddr - 4;
251
// In case the delay slot was a break or something.
252
js.compiling = true;
253
}
254
else
255
{
256
// Take the branch
257
CONDITIONAL_LOG_EXIT(targetAddr);
258
WriteExit(targetAddr, js.nextExit++);
259
260
// Not taken
261
SetJumpTarget(ptr);
262
RestoreState(state);
263
CONDITIONAL_LOG_EXIT(notTakenAddr);
264
265
// Account for the delay slot.
266
js.compilerPC += 4;
267
// In case the delay slot was a break or something.
268
js.compiling = true;
269
}
270
}
271
else
272
{
273
Gen::FixupBranch ptr;
274
if (!branchInfo.likely)
275
{
276
if (!branchInfo.delaySlotIsNice && !branchInfo.delaySlotIsBranch)
277
CompileDelaySlot(DELAYSLOT_SAFE_FLUSH);
278
else
279
FlushAll();
280
ptr = J_CC(cc, true);
281
}
282
else
283
{
284
FlushAll();
285
ptr = J_CC(cc, true);
286
if (!branchInfo.delaySlotIsBranch)
287
CompileDelaySlot(DELAYSLOT_FLUSH);
288
}
289
290
// Handle the linkage of a delay slot, even when we're taking the branch.
291
if (branchInfo.delaySlotIsBranch) {
292
// We still link when the branch is taken (targetAddr case.)
293
// Remember, it's from the perspective of the delay slot, so +12.
294
if ((branchInfo.delaySlotInfo & OUT_RA) != 0)
295
gpr.SetImm(MIPS_REG_RA, GetCompilerPC() + 12);
296
if ((branchInfo.delaySlotInfo & OUT_RD) != 0)
297
gpr.SetImm(MIPS_GET_RD(branchInfo.delaySlotOp), GetCompilerPC() + 12);
298
FlushAll();
299
}
300
301
// Take the branch
302
CONDITIONAL_LOG_EXIT(targetAddr);
303
WriteExit(targetAddr, js.nextExit++);
304
305
// Not taken
306
SetJumpTarget(ptr);
307
CONDITIONAL_LOG_EXIT(notTakenAddr);
308
WriteExit(notTakenAddr, js.nextExit++);
309
js.compiling = false;
310
}
311
}
312
313
void Jit::CompBranchExit(bool taken, u32 targetAddr, u32 notTakenAddr, const BranchInfo &branchInfo) {
314
// Continuing is handled in the imm branch case... TODO: move it here?
315
if (branchInfo.andLink)
316
gpr.SetImm(MIPS_REG_RA, GetCompilerPC() + 8);
317
if (branchInfo.delaySlotIsBranch) {
318
if (taken) {
319
// We still link when the branch is taken (targetAddr case.)
320
// Remember, it's from the perspective of the delay slot, so +12.
321
if ((branchInfo.delaySlotInfo & OUT_RA) != 0)
322
gpr.SetImm(MIPS_REG_RA, GetCompilerPC() + 12);
323
if ((branchInfo.delaySlotInfo & OUT_RD) != 0)
324
gpr.SetImm(MIPS_GET_RD(branchInfo.delaySlotOp), GetCompilerPC() + 12);
325
}
326
FlushAll();
327
} else if (taken || !branchInfo.likely) {
328
CompileDelaySlot(DELAYSLOT_FLUSH);
329
} else {
330
FlushAll();
331
}
332
333
const u32 destAddr = taken ? targetAddr : notTakenAddr;
334
CONDITIONAL_LOG_EXIT(destAddr);
335
WriteExit(destAddr, js.nextExit++);
336
js.compiling = false;
337
}
338
339
void Jit::BranchRSRTComp(MIPSOpcode op, Gen::CCFlags cc, bool likely)
340
{
341
CONDITIONAL_LOG;
342
if (js.inDelaySlot) {
343
ERROR_LOG_REPORT(Log::JIT, "Branch in RSRTComp delay slot at %08x in block starting at %08x", GetCompilerPC(), js.blockStart);
344
return;
345
}
346
int offset = TARGET16;
347
MIPSGPReg rt = _RT;
348
MIPSGPReg rs = _RS;
349
u32 targetAddr = GetCompilerPC() + offset + 4;
350
351
BranchInfo branchInfo(GetCompilerPC(), op, GetOffsetInstruction(1), false, likely);
352
branchInfo.delaySlotIsNice = IsDelaySlotNiceReg(op, branchInfo.delaySlotOp, rt, rs);
353
CONDITIONAL_NICE_DELAYSLOT;
354
355
bool immBranch = false;
356
bool immBranchTaken = false;
357
if (gpr.IsImm(rs) && gpr.IsImm(rt) && !branchInfo.delaySlotIsBranch) {
358
// The cc flags are opposites: when NOT to take the branch.
359
bool immBranchNotTaken;
360
s32 rsImm = (s32)gpr.GetImm(rs);
361
s32 rtImm = (s32)gpr.GetImm(rt);
362
363
switch (cc)
364
{
365
case CC_E: immBranchNotTaken = rsImm == rtImm; break;
366
case CC_NE: immBranchNotTaken = rsImm != rtImm; break;
367
default: immBranchNotTaken = false; _dbg_assert_msg_(false, "Bad cc flag in BranchRSRTComp().");
368
}
369
immBranch = true;
370
immBranchTaken = !immBranchNotTaken;
371
}
372
373
if (jo.immBranches && immBranch && js.numInstructions < jo.continueMaxInstructions)
374
{
375
if (!immBranchTaken)
376
{
377
// Skip the delay slot if likely, otherwise it'll be the next instruction.
378
if (likely)
379
js.compilerPC += 4;
380
return;
381
}
382
383
// Branch taken. Always compile the delay slot, and then go to dest.
384
CompileDelaySlot(DELAYSLOT_NICE);
385
AddContinuedBlock(targetAddr);
386
// Account for the increment in the loop.
387
js.compilerPC = targetAddr - 4;
388
// In case the delay slot was a break or something.
389
js.compiling = true;
390
return;
391
}
392
393
js.downcountAmount += MIPSGetInstructionCycleEstimate(branchInfo.delaySlotOp);
394
395
u32 notTakenTarget = ResolveNotTakenTarget(branchInfo);
396
if (immBranch)
397
CompBranchExit(immBranchTaken, targetAddr, notTakenTarget, branchInfo);
398
else
399
{
400
if (!likely && branchInfo.delaySlotIsNice && !branchInfo.delaySlotIsBranch)
401
CompileDelaySlot(DELAYSLOT_NICE);
402
403
if (gpr.IsImm(rt) && gpr.GetImm(rt) == 0)
404
{
405
gpr.KillImmediate(rs, true, false);
406
CMP(32, gpr.R(rs), Imm32(0));
407
}
408
else
409
{
410
gpr.MapReg(rs, true, false);
411
CMP(32, gpr.R(rs), gpr.R(rt));
412
}
413
414
CompBranchExits(cc, targetAddr, notTakenTarget, branchInfo);
415
}
416
}
417
418
void Jit::BranchRSZeroComp(MIPSOpcode op, Gen::CCFlags cc, bool andLink, bool likely)
419
{
420
CONDITIONAL_LOG;
421
if (js.inDelaySlot) {
422
ERROR_LOG_REPORT(Log::JIT, "Branch in RSZeroComp delay slot at %08x in block starting at %08x", GetCompilerPC(), js.blockStart);
423
return;
424
}
425
int offset = TARGET16;
426
MIPSGPReg rs = _RS;
427
u32 targetAddr = GetCompilerPC() + offset + 4;
428
429
BranchInfo branchInfo(GetCompilerPC(), op, GetOffsetInstruction(1), andLink, likely);
430
branchInfo.delaySlotIsNice = IsDelaySlotNiceReg(op, branchInfo.delaySlotOp, rs);
431
js.downcountAmount += MIPSGetInstructionCycleEstimate(branchInfo.delaySlotOp);
432
CONDITIONAL_NICE_DELAYSLOT;
433
434
bool immBranch = false;
435
bool immBranchTaken = false;
436
if (gpr.IsImm(rs) && !branchInfo.delaySlotIsBranch) {
437
// The cc flags are opposites: when NOT to take the branch.
438
bool immBranchNotTaken;
439
s32 imm = (s32)gpr.GetImm(rs);
440
441
switch (cc)
442
{
443
case CC_G: immBranchNotTaken = imm > 0; break;
444
case CC_GE: immBranchNotTaken = imm >= 0; break;
445
case CC_L: immBranchNotTaken = imm < 0; break;
446
case CC_LE: immBranchNotTaken = imm <= 0; break;
447
default: immBranchNotTaken = false; _dbg_assert_msg_(false, "Bad cc flag in BranchRSZeroComp().");
448
}
449
immBranch = true;
450
immBranchTaken = !immBranchNotTaken;
451
}
452
453
if (jo.immBranches && immBranch && js.numInstructions < jo.continueMaxInstructions)
454
{
455
if (!immBranchTaken)
456
{
457
// Skip the delay slot if likely, otherwise it'll be the next instruction.
458
if (andLink)
459
gpr.SetImm(MIPS_REG_RA, GetCompilerPC() + 8);
460
if (likely)
461
js.compilerPC += 4;
462
return;
463
}
464
465
// Branch taken. Always compile the delay slot, and then go to dest.
466
if (andLink)
467
gpr.SetImm(MIPS_REG_RA, GetCompilerPC() + 8);
468
CompileDelaySlot(DELAYSLOT_NICE);
469
470
AddContinuedBlock(targetAddr);
471
// Account for the increment in the loop.
472
js.compilerPC = targetAddr - 4;
473
// In case the delay slot was a break or something.
474
js.compiling = true;
475
return;
476
}
477
478
js.downcountAmount += MIPSGetInstructionCycleEstimate(branchInfo.delaySlotOp);
479
480
u32 notTakenTarget = ResolveNotTakenTarget(branchInfo);
481
if (immBranch)
482
CompBranchExit(immBranchTaken, targetAddr, notTakenTarget, branchInfo);
483
else
484
{
485
if (!likely && branchInfo.delaySlotIsNice && !branchInfo.delaySlotIsBranch)
486
CompileDelaySlot(DELAYSLOT_NICE);
487
488
gpr.MapReg(rs, true, false);
489
CMP(32, gpr.R(rs), Imm32(0));
490
491
CompBranchExits(cc, targetAddr, notTakenTarget, branchInfo);
492
}
493
}
494
495
496
void Jit::Comp_RelBranch(MIPSOpcode op)
497
{
498
switch (op>>26)
499
{
500
case 4: BranchRSRTComp(op, CC_NZ, false); break;//beq
501
case 5: BranchRSRTComp(op, CC_Z, false); break;//bne
502
503
case 6: BranchRSZeroComp(op, CC_G, false, false); break;//blez
504
case 7: BranchRSZeroComp(op, CC_LE, false, false); break;//bgtz
505
506
case 20: BranchRSRTComp(op, CC_NZ, true); break;//beql
507
case 21: BranchRSRTComp(op, CC_Z, true); break;//bnel
508
509
case 22: BranchRSZeroComp(op, CC_G, false, true); break;//blezl
510
case 23: BranchRSZeroComp(op, CC_LE, false, true); break;//bgtzl
511
512
default:
513
_dbg_assert_msg_(false,"Trying to compile instruction that can't be compiled");
514
break;
515
}
516
}
517
518
void Jit::Comp_RelBranchRI(MIPSOpcode op)
519
{
520
switch ((op >> 16) & 0x1F)
521
{
522
case 0: BranchRSZeroComp(op, CC_GE, false, false); break; //if ((s32)R(rs) < 0) DelayBranchTo(addr); else PC += 4; break;//bltz
523
case 1: BranchRSZeroComp(op, CC_L, false, false); break; //if ((s32)R(rs) >= 0) DelayBranchTo(addr); else PC += 4; break;//bgez
524
case 2: BranchRSZeroComp(op, CC_GE, false, true); break; //if ((s32)R(rs) < 0) DelayBranchTo(addr); else PC += 8; break;//bltzl
525
case 3: BranchRSZeroComp(op, CC_L, false, true); break; //if ((s32)R(rs) >= 0) DelayBranchTo(addr); else PC += 8; break;//bgezl
526
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
527
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
528
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
529
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
530
default:
531
_dbg_assert_msg_(false,"Trying to compile instruction that can't be compiled");
532
break;
533
}
534
}
535
536
537
// If likely is set, discard the branch slot if NOT taken.
538
void Jit::BranchFPFlag(MIPSOpcode op, Gen::CCFlags cc, bool likely)
539
{
540
CONDITIONAL_LOG;
541
if (js.inDelaySlot) {
542
ERROR_LOG_REPORT(Log::JIT, "Branch in FPFlag delay slot at %08x in block starting at %08x", GetCompilerPC(), js.blockStart);
543
return;
544
}
545
int offset = TARGET16;
546
u32 targetAddr = GetCompilerPC() + offset + 4;
547
548
BranchInfo branchInfo(GetCompilerPC(), op, GetOffsetInstruction(1), false, likely);
549
branchInfo.delaySlotIsNice = IsDelaySlotNiceFPU(op, branchInfo.delaySlotOp);
550
CONDITIONAL_NICE_DELAYSLOT;
551
552
js.downcountAmount += MIPSGetInstructionCycleEstimate(branchInfo.delaySlotOp);
553
if (!likely && branchInfo.delaySlotIsNice && !branchInfo.delaySlotIsBranch)
554
CompileDelaySlot(DELAYSLOT_NICE);
555
556
gpr.KillImmediate(MIPS_REG_FPCOND, true, false);
557
TEST(32, gpr.R(MIPS_REG_FPCOND), Imm32(1));
558
559
u32 notTakenTarget = ResolveNotTakenTarget(branchInfo);
560
CompBranchExits(cc, targetAddr, notTakenTarget, branchInfo);
561
}
562
563
564
void Jit::Comp_FPUBranch(MIPSOpcode op)
565
{
566
switch((op >> 16) & 0x1f)
567
{
568
case 0: BranchFPFlag(op, CC_NZ, false); break; //bc1f
569
case 1: BranchFPFlag(op, CC_Z, false); break; //bc1t
570
case 2: BranchFPFlag(op, CC_NZ, true); break; //bc1fl
571
case 3: BranchFPFlag(op, CC_Z, true); break; //bc1tl
572
default:
573
_dbg_assert_msg_(false,"Trying to interpret instruction that can't be interpreted");
574
break;
575
}
576
}
577
578
// If likely is set, discard the branch slot if NOT taken.
579
void Jit::BranchVFPUFlag(MIPSOpcode op, Gen::CCFlags cc, bool likely)
580
{
581
CONDITIONAL_LOG;
582
if (js.inDelaySlot) {
583
// I think we can safely just warn-log this without reporting, it's pretty clear that this type
584
// of branch is ignored.
585
WARN_LOG(Log::JIT, "Branch in VFPU delay slot at %08x in block starting at %08x", GetCompilerPC(), js.blockStart);
586
return;
587
}
588
int offset = TARGET16;
589
u32 targetAddr = GetCompilerPC() + offset + 4;
590
591
BranchInfo branchInfo(GetCompilerPC(), op, GetOffsetInstruction(1), false, likely);
592
// Sometimes there's a VFPU branch in a delay slot (Disgaea 2: Dark Hero Days, Zettai Hero Project, La Pucelle)
593
// The behavior is undefined - the CPU may take the second branch even if the first one passes.
594
// However, it does consistently try each branch, which these games seem to expect.
595
branchInfo.delaySlotIsNice = IsDelaySlotNiceVFPU(op, branchInfo.delaySlotOp);
596
CONDITIONAL_NICE_DELAYSLOT;
597
598
js.downcountAmount += MIPSGetInstructionCycleEstimate(branchInfo.delaySlotOp);
599
if (!likely && branchInfo.delaySlotIsNice && !branchInfo.delaySlotIsBranch)
600
CompileDelaySlot(DELAYSLOT_NICE);
601
602
// THE CONDITION
603
int imm3 = (op >> 18) & 7;
604
605
gpr.KillImmediate(MIPS_REG_VFPUCC, true, false);
606
TEST(32, gpr.R(MIPS_REG_VFPUCC), Imm32(1 << imm3));
607
608
u32 notTakenTarget = ResolveNotTakenTarget(branchInfo);
609
CompBranchExits(cc, targetAddr, notTakenTarget, branchInfo);
610
}
611
612
613
void Jit::Comp_VBranch(MIPSOpcode op)
614
{
615
switch ((op >> 16) & 3)
616
{
617
case 0: BranchVFPUFlag(op, CC_NZ, false); break; //bvf
618
case 1: BranchVFPUFlag(op, CC_Z, false); break; //bvt
619
case 2: BranchVFPUFlag(op, CC_NZ, true); break; //bvfl
620
case 3: BranchVFPUFlag(op, CC_Z, true); break; //bvtl
621
default:
622
_dbg_assert_msg_(false,"Comp_VBranch: Invalid instruction");
623
break;
624
}
625
}
626
627
static void HitInvalidJump(uint32_t dest) {
628
Core_ExecException(dest, currentMIPS->pc - 8, ExecExceptionType::JUMP);
629
}
630
631
void Jit::Comp_Jump(MIPSOpcode op) {
632
CONDITIONAL_LOG;
633
if (js.inDelaySlot) {
634
ERROR_LOG_REPORT(Log::JIT, "Branch in Jump delay slot at %08x in block starting at %08x", GetCompilerPC(), js.blockStart);
635
return;
636
}
637
u32 off = TARGET26;
638
u32 targetAddr = (GetCompilerPC() & 0xF0000000) | off;
639
640
// Might be a stubbed address or something?
641
if (!Memory::IsValidAddress(targetAddr) || (targetAddr & 3) != 0) {
642
if (js.nextExit == 0) {
643
ERROR_LOG_REPORT(Log::JIT, "Jump to invalid address: %08x PC %08x LR %08x", targetAddr, GetCompilerPC(), currentMIPS->r[MIPS_REG_RA]);
644
} else {
645
js.compiling = false;
646
}
647
// TODO: Mark this block dirty or something? May be indication it will be changed by imports.
648
649
CompileDelaySlot(DELAYSLOT_NICE);
650
FlushAll();
651
MOV(32, MIPSSTATE_VAR(pc), Imm32(GetCompilerPC() + 8));
652
ABI_CallFunctionC(&HitInvalidJump, targetAddr);
653
WriteSyscallExit();
654
return;
655
}
656
657
switch (op >> 26) {
658
case 2: //j
659
CompileDelaySlot(DELAYSLOT_NICE);
660
if (CanContinueJump(targetAddr))
661
{
662
AddContinuedBlock(targetAddr);
663
// Account for the increment in the loop.
664
js.compilerPC = targetAddr - 4;
665
// In case the delay slot was a break or something.
666
js.compiling = true;
667
return;
668
}
669
FlushAll();
670
CONDITIONAL_LOG_EXIT(targetAddr);
671
WriteExit(targetAddr, js.nextExit++);
672
break;
673
674
case 3: //jal
675
// Special case for branches to "replace functions":
676
if (ReplaceJalTo(targetAddr))
677
return;
678
679
// Check for small function inlining (future)
680
681
682
// Save return address - might be overwritten by delay slot.
683
gpr.SetImm(MIPS_REG_RA, GetCompilerPC() + 8);
684
CompileDelaySlot(DELAYSLOT_NICE);
685
if (CanContinueJump(targetAddr))
686
{
687
AddContinuedBlock(targetAddr);
688
// Account for the increment in the loop.
689
js.compilerPC = targetAddr - 4;
690
// In case the delay slot was a break or something.
691
js.compiling = true;
692
return;
693
}
694
FlushAll();
695
CONDITIONAL_LOG_EXIT(targetAddr);
696
WriteExit(targetAddr, js.nextExit++);
697
break;
698
699
default:
700
_dbg_assert_msg_(false,"Trying to compile instruction that can't be compiled");
701
break;
702
}
703
js.compiling = false;
704
}
705
706
void Jit::Comp_JumpReg(MIPSOpcode op)
707
{
708
CONDITIONAL_LOG;
709
if (js.inDelaySlot) {
710
ERROR_LOG_REPORT(Log::JIT, "Branch in JumpReg delay slot at %08x in block starting at %08x", GetCompilerPC(), js.blockStart);
711
return;
712
}
713
MIPSGPReg rs = _RS;
714
MIPSGPReg rd = _RD;
715
bool andLink = (op & 0x3f) == 9 && rd != MIPS_REG_ZERO;
716
717
MIPSOpcode delaySlotOp = GetOffsetInstruction(1);
718
js.downcountAmount += MIPSGetInstructionCycleEstimate(delaySlotOp);
719
bool delaySlotIsNice = IsDelaySlotNiceReg(op, delaySlotOp, rs);
720
if (andLink && rs == rd)
721
delaySlotIsNice = false;
722
CONDITIONAL_NICE_DELAYSLOT;
723
724
X64Reg destReg = EAX;
725
if (IsSyscall(delaySlotOp))
726
{
727
// If this is a syscall, write the pc (for thread switching and other good reasons.)
728
gpr.MapReg(rs, true, false);
729
MOV(32, MIPSSTATE_VAR(pc), gpr.R(rs));
730
if (andLink)
731
gpr.SetImm(rd, GetCompilerPC() + 8);
732
CompileDelaySlot(DELAYSLOT_FLUSH);
733
734
// Syscalls write the exit code for us.
735
_dbg_assert_msg_(!js.compiling, "Expected syscall to write an exit code.");
736
return;
737
}
738
else if (delaySlotIsNice)
739
{
740
if (andLink)
741
gpr.SetImm(rd, GetCompilerPC() + 8);
742
CompileDelaySlot(DELAYSLOT_NICE);
743
744
if (!andLink && rs == MIPS_REG_RA && g_Config.bDiscardRegsOnJRRA) {
745
// According to the MIPS ABI, there are some regs we don't need to preserve.
746
// Let's discard them so we don't need to write them back.
747
// NOTE: Not all games follow the MIPS ABI! Tekken 6, for example, will crash
748
// with this enabled.
749
gpr.DiscardRegContentsIfCached(MIPS_REG_COMPILER_SCRATCH);
750
for (int i = MIPS_REG_A0; i <= MIPS_REG_T7; i++)
751
gpr.DiscardRegContentsIfCached((MIPSGPReg)i);
752
gpr.DiscardRegContentsIfCached(MIPS_REG_T8);
753
gpr.DiscardRegContentsIfCached(MIPS_REG_T9);
754
}
755
756
if (gpr.IsImm(rs) && CanContinueJump(gpr.GetImm(rs)))
757
{
758
AddContinuedBlock(gpr.GetImm(rs));
759
// Account for the increment in the loop.
760
js.compilerPC = gpr.GetImm(rs) - 4;
761
// In case the delay slot was a break or something.
762
js.compiling = true;
763
return;
764
}
765
766
if (gpr.R(rs).IsSimpleReg()) {
767
destReg = gpr.R(rs).GetSimpleReg();
768
} else {
769
MOV(32, R(EAX), gpr.R(rs));
770
}
771
FlushAll();
772
} else {
773
// Latch destination now - save it in memory.
774
gpr.MapReg(rs, true, false);
775
MOV(32, MIPSSTATE_VAR(savedPC), gpr.R(rs));
776
if (andLink)
777
gpr.SetImm(rd, GetCompilerPC() + 8);
778
CompileDelaySlot(DELAYSLOT_NICE);
779
MOV(32, R(EAX), MIPSSTATE_VAR(savedPC));
780
FlushAll();
781
}
782
783
switch (op & 0x3f) {
784
case 8: //jr
785
break;
786
case 9: //jalr
787
break;
788
default:
789
_dbg_assert_msg_(false,"Trying to compile instruction that can't be compiled");
790
break;
791
}
792
793
CONDITIONAL_LOG_EXIT_EAX();
794
WriteExitDestInReg(destReg);
795
js.compiling = false;
796
}
797
798
void Jit::Comp_Syscall(MIPSOpcode op)
799
{
800
if (op.encoding == 0x03FFFFcc) {
801
WARN_LOG(Log::JIT, "Encountered bad syscall instruction at %08x (%08x)", js.compilerPC, op.encoding);
802
}
803
804
if (!g_Config.bSkipDeadbeefFilling)
805
{
806
// All of these will be overwritten with DEADBEEF anyway.
807
gpr.DiscardR(MIPS_REG_COMPILER_SCRATCH);
808
// We need to keep A0 - T3, which are used for args.
809
gpr.DiscardR(MIPS_REG_T4);
810
gpr.DiscardR(MIPS_REG_T5);
811
gpr.DiscardR(MIPS_REG_T6);
812
gpr.DiscardR(MIPS_REG_T7);
813
gpr.DiscardR(MIPS_REG_T8);
814
gpr.DiscardR(MIPS_REG_T9);
815
816
gpr.DiscardR(MIPS_REG_HI);
817
gpr.DiscardR(MIPS_REG_LO);
818
}
819
FlushAll();
820
821
// If we're in a delay slot, this is off by one.
822
const int offset = js.inDelaySlot ? -1 : 0;
823
WriteDowncount(offset);
824
RestoreRoundingMode();
825
js.downcountAmount = -offset;
826
827
if (!js.inDelaySlot) {
828
MOV(32, MIPSSTATE_VAR(pc), Imm32(GetCompilerPC() + 4));
829
}
830
831
#ifdef USE_PROFILER
832
// When profiling, we can't skip CallSyscall, since it times syscalls.
833
ABI_CallFunctionC(&CallSyscall, op.encoding);
834
#else
835
// Skip the CallSyscall where possible.
836
void *quickFunc = GetQuickSyscallFunc(op);
837
if (quickFunc)
838
ABI_CallFunctionP(quickFunc, (void *)GetSyscallFuncPointer(op));
839
else
840
ABI_CallFunctionC(&CallSyscall, op.encoding);
841
#endif
842
843
ApplyRoundingMode();
844
WriteSyscallExit();
845
js.compiling = false;
846
}
847
848
void Jit::Comp_Break(MIPSOpcode op)
849
{
850
Comp_Generic(op);
851
WriteSyscallExit();
852
js.compiling = false;
853
}
854
855
} // namespace Mipscomp
856
857
#endif // PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64)
858
859