Home | History | Annotate | Download | only in arm
      1 /*
      2  * Copyright (C) 2011 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 #include "codegen_arm.h"
     18 
     19 #include "arm_lir.h"
     20 #include "base/logging.h"
     21 #include "dex/mir_graph.h"
     22 #include "dex/quick/mir_to_lir-inl.h"
     23 
     24 namespace art {
     25 
     26 void ArmMir2Lir::GenArithOpFloat(Instruction::Code opcode, RegLocation rl_dest,
     27                                  RegLocation rl_src1, RegLocation rl_src2) {
     28   int op = kThumbBkpt;
     29   RegLocation rl_result;
     30 
     31   /*
     32    * Don't attempt to optimize register usage since these opcodes call out to
     33    * the handlers.
     34    */
     35   switch (opcode) {
     36     case Instruction::ADD_FLOAT_2ADDR:
     37     case Instruction::ADD_FLOAT:
     38       op = kThumb2Vadds;
     39       break;
     40     case Instruction::SUB_FLOAT_2ADDR:
     41     case Instruction::SUB_FLOAT:
     42       op = kThumb2Vsubs;
     43       break;
     44     case Instruction::DIV_FLOAT_2ADDR:
     45     case Instruction::DIV_FLOAT:
     46       op = kThumb2Vdivs;
     47       break;
     48     case Instruction::MUL_FLOAT_2ADDR:
     49     case Instruction::MUL_FLOAT:
     50       op = kThumb2Vmuls;
     51       break;
     52     case Instruction::REM_FLOAT_2ADDR:
     53     case Instruction::REM_FLOAT:
     54       FlushAllRegs();   // Send everything to home location
     55       CallRuntimeHelperRegLocationRegLocation(kQuickFmodf, rl_src1, rl_src2, false);
     56       rl_result = GetReturn(kFPReg);
     57       StoreValue(rl_dest, rl_result);
     58       return;
     59     case Instruction::NEG_FLOAT:
     60       GenNegFloat(rl_dest, rl_src1);
     61       return;
     62     default:
     63       LOG(FATAL) << "Unexpected opcode: " << opcode;
     64   }
     65   rl_src1 = LoadValue(rl_src1, kFPReg);
     66   rl_src2 = LoadValue(rl_src2, kFPReg);
     67   rl_result = EvalLoc(rl_dest, kFPReg, true);
     68   NewLIR3(op, rl_result.reg.GetReg(), rl_src1.reg.GetReg(), rl_src2.reg.GetReg());
     69   StoreValue(rl_dest, rl_result);
     70 }
     71 
     72 void ArmMir2Lir::GenArithOpDouble(Instruction::Code opcode,
     73                                   RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2) {
     74   int op = kThumbBkpt;
     75   RegLocation rl_result;
     76 
     77   switch (opcode) {
     78     case Instruction::ADD_DOUBLE_2ADDR:
     79     case Instruction::ADD_DOUBLE:
     80       op = kThumb2Vaddd;
     81       break;
     82     case Instruction::SUB_DOUBLE_2ADDR:
     83     case Instruction::SUB_DOUBLE:
     84       op = kThumb2Vsubd;
     85       break;
     86     case Instruction::DIV_DOUBLE_2ADDR:
     87     case Instruction::DIV_DOUBLE:
     88       op = kThumb2Vdivd;
     89       break;
     90     case Instruction::MUL_DOUBLE_2ADDR:
     91     case Instruction::MUL_DOUBLE:
     92       op = kThumb2Vmuld;
     93       break;
     94     case Instruction::REM_DOUBLE_2ADDR:
     95     case Instruction::REM_DOUBLE:
     96       FlushAllRegs();   // Send everything to home location
     97       CallRuntimeHelperRegLocationRegLocation(kQuickFmod, rl_src1, rl_src2, false);
     98       rl_result = GetReturnWide(kFPReg);
     99       StoreValueWide(rl_dest, rl_result);
    100       return;
    101     case Instruction::NEG_DOUBLE:
    102       GenNegDouble(rl_dest, rl_src1);
    103       return;
    104     default:
    105       LOG(FATAL) << "Unexpected opcode: " << opcode;
    106   }
    107 
    108   rl_src1 = LoadValueWide(rl_src1, kFPReg);
    109   DCHECK(rl_src1.wide);
    110   rl_src2 = LoadValueWide(rl_src2, kFPReg);
    111   DCHECK(rl_src2.wide);
    112   rl_result = EvalLoc(rl_dest, kFPReg, true);
    113   DCHECK(rl_dest.wide);
    114   DCHECK(rl_result.wide);
    115   NewLIR3(op, rl_result.reg.GetReg(), rl_src1.reg.GetReg(), rl_src2.reg.GetReg());
    116   StoreValueWide(rl_dest, rl_result);
    117 }
    118 
    119 void ArmMir2Lir::GenMultiplyByConstantFloat(RegLocation rl_dest, RegLocation rl_src1,
    120                                             int32_t constant) {
    121   RegLocation rl_result;
    122   RegStorage r_tmp = AllocTempSingle();
    123   LoadConstantNoClobber(r_tmp, constant);
    124   rl_src1 = LoadValue(rl_src1, kFPReg);
    125   rl_result = EvalLoc(rl_dest, kFPReg, true);
    126   NewLIR3(kThumb2Vmuls, rl_result.reg.GetReg(), rl_src1.reg.GetReg(), r_tmp.GetReg());
    127   StoreValue(rl_dest, rl_result);
    128 }
    129 
    130 void ArmMir2Lir::GenMultiplyByConstantDouble(RegLocation rl_dest, RegLocation rl_src1,
    131                                              int64_t constant) {
    132   RegLocation rl_result;
    133   RegStorage r_tmp = AllocTempDouble();
    134   DCHECK(r_tmp.IsDouble());
    135   LoadConstantWide(r_tmp, constant);
    136   rl_src1 = LoadValueWide(rl_src1, kFPReg);
    137   DCHECK(rl_src1.wide);
    138   rl_result = EvalLocWide(rl_dest, kFPReg, true);
    139   DCHECK(rl_dest.wide);
    140   DCHECK(rl_result.wide);
    141   NewLIR3(kThumb2Vmuld, rl_result.reg.GetReg(), rl_src1.reg.GetReg(), r_tmp.GetReg());
    142   StoreValueWide(rl_dest, rl_result);
    143 }
    144 
    145 void ArmMir2Lir::GenConversion(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src) {
    146   int op = kThumbBkpt;
    147   int src_reg;
    148   RegLocation rl_result;
    149 
    150   switch (opcode) {
    151     case Instruction::INT_TO_FLOAT:
    152       op = kThumb2VcvtIF;
    153       break;
    154     case Instruction::FLOAT_TO_INT:
    155       op = kThumb2VcvtFI;
    156       break;
    157     case Instruction::DOUBLE_TO_FLOAT:
    158       op = kThumb2VcvtDF;
    159       break;
    160     case Instruction::FLOAT_TO_DOUBLE:
    161       op = kThumb2VcvtFd;
    162       break;
    163     case Instruction::INT_TO_DOUBLE:
    164       op = kThumb2VcvtF64S32;
    165       break;
    166     case Instruction::DOUBLE_TO_INT:
    167       op = kThumb2VcvtDI;
    168       break;
    169     case Instruction::LONG_TO_DOUBLE: {
    170       rl_src = LoadValueWide(rl_src, kFPReg);
    171       RegisterInfo* info = GetRegInfo(rl_src.reg);
    172       RegStorage src_low = info->FindMatchingView(RegisterInfo::kLowSingleStorageMask)->GetReg();
    173       DCHECK(src_low.Valid());
    174       RegStorage src_high = info->FindMatchingView(RegisterInfo::kHighSingleStorageMask)->GetReg();
    175       DCHECK(src_high.Valid());
    176       rl_result = EvalLoc(rl_dest, kFPReg, true);
    177       RegStorage tmp1 = AllocTempDouble();
    178       RegStorage tmp2 = AllocTempDouble();
    179 
    180       NewLIR2(kThumb2VcvtF64S32, tmp1.GetReg(), src_high.GetReg());
    181       NewLIR2(kThumb2VcvtF64U32, rl_result.reg.GetReg(), src_low.GetReg());
    182       LoadConstantWide(tmp2, 0x41f0000000000000LL);
    183       NewLIR3(kThumb2VmlaF64, rl_result.reg.GetReg(), tmp1.GetReg(), tmp2.GetReg());
    184       FreeTemp(tmp1);
    185       FreeTemp(tmp2);
    186       StoreValueWide(rl_dest, rl_result);
    187       return;
    188     }
    189     case Instruction::FLOAT_TO_LONG:
    190       CheckEntrypointTypes<kQuickF2l, int64_t, float>();  // int64_t -> kCoreReg
    191       GenConversionCall(kQuickF2l, rl_dest, rl_src, kCoreReg);
    192       return;
    193     case Instruction::LONG_TO_FLOAT: {
    194       rl_src = LoadValueWide(rl_src, kFPReg);
    195       RegisterInfo* info = GetRegInfo(rl_src.reg);
    196       RegStorage src_low = info->FindMatchingView(RegisterInfo::kLowSingleStorageMask)->GetReg();
    197       DCHECK(src_low.Valid());
    198       RegStorage src_high = info->FindMatchingView(RegisterInfo::kHighSingleStorageMask)->GetReg();
    199       DCHECK(src_high.Valid());
    200       rl_result = EvalLoc(rl_dest, kFPReg, true);
    201       // Allocate temp registers.
    202       RegStorage high_val = AllocTempDouble();
    203       RegStorage low_val = AllocTempDouble();
    204       RegStorage const_val = AllocTempDouble();
    205       // Long to double.
    206       NewLIR2(kThumb2VcvtF64S32, high_val.GetReg(), src_high.GetReg());
    207       NewLIR2(kThumb2VcvtF64U32, low_val.GetReg(), src_low.GetReg());
    208       LoadConstantWide(const_val, INT64_C(0x41f0000000000000));
    209       NewLIR3(kThumb2VmlaF64, low_val.GetReg(), high_val.GetReg(), const_val.GetReg());
    210       // Double to float.
    211       NewLIR2(kThumb2VcvtDF, rl_result.reg.GetReg(), low_val.GetReg());
    212       // Free temp registers.
    213       FreeTemp(high_val);
    214       FreeTemp(low_val);
    215       FreeTemp(const_val);
    216       // Store result.
    217       StoreValue(rl_dest, rl_result);
    218       return;
    219     }
    220     case Instruction::DOUBLE_TO_LONG:
    221       CheckEntrypointTypes<kQuickD2l, int64_t, double>();  // int64_t -> kCoreReg
    222       GenConversionCall(kQuickD2l, rl_dest, rl_src, kCoreReg);
    223       return;
    224     default:
    225       LOG(FATAL) << "Unexpected opcode: " << opcode;
    226   }
    227   if (rl_src.wide) {
    228     rl_src = LoadValueWide(rl_src, kFPReg);
    229     src_reg = rl_src.reg.GetReg();
    230   } else {
    231     rl_src = LoadValue(rl_src, kFPReg);
    232     src_reg = rl_src.reg.GetReg();
    233   }
    234   if (rl_dest.wide) {
    235     rl_result = EvalLoc(rl_dest, kFPReg, true);
    236     NewLIR2(op, rl_result.reg.GetReg(), src_reg);
    237     StoreValueWide(rl_dest, rl_result);
    238   } else {
    239     rl_result = EvalLoc(rl_dest, kFPReg, true);
    240     NewLIR2(op, rl_result.reg.GetReg(), src_reg);
    241     StoreValue(rl_dest, rl_result);
    242   }
    243 }
    244 
    245 void ArmMir2Lir::GenFusedFPCmpBranch(BasicBlock* bb, MIR* mir, bool gt_bias,
    246                                      bool is_double) {
    247   LIR* target = &block_label_list_[bb->taken];
    248   RegLocation rl_src1;
    249   RegLocation rl_src2;
    250   if (is_double) {
    251     rl_src1 = mir_graph_->GetSrcWide(mir, 0);
    252     rl_src2 = mir_graph_->GetSrcWide(mir, 2);
    253     rl_src1 = LoadValueWide(rl_src1, kFPReg);
    254     rl_src2 = LoadValueWide(rl_src2, kFPReg);
    255     NewLIR2(kThumb2Vcmpd, rl_src1.reg.GetReg(), rl_src2.reg.GetReg());
    256   } else {
    257     rl_src1 = mir_graph_->GetSrc(mir, 0);
    258     rl_src2 = mir_graph_->GetSrc(mir, 1);
    259     rl_src1 = LoadValue(rl_src1, kFPReg);
    260     rl_src2 = LoadValue(rl_src2, kFPReg);
    261     NewLIR2(kThumb2Vcmps, rl_src1.reg.GetReg(), rl_src2.reg.GetReg());
    262   }
    263   NewLIR0(kThumb2Fmstat);
    264   ConditionCode ccode = mir->meta.ccode;
    265   switch (ccode) {
    266     case kCondEq:
    267     case kCondNe:
    268       break;
    269     case kCondLt:
    270       if (gt_bias) {
    271         ccode = kCondMi;
    272       }
    273       break;
    274     case kCondLe:
    275       if (gt_bias) {
    276         ccode = kCondLs;
    277       }
    278       break;
    279     case kCondGt:
    280       if (gt_bias) {
    281         ccode = kCondHi;
    282       }
    283       break;
    284     case kCondGe:
    285       if (gt_bias) {
    286         ccode = kCondUge;
    287       }
    288       break;
    289     default:
    290       LOG(FATAL) << "Unexpected ccode: " << ccode;
    291   }
    292   OpCondBranch(ccode, target);
    293 }
    294 
    295 
    296 void ArmMir2Lir::GenCmpFP(Instruction::Code opcode, RegLocation rl_dest,
    297                           RegLocation rl_src1, RegLocation rl_src2) {
    298   bool is_double = false;
    299   int default_result = -1;
    300   RegLocation rl_result;
    301 
    302   switch (opcode) {
    303     case Instruction::CMPL_FLOAT:
    304       is_double = false;
    305       default_result = -1;
    306       break;
    307     case Instruction::CMPG_FLOAT:
    308       is_double = false;
    309       default_result = 1;
    310       break;
    311     case Instruction::CMPL_DOUBLE:
    312       is_double = true;
    313       default_result = -1;
    314       break;
    315     case Instruction::CMPG_DOUBLE:
    316       is_double = true;
    317       default_result = 1;
    318       break;
    319     default:
    320       LOG(FATAL) << "Unexpected opcode: " << opcode;
    321   }
    322   if (is_double) {
    323     rl_src1 = LoadValueWide(rl_src1, kFPReg);
    324     rl_src2 = LoadValueWide(rl_src2, kFPReg);
    325     // In case result vreg is also a src vreg, break association to avoid useless copy by EvalLoc()
    326     ClobberSReg(rl_dest.s_reg_low);
    327     rl_result = EvalLoc(rl_dest, kCoreReg, true);
    328     LoadConstant(rl_result.reg, default_result);
    329     NewLIR2(kThumb2Vcmpd, rl_src1.reg.GetReg(), rl_src2.reg.GetReg());
    330   } else {
    331     rl_src1 = LoadValue(rl_src1, kFPReg);
    332     rl_src2 = LoadValue(rl_src2, kFPReg);
    333     // In case result vreg is also a srcvreg, break association to avoid useless copy by EvalLoc()
    334     ClobberSReg(rl_dest.s_reg_low);
    335     rl_result = EvalLoc(rl_dest, kCoreReg, true);
    336     LoadConstant(rl_result.reg, default_result);
    337     NewLIR2(kThumb2Vcmps, rl_src1.reg.GetReg(), rl_src2.reg.GetReg());
    338   }
    339   DCHECK(!rl_result.reg.IsFloat());
    340   NewLIR0(kThumb2Fmstat);
    341 
    342   LIR* it = OpIT((default_result == -1) ? kCondGt : kCondMi, "");
    343   NewLIR2(kThumb2MovI8M, rl_result.reg.GetReg(),
    344           ModifiedImmediate(-default_result));  // Must not alter ccodes
    345   OpEndIT(it);
    346 
    347   it = OpIT(kCondEq, "");
    348   LoadConstant(rl_result.reg, 0);
    349   OpEndIT(it);
    350 
    351   StoreValue(rl_dest, rl_result);
    352 }
    353 
    354 void ArmMir2Lir::GenNegFloat(RegLocation rl_dest, RegLocation rl_src) {
    355   RegLocation rl_result;
    356   rl_src = LoadValue(rl_src, kFPReg);
    357   rl_result = EvalLoc(rl_dest, kFPReg, true);
    358   NewLIR2(kThumb2Vnegs, rl_result.reg.GetReg(), rl_src.reg.GetReg());
    359   StoreValue(rl_dest, rl_result);
    360 }
    361 
    362 void ArmMir2Lir::GenNegDouble(RegLocation rl_dest, RegLocation rl_src) {
    363   RegLocation rl_result;
    364   rl_src = LoadValueWide(rl_src, kFPReg);
    365   rl_result = EvalLoc(rl_dest, kFPReg, true);
    366   NewLIR2(kThumb2Vnegd, rl_result.reg.GetReg(), rl_src.reg.GetReg());
    367   StoreValueWide(rl_dest, rl_result);
    368 }
    369 
    370 static RegisterClass RegClassForAbsFP(RegLocation rl_src, RegLocation rl_dest) {
    371   // If src is in a core reg or, unlikely, dest has been promoted to a core reg, use core reg.
    372   if ((rl_src.location == kLocPhysReg && !rl_src.reg.IsFloat()) ||
    373       (rl_dest.location == kLocPhysReg && !rl_dest.reg.IsFloat())) {
    374     return kCoreReg;
    375   }
    376   // If src is in an fp reg or dest has been promoted to an fp reg, use fp reg.
    377   if (rl_src.location == kLocPhysReg || rl_dest.location == kLocPhysReg) {
    378     return kFPReg;
    379   }
    380   // With both src and dest in the stack frame we have to perform load+abs+store. Whether this
    381   // is faster using a core reg or fp reg depends on the particular CPU. Without further
    382   // investigation and testing we prefer core register. (If the result is subsequently used in
    383   // another fp operation, the dalvik reg will probably get promoted and that should be handled
    384   // by the cases above.)
    385   return kCoreReg;
    386 }
    387 
    388 bool ArmMir2Lir::GenInlinedAbsFloat(CallInfo* info) {
    389   if (info->result.location == kLocInvalid) {
    390     return true;  // Result is unused: inlining successful, no code generated.
    391   }
    392   RegLocation rl_dest = info->result;
    393   RegLocation rl_src = UpdateLoc(info->args[0]);
    394   RegisterClass reg_class = RegClassForAbsFP(rl_src, rl_dest);
    395   rl_src = LoadValue(rl_src, reg_class);
    396   RegLocation rl_result = EvalLoc(rl_dest, reg_class, true);
    397   if (reg_class == kFPReg) {
    398     NewLIR2(kThumb2Vabss, rl_result.reg.GetReg(), rl_src.reg.GetReg());
    399   } else {
    400     OpRegRegImm(kOpAnd, rl_result.reg, rl_src.reg, 0x7fffffff);
    401   }
    402   StoreValue(rl_dest, rl_result);
    403   return true;
    404 }
    405 
    406 bool ArmMir2Lir::GenInlinedAbsDouble(CallInfo* info) {
    407   if (info->result.location == kLocInvalid) {
    408     return true;  // Result is unused: inlining successful, no code generated.
    409   }
    410   RegLocation rl_dest = info->result;
    411   RegLocation rl_src = UpdateLocWide(info->args[0]);
    412   RegisterClass reg_class = RegClassForAbsFP(rl_src, rl_dest);
    413   rl_src = LoadValueWide(rl_src, reg_class);
    414   RegLocation rl_result = EvalLoc(rl_dest, reg_class, true);
    415   if (reg_class == kFPReg) {
    416     NewLIR2(kThumb2Vabsd, rl_result.reg.GetReg(), rl_src.reg.GetReg());
    417   } else if (rl_result.reg.GetLow().GetReg() != rl_src.reg.GetHigh().GetReg()) {
    418     // No inconvenient overlap.
    419     OpRegCopy(rl_result.reg.GetLow(), rl_src.reg.GetLow());
    420     OpRegRegImm(kOpAnd, rl_result.reg.GetHigh(), rl_src.reg.GetHigh(), 0x7fffffff);
    421   } else {
    422     // Inconvenient overlap, use a temp register to preserve the high word of the source.
    423     RegStorage rs_tmp = AllocTemp();
    424     OpRegCopy(rs_tmp, rl_src.reg.GetHigh());
    425     OpRegCopy(rl_result.reg.GetLow(), rl_src.reg.GetLow());
    426     OpRegRegImm(kOpAnd, rl_result.reg.GetHigh(), rs_tmp, 0x7fffffff);
    427     FreeTemp(rs_tmp);
    428   }
    429   StoreValueWide(rl_dest, rl_result);
    430   return true;
    431 }
    432 
    433 bool ArmMir2Lir::GenInlinedSqrt(CallInfo* info) {
    434   DCHECK_EQ(cu_->instruction_set, kThumb2);
    435   RegLocation rl_src = info->args[0];
    436   RegLocation rl_dest = InlineTargetWide(info);  // double place for result
    437   rl_src = LoadValueWide(rl_src, kFPReg);
    438   RegLocation rl_result = EvalLoc(rl_dest, kFPReg, true);
    439   NewLIR2(kThumb2Vsqrtd, rl_result.reg.GetReg(), rl_src.reg.GetReg());
    440   StoreValueWide(rl_dest, rl_result);
    441   return true;
    442 }
    443 
    444 
    445 }  // namespace art
    446