1 // Copyright 2014 Dolphin Emulator Project
2 // Licensed under GPLv2+
3 // Refer to the license.txt file included.
4
5 #include "Common/Arm64Emitter.h"
6 #include "Common/CommonTypes.h"
7
8 #include "Core/Core.h"
9 #include "Core/CoreTiming.h"
10 #include "Core/PowerPC/JitArm64/Jit.h"
11 #include "Core/PowerPC/JitArm64/JitArm64_RegCache.h"
12 #include "Core/PowerPC/PPCTables.h"
13 #include "Core/PowerPC/PowerPC.h"
14
15 using namespace Arm64Gen;
16
sc(UGeckoInstruction inst)17 void JitArm64::sc(UGeckoInstruction inst)
18 {
19 INSTRUCTION_START
20 JITDISABLE(bJITBranchOff);
21
22 gpr.Flush(FlushMode::FLUSH_ALL);
23 fpr.Flush(FlushMode::FLUSH_ALL);
24
25 ARM64Reg WA = gpr.GetReg();
26
27 LDR(INDEX_UNSIGNED, WA, PPC_REG, PPCSTATE_OFF(Exceptions));
28 ORR(WA, WA, 31, 0); // Same as WA | EXCEPTION_SYSCALL
29 STR(INDEX_UNSIGNED, WA, PPC_REG, PPCSTATE_OFF(Exceptions));
30
31 gpr.Unlock(WA);
32
33 WriteExceptionExit(js.compilerPC + 4);
34 }
35
rfi(UGeckoInstruction inst)36 void JitArm64::rfi(UGeckoInstruction inst)
37 {
38 INSTRUCTION_START
39 JITDISABLE(bJITBranchOff);
40
41 gpr.Flush(FlushMode::FLUSH_ALL);
42 fpr.Flush(FlushMode::FLUSH_ALL);
43
44 // See Interpreter rfi for details
45 const u32 mask = 0x87C0FFFF;
46 const u32 clearMSR13 = 0xFFFBFFFF; // Mask used to clear the bit MSR[13]
47 // MSR = ((MSR & ~mask) | (SRR1 & mask)) & clearMSR13;
48 // R0 = MSR location
49 // R1 = MSR contents
50 // R2 = Mask
51 // R3 = Mask
52 ARM64Reg WA = gpr.GetReg();
53 ARM64Reg WB = gpr.GetReg();
54 ARM64Reg WC = gpr.GetReg();
55
56 LDR(INDEX_UNSIGNED, WC, PPC_REG, PPCSTATE_OFF(msr));
57
58 ANDI2R(WC, WC, (~mask) & clearMSR13, WA); // rD = Masked MSR
59
60 LDR(INDEX_UNSIGNED, WA, PPC_REG, PPCSTATE_OFF(spr[SPR_SRR1])); // rB contains SRR1 here
61
62 ANDI2R(WA, WA, mask & clearMSR13, WB); // rB contains masked SRR1 here
63 ORR(WA, WA, WC); // rB = Masked MSR OR masked SRR1
64
65 STR(INDEX_UNSIGNED, WA, PPC_REG, PPCSTATE_OFF(msr)); // STR rB in to rA
66
67 LDR(INDEX_UNSIGNED, WA, PPC_REG, PPCSTATE_OFF(spr[SPR_SRR0]));
68 gpr.Unlock(WB, WC);
69
70 WriteExceptionExit(WA);
71 gpr.Unlock(WA);
72 }
73
bx(UGeckoInstruction inst)74 void JitArm64::bx(UGeckoInstruction inst)
75 {
76 INSTRUCTION_START
77 JITDISABLE(bJITBranchOff);
78
79 if (inst.LK)
80 {
81 ARM64Reg WA = gpr.GetReg();
82 MOVI2R(WA, js.compilerPC + 4);
83 STR(INDEX_UNSIGNED, WA, PPC_REG, PPCSTATE_OFF(spr[SPR_LR]));
84 gpr.Unlock(WA);
85 }
86
87 if (!js.isLastInstruction)
88 {
89 if (inst.LK && !js.op->skipLRStack)
90 {
91 // We have to fake the stack as the RET instruction was not
92 // found in the same block. This is a big overhead, but still
93 // better than calling the dispatcher.
94 FakeLKExit(js.compilerPC + 4);
95 }
96 return;
97 }
98
99 gpr.Flush(FlushMode::FLUSH_ALL);
100 fpr.Flush(FlushMode::FLUSH_ALL);
101
102 if (js.op->branchIsIdleLoop)
103 {
104 // make idle loops go faster
105 ARM64Reg WA = gpr.GetReg();
106 ARM64Reg XA = EncodeRegTo64(WA);
107
108 MOVP2R(XA, &CoreTiming::Idle);
109 BLR(XA);
110 gpr.Unlock(WA);
111
112 WriteExceptionExit(js.op->branchTo);
113 return;
114 }
115
116 WriteExit(js.op->branchTo, inst.LK, js.compilerPC + 4);
117 }
118
bcx(UGeckoInstruction inst)119 void JitArm64::bcx(UGeckoInstruction inst)
120 {
121 INSTRUCTION_START
122 JITDISABLE(bJITBranchOff);
123
124 ARM64Reg WA = gpr.GetReg();
125 FixupBranch pCTRDontBranch;
126 if ((inst.BO & BO_DONT_DECREMENT_FLAG) == 0) // Decrement and test CTR
127 {
128 LDR(INDEX_UNSIGNED, WA, PPC_REG, PPCSTATE_OFF(spr[SPR_CTR]));
129 SUBS(WA, WA, 1);
130 STR(INDEX_UNSIGNED, WA, PPC_REG, PPCSTATE_OFF(spr[SPR_CTR]));
131
132 if (inst.BO & BO_BRANCH_IF_CTR_0)
133 pCTRDontBranch = B(CC_NEQ);
134 else
135 pCTRDontBranch = B(CC_EQ);
136 }
137
138 FixupBranch pConditionDontBranch;
139
140 if ((inst.BO & BO_DONT_CHECK_CONDITION) == 0) // Test a CR bit
141 {
142 pConditionDontBranch =
143 JumpIfCRFieldBit(inst.BI >> 2, 3 - (inst.BI & 3), !(inst.BO_2 & BO_BRANCH_IF_TRUE));
144 }
145
146 FixupBranch far_addr = B();
147 SwitchToFarCode();
148 SetJumpTarget(far_addr);
149
150 if (inst.LK)
151 {
152 MOVI2R(WA, js.compilerPC + 4);
153 STR(INDEX_UNSIGNED, WA, PPC_REG, PPCSTATE_OFF(spr[SPR_LR]));
154 }
155 gpr.Unlock(WA);
156
157 gpr.Flush(FlushMode::FLUSH_MAINTAIN_STATE);
158 fpr.Flush(FlushMode::FLUSH_MAINTAIN_STATE);
159
160 if (js.op->branchIsIdleLoop)
161 {
162 // make idle loops go faster
163 ARM64Reg WA2 = gpr.GetReg();
164 ARM64Reg XA2 = EncodeRegTo64(WA2);
165
166 MOVP2R(XA2, &CoreTiming::Idle);
167 BLR(XA2);
168 gpr.Unlock(WA2);
169
170 WriteExceptionExit(js.op->branchTo);
171 }
172 else
173 {
174 WriteExit(js.op->branchTo, inst.LK, js.compilerPC + 4);
175 }
176
177 SwitchToNearCode();
178
179 if ((inst.BO & BO_DONT_CHECK_CONDITION) == 0)
180 SetJumpTarget(pConditionDontBranch);
181 if ((inst.BO & BO_DONT_DECREMENT_FLAG) == 0)
182 SetJumpTarget(pCTRDontBranch);
183
184 if (!analyzer.HasOption(PPCAnalyst::PPCAnalyzer::OPTION_CONDITIONAL_CONTINUE))
185 {
186 gpr.Flush(FlushMode::FLUSH_ALL);
187 fpr.Flush(FlushMode::FLUSH_ALL);
188 WriteExit(js.compilerPC + 4);
189 }
190 }
191
bcctrx(UGeckoInstruction inst)192 void JitArm64::bcctrx(UGeckoInstruction inst)
193 {
194 INSTRUCTION_START
195 JITDISABLE(bJITBranchOff);
196
197 // Rare condition seen in (just some versions of?) Nintendo's NES Emulator
198 // BO_2 == 001zy -> b if false
199 // BO_2 == 011zy -> b if true
200 FALLBACK_IF(!(inst.BO_2 & BO_DONT_CHECK_CONDITION));
201
202 // bcctrx doesn't decrement and/or test CTR
203 ASSERT_MSG(DYNA_REC, inst.BO_2 & BO_DONT_DECREMENT_FLAG,
204 "bcctrx with decrement and test CTR option is invalid!");
205
206 // BO_2 == 1z1zz -> b always
207
208 // NPC = CTR & 0xfffffffc;
209 gpr.Flush(FlushMode::FLUSH_ALL);
210 fpr.Flush(FlushMode::FLUSH_ALL);
211
212 if (inst.LK_3)
213 {
214 ARM64Reg WB = gpr.GetReg();
215 MOVI2R(WB, js.compilerPC + 4);
216 STR(INDEX_UNSIGNED, WB, PPC_REG, PPCSTATE_OFF(spr[SPR_LR]));
217 gpr.Unlock(WB);
218 }
219
220 ARM64Reg WA = gpr.GetReg();
221
222 LDR(INDEX_UNSIGNED, WA, PPC_REG, PPCSTATE_OFF(spr[SPR_CTR]));
223 AND(WA, WA, 30, 29); // Wipe the bottom 2 bits.
224
225 WriteExit(WA, inst.LK_3, js.compilerPC + 4);
226
227 gpr.Unlock(WA);
228 }
229
bclrx(UGeckoInstruction inst)230 void JitArm64::bclrx(UGeckoInstruction inst)
231 {
232 INSTRUCTION_START
233 JITDISABLE(bJITBranchOff);
234
235 bool conditional =
236 (inst.BO & BO_DONT_DECREMENT_FLAG) == 0 || (inst.BO & BO_DONT_CHECK_CONDITION) == 0;
237
238 ARM64Reg WA = gpr.GetReg();
239 ARM64Reg WB = inst.LK ? gpr.GetReg() : INVALID_REG;
240
241 FixupBranch pCTRDontBranch;
242 if ((inst.BO & BO_DONT_DECREMENT_FLAG) == 0) // Decrement and test CTR
243 {
244 LDR(INDEX_UNSIGNED, WA, PPC_REG, PPCSTATE_OFF(spr[SPR_CTR]));
245 SUBS(WA, WA, 1);
246 STR(INDEX_UNSIGNED, WA, PPC_REG, PPCSTATE_OFF(spr[SPR_CTR]));
247
248 if (inst.BO & BO_BRANCH_IF_CTR_0)
249 pCTRDontBranch = B(CC_NEQ);
250 else
251 pCTRDontBranch = B(CC_EQ);
252 }
253
254 FixupBranch pConditionDontBranch;
255 if ((inst.BO & BO_DONT_CHECK_CONDITION) == 0) // Test a CR bit
256 {
257 pConditionDontBranch =
258 JumpIfCRFieldBit(inst.BI >> 2, 3 - (inst.BI & 3), !(inst.BO_2 & BO_BRANCH_IF_TRUE));
259 }
260
261 if (conditional)
262 {
263 FixupBranch far_addr = B();
264 SwitchToFarCode();
265 SetJumpTarget(far_addr);
266 }
267
268 LDR(INDEX_UNSIGNED, WA, PPC_REG, PPCSTATE_OFF(spr[SPR_LR]));
269 AND(WA, WA, 30, 29); // Wipe the bottom 2 bits.
270
271 if (inst.LK)
272 {
273 MOVI2R(WB, js.compilerPC + 4);
274 STR(INDEX_UNSIGNED, WB, PPC_REG, PPCSTATE_OFF(spr[SPR_LR]));
275 gpr.Unlock(WB);
276 }
277
278 gpr.Flush(conditional ? FlushMode::FLUSH_MAINTAIN_STATE : FlushMode::FLUSH_ALL);
279 fpr.Flush(conditional ? FlushMode::FLUSH_MAINTAIN_STATE : FlushMode::FLUSH_ALL);
280
281 if (js.op->branchIsIdleLoop)
282 {
283 // make idle loops go faster
284 ARM64Reg XA = EncodeRegTo64(WA);
285
286 MOVP2R(XA, &CoreTiming::Idle);
287 BLR(XA);
288
289 WriteExceptionExit(js.op->branchTo);
290 }
291 else
292 {
293 WriteBLRExit(WA);
294 }
295
296 gpr.Unlock(WA);
297
298 if (conditional)
299 SwitchToNearCode();
300
301 if ((inst.BO & BO_DONT_CHECK_CONDITION) == 0)
302 SetJumpTarget(pConditionDontBranch);
303 if ((inst.BO & BO_DONT_DECREMENT_FLAG) == 0)
304 SetJumpTarget(pCTRDontBranch);
305
306 if (!analyzer.HasOption(PPCAnalyst::PPCAnalyzer::OPTION_CONDITIONAL_CONTINUE))
307 {
308 gpr.Flush(FlushMode::FLUSH_ALL);
309 fpr.Flush(FlushMode::FLUSH_ALL);
310 WriteExit(js.compilerPC + 4);
311 }
312 }
313