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/Jit.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 <algorithm>
22
#include <iterator>
23
24
#include "Common/Math/math_util.h"
25
#include "Common/Profiler/Profiler.h"
26
27
#include "Common/Serialize/Serializer.h"
28
#include "Common/Serialize/SerializeFuncs.h"
29
#include "Core/Core.h"
30
#include "Core/MemMap.h"
31
#include "Core/System.h"
32
#include "Core/CoreTiming.h"
33
#include "Core/Config.h"
34
#include "Core/Reporting.h"
35
#include "Core/Debugger/SymbolMap.h"
36
#include "Core/MIPS/MIPS.h"
37
#include "Core/MIPS/MIPSAnalyst.h"
38
#include "Core/MIPS/MIPSCodeUtils.h"
39
#include "Core/MIPS/MIPSInt.h"
40
#include "Core/MIPS/MIPSTables.h"
41
#include "Core/HLE/ReplaceTables.h"
42
43
#include "RegCache.h"
44
#include "Jit.h"
45
46
#include "Core/Debugger/Breakpoints.h"
47
48
namespace MIPSComp
49
{
50
using namespace Gen;
51
52
const bool USE_JIT_MISSMAP = false;
53
static std::map<std::string, u32> notJitOps;
54
55
template<typename A, typename B>
56
std::pair<B,A> flip_pair(const std::pair<A,B> &p) {
57
return std::pair<B, A>(p.second, p.first);
58
}
59
60
// This is called when Jit hits a breakpoint. Returns 1 when hit.
61
u32 JitBreakpoint(uint32_t addr)
62
{
63
// Should we skip this breakpoint?
64
if (CBreakPoints::CheckSkipFirst() == currentMIPS->pc || CBreakPoints::CheckSkipFirst() == addr)
65
return 0;
66
67
BreakAction result = CBreakPoints::ExecBreakPoint(addr);
68
if ((result & BREAK_ACTION_PAUSE) == 0)
69
return 0;
70
71
// There's probably a better place for this.
72
if (USE_JIT_MISSMAP) {
73
std::map<u32, std::string> notJitSorted;
74
std::transform(notJitOps.begin(), notJitOps.end(), std::inserter(notJitSorted, notJitSorted.begin()), flip_pair<std::string, u32>);
75
76
std::string message;
77
char temp[256];
78
int remaining = 15;
79
for (auto it = notJitSorted.rbegin(), end = notJitSorted.rend(); it != end && --remaining >= 0; ++it)
80
{
81
snprintf(temp, 256, " (%d), ", it->first);
82
message += it->second + temp;
83
}
84
85
if (message.size() > 2)
86
message.resize(message.size() - 2);
87
88
NOTICE_LOG(Log::JIT, "Top ops compiled to interpreter: %s", message.c_str());
89
}
90
91
return 1;
92
}
93
94
static u32 JitMemCheck(u32 addr, u32 pc) {
95
// Should we skip this breakpoint?
96
if (CBreakPoints::CheckSkipFirst() == currentMIPS->pc)
97
return 0;
98
99
// Did we already hit one?
100
if (coreState != CORE_RUNNING && coreState != CORE_NEXTFRAME)
101
return 1;
102
103
// Note: pc may be the delay slot.
104
CBreakPoints::ExecOpMemCheck(addr, pc);
105
return coreState == CORE_RUNNING || coreState == CORE_NEXTFRAME ? 0 : 1;
106
}
107
108
static void JitLogMiss(MIPSOpcode op)
109
{
110
if (USE_JIT_MISSMAP)
111
notJitOps[MIPSGetName(op)]++;
112
113
MIPSInterpretFunc func = MIPSGetInterpretFunc(op);
114
func(op);
115
}
116
117
#ifdef _MSC_VER
118
// JitBlockCache doesn't use this, just stores it.
119
#pragma warning(disable:4355)
120
#endif
121
Jit::Jit(MIPSState *mipsState)
122
: blocks(mipsState, this), mips_(mipsState) {
123
blocks.Init();
124
gpr.SetEmitter(this);
125
fpr.SetEmitter(this);
126
AllocCodeSpace(1024 * 1024 * 16);
127
GenerateFixedCode(jo);
128
129
safeMemFuncs.Init(&thunks);
130
131
js.startDefaultPrefix = mips_->HasDefaultPrefix();
132
133
// The debugger sets this so that "go" on a breakpoint will actually... go.
134
// But if they reset, we can end up hitting it by mistake, since it's based on PC and ticks.
135
CBreakPoints::SetSkipFirst(0);
136
}
137
138
Jit::~Jit() {
139
}
140
141
void Jit::DoState(PointerWrap &p) {
142
auto s = p.Section("Jit", 1, 2);
143
if (!s)
144
return;
145
146
Do(p, js.startDefaultPrefix);
147
if (p.mode == PointerWrap::MODE_READ && !js.startDefaultPrefix) {
148
WARN_LOG(Log::CPU, "Jit: An uneaten prefix was previously detected. Jitting in unknown-prefix mode.");
149
}
150
if (s >= 2) {
151
Do(p, js.hasSetRounding);
152
if (p.mode == PointerWrap::MODE_READ) {
153
js.lastSetRounding = 0;
154
}
155
} else {
156
js.hasSetRounding = 1;
157
}
158
159
// The debugger sets this so that "go" on a breakpoint will actually... go.
160
// But if they load a state, we can end up hitting it by mistake, since it's based on PC and ticks.
161
CBreakPoints::SetSkipFirst(0);
162
}
163
164
void Jit::UpdateFCR31() {
165
}
166
167
void Jit::GetStateAndFlushAll(RegCacheState &state) {
168
gpr.GetState(state.gpr);
169
fpr.GetState(state.fpr);
170
FlushAll();
171
}
172
173
void Jit::RestoreState(const RegCacheState& state) {
174
gpr.RestoreState(state.gpr);
175
fpr.RestoreState(state.fpr);
176
}
177
178
void Jit::FlushAll() {
179
gpr.Flush();
180
fpr.Flush();
181
FlushPrefixV();
182
}
183
184
void Jit::FlushPrefixV() {
185
if (js.startDefaultPrefix && !js.blockWrotePrefixes && js.HasNoPrefix()) {
186
// They started default, we never modified in memory, and they're default now.
187
// No reason to modify memory. This is common at end of blocks. Just clear dirty.
188
js.prefixSFlag = (JitState::PrefixState)(js.prefixSFlag & ~JitState::PREFIX_DIRTY);
189
js.prefixTFlag = (JitState::PrefixState)(js.prefixTFlag & ~JitState::PREFIX_DIRTY);
190
js.prefixDFlag = (JitState::PrefixState)(js.prefixDFlag & ~JitState::PREFIX_DIRTY);
191
return;
192
}
193
194
if ((js.prefixSFlag & JitState::PREFIX_DIRTY) != 0) {
195
MOV(32, MIPSSTATE_VAR(vfpuCtrl[VFPU_CTRL_SPREFIX]), Imm32(js.prefixS));
196
js.prefixSFlag = (JitState::PrefixState) (js.prefixSFlag & ~JitState::PREFIX_DIRTY);
197
}
198
199
if ((js.prefixTFlag & JitState::PREFIX_DIRTY) != 0) {
200
MOV(32, MIPSSTATE_VAR(vfpuCtrl[VFPU_CTRL_TPREFIX]), Imm32(js.prefixT));
201
js.prefixTFlag = (JitState::PrefixState) (js.prefixTFlag & ~JitState::PREFIX_DIRTY);
202
}
203
204
if ((js.prefixDFlag & JitState::PREFIX_DIRTY) != 0) {
205
MOV(32, MIPSSTATE_VAR(vfpuCtrl[VFPU_CTRL_DPREFIX]), Imm32(js.prefixD));
206
js.prefixDFlag = (JitState::PrefixState) (js.prefixDFlag & ~JitState::PREFIX_DIRTY);
207
}
208
209
// If we got here, we must've written prefixes to memory in this block.
210
js.blockWrotePrefixes = true;
211
}
212
213
void Jit::WriteDowncount(int offset) {
214
const int downcount = js.downcountAmount + offset;
215
SUB(32, MIPSSTATE_VAR(downcount), downcount > 127 ? Imm32(downcount) : Imm8(downcount));
216
}
217
218
void Jit::RestoreRoundingMode(bool force) {
219
// If the game has never set an interesting rounding mode, we can safely skip this.
220
if (force || js.hasSetRounding) {
221
CALL(restoreRoundingMode);
222
}
223
}
224
225
void Jit::ApplyRoundingMode(bool force) {
226
// If the game has never set an interesting rounding mode, we can safely skip this.
227
if (force || js.hasSetRounding) {
228
CALL(applyRoundingMode);
229
}
230
}
231
232
void Jit::UpdateRoundingMode(u32 fcr31) {
233
// We must set js.hasSetRounding at compile time, or this block will use the wrong rounding mode.
234
// The fcr31 parameter is -1 when not known at compile time, so we just assume it was changed.
235
if (fcr31 & 0x01000003) {
236
js.hasSetRounding = true;
237
}
238
}
239
240
void Jit::ClearCache()
241
{
242
blocks.Clear();
243
ClearCodeSpace(0);
244
GenerateFixedCode(jo);
245
}
246
247
void Jit::SaveFlags() {
248
PUSHF();
249
#if PPSSPP_ARCH(AMD64)
250
// On X64, the above misaligns the stack. However there might be a cheaper solution than this.
251
POP(64, R(EAX));
252
MOV(64, MIPSSTATE_VAR(saved_flags), R(EAX));
253
#endif
254
}
255
256
void Jit::LoadFlags() {
257
#if PPSSPP_ARCH(AMD64)
258
MOV(64, R(EAX), MIPSSTATE_VAR(saved_flags));
259
PUSH(64, R(EAX));
260
#endif
261
POPF();
262
}
263
264
void Jit::CompileDelaySlot(int flags, RegCacheState *state) {
265
// Need to offset the downcount which was already incremented for the branch + delay slot.
266
CheckJitBreakpoint(GetCompilerPC() + 4, -2);
267
268
if (flags & DELAYSLOT_SAFE)
269
SaveFlags(); // preserve flag around the delay slot!
270
271
js.inDelaySlot = true;
272
MIPSOpcode op = GetOffsetInstruction(1);
273
MIPSCompileOp(op, this);
274
js.inDelaySlot = false;
275
276
if (flags & DELAYSLOT_FLUSH) {
277
if (state != NULL)
278
GetStateAndFlushAll(*state);
279
else
280
FlushAll();
281
}
282
if (flags & DELAYSLOT_SAFE)
283
LoadFlags(); // restore flag!
284
}
285
286
void Jit::EatInstruction(MIPSOpcode op) {
287
MIPSInfo info = MIPSGetInfo(op);
288
if (info & DELAYSLOT) {
289
ERROR_LOG_REPORT_ONCE(ateDelaySlot, Log::JIT, "Ate a branch op.");
290
}
291
if (js.inDelaySlot) {
292
ERROR_LOG_REPORT_ONCE(ateInDelaySlot, Log::JIT, "Ate an instruction inside a delay slot.");
293
}
294
295
CheckJitBreakpoint(GetCompilerPC() + 4, 0);
296
js.numInstructions++;
297
js.compilerPC += 4;
298
js.downcountAmount += MIPSGetInstructionCycleEstimate(op);
299
}
300
301
void Jit::Compile(u32 em_address) {
302
PROFILE_THIS_SCOPE("jitc");
303
if (GetSpaceLeft() < 0x10000 || blocks.IsFull()) {
304
ClearCache();
305
}
306
307
if (!Memory::IsValidAddress(em_address) || (em_address & 3) != 0) {
308
Core_ExecException(em_address, em_address, ExecExceptionType::JUMP);
309
return;
310
}
311
312
// Sometimes we compile fairly large blocks, although it's uncommon.
313
BeginWrite(JitBlockCache::MAX_BLOCK_INSTRUCTIONS * 16);
314
315
int block_num = blocks.AllocateBlock(em_address);
316
JitBlock *b = blocks.GetBlock(block_num);
317
DoJit(em_address, b);
318
_assert_msg_(b->originalAddress == em_address, "original %08x != em_address %08x (block %d)", b->originalAddress, em_address, b->blockNum);
319
blocks.FinalizeBlock(block_num, jo.enableBlocklink);
320
321
EndWrite();
322
323
bool cleanSlate = false;
324
325
if (js.hasSetRounding && !js.lastSetRounding) {
326
WARN_LOG(Log::JIT, "Detected rounding mode usage, rebuilding jit with checks");
327
// Won't loop, since hasSetRounding is only ever set to 1.
328
js.lastSetRounding = js.hasSetRounding;
329
cleanSlate = true;
330
}
331
332
// Drat. The VFPU hit an uneaten prefix at the end of a block.
333
if (js.startDefaultPrefix && js.MayHavePrefix()) {
334
WARN_LOG_REPORT(Log::JIT, "An uneaten prefix at end of block: %08x", GetCompilerPC() - 4);
335
js.LogPrefix();
336
337
// Let's try that one more time. We won't get back here because we toggled the value.
338
js.startDefaultPrefix = false;
339
cleanSlate = true;
340
}
341
342
if (cleanSlate) {
343
// Our assumptions are all wrong so it's clean-slate time.
344
ClearCache();
345
Compile(em_address);
346
}
347
}
348
349
void Jit::RunLoopUntil(u64 globalticks) {
350
PROFILE_THIS_SCOPE("jit");
351
((void (*)())enterDispatcher)();
352
}
353
354
u32 Jit::GetCompilerPC() {
355
return js.compilerPC;
356
}
357
358
MIPSOpcode Jit::GetOffsetInstruction(int offset) {
359
return Memory::Read_Instruction(GetCompilerPC() + 4 * offset);
360
}
361
362
const u8 *Jit::DoJit(u32 em_address, JitBlock *b) {
363
js.cancel = false;
364
js.blockStart = em_address;
365
js.compilerPC = em_address;
366
js.lastContinuedPC = 0;
367
js.initialBlockSize = 0;
368
js.nextExit = 0;
369
js.downcountAmount = 0;
370
js.curBlock = b;
371
js.compiling = true;
372
js.inDelaySlot = false;
373
js.blockWrotePrefixes = false;
374
js.afterOp = JitState::AFTER_NONE;
375
js.PrefixStart();
376
377
// We add a check before the block, used when entering from a linked block.
378
b->checkedEntry = GetCodePtr();
379
// Downcount flag check. The last block decremented downcounter, and the flag should still be available.
380
FixupBranch skip = J_CC(CC_NS);
381
MOV(32, MIPSSTATE_VAR(pc), Imm32(js.blockStart));
382
JMP(outerLoop, true); // downcount hit zero - go advance.
383
SetJumpTarget(skip);
384
385
b->normalEntry = GetCodePtr();
386
387
MIPSAnalyst::AnalysisResults analysis = MIPSAnalyst::Analyze(em_address);
388
389
gpr.Start(mips_, &js, &jo, analysis);
390
fpr.Start(mips_, &js, &jo, analysis, RipAccessible(&mips_->v[0]));
391
392
js.numInstructions = 0;
393
while (js.compiling) {
394
// Jit breakpoints are quite fast, so let's do them in release too.
395
CheckJitBreakpoint(GetCompilerPC(), 0);
396
397
MIPSOpcode inst = Memory::Read_Opcode_JIT(GetCompilerPC());
398
js.downcountAmount += MIPSGetInstructionCycleEstimate(inst);
399
400
MIPSCompileOp(inst, this);
401
402
if (js.afterOp & JitState::AFTER_CORE_STATE) {
403
// CORE_RUNNING is <= CORE_NEXTFRAME.
404
if (RipAccessible((const void *)&coreState)) {
405
CMP(32, M(&coreState), Imm32(CORE_NEXTFRAME)); // rip accessible
406
} else {
407
MOV(PTRBITS, R(RAX), ImmPtr((const void *)&coreState));
408
CMP(32, MatR(RAX), Imm32(CORE_NEXTFRAME));
409
}
410
FixupBranch skipCheck = J_CC(CC_LE, true);
411
// All cases of AFTER_CORE_STATE should update PC. We don't update here.
412
RegCacheState state;
413
GetStateAndFlushAll(state);
414
WriteSyscallExit();
415
416
SetJumpTarget(skipCheck);
417
// If we didn't jump, we can keep our regs as they were.
418
RestoreState(state);
419
420
js.afterOp = JitState::AFTER_NONE;
421
}
422
423
js.compilerPC += 4;
424
js.numInstructions++;
425
426
if (jo.Disabled(JitDisable::REGALLOC_GPR)) {
427
gpr.Flush();
428
}
429
if (jo.Disabled(JitDisable::REGALLOC_FPR)) {
430
fpr.Flush();
431
FlushPrefixV();
432
}
433
434
// Safety check, in case we get a bunch of really large jit ops without a lot of branching.
435
if (GetSpaceLeft() < 0x800 || js.numInstructions >= JitBlockCache::MAX_BLOCK_INSTRUCTIONS) {
436
FlushAll();
437
WriteExit(GetCompilerPC(), js.nextExit++);
438
js.compiling = false;
439
}
440
}
441
442
b->codeSize = (u32)(GetCodePtr() - b->normalEntry);
443
NOP();
444
AlignCode4();
445
if (js.lastContinuedPC == 0) {
446
b->originalSize = js.numInstructions;
447
} else {
448
// We continued at least once. Add the last proxy and set the originalSize correctly.
449
blocks.ProxyBlock(js.blockStart, js.lastContinuedPC, (GetCompilerPC() - js.lastContinuedPC) / sizeof(u32), GetCodePtr());
450
b->originalSize = js.initialBlockSize;
451
}
452
return b->normalEntry;
453
}
454
455
void Jit::AddContinuedBlock(u32 dest) {
456
// The first block is the root block. When we continue, we create proxy blocks after that.
457
if (js.lastContinuedPC == 0)
458
js.initialBlockSize = js.numInstructions;
459
else
460
blocks.ProxyBlock(js.blockStart, js.lastContinuedPC, (GetCompilerPC() - js.lastContinuedPC) / sizeof(u32), GetCodePtr());
461
js.lastContinuedPC = dest;
462
}
463
464
bool Jit::DescribeCodePtr(const u8 *ptr, std::string &name) {
465
if (ptr == applyRoundingMode)
466
name = "applyRoundingMode";
467
else if (ptr == dispatcher)
468
name = "dispatcher";
469
else if (ptr == dispatcherInEAXNoCheck)
470
name = "dispatcher (PC in EAX)";
471
else if (ptr == dispatcherNoCheck)
472
name = "dispatcherNoCheck";
473
else if (ptr == dispatcherCheckCoreState)
474
name = "dispatcherCheckCoreState";
475
else if (ptr == enterDispatcher)
476
name = "enterDispatcher";
477
else if (ptr == restoreRoundingMode)
478
name = "restoreRoundingMode";
479
else if (ptr == crashHandler)
480
name = "crashHandler";
481
else {
482
u32 jitAddr = blocks.GetAddressFromBlockPtr(ptr);
483
484
// Returns 0 when it's valid, but unknown.
485
if (jitAddr == 0) {
486
name = "UnknownOrDeletedBlock";
487
} else if (jitAddr != (u32)-1) {
488
char temp[1024];
489
const std::string label = g_symbolMap ? g_symbolMap->GetDescription(jitAddr) : "";
490
if (!label.empty())
491
snprintf(temp, sizeof(temp), "%08x_%s", jitAddr, label.c_str());
492
else
493
snprintf(temp, sizeof(temp), "%08x", jitAddr);
494
name = temp;
495
} else if (IsInSpace(ptr)) {
496
if (ptr < endOfPregeneratedCode) {
497
name = "PreGenCode";
498
} else {
499
name = "Unknown";
500
}
501
} else if (thunks.IsInSpace(ptr)) {
502
name = "Thunk";
503
} else if (safeMemFuncs.IsInSpace(ptr)) {
504
name = "JitSafeMem";
505
} else {
506
// Not anywhere in jit, then.
507
return false;
508
}
509
}
510
// If we got here, one of the above cases matched.
511
return true;
512
}
513
514
void Jit::Comp_RunBlock(MIPSOpcode op) {
515
// This shouldn't be necessary, the dispatcher should catch us before we get here.
516
ERROR_LOG(Log::JIT, "Comp_RunBlock");
517
}
518
519
void Jit::LinkBlock(u8 *exitPoint, const u8 *checkedEntry) {
520
if (PlatformIsWXExclusive()) {
521
ProtectMemoryPages(exitPoint, 32, MEM_PROT_READ | MEM_PROT_WRITE);
522
}
523
XEmitter emit(exitPoint);
524
// Okay, this is a bit ugly, but we check here if it already has a JMP.
525
// That means it doesn't have a full exit to pad with INT 3.
526
bool prelinked = *emit.GetCodePointer() == 0xE9;
527
emit.JMP(checkedEntry, true);
528
if (!prelinked) {
529
ptrdiff_t actualSize = emit.GetWritableCodePtr() - exitPoint;
530
int pad = JitBlockCache::GetBlockExitSize() - (int)actualSize;
531
for (int i = 0; i < pad; ++i) {
532
emit.INT3();
533
}
534
}
535
if (PlatformIsWXExclusive()) {
536
ProtectMemoryPages(exitPoint, 32, MEM_PROT_READ | MEM_PROT_EXEC);
537
}
538
}
539
540
void Jit::UnlinkBlock(u8 *checkedEntry, u32 originalAddress) {
541
if (PlatformIsWXExclusive()) {
542
ProtectMemoryPages(checkedEntry, 16, MEM_PROT_READ | MEM_PROT_WRITE);
543
}
544
// Send anyone who tries to run this block back to the dispatcher.
545
// Not entirely ideal, but .. pretty good.
546
// Spurious entrances from previously linked blocks can only come through checkedEntry
547
XEmitter emit(checkedEntry);
548
emit.MOV(32, MIPSSTATE_VAR(pc), Imm32(originalAddress));
549
emit.JMP(MIPSComp::jit->GetDispatcher(), true);
550
if (PlatformIsWXExclusive()) {
551
ProtectMemoryPages(checkedEntry, 16, MEM_PROT_READ | MEM_PROT_EXEC);
552
}
553
}
554
555
bool Jit::ReplaceJalTo(u32 dest) {
556
const ReplacementTableEntry *entry = nullptr;
557
u32 funcSize = 0;
558
if (!CanReplaceJalTo(dest, &entry, &funcSize)) {
559
return false;
560
}
561
562
// Warning - this might be bad if the code at the destination changes...
563
if (entry->flags & REPFLAG_ALLOWINLINE) {
564
// Jackpot! Just do it, no flushing. The code will be entirely inlined.
565
566
// First, compile the delay slot. It's unconditional so no issues.
567
CompileDelaySlot(DELAYSLOT_NICE);
568
// Technically, we should write the unused return address to RA, but meh.
569
MIPSReplaceFunc repl = entry->jitReplaceFunc;
570
int cycles = (this->*repl)();
571
js.downcountAmount += cycles;
572
} else {
573
gpr.SetImm(MIPS_REG_RA, GetCompilerPC() + 8);
574
CompileDelaySlot(DELAYSLOT_NICE);
575
FlushAll();
576
MOV(32, MIPSSTATE_VAR(pc), Imm32(GetCompilerPC()));
577
RestoreRoundingMode();
578
ABI_CallFunction(entry->replaceFunc);
579
SUB(32, MIPSSTATE_VAR(downcount), R(EAX));
580
ApplyRoundingMode();
581
}
582
583
js.compilerPC += 4;
584
// No writing exits, keep going!
585
586
if (CBreakPoints::HasMemChecks()) {
587
// We could modify coreState, so we need to write PC and check.
588
// Otherwise, PC may end up on the jal. We add 4 to skip the delay slot.
589
MOV(32, MIPSSTATE_VAR(pc), Imm32(GetCompilerPC() + 4));
590
js.afterOp |= JitState::AFTER_CORE_STATE;
591
}
592
593
// Add a trigger so that if the inlined code changes, we invalidate this block.
594
blocks.ProxyBlock(js.blockStart, dest, funcSize / sizeof(u32), GetCodePtr());
595
return true;
596
}
597
598
void Jit::Comp_ReplacementFunc(MIPSOpcode op) {
599
// We get here if we execute the first instruction of a replaced function. This means
600
// that we do need to return to RA.
601
602
// Inlined function calls (caught in jal) are handled differently.
603
604
int index = op.encoding & MIPS_EMUHACK_VALUE_MASK;
605
606
const ReplacementTableEntry *entry = GetReplacementFunc(index);
607
if (!entry) {
608
ERROR_LOG_REPORT_ONCE(replFunc, Log::HLE, "Invalid replacement op %08x at %08x", op.encoding, js.compilerPC);
609
return;
610
}
611
612
u32 funcSize = g_symbolMap->GetFunctionSize(GetCompilerPC());
613
bool disabled = (entry->flags & REPFLAG_DISABLED) != 0;
614
if (!disabled && funcSize != SymbolMap::INVALID_ADDRESS && funcSize > sizeof(u32)) {
615
// We don't need to disable hooks, the code will still run.
616
if ((entry->flags & (REPFLAG_HOOKENTER | REPFLAG_HOOKEXIT)) == 0) {
617
// Any breakpoint at the func entry was already tripped, so we can still run the replacement.
618
// That's a common case - just to see how often the replacement hits.
619
disabled = CBreakPoints::RangeContainsBreakPoint(GetCompilerPC() + sizeof(u32), funcSize - sizeof(u32));
620
}
621
}
622
623
// Hack for old savestates: Avoid stack overflow (MIPSCompileOp/CompReplacementFunc)
624
// Not sure about the cause.
625
Memory::Opcode origInstruction = Memory::Read_Instruction(GetCompilerPC(), true);
626
if (origInstruction.encoding == op.encoding) {
627
ERROR_LOG(Log::HLE, "Replacement broken (savestate problem?): %08x at %08x", op.encoding, GetCompilerPC());
628
return;
629
}
630
631
if (disabled) {
632
MIPSCompileOp(origInstruction, this);
633
} else if (entry->jitReplaceFunc) {
634
MIPSReplaceFunc repl = entry->jitReplaceFunc;
635
int cycles = (this->*repl)();
636
637
if (entry->flags & (REPFLAG_HOOKENTER | REPFLAG_HOOKEXIT)) {
638
// Compile the original instruction at this address. We ignore cycles for hooks.
639
MIPSCompileOp(origInstruction, this);
640
} else {
641
FlushAll();
642
MOV(32, R(ECX), MIPSSTATE_VAR(r[MIPS_REG_RA]));
643
js.downcountAmount += cycles;
644
WriteExitDestInReg(ECX);
645
js.compiling = false;
646
}
647
} else if (entry->replaceFunc) {
648
FlushAll();
649
650
// Standard function call, nothing fancy.
651
// The function returns the number of cycles it took in EAX.
652
MOV(32, MIPSSTATE_VAR(pc), Imm32(GetCompilerPC()));
653
RestoreRoundingMode();
654
ABI_CallFunction(entry->replaceFunc);
655
656
if (entry->flags & (REPFLAG_HOOKENTER | REPFLAG_HOOKEXIT)) {
657
// Compile the original instruction at this address. We ignore cycles for hooks.
658
ApplyRoundingMode();
659
MIPSCompileOp(Memory::Read_Instruction(GetCompilerPC(), true), this);
660
} else {
661
CMP(32, R(EAX), Imm32(0));
662
FixupBranch positive = J_CC(CC_GE);
663
664
MOV(32, R(ECX), MIPSSTATE_VAR(pc));
665
ADD(32, MIPSSTATE_VAR(downcount), R(EAX));
666
FixupBranch done = J();
667
668
SetJumpTarget(positive);
669
MOV(32, R(ECX), MIPSSTATE_VAR(r[MIPS_REG_RA]));
670
SUB(32, MIPSSTATE_VAR(downcount), R(EAX));
671
672
SetJumpTarget(done);
673
ApplyRoundingMode();
674
// Need to set flags again, ApplyRoundingMode destroyed them (and EAX.)
675
SUB(32, MIPSSTATE_VAR(downcount), Imm8(0));
676
WriteExitDestInReg(ECX);
677
js.compiling = false;
678
}
679
} else {
680
ERROR_LOG(Log::HLE, "Replacement function %s has neither jit nor regular impl", entry->name);
681
}
682
}
683
684
void Jit::Comp_Generic(MIPSOpcode op) {
685
FlushAll();
686
MIPSInterpretFunc func = MIPSGetInterpretFunc(op);
687
_dbg_assert_msg_((MIPSGetInfo(op) & DELAYSLOT) == 0, "Cannot use interpreter for branch ops.");
688
689
if (func)
690
{
691
// TODO: Maybe we'd be better off keeping the rounding mode within interp?
692
RestoreRoundingMode();
693
MOV(32, MIPSSTATE_VAR(pc), Imm32(GetCompilerPC()));
694
if (USE_JIT_MISSMAP)
695
ABI_CallFunctionC(&JitLogMiss, op.encoding);
696
else
697
ABI_CallFunctionC(func, op.encoding);
698
ApplyRoundingMode();
699
}
700
else
701
ERROR_LOG_REPORT(Log::JIT, "Trying to compile instruction %08x that can't be interpreted", op.encoding);
702
703
const MIPSInfo info = MIPSGetInfo(op);
704
if ((info & IS_VFPU) != 0 && (info & VFPU_NO_PREFIX) == 0)
705
{
706
// If it does eat them, it'll happen in MIPSCompileOp().
707
if ((info & OUT_EAT_PREFIX) == 0)
708
js.PrefixUnknown();
709
710
// Even if DISABLE'd, we want to set this flag so we overwrite.
711
if ((info & OUT_VFPU_PREFIX) != 0)
712
js.blockWrotePrefixes = true;
713
}
714
}
715
716
static void HitInvalidBranch(uint32_t dest) {
717
Core_ExecException(dest, currentMIPS->pc, ExecExceptionType::JUMP);
718
}
719
720
void Jit::WriteExit(u32 destination, int exit_num) {
721
_assert_msg_(exit_num < MAX_JIT_BLOCK_EXITS, "Expected a valid exit_num. dest=%08x", destination);
722
723
if (!Memory::IsValidAddress(destination) || (destination & 3) != 0) {
724
ERROR_LOG_REPORT(Log::JIT, "Trying to write block exit to illegal destination %08x: pc = %08x", destination, currentMIPS->pc);
725
MOV(32, MIPSSTATE_VAR(pc), Imm32(GetCompilerPC()));
726
ABI_CallFunctionC(&HitInvalidBranch, destination);
727
js.afterOp |= JitState::AFTER_CORE_STATE;
728
}
729
// If we need to verify coreState, we may not jump yet.
730
if (js.afterOp & JitState::AFTER_CORE_STATE) {
731
// CORE_RUNNING is <= CORE_NEXTFRAME.
732
if (RipAccessible((const void *)&coreState)) {
733
CMP(32, M(&coreState), Imm32(CORE_NEXTFRAME)); // rip accessible
734
} else {
735
MOV(PTRBITS, R(RAX), ImmPtr((const void *)&coreState));
736
CMP(32, MatR(RAX), Imm32(CORE_NEXTFRAME));
737
}
738
FixupBranch skipCheck = J_CC(CC_LE);
739
// All cases of AFTER_CORE_STATE should update PC. We don't update here.
740
WriteSyscallExit();
741
SetJumpTarget(skipCheck);
742
}
743
744
WriteDowncount();
745
746
//If nobody has taken care of this yet (this can be removed when all branches are done)
747
JitBlock *b = js.curBlock;
748
b->exitAddress[exit_num] = destination;
749
b->exitPtrs[exit_num] = GetWritableCodePtr();
750
751
// Link opportunity!
752
int block = blocks.GetBlockNumberFromStartAddress(destination);
753
if (block >= 0 && jo.enableBlocklink) {
754
// It exists! Joy of joy!
755
JMP(blocks.GetBlock(block)->checkedEntry, true);
756
b->linkStatus[exit_num] = true;
757
} else {
758
// No blocklinking.
759
MOV(32, MIPSSTATE_VAR(pc), Imm32(destination));
760
JMP(dispatcher, true);
761
762
// Normally, exits are 15 bytes (MOV + &pc + dest + JMP + dest) on 64 or 32 bit.
763
// But just in case we somehow optimized, pad.
764
ptrdiff_t actualSize = GetWritableCodePtr() - b->exitPtrs[exit_num];
765
int pad = JitBlockCache::GetBlockExitSize() - (int)actualSize;
766
for (int i = 0; i < pad; ++i) {
767
INT3();
768
}
769
}
770
}
771
772
static u32 IsValidJumpTarget(uint32_t addr) {
773
if (Memory::IsValidAddress(addr) && (addr & 3) == 0)
774
return 1;
775
return 0;
776
}
777
778
static void HitInvalidJumpReg(uint32_t source) {
779
Core_ExecException(currentMIPS->pc, source, ExecExceptionType::JUMP);
780
currentMIPS->pc = source + 8;
781
}
782
783
void Jit::WriteExitDestInReg(X64Reg reg) {
784
// If we need to verify coreState, we may not jump yet.
785
if (js.afterOp & JitState::AFTER_CORE_STATE) {
786
// CORE_RUNNING is <= CORE_NEXTFRAME.
787
if (RipAccessible((const void *)&coreState)) {
788
CMP(32, M(&coreState), Imm32(CORE_NEXTFRAME)); // rip accessible
789
} else {
790
X64Reg temp = reg == RAX ? RDX : RAX;
791
MOV(PTRBITS, R(temp), ImmPtr((const void *)&coreState));
792
CMP(32, MatR(temp), Imm32(CORE_NEXTFRAME));
793
}
794
FixupBranch skipCheck = J_CC(CC_LE);
795
// All cases of AFTER_CORE_STATE should update PC. We don't update here.
796
WriteSyscallExit();
797
SetJumpTarget(skipCheck);
798
}
799
800
MOV(32, MIPSSTATE_VAR(pc), R(reg));
801
WriteDowncount();
802
803
// Validate the jump to avoid a crash?
804
if (!g_Config.bFastMemory) {
805
CMP(32, R(reg), Imm32(PSP_GetKernelMemoryBase()));
806
FixupBranch tooLow = J_CC(CC_B);
807
CMP(32, R(reg), Imm32(PSP_GetUserMemoryEnd()));
808
FixupBranch tooHigh = J_CC(CC_AE);
809
810
// Need to set neg flag again.
811
SUB(32, MIPSSTATE_VAR(downcount), Imm8(0));
812
if (reg == EAX)
813
J_CC(CC_NS, dispatcherInEAXNoCheck, true);
814
JMP(dispatcher, true);
815
816
SetJumpTarget(tooLow);
817
SetJumpTarget(tooHigh);
818
819
ABI_CallFunctionA((const void *)&IsValidJumpTarget, R(reg));
820
821
// If we're ignoring, coreState didn't trip - so trip it now.
822
CMP(32, R(EAX), Imm32(0));
823
FixupBranch skip = J_CC(CC_NE);
824
ABI_CallFunctionC(&HitInvalidJumpReg, GetCompilerPC());
825
SetJumpTarget(skip);
826
827
SUB(32, MIPSSTATE_VAR(downcount), Imm8(0));
828
JMP(dispatcherCheckCoreState, true);
829
} else if (reg == EAX) {
830
J_CC(CC_NS, dispatcherInEAXNoCheck, true);
831
JMP(dispatcher, true);
832
} else {
833
JMP(dispatcher, true);
834
}
835
}
836
837
void Jit::WriteSyscallExit() {
838
WriteDowncount();
839
JMP(dispatcherCheckCoreState, true);
840
}
841
842
bool Jit::CheckJitBreakpoint(u32 addr, int downcountOffset) {
843
if (CBreakPoints::IsAddressBreakPoint(addr)) {
844
SaveFlags();
845
FlushAll();
846
MOV(32, MIPSSTATE_VAR(pc), Imm32(GetCompilerPC()));
847
RestoreRoundingMode();
848
ABI_CallFunctionC(&JitBreakpoint, addr);
849
850
// If 0, the conditional breakpoint wasn't taken.
851
CMP(32, R(EAX), Imm32(0));
852
FixupBranch skip = J_CC(CC_Z);
853
WriteDowncount(downcountOffset);
854
ApplyRoundingMode();
855
// Just to fix the stack.
856
LoadFlags();
857
JMP(dispatcherCheckCoreState, true);
858
SetJumpTarget(skip);
859
860
ApplyRoundingMode();
861
LoadFlags();
862
return true;
863
}
864
865
return false;
866
}
867
868
void Jit::CheckMemoryBreakpoint(int instructionOffset, MIPSGPReg rs, int offset) {
869
if (!CBreakPoints::HasMemChecks())
870
return;
871
872
int totalInstructionOffset = instructionOffset + (js.inDelaySlot ? 1 : 0);
873
uint32_t checkedPC = GetCompilerPC() + totalInstructionOffset * 4;
874
int size = MIPSAnalyst::OpMemoryAccessSize(checkedPC);
875
bool isWrite = MIPSAnalyst::IsOpMemoryWrite(checkedPC);
876
877
// 0 because we normally execute before increasing.
878
int downcountOffset = js.inDelaySlot ? -2 : -1;
879
// TODO: In likely branches, downcount will be incorrect. This might make resume fail.
880
if (js.downcountAmount + downcountOffset < 0) {
881
downcountOffset = 0;
882
}
883
884
if (gpr.IsImm(rs)) {
885
uint32_t iaddr = gpr.GetImm(rs) + offset;
886
MemCheck check;
887
if (CBreakPoints::GetMemCheckInRange(iaddr, size, &check)) {
888
if (!(check.cond & MEMCHECK_READ) && !isWrite)
889
return;
890
if (!(check.cond & MEMCHECK_WRITE) && isWrite)
891
return;
892
893
// We need to flush, or conditions and log expressions will see old register values.
894
FlushAll();
895
896
MOV(32, MIPSSTATE_VAR(pc), Imm32(GetCompilerPC()));
897
CallProtectedFunction(&JitMemCheck, iaddr, checkedPC);
898
899
CMP(32, R(RAX), Imm32(0));
900
FixupBranch skipCheck = J_CC(CC_E);
901
WriteDowncount(downcountOffset);
902
JMP(dispatcherCheckCoreState, true);
903
904
SetJumpTarget(skipCheck);
905
}
906
} else {
907
const auto memchecks = CBreakPoints::GetMemCheckRanges(isWrite);
908
bool possible = !memchecks.empty();
909
if (!possible)
910
return;
911
912
gpr.Lock(rs);
913
gpr.MapReg(rs, true, false);
914
LEA(32, RAX, MDisp(gpr.RX(rs), offset));
915
gpr.UnlockAll();
916
917
// We need to flush, or conditions and log expressions will see old register values.
918
FlushAll();
919
920
std::vector<FixupBranch> hitChecks;
921
hitChecks.reserve(memchecks.size());
922
for (auto it = memchecks.begin(), end = memchecks.end(); it != end; ++it) {
923
if (it->end != 0) {
924
CMP(32, R(RAX), Imm32(it->start - size));
925
FixupBranch skipNext = J_CC(CC_BE);
926
927
CMP(32, R(RAX), Imm32(it->end));
928
hitChecks.push_back(J_CC(CC_B, true));
929
930
SetJumpTarget(skipNext);
931
} else {
932
CMP(32, R(RAX), Imm32(it->start));
933
hitChecks.push_back(J_CC(CC_E, true));
934
}
935
}
936
937
FixupBranch noHits = J(true);
938
939
// Okay, now land any hit here.
940
for (auto &fixup : hitChecks)
941
SetJumpTarget(fixup);
942
hitChecks.clear();
943
944
MOV(32, MIPSSTATE_VAR(pc), Imm32(GetCompilerPC()));
945
CallProtectedFunction(&JitMemCheck, R(RAX), checkedPC);
946
947
CMP(32, R(RAX), Imm32(0));
948
FixupBranch skipCheck = J_CC(CC_E);
949
WriteDowncount(downcountOffset);
950
JMP(dispatcherCheckCoreState, true);
951
952
SetJumpTarget(skipCheck);
953
SetJumpTarget(noHits);
954
}
955
}
956
957
void Jit::CallProtectedFunction(const void *func, const OpArg &arg1) {
958
// We don't regcache RCX, so the below is safe (and also faster, maybe branch prediction?)
959
ABI_CallFunctionA(thunks.ProtectFunction(func, 1), arg1);
960
}
961
962
void Jit::CallProtectedFunction(const void *func, const OpArg &arg1, const OpArg &arg2) {
963
// We don't regcache RCX/RDX, so the below is safe (and also faster, maybe branch prediction?)
964
ABI_CallFunctionAA(thunks.ProtectFunction(func, 2), arg1, arg2);
965
}
966
967
void Jit::CallProtectedFunction(const void *func, const u32 arg1, const u32 arg2) {
968
// We don't regcache RCX/RDX, so the below is safe (and also faster, maybe branch prediction?)
969
ABI_CallFunctionCC(thunks.ProtectFunction(func, 2), arg1, arg2);
970
}
971
972
void Jit::CallProtectedFunction(const void *func, const OpArg &arg1, const u32 arg2) {
973
// We don't regcache RCX/RDX, so the below is safe (and also faster, maybe branch prediction?)
974
ABI_CallFunctionAC(thunks.ProtectFunction(func, 2), arg1, arg2);
975
}
976
977
void Jit::Comp_DoNothing(MIPSOpcode op) { }
978
979
MIPSOpcode Jit::GetOriginalOp(MIPSOpcode op) {
980
JitBlockCache *bc = GetBlockCache();
981
int block_num = bc->GetBlockNumberFromEmuHackOp(op, true);
982
if (block_num >= 0) {
983
return bc->GetOriginalFirstOp(block_num);
984
} else {
985
return op;
986
}
987
}
988
989
} // namespace
990
991
#endif // PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64)
992
993