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/ArmAsm.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 "Core/MemMap.h"
22
#include "Core/MIPS/MIPS.h"
23
#include "Core/System.h"
24
#include "Core/CoreTiming.h"
25
#include "Common/MemoryUtil.h"
26
#include "Common/CPUDetect.h"
27
#include "Common/ArmEmitter.h"
28
#include "Core/MIPS/ARM/ArmJit.h"
29
#include "Core/MIPS/JitCommon/JitCommon.h"
30
31
using namespace ArmGen;
32
33
//static int temp32; // unused?
34
35
static const bool enableDebug = false;
36
static const bool disasm = false;
37
38
//static bool enableStatistics = false; //unused?
39
40
//The standard ARM calling convention allocates the 16 ARM registers as:
41
42
// r15 is the program counter.
43
// r14 is the link register. (The BL instruction, used in a subroutine call, stores the return address in this register).
44
// r13 is the stack pointer. (The Push/Pop instructions in "Thumb" operating mode use this register only).
45
// r12 is the Intra-Procedure-call scratch register.
46
// r4 to r11: used to hold local variables.
47
// r0 to r3: used to hold argument values passed to a subroutine, and also hold results returned from a subroutine.
48
49
// Mappable registers:
50
// R2, R3, R4, R5, R6, R8, R11
51
52
// STATIC ALLOCATION ARM:
53
// R10 : MIPS state
54
// R11 : Memory base pointer.
55
// R7 : Down counter
56
extern volatile CoreState coreState;
57
58
void ShowPC(u32 sp) {
59
ERROR_LOG(Log::JIT, "ShowPC : %08x ArmSP : %08x", currentMIPS->pc, sp);
60
// Sleep(1);
61
}
62
63
void DisassembleArm(const u8 *data, int size);
64
65
// PLAN: no more block numbers - crazy opcodes just contain offset within
66
// dynarec buffer
67
// At this offset - 4, there is an int specifying the block number.
68
69
namespace MIPSComp {
70
71
using namespace ArmJitConstants;
72
73
void ArmJit::GenerateFixedCode() {
74
BeginWrite(GetMemoryProtectPageSize());
75
const u8 *start = AlignCodePage();
76
77
// LR == SCRATCHREG2 on ARM32 so it needs to be pushed.
78
restoreRoundingMode = AlignCode16(); {
79
PUSH(1, R_LR);
80
VMRS(SCRATCHREG2);
81
// Outside the JIT we run with round-to-nearest and flush0 off.
82
BIC(SCRATCHREG2, SCRATCHREG2, AssumeMakeOperand2((3 | 4) << 22));
83
VMSR(SCRATCHREG2);
84
POP(1, R_PC);
85
}
86
87
// Must preserve SCRATCHREG1 (R0), destroys SCRATCHREG2 (LR)
88
applyRoundingMode = AlignCode16(); {
89
PUSH(2, SCRATCHREG1, R_LR);
90
LDR(SCRATCHREG2, CTXREG, offsetof(MIPSState, fcr31));
91
92
TST(SCRATCHREG2, AssumeMakeOperand2(1 << 24));
93
AND(SCRATCHREG2, SCRATCHREG2, Operand2(3));
94
SetCC(CC_NEQ);
95
ADD(SCRATCHREG2, SCRATCHREG2, Operand2(4));
96
SetCC(CC_AL);
97
98
// We can skip if the rounding mode is nearest (0) and flush is not set.
99
// (as restoreRoundingMode cleared it out anyway)
100
CMP(SCRATCHREG2, Operand2(0));
101
FixupBranch skip = B_CC(CC_EQ);
102
103
// MIPS Rounding Mode: ARM Rounding Mode
104
// 0: Round nearest 0
105
// 1: Round to zero 3
106
// 2: Round up (ceil) 1
107
// 3: Round down (floor) 2
108
AND(SCRATCHREG1, SCRATCHREG2, Operand2(3));
109
CMP(SCRATCHREG1, Operand2(1));
110
111
SetCC(CC_EQ); ADD(SCRATCHREG2, SCRATCHREG2, Operand2(2));
112
SetCC(CC_GT); SUB(SCRATCHREG2, SCRATCHREG2, Operand2(1));
113
SetCC(CC_AL);
114
115
VMRS(SCRATCHREG1);
116
// Assume we're always in round-to-nearest mode beforehand.
117
// But we need to clear flush to zero in this case anyway.
118
BIC(SCRATCHREG1, SCRATCHREG1, AssumeMakeOperand2((3 | 4) << 22));
119
ORR(SCRATCHREG1, SCRATCHREG1, Operand2(SCRATCHREG2, ST_LSL, 22));
120
VMSR(SCRATCHREG1);
121
122
SetJumpTarget(skip);
123
POP(2, SCRATCHREG1, R_PC);
124
}
125
126
FlushLitPool();
127
128
enterDispatcher = AlignCode16();
129
130
DEBUG_LOG(Log::JIT, "Base: %08x", (u32)Memory::base);
131
132
SetCC(CC_AL);
133
134
PUSH(9, R4, R5, R6, R7, R8, R9, R10, R11, R_LR);
135
// Take care to 8-byte align stack for function calls.
136
// We are misaligned here because of an odd number of args for PUSH.
137
// It's not like x86 where you need to account for an extra 4 bytes
138
// consumed by CALL.
139
SUB(R_SP, R_SP, 4);
140
// Now we are correctly aligned and plan to stay that way.
141
VPUSH(D8, 8);
142
143
// Fixed registers, these are always kept when in Jit context.
144
// R8 is used to hold flags during delay slots. Not always needed.
145
// R13 cannot be used as it's the stack pointer.
146
// TODO: Consider statically allocating:
147
// * r2-r4
148
// Really starting to run low on registers already though...
149
150
// R11, R10, R9
151
MOVP2R(MEMBASEREG, Memory::base);
152
MOVP2R(CTXREG, mips_);
153
MOVP2R(JITBASEREG, GetBasePtr());
154
155
RestoreDowncount();
156
MovFromPC(R0);
157
outerLoopPCInR0 = GetCodePtr();
158
MovToPC(R0);
159
outerLoop = GetCodePtr();
160
SaveDowncount();
161
RestoreRoundingMode(true);
162
QuickCallFunction(R0, &CoreTiming::Advance);
163
ApplyRoundingMode(true);
164
RestoreDowncount();
165
FixupBranch skipToCoreStateCheck = B(); //skip the downcount check
166
167
dispatcherCheckCoreState = GetCodePtr();
168
169
// The result of slice decrementation should be in flags if somebody jumped here
170
// IMPORTANT - We jump on negative, not carry!!!
171
FixupBranch bailCoreState = B_CC(CC_MI);
172
173
SetJumpTarget(skipToCoreStateCheck);
174
175
MOVI2R(R0, (u32)(uintptr_t)&coreState);
176
LDR(R0, R0);
177
CMP(R0, 0);
178
FixupBranch badCoreState = B_CC(CC_NEQ);
179
FixupBranch skipToRealDispatch2 = B(); //skip the sync and compare first time
180
181
dispatcherPCInR0 = GetCodePtr();
182
// TODO: Do we always need to write PC to RAM here?
183
MovToPC(R0);
184
185
// At this point : flags = EQ. Fine for the next check, no need to jump over it.
186
dispatcher = GetCodePtr();
187
188
// The result of slice decrementation should be in flags if somebody jumped here
189
// IMPORTANT - We jump on negative, not carry!!!
190
FixupBranch bail = B_CC(CC_MI);
191
192
SetJumpTarget(skipToRealDispatch2);
193
194
dispatcherNoCheck = GetCodePtr();
195
196
// Debug
197
if (enableDebug) {
198
MOV(R0, R13);
199
QuickCallFunction(R1, (void *)&ShowPC);
200
}
201
202
LDR(R0, CTXREG, offsetof(MIPSState, pc));
203
// TODO: In practice, do we ever run code from uncached space (| 0x40000000)? If not, we can remove this BIC.
204
BIC(R0, R0, Operand2(0xC0, 4)); // &= 0x3FFFFFFF
205
dispatcherFetch = GetCodePtr();
206
LDR(R0, MEMBASEREG, R0);
207
AND(R1, R0, Operand2(0xFF, 4)); // rotation is to the right, in 2-bit increments.
208
BIC(R0, R0, Operand2(0xFF, 4));
209
CMP(R1, Operand2(MIPS_EMUHACK_OPCODE >> 24, 4));
210
SetCC(CC_EQ);
211
// IDEA - we have 26 bits, why not just use offsets from base of code?
212
// Another idea: Shift the bloc number left by two in the op, this would let us do
213
// LDR(R0, R9, R0); here, replacing the next instructions.
214
#if PPSSPP_PLATFORM(IOS)
215
// On iOS, R9 (JITBASEREG) is volatile. We have to reload it.
216
MOVI2R(JITBASEREG, (u32)(uintptr_t)GetBasePtr());
217
#endif
218
ADD(R0, R0, JITBASEREG);
219
B(R0);
220
SetCC(CC_AL);
221
222
// No block found, let's jit
223
SaveDowncount();
224
RestoreRoundingMode(true);
225
QuickCallFunction(R2, (void *)&MIPSComp::JitAt);
226
ApplyRoundingMode(true);
227
RestoreDowncount();
228
229
B(dispatcherNoCheck); // no point in special casing this
230
231
SetJumpTarget(bail);
232
SetJumpTarget(bailCoreState);
233
234
MOVI2R(R0, (u32)(uintptr_t)&coreState);
235
LDR(R0, R0);
236
CMP(R0, 0);
237
B_CC(CC_EQ, outerLoop);
238
239
const uint8_t *quitLoop = GetCodePtr();
240
SetJumpTarget(badCoreState);
241
242
SaveDowncount();
243
RestoreRoundingMode(true);
244
245
VPOP(D8, 8);
246
247
ADD(R_SP, R_SP, 4);
248
249
POP(9, R4, R5, R6, R7, R8, R9, R10, R11, R_PC); // Returns
250
251
crashHandler = GetCodePtr();
252
MOVP2R(R0, &coreState);
253
MOVI2R(R1, CORE_RUNTIME_ERROR);
254
STR(R1, R0, 0);
255
B(quitLoop);
256
257
// Uncomment if you want to see the output...
258
if (disasm) {
259
INFO_LOG(Log::JIT, "THE DISASM ========================");
260
DisassembleArm(start, GetCodePtr() - start);
261
INFO_LOG(Log::JIT, "END OF THE DISASM ========================");
262
}
263
264
// Don't forget to zap the instruction cache!
265
FlushLitPool();
266
FlushIcache();
267
268
// Let's spare the pre-generated code from unprotect-reprotect.
269
AlignCodePage();
270
EndWrite();
271
}
272
273
} // namespace MIPSComp
274
275
#endif // PPSSPP_ARCH(ARM)
276
277