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/ARM/ArmCompBranch.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(ARM)
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/MemMap.h"
28
#include "Core/HLE/HLE.h"
29
#include "Core/HLE/HLETables.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/ARM/ArmJit.h"
37
#include "Core/MIPS/ARM/ArmRegCache.h"
38
#include "Core/MIPS/JitCommon/JitBlockCache.h"
39
40
#include "Common/ArmEmitter.h"
41
42
#define _RS MIPS_GET_RS(op)
43
#define _RT MIPS_GET_RT(op)
44
#define _RD MIPS_GET_RD(op)
45
#define _FS MIPS_GET_FS(op)
46
#define _FT MIPS_GET_FT(op)
47
#define _FD MIPS_GET_FD(op)
48
#define _SA MIPS_GET_SA(op)
49
#define _POS ((op>> 6) & 0x1F)
50
#define _SIZE ((op>>11) & 0x1F)
51
#define _IMM26 (op & 0x03FFFFFF)
52
#define TARGET16 ((int)(SignExtend16ToU32(op) << 2))
53
#define TARGET26 (_IMM26 << 2)
54
55
#define LOOPOPTIMIZATION 0
56
57
// We can disable nice delay slots.
58
// #define CONDITIONAL_NICE_DELAYSLOT branchInfo.delaySlotIsNice = false;
59
#define CONDITIONAL_NICE_DELAYSLOT ;
60
61
using namespace MIPSAnalyst;
62
63
namespace MIPSComp
64
{
65
using namespace ArmGen;
66
using namespace ArmJitConstants;
67
68
void ArmJit::BranchRSRTComp(MIPSOpcode op, CCFlags cc, bool likely)
69
{
70
if (js.inDelaySlot) {
71
ERROR_LOG_REPORT(Log::JIT, "Branch in RSRTComp delay slot at %08x in block starting at %08x", GetCompilerPC(), js.blockStart);
72
return;
73
}
74
int offset = TARGET16;
75
MIPSGPReg rt = _RT;
76
MIPSGPReg rs = _RS;
77
u32 targetAddr = GetCompilerPC() + offset + 4;
78
79
BranchInfo branchInfo(GetCompilerPC(), op, GetOffsetInstruction(1), false, likely);
80
branchInfo.delaySlotIsNice = IsDelaySlotNiceReg(op, branchInfo.delaySlotOp, rt, rs);
81
CONDITIONAL_NICE_DELAYSLOT;
82
83
bool immBranch = false;
84
bool immBranchTaken = false;
85
if (gpr.IsImm(rs) && gpr.IsImm(rt) && !branchInfo.delaySlotIsBranch) {
86
// The cc flags are opposites: when NOT to take the branch.
87
bool immBranchNotTaken;
88
s32 rsImm = (s32)gpr.GetImm(rs);
89
s32 rtImm = (s32)gpr.GetImm(rt);
90
91
switch (cc)
92
{
93
case CC_EQ: immBranchNotTaken = rsImm == rtImm; break;
94
case CC_NEQ: immBranchNotTaken = rsImm != rtImm; break;
95
default: immBranchNotTaken = false; _dbg_assert_msg_(false, "Bad cc flag in BranchRSRTComp().");
96
}
97
immBranch = true;
98
immBranchTaken = !immBranchNotTaken;
99
}
100
101
if (jo.immBranches && immBranch && js.numInstructions < jo.continueMaxInstructions) {
102
if (!immBranchTaken) {
103
// Skip the delay slot if likely, otherwise it'll be the next instruction.
104
if (likely)
105
js.compilerPC += 4;
106
return;
107
}
108
109
// Branch taken. Always compile the delay slot, and then go to dest.
110
CompileDelaySlot(DELAYSLOT_NICE);
111
AddContinuedBlock(targetAddr);
112
// Account for the increment in the loop.
113
js.compilerPC = targetAddr - 4;
114
// In case the delay slot was a break or something.
115
js.compiling = true;
116
return;
117
}
118
119
js.downcountAmount += MIPSGetInstructionCycleEstimate(branchInfo.delaySlotOp);
120
121
u32 notTakenTarget = ResolveNotTakenTarget(branchInfo);
122
if (immBranch) {
123
// Continuing is handled above, this is just static jumping.
124
if (immBranchTaken || !likely)
125
CompileDelaySlot(DELAYSLOT_FLUSH);
126
else
127
FlushAll();
128
129
const u32 destAddr = immBranchTaken ? targetAddr : notTakenTarget;
130
WriteExit(destAddr, js.nextExit++);
131
} else {
132
if (!likely && branchInfo.delaySlotIsNice && !branchInfo.delaySlotIsBranch)
133
CompileDelaySlot(DELAYSLOT_NICE);
134
135
// We might be able to flip the condition (EQ/NEQ are easy.)
136
const bool canFlip = cc == CC_EQ || cc == CC_NEQ;
137
138
Operand2 op2;
139
bool negated;
140
if (gpr.IsImm(rt) && TryMakeOperand2_AllowNegation(gpr.GetImm(rt), op2, &negated)) {
141
gpr.MapReg(rs);
142
if (!negated)
143
CMP(gpr.R(rs), op2);
144
else
145
CMN(gpr.R(rs), op2);
146
} else {
147
if (gpr.IsImm(rs) && TryMakeOperand2_AllowNegation(gpr.GetImm(rs), op2, &negated) && canFlip) {
148
gpr.MapReg(rt);
149
if (!negated)
150
CMP(gpr.R(rt), op2);
151
else
152
CMN(gpr.R(rt), op2);
153
} else {
154
gpr.MapInIn(rs, rt);
155
CMP(gpr.R(rs), gpr.R(rt));
156
}
157
}
158
159
ArmGen::FixupBranch ptr;
160
if (!likely) {
161
if (!branchInfo.delaySlotIsNice && !branchInfo.delaySlotIsBranch)
162
CompileDelaySlot(DELAYSLOT_SAFE_FLUSH);
163
else
164
FlushAll();
165
ptr = B_CC(cc);
166
} else {
167
FlushAll();
168
ptr = B_CC(cc);
169
if (!branchInfo.delaySlotIsBranch)
170
CompileDelaySlot(DELAYSLOT_FLUSH);
171
}
172
173
if (branchInfo.delaySlotIsBranch) {
174
// We still link when the branch is taken (targetAddr case.)
175
// Remember, it's from the perspective of the delay slot, so +12.
176
if ((branchInfo.delaySlotInfo & OUT_RA) != 0)
177
gpr.SetImm(MIPS_REG_RA, GetCompilerPC() + 12);
178
if ((branchInfo.delaySlotInfo & OUT_RD) != 0)
179
gpr.SetImm(MIPS_GET_RD(branchInfo.delaySlotOp), GetCompilerPC() + 12);
180
FlushAll();
181
}
182
183
// Take the branch
184
WriteExit(targetAddr, js.nextExit++);
185
186
SetJumpTarget(ptr);
187
// Not taken
188
WriteExit(notTakenTarget, js.nextExit++);
189
}
190
191
js.compiling = false;
192
}
193
194
195
void ArmJit::BranchRSZeroComp(MIPSOpcode op, CCFlags cc, bool andLink, bool likely)
196
{
197
if (js.inDelaySlot) {
198
ERROR_LOG_REPORT(Log::JIT, "Branch in RSZeroComp delay slot at %08x in block starting at %08x", GetCompilerPC(), js.blockStart);
199
return;
200
}
201
int offset = TARGET16;
202
MIPSGPReg rs = _RS;
203
u32 targetAddr = GetCompilerPC() + offset + 4;
204
205
BranchInfo branchInfo(GetCompilerPC(), op, GetOffsetInstruction(1), andLink, likely);
206
branchInfo.delaySlotIsNice = IsDelaySlotNiceReg(op, branchInfo.delaySlotOp, rs);
207
CONDITIONAL_NICE_DELAYSLOT;
208
209
bool immBranch = false;
210
bool immBranchTaken = false;
211
if (gpr.IsImm(rs) && !branchInfo.delaySlotIsBranch) {
212
// The cc flags are opposites: when NOT to take the branch.
213
bool immBranchNotTaken;
214
s32 imm = (s32)gpr.GetImm(rs);
215
216
switch (cc)
217
{
218
case CC_GT: immBranchNotTaken = imm > 0; break;
219
case CC_GE: immBranchNotTaken = imm >= 0; break;
220
case CC_LT: immBranchNotTaken = imm < 0; break;
221
case CC_LE: immBranchNotTaken = imm <= 0; break;
222
default: immBranchNotTaken = false; _dbg_assert_msg_(false, "Bad cc flag in BranchRSZeroComp().");
223
}
224
immBranch = true;
225
immBranchTaken = !immBranchNotTaken;
226
}
227
228
if (jo.immBranches && immBranch && js.numInstructions < jo.continueMaxInstructions) {
229
if (!immBranchTaken) {
230
// Skip the delay slot if likely, otherwise it'll be the next instruction.
231
if (andLink)
232
gpr.SetImm(MIPS_REG_RA, GetCompilerPC() + 8);
233
if (likely)
234
js.compilerPC += 4;
235
return;
236
}
237
238
// Branch taken. Always compile the delay slot, and then go to dest.
239
if (andLink)
240
gpr.SetImm(MIPS_REG_RA, GetCompilerPC() + 8);
241
CompileDelaySlot(DELAYSLOT_NICE);
242
243
AddContinuedBlock(targetAddr);
244
// Account for the increment in the loop.
245
js.compilerPC = targetAddr - 4;
246
// In case the delay slot was a break or something.
247
js.compiling = true;
248
return;
249
}
250
251
js.downcountAmount += MIPSGetInstructionCycleEstimate(branchInfo.delaySlotOp);
252
253
u32 notTakenTarget = ResolveNotTakenTarget(branchInfo);
254
if (immBranch) {
255
// Continuing is handled above, this is just static jumping.
256
if (andLink)
257
gpr.SetImm(MIPS_REG_RA, GetCompilerPC() + 8);
258
if (immBranchTaken || !likely)
259
CompileDelaySlot(DELAYSLOT_FLUSH);
260
else
261
FlushAll();
262
263
const u32 destAddr = immBranchTaken ? targetAddr : notTakenTarget;
264
WriteExit(destAddr, js.nextExit++);
265
} else {
266
if (!likely && branchInfo.delaySlotIsNice && !branchInfo.delaySlotIsBranch)
267
CompileDelaySlot(DELAYSLOT_NICE);
268
269
gpr.MapReg(rs);
270
CMP(gpr.R(rs), Operand2(0, TYPE_IMM));
271
272
if (andLink)
273
gpr.SetImm(MIPS_REG_RA, GetCompilerPC() + 8);
274
275
ArmGen::FixupBranch ptr;
276
if (!likely)
277
{
278
if (!branchInfo.delaySlotIsNice && !branchInfo.delaySlotIsBranch)
279
CompileDelaySlot(DELAYSLOT_SAFE_FLUSH);
280
else
281
FlushAll();
282
ptr = B_CC(cc);
283
}
284
else
285
{
286
FlushAll();
287
ptr = B_CC(cc);
288
if (!branchInfo.delaySlotIsBranch)
289
CompileDelaySlot(DELAYSLOT_FLUSH);
290
}
291
292
if (branchInfo.delaySlotIsBranch) {
293
// We still link when the branch is taken (targetAddr case.)
294
// Remember, it's from the perspective of the delay slot, so +12.
295
if ((branchInfo.delaySlotInfo & OUT_RA) != 0)
296
gpr.SetImm(MIPS_REG_RA, GetCompilerPC() + 12);
297
if ((branchInfo.delaySlotInfo & OUT_RD) != 0)
298
gpr.SetImm(MIPS_GET_RD(branchInfo.delaySlotOp), GetCompilerPC() + 12);
299
FlushAll();
300
}
301
302
// Take the branch
303
WriteExit(targetAddr, js.nextExit++);
304
305
SetJumpTarget(ptr);
306
// Not taken
307
WriteExit(notTakenTarget, js.nextExit++);
308
}
309
js.compiling = false;
310
}
311
312
313
void ArmJit::Comp_RelBranch(MIPSOpcode op)
314
{
315
// The CC flags here should be opposite of the actual branch becuase they skip the branching action.
316
switch (op >> 26)
317
{
318
case 4: BranchRSRTComp(op, CC_NEQ, false); break;//beq
319
case 5: BranchRSRTComp(op, CC_EQ, false); break;//bne
320
321
case 6: BranchRSZeroComp(op, CC_GT, false, false); break;//blez
322
case 7: BranchRSZeroComp(op, CC_LE, false, false); break;//bgtz
323
324
case 20: BranchRSRTComp(op, CC_NEQ, true); break;//beql
325
case 21: BranchRSRTComp(op, CC_EQ, true); break;//bnel
326
327
case 22: BranchRSZeroComp(op, CC_GT, false, true); break;//blezl
328
case 23: BranchRSZeroComp(op, CC_LE, false, true); break;//bgtzl
329
330
default:
331
_dbg_assert_msg_(false,"Trying to compile instruction that can't be compiled");
332
break;
333
}
334
}
335
336
void ArmJit::Comp_RelBranchRI(MIPSOpcode op)
337
{
338
switch ((op >> 16) & 0x1F)
339
{
340
case 0: BranchRSZeroComp(op, CC_GE, false, false); break; //if ((s32)R(rs) < 0) DelayBranchTo(addr); else PC += 4; break;//bltz
341
case 1: BranchRSZeroComp(op, CC_LT, false, false); break; //if ((s32)R(rs) >= 0) DelayBranchTo(addr); else PC += 4; break;//bgez
342
case 2: BranchRSZeroComp(op, CC_GE, false, true); break; //if ((s32)R(rs) < 0) DelayBranchTo(addr); else PC += 8; break;//bltzl
343
case 3: BranchRSZeroComp(op, CC_LT, false, true); break; //if ((s32)R(rs) >= 0) DelayBranchTo(addr); else PC += 8; break;//bgezl
344
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
345
case 17: BranchRSZeroComp(op, CC_LT, true, false); break; //R(MIPS_REG_RA) = PC + 8; if ((s32)R(rs) >= 0) DelayBranchTo(addr); else PC += 4; break;//bgezal
346
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
347
case 19: BranchRSZeroComp(op, CC_LT, true, true); break; //R(MIPS_REG_RA) = PC + 8; if ((s32)R(rs) >= 0) DelayBranchTo(addr); else SkipLikely(); break;//bgezall
348
default:
349
_dbg_assert_msg_(false,"Trying to compile instruction that can't be compiled");
350
break;
351
}
352
}
353
354
// If likely is set, discard the branch slot if NOT taken.
355
void ArmJit::BranchFPFlag(MIPSOpcode op, CCFlags cc, bool likely)
356
{
357
if (js.inDelaySlot) {
358
ERROR_LOG_REPORT(Log::JIT, "Branch in FPFlag delay slot at %08x in block starting at %08x", GetCompilerPC(), js.blockStart);
359
return;
360
}
361
int offset = TARGET16;
362
u32 targetAddr = GetCompilerPC() + offset + 4;
363
364
BranchInfo branchInfo(GetCompilerPC(), op, GetOffsetInstruction(1), false, likely);
365
branchInfo.delaySlotIsNice = IsDelaySlotNiceFPU(op, branchInfo.delaySlotOp);
366
CONDITIONAL_NICE_DELAYSLOT;
367
368
js.downcountAmount += MIPSGetInstructionCycleEstimate(branchInfo.delaySlotOp);
369
if (!likely && branchInfo.delaySlotIsNice && !branchInfo.delaySlotIsBranch)
370
CompileDelaySlot(DELAYSLOT_NICE);
371
372
gpr.MapReg(MIPS_REG_FPCOND);
373
TST(gpr.R(MIPS_REG_FPCOND), Operand2(1, TYPE_IMM));
374
375
ArmGen::FixupBranch ptr;
376
if (!likely)
377
{
378
if (!branchInfo.delaySlotIsNice && !branchInfo.delaySlotIsBranch)
379
CompileDelaySlot(DELAYSLOT_SAFE_FLUSH);
380
else
381
FlushAll();
382
ptr = B_CC(cc);
383
}
384
else
385
{
386
FlushAll();
387
ptr = B_CC(cc);
388
if (!branchInfo.delaySlotIsBranch)
389
CompileDelaySlot(DELAYSLOT_FLUSH);
390
}
391
392
if (branchInfo.delaySlotIsBranch) {
393
// We still link when the branch is taken (targetAddr case.)
394
// Remember, it's from the perspective of the delay slot, so +12.
395
if ((branchInfo.delaySlotInfo & OUT_RA) != 0)
396
gpr.SetImm(MIPS_REG_RA, GetCompilerPC() + 12);
397
if ((branchInfo.delaySlotInfo & OUT_RD) != 0)
398
gpr.SetImm(MIPS_GET_RD(branchInfo.delaySlotOp), GetCompilerPC() + 12);
399
FlushAll();
400
}
401
402
// Take the branch
403
WriteExit(targetAddr, js.nextExit++);
404
405
SetJumpTarget(ptr);
406
// Not taken
407
WriteExit(ResolveNotTakenTarget(branchInfo), js.nextExit++);
408
js.compiling = false;
409
}
410
411
void ArmJit::Comp_FPUBranch(MIPSOpcode op)
412
{
413
switch((op >> 16) & 0x1f)
414
{
415
case 0: BranchFPFlag(op, CC_NEQ, false); break; // bc1f
416
case 1: BranchFPFlag(op, CC_EQ, false); break; // bc1t
417
case 2: BranchFPFlag(op, CC_NEQ, true); break; // bc1fl
418
case 3: BranchFPFlag(op, CC_EQ, true); break; // bc1tl
419
default:
420
_dbg_assert_msg_(false,"Trying to interpret instruction that can't be interpreted");
421
break;
422
}
423
}
424
425
// If likely is set, discard the branch slot if NOT taken.
426
void ArmJit::BranchVFPUFlag(MIPSOpcode op, CCFlags cc, bool likely)
427
{
428
if (js.inDelaySlot) {
429
ERROR_LOG_REPORT(Log::JIT, "Branch in VFPU delay slot at %08x in block starting at %08x", GetCompilerPC(), js.blockStart);
430
return;
431
}
432
int offset = TARGET16;
433
u32 targetAddr = GetCompilerPC() + offset + 4;
434
435
BranchInfo branchInfo(GetCompilerPC(), op, GetOffsetInstruction(1), false, likely);
436
// Sometimes there's a VFPU branch in a delay slot (Disgaea 2: Dark Hero Days, Zettai Hero Project, La Pucelle)
437
// The behavior is undefined - the CPU may take the second branch even if the first one passes.
438
// However, it does consistently try each branch, which these games seem to expect.
439
branchInfo.delaySlotIsNice = IsDelaySlotNiceVFPU(op, branchInfo.delaySlotOp);
440
CONDITIONAL_NICE_DELAYSLOT;
441
442
js.downcountAmount += MIPSGetInstructionCycleEstimate(branchInfo.delaySlotOp);
443
if (!likely && branchInfo.delaySlotIsNice)
444
CompileDelaySlot(DELAYSLOT_NICE);
445
446
int imm3 = (op >> 18) & 7;
447
448
gpr.MapReg(MIPS_REG_VFPUCC);
449
TST(gpr.R(MIPS_REG_VFPUCC), Operand2(1 << imm3, TYPE_IMM));
450
451
ArmGen::FixupBranch ptr;
452
js.inDelaySlot = true;
453
if (!likely)
454
{
455
if (!branchInfo.delaySlotIsNice && !branchInfo.delaySlotIsBranch)
456
CompileDelaySlot(DELAYSLOT_SAFE_FLUSH);
457
else
458
FlushAll();
459
ptr = B_CC(cc);
460
}
461
else
462
{
463
FlushAll();
464
ptr = B_CC(cc);
465
if (!branchInfo.delaySlotIsBranch)
466
CompileDelaySlot(DELAYSLOT_FLUSH);
467
}
468
js.inDelaySlot = false;
469
470
if (branchInfo.delaySlotIsBranch) {
471
// We still link when the branch is taken (targetAddr case.)
472
// Remember, it's from the perspective of the delay slot, so +12.
473
if ((branchInfo.delaySlotInfo & OUT_RA) != 0)
474
gpr.SetImm(MIPS_REG_RA, GetCompilerPC() + 12);
475
if ((branchInfo.delaySlotInfo & OUT_RD) != 0)
476
gpr.SetImm(MIPS_GET_RD(branchInfo.delaySlotOp), GetCompilerPC() + 12);
477
FlushAll();
478
}
479
480
// Take the branch
481
WriteExit(targetAddr, js.nextExit++);
482
483
SetJumpTarget(ptr);
484
// Not taken
485
WriteExit(ResolveNotTakenTarget(branchInfo), js.nextExit++);
486
js.compiling = false;
487
}
488
489
void ArmJit::Comp_VBranch(MIPSOpcode op)
490
{
491
switch ((op >> 16) & 3)
492
{
493
case 0: BranchVFPUFlag(op, CC_NEQ, false); break; // bvf
494
case 1: BranchVFPUFlag(op, CC_EQ, false); break; // bvt
495
case 2: BranchVFPUFlag(op, CC_NEQ, true); break; // bvfl
496
case 3: BranchVFPUFlag(op, CC_EQ, true); break; // bvtl
497
}
498
}
499
500
static void HitInvalidJump(uint32_t dest) {
501
Core_ExecException(dest, currentMIPS->pc - 8, ExecExceptionType::JUMP);
502
}
503
504
void ArmJit::Comp_Jump(MIPSOpcode op) {
505
if (js.inDelaySlot) {
506
ERROR_LOG_REPORT(Log::JIT, "Branch in Jump delay slot at %08x in block starting at %08x", GetCompilerPC(), js.blockStart);
507
return;
508
}
509
u32 off = TARGET26;
510
u32 targetAddr = (GetCompilerPC() & 0xF0000000) | off;
511
512
// Might be a stubbed address or something?
513
if (!Memory::IsValidAddress(targetAddr) || (targetAddr & 3) != 0) {
514
if (js.nextExit == 0) {
515
ERROR_LOG_REPORT(Log::JIT, "Jump to invalid address: %08x", targetAddr);
516
} else {
517
js.compiling = false;
518
}
519
// TODO: Mark this block dirty or something? May be indication it will be changed by imports.
520
CompileDelaySlot(DELAYSLOT_NICE);
521
FlushAll();
522
gpr.SetRegImm(SCRATCHREG1, GetCompilerPC() + 8);
523
MovToPC(SCRATCHREG1);
524
MOVI2R(R0, targetAddr);
525
QuickCallFunction(SCRATCHREG2, &HitInvalidJump);
526
WriteSyscallExit();
527
return;
528
}
529
530
switch (op >> 26) {
531
case 2: //j
532
CompileDelaySlot(DELAYSLOT_NICE);
533
if (jo.continueJumps && js.numInstructions < jo.continueMaxInstructions) {
534
AddContinuedBlock(targetAddr);
535
// Account for the increment in the loop.
536
js.compilerPC = targetAddr - 4;
537
// In case the delay slot was a break or something.
538
js.compiling = true;
539
return;
540
}
541
FlushAll();
542
WriteExit(targetAddr, js.nextExit++);
543
break;
544
545
case 3: //jal
546
if (ReplaceJalTo(targetAddr))
547
return;
548
549
gpr.SetImm(MIPS_REG_RA, GetCompilerPC() + 8);
550
CompileDelaySlot(DELAYSLOT_NICE);
551
if (jo.continueJumps && js.numInstructions < jo.continueMaxInstructions) {
552
AddContinuedBlock(targetAddr);
553
// Account for the increment in the loop.
554
js.compilerPC = targetAddr - 4;
555
// In case the delay slot was a break or something.
556
js.compiling = true;
557
return;
558
}
559
FlushAll();
560
WriteExit(targetAddr, js.nextExit++);
561
break;
562
563
default:
564
_dbg_assert_msg_(false,"Trying to compile instruction that can't be compiled");
565
break;
566
}
567
js.compiling = false;
568
}
569
570
void ArmJit::Comp_JumpReg(MIPSOpcode op)
571
{
572
if (js.inDelaySlot) {
573
ERROR_LOG_REPORT(Log::JIT, "Branch in JumpReg delay slot at %08x in block starting at %08x", GetCompilerPC(), js.blockStart);
574
return;
575
}
576
MIPSGPReg rs = _RS;
577
MIPSGPReg rd = _RD;
578
bool andLink = (op & 0x3f) == 9 && rd != MIPS_REG_ZERO;
579
580
MIPSOpcode delaySlotOp = GetOffsetInstruction(1);
581
js.downcountAmount += MIPSGetInstructionCycleEstimate(delaySlotOp);
582
bool delaySlotIsNice = IsDelaySlotNiceReg(op, delaySlotOp, rs);
583
if (andLink && rs == rd)
584
delaySlotIsNice = false;
585
CONDITIONAL_NICE_DELAYSLOT;
586
587
ARMReg destReg = R8;
588
if (IsSyscall(delaySlotOp)) {
589
gpr.MapReg(rs);
590
MovToPC(gpr.R(rs)); // For syscall to be able to return.
591
if (andLink)
592
gpr.SetImm(rd, GetCompilerPC() + 8);
593
CompileDelaySlot(DELAYSLOT_FLUSH);
594
return; // Syscall wrote exit code.
595
} else if (delaySlotIsNice) {
596
if (andLink)
597
gpr.SetImm(rd, GetCompilerPC() + 8);
598
CompileDelaySlot(DELAYSLOT_NICE);
599
600
if (!andLink && rs == MIPS_REG_RA && g_Config.bDiscardRegsOnJRRA) {
601
// According to the MIPS ABI, there are some regs we don't need to preserve.
602
// Let's discard them so we don't need to write them back.
603
// NOTE: Not all games follow the MIPS ABI! Tekken 6, for example, will crash
604
// with this enabled.
605
gpr.DiscardR(MIPS_REG_COMPILER_SCRATCH);
606
for (int i = MIPS_REG_A0; i <= MIPS_REG_T7; i++)
607
gpr.DiscardR((MIPSGPReg)i);
608
gpr.DiscardR(MIPS_REG_T8);
609
gpr.DiscardR(MIPS_REG_T9);
610
}
611
612
if (jo.continueJumps && gpr.IsImm(rs) && js.numInstructions < jo.continueMaxInstructions) {
613
AddContinuedBlock(gpr.GetImm(rs));
614
// Account for the increment in the loop.
615
js.compilerPC = gpr.GetImm(rs) - 4;
616
// In case the delay slot was a break or something.
617
js.compiling = true;
618
return;
619
}
620
621
gpr.MapReg(rs);
622
destReg = gpr.R(rs); // Safe because FlushAll doesn't change any regs
623
FlushAll();
624
} else {
625
// Delay slot - this case is very rare, might be able to free up R8.
626
gpr.MapReg(rs);
627
MOV(R8, gpr.R(rs));
628
if (andLink)
629
gpr.SetImm(rd, GetCompilerPC() + 8);
630
CompileDelaySlot(DELAYSLOT_NICE);
631
FlushAll();
632
}
633
634
switch (op & 0x3f)
635
{
636
case 8: //jr
637
break;
638
case 9: //jalr
639
break;
640
default:
641
_dbg_assert_msg_(false,"Trying to compile instruction that can't be compiled");
642
break;
643
}
644
645
WriteExitDestInR(destReg);
646
js.compiling = false;
647
}
648
649
650
void ArmJit::Comp_Syscall(MIPSOpcode op)
651
{
652
if (op.encoding == 0x03FFFFcc) {
653
WARN_LOG(Log::JIT, "Encountered bad syscall instruction at %08x (%08x)", js.compilerPC, op.encoding);
654
}
655
656
if (!g_Config.bSkipDeadbeefFilling)
657
{
658
// All of these will be overwritten with DEADBEEF anyway.
659
gpr.DiscardR(MIPS_REG_COMPILER_SCRATCH);
660
// We need to keep A0 - T3, which are used for args.
661
gpr.DiscardR(MIPS_REG_T4);
662
gpr.DiscardR(MIPS_REG_T5);
663
gpr.DiscardR(MIPS_REG_T6);
664
gpr.DiscardR(MIPS_REG_T7);
665
gpr.DiscardR(MIPS_REG_T8);
666
gpr.DiscardR(MIPS_REG_T9);
667
668
gpr.DiscardR(MIPS_REG_HI);
669
gpr.DiscardR(MIPS_REG_LO);
670
}
671
672
// If we're in a delay slot, this is off by one.
673
const int offset = js.inDelaySlot ? -1 : 0;
674
WriteDownCount(offset);
675
RestoreRoundingMode();
676
js.downcountAmount = -offset;
677
678
if (!js.inDelaySlot) {
679
gpr.SetRegImm(SCRATCHREG1, GetCompilerPC() + 4);
680
MovToPC(SCRATCHREG1);
681
}
682
683
FlushAll();
684
685
SaveDowncount();
686
#ifdef USE_PROFILER
687
// When profiling, we can't skip CallSyscall, since it times syscalls.
688
gpr.SetRegImm(R0, op.encoding);
689
QuickCallFunction(R1, (void *)&CallSyscall);
690
#else
691
// Skip the CallSyscall where possible.
692
void *quickFunc = GetQuickSyscallFunc(op);
693
if (quickFunc)
694
{
695
gpr.SetRegImm(R0, (u32)(intptr_t)GetSyscallFuncPointer(op));
696
// Already flushed, so R1 is safe.
697
QuickCallFunction(R1, quickFunc);
698
}
699
else
700
{
701
gpr.SetRegImm(R0, op.encoding);
702
QuickCallFunction(R1, (void *)&CallSyscall);
703
}
704
#endif
705
ApplyRoundingMode();
706
RestoreDowncount();
707
708
WriteSyscallExit();
709
js.compiling = false;
710
}
711
712
void ArmJit::Comp_Break(MIPSOpcode op)
713
{
714
Comp_Generic(op);
715
WriteSyscallExit();
716
js.compiling = false;
717
}
718
719
} // namespace Mipscomp
720
721
#endif // PPSSPP_ARCH(ARM)
722
723