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 24namespace art { 25 26void 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 72void 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 119void 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 130void 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 145void 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 245void 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 296void 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 354void 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 362void 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 370static 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 388bool 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 406bool 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 433bool 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