Home | History | Annotate | Download | only in mips
      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 #include "dex/quick/mir_to_lir-inl.h"
     21 #include "dex/reg_storage_eq.h"
     22 #include "entrypoints/quick/quick_entrypoints.h"
     23 #include "mips_lir.h"
     24 #include "mirror/array.h"
     25 
     26 namespace art {
     27 
     28 /*
     29  * Compare two 64-bit values
     30  *    x = y     return  0
     31  *    x < y     return -1
     32  *    x > y     return  1
     33  *
     34  *    slt   t0,  x.hi, y.hi;        # (x.hi < y.hi) ? 1:0
     35  *    sgt   t1,  x.hi, y.hi;        # (y.hi > x.hi) ? 1:0
     36  *    subu  res, t0, t1             # res = -1:1:0 for [ < > = ]
     37  *    bnez  res, finish
     38  *    sltu  t0, x.lo, y.lo
     39  *    sgtu  r1, x.lo, y.lo
     40  *    subu  res, t0, t1
     41  * finish:
     42  *
     43  */
     44 void MipsMir2Lir::GenCmpLong(RegLocation rl_dest, RegLocation rl_src1,
     45                              RegLocation rl_src2) {
     46   rl_src1 = LoadValueWide(rl_src1, kCoreReg);
     47   rl_src2 = LoadValueWide(rl_src2, kCoreReg);
     48   RegStorage t0 = AllocTemp();
     49   RegStorage t1 = AllocTemp();
     50   RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
     51   NewLIR3(kMipsSlt, t0.GetReg(), rl_src1.reg.GetHighReg(), rl_src2.reg.GetHighReg());
     52   NewLIR3(kMipsSlt, t1.GetReg(), rl_src2.reg.GetHighReg(), rl_src1.reg.GetHighReg());
     53   NewLIR3(kMipsSubu, rl_result.reg.GetReg(), t1.GetReg(), t0.GetReg());
     54   LIR* branch = OpCmpImmBranch(kCondNe, rl_result.reg, 0, NULL);
     55   NewLIR3(kMipsSltu, t0.GetReg(), rl_src1.reg.GetLowReg(), rl_src2.reg.GetLowReg());
     56   NewLIR3(kMipsSltu, t1.GetReg(), rl_src2.reg.GetLowReg(), rl_src1.reg.GetLowReg());
     57   NewLIR3(kMipsSubu, rl_result.reg.GetReg(), t1.GetReg(), t0.GetReg());
     58   FreeTemp(t0);
     59   FreeTemp(t1);
     60   LIR* target = NewLIR0(kPseudoTargetLabel);
     61   branch->target = target;
     62   StoreValue(rl_dest, rl_result);
     63 }
     64 
     65 LIR* MipsMir2Lir::OpCmpBranch(ConditionCode cond, RegStorage src1, RegStorage src2, LIR* target) {
     66   LIR* branch;
     67   MipsOpCode slt_op;
     68   MipsOpCode br_op;
     69   bool cmp_zero = false;
     70   bool swapped = false;
     71   switch (cond) {
     72     case kCondEq:
     73       br_op = kMipsBeq;
     74       cmp_zero = true;
     75       break;
     76     case kCondNe:
     77       br_op = kMipsBne;
     78       cmp_zero = true;
     79       break;
     80     case kCondUlt:
     81       slt_op = kMipsSltu;
     82       br_op = kMipsBnez;
     83       break;
     84     case kCondUge:
     85       slt_op = kMipsSltu;
     86       br_op = kMipsBeqz;
     87       break;
     88     case kCondGe:
     89       slt_op = kMipsSlt;
     90       br_op = kMipsBeqz;
     91       break;
     92     case kCondGt:
     93       slt_op = kMipsSlt;
     94       br_op = kMipsBnez;
     95       swapped = true;
     96       break;
     97     case kCondLe:
     98       slt_op = kMipsSlt;
     99       br_op = kMipsBeqz;
    100       swapped = true;
    101       break;
    102     case kCondLt:
    103       slt_op = kMipsSlt;
    104       br_op = kMipsBnez;
    105       break;
    106     case kCondHi:  // Gtu
    107       slt_op = kMipsSltu;
    108       br_op = kMipsBnez;
    109       swapped = true;
    110       break;
    111     default:
    112       LOG(FATAL) << "No support for ConditionCode: " << cond;
    113       return NULL;
    114   }
    115   if (cmp_zero) {
    116     branch = NewLIR2(br_op, src1.GetReg(), src2.GetReg());
    117   } else {
    118     RegStorage t_reg = AllocTemp();
    119     if (swapped) {
    120       NewLIR3(slt_op, t_reg.GetReg(), src2.GetReg(), src1.GetReg());
    121     } else {
    122       NewLIR3(slt_op, t_reg.GetReg(), src1.GetReg(), src2.GetReg());
    123     }
    124     branch = NewLIR1(br_op, t_reg.GetReg());
    125     FreeTemp(t_reg);
    126   }
    127   branch->target = target;
    128   return branch;
    129 }
    130 
    131 LIR* MipsMir2Lir::OpCmpImmBranch(ConditionCode cond, RegStorage reg, int check_value, LIR* target) {
    132   LIR* branch;
    133   if (check_value != 0) {
    134     // TUNING: handle s16 & kCondLt/Mi case using slti
    135     RegStorage t_reg = AllocTemp();
    136     LoadConstant(t_reg, check_value);
    137     branch = OpCmpBranch(cond, reg, t_reg, target);
    138     FreeTemp(t_reg);
    139     return branch;
    140   }
    141   MipsOpCode opc;
    142   switch (cond) {
    143     case kCondEq: opc = kMipsBeqz; break;
    144     case kCondGe: opc = kMipsBgez; break;
    145     case kCondGt: opc = kMipsBgtz; break;
    146     case kCondLe: opc = kMipsBlez; break;
    147     // case KCondMi:
    148     case kCondLt: opc = kMipsBltz; break;
    149     case kCondNe: opc = kMipsBnez; break;
    150     default:
    151       // Tuning: use slti when applicable
    152       RegStorage t_reg = AllocTemp();
    153       LoadConstant(t_reg, check_value);
    154       branch = OpCmpBranch(cond, reg, t_reg, target);
    155       FreeTemp(t_reg);
    156       return branch;
    157   }
    158   branch = NewLIR1(opc, reg.GetReg());
    159   branch->target = target;
    160   return branch;
    161 }
    162 
    163 LIR* MipsMir2Lir::OpRegCopyNoInsert(RegStorage r_dest, RegStorage r_src) {
    164   // If src or dest is a pair, we'll be using low reg.
    165   if (r_dest.IsPair()) {
    166     r_dest = r_dest.GetLow();
    167   }
    168   if (r_src.IsPair()) {
    169     r_src = r_src.GetLow();
    170   }
    171   if (r_dest.IsFloat() || r_src.IsFloat())
    172     return OpFpRegCopy(r_dest, r_src);
    173   LIR* res = RawLIR(current_dalvik_offset_, kMipsMove,
    174             r_dest.GetReg(), r_src.GetReg());
    175   if (!(cu_->disable_opt & (1 << kSafeOptimizations)) && r_dest == r_src) {
    176     res->flags.is_nop = true;
    177   }
    178   return res;
    179 }
    180 
    181 void MipsMir2Lir::OpRegCopy(RegStorage r_dest, RegStorage r_src) {
    182   if (r_dest != r_src) {
    183     LIR *res = OpRegCopyNoInsert(r_dest, r_src);
    184     AppendLIR(res);
    185   }
    186 }
    187 
    188 void MipsMir2Lir::OpRegCopyWide(RegStorage r_dest, RegStorage r_src) {
    189   if (r_dest != r_src) {
    190     bool dest_fp = r_dest.IsFloat();
    191     bool src_fp = r_src.IsFloat();
    192     if (dest_fp) {
    193       if (src_fp) {
    194         OpRegCopy(r_dest, r_src);
    195       } else {
    196          /* note the operands are swapped for the mtc1 instr */
    197         NewLIR2(kMipsMtc1, r_src.GetLowReg(), r_dest.GetLowReg());
    198         NewLIR2(kMipsMtc1, r_src.GetHighReg(), r_dest.GetHighReg());
    199       }
    200     } else {
    201       if (src_fp) {
    202         NewLIR2(kMipsMfc1, r_dest.GetLowReg(), r_src.GetLowReg());
    203         NewLIR2(kMipsMfc1, r_dest.GetHighReg(), r_src.GetHighReg());
    204       } else {
    205         // Handle overlap
    206         if (r_src.GetHighReg() == r_dest.GetLowReg()) {
    207           OpRegCopy(r_dest.GetHigh(), r_src.GetHigh());
    208           OpRegCopy(r_dest.GetLow(), r_src.GetLow());
    209         } else {
    210           OpRegCopy(r_dest.GetLow(), r_src.GetLow());
    211           OpRegCopy(r_dest.GetHigh(), r_src.GetHigh());
    212         }
    213       }
    214     }
    215   }
    216 }
    217 
    218 void MipsMir2Lir::GenSelectConst32(RegStorage left_op, RegStorage right_op, ConditionCode code,
    219                                    int32_t true_val, int32_t false_val, RegStorage rs_dest,
    220                                    int dest_reg_class) {
    221   // Implement as a branch-over.
    222   // TODO: Conditional move?
    223   LoadConstant(rs_dest, false_val);  // Favors false.
    224   LIR* ne_branchover = OpCmpBranch(code, left_op, right_op, NULL);
    225   LoadConstant(rs_dest, true_val);
    226   LIR* target_label = NewLIR0(kPseudoTargetLabel);
    227   ne_branchover->target = target_label;
    228 }
    229 
    230 void MipsMir2Lir::GenSelect(BasicBlock* bb, MIR* mir) {
    231   UNIMPLEMENTED(FATAL) << "Need codegen for select";
    232 }
    233 
    234 void MipsMir2Lir::GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir) {
    235   UNIMPLEMENTED(FATAL) << "Need codegen for fused long cmp branch";
    236 }
    237 
    238 RegLocation MipsMir2Lir::GenDivRem(RegLocation rl_dest, RegStorage reg1, RegStorage reg2,
    239                                     bool is_div) {
    240   NewLIR2(kMipsDiv, reg1.GetReg(), reg2.GetReg());
    241   RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
    242   if (is_div) {
    243     NewLIR1(kMipsMflo, rl_result.reg.GetReg());
    244   } else {
    245     NewLIR1(kMipsMfhi, rl_result.reg.GetReg());
    246   }
    247   return rl_result;
    248 }
    249 
    250 RegLocation MipsMir2Lir::GenDivRemLit(RegLocation rl_dest, RegStorage reg1, int lit,
    251                                        bool is_div) {
    252   RegStorage t_reg = AllocTemp();
    253   NewLIR3(kMipsAddiu, t_reg.GetReg(), rZERO, lit);
    254   NewLIR2(kMipsDiv, reg1.GetReg(), t_reg.GetReg());
    255   RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
    256   if (is_div) {
    257     NewLIR1(kMipsMflo, rl_result.reg.GetReg());
    258   } else {
    259     NewLIR1(kMipsMfhi, rl_result.reg.GetReg());
    260   }
    261   FreeTemp(t_reg);
    262   return rl_result;
    263 }
    264 
    265 RegLocation MipsMir2Lir::GenDivRem(RegLocation rl_dest, RegLocation rl_src1,
    266                       RegLocation rl_src2, bool is_div, bool check_zero) {
    267   LOG(FATAL) << "Unexpected use of GenDivRem for Mips";
    268   return rl_dest;
    269 }
    270 
    271 RegLocation MipsMir2Lir::GenDivRemLit(RegLocation rl_dest, RegLocation rl_src1, int lit, bool is_div) {
    272   LOG(FATAL) << "Unexpected use of GenDivRemLit for Mips";
    273   return rl_dest;
    274 }
    275 
    276 bool MipsMir2Lir::GenInlinedCas(CallInfo* info, bool is_long, bool is_object) {
    277   DCHECK_NE(cu_->instruction_set, kThumb2);
    278   return false;
    279 }
    280 
    281 bool MipsMir2Lir::GenInlinedAbsFloat(CallInfo* info) {
    282   // TODO - add Mips implementation
    283   return false;
    284 }
    285 
    286 bool MipsMir2Lir::GenInlinedAbsDouble(CallInfo* info) {
    287   // TODO - add Mips implementation
    288   return false;
    289 }
    290 
    291 bool MipsMir2Lir::GenInlinedSqrt(CallInfo* info) {
    292   DCHECK_NE(cu_->instruction_set, kThumb2);
    293   return false;
    294 }
    295 
    296 bool MipsMir2Lir::GenInlinedPeek(CallInfo* info, OpSize size) {
    297   if (size != kSignedByte) {
    298     // MIPS supports only aligned access. Defer unaligned access to JNI implementation.
    299     return false;
    300   }
    301   RegLocation rl_src_address = info->args[0];  // long address
    302   rl_src_address = NarrowRegLoc(rl_src_address);  // ignore high half in info->args[1]
    303   RegLocation rl_dest = InlineTarget(info);
    304   RegLocation rl_address = LoadValue(rl_src_address, kCoreReg);
    305   RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
    306   DCHECK(size == kSignedByte);
    307   LoadBaseDisp(rl_address.reg, 0, rl_result.reg, size, kNotVolatile);
    308   StoreValue(rl_dest, rl_result);
    309   return true;
    310 }
    311 
    312 bool MipsMir2Lir::GenInlinedPoke(CallInfo* info, OpSize size) {
    313   if (size != kSignedByte) {
    314     // MIPS supports only aligned access. Defer unaligned access to JNI implementation.
    315     return false;
    316   }
    317   RegLocation rl_src_address = info->args[0];  // long address
    318   rl_src_address = NarrowRegLoc(rl_src_address);  // ignore high half in info->args[1]
    319   RegLocation rl_src_value = info->args[2];  // [size] value
    320   RegLocation rl_address = LoadValue(rl_src_address, kCoreReg);
    321   DCHECK(size == kSignedByte);
    322   RegLocation rl_value = LoadValue(rl_src_value, kCoreReg);
    323   StoreBaseDisp(rl_address.reg, 0, rl_value.reg, size, kNotVolatile);
    324   return true;
    325 }
    326 
    327 LIR* MipsMir2Lir::OpPcRelLoad(RegStorage reg, LIR* target) {
    328   LOG(FATAL) << "Unexpected use of OpPcRelLoad for Mips";
    329   return NULL;
    330 }
    331 
    332 LIR* MipsMir2Lir::OpVldm(RegStorage r_base, int count) {
    333   LOG(FATAL) << "Unexpected use of OpVldm for Mips";
    334   return NULL;
    335 }
    336 
    337 LIR* MipsMir2Lir::OpVstm(RegStorage r_base, int count) {
    338   LOG(FATAL) << "Unexpected use of OpVstm for Mips";
    339   return NULL;
    340 }
    341 
    342 void MipsMir2Lir::GenMultiplyByTwoBitMultiplier(RegLocation rl_src,
    343                                                 RegLocation rl_result, int lit,
    344                                                 int first_bit, int second_bit) {
    345   RegStorage t_reg = AllocTemp();
    346   OpRegRegImm(kOpLsl, t_reg, rl_src.reg, second_bit - first_bit);
    347   OpRegRegReg(kOpAdd, rl_result.reg, rl_src.reg, t_reg);
    348   FreeTemp(t_reg);
    349   if (first_bit != 0) {
    350     OpRegRegImm(kOpLsl, rl_result.reg, rl_result.reg, first_bit);
    351   }
    352 }
    353 
    354 void MipsMir2Lir::GenDivZeroCheckWide(RegStorage reg) {
    355   DCHECK(reg.IsPair());   // TODO: support k64BitSolo.
    356   RegStorage t_reg = AllocTemp();
    357   OpRegRegReg(kOpOr, t_reg, reg.GetLow(), reg.GetHigh());
    358   GenDivZeroCheck(t_reg);
    359   FreeTemp(t_reg);
    360 }
    361 
    362 // Test suspend flag, return target of taken suspend branch
    363 LIR* MipsMir2Lir::OpTestSuspend(LIR* target) {
    364   OpRegImm(kOpSub, rs_rMIPS_SUSPEND, 1);
    365   return OpCmpImmBranch((target == NULL) ? kCondEq : kCondNe, rs_rMIPS_SUSPEND, 0, target);
    366 }
    367 
    368 // Decrement register and branch on condition
    369 LIR* MipsMir2Lir::OpDecAndBranch(ConditionCode c_code, RegStorage reg, LIR* target) {
    370   OpRegImm(kOpSub, reg, 1);
    371   return OpCmpImmBranch(c_code, reg, 0, target);
    372 }
    373 
    374 bool MipsMir2Lir::SmallLiteralDivRem(Instruction::Code dalvik_opcode, bool is_div,
    375                                      RegLocation rl_src, RegLocation rl_dest, int lit) {
    376   LOG(FATAL) << "Unexpected use of smallLiteralDive in Mips";
    377   return false;
    378 }
    379 
    380 bool MipsMir2Lir::EasyMultiply(RegLocation rl_src, RegLocation rl_dest, int lit) {
    381   LOG(FATAL) << "Unexpected use of easyMultiply in Mips";
    382   return false;
    383 }
    384 
    385 LIR* MipsMir2Lir::OpIT(ConditionCode cond, const char* guide) {
    386   LOG(FATAL) << "Unexpected use of OpIT in Mips";
    387   return NULL;
    388 }
    389 
    390 void MipsMir2Lir::OpEndIT(LIR* it) {
    391   LOG(FATAL) << "Unexpected use of OpEndIT in Mips";
    392 }
    393 
    394 
    395 void MipsMir2Lir::GenAddLong(Instruction::Code opcode, RegLocation rl_dest,
    396                              RegLocation rl_src1, RegLocation rl_src2) {
    397   rl_src1 = LoadValueWide(rl_src1, kCoreReg);
    398   rl_src2 = LoadValueWide(rl_src2, kCoreReg);
    399   RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
    400   /*
    401    *  [v1 v0] =  [a1 a0] + [a3 a2];
    402    *  addu v0,a2,a0
    403    *  addu t1,a3,a1
    404    *  sltu v1,v0,a2
    405    *  addu v1,v1,t1
    406    */
    407 
    408   OpRegRegReg(kOpAdd, rl_result.reg.GetLow(), rl_src2.reg.GetLow(), rl_src1.reg.GetLow());
    409   RegStorage t_reg = AllocTemp();
    410   OpRegRegReg(kOpAdd, t_reg, rl_src2.reg.GetHigh(), rl_src1.reg.GetHigh());
    411   NewLIR3(kMipsSltu, rl_result.reg.GetHighReg(), rl_result.reg.GetLowReg(), rl_src2.reg.GetLowReg());
    412   OpRegRegReg(kOpAdd, rl_result.reg.GetHigh(), rl_result.reg.GetHigh(), t_reg);
    413   FreeTemp(t_reg);
    414   StoreValueWide(rl_dest, rl_result);
    415 }
    416 
    417 void MipsMir2Lir::GenSubLong(Instruction::Code opcode, RegLocation rl_dest,
    418                              RegLocation rl_src1, RegLocation rl_src2) {
    419   rl_src1 = LoadValueWide(rl_src1, kCoreReg);
    420   rl_src2 = LoadValueWide(rl_src2, kCoreReg);
    421   RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
    422   /*
    423    *  [v1 v0] =  [a1 a0] - [a3 a2];
    424    *  sltu  t1,a0,a2
    425    *  subu  v0,a0,a2
    426    *  subu  v1,a1,a3
    427    *  subu  v1,v1,t1
    428    */
    429 
    430   RegStorage t_reg = AllocTemp();
    431   NewLIR3(kMipsSltu, t_reg.GetReg(), rl_src1.reg.GetLowReg(), rl_src2.reg.GetLowReg());
    432   OpRegRegReg(kOpSub, rl_result.reg.GetLow(), rl_src1.reg.GetLow(), rl_src2.reg.GetLow());
    433   OpRegRegReg(kOpSub, rl_result.reg.GetHigh(), rl_src1.reg.GetHigh(), rl_src2.reg.GetHigh());
    434   OpRegRegReg(kOpSub, rl_result.reg.GetHigh(), rl_result.reg.GetHigh(), t_reg);
    435   FreeTemp(t_reg);
    436   StoreValueWide(rl_dest, rl_result);
    437 }
    438 
    439 void MipsMir2Lir::GenArithOpLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1,
    440                                  RegLocation rl_src2) {
    441   switch (opcode) {
    442     case Instruction::ADD_LONG:
    443     case Instruction::ADD_LONG_2ADDR:
    444       GenAddLong(opcode, rl_dest, rl_src1, rl_src2);
    445       return;
    446     case Instruction::SUB_LONG:
    447     case Instruction::SUB_LONG_2ADDR:
    448       GenSubLong(opcode, rl_dest, rl_src1, rl_src2);
    449       return;
    450     case Instruction::NEG_LONG:
    451       GenNegLong(rl_dest, rl_src2);
    452       return;
    453 
    454     default:
    455       break;
    456   }
    457 
    458   // Fallback for all other ops.
    459   Mir2Lir::GenArithOpLong(opcode, rl_dest, rl_src1, rl_src2);
    460 }
    461 
    462 void MipsMir2Lir::GenNegLong(RegLocation rl_dest, RegLocation rl_src) {
    463   rl_src = LoadValueWide(rl_src, kCoreReg);
    464   RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
    465   /*
    466    *  [v1 v0] =  -[a1 a0]
    467    *  negu  v0,a0
    468    *  negu  v1,a1
    469    *  sltu  t1,r_zero
    470    *  subu  v1,v1,t1
    471    */
    472 
    473   OpRegReg(kOpNeg, rl_result.reg.GetLow(), rl_src.reg.GetLow());
    474   OpRegReg(kOpNeg, rl_result.reg.GetHigh(), rl_src.reg.GetHigh());
    475   RegStorage t_reg = AllocTemp();
    476   NewLIR3(kMipsSltu, t_reg.GetReg(), rZERO, rl_result.reg.GetLowReg());
    477   OpRegRegReg(kOpSub, rl_result.reg.GetHigh(), rl_result.reg.GetHigh(), t_reg);
    478   FreeTemp(t_reg);
    479   StoreValueWide(rl_dest, rl_result);
    480 }
    481 
    482 /*
    483  * Generate array load
    484  */
    485 void MipsMir2Lir::GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array,
    486                           RegLocation rl_index, RegLocation rl_dest, int scale) {
    487   RegisterClass reg_class = RegClassBySize(size);
    488   int len_offset = mirror::Array::LengthOffset().Int32Value();
    489   int data_offset;
    490   RegLocation rl_result;
    491   rl_array = LoadValue(rl_array, kRefReg);
    492   rl_index = LoadValue(rl_index, kCoreReg);
    493 
    494   // FIXME: need to add support for rl_index.is_const.
    495 
    496   if (size == k64 || size == kDouble) {
    497     data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Int32Value();
    498   } else {
    499     data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Int32Value();
    500   }
    501 
    502   /* null object? */
    503   GenNullCheck(rl_array.reg, opt_flags);
    504 
    505   RegStorage reg_ptr = AllocTemp();
    506   bool needs_range_check = (!(opt_flags & MIR_IGNORE_RANGE_CHECK));
    507   RegStorage reg_len;
    508   if (needs_range_check) {
    509     reg_len = AllocTemp();
    510     /* Get len */
    511     Load32Disp(rl_array.reg, len_offset, reg_len);
    512   }
    513   /* reg_ptr -> array data */
    514   OpRegRegImm(kOpAdd, reg_ptr, rl_array.reg, data_offset);
    515   FreeTemp(rl_array.reg);
    516   if ((size == k64) || (size == kDouble)) {
    517     if (scale) {
    518       RegStorage r_new_index = AllocTemp();
    519       OpRegRegImm(kOpLsl, r_new_index, rl_index.reg, scale);
    520       OpRegReg(kOpAdd, reg_ptr, r_new_index);
    521       FreeTemp(r_new_index);
    522     } else {
    523       OpRegReg(kOpAdd, reg_ptr, rl_index.reg);
    524     }
    525     FreeTemp(rl_index.reg);
    526     rl_result = EvalLoc(rl_dest, reg_class, true);
    527 
    528     if (needs_range_check) {
    529       GenArrayBoundsCheck(rl_index.reg, reg_len);
    530       FreeTemp(reg_len);
    531     }
    532     LoadBaseDisp(reg_ptr, 0, rl_result.reg, size, kNotVolatile);
    533 
    534     FreeTemp(reg_ptr);
    535     StoreValueWide(rl_dest, rl_result);
    536   } else {
    537     rl_result = EvalLoc(rl_dest, reg_class, true);
    538 
    539     if (needs_range_check) {
    540       GenArrayBoundsCheck(rl_index.reg, reg_len);
    541       FreeTemp(reg_len);
    542     }
    543     LoadBaseIndexed(reg_ptr, rl_index.reg, rl_result.reg, scale, size);
    544 
    545     FreeTemp(reg_ptr);
    546     StoreValue(rl_dest, rl_result);
    547   }
    548 }
    549 
    550 /*
    551  * Generate array store
    552  *
    553  */
    554 void MipsMir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array,
    555                           RegLocation rl_index, RegLocation rl_src, int scale, bool card_mark) {
    556   RegisterClass reg_class = RegClassBySize(size);
    557   int len_offset = mirror::Array::LengthOffset().Int32Value();
    558   int data_offset;
    559 
    560   if (size == k64 || size == kDouble) {
    561     data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Int32Value();
    562   } else {
    563     data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Int32Value();
    564   }
    565 
    566   rl_array = LoadValue(rl_array, kRefReg);
    567   rl_index = LoadValue(rl_index, kCoreReg);
    568 
    569   // FIXME: need to add support for rl_index.is_const.
    570 
    571   RegStorage reg_ptr;
    572   bool allocated_reg_ptr_temp = false;
    573   if (IsTemp(rl_array.reg) && !card_mark) {
    574     Clobber(rl_array.reg);
    575     reg_ptr = rl_array.reg;
    576   } else {
    577     reg_ptr = AllocTemp();
    578     OpRegCopy(reg_ptr, rl_array.reg);
    579     allocated_reg_ptr_temp = true;
    580   }
    581 
    582   /* null object? */
    583   GenNullCheck(rl_array.reg, opt_flags);
    584 
    585   bool needs_range_check = (!(opt_flags & MIR_IGNORE_RANGE_CHECK));
    586   RegStorage reg_len;
    587   if (needs_range_check) {
    588     reg_len = AllocTemp();
    589     // NOTE: max live temps(4) here.
    590     /* Get len */
    591     Load32Disp(rl_array.reg, len_offset, reg_len);
    592   }
    593   /* reg_ptr -> array data */
    594   OpRegImm(kOpAdd, reg_ptr, data_offset);
    595   /* at this point, reg_ptr points to array, 2 live temps */
    596   if ((size == k64) || (size == kDouble)) {
    597     // TUNING: specific wide routine that can handle fp regs
    598     if (scale) {
    599       RegStorage r_new_index = AllocTemp();
    600       OpRegRegImm(kOpLsl, r_new_index, rl_index.reg, scale);
    601       OpRegReg(kOpAdd, reg_ptr, r_new_index);
    602       FreeTemp(r_new_index);
    603     } else {
    604       OpRegReg(kOpAdd, reg_ptr, rl_index.reg);
    605     }
    606     rl_src = LoadValueWide(rl_src, reg_class);
    607 
    608     if (needs_range_check) {
    609       GenArrayBoundsCheck(rl_index.reg, reg_len);
    610       FreeTemp(reg_len);
    611     }
    612 
    613     StoreBaseDisp(reg_ptr, 0, rl_src.reg, size, kNotVolatile);
    614   } else {
    615     rl_src = LoadValue(rl_src, reg_class);
    616     if (needs_range_check) {
    617        GenArrayBoundsCheck(rl_index.reg, reg_len);
    618       FreeTemp(reg_len);
    619     }
    620     StoreBaseIndexed(reg_ptr, rl_index.reg, rl_src.reg, scale, size);
    621   }
    622   if (allocated_reg_ptr_temp) {
    623     FreeTemp(reg_ptr);
    624   }
    625   if (card_mark) {
    626     MarkGCCard(rl_src.reg, rl_array.reg);
    627   }
    628 }
    629 
    630 void MipsMir2Lir::GenShiftImmOpLong(Instruction::Code opcode, RegLocation rl_dest,
    631                                     RegLocation rl_src1, RegLocation rl_shift) {
    632   // Default implementation is just to ignore the constant case.
    633   GenShiftOpLong(opcode, rl_dest, rl_src1, rl_shift);
    634 }
    635 
    636 void MipsMir2Lir::GenArithImmOpLong(Instruction::Code opcode,
    637                                     RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2) {
    638   // Default - bail to non-const handler.
    639   GenArithOpLong(opcode, rl_dest, rl_src1, rl_src2);
    640 }
    641 
    642 }  // namespace art
    643