gen_invoke.cc revision 88474b416eb257078e590bf9bc7957cee604a186
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#include "dex/compiler_ir.h" 18#include "dex_file-inl.h" 19#include "entrypoints/quick/quick_entrypoints.h" 20#include "invoke_type.h" 21#include "mirror/array.h" 22#include "mirror/string.h" 23#include "mir_to_lir-inl.h" 24#include "x86/codegen_x86.h" 25 26namespace art { 27 28/* 29 * This source files contains "gen" codegen routines that should 30 * be applicable to most targets. Only mid-level support utilities 31 * and "op" calls may be used here. 32 */ 33 34/* 35 * To save scheduling time, helper calls are broken into two parts: generation of 36 * the helper target address, and the actuall call to the helper. Because x86 37 * has a memory call operation, part 1 is a NOP for x86. For other targets, 38 * load arguments between the two parts. 39 */ 40int Mir2Lir::CallHelperSetup(ThreadOffset helper_offset) { 41 return (cu_->instruction_set == kX86) ? 0 : LoadHelper(helper_offset); 42} 43 44/* NOTE: if r_tgt is a temp, it will be freed following use */ 45LIR* Mir2Lir::CallHelper(int r_tgt, ThreadOffset helper_offset, bool safepoint_pc) { 46 LIR* call_inst; 47 if (cu_->instruction_set == kX86) { 48 call_inst = OpThreadMem(kOpBlx, helper_offset); 49 } else { 50 call_inst = OpReg(kOpBlx, r_tgt); 51 FreeTemp(r_tgt); 52 } 53 if (safepoint_pc) { 54 MarkSafepointPC(call_inst); 55 } 56 return call_inst; 57} 58 59void Mir2Lir::CallRuntimeHelperImm(ThreadOffset helper_offset, int arg0, bool safepoint_pc) { 60 int r_tgt = CallHelperSetup(helper_offset); 61 LoadConstant(TargetReg(kArg0), arg0); 62 ClobberCalleeSave(); 63 CallHelper(r_tgt, helper_offset, safepoint_pc); 64} 65 66void Mir2Lir::CallRuntimeHelperReg(ThreadOffset helper_offset, int arg0, bool safepoint_pc) { 67 int r_tgt = CallHelperSetup(helper_offset); 68 OpRegCopy(TargetReg(kArg0), arg0); 69 ClobberCalleeSave(); 70 CallHelper(r_tgt, helper_offset, safepoint_pc); 71} 72 73void Mir2Lir::CallRuntimeHelperRegLocation(ThreadOffset helper_offset, RegLocation arg0, 74 bool safepoint_pc) { 75 int r_tgt = CallHelperSetup(helper_offset); 76 if (arg0.wide == 0) { 77 LoadValueDirectFixed(arg0, TargetReg(kArg0)); 78 } else { 79 LoadValueDirectWideFixed(arg0, TargetReg(kArg0), TargetReg(kArg1)); 80 } 81 ClobberCalleeSave(); 82 CallHelper(r_tgt, helper_offset, safepoint_pc); 83} 84 85void Mir2Lir::CallRuntimeHelperImmImm(ThreadOffset helper_offset, int arg0, int arg1, 86 bool safepoint_pc) { 87 int r_tgt = CallHelperSetup(helper_offset); 88 LoadConstant(TargetReg(kArg0), arg0); 89 LoadConstant(TargetReg(kArg1), arg1); 90 ClobberCalleeSave(); 91 CallHelper(r_tgt, helper_offset, safepoint_pc); 92} 93 94void Mir2Lir::CallRuntimeHelperImmRegLocation(ThreadOffset helper_offset, int arg0, 95 RegLocation arg1, bool safepoint_pc) { 96 int r_tgt = CallHelperSetup(helper_offset); 97 if (arg1.wide == 0) { 98 LoadValueDirectFixed(arg1, TargetReg(kArg1)); 99 } else { 100 LoadValueDirectWideFixed(arg1, TargetReg(kArg1), TargetReg(kArg2)); 101 } 102 LoadConstant(TargetReg(kArg0), arg0); 103 ClobberCalleeSave(); 104 CallHelper(r_tgt, helper_offset, safepoint_pc); 105} 106 107void Mir2Lir::CallRuntimeHelperRegLocationImm(ThreadOffset helper_offset, RegLocation arg0, int arg1, 108 bool safepoint_pc) { 109 int r_tgt = CallHelperSetup(helper_offset); 110 LoadValueDirectFixed(arg0, TargetReg(kArg0)); 111 LoadConstant(TargetReg(kArg1), arg1); 112 ClobberCalleeSave(); 113 CallHelper(r_tgt, helper_offset, safepoint_pc); 114} 115 116void Mir2Lir::CallRuntimeHelperImmReg(ThreadOffset helper_offset, int arg0, int arg1, 117 bool safepoint_pc) { 118 int r_tgt = CallHelperSetup(helper_offset); 119 OpRegCopy(TargetReg(kArg1), arg1); 120 LoadConstant(TargetReg(kArg0), arg0); 121 ClobberCalleeSave(); 122 CallHelper(r_tgt, helper_offset, safepoint_pc); 123} 124 125void Mir2Lir::CallRuntimeHelperRegImm(ThreadOffset helper_offset, int arg0, int arg1, 126 bool safepoint_pc) { 127 int r_tgt = CallHelperSetup(helper_offset); 128 OpRegCopy(TargetReg(kArg0), arg0); 129 LoadConstant(TargetReg(kArg1), arg1); 130 ClobberCalleeSave(); 131 CallHelper(r_tgt, helper_offset, safepoint_pc); 132} 133 134void Mir2Lir::CallRuntimeHelperImmMethod(ThreadOffset helper_offset, int arg0, bool safepoint_pc) { 135 int r_tgt = CallHelperSetup(helper_offset); 136 LoadCurrMethodDirect(TargetReg(kArg1)); 137 LoadConstant(TargetReg(kArg0), arg0); 138 ClobberCalleeSave(); 139 CallHelper(r_tgt, helper_offset, safepoint_pc); 140} 141 142void Mir2Lir::CallRuntimeHelperRegLocationRegLocation(ThreadOffset helper_offset, RegLocation arg0, 143 RegLocation arg1, bool safepoint_pc) { 144 int r_tgt = CallHelperSetup(helper_offset); 145 if (arg0.wide == 0) { 146 LoadValueDirectFixed(arg0, arg0.fp ? TargetReg(kFArg0) : TargetReg(kArg0)); 147 if (arg1.wide == 0) { 148 if (cu_->instruction_set == kMips) { 149 LoadValueDirectFixed(arg1, arg1.fp ? TargetReg(kFArg2) : TargetReg(kArg1)); 150 } else { 151 LoadValueDirectFixed(arg1, TargetReg(kArg1)); 152 } 153 } else { 154 if (cu_->instruction_set == kMips) { 155 LoadValueDirectWideFixed(arg1, arg1.fp ? TargetReg(kFArg2) : TargetReg(kArg1), arg1.fp ? TargetReg(kFArg3) : TargetReg(kArg2)); 156 } else { 157 LoadValueDirectWideFixed(arg1, TargetReg(kArg1), TargetReg(kArg2)); 158 } 159 } 160 } else { 161 LoadValueDirectWideFixed(arg0, arg0.fp ? TargetReg(kFArg0) : TargetReg(kArg0), arg0.fp ? TargetReg(kFArg1) : TargetReg(kArg1)); 162 if (arg1.wide == 0) { 163 LoadValueDirectFixed(arg1, arg1.fp ? TargetReg(kFArg2) : TargetReg(kArg2)); 164 } else { 165 LoadValueDirectWideFixed(arg1, arg1.fp ? TargetReg(kFArg2) : TargetReg(kArg2), arg1.fp ? TargetReg(kFArg3) : TargetReg(kArg3)); 166 } 167 } 168 ClobberCalleeSave(); 169 CallHelper(r_tgt, helper_offset, safepoint_pc); 170} 171 172void Mir2Lir::CallRuntimeHelperRegReg(ThreadOffset helper_offset, int arg0, int arg1, 173 bool safepoint_pc) { 174 int r_tgt = CallHelperSetup(helper_offset); 175 DCHECK_NE(TargetReg(kArg0), arg1); // check copy into arg0 won't clobber arg1 176 OpRegCopy(TargetReg(kArg0), arg0); 177 OpRegCopy(TargetReg(kArg1), arg1); 178 ClobberCalleeSave(); 179 CallHelper(r_tgt, helper_offset, safepoint_pc); 180} 181 182void Mir2Lir::CallRuntimeHelperRegRegImm(ThreadOffset helper_offset, int arg0, int arg1, 183 int arg2, bool safepoint_pc) { 184 int r_tgt = CallHelperSetup(helper_offset); 185 DCHECK_NE(TargetReg(kArg0), arg1); // check copy into arg0 won't clobber arg1 186 OpRegCopy(TargetReg(kArg0), arg0); 187 OpRegCopy(TargetReg(kArg1), arg1); 188 LoadConstant(TargetReg(kArg2), arg2); 189 ClobberCalleeSave(); 190 CallHelper(r_tgt, helper_offset, safepoint_pc); 191} 192 193void Mir2Lir::CallRuntimeHelperImmMethodRegLocation(ThreadOffset helper_offset, 194 int arg0, RegLocation arg2, bool safepoint_pc) { 195 int r_tgt = CallHelperSetup(helper_offset); 196 LoadValueDirectFixed(arg2, TargetReg(kArg2)); 197 LoadCurrMethodDirect(TargetReg(kArg1)); 198 LoadConstant(TargetReg(kArg0), arg0); 199 ClobberCalleeSave(); 200 CallHelper(r_tgt, helper_offset, safepoint_pc); 201} 202 203void Mir2Lir::CallRuntimeHelperImmMethodImm(ThreadOffset helper_offset, int arg0, 204 int arg2, bool safepoint_pc) { 205 int r_tgt = CallHelperSetup(helper_offset); 206 LoadCurrMethodDirect(TargetReg(kArg1)); 207 LoadConstant(TargetReg(kArg2), arg2); 208 LoadConstant(TargetReg(kArg0), arg0); 209 ClobberCalleeSave(); 210 CallHelper(r_tgt, helper_offset, safepoint_pc); 211} 212 213void Mir2Lir::CallRuntimeHelperImmRegLocationRegLocation(ThreadOffset helper_offset, 214 int arg0, RegLocation arg1, 215 RegLocation arg2, bool safepoint_pc) { 216 int r_tgt = CallHelperSetup(helper_offset); 217 DCHECK_EQ(arg1.wide, 0U); 218 LoadValueDirectFixed(arg1, TargetReg(kArg1)); 219 if (arg2.wide == 0) { 220 LoadValueDirectFixed(arg2, TargetReg(kArg2)); 221 } else { 222 LoadValueDirectWideFixed(arg2, TargetReg(kArg2), TargetReg(kArg3)); 223 } 224 LoadConstant(TargetReg(kArg0), arg0); 225 ClobberCalleeSave(); 226 CallHelper(r_tgt, helper_offset, safepoint_pc); 227} 228 229void Mir2Lir::CallRuntimeHelperRegLocationRegLocationRegLocation(ThreadOffset helper_offset, 230 RegLocation arg0, RegLocation arg1, 231 RegLocation arg2, 232 bool safepoint_pc) { 233 int r_tgt = CallHelperSetup(helper_offset); 234 DCHECK_EQ(arg0.wide, 0U); 235 LoadValueDirectFixed(arg0, TargetReg(kArg0)); 236 DCHECK_EQ(arg1.wide, 0U); 237 LoadValueDirectFixed(arg1, TargetReg(kArg1)); 238 DCHECK_EQ(arg1.wide, 0U); 239 LoadValueDirectFixed(arg2, TargetReg(kArg2)); 240 ClobberCalleeSave(); 241 CallHelper(r_tgt, helper_offset, safepoint_pc); 242} 243 244/* 245 * If there are any ins passed in registers that have not been promoted 246 * to a callee-save register, flush them to the frame. Perform intial 247 * assignment of promoted arguments. 248 * 249 * ArgLocs is an array of location records describing the incoming arguments 250 * with one location record per word of argument. 251 */ 252void Mir2Lir::FlushIns(RegLocation* ArgLocs, RegLocation rl_method) { 253 /* 254 * Dummy up a RegLocation for the incoming Method* 255 * It will attempt to keep kArg0 live (or copy it to home location 256 * if promoted). 257 */ 258 RegLocation rl_src = rl_method; 259 rl_src.location = kLocPhysReg; 260 rl_src.low_reg = TargetReg(kArg0); 261 rl_src.home = false; 262 MarkLive(rl_src.low_reg, rl_src.s_reg_low); 263 StoreValue(rl_method, rl_src); 264 // If Method* has been promoted, explicitly flush 265 if (rl_method.location == kLocPhysReg) { 266 StoreWordDisp(TargetReg(kSp), 0, TargetReg(kArg0)); 267 } 268 269 if (cu_->num_ins == 0) 270 return; 271 const int num_arg_regs = 3; 272 static SpecialTargetRegister arg_regs[] = {kArg1, kArg2, kArg3}; 273 int start_vreg = cu_->num_dalvik_registers - cu_->num_ins; 274 /* 275 * Copy incoming arguments to their proper home locations. 276 * NOTE: an older version of dx had an issue in which 277 * it would reuse static method argument registers. 278 * This could result in the same Dalvik virtual register 279 * being promoted to both core and fp regs. To account for this, 280 * we only copy to the corresponding promoted physical register 281 * if it matches the type of the SSA name for the incoming 282 * argument. It is also possible that long and double arguments 283 * end up half-promoted. In those cases, we must flush the promoted 284 * half to memory as well. 285 */ 286 for (int i = 0; i < cu_->num_ins; i++) { 287 PromotionMap* v_map = &promotion_map_[start_vreg + i]; 288 if (i < num_arg_regs) { 289 // If arriving in register 290 bool need_flush = true; 291 RegLocation* t_loc = &ArgLocs[i]; 292 if ((v_map->core_location == kLocPhysReg) && !t_loc->fp) { 293 OpRegCopy(v_map->core_reg, TargetReg(arg_regs[i])); 294 need_flush = false; 295 } else if ((v_map->fp_location == kLocPhysReg) && t_loc->fp) { 296 OpRegCopy(v_map->FpReg, TargetReg(arg_regs[i])); 297 need_flush = false; 298 } else { 299 need_flush = true; 300 } 301 302 // For wide args, force flush if not fully promoted 303 if (t_loc->wide) { 304 PromotionMap* p_map = v_map + (t_loc->high_word ? -1 : +1); 305 // Is only half promoted? 306 need_flush |= (p_map->core_location != v_map->core_location) || 307 (p_map->fp_location != v_map->fp_location); 308 if ((cu_->instruction_set == kThumb2) && t_loc->fp && !need_flush) { 309 /* 310 * In Arm, a double is represented as a pair of consecutive single float 311 * registers starting at an even number. It's possible that both Dalvik vRegs 312 * representing the incoming double were independently promoted as singles - but 313 * not in a form usable as a double. If so, we need to flush - even though the 314 * incoming arg appears fully in register. At this point in the code, both 315 * halves of the double are promoted. Make sure they are in a usable form. 316 */ 317 int lowreg_index = start_vreg + i + (t_loc->high_word ? -1 : 0); 318 int low_reg = promotion_map_[lowreg_index].FpReg; 319 int high_reg = promotion_map_[lowreg_index + 1].FpReg; 320 if (((low_reg & 0x1) != 0) || (high_reg != (low_reg + 1))) { 321 need_flush = true; 322 } 323 } 324 } 325 if (need_flush) { 326 StoreBaseDisp(TargetReg(kSp), SRegOffset(start_vreg + i), 327 TargetReg(arg_regs[i]), kWord); 328 } 329 } else { 330 // If arriving in frame & promoted 331 if (v_map->core_location == kLocPhysReg) { 332 LoadWordDisp(TargetReg(kSp), SRegOffset(start_vreg + i), 333 v_map->core_reg); 334 } 335 if (v_map->fp_location == kLocPhysReg) { 336 LoadWordDisp(TargetReg(kSp), SRegOffset(start_vreg + i), 337 v_map->FpReg); 338 } 339 } 340 } 341} 342 343/* 344 * Bit of a hack here - in the absence of a real scheduling pass, 345 * emit the next instruction in static & direct invoke sequences. 346 */ 347static int NextSDCallInsn(CompilationUnit* cu, CallInfo* info, 348 int state, const MethodReference& target_method, 349 uint32_t unused, 350 uintptr_t direct_code, uintptr_t direct_method, 351 InvokeType type) { 352 Mir2Lir* cg = static_cast<Mir2Lir*>(cu->cg.get()); 353 if (direct_code != 0 && direct_method != 0) { 354 switch (state) { 355 case 0: // Get the current Method* [sets kArg0] 356 if (direct_code != static_cast<unsigned int>(-1)) { 357 if (cu->instruction_set != kX86) { 358 cg->LoadConstant(cg->TargetReg(kInvokeTgt), direct_code); 359 } 360 } else { 361 CHECK_EQ(cu->dex_file, target_method.dex_file); 362 LIR* data_target = cg->ScanLiteralPool(cg->code_literal_list_, 363 target_method.dex_method_index, 0); 364 if (data_target == NULL) { 365 data_target = cg->AddWordData(&cg->code_literal_list_, target_method.dex_method_index); 366 data_target->operands[1] = type; 367 } 368 LIR* load_pc_rel = cg->OpPcRelLoad(cg->TargetReg(kInvokeTgt), data_target); 369 cg->AppendLIR(load_pc_rel); 370 DCHECK_EQ(cu->instruction_set, kThumb2) << reinterpret_cast<void*>(data_target); 371 } 372 if (direct_method != static_cast<unsigned int>(-1)) { 373 cg->LoadConstant(cg->TargetReg(kArg0), direct_method); 374 } else { 375 CHECK_EQ(cu->dex_file, target_method.dex_file); 376 LIR* data_target = cg->ScanLiteralPool(cg->method_literal_list_, 377 target_method.dex_method_index, 0); 378 if (data_target == NULL) { 379 data_target = cg->AddWordData(&cg->method_literal_list_, target_method.dex_method_index); 380 data_target->operands[1] = type; 381 } 382 LIR* load_pc_rel = cg->OpPcRelLoad(cg->TargetReg(kArg0), data_target); 383 cg->AppendLIR(load_pc_rel); 384 DCHECK_EQ(cu->instruction_set, kThumb2) << reinterpret_cast<void*>(data_target); 385 } 386 break; 387 default: 388 return -1; 389 } 390 } else { 391 switch (state) { 392 case 0: // Get the current Method* [sets kArg0] 393 // TUNING: we can save a reg copy if Method* has been promoted. 394 cg->LoadCurrMethodDirect(cg->TargetReg(kArg0)); 395 break; 396 case 1: // Get method->dex_cache_resolved_methods_ 397 cg->LoadWordDisp(cg->TargetReg(kArg0), 398 mirror::ArtMethod::DexCacheResolvedMethodsOffset().Int32Value(), cg->TargetReg(kArg0)); 399 // Set up direct code if known. 400 if (direct_code != 0) { 401 if (direct_code != static_cast<unsigned int>(-1)) { 402 cg->LoadConstant(cg->TargetReg(kInvokeTgt), direct_code); 403 } else { 404 CHECK_EQ(cu->dex_file, target_method.dex_file); 405 CHECK_LT(target_method.dex_method_index, target_method.dex_file->NumMethodIds()); 406 LIR* data_target = cg->ScanLiteralPool(cg->code_literal_list_, 407 target_method.dex_method_index, 0); 408 if (data_target == NULL) { 409 data_target = cg->AddWordData(&cg->code_literal_list_, target_method.dex_method_index); 410 data_target->operands[1] = type; 411 } 412 LIR* load_pc_rel = cg->OpPcRelLoad(cg->TargetReg(kInvokeTgt), data_target); 413 cg->AppendLIR(load_pc_rel); 414 DCHECK_EQ(cu->instruction_set, kThumb2) << reinterpret_cast<void*>(data_target); 415 } 416 } 417 break; 418 case 2: // Grab target method* 419 CHECK_EQ(cu->dex_file, target_method.dex_file); 420 cg->LoadWordDisp(cg->TargetReg(kArg0), 421 mirror::Array::DataOffset(sizeof(mirror::Object*)).Int32Value() + 422 (target_method.dex_method_index * 4), 423 cg-> TargetReg(kArg0)); 424 break; 425 case 3: // Grab the code from the method* 426 if (cu->instruction_set != kX86) { 427 if (direct_code == 0) { 428 cg->LoadWordDisp(cg->TargetReg(kArg0), 429 mirror::ArtMethod::GetEntryPointFromCompiledCodeOffset().Int32Value(), 430 cg->TargetReg(kInvokeTgt)); 431 } 432 break; 433 } 434 // Intentional fallthrough for x86 435 default: 436 return -1; 437 } 438 } 439 return state + 1; 440} 441 442/* 443 * Bit of a hack here - in the absence of a real scheduling pass, 444 * emit the next instruction in a virtual invoke sequence. 445 * We can use kLr as a temp prior to target address loading 446 * Note also that we'll load the first argument ("this") into 447 * kArg1 here rather than the standard LoadArgRegs. 448 */ 449static int NextVCallInsn(CompilationUnit* cu, CallInfo* info, 450 int state, const MethodReference& target_method, 451 uint32_t method_idx, uintptr_t unused, uintptr_t unused2, 452 InvokeType unused3) { 453 Mir2Lir* cg = static_cast<Mir2Lir*>(cu->cg.get()); 454 /* 455 * This is the fast path in which the target virtual method is 456 * fully resolved at compile time. 457 */ 458 switch (state) { 459 case 0: { // Get "this" [set kArg1] 460 RegLocation rl_arg = info->args[0]; 461 cg->LoadValueDirectFixed(rl_arg, cg->TargetReg(kArg1)); 462 break; 463 } 464 case 1: // Is "this" null? [use kArg1] 465 cg->GenNullCheck(info->args[0].s_reg_low, cg->TargetReg(kArg1), info->opt_flags); 466 // get this->klass_ [use kArg1, set kInvokeTgt] 467 cg->LoadWordDisp(cg->TargetReg(kArg1), mirror::Object::ClassOffset().Int32Value(), 468 cg->TargetReg(kInvokeTgt)); 469 break; 470 case 2: // Get this->klass_->vtable [usr kInvokeTgt, set kInvokeTgt] 471 cg->LoadWordDisp(cg->TargetReg(kInvokeTgt), mirror::Class::VTableOffset().Int32Value(), 472 cg->TargetReg(kInvokeTgt)); 473 break; 474 case 3: // Get target method [use kInvokeTgt, set kArg0] 475 cg->LoadWordDisp(cg->TargetReg(kInvokeTgt), (method_idx * 4) + 476 mirror::Array::DataOffset(sizeof(mirror::Object*)).Int32Value(), 477 cg->TargetReg(kArg0)); 478 break; 479 case 4: // Get the compiled code address [uses kArg0, sets kInvokeTgt] 480 if (cu->instruction_set != kX86) { 481 cg->LoadWordDisp(cg->TargetReg(kArg0), 482 mirror::ArtMethod::GetEntryPointFromCompiledCodeOffset().Int32Value(), 483 cg->TargetReg(kInvokeTgt)); 484 break; 485 } 486 // Intentional fallthrough for X86 487 default: 488 return -1; 489 } 490 return state + 1; 491} 492 493/* 494 * Emit the next instruction in an invoke interface sequence. This will do a lookup in the 495 * class's IMT, calling either the actual method or art_quick_imt_conflict_trampoline if 496 * more than one interface method map to the same index. Note also that we'll load the first 497 * argument ("this") into kArg1 here rather than the standard LoadArgRegs. 498 */ 499static int NextInterfaceCallInsn(CompilationUnit* cu, CallInfo* info, int state, 500 const MethodReference& target_method, 501 uint32_t method_idx, uintptr_t unused, 502 uintptr_t direct_method, InvokeType unused2) { 503 Mir2Lir* cg = static_cast<Mir2Lir*>(cu->cg.get()); 504 505 switch (state) { 506 case 0: // Set target method index in case of conflict [set kHiddenArg, kHiddenFpArg (x86)] 507 CHECK_EQ(cu->dex_file, target_method.dex_file); 508 CHECK_LT(target_method.dex_method_index, target_method.dex_file->NumMethodIds()); 509 cg->LoadConstant(cg->TargetReg(kHiddenArg), target_method.dex_method_index); 510 if (cu->instruction_set == kX86) { 511 cg->OpRegCopy(cg->TargetReg(kHiddenFpArg), cg->TargetReg(kHiddenArg)); 512 } 513 break; 514 case 1: { // Get "this" [set kArg1] 515 RegLocation rl_arg = info->args[0]; 516 cg->LoadValueDirectFixed(rl_arg, cg->TargetReg(kArg1)); 517 break; 518 } 519 case 2: // Is "this" null? [use kArg1] 520 cg->GenNullCheck(info->args[0].s_reg_low, cg->TargetReg(kArg1), info->opt_flags); 521 // Get this->klass_ [use kArg1, set kInvokeTgt] 522 cg->LoadWordDisp(cg->TargetReg(kArg1), mirror::Object::ClassOffset().Int32Value(), 523 cg->TargetReg(kInvokeTgt)); 524 break; 525 case 3: // Get this->klass_->imtable [use kInvokeTgt, set kInvokeTgt] 526 cg->LoadWordDisp(cg->TargetReg(kInvokeTgt), mirror::Class::ImTableOffset().Int32Value(), 527 cg->TargetReg(kInvokeTgt)); 528 break; 529 case 4: // Get target method [use kInvokeTgt, set kArg0] 530 cg->LoadWordDisp(cg->TargetReg(kInvokeTgt), ((method_idx % ClassLinker::kImtSize) * 4) + 531 mirror::Array::DataOffset(sizeof(mirror::Object*)).Int32Value(), 532 cg->TargetReg(kArg0)); 533 break; 534 case 5: // Get the compiled code address [use kArg0, set kInvokeTgt] 535 if (cu->instruction_set != kX86) { 536 cg->LoadWordDisp(cg->TargetReg(kArg0), 537 mirror::ArtMethod::GetEntryPointFromCompiledCodeOffset().Int32Value(), 538 cg->TargetReg(kInvokeTgt)); 539 break; 540 } 541 // Intentional fallthrough for X86 542 default: 543 return -1; 544 } 545 return state + 1; 546} 547 548static int NextInvokeInsnSP(CompilationUnit* cu, CallInfo* info, ThreadOffset trampoline, 549 int state, const MethodReference& target_method, 550 uint32_t method_idx) { 551 Mir2Lir* cg = static_cast<Mir2Lir*>(cu->cg.get()); 552 /* 553 * This handles the case in which the base method is not fully 554 * resolved at compile time, we bail to a runtime helper. 555 */ 556 if (state == 0) { 557 if (cu->instruction_set != kX86) { 558 // Load trampoline target 559 cg->LoadWordDisp(cg->TargetReg(kSelf), trampoline.Int32Value(), cg->TargetReg(kInvokeTgt)); 560 } 561 // Load kArg0 with method index 562 CHECK_EQ(cu->dex_file, target_method.dex_file); 563 cg->LoadConstant(cg->TargetReg(kArg0), target_method.dex_method_index); 564 return 1; 565 } 566 return -1; 567} 568 569static int NextStaticCallInsnSP(CompilationUnit* cu, CallInfo* info, 570 int state, 571 const MethodReference& target_method, 572 uint32_t method_idx, 573 uintptr_t unused, uintptr_t unused2, 574 InvokeType unused3) { 575 ThreadOffset trampoline = QUICK_ENTRYPOINT_OFFSET(pInvokeStaticTrampolineWithAccessCheck); 576 return NextInvokeInsnSP(cu, info, trampoline, state, target_method, 0); 577} 578 579static int NextDirectCallInsnSP(CompilationUnit* cu, CallInfo* info, int state, 580 const MethodReference& target_method, 581 uint32_t method_idx, uintptr_t unused, 582 uintptr_t unused2, InvokeType unused3) { 583 ThreadOffset trampoline = QUICK_ENTRYPOINT_OFFSET(pInvokeDirectTrampolineWithAccessCheck); 584 return NextInvokeInsnSP(cu, info, trampoline, state, target_method, 0); 585} 586 587static int NextSuperCallInsnSP(CompilationUnit* cu, CallInfo* info, int state, 588 const MethodReference& target_method, 589 uint32_t method_idx, uintptr_t unused, 590 uintptr_t unused2, InvokeType unused3) { 591 ThreadOffset trampoline = QUICK_ENTRYPOINT_OFFSET(pInvokeSuperTrampolineWithAccessCheck); 592 return NextInvokeInsnSP(cu, info, trampoline, state, target_method, 0); 593} 594 595static int NextVCallInsnSP(CompilationUnit* cu, CallInfo* info, int state, 596 const MethodReference& target_method, 597 uint32_t method_idx, uintptr_t unused, 598 uintptr_t unused2, InvokeType unused3) { 599 ThreadOffset trampoline = QUICK_ENTRYPOINT_OFFSET(pInvokeVirtualTrampolineWithAccessCheck); 600 return NextInvokeInsnSP(cu, info, trampoline, state, target_method, 0); 601} 602 603static int NextInterfaceCallInsnWithAccessCheck(CompilationUnit* cu, 604 CallInfo* info, int state, 605 const MethodReference& target_method, 606 uint32_t unused, 607 uintptr_t unused2, uintptr_t unused3, 608 InvokeType unused4) { 609 ThreadOffset trampoline = QUICK_ENTRYPOINT_OFFSET(pInvokeInterfaceTrampolineWithAccessCheck); 610 return NextInvokeInsnSP(cu, info, trampoline, state, target_method, 0); 611} 612 613int Mir2Lir::LoadArgRegs(CallInfo* info, int call_state, 614 NextCallInsn next_call_insn, 615 const MethodReference& target_method, 616 uint32_t vtable_idx, uintptr_t direct_code, 617 uintptr_t direct_method, InvokeType type, bool skip_this) { 618 int last_arg_reg = TargetReg(kArg3); 619 int next_reg = TargetReg(kArg1); 620 int next_arg = 0; 621 if (skip_this) { 622 next_reg++; 623 next_arg++; 624 } 625 for (; (next_reg <= last_arg_reg) && (next_arg < info->num_arg_words); next_reg++) { 626 RegLocation rl_arg = info->args[next_arg++]; 627 rl_arg = UpdateRawLoc(rl_arg); 628 if (rl_arg.wide && (next_reg <= TargetReg(kArg2))) { 629 LoadValueDirectWideFixed(rl_arg, next_reg, next_reg + 1); 630 next_reg++; 631 next_arg++; 632 } else { 633 if (rl_arg.wide) { 634 rl_arg.wide = false; 635 rl_arg.is_const = false; 636 } 637 LoadValueDirectFixed(rl_arg, next_reg); 638 } 639 call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx, 640 direct_code, direct_method, type); 641 } 642 return call_state; 643} 644 645/* 646 * Load up to 5 arguments, the first three of which will be in 647 * kArg1 .. kArg3. On entry kArg0 contains the current method pointer, 648 * and as part of the load sequence, it must be replaced with 649 * the target method pointer. Note, this may also be called 650 * for "range" variants if the number of arguments is 5 or fewer. 651 */ 652int Mir2Lir::GenDalvikArgsNoRange(CallInfo* info, 653 int call_state, LIR** pcrLabel, NextCallInsn next_call_insn, 654 const MethodReference& target_method, 655 uint32_t vtable_idx, uintptr_t direct_code, 656 uintptr_t direct_method, InvokeType type, bool skip_this) { 657 RegLocation rl_arg; 658 659 /* If no arguments, just return */ 660 if (info->num_arg_words == 0) 661 return call_state; 662 663 call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx, 664 direct_code, direct_method, type); 665 666 DCHECK_LE(info->num_arg_words, 5); 667 if (info->num_arg_words > 3) { 668 int32_t next_use = 3; 669 // Detect special case of wide arg spanning arg3/arg4 670 RegLocation rl_use0 = info->args[0]; 671 RegLocation rl_use1 = info->args[1]; 672 RegLocation rl_use2 = info->args[2]; 673 if (((!rl_use0.wide && !rl_use1.wide) || rl_use0.wide) && 674 rl_use2.wide) { 675 int reg = -1; 676 // Wide spans, we need the 2nd half of uses[2]. 677 rl_arg = UpdateLocWide(rl_use2); 678 if (rl_arg.location == kLocPhysReg) { 679 reg = rl_arg.high_reg; 680 } else { 681 // kArg2 & rArg3 can safely be used here 682 reg = TargetReg(kArg3); 683 LoadWordDisp(TargetReg(kSp), SRegOffset(rl_arg.s_reg_low) + 4, reg); 684 call_state = next_call_insn(cu_, info, call_state, target_method, 685 vtable_idx, direct_code, direct_method, type); 686 } 687 StoreBaseDisp(TargetReg(kSp), (next_use + 1) * 4, reg, kWord); 688 StoreBaseDisp(TargetReg(kSp), 16 /* (3+1)*4 */, reg, kWord); 689 call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx, 690 direct_code, direct_method, type); 691 next_use++; 692 } 693 // Loop through the rest 694 while (next_use < info->num_arg_words) { 695 int low_reg; 696 int high_reg = -1; 697 rl_arg = info->args[next_use]; 698 rl_arg = UpdateRawLoc(rl_arg); 699 if (rl_arg.location == kLocPhysReg) { 700 low_reg = rl_arg.low_reg; 701 high_reg = rl_arg.high_reg; 702 } else { 703 low_reg = TargetReg(kArg2); 704 if (rl_arg.wide) { 705 high_reg = TargetReg(kArg3); 706 LoadValueDirectWideFixed(rl_arg, low_reg, high_reg); 707 } else { 708 LoadValueDirectFixed(rl_arg, low_reg); 709 } 710 call_state = next_call_insn(cu_, info, call_state, target_method, 711 vtable_idx, direct_code, direct_method, type); 712 } 713 int outs_offset = (next_use + 1) * 4; 714 if (rl_arg.wide) { 715 StoreBaseDispWide(TargetReg(kSp), outs_offset, low_reg, high_reg); 716 next_use += 2; 717 } else { 718 StoreWordDisp(TargetReg(kSp), outs_offset, low_reg); 719 next_use++; 720 } 721 call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx, 722 direct_code, direct_method, type); 723 } 724 } 725 726 call_state = LoadArgRegs(info, call_state, next_call_insn, 727 target_method, vtable_idx, direct_code, direct_method, 728 type, skip_this); 729 730 if (pcrLabel) { 731 *pcrLabel = GenNullCheck(info->args[0].s_reg_low, TargetReg(kArg1), info->opt_flags); 732 } 733 return call_state; 734} 735 736/* 737 * May have 0+ arguments (also used for jumbo). Note that 738 * source virtual registers may be in physical registers, so may 739 * need to be flushed to home location before copying. This 740 * applies to arg3 and above (see below). 741 * 742 * Two general strategies: 743 * If < 20 arguments 744 * Pass args 3-18 using vldm/vstm block copy 745 * Pass arg0, arg1 & arg2 in kArg1-kArg3 746 * If 20+ arguments 747 * Pass args arg19+ using memcpy block copy 748 * Pass arg0, arg1 & arg2 in kArg1-kArg3 749 * 750 */ 751int Mir2Lir::GenDalvikArgsRange(CallInfo* info, int call_state, 752 LIR** pcrLabel, NextCallInsn next_call_insn, 753 const MethodReference& target_method, 754 uint32_t vtable_idx, uintptr_t direct_code, uintptr_t direct_method, 755 InvokeType type, bool skip_this) { 756 // If we can treat it as non-range (Jumbo ops will use range form) 757 if (info->num_arg_words <= 5) 758 return GenDalvikArgsNoRange(info, call_state, pcrLabel, 759 next_call_insn, target_method, vtable_idx, 760 direct_code, direct_method, type, skip_this); 761 /* 762 * First load the non-register arguments. Both forms expect all 763 * of the source arguments to be in their home frame location, so 764 * scan the s_reg names and flush any that have been promoted to 765 * frame backing storage. 766 */ 767 // Scan the rest of the args - if in phys_reg flush to memory 768 for (int next_arg = 0; next_arg < info->num_arg_words;) { 769 RegLocation loc = info->args[next_arg]; 770 if (loc.wide) { 771 loc = UpdateLocWide(loc); 772 if ((next_arg >= 2) && (loc.location == kLocPhysReg)) { 773 StoreBaseDispWide(TargetReg(kSp), SRegOffset(loc.s_reg_low), 774 loc.low_reg, loc.high_reg); 775 } 776 next_arg += 2; 777 } else { 778 loc = UpdateLoc(loc); 779 if ((next_arg >= 3) && (loc.location == kLocPhysReg)) { 780 StoreBaseDisp(TargetReg(kSp), SRegOffset(loc.s_reg_low), 781 loc.low_reg, kWord); 782 } 783 next_arg++; 784 } 785 } 786 787 int start_offset = SRegOffset(info->args[3].s_reg_low); 788 int outs_offset = 4 /* Method* */ + (3 * 4); 789 if (cu_->instruction_set != kThumb2) { 790 // Generate memcpy 791 OpRegRegImm(kOpAdd, TargetReg(kArg0), TargetReg(kSp), outs_offset); 792 OpRegRegImm(kOpAdd, TargetReg(kArg1), TargetReg(kSp), start_offset); 793 CallRuntimeHelperRegRegImm(QUICK_ENTRYPOINT_OFFSET(pMemcpy), TargetReg(kArg0), 794 TargetReg(kArg1), (info->num_arg_words - 3) * 4, false); 795 } else { 796 if (info->num_arg_words >= 20) { 797 // Generate memcpy 798 OpRegRegImm(kOpAdd, TargetReg(kArg0), TargetReg(kSp), outs_offset); 799 OpRegRegImm(kOpAdd, TargetReg(kArg1), TargetReg(kSp), start_offset); 800 CallRuntimeHelperRegRegImm(QUICK_ENTRYPOINT_OFFSET(pMemcpy), TargetReg(kArg0), 801 TargetReg(kArg1), (info->num_arg_words - 3) * 4, false); 802 } else { 803 // Use vldm/vstm pair using kArg3 as a temp 804 int regs_left = std::min(info->num_arg_words - 3, 16); 805 call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx, 806 direct_code, direct_method, type); 807 OpRegRegImm(kOpAdd, TargetReg(kArg3), TargetReg(kSp), start_offset); 808 LIR* ld = OpVldm(TargetReg(kArg3), regs_left); 809 // TUNING: loosen barrier 810 ld->u.m.def_mask = ENCODE_ALL; 811 SetMemRefType(ld, true /* is_load */, kDalvikReg); 812 call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx, 813 direct_code, direct_method, type); 814 OpRegRegImm(kOpAdd, TargetReg(kArg3), TargetReg(kSp), 4 /* Method* */ + (3 * 4)); 815 call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx, 816 direct_code, direct_method, type); 817 LIR* st = OpVstm(TargetReg(kArg3), regs_left); 818 SetMemRefType(st, false /* is_load */, kDalvikReg); 819 st->u.m.def_mask = ENCODE_ALL; 820 call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx, 821 direct_code, direct_method, type); 822 } 823 } 824 825 call_state = LoadArgRegs(info, call_state, next_call_insn, 826 target_method, vtable_idx, direct_code, direct_method, 827 type, skip_this); 828 829 call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx, 830 direct_code, direct_method, type); 831 if (pcrLabel) { 832 *pcrLabel = GenNullCheck(info->args[0].s_reg_low, TargetReg(kArg1), info->opt_flags); 833 } 834 return call_state; 835} 836 837RegLocation Mir2Lir::InlineTarget(CallInfo* info) { 838 RegLocation res; 839 if (info->result.location == kLocInvalid) { 840 res = GetReturn(false); 841 } else { 842 res = info->result; 843 } 844 return res; 845} 846 847RegLocation Mir2Lir::InlineTargetWide(CallInfo* info) { 848 RegLocation res; 849 if (info->result.location == kLocInvalid) { 850 res = GetReturnWide(false); 851 } else { 852 res = info->result; 853 } 854 return res; 855} 856 857bool Mir2Lir::GenInlinedCharAt(CallInfo* info) { 858 if (cu_->instruction_set == kMips) { 859 // TODO - add Mips implementation 860 return false; 861 } 862 // Location of reference to data array 863 int value_offset = mirror::String::ValueOffset().Int32Value(); 864 // Location of count 865 int count_offset = mirror::String::CountOffset().Int32Value(); 866 // Starting offset within data array 867 int offset_offset = mirror::String::OffsetOffset().Int32Value(); 868 // Start of char data with array_ 869 int data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Int32Value(); 870 871 RegLocation rl_obj = info->args[0]; 872 RegLocation rl_idx = info->args[1]; 873 rl_obj = LoadValue(rl_obj, kCoreReg); 874 rl_idx = LoadValue(rl_idx, kCoreReg); 875 int reg_max; 876 GenNullCheck(rl_obj.s_reg_low, rl_obj.low_reg, info->opt_flags); 877 bool range_check = (!(info->opt_flags & MIR_IGNORE_RANGE_CHECK)); 878 LIR* launch_pad = NULL; 879 int reg_off = INVALID_REG; 880 int reg_ptr = INVALID_REG; 881 if (cu_->instruction_set != kX86) { 882 reg_off = AllocTemp(); 883 reg_ptr = AllocTemp(); 884 if (range_check) { 885 reg_max = AllocTemp(); 886 LoadWordDisp(rl_obj.low_reg, count_offset, reg_max); 887 } 888 LoadWordDisp(rl_obj.low_reg, offset_offset, reg_off); 889 LoadWordDisp(rl_obj.low_reg, value_offset, reg_ptr); 890 if (range_check) { 891 // Set up a launch pad to allow retry in case of bounds violation */ 892 launch_pad = RawLIR(0, kPseudoIntrinsicRetry, WrapPointer(info)); 893 intrinsic_launchpads_.Insert(launch_pad); 894 OpRegReg(kOpCmp, rl_idx.low_reg, reg_max); 895 FreeTemp(reg_max); 896 OpCondBranch(kCondCs, launch_pad); 897 } 898 } else { 899 if (range_check) { 900 reg_max = AllocTemp(); 901 LoadWordDisp(rl_obj.low_reg, count_offset, reg_max); 902 // Set up a launch pad to allow retry in case of bounds violation */ 903 launch_pad = RawLIR(0, kPseudoIntrinsicRetry, WrapPointer(info)); 904 intrinsic_launchpads_.Insert(launch_pad); 905 OpRegReg(kOpCmp, rl_idx.low_reg, reg_max); 906 FreeTemp(reg_max); 907 OpCondBranch(kCondCc, launch_pad); 908 } 909 reg_off = AllocTemp(); 910 reg_ptr = AllocTemp(); 911 LoadWordDisp(rl_obj.low_reg, offset_offset, reg_off); 912 LoadWordDisp(rl_obj.low_reg, value_offset, reg_ptr); 913 } 914 OpRegImm(kOpAdd, reg_ptr, data_offset); 915 OpRegReg(kOpAdd, reg_off, rl_idx.low_reg); 916 FreeTemp(rl_obj.low_reg); 917 FreeTemp(rl_idx.low_reg); 918 RegLocation rl_dest = InlineTarget(info); 919 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); 920 LoadBaseIndexed(reg_ptr, reg_off, rl_result.low_reg, 1, kUnsignedHalf); 921 FreeTemp(reg_off); 922 FreeTemp(reg_ptr); 923 StoreValue(rl_dest, rl_result); 924 if (range_check) { 925 launch_pad->operands[2] = 0; // no resumption 926 } 927 // Record that we've already inlined & null checked 928 info->opt_flags |= (MIR_INLINED | MIR_IGNORE_NULL_CHECK); 929 return true; 930} 931 932// Generates an inlined String.is_empty or String.length. 933bool Mir2Lir::GenInlinedStringIsEmptyOrLength(CallInfo* info, bool is_empty) { 934 if (cu_->instruction_set == kMips) { 935 // TODO - add Mips implementation 936 return false; 937 } 938 // dst = src.length(); 939 RegLocation rl_obj = info->args[0]; 940 rl_obj = LoadValue(rl_obj, kCoreReg); 941 RegLocation rl_dest = InlineTarget(info); 942 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); 943 GenNullCheck(rl_obj.s_reg_low, rl_obj.low_reg, info->opt_flags); 944 LoadWordDisp(rl_obj.low_reg, mirror::String::CountOffset().Int32Value(), rl_result.low_reg); 945 if (is_empty) { 946 // dst = (dst == 0); 947 if (cu_->instruction_set == kThumb2) { 948 int t_reg = AllocTemp(); 949 OpRegReg(kOpNeg, t_reg, rl_result.low_reg); 950 OpRegRegReg(kOpAdc, rl_result.low_reg, rl_result.low_reg, t_reg); 951 } else { 952 DCHECK_EQ(cu_->instruction_set, kX86); 953 OpRegImm(kOpSub, rl_result.low_reg, 1); 954 OpRegImm(kOpLsr, rl_result.low_reg, 31); 955 } 956 } 957 StoreValue(rl_dest, rl_result); 958 return true; 959} 960 961bool Mir2Lir::GenInlinedAbsInt(CallInfo* info) { 962 if (cu_->instruction_set == kMips) { 963 // TODO - add Mips implementation 964 return false; 965 } 966 RegLocation rl_src = info->args[0]; 967 rl_src = LoadValue(rl_src, kCoreReg); 968 RegLocation rl_dest = InlineTarget(info); 969 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); 970 int sign_reg = AllocTemp(); 971 // abs(x) = y<=x>>31, (x+y)^y. 972 OpRegRegImm(kOpAsr, sign_reg, rl_src.low_reg, 31); 973 OpRegRegReg(kOpAdd, rl_result.low_reg, rl_src.low_reg, sign_reg); 974 OpRegReg(kOpXor, rl_result.low_reg, sign_reg); 975 StoreValue(rl_dest, rl_result); 976 return true; 977} 978 979bool Mir2Lir::GenInlinedAbsLong(CallInfo* info) { 980 if (cu_->instruction_set == kMips) { 981 // TODO - add Mips implementation 982 return false; 983 } 984 if (cu_->instruction_set == kThumb2) { 985 RegLocation rl_src = info->args[0]; 986 rl_src = LoadValueWide(rl_src, kCoreReg); 987 RegLocation rl_dest = InlineTargetWide(info); 988 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); 989 int sign_reg = AllocTemp(); 990 // abs(x) = y<=x>>31, (x+y)^y. 991 OpRegRegImm(kOpAsr, sign_reg, rl_src.high_reg, 31); 992 OpRegRegReg(kOpAdd, rl_result.low_reg, rl_src.low_reg, sign_reg); 993 OpRegRegReg(kOpAdc, rl_result.high_reg, rl_src.high_reg, sign_reg); 994 OpRegReg(kOpXor, rl_result.low_reg, sign_reg); 995 OpRegReg(kOpXor, rl_result.high_reg, sign_reg); 996 StoreValueWide(rl_dest, rl_result); 997 return true; 998 } else { 999 DCHECK_EQ(cu_->instruction_set, kX86); 1000 // Reuse source registers to avoid running out of temps 1001 RegLocation rl_src = info->args[0]; 1002 rl_src = LoadValueWide(rl_src, kCoreReg); 1003 RegLocation rl_dest = InlineTargetWide(info); 1004 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); 1005 OpRegCopyWide(rl_result.low_reg, rl_result.high_reg, rl_src.low_reg, rl_src.high_reg); 1006 FreeTemp(rl_src.low_reg); 1007 FreeTemp(rl_src.high_reg); 1008 int sign_reg = AllocTemp(); 1009 // abs(x) = y<=x>>31, (x+y)^y. 1010 OpRegRegImm(kOpAsr, sign_reg, rl_result.high_reg, 31); 1011 OpRegReg(kOpAdd, rl_result.low_reg, sign_reg); 1012 OpRegReg(kOpAdc, rl_result.high_reg, sign_reg); 1013 OpRegReg(kOpXor, rl_result.low_reg, sign_reg); 1014 OpRegReg(kOpXor, rl_result.high_reg, sign_reg); 1015 StoreValueWide(rl_dest, rl_result); 1016 return true; 1017 } 1018} 1019 1020bool Mir2Lir::GenInlinedFloatCvt(CallInfo* info) { 1021 if (cu_->instruction_set == kMips) { 1022 // TODO - add Mips implementation 1023 return false; 1024 } 1025 RegLocation rl_src = info->args[0]; 1026 RegLocation rl_dest = InlineTarget(info); 1027 StoreValue(rl_dest, rl_src); 1028 return true; 1029} 1030 1031bool Mir2Lir::GenInlinedDoubleCvt(CallInfo* info) { 1032 if (cu_->instruction_set == kMips) { 1033 // TODO - add Mips implementation 1034 return false; 1035 } 1036 RegLocation rl_src = info->args[0]; 1037 RegLocation rl_dest = InlineTargetWide(info); 1038 StoreValueWide(rl_dest, rl_src); 1039 return true; 1040} 1041 1042/* 1043 * Fast string.index_of(I) & (II). Tests for simple case of char <= 0xffff, 1044 * otherwise bails to standard library code. 1045 */ 1046bool Mir2Lir::GenInlinedIndexOf(CallInfo* info, bool zero_based) { 1047 if (cu_->instruction_set == kMips) { 1048 // TODO - add Mips implementation 1049 return false; 1050 } 1051 ClobberCalleeSave(); 1052 LockCallTemps(); // Using fixed registers 1053 int reg_ptr = TargetReg(kArg0); 1054 int reg_char = TargetReg(kArg1); 1055 int reg_start = TargetReg(kArg2); 1056 1057 RegLocation rl_obj = info->args[0]; 1058 RegLocation rl_char = info->args[1]; 1059 RegLocation rl_start = info->args[2]; 1060 LoadValueDirectFixed(rl_obj, reg_ptr); 1061 LoadValueDirectFixed(rl_char, reg_char); 1062 if (zero_based) { 1063 LoadConstant(reg_start, 0); 1064 } else { 1065 LoadValueDirectFixed(rl_start, reg_start); 1066 } 1067 int r_tgt = (cu_->instruction_set != kX86) ? LoadHelper(QUICK_ENTRYPOINT_OFFSET(pIndexOf)) : 0; 1068 GenNullCheck(rl_obj.s_reg_low, reg_ptr, info->opt_flags); 1069 LIR* launch_pad = RawLIR(0, kPseudoIntrinsicRetry, WrapPointer(info)); 1070 intrinsic_launchpads_.Insert(launch_pad); 1071 OpCmpImmBranch(kCondGt, reg_char, 0xFFFF, launch_pad); 1072 // NOTE: not a safepoint 1073 if (cu_->instruction_set != kX86) { 1074 OpReg(kOpBlx, r_tgt); 1075 } else { 1076 OpThreadMem(kOpBlx, QUICK_ENTRYPOINT_OFFSET(pIndexOf)); 1077 } 1078 LIR* resume_tgt = NewLIR0(kPseudoTargetLabel); 1079 launch_pad->operands[2] = WrapPointer(resume_tgt); 1080 // Record that we've already inlined & null checked 1081 info->opt_flags |= (MIR_INLINED | MIR_IGNORE_NULL_CHECK); 1082 RegLocation rl_return = GetReturn(false); 1083 RegLocation rl_dest = InlineTarget(info); 1084 StoreValue(rl_dest, rl_return); 1085 return true; 1086} 1087 1088/* Fast string.compareTo(Ljava/lang/string;)I. */ 1089bool Mir2Lir::GenInlinedStringCompareTo(CallInfo* info) { 1090 if (cu_->instruction_set == kMips) { 1091 // TODO - add Mips implementation 1092 return false; 1093 } 1094 ClobberCalleeSave(); 1095 LockCallTemps(); // Using fixed registers 1096 int reg_this = TargetReg(kArg0); 1097 int reg_cmp = TargetReg(kArg1); 1098 1099 RegLocation rl_this = info->args[0]; 1100 RegLocation rl_cmp = info->args[1]; 1101 LoadValueDirectFixed(rl_this, reg_this); 1102 LoadValueDirectFixed(rl_cmp, reg_cmp); 1103 int r_tgt = (cu_->instruction_set != kX86) ? 1104 LoadHelper(QUICK_ENTRYPOINT_OFFSET(pStringCompareTo)) : 0; 1105 GenNullCheck(rl_this.s_reg_low, reg_this, info->opt_flags); 1106 // TUNING: check if rl_cmp.s_reg_low is already null checked 1107 LIR* launch_pad = RawLIR(0, kPseudoIntrinsicRetry, WrapPointer(info)); 1108 intrinsic_launchpads_.Insert(launch_pad); 1109 OpCmpImmBranch(kCondEq, reg_cmp, 0, launch_pad); 1110 // NOTE: not a safepoint 1111 if (cu_->instruction_set != kX86) { 1112 OpReg(kOpBlx, r_tgt); 1113 } else { 1114 OpThreadMem(kOpBlx, QUICK_ENTRYPOINT_OFFSET(pStringCompareTo)); 1115 } 1116 launch_pad->operands[2] = 0; // No return possible 1117 // Record that we've already inlined & null checked 1118 info->opt_flags |= (MIR_INLINED | MIR_IGNORE_NULL_CHECK); 1119 RegLocation rl_return = GetReturn(false); 1120 RegLocation rl_dest = InlineTarget(info); 1121 StoreValue(rl_dest, rl_return); 1122 return true; 1123} 1124 1125bool Mir2Lir::GenInlinedCurrentThread(CallInfo* info) { 1126 RegLocation rl_dest = InlineTarget(info); 1127 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); 1128 ThreadOffset offset = Thread::PeerOffset(); 1129 if (cu_->instruction_set == kThumb2 || cu_->instruction_set == kMips) { 1130 LoadWordDisp(TargetReg(kSelf), offset.Int32Value(), rl_result.low_reg); 1131 } else { 1132 CHECK(cu_->instruction_set == kX86); 1133 reinterpret_cast<X86Mir2Lir*>(this)->OpRegThreadMem(kOpMov, rl_result.low_reg, offset); 1134 } 1135 StoreValue(rl_dest, rl_result); 1136 return true; 1137} 1138 1139bool Mir2Lir::GenInlinedUnsafeGet(CallInfo* info, 1140 bool is_long, bool is_volatile) { 1141 if (cu_->instruction_set == kMips) { 1142 // TODO - add Mips implementation 1143 return false; 1144 } 1145 // Unused - RegLocation rl_src_unsafe = info->args[0]; 1146 RegLocation rl_src_obj = info->args[1]; // Object 1147 RegLocation rl_src_offset = info->args[2]; // long low 1148 rl_src_offset.wide = 0; // ignore high half in info->args[3] 1149 RegLocation rl_dest = InlineTarget(info); // result reg 1150 if (is_volatile) { 1151 GenMemBarrier(kLoadLoad); 1152 } 1153 RegLocation rl_object = LoadValue(rl_src_obj, kCoreReg); 1154 RegLocation rl_offset = LoadValue(rl_src_offset, kCoreReg); 1155 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); 1156 if (is_long) { 1157 OpRegReg(kOpAdd, rl_object.low_reg, rl_offset.low_reg); 1158 LoadBaseDispWide(rl_object.low_reg, 0, rl_result.low_reg, rl_result.high_reg, INVALID_SREG); 1159 StoreValueWide(rl_dest, rl_result); 1160 } else { 1161 LoadBaseIndexed(rl_object.low_reg, rl_offset.low_reg, rl_result.low_reg, 0, kWord); 1162 StoreValue(rl_dest, rl_result); 1163 } 1164 return true; 1165} 1166 1167bool Mir2Lir::GenInlinedUnsafePut(CallInfo* info, bool is_long, 1168 bool is_object, bool is_volatile, bool is_ordered) { 1169 if (cu_->instruction_set == kMips) { 1170 // TODO - add Mips implementation 1171 return false; 1172 } 1173 if (cu_->instruction_set == kX86 && is_object) { 1174 // TODO: fix X86, it exhausts registers for card marking. 1175 return false; 1176 } 1177 // Unused - RegLocation rl_src_unsafe = info->args[0]; 1178 RegLocation rl_src_obj = info->args[1]; // Object 1179 RegLocation rl_src_offset = info->args[2]; // long low 1180 rl_src_offset.wide = 0; // ignore high half in info->args[3] 1181 RegLocation rl_src_value = info->args[4]; // value to store 1182 if (is_volatile || is_ordered) { 1183 GenMemBarrier(kStoreStore); 1184 } 1185 RegLocation rl_object = LoadValue(rl_src_obj, kCoreReg); 1186 RegLocation rl_offset = LoadValue(rl_src_offset, kCoreReg); 1187 RegLocation rl_value; 1188 if (is_long) { 1189 rl_value = LoadValueWide(rl_src_value, kCoreReg); 1190 OpRegReg(kOpAdd, rl_object.low_reg, rl_offset.low_reg); 1191 StoreBaseDispWide(rl_object.low_reg, 0, rl_value.low_reg, rl_value.high_reg); 1192 } else { 1193 rl_value = LoadValue(rl_src_value, kCoreReg); 1194 StoreBaseIndexed(rl_object.low_reg, rl_offset.low_reg, rl_value.low_reg, 0, kWord); 1195 } 1196 if (is_volatile) { 1197 GenMemBarrier(kStoreLoad); 1198 } 1199 if (is_object) { 1200 MarkGCCard(rl_value.low_reg, rl_object.low_reg); 1201 } 1202 return true; 1203} 1204 1205bool Mir2Lir::GenIntrinsic(CallInfo* info) { 1206 if (info->opt_flags & MIR_INLINED) { 1207 return false; 1208 } 1209 /* 1210 * TODO: move these to a target-specific structured constant array 1211 * and use a generic match function. The list of intrinsics may be 1212 * slightly different depending on target. 1213 * TODO: Fold this into a matching function that runs during 1214 * basic block building. This should be part of the action for 1215 * small method inlining and recognition of the special object init 1216 * method. By doing this during basic block construction, we can also 1217 * take advantage of/generate new useful dataflow info. 1218 */ 1219 const DexFile::MethodId& target_mid = cu_->dex_file->GetMethodId(info->index); 1220 const DexFile::TypeId& declaring_type = cu_->dex_file->GetTypeId(target_mid.class_idx_); 1221 StringPiece tgt_methods_declaring_class( 1222 cu_->dex_file->StringDataAsStringPieceByIdx(declaring_type.descriptor_idx_)); 1223 if (tgt_methods_declaring_class.starts_with("Ljava/lang/Double;")) { 1224 std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file)); 1225 if (tgt_method == "long java.lang.Double.doubleToRawLongBits(double)") { 1226 return GenInlinedDoubleCvt(info); 1227 } 1228 if (tgt_method == "double java.lang.Double.longBitsToDouble(long)") { 1229 return GenInlinedDoubleCvt(info); 1230 } 1231 } else if (tgt_methods_declaring_class.starts_with("Ljava/lang/Float;")) { 1232 std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file)); 1233 if (tgt_method == "int java.lang.Float.floatToRawIntBits(float)") { 1234 return GenInlinedFloatCvt(info); 1235 } 1236 if (tgt_method == "float java.lang.Float.intBitsToFloat(int)") { 1237 return GenInlinedFloatCvt(info); 1238 } 1239 } else if (tgt_methods_declaring_class.starts_with("Ljava/lang/Math;") || 1240 tgt_methods_declaring_class.starts_with("Ljava/lang/StrictMath;")) { 1241 std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file)); 1242 if (tgt_method == "int java.lang.Math.abs(int)" || 1243 tgt_method == "int java.lang.StrictMath.abs(int)") { 1244 return GenInlinedAbsInt(info); 1245 } 1246 if (tgt_method == "long java.lang.Math.abs(long)" || 1247 tgt_method == "long java.lang.StrictMath.abs(long)") { 1248 return GenInlinedAbsLong(info); 1249 } 1250 if (tgt_method == "int java.lang.Math.max(int, int)" || 1251 tgt_method == "int java.lang.StrictMath.max(int, int)") { 1252 return GenInlinedMinMaxInt(info, false /* is_min */); 1253 } 1254 if (tgt_method == "int java.lang.Math.min(int, int)" || 1255 tgt_method == "int java.lang.StrictMath.min(int, int)") { 1256 return GenInlinedMinMaxInt(info, true /* is_min */); 1257 } 1258 if (tgt_method == "double java.lang.Math.sqrt(double)" || 1259 tgt_method == "double java.lang.StrictMath.sqrt(double)") { 1260 return GenInlinedSqrt(info); 1261 } 1262 } else if (tgt_methods_declaring_class.starts_with("Ljava/lang/String;")) { 1263 std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file)); 1264 if (tgt_method == "char java.lang.String.charAt(int)") { 1265 return GenInlinedCharAt(info); 1266 } 1267 if (tgt_method == "int java.lang.String.compareTo(java.lang.String)") { 1268 return GenInlinedStringCompareTo(info); 1269 } 1270 if (tgt_method == "boolean java.lang.String.is_empty()") { 1271 return GenInlinedStringIsEmptyOrLength(info, true /* is_empty */); 1272 } 1273 if (tgt_method == "int java.lang.String.index_of(int, int)") { 1274 return GenInlinedIndexOf(info, false /* base 0 */); 1275 } 1276 if (tgt_method == "int java.lang.String.index_of(int)") { 1277 return GenInlinedIndexOf(info, true /* base 0 */); 1278 } 1279 if (tgt_method == "int java.lang.String.length()") { 1280 return GenInlinedStringIsEmptyOrLength(info, false /* is_empty */); 1281 } 1282 } else if (tgt_methods_declaring_class.starts_with("Ljava/lang/Thread;")) { 1283 std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file)); 1284 if (tgt_method == "java.lang.Thread java.lang.Thread.currentThread()") { 1285 return GenInlinedCurrentThread(info); 1286 } 1287 } else if (tgt_methods_declaring_class.starts_with("Lsun/misc/Unsafe;")) { 1288 std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file)); 1289 if (tgt_method == "boolean sun.misc.Unsafe.compareAndSwapInt(java.lang.Object, long, int, int)") { 1290 return GenInlinedCas32(info, false); 1291 } 1292 if (tgt_method == "boolean sun.misc.Unsafe.compareAndSwapObject(java.lang.Object, long, java.lang.Object, java.lang.Object)") { 1293 return GenInlinedCas32(info, true); 1294 } 1295 if (tgt_method == "int sun.misc.Unsafe.getInt(java.lang.Object, long)") { 1296 return GenInlinedUnsafeGet(info, false /* is_long */, false /* is_volatile */); 1297 } 1298 if (tgt_method == "int sun.misc.Unsafe.getIntVolatile(java.lang.Object, long)") { 1299 return GenInlinedUnsafeGet(info, false /* is_long */, true /* is_volatile */); 1300 } 1301 if (tgt_method == "void sun.misc.Unsafe.putInt(java.lang.Object, long, int)") { 1302 return GenInlinedUnsafePut(info, false /* is_long */, false /* is_object */, 1303 false /* is_volatile */, false /* is_ordered */); 1304 } 1305 if (tgt_method == "void sun.misc.Unsafe.putIntVolatile(java.lang.Object, long, int)") { 1306 return GenInlinedUnsafePut(info, false /* is_long */, false /* is_object */, 1307 true /* is_volatile */, false /* is_ordered */); 1308 } 1309 if (tgt_method == "void sun.misc.Unsafe.putOrderedInt(java.lang.Object, long, int)") { 1310 return GenInlinedUnsafePut(info, false /* is_long */, false /* is_object */, 1311 false /* is_volatile */, true /* is_ordered */); 1312 } 1313 if (tgt_method == "long sun.misc.Unsafe.getLong(java.lang.Object, long)") { 1314 return GenInlinedUnsafeGet(info, true /* is_long */, false /* is_volatile */); 1315 } 1316 if (tgt_method == "long sun.misc.Unsafe.getLongVolatile(java.lang.Object, long)") { 1317 return GenInlinedUnsafeGet(info, true /* is_long */, true /* is_volatile */); 1318 } 1319 if (tgt_method == "void sun.misc.Unsafe.putLong(java.lang.Object, long, long)") { 1320 return GenInlinedUnsafePut(info, true /* is_long */, false /* is_object */, 1321 false /* is_volatile */, false /* is_ordered */); 1322 } 1323 if (tgt_method == "void sun.misc.Unsafe.putLongVolatile(java.lang.Object, long, long)") { 1324 return GenInlinedUnsafePut(info, true /* is_long */, false /* is_object */, 1325 true /* is_volatile */, false /* is_ordered */); 1326 } 1327 if (tgt_method == "void sun.misc.Unsafe.putOrderedLong(java.lang.Object, long, long)") { 1328 return GenInlinedUnsafePut(info, true /* is_long */, false /* is_object */, 1329 false /* is_volatile */, true /* is_ordered */); 1330 } 1331 if (tgt_method == "java.lang.Object sun.misc.Unsafe.getObject(java.lang.Object, long)") { 1332 return GenInlinedUnsafeGet(info, false /* is_long */, false /* is_volatile */); 1333 } 1334 if (tgt_method == "java.lang.Object sun.misc.Unsafe.getObjectVolatile(java.lang.Object, long)") { 1335 return GenInlinedUnsafeGet(info, false /* is_long */, true /* is_volatile */); 1336 } 1337 if (tgt_method == "void sun.misc.Unsafe.putObject(java.lang.Object, long, java.lang.Object)") { 1338 return GenInlinedUnsafePut(info, false /* is_long */, true /* is_object */, 1339 false /* is_volatile */, false /* is_ordered */); 1340 } 1341 if (tgt_method == "void sun.misc.Unsafe.putObjectVolatile(java.lang.Object, long, java.lang.Object)") { 1342 return GenInlinedUnsafePut(info, false /* is_long */, true /* is_object */, 1343 true /* is_volatile */, false /* is_ordered */); 1344 } 1345 if (tgt_method == "void sun.misc.Unsafe.putOrderedObject(java.lang.Object, long, java.lang.Object)") { 1346 return GenInlinedUnsafePut(info, false /* is_long */, true /* is_object */, 1347 false /* is_volatile */, true /* is_ordered */); 1348 } 1349 } 1350 return false; 1351} 1352 1353void Mir2Lir::GenInvoke(CallInfo* info) { 1354 if (GenIntrinsic(info)) { 1355 return; 1356 } 1357 InvokeType original_type = info->type; // avoiding mutation by ComputeInvokeInfo 1358 int call_state = 0; 1359 LIR* null_ck; 1360 LIR** p_null_ck = NULL; 1361 NextCallInsn next_call_insn; 1362 FlushAllRegs(); /* Everything to home location */ 1363 // Explicit register usage 1364 LockCallTemps(); 1365 1366 DexCompilationUnit* cUnit = mir_graph_->GetCurrentDexCompilationUnit(); 1367 MethodReference target_method(cUnit->GetDexFile(), info->index); 1368 int vtable_idx; 1369 uintptr_t direct_code; 1370 uintptr_t direct_method; 1371 bool skip_this; 1372 bool fast_path = 1373 cu_->compiler_driver->ComputeInvokeInfo(mir_graph_->GetCurrentDexCompilationUnit(), 1374 current_dalvik_offset_, 1375 true, true, 1376 &info->type, &target_method, 1377 &vtable_idx, 1378 &direct_code, &direct_method) && !SLOW_INVOKE_PATH; 1379 if (info->type == kInterface) { 1380 next_call_insn = fast_path ? NextInterfaceCallInsn : NextInterfaceCallInsnWithAccessCheck; 1381 skip_this = fast_path; 1382 } else if (info->type == kDirect) { 1383 if (fast_path) { 1384 p_null_ck = &null_ck; 1385 } 1386 next_call_insn = fast_path ? NextSDCallInsn : NextDirectCallInsnSP; 1387 skip_this = false; 1388 } else if (info->type == kStatic) { 1389 next_call_insn = fast_path ? NextSDCallInsn : NextStaticCallInsnSP; 1390 skip_this = false; 1391 } else if (info->type == kSuper) { 1392 DCHECK(!fast_path); // Fast path is a direct call. 1393 next_call_insn = NextSuperCallInsnSP; 1394 skip_this = false; 1395 } else { 1396 DCHECK_EQ(info->type, kVirtual); 1397 next_call_insn = fast_path ? NextVCallInsn : NextVCallInsnSP; 1398 skip_this = fast_path; 1399 } 1400 if (!info->is_range) { 1401 call_state = GenDalvikArgsNoRange(info, call_state, p_null_ck, 1402 next_call_insn, target_method, 1403 vtable_idx, direct_code, direct_method, 1404 original_type, skip_this); 1405 } else { 1406 call_state = GenDalvikArgsRange(info, call_state, p_null_ck, 1407 next_call_insn, target_method, vtable_idx, 1408 direct_code, direct_method, original_type, 1409 skip_this); 1410 } 1411 // Finish up any of the call sequence not interleaved in arg loading 1412 while (call_state >= 0) { 1413 call_state = next_call_insn(cu_, info, call_state, target_method, 1414 vtable_idx, direct_code, direct_method, 1415 original_type); 1416 } 1417 LIR* call_inst; 1418 if (cu_->instruction_set != kX86) { 1419 call_inst = OpReg(kOpBlx, TargetReg(kInvokeTgt)); 1420 } else { 1421 if (fast_path) { 1422 call_inst = OpMem(kOpBlx, TargetReg(kArg0), 1423 mirror::ArtMethod::GetEntryPointFromCompiledCodeOffset().Int32Value()); 1424 } else { 1425 ThreadOffset trampoline(-1); 1426 switch (info->type) { 1427 case kInterface: 1428 trampoline = QUICK_ENTRYPOINT_OFFSET(pInvokeInterfaceTrampolineWithAccessCheck); 1429 break; 1430 case kDirect: 1431 trampoline = QUICK_ENTRYPOINT_OFFSET(pInvokeDirectTrampolineWithAccessCheck); 1432 break; 1433 case kStatic: 1434 trampoline = QUICK_ENTRYPOINT_OFFSET(pInvokeStaticTrampolineWithAccessCheck); 1435 break; 1436 case kSuper: 1437 trampoline = QUICK_ENTRYPOINT_OFFSET(pInvokeSuperTrampolineWithAccessCheck); 1438 break; 1439 case kVirtual: 1440 trampoline = QUICK_ENTRYPOINT_OFFSET(pInvokeVirtualTrampolineWithAccessCheck); 1441 break; 1442 default: 1443 LOG(FATAL) << "Unexpected invoke type"; 1444 } 1445 call_inst = OpThreadMem(kOpBlx, trampoline); 1446 } 1447 } 1448 MarkSafepointPC(call_inst); 1449 1450 ClobberCalleeSave(); 1451 if (info->result.location != kLocInvalid) { 1452 // We have a following MOVE_RESULT - do it now. 1453 if (info->result.wide) { 1454 RegLocation ret_loc = GetReturnWide(info->result.fp); 1455 StoreValueWide(info->result, ret_loc); 1456 } else { 1457 RegLocation ret_loc = GetReturn(info->result.fp); 1458 StoreValue(info->result, ret_loc); 1459 } 1460 } 1461} 1462 1463} // namespace art 1464