fp_arm.cc revision 2700f7e1edbcd2518f4978e4cd0e05a4149f91b6
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 "arm_lir.h" 18#include "codegen_arm.h" 19#include "dex/quick/mir_to_lir-inl.h" 20 21namespace art { 22 23void ArmMir2Lir::GenArithOpFloat(Instruction::Code opcode, RegLocation rl_dest, 24 RegLocation rl_src1, RegLocation rl_src2) { 25 int op = kThumbBkpt; 26 RegLocation rl_result; 27 28 /* 29 * Don't attempt to optimize register usage since these opcodes call out to 30 * the handlers. 31 */ 32 switch (opcode) { 33 case Instruction::ADD_FLOAT_2ADDR: 34 case Instruction::ADD_FLOAT: 35 op = kThumb2Vadds; 36 break; 37 case Instruction::SUB_FLOAT_2ADDR: 38 case Instruction::SUB_FLOAT: 39 op = kThumb2Vsubs; 40 break; 41 case Instruction::DIV_FLOAT_2ADDR: 42 case Instruction::DIV_FLOAT: 43 op = kThumb2Vdivs; 44 break; 45 case Instruction::MUL_FLOAT_2ADDR: 46 case Instruction::MUL_FLOAT: 47 op = kThumb2Vmuls; 48 break; 49 case Instruction::REM_FLOAT_2ADDR: 50 case Instruction::REM_FLOAT: 51 FlushAllRegs(); // Send everything to home location 52 CallRuntimeHelperRegLocationRegLocation(QUICK_ENTRYPOINT_OFFSET(pFmodf), rl_src1, rl_src2, 53 false); 54 rl_result = GetReturn(true); 55 StoreValue(rl_dest, rl_result); 56 return; 57 case Instruction::NEG_FLOAT: 58 GenNegFloat(rl_dest, rl_src1); 59 return; 60 default: 61 LOG(FATAL) << "Unexpected opcode: " << opcode; 62 } 63 rl_src1 = LoadValue(rl_src1, kFPReg); 64 rl_src2 = LoadValue(rl_src2, kFPReg); 65 rl_result = EvalLoc(rl_dest, kFPReg, true); 66 NewLIR3(op, rl_result.reg.GetReg(), rl_src1.reg.GetReg(), rl_src2.reg.GetReg()); 67 StoreValue(rl_dest, rl_result); 68} 69 70void ArmMir2Lir::GenArithOpDouble(Instruction::Code opcode, 71 RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2) { 72 int op = kThumbBkpt; 73 RegLocation rl_result; 74 75 switch (opcode) { 76 case Instruction::ADD_DOUBLE_2ADDR: 77 case Instruction::ADD_DOUBLE: 78 op = kThumb2Vaddd; 79 break; 80 case Instruction::SUB_DOUBLE_2ADDR: 81 case Instruction::SUB_DOUBLE: 82 op = kThumb2Vsubd; 83 break; 84 case Instruction::DIV_DOUBLE_2ADDR: 85 case Instruction::DIV_DOUBLE: 86 op = kThumb2Vdivd; 87 break; 88 case Instruction::MUL_DOUBLE_2ADDR: 89 case Instruction::MUL_DOUBLE: 90 op = kThumb2Vmuld; 91 break; 92 case Instruction::REM_DOUBLE_2ADDR: 93 case Instruction::REM_DOUBLE: 94 FlushAllRegs(); // Send everything to home location 95 CallRuntimeHelperRegLocationRegLocation(QUICK_ENTRYPOINT_OFFSET(pFmod), rl_src1, rl_src2, 96 false); 97 rl_result = GetReturnWide(true); 98 StoreValueWide(rl_dest, rl_result); 99 return; 100 case Instruction::NEG_DOUBLE: 101 GenNegDouble(rl_dest, rl_src1); 102 return; 103 default: 104 LOG(FATAL) << "Unexpected opcode: " << opcode; 105 } 106 107 rl_src1 = LoadValueWide(rl_src1, kFPReg); 108 DCHECK(rl_src1.wide); 109 rl_src2 = LoadValueWide(rl_src2, kFPReg); 110 DCHECK(rl_src2.wide); 111 rl_result = EvalLoc(rl_dest, kFPReg, true); 112 DCHECK(rl_dest.wide); 113 DCHECK(rl_result.wide); 114 NewLIR3(op, S2d(rl_result.reg.GetLowReg(), rl_result.reg.GetHighReg()), S2d(rl_src1.reg.GetLowReg(), rl_src1.reg.GetHighReg()), 115 S2d(rl_src2.reg.GetLowReg(), rl_src2.reg.GetHighReg())); 116 StoreValueWide(rl_dest, rl_result); 117} 118 119void ArmMir2Lir::GenConversion(Instruction::Code opcode, 120 RegLocation rl_dest, RegLocation rl_src) { 121 int op = kThumbBkpt; 122 int src_reg; 123 RegLocation rl_result; 124 125 switch (opcode) { 126 case Instruction::INT_TO_FLOAT: 127 op = kThumb2VcvtIF; 128 break; 129 case Instruction::FLOAT_TO_INT: 130 op = kThumb2VcvtFI; 131 break; 132 case Instruction::DOUBLE_TO_FLOAT: 133 op = kThumb2VcvtDF; 134 break; 135 case Instruction::FLOAT_TO_DOUBLE: 136 op = kThumb2VcvtFd; 137 break; 138 case Instruction::INT_TO_DOUBLE: 139 op = kThumb2VcvtF64S32; 140 break; 141 case Instruction::DOUBLE_TO_INT: 142 op = kThumb2VcvtDI; 143 break; 144 case Instruction::LONG_TO_DOUBLE: { 145 rl_src = LoadValueWide(rl_src, kFPReg); 146 src_reg = S2d(rl_src.reg.GetLowReg(), rl_src.reg.GetHighReg()); 147 rl_result = EvalLoc(rl_dest, kFPReg, true); 148 // TODO: fix AllocTempDouble to return a k64BitSolo double reg and lose the ARM_FP_DOUBLE. 149 RegStorage tmp1 = AllocTempDouble(); 150 RegStorage tmp2 = AllocTempDouble(); 151 152 // FIXME: needs 64-bit register cleanup. 153 NewLIR2(kThumb2VcvtF64S32, tmp1.GetLowReg() | ARM_FP_DOUBLE, (src_reg & ~ARM_FP_DOUBLE) + 1); 154 NewLIR2(kThumb2VcvtF64U32, S2d(rl_result.reg.GetLowReg(), rl_result.reg.GetHighReg()), 155 (src_reg & ~ARM_FP_DOUBLE)); 156 LoadConstantWide(tmp2, 0x41f0000000000000LL); 157 NewLIR3(kThumb2VmlaF64, S2d(rl_result.reg.GetLowReg(), rl_result.reg.GetHighReg()), 158 tmp1.GetLowReg() | ARM_FP_DOUBLE, tmp2.GetLowReg() | ARM_FP_DOUBLE); 159 FreeTemp(tmp1); 160 FreeTemp(tmp2); 161 StoreValueWide(rl_dest, rl_result); 162 return; 163 } 164 case Instruction::FLOAT_TO_LONG: 165 GenConversionCall(QUICK_ENTRYPOINT_OFFSET(pF2l), rl_dest, rl_src); 166 return; 167 case Instruction::LONG_TO_FLOAT: { 168 rl_src = LoadValueWide(rl_src, kFPReg); 169 src_reg = S2d(rl_src.reg.GetLowReg(), rl_src.reg.GetHighReg()); 170 rl_result = EvalLoc(rl_dest, kFPReg, true); 171 // Allocate temp registers. 172 RegStorage high_val = AllocTempDouble(); 173 RegStorage low_val = AllocTempDouble(); 174 RegStorage const_val = AllocTempDouble(); 175 // Long to double. 176 NewLIR2(kThumb2VcvtF64S32, high_val.GetLowReg() | ARM_FP_DOUBLE, 177 (src_reg & ~ARM_FP_DOUBLE) + 1); 178 NewLIR2(kThumb2VcvtF64U32, low_val.GetLowReg() | ARM_FP_DOUBLE, 179 (src_reg & ~ARM_FP_DOUBLE)); 180 LoadConstantWide(const_val, INT64_C(0x41f0000000000000)); 181 NewLIR3(kThumb2VmlaF64, low_val.GetLowReg() | ARM_FP_DOUBLE, 182 high_val.GetLowReg() | ARM_FP_DOUBLE, 183 const_val.GetLowReg() | ARM_FP_DOUBLE); 184 // Double to float. 185 NewLIR2(kThumb2VcvtDF, rl_result.reg.GetReg(), low_val.GetLowReg() | ARM_FP_DOUBLE); 186 // Free temp registers. 187 FreeTemp(high_val); 188 FreeTemp(low_val); 189 FreeTemp(const_val); 190 // Store result. 191 StoreValue(rl_dest, rl_result); 192 return; 193 } 194 case Instruction::DOUBLE_TO_LONG: 195 GenConversionCall(QUICK_ENTRYPOINT_OFFSET(pD2l), rl_dest, rl_src); 196 return; 197 default: 198 LOG(FATAL) << "Unexpected opcode: " << opcode; 199 } 200 if (rl_src.wide) { 201 rl_src = LoadValueWide(rl_src, kFPReg); 202 src_reg = S2d(rl_src.reg.GetLowReg(), rl_src.reg.GetHighReg()); 203 } else { 204 rl_src = LoadValue(rl_src, kFPReg); 205 src_reg = rl_src.reg.GetReg(); 206 } 207 if (rl_dest.wide) { 208 rl_result = EvalLoc(rl_dest, kFPReg, true); 209 NewLIR2(op, S2d(rl_result.reg.GetLowReg(), rl_result.reg.GetHighReg()), src_reg); 210 StoreValueWide(rl_dest, rl_result); 211 } else { 212 rl_result = EvalLoc(rl_dest, kFPReg, true); 213 NewLIR2(op, rl_result.reg.GetReg(), src_reg); 214 StoreValue(rl_dest, rl_result); 215 } 216} 217 218void ArmMir2Lir::GenFusedFPCmpBranch(BasicBlock* bb, MIR* mir, bool gt_bias, 219 bool is_double) { 220 LIR* target = &block_label_list_[bb->taken]; 221 RegLocation rl_src1; 222 RegLocation rl_src2; 223 if (is_double) { 224 rl_src1 = mir_graph_->GetSrcWide(mir, 0); 225 rl_src2 = mir_graph_->GetSrcWide(mir, 2); 226 rl_src1 = LoadValueWide(rl_src1, kFPReg); 227 rl_src2 = LoadValueWide(rl_src2, kFPReg); 228 NewLIR2(kThumb2Vcmpd, S2d(rl_src1.reg.GetLowReg(), rl_src2.reg.GetHighReg()), 229 S2d(rl_src2.reg.GetLowReg(), rl_src2.reg.GetHighReg())); 230 } else { 231 rl_src1 = mir_graph_->GetSrc(mir, 0); 232 rl_src2 = mir_graph_->GetSrc(mir, 1); 233 rl_src1 = LoadValue(rl_src1, kFPReg); 234 rl_src2 = LoadValue(rl_src2, kFPReg); 235 NewLIR2(kThumb2Vcmps, rl_src1.reg.GetReg(), rl_src2.reg.GetReg()); 236 } 237 NewLIR0(kThumb2Fmstat); 238 ConditionCode ccode = mir->meta.ccode; 239 switch (ccode) { 240 case kCondEq: 241 case kCondNe: 242 break; 243 case kCondLt: 244 if (gt_bias) { 245 ccode = kCondMi; 246 } 247 break; 248 case kCondLe: 249 if (gt_bias) { 250 ccode = kCondLs; 251 } 252 break; 253 case kCondGt: 254 if (gt_bias) { 255 ccode = kCondHi; 256 } 257 break; 258 case kCondGe: 259 if (gt_bias) { 260 ccode = kCondUge; 261 } 262 break; 263 default: 264 LOG(FATAL) << "Unexpected ccode: " << ccode; 265 } 266 OpCondBranch(ccode, target); 267} 268 269 270void ArmMir2Lir::GenCmpFP(Instruction::Code opcode, RegLocation rl_dest, 271 RegLocation rl_src1, RegLocation rl_src2) { 272 bool is_double = false; 273 int default_result = -1; 274 RegLocation rl_result; 275 276 switch (opcode) { 277 case Instruction::CMPL_FLOAT: 278 is_double = false; 279 default_result = -1; 280 break; 281 case Instruction::CMPG_FLOAT: 282 is_double = false; 283 default_result = 1; 284 break; 285 case Instruction::CMPL_DOUBLE: 286 is_double = true; 287 default_result = -1; 288 break; 289 case Instruction::CMPG_DOUBLE: 290 is_double = true; 291 default_result = 1; 292 break; 293 default: 294 LOG(FATAL) << "Unexpected opcode: " << opcode; 295 } 296 if (is_double) { 297 rl_src1 = LoadValueWide(rl_src1, kFPReg); 298 rl_src2 = LoadValueWide(rl_src2, kFPReg); 299 // In case result vreg is also a src vreg, break association to avoid useless copy by EvalLoc() 300 ClobberSReg(rl_dest.s_reg_low); 301 rl_result = EvalLoc(rl_dest, kCoreReg, true); 302 LoadConstant(rl_result.reg, default_result); 303 NewLIR2(kThumb2Vcmpd, S2d(rl_src1.reg.GetLowReg(), rl_src2.reg.GetHighReg()), 304 S2d(rl_src2.reg.GetLowReg(), rl_src2.reg.GetHighReg())); 305 } else { 306 rl_src1 = LoadValue(rl_src1, kFPReg); 307 rl_src2 = LoadValue(rl_src2, kFPReg); 308 // In case result vreg is also a srcvreg, break association to avoid useless copy by EvalLoc() 309 ClobberSReg(rl_dest.s_reg_low); 310 rl_result = EvalLoc(rl_dest, kCoreReg, true); 311 LoadConstant(rl_result.reg, default_result); 312 NewLIR2(kThumb2Vcmps, rl_src1.reg.GetReg(), rl_src2.reg.GetReg()); 313 } 314 DCHECK(!ARM_FPREG(rl_result.reg.GetReg())); 315 NewLIR0(kThumb2Fmstat); 316 317 OpIT((default_result == -1) ? kCondGt : kCondMi, ""); 318 NewLIR2(kThumb2MovI8M, rl_result.reg.GetReg(), 319 ModifiedImmediate(-default_result)); // Must not alter ccodes 320 GenBarrier(); 321 322 OpIT(kCondEq, ""); 323 LoadConstant(rl_result.reg, 0); 324 GenBarrier(); 325 326 StoreValue(rl_dest, rl_result); 327} 328 329void ArmMir2Lir::GenNegFloat(RegLocation rl_dest, RegLocation rl_src) { 330 RegLocation rl_result; 331 rl_src = LoadValue(rl_src, kFPReg); 332 rl_result = EvalLoc(rl_dest, kFPReg, true); 333 NewLIR2(kThumb2Vnegs, rl_result.reg.GetReg(), rl_src.reg.GetReg()); 334 StoreValue(rl_dest, rl_result); 335} 336 337void ArmMir2Lir::GenNegDouble(RegLocation rl_dest, RegLocation rl_src) { 338 RegLocation rl_result; 339 rl_src = LoadValueWide(rl_src, kFPReg); 340 rl_result = EvalLoc(rl_dest, kFPReg, true); 341 NewLIR2(kThumb2Vnegd, S2d(rl_result.reg.GetLowReg(), rl_result.reg.GetHighReg()), 342 S2d(rl_src.reg.GetLowReg(), rl_src.reg.GetHighReg())); 343 StoreValueWide(rl_dest, rl_result); 344} 345 346bool ArmMir2Lir::GenInlinedSqrt(CallInfo* info) { 347 DCHECK_EQ(cu_->instruction_set, kThumb2); 348 LIR *branch; 349 RegLocation rl_src = info->args[0]; 350 RegLocation rl_dest = InlineTargetWide(info); // double place for result 351 rl_src = LoadValueWide(rl_src, kFPReg); 352 RegLocation rl_result = EvalLoc(rl_dest, kFPReg, true); 353 // TODO: shouldn't need S2d once 64bitSolo has proper double tag bit. 354 NewLIR2(kThumb2Vsqrtd, S2d(rl_result.reg.GetLowReg(), rl_result.reg.GetHighReg()), 355 S2d(rl_src.reg.GetLowReg(), rl_src.reg.GetHighReg())); 356 NewLIR2(kThumb2Vcmpd, S2d(rl_result.reg.GetLowReg(), rl_result.reg.GetHighReg()), 357 S2d(rl_result.reg.GetLowReg(), rl_result.reg.GetHighReg())); 358 NewLIR0(kThumb2Fmstat); 359 branch = NewLIR2(kThumbBCond, 0, kArmCondEq); 360 ClobberCallerSave(); 361 LockCallTemps(); // Using fixed registers 362 RegStorage r_tgt = LoadHelper(QUICK_ENTRYPOINT_OFFSET(pSqrt)); 363 NewLIR3(kThumb2Fmrrd, r0, r1, S2d(rl_src.reg.GetLowReg(), rl_src.reg.GetHighReg())); 364 NewLIR1(kThumbBlxR, r_tgt.GetReg()); 365 NewLIR3(kThumb2Fmdrr, S2d(rl_result.reg.GetLowReg(), rl_result.reg.GetHighReg()), r0, r1); 366 branch->target = NewLIR0(kPseudoTargetLabel); 367 StoreValueWide(rl_dest, rl_result); 368 return true; 369} 370 371 372} // namespace art 373