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