gen_invoke.cc revision bb8f0ab736b61db8f543e433859272e83f96ee9b
1/* 2 * Copyright (C) 2012 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include "dex/compiler_ir.h" 18#include "dex/frontend.h" 19#include "dex/quick/dex_file_method_inliner.h" 20#include "dex/quick/dex_file_to_method_inliner_map.h" 21#include "dex_file-inl.h" 22#include "entrypoints/quick/quick_entrypoints.h" 23#include "invoke_type.h" 24#include "mirror/array.h" 25#include "mirror/string.h" 26#include "mir_to_lir-inl.h" 27#include "x86/codegen_x86.h" 28 29namespace art { 30 31/* 32 * This source files contains "gen" codegen routines that should 33 * be applicable to most targets. Only mid-level support utilities 34 * and "op" calls may be used here. 35 */ 36 37/* 38 * To save scheduling time, helper calls are broken into two parts: generation of 39 * the helper target address, and the actuall call to the helper. Because x86 40 * has a memory call operation, part 1 is a NOP for x86. For other targets, 41 * load arguments between the two parts. 42 */ 43int Mir2Lir::CallHelperSetup(ThreadOffset helper_offset) { 44 return (cu_->instruction_set == kX86) ? 0 : LoadHelper(helper_offset); 45} 46 47/* NOTE: if r_tgt is a temp, it will be freed following use */ 48LIR* Mir2Lir::CallHelper(int r_tgt, ThreadOffset helper_offset, bool safepoint_pc) { 49 LIR* call_inst; 50 if (cu_->instruction_set == kX86) { 51 call_inst = OpThreadMem(kOpBlx, helper_offset); 52 } else { 53 call_inst = OpReg(kOpBlx, r_tgt); 54 FreeTemp(r_tgt); 55 } 56 if (safepoint_pc) { 57 MarkSafepointPC(call_inst); 58 } 59 return call_inst; 60} 61 62void Mir2Lir::CallRuntimeHelperImm(ThreadOffset helper_offset, int arg0, bool safepoint_pc) { 63 int r_tgt = CallHelperSetup(helper_offset); 64 LoadConstant(TargetReg(kArg0), arg0); 65 ClobberCallerSave(); 66 CallHelper(r_tgt, helper_offset, safepoint_pc); 67} 68 69void Mir2Lir::CallRuntimeHelperReg(ThreadOffset helper_offset, int arg0, bool safepoint_pc) { 70 int r_tgt = CallHelperSetup(helper_offset); 71 OpRegCopy(TargetReg(kArg0), arg0); 72 ClobberCallerSave(); 73 CallHelper(r_tgt, helper_offset, safepoint_pc); 74} 75 76void Mir2Lir::CallRuntimeHelperRegLocation(ThreadOffset helper_offset, RegLocation arg0, 77 bool safepoint_pc) { 78 int r_tgt = CallHelperSetup(helper_offset); 79 if (arg0.wide == 0) { 80 LoadValueDirectFixed(arg0, TargetReg(kArg0)); 81 } else { 82 LoadValueDirectWideFixed(arg0, TargetReg(kArg0), TargetReg(kArg1)); 83 } 84 ClobberCallerSave(); 85 CallHelper(r_tgt, helper_offset, safepoint_pc); 86} 87 88void Mir2Lir::CallRuntimeHelperImmImm(ThreadOffset helper_offset, int arg0, int arg1, 89 bool safepoint_pc) { 90 int r_tgt = CallHelperSetup(helper_offset); 91 LoadConstant(TargetReg(kArg0), arg0); 92 LoadConstant(TargetReg(kArg1), arg1); 93 ClobberCallerSave(); 94 CallHelper(r_tgt, helper_offset, safepoint_pc); 95} 96 97void Mir2Lir::CallRuntimeHelperImmRegLocation(ThreadOffset helper_offset, int arg0, 98 RegLocation arg1, bool safepoint_pc) { 99 int r_tgt = CallHelperSetup(helper_offset); 100 if (arg1.wide == 0) { 101 LoadValueDirectFixed(arg1, TargetReg(kArg1)); 102 } else { 103 LoadValueDirectWideFixed(arg1, TargetReg(kArg1), TargetReg(kArg2)); 104 } 105 LoadConstant(TargetReg(kArg0), arg0); 106 ClobberCallerSave(); 107 CallHelper(r_tgt, helper_offset, safepoint_pc); 108} 109 110void Mir2Lir::CallRuntimeHelperRegLocationImm(ThreadOffset helper_offset, RegLocation arg0, int arg1, 111 bool safepoint_pc) { 112 int r_tgt = CallHelperSetup(helper_offset); 113 LoadValueDirectFixed(arg0, TargetReg(kArg0)); 114 LoadConstant(TargetReg(kArg1), arg1); 115 ClobberCallerSave(); 116 CallHelper(r_tgt, helper_offset, safepoint_pc); 117} 118 119void Mir2Lir::CallRuntimeHelperImmReg(ThreadOffset helper_offset, int arg0, int arg1, 120 bool safepoint_pc) { 121 int r_tgt = CallHelperSetup(helper_offset); 122 OpRegCopy(TargetReg(kArg1), arg1); 123 LoadConstant(TargetReg(kArg0), arg0); 124 ClobberCallerSave(); 125 CallHelper(r_tgt, helper_offset, safepoint_pc); 126} 127 128void Mir2Lir::CallRuntimeHelperRegImm(ThreadOffset helper_offset, int arg0, int arg1, 129 bool safepoint_pc) { 130 int r_tgt = CallHelperSetup(helper_offset); 131 OpRegCopy(TargetReg(kArg0), arg0); 132 LoadConstant(TargetReg(kArg1), arg1); 133 ClobberCallerSave(); 134 CallHelper(r_tgt, helper_offset, safepoint_pc); 135} 136 137void Mir2Lir::CallRuntimeHelperImmMethod(ThreadOffset helper_offset, int arg0, bool safepoint_pc) { 138 int r_tgt = CallHelperSetup(helper_offset); 139 LoadCurrMethodDirect(TargetReg(kArg1)); 140 LoadConstant(TargetReg(kArg0), arg0); 141 ClobberCallerSave(); 142 CallHelper(r_tgt, helper_offset, safepoint_pc); 143} 144 145void Mir2Lir::CallRuntimeHelperRegMethod(ThreadOffset helper_offset, int arg0, bool safepoint_pc) { 146 int r_tgt = CallHelperSetup(helper_offset); 147 DCHECK_NE(TargetReg(kArg1), arg0); 148 if (TargetReg(kArg0) != arg0) { 149 OpRegCopy(TargetReg(kArg0), arg0); 150 } 151 LoadCurrMethodDirect(TargetReg(kArg1)); 152 ClobberCallerSave(); 153 CallHelper(r_tgt, helper_offset, safepoint_pc); 154} 155 156void Mir2Lir::CallRuntimeHelperRegMethodRegLocation(ThreadOffset helper_offset, int arg0, 157 RegLocation arg2, bool safepoint_pc) { 158 int r_tgt = CallHelperSetup(helper_offset); 159 DCHECK_NE(TargetReg(kArg1), arg0); 160 if (TargetReg(kArg0) != arg0) { 161 OpRegCopy(TargetReg(kArg0), arg0); 162 } 163 LoadCurrMethodDirect(TargetReg(kArg1)); 164 LoadValueDirectFixed(arg2, TargetReg(kArg2)); 165 ClobberCallerSave(); 166 CallHelper(r_tgt, helper_offset, safepoint_pc); 167} 168 169void Mir2Lir::CallRuntimeHelperRegLocationRegLocation(ThreadOffset helper_offset, RegLocation arg0, 170 RegLocation arg1, bool safepoint_pc) { 171 int r_tgt = CallHelperSetup(helper_offset); 172 if (arg0.wide == 0) { 173 LoadValueDirectFixed(arg0, arg0.fp ? TargetReg(kFArg0) : TargetReg(kArg0)); 174 if (arg1.wide == 0) { 175 if (cu_->instruction_set == kMips) { 176 LoadValueDirectFixed(arg1, arg1.fp ? TargetReg(kFArg2) : TargetReg(kArg1)); 177 } else { 178 LoadValueDirectFixed(arg1, TargetReg(kArg1)); 179 } 180 } else { 181 if (cu_->instruction_set == kMips) { 182 LoadValueDirectWideFixed(arg1, arg1.fp ? TargetReg(kFArg2) : TargetReg(kArg1), arg1.fp ? TargetReg(kFArg3) : TargetReg(kArg2)); 183 } else { 184 LoadValueDirectWideFixed(arg1, TargetReg(kArg1), TargetReg(kArg2)); 185 } 186 } 187 } else { 188 LoadValueDirectWideFixed(arg0, arg0.fp ? TargetReg(kFArg0) : TargetReg(kArg0), arg0.fp ? TargetReg(kFArg1) : TargetReg(kArg1)); 189 if (arg1.wide == 0) { 190 LoadValueDirectFixed(arg1, arg1.fp ? TargetReg(kFArg2) : TargetReg(kArg2)); 191 } else { 192 LoadValueDirectWideFixed(arg1, arg1.fp ? TargetReg(kFArg2) : TargetReg(kArg2), arg1.fp ? TargetReg(kFArg3) : TargetReg(kArg3)); 193 } 194 } 195 ClobberCallerSave(); 196 CallHelper(r_tgt, helper_offset, safepoint_pc); 197} 198 199void Mir2Lir::CallRuntimeHelperRegReg(ThreadOffset helper_offset, int arg0, int arg1, 200 bool safepoint_pc) { 201 int r_tgt = CallHelperSetup(helper_offset); 202 DCHECK_NE(TargetReg(kArg0), arg1); // check copy into arg0 won't clobber arg1 203 OpRegCopy(TargetReg(kArg0), arg0); 204 OpRegCopy(TargetReg(kArg1), arg1); 205 ClobberCallerSave(); 206 CallHelper(r_tgt, helper_offset, safepoint_pc); 207} 208 209void Mir2Lir::CallRuntimeHelperRegRegImm(ThreadOffset helper_offset, int arg0, int arg1, 210 int arg2, bool safepoint_pc) { 211 int r_tgt = CallHelperSetup(helper_offset); 212 DCHECK_NE(TargetReg(kArg0), arg1); // check copy into arg0 won't clobber arg1 213 OpRegCopy(TargetReg(kArg0), arg0); 214 OpRegCopy(TargetReg(kArg1), arg1); 215 LoadConstant(TargetReg(kArg2), arg2); 216 ClobberCallerSave(); 217 CallHelper(r_tgt, helper_offset, safepoint_pc); 218} 219 220void Mir2Lir::CallRuntimeHelperImmMethodRegLocation(ThreadOffset helper_offset, 221 int arg0, RegLocation arg2, bool safepoint_pc) { 222 int r_tgt = CallHelperSetup(helper_offset); 223 LoadValueDirectFixed(arg2, TargetReg(kArg2)); 224 LoadCurrMethodDirect(TargetReg(kArg1)); 225 LoadConstant(TargetReg(kArg0), arg0); 226 ClobberCallerSave(); 227 CallHelper(r_tgt, helper_offset, safepoint_pc); 228} 229 230void Mir2Lir::CallRuntimeHelperImmMethodImm(ThreadOffset helper_offset, int arg0, 231 int arg2, bool safepoint_pc) { 232 int r_tgt = CallHelperSetup(helper_offset); 233 LoadCurrMethodDirect(TargetReg(kArg1)); 234 LoadConstant(TargetReg(kArg2), arg2); 235 LoadConstant(TargetReg(kArg0), arg0); 236 ClobberCallerSave(); 237 CallHelper(r_tgt, helper_offset, safepoint_pc); 238} 239 240void Mir2Lir::CallRuntimeHelperImmRegLocationRegLocation(ThreadOffset helper_offset, 241 int arg0, RegLocation arg1, 242 RegLocation arg2, bool safepoint_pc) { 243 int r_tgt = CallHelperSetup(helper_offset); 244 DCHECK_EQ(arg1.wide, 0U); 245 LoadValueDirectFixed(arg1, TargetReg(kArg1)); 246 if (arg2.wide == 0) { 247 LoadValueDirectFixed(arg2, TargetReg(kArg2)); 248 } else { 249 LoadValueDirectWideFixed(arg2, TargetReg(kArg2), TargetReg(kArg3)); 250 } 251 LoadConstant(TargetReg(kArg0), arg0); 252 ClobberCallerSave(); 253 CallHelper(r_tgt, helper_offset, safepoint_pc); 254} 255 256void Mir2Lir::CallRuntimeHelperRegLocationRegLocationRegLocation(ThreadOffset helper_offset, 257 RegLocation arg0, RegLocation arg1, 258 RegLocation arg2, 259 bool safepoint_pc) { 260 int r_tgt = CallHelperSetup(helper_offset); 261 DCHECK_EQ(arg0.wide, 0U); 262 LoadValueDirectFixed(arg0, TargetReg(kArg0)); 263 DCHECK_EQ(arg1.wide, 0U); 264 LoadValueDirectFixed(arg1, TargetReg(kArg1)); 265 DCHECK_EQ(arg1.wide, 0U); 266 LoadValueDirectFixed(arg2, TargetReg(kArg2)); 267 ClobberCallerSave(); 268 CallHelper(r_tgt, helper_offset, safepoint_pc); 269} 270 271/* 272 * If there are any ins passed in registers that have not been promoted 273 * to a callee-save register, flush them to the frame. Perform intial 274 * assignment of promoted arguments. 275 * 276 * ArgLocs is an array of location records describing the incoming arguments 277 * with one location record per word of argument. 278 */ 279void Mir2Lir::FlushIns(RegLocation* ArgLocs, RegLocation rl_method) { 280 /* 281 * Dummy up a RegLocation for the incoming Method* 282 * It will attempt to keep kArg0 live (or copy it to home location 283 * if promoted). 284 */ 285 RegLocation rl_src = rl_method; 286 rl_src.location = kLocPhysReg; 287 rl_src.low_reg = TargetReg(kArg0); 288 rl_src.home = false; 289 MarkLive(rl_src.low_reg, rl_src.s_reg_low); 290 StoreValue(rl_method, rl_src); 291 // If Method* has been promoted, explicitly flush 292 if (rl_method.location == kLocPhysReg) { 293 StoreWordDisp(TargetReg(kSp), 0, TargetReg(kArg0)); 294 } 295 296 if (cu_->num_ins == 0) 297 return; 298 const int num_arg_regs = 3; 299 static SpecialTargetRegister arg_regs[] = {kArg1, kArg2, kArg3}; 300 int start_vreg = cu_->num_dalvik_registers - cu_->num_ins; 301 /* 302 * Copy incoming arguments to their proper home locations. 303 * NOTE: an older version of dx had an issue in which 304 * it would reuse static method argument registers. 305 * This could result in the same Dalvik virtual register 306 * being promoted to both core and fp regs. To account for this, 307 * we only copy to the corresponding promoted physical register 308 * if it matches the type of the SSA name for the incoming 309 * argument. It is also possible that long and double arguments 310 * end up half-promoted. In those cases, we must flush the promoted 311 * half to memory as well. 312 */ 313 for (int i = 0; i < cu_->num_ins; i++) { 314 PromotionMap* v_map = &promotion_map_[start_vreg + i]; 315 if (i < num_arg_regs) { 316 // If arriving in register 317 bool need_flush = true; 318 RegLocation* t_loc = &ArgLocs[i]; 319 if ((v_map->core_location == kLocPhysReg) && !t_loc->fp) { 320 OpRegCopy(v_map->core_reg, TargetReg(arg_regs[i])); 321 need_flush = false; 322 } else if ((v_map->fp_location == kLocPhysReg) && t_loc->fp) { 323 OpRegCopy(v_map->FpReg, TargetReg(arg_regs[i])); 324 need_flush = false; 325 } else { 326 need_flush = true; 327 } 328 329 // For wide args, force flush if not fully promoted 330 if (t_loc->wide) { 331 PromotionMap* p_map = v_map + (t_loc->high_word ? -1 : +1); 332 // Is only half promoted? 333 need_flush |= (p_map->core_location != v_map->core_location) || 334 (p_map->fp_location != v_map->fp_location); 335 if ((cu_->instruction_set == kThumb2) && t_loc->fp && !need_flush) { 336 /* 337 * In Arm, a double is represented as a pair of consecutive single float 338 * registers starting at an even number. It's possible that both Dalvik vRegs 339 * representing the incoming double were independently promoted as singles - but 340 * not in a form usable as a double. If so, we need to flush - even though the 341 * incoming arg appears fully in register. At this point in the code, both 342 * halves of the double are promoted. Make sure they are in a usable form. 343 */ 344 int lowreg_index = start_vreg + i + (t_loc->high_word ? -1 : 0); 345 int low_reg = promotion_map_[lowreg_index].FpReg; 346 int high_reg = promotion_map_[lowreg_index + 1].FpReg; 347 if (((low_reg & 0x1) != 0) || (high_reg != (low_reg + 1))) { 348 need_flush = true; 349 } 350 } 351 } 352 if (need_flush) { 353 StoreBaseDisp(TargetReg(kSp), SRegOffset(start_vreg + i), 354 TargetReg(arg_regs[i]), kWord); 355 } 356 } else { 357 // If arriving in frame & promoted 358 if (v_map->core_location == kLocPhysReg) { 359 LoadWordDisp(TargetReg(kSp), SRegOffset(start_vreg + i), 360 v_map->core_reg); 361 } 362 if (v_map->fp_location == kLocPhysReg) { 363 LoadWordDisp(TargetReg(kSp), SRegOffset(start_vreg + i), 364 v_map->FpReg); 365 } 366 } 367 } 368} 369 370/* 371 * Bit of a hack here - in the absence of a real scheduling pass, 372 * emit the next instruction in static & direct invoke sequences. 373 */ 374static int NextSDCallInsn(CompilationUnit* cu, CallInfo* info, 375 int state, const MethodReference& target_method, 376 uint32_t unused, 377 uintptr_t direct_code, uintptr_t direct_method, 378 InvokeType type) { 379 Mir2Lir* cg = static_cast<Mir2Lir*>(cu->cg.get()); 380 if (direct_code != 0 && direct_method != 0) { 381 switch (state) { 382 case 0: // Get the current Method* [sets kArg0] 383 if (direct_code != static_cast<unsigned int>(-1)) { 384 if (cu->instruction_set != kX86) { 385 cg->LoadConstant(cg->TargetReg(kInvokeTgt), direct_code); 386 } 387 } else { 388 CHECK_EQ(cu->dex_file, target_method.dex_file); 389 LIR* data_target = cg->ScanLiteralPool(cg->code_literal_list_, 390 target_method.dex_method_index, 0); 391 if (data_target == NULL) { 392 data_target = cg->AddWordData(&cg->code_literal_list_, target_method.dex_method_index); 393 data_target->operands[1] = type; 394 } 395 LIR* load_pc_rel = cg->OpPcRelLoad(cg->TargetReg(kInvokeTgt), data_target); 396 cg->AppendLIR(load_pc_rel); 397 DCHECK_EQ(cu->instruction_set, kThumb2) << reinterpret_cast<void*>(data_target); 398 } 399 if (direct_method != static_cast<unsigned int>(-1)) { 400 cg->LoadConstant(cg->TargetReg(kArg0), direct_method); 401 } else { 402 CHECK_EQ(cu->dex_file, target_method.dex_file); 403 LIR* data_target = cg->ScanLiteralPool(cg->method_literal_list_, 404 target_method.dex_method_index, 0); 405 if (data_target == NULL) { 406 data_target = cg->AddWordData(&cg->method_literal_list_, target_method.dex_method_index); 407 data_target->operands[1] = type; 408 } 409 LIR* load_pc_rel = cg->OpPcRelLoad(cg->TargetReg(kArg0), data_target); 410 cg->AppendLIR(load_pc_rel); 411 DCHECK_EQ(cu->instruction_set, kThumb2) << reinterpret_cast<void*>(data_target); 412 } 413 break; 414 default: 415 return -1; 416 } 417 } else { 418 switch (state) { 419 case 0: // Get the current Method* [sets kArg0] 420 // TUNING: we can save a reg copy if Method* has been promoted. 421 cg->LoadCurrMethodDirect(cg->TargetReg(kArg0)); 422 break; 423 case 1: // Get method->dex_cache_resolved_methods_ 424 cg->LoadWordDisp(cg->TargetReg(kArg0), 425 mirror::ArtMethod::DexCacheResolvedMethodsOffset().Int32Value(), cg->TargetReg(kArg0)); 426 // Set up direct code if known. 427 if (direct_code != 0) { 428 if (direct_code != static_cast<unsigned int>(-1)) { 429 cg->LoadConstant(cg->TargetReg(kInvokeTgt), direct_code); 430 } else { 431 CHECK_EQ(cu->dex_file, target_method.dex_file); 432 CHECK_LT(target_method.dex_method_index, target_method.dex_file->NumMethodIds()); 433 LIR* data_target = cg->ScanLiteralPool(cg->code_literal_list_, 434 target_method.dex_method_index, 0); 435 if (data_target == NULL) { 436 data_target = cg->AddWordData(&cg->code_literal_list_, target_method.dex_method_index); 437 data_target->operands[1] = type; 438 } 439 LIR* load_pc_rel = cg->OpPcRelLoad(cg->TargetReg(kInvokeTgt), data_target); 440 cg->AppendLIR(load_pc_rel); 441 DCHECK_EQ(cu->instruction_set, kThumb2) << reinterpret_cast<void*>(data_target); 442 } 443 } 444 break; 445 case 2: // Grab target method* 446 CHECK_EQ(cu->dex_file, target_method.dex_file); 447 cg->LoadWordDisp(cg->TargetReg(kArg0), 448 mirror::Array::DataOffset(sizeof(mirror::Object*)).Int32Value() + 449 (target_method.dex_method_index * 4), 450 cg-> TargetReg(kArg0)); 451 break; 452 case 3: // Grab the code from the method* 453 if (cu->instruction_set != kX86) { 454 if (direct_code == 0) { 455 cg->LoadWordDisp(cg->TargetReg(kArg0), 456 mirror::ArtMethod::GetEntryPointFromCompiledCodeOffset().Int32Value(), 457 cg->TargetReg(kInvokeTgt)); 458 } 459 break; 460 } 461 // Intentional fallthrough for x86 462 default: 463 return -1; 464 } 465 } 466 return state + 1; 467} 468 469/* 470 * Bit of a hack here - in the absence of a real scheduling pass, 471 * emit the next instruction in a virtual invoke sequence. 472 * We can use kLr as a temp prior to target address loading 473 * Note also that we'll load the first argument ("this") into 474 * kArg1 here rather than the standard LoadArgRegs. 475 */ 476static int NextVCallInsn(CompilationUnit* cu, CallInfo* info, 477 int state, const MethodReference& target_method, 478 uint32_t method_idx, uintptr_t unused, uintptr_t unused2, 479 InvokeType unused3) { 480 Mir2Lir* cg = static_cast<Mir2Lir*>(cu->cg.get()); 481 /* 482 * This is the fast path in which the target virtual method is 483 * fully resolved at compile time. 484 */ 485 switch (state) { 486 case 0: { // Get "this" [set kArg1] 487 RegLocation rl_arg = info->args[0]; 488 cg->LoadValueDirectFixed(rl_arg, cg->TargetReg(kArg1)); 489 break; 490 } 491 case 1: // Is "this" null? [use kArg1] 492 cg->GenNullCheck(info->args[0].s_reg_low, cg->TargetReg(kArg1), info->opt_flags); 493 // get this->klass_ [use kArg1, set kInvokeTgt] 494 cg->LoadWordDisp(cg->TargetReg(kArg1), mirror::Object::ClassOffset().Int32Value(), 495 cg->TargetReg(kInvokeTgt)); 496 break; 497 case 2: // Get this->klass_->vtable [usr kInvokeTgt, set kInvokeTgt] 498 cg->LoadWordDisp(cg->TargetReg(kInvokeTgt), mirror::Class::VTableOffset().Int32Value(), 499 cg->TargetReg(kInvokeTgt)); 500 break; 501 case 3: // Get target method [use kInvokeTgt, set kArg0] 502 cg->LoadWordDisp(cg->TargetReg(kInvokeTgt), (method_idx * 4) + 503 mirror::Array::DataOffset(sizeof(mirror::Object*)).Int32Value(), 504 cg->TargetReg(kArg0)); 505 break; 506 case 4: // Get the compiled code address [uses kArg0, sets kInvokeTgt] 507 if (cu->instruction_set != kX86) { 508 cg->LoadWordDisp(cg->TargetReg(kArg0), 509 mirror::ArtMethod::GetEntryPointFromCompiledCodeOffset().Int32Value(), 510 cg->TargetReg(kInvokeTgt)); 511 break; 512 } 513 // Intentional fallthrough for X86 514 default: 515 return -1; 516 } 517 return state + 1; 518} 519 520/* 521 * Emit the next instruction in an invoke interface sequence. This will do a lookup in the 522 * class's IMT, calling either the actual method or art_quick_imt_conflict_trampoline if 523 * more than one interface method map to the same index. Note also that we'll load the first 524 * argument ("this") into kArg1 here rather than the standard LoadArgRegs. 525 */ 526static int NextInterfaceCallInsn(CompilationUnit* cu, CallInfo* info, int state, 527 const MethodReference& target_method, 528 uint32_t method_idx, uintptr_t unused, 529 uintptr_t direct_method, InvokeType unused2) { 530 Mir2Lir* cg = static_cast<Mir2Lir*>(cu->cg.get()); 531 532 switch (state) { 533 case 0: // Set target method index in case of conflict [set kHiddenArg, kHiddenFpArg (x86)] 534 CHECK_EQ(cu->dex_file, target_method.dex_file); 535 CHECK_LT(target_method.dex_method_index, target_method.dex_file->NumMethodIds()); 536 cg->LoadConstant(cg->TargetReg(kHiddenArg), target_method.dex_method_index); 537 if (cu->instruction_set == kX86) { 538 cg->OpRegCopy(cg->TargetReg(kHiddenFpArg), cg->TargetReg(kHiddenArg)); 539 } 540 break; 541 case 1: { // Get "this" [set kArg1] 542 RegLocation rl_arg = info->args[0]; 543 cg->LoadValueDirectFixed(rl_arg, cg->TargetReg(kArg1)); 544 break; 545 } 546 case 2: // Is "this" null? [use kArg1] 547 cg->GenNullCheck(info->args[0].s_reg_low, cg->TargetReg(kArg1), info->opt_flags); 548 // Get this->klass_ [use kArg1, set kInvokeTgt] 549 cg->LoadWordDisp(cg->TargetReg(kArg1), mirror::Object::ClassOffset().Int32Value(), 550 cg->TargetReg(kInvokeTgt)); 551 break; 552 case 3: // Get this->klass_->imtable [use kInvokeTgt, set kInvokeTgt] 553 cg->LoadWordDisp(cg->TargetReg(kInvokeTgt), mirror::Class::ImTableOffset().Int32Value(), 554 cg->TargetReg(kInvokeTgt)); 555 break; 556 case 4: // Get target method [use kInvokeTgt, set kArg0] 557 cg->LoadWordDisp(cg->TargetReg(kInvokeTgt), ((method_idx % ClassLinker::kImtSize) * 4) + 558 mirror::Array::DataOffset(sizeof(mirror::Object*)).Int32Value(), 559 cg->TargetReg(kArg0)); 560 break; 561 case 5: // Get the compiled code address [use kArg0, set kInvokeTgt] 562 if (cu->instruction_set != kX86) { 563 cg->LoadWordDisp(cg->TargetReg(kArg0), 564 mirror::ArtMethod::GetEntryPointFromCompiledCodeOffset().Int32Value(), 565 cg->TargetReg(kInvokeTgt)); 566 break; 567 } 568 // Intentional fallthrough for X86 569 default: 570 return -1; 571 } 572 return state + 1; 573} 574 575static int NextInvokeInsnSP(CompilationUnit* cu, CallInfo* info, ThreadOffset trampoline, 576 int state, const MethodReference& target_method, 577 uint32_t method_idx) { 578 Mir2Lir* cg = static_cast<Mir2Lir*>(cu->cg.get()); 579 /* 580 * This handles the case in which the base method is not fully 581 * resolved at compile time, we bail to a runtime helper. 582 */ 583 if (state == 0) { 584 if (cu->instruction_set != kX86) { 585 // Load trampoline target 586 cg->LoadWordDisp(cg->TargetReg(kSelf), trampoline.Int32Value(), cg->TargetReg(kInvokeTgt)); 587 } 588 // Load kArg0 with method index 589 CHECK_EQ(cu->dex_file, target_method.dex_file); 590 cg->LoadConstant(cg->TargetReg(kArg0), target_method.dex_method_index); 591 return 1; 592 } 593 return -1; 594} 595 596static int NextStaticCallInsnSP(CompilationUnit* cu, CallInfo* info, 597 int state, 598 const MethodReference& target_method, 599 uint32_t method_idx, 600 uintptr_t unused, uintptr_t unused2, 601 InvokeType unused3) { 602 ThreadOffset trampoline = QUICK_ENTRYPOINT_OFFSET(pInvokeStaticTrampolineWithAccessCheck); 603 return NextInvokeInsnSP(cu, info, trampoline, state, target_method, 0); 604} 605 606static int NextDirectCallInsnSP(CompilationUnit* cu, CallInfo* info, int state, 607 const MethodReference& target_method, 608 uint32_t method_idx, uintptr_t unused, 609 uintptr_t unused2, InvokeType unused3) { 610 ThreadOffset trampoline = QUICK_ENTRYPOINT_OFFSET(pInvokeDirectTrampolineWithAccessCheck); 611 return NextInvokeInsnSP(cu, info, trampoline, state, target_method, 0); 612} 613 614static int NextSuperCallInsnSP(CompilationUnit* cu, CallInfo* info, int state, 615 const MethodReference& target_method, 616 uint32_t method_idx, uintptr_t unused, 617 uintptr_t unused2, InvokeType unused3) { 618 ThreadOffset trampoline = QUICK_ENTRYPOINT_OFFSET(pInvokeSuperTrampolineWithAccessCheck); 619 return NextInvokeInsnSP(cu, info, trampoline, state, target_method, 0); 620} 621 622static int NextVCallInsnSP(CompilationUnit* cu, CallInfo* info, int state, 623 const MethodReference& target_method, 624 uint32_t method_idx, uintptr_t unused, 625 uintptr_t unused2, InvokeType unused3) { 626 ThreadOffset trampoline = QUICK_ENTRYPOINT_OFFSET(pInvokeVirtualTrampolineWithAccessCheck); 627 return NextInvokeInsnSP(cu, info, trampoline, state, target_method, 0); 628} 629 630static int NextInterfaceCallInsnWithAccessCheck(CompilationUnit* cu, 631 CallInfo* info, int state, 632 const MethodReference& target_method, 633 uint32_t unused, 634 uintptr_t unused2, uintptr_t unused3, 635 InvokeType unused4) { 636 ThreadOffset trampoline = QUICK_ENTRYPOINT_OFFSET(pInvokeInterfaceTrampolineWithAccessCheck); 637 return NextInvokeInsnSP(cu, info, trampoline, state, target_method, 0); 638} 639 640int Mir2Lir::LoadArgRegs(CallInfo* info, int call_state, 641 NextCallInsn next_call_insn, 642 const MethodReference& target_method, 643 uint32_t vtable_idx, uintptr_t direct_code, 644 uintptr_t direct_method, InvokeType type, bool skip_this) { 645 int last_arg_reg = TargetReg(kArg3); 646 int next_reg = TargetReg(kArg1); 647 int next_arg = 0; 648 if (skip_this) { 649 next_reg++; 650 next_arg++; 651 } 652 for (; (next_reg <= last_arg_reg) && (next_arg < info->num_arg_words); next_reg++) { 653 RegLocation rl_arg = info->args[next_arg++]; 654 rl_arg = UpdateRawLoc(rl_arg); 655 if (rl_arg.wide && (next_reg <= TargetReg(kArg2))) { 656 LoadValueDirectWideFixed(rl_arg, next_reg, next_reg + 1); 657 next_reg++; 658 next_arg++; 659 } else { 660 if (rl_arg.wide) { 661 rl_arg.wide = false; 662 rl_arg.is_const = false; 663 } 664 LoadValueDirectFixed(rl_arg, next_reg); 665 } 666 call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx, 667 direct_code, direct_method, type); 668 } 669 return call_state; 670} 671 672/* 673 * Load up to 5 arguments, the first three of which will be in 674 * kArg1 .. kArg3. On entry kArg0 contains the current method pointer, 675 * and as part of the load sequence, it must be replaced with 676 * the target method pointer. Note, this may also be called 677 * for "range" variants if the number of arguments is 5 or fewer. 678 */ 679int Mir2Lir::GenDalvikArgsNoRange(CallInfo* info, 680 int call_state, LIR** pcrLabel, NextCallInsn next_call_insn, 681 const MethodReference& target_method, 682 uint32_t vtable_idx, uintptr_t direct_code, 683 uintptr_t direct_method, InvokeType type, bool skip_this) { 684 RegLocation rl_arg; 685 686 /* If no arguments, just return */ 687 if (info->num_arg_words == 0) 688 return call_state; 689 690 call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx, 691 direct_code, direct_method, type); 692 693 DCHECK_LE(info->num_arg_words, 5); 694 if (info->num_arg_words > 3) { 695 int32_t next_use = 3; 696 // Detect special case of wide arg spanning arg3/arg4 697 RegLocation rl_use0 = info->args[0]; 698 RegLocation rl_use1 = info->args[1]; 699 RegLocation rl_use2 = info->args[2]; 700 if (((!rl_use0.wide && !rl_use1.wide) || rl_use0.wide) && 701 rl_use2.wide) { 702 int reg = -1; 703 // Wide spans, we need the 2nd half of uses[2]. 704 rl_arg = UpdateLocWide(rl_use2); 705 if (rl_arg.location == kLocPhysReg) { 706 reg = rl_arg.high_reg; 707 } else { 708 // kArg2 & rArg3 can safely be used here 709 reg = TargetReg(kArg3); 710 LoadWordDisp(TargetReg(kSp), SRegOffset(rl_arg.s_reg_low) + 4, reg); 711 call_state = next_call_insn(cu_, info, call_state, target_method, 712 vtable_idx, direct_code, direct_method, type); 713 } 714 StoreBaseDisp(TargetReg(kSp), (next_use + 1) * 4, reg, kWord); 715 StoreBaseDisp(TargetReg(kSp), 16 /* (3+1)*4 */, reg, kWord); 716 call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx, 717 direct_code, direct_method, type); 718 next_use++; 719 } 720 // Loop through the rest 721 while (next_use < info->num_arg_words) { 722 int low_reg; 723 int high_reg = -1; 724 rl_arg = info->args[next_use]; 725 rl_arg = UpdateRawLoc(rl_arg); 726 if (rl_arg.location == kLocPhysReg) { 727 low_reg = rl_arg.low_reg; 728 high_reg = rl_arg.high_reg; 729 } else { 730 low_reg = TargetReg(kArg2); 731 if (rl_arg.wide) { 732 high_reg = TargetReg(kArg3); 733 LoadValueDirectWideFixed(rl_arg, low_reg, high_reg); 734 } else { 735 LoadValueDirectFixed(rl_arg, low_reg); 736 } 737 call_state = next_call_insn(cu_, info, call_state, target_method, 738 vtable_idx, direct_code, direct_method, type); 739 } 740 int outs_offset = (next_use + 1) * 4; 741 if (rl_arg.wide) { 742 StoreBaseDispWide(TargetReg(kSp), outs_offset, low_reg, high_reg); 743 next_use += 2; 744 } else { 745 StoreWordDisp(TargetReg(kSp), outs_offset, low_reg); 746 next_use++; 747 } 748 call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx, 749 direct_code, direct_method, type); 750 } 751 } 752 753 call_state = LoadArgRegs(info, call_state, next_call_insn, 754 target_method, vtable_idx, direct_code, direct_method, 755 type, skip_this); 756 757 if (pcrLabel) { 758 *pcrLabel = GenNullCheck(info->args[0].s_reg_low, TargetReg(kArg1), info->opt_flags); 759 } 760 return call_state; 761} 762 763/* 764 * May have 0+ arguments (also used for jumbo). Note that 765 * source virtual registers may be in physical registers, so may 766 * need to be flushed to home location before copying. This 767 * applies to arg3 and above (see below). 768 * 769 * Two general strategies: 770 * If < 20 arguments 771 * Pass args 3-18 using vldm/vstm block copy 772 * Pass arg0, arg1 & arg2 in kArg1-kArg3 773 * If 20+ arguments 774 * Pass args arg19+ using memcpy block copy 775 * Pass arg0, arg1 & arg2 in kArg1-kArg3 776 * 777 */ 778int Mir2Lir::GenDalvikArgsRange(CallInfo* info, int call_state, 779 LIR** pcrLabel, NextCallInsn next_call_insn, 780 const MethodReference& target_method, 781 uint32_t vtable_idx, uintptr_t direct_code, uintptr_t direct_method, 782 InvokeType type, bool skip_this) { 783 // If we can treat it as non-range (Jumbo ops will use range form) 784 if (info->num_arg_words <= 5) 785 return GenDalvikArgsNoRange(info, call_state, pcrLabel, 786 next_call_insn, target_method, vtable_idx, 787 direct_code, direct_method, type, skip_this); 788 /* 789 * First load the non-register arguments. Both forms expect all 790 * of the source arguments to be in their home frame location, so 791 * scan the s_reg names and flush any that have been promoted to 792 * frame backing storage. 793 */ 794 // Scan the rest of the args - if in phys_reg flush to memory 795 for (int next_arg = 0; next_arg < info->num_arg_words;) { 796 RegLocation loc = info->args[next_arg]; 797 if (loc.wide) { 798 loc = UpdateLocWide(loc); 799 if ((next_arg >= 2) && (loc.location == kLocPhysReg)) { 800 StoreBaseDispWide(TargetReg(kSp), SRegOffset(loc.s_reg_low), 801 loc.low_reg, loc.high_reg); 802 } 803 next_arg += 2; 804 } else { 805 loc = UpdateLoc(loc); 806 if ((next_arg >= 3) && (loc.location == kLocPhysReg)) { 807 StoreBaseDisp(TargetReg(kSp), SRegOffset(loc.s_reg_low), 808 loc.low_reg, kWord); 809 } 810 next_arg++; 811 } 812 } 813 814 int start_offset = SRegOffset(info->args[3].s_reg_low); 815 int outs_offset = 4 /* Method* */ + (3 * 4); 816 if (cu_->instruction_set != kThumb2) { 817 // Generate memcpy 818 OpRegRegImm(kOpAdd, TargetReg(kArg0), TargetReg(kSp), outs_offset); 819 OpRegRegImm(kOpAdd, TargetReg(kArg1), TargetReg(kSp), start_offset); 820 CallRuntimeHelperRegRegImm(QUICK_ENTRYPOINT_OFFSET(pMemcpy), TargetReg(kArg0), 821 TargetReg(kArg1), (info->num_arg_words - 3) * 4, false); 822 } else { 823 if (info->num_arg_words >= 20) { 824 // Generate memcpy 825 OpRegRegImm(kOpAdd, TargetReg(kArg0), TargetReg(kSp), outs_offset); 826 OpRegRegImm(kOpAdd, TargetReg(kArg1), TargetReg(kSp), start_offset); 827 CallRuntimeHelperRegRegImm(QUICK_ENTRYPOINT_OFFSET(pMemcpy), TargetReg(kArg0), 828 TargetReg(kArg1), (info->num_arg_words - 3) * 4, false); 829 } else { 830 // Use vldm/vstm pair using kArg3 as a temp 831 int regs_left = std::min(info->num_arg_words - 3, 16); 832 call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx, 833 direct_code, direct_method, type); 834 OpRegRegImm(kOpAdd, TargetReg(kArg3), TargetReg(kSp), start_offset); 835 LIR* ld = OpVldm(TargetReg(kArg3), regs_left); 836 // TUNING: loosen barrier 837 ld->u.m.def_mask = ENCODE_ALL; 838 SetMemRefType(ld, true /* is_load */, kDalvikReg); 839 call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx, 840 direct_code, direct_method, type); 841 OpRegRegImm(kOpAdd, TargetReg(kArg3), TargetReg(kSp), 4 /* Method* */ + (3 * 4)); 842 call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx, 843 direct_code, direct_method, type); 844 LIR* st = OpVstm(TargetReg(kArg3), regs_left); 845 SetMemRefType(st, false /* is_load */, kDalvikReg); 846 st->u.m.def_mask = ENCODE_ALL; 847 call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx, 848 direct_code, direct_method, type); 849 } 850 } 851 852 call_state = LoadArgRegs(info, call_state, next_call_insn, 853 target_method, vtable_idx, direct_code, direct_method, 854 type, skip_this); 855 856 call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx, 857 direct_code, direct_method, type); 858 if (pcrLabel) { 859 *pcrLabel = GenNullCheck(info->args[0].s_reg_low, TargetReg(kArg1), info->opt_flags); 860 } 861 return call_state; 862} 863 864RegLocation Mir2Lir::InlineTarget(CallInfo* info) { 865 RegLocation res; 866 if (info->result.location == kLocInvalid) { 867 res = GetReturn(false); 868 } else { 869 res = info->result; 870 } 871 return res; 872} 873 874RegLocation Mir2Lir::InlineTargetWide(CallInfo* info) { 875 RegLocation res; 876 if (info->result.location == kLocInvalid) { 877 res = GetReturnWide(false); 878 } else { 879 res = info->result; 880 } 881 return res; 882} 883 884bool Mir2Lir::GenInlinedCharAt(CallInfo* info) { 885 if (cu_->instruction_set == kMips) { 886 // TODO - add Mips implementation 887 return false; 888 } 889 // Location of reference to data array 890 int value_offset = mirror::String::ValueOffset().Int32Value(); 891 // Location of count 892 int count_offset = mirror::String::CountOffset().Int32Value(); 893 // Starting offset within data array 894 int offset_offset = mirror::String::OffsetOffset().Int32Value(); 895 // Start of char data with array_ 896 int data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Int32Value(); 897 898 RegLocation rl_obj = info->args[0]; 899 RegLocation rl_idx = info->args[1]; 900 rl_obj = LoadValue(rl_obj, kCoreReg); 901 rl_idx = LoadValue(rl_idx, kCoreReg); 902 int reg_max; 903 GenNullCheck(rl_obj.s_reg_low, rl_obj.low_reg, info->opt_flags); 904 bool range_check = (!(info->opt_flags & MIR_IGNORE_RANGE_CHECK)); 905 LIR* launch_pad = NULL; 906 int reg_off = INVALID_REG; 907 int reg_ptr = INVALID_REG; 908 if (cu_->instruction_set != kX86) { 909 reg_off = AllocTemp(); 910 reg_ptr = AllocTemp(); 911 if (range_check) { 912 reg_max = AllocTemp(); 913 LoadWordDisp(rl_obj.low_reg, count_offset, reg_max); 914 } 915 LoadWordDisp(rl_obj.low_reg, offset_offset, reg_off); 916 LoadWordDisp(rl_obj.low_reg, value_offset, reg_ptr); 917 if (range_check) { 918 // Set up a launch pad to allow retry in case of bounds violation */ 919 launch_pad = RawLIR(0, kPseudoIntrinsicRetry, WrapPointer(info)); 920 intrinsic_launchpads_.Insert(launch_pad); 921 OpRegReg(kOpCmp, rl_idx.low_reg, reg_max); 922 FreeTemp(reg_max); 923 OpCondBranch(kCondUge, launch_pad); 924 } 925 } else { 926 if (range_check) { 927 reg_max = AllocTemp(); 928 LoadWordDisp(rl_obj.low_reg, count_offset, reg_max); 929 // Set up a launch pad to allow retry in case of bounds violation */ 930 launch_pad = RawLIR(0, kPseudoIntrinsicRetry, WrapPointer(info)); 931 intrinsic_launchpads_.Insert(launch_pad); 932 OpRegReg(kOpCmp, rl_idx.low_reg, reg_max); 933 FreeTemp(reg_max); 934 OpCondBranch(kCondUge, launch_pad); 935 } 936 reg_off = AllocTemp(); 937 reg_ptr = AllocTemp(); 938 LoadWordDisp(rl_obj.low_reg, offset_offset, reg_off); 939 LoadWordDisp(rl_obj.low_reg, value_offset, reg_ptr); 940 } 941 OpRegImm(kOpAdd, reg_ptr, data_offset); 942 OpRegReg(kOpAdd, reg_off, rl_idx.low_reg); 943 FreeTemp(rl_obj.low_reg); 944 FreeTemp(rl_idx.low_reg); 945 RegLocation rl_dest = InlineTarget(info); 946 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); 947 LoadBaseIndexed(reg_ptr, reg_off, rl_result.low_reg, 1, kUnsignedHalf); 948 FreeTemp(reg_off); 949 FreeTemp(reg_ptr); 950 StoreValue(rl_dest, rl_result); 951 if (range_check) { 952 launch_pad->operands[2] = 0; // no resumption 953 } 954 // Record that we've already inlined & null checked 955 info->opt_flags |= (MIR_INLINED | MIR_IGNORE_NULL_CHECK); 956 return true; 957} 958 959// Generates an inlined String.is_empty or String.length. 960bool Mir2Lir::GenInlinedStringIsEmptyOrLength(CallInfo* info, bool is_empty) { 961 if (cu_->instruction_set == kMips) { 962 // TODO - add Mips implementation 963 return false; 964 } 965 // dst = src.length(); 966 RegLocation rl_obj = info->args[0]; 967 rl_obj = LoadValue(rl_obj, kCoreReg); 968 RegLocation rl_dest = InlineTarget(info); 969 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); 970 GenNullCheck(rl_obj.s_reg_low, rl_obj.low_reg, info->opt_flags); 971 LoadWordDisp(rl_obj.low_reg, mirror::String::CountOffset().Int32Value(), rl_result.low_reg); 972 if (is_empty) { 973 // dst = (dst == 0); 974 if (cu_->instruction_set == kThumb2) { 975 int t_reg = AllocTemp(); 976 OpRegReg(kOpNeg, t_reg, rl_result.low_reg); 977 OpRegRegReg(kOpAdc, rl_result.low_reg, rl_result.low_reg, t_reg); 978 } else { 979 DCHECK_EQ(cu_->instruction_set, kX86); 980 OpRegImm(kOpSub, rl_result.low_reg, 1); 981 OpRegImm(kOpLsr, rl_result.low_reg, 31); 982 } 983 } 984 StoreValue(rl_dest, rl_result); 985 return true; 986} 987 988bool Mir2Lir::GenInlinedReverseBytes(CallInfo* info, OpSize size) { 989 if (cu_->instruction_set == kMips) { 990 // TODO - add Mips implementation 991 return false; 992 } 993 RegLocation rl_src_i = info->args[0]; 994 RegLocation rl_dest = InlineTarget(info); // result reg 995 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); 996 if (size == kLong) { 997 RegLocation rl_i = LoadValueWide(rl_src_i, kCoreReg); 998 int r_i_low = rl_i.low_reg; 999 if (rl_i.low_reg == rl_result.low_reg) { 1000 // First REV shall clobber rl_result.low_reg, save the value in a temp for the second REV. 1001 r_i_low = AllocTemp(); 1002 OpRegCopy(r_i_low, rl_i.low_reg); 1003 } 1004 OpRegReg(kOpRev, rl_result.low_reg, rl_i.high_reg); 1005 OpRegReg(kOpRev, rl_result.high_reg, r_i_low); 1006 if (rl_i.low_reg == rl_result.low_reg) { 1007 FreeTemp(r_i_low); 1008 } 1009 StoreValueWide(rl_dest, rl_result); 1010 } else { 1011 DCHECK(size == kWord || size == kSignedHalf); 1012 OpKind op = (size == kWord) ? kOpRev : kOpRevsh; 1013 RegLocation rl_i = LoadValue(rl_src_i, kCoreReg); 1014 OpRegReg(op, rl_result.low_reg, rl_i.low_reg); 1015 StoreValue(rl_dest, rl_result); 1016 } 1017 return true; 1018} 1019 1020bool Mir2Lir::GenInlinedAbsInt(CallInfo* info) { 1021 if (cu_->instruction_set == kMips) { 1022 // TODO - add Mips implementation 1023 return false; 1024 } 1025 RegLocation rl_src = info->args[0]; 1026 rl_src = LoadValue(rl_src, kCoreReg); 1027 RegLocation rl_dest = InlineTarget(info); 1028 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); 1029 int sign_reg = AllocTemp(); 1030 // abs(x) = y<=x>>31, (x+y)^y. 1031 OpRegRegImm(kOpAsr, sign_reg, rl_src.low_reg, 31); 1032 OpRegRegReg(kOpAdd, rl_result.low_reg, rl_src.low_reg, sign_reg); 1033 OpRegReg(kOpXor, rl_result.low_reg, sign_reg); 1034 StoreValue(rl_dest, rl_result); 1035 return true; 1036} 1037 1038bool Mir2Lir::GenInlinedAbsLong(CallInfo* info) { 1039 if (cu_->instruction_set == kMips) { 1040 // TODO - add Mips implementation 1041 return false; 1042 } 1043 if (cu_->instruction_set == kThumb2) { 1044 RegLocation rl_src = info->args[0]; 1045 rl_src = LoadValueWide(rl_src, kCoreReg); 1046 RegLocation rl_dest = InlineTargetWide(info); 1047 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); 1048 int sign_reg = AllocTemp(); 1049 // abs(x) = y<=x>>31, (x+y)^y. 1050 OpRegRegImm(kOpAsr, sign_reg, rl_src.high_reg, 31); 1051 OpRegRegReg(kOpAdd, rl_result.low_reg, rl_src.low_reg, sign_reg); 1052 OpRegRegReg(kOpAdc, rl_result.high_reg, rl_src.high_reg, sign_reg); 1053 OpRegReg(kOpXor, rl_result.low_reg, sign_reg); 1054 OpRegReg(kOpXor, rl_result.high_reg, sign_reg); 1055 StoreValueWide(rl_dest, rl_result); 1056 return true; 1057 } else { 1058 DCHECK_EQ(cu_->instruction_set, kX86); 1059 // Reuse source registers to avoid running out of temps 1060 RegLocation rl_src = info->args[0]; 1061 rl_src = LoadValueWide(rl_src, kCoreReg); 1062 RegLocation rl_dest = InlineTargetWide(info); 1063 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); 1064 OpRegCopyWide(rl_result.low_reg, rl_result.high_reg, rl_src.low_reg, rl_src.high_reg); 1065 FreeTemp(rl_src.low_reg); 1066 FreeTemp(rl_src.high_reg); 1067 int sign_reg = AllocTemp(); 1068 // abs(x) = y<=x>>31, (x+y)^y. 1069 OpRegRegImm(kOpAsr, sign_reg, rl_result.high_reg, 31); 1070 OpRegReg(kOpAdd, rl_result.low_reg, sign_reg); 1071 OpRegReg(kOpAdc, rl_result.high_reg, sign_reg); 1072 OpRegReg(kOpXor, rl_result.low_reg, sign_reg); 1073 OpRegReg(kOpXor, rl_result.high_reg, sign_reg); 1074 StoreValueWide(rl_dest, rl_result); 1075 return true; 1076 } 1077} 1078 1079bool Mir2Lir::GenInlinedFloatCvt(CallInfo* info) { 1080 if (cu_->instruction_set == kMips) { 1081 // TODO - add Mips implementation 1082 return false; 1083 } 1084 RegLocation rl_src = info->args[0]; 1085 RegLocation rl_dest = InlineTarget(info); 1086 StoreValue(rl_dest, rl_src); 1087 return true; 1088} 1089 1090bool Mir2Lir::GenInlinedDoubleCvt(CallInfo* info) { 1091 if (cu_->instruction_set == kMips) { 1092 // TODO - add Mips implementation 1093 return false; 1094 } 1095 RegLocation rl_src = info->args[0]; 1096 RegLocation rl_dest = InlineTargetWide(info); 1097 StoreValueWide(rl_dest, rl_src); 1098 return true; 1099} 1100 1101/* 1102 * Fast string.index_of(I) & (II). Tests for simple case of char <= 0xffff, 1103 * otherwise bails to standard library code. 1104 */ 1105bool Mir2Lir::GenInlinedIndexOf(CallInfo* info, bool zero_based) { 1106 if (cu_->instruction_set == kMips) { 1107 // TODO - add Mips implementation 1108 return false; 1109 } 1110 ClobberCallerSave(); 1111 LockCallTemps(); // Using fixed registers 1112 int reg_ptr = TargetReg(kArg0); 1113 int reg_char = TargetReg(kArg1); 1114 int reg_start = TargetReg(kArg2); 1115 1116 RegLocation rl_obj = info->args[0]; 1117 RegLocation rl_char = info->args[1]; 1118 RegLocation rl_start = info->args[2]; 1119 LoadValueDirectFixed(rl_obj, reg_ptr); 1120 LoadValueDirectFixed(rl_char, reg_char); 1121 if (zero_based) { 1122 LoadConstant(reg_start, 0); 1123 } else { 1124 LoadValueDirectFixed(rl_start, reg_start); 1125 } 1126 int r_tgt = (cu_->instruction_set != kX86) ? LoadHelper(QUICK_ENTRYPOINT_OFFSET(pIndexOf)) : 0; 1127 GenNullCheck(rl_obj.s_reg_low, reg_ptr, info->opt_flags); 1128 LIR* launch_pad = RawLIR(0, kPseudoIntrinsicRetry, WrapPointer(info)); 1129 intrinsic_launchpads_.Insert(launch_pad); 1130 OpCmpImmBranch(kCondGt, reg_char, 0xFFFF, launch_pad); 1131 // NOTE: not a safepoint 1132 if (cu_->instruction_set != kX86) { 1133 OpReg(kOpBlx, r_tgt); 1134 } else { 1135 OpThreadMem(kOpBlx, QUICK_ENTRYPOINT_OFFSET(pIndexOf)); 1136 } 1137 LIR* resume_tgt = NewLIR0(kPseudoTargetLabel); 1138 launch_pad->operands[2] = WrapPointer(resume_tgt); 1139 // Record that we've already inlined & null checked 1140 info->opt_flags |= (MIR_INLINED | MIR_IGNORE_NULL_CHECK); 1141 RegLocation rl_return = GetReturn(false); 1142 RegLocation rl_dest = InlineTarget(info); 1143 StoreValue(rl_dest, rl_return); 1144 return true; 1145} 1146 1147/* Fast string.compareTo(Ljava/lang/string;)I. */ 1148bool Mir2Lir::GenInlinedStringCompareTo(CallInfo* info) { 1149 if (cu_->instruction_set == kMips) { 1150 // TODO - add Mips implementation 1151 return false; 1152 } 1153 ClobberCallerSave(); 1154 LockCallTemps(); // Using fixed registers 1155 int reg_this = TargetReg(kArg0); 1156 int reg_cmp = TargetReg(kArg1); 1157 1158 RegLocation rl_this = info->args[0]; 1159 RegLocation rl_cmp = info->args[1]; 1160 LoadValueDirectFixed(rl_this, reg_this); 1161 LoadValueDirectFixed(rl_cmp, reg_cmp); 1162 int r_tgt = (cu_->instruction_set != kX86) ? 1163 LoadHelper(QUICK_ENTRYPOINT_OFFSET(pStringCompareTo)) : 0; 1164 GenNullCheck(rl_this.s_reg_low, reg_this, info->opt_flags); 1165 // TUNING: check if rl_cmp.s_reg_low is already null checked 1166 LIR* launch_pad = RawLIR(0, kPseudoIntrinsicRetry, WrapPointer(info)); 1167 intrinsic_launchpads_.Insert(launch_pad); 1168 OpCmpImmBranch(kCondEq, reg_cmp, 0, launch_pad); 1169 // NOTE: not a safepoint 1170 if (cu_->instruction_set != kX86) { 1171 OpReg(kOpBlx, r_tgt); 1172 } else { 1173 OpThreadMem(kOpBlx, QUICK_ENTRYPOINT_OFFSET(pStringCompareTo)); 1174 } 1175 launch_pad->operands[2] = 0; // No return possible 1176 // Record that we've already inlined & null checked 1177 info->opt_flags |= (MIR_INLINED | MIR_IGNORE_NULL_CHECK); 1178 RegLocation rl_return = GetReturn(false); 1179 RegLocation rl_dest = InlineTarget(info); 1180 StoreValue(rl_dest, rl_return); 1181 return true; 1182} 1183 1184bool Mir2Lir::GenInlinedCurrentThread(CallInfo* info) { 1185 RegLocation rl_dest = InlineTarget(info); 1186 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); 1187 ThreadOffset offset = Thread::PeerOffset(); 1188 if (cu_->instruction_set == kThumb2 || cu_->instruction_set == kMips) { 1189 LoadWordDisp(TargetReg(kSelf), offset.Int32Value(), rl_result.low_reg); 1190 } else { 1191 CHECK(cu_->instruction_set == kX86); 1192 reinterpret_cast<X86Mir2Lir*>(this)->OpRegThreadMem(kOpMov, rl_result.low_reg, offset); 1193 } 1194 StoreValue(rl_dest, rl_result); 1195 return true; 1196} 1197 1198bool Mir2Lir::GenInlinedUnsafeGet(CallInfo* info, 1199 bool is_long, bool is_volatile) { 1200 if (cu_->instruction_set == kMips) { 1201 // TODO - add Mips implementation 1202 return false; 1203 } 1204 // Unused - RegLocation rl_src_unsafe = info->args[0]; 1205 RegLocation rl_src_obj = info->args[1]; // Object 1206 RegLocation rl_src_offset = info->args[2]; // long low 1207 rl_src_offset.wide = 0; // ignore high half in info->args[3] 1208 RegLocation rl_dest = InlineTarget(info); // result reg 1209 if (is_volatile) { 1210 GenMemBarrier(kLoadLoad); 1211 } 1212 RegLocation rl_object = LoadValue(rl_src_obj, kCoreReg); 1213 RegLocation rl_offset = LoadValue(rl_src_offset, kCoreReg); 1214 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); 1215 if (is_long) { 1216 OpRegReg(kOpAdd, rl_object.low_reg, rl_offset.low_reg); 1217 LoadBaseDispWide(rl_object.low_reg, 0, rl_result.low_reg, rl_result.high_reg, INVALID_SREG); 1218 StoreValueWide(rl_dest, rl_result); 1219 } else { 1220 LoadBaseIndexed(rl_object.low_reg, rl_offset.low_reg, rl_result.low_reg, 0, kWord); 1221 StoreValue(rl_dest, rl_result); 1222 } 1223 return true; 1224} 1225 1226bool Mir2Lir::GenInlinedUnsafePut(CallInfo* info, bool is_long, 1227 bool is_object, bool is_volatile, bool is_ordered) { 1228 if (cu_->instruction_set == kMips) { 1229 // TODO - add Mips implementation 1230 return false; 1231 } 1232 if (cu_->instruction_set == kX86 && is_object) { 1233 // TODO: fix X86, it exhausts registers for card marking. 1234 return false; 1235 } 1236 // Unused - RegLocation rl_src_unsafe = info->args[0]; 1237 RegLocation rl_src_obj = info->args[1]; // Object 1238 RegLocation rl_src_offset = info->args[2]; // long low 1239 rl_src_offset.wide = 0; // ignore high half in info->args[3] 1240 RegLocation rl_src_value = info->args[4]; // value to store 1241 if (is_volatile || is_ordered) { 1242 GenMemBarrier(kStoreStore); 1243 } 1244 RegLocation rl_object = LoadValue(rl_src_obj, kCoreReg); 1245 RegLocation rl_offset = LoadValue(rl_src_offset, kCoreReg); 1246 RegLocation rl_value; 1247 if (is_long) { 1248 rl_value = LoadValueWide(rl_src_value, kCoreReg); 1249 OpRegReg(kOpAdd, rl_object.low_reg, rl_offset.low_reg); 1250 StoreBaseDispWide(rl_object.low_reg, 0, rl_value.low_reg, rl_value.high_reg); 1251 } else { 1252 rl_value = LoadValue(rl_src_value, kCoreReg); 1253 StoreBaseIndexed(rl_object.low_reg, rl_offset.low_reg, rl_value.low_reg, 0, kWord); 1254 } 1255 if (is_volatile) { 1256 GenMemBarrier(kStoreLoad); 1257 } 1258 if (is_object) { 1259 MarkGCCard(rl_value.low_reg, rl_object.low_reg); 1260 } 1261 return true; 1262} 1263 1264void Mir2Lir::GenInvoke(CallInfo* info) { 1265 if (!(info->opt_flags & MIR_INLINED)) { 1266 DCHECK(cu_->compiler_driver->GetMethodInlinerMap() != nullptr); 1267 if (cu_->compiler_driver->GetMethodInlinerMap()->GetMethodInliner(cu_->dex_file) 1268 ->GenIntrinsic(this, info)) { 1269 return; 1270 } 1271 } 1272 InvokeType original_type = info->type; // avoiding mutation by ComputeInvokeInfo 1273 int call_state = 0; 1274 LIR* null_ck; 1275 LIR** p_null_ck = NULL; 1276 NextCallInsn next_call_insn; 1277 FlushAllRegs(); /* Everything to home location */ 1278 // Explicit register usage 1279 LockCallTemps(); 1280 1281 DexCompilationUnit* cUnit = mir_graph_->GetCurrentDexCompilationUnit(); 1282 MethodReference target_method(cUnit->GetDexFile(), info->index); 1283 int vtable_idx; 1284 uintptr_t direct_code; 1285 uintptr_t direct_method; 1286 bool skip_this; 1287 bool fast_path = 1288 cu_->compiler_driver->ComputeInvokeInfo(mir_graph_->GetCurrentDexCompilationUnit(), 1289 current_dalvik_offset_, 1290 true, true, 1291 &info->type, &target_method, 1292 &vtable_idx, 1293 &direct_code, &direct_method) && !SLOW_INVOKE_PATH; 1294 if (info->type == kInterface) { 1295 next_call_insn = fast_path ? NextInterfaceCallInsn : NextInterfaceCallInsnWithAccessCheck; 1296 skip_this = fast_path; 1297 } else if (info->type == kDirect) { 1298 if (fast_path) { 1299 p_null_ck = &null_ck; 1300 } 1301 next_call_insn = fast_path ? NextSDCallInsn : NextDirectCallInsnSP; 1302 skip_this = false; 1303 } else if (info->type == kStatic) { 1304 next_call_insn = fast_path ? NextSDCallInsn : NextStaticCallInsnSP; 1305 skip_this = false; 1306 } else if (info->type == kSuper) { 1307 DCHECK(!fast_path); // Fast path is a direct call. 1308 next_call_insn = NextSuperCallInsnSP; 1309 skip_this = false; 1310 } else { 1311 DCHECK_EQ(info->type, kVirtual); 1312 next_call_insn = fast_path ? NextVCallInsn : NextVCallInsnSP; 1313 skip_this = fast_path; 1314 } 1315 if (!info->is_range) { 1316 call_state = GenDalvikArgsNoRange(info, call_state, p_null_ck, 1317 next_call_insn, target_method, 1318 vtable_idx, direct_code, direct_method, 1319 original_type, skip_this); 1320 } else { 1321 call_state = GenDalvikArgsRange(info, call_state, p_null_ck, 1322 next_call_insn, target_method, vtable_idx, 1323 direct_code, direct_method, original_type, 1324 skip_this); 1325 } 1326 // Finish up any of the call sequence not interleaved in arg loading 1327 while (call_state >= 0) { 1328 call_state = next_call_insn(cu_, info, call_state, target_method, 1329 vtable_idx, direct_code, direct_method, 1330 original_type); 1331 } 1332 LIR* call_inst; 1333 if (cu_->instruction_set != kX86) { 1334 call_inst = OpReg(kOpBlx, TargetReg(kInvokeTgt)); 1335 } else { 1336 if (fast_path) { 1337 call_inst = OpMem(kOpBlx, TargetReg(kArg0), 1338 mirror::ArtMethod::GetEntryPointFromCompiledCodeOffset().Int32Value()); 1339 } else { 1340 ThreadOffset trampoline(-1); 1341 switch (info->type) { 1342 case kInterface: 1343 trampoline = QUICK_ENTRYPOINT_OFFSET(pInvokeInterfaceTrampolineWithAccessCheck); 1344 break; 1345 case kDirect: 1346 trampoline = QUICK_ENTRYPOINT_OFFSET(pInvokeDirectTrampolineWithAccessCheck); 1347 break; 1348 case kStatic: 1349 trampoline = QUICK_ENTRYPOINT_OFFSET(pInvokeStaticTrampolineWithAccessCheck); 1350 break; 1351 case kSuper: 1352 trampoline = QUICK_ENTRYPOINT_OFFSET(pInvokeSuperTrampolineWithAccessCheck); 1353 break; 1354 case kVirtual: 1355 trampoline = QUICK_ENTRYPOINT_OFFSET(pInvokeVirtualTrampolineWithAccessCheck); 1356 break; 1357 default: 1358 LOG(FATAL) << "Unexpected invoke type"; 1359 } 1360 call_inst = OpThreadMem(kOpBlx, trampoline); 1361 } 1362 } 1363 MarkSafepointPC(call_inst); 1364 1365 ClobberCallerSave(); 1366 if (info->result.location != kLocInvalid) { 1367 // We have a following MOVE_RESULT - do it now. 1368 if (info->result.wide) { 1369 RegLocation ret_loc = GetReturnWide(info->result.fp); 1370 StoreValueWide(info->result, ret_loc); 1371 } else { 1372 RegLocation ret_loc = GetReturn(info->result.fp); 1373 StoreValue(info->result, ret_loc); 1374 } 1375 } 1376} 1377 1378} // namespace art 1379