gen_invoke.cc revision 9d46314a309aff327f9913789b5f61200c162609
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/frontend.h" 19#include "dex/quick/dex_file_method_inliner.h" 20#include "dex/quick/dex_file_to_method_inliner_map.h" 21#include "dex_file-inl.h" 22#include "entrypoints/quick/quick_entrypoints.h" 23#include "invoke_type.h" 24#include "mirror/array.h" 25#include "mirror/string.h" 26#include "mir_to_lir-inl.h" 27#include "x86/codegen_x86.h" 28 29namespace art { 30 31/* 32 * This source files contains "gen" codegen routines that should 33 * be applicable to most targets. Only mid-level support utilities 34 * and "op" calls may be used here. 35 */ 36 37void Mir2Lir::AddIntrinsicLaunchpad(CallInfo* info, LIR* branch, LIR* resume) { 38 class IntrinsicLaunchpadPath : public Mir2Lir::LIRSlowPath { 39 public: 40 IntrinsicLaunchpadPath(Mir2Lir* m2l, CallInfo* info, LIR* branch, LIR* resume = nullptr) 41 : LIRSlowPath(m2l, info->offset, branch, resume), info_(info) { 42 } 43 44 void Compile() { 45 m2l_->ResetRegPool(); 46 m2l_->ResetDefTracking(); 47 LIR* label = GenerateTargetLabel(); 48 label->opcode = kPseudoIntrinsicRetry; 49 // NOTE: GenInvokeNoInline() handles MarkSafepointPC. 50 m2l_->GenInvokeNoInline(info_); 51 if (cont_ != nullptr) { 52 m2l_->OpUnconditionalBranch(cont_); 53 } 54 } 55 56 private: 57 CallInfo* const info_; 58 }; 59 60 AddSlowPath(new (arena_) IntrinsicLaunchpadPath(this, info, branch, resume)); 61} 62 63/* 64 * To save scheduling time, helper calls are broken into two parts: generation of 65 * the helper target address, and the actual call to the helper. Because x86 66 * has a memory call operation, part 1 is a NOP for x86. For other targets, 67 * load arguments between the two parts. 68 */ 69RegStorage Mir2Lir::CallHelperSetup(ThreadOffset<4> helper_offset) { 70 return (cu_->instruction_set == kX86 || cu_->instruction_set == kX86_64) ? RegStorage::InvalidReg() : LoadHelper(helper_offset); 71} 72 73/* NOTE: if r_tgt is a temp, it will be freed following use */ 74LIR* Mir2Lir::CallHelper(RegStorage r_tgt, ThreadOffset<4> helper_offset, bool safepoint_pc, 75 bool use_link) { 76 LIR* call_inst; 77 OpKind op = use_link ? kOpBlx : kOpBx; 78 if (cu_->instruction_set == kX86 || cu_->instruction_set == kX86_64) { 79 call_inst = OpThreadMem(op, helper_offset); 80 } else { 81 call_inst = OpReg(op, r_tgt); 82 FreeTemp(r_tgt); 83 } 84 if (safepoint_pc) { 85 MarkSafepointPC(call_inst); 86 } 87 return call_inst; 88} 89 90void Mir2Lir::CallRuntimeHelper(ThreadOffset<4> helper_offset, bool safepoint_pc) { 91 RegStorage r_tgt = CallHelperSetup(helper_offset); 92 ClobberCallerSave(); 93 CallHelper(r_tgt, helper_offset, safepoint_pc); 94} 95 96void Mir2Lir::CallRuntimeHelperImm(ThreadOffset<4> helper_offset, int arg0, bool safepoint_pc) { 97 RegStorage r_tgt = CallHelperSetup(helper_offset); 98 LoadConstant(TargetReg(kArg0), arg0); 99 ClobberCallerSave(); 100 CallHelper(r_tgt, helper_offset, safepoint_pc); 101} 102 103void Mir2Lir::CallRuntimeHelperReg(ThreadOffset<4> helper_offset, RegStorage arg0, 104 bool safepoint_pc) { 105 RegStorage r_tgt = CallHelperSetup(helper_offset); 106 OpRegCopy(TargetReg(kArg0), arg0); 107 ClobberCallerSave(); 108 CallHelper(r_tgt, helper_offset, safepoint_pc); 109} 110 111void Mir2Lir::CallRuntimeHelperRegLocation(ThreadOffset<4> helper_offset, RegLocation arg0, 112 bool safepoint_pc) { 113 RegStorage r_tgt = CallHelperSetup(helper_offset); 114 if (arg0.wide == 0) { 115 LoadValueDirectFixed(arg0, TargetReg(kArg0)); 116 } else { 117 RegStorage r_tmp = RegStorage::MakeRegPair(TargetReg(kArg0), TargetReg(kArg1)); 118 LoadValueDirectWideFixed(arg0, r_tmp); 119 } 120 ClobberCallerSave(); 121 CallHelper(r_tgt, helper_offset, safepoint_pc); 122} 123 124void Mir2Lir::CallRuntimeHelperImmImm(ThreadOffset<4> helper_offset, int arg0, int arg1, 125 bool safepoint_pc) { 126 RegStorage r_tgt = CallHelperSetup(helper_offset); 127 LoadConstant(TargetReg(kArg0), arg0); 128 LoadConstant(TargetReg(kArg1), arg1); 129 ClobberCallerSave(); 130 CallHelper(r_tgt, helper_offset, safepoint_pc); 131} 132 133void Mir2Lir::CallRuntimeHelperImmRegLocation(ThreadOffset<4> helper_offset, int arg0, 134 RegLocation arg1, bool safepoint_pc) { 135 RegStorage r_tgt = CallHelperSetup(helper_offset); 136 if (arg1.wide == 0) { 137 LoadValueDirectFixed(arg1, TargetReg(kArg1)); 138 } else { 139 RegStorage r_tmp = RegStorage::MakeRegPair(TargetReg(kArg1), TargetReg(kArg2)); 140 LoadValueDirectWideFixed(arg1, r_tmp); 141 } 142 LoadConstant(TargetReg(kArg0), arg0); 143 ClobberCallerSave(); 144 CallHelper(r_tgt, helper_offset, safepoint_pc); 145} 146 147void Mir2Lir::CallRuntimeHelperRegLocationImm(ThreadOffset<4> helper_offset, RegLocation arg0, 148 int arg1, bool safepoint_pc) { 149 RegStorage r_tgt = CallHelperSetup(helper_offset); 150 LoadValueDirectFixed(arg0, TargetReg(kArg0)); 151 LoadConstant(TargetReg(kArg1), arg1); 152 ClobberCallerSave(); 153 CallHelper(r_tgt, helper_offset, safepoint_pc); 154} 155 156void Mir2Lir::CallRuntimeHelperImmReg(ThreadOffset<4> helper_offset, int arg0, RegStorage arg1, 157 bool safepoint_pc) { 158 RegStorage r_tgt = CallHelperSetup(helper_offset); 159 OpRegCopy(TargetReg(kArg1), arg1); 160 LoadConstant(TargetReg(kArg0), arg0); 161 ClobberCallerSave(); 162 CallHelper(r_tgt, helper_offset, safepoint_pc); 163} 164 165void Mir2Lir::CallRuntimeHelperRegImm(ThreadOffset<4> helper_offset, RegStorage arg0, int arg1, 166 bool safepoint_pc) { 167 RegStorage r_tgt = CallHelperSetup(helper_offset); 168 OpRegCopy(TargetReg(kArg0), arg0); 169 LoadConstant(TargetReg(kArg1), arg1); 170 ClobberCallerSave(); 171 CallHelper(r_tgt, helper_offset, safepoint_pc); 172} 173 174void Mir2Lir::CallRuntimeHelperImmMethod(ThreadOffset<4> helper_offset, int arg0, 175 bool safepoint_pc) { 176 RegStorage r_tgt = CallHelperSetup(helper_offset); 177 LoadCurrMethodDirect(TargetReg(kArg1)); 178 LoadConstant(TargetReg(kArg0), arg0); 179 ClobberCallerSave(); 180 CallHelper(r_tgt, helper_offset, safepoint_pc); 181} 182 183void Mir2Lir::CallRuntimeHelperRegMethod(ThreadOffset<4> helper_offset, RegStorage arg0, 184 bool safepoint_pc) { 185 RegStorage r_tgt = CallHelperSetup(helper_offset); 186 DCHECK_NE(TargetReg(kArg1).GetReg(), arg0.GetReg()); 187 if (TargetReg(kArg0) != arg0) { 188 OpRegCopy(TargetReg(kArg0), arg0); 189 } 190 LoadCurrMethodDirect(TargetReg(kArg1)); 191 ClobberCallerSave(); 192 CallHelper(r_tgt, helper_offset, safepoint_pc); 193} 194 195void Mir2Lir::CallRuntimeHelperRegMethodRegLocation(ThreadOffset<4> helper_offset, RegStorage arg0, 196 RegLocation arg2, bool safepoint_pc) { 197 RegStorage r_tgt = CallHelperSetup(helper_offset); 198 DCHECK_NE(TargetReg(kArg1).GetReg(), arg0.GetReg()); 199 if (TargetReg(kArg0) != arg0) { 200 OpRegCopy(TargetReg(kArg0), arg0); 201 } 202 LoadCurrMethodDirect(TargetReg(kArg1)); 203 LoadValueDirectFixed(arg2, TargetReg(kArg2)); 204 ClobberCallerSave(); 205 CallHelper(r_tgt, helper_offset, safepoint_pc); 206} 207 208void Mir2Lir::CallRuntimeHelperRegLocationRegLocation(ThreadOffset<4> helper_offset, 209 RegLocation arg0, RegLocation arg1, 210 bool safepoint_pc) { 211 RegStorage r_tgt = CallHelperSetup(helper_offset); 212 if (arg0.wide == 0) { 213 LoadValueDirectFixed(arg0, arg0.fp ? TargetReg(kFArg0) : TargetReg(kArg0)); 214 if (arg1.wide == 0) { 215 if (cu_->instruction_set == kMips) { 216 LoadValueDirectFixed(arg1, arg1.fp ? TargetReg(kFArg2) : TargetReg(kArg1)); 217 } else { 218 LoadValueDirectFixed(arg1, TargetReg(kArg1)); 219 } 220 } else { 221 if (cu_->instruction_set == kMips) { 222 RegStorage r_tmp; 223 if (arg1.fp) { 224 r_tmp = RegStorage::MakeRegPair(TargetReg(kFArg2), TargetReg(kFArg3)); 225 } else { 226 r_tmp = RegStorage::MakeRegPair(TargetReg(kArg1), TargetReg(kArg2)); 227 } 228 LoadValueDirectWideFixed(arg1, r_tmp); 229 } else { 230 RegStorage r_tmp = RegStorage::MakeRegPair(TargetReg(kArg1), TargetReg(kArg2)); 231 LoadValueDirectWideFixed(arg1, r_tmp); 232 } 233 } 234 } else { 235 RegStorage r_tmp; 236 if (arg0.fp) { 237 r_tmp = RegStorage::MakeRegPair(TargetReg(kFArg0), TargetReg(kFArg1)); 238 } else { 239 r_tmp = RegStorage::MakeRegPair(TargetReg(kArg0), TargetReg(kArg1)); 240 } 241 LoadValueDirectWideFixed(arg0, r_tmp); 242 if (arg1.wide == 0) { 243 LoadValueDirectFixed(arg1, arg1.fp ? TargetReg(kFArg2) : TargetReg(kArg2)); 244 } else { 245 RegStorage r_tmp; 246 if (arg1.fp) { 247 r_tmp = RegStorage::MakeRegPair(TargetReg(kFArg2), TargetReg(kFArg3)); 248 } else { 249 r_tmp = RegStorage::MakeRegPair(TargetReg(kArg2), TargetReg(kArg3)); 250 } 251 LoadValueDirectWideFixed(arg1, r_tmp); 252 } 253 } 254 ClobberCallerSave(); 255 CallHelper(r_tgt, helper_offset, safepoint_pc); 256} 257 258void Mir2Lir::CopyToArgumentRegs(RegStorage arg0, RegStorage arg1) { 259 if (arg1.GetReg() == TargetReg(kArg0).GetReg()) { 260 if (arg0.GetReg() == TargetReg(kArg1).GetReg()) { 261 // Swap kArg0 and kArg1 with kArg2 as temp. 262 OpRegCopy(TargetReg(kArg2), arg1); 263 OpRegCopy(TargetReg(kArg0), arg0); 264 OpRegCopy(TargetReg(kArg1), TargetReg(kArg2)); 265 } else { 266 OpRegCopy(TargetReg(kArg1), arg1); 267 OpRegCopy(TargetReg(kArg0), arg0); 268 } 269 } else { 270 OpRegCopy(TargetReg(kArg0), arg0); 271 OpRegCopy(TargetReg(kArg1), arg1); 272 } 273} 274 275void Mir2Lir::CallRuntimeHelperRegReg(ThreadOffset<4> helper_offset, RegStorage arg0, 276 RegStorage arg1, bool safepoint_pc) { 277 RegStorage r_tgt = CallHelperSetup(helper_offset); 278 CopyToArgumentRegs(arg0, arg1); 279 ClobberCallerSave(); 280 CallHelper(r_tgt, helper_offset, safepoint_pc); 281} 282 283void Mir2Lir::CallRuntimeHelperRegRegImm(ThreadOffset<4> helper_offset, RegStorage arg0, 284 RegStorage arg1, int arg2, bool safepoint_pc) { 285 RegStorage r_tgt = CallHelperSetup(helper_offset); 286 CopyToArgumentRegs(arg0, arg1); 287 LoadConstant(TargetReg(kArg2), arg2); 288 ClobberCallerSave(); 289 CallHelper(r_tgt, helper_offset, safepoint_pc); 290} 291 292void Mir2Lir::CallRuntimeHelperImmMethodRegLocation(ThreadOffset<4> helper_offset, 293 int arg0, RegLocation arg2, bool safepoint_pc) { 294 RegStorage r_tgt = CallHelperSetup(helper_offset); 295 LoadValueDirectFixed(arg2, TargetReg(kArg2)); 296 LoadCurrMethodDirect(TargetReg(kArg1)); 297 LoadConstant(TargetReg(kArg0), arg0); 298 ClobberCallerSave(); 299 CallHelper(r_tgt, helper_offset, safepoint_pc); 300} 301 302void Mir2Lir::CallRuntimeHelperImmMethodImm(ThreadOffset<4> helper_offset, int arg0, 303 int arg2, bool safepoint_pc) { 304 RegStorage r_tgt = CallHelperSetup(helper_offset); 305 LoadCurrMethodDirect(TargetReg(kArg1)); 306 LoadConstant(TargetReg(kArg2), arg2); 307 LoadConstant(TargetReg(kArg0), arg0); 308 ClobberCallerSave(); 309 CallHelper(r_tgt, helper_offset, safepoint_pc); 310} 311 312void Mir2Lir::CallRuntimeHelperImmRegLocationRegLocation(ThreadOffset<4> helper_offset, 313 int arg0, RegLocation arg1, 314 RegLocation arg2, bool safepoint_pc) { 315 RegStorage r_tgt = CallHelperSetup(helper_offset); 316 DCHECK_EQ(arg1.wide, 0U); 317 LoadValueDirectFixed(arg1, TargetReg(kArg1)); 318 if (arg2.wide == 0) { 319 LoadValueDirectFixed(arg2, TargetReg(kArg2)); 320 } else { 321 RegStorage r_tmp = RegStorage::MakeRegPair(TargetReg(kArg2), TargetReg(kArg3)); 322 LoadValueDirectWideFixed(arg2, r_tmp); 323 } 324 LoadConstant(TargetReg(kArg0), arg0); 325 ClobberCallerSave(); 326 CallHelper(r_tgt, helper_offset, safepoint_pc); 327} 328 329void Mir2Lir::CallRuntimeHelperRegLocationRegLocationRegLocation(ThreadOffset<4> helper_offset, 330 RegLocation arg0, RegLocation arg1, 331 RegLocation arg2, 332 bool safepoint_pc) { 333 RegStorage r_tgt = CallHelperSetup(helper_offset); 334 DCHECK_EQ(arg0.wide, 0U); 335 LoadValueDirectFixed(arg0, TargetReg(kArg0)); 336 DCHECK_EQ(arg1.wide, 0U); 337 LoadValueDirectFixed(arg1, TargetReg(kArg1)); 338 DCHECK_EQ(arg1.wide, 0U); 339 LoadValueDirectFixed(arg2, TargetReg(kArg2)); 340 ClobberCallerSave(); 341 CallHelper(r_tgt, helper_offset, safepoint_pc); 342} 343 344/* 345 * If there are any ins passed in registers that have not been promoted 346 * to a callee-save register, flush them to the frame. Perform intial 347 * assignment of promoted arguments. 348 * 349 * ArgLocs is an array of location records describing the incoming arguments 350 * with one location record per word of argument. 351 */ 352void Mir2Lir::FlushIns(RegLocation* ArgLocs, RegLocation rl_method) { 353 /* 354 * Dummy up a RegLocation for the incoming Method* 355 * It will attempt to keep kArg0 live (or copy it to home location 356 * if promoted). 357 */ 358 RegLocation rl_src = rl_method; 359 rl_src.location = kLocPhysReg; 360 rl_src.reg = TargetReg(kArg0); 361 rl_src.home = false; 362 MarkLive(rl_src.reg, rl_src.s_reg_low); 363 StoreValue(rl_method, rl_src); 364 // If Method* has been promoted, explicitly flush 365 if (rl_method.location == kLocPhysReg) { 366 StoreWordDisp(TargetReg(kSp), 0, TargetReg(kArg0)); 367 } 368 369 if (cu_->num_ins == 0) { 370 return; 371 } 372 373 int start_vreg = cu_->num_dalvik_registers - cu_->num_ins; 374 /* 375 * Copy incoming arguments to their proper home locations. 376 * NOTE: an older version of dx had an issue in which 377 * it would reuse static method argument registers. 378 * This could result in the same Dalvik virtual register 379 * being promoted to both core and fp regs. To account for this, 380 * we only copy to the corresponding promoted physical register 381 * if it matches the type of the SSA name for the incoming 382 * argument. It is also possible that long and double arguments 383 * end up half-promoted. In those cases, we must flush the promoted 384 * half to memory as well. 385 */ 386 for (int i = 0; i < cu_->num_ins; i++) { 387 PromotionMap* v_map = &promotion_map_[start_vreg + i]; 388 RegStorage reg = GetArgMappingToPhysicalReg(i); 389 390 if (reg.Valid()) { 391 // If arriving in register 392 bool need_flush = true; 393 RegLocation* t_loc = &ArgLocs[i]; 394 if ((v_map->core_location == kLocPhysReg) && !t_loc->fp) { 395 OpRegCopy(RegStorage::Solo32(v_map->core_reg), reg); 396 need_flush = false; 397 } else if ((v_map->fp_location == kLocPhysReg) && t_loc->fp) { 398 OpRegCopy(RegStorage::Solo32(v_map->FpReg), reg); 399 need_flush = false; 400 } else { 401 need_flush = true; 402 } 403 404 // For wide args, force flush if not fully promoted 405 if (t_loc->wide) { 406 PromotionMap* p_map = v_map + (t_loc->high_word ? -1 : +1); 407 // Is only half promoted? 408 need_flush |= (p_map->core_location != v_map->core_location) || 409 (p_map->fp_location != v_map->fp_location); 410 if ((cu_->instruction_set == kThumb2) && t_loc->fp && !need_flush) { 411 /* 412 * In Arm, a double is represented as a pair of consecutive single float 413 * registers starting at an even number. It's possible that both Dalvik vRegs 414 * representing the incoming double were independently promoted as singles - but 415 * not in a form usable as a double. If so, we need to flush - even though the 416 * incoming arg appears fully in register. At this point in the code, both 417 * halves of the double are promoted. Make sure they are in a usable form. 418 */ 419 int lowreg_index = start_vreg + i + (t_loc->high_word ? -1 : 0); 420 int low_reg = promotion_map_[lowreg_index].FpReg; 421 int high_reg = promotion_map_[lowreg_index + 1].FpReg; 422 if (((low_reg & 0x1) != 0) || (high_reg != (low_reg + 1))) { 423 need_flush = true; 424 } 425 } 426 } 427 if (need_flush) { 428 StoreBaseDisp(TargetReg(kSp), SRegOffset(start_vreg + i), reg, kWord); 429 } 430 } else { 431 // If arriving in frame & promoted 432 if (v_map->core_location == kLocPhysReg) { 433 LoadWordDisp(TargetReg(kSp), SRegOffset(start_vreg + i), 434 RegStorage::Solo32(v_map->core_reg)); 435 } 436 if (v_map->fp_location == kLocPhysReg) { 437 LoadWordDisp(TargetReg(kSp), SRegOffset(start_vreg + i), RegStorage::Solo32(v_map->FpReg)); 438 } 439 } 440 } 441} 442 443/* 444 * Bit of a hack here - in the absence of a real scheduling pass, 445 * emit the next instruction in static & direct invoke sequences. 446 */ 447static int NextSDCallInsn(CompilationUnit* cu, CallInfo* info, 448 int state, const MethodReference& target_method, 449 uint32_t unused, 450 uintptr_t direct_code, uintptr_t direct_method, 451 InvokeType type) { 452 Mir2Lir* cg = static_cast<Mir2Lir*>(cu->cg.get()); 453 if (direct_code != 0 && direct_method != 0) { 454 switch (state) { 455 case 0: // Get the current Method* [sets kArg0] 456 if (direct_code != static_cast<unsigned int>(-1)) { 457 if (cu->instruction_set != kX86 && cu->instruction_set != kX86_64) { 458 cg->LoadConstant(cg->TargetReg(kInvokeTgt), direct_code); 459 } 460 } else if (cu->instruction_set != kX86 && cu->instruction_set != kX86_64) { 461 cg->LoadCodeAddress(target_method, type, kInvokeTgt); 462 } 463 if (direct_method != static_cast<unsigned int>(-1)) { 464 cg->LoadConstant(cg->TargetReg(kArg0), direct_method); 465 } else { 466 cg->LoadMethodAddress(target_method, type, kArg0); 467 } 468 break; 469 default: 470 return -1; 471 } 472 } else { 473 switch (state) { 474 case 0: // Get the current Method* [sets kArg0] 475 // TUNING: we can save a reg copy if Method* has been promoted. 476 cg->LoadCurrMethodDirect(cg->TargetReg(kArg0)); 477 break; 478 case 1: // Get method->dex_cache_resolved_methods_ 479 cg->LoadWordDisp(cg->TargetReg(kArg0), 480 mirror::ArtMethod::DexCacheResolvedMethodsOffset().Int32Value(), 481 cg->TargetReg(kArg0)); 482 // Set up direct code if known. 483 if (direct_code != 0) { 484 if (direct_code != static_cast<unsigned int>(-1)) { 485 cg->LoadConstant(cg->TargetReg(kInvokeTgt), direct_code); 486 } else if (cu->instruction_set != kX86 && cu->instruction_set != kX86_64) { 487 CHECK_LT(target_method.dex_method_index, target_method.dex_file->NumMethodIds()); 488 cg->LoadCodeAddress(target_method, type, kInvokeTgt); 489 } 490 } 491 break; 492 case 2: // Grab target method* 493 CHECK_EQ(cu->dex_file, target_method.dex_file); 494 cg->LoadWordDisp(cg->TargetReg(kArg0), 495 mirror::Array::DataOffset(sizeof(mirror::Object*)).Int32Value() + 496 (target_method.dex_method_index * 4), cg->TargetReg(kArg0)); 497 break; 498 case 3: // Grab the code from the method* 499 if (cu->instruction_set != kX86 && cu->instruction_set != kX86_64) { 500 if (direct_code == 0) { 501 cg->LoadWordDisp(cg->TargetReg(kArg0), 502 mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value(), 503 cg->TargetReg(kInvokeTgt)); 504 } 505 break; 506 } 507 // Intentional fallthrough for x86 508 default: 509 return -1; 510 } 511 } 512 return state + 1; 513} 514 515/* 516 * Bit of a hack here - in the absence of a real scheduling pass, 517 * emit the next instruction in a virtual invoke sequence. 518 * We can use kLr as a temp prior to target address loading 519 * Note also that we'll load the first argument ("this") into 520 * kArg1 here rather than the standard LoadArgRegs. 521 */ 522static int NextVCallInsn(CompilationUnit* cu, CallInfo* info, 523 int state, const MethodReference& target_method, 524 uint32_t method_idx, uintptr_t unused, uintptr_t unused2, 525 InvokeType unused3) { 526 Mir2Lir* cg = static_cast<Mir2Lir*>(cu->cg.get()); 527 /* 528 * This is the fast path in which the target virtual method is 529 * fully resolved at compile time. 530 */ 531 switch (state) { 532 case 0: { // Get "this" [set kArg1] 533 RegLocation rl_arg = info->args[0]; 534 cg->LoadValueDirectFixed(rl_arg, cg->TargetReg(kArg1)); 535 break; 536 } 537 case 1: // Is "this" null? [use kArg1] 538 cg->GenNullCheck(cg->TargetReg(kArg1), info->opt_flags); 539 // get this->klass_ [use kArg1, set kInvokeTgt] 540 cg->LoadWordDisp(cg->TargetReg(kArg1), mirror::Object::ClassOffset().Int32Value(), 541 cg->TargetReg(kInvokeTgt)); 542 cg->MarkPossibleNullPointerException(info->opt_flags); 543 break; 544 case 2: // Get this->klass_->vtable [usr kInvokeTgt, set kInvokeTgt] 545 cg->LoadWordDisp(cg->TargetReg(kInvokeTgt), mirror::Class::VTableOffset().Int32Value(), 546 cg->TargetReg(kInvokeTgt)); 547 break; 548 case 3: // Get target method [use kInvokeTgt, set kArg0] 549 cg->LoadWordDisp(cg->TargetReg(kInvokeTgt), (method_idx * 4) + 550 mirror::Array::DataOffset(sizeof(mirror::Object*)).Int32Value(), 551 cg->TargetReg(kArg0)); 552 break; 553 case 4: // Get the compiled code address [uses kArg0, sets kInvokeTgt] 554 if (cu->instruction_set != kX86 && cu->instruction_set != kX86_64) { 555 cg->LoadWordDisp(cg->TargetReg(kArg0), 556 mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value(), 557 cg->TargetReg(kInvokeTgt)); 558 break; 559 } 560 // Intentional fallthrough for X86 561 default: 562 return -1; 563 } 564 return state + 1; 565} 566 567/* 568 * Emit the next instruction in an invoke interface sequence. This will do a lookup in the 569 * class's IMT, calling either the actual method or art_quick_imt_conflict_trampoline if 570 * more than one interface method map to the same index. Note also that we'll load the first 571 * argument ("this") into kArg1 here rather than the standard LoadArgRegs. 572 */ 573static int NextInterfaceCallInsn(CompilationUnit* cu, CallInfo* info, int state, 574 const MethodReference& target_method, 575 uint32_t method_idx, uintptr_t unused, 576 uintptr_t direct_method, InvokeType unused2) { 577 Mir2Lir* cg = static_cast<Mir2Lir*>(cu->cg.get()); 578 579 switch (state) { 580 case 0: // Set target method index in case of conflict [set kHiddenArg, kHiddenFpArg (x86)] 581 CHECK_LT(target_method.dex_method_index, target_method.dex_file->NumMethodIds()); 582 cg->LoadConstant(cg->TargetReg(kHiddenArg), target_method.dex_method_index); 583 if (cu->instruction_set == kX86 || cu->instruction_set == kX86_64) { 584 cg->OpRegCopy(cg->TargetReg(kHiddenFpArg), cg->TargetReg(kHiddenArg)); 585 } 586 break; 587 case 1: { // Get "this" [set kArg1] 588 RegLocation rl_arg = info->args[0]; 589 cg->LoadValueDirectFixed(rl_arg, cg->TargetReg(kArg1)); 590 break; 591 } 592 case 2: // Is "this" null? [use kArg1] 593 cg->GenNullCheck(cg->TargetReg(kArg1), info->opt_flags); 594 // Get this->klass_ [use kArg1, set kInvokeTgt] 595 cg->LoadWordDisp(cg->TargetReg(kArg1), mirror::Object::ClassOffset().Int32Value(), 596 cg->TargetReg(kInvokeTgt)); 597 cg->MarkPossibleNullPointerException(info->opt_flags); 598 break; 599 case 3: // Get this->klass_->imtable [use kInvokeTgt, set kInvokeTgt] 600 cg->LoadWordDisp(cg->TargetReg(kInvokeTgt), mirror::Class::ImTableOffset().Int32Value(), 601 cg->TargetReg(kInvokeTgt)); 602 break; 603 case 4: // Get target method [use kInvokeTgt, set kArg0] 604 cg->LoadWordDisp(cg->TargetReg(kInvokeTgt), ((method_idx % ClassLinker::kImtSize) * 4) + 605 mirror::Array::DataOffset(sizeof(mirror::Object*)).Int32Value(), 606 cg->TargetReg(kArg0)); 607 break; 608 case 5: // Get the compiled code address [use kArg0, set kInvokeTgt] 609 if (cu->instruction_set != kX86 && cu->instruction_set != kX86_64) { 610 cg->LoadWordDisp(cg->TargetReg(kArg0), 611 mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value(), 612 cg->TargetReg(kInvokeTgt)); 613 break; 614 } 615 // Intentional fallthrough for X86 616 default: 617 return -1; 618 } 619 return state + 1; 620} 621 622static int NextInvokeInsnSP(CompilationUnit* cu, CallInfo* info, ThreadOffset<4> trampoline, 623 int state, const MethodReference& target_method, 624 uint32_t method_idx) { 625 Mir2Lir* cg = static_cast<Mir2Lir*>(cu->cg.get()); 626 /* 627 * This handles the case in which the base method is not fully 628 * resolved at compile time, we bail to a runtime helper. 629 */ 630 if (state == 0) { 631 if (cu->instruction_set != kX86 && cu->instruction_set != kX86_64) { 632 // Load trampoline target 633 cg->LoadWordDisp(cg->TargetReg(kSelf), trampoline.Int32Value(), cg->TargetReg(kInvokeTgt)); 634 } 635 // Load kArg0 with method index 636 CHECK_EQ(cu->dex_file, target_method.dex_file); 637 cg->LoadConstant(cg->TargetReg(kArg0), target_method.dex_method_index); 638 return 1; 639 } 640 return -1; 641} 642 643static int NextStaticCallInsnSP(CompilationUnit* cu, CallInfo* info, 644 int state, 645 const MethodReference& target_method, 646 uint32_t unused, uintptr_t unused2, 647 uintptr_t unused3, InvokeType unused4) { 648 ThreadOffset<4> trampoline = QUICK_ENTRYPOINT_OFFSET(4, pInvokeStaticTrampolineWithAccessCheck); 649 return NextInvokeInsnSP(cu, info, trampoline, state, target_method, 0); 650} 651 652static int NextDirectCallInsnSP(CompilationUnit* cu, CallInfo* info, int state, 653 const MethodReference& target_method, 654 uint32_t unused, uintptr_t unused2, 655 uintptr_t unused3, InvokeType unused4) { 656 ThreadOffset<4> trampoline = QUICK_ENTRYPOINT_OFFSET(4, pInvokeDirectTrampolineWithAccessCheck); 657 return NextInvokeInsnSP(cu, info, trampoline, state, target_method, 0); 658} 659 660static int NextSuperCallInsnSP(CompilationUnit* cu, CallInfo* info, int state, 661 const MethodReference& target_method, 662 uint32_t unused, uintptr_t unused2, 663 uintptr_t unused3, InvokeType unused4) { 664 ThreadOffset<4> trampoline = QUICK_ENTRYPOINT_OFFSET(4, pInvokeSuperTrampolineWithAccessCheck); 665 return NextInvokeInsnSP(cu, info, trampoline, state, target_method, 0); 666} 667 668static int NextVCallInsnSP(CompilationUnit* cu, CallInfo* info, int state, 669 const MethodReference& target_method, 670 uint32_t unused, uintptr_t unused2, 671 uintptr_t unused3, InvokeType unused4) { 672 ThreadOffset<4> trampoline = QUICK_ENTRYPOINT_OFFSET(4, pInvokeVirtualTrampolineWithAccessCheck); 673 return NextInvokeInsnSP(cu, info, trampoline, state, target_method, 0); 674} 675 676static int NextInterfaceCallInsnWithAccessCheck(CompilationUnit* cu, 677 CallInfo* info, int state, 678 const MethodReference& target_method, 679 uint32_t unused, uintptr_t unused2, 680 uintptr_t unused3, InvokeType unused4) { 681 ThreadOffset<4> trampoline = 682 QUICK_ENTRYPOINT_OFFSET(4, pInvokeInterfaceTrampolineWithAccessCheck); 683 return NextInvokeInsnSP(cu, info, trampoline, state, target_method, 0); 684} 685 686int Mir2Lir::LoadArgRegs(CallInfo* info, int call_state, 687 NextCallInsn next_call_insn, 688 const MethodReference& target_method, 689 uint32_t vtable_idx, uintptr_t direct_code, 690 uintptr_t direct_method, InvokeType type, bool skip_this) { 691 int last_arg_reg = TargetReg(kArg3).GetReg(); 692 int next_reg = TargetReg(kArg1).GetReg(); 693 int next_arg = 0; 694 if (skip_this) { 695 next_reg++; 696 next_arg++; 697 } 698 for (; (next_reg <= last_arg_reg) && (next_arg < info->num_arg_words); next_reg++) { 699 RegLocation rl_arg = info->args[next_arg++]; 700 rl_arg = UpdateRawLoc(rl_arg); 701 if (rl_arg.wide && (next_reg <= TargetReg(kArg2).GetReg())) { 702 RegStorage r_tmp(RegStorage::k64BitPair, next_reg, next_reg + 1); 703 LoadValueDirectWideFixed(rl_arg, r_tmp); 704 next_reg++; 705 next_arg++; 706 } else { 707 if (rl_arg.wide) { 708 rl_arg = NarrowRegLoc(rl_arg); 709 rl_arg.is_const = false; 710 } 711 LoadValueDirectFixed(rl_arg, RegStorage::Solo32(next_reg)); 712 } 713 call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx, 714 direct_code, direct_method, type); 715 } 716 return call_state; 717} 718 719/* 720 * Load up to 5 arguments, the first three of which will be in 721 * kArg1 .. kArg3. On entry kArg0 contains the current method pointer, 722 * and as part of the load sequence, it must be replaced with 723 * the target method pointer. Note, this may also be called 724 * for "range" variants if the number of arguments is 5 or fewer. 725 */ 726int Mir2Lir::GenDalvikArgsNoRange(CallInfo* info, 727 int call_state, LIR** pcrLabel, NextCallInsn next_call_insn, 728 const MethodReference& target_method, 729 uint32_t vtable_idx, uintptr_t direct_code, 730 uintptr_t direct_method, InvokeType type, bool skip_this) { 731 RegLocation rl_arg; 732 733 /* If no arguments, just return */ 734 if (info->num_arg_words == 0) 735 return call_state; 736 737 call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx, 738 direct_code, direct_method, type); 739 740 DCHECK_LE(info->num_arg_words, 5); 741 if (info->num_arg_words > 3) { 742 int32_t next_use = 3; 743 // Detect special case of wide arg spanning arg3/arg4 744 RegLocation rl_use0 = info->args[0]; 745 RegLocation rl_use1 = info->args[1]; 746 RegLocation rl_use2 = info->args[2]; 747 if (((!rl_use0.wide && !rl_use1.wide) || rl_use0.wide) && rl_use2.wide) { 748 RegStorage reg; 749 // Wide spans, we need the 2nd half of uses[2]. 750 rl_arg = UpdateLocWide(rl_use2); 751 if (rl_arg.location == kLocPhysReg) { 752 reg = rl_arg.reg.GetHigh(); 753 } else { 754 // kArg2 & rArg3 can safely be used here 755 reg = TargetReg(kArg3); 756 LoadWordDisp(TargetReg(kSp), SRegOffset(rl_arg.s_reg_low) + 4, reg); 757 call_state = next_call_insn(cu_, info, call_state, target_method, 758 vtable_idx, direct_code, direct_method, type); 759 } 760 StoreBaseDisp(TargetReg(kSp), (next_use + 1) * 4, reg, kWord); 761 call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx, 762 direct_code, direct_method, type); 763 next_use++; 764 } 765 // Loop through the rest 766 while (next_use < info->num_arg_words) { 767 RegStorage low_reg; 768 RegStorage high_reg; 769 rl_arg = info->args[next_use]; 770 rl_arg = UpdateRawLoc(rl_arg); 771 if (rl_arg.location == kLocPhysReg) { 772 if (rl_arg.wide) { 773 low_reg = rl_arg.reg.GetLow(); 774 high_reg = rl_arg.reg.GetHigh(); 775 } else { 776 low_reg = rl_arg.reg; 777 } 778 } else { 779 low_reg = TargetReg(kArg2); 780 if (rl_arg.wide) { 781 high_reg = TargetReg(kArg3); 782 LoadValueDirectWideFixed(rl_arg, RegStorage::MakeRegPair(low_reg, high_reg)); 783 } else { 784 LoadValueDirectFixed(rl_arg, low_reg); 785 } 786 call_state = next_call_insn(cu_, info, call_state, target_method, 787 vtable_idx, direct_code, direct_method, type); 788 } 789 int outs_offset = (next_use + 1) * 4; 790 if (rl_arg.wide) { 791 StoreBaseDispWide(TargetReg(kSp), outs_offset, RegStorage::MakeRegPair(low_reg, high_reg)); 792 next_use += 2; 793 } else { 794 StoreWordDisp(TargetReg(kSp), outs_offset, low_reg); 795 next_use++; 796 } 797 call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx, 798 direct_code, direct_method, type); 799 } 800 } 801 802 call_state = LoadArgRegs(info, call_state, next_call_insn, 803 target_method, vtable_idx, direct_code, direct_method, 804 type, skip_this); 805 806 if (pcrLabel) { 807 if (Runtime::Current()->ExplicitNullChecks()) { 808 *pcrLabel = GenExplicitNullCheck(TargetReg(kArg1), info->opt_flags); 809 } else { 810 *pcrLabel = nullptr; 811 // In lieu of generating a check for kArg1 being null, we need to 812 // perform a load when doing implicit checks. 813 RegStorage tmp = AllocTemp(); 814 LoadWordDisp(TargetReg(kArg1), 0, tmp); 815 MarkPossibleNullPointerException(info->opt_flags); 816 FreeTemp(tmp); 817 } 818 } 819 return call_state; 820} 821 822/* 823 * May have 0+ arguments (also used for jumbo). Note that 824 * source virtual registers may be in physical registers, so may 825 * need to be flushed to home location before copying. This 826 * applies to arg3 and above (see below). 827 * 828 * Two general strategies: 829 * If < 20 arguments 830 * Pass args 3-18 using vldm/vstm block copy 831 * Pass arg0, arg1 & arg2 in kArg1-kArg3 832 * If 20+ arguments 833 * Pass args arg19+ using memcpy block copy 834 * Pass arg0, arg1 & arg2 in kArg1-kArg3 835 * 836 */ 837int Mir2Lir::GenDalvikArgsRange(CallInfo* info, int call_state, 838 LIR** pcrLabel, NextCallInsn next_call_insn, 839 const MethodReference& target_method, 840 uint32_t vtable_idx, uintptr_t direct_code, uintptr_t direct_method, 841 InvokeType type, bool skip_this) { 842 // If we can treat it as non-range (Jumbo ops will use range form) 843 if (info->num_arg_words <= 5) 844 return GenDalvikArgsNoRange(info, call_state, pcrLabel, 845 next_call_insn, target_method, vtable_idx, 846 direct_code, direct_method, type, skip_this); 847 /* 848 * First load the non-register arguments. Both forms expect all 849 * of the source arguments to be in their home frame location, so 850 * scan the s_reg names and flush any that have been promoted to 851 * frame backing storage. 852 */ 853 // Scan the rest of the args - if in phys_reg flush to memory 854 for (int next_arg = 0; next_arg < info->num_arg_words;) { 855 RegLocation loc = info->args[next_arg]; 856 if (loc.wide) { 857 loc = UpdateLocWide(loc); 858 if ((next_arg >= 2) && (loc.location == kLocPhysReg)) { 859 StoreBaseDispWide(TargetReg(kSp), SRegOffset(loc.s_reg_low), loc.reg); 860 } 861 next_arg += 2; 862 } else { 863 loc = UpdateLoc(loc); 864 if ((next_arg >= 3) && (loc.location == kLocPhysReg)) { 865 StoreBaseDisp(TargetReg(kSp), SRegOffset(loc.s_reg_low), loc.reg, kWord); 866 } 867 next_arg++; 868 } 869 } 870 871 // Logic below assumes that Method pointer is at offset zero from SP. 872 DCHECK_EQ(VRegOffset(static_cast<int>(kVRegMethodPtrBaseReg)), 0); 873 874 // The first 3 arguments are passed via registers. 875 // TODO: For 64-bit, instead of hardcoding 4 for Method* size, we should either 876 // get size of uintptr_t or size of object reference according to model being used. 877 int outs_offset = 4 /* Method* */ + (3 * sizeof(uint32_t)); 878 int start_offset = SRegOffset(info->args[3].s_reg_low); 879 int regs_left_to_pass_via_stack = info->num_arg_words - 3; 880 DCHECK_GT(regs_left_to_pass_via_stack, 0); 881 882 if (cu_->instruction_set == kThumb2 && regs_left_to_pass_via_stack <= 16) { 883 // Use vldm/vstm pair using kArg3 as a temp 884 call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx, 885 direct_code, direct_method, type); 886 OpRegRegImm(kOpAdd, TargetReg(kArg3), TargetReg(kSp), start_offset); 887 LIR* ld = OpVldm(TargetReg(kArg3), regs_left_to_pass_via_stack); 888 // TUNING: loosen barrier 889 ld->u.m.def_mask = ENCODE_ALL; 890 SetMemRefType(ld, true /* is_load */, kDalvikReg); 891 call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx, 892 direct_code, direct_method, type); 893 OpRegRegImm(kOpAdd, TargetReg(kArg3), TargetReg(kSp), 4 /* Method* */ + (3 * 4)); 894 call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx, 895 direct_code, direct_method, type); 896 LIR* st = OpVstm(TargetReg(kArg3), regs_left_to_pass_via_stack); 897 SetMemRefType(st, false /* is_load */, kDalvikReg); 898 st->u.m.def_mask = ENCODE_ALL; 899 call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx, 900 direct_code, direct_method, type); 901 } else if (cu_->instruction_set == kX86 || cu_->instruction_set == kX86_64) { 902 int current_src_offset = start_offset; 903 int current_dest_offset = outs_offset; 904 905 while (regs_left_to_pass_via_stack > 0) { 906 // This is based on the knowledge that the stack itself is 16-byte aligned. 907 bool src_is_16b_aligned = (current_src_offset & 0xF) == 0; 908 bool dest_is_16b_aligned = (current_dest_offset & 0xF) == 0; 909 size_t bytes_to_move; 910 911 /* 912 * The amount to move defaults to 32-bit. If there are 4 registers left to move, then do a 913 * a 128-bit move because we won't get the chance to try to aligned. If there are more than 914 * 4 registers left to move, consider doing a 128-bit only if either src or dest are aligned. 915 * We do this because we could potentially do a smaller move to align. 916 */ 917 if (regs_left_to_pass_via_stack == 4 || 918 (regs_left_to_pass_via_stack > 4 && (src_is_16b_aligned || dest_is_16b_aligned))) { 919 // Moving 128-bits via xmm register. 920 bytes_to_move = sizeof(uint32_t) * 4; 921 922 // Allocate a free xmm temp. Since we are working through the calling sequence, 923 // we expect to have an xmm temporary available. 924 RegStorage temp = AllocTempDouble(); 925 CHECK_GT(temp.GetLowReg(), 0); 926 927 LIR* ld1 = nullptr; 928 LIR* ld2 = nullptr; 929 LIR* st1 = nullptr; 930 LIR* st2 = nullptr; 931 932 /* 933 * The logic is similar for both loads and stores. If we have 16-byte alignment, 934 * do an aligned move. If we have 8-byte alignment, then do the move in two 935 * parts. This approach prevents possible cache line splits. Finally, fall back 936 * to doing an unaligned move. In most cases we likely won't split the cache 937 * line but we cannot prove it and thus take a conservative approach. 938 */ 939 bool src_is_8b_aligned = (current_src_offset & 0x7) == 0; 940 bool dest_is_8b_aligned = (current_dest_offset & 0x7) == 0; 941 942 if (src_is_16b_aligned) { 943 ld1 = OpMovRegMem(temp, TargetReg(kSp), current_src_offset, kMovA128FP); 944 } else if (src_is_8b_aligned) { 945 ld1 = OpMovRegMem(temp, TargetReg(kSp), current_src_offset, kMovLo128FP); 946 ld2 = OpMovRegMem(temp, TargetReg(kSp), current_src_offset + (bytes_to_move >> 1), 947 kMovHi128FP); 948 } else { 949 ld1 = OpMovRegMem(temp, TargetReg(kSp), current_src_offset, kMovU128FP); 950 } 951 952 if (dest_is_16b_aligned) { 953 st1 = OpMovMemReg(TargetReg(kSp), current_dest_offset, temp, kMovA128FP); 954 } else if (dest_is_8b_aligned) { 955 st1 = OpMovMemReg(TargetReg(kSp), current_dest_offset, temp, kMovLo128FP); 956 st2 = OpMovMemReg(TargetReg(kSp), current_dest_offset + (bytes_to_move >> 1), 957 temp, kMovHi128FP); 958 } else { 959 st1 = OpMovMemReg(TargetReg(kSp), current_dest_offset, temp, kMovU128FP); 960 } 961 962 // TODO If we could keep track of aliasing information for memory accesses that are wider 963 // than 64-bit, we wouldn't need to set up a barrier. 964 if (ld1 != nullptr) { 965 if (ld2 != nullptr) { 966 // For 64-bit load we can actually set up the aliasing information. 967 AnnotateDalvikRegAccess(ld1, current_src_offset >> 2, true, true); 968 AnnotateDalvikRegAccess(ld2, (current_src_offset + (bytes_to_move >> 1)) >> 2, true, true); 969 } else { 970 // Set barrier for 128-bit load. 971 SetMemRefType(ld1, true /* is_load */, kDalvikReg); 972 ld1->u.m.def_mask = ENCODE_ALL; 973 } 974 } 975 if (st1 != nullptr) { 976 if (st2 != nullptr) { 977 // For 64-bit store we can actually set up the aliasing information. 978 AnnotateDalvikRegAccess(st1, current_dest_offset >> 2, false, true); 979 AnnotateDalvikRegAccess(st2, (current_dest_offset + (bytes_to_move >> 1)) >> 2, false, true); 980 } else { 981 // Set barrier for 128-bit store. 982 SetMemRefType(st1, false /* is_load */, kDalvikReg); 983 st1->u.m.def_mask = ENCODE_ALL; 984 } 985 } 986 987 // Free the temporary used for the data movement. 988 // CLEANUP: temp is currently a bogus pair, elmiminate extra free when updated. 989 FreeTemp(temp.GetLow()); 990 FreeTemp(temp.GetHigh()); 991 } else { 992 // Moving 32-bits via general purpose register. 993 bytes_to_move = sizeof(uint32_t); 994 995 // Instead of allocating a new temp, simply reuse one of the registers being used 996 // for argument passing. 997 RegStorage temp = TargetReg(kArg3); 998 999 // Now load the argument VR and store to the outs. 1000 LoadWordDisp(TargetReg(kSp), current_src_offset, temp); 1001 StoreWordDisp(TargetReg(kSp), current_dest_offset, temp); 1002 } 1003 1004 current_src_offset += bytes_to_move; 1005 current_dest_offset += bytes_to_move; 1006 regs_left_to_pass_via_stack -= (bytes_to_move >> 2); 1007 } 1008 } else { 1009 // Generate memcpy 1010 OpRegRegImm(kOpAdd, TargetReg(kArg0), TargetReg(kSp), outs_offset); 1011 OpRegRegImm(kOpAdd, TargetReg(kArg1), TargetReg(kSp), start_offset); 1012 CallRuntimeHelperRegRegImm(QUICK_ENTRYPOINT_OFFSET(4, pMemcpy), TargetReg(kArg0), 1013 TargetReg(kArg1), (info->num_arg_words - 3) * 4, false); 1014 } 1015 1016 call_state = LoadArgRegs(info, call_state, next_call_insn, 1017 target_method, vtable_idx, direct_code, direct_method, 1018 type, skip_this); 1019 1020 call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx, 1021 direct_code, direct_method, type); 1022 if (pcrLabel) { 1023 if (Runtime::Current()->ExplicitNullChecks()) { 1024 *pcrLabel = GenExplicitNullCheck(TargetReg(kArg1), info->opt_flags); 1025 } else { 1026 *pcrLabel = nullptr; 1027 // In lieu of generating a check for kArg1 being null, we need to 1028 // perform a load when doing implicit checks. 1029 RegStorage tmp = AllocTemp(); 1030 LoadWordDisp(TargetReg(kArg1), 0, tmp); 1031 MarkPossibleNullPointerException(info->opt_flags); 1032 FreeTemp(tmp); 1033 } 1034 } 1035 return call_state; 1036} 1037 1038RegLocation Mir2Lir::InlineTarget(CallInfo* info) { 1039 RegLocation res; 1040 if (info->result.location == kLocInvalid) { 1041 res = GetReturn(false); 1042 } else { 1043 res = info->result; 1044 } 1045 return res; 1046} 1047 1048RegLocation Mir2Lir::InlineTargetWide(CallInfo* info) { 1049 RegLocation res; 1050 if (info->result.location == kLocInvalid) { 1051 res = GetReturnWide(false); 1052 } else { 1053 res = info->result; 1054 } 1055 return res; 1056} 1057 1058bool Mir2Lir::GenInlinedCharAt(CallInfo* info) { 1059 if (cu_->instruction_set == kMips) { 1060 // TODO - add Mips implementation 1061 return false; 1062 } 1063 // Location of reference to data array 1064 int value_offset = mirror::String::ValueOffset().Int32Value(); 1065 // Location of count 1066 int count_offset = mirror::String::CountOffset().Int32Value(); 1067 // Starting offset within data array 1068 int offset_offset = mirror::String::OffsetOffset().Int32Value(); 1069 // Start of char data with array_ 1070 int data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Int32Value(); 1071 1072 RegLocation rl_obj = info->args[0]; 1073 RegLocation rl_idx = info->args[1]; 1074 rl_obj = LoadValue(rl_obj, kCoreReg); 1075 // X86 wants to avoid putting a constant index into a register. 1076 if (!((cu_->instruction_set == kX86 || cu_->instruction_set == kX86_64)&& rl_idx.is_const)) { 1077 rl_idx = LoadValue(rl_idx, kCoreReg); 1078 } 1079 RegStorage reg_max; 1080 GenNullCheck(rl_obj.reg, info->opt_flags); 1081 bool range_check = (!(info->opt_flags & MIR_IGNORE_RANGE_CHECK)); 1082 LIR* range_check_branch = nullptr; 1083 RegStorage reg_off; 1084 RegStorage reg_ptr; 1085 if (cu_->instruction_set != kX86 && cu_->instruction_set != kX86_64) { 1086 reg_off = AllocTemp(); 1087 reg_ptr = AllocTemp(); 1088 if (range_check) { 1089 reg_max = AllocTemp(); 1090 LoadWordDisp(rl_obj.reg, count_offset, reg_max); 1091 MarkPossibleNullPointerException(info->opt_flags); 1092 } 1093 LoadWordDisp(rl_obj.reg, offset_offset, reg_off); 1094 MarkPossibleNullPointerException(info->opt_flags); 1095 LoadWordDisp(rl_obj.reg, value_offset, reg_ptr); 1096 if (range_check) { 1097 // Set up a launch pad to allow retry in case of bounds violation */ 1098 OpRegReg(kOpCmp, rl_idx.reg, reg_max); 1099 FreeTemp(reg_max); 1100 range_check_branch = OpCondBranch(kCondUge, nullptr); 1101 } 1102 OpRegImm(kOpAdd, reg_ptr, data_offset); 1103 } else { 1104 if (range_check) { 1105 // On x86, we can compare to memory directly 1106 // Set up a launch pad to allow retry in case of bounds violation */ 1107 if (rl_idx.is_const) { 1108 range_check_branch = OpCmpMemImmBranch( 1109 kCondUlt, RegStorage::InvalidReg(), rl_obj.reg, count_offset, 1110 mir_graph_->ConstantValue(rl_idx.orig_sreg), nullptr); 1111 } else { 1112 OpRegMem(kOpCmp, rl_idx.reg, rl_obj.reg, count_offset); 1113 range_check_branch = OpCondBranch(kCondUge, nullptr); 1114 } 1115 } 1116 reg_off = AllocTemp(); 1117 reg_ptr = AllocTemp(); 1118 LoadWordDisp(rl_obj.reg, offset_offset, reg_off); 1119 LoadWordDisp(rl_obj.reg, value_offset, reg_ptr); 1120 } 1121 if (rl_idx.is_const) { 1122 OpRegImm(kOpAdd, reg_off, mir_graph_->ConstantValue(rl_idx.orig_sreg)); 1123 } else { 1124 OpRegReg(kOpAdd, reg_off, rl_idx.reg); 1125 } 1126 FreeTemp(rl_obj.reg); 1127 if (rl_idx.location == kLocPhysReg) { 1128 FreeTemp(rl_idx.reg); 1129 } 1130 RegLocation rl_dest = InlineTarget(info); 1131 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); 1132 if (cu_->instruction_set != kX86 && cu_->instruction_set != kX86_64) { 1133 LoadBaseIndexed(reg_ptr, reg_off, rl_result.reg, 1, kUnsignedHalf); 1134 } else { 1135 LoadBaseIndexedDisp(reg_ptr, reg_off, 1, data_offset, rl_result.reg, 1136 RegStorage::InvalidReg(), kUnsignedHalf, INVALID_SREG); 1137 } 1138 FreeTemp(reg_off); 1139 FreeTemp(reg_ptr); 1140 StoreValue(rl_dest, rl_result); 1141 if (range_check) { 1142 DCHECK(range_check_branch != nullptr); 1143 info->opt_flags |= MIR_IGNORE_NULL_CHECK; // Record that we've already null checked. 1144 AddIntrinsicLaunchpad(info, range_check_branch); 1145 } 1146 return true; 1147} 1148 1149// Generates an inlined String.is_empty or String.length. 1150bool Mir2Lir::GenInlinedStringIsEmptyOrLength(CallInfo* info, bool is_empty) { 1151 if (cu_->instruction_set == kMips) { 1152 // TODO - add Mips implementation 1153 return false; 1154 } 1155 // dst = src.length(); 1156 RegLocation rl_obj = info->args[0]; 1157 rl_obj = LoadValue(rl_obj, kCoreReg); 1158 RegLocation rl_dest = InlineTarget(info); 1159 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); 1160 GenNullCheck(rl_obj.reg, info->opt_flags); 1161 LoadWordDisp(rl_obj.reg, mirror::String::CountOffset().Int32Value(), rl_result.reg); 1162 MarkPossibleNullPointerException(info->opt_flags); 1163 if (is_empty) { 1164 // dst = (dst == 0); 1165 if (cu_->instruction_set == kThumb2) { 1166 RegStorage t_reg = AllocTemp(); 1167 OpRegReg(kOpNeg, t_reg, rl_result.reg); 1168 OpRegRegReg(kOpAdc, rl_result.reg, rl_result.reg, t_reg); 1169 } else { 1170 DCHECK(cu_->instruction_set == kX86 || cu_->instruction_set == kX86_64); 1171 OpRegImm(kOpSub, rl_result.reg, 1); 1172 OpRegImm(kOpLsr, rl_result.reg, 31); 1173 } 1174 } 1175 StoreValue(rl_dest, rl_result); 1176 return true; 1177} 1178 1179bool Mir2Lir::GenInlinedReverseBytes(CallInfo* info, OpSize size) { 1180 if (cu_->instruction_set == kMips) { 1181 // TODO - add Mips implementation 1182 return false; 1183 } 1184 RegLocation rl_src_i = info->args[0]; 1185 RegLocation rl_dest = (size == kLong) ? InlineTargetWide(info) : InlineTarget(info); // result reg 1186 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); 1187 if (size == kLong) { 1188 RegLocation rl_i = LoadValueWide(rl_src_i, kCoreReg); 1189 RegStorage r_i_low = rl_i.reg.GetLow(); 1190 if (rl_i.reg.GetLowReg() == rl_result.reg.GetLowReg()) { 1191 // First REV shall clobber rl_result.reg.GetReg(), save the value in a temp for the second REV. 1192 r_i_low = AllocTemp(); 1193 OpRegCopy(r_i_low, rl_i.reg); 1194 } 1195 OpRegReg(kOpRev, rl_result.reg.GetLow(), rl_i.reg.GetHigh()); 1196 OpRegReg(kOpRev, rl_result.reg.GetHigh(), r_i_low); 1197 if (rl_i.reg.GetLowReg() == rl_result.reg.GetLowReg()) { 1198 FreeTemp(r_i_low); 1199 } 1200 StoreValueWide(rl_dest, rl_result); 1201 } else { 1202 DCHECK(size == kWord || size == kSignedHalf); 1203 OpKind op = (size == kWord) ? kOpRev : kOpRevsh; 1204 RegLocation rl_i = LoadValue(rl_src_i, kCoreReg); 1205 OpRegReg(op, rl_result.reg, rl_i.reg); 1206 StoreValue(rl_dest, rl_result); 1207 } 1208 return true; 1209} 1210 1211bool Mir2Lir::GenInlinedAbsInt(CallInfo* info) { 1212 if (cu_->instruction_set == kMips) { 1213 // TODO - add Mips implementation 1214 return false; 1215 } 1216 RegLocation rl_src = info->args[0]; 1217 rl_src = LoadValue(rl_src, kCoreReg); 1218 RegLocation rl_dest = InlineTarget(info); 1219 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); 1220 RegStorage sign_reg = AllocTemp(); 1221 // abs(x) = y<=x>>31, (x+y)^y. 1222 OpRegRegImm(kOpAsr, sign_reg, rl_src.reg, 31); 1223 OpRegRegReg(kOpAdd, rl_result.reg, rl_src.reg, sign_reg); 1224 OpRegReg(kOpXor, rl_result.reg, sign_reg); 1225 StoreValue(rl_dest, rl_result); 1226 return true; 1227} 1228 1229bool Mir2Lir::GenInlinedAbsLong(CallInfo* info) { 1230 if (cu_->instruction_set == kMips) { 1231 // TODO - add Mips implementation 1232 return false; 1233 } 1234 RegLocation rl_src = info->args[0]; 1235 rl_src = LoadValueWide(rl_src, kCoreReg); 1236 RegLocation rl_dest = InlineTargetWide(info); 1237 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); 1238 1239 // If on x86 or if we would clobber a register needed later, just copy the source first. 1240 if (cu_->instruction_set == kX86 || cu_->instruction_set == kX86_64 || rl_result.reg.GetLowReg() == rl_src.reg.GetHighReg()) { 1241 OpRegCopyWide(rl_result.reg, rl_src.reg); 1242 if (rl_result.reg.GetLowReg() != rl_src.reg.GetLowReg() && 1243 rl_result.reg.GetLowReg() != rl_src.reg.GetHighReg() && 1244 rl_result.reg.GetHighReg() != rl_src.reg.GetLowReg() && 1245 rl_result.reg.GetHighReg() != rl_src.reg.GetHighReg()) { 1246 // Reuse source registers to avoid running out of temps. 1247 FreeTemp(rl_src.reg); 1248 } 1249 rl_src = rl_result; 1250 } 1251 1252 // abs(x) = y<=x>>31, (x+y)^y. 1253 RegStorage sign_reg = AllocTemp(); 1254 OpRegRegImm(kOpAsr, sign_reg, rl_src.reg.GetHigh(), 31); 1255 OpRegRegReg(kOpAdd, rl_result.reg.GetLow(), rl_src.reg.GetLow(), sign_reg); 1256 OpRegRegReg(kOpAdc, rl_result.reg.GetHigh(), rl_src.reg.GetHigh(), sign_reg); 1257 OpRegReg(kOpXor, rl_result.reg.GetLow(), sign_reg); 1258 OpRegReg(kOpXor, rl_result.reg.GetHigh(), sign_reg); 1259 StoreValueWide(rl_dest, rl_result); 1260 return true; 1261} 1262 1263bool Mir2Lir::GenInlinedAbsFloat(CallInfo* info) { 1264 if (cu_->instruction_set == kMips) { 1265 // TODO - add Mips implementation 1266 return false; 1267 } 1268 RegLocation rl_src = info->args[0]; 1269 rl_src = LoadValue(rl_src, kCoreReg); 1270 RegLocation rl_dest = InlineTarget(info); 1271 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); 1272 OpRegRegImm(kOpAnd, rl_result.reg, rl_src.reg, 0x7fffffff); 1273 StoreValue(rl_dest, rl_result); 1274 return true; 1275} 1276 1277bool Mir2Lir::GenInlinedAbsDouble(CallInfo* info) { 1278 if (cu_->instruction_set == kMips) { 1279 // TODO - add Mips implementation 1280 return false; 1281 } 1282 RegLocation rl_src = info->args[0]; 1283 rl_src = LoadValueWide(rl_src, kCoreReg); 1284 RegLocation rl_dest = InlineTargetWide(info); 1285 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); 1286 OpRegCopyWide(rl_result.reg, rl_src.reg); 1287 OpRegImm(kOpAnd, rl_result.reg.GetHigh(), 0x7fffffff); 1288 StoreValueWide(rl_dest, rl_result); 1289 return true; 1290} 1291 1292bool Mir2Lir::GenInlinedFloatCvt(CallInfo* info) { 1293 if (cu_->instruction_set == kMips) { 1294 // TODO - add Mips implementation 1295 return false; 1296 } 1297 RegLocation rl_src = info->args[0]; 1298 RegLocation rl_dest = InlineTarget(info); 1299 StoreValue(rl_dest, rl_src); 1300 return true; 1301} 1302 1303bool Mir2Lir::GenInlinedDoubleCvt(CallInfo* info) { 1304 if (cu_->instruction_set == kMips) { 1305 // TODO - add Mips implementation 1306 return false; 1307 } 1308 RegLocation rl_src = info->args[0]; 1309 RegLocation rl_dest = InlineTargetWide(info); 1310 StoreValueWide(rl_dest, rl_src); 1311 return true; 1312} 1313 1314/* 1315 * Fast String.indexOf(I) & (II). Tests for simple case of char <= 0xFFFF, 1316 * otherwise bails to standard library code. 1317 */ 1318bool Mir2Lir::GenInlinedIndexOf(CallInfo* info, bool zero_based) { 1319 if (cu_->instruction_set == kMips) { 1320 // TODO - add Mips implementation 1321 return false; 1322 } 1323 RegLocation rl_obj = info->args[0]; 1324 RegLocation rl_char = info->args[1]; 1325 if (rl_char.is_const && (mir_graph_->ConstantValue(rl_char) & ~0xFFFF) != 0) { 1326 // Code point beyond 0xFFFF. Punt to the real String.indexOf(). 1327 return false; 1328 } 1329 1330 ClobberCallerSave(); 1331 LockCallTemps(); // Using fixed registers 1332 RegStorage reg_ptr = TargetReg(kArg0); 1333 RegStorage reg_char = TargetReg(kArg1); 1334 RegStorage reg_start = TargetReg(kArg2); 1335 1336 LoadValueDirectFixed(rl_obj, reg_ptr); 1337 LoadValueDirectFixed(rl_char, reg_char); 1338 if (zero_based) { 1339 LoadConstant(reg_start, 0); 1340 } else { 1341 RegLocation rl_start = info->args[2]; // 3rd arg only present in III flavor of IndexOf. 1342 LoadValueDirectFixed(rl_start, reg_start); 1343 } 1344 RegStorage r_tgt = LoadHelper(QUICK_ENTRYPOINT_OFFSET(4, pIndexOf)); 1345 GenExplicitNullCheck(reg_ptr, info->opt_flags); 1346 LIR* high_code_point_branch = 1347 rl_char.is_const ? nullptr : OpCmpImmBranch(kCondGt, reg_char, 0xFFFF, nullptr); 1348 // NOTE: not a safepoint 1349 OpReg(kOpBlx, r_tgt); 1350 if (!rl_char.is_const) { 1351 // Add the slow path for code points beyond 0xFFFF. 1352 DCHECK(high_code_point_branch != nullptr); 1353 LIR* resume_tgt = NewLIR0(kPseudoTargetLabel); 1354 info->opt_flags |= MIR_IGNORE_NULL_CHECK; // Record that we've null checked. 1355 AddIntrinsicLaunchpad(info, high_code_point_branch, resume_tgt); 1356 } else { 1357 DCHECK_EQ(mir_graph_->ConstantValue(rl_char) & ~0xFFFF, 0); 1358 DCHECK(high_code_point_branch == nullptr); 1359 } 1360 RegLocation rl_return = GetReturn(false); 1361 RegLocation rl_dest = InlineTarget(info); 1362 StoreValue(rl_dest, rl_return); 1363 return true; 1364} 1365 1366/* Fast string.compareTo(Ljava/lang/string;)I. */ 1367bool Mir2Lir::GenInlinedStringCompareTo(CallInfo* info) { 1368 if (cu_->instruction_set == kMips) { 1369 // TODO - add Mips implementation 1370 return false; 1371 } 1372 ClobberCallerSave(); 1373 LockCallTemps(); // Using fixed registers 1374 RegStorage reg_this = TargetReg(kArg0); 1375 RegStorage reg_cmp = TargetReg(kArg1); 1376 1377 RegLocation rl_this = info->args[0]; 1378 RegLocation rl_cmp = info->args[1]; 1379 LoadValueDirectFixed(rl_this, reg_this); 1380 LoadValueDirectFixed(rl_cmp, reg_cmp); 1381 RegStorage r_tgt = (cu_->instruction_set != kX86 && cu_->instruction_set != kX86_64) ? 1382 LoadHelper(QUICK_ENTRYPOINT_OFFSET(4, pStringCompareTo)) : RegStorage::InvalidReg(); 1383 GenExplicitNullCheck(reg_this, info->opt_flags); 1384 info->opt_flags |= MIR_IGNORE_NULL_CHECK; // Record that we've null checked. 1385 // TUNING: check if rl_cmp.s_reg_low is already null checked 1386 LIR* cmp_null_check_branch = OpCmpImmBranch(kCondEq, reg_cmp, 0, nullptr); 1387 AddIntrinsicLaunchpad(info, cmp_null_check_branch); 1388 // NOTE: not a safepoint 1389 if (cu_->instruction_set != kX86 && cu_->instruction_set != kX86_64) { 1390 OpReg(kOpBlx, r_tgt); 1391 } else { 1392 OpThreadMem(kOpBlx, QUICK_ENTRYPOINT_OFFSET(4, pStringCompareTo)); 1393 } 1394 RegLocation rl_return = GetReturn(false); 1395 RegLocation rl_dest = InlineTarget(info); 1396 StoreValue(rl_dest, rl_return); 1397 return true; 1398} 1399 1400bool Mir2Lir::GenInlinedCurrentThread(CallInfo* info) { 1401 RegLocation rl_dest = InlineTarget(info); 1402 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); 1403 ThreadOffset<4> offset = Thread::PeerOffset<4>(); 1404 if (cu_->instruction_set == kThumb2 || cu_->instruction_set == kMips) { 1405 LoadWordDisp(TargetReg(kSelf), offset.Int32Value(), rl_result.reg); 1406 } else { 1407 CHECK(cu_->instruction_set == kX86 || cu_->instruction_set == kX86_64); 1408 reinterpret_cast<X86Mir2Lir*>(this)->OpRegThreadMem(kOpMov, rl_result.reg.GetReg(), offset); 1409 } 1410 StoreValue(rl_dest, rl_result); 1411 return true; 1412} 1413 1414bool Mir2Lir::GenInlinedUnsafeGet(CallInfo* info, 1415 bool is_long, bool is_volatile) { 1416 if (cu_->instruction_set == kMips) { 1417 // TODO - add Mips implementation 1418 return false; 1419 } 1420 // Unused - RegLocation rl_src_unsafe = info->args[0]; 1421 RegLocation rl_src_obj = info->args[1]; // Object 1422 RegLocation rl_src_offset = info->args[2]; // long low 1423 rl_src_offset = NarrowRegLoc(rl_src_offset); // ignore high half in info->args[3] 1424 RegLocation rl_dest = is_long ? InlineTargetWide(info) : InlineTarget(info); // result reg 1425 1426 RegLocation rl_object = LoadValue(rl_src_obj, kCoreReg); 1427 RegLocation rl_offset = LoadValue(rl_src_offset, kCoreReg); 1428 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); 1429 if (is_long) { 1430 if (cu_->instruction_set == kX86) { 1431 LoadBaseIndexedDisp(rl_object.reg, rl_offset.reg, 0, 0, rl_result.reg.GetLow(), 1432 rl_result.reg.GetHigh(), kLong, INVALID_SREG); 1433 } else { 1434 RegStorage rl_temp_offset = AllocTemp(); 1435 OpRegRegReg(kOpAdd, rl_temp_offset, rl_object.reg, rl_offset.reg); 1436 LoadBaseDispWide(rl_temp_offset, 0, rl_result.reg, INVALID_SREG); 1437 FreeTemp(rl_temp_offset.GetReg()); 1438 } 1439 } else { 1440 LoadBaseIndexed(rl_object.reg, rl_offset.reg, rl_result.reg, 0, kWord); 1441 } 1442 1443 if (is_volatile) { 1444 // Without context sensitive analysis, we must issue the most conservative barriers. 1445 // In this case, either a load or store may follow so we issue both barriers. 1446 GenMemBarrier(kLoadLoad); 1447 GenMemBarrier(kLoadStore); 1448 } 1449 1450 if (is_long) { 1451 StoreValueWide(rl_dest, rl_result); 1452 } else { 1453 StoreValue(rl_dest, rl_result); 1454 } 1455 return true; 1456} 1457 1458bool Mir2Lir::GenInlinedUnsafePut(CallInfo* info, bool is_long, 1459 bool is_object, bool is_volatile, bool is_ordered) { 1460 if (cu_->instruction_set == kMips) { 1461 // TODO - add Mips implementation 1462 return false; 1463 } 1464 // Unused - RegLocation rl_src_unsafe = info->args[0]; 1465 RegLocation rl_src_obj = info->args[1]; // Object 1466 RegLocation rl_src_offset = info->args[2]; // long low 1467 rl_src_offset = NarrowRegLoc(rl_src_offset); // ignore high half in info->args[3] 1468 RegLocation rl_src_value = info->args[4]; // value to store 1469 if (is_volatile || is_ordered) { 1470 // There might have been a store before this volatile one so insert StoreStore barrier. 1471 GenMemBarrier(kStoreStore); 1472 } 1473 RegLocation rl_object = LoadValue(rl_src_obj, kCoreReg); 1474 RegLocation rl_offset = LoadValue(rl_src_offset, kCoreReg); 1475 RegLocation rl_value; 1476 if (is_long) { 1477 rl_value = LoadValueWide(rl_src_value, kCoreReg); 1478 if (cu_->instruction_set == kX86) { 1479 StoreBaseIndexedDisp(rl_object.reg, rl_offset.reg, 0, 0, rl_value.reg.GetLow(), 1480 rl_value.reg.GetHigh(), kLong, INVALID_SREG); 1481 } else { 1482 RegStorage rl_temp_offset = AllocTemp(); 1483 OpRegRegReg(kOpAdd, rl_temp_offset, rl_object.reg, rl_offset.reg); 1484 StoreBaseDispWide(rl_temp_offset, 0, rl_value.reg); 1485 FreeTemp(rl_temp_offset.GetReg()); 1486 } 1487 } else { 1488 rl_value = LoadValue(rl_src_value, kCoreReg); 1489 StoreBaseIndexed(rl_object.reg, rl_offset.reg, rl_value.reg, 0, kWord); 1490 } 1491 1492 // Free up the temp early, to ensure x86 doesn't run out of temporaries in MarkGCCard. 1493 FreeTemp(rl_offset.reg.GetReg()); 1494 1495 if (is_volatile) { 1496 // A load might follow the volatile store so insert a StoreLoad barrier. 1497 GenMemBarrier(kStoreLoad); 1498 } 1499 if (is_object) { 1500 MarkGCCard(rl_value.reg, rl_object.reg); 1501 } 1502 return true; 1503} 1504 1505void Mir2Lir::GenInvoke(CallInfo* info) { 1506 if ((info->opt_flags & MIR_INLINED) != 0) { 1507 // Already inlined but we may still need the null check. 1508 if (info->type != kStatic && 1509 ((cu_->disable_opt & (1 << kNullCheckElimination)) != 0 || 1510 (info->opt_flags & MIR_IGNORE_NULL_CHECK) == 0)) { 1511 RegLocation rl_obj = LoadValue(info->args[0], kCoreReg); 1512 GenNullCheck(rl_obj.reg); 1513 } 1514 return; 1515 } 1516 DCHECK(cu_->compiler_driver->GetMethodInlinerMap() != nullptr); 1517 if (cu_->compiler_driver->GetMethodInlinerMap()->GetMethodInliner(cu_->dex_file) 1518 ->GenIntrinsic(this, info)) { 1519 return; 1520 } 1521 GenInvokeNoInline(info); 1522} 1523 1524void Mir2Lir::GenInvokeNoInline(CallInfo* info) { 1525 int call_state = 0; 1526 LIR* null_ck; 1527 LIR** p_null_ck = NULL; 1528 NextCallInsn next_call_insn; 1529 FlushAllRegs(); /* Everything to home location */ 1530 // Explicit register usage 1531 LockCallTemps(); 1532 1533 const MirMethodLoweringInfo& method_info = mir_graph_->GetMethodLoweringInfo(info->mir); 1534 cu_->compiler_driver->ProcessedInvoke(method_info.GetInvokeType(), method_info.StatsFlags()); 1535 InvokeType original_type = static_cast<InvokeType>(method_info.GetInvokeType()); 1536 info->type = static_cast<InvokeType>(method_info.GetSharpType()); 1537 bool fast_path = method_info.FastPath(); 1538 bool skip_this; 1539 if (info->type == kInterface) { 1540 next_call_insn = fast_path ? NextInterfaceCallInsn : NextInterfaceCallInsnWithAccessCheck; 1541 skip_this = fast_path; 1542 } else if (info->type == kDirect) { 1543 if (fast_path) { 1544 p_null_ck = &null_ck; 1545 } 1546 next_call_insn = fast_path ? NextSDCallInsn : NextDirectCallInsnSP; 1547 skip_this = false; 1548 } else if (info->type == kStatic) { 1549 next_call_insn = fast_path ? NextSDCallInsn : NextStaticCallInsnSP; 1550 skip_this = false; 1551 } else if (info->type == kSuper) { 1552 DCHECK(!fast_path); // Fast path is a direct call. 1553 next_call_insn = NextSuperCallInsnSP; 1554 skip_this = false; 1555 } else { 1556 DCHECK_EQ(info->type, kVirtual); 1557 next_call_insn = fast_path ? NextVCallInsn : NextVCallInsnSP; 1558 skip_this = fast_path; 1559 } 1560 MethodReference target_method = method_info.GetTargetMethod(); 1561 if (!info->is_range) { 1562 call_state = GenDalvikArgsNoRange(info, call_state, p_null_ck, 1563 next_call_insn, target_method, method_info.VTableIndex(), 1564 method_info.DirectCode(), method_info.DirectMethod(), 1565 original_type, skip_this); 1566 } else { 1567 call_state = GenDalvikArgsRange(info, call_state, p_null_ck, 1568 next_call_insn, target_method, method_info.VTableIndex(), 1569 method_info.DirectCode(), method_info.DirectMethod(), 1570 original_type, skip_this); 1571 } 1572 // Finish up any of the call sequence not interleaved in arg loading 1573 while (call_state >= 0) { 1574 call_state = next_call_insn(cu_, info, call_state, target_method, method_info.VTableIndex(), 1575 method_info.DirectCode(), method_info.DirectMethod(), original_type); 1576 } 1577 LIR* call_inst; 1578 if (cu_->instruction_set != kX86 && cu_->instruction_set != kX86_64) { 1579 call_inst = OpReg(kOpBlx, TargetReg(kInvokeTgt)); 1580 } else { 1581 if (fast_path) { 1582 if (method_info.DirectCode() == static_cast<uintptr_t>(-1)) { 1583 // We can have the linker fixup a call relative. 1584 call_inst = 1585 reinterpret_cast<X86Mir2Lir*>(this)->CallWithLinkerFixup(target_method, info->type); 1586 } else { 1587 call_inst = OpMem(kOpBlx, TargetReg(kArg0), 1588 mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value()); 1589 } 1590 } else { 1591 ThreadOffset<4> trampoline(-1); 1592 switch (info->type) { 1593 case kInterface: 1594 trampoline = QUICK_ENTRYPOINT_OFFSET(4, pInvokeInterfaceTrampolineWithAccessCheck); 1595 break; 1596 case kDirect: 1597 trampoline = QUICK_ENTRYPOINT_OFFSET(4, pInvokeDirectTrampolineWithAccessCheck); 1598 break; 1599 case kStatic: 1600 trampoline = QUICK_ENTRYPOINT_OFFSET(4, pInvokeStaticTrampolineWithAccessCheck); 1601 break; 1602 case kSuper: 1603 trampoline = QUICK_ENTRYPOINT_OFFSET(4, pInvokeSuperTrampolineWithAccessCheck); 1604 break; 1605 case kVirtual: 1606 trampoline = QUICK_ENTRYPOINT_OFFSET(4, pInvokeVirtualTrampolineWithAccessCheck); 1607 break; 1608 default: 1609 LOG(FATAL) << "Unexpected invoke type"; 1610 } 1611 call_inst = OpThreadMem(kOpBlx, trampoline); 1612 } 1613 } 1614 MarkSafepointPC(call_inst); 1615 1616 ClobberCallerSave(); 1617 if (info->result.location != kLocInvalid) { 1618 // We have a following MOVE_RESULT - do it now. 1619 if (info->result.wide) { 1620 RegLocation ret_loc = GetReturnWide(info->result.fp); 1621 StoreValueWide(info->result, ret_loc); 1622 } else { 1623 RegLocation ret_loc = GetReturn(info->result.fp); 1624 StoreValue(info->result, ret_loc); 1625 } 1626 } 1627} 1628 1629} // namespace art 1630