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