gen_invoke.cc revision dfb325e0ddd746cd8f7c2e3723b3a573eb7cc111
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 * All invoke-interface calls bounce off of art_quick_invoke_interface_trampoline, 495 * which will locate the target and continue on via a tail call. 496 */ 497static int NextInterfaceCallInsn(CompilationUnit* cu, CallInfo* info, int state, 498 const MethodReference& target_method, 499 uint32_t unused, uintptr_t unused2, 500 uintptr_t direct_method, InvokeType unused4) { 501 Mir2Lir* cg = static_cast<Mir2Lir*>(cu->cg.get()); 502 ThreadOffset trampoline = QUICK_ENTRYPOINT_OFFSET(pInvokeInterfaceTrampoline); 503 504 if (direct_method != 0) { 505 switch (state) { 506 case 0: // Load the trampoline target [sets kInvokeTgt]. 507 if (cu->instruction_set != kX86) { 508 cg->LoadWordDisp(cg->TargetReg(kSelf), trampoline.Int32Value(), 509 cg->TargetReg(kInvokeTgt)); 510 } 511 // Get the interface Method* [sets kArg0] 512 if (direct_method != static_cast<unsigned int>(-1)) { 513 cg->LoadConstant(cg->TargetReg(kArg0), direct_method); 514 } else { 515 CHECK_EQ(cu->dex_file, target_method.dex_file); 516 LIR* data_target = cg->ScanLiteralPool(cg->method_literal_list_, 517 target_method.dex_method_index, 0); 518 if (data_target == NULL) { 519 data_target = cg->AddWordData(&cg->method_literal_list_, 520 target_method.dex_method_index); 521 data_target->operands[1] = kInterface; 522 } 523 LIR* load_pc_rel = cg->OpPcRelLoad(cg->TargetReg(kArg0), data_target); 524 cg->AppendLIR(load_pc_rel); 525 DCHECK_EQ(cu->instruction_set, kThumb2) << reinterpret_cast<void*>(data_target); 526 } 527 break; 528 default: 529 return -1; 530 } 531 } else { 532 switch (state) { 533 case 0: 534 // Get the current Method* [sets kArg0] - TUNING: remove copy of method if it is promoted. 535 cg->LoadCurrMethodDirect(cg->TargetReg(kArg0)); 536 // Load the trampoline target [sets kInvokeTgt]. 537 if (cu->instruction_set != kX86) { 538 cg->LoadWordDisp(cg->TargetReg(kSelf), trampoline.Int32Value(), 539 cg->TargetReg(kInvokeTgt)); 540 } 541 break; 542 case 1: // Get method->dex_cache_resolved_methods_ [set/use kArg0] 543 cg->LoadWordDisp(cg->TargetReg(kArg0), 544 mirror::ArtMethod::DexCacheResolvedMethodsOffset().Int32Value(), 545 cg->TargetReg(kArg0)); 546 break; 547 case 2: // Grab target method* [set/use kArg0] 548 CHECK_EQ(cu->dex_file, target_method.dex_file); 549 cg->LoadWordDisp(cg->TargetReg(kArg0), 550 mirror::Array::DataOffset(sizeof(mirror::Object*)).Int32Value() + 551 (target_method.dex_method_index * 4), 552 cg->TargetReg(kArg0)); 553 break; 554 default: 555 return -1; 556 } 557 } 558 return state + 1; 559} 560 561static int NextInvokeInsnSP(CompilationUnit* cu, CallInfo* info, ThreadOffset trampoline, 562 int state, const MethodReference& target_method, 563 uint32_t method_idx) { 564 Mir2Lir* cg = static_cast<Mir2Lir*>(cu->cg.get()); 565 /* 566 * This handles the case in which the base method is not fully 567 * resolved at compile time, we bail to a runtime helper. 568 */ 569 if (state == 0) { 570 if (cu->instruction_set != kX86) { 571 // Load trampoline target 572 cg->LoadWordDisp(cg->TargetReg(kSelf), trampoline.Int32Value(), cg->TargetReg(kInvokeTgt)); 573 } 574 // Load kArg0 with method index 575 CHECK_EQ(cu->dex_file, target_method.dex_file); 576 cg->LoadConstant(cg->TargetReg(kArg0), target_method.dex_method_index); 577 return 1; 578 } 579 return -1; 580} 581 582static int NextStaticCallInsnSP(CompilationUnit* cu, CallInfo* info, 583 int state, 584 const MethodReference& target_method, 585 uint32_t method_idx, 586 uintptr_t unused, uintptr_t unused2, 587 InvokeType unused3) { 588 ThreadOffset trampoline = QUICK_ENTRYPOINT_OFFSET(pInvokeStaticTrampolineWithAccessCheck); 589 return NextInvokeInsnSP(cu, info, trampoline, state, target_method, 0); 590} 591 592static int NextDirectCallInsnSP(CompilationUnit* cu, CallInfo* info, int state, 593 const MethodReference& target_method, 594 uint32_t method_idx, uintptr_t unused, 595 uintptr_t unused2, InvokeType unused3) { 596 ThreadOffset trampoline = QUICK_ENTRYPOINT_OFFSET(pInvokeDirectTrampolineWithAccessCheck); 597 return NextInvokeInsnSP(cu, info, trampoline, state, target_method, 0); 598} 599 600static int NextSuperCallInsnSP(CompilationUnit* cu, CallInfo* info, int state, 601 const MethodReference& target_method, 602 uint32_t method_idx, uintptr_t unused, 603 uintptr_t unused2, InvokeType unused3) { 604 ThreadOffset trampoline = QUICK_ENTRYPOINT_OFFSET(pInvokeSuperTrampolineWithAccessCheck); 605 return NextInvokeInsnSP(cu, info, trampoline, state, target_method, 0); 606} 607 608static int NextVCallInsnSP(CompilationUnit* cu, CallInfo* info, int state, 609 const MethodReference& target_method, 610 uint32_t method_idx, uintptr_t unused, 611 uintptr_t unused2, InvokeType unused3) { 612 ThreadOffset trampoline = QUICK_ENTRYPOINT_OFFSET(pInvokeVirtualTrampolineWithAccessCheck); 613 return NextInvokeInsnSP(cu, info, trampoline, state, target_method, 0); 614} 615 616static int NextInterfaceCallInsnWithAccessCheck(CompilationUnit* cu, 617 CallInfo* info, int state, 618 const MethodReference& target_method, 619 uint32_t unused, 620 uintptr_t unused2, uintptr_t unused3, 621 InvokeType unused4) { 622 ThreadOffset trampoline = QUICK_ENTRYPOINT_OFFSET(pInvokeInterfaceTrampolineWithAccessCheck); 623 return NextInvokeInsnSP(cu, info, trampoline, state, target_method, 0); 624} 625 626int Mir2Lir::LoadArgRegs(CallInfo* info, int call_state, 627 NextCallInsn next_call_insn, 628 const MethodReference& target_method, 629 uint32_t vtable_idx, uintptr_t direct_code, 630 uintptr_t direct_method, InvokeType type, bool skip_this) { 631 int last_arg_reg = TargetReg(kArg3); 632 int next_reg = TargetReg(kArg1); 633 int next_arg = 0; 634 if (skip_this) { 635 next_reg++; 636 next_arg++; 637 } 638 for (; (next_reg <= last_arg_reg) && (next_arg < info->num_arg_words); next_reg++) { 639 RegLocation rl_arg = info->args[next_arg++]; 640 rl_arg = UpdateRawLoc(rl_arg); 641 if (rl_arg.wide && (next_reg <= TargetReg(kArg2))) { 642 LoadValueDirectWideFixed(rl_arg, next_reg, next_reg + 1); 643 next_reg++; 644 next_arg++; 645 } else { 646 if (rl_arg.wide) { 647 rl_arg.wide = false; 648 rl_arg.is_const = false; 649 } 650 LoadValueDirectFixed(rl_arg, next_reg); 651 } 652 call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx, 653 direct_code, direct_method, type); 654 } 655 return call_state; 656} 657 658/* 659 * Load up to 5 arguments, the first three of which will be in 660 * kArg1 .. kArg3. On entry kArg0 contains the current method pointer, 661 * and as part of the load sequence, it must be replaced with 662 * the target method pointer. Note, this may also be called 663 * for "range" variants if the number of arguments is 5 or fewer. 664 */ 665int Mir2Lir::GenDalvikArgsNoRange(CallInfo* info, 666 int call_state, LIR** pcrLabel, NextCallInsn next_call_insn, 667 const MethodReference& target_method, 668 uint32_t vtable_idx, uintptr_t direct_code, 669 uintptr_t direct_method, InvokeType type, bool skip_this) { 670 RegLocation rl_arg; 671 672 /* If no arguments, just return */ 673 if (info->num_arg_words == 0) 674 return call_state; 675 676 call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx, 677 direct_code, direct_method, type); 678 679 DCHECK_LE(info->num_arg_words, 5); 680 if (info->num_arg_words > 3) { 681 int32_t next_use = 3; 682 // Detect special case of wide arg spanning arg3/arg4 683 RegLocation rl_use0 = info->args[0]; 684 RegLocation rl_use1 = info->args[1]; 685 RegLocation rl_use2 = info->args[2]; 686 if (((!rl_use0.wide && !rl_use1.wide) || rl_use0.wide) && 687 rl_use2.wide) { 688 int reg = -1; 689 // Wide spans, we need the 2nd half of uses[2]. 690 rl_arg = UpdateLocWide(rl_use2); 691 if (rl_arg.location == kLocPhysReg) { 692 reg = rl_arg.high_reg; 693 } else { 694 // kArg2 & rArg3 can safely be used here 695 reg = TargetReg(kArg3); 696 LoadWordDisp(TargetReg(kSp), SRegOffset(rl_arg.s_reg_low) + 4, reg); 697 call_state = next_call_insn(cu_, info, call_state, target_method, 698 vtable_idx, direct_code, direct_method, type); 699 } 700 StoreBaseDisp(TargetReg(kSp), (next_use + 1) * 4, reg, kWord); 701 StoreBaseDisp(TargetReg(kSp), 16 /* (3+1)*4 */, reg, kWord); 702 call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx, 703 direct_code, direct_method, type); 704 next_use++; 705 } 706 // Loop through the rest 707 while (next_use < info->num_arg_words) { 708 int low_reg; 709 int high_reg = -1; 710 rl_arg = info->args[next_use]; 711 rl_arg = UpdateRawLoc(rl_arg); 712 if (rl_arg.location == kLocPhysReg) { 713 low_reg = rl_arg.low_reg; 714 high_reg = rl_arg.high_reg; 715 } else { 716 low_reg = TargetReg(kArg2); 717 if (rl_arg.wide) { 718 high_reg = TargetReg(kArg3); 719 LoadValueDirectWideFixed(rl_arg, low_reg, high_reg); 720 } else { 721 LoadValueDirectFixed(rl_arg, low_reg); 722 } 723 call_state = next_call_insn(cu_, info, call_state, target_method, 724 vtable_idx, direct_code, direct_method, type); 725 } 726 int outs_offset = (next_use + 1) * 4; 727 if (rl_arg.wide) { 728 StoreBaseDispWide(TargetReg(kSp), outs_offset, low_reg, high_reg); 729 next_use += 2; 730 } else { 731 StoreWordDisp(TargetReg(kSp), outs_offset, low_reg); 732 next_use++; 733 } 734 call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx, 735 direct_code, direct_method, type); 736 } 737 } 738 739 call_state = LoadArgRegs(info, call_state, next_call_insn, 740 target_method, vtable_idx, direct_code, direct_method, 741 type, skip_this); 742 743 if (pcrLabel) { 744 *pcrLabel = GenNullCheck(info->args[0].s_reg_low, TargetReg(kArg1), info->opt_flags); 745 } 746 return call_state; 747} 748 749/* 750 * May have 0+ arguments (also used for jumbo). Note that 751 * source virtual registers may be in physical registers, so may 752 * need to be flushed to home location before copying. This 753 * applies to arg3 and above (see below). 754 * 755 * Two general strategies: 756 * If < 20 arguments 757 * Pass args 3-18 using vldm/vstm block copy 758 * Pass arg0, arg1 & arg2 in kArg1-kArg3 759 * If 20+ arguments 760 * Pass args arg19+ using memcpy block copy 761 * Pass arg0, arg1 & arg2 in kArg1-kArg3 762 * 763 */ 764int Mir2Lir::GenDalvikArgsRange(CallInfo* info, int call_state, 765 LIR** pcrLabel, NextCallInsn next_call_insn, 766 const MethodReference& target_method, 767 uint32_t vtable_idx, uintptr_t direct_code, uintptr_t direct_method, 768 InvokeType type, bool skip_this) { 769 // If we can treat it as non-range (Jumbo ops will use range form) 770 if (info->num_arg_words <= 5) 771 return GenDalvikArgsNoRange(info, call_state, pcrLabel, 772 next_call_insn, target_method, vtable_idx, 773 direct_code, direct_method, type, skip_this); 774 /* 775 * First load the non-register arguments. Both forms expect all 776 * of the source arguments to be in their home frame location, so 777 * scan the s_reg names and flush any that have been promoted to 778 * frame backing storage. 779 */ 780 // Scan the rest of the args - if in phys_reg flush to memory 781 for (int next_arg = 0; next_arg < info->num_arg_words;) { 782 RegLocation loc = info->args[next_arg]; 783 if (loc.wide) { 784 loc = UpdateLocWide(loc); 785 if ((next_arg >= 2) && (loc.location == kLocPhysReg)) { 786 StoreBaseDispWide(TargetReg(kSp), SRegOffset(loc.s_reg_low), 787 loc.low_reg, loc.high_reg); 788 } 789 next_arg += 2; 790 } else { 791 loc = UpdateLoc(loc); 792 if ((next_arg >= 3) && (loc.location == kLocPhysReg)) { 793 StoreBaseDisp(TargetReg(kSp), SRegOffset(loc.s_reg_low), 794 loc.low_reg, kWord); 795 } 796 next_arg++; 797 } 798 } 799 800 int start_offset = SRegOffset(info->args[3].s_reg_low); 801 int outs_offset = 4 /* Method* */ + (3 * 4); 802 if (cu_->instruction_set != kThumb2) { 803 // Generate memcpy 804 OpRegRegImm(kOpAdd, TargetReg(kArg0), TargetReg(kSp), outs_offset); 805 OpRegRegImm(kOpAdd, TargetReg(kArg1), TargetReg(kSp), start_offset); 806 CallRuntimeHelperRegRegImm(QUICK_ENTRYPOINT_OFFSET(pMemcpy), TargetReg(kArg0), 807 TargetReg(kArg1), (info->num_arg_words - 3) * 4, false); 808 } else { 809 if (info->num_arg_words >= 20) { 810 // Generate memcpy 811 OpRegRegImm(kOpAdd, TargetReg(kArg0), TargetReg(kSp), outs_offset); 812 OpRegRegImm(kOpAdd, TargetReg(kArg1), TargetReg(kSp), start_offset); 813 CallRuntimeHelperRegRegImm(QUICK_ENTRYPOINT_OFFSET(pMemcpy), TargetReg(kArg0), 814 TargetReg(kArg1), (info->num_arg_words - 3) * 4, false); 815 } else { 816 // Use vldm/vstm pair using kArg3 as a temp 817 int regs_left = std::min(info->num_arg_words - 3, 16); 818 call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx, 819 direct_code, direct_method, type); 820 OpRegRegImm(kOpAdd, TargetReg(kArg3), TargetReg(kSp), start_offset); 821 LIR* ld = OpVldm(TargetReg(kArg3), regs_left); 822 // TUNING: loosen barrier 823 ld->u.m.def_mask = ENCODE_ALL; 824 SetMemRefType(ld, true /* is_load */, kDalvikReg); 825 call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx, 826 direct_code, direct_method, type); 827 OpRegRegImm(kOpAdd, TargetReg(kArg3), TargetReg(kSp), 4 /* Method* */ + (3 * 4)); 828 call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx, 829 direct_code, direct_method, type); 830 LIR* st = OpVstm(TargetReg(kArg3), regs_left); 831 SetMemRefType(st, false /* is_load */, kDalvikReg); 832 st->u.m.def_mask = ENCODE_ALL; 833 call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx, 834 direct_code, direct_method, type); 835 } 836 } 837 838 call_state = LoadArgRegs(info, call_state, next_call_insn, 839 target_method, vtable_idx, direct_code, direct_method, 840 type, skip_this); 841 842 call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx, 843 direct_code, direct_method, type); 844 if (pcrLabel) { 845 *pcrLabel = GenNullCheck(info->args[0].s_reg_low, TargetReg(kArg1), info->opt_flags); 846 } 847 return call_state; 848} 849 850RegLocation Mir2Lir::InlineTarget(CallInfo* info) { 851 RegLocation res; 852 if (info->result.location == kLocInvalid) { 853 res = GetReturn(false); 854 } else { 855 res = info->result; 856 } 857 return res; 858} 859 860RegLocation Mir2Lir::InlineTargetWide(CallInfo* info) { 861 RegLocation res; 862 if (info->result.location == kLocInvalid) { 863 res = GetReturnWide(false); 864 } else { 865 res = info->result; 866 } 867 return res; 868} 869 870bool Mir2Lir::GenInlinedCharAt(CallInfo* info) { 871 if (cu_->instruction_set == kMips) { 872 // TODO - add Mips implementation 873 return false; 874 } 875 // Location of reference to data array 876 int value_offset = mirror::String::ValueOffset().Int32Value(); 877 // Location of count 878 int count_offset = mirror::String::CountOffset().Int32Value(); 879 // Starting offset within data array 880 int offset_offset = mirror::String::OffsetOffset().Int32Value(); 881 // Start of char data with array_ 882 int data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Int32Value(); 883 884 RegLocation rl_obj = info->args[0]; 885 RegLocation rl_idx = info->args[1]; 886 rl_obj = LoadValue(rl_obj, kCoreReg); 887 rl_idx = LoadValue(rl_idx, kCoreReg); 888 int reg_max; 889 GenNullCheck(rl_obj.s_reg_low, rl_obj.low_reg, info->opt_flags); 890 bool range_check = (!(info->opt_flags & MIR_IGNORE_RANGE_CHECK)); 891 LIR* launch_pad = NULL; 892 int reg_off = INVALID_REG; 893 int reg_ptr = INVALID_REG; 894 if (cu_->instruction_set != kX86) { 895 reg_off = AllocTemp(); 896 reg_ptr = AllocTemp(); 897 if (range_check) { 898 reg_max = AllocTemp(); 899 LoadWordDisp(rl_obj.low_reg, count_offset, reg_max); 900 } 901 LoadWordDisp(rl_obj.low_reg, offset_offset, reg_off); 902 LoadWordDisp(rl_obj.low_reg, value_offset, reg_ptr); 903 if (range_check) { 904 // Set up a launch pad to allow retry in case of bounds violation */ 905 launch_pad = RawLIR(0, kPseudoIntrinsicRetry, WrapPointer(info)); 906 intrinsic_launchpads_.Insert(launch_pad); 907 OpRegReg(kOpCmp, rl_idx.low_reg, reg_max); 908 FreeTemp(reg_max); 909 OpCondBranch(kCondCs, launch_pad); 910 } 911 } else { 912 if (range_check) { 913 reg_max = AllocTemp(); 914 LoadWordDisp(rl_obj.low_reg, count_offset, reg_max); 915 // Set up a launch pad to allow retry in case of bounds violation */ 916 launch_pad = RawLIR(0, kPseudoIntrinsicRetry, WrapPointer(info)); 917 intrinsic_launchpads_.Insert(launch_pad); 918 OpRegReg(kOpCmp, rl_idx.low_reg, reg_max); 919 FreeTemp(reg_max); 920 OpCondBranch(kCondCc, launch_pad); 921 } 922 reg_off = AllocTemp(); 923 reg_ptr = AllocTemp(); 924 LoadWordDisp(rl_obj.low_reg, offset_offset, reg_off); 925 LoadWordDisp(rl_obj.low_reg, value_offset, reg_ptr); 926 } 927 OpRegImm(kOpAdd, reg_ptr, data_offset); 928 OpRegReg(kOpAdd, reg_off, rl_idx.low_reg); 929 FreeTemp(rl_obj.low_reg); 930 FreeTemp(rl_idx.low_reg); 931 RegLocation rl_dest = InlineTarget(info); 932 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); 933 LoadBaseIndexed(reg_ptr, reg_off, rl_result.low_reg, 1, kUnsignedHalf); 934 FreeTemp(reg_off); 935 FreeTemp(reg_ptr); 936 StoreValue(rl_dest, rl_result); 937 if (range_check) { 938 launch_pad->operands[2] = 0; // no resumption 939 } 940 // Record that we've already inlined & null checked 941 info->opt_flags |= (MIR_INLINED | MIR_IGNORE_NULL_CHECK); 942 return true; 943} 944 945// Generates an inlined String.is_empty or String.length. 946bool Mir2Lir::GenInlinedStringIsEmptyOrLength(CallInfo* info, bool is_empty) { 947 if (cu_->instruction_set == kMips) { 948 // TODO - add Mips implementation 949 return false; 950 } 951 // dst = src.length(); 952 RegLocation rl_obj = info->args[0]; 953 rl_obj = LoadValue(rl_obj, kCoreReg); 954 RegLocation rl_dest = InlineTarget(info); 955 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); 956 GenNullCheck(rl_obj.s_reg_low, rl_obj.low_reg, info->opt_flags); 957 LoadWordDisp(rl_obj.low_reg, mirror::String::CountOffset().Int32Value(), rl_result.low_reg); 958 if (is_empty) { 959 // dst = (dst == 0); 960 if (cu_->instruction_set == kThumb2) { 961 int t_reg = AllocTemp(); 962 OpRegReg(kOpNeg, t_reg, rl_result.low_reg); 963 OpRegRegReg(kOpAdc, rl_result.low_reg, rl_result.low_reg, t_reg); 964 } else { 965 DCHECK_EQ(cu_->instruction_set, kX86); 966 OpRegImm(kOpSub, rl_result.low_reg, 1); 967 OpRegImm(kOpLsr, rl_result.low_reg, 31); 968 } 969 } 970 StoreValue(rl_dest, rl_result); 971 return true; 972} 973 974bool Mir2Lir::GenInlinedAbsInt(CallInfo* info) { 975 if (cu_->instruction_set == kMips) { 976 // TODO - add Mips implementation 977 return false; 978 } 979 RegLocation rl_src = info->args[0]; 980 rl_src = LoadValue(rl_src, kCoreReg); 981 RegLocation rl_dest = InlineTarget(info); 982 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); 983 int sign_reg = AllocTemp(); 984 // abs(x) = y<=x>>31, (x+y)^y. 985 OpRegRegImm(kOpAsr, sign_reg, rl_src.low_reg, 31); 986 OpRegRegReg(kOpAdd, rl_result.low_reg, rl_src.low_reg, sign_reg); 987 OpRegReg(kOpXor, rl_result.low_reg, sign_reg); 988 StoreValue(rl_dest, rl_result); 989 return true; 990} 991 992bool Mir2Lir::GenInlinedAbsLong(CallInfo* info) { 993 if (cu_->instruction_set == kMips) { 994 // TODO - add Mips implementation 995 return false; 996 } 997 if (cu_->instruction_set == kThumb2) { 998 RegLocation rl_src = info->args[0]; 999 rl_src = LoadValueWide(rl_src, kCoreReg); 1000 RegLocation rl_dest = InlineTargetWide(info); 1001 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); 1002 int sign_reg = AllocTemp(); 1003 // abs(x) = y<=x>>31, (x+y)^y. 1004 OpRegRegImm(kOpAsr, sign_reg, rl_src.high_reg, 31); 1005 OpRegRegReg(kOpAdd, rl_result.low_reg, rl_src.low_reg, sign_reg); 1006 OpRegRegReg(kOpAdc, rl_result.high_reg, rl_src.high_reg, sign_reg); 1007 OpRegReg(kOpXor, rl_result.low_reg, sign_reg); 1008 OpRegReg(kOpXor, rl_result.high_reg, sign_reg); 1009 StoreValueWide(rl_dest, rl_result); 1010 return true; 1011 } else { 1012 DCHECK_EQ(cu_->instruction_set, kX86); 1013 // Reuse source registers to avoid running out of temps 1014 RegLocation rl_src = info->args[0]; 1015 rl_src = LoadValueWide(rl_src, kCoreReg); 1016 RegLocation rl_dest = InlineTargetWide(info); 1017 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); 1018 OpRegCopyWide(rl_result.low_reg, rl_result.high_reg, rl_src.low_reg, rl_src.high_reg); 1019 FreeTemp(rl_src.low_reg); 1020 FreeTemp(rl_src.high_reg); 1021 int sign_reg = AllocTemp(); 1022 // abs(x) = y<=x>>31, (x+y)^y. 1023 OpRegRegImm(kOpAsr, sign_reg, rl_result.high_reg, 31); 1024 OpRegReg(kOpAdd, rl_result.low_reg, sign_reg); 1025 OpRegReg(kOpAdc, rl_result.high_reg, sign_reg); 1026 OpRegReg(kOpXor, rl_result.low_reg, sign_reg); 1027 OpRegReg(kOpXor, rl_result.high_reg, sign_reg); 1028 StoreValueWide(rl_dest, rl_result); 1029 return true; 1030 } 1031} 1032 1033bool Mir2Lir::GenInlinedFloatCvt(CallInfo* info) { 1034 if (cu_->instruction_set == kMips) { 1035 // TODO - add Mips implementation 1036 return false; 1037 } 1038 RegLocation rl_src = info->args[0]; 1039 RegLocation rl_dest = InlineTarget(info); 1040 StoreValue(rl_dest, rl_src); 1041 return true; 1042} 1043 1044bool Mir2Lir::GenInlinedDoubleCvt(CallInfo* info) { 1045 if (cu_->instruction_set == kMips) { 1046 // TODO - add Mips implementation 1047 return false; 1048 } 1049 RegLocation rl_src = info->args[0]; 1050 RegLocation rl_dest = InlineTargetWide(info); 1051 StoreValueWide(rl_dest, rl_src); 1052 return true; 1053} 1054 1055/* 1056 * Fast string.index_of(I) & (II). Tests for simple case of char <= 0xffff, 1057 * otherwise bails to standard library code. 1058 */ 1059bool Mir2Lir::GenInlinedIndexOf(CallInfo* info, bool zero_based) { 1060 if (cu_->instruction_set == kMips) { 1061 // TODO - add Mips implementation 1062 return false; 1063 } 1064 ClobberCalleeSave(); 1065 LockCallTemps(); // Using fixed registers 1066 int reg_ptr = TargetReg(kArg0); 1067 int reg_char = TargetReg(kArg1); 1068 int reg_start = TargetReg(kArg2); 1069 1070 RegLocation rl_obj = info->args[0]; 1071 RegLocation rl_char = info->args[1]; 1072 RegLocation rl_start = info->args[2]; 1073 LoadValueDirectFixed(rl_obj, reg_ptr); 1074 LoadValueDirectFixed(rl_char, reg_char); 1075 if (zero_based) { 1076 LoadConstant(reg_start, 0); 1077 } else { 1078 LoadValueDirectFixed(rl_start, reg_start); 1079 } 1080 int r_tgt = (cu_->instruction_set != kX86) ? LoadHelper(QUICK_ENTRYPOINT_OFFSET(pIndexOf)) : 0; 1081 GenNullCheck(rl_obj.s_reg_low, reg_ptr, info->opt_flags); 1082 LIR* launch_pad = RawLIR(0, kPseudoIntrinsicRetry, WrapPointer(info)); 1083 intrinsic_launchpads_.Insert(launch_pad); 1084 OpCmpImmBranch(kCondGt, reg_char, 0xFFFF, launch_pad); 1085 // NOTE: not a safepoint 1086 if (cu_->instruction_set != kX86) { 1087 OpReg(kOpBlx, r_tgt); 1088 } else { 1089 OpThreadMem(kOpBlx, QUICK_ENTRYPOINT_OFFSET(pIndexOf)); 1090 } 1091 LIR* resume_tgt = NewLIR0(kPseudoTargetLabel); 1092 launch_pad->operands[2] = WrapPointer(resume_tgt); 1093 // Record that we've already inlined & null checked 1094 info->opt_flags |= (MIR_INLINED | MIR_IGNORE_NULL_CHECK); 1095 RegLocation rl_return = GetReturn(false); 1096 RegLocation rl_dest = InlineTarget(info); 1097 StoreValue(rl_dest, rl_return); 1098 return true; 1099} 1100 1101/* Fast string.compareTo(Ljava/lang/string;)I. */ 1102bool Mir2Lir::GenInlinedStringCompareTo(CallInfo* info) { 1103 if (cu_->instruction_set == kMips) { 1104 // TODO - add Mips implementation 1105 return false; 1106 } 1107 ClobberCalleeSave(); 1108 LockCallTemps(); // Using fixed registers 1109 int reg_this = TargetReg(kArg0); 1110 int reg_cmp = TargetReg(kArg1); 1111 1112 RegLocation rl_this = info->args[0]; 1113 RegLocation rl_cmp = info->args[1]; 1114 LoadValueDirectFixed(rl_this, reg_this); 1115 LoadValueDirectFixed(rl_cmp, reg_cmp); 1116 int r_tgt = (cu_->instruction_set != kX86) ? 1117 LoadHelper(QUICK_ENTRYPOINT_OFFSET(pStringCompareTo)) : 0; 1118 GenNullCheck(rl_this.s_reg_low, reg_this, info->opt_flags); 1119 // TUNING: check if rl_cmp.s_reg_low is already null checked 1120 LIR* launch_pad = RawLIR(0, kPseudoIntrinsicRetry, WrapPointer(info)); 1121 intrinsic_launchpads_.Insert(launch_pad); 1122 OpCmpImmBranch(kCondEq, reg_cmp, 0, launch_pad); 1123 // NOTE: not a safepoint 1124 if (cu_->instruction_set != kX86) { 1125 OpReg(kOpBlx, r_tgt); 1126 } else { 1127 OpThreadMem(kOpBlx, QUICK_ENTRYPOINT_OFFSET(pStringCompareTo)); 1128 } 1129 launch_pad->operands[2] = 0; // No return possible 1130 // Record that we've already inlined & null checked 1131 info->opt_flags |= (MIR_INLINED | MIR_IGNORE_NULL_CHECK); 1132 RegLocation rl_return = GetReturn(false); 1133 RegLocation rl_dest = InlineTarget(info); 1134 StoreValue(rl_dest, rl_return); 1135 return true; 1136} 1137 1138bool Mir2Lir::GenInlinedCurrentThread(CallInfo* info) { 1139 RegLocation rl_dest = InlineTarget(info); 1140 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); 1141 ThreadOffset offset = Thread::PeerOffset(); 1142 if (cu_->instruction_set == kThumb2 || cu_->instruction_set == kMips) { 1143 LoadWordDisp(TargetReg(kSelf), offset.Int32Value(), rl_result.low_reg); 1144 } else { 1145 CHECK(cu_->instruction_set == kX86); 1146 reinterpret_cast<X86Mir2Lir*>(this)->OpRegThreadMem(kOpMov, rl_result.low_reg, offset); 1147 } 1148 StoreValue(rl_dest, rl_result); 1149 return true; 1150} 1151 1152bool Mir2Lir::GenInlinedUnsafeGet(CallInfo* info, 1153 bool is_long, bool is_volatile) { 1154 if (cu_->instruction_set == kMips) { 1155 // TODO - add Mips implementation 1156 return false; 1157 } 1158 // Unused - RegLocation rl_src_unsafe = info->args[0]; 1159 RegLocation rl_src_obj = info->args[1]; // Object 1160 RegLocation rl_src_offset = info->args[2]; // long low 1161 rl_src_offset.wide = 0; // ignore high half in info->args[3] 1162 RegLocation rl_dest = InlineTarget(info); // result reg 1163 if (is_volatile) { 1164 GenMemBarrier(kLoadLoad); 1165 } 1166 RegLocation rl_object = LoadValue(rl_src_obj, kCoreReg); 1167 RegLocation rl_offset = LoadValue(rl_src_offset, kCoreReg); 1168 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); 1169 if (is_long) { 1170 OpRegReg(kOpAdd, rl_object.low_reg, rl_offset.low_reg); 1171 LoadBaseDispWide(rl_object.low_reg, 0, rl_result.low_reg, rl_result.high_reg, INVALID_SREG); 1172 StoreValueWide(rl_dest, rl_result); 1173 } else { 1174 LoadBaseIndexed(rl_object.low_reg, rl_offset.low_reg, rl_result.low_reg, 0, kWord); 1175 StoreValue(rl_dest, rl_result); 1176 } 1177 return true; 1178} 1179 1180bool Mir2Lir::GenInlinedUnsafePut(CallInfo* info, bool is_long, 1181 bool is_object, bool is_volatile, bool is_ordered) { 1182 if (cu_->instruction_set == kMips) { 1183 // TODO - add Mips implementation 1184 return false; 1185 } 1186 if (cu_->instruction_set == kX86 && is_object) { 1187 // TODO: fix X86, it exhausts registers for card marking. 1188 return false; 1189 } 1190 // Unused - RegLocation rl_src_unsafe = info->args[0]; 1191 RegLocation rl_src_obj = info->args[1]; // Object 1192 RegLocation rl_src_offset = info->args[2]; // long low 1193 rl_src_offset.wide = 0; // ignore high half in info->args[3] 1194 RegLocation rl_src_value = info->args[4]; // value to store 1195 if (is_volatile || is_ordered) { 1196 GenMemBarrier(kStoreStore); 1197 } 1198 RegLocation rl_object = LoadValue(rl_src_obj, kCoreReg); 1199 RegLocation rl_offset = LoadValue(rl_src_offset, kCoreReg); 1200 RegLocation rl_value; 1201 if (is_long) { 1202 rl_value = LoadValueWide(rl_src_value, kCoreReg); 1203 OpRegReg(kOpAdd, rl_object.low_reg, rl_offset.low_reg); 1204 StoreBaseDispWide(rl_object.low_reg, 0, rl_value.low_reg, rl_value.high_reg); 1205 } else { 1206 rl_value = LoadValue(rl_src_value, kCoreReg); 1207 StoreBaseIndexed(rl_object.low_reg, rl_offset.low_reg, rl_value.low_reg, 0, kWord); 1208 } 1209 if (is_volatile) { 1210 GenMemBarrier(kStoreLoad); 1211 } 1212 if (is_object) { 1213 MarkGCCard(rl_value.low_reg, rl_object.low_reg); 1214 } 1215 return true; 1216} 1217 1218bool Mir2Lir::GenIntrinsic(CallInfo* info) { 1219 if (info->opt_flags & MIR_INLINED) { 1220 return false; 1221 } 1222 /* 1223 * TODO: move these to a target-specific structured constant array 1224 * and use a generic match function. The list of intrinsics may be 1225 * slightly different depending on target. 1226 * TODO: Fold this into a matching function that runs during 1227 * basic block building. This should be part of the action for 1228 * small method inlining and recognition of the special object init 1229 * method. By doing this during basic block construction, we can also 1230 * take advantage of/generate new useful dataflow info. 1231 */ 1232 const DexFile::MethodId& target_mid = cu_->dex_file->GetMethodId(info->index); 1233 const DexFile::TypeId& declaring_type = cu_->dex_file->GetTypeId(target_mid.class_idx_); 1234 StringPiece tgt_methods_declaring_class( 1235 cu_->dex_file->StringDataByIdx(declaring_type.descriptor_idx_)); 1236 if (tgt_methods_declaring_class.starts_with("Ljava/lang/Double;")) { 1237 std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file)); 1238 if (tgt_method == "long java.lang.Double.doubleToRawLongBits(double)") { 1239 return GenInlinedDoubleCvt(info); 1240 } 1241 if (tgt_method == "double java.lang.Double.longBitsToDouble(long)") { 1242 return GenInlinedDoubleCvt(info); 1243 } 1244 } else if (tgt_methods_declaring_class.starts_with("Ljava/lang/Float;")) { 1245 std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file)); 1246 if (tgt_method == "int java.lang.Float.floatToRawIntBits(float)") { 1247 return GenInlinedFloatCvt(info); 1248 } 1249 if (tgt_method == "float java.lang.Float.intBitsToFloat(int)") { 1250 return GenInlinedFloatCvt(info); 1251 } 1252 } else if (tgt_methods_declaring_class.starts_with("Ljava/lang/Math;") || 1253 tgt_methods_declaring_class.starts_with("Ljava/lang/StrictMath;")) { 1254 std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file)); 1255 if (tgt_method == "int java.lang.Math.abs(int)" || 1256 tgt_method == "int java.lang.StrictMath.abs(int)") { 1257 return GenInlinedAbsInt(info); 1258 } 1259 if (tgt_method == "long java.lang.Math.abs(long)" || 1260 tgt_method == "long java.lang.StrictMath.abs(long)") { 1261 return GenInlinedAbsLong(info); 1262 } 1263 if (tgt_method == "int java.lang.Math.max(int, int)" || 1264 tgt_method == "int java.lang.StrictMath.max(int, int)") { 1265 return GenInlinedMinMaxInt(info, false /* is_min */); 1266 } 1267 if (tgt_method == "int java.lang.Math.min(int, int)" || 1268 tgt_method == "int java.lang.StrictMath.min(int, int)") { 1269 return GenInlinedMinMaxInt(info, true /* is_min */); 1270 } 1271 if (tgt_method == "double java.lang.Math.sqrt(double)" || 1272 tgt_method == "double java.lang.StrictMath.sqrt(double)") { 1273 return GenInlinedSqrt(info); 1274 } 1275 } else if (tgt_methods_declaring_class.starts_with("Ljava/lang/String;")) { 1276 std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file)); 1277 if (tgt_method == "char java.lang.String.charAt(int)") { 1278 return GenInlinedCharAt(info); 1279 } 1280 if (tgt_method == "int java.lang.String.compareTo(java.lang.String)") { 1281 return GenInlinedStringCompareTo(info); 1282 } 1283 if (tgt_method == "boolean java.lang.String.is_empty()") { 1284 return GenInlinedStringIsEmptyOrLength(info, true /* is_empty */); 1285 } 1286 if (tgt_method == "int java.lang.String.index_of(int, int)") { 1287 return GenInlinedIndexOf(info, false /* base 0 */); 1288 } 1289 if (tgt_method == "int java.lang.String.index_of(int)") { 1290 return GenInlinedIndexOf(info, true /* base 0 */); 1291 } 1292 if (tgt_method == "int java.lang.String.length()") { 1293 return GenInlinedStringIsEmptyOrLength(info, false /* is_empty */); 1294 } 1295 } else if (tgt_methods_declaring_class.starts_with("Ljava/lang/Thread;")) { 1296 std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file)); 1297 if (tgt_method == "java.lang.Thread java.lang.Thread.currentThread()") { 1298 return GenInlinedCurrentThread(info); 1299 } 1300 } else if (tgt_methods_declaring_class.starts_with("Lsun/misc/Unsafe;")) { 1301 std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file)); 1302 if (tgt_method == "boolean sun.misc.Unsafe.compareAndSwapInt(java.lang.Object, long, int, int)") { 1303 return GenInlinedCas32(info, false); 1304 } 1305 if (tgt_method == "boolean sun.misc.Unsafe.compareAndSwapObject(java.lang.Object, long, java.lang.Object, java.lang.Object)") { 1306 return GenInlinedCas32(info, true); 1307 } 1308 if (tgt_method == "int sun.misc.Unsafe.getInt(java.lang.Object, long)") { 1309 return GenInlinedUnsafeGet(info, false /* is_long */, false /* is_volatile */); 1310 } 1311 if (tgt_method == "int sun.misc.Unsafe.getIntVolatile(java.lang.Object, long)") { 1312 return GenInlinedUnsafeGet(info, false /* is_long */, true /* is_volatile */); 1313 } 1314 if (tgt_method == "void sun.misc.Unsafe.putInt(java.lang.Object, long, int)") { 1315 return GenInlinedUnsafePut(info, false /* is_long */, false /* is_object */, 1316 false /* is_volatile */, false /* is_ordered */); 1317 } 1318 if (tgt_method == "void sun.misc.Unsafe.putIntVolatile(java.lang.Object, long, int)") { 1319 return GenInlinedUnsafePut(info, false /* is_long */, false /* is_object */, 1320 true /* is_volatile */, false /* is_ordered */); 1321 } 1322 if (tgt_method == "void sun.misc.Unsafe.putOrderedInt(java.lang.Object, long, int)") { 1323 return GenInlinedUnsafePut(info, false /* is_long */, false /* is_object */, 1324 false /* is_volatile */, true /* is_ordered */); 1325 } 1326 if (tgt_method == "long sun.misc.Unsafe.getLong(java.lang.Object, long)") { 1327 return GenInlinedUnsafeGet(info, true /* is_long */, false /* is_volatile */); 1328 } 1329 if (tgt_method == "long sun.misc.Unsafe.getLongVolatile(java.lang.Object, long)") { 1330 return GenInlinedUnsafeGet(info, true /* is_long */, true /* is_volatile */); 1331 } 1332 if (tgt_method == "void sun.misc.Unsafe.putLong(java.lang.Object, long, long)") { 1333 return GenInlinedUnsafePut(info, true /* is_long */, false /* is_object */, 1334 false /* is_volatile */, false /* is_ordered */); 1335 } 1336 if (tgt_method == "void sun.misc.Unsafe.putLongVolatile(java.lang.Object, long, long)") { 1337 return GenInlinedUnsafePut(info, true /* is_long */, false /* is_object */, 1338 true /* is_volatile */, false /* is_ordered */); 1339 } 1340 if (tgt_method == "void sun.misc.Unsafe.putOrderedLong(java.lang.Object, long, long)") { 1341 return GenInlinedUnsafePut(info, true /* is_long */, false /* is_object */, 1342 false /* is_volatile */, true /* is_ordered */); 1343 } 1344 if (tgt_method == "java.lang.Object sun.misc.Unsafe.getObject(java.lang.Object, long)") { 1345 return GenInlinedUnsafeGet(info, false /* is_long */, false /* is_volatile */); 1346 } 1347 if (tgt_method == "java.lang.Object sun.misc.Unsafe.getObjectVolatile(java.lang.Object, long)") { 1348 return GenInlinedUnsafeGet(info, false /* is_long */, true /* is_volatile */); 1349 } 1350 if (tgt_method == "void sun.misc.Unsafe.putObject(java.lang.Object, long, java.lang.Object)") { 1351 return GenInlinedUnsafePut(info, false /* is_long */, true /* is_object */, 1352 false /* is_volatile */, false /* is_ordered */); 1353 } 1354 if (tgt_method == "void sun.misc.Unsafe.putObjectVolatile(java.lang.Object, long, java.lang.Object)") { 1355 return GenInlinedUnsafePut(info, false /* is_long */, true /* is_object */, 1356 true /* is_volatile */, false /* is_ordered */); 1357 } 1358 if (tgt_method == "void sun.misc.Unsafe.putOrderedObject(java.lang.Object, long, java.lang.Object)") { 1359 return GenInlinedUnsafePut(info, false /* is_long */, true /* is_object */, 1360 false /* is_volatile */, true /* is_ordered */); 1361 } 1362 } 1363 return false; 1364} 1365 1366void Mir2Lir::GenInvoke(CallInfo* info) { 1367 if (GenIntrinsic(info)) { 1368 return; 1369 } 1370 InvokeType original_type = info->type; // avoiding mutation by ComputeInvokeInfo 1371 int call_state = 0; 1372 LIR* null_ck; 1373 LIR** p_null_ck = NULL; 1374 NextCallInsn next_call_insn; 1375 FlushAllRegs(); /* Everything to home location */ 1376 // Explicit register usage 1377 LockCallTemps(); 1378 1379 DexCompilationUnit* cUnit = mir_graph_->GetCurrentDexCompilationUnit(); 1380 MethodReference target_method(cUnit->GetDexFile(), info->index); 1381 int vtable_idx; 1382 uintptr_t direct_code; 1383 uintptr_t direct_method; 1384 bool skip_this; 1385 bool fast_path = 1386 cu_->compiler_driver->ComputeInvokeInfo(mir_graph_->GetCurrentDexCompilationUnit(), 1387 current_dalvik_offset_, 1388 true, true, 1389 &info->type, &target_method, 1390 &vtable_idx, 1391 &direct_code, &direct_method) && !SLOW_INVOKE_PATH; 1392 if (info->type == kInterface) { 1393 if (fast_path) { 1394 p_null_ck = &null_ck; 1395 } 1396 next_call_insn = fast_path ? NextInterfaceCallInsn : NextInterfaceCallInsnWithAccessCheck; 1397 skip_this = false; 1398 } else if (info->type == kDirect) { 1399 if (fast_path) { 1400 p_null_ck = &null_ck; 1401 } 1402 next_call_insn = fast_path ? NextSDCallInsn : NextDirectCallInsnSP; 1403 skip_this = false; 1404 } else if (info->type == kStatic) { 1405 next_call_insn = fast_path ? NextSDCallInsn : NextStaticCallInsnSP; 1406 skip_this = false; 1407 } else if (info->type == kSuper) { 1408 DCHECK(!fast_path); // Fast path is a direct call. 1409 next_call_insn = NextSuperCallInsnSP; 1410 skip_this = false; 1411 } else { 1412 DCHECK_EQ(info->type, kVirtual); 1413 next_call_insn = fast_path ? NextVCallInsn : NextVCallInsnSP; 1414 skip_this = fast_path; 1415 } 1416 if (!info->is_range) { 1417 call_state = GenDalvikArgsNoRange(info, call_state, p_null_ck, 1418 next_call_insn, target_method, 1419 vtable_idx, direct_code, direct_method, 1420 original_type, skip_this); 1421 } else { 1422 call_state = GenDalvikArgsRange(info, call_state, p_null_ck, 1423 next_call_insn, target_method, vtable_idx, 1424 direct_code, direct_method, original_type, 1425 skip_this); 1426 } 1427 // Finish up any of the call sequence not interleaved in arg loading 1428 while (call_state >= 0) { 1429 call_state = next_call_insn(cu_, info, call_state, target_method, 1430 vtable_idx, direct_code, direct_method, 1431 original_type); 1432 } 1433 LIR* call_inst; 1434 if (cu_->instruction_set != kX86) { 1435 call_inst = OpReg(kOpBlx, TargetReg(kInvokeTgt)); 1436 } else { 1437 if (fast_path && info->type != kInterface) { 1438 call_inst = OpMem(kOpBlx, TargetReg(kArg0), 1439 mirror::ArtMethod::GetEntryPointFromCompiledCodeOffset().Int32Value()); 1440 } else { 1441 ThreadOffset trampoline(-1); 1442 switch (info->type) { 1443 case kInterface: 1444 trampoline = fast_path ? QUICK_ENTRYPOINT_OFFSET(pInvokeInterfaceTrampoline) 1445 : QUICK_ENTRYPOINT_OFFSET(pInvokeInterfaceTrampolineWithAccessCheck); 1446 break; 1447 case kDirect: 1448 trampoline = QUICK_ENTRYPOINT_OFFSET(pInvokeDirectTrampolineWithAccessCheck); 1449 break; 1450 case kStatic: 1451 trampoline = QUICK_ENTRYPOINT_OFFSET(pInvokeStaticTrampolineWithAccessCheck); 1452 break; 1453 case kSuper: 1454 trampoline = QUICK_ENTRYPOINT_OFFSET(pInvokeSuperTrampolineWithAccessCheck); 1455 break; 1456 case kVirtual: 1457 trampoline = QUICK_ENTRYPOINT_OFFSET(pInvokeVirtualTrampolineWithAccessCheck); 1458 break; 1459 default: 1460 LOG(FATAL) << "Unexpected invoke type"; 1461 } 1462 call_inst = OpThreadMem(kOpBlx, trampoline); 1463 } 1464 } 1465 MarkSafepointPC(call_inst); 1466 1467 ClobberCalleeSave(); 1468 if (info->result.location != kLocInvalid) { 1469 // We have a following MOVE_RESULT - do it now. 1470 if (info->result.wide) { 1471 RegLocation ret_loc = GetReturnWide(info->result.fp); 1472 StoreValueWide(info->result, ret_loc); 1473 } else { 1474 RegLocation ret_loc = GetReturn(info->result.fp); 1475 StoreValue(info->result, ret_loc); 1476 } 1477 } 1478} 1479 1480} // namespace art 1481