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