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