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