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