1 /*
2  * Copyright (C) 2012 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 /* This file contains codegen for the Mips ISA */
18 
19 #include "codegen_mips.h"
20 
21 #include "base/logging.h"
22 #include "dex/mir_graph.h"
23 #include "dex/quick/mir_to_lir-inl.h"
24 #include "dex/reg_storage_eq.h"
25 #include "entrypoints/quick/quick_entrypoints.h"
26 #include "mips_lir.h"
27 #include "mirror/array-inl.h"
28 
29 namespace art {
30 
31 /*
32  * Compare two 64-bit values
33  *    x = y     return  0
34  *    x < y     return -1
35  *    x > y     return  1
36  *
37  * Mips32 implementation
38  *    slt   t0,  x.hi, y.hi;        # (x.hi < y.hi) ? 1:0
39  *    sgt   t1,  x.hi, y.hi;        # (y.hi > x.hi) ? 1:0
40  *    subu  res, t0, t1             # res = -1:1:0 for [ < > = ]
41  *    bnez  res, finish
42  *    sltu  t0, x.lo, y.lo
43  *    sgtu  r1, x.lo, y.lo
44  *    subu  res, t0, t1
45  * finish:
46  *
47  * Mips64 implementation
48  *    slt   temp, x, y;             # (x < y) ? 1:0
49  *    slt   res, y, x;              # (x > y) ? 1:0
50  *    subu  res, res, temp;         # res = -1:1:0 for [ < > = ]
51  *
52  */
GenCmpLong(RegLocation rl_dest,RegLocation rl_src1,RegLocation rl_src2)53 void MipsMir2Lir::GenCmpLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2) {
54   rl_src1 = LoadValueWide(rl_src1, kCoreReg);
55   rl_src2 = LoadValueWide(rl_src2, kCoreReg);
56   if (cu_->target64) {
57     RegStorage temp = AllocTempWide();
58     RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
59     NewLIR3(kMipsSlt, temp.GetReg(), rl_src1.reg.GetReg(), rl_src2.reg.GetReg());
60     NewLIR3(kMipsSlt, rl_result.reg.GetReg(), rl_src2.reg.GetReg(), rl_src1.reg.GetReg());
61     NewLIR3(kMipsSubu, rl_result.reg.GetReg(), rl_result.reg.GetReg(), temp.GetReg());
62     FreeTemp(temp);
63     StoreValue(rl_dest, rl_result);
64   } else {
65     RegStorage t0 = AllocTemp();
66     RegStorage t1 = AllocTemp();
67     RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
68     NewLIR3(kMipsSlt, t0.GetReg(), rl_src1.reg.GetHighReg(), rl_src2.reg.GetHighReg());
69     NewLIR3(kMipsSlt, t1.GetReg(), rl_src2.reg.GetHighReg(), rl_src1.reg.GetHighReg());
70     NewLIR3(kMipsSubu, rl_result.reg.GetReg(), t1.GetReg(), t0.GetReg());
71     LIR* branch = OpCmpImmBranch(kCondNe, rl_result.reg, 0, nullptr);
72     NewLIR3(kMipsSltu, t0.GetReg(), rl_src1.reg.GetLowReg(), rl_src2.reg.GetLowReg());
73     NewLIR3(kMipsSltu, t1.GetReg(), rl_src2.reg.GetLowReg(), rl_src1.reg.GetLowReg());
74     NewLIR3(kMipsSubu, rl_result.reg.GetReg(), t1.GetReg(), t0.GetReg());
75     FreeTemp(t0);
76     FreeTemp(t1);
77     LIR* target = NewLIR0(kPseudoTargetLabel);
78     branch->target = target;
79     StoreValue(rl_dest, rl_result);
80   }
81 }
82 
OpCmpBranch(ConditionCode cond,RegStorage src1,RegStorage src2,LIR * target)83 LIR* MipsMir2Lir::OpCmpBranch(ConditionCode cond, RegStorage src1, RegStorage src2, LIR* target) {
84   LIR* branch;
85   MipsOpCode slt_op;
86   MipsOpCode br_op;
87   bool cmp_zero = false;
88   bool swapped = false;
89   switch (cond) {
90     case kCondEq:
91       br_op = kMipsBeq;
92       cmp_zero = true;
93       break;
94     case kCondNe:
95       br_op = kMipsBne;
96       cmp_zero = true;
97       break;
98     case kCondUlt:
99       slt_op = kMipsSltu;
100       br_op = kMipsBnez;
101       break;
102     case kCondUge:
103       slt_op = kMipsSltu;
104       br_op = kMipsBeqz;
105       break;
106     case kCondGe:
107       slt_op = kMipsSlt;
108       br_op = kMipsBeqz;
109       break;
110     case kCondGt:
111       slt_op = kMipsSlt;
112       br_op = kMipsBnez;
113       swapped = true;
114       break;
115     case kCondLe:
116       slt_op = kMipsSlt;
117       br_op = kMipsBeqz;
118       swapped = true;
119       break;
120     case kCondLt:
121       slt_op = kMipsSlt;
122       br_op = kMipsBnez;
123       break;
124     case kCondHi:  // Gtu
125       slt_op = kMipsSltu;
126       br_op = kMipsBnez;
127       swapped = true;
128       break;
129     default:
130       LOG(FATAL) << "No support for ConditionCode: " << cond;
131       return nullptr;
132   }
133   if (cmp_zero) {
134     branch = NewLIR2(br_op, src1.GetReg(), src2.GetReg());
135   } else {
136     RegStorage t_reg = AllocTemp();
137     if (swapped) {
138       NewLIR3(slt_op, t_reg.GetReg(), src2.GetReg(), src1.GetReg());
139     } else {
140       NewLIR3(slt_op, t_reg.GetReg(), src1.GetReg(), src2.GetReg());
141     }
142     branch = NewLIR1(br_op, t_reg.GetReg());
143     FreeTemp(t_reg);
144   }
145   branch->target = target;
146   return branch;
147 }
148 
OpCmpImmBranch(ConditionCode cond,RegStorage reg,int check_value,LIR * target)149 LIR* MipsMir2Lir::OpCmpImmBranch(ConditionCode cond, RegStorage reg, int check_value, LIR* target) {
150   LIR* branch;
151   if (check_value != 0) {
152     // TUNING: handle s16 & kCondLt/Mi case using slti.
153     RegStorage t_reg = AllocTemp();
154     LoadConstant(t_reg, check_value);
155     branch = OpCmpBranch(cond, reg, t_reg, target);
156     FreeTemp(t_reg);
157     return branch;
158   }
159   MipsOpCode opc;
160   switch (cond) {
161     case kCondEq: opc = kMipsBeqz; break;
162     case kCondGe: opc = kMipsBgez; break;
163     case kCondGt: opc = kMipsBgtz; break;
164     case kCondLe: opc = kMipsBlez; break;
165     // case KCondMi:
166     case kCondLt: opc = kMipsBltz; break;
167     case kCondNe: opc = kMipsBnez; break;
168     default:
169       // Tuning: use slti when applicable
170       RegStorage t_reg = AllocTemp();
171       LoadConstant(t_reg, check_value);
172       branch = OpCmpBranch(cond, reg, t_reg, target);
173       FreeTemp(t_reg);
174       return branch;
175   }
176   branch = NewLIR1(opc, reg.GetReg());
177   branch->target = target;
178   return branch;
179 }
180 
OpRegCopyNoInsert(RegStorage r_dest,RegStorage r_src)181 LIR* MipsMir2Lir::OpRegCopyNoInsert(RegStorage r_dest, RegStorage r_src) {
182   LIR* res;
183   MipsOpCode opcode;
184 
185   if (!cu_->target64) {
186     // If src or dest is a pair, we'll be using low reg.
187     if (r_dest.IsPair()) {
188       r_dest = r_dest.GetLow();
189     }
190     if (r_src.IsPair()) {
191       r_src = r_src.GetLow();
192     }
193   } else {
194     DCHECK(!r_dest.IsPair() && !r_src.IsPair());
195   }
196 
197   if (r_dest.IsFloat() || r_src.IsFloat())
198     return OpFpRegCopy(r_dest, r_src);
199   if (cu_->target64) {
200     // TODO: Check that r_src and r_dest are both 32 or both 64 bits length on Mips64.
201     if (r_dest.Is64Bit() || r_src.Is64Bit()) {
202       opcode = kMipsMove;
203     } else {
204       opcode = kMipsSll;
205     }
206   } else {
207     opcode = kMipsMove;
208   }
209   res = RawLIR(current_dalvik_offset_, opcode, r_dest.GetReg(), r_src.GetReg());
210   if (!(cu_->disable_opt & (1 << kSafeOptimizations)) && r_dest == r_src) {
211     res->flags.is_nop = true;
212   }
213   return res;
214 }
215 
OpRegCopy(RegStorage r_dest,RegStorage r_src)216 void MipsMir2Lir::OpRegCopy(RegStorage r_dest, RegStorage r_src) {
217   if (r_dest != r_src) {
218     LIR *res = OpRegCopyNoInsert(r_dest, r_src);
219     AppendLIR(res);
220   }
221 }
222 
OpRegCopyWide(RegStorage r_dest,RegStorage r_src)223 void MipsMir2Lir::OpRegCopyWide(RegStorage r_dest, RegStorage r_src) {
224   if (cu_->target64) {
225     OpRegCopy(r_dest, r_src);
226     return;
227   }
228   if (r_dest != r_src) {
229     bool dest_fp = r_dest.IsFloat();
230     bool src_fp = r_src.IsFloat();
231     if (dest_fp) {
232       if (src_fp) {
233         // Here if both src and dest are fp registers. OpRegCopy will choose the right copy
234         // (solo or pair).
235         OpRegCopy(r_dest, r_src);
236       } else {
237         // note the operands are swapped for the mtc1 and mthc1 instr.
238         // Here if dest is fp reg and src is core reg.
239         if (fpuIs32Bit_) {
240           NewLIR2(kMipsMtc1, r_src.GetLowReg(), r_dest.GetLowReg());
241           NewLIR2(kMipsMtc1, r_src.GetHighReg(), r_dest.GetHighReg());
242         } else {
243           r_dest = Fp64ToSolo32(r_dest);
244           NewLIR2(kMipsMtc1, r_src.GetLowReg(), r_dest.GetReg());
245           NewLIR2(kMipsMthc1, r_src.GetHighReg(), r_dest.GetReg());
246         }
247       }
248     } else {
249       if (src_fp) {
250         // Here if dest is core reg and src is fp reg.
251         if (fpuIs32Bit_) {
252           NewLIR2(kMipsMfc1, r_dest.GetLowReg(), r_src.GetLowReg());
253           NewLIR2(kMipsMfc1, r_dest.GetHighReg(), r_src.GetHighReg());
254         } else {
255           r_src = Fp64ToSolo32(r_src);
256           NewLIR2(kMipsMfc1, r_dest.GetLowReg(), r_src.GetReg());
257           NewLIR2(kMipsMfhc1, r_dest.GetHighReg(), r_src.GetReg());
258         }
259       } else {
260         // Here if both src and dest are core registers.
261         // Handle overlap
262         if (r_src.GetHighReg() != r_dest.GetLowReg()) {
263           OpRegCopy(r_dest.GetLow(), r_src.GetLow());
264           OpRegCopy(r_dest.GetHigh(), r_src.GetHigh());
265         } else if (r_src.GetLowReg() != r_dest.GetHighReg()) {
266           OpRegCopy(r_dest.GetHigh(), r_src.GetHigh());
267           OpRegCopy(r_dest.GetLow(), r_src.GetLow());
268         } else {
269           RegStorage r_tmp = AllocTemp();
270           OpRegCopy(r_tmp, r_src.GetHigh());
271           OpRegCopy(r_dest.GetLow(), r_src.GetLow());
272           OpRegCopy(r_dest.GetHigh(), r_tmp);
273           FreeTemp(r_tmp);
274         }
275       }
276     }
277   }
278 }
279 
GenSelectConst32(RegStorage left_op,RegStorage right_op,ConditionCode code,int32_t true_val,int32_t false_val,RegStorage rs_dest,RegisterClass dest_reg_class)280 void MipsMir2Lir::GenSelectConst32(RegStorage left_op, RegStorage right_op, ConditionCode code,
281                                    int32_t true_val, int32_t false_val, RegStorage rs_dest,
282                                    RegisterClass dest_reg_class) {
283   UNUSED(dest_reg_class);
284   // Implement as a branch-over.
285   // TODO: Conditional move?
286   LoadConstant(rs_dest, true_val);
287   LIR* ne_branchover = OpCmpBranch(code, left_op, right_op, nullptr);
288   LoadConstant(rs_dest, false_val);
289   LIR* target_label = NewLIR0(kPseudoTargetLabel);
290   ne_branchover->target = target_label;
291 }
292 
GenSelect(BasicBlock * bb,MIR * mir)293 void MipsMir2Lir::GenSelect(BasicBlock* bb, MIR* mir) {
294   UNUSED(bb, mir);
295   UNIMPLEMENTED(FATAL) << "Need codegen for select";
296 }
297 
GenFusedLongCmpBranch(BasicBlock * bb,MIR * mir)298 void MipsMir2Lir::GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir) {
299   UNUSED(bb, mir);
300   UNIMPLEMENTED(FATAL) << "Need codegen for fused long cmp branch";
301 }
302 
GenDivRem(RegLocation rl_dest,RegStorage reg1,RegStorage reg2,bool is_div)303 RegLocation MipsMir2Lir::GenDivRem(RegLocation rl_dest, RegStorage reg1, RegStorage reg2,
304                                    bool is_div) {
305   RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
306 
307   if (isaIsR6_) {
308     NewLIR3(is_div ? kMipsR6Div : kMipsR6Mod, rl_result.reg.GetReg(), reg1.GetReg(), reg2.GetReg());
309   } else {
310     NewLIR2(kMipsR2Div, reg1.GetReg(), reg2.GetReg());
311     NewLIR1(is_div ? kMipsR2Mflo : kMipsR2Mfhi, rl_result.reg.GetReg());
312   }
313   return rl_result;
314 }
315 
GenDivRemLit(RegLocation rl_dest,RegStorage reg1,int lit,bool is_div)316 RegLocation MipsMir2Lir::GenDivRemLit(RegLocation rl_dest, RegStorage reg1, int lit, bool is_div) {
317   RegStorage t_reg = AllocTemp();
318   // lit is guarantee to be a 16-bit constant
319   if (IsUint<16>(lit)) {
320     NewLIR3(kMipsOri, t_reg.GetReg(), rZERO, lit);
321   } else {
322     // Addiu will sign extend the entire width (32 or 64) of the register.
323     NewLIR3(kMipsAddiu, t_reg.GetReg(), rZERO, lit);
324   }
325   RegLocation rl_result = GenDivRem(rl_dest, reg1, t_reg, is_div);
326   FreeTemp(t_reg);
327   return rl_result;
328 }
329 
GenDivRem(RegLocation rl_dest,RegLocation rl_src1,RegLocation rl_src2,bool is_div,int flags)330 RegLocation MipsMir2Lir::GenDivRem(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2,
331                                    bool is_div, int flags) {
332   UNUSED(rl_dest, rl_src1, rl_src2, is_div, flags);
333   LOG(FATAL) << "Unexpected use of GenDivRem for Mips";
334   UNREACHABLE();
335 }
336 
GenDivRemLit(RegLocation rl_dest,RegLocation rl_src1,int lit,bool is_div)337 RegLocation MipsMir2Lir::GenDivRemLit(RegLocation rl_dest, RegLocation rl_src1, int lit,
338                                       bool is_div) {
339   UNUSED(rl_dest, rl_src1, lit, is_div);
340   LOG(FATAL) << "Unexpected use of GenDivRemLit for Mips";
341   UNREACHABLE();
342 }
343 
GenInlinedCas(CallInfo * info,bool is_long,bool is_object)344 bool MipsMir2Lir::GenInlinedCas(CallInfo* info, bool is_long, bool is_object) {
345   UNUSED(info, is_long, is_object);
346   return false;
347 }
348 
GenInlinedAbsFloat(CallInfo * info)349 bool MipsMir2Lir::GenInlinedAbsFloat(CallInfo* info) {
350   UNUSED(info);
351   // TODO: add Mips implementation.
352   return false;
353 }
354 
GenInlinedAbsDouble(CallInfo * info)355 bool MipsMir2Lir::GenInlinedAbsDouble(CallInfo* info) {
356   UNUSED(info);
357   // TODO: add Mips implementation.
358   return false;
359 }
360 
GenInlinedSqrt(CallInfo * info)361 bool MipsMir2Lir::GenInlinedSqrt(CallInfo* info) {
362   UNUSED(info);
363   return false;
364 }
365 
GenInlinedPeek(CallInfo * info,OpSize size)366 bool MipsMir2Lir::GenInlinedPeek(CallInfo* info, OpSize size) {
367   if (size != kSignedByte) {
368     // MIPS supports only aligned access. Defer unaligned access to JNI implementation.
369     return false;
370   }
371   RegLocation rl_src_address = info->args[0];       // Long address.
372   if (!cu_->target64) {
373     rl_src_address = NarrowRegLoc(rl_src_address);  // Ignore high half in info->args[1].
374   }
375   RegLocation rl_dest = InlineTarget(info);
376   RegLocation rl_address;
377   if (cu_->target64) {
378     rl_address = LoadValueWide(rl_src_address, kCoreReg);
379   } else {
380     rl_address = LoadValue(rl_src_address, kCoreReg);
381   }
382   RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
383   DCHECK(size == kSignedByte);
384   LoadBaseDisp(rl_address.reg, 0, rl_result.reg, size, kNotVolatile);
385   StoreValue(rl_dest, rl_result);
386   return true;
387 }
388 
GenInlinedPoke(CallInfo * info,OpSize size)389 bool MipsMir2Lir::GenInlinedPoke(CallInfo* info, OpSize size) {
390   if (size != kSignedByte) {
391     // MIPS supports only aligned access. Defer unaligned access to JNI implementation.
392     return false;
393   }
394   RegLocation rl_src_address = info->args[0];       // Long address.
395   if (!cu_->target64) {
396     rl_src_address = NarrowRegLoc(rl_src_address);  // Ignore high half in info->args[1].
397   }
398   RegLocation rl_src_value = info->args[2];         // [size] value.
399   RegLocation rl_address;
400   if (cu_->target64) {
401     rl_address = LoadValueWide(rl_src_address, kCoreReg);
402   } else {
403     rl_address = LoadValue(rl_src_address, kCoreReg);
404   }
405   DCHECK(size == kSignedByte);
406   RegLocation rl_value = LoadValue(rl_src_value, kCoreReg);
407   StoreBaseDisp(rl_address.reg, 0, rl_value.reg, size, kNotVolatile);
408   return true;
409 }
410 
OpPcRelLoad(RegStorage reg,LIR * target)411 void MipsMir2Lir::OpPcRelLoad(RegStorage reg, LIR* target) {
412   UNUSED(reg, target);
413   LOG(FATAL) << "Unexpected use of OpPcRelLoad for Mips";
414   UNREACHABLE();
415 }
416 
OpVldm(RegStorage r_base,int count)417 LIR* MipsMir2Lir::OpVldm(RegStorage r_base, int count) {
418   UNUSED(r_base, count);
419   LOG(FATAL) << "Unexpected use of OpVldm for Mips";
420   UNREACHABLE();
421 }
422 
OpVstm(RegStorage r_base,int count)423 LIR* MipsMir2Lir::OpVstm(RegStorage r_base, int count) {
424   UNUSED(r_base, count);
425   LOG(FATAL) << "Unexpected use of OpVstm for Mips";
426   UNREACHABLE();
427 }
428 
GenMultiplyByTwoBitMultiplier(RegLocation rl_src,RegLocation rl_result,int lit,int first_bit,int second_bit)429 void MipsMir2Lir::GenMultiplyByTwoBitMultiplier(RegLocation rl_src, RegLocation rl_result, int lit,
430                                                 int first_bit, int second_bit) {
431   UNUSED(lit);
432   RegStorage t_reg = AllocTemp();
433   OpRegRegImm(kOpLsl, t_reg, rl_src.reg, second_bit - first_bit);
434   OpRegRegReg(kOpAdd, rl_result.reg, rl_src.reg, t_reg);
435   FreeTemp(t_reg);
436   if (first_bit != 0) {
437     OpRegRegImm(kOpLsl, rl_result.reg, rl_result.reg, first_bit);
438   }
439 }
440 
GenDivZeroCheckWide(RegStorage reg)441 void MipsMir2Lir::GenDivZeroCheckWide(RegStorage reg) {
442   if (cu_->target64) {
443     GenDivZeroCheck(reg);
444   } else {
445     DCHECK(reg.IsPair());   // TODO: support k64BitSolo.
446     RegStorage t_reg = AllocTemp();
447     OpRegRegReg(kOpOr, t_reg, reg.GetLow(), reg.GetHigh());
448     GenDivZeroCheck(t_reg);
449     FreeTemp(t_reg);
450   }
451 }
452 
453 // Test suspend flag, return target of taken suspend branch.
OpTestSuspend(LIR * target)454 LIR* MipsMir2Lir::OpTestSuspend(LIR* target) {
455   OpRegImm(kOpSub, TargetPtrReg(kSuspend), 1);
456   return OpCmpImmBranch((target == nullptr) ? kCondEq : kCondNe, TargetPtrReg(kSuspend), 0, target);
457 }
458 
459 // Decrement register and branch on condition.
OpDecAndBranch(ConditionCode c_code,RegStorage reg,LIR * target)460 LIR* MipsMir2Lir::OpDecAndBranch(ConditionCode c_code, RegStorage reg, LIR* target) {
461   OpRegImm(kOpSub, reg, 1);
462   return OpCmpImmBranch(c_code, reg, 0, target);
463 }
464 
SmallLiteralDivRem(Instruction::Code dalvik_opcode,bool is_div,RegLocation rl_src,RegLocation rl_dest,int lit)465 bool MipsMir2Lir::SmallLiteralDivRem(Instruction::Code dalvik_opcode, bool is_div,
466                                      RegLocation rl_src, RegLocation rl_dest, int lit) {
467   UNUSED(dalvik_opcode, is_div, rl_src, rl_dest, lit);
468   LOG(FATAL) << "Unexpected use of smallLiteralDive in Mips";
469   UNREACHABLE();
470 }
471 
EasyMultiply(RegLocation rl_src,RegLocation rl_dest,int lit)472 bool MipsMir2Lir::EasyMultiply(RegLocation rl_src, RegLocation rl_dest, int lit) {
473   UNUSED(rl_src, rl_dest, lit);
474   LOG(FATAL) << "Unexpected use of easyMultiply in Mips";
475   UNREACHABLE();
476 }
477 
OpIT(ConditionCode cond,const char * guide)478 LIR* MipsMir2Lir::OpIT(ConditionCode cond, const char* guide) {
479   UNUSED(cond, guide);
480   LOG(FATAL) << "Unexpected use of OpIT in Mips";
481   UNREACHABLE();
482 }
483 
OpEndIT(LIR * it)484 void MipsMir2Lir::OpEndIT(LIR* it) {
485   UNUSED(it);
486   LOG(FATAL) << "Unexpected use of OpEndIT in Mips";
487 }
488 
GenAddLong(RegLocation rl_dest,RegLocation rl_src1,RegLocation rl_src2)489 void MipsMir2Lir::GenAddLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2) {
490   rl_src1 = LoadValueWide(rl_src1, kCoreReg);
491   rl_src2 = LoadValueWide(rl_src2, kCoreReg);
492   RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
493   /*
494    *  [v1 v0] =  [a1 a0] + [a3 a2];
495    *  addu v0,a2,a0
496    *  addu t1,a3,a1
497    *  sltu v1,v0,a2
498    *  addu v1,v1,t1
499    */
500 
501   OpRegRegReg(kOpAdd, rl_result.reg.GetLow(), rl_src2.reg.GetLow(), rl_src1.reg.GetLow());
502   RegStorage t_reg = AllocTemp();
503   OpRegRegReg(kOpAdd, t_reg, rl_src2.reg.GetHigh(), rl_src1.reg.GetHigh());
504   NewLIR3(kMipsSltu, rl_result.reg.GetHighReg(), rl_result.reg.GetLowReg(),
505           rl_src2.reg.GetLowReg());
506   OpRegRegReg(kOpAdd, rl_result.reg.GetHigh(), rl_result.reg.GetHigh(), t_reg);
507   FreeTemp(t_reg);
508   StoreValueWide(rl_dest, rl_result);
509 }
510 
GenSubLong(RegLocation rl_dest,RegLocation rl_src1,RegLocation rl_src2)511 void MipsMir2Lir::GenSubLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2) {
512   rl_src1 = LoadValueWide(rl_src1, kCoreReg);
513   rl_src2 = LoadValueWide(rl_src2, kCoreReg);
514   RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
515   /*
516    *  [v1 v0] =  [a1 a0] - [a3 a2];
517    *  sltu  t1,a0,a2
518    *  subu  v0,a0,a2
519    *  subu  v1,a1,a3
520    *  subu  v1,v1,t1
521    */
522 
523   RegStorage t_reg = AllocTemp();
524   NewLIR3(kMipsSltu, t_reg.GetReg(), rl_src1.reg.GetLowReg(), rl_src2.reg.GetLowReg());
525   OpRegRegReg(kOpSub, rl_result.reg.GetLow(), rl_src1.reg.GetLow(), rl_src2.reg.GetLow());
526   OpRegRegReg(kOpSub, rl_result.reg.GetHigh(), rl_src1.reg.GetHigh(), rl_src2.reg.GetHigh());
527   OpRegRegReg(kOpSub, rl_result.reg.GetHigh(), rl_result.reg.GetHigh(), t_reg);
528   FreeTemp(t_reg);
529   StoreValueWide(rl_dest, rl_result);
530 }
531 
GenArithOpLong(Instruction::Code opcode,RegLocation rl_dest,RegLocation rl_src1,RegLocation rl_src2,int flags)532 void MipsMir2Lir::GenArithOpLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1,
533                                  RegLocation rl_src2, int flags) {
534   if (cu_->target64) {
535     switch (opcode) {
536       case Instruction::NOT_LONG:
537         GenNotLong(rl_dest, rl_src2);
538         return;
539       case Instruction::ADD_LONG:
540       case Instruction::ADD_LONG_2ADDR:
541         GenLongOp(kOpAdd, rl_dest, rl_src1, rl_src2);
542         return;
543       case Instruction::SUB_LONG:
544       case Instruction::SUB_LONG_2ADDR:
545         GenLongOp(kOpSub, rl_dest, rl_src1, rl_src2);
546         return;
547       case Instruction::MUL_LONG:
548       case Instruction::MUL_LONG_2ADDR:
549         GenMulLong(rl_dest, rl_src1, rl_src2);
550         return;
551       case Instruction::DIV_LONG:
552       case Instruction::DIV_LONG_2ADDR:
553         GenDivRemLong(opcode, rl_dest, rl_src1, rl_src2, /*is_div*/ true, flags);
554         return;
555       case Instruction::REM_LONG:
556       case Instruction::REM_LONG_2ADDR:
557         GenDivRemLong(opcode, rl_dest, rl_src1, rl_src2, /*is_div*/ false, flags);
558         return;
559       case Instruction::AND_LONG:
560       case Instruction::AND_LONG_2ADDR:
561         GenLongOp(kOpAnd, rl_dest, rl_src1, rl_src2);
562         return;
563       case Instruction::OR_LONG:
564       case Instruction::OR_LONG_2ADDR:
565         GenLongOp(kOpOr, rl_dest, rl_src1, rl_src2);
566         return;
567       case Instruction::XOR_LONG:
568       case Instruction::XOR_LONG_2ADDR:
569         GenLongOp(kOpXor, rl_dest, rl_src1, rl_src2);
570         return;
571       case Instruction::NEG_LONG:
572         GenNegLong(rl_dest, rl_src2);
573         return;
574 
575       default:
576         LOG(FATAL) << "Invalid long arith op";
577         return;
578     }
579   } else {
580     switch (opcode) {
581       case Instruction::ADD_LONG:
582       case Instruction::ADD_LONG_2ADDR:
583         GenAddLong(rl_dest, rl_src1, rl_src2);
584         return;
585       case Instruction::SUB_LONG:
586       case Instruction::SUB_LONG_2ADDR:
587         GenSubLong(rl_dest, rl_src1, rl_src2);
588         return;
589       case Instruction::NEG_LONG:
590         GenNegLong(rl_dest, rl_src2);
591         return;
592       default:
593         break;
594     }
595     // Fallback for all other ops.
596     Mir2Lir::GenArithOpLong(opcode, rl_dest, rl_src1, rl_src2, flags);
597   }
598 }
599 
GenLongOp(OpKind op,RegLocation rl_dest,RegLocation rl_src1,RegLocation rl_src2)600 void MipsMir2Lir::GenLongOp(OpKind op, RegLocation rl_dest, RegLocation rl_src1,
601                             RegLocation rl_src2) {
602   rl_src1 = LoadValueWide(rl_src1, kCoreReg);
603   rl_src2 = LoadValueWide(rl_src2, kCoreReg);
604   RegLocation rl_result = EvalLocWide(rl_dest, kCoreReg, true);
605   OpRegRegReg(op, rl_result.reg, rl_src1.reg, rl_src2.reg);
606   StoreValueWide(rl_dest, rl_result);
607 }
608 
GenNotLong(RegLocation rl_dest,RegLocation rl_src)609 void MipsMir2Lir::GenNotLong(RegLocation rl_dest, RegLocation rl_src) {
610   rl_src = LoadValueWide(rl_src, kCoreReg);
611   RegLocation rl_result = EvalLocWide(rl_dest, kCoreReg, true);
612   OpRegReg(kOpMvn, rl_result.reg, rl_src.reg);
613   StoreValueWide(rl_dest, rl_result);
614 }
615 
GenMulLong(RegLocation rl_dest,RegLocation rl_src1,RegLocation rl_src2)616 void MipsMir2Lir::GenMulLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2) {
617   rl_src1 = LoadValueWide(rl_src1, kCoreReg);
618   rl_src2 = LoadValueWide(rl_src2, kCoreReg);
619   RegLocation rl_result = EvalLocWide(rl_dest, kCoreReg, true);
620   NewLIR3(kMips64Dmul, rl_result.reg.GetReg(), rl_src1.reg.GetReg(), rl_src2.reg.GetReg());
621   StoreValueWide(rl_dest, rl_result);
622 }
623 
GenDivRemLong(Instruction::Code opcode,RegLocation rl_dest,RegLocation rl_src1,RegLocation rl_src2,bool is_div,int flags)624 void MipsMir2Lir::GenDivRemLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1,
625                                 RegLocation rl_src2, bool is_div, int flags) {
626   UNUSED(opcode);
627   // TODO: Implement easy div/rem?
628   rl_src1 = LoadValueWide(rl_src1, kCoreReg);
629   rl_src2 = LoadValueWide(rl_src2, kCoreReg);
630   if ((flags & MIR_IGNORE_DIV_ZERO_CHECK) == 0) {
631     GenDivZeroCheckWide(rl_src2.reg);
632   }
633   RegLocation rl_result = EvalLocWide(rl_dest, kCoreReg, true);
634   NewLIR3(is_div ? kMips64Ddiv : kMips64Dmod, rl_result.reg.GetReg(), rl_src1.reg.GetReg(),
635           rl_src2.reg.GetReg());
636   StoreValueWide(rl_dest, rl_result);
637 }
638 
GenNegLong(RegLocation rl_dest,RegLocation rl_src)639 void MipsMir2Lir::GenNegLong(RegLocation rl_dest, RegLocation rl_src) {
640   rl_src = LoadValueWide(rl_src, kCoreReg);
641   RegLocation rl_result;
642 
643   if (cu_->target64) {
644     rl_result = EvalLocWide(rl_dest, kCoreReg, true);
645     OpRegReg(kOpNeg, rl_result.reg, rl_src.reg);
646     StoreValueWide(rl_dest, rl_result);
647   } else {
648     rl_result = EvalLoc(rl_dest, kCoreReg, true);
649     //  [v1 v0] =  -[a1 a0]
650     //  negu  v0,a0
651     //  negu  v1,a1
652     //  sltu  t1,r_zero
653     //  subu  v1,v1,t1
654     OpRegReg(kOpNeg, rl_result.reg.GetLow(), rl_src.reg.GetLow());
655     OpRegReg(kOpNeg, rl_result.reg.GetHigh(), rl_src.reg.GetHigh());
656     RegStorage t_reg = AllocTemp();
657     NewLIR3(kMipsSltu, t_reg.GetReg(), rZERO, rl_result.reg.GetLowReg());
658     OpRegRegReg(kOpSub, rl_result.reg.GetHigh(), rl_result.reg.GetHigh(), t_reg);
659     FreeTemp(t_reg);
660     StoreValueWide(rl_dest, rl_result);
661   }
662 }
663 
664 /*
665  * Generate array load
666  */
GenArrayGet(int opt_flags,OpSize size,RegLocation rl_array,RegLocation rl_index,RegLocation rl_dest,int scale)667 void MipsMir2Lir::GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array,
668                               RegLocation rl_index, RegLocation rl_dest, int scale) {
669   RegisterClass reg_class = RegClassBySize(size);
670   int len_offset = mirror::Array::LengthOffset().Int32Value();
671   int data_offset;
672   RegLocation rl_result;
673   rl_array = LoadValue(rl_array, kRefReg);
674   rl_index = LoadValue(rl_index, kCoreReg);
675 
676   // FIXME: need to add support for rl_index.is_const.
677 
678   if (size == k64 || size == kDouble) {
679     data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Int32Value();
680   } else {
681     data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Int32Value();
682   }
683 
684   // Null object?
685   GenNullCheck(rl_array.reg, opt_flags);
686 
687   RegStorage reg_ptr = (cu_->target64) ? AllocTempRef() : AllocTemp();
688   bool needs_range_check = (!(opt_flags & MIR_IGNORE_RANGE_CHECK));
689   RegStorage reg_len;
690   if (needs_range_check) {
691     reg_len = AllocTemp();
692     // Get len.
693     Load32Disp(rl_array.reg, len_offset, reg_len);
694     MarkPossibleNullPointerException(opt_flags);
695   } else {
696     ForceImplicitNullCheck(rl_array.reg, opt_flags, false);
697   }
698   // reg_ptr -> array data.
699   OpRegRegImm(kOpAdd, reg_ptr, rl_array.reg, data_offset);
700   FreeTemp(rl_array.reg);
701   if ((size == k64) || (size == kDouble)) {
702     if (scale) {
703       RegStorage r_new_index = AllocTemp();
704       OpRegRegImm(kOpLsl, r_new_index, rl_index.reg, scale);
705       OpRegReg(kOpAdd, reg_ptr, r_new_index);
706       FreeTemp(r_new_index);
707     } else {
708       OpRegReg(kOpAdd, reg_ptr, rl_index.reg);
709     }
710     FreeTemp(rl_index.reg);
711     rl_result = EvalLoc(rl_dest, reg_class, true);
712 
713     if (needs_range_check) {
714       GenArrayBoundsCheck(rl_index.reg, reg_len);
715       FreeTemp(reg_len);
716     }
717     LoadBaseDisp(reg_ptr, 0, rl_result.reg, size, kNotVolatile);
718 
719     FreeTemp(reg_ptr);
720     StoreValueWide(rl_dest, rl_result);
721   } else {
722     rl_result = EvalLoc(rl_dest, reg_class, true);
723 
724     if (needs_range_check) {
725       GenArrayBoundsCheck(rl_index.reg, reg_len);
726       FreeTemp(reg_len);
727     }
728 
729     if (cu_->target64) {
730       if (rl_result.ref) {
731         LoadBaseIndexed(reg_ptr, As64BitReg(rl_index.reg), As32BitReg(rl_result.reg), scale,
732                         kReference);
733       } else {
734         LoadBaseIndexed(reg_ptr, As64BitReg(rl_index.reg), rl_result.reg, scale, size);
735       }
736     } else {
737       LoadBaseIndexed(reg_ptr, rl_index.reg, rl_result.reg, scale, size);
738     }
739 
740     FreeTemp(reg_ptr);
741     StoreValue(rl_dest, rl_result);
742   }
743 }
744 
745 /*
746  * Generate array store
747  *
748  */
GenArrayPut(int opt_flags,OpSize size,RegLocation rl_array,RegLocation rl_index,RegLocation rl_src,int scale,bool card_mark)749 void MipsMir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array,
750                               RegLocation rl_index, RegLocation rl_src, int scale, bool card_mark) {
751   RegisterClass reg_class = RegClassBySize(size);
752   int len_offset = mirror::Array::LengthOffset().Int32Value();
753   int data_offset;
754 
755   if (size == k64 || size == kDouble) {
756     data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Int32Value();
757   } else {
758     data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Int32Value();
759   }
760 
761   rl_array = LoadValue(rl_array, kRefReg);
762   rl_index = LoadValue(rl_index, kCoreReg);
763 
764   // FIXME: need to add support for rl_index.is_const.
765 
766   RegStorage reg_ptr;
767   bool allocated_reg_ptr_temp = false;
768   if (IsTemp(rl_array.reg) && !card_mark) {
769     Clobber(rl_array.reg);
770     reg_ptr = rl_array.reg;
771   } else {
772     reg_ptr = AllocTemp();
773     OpRegCopy(reg_ptr, rl_array.reg);
774     allocated_reg_ptr_temp = true;
775   }
776 
777   // Null object?
778   GenNullCheck(rl_array.reg, opt_flags);
779 
780   bool needs_range_check = (!(opt_flags & MIR_IGNORE_RANGE_CHECK));
781   RegStorage reg_len;
782   if (needs_range_check) {
783     reg_len = AllocTemp();
784     // NOTE: max live temps(4) here.
785     // Get len.
786     Load32Disp(rl_array.reg, len_offset, reg_len);
787     MarkPossibleNullPointerException(opt_flags);
788   } else {
789     ForceImplicitNullCheck(rl_array.reg, opt_flags, false);
790   }
791   // reg_ptr -> array data.
792   OpRegImm(kOpAdd, reg_ptr, data_offset);
793   // At this point, reg_ptr points to array, 2 live temps.
794   if ((size == k64) || (size == kDouble)) {
795     // TUNING: specific wide routine that can handle fp regs.
796     if (scale) {
797       RegStorage r_new_index = AllocTemp();
798       OpRegRegImm(kOpLsl, r_new_index, rl_index.reg, scale);
799       OpRegReg(kOpAdd, reg_ptr, r_new_index);
800       FreeTemp(r_new_index);
801     } else {
802       OpRegReg(kOpAdd, reg_ptr, rl_index.reg);
803     }
804     rl_src = LoadValueWide(rl_src, reg_class);
805 
806     if (needs_range_check) {
807       GenArrayBoundsCheck(rl_index.reg, reg_len);
808       FreeTemp(reg_len);
809     }
810 
811     StoreBaseDisp(reg_ptr, 0, rl_src.reg, size, kNotVolatile);
812   } else {
813     rl_src = LoadValue(rl_src, reg_class);
814     if (needs_range_check) {
815       GenArrayBoundsCheck(rl_index.reg, reg_len);
816       FreeTemp(reg_len);
817     }
818     StoreBaseIndexed(reg_ptr, rl_index.reg, rl_src.reg, scale, size);
819   }
820   if (allocated_reg_ptr_temp) {
821     FreeTemp(reg_ptr);
822   }
823   if (card_mark) {
824     MarkGCCard(opt_flags, rl_src.reg, rl_array.reg);
825   }
826 }
827 
GenShiftOpLong(Instruction::Code opcode,RegLocation rl_dest,RegLocation rl_src1,RegLocation rl_shift)828 void MipsMir2Lir::GenShiftOpLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1,
829                                  RegLocation rl_shift) {
830   if (!cu_->target64) {
831     Mir2Lir::GenShiftOpLong(opcode, rl_dest, rl_src1, rl_shift);
832     return;
833   }
834   OpKind op = kOpBkpt;
835   switch (opcode) {
836     case Instruction::SHL_LONG:
837     case Instruction::SHL_LONG_2ADDR:
838       op = kOpLsl;
839       break;
840     case Instruction::SHR_LONG:
841     case Instruction::SHR_LONG_2ADDR:
842       op = kOpAsr;
843       break;
844     case Instruction::USHR_LONG:
845     case Instruction::USHR_LONG_2ADDR:
846       op = kOpLsr;
847       break;
848     default:
849       LOG(FATAL) << "Unexpected case: " << opcode;
850   }
851   rl_shift = LoadValue(rl_shift, kCoreReg);
852   rl_src1 = LoadValueWide(rl_src1, kCoreReg);
853   RegLocation rl_result = EvalLocWide(rl_dest, kCoreReg, true);
854   OpRegRegReg(op, rl_result.reg, rl_src1.reg, As64BitReg(rl_shift.reg));
855   StoreValueWide(rl_dest, rl_result);
856 }
857 
GenShiftImmOpLong(Instruction::Code opcode,RegLocation rl_dest,RegLocation rl_src1,RegLocation rl_shift,int flags)858 void MipsMir2Lir::GenShiftImmOpLong(Instruction::Code opcode, RegLocation rl_dest,
859                                     RegLocation rl_src1, RegLocation rl_shift, int flags) {
860   UNUSED(flags);
861   if (!cu_->target64) {
862     // Default implementation is just to ignore the constant case.
863     GenShiftOpLong(opcode, rl_dest, rl_src1, rl_shift);
864     return;
865   }
866   OpKind op = kOpBkpt;
867   // Per spec, we only care about low 6 bits of shift amount.
868   int shift_amount = mir_graph_->ConstantValue(rl_shift) & 0x3f;
869   rl_src1 = LoadValueWide(rl_src1, kCoreReg);
870   if (shift_amount == 0) {
871     StoreValueWide(rl_dest, rl_src1);
872     return;
873   }
874 
875   RegLocation rl_result = EvalLocWide(rl_dest, kCoreReg, true);
876   switch (opcode) {
877     case Instruction::SHL_LONG:
878     case Instruction::SHL_LONG_2ADDR:
879       op = kOpLsl;
880       break;
881     case Instruction::SHR_LONG:
882     case Instruction::SHR_LONG_2ADDR:
883       op = kOpAsr;
884       break;
885     case Instruction::USHR_LONG:
886     case Instruction::USHR_LONG_2ADDR:
887       op = kOpLsr;
888       break;
889     default:
890       LOG(FATAL) << "Unexpected case";
891   }
892   OpRegRegImm(op, rl_result.reg, rl_src1.reg, shift_amount);
893   StoreValueWide(rl_dest, rl_result);
894 }
895 
GenArithImmOpLong(Instruction::Code opcode,RegLocation rl_dest,RegLocation rl_src1,RegLocation rl_src2,int flags)896 void MipsMir2Lir::GenArithImmOpLong(Instruction::Code opcode, RegLocation rl_dest,
897                                     RegLocation rl_src1, RegLocation rl_src2, int flags) {
898   // Default - bail to non-const handler.
899   GenArithOpLong(opcode, rl_dest, rl_src1, rl_src2, flags);
900 }
901 
GenIntToLong(RegLocation rl_dest,RegLocation rl_src)902 void MipsMir2Lir::GenIntToLong(RegLocation rl_dest, RegLocation rl_src) {
903   if (!cu_->target64) {
904     Mir2Lir::GenIntToLong(rl_dest, rl_src);
905     return;
906   }
907   rl_src = LoadValue(rl_src, kCoreReg);
908   RegLocation rl_result = EvalLocWide(rl_dest, kCoreReg, true);
909   NewLIR3(kMipsSll, rl_result.reg.GetReg(), As64BitReg(rl_src.reg).GetReg(), 0);
910   StoreValueWide(rl_dest, rl_result);
911 }
912 
GenConversionCall(QuickEntrypointEnum trampoline,RegLocation rl_dest,RegLocation rl_src,RegisterClass reg_class)913 void MipsMir2Lir::GenConversionCall(QuickEntrypointEnum trampoline, RegLocation rl_dest,
914                                     RegLocation rl_src, RegisterClass reg_class) {
915   FlushAllRegs();   // Send everything to home location.
916   CallRuntimeHelperRegLocation(trampoline, rl_src, false);
917   if (rl_dest.wide) {
918     RegLocation rl_result;
919     rl_result = GetReturnWide(reg_class);
920     StoreValueWide(rl_dest, rl_result);
921   } else {
922     RegLocation rl_result;
923     rl_result = GetReturn(reg_class);
924     StoreValue(rl_dest, rl_result);
925   }
926 }
927 
928 }  // namespace art
929