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