fp_arm.cc revision f9d6aede77c700118e225f8312cd888262b77862
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(4, pFmodf), rl_src1, rl_src2, 53 false); 54 rl_result = GetReturn(kFPReg); 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(4, pFmod), rl_src1, rl_src2, 96 false); 97 rl_result = GetReturnWide(kFPReg); 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, rl_result.reg.GetReg(), rl_src1.reg.GetReg(), rl_src2.reg.GetReg()); 115 StoreValueWide(rl_dest, rl_result); 116} 117 118void ArmMir2Lir::GenConversion(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src) { 119 int op = kThumbBkpt; 120 int src_reg; 121 RegLocation rl_result; 122 123 switch (opcode) { 124 case Instruction::INT_TO_FLOAT: 125 op = kThumb2VcvtIF; 126 break; 127 case Instruction::FLOAT_TO_INT: 128 op = kThumb2VcvtFI; 129 break; 130 case Instruction::DOUBLE_TO_FLOAT: 131 op = kThumb2VcvtDF; 132 break; 133 case Instruction::FLOAT_TO_DOUBLE: 134 op = kThumb2VcvtFd; 135 break; 136 case Instruction::INT_TO_DOUBLE: 137 op = kThumb2VcvtF64S32; 138 break; 139 case Instruction::DOUBLE_TO_INT: 140 op = kThumb2VcvtDI; 141 break; 142 case Instruction::LONG_TO_DOUBLE: { 143 rl_src = LoadValueWide(rl_src, kFPReg); 144 RegisterInfo* info = GetRegInfo(rl_src.reg); 145 RegStorage src_low = info->FindMatchingView(RegisterInfo::kLowSingleStorageMask)->GetReg(); 146 DCHECK(src_low.Valid()); 147 RegStorage src_high = info->FindMatchingView(RegisterInfo::kHighSingleStorageMask)->GetReg(); 148 DCHECK(src_high.Valid()); 149 rl_result = EvalLoc(rl_dest, kFPReg, true); 150 RegStorage tmp1 = AllocTempDouble(); 151 RegStorage tmp2 = AllocTempDouble(); 152 153 NewLIR2(kThumb2VcvtF64S32, tmp1.GetReg(), src_high.GetReg()); 154 NewLIR2(kThumb2VcvtF64U32, rl_result.reg.GetReg(), src_low.GetReg()); 155 LoadConstantWide(tmp2, 0x41f0000000000000LL); 156 NewLIR3(kThumb2VmlaF64, rl_result.reg.GetReg(), tmp1.GetReg(), tmp2.GetReg()); 157 FreeTemp(tmp1); 158 FreeTemp(tmp2); 159 StoreValueWide(rl_dest, rl_result); 160 return; 161 } 162 case Instruction::FLOAT_TO_LONG: 163 GenConversionCall(QUICK_ENTRYPOINT_OFFSET(4, pF2l), rl_dest, rl_src); 164 return; 165 case Instruction::LONG_TO_FLOAT: { 166 rl_src = LoadValueWide(rl_src, kFPReg); 167 RegisterInfo* info = GetRegInfo(rl_src.reg); 168 RegStorage src_low = info->FindMatchingView(RegisterInfo::kLowSingleStorageMask)->GetReg(); 169 DCHECK(src_low.Valid()); 170 RegStorage src_high = info->FindMatchingView(RegisterInfo::kHighSingleStorageMask)->GetReg(); 171 DCHECK(src_high.Valid()); 172 rl_result = EvalLoc(rl_dest, kFPReg, true); 173 // Allocate temp registers. 174 RegStorage high_val = AllocTempDouble(); 175 RegStorage low_val = AllocTempDouble(); 176 RegStorage const_val = AllocTempDouble(); 177 // Long to double. 178 NewLIR2(kThumb2VcvtF64S32, high_val.GetReg(), src_high.GetReg()); 179 NewLIR2(kThumb2VcvtF64U32, low_val.GetReg(), src_low.GetReg()); 180 LoadConstantWide(const_val, INT64_C(0x41f0000000000000)); 181 NewLIR3(kThumb2VmlaF64, low_val.GetReg(), high_val.GetReg(), const_val.GetReg()); 182 // Double to float. 183 NewLIR2(kThumb2VcvtDF, rl_result.reg.GetReg(), low_val.GetReg()); 184 // Free temp registers. 185 FreeTemp(high_val); 186 FreeTemp(low_val); 187 FreeTemp(const_val); 188 // Store result. 189 StoreValue(rl_dest, rl_result); 190 return; 191 } 192 case Instruction::DOUBLE_TO_LONG: 193 GenConversionCall(QUICK_ENTRYPOINT_OFFSET(4, pD2l), rl_dest, rl_src); 194 return; 195 default: 196 LOG(FATAL) << "Unexpected opcode: " << opcode; 197 } 198 if (rl_src.wide) { 199 rl_src = LoadValueWide(rl_src, kFPReg); 200 src_reg = rl_src.reg.GetReg(); 201 } else { 202 rl_src = LoadValue(rl_src, kFPReg); 203 src_reg = rl_src.reg.GetReg(); 204 } 205 if (rl_dest.wide) { 206 rl_result = EvalLoc(rl_dest, kFPReg, true); 207 NewLIR2(op, rl_result.reg.GetReg(), src_reg); 208 StoreValueWide(rl_dest, rl_result); 209 } else { 210 rl_result = EvalLoc(rl_dest, kFPReg, true); 211 NewLIR2(op, rl_result.reg.GetReg(), src_reg); 212 StoreValue(rl_dest, rl_result); 213 } 214} 215 216void ArmMir2Lir::GenFusedFPCmpBranch(BasicBlock* bb, MIR* mir, bool gt_bias, 217 bool is_double) { 218 LIR* target = &block_label_list_[bb->taken]; 219 RegLocation rl_src1; 220 RegLocation rl_src2; 221 if (is_double) { 222 rl_src1 = mir_graph_->GetSrcWide(mir, 0); 223 rl_src2 = mir_graph_->GetSrcWide(mir, 2); 224 rl_src1 = LoadValueWide(rl_src1, kFPReg); 225 rl_src2 = LoadValueWide(rl_src2, kFPReg); 226 NewLIR2(kThumb2Vcmpd, rl_src1.reg.GetReg(), rl_src2.reg.GetReg()); 227 } else { 228 rl_src1 = mir_graph_->GetSrc(mir, 0); 229 rl_src2 = mir_graph_->GetSrc(mir, 1); 230 rl_src1 = LoadValue(rl_src1, kFPReg); 231 rl_src2 = LoadValue(rl_src2, kFPReg); 232 NewLIR2(kThumb2Vcmps, rl_src1.reg.GetReg(), rl_src2.reg.GetReg()); 233 } 234 NewLIR0(kThumb2Fmstat); 235 ConditionCode ccode = mir->meta.ccode; 236 switch (ccode) { 237 case kCondEq: 238 case kCondNe: 239 break; 240 case kCondLt: 241 if (gt_bias) { 242 ccode = kCondMi; 243 } 244 break; 245 case kCondLe: 246 if (gt_bias) { 247 ccode = kCondLs; 248 } 249 break; 250 case kCondGt: 251 if (gt_bias) { 252 ccode = kCondHi; 253 } 254 break; 255 case kCondGe: 256 if (gt_bias) { 257 ccode = kCondUge; 258 } 259 break; 260 default: 261 LOG(FATAL) << "Unexpected ccode: " << ccode; 262 } 263 OpCondBranch(ccode, target); 264} 265 266 267void ArmMir2Lir::GenCmpFP(Instruction::Code opcode, RegLocation rl_dest, 268 RegLocation rl_src1, RegLocation rl_src2) { 269 bool is_double = false; 270 int default_result = -1; 271 RegLocation rl_result; 272 273 switch (opcode) { 274 case Instruction::CMPL_FLOAT: 275 is_double = false; 276 default_result = -1; 277 break; 278 case Instruction::CMPG_FLOAT: 279 is_double = false; 280 default_result = 1; 281 break; 282 case Instruction::CMPL_DOUBLE: 283 is_double = true; 284 default_result = -1; 285 break; 286 case Instruction::CMPG_DOUBLE: 287 is_double = true; 288 default_result = 1; 289 break; 290 default: 291 LOG(FATAL) << "Unexpected opcode: " << opcode; 292 } 293 if (is_double) { 294 rl_src1 = LoadValueWide(rl_src1, kFPReg); 295 rl_src2 = LoadValueWide(rl_src2, kFPReg); 296 // In case result vreg is also a src vreg, break association to avoid useless copy by EvalLoc() 297 ClobberSReg(rl_dest.s_reg_low); 298 rl_result = EvalLoc(rl_dest, kCoreReg, true); 299 LoadConstant(rl_result.reg, default_result); 300 NewLIR2(kThumb2Vcmpd, rl_src1.reg.GetReg(), rl_src2.reg.GetReg()); 301 } else { 302 rl_src1 = LoadValue(rl_src1, kFPReg); 303 rl_src2 = LoadValue(rl_src2, kFPReg); 304 // In case result vreg is also a srcvreg, break association to avoid useless copy by EvalLoc() 305 ClobberSReg(rl_dest.s_reg_low); 306 rl_result = EvalLoc(rl_dest, kCoreReg, true); 307 LoadConstant(rl_result.reg, default_result); 308 NewLIR2(kThumb2Vcmps, rl_src1.reg.GetReg(), rl_src2.reg.GetReg()); 309 } 310 DCHECK(!rl_result.reg.IsFloat()); 311 NewLIR0(kThumb2Fmstat); 312 313 LIR* it = OpIT((default_result == -1) ? kCondGt : kCondMi, ""); 314 NewLIR2(kThumb2MovI8M, rl_result.reg.GetReg(), 315 ModifiedImmediate(-default_result)); // Must not alter ccodes 316 OpEndIT(it); 317 318 it = OpIT(kCondEq, ""); 319 LoadConstant(rl_result.reg, 0); 320 OpEndIT(it); 321 322 StoreValue(rl_dest, rl_result); 323} 324 325void ArmMir2Lir::GenNegFloat(RegLocation rl_dest, RegLocation rl_src) { 326 RegLocation rl_result; 327 rl_src = LoadValue(rl_src, kFPReg); 328 rl_result = EvalLoc(rl_dest, kFPReg, true); 329 NewLIR2(kThumb2Vnegs, rl_result.reg.GetReg(), rl_src.reg.GetReg()); 330 StoreValue(rl_dest, rl_result); 331} 332 333void ArmMir2Lir::GenNegDouble(RegLocation rl_dest, RegLocation rl_src) { 334 RegLocation rl_result; 335 rl_src = LoadValueWide(rl_src, kFPReg); 336 rl_result = EvalLoc(rl_dest, kFPReg, true); 337 NewLIR2(kThumb2Vnegd, rl_result.reg.GetReg(), rl_src.reg.GetReg()); 338 StoreValueWide(rl_dest, rl_result); 339} 340 341static RegisterClass RegClassForAbsFP(RegLocation rl_src, RegLocation rl_dest) { 342 // If src is in a core reg or, unlikely, dest has been promoted to a core reg, use core reg. 343 if ((rl_src.location == kLocPhysReg && !rl_src.reg.IsFloat()) || 344 (rl_dest.location == kLocPhysReg && !rl_dest.reg.IsFloat())) { 345 return kCoreReg; 346 } 347 // If src is in an fp reg or dest has been promoted to an fp reg, use fp reg. 348 if (rl_src.location == kLocPhysReg || rl_dest.location == kLocPhysReg) { 349 return kFPReg; 350 } 351 // With both src and dest in the stack frame we have to perform load+abs+store. Whether this 352 // is faster using a core reg or fp reg depends on the particular CPU. Without further 353 // investigation and testing we prefer core register. (If the result is subsequently used in 354 // another fp operation, the dalvik reg will probably get promoted and that should be handled 355 // by the cases above.) 356 return kCoreReg; 357} 358 359bool ArmMir2Lir::GenInlinedAbsFloat(CallInfo* info) { 360 if (info->result.location == kLocInvalid) { 361 return true; // Result is unused: inlining successful, no code generated. 362 } 363 RegLocation rl_dest = info->result; 364 RegLocation rl_src = UpdateLoc(info->args[0]); 365 RegisterClass reg_class = RegClassForAbsFP(rl_src, rl_dest); 366 rl_src = LoadValue(rl_src, reg_class); 367 RegLocation rl_result = EvalLoc(rl_dest, reg_class, true); 368 if (reg_class == kFPReg) { 369 NewLIR2(kThumb2Vabss, rl_result.reg.GetReg(), rl_src.reg.GetReg()); 370 } else { 371 OpRegRegImm(kOpAnd, rl_result.reg, rl_src.reg, 0x7fffffff); 372 } 373 StoreValue(rl_dest, rl_result); 374 return true; 375} 376 377bool ArmMir2Lir::GenInlinedAbsDouble(CallInfo* info) { 378 if (info->result.location == kLocInvalid) { 379 return true; // Result is unused: inlining successful, no code generated. 380 } 381 RegLocation rl_dest = info->result; 382 RegLocation rl_src = UpdateLocWide(info->args[0]); 383 RegisterClass reg_class = RegClassForAbsFP(rl_src, rl_dest); 384 rl_src = LoadValueWide(rl_src, reg_class); 385 RegLocation rl_result = EvalLoc(rl_dest, reg_class, true); 386 if (reg_class == kFPReg) { 387 NewLIR2(kThumb2Vabsd, rl_result.reg.GetReg(), rl_src.reg.GetReg()); 388 } else { 389 OpRegImm(kOpAnd, rl_result.reg.GetHigh(), 0x7fffffff); 390 } 391 StoreValueWide(rl_dest, rl_result); 392 return true; 393} 394 395bool ArmMir2Lir::GenInlinedSqrt(CallInfo* info) { 396 DCHECK_EQ(cu_->instruction_set, kThumb2); 397 RegLocation rl_src = info->args[0]; 398 RegLocation rl_dest = InlineTargetWide(info); // double place for result 399 rl_src = LoadValueWide(rl_src, kFPReg); 400 RegLocation rl_result = EvalLoc(rl_dest, kFPReg, true); 401 NewLIR2(kThumb2Vsqrtd, rl_result.reg.GetReg(), rl_src.reg.GetReg()); 402 StoreValueWide(rl_dest, rl_result); 403 return true; 404} 405 406 407} // namespace art 408