code_generator_arm.cc revision b4536b7de576b20c74c612406c5d3132998075ef
1/* 2 * Copyright (C) 2014 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 "code_generator_arm.h" 18 19#include "arch/arm/instruction_set_features_arm.h" 20#include "art_method.h" 21#include "code_generator_utils.h" 22#include "compiled_method.h" 23#include "entrypoints/quick/quick_entrypoints.h" 24#include "gc/accounting/card_table.h" 25#include "intrinsics.h" 26#include "intrinsics_arm.h" 27#include "mirror/array-inl.h" 28#include "mirror/class-inl.h" 29#include "thread.h" 30#include "utils/arm/assembler_arm.h" 31#include "utils/arm/managed_register_arm.h" 32#include "utils/assembler.h" 33#include "utils/stack_checks.h" 34 35namespace art { 36 37template<class MirrorType> 38class GcRoot; 39 40namespace arm { 41 42static bool ExpectedPairLayout(Location location) { 43 // We expected this for both core and fpu register pairs. 44 return ((location.low() & 1) == 0) && (location.low() + 1 == location.high()); 45} 46 47static constexpr int kCurrentMethodStackOffset = 0; 48static constexpr Register kMethodRegisterArgument = R0; 49 50// We unconditionally allocate R5 to ensure we can do long operations 51// with baseline. 52static constexpr Register kCoreSavedRegisterForBaseline = R5; 53static constexpr Register kCoreCalleeSaves[] = 54 { R5, R6, R7, R8, R10, R11, LR }; 55static constexpr SRegister kFpuCalleeSaves[] = 56 { S16, S17, S18, S19, S20, S21, S22, S23, S24, S25, S26, S27, S28, S29, S30, S31 }; 57 58// D31 cannot be split into two S registers, and the register allocator only works on 59// S registers. Therefore there is no need to block it. 60static constexpr DRegister DTMP = D31; 61 62static constexpr uint32_t kPackedSwitchJumpTableThreshold = 6; 63 64#define __ down_cast<ArmAssembler*>(codegen->GetAssembler())-> 65#define QUICK_ENTRY_POINT(x) QUICK_ENTRYPOINT_OFFSET(kArmWordSize, x).Int32Value() 66 67class NullCheckSlowPathARM : public SlowPathCode { 68 public: 69 explicit NullCheckSlowPathARM(HNullCheck* instruction) : instruction_(instruction) {} 70 71 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { 72 CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen); 73 __ Bind(GetEntryLabel()); 74 if (instruction_->CanThrowIntoCatchBlock()) { 75 // Live registers will be restored in the catch block if caught. 76 SaveLiveRegisters(codegen, instruction_->GetLocations()); 77 } 78 arm_codegen->InvokeRuntime( 79 QUICK_ENTRY_POINT(pThrowNullPointer), instruction_, instruction_->GetDexPc(), this); 80 CheckEntrypointTypes<kQuickThrowNullPointer, void, void>(); 81 } 82 83 bool IsFatal() const OVERRIDE { return true; } 84 85 const char* GetDescription() const OVERRIDE { return "NullCheckSlowPathARM"; } 86 87 private: 88 HNullCheck* const instruction_; 89 DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathARM); 90}; 91 92class DivZeroCheckSlowPathARM : public SlowPathCode { 93 public: 94 explicit DivZeroCheckSlowPathARM(HDivZeroCheck* instruction) : instruction_(instruction) {} 95 96 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { 97 CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen); 98 __ Bind(GetEntryLabel()); 99 if (instruction_->CanThrowIntoCatchBlock()) { 100 // Live registers will be restored in the catch block if caught. 101 SaveLiveRegisters(codegen, instruction_->GetLocations()); 102 } 103 arm_codegen->InvokeRuntime( 104 QUICK_ENTRY_POINT(pThrowDivZero), instruction_, instruction_->GetDexPc(), this); 105 CheckEntrypointTypes<kQuickThrowDivZero, void, void>(); 106 } 107 108 bool IsFatal() const OVERRIDE { return true; } 109 110 const char* GetDescription() const OVERRIDE { return "DivZeroCheckSlowPathARM"; } 111 112 private: 113 HDivZeroCheck* const instruction_; 114 DISALLOW_COPY_AND_ASSIGN(DivZeroCheckSlowPathARM); 115}; 116 117class SuspendCheckSlowPathARM : public SlowPathCode { 118 public: 119 SuspendCheckSlowPathARM(HSuspendCheck* instruction, HBasicBlock* successor) 120 : instruction_(instruction), successor_(successor) {} 121 122 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { 123 CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen); 124 __ Bind(GetEntryLabel()); 125 SaveLiveRegisters(codegen, instruction_->GetLocations()); 126 arm_codegen->InvokeRuntime( 127 QUICK_ENTRY_POINT(pTestSuspend), instruction_, instruction_->GetDexPc(), this); 128 CheckEntrypointTypes<kQuickTestSuspend, void, void>(); 129 RestoreLiveRegisters(codegen, instruction_->GetLocations()); 130 if (successor_ == nullptr) { 131 __ b(GetReturnLabel()); 132 } else { 133 __ b(arm_codegen->GetLabelOf(successor_)); 134 } 135 } 136 137 Label* GetReturnLabel() { 138 DCHECK(successor_ == nullptr); 139 return &return_label_; 140 } 141 142 HBasicBlock* GetSuccessor() const { 143 return successor_; 144 } 145 146 const char* GetDescription() const OVERRIDE { return "SuspendCheckSlowPathARM"; } 147 148 private: 149 HSuspendCheck* const instruction_; 150 // If not null, the block to branch to after the suspend check. 151 HBasicBlock* const successor_; 152 153 // If `successor_` is null, the label to branch to after the suspend check. 154 Label return_label_; 155 156 DISALLOW_COPY_AND_ASSIGN(SuspendCheckSlowPathARM); 157}; 158 159class BoundsCheckSlowPathARM : public SlowPathCode { 160 public: 161 explicit BoundsCheckSlowPathARM(HBoundsCheck* instruction) 162 : instruction_(instruction) {} 163 164 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { 165 CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen); 166 LocationSummary* locations = instruction_->GetLocations(); 167 168 __ Bind(GetEntryLabel()); 169 if (instruction_->CanThrowIntoCatchBlock()) { 170 // Live registers will be restored in the catch block if caught. 171 SaveLiveRegisters(codegen, instruction_->GetLocations()); 172 } 173 // We're moving two locations to locations that could overlap, so we need a parallel 174 // move resolver. 175 InvokeRuntimeCallingConvention calling_convention; 176 codegen->EmitParallelMoves( 177 locations->InAt(0), 178 Location::RegisterLocation(calling_convention.GetRegisterAt(0)), 179 Primitive::kPrimInt, 180 locations->InAt(1), 181 Location::RegisterLocation(calling_convention.GetRegisterAt(1)), 182 Primitive::kPrimInt); 183 arm_codegen->InvokeRuntime( 184 QUICK_ENTRY_POINT(pThrowArrayBounds), instruction_, instruction_->GetDexPc(), this); 185 CheckEntrypointTypes<kQuickThrowArrayBounds, void, int32_t, int32_t>(); 186 } 187 188 bool IsFatal() const OVERRIDE { return true; } 189 190 const char* GetDescription() const OVERRIDE { return "BoundsCheckSlowPathARM"; } 191 192 private: 193 HBoundsCheck* const instruction_; 194 195 DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathARM); 196}; 197 198class LoadClassSlowPathARM : public SlowPathCode { 199 public: 200 LoadClassSlowPathARM(HLoadClass* cls, 201 HInstruction* at, 202 uint32_t dex_pc, 203 bool do_clinit) 204 : cls_(cls), at_(at), dex_pc_(dex_pc), do_clinit_(do_clinit) { 205 DCHECK(at->IsLoadClass() || at->IsClinitCheck()); 206 } 207 208 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { 209 LocationSummary* locations = at_->GetLocations(); 210 211 CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen); 212 __ Bind(GetEntryLabel()); 213 SaveLiveRegisters(codegen, locations); 214 215 InvokeRuntimeCallingConvention calling_convention; 216 __ LoadImmediate(calling_convention.GetRegisterAt(0), cls_->GetTypeIndex()); 217 int32_t entry_point_offset = do_clinit_ 218 ? QUICK_ENTRY_POINT(pInitializeStaticStorage) 219 : QUICK_ENTRY_POINT(pInitializeType); 220 arm_codegen->InvokeRuntime(entry_point_offset, at_, dex_pc_, this); 221 if (do_clinit_) { 222 CheckEntrypointTypes<kQuickInitializeStaticStorage, void*, uint32_t>(); 223 } else { 224 CheckEntrypointTypes<kQuickInitializeType, void*, uint32_t>(); 225 } 226 227 // Move the class to the desired location. 228 Location out = locations->Out(); 229 if (out.IsValid()) { 230 DCHECK(out.IsRegister() && !locations->GetLiveRegisters()->ContainsCoreRegister(out.reg())); 231 arm_codegen->Move32(locations->Out(), Location::RegisterLocation(R0)); 232 } 233 RestoreLiveRegisters(codegen, locations); 234 __ b(GetExitLabel()); 235 } 236 237 const char* GetDescription() const OVERRIDE { return "LoadClassSlowPathARM"; } 238 239 private: 240 // The class this slow path will load. 241 HLoadClass* const cls_; 242 243 // The instruction where this slow path is happening. 244 // (Might be the load class or an initialization check). 245 HInstruction* const at_; 246 247 // The dex PC of `at_`. 248 const uint32_t dex_pc_; 249 250 // Whether to initialize the class. 251 const bool do_clinit_; 252 253 DISALLOW_COPY_AND_ASSIGN(LoadClassSlowPathARM); 254}; 255 256class LoadStringSlowPathARM : public SlowPathCode { 257 public: 258 explicit LoadStringSlowPathARM(HLoadString* instruction) : instruction_(instruction) {} 259 260 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { 261 LocationSummary* locations = instruction_->GetLocations(); 262 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg())); 263 264 CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen); 265 __ Bind(GetEntryLabel()); 266 SaveLiveRegisters(codegen, locations); 267 268 InvokeRuntimeCallingConvention calling_convention; 269 __ LoadImmediate(calling_convention.GetRegisterAt(0), instruction_->GetStringIndex()); 270 arm_codegen->InvokeRuntime( 271 QUICK_ENTRY_POINT(pResolveString), instruction_, instruction_->GetDexPc(), this); 272 CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>(); 273 arm_codegen->Move32(locations->Out(), Location::RegisterLocation(R0)); 274 275 RestoreLiveRegisters(codegen, locations); 276 __ b(GetExitLabel()); 277 } 278 279 const char* GetDescription() const OVERRIDE { return "LoadStringSlowPathARM"; } 280 281 private: 282 HLoadString* const instruction_; 283 284 DISALLOW_COPY_AND_ASSIGN(LoadStringSlowPathARM); 285}; 286 287class TypeCheckSlowPathARM : public SlowPathCode { 288 public: 289 TypeCheckSlowPathARM(HInstruction* instruction, bool is_fatal) 290 : instruction_(instruction), is_fatal_(is_fatal) {} 291 292 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { 293 LocationSummary* locations = instruction_->GetLocations(); 294 Location object_class = instruction_->IsCheckCast() ? locations->GetTemp(0) 295 : locations->Out(); 296 DCHECK(instruction_->IsCheckCast() 297 || !locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg())); 298 299 CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen); 300 __ Bind(GetEntryLabel()); 301 302 if (!is_fatal_) { 303 SaveLiveRegisters(codegen, locations); 304 } 305 306 // We're moving two locations to locations that could overlap, so we need a parallel 307 // move resolver. 308 InvokeRuntimeCallingConvention calling_convention; 309 codegen->EmitParallelMoves( 310 locations->InAt(1), 311 Location::RegisterLocation(calling_convention.GetRegisterAt(0)), 312 Primitive::kPrimNot, 313 object_class, 314 Location::RegisterLocation(calling_convention.GetRegisterAt(1)), 315 Primitive::kPrimNot); 316 317 if (instruction_->IsInstanceOf()) { 318 arm_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pInstanceofNonTrivial), 319 instruction_, 320 instruction_->GetDexPc(), 321 this); 322 CheckEntrypointTypes< 323 kQuickInstanceofNonTrivial, uint32_t, const mirror::Class*, const mirror::Class*>(); 324 arm_codegen->Move32(locations->Out(), Location::RegisterLocation(R0)); 325 } else { 326 DCHECK(instruction_->IsCheckCast()); 327 arm_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pCheckCast), 328 instruction_, 329 instruction_->GetDexPc(), 330 this); 331 CheckEntrypointTypes<kQuickCheckCast, void, const mirror::Class*, const mirror::Class*>(); 332 } 333 334 if (!is_fatal_) { 335 RestoreLiveRegisters(codegen, locations); 336 __ b(GetExitLabel()); 337 } 338 } 339 340 const char* GetDescription() const OVERRIDE { return "TypeCheckSlowPathARM"; } 341 342 bool IsFatal() const OVERRIDE { return is_fatal_; } 343 344 private: 345 HInstruction* const instruction_; 346 const bool is_fatal_; 347 348 DISALLOW_COPY_AND_ASSIGN(TypeCheckSlowPathARM); 349}; 350 351class DeoptimizationSlowPathARM : public SlowPathCode { 352 public: 353 explicit DeoptimizationSlowPathARM(HInstruction* instruction) 354 : instruction_(instruction) {} 355 356 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { 357 __ Bind(GetEntryLabel()); 358 SaveLiveRegisters(codegen, instruction_->GetLocations()); 359 DCHECK(instruction_->IsDeoptimize()); 360 HDeoptimize* deoptimize = instruction_->AsDeoptimize(); 361 uint32_t dex_pc = deoptimize->GetDexPc(); 362 CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen); 363 arm_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pDeoptimize), instruction_, dex_pc, this); 364 CheckEntrypointTypes<kQuickDeoptimize, void, void>(); 365 } 366 367 const char* GetDescription() const OVERRIDE { return "DeoptimizationSlowPathARM"; } 368 369 private: 370 HInstruction* const instruction_; 371 DISALLOW_COPY_AND_ASSIGN(DeoptimizationSlowPathARM); 372}; 373 374class ArraySetSlowPathARM : public SlowPathCode { 375 public: 376 explicit ArraySetSlowPathARM(HInstruction* instruction) : instruction_(instruction) {} 377 378 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { 379 LocationSummary* locations = instruction_->GetLocations(); 380 __ Bind(GetEntryLabel()); 381 SaveLiveRegisters(codegen, locations); 382 383 InvokeRuntimeCallingConvention calling_convention; 384 HParallelMove parallel_move(codegen->GetGraph()->GetArena()); 385 parallel_move.AddMove( 386 locations->InAt(0), 387 Location::RegisterLocation(calling_convention.GetRegisterAt(0)), 388 Primitive::kPrimNot, 389 nullptr); 390 parallel_move.AddMove( 391 locations->InAt(1), 392 Location::RegisterLocation(calling_convention.GetRegisterAt(1)), 393 Primitive::kPrimInt, 394 nullptr); 395 parallel_move.AddMove( 396 locations->InAt(2), 397 Location::RegisterLocation(calling_convention.GetRegisterAt(2)), 398 Primitive::kPrimNot, 399 nullptr); 400 codegen->GetMoveResolver()->EmitNativeCode(¶llel_move); 401 402 CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen); 403 arm_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pAputObject), 404 instruction_, 405 instruction_->GetDexPc(), 406 this); 407 CheckEntrypointTypes<kQuickAputObject, void, mirror::Array*, int32_t, mirror::Object*>(); 408 RestoreLiveRegisters(codegen, locations); 409 __ b(GetExitLabel()); 410 } 411 412 const char* GetDescription() const OVERRIDE { return "ArraySetSlowPathARM"; } 413 414 private: 415 HInstruction* const instruction_; 416 417 DISALLOW_COPY_AND_ASSIGN(ArraySetSlowPathARM); 418}; 419 420// Slow path generating a read barrier for a heap reference. 421class ReadBarrierForHeapReferenceSlowPathARM : public SlowPathCode { 422 public: 423 ReadBarrierForHeapReferenceSlowPathARM(HInstruction* instruction, 424 Location out, 425 Location ref, 426 Location obj, 427 uint32_t offset, 428 Location index) 429 : instruction_(instruction), 430 out_(out), 431 ref_(ref), 432 obj_(obj), 433 offset_(offset), 434 index_(index) { 435 DCHECK(kEmitCompilerReadBarrier); 436 // If `obj` is equal to `out` or `ref`, it means the initial object 437 // has been overwritten by (or after) the heap object reference load 438 // to be instrumented, e.g.: 439 // 440 // __ LoadFromOffset(kLoadWord, out, out, offset); 441 // codegen_->GenerateReadBarrier(instruction, out_loc, out_loc, out_loc, offset); 442 // 443 // In that case, we have lost the information about the original 444 // object, and the emitted read barrier cannot work properly. 445 DCHECK(!obj.Equals(out)) << "obj=" << obj << " out=" << out; 446 DCHECK(!obj.Equals(ref)) << "obj=" << obj << " ref=" << ref; 447 } 448 449 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { 450 CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen); 451 LocationSummary* locations = instruction_->GetLocations(); 452 Register reg_out = out_.AsRegister<Register>(); 453 DCHECK(locations->CanCall()); 454 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(reg_out)); 455 DCHECK(!instruction_->IsInvoke() || 456 (instruction_->IsInvokeStaticOrDirect() && 457 instruction_->GetLocations()->Intrinsified())); 458 459 __ Bind(GetEntryLabel()); 460 SaveLiveRegisters(codegen, locations); 461 462 // We may have to change the index's value, but as `index_` is a 463 // constant member (like other "inputs" of this slow path), 464 // introduce a copy of it, `index`. 465 Location index = index_; 466 if (index_.IsValid()) { 467 // Handle `index_` for HArrayGet and intrinsic UnsafeGetObject. 468 if (instruction_->IsArrayGet()) { 469 // Compute the actual memory offset and store it in `index`. 470 Register index_reg = index_.AsRegister<Register>(); 471 DCHECK(locations->GetLiveRegisters()->ContainsCoreRegister(index_reg)); 472 if (codegen->IsCoreCalleeSaveRegister(index_reg)) { 473 // We are about to change the value of `index_reg` (see the 474 // calls to art::arm::Thumb2Assembler::Lsl and 475 // art::arm::Thumb2Assembler::AddConstant below), but it has 476 // not been saved by the previous call to 477 // art::SlowPathCode::SaveLiveRegisters, as it is a 478 // callee-save register -- 479 // art::SlowPathCode::SaveLiveRegisters does not consider 480 // callee-save registers, as it has been designed with the 481 // assumption that callee-save registers are supposed to be 482 // handled by the called function. So, as a callee-save 483 // register, `index_reg` _would_ eventually be saved onto 484 // the stack, but it would be too late: we would have 485 // changed its value earlier. Therefore, we manually save 486 // it here into another freely available register, 487 // `free_reg`, chosen of course among the caller-save 488 // registers (as a callee-save `free_reg` register would 489 // exhibit the same problem). 490 // 491 // Note we could have requested a temporary register from 492 // the register allocator instead; but we prefer not to, as 493 // this is a slow path, and we know we can find a 494 // caller-save register that is available. 495 Register free_reg = FindAvailableCallerSaveRegister(codegen); 496 __ Mov(free_reg, index_reg); 497 index_reg = free_reg; 498 index = Location::RegisterLocation(index_reg); 499 } else { 500 // The initial register stored in `index_` has already been 501 // saved in the call to art::SlowPathCode::SaveLiveRegisters 502 // (as it is not a callee-save register), so we can freely 503 // use it. 504 } 505 // Shifting the index value contained in `index_reg` by the scale 506 // factor (2) cannot overflow in practice, as the runtime is 507 // unable to allocate object arrays with a size larger than 508 // 2^26 - 1 (that is, 2^28 - 4 bytes). 509 __ Lsl(index_reg, index_reg, TIMES_4); 510 static_assert( 511 sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t), 512 "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes."); 513 __ AddConstant(index_reg, index_reg, offset_); 514 } else { 515 DCHECK(instruction_->IsInvoke()); 516 DCHECK(instruction_->GetLocations()->Intrinsified()); 517 DCHECK((instruction_->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObject) || 518 (instruction_->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile)) 519 << instruction_->AsInvoke()->GetIntrinsic(); 520 DCHECK_EQ(offset_, 0U); 521 DCHECK(index_.IsRegisterPair()); 522 // UnsafeGet's offset location is a register pair, the low 523 // part contains the correct offset. 524 index = index_.ToLow(); 525 } 526 } 527 528 // We're moving two or three locations to locations that could 529 // overlap, so we need a parallel move resolver. 530 InvokeRuntimeCallingConvention calling_convention; 531 HParallelMove parallel_move(codegen->GetGraph()->GetArena()); 532 parallel_move.AddMove(ref_, 533 Location::RegisterLocation(calling_convention.GetRegisterAt(0)), 534 Primitive::kPrimNot, 535 nullptr); 536 parallel_move.AddMove(obj_, 537 Location::RegisterLocation(calling_convention.GetRegisterAt(1)), 538 Primitive::kPrimNot, 539 nullptr); 540 if (index.IsValid()) { 541 parallel_move.AddMove(index, 542 Location::RegisterLocation(calling_convention.GetRegisterAt(2)), 543 Primitive::kPrimInt, 544 nullptr); 545 codegen->GetMoveResolver()->EmitNativeCode(¶llel_move); 546 } else { 547 codegen->GetMoveResolver()->EmitNativeCode(¶llel_move); 548 __ LoadImmediate(calling_convention.GetRegisterAt(2), offset_); 549 } 550 arm_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pReadBarrierSlow), 551 instruction_, 552 instruction_->GetDexPc(), 553 this); 554 CheckEntrypointTypes< 555 kQuickReadBarrierSlow, mirror::Object*, mirror::Object*, mirror::Object*, uint32_t>(); 556 arm_codegen->Move32(out_, Location::RegisterLocation(R0)); 557 558 RestoreLiveRegisters(codegen, locations); 559 __ b(GetExitLabel()); 560 } 561 562 const char* GetDescription() const OVERRIDE { return "ReadBarrierForHeapReferenceSlowPathARM"; } 563 564 private: 565 Register FindAvailableCallerSaveRegister(CodeGenerator* codegen) { 566 size_t ref = static_cast<int>(ref_.AsRegister<Register>()); 567 size_t obj = static_cast<int>(obj_.AsRegister<Register>()); 568 for (size_t i = 0, e = codegen->GetNumberOfCoreRegisters(); i < e; ++i) { 569 if (i != ref && i != obj && !codegen->IsCoreCalleeSaveRegister(i)) { 570 return static_cast<Register>(i); 571 } 572 } 573 // We shall never fail to find a free caller-save register, as 574 // there are more than two core caller-save registers on ARM 575 // (meaning it is possible to find one which is different from 576 // `ref` and `obj`). 577 DCHECK_GT(codegen->GetNumberOfCoreCallerSaveRegisters(), 2u); 578 LOG(FATAL) << "Could not find a free caller-save register"; 579 UNREACHABLE(); 580 } 581 582 HInstruction* const instruction_; 583 const Location out_; 584 const Location ref_; 585 const Location obj_; 586 const uint32_t offset_; 587 // An additional location containing an index to an array. 588 // Only used for HArrayGet and the UnsafeGetObject & 589 // UnsafeGetObjectVolatile intrinsics. 590 const Location index_; 591 592 DISALLOW_COPY_AND_ASSIGN(ReadBarrierForHeapReferenceSlowPathARM); 593}; 594 595// Slow path generating a read barrier for a GC root. 596class ReadBarrierForRootSlowPathARM : public SlowPathCode { 597 public: 598 ReadBarrierForRootSlowPathARM(HInstruction* instruction, Location out, Location root) 599 : instruction_(instruction), out_(out), root_(root) {} 600 601 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { 602 LocationSummary* locations = instruction_->GetLocations(); 603 Register reg_out = out_.AsRegister<Register>(); 604 DCHECK(locations->CanCall()); 605 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(reg_out)); 606 DCHECK(instruction_->IsLoadClass() || instruction_->IsLoadString()); 607 608 __ Bind(GetEntryLabel()); 609 SaveLiveRegisters(codegen, locations); 610 611 InvokeRuntimeCallingConvention calling_convention; 612 CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen); 613 arm_codegen->Move32(Location::RegisterLocation(calling_convention.GetRegisterAt(0)), root_); 614 arm_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pReadBarrierForRootSlow), 615 instruction_, 616 instruction_->GetDexPc(), 617 this); 618 CheckEntrypointTypes<kQuickReadBarrierForRootSlow, mirror::Object*, GcRoot<mirror::Object>*>(); 619 arm_codegen->Move32(out_, Location::RegisterLocation(R0)); 620 621 RestoreLiveRegisters(codegen, locations); 622 __ b(GetExitLabel()); 623 } 624 625 const char* GetDescription() const OVERRIDE { return "ReadBarrierForRootSlowPathARM"; } 626 627 private: 628 HInstruction* const instruction_; 629 const Location out_; 630 const Location root_; 631 632 DISALLOW_COPY_AND_ASSIGN(ReadBarrierForRootSlowPathARM); 633}; 634 635#undef __ 636#define __ down_cast<ArmAssembler*>(GetAssembler())-> 637 638inline Condition ARMCondition(IfCondition cond) { 639 switch (cond) { 640 case kCondEQ: return EQ; 641 case kCondNE: return NE; 642 case kCondLT: return LT; 643 case kCondLE: return LE; 644 case kCondGT: return GT; 645 case kCondGE: return GE; 646 case kCondB: return LO; 647 case kCondBE: return LS; 648 case kCondA: return HI; 649 case kCondAE: return HS; 650 } 651 LOG(FATAL) << "Unreachable"; 652 UNREACHABLE(); 653} 654 655// Maps signed condition to unsigned condition. 656inline Condition ARMUnsignedCondition(IfCondition cond) { 657 switch (cond) { 658 case kCondEQ: return EQ; 659 case kCondNE: return NE; 660 // Signed to unsigned. 661 case kCondLT: return LO; 662 case kCondLE: return LS; 663 case kCondGT: return HI; 664 case kCondGE: return HS; 665 // Unsigned remain unchanged. 666 case kCondB: return LO; 667 case kCondBE: return LS; 668 case kCondA: return HI; 669 case kCondAE: return HS; 670 } 671 LOG(FATAL) << "Unreachable"; 672 UNREACHABLE(); 673} 674 675void CodeGeneratorARM::DumpCoreRegister(std::ostream& stream, int reg) const { 676 stream << Register(reg); 677} 678 679void CodeGeneratorARM::DumpFloatingPointRegister(std::ostream& stream, int reg) const { 680 stream << SRegister(reg); 681} 682 683size_t CodeGeneratorARM::SaveCoreRegister(size_t stack_index, uint32_t reg_id) { 684 __ StoreToOffset(kStoreWord, static_cast<Register>(reg_id), SP, stack_index); 685 return kArmWordSize; 686} 687 688size_t CodeGeneratorARM::RestoreCoreRegister(size_t stack_index, uint32_t reg_id) { 689 __ LoadFromOffset(kLoadWord, static_cast<Register>(reg_id), SP, stack_index); 690 return kArmWordSize; 691} 692 693size_t CodeGeneratorARM::SaveFloatingPointRegister(size_t stack_index, uint32_t reg_id) { 694 __ StoreSToOffset(static_cast<SRegister>(reg_id), SP, stack_index); 695 return kArmWordSize; 696} 697 698size_t CodeGeneratorARM::RestoreFloatingPointRegister(size_t stack_index, uint32_t reg_id) { 699 __ LoadSFromOffset(static_cast<SRegister>(reg_id), SP, stack_index); 700 return kArmWordSize; 701} 702 703CodeGeneratorARM::CodeGeneratorARM(HGraph* graph, 704 const ArmInstructionSetFeatures& isa_features, 705 const CompilerOptions& compiler_options, 706 OptimizingCompilerStats* stats) 707 : CodeGenerator(graph, 708 kNumberOfCoreRegisters, 709 kNumberOfSRegisters, 710 kNumberOfRegisterPairs, 711 ComputeRegisterMask(reinterpret_cast<const int*>(kCoreCalleeSaves), 712 arraysize(kCoreCalleeSaves)), 713 ComputeRegisterMask(reinterpret_cast<const int*>(kFpuCalleeSaves), 714 arraysize(kFpuCalleeSaves)), 715 compiler_options, 716 stats), 717 block_labels_(nullptr), 718 location_builder_(graph, this), 719 instruction_visitor_(graph, this), 720 move_resolver_(graph->GetArena(), this), 721 assembler_(), 722 isa_features_(isa_features), 723 method_patches_(MethodReferenceComparator(), 724 graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), 725 call_patches_(MethodReferenceComparator(), 726 graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), 727 relative_call_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), 728 dex_cache_arrays_base_labels_(std::less<HArmDexCacheArraysBase*>(), 729 graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) { 730 // Always save the LR register to mimic Quick. 731 AddAllocatedRegister(Location::RegisterLocation(LR)); 732} 733 734void CodeGeneratorARM::Finalize(CodeAllocator* allocator) { 735 // Ensure that we fix up branches and literal loads and emit the literal pool. 736 __ FinalizeCode(); 737 738 // Adjust native pc offsets in stack maps. 739 for (size_t i = 0, num = stack_map_stream_.GetNumberOfStackMaps(); i != num; ++i) { 740 uint32_t old_position = stack_map_stream_.GetStackMap(i).native_pc_offset; 741 uint32_t new_position = __ GetAdjustedPosition(old_position); 742 stack_map_stream_.SetStackMapNativePcOffset(i, new_position); 743 } 744 // Adjust pc offsets for the disassembly information. 745 if (disasm_info_ != nullptr) { 746 GeneratedCodeInterval* frame_entry_interval = disasm_info_->GetFrameEntryInterval(); 747 frame_entry_interval->start = __ GetAdjustedPosition(frame_entry_interval->start); 748 frame_entry_interval->end = __ GetAdjustedPosition(frame_entry_interval->end); 749 for (auto& it : *disasm_info_->GetInstructionIntervals()) { 750 it.second.start = __ GetAdjustedPosition(it.second.start); 751 it.second.end = __ GetAdjustedPosition(it.second.end); 752 } 753 for (auto& it : *disasm_info_->GetSlowPathIntervals()) { 754 it.code_interval.start = __ GetAdjustedPosition(it.code_interval.start); 755 it.code_interval.end = __ GetAdjustedPosition(it.code_interval.end); 756 } 757 } 758 759 CodeGenerator::Finalize(allocator); 760} 761 762Location CodeGeneratorARM::AllocateFreeRegister(Primitive::Type type) const { 763 switch (type) { 764 case Primitive::kPrimLong: { 765 size_t reg = FindFreeEntry(blocked_register_pairs_, kNumberOfRegisterPairs); 766 ArmManagedRegister pair = 767 ArmManagedRegister::FromRegisterPair(static_cast<RegisterPair>(reg)); 768 DCHECK(!blocked_core_registers_[pair.AsRegisterPairLow()]); 769 DCHECK(!blocked_core_registers_[pair.AsRegisterPairHigh()]); 770 771 blocked_core_registers_[pair.AsRegisterPairLow()] = true; 772 blocked_core_registers_[pair.AsRegisterPairHigh()] = true; 773 UpdateBlockedPairRegisters(); 774 return Location::RegisterPairLocation(pair.AsRegisterPairLow(), pair.AsRegisterPairHigh()); 775 } 776 777 case Primitive::kPrimByte: 778 case Primitive::kPrimBoolean: 779 case Primitive::kPrimChar: 780 case Primitive::kPrimShort: 781 case Primitive::kPrimInt: 782 case Primitive::kPrimNot: { 783 int reg = FindFreeEntry(blocked_core_registers_, kNumberOfCoreRegisters); 784 // Block all register pairs that contain `reg`. 785 for (int i = 0; i < kNumberOfRegisterPairs; i++) { 786 ArmManagedRegister current = 787 ArmManagedRegister::FromRegisterPair(static_cast<RegisterPair>(i)); 788 if (current.AsRegisterPairLow() == reg || current.AsRegisterPairHigh() == reg) { 789 blocked_register_pairs_[i] = true; 790 } 791 } 792 return Location::RegisterLocation(reg); 793 } 794 795 case Primitive::kPrimFloat: { 796 int reg = FindFreeEntry(blocked_fpu_registers_, kNumberOfSRegisters); 797 return Location::FpuRegisterLocation(reg); 798 } 799 800 case Primitive::kPrimDouble: { 801 int reg = FindTwoFreeConsecutiveAlignedEntries(blocked_fpu_registers_, kNumberOfSRegisters); 802 DCHECK_EQ(reg % 2, 0); 803 return Location::FpuRegisterPairLocation(reg, reg + 1); 804 } 805 806 case Primitive::kPrimVoid: 807 LOG(FATAL) << "Unreachable type " << type; 808 } 809 810 return Location::NoLocation(); 811} 812 813void CodeGeneratorARM::SetupBlockedRegisters(bool is_baseline) const { 814 // Don't allocate the dalvik style register pair passing. 815 blocked_register_pairs_[R1_R2] = true; 816 817 // Stack register, LR and PC are always reserved. 818 blocked_core_registers_[SP] = true; 819 blocked_core_registers_[LR] = true; 820 blocked_core_registers_[PC] = true; 821 822 // Reserve thread register. 823 blocked_core_registers_[TR] = true; 824 825 // Reserve temp register. 826 blocked_core_registers_[IP] = true; 827 828 if (is_baseline) { 829 for (size_t i = 0; i < arraysize(kCoreCalleeSaves); ++i) { 830 blocked_core_registers_[kCoreCalleeSaves[i]] = true; 831 } 832 833 blocked_core_registers_[kCoreSavedRegisterForBaseline] = false; 834 } 835 836 if (is_baseline || GetGraph()->IsDebuggable()) { 837 // Stubs do not save callee-save floating point registers. If the graph 838 // is debuggable, we need to deal with these registers differently. For 839 // now, just block them. 840 for (size_t i = 0; i < arraysize(kFpuCalleeSaves); ++i) { 841 blocked_fpu_registers_[kFpuCalleeSaves[i]] = true; 842 } 843 } 844 845 UpdateBlockedPairRegisters(); 846} 847 848void CodeGeneratorARM::UpdateBlockedPairRegisters() const { 849 for (int i = 0; i < kNumberOfRegisterPairs; i++) { 850 ArmManagedRegister current = 851 ArmManagedRegister::FromRegisterPair(static_cast<RegisterPair>(i)); 852 if (blocked_core_registers_[current.AsRegisterPairLow()] 853 || blocked_core_registers_[current.AsRegisterPairHigh()]) { 854 blocked_register_pairs_[i] = true; 855 } 856 } 857} 858 859InstructionCodeGeneratorARM::InstructionCodeGeneratorARM(HGraph* graph, CodeGeneratorARM* codegen) 860 : HGraphVisitor(graph), 861 assembler_(codegen->GetAssembler()), 862 codegen_(codegen) {} 863 864void CodeGeneratorARM::ComputeSpillMask() { 865 core_spill_mask_ = allocated_registers_.GetCoreRegisters() & core_callee_save_mask_; 866 // Save one extra register for baseline. Note that on thumb2, there is no easy 867 // instruction to restore just the PC, so this actually helps both baseline 868 // and non-baseline to save and restore at least two registers at entry and exit. 869 core_spill_mask_ |= (1 << kCoreSavedRegisterForBaseline); 870 DCHECK_NE(core_spill_mask_, 0u) << "At least the return address register must be saved"; 871 fpu_spill_mask_ = allocated_registers_.GetFloatingPointRegisters() & fpu_callee_save_mask_; 872 // We use vpush and vpop for saving and restoring floating point registers, which take 873 // a SRegister and the number of registers to save/restore after that SRegister. We 874 // therefore update the `fpu_spill_mask_` to also contain those registers not allocated, 875 // but in the range. 876 if (fpu_spill_mask_ != 0) { 877 uint32_t least_significant_bit = LeastSignificantBit(fpu_spill_mask_); 878 uint32_t most_significant_bit = MostSignificantBit(fpu_spill_mask_); 879 for (uint32_t i = least_significant_bit + 1 ; i < most_significant_bit; ++i) { 880 fpu_spill_mask_ |= (1 << i); 881 } 882 } 883} 884 885static dwarf::Reg DWARFReg(Register reg) { 886 return dwarf::Reg::ArmCore(static_cast<int>(reg)); 887} 888 889static dwarf::Reg DWARFReg(SRegister reg) { 890 return dwarf::Reg::ArmFp(static_cast<int>(reg)); 891} 892 893void CodeGeneratorARM::GenerateFrameEntry() { 894 bool skip_overflow_check = 895 IsLeafMethod() && !FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kArm); 896 DCHECK(GetCompilerOptions().GetImplicitStackOverflowChecks()); 897 __ Bind(&frame_entry_label_); 898 899 if (HasEmptyFrame()) { 900 return; 901 } 902 903 if (!skip_overflow_check) { 904 __ AddConstant(IP, SP, -static_cast<int32_t>(GetStackOverflowReservedBytes(kArm))); 905 __ LoadFromOffset(kLoadWord, IP, IP, 0); 906 RecordPcInfo(nullptr, 0); 907 } 908 909 __ PushList(core_spill_mask_); 910 __ cfi().AdjustCFAOffset(kArmWordSize * POPCOUNT(core_spill_mask_)); 911 __ cfi().RelOffsetForMany(DWARFReg(kMethodRegisterArgument), 0, core_spill_mask_, kArmWordSize); 912 if (fpu_spill_mask_ != 0) { 913 SRegister start_register = SRegister(LeastSignificantBit(fpu_spill_mask_)); 914 __ vpushs(start_register, POPCOUNT(fpu_spill_mask_)); 915 __ cfi().AdjustCFAOffset(kArmWordSize * POPCOUNT(fpu_spill_mask_)); 916 __ cfi().RelOffsetForMany(DWARFReg(S0), 0, fpu_spill_mask_, kArmWordSize); 917 } 918 int adjust = GetFrameSize() - FrameEntrySpillSize(); 919 __ AddConstant(SP, -adjust); 920 __ cfi().AdjustCFAOffset(adjust); 921 __ StoreToOffset(kStoreWord, kMethodRegisterArgument, SP, 0); 922} 923 924void CodeGeneratorARM::GenerateFrameExit() { 925 if (HasEmptyFrame()) { 926 __ bx(LR); 927 return; 928 } 929 __ cfi().RememberState(); 930 int adjust = GetFrameSize() - FrameEntrySpillSize(); 931 __ AddConstant(SP, adjust); 932 __ cfi().AdjustCFAOffset(-adjust); 933 if (fpu_spill_mask_ != 0) { 934 SRegister start_register = SRegister(LeastSignificantBit(fpu_spill_mask_)); 935 __ vpops(start_register, POPCOUNT(fpu_spill_mask_)); 936 __ cfi().AdjustCFAOffset(-kArmPointerSize * POPCOUNT(fpu_spill_mask_)); 937 __ cfi().RestoreMany(DWARFReg(SRegister(0)), fpu_spill_mask_); 938 } 939 // Pop LR into PC to return. 940 DCHECK_NE(core_spill_mask_ & (1 << LR), 0U); 941 uint32_t pop_mask = (core_spill_mask_ & (~(1 << LR))) | 1 << PC; 942 __ PopList(pop_mask); 943 __ cfi().RestoreState(); 944 __ cfi().DefCFAOffset(GetFrameSize()); 945} 946 947void CodeGeneratorARM::Bind(HBasicBlock* block) { 948 Label* label = GetLabelOf(block); 949 __ BindTrackedLabel(label); 950} 951 952Location CodeGeneratorARM::GetStackLocation(HLoadLocal* load) const { 953 switch (load->GetType()) { 954 case Primitive::kPrimLong: 955 case Primitive::kPrimDouble: 956 return Location::DoubleStackSlot(GetStackSlot(load->GetLocal())); 957 958 case Primitive::kPrimInt: 959 case Primitive::kPrimNot: 960 case Primitive::kPrimFloat: 961 return Location::StackSlot(GetStackSlot(load->GetLocal())); 962 963 case Primitive::kPrimBoolean: 964 case Primitive::kPrimByte: 965 case Primitive::kPrimChar: 966 case Primitive::kPrimShort: 967 case Primitive::kPrimVoid: 968 LOG(FATAL) << "Unexpected type " << load->GetType(); 969 UNREACHABLE(); 970 } 971 972 LOG(FATAL) << "Unreachable"; 973 UNREACHABLE(); 974} 975 976Location InvokeDexCallingConventionVisitorARM::GetNextLocation(Primitive::Type type) { 977 switch (type) { 978 case Primitive::kPrimBoolean: 979 case Primitive::kPrimByte: 980 case Primitive::kPrimChar: 981 case Primitive::kPrimShort: 982 case Primitive::kPrimInt: 983 case Primitive::kPrimNot: { 984 uint32_t index = gp_index_++; 985 uint32_t stack_index = stack_index_++; 986 if (index < calling_convention.GetNumberOfRegisters()) { 987 return Location::RegisterLocation(calling_convention.GetRegisterAt(index)); 988 } else { 989 return Location::StackSlot(calling_convention.GetStackOffsetOf(stack_index)); 990 } 991 } 992 993 case Primitive::kPrimLong: { 994 uint32_t index = gp_index_; 995 uint32_t stack_index = stack_index_; 996 gp_index_ += 2; 997 stack_index_ += 2; 998 if (index + 1 < calling_convention.GetNumberOfRegisters()) { 999 if (calling_convention.GetRegisterAt(index) == R1) { 1000 // Skip R1, and use R2_R3 instead. 1001 gp_index_++; 1002 index++; 1003 } 1004 } 1005 if (index + 1 < calling_convention.GetNumberOfRegisters()) { 1006 DCHECK_EQ(calling_convention.GetRegisterAt(index) + 1, 1007 calling_convention.GetRegisterAt(index + 1)); 1008 1009 return Location::RegisterPairLocation(calling_convention.GetRegisterAt(index), 1010 calling_convention.GetRegisterAt(index + 1)); 1011 } else { 1012 return Location::DoubleStackSlot(calling_convention.GetStackOffsetOf(stack_index)); 1013 } 1014 } 1015 1016 case Primitive::kPrimFloat: { 1017 uint32_t stack_index = stack_index_++; 1018 if (float_index_ % 2 == 0) { 1019 float_index_ = std::max(double_index_, float_index_); 1020 } 1021 if (float_index_ < calling_convention.GetNumberOfFpuRegisters()) { 1022 return Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(float_index_++)); 1023 } else { 1024 return Location::StackSlot(calling_convention.GetStackOffsetOf(stack_index)); 1025 } 1026 } 1027 1028 case Primitive::kPrimDouble: { 1029 double_index_ = std::max(double_index_, RoundUp(float_index_, 2)); 1030 uint32_t stack_index = stack_index_; 1031 stack_index_ += 2; 1032 if (double_index_ + 1 < calling_convention.GetNumberOfFpuRegisters()) { 1033 uint32_t index = double_index_; 1034 double_index_ += 2; 1035 Location result = Location::FpuRegisterPairLocation( 1036 calling_convention.GetFpuRegisterAt(index), 1037 calling_convention.GetFpuRegisterAt(index + 1)); 1038 DCHECK(ExpectedPairLayout(result)); 1039 return result; 1040 } else { 1041 return Location::DoubleStackSlot(calling_convention.GetStackOffsetOf(stack_index)); 1042 } 1043 } 1044 1045 case Primitive::kPrimVoid: 1046 LOG(FATAL) << "Unexpected parameter type " << type; 1047 break; 1048 } 1049 return Location::NoLocation(); 1050} 1051 1052Location InvokeDexCallingConventionVisitorARM::GetReturnLocation(Primitive::Type type) const { 1053 switch (type) { 1054 case Primitive::kPrimBoolean: 1055 case Primitive::kPrimByte: 1056 case Primitive::kPrimChar: 1057 case Primitive::kPrimShort: 1058 case Primitive::kPrimInt: 1059 case Primitive::kPrimNot: { 1060 return Location::RegisterLocation(R0); 1061 } 1062 1063 case Primitive::kPrimFloat: { 1064 return Location::FpuRegisterLocation(S0); 1065 } 1066 1067 case Primitive::kPrimLong: { 1068 return Location::RegisterPairLocation(R0, R1); 1069 } 1070 1071 case Primitive::kPrimDouble: { 1072 return Location::FpuRegisterPairLocation(S0, S1); 1073 } 1074 1075 case Primitive::kPrimVoid: 1076 return Location::NoLocation(); 1077 } 1078 1079 UNREACHABLE(); 1080} 1081 1082Location InvokeDexCallingConventionVisitorARM::GetMethodLocation() const { 1083 return Location::RegisterLocation(kMethodRegisterArgument); 1084} 1085 1086void CodeGeneratorARM::Move32(Location destination, Location source) { 1087 if (source.Equals(destination)) { 1088 return; 1089 } 1090 if (destination.IsRegister()) { 1091 if (source.IsRegister()) { 1092 __ Mov(destination.AsRegister<Register>(), source.AsRegister<Register>()); 1093 } else if (source.IsFpuRegister()) { 1094 __ vmovrs(destination.AsRegister<Register>(), source.AsFpuRegister<SRegister>()); 1095 } else { 1096 __ LoadFromOffset(kLoadWord, destination.AsRegister<Register>(), SP, source.GetStackIndex()); 1097 } 1098 } else if (destination.IsFpuRegister()) { 1099 if (source.IsRegister()) { 1100 __ vmovsr(destination.AsFpuRegister<SRegister>(), source.AsRegister<Register>()); 1101 } else if (source.IsFpuRegister()) { 1102 __ vmovs(destination.AsFpuRegister<SRegister>(), source.AsFpuRegister<SRegister>()); 1103 } else { 1104 __ LoadSFromOffset(destination.AsFpuRegister<SRegister>(), SP, source.GetStackIndex()); 1105 } 1106 } else { 1107 DCHECK(destination.IsStackSlot()) << destination; 1108 if (source.IsRegister()) { 1109 __ StoreToOffset(kStoreWord, source.AsRegister<Register>(), SP, destination.GetStackIndex()); 1110 } else if (source.IsFpuRegister()) { 1111 __ StoreSToOffset(source.AsFpuRegister<SRegister>(), SP, destination.GetStackIndex()); 1112 } else { 1113 DCHECK(source.IsStackSlot()) << source; 1114 __ LoadFromOffset(kLoadWord, IP, SP, source.GetStackIndex()); 1115 __ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex()); 1116 } 1117 } 1118} 1119 1120void CodeGeneratorARM::Move64(Location destination, Location source) { 1121 if (source.Equals(destination)) { 1122 return; 1123 } 1124 if (destination.IsRegisterPair()) { 1125 if (source.IsRegisterPair()) { 1126 EmitParallelMoves( 1127 Location::RegisterLocation(source.AsRegisterPairHigh<Register>()), 1128 Location::RegisterLocation(destination.AsRegisterPairHigh<Register>()), 1129 Primitive::kPrimInt, 1130 Location::RegisterLocation(source.AsRegisterPairLow<Register>()), 1131 Location::RegisterLocation(destination.AsRegisterPairLow<Register>()), 1132 Primitive::kPrimInt); 1133 } else if (source.IsFpuRegister()) { 1134 UNIMPLEMENTED(FATAL); 1135 } else if (source.IsFpuRegisterPair()) { 1136 __ vmovrrd(destination.AsRegisterPairLow<Register>(), 1137 destination.AsRegisterPairHigh<Register>(), 1138 FromLowSToD(source.AsFpuRegisterPairLow<SRegister>())); 1139 } else { 1140 DCHECK(source.IsDoubleStackSlot()); 1141 DCHECK(ExpectedPairLayout(destination)); 1142 __ LoadFromOffset(kLoadWordPair, destination.AsRegisterPairLow<Register>(), 1143 SP, source.GetStackIndex()); 1144 } 1145 } else if (destination.IsFpuRegisterPair()) { 1146 if (source.IsDoubleStackSlot()) { 1147 __ LoadDFromOffset(FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()), 1148 SP, 1149 source.GetStackIndex()); 1150 } else if (source.IsRegisterPair()) { 1151 __ vmovdrr(FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()), 1152 source.AsRegisterPairLow<Register>(), 1153 source.AsRegisterPairHigh<Register>()); 1154 } else { 1155 UNIMPLEMENTED(FATAL); 1156 } 1157 } else { 1158 DCHECK(destination.IsDoubleStackSlot()); 1159 if (source.IsRegisterPair()) { 1160 // No conflict possible, so just do the moves. 1161 if (source.AsRegisterPairLow<Register>() == R1) { 1162 DCHECK_EQ(source.AsRegisterPairHigh<Register>(), R2); 1163 __ StoreToOffset(kStoreWord, R1, SP, destination.GetStackIndex()); 1164 __ StoreToOffset(kStoreWord, R2, SP, destination.GetHighStackIndex(kArmWordSize)); 1165 } else { 1166 __ StoreToOffset(kStoreWordPair, source.AsRegisterPairLow<Register>(), 1167 SP, destination.GetStackIndex()); 1168 } 1169 } else if (source.IsFpuRegisterPair()) { 1170 __ StoreDToOffset(FromLowSToD(source.AsFpuRegisterPairLow<SRegister>()), 1171 SP, 1172 destination.GetStackIndex()); 1173 } else { 1174 DCHECK(source.IsDoubleStackSlot()); 1175 EmitParallelMoves( 1176 Location::StackSlot(source.GetStackIndex()), 1177 Location::StackSlot(destination.GetStackIndex()), 1178 Primitive::kPrimInt, 1179 Location::StackSlot(source.GetHighStackIndex(kArmWordSize)), 1180 Location::StackSlot(destination.GetHighStackIndex(kArmWordSize)), 1181 Primitive::kPrimInt); 1182 } 1183 } 1184} 1185 1186void CodeGeneratorARM::Move(HInstruction* instruction, Location location, HInstruction* move_for) { 1187 LocationSummary* locations = instruction->GetLocations(); 1188 if (instruction->IsCurrentMethod()) { 1189 Move32(location, Location::StackSlot(kCurrentMethodStackOffset)); 1190 } else if (locations != nullptr && locations->Out().Equals(location)) { 1191 return; 1192 } else if (locations != nullptr && locations->Out().IsConstant()) { 1193 HConstant* const_to_move = locations->Out().GetConstant(); 1194 if (const_to_move->IsIntConstant() || const_to_move->IsNullConstant()) { 1195 int32_t value = GetInt32ValueOf(const_to_move); 1196 if (location.IsRegister()) { 1197 __ LoadImmediate(location.AsRegister<Register>(), value); 1198 } else { 1199 DCHECK(location.IsStackSlot()); 1200 __ LoadImmediate(IP, value); 1201 __ StoreToOffset(kStoreWord, IP, SP, location.GetStackIndex()); 1202 } 1203 } else { 1204 DCHECK(const_to_move->IsLongConstant()) << const_to_move->DebugName(); 1205 int64_t value = const_to_move->AsLongConstant()->GetValue(); 1206 if (location.IsRegisterPair()) { 1207 __ LoadImmediate(location.AsRegisterPairLow<Register>(), Low32Bits(value)); 1208 __ LoadImmediate(location.AsRegisterPairHigh<Register>(), High32Bits(value)); 1209 } else { 1210 DCHECK(location.IsDoubleStackSlot()); 1211 __ LoadImmediate(IP, Low32Bits(value)); 1212 __ StoreToOffset(kStoreWord, IP, SP, location.GetStackIndex()); 1213 __ LoadImmediate(IP, High32Bits(value)); 1214 __ StoreToOffset(kStoreWord, IP, SP, location.GetHighStackIndex(kArmWordSize)); 1215 } 1216 } 1217 } else if (instruction->IsLoadLocal()) { 1218 uint32_t stack_slot = GetStackSlot(instruction->AsLoadLocal()->GetLocal()); 1219 switch (instruction->GetType()) { 1220 case Primitive::kPrimBoolean: 1221 case Primitive::kPrimByte: 1222 case Primitive::kPrimChar: 1223 case Primitive::kPrimShort: 1224 case Primitive::kPrimInt: 1225 case Primitive::kPrimNot: 1226 case Primitive::kPrimFloat: 1227 Move32(location, Location::StackSlot(stack_slot)); 1228 break; 1229 1230 case Primitive::kPrimLong: 1231 case Primitive::kPrimDouble: 1232 Move64(location, Location::DoubleStackSlot(stack_slot)); 1233 break; 1234 1235 default: 1236 LOG(FATAL) << "Unexpected type " << instruction->GetType(); 1237 } 1238 } else if (instruction->IsTemporary()) { 1239 Location temp_location = GetTemporaryLocation(instruction->AsTemporary()); 1240 if (temp_location.IsStackSlot()) { 1241 Move32(location, temp_location); 1242 } else { 1243 DCHECK(temp_location.IsDoubleStackSlot()); 1244 Move64(location, temp_location); 1245 } 1246 } else { 1247 DCHECK((instruction->GetNext() == move_for) || instruction->GetNext()->IsTemporary()); 1248 switch (instruction->GetType()) { 1249 case Primitive::kPrimBoolean: 1250 case Primitive::kPrimByte: 1251 case Primitive::kPrimChar: 1252 case Primitive::kPrimShort: 1253 case Primitive::kPrimNot: 1254 case Primitive::kPrimInt: 1255 case Primitive::kPrimFloat: 1256 Move32(location, locations->Out()); 1257 break; 1258 1259 case Primitive::kPrimLong: 1260 case Primitive::kPrimDouble: 1261 Move64(location, locations->Out()); 1262 break; 1263 1264 default: 1265 LOG(FATAL) << "Unexpected type " << instruction->GetType(); 1266 } 1267 } 1268} 1269 1270void CodeGeneratorARM::MoveConstant(Location location, int32_t value) { 1271 DCHECK(location.IsRegister()); 1272 __ LoadImmediate(location.AsRegister<Register>(), value); 1273} 1274 1275void CodeGeneratorARM::MoveLocation(Location dst, Location src, Primitive::Type dst_type) { 1276 if (Primitive::Is64BitType(dst_type)) { 1277 Move64(dst, src); 1278 } else { 1279 Move32(dst, src); 1280 } 1281} 1282 1283void CodeGeneratorARM::AddLocationAsTemp(Location location, LocationSummary* locations) { 1284 if (location.IsRegister()) { 1285 locations->AddTemp(location); 1286 } else if (location.IsRegisterPair()) { 1287 locations->AddTemp(Location::RegisterLocation(location.AsRegisterPairLow<Register>())); 1288 locations->AddTemp(Location::RegisterLocation(location.AsRegisterPairHigh<Register>())); 1289 } else { 1290 UNIMPLEMENTED(FATAL) << "AddLocationAsTemp not implemented for location " << location; 1291 } 1292} 1293 1294void CodeGeneratorARM::InvokeRuntime(QuickEntrypointEnum entrypoint, 1295 HInstruction* instruction, 1296 uint32_t dex_pc, 1297 SlowPathCode* slow_path) { 1298 InvokeRuntime(GetThreadOffset<kArmWordSize>(entrypoint).Int32Value(), 1299 instruction, 1300 dex_pc, 1301 slow_path); 1302} 1303 1304void CodeGeneratorARM::InvokeRuntime(int32_t entry_point_offset, 1305 HInstruction* instruction, 1306 uint32_t dex_pc, 1307 SlowPathCode* slow_path) { 1308 ValidateInvokeRuntime(instruction, slow_path); 1309 __ LoadFromOffset(kLoadWord, LR, TR, entry_point_offset); 1310 __ blx(LR); 1311 RecordPcInfo(instruction, dex_pc, slow_path); 1312} 1313 1314void InstructionCodeGeneratorARM::HandleGoto(HInstruction* got, HBasicBlock* successor) { 1315 DCHECK(!successor->IsExitBlock()); 1316 1317 HBasicBlock* block = got->GetBlock(); 1318 HInstruction* previous = got->GetPrevious(); 1319 1320 HLoopInformation* info = block->GetLoopInformation(); 1321 if (info != nullptr && info->IsBackEdge(*block) && info->HasSuspendCheck()) { 1322 codegen_->ClearSpillSlotsFromLoopPhisInStackMap(info->GetSuspendCheck()); 1323 GenerateSuspendCheck(info->GetSuspendCheck(), successor); 1324 return; 1325 } 1326 1327 if (block->IsEntryBlock() && (previous != nullptr) && previous->IsSuspendCheck()) { 1328 GenerateSuspendCheck(previous->AsSuspendCheck(), nullptr); 1329 } 1330 if (!codegen_->GoesToNextBlock(got->GetBlock(), successor)) { 1331 __ b(codegen_->GetLabelOf(successor)); 1332 } 1333} 1334 1335void LocationsBuilderARM::VisitGoto(HGoto* got) { 1336 got->SetLocations(nullptr); 1337} 1338 1339void InstructionCodeGeneratorARM::VisitGoto(HGoto* got) { 1340 HandleGoto(got, got->GetSuccessor()); 1341} 1342 1343void LocationsBuilderARM::VisitTryBoundary(HTryBoundary* try_boundary) { 1344 try_boundary->SetLocations(nullptr); 1345} 1346 1347void InstructionCodeGeneratorARM::VisitTryBoundary(HTryBoundary* try_boundary) { 1348 HBasicBlock* successor = try_boundary->GetNormalFlowSuccessor(); 1349 if (!successor->IsExitBlock()) { 1350 HandleGoto(try_boundary, successor); 1351 } 1352} 1353 1354void LocationsBuilderARM::VisitExit(HExit* exit) { 1355 exit->SetLocations(nullptr); 1356} 1357 1358void InstructionCodeGeneratorARM::VisitExit(HExit* exit ATTRIBUTE_UNUSED) { 1359} 1360 1361void InstructionCodeGeneratorARM::GenerateCompareWithImmediate(Register left, int32_t right) { 1362 ShifterOperand operand; 1363 if (GetAssembler()->ShifterOperandCanHold(R0, left, CMP, right, &operand)) { 1364 __ cmp(left, operand); 1365 } else { 1366 Register temp = IP; 1367 __ LoadImmediate(temp, right); 1368 __ cmp(left, ShifterOperand(temp)); 1369 } 1370} 1371 1372void InstructionCodeGeneratorARM::GenerateFPJumps(HCondition* cond, 1373 Label* true_label, 1374 Label* false_label) { 1375 __ vmstat(); // transfer FP status register to ARM APSR. 1376 // TODO: merge into a single branch (except "equal or unordered" and "not equal") 1377 if (cond->IsFPConditionTrueIfNaN()) { 1378 __ b(true_label, VS); // VS for unordered. 1379 } else if (cond->IsFPConditionFalseIfNaN()) { 1380 __ b(false_label, VS); // VS for unordered. 1381 } 1382 __ b(true_label, ARMCondition(cond->GetCondition())); 1383} 1384 1385void InstructionCodeGeneratorARM::GenerateLongComparesAndJumps(HCondition* cond, 1386 Label* true_label, 1387 Label* false_label) { 1388 LocationSummary* locations = cond->GetLocations(); 1389 Location left = locations->InAt(0); 1390 Location right = locations->InAt(1); 1391 IfCondition if_cond = cond->GetCondition(); 1392 1393 Register left_high = left.AsRegisterPairHigh<Register>(); 1394 Register left_low = left.AsRegisterPairLow<Register>(); 1395 IfCondition true_high_cond = if_cond; 1396 IfCondition false_high_cond = cond->GetOppositeCondition(); 1397 Condition final_condition = ARMUnsignedCondition(if_cond); // unsigned on lower part 1398 1399 // Set the conditions for the test, remembering that == needs to be 1400 // decided using the low words. 1401 // TODO: consider avoiding jumps with temporary and CMP low+SBC high 1402 switch (if_cond) { 1403 case kCondEQ: 1404 case kCondNE: 1405 // Nothing to do. 1406 break; 1407 case kCondLT: 1408 false_high_cond = kCondGT; 1409 break; 1410 case kCondLE: 1411 true_high_cond = kCondLT; 1412 break; 1413 case kCondGT: 1414 false_high_cond = kCondLT; 1415 break; 1416 case kCondGE: 1417 true_high_cond = kCondGT; 1418 break; 1419 case kCondB: 1420 false_high_cond = kCondA; 1421 break; 1422 case kCondBE: 1423 true_high_cond = kCondB; 1424 break; 1425 case kCondA: 1426 false_high_cond = kCondB; 1427 break; 1428 case kCondAE: 1429 true_high_cond = kCondA; 1430 break; 1431 } 1432 if (right.IsConstant()) { 1433 int64_t value = right.GetConstant()->AsLongConstant()->GetValue(); 1434 int32_t val_low = Low32Bits(value); 1435 int32_t val_high = High32Bits(value); 1436 1437 GenerateCompareWithImmediate(left_high, val_high); 1438 if (if_cond == kCondNE) { 1439 __ b(true_label, ARMCondition(true_high_cond)); 1440 } else if (if_cond == kCondEQ) { 1441 __ b(false_label, ARMCondition(false_high_cond)); 1442 } else { 1443 __ b(true_label, ARMCondition(true_high_cond)); 1444 __ b(false_label, ARMCondition(false_high_cond)); 1445 } 1446 // Must be equal high, so compare the lows. 1447 GenerateCompareWithImmediate(left_low, val_low); 1448 } else { 1449 Register right_high = right.AsRegisterPairHigh<Register>(); 1450 Register right_low = right.AsRegisterPairLow<Register>(); 1451 1452 __ cmp(left_high, ShifterOperand(right_high)); 1453 if (if_cond == kCondNE) { 1454 __ b(true_label, ARMCondition(true_high_cond)); 1455 } else if (if_cond == kCondEQ) { 1456 __ b(false_label, ARMCondition(false_high_cond)); 1457 } else { 1458 __ b(true_label, ARMCondition(true_high_cond)); 1459 __ b(false_label, ARMCondition(false_high_cond)); 1460 } 1461 // Must be equal high, so compare the lows. 1462 __ cmp(left_low, ShifterOperand(right_low)); 1463 } 1464 // The last comparison might be unsigned. 1465 // TODO: optimize cases where this is always true/false 1466 __ b(true_label, final_condition); 1467} 1468 1469void InstructionCodeGeneratorARM::GenerateCompareTestAndBranch(HCondition* condition, 1470 Label* true_target_in, 1471 Label* false_target_in) { 1472 // Generated branching requires both targets to be explicit. If either of the 1473 // targets is nullptr (fallthrough) use and bind `fallthrough_target` instead. 1474 Label fallthrough_target; 1475 Label* true_target = true_target_in == nullptr ? &fallthrough_target : true_target_in; 1476 Label* false_target = false_target_in == nullptr ? &fallthrough_target : false_target_in; 1477 1478 LocationSummary* locations = condition->GetLocations(); 1479 Location left = locations->InAt(0); 1480 Location right = locations->InAt(1); 1481 1482 Primitive::Type type = condition->InputAt(0)->GetType(); 1483 switch (type) { 1484 case Primitive::kPrimLong: 1485 GenerateLongComparesAndJumps(condition, true_target, false_target); 1486 break; 1487 case Primitive::kPrimFloat: 1488 __ vcmps(left.AsFpuRegister<SRegister>(), right.AsFpuRegister<SRegister>()); 1489 GenerateFPJumps(condition, true_target, false_target); 1490 break; 1491 case Primitive::kPrimDouble: 1492 __ vcmpd(FromLowSToD(left.AsFpuRegisterPairLow<SRegister>()), 1493 FromLowSToD(right.AsFpuRegisterPairLow<SRegister>())); 1494 GenerateFPJumps(condition, true_target, false_target); 1495 break; 1496 default: 1497 LOG(FATAL) << "Unexpected compare type " << type; 1498 } 1499 1500 if (false_target != &fallthrough_target) { 1501 __ b(false_target); 1502 } 1503 1504 if (fallthrough_target.IsLinked()) { 1505 __ Bind(&fallthrough_target); 1506 } 1507} 1508 1509void InstructionCodeGeneratorARM::GenerateTestAndBranch(HInstruction* instruction, 1510 size_t condition_input_index, 1511 Label* true_target, 1512 Label* false_target) { 1513 HInstruction* cond = instruction->InputAt(condition_input_index); 1514 1515 if (true_target == nullptr && false_target == nullptr) { 1516 // Nothing to do. The code always falls through. 1517 return; 1518 } else if (cond->IsIntConstant()) { 1519 // Constant condition, statically compared against 1. 1520 if (cond->AsIntConstant()->IsOne()) { 1521 if (true_target != nullptr) { 1522 __ b(true_target); 1523 } 1524 } else { 1525 DCHECK(cond->AsIntConstant()->IsZero()); 1526 if (false_target != nullptr) { 1527 __ b(false_target); 1528 } 1529 } 1530 return; 1531 } 1532 1533 // The following code generates these patterns: 1534 // (1) true_target == nullptr && false_target != nullptr 1535 // - opposite condition true => branch to false_target 1536 // (2) true_target != nullptr && false_target == nullptr 1537 // - condition true => branch to true_target 1538 // (3) true_target != nullptr && false_target != nullptr 1539 // - condition true => branch to true_target 1540 // - branch to false_target 1541 if (IsBooleanValueOrMaterializedCondition(cond)) { 1542 // Condition has been materialized, compare the output to 0. 1543 Location cond_val = instruction->GetLocations()->InAt(condition_input_index); 1544 DCHECK(cond_val.IsRegister()); 1545 if (true_target == nullptr) { 1546 __ CompareAndBranchIfZero(cond_val.AsRegister<Register>(), false_target); 1547 } else { 1548 __ CompareAndBranchIfNonZero(cond_val.AsRegister<Register>(), true_target); 1549 } 1550 } else { 1551 // Condition has not been materialized. Use its inputs as the comparison and 1552 // its condition as the branch condition. 1553 HCondition* condition = cond->AsCondition(); 1554 1555 // If this is a long or FP comparison that has been folded into 1556 // the HCondition, generate the comparison directly. 1557 Primitive::Type type = condition->InputAt(0)->GetType(); 1558 if (type == Primitive::kPrimLong || Primitive::IsFloatingPointType(type)) { 1559 GenerateCompareTestAndBranch(condition, true_target, false_target); 1560 return; 1561 } 1562 1563 LocationSummary* locations = cond->GetLocations(); 1564 DCHECK(locations->InAt(0).IsRegister()); 1565 Register left = locations->InAt(0).AsRegister<Register>(); 1566 Location right = locations->InAt(1); 1567 if (right.IsRegister()) { 1568 __ cmp(left, ShifterOperand(right.AsRegister<Register>())); 1569 } else { 1570 DCHECK(right.IsConstant()); 1571 GenerateCompareWithImmediate(left, CodeGenerator::GetInt32ValueOf(right.GetConstant())); 1572 } 1573 if (true_target == nullptr) { 1574 __ b(false_target, ARMCondition(condition->GetOppositeCondition())); 1575 } else { 1576 __ b(true_target, ARMCondition(condition->GetCondition())); 1577 } 1578 } 1579 1580 // If neither branch falls through (case 3), the conditional branch to `true_target` 1581 // was already emitted (case 2) and we need to emit a jump to `false_target`. 1582 if (true_target != nullptr && false_target != nullptr) { 1583 __ b(false_target); 1584 } 1585} 1586 1587void LocationsBuilderARM::VisitIf(HIf* if_instr) { 1588 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(if_instr); 1589 if (IsBooleanValueOrMaterializedCondition(if_instr->InputAt(0))) { 1590 locations->SetInAt(0, Location::RequiresRegister()); 1591 } 1592} 1593 1594void InstructionCodeGeneratorARM::VisitIf(HIf* if_instr) { 1595 HBasicBlock* true_successor = if_instr->IfTrueSuccessor(); 1596 HBasicBlock* false_successor = if_instr->IfFalseSuccessor(); 1597 Label* true_target = codegen_->GoesToNextBlock(if_instr->GetBlock(), true_successor) ? 1598 nullptr : codegen_->GetLabelOf(true_successor); 1599 Label* false_target = codegen_->GoesToNextBlock(if_instr->GetBlock(), false_successor) ? 1600 nullptr : codegen_->GetLabelOf(false_successor); 1601 GenerateTestAndBranch(if_instr, /* condition_input_index */ 0, true_target, false_target); 1602} 1603 1604void LocationsBuilderARM::VisitDeoptimize(HDeoptimize* deoptimize) { 1605 LocationSummary* locations = new (GetGraph()->GetArena()) 1606 LocationSummary(deoptimize, LocationSummary::kCallOnSlowPath); 1607 if (IsBooleanValueOrMaterializedCondition(deoptimize->InputAt(0))) { 1608 locations->SetInAt(0, Location::RequiresRegister()); 1609 } 1610} 1611 1612void InstructionCodeGeneratorARM::VisitDeoptimize(HDeoptimize* deoptimize) { 1613 SlowPathCode* slow_path = new (GetGraph()->GetArena()) DeoptimizationSlowPathARM(deoptimize); 1614 codegen_->AddSlowPath(slow_path); 1615 GenerateTestAndBranch(deoptimize, 1616 /* condition_input_index */ 0, 1617 slow_path->GetEntryLabel(), 1618 /* false_target */ nullptr); 1619} 1620 1621void LocationsBuilderARM::VisitCondition(HCondition* cond) { 1622 LocationSummary* locations = 1623 new (GetGraph()->GetArena()) LocationSummary(cond, LocationSummary::kNoCall); 1624 // Handle the long/FP comparisons made in instruction simplification. 1625 switch (cond->InputAt(0)->GetType()) { 1626 case Primitive::kPrimLong: 1627 locations->SetInAt(0, Location::RequiresRegister()); 1628 locations->SetInAt(1, Location::RegisterOrConstant(cond->InputAt(1))); 1629 if (cond->NeedsMaterialization()) { 1630 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); 1631 } 1632 break; 1633 1634 case Primitive::kPrimFloat: 1635 case Primitive::kPrimDouble: 1636 locations->SetInAt(0, Location::RequiresFpuRegister()); 1637 locations->SetInAt(1, Location::RequiresFpuRegister()); 1638 if (cond->NeedsMaterialization()) { 1639 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 1640 } 1641 break; 1642 1643 default: 1644 locations->SetInAt(0, Location::RequiresRegister()); 1645 locations->SetInAt(1, Location::RegisterOrConstant(cond->InputAt(1))); 1646 if (cond->NeedsMaterialization()) { 1647 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 1648 } 1649 } 1650} 1651 1652void InstructionCodeGeneratorARM::VisitCondition(HCondition* cond) { 1653 if (!cond->NeedsMaterialization()) { 1654 return; 1655 } 1656 1657 LocationSummary* locations = cond->GetLocations(); 1658 Location left = locations->InAt(0); 1659 Location right = locations->InAt(1); 1660 Register out = locations->Out().AsRegister<Register>(); 1661 Label true_label, false_label; 1662 1663 switch (cond->InputAt(0)->GetType()) { 1664 default: { 1665 // Integer case. 1666 if (right.IsRegister()) { 1667 __ cmp(left.AsRegister<Register>(), ShifterOperand(right.AsRegister<Register>())); 1668 } else { 1669 DCHECK(right.IsConstant()); 1670 GenerateCompareWithImmediate(left.AsRegister<Register>(), 1671 CodeGenerator::GetInt32ValueOf(right.GetConstant())); 1672 } 1673 __ it(ARMCondition(cond->GetCondition()), kItElse); 1674 __ mov(locations->Out().AsRegister<Register>(), ShifterOperand(1), 1675 ARMCondition(cond->GetCondition())); 1676 __ mov(locations->Out().AsRegister<Register>(), ShifterOperand(0), 1677 ARMCondition(cond->GetOppositeCondition())); 1678 return; 1679 } 1680 case Primitive::kPrimLong: 1681 GenerateLongComparesAndJumps(cond, &true_label, &false_label); 1682 break; 1683 case Primitive::kPrimFloat: 1684 __ vcmps(left.AsFpuRegister<SRegister>(), right.AsFpuRegister<SRegister>()); 1685 GenerateFPJumps(cond, &true_label, &false_label); 1686 break; 1687 case Primitive::kPrimDouble: 1688 __ vcmpd(FromLowSToD(left.AsFpuRegisterPairLow<SRegister>()), 1689 FromLowSToD(right.AsFpuRegisterPairLow<SRegister>())); 1690 GenerateFPJumps(cond, &true_label, &false_label); 1691 break; 1692 } 1693 1694 // Convert the jumps into the result. 1695 Label done_label; 1696 1697 // False case: result = 0. 1698 __ Bind(&false_label); 1699 __ LoadImmediate(out, 0); 1700 __ b(&done_label); 1701 1702 // True case: result = 1. 1703 __ Bind(&true_label); 1704 __ LoadImmediate(out, 1); 1705 __ Bind(&done_label); 1706} 1707 1708void LocationsBuilderARM::VisitEqual(HEqual* comp) { 1709 VisitCondition(comp); 1710} 1711 1712void InstructionCodeGeneratorARM::VisitEqual(HEqual* comp) { 1713 VisitCondition(comp); 1714} 1715 1716void LocationsBuilderARM::VisitNotEqual(HNotEqual* comp) { 1717 VisitCondition(comp); 1718} 1719 1720void InstructionCodeGeneratorARM::VisitNotEqual(HNotEqual* comp) { 1721 VisitCondition(comp); 1722} 1723 1724void LocationsBuilderARM::VisitLessThan(HLessThan* comp) { 1725 VisitCondition(comp); 1726} 1727 1728void InstructionCodeGeneratorARM::VisitLessThan(HLessThan* comp) { 1729 VisitCondition(comp); 1730} 1731 1732void LocationsBuilderARM::VisitLessThanOrEqual(HLessThanOrEqual* comp) { 1733 VisitCondition(comp); 1734} 1735 1736void InstructionCodeGeneratorARM::VisitLessThanOrEqual(HLessThanOrEqual* comp) { 1737 VisitCondition(comp); 1738} 1739 1740void LocationsBuilderARM::VisitGreaterThan(HGreaterThan* comp) { 1741 VisitCondition(comp); 1742} 1743 1744void InstructionCodeGeneratorARM::VisitGreaterThan(HGreaterThan* comp) { 1745 VisitCondition(comp); 1746} 1747 1748void LocationsBuilderARM::VisitGreaterThanOrEqual(HGreaterThanOrEqual* comp) { 1749 VisitCondition(comp); 1750} 1751 1752void InstructionCodeGeneratorARM::VisitGreaterThanOrEqual(HGreaterThanOrEqual* comp) { 1753 VisitCondition(comp); 1754} 1755 1756void LocationsBuilderARM::VisitBelow(HBelow* comp) { 1757 VisitCondition(comp); 1758} 1759 1760void InstructionCodeGeneratorARM::VisitBelow(HBelow* comp) { 1761 VisitCondition(comp); 1762} 1763 1764void LocationsBuilderARM::VisitBelowOrEqual(HBelowOrEqual* comp) { 1765 VisitCondition(comp); 1766} 1767 1768void InstructionCodeGeneratorARM::VisitBelowOrEqual(HBelowOrEqual* comp) { 1769 VisitCondition(comp); 1770} 1771 1772void LocationsBuilderARM::VisitAbove(HAbove* comp) { 1773 VisitCondition(comp); 1774} 1775 1776void InstructionCodeGeneratorARM::VisitAbove(HAbove* comp) { 1777 VisitCondition(comp); 1778} 1779 1780void LocationsBuilderARM::VisitAboveOrEqual(HAboveOrEqual* comp) { 1781 VisitCondition(comp); 1782} 1783 1784void InstructionCodeGeneratorARM::VisitAboveOrEqual(HAboveOrEqual* comp) { 1785 VisitCondition(comp); 1786} 1787 1788void LocationsBuilderARM::VisitLocal(HLocal* local) { 1789 local->SetLocations(nullptr); 1790} 1791 1792void InstructionCodeGeneratorARM::VisitLocal(HLocal* local) { 1793 DCHECK_EQ(local->GetBlock(), GetGraph()->GetEntryBlock()); 1794} 1795 1796void LocationsBuilderARM::VisitLoadLocal(HLoadLocal* load) { 1797 load->SetLocations(nullptr); 1798} 1799 1800void InstructionCodeGeneratorARM::VisitLoadLocal(HLoadLocal* load ATTRIBUTE_UNUSED) { 1801 // Nothing to do, this is driven by the code generator. 1802} 1803 1804void LocationsBuilderARM::VisitStoreLocal(HStoreLocal* store) { 1805 LocationSummary* locations = 1806 new (GetGraph()->GetArena()) LocationSummary(store, LocationSummary::kNoCall); 1807 switch (store->InputAt(1)->GetType()) { 1808 case Primitive::kPrimBoolean: 1809 case Primitive::kPrimByte: 1810 case Primitive::kPrimChar: 1811 case Primitive::kPrimShort: 1812 case Primitive::kPrimInt: 1813 case Primitive::kPrimNot: 1814 case Primitive::kPrimFloat: 1815 locations->SetInAt(1, Location::StackSlot(codegen_->GetStackSlot(store->GetLocal()))); 1816 break; 1817 1818 case Primitive::kPrimLong: 1819 case Primitive::kPrimDouble: 1820 locations->SetInAt(1, Location::DoubleStackSlot(codegen_->GetStackSlot(store->GetLocal()))); 1821 break; 1822 1823 default: 1824 LOG(FATAL) << "Unexpected local type " << store->InputAt(1)->GetType(); 1825 } 1826} 1827 1828void InstructionCodeGeneratorARM::VisitStoreLocal(HStoreLocal* store ATTRIBUTE_UNUSED) { 1829} 1830 1831void LocationsBuilderARM::VisitIntConstant(HIntConstant* constant) { 1832 LocationSummary* locations = 1833 new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall); 1834 locations->SetOut(Location::ConstantLocation(constant)); 1835} 1836 1837void InstructionCodeGeneratorARM::VisitIntConstant(HIntConstant* constant ATTRIBUTE_UNUSED) { 1838 // Will be generated at use site. 1839} 1840 1841void LocationsBuilderARM::VisitNullConstant(HNullConstant* constant) { 1842 LocationSummary* locations = 1843 new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall); 1844 locations->SetOut(Location::ConstantLocation(constant)); 1845} 1846 1847void InstructionCodeGeneratorARM::VisitNullConstant(HNullConstant* constant ATTRIBUTE_UNUSED) { 1848 // Will be generated at use site. 1849} 1850 1851void LocationsBuilderARM::VisitLongConstant(HLongConstant* constant) { 1852 LocationSummary* locations = 1853 new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall); 1854 locations->SetOut(Location::ConstantLocation(constant)); 1855} 1856 1857void InstructionCodeGeneratorARM::VisitLongConstant(HLongConstant* constant ATTRIBUTE_UNUSED) { 1858 // Will be generated at use site. 1859} 1860 1861void LocationsBuilderARM::VisitFloatConstant(HFloatConstant* constant) { 1862 LocationSummary* locations = 1863 new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall); 1864 locations->SetOut(Location::ConstantLocation(constant)); 1865} 1866 1867void InstructionCodeGeneratorARM::VisitFloatConstant(HFloatConstant* constant ATTRIBUTE_UNUSED) { 1868 // Will be generated at use site. 1869} 1870 1871void LocationsBuilderARM::VisitDoubleConstant(HDoubleConstant* constant) { 1872 LocationSummary* locations = 1873 new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall); 1874 locations->SetOut(Location::ConstantLocation(constant)); 1875} 1876 1877void InstructionCodeGeneratorARM::VisitDoubleConstant(HDoubleConstant* constant ATTRIBUTE_UNUSED) { 1878 // Will be generated at use site. 1879} 1880 1881void LocationsBuilderARM::VisitMemoryBarrier(HMemoryBarrier* memory_barrier) { 1882 memory_barrier->SetLocations(nullptr); 1883} 1884 1885void InstructionCodeGeneratorARM::VisitMemoryBarrier(HMemoryBarrier* memory_barrier) { 1886 GenerateMemoryBarrier(memory_barrier->GetBarrierKind()); 1887} 1888 1889void LocationsBuilderARM::VisitReturnVoid(HReturnVoid* ret) { 1890 ret->SetLocations(nullptr); 1891} 1892 1893void InstructionCodeGeneratorARM::VisitReturnVoid(HReturnVoid* ret ATTRIBUTE_UNUSED) { 1894 codegen_->GenerateFrameExit(); 1895} 1896 1897void LocationsBuilderARM::VisitReturn(HReturn* ret) { 1898 LocationSummary* locations = 1899 new (GetGraph()->GetArena()) LocationSummary(ret, LocationSummary::kNoCall); 1900 locations->SetInAt(0, parameter_visitor_.GetReturnLocation(ret->InputAt(0)->GetType())); 1901} 1902 1903void InstructionCodeGeneratorARM::VisitReturn(HReturn* ret ATTRIBUTE_UNUSED) { 1904 codegen_->GenerateFrameExit(); 1905} 1906 1907void LocationsBuilderARM::VisitInvokeUnresolved(HInvokeUnresolved* invoke) { 1908 // The trampoline uses the same calling convention as dex calling conventions, 1909 // except instead of loading arg0/r0 with the target Method*, arg0/r0 will contain 1910 // the method_idx. 1911 HandleInvoke(invoke); 1912} 1913 1914void InstructionCodeGeneratorARM::VisitInvokeUnresolved(HInvokeUnresolved* invoke) { 1915 codegen_->GenerateInvokeUnresolvedRuntimeCall(invoke); 1916} 1917 1918void LocationsBuilderARM::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) { 1919 // When we do not run baseline, explicit clinit checks triggered by static 1920 // invokes must have been pruned by art::PrepareForRegisterAllocation. 1921 DCHECK(codegen_->IsBaseline() || !invoke->IsStaticWithExplicitClinitCheck()); 1922 1923 IntrinsicLocationsBuilderARM intrinsic(GetGraph()->GetArena(), 1924 codegen_->GetAssembler(), 1925 codegen_->GetInstructionSetFeatures()); 1926 if (intrinsic.TryDispatch(invoke)) { 1927 if (invoke->GetLocations()->CanCall() && invoke->HasPcRelativeDexCache()) { 1928 invoke->GetLocations()->SetInAt(invoke->GetSpecialInputIndex(), Location::Any()); 1929 } 1930 return; 1931 } 1932 1933 HandleInvoke(invoke); 1934 1935 // For PC-relative dex cache the invoke has an extra input, the PC-relative address base. 1936 if (invoke->HasPcRelativeDexCache()) { 1937 invoke->GetLocations()->SetInAt(invoke->GetSpecialInputIndex(), Location::RequiresRegister()); 1938 } 1939} 1940 1941static bool TryGenerateIntrinsicCode(HInvoke* invoke, CodeGeneratorARM* codegen) { 1942 if (invoke->GetLocations()->Intrinsified()) { 1943 IntrinsicCodeGeneratorARM intrinsic(codegen); 1944 intrinsic.Dispatch(invoke); 1945 return true; 1946 } 1947 return false; 1948} 1949 1950void InstructionCodeGeneratorARM::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) { 1951 // When we do not run baseline, explicit clinit checks triggered by static 1952 // invokes must have been pruned by art::PrepareForRegisterAllocation. 1953 DCHECK(codegen_->IsBaseline() || !invoke->IsStaticWithExplicitClinitCheck()); 1954 1955 if (TryGenerateIntrinsicCode(invoke, codegen_)) { 1956 return; 1957 } 1958 1959 LocationSummary* locations = invoke->GetLocations(); 1960 codegen_->GenerateStaticOrDirectCall( 1961 invoke, locations->HasTemps() ? locations->GetTemp(0) : Location::NoLocation()); 1962 codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); 1963} 1964 1965void LocationsBuilderARM::HandleInvoke(HInvoke* invoke) { 1966 InvokeDexCallingConventionVisitorARM calling_convention_visitor; 1967 CodeGenerator::CreateCommonInvokeLocationSummary(invoke, &calling_convention_visitor); 1968} 1969 1970void LocationsBuilderARM::VisitInvokeVirtual(HInvokeVirtual* invoke) { 1971 IntrinsicLocationsBuilderARM intrinsic(GetGraph()->GetArena(), 1972 codegen_->GetAssembler(), 1973 codegen_->GetInstructionSetFeatures()); 1974 if (intrinsic.TryDispatch(invoke)) { 1975 return; 1976 } 1977 1978 HandleInvoke(invoke); 1979} 1980 1981void InstructionCodeGeneratorARM::VisitInvokeVirtual(HInvokeVirtual* invoke) { 1982 if (TryGenerateIntrinsicCode(invoke, codegen_)) { 1983 return; 1984 } 1985 1986 codegen_->GenerateVirtualCall(invoke, invoke->GetLocations()->GetTemp(0)); 1987 DCHECK(!codegen_->IsLeafMethod()); 1988 codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); 1989} 1990 1991void LocationsBuilderARM::VisitInvokeInterface(HInvokeInterface* invoke) { 1992 HandleInvoke(invoke); 1993 // Add the hidden argument. 1994 invoke->GetLocations()->AddTemp(Location::RegisterLocation(R12)); 1995} 1996 1997void InstructionCodeGeneratorARM::VisitInvokeInterface(HInvokeInterface* invoke) { 1998 // TODO: b/18116999, our IMTs can miss an IncompatibleClassChangeError. 1999 LocationSummary* locations = invoke->GetLocations(); 2000 Register temp = locations->GetTemp(0).AsRegister<Register>(); 2001 Register hidden_reg = locations->GetTemp(1).AsRegister<Register>(); 2002 uint32_t method_offset = mirror::Class::EmbeddedImTableEntryOffset( 2003 invoke->GetImtIndex() % mirror::Class::kImtSize, kArmPointerSize).Uint32Value(); 2004 Location receiver = locations->InAt(0); 2005 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value(); 2006 2007 // Set the hidden argument. This is safe to do this here, as R12 2008 // won't be modified thereafter, before the `blx` (call) instruction. 2009 DCHECK_EQ(R12, hidden_reg); 2010 __ LoadImmediate(hidden_reg, invoke->GetDexMethodIndex()); 2011 2012 if (receiver.IsStackSlot()) { 2013 __ LoadFromOffset(kLoadWord, temp, SP, receiver.GetStackIndex()); 2014 // /* HeapReference<Class> */ temp = temp->klass_ 2015 __ LoadFromOffset(kLoadWord, temp, temp, class_offset); 2016 } else { 2017 // /* HeapReference<Class> */ temp = receiver->klass_ 2018 __ LoadFromOffset(kLoadWord, temp, receiver.AsRegister<Register>(), class_offset); 2019 } 2020 codegen_->MaybeRecordImplicitNullCheck(invoke); 2021 // Instead of simply (possibly) unpoisoning `temp` here, we should 2022 // emit a read barrier for the previous class reference load. 2023 // However this is not required in practice, as this is an 2024 // intermediate/temporary reference and because the current 2025 // concurrent copying collector keeps the from-space memory 2026 // intact/accessible until the end of the marking phase (the 2027 // concurrent copying collector may not in the future). 2028 __ MaybeUnpoisonHeapReference(temp); 2029 // temp = temp->GetImtEntryAt(method_offset); 2030 uint32_t entry_point = 2031 ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmWordSize).Int32Value(); 2032 __ LoadFromOffset(kLoadWord, temp, temp, method_offset); 2033 // LR = temp->GetEntryPoint(); 2034 __ LoadFromOffset(kLoadWord, LR, temp, entry_point); 2035 // LR(); 2036 __ blx(LR); 2037 DCHECK(!codegen_->IsLeafMethod()); 2038 codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); 2039} 2040 2041void LocationsBuilderARM::VisitNeg(HNeg* neg) { 2042 LocationSummary* locations = 2043 new (GetGraph()->GetArena()) LocationSummary(neg, LocationSummary::kNoCall); 2044 switch (neg->GetResultType()) { 2045 case Primitive::kPrimInt: { 2046 locations->SetInAt(0, Location::RequiresRegister()); 2047 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 2048 break; 2049 } 2050 case Primitive::kPrimLong: { 2051 locations->SetInAt(0, Location::RequiresRegister()); 2052 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); 2053 break; 2054 } 2055 2056 case Primitive::kPrimFloat: 2057 case Primitive::kPrimDouble: 2058 locations->SetInAt(0, Location::RequiresFpuRegister()); 2059 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); 2060 break; 2061 2062 default: 2063 LOG(FATAL) << "Unexpected neg type " << neg->GetResultType(); 2064 } 2065} 2066 2067void InstructionCodeGeneratorARM::VisitNeg(HNeg* neg) { 2068 LocationSummary* locations = neg->GetLocations(); 2069 Location out = locations->Out(); 2070 Location in = locations->InAt(0); 2071 switch (neg->GetResultType()) { 2072 case Primitive::kPrimInt: 2073 DCHECK(in.IsRegister()); 2074 __ rsb(out.AsRegister<Register>(), in.AsRegister<Register>(), ShifterOperand(0)); 2075 break; 2076 2077 case Primitive::kPrimLong: 2078 DCHECK(in.IsRegisterPair()); 2079 // out.lo = 0 - in.lo (and update the carry/borrow (C) flag) 2080 __ rsbs(out.AsRegisterPairLow<Register>(), 2081 in.AsRegisterPairLow<Register>(), 2082 ShifterOperand(0)); 2083 // We cannot emit an RSC (Reverse Subtract with Carry) 2084 // instruction here, as it does not exist in the Thumb-2 2085 // instruction set. We use the following approach 2086 // using SBC and SUB instead. 2087 // 2088 // out.hi = -C 2089 __ sbc(out.AsRegisterPairHigh<Register>(), 2090 out.AsRegisterPairHigh<Register>(), 2091 ShifterOperand(out.AsRegisterPairHigh<Register>())); 2092 // out.hi = out.hi - in.hi 2093 __ sub(out.AsRegisterPairHigh<Register>(), 2094 out.AsRegisterPairHigh<Register>(), 2095 ShifterOperand(in.AsRegisterPairHigh<Register>())); 2096 break; 2097 2098 case Primitive::kPrimFloat: 2099 DCHECK(in.IsFpuRegister()); 2100 __ vnegs(out.AsFpuRegister<SRegister>(), in.AsFpuRegister<SRegister>()); 2101 break; 2102 2103 case Primitive::kPrimDouble: 2104 DCHECK(in.IsFpuRegisterPair()); 2105 __ vnegd(FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()), 2106 FromLowSToD(in.AsFpuRegisterPairLow<SRegister>())); 2107 break; 2108 2109 default: 2110 LOG(FATAL) << "Unexpected neg type " << neg->GetResultType(); 2111 } 2112} 2113 2114void LocationsBuilderARM::VisitTypeConversion(HTypeConversion* conversion) { 2115 Primitive::Type result_type = conversion->GetResultType(); 2116 Primitive::Type input_type = conversion->GetInputType(); 2117 DCHECK_NE(result_type, input_type); 2118 2119 // The float-to-long, double-to-long and long-to-float type conversions 2120 // rely on a call to the runtime. 2121 LocationSummary::CallKind call_kind = 2122 (((input_type == Primitive::kPrimFloat || input_type == Primitive::kPrimDouble) 2123 && result_type == Primitive::kPrimLong) 2124 || (input_type == Primitive::kPrimLong && result_type == Primitive::kPrimFloat)) 2125 ? LocationSummary::kCall 2126 : LocationSummary::kNoCall; 2127 LocationSummary* locations = 2128 new (GetGraph()->GetArena()) LocationSummary(conversion, call_kind); 2129 2130 // The Java language does not allow treating boolean as an integral type but 2131 // our bit representation makes it safe. 2132 2133 switch (result_type) { 2134 case Primitive::kPrimByte: 2135 switch (input_type) { 2136 case Primitive::kPrimBoolean: 2137 // Boolean input is a result of code transformations. 2138 case Primitive::kPrimShort: 2139 case Primitive::kPrimInt: 2140 case Primitive::kPrimChar: 2141 // Processing a Dex `int-to-byte' instruction. 2142 locations->SetInAt(0, Location::RequiresRegister()); 2143 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 2144 break; 2145 2146 default: 2147 LOG(FATAL) << "Unexpected type conversion from " << input_type 2148 << " to " << result_type; 2149 } 2150 break; 2151 2152 case Primitive::kPrimShort: 2153 switch (input_type) { 2154 case Primitive::kPrimBoolean: 2155 // Boolean input is a result of code transformations. 2156 case Primitive::kPrimByte: 2157 case Primitive::kPrimInt: 2158 case Primitive::kPrimChar: 2159 // Processing a Dex `int-to-short' instruction. 2160 locations->SetInAt(0, Location::RequiresRegister()); 2161 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 2162 break; 2163 2164 default: 2165 LOG(FATAL) << "Unexpected type conversion from " << input_type 2166 << " to " << result_type; 2167 } 2168 break; 2169 2170 case Primitive::kPrimInt: 2171 switch (input_type) { 2172 case Primitive::kPrimLong: 2173 // Processing a Dex `long-to-int' instruction. 2174 locations->SetInAt(0, Location::Any()); 2175 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 2176 break; 2177 2178 case Primitive::kPrimFloat: 2179 // Processing a Dex `float-to-int' instruction. 2180 locations->SetInAt(0, Location::RequiresFpuRegister()); 2181 locations->SetOut(Location::RequiresRegister()); 2182 locations->AddTemp(Location::RequiresFpuRegister()); 2183 break; 2184 2185 case Primitive::kPrimDouble: 2186 // Processing a Dex `double-to-int' instruction. 2187 locations->SetInAt(0, Location::RequiresFpuRegister()); 2188 locations->SetOut(Location::RequiresRegister()); 2189 locations->AddTemp(Location::RequiresFpuRegister()); 2190 break; 2191 2192 default: 2193 LOG(FATAL) << "Unexpected type conversion from " << input_type 2194 << " to " << result_type; 2195 } 2196 break; 2197 2198 case Primitive::kPrimLong: 2199 switch (input_type) { 2200 case Primitive::kPrimBoolean: 2201 // Boolean input is a result of code transformations. 2202 case Primitive::kPrimByte: 2203 case Primitive::kPrimShort: 2204 case Primitive::kPrimInt: 2205 case Primitive::kPrimChar: 2206 // Processing a Dex `int-to-long' instruction. 2207 locations->SetInAt(0, Location::RequiresRegister()); 2208 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 2209 break; 2210 2211 case Primitive::kPrimFloat: { 2212 // Processing a Dex `float-to-long' instruction. 2213 InvokeRuntimeCallingConvention calling_convention; 2214 locations->SetInAt(0, Location::FpuRegisterLocation( 2215 calling_convention.GetFpuRegisterAt(0))); 2216 locations->SetOut(Location::RegisterPairLocation(R0, R1)); 2217 break; 2218 } 2219 2220 case Primitive::kPrimDouble: { 2221 // Processing a Dex `double-to-long' instruction. 2222 InvokeRuntimeCallingConvention calling_convention; 2223 locations->SetInAt(0, Location::FpuRegisterPairLocation( 2224 calling_convention.GetFpuRegisterAt(0), 2225 calling_convention.GetFpuRegisterAt(1))); 2226 locations->SetOut(Location::RegisterPairLocation(R0, R1)); 2227 break; 2228 } 2229 2230 default: 2231 LOG(FATAL) << "Unexpected type conversion from " << input_type 2232 << " to " << result_type; 2233 } 2234 break; 2235 2236 case Primitive::kPrimChar: 2237 switch (input_type) { 2238 case Primitive::kPrimBoolean: 2239 // Boolean input is a result of code transformations. 2240 case Primitive::kPrimByte: 2241 case Primitive::kPrimShort: 2242 case Primitive::kPrimInt: 2243 // Processing a Dex `int-to-char' instruction. 2244 locations->SetInAt(0, Location::RequiresRegister()); 2245 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 2246 break; 2247 2248 default: 2249 LOG(FATAL) << "Unexpected type conversion from " << input_type 2250 << " to " << result_type; 2251 } 2252 break; 2253 2254 case Primitive::kPrimFloat: 2255 switch (input_type) { 2256 case Primitive::kPrimBoolean: 2257 // Boolean input is a result of code transformations. 2258 case Primitive::kPrimByte: 2259 case Primitive::kPrimShort: 2260 case Primitive::kPrimInt: 2261 case Primitive::kPrimChar: 2262 // Processing a Dex `int-to-float' instruction. 2263 locations->SetInAt(0, Location::RequiresRegister()); 2264 locations->SetOut(Location::RequiresFpuRegister()); 2265 break; 2266 2267 case Primitive::kPrimLong: { 2268 // Processing a Dex `long-to-float' instruction. 2269 InvokeRuntimeCallingConvention calling_convention; 2270 locations->SetInAt(0, Location::RegisterPairLocation( 2271 calling_convention.GetRegisterAt(0), calling_convention.GetRegisterAt(1))); 2272 locations->SetOut(Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(0))); 2273 break; 2274 } 2275 2276 case Primitive::kPrimDouble: 2277 // Processing a Dex `double-to-float' instruction. 2278 locations->SetInAt(0, Location::RequiresFpuRegister()); 2279 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); 2280 break; 2281 2282 default: 2283 LOG(FATAL) << "Unexpected type conversion from " << input_type 2284 << " to " << result_type; 2285 }; 2286 break; 2287 2288 case Primitive::kPrimDouble: 2289 switch (input_type) { 2290 case Primitive::kPrimBoolean: 2291 // Boolean input is a result of code transformations. 2292 case Primitive::kPrimByte: 2293 case Primitive::kPrimShort: 2294 case Primitive::kPrimInt: 2295 case Primitive::kPrimChar: 2296 // Processing a Dex `int-to-double' instruction. 2297 locations->SetInAt(0, Location::RequiresRegister()); 2298 locations->SetOut(Location::RequiresFpuRegister()); 2299 break; 2300 2301 case Primitive::kPrimLong: 2302 // Processing a Dex `long-to-double' instruction. 2303 locations->SetInAt(0, Location::RequiresRegister()); 2304 locations->SetOut(Location::RequiresFpuRegister()); 2305 locations->AddTemp(Location::RequiresFpuRegister()); 2306 locations->AddTemp(Location::RequiresFpuRegister()); 2307 break; 2308 2309 case Primitive::kPrimFloat: 2310 // Processing a Dex `float-to-double' instruction. 2311 locations->SetInAt(0, Location::RequiresFpuRegister()); 2312 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); 2313 break; 2314 2315 default: 2316 LOG(FATAL) << "Unexpected type conversion from " << input_type 2317 << " to " << result_type; 2318 }; 2319 break; 2320 2321 default: 2322 LOG(FATAL) << "Unexpected type conversion from " << input_type 2323 << " to " << result_type; 2324 } 2325} 2326 2327void InstructionCodeGeneratorARM::VisitTypeConversion(HTypeConversion* conversion) { 2328 LocationSummary* locations = conversion->GetLocations(); 2329 Location out = locations->Out(); 2330 Location in = locations->InAt(0); 2331 Primitive::Type result_type = conversion->GetResultType(); 2332 Primitive::Type input_type = conversion->GetInputType(); 2333 DCHECK_NE(result_type, input_type); 2334 switch (result_type) { 2335 case Primitive::kPrimByte: 2336 switch (input_type) { 2337 case Primitive::kPrimBoolean: 2338 // Boolean input is a result of code transformations. 2339 case Primitive::kPrimShort: 2340 case Primitive::kPrimInt: 2341 case Primitive::kPrimChar: 2342 // Processing a Dex `int-to-byte' instruction. 2343 __ sbfx(out.AsRegister<Register>(), in.AsRegister<Register>(), 0, 8); 2344 break; 2345 2346 default: 2347 LOG(FATAL) << "Unexpected type conversion from " << input_type 2348 << " to " << result_type; 2349 } 2350 break; 2351 2352 case Primitive::kPrimShort: 2353 switch (input_type) { 2354 case Primitive::kPrimBoolean: 2355 // Boolean input is a result of code transformations. 2356 case Primitive::kPrimByte: 2357 case Primitive::kPrimInt: 2358 case Primitive::kPrimChar: 2359 // Processing a Dex `int-to-short' instruction. 2360 __ sbfx(out.AsRegister<Register>(), in.AsRegister<Register>(), 0, 16); 2361 break; 2362 2363 default: 2364 LOG(FATAL) << "Unexpected type conversion from " << input_type 2365 << " to " << result_type; 2366 } 2367 break; 2368 2369 case Primitive::kPrimInt: 2370 switch (input_type) { 2371 case Primitive::kPrimLong: 2372 // Processing a Dex `long-to-int' instruction. 2373 DCHECK(out.IsRegister()); 2374 if (in.IsRegisterPair()) { 2375 __ Mov(out.AsRegister<Register>(), in.AsRegisterPairLow<Register>()); 2376 } else if (in.IsDoubleStackSlot()) { 2377 __ LoadFromOffset(kLoadWord, out.AsRegister<Register>(), SP, in.GetStackIndex()); 2378 } else { 2379 DCHECK(in.IsConstant()); 2380 DCHECK(in.GetConstant()->IsLongConstant()); 2381 int64_t value = in.GetConstant()->AsLongConstant()->GetValue(); 2382 __ LoadImmediate(out.AsRegister<Register>(), static_cast<int32_t>(value)); 2383 } 2384 break; 2385 2386 case Primitive::kPrimFloat: { 2387 // Processing a Dex `float-to-int' instruction. 2388 SRegister temp = locations->GetTemp(0).AsFpuRegisterPairLow<SRegister>(); 2389 __ vmovs(temp, in.AsFpuRegister<SRegister>()); 2390 __ vcvtis(temp, temp); 2391 __ vmovrs(out.AsRegister<Register>(), temp); 2392 break; 2393 } 2394 2395 case Primitive::kPrimDouble: { 2396 // Processing a Dex `double-to-int' instruction. 2397 SRegister temp_s = locations->GetTemp(0).AsFpuRegisterPairLow<SRegister>(); 2398 DRegister temp_d = FromLowSToD(temp_s); 2399 __ vmovd(temp_d, FromLowSToD(in.AsFpuRegisterPairLow<SRegister>())); 2400 __ vcvtid(temp_s, temp_d); 2401 __ vmovrs(out.AsRegister<Register>(), temp_s); 2402 break; 2403 } 2404 2405 default: 2406 LOG(FATAL) << "Unexpected type conversion from " << input_type 2407 << " to " << result_type; 2408 } 2409 break; 2410 2411 case Primitive::kPrimLong: 2412 switch (input_type) { 2413 case Primitive::kPrimBoolean: 2414 // Boolean input is a result of code transformations. 2415 case Primitive::kPrimByte: 2416 case Primitive::kPrimShort: 2417 case Primitive::kPrimInt: 2418 case Primitive::kPrimChar: 2419 // Processing a Dex `int-to-long' instruction. 2420 DCHECK(out.IsRegisterPair()); 2421 DCHECK(in.IsRegister()); 2422 __ Mov(out.AsRegisterPairLow<Register>(), in.AsRegister<Register>()); 2423 // Sign extension. 2424 __ Asr(out.AsRegisterPairHigh<Register>(), 2425 out.AsRegisterPairLow<Register>(), 2426 31); 2427 break; 2428 2429 case Primitive::kPrimFloat: 2430 // Processing a Dex `float-to-long' instruction. 2431 codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pF2l), 2432 conversion, 2433 conversion->GetDexPc(), 2434 nullptr); 2435 CheckEntrypointTypes<kQuickF2l, int64_t, float>(); 2436 break; 2437 2438 case Primitive::kPrimDouble: 2439 // Processing a Dex `double-to-long' instruction. 2440 codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pD2l), 2441 conversion, 2442 conversion->GetDexPc(), 2443 nullptr); 2444 CheckEntrypointTypes<kQuickD2l, int64_t, double>(); 2445 break; 2446 2447 default: 2448 LOG(FATAL) << "Unexpected type conversion from " << input_type 2449 << " to " << result_type; 2450 } 2451 break; 2452 2453 case Primitive::kPrimChar: 2454 switch (input_type) { 2455 case Primitive::kPrimBoolean: 2456 // Boolean input is a result of code transformations. 2457 case Primitive::kPrimByte: 2458 case Primitive::kPrimShort: 2459 case Primitive::kPrimInt: 2460 // Processing a Dex `int-to-char' instruction. 2461 __ ubfx(out.AsRegister<Register>(), in.AsRegister<Register>(), 0, 16); 2462 break; 2463 2464 default: 2465 LOG(FATAL) << "Unexpected type conversion from " << input_type 2466 << " to " << result_type; 2467 } 2468 break; 2469 2470 case Primitive::kPrimFloat: 2471 switch (input_type) { 2472 case Primitive::kPrimBoolean: 2473 // Boolean input is a result of code transformations. 2474 case Primitive::kPrimByte: 2475 case Primitive::kPrimShort: 2476 case Primitive::kPrimInt: 2477 case Primitive::kPrimChar: { 2478 // Processing a Dex `int-to-float' instruction. 2479 __ vmovsr(out.AsFpuRegister<SRegister>(), in.AsRegister<Register>()); 2480 __ vcvtsi(out.AsFpuRegister<SRegister>(), out.AsFpuRegister<SRegister>()); 2481 break; 2482 } 2483 2484 case Primitive::kPrimLong: 2485 // Processing a Dex `long-to-float' instruction. 2486 codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pL2f), 2487 conversion, 2488 conversion->GetDexPc(), 2489 nullptr); 2490 CheckEntrypointTypes<kQuickL2f, float, int64_t>(); 2491 break; 2492 2493 case Primitive::kPrimDouble: 2494 // Processing a Dex `double-to-float' instruction. 2495 __ vcvtsd(out.AsFpuRegister<SRegister>(), 2496 FromLowSToD(in.AsFpuRegisterPairLow<SRegister>())); 2497 break; 2498 2499 default: 2500 LOG(FATAL) << "Unexpected type conversion from " << input_type 2501 << " to " << result_type; 2502 }; 2503 break; 2504 2505 case Primitive::kPrimDouble: 2506 switch (input_type) { 2507 case Primitive::kPrimBoolean: 2508 // Boolean input is a result of code transformations. 2509 case Primitive::kPrimByte: 2510 case Primitive::kPrimShort: 2511 case Primitive::kPrimInt: 2512 case Primitive::kPrimChar: { 2513 // Processing a Dex `int-to-double' instruction. 2514 __ vmovsr(out.AsFpuRegisterPairLow<SRegister>(), in.AsRegister<Register>()); 2515 __ vcvtdi(FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()), 2516 out.AsFpuRegisterPairLow<SRegister>()); 2517 break; 2518 } 2519 2520 case Primitive::kPrimLong: { 2521 // Processing a Dex `long-to-double' instruction. 2522 Register low = in.AsRegisterPairLow<Register>(); 2523 Register high = in.AsRegisterPairHigh<Register>(); 2524 SRegister out_s = out.AsFpuRegisterPairLow<SRegister>(); 2525 DRegister out_d = FromLowSToD(out_s); 2526 SRegister temp_s = locations->GetTemp(0).AsFpuRegisterPairLow<SRegister>(); 2527 DRegister temp_d = FromLowSToD(temp_s); 2528 SRegister constant_s = locations->GetTemp(1).AsFpuRegisterPairLow<SRegister>(); 2529 DRegister constant_d = FromLowSToD(constant_s); 2530 2531 // temp_d = int-to-double(high) 2532 __ vmovsr(temp_s, high); 2533 __ vcvtdi(temp_d, temp_s); 2534 // constant_d = k2Pow32EncodingForDouble 2535 __ LoadDImmediate(constant_d, bit_cast<double, int64_t>(k2Pow32EncodingForDouble)); 2536 // out_d = unsigned-to-double(low) 2537 __ vmovsr(out_s, low); 2538 __ vcvtdu(out_d, out_s); 2539 // out_d += temp_d * constant_d 2540 __ vmlad(out_d, temp_d, constant_d); 2541 break; 2542 } 2543 2544 case Primitive::kPrimFloat: 2545 // Processing a Dex `float-to-double' instruction. 2546 __ vcvtds(FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()), 2547 in.AsFpuRegister<SRegister>()); 2548 break; 2549 2550 default: 2551 LOG(FATAL) << "Unexpected type conversion from " << input_type 2552 << " to " << result_type; 2553 }; 2554 break; 2555 2556 default: 2557 LOG(FATAL) << "Unexpected type conversion from " << input_type 2558 << " to " << result_type; 2559 } 2560} 2561 2562void LocationsBuilderARM::VisitAdd(HAdd* add) { 2563 LocationSummary* locations = 2564 new (GetGraph()->GetArena()) LocationSummary(add, LocationSummary::kNoCall); 2565 switch (add->GetResultType()) { 2566 case Primitive::kPrimInt: { 2567 locations->SetInAt(0, Location::RequiresRegister()); 2568 locations->SetInAt(1, Location::RegisterOrConstant(add->InputAt(1))); 2569 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 2570 break; 2571 } 2572 2573 case Primitive::kPrimLong: { 2574 locations->SetInAt(0, Location::RequiresRegister()); 2575 locations->SetInAt(1, Location::RequiresRegister()); 2576 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 2577 break; 2578 } 2579 2580 case Primitive::kPrimFloat: 2581 case Primitive::kPrimDouble: { 2582 locations->SetInAt(0, Location::RequiresFpuRegister()); 2583 locations->SetInAt(1, Location::RequiresFpuRegister()); 2584 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); 2585 break; 2586 } 2587 2588 default: 2589 LOG(FATAL) << "Unexpected add type " << add->GetResultType(); 2590 } 2591} 2592 2593void InstructionCodeGeneratorARM::VisitAdd(HAdd* add) { 2594 LocationSummary* locations = add->GetLocations(); 2595 Location out = locations->Out(); 2596 Location first = locations->InAt(0); 2597 Location second = locations->InAt(1); 2598 switch (add->GetResultType()) { 2599 case Primitive::kPrimInt: 2600 if (second.IsRegister()) { 2601 __ add(out.AsRegister<Register>(), 2602 first.AsRegister<Register>(), 2603 ShifterOperand(second.AsRegister<Register>())); 2604 } else { 2605 __ AddConstant(out.AsRegister<Register>(), 2606 first.AsRegister<Register>(), 2607 second.GetConstant()->AsIntConstant()->GetValue()); 2608 } 2609 break; 2610 2611 case Primitive::kPrimLong: { 2612 DCHECK(second.IsRegisterPair()); 2613 __ adds(out.AsRegisterPairLow<Register>(), 2614 first.AsRegisterPairLow<Register>(), 2615 ShifterOperand(second.AsRegisterPairLow<Register>())); 2616 __ adc(out.AsRegisterPairHigh<Register>(), 2617 first.AsRegisterPairHigh<Register>(), 2618 ShifterOperand(second.AsRegisterPairHigh<Register>())); 2619 break; 2620 } 2621 2622 case Primitive::kPrimFloat: 2623 __ vadds(out.AsFpuRegister<SRegister>(), 2624 first.AsFpuRegister<SRegister>(), 2625 second.AsFpuRegister<SRegister>()); 2626 break; 2627 2628 case Primitive::kPrimDouble: 2629 __ vaddd(FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()), 2630 FromLowSToD(first.AsFpuRegisterPairLow<SRegister>()), 2631 FromLowSToD(second.AsFpuRegisterPairLow<SRegister>())); 2632 break; 2633 2634 default: 2635 LOG(FATAL) << "Unexpected add type " << add->GetResultType(); 2636 } 2637} 2638 2639void LocationsBuilderARM::VisitSub(HSub* sub) { 2640 LocationSummary* locations = 2641 new (GetGraph()->GetArena()) LocationSummary(sub, LocationSummary::kNoCall); 2642 switch (sub->GetResultType()) { 2643 case Primitive::kPrimInt: { 2644 locations->SetInAt(0, Location::RequiresRegister()); 2645 locations->SetInAt(1, Location::RegisterOrConstant(sub->InputAt(1))); 2646 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 2647 break; 2648 } 2649 2650 case Primitive::kPrimLong: { 2651 locations->SetInAt(0, Location::RequiresRegister()); 2652 locations->SetInAt(1, Location::RequiresRegister()); 2653 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 2654 break; 2655 } 2656 case Primitive::kPrimFloat: 2657 case Primitive::kPrimDouble: { 2658 locations->SetInAt(0, Location::RequiresFpuRegister()); 2659 locations->SetInAt(1, Location::RequiresFpuRegister()); 2660 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); 2661 break; 2662 } 2663 default: 2664 LOG(FATAL) << "Unexpected sub type " << sub->GetResultType(); 2665 } 2666} 2667 2668void InstructionCodeGeneratorARM::VisitSub(HSub* sub) { 2669 LocationSummary* locations = sub->GetLocations(); 2670 Location out = locations->Out(); 2671 Location first = locations->InAt(0); 2672 Location second = locations->InAt(1); 2673 switch (sub->GetResultType()) { 2674 case Primitive::kPrimInt: { 2675 if (second.IsRegister()) { 2676 __ sub(out.AsRegister<Register>(), 2677 first.AsRegister<Register>(), 2678 ShifterOperand(second.AsRegister<Register>())); 2679 } else { 2680 __ AddConstant(out.AsRegister<Register>(), 2681 first.AsRegister<Register>(), 2682 -second.GetConstant()->AsIntConstant()->GetValue()); 2683 } 2684 break; 2685 } 2686 2687 case Primitive::kPrimLong: { 2688 DCHECK(second.IsRegisterPair()); 2689 __ subs(out.AsRegisterPairLow<Register>(), 2690 first.AsRegisterPairLow<Register>(), 2691 ShifterOperand(second.AsRegisterPairLow<Register>())); 2692 __ sbc(out.AsRegisterPairHigh<Register>(), 2693 first.AsRegisterPairHigh<Register>(), 2694 ShifterOperand(second.AsRegisterPairHigh<Register>())); 2695 break; 2696 } 2697 2698 case Primitive::kPrimFloat: { 2699 __ vsubs(out.AsFpuRegister<SRegister>(), 2700 first.AsFpuRegister<SRegister>(), 2701 second.AsFpuRegister<SRegister>()); 2702 break; 2703 } 2704 2705 case Primitive::kPrimDouble: { 2706 __ vsubd(FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()), 2707 FromLowSToD(first.AsFpuRegisterPairLow<SRegister>()), 2708 FromLowSToD(second.AsFpuRegisterPairLow<SRegister>())); 2709 break; 2710 } 2711 2712 2713 default: 2714 LOG(FATAL) << "Unexpected sub type " << sub->GetResultType(); 2715 } 2716} 2717 2718void LocationsBuilderARM::VisitMul(HMul* mul) { 2719 LocationSummary* locations = 2720 new (GetGraph()->GetArena()) LocationSummary(mul, LocationSummary::kNoCall); 2721 switch (mul->GetResultType()) { 2722 case Primitive::kPrimInt: 2723 case Primitive::kPrimLong: { 2724 locations->SetInAt(0, Location::RequiresRegister()); 2725 locations->SetInAt(1, Location::RequiresRegister()); 2726 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 2727 break; 2728 } 2729 2730 case Primitive::kPrimFloat: 2731 case Primitive::kPrimDouble: { 2732 locations->SetInAt(0, Location::RequiresFpuRegister()); 2733 locations->SetInAt(1, Location::RequiresFpuRegister()); 2734 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); 2735 break; 2736 } 2737 2738 default: 2739 LOG(FATAL) << "Unexpected mul type " << mul->GetResultType(); 2740 } 2741} 2742 2743void InstructionCodeGeneratorARM::VisitMul(HMul* mul) { 2744 LocationSummary* locations = mul->GetLocations(); 2745 Location out = locations->Out(); 2746 Location first = locations->InAt(0); 2747 Location second = locations->InAt(1); 2748 switch (mul->GetResultType()) { 2749 case Primitive::kPrimInt: { 2750 __ mul(out.AsRegister<Register>(), 2751 first.AsRegister<Register>(), 2752 second.AsRegister<Register>()); 2753 break; 2754 } 2755 case Primitive::kPrimLong: { 2756 Register out_hi = out.AsRegisterPairHigh<Register>(); 2757 Register out_lo = out.AsRegisterPairLow<Register>(); 2758 Register in1_hi = first.AsRegisterPairHigh<Register>(); 2759 Register in1_lo = first.AsRegisterPairLow<Register>(); 2760 Register in2_hi = second.AsRegisterPairHigh<Register>(); 2761 Register in2_lo = second.AsRegisterPairLow<Register>(); 2762 2763 // Extra checks to protect caused by the existence of R1_R2. 2764 // The algorithm is wrong if out.hi is either in1.lo or in2.lo: 2765 // (e.g. in1=r0_r1, in2=r2_r3 and out=r1_r2); 2766 DCHECK_NE(out_hi, in1_lo); 2767 DCHECK_NE(out_hi, in2_lo); 2768 2769 // input: in1 - 64 bits, in2 - 64 bits 2770 // output: out 2771 // formula: out.hi : out.lo = (in1.lo * in2.hi + in1.hi * in2.lo)* 2^32 + in1.lo * in2.lo 2772 // parts: out.hi = in1.lo * in2.hi + in1.hi * in2.lo + (in1.lo * in2.lo)[63:32] 2773 // parts: out.lo = (in1.lo * in2.lo)[31:0] 2774 2775 // IP <- in1.lo * in2.hi 2776 __ mul(IP, in1_lo, in2_hi); 2777 // out.hi <- in1.lo * in2.hi + in1.hi * in2.lo 2778 __ mla(out_hi, in1_hi, in2_lo, IP); 2779 // out.lo <- (in1.lo * in2.lo)[31:0]; 2780 __ umull(out_lo, IP, in1_lo, in2_lo); 2781 // out.hi <- in2.hi * in1.lo + in2.lo * in1.hi + (in1.lo * in2.lo)[63:32] 2782 __ add(out_hi, out_hi, ShifterOperand(IP)); 2783 break; 2784 } 2785 2786 case Primitive::kPrimFloat: { 2787 __ vmuls(out.AsFpuRegister<SRegister>(), 2788 first.AsFpuRegister<SRegister>(), 2789 second.AsFpuRegister<SRegister>()); 2790 break; 2791 } 2792 2793 case Primitive::kPrimDouble: { 2794 __ vmuld(FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()), 2795 FromLowSToD(first.AsFpuRegisterPairLow<SRegister>()), 2796 FromLowSToD(second.AsFpuRegisterPairLow<SRegister>())); 2797 break; 2798 } 2799 2800 default: 2801 LOG(FATAL) << "Unexpected mul type " << mul->GetResultType(); 2802 } 2803} 2804 2805void InstructionCodeGeneratorARM::DivRemOneOrMinusOne(HBinaryOperation* instruction) { 2806 DCHECK(instruction->IsDiv() || instruction->IsRem()); 2807 DCHECK(instruction->GetResultType() == Primitive::kPrimInt); 2808 2809 LocationSummary* locations = instruction->GetLocations(); 2810 Location second = locations->InAt(1); 2811 DCHECK(second.IsConstant()); 2812 2813 Register out = locations->Out().AsRegister<Register>(); 2814 Register dividend = locations->InAt(0).AsRegister<Register>(); 2815 int32_t imm = second.GetConstant()->AsIntConstant()->GetValue(); 2816 DCHECK(imm == 1 || imm == -1); 2817 2818 if (instruction->IsRem()) { 2819 __ LoadImmediate(out, 0); 2820 } else { 2821 if (imm == 1) { 2822 __ Mov(out, dividend); 2823 } else { 2824 __ rsb(out, dividend, ShifterOperand(0)); 2825 } 2826 } 2827} 2828 2829void InstructionCodeGeneratorARM::DivRemByPowerOfTwo(HBinaryOperation* instruction) { 2830 DCHECK(instruction->IsDiv() || instruction->IsRem()); 2831 DCHECK(instruction->GetResultType() == Primitive::kPrimInt); 2832 2833 LocationSummary* locations = instruction->GetLocations(); 2834 Location second = locations->InAt(1); 2835 DCHECK(second.IsConstant()); 2836 2837 Register out = locations->Out().AsRegister<Register>(); 2838 Register dividend = locations->InAt(0).AsRegister<Register>(); 2839 Register temp = locations->GetTemp(0).AsRegister<Register>(); 2840 int32_t imm = second.GetConstant()->AsIntConstant()->GetValue(); 2841 uint32_t abs_imm = static_cast<uint32_t>(std::abs(imm)); 2842 DCHECK(IsPowerOfTwo(abs_imm)); 2843 int ctz_imm = CTZ(abs_imm); 2844 2845 if (ctz_imm == 1) { 2846 __ Lsr(temp, dividend, 32 - ctz_imm); 2847 } else { 2848 __ Asr(temp, dividend, 31); 2849 __ Lsr(temp, temp, 32 - ctz_imm); 2850 } 2851 __ add(out, temp, ShifterOperand(dividend)); 2852 2853 if (instruction->IsDiv()) { 2854 __ Asr(out, out, ctz_imm); 2855 if (imm < 0) { 2856 __ rsb(out, out, ShifterOperand(0)); 2857 } 2858 } else { 2859 __ ubfx(out, out, 0, ctz_imm); 2860 __ sub(out, out, ShifterOperand(temp)); 2861 } 2862} 2863 2864void InstructionCodeGeneratorARM::GenerateDivRemWithAnyConstant(HBinaryOperation* instruction) { 2865 DCHECK(instruction->IsDiv() || instruction->IsRem()); 2866 DCHECK(instruction->GetResultType() == Primitive::kPrimInt); 2867 2868 LocationSummary* locations = instruction->GetLocations(); 2869 Location second = locations->InAt(1); 2870 DCHECK(second.IsConstant()); 2871 2872 Register out = locations->Out().AsRegister<Register>(); 2873 Register dividend = locations->InAt(0).AsRegister<Register>(); 2874 Register temp1 = locations->GetTemp(0).AsRegister<Register>(); 2875 Register temp2 = locations->GetTemp(1).AsRegister<Register>(); 2876 int64_t imm = second.GetConstant()->AsIntConstant()->GetValue(); 2877 2878 int64_t magic; 2879 int shift; 2880 CalculateMagicAndShiftForDivRem(imm, false /* is_long */, &magic, &shift); 2881 2882 __ LoadImmediate(temp1, magic); 2883 __ smull(temp2, temp1, dividend, temp1); 2884 2885 if (imm > 0 && magic < 0) { 2886 __ add(temp1, temp1, ShifterOperand(dividend)); 2887 } else if (imm < 0 && magic > 0) { 2888 __ sub(temp1, temp1, ShifterOperand(dividend)); 2889 } 2890 2891 if (shift != 0) { 2892 __ Asr(temp1, temp1, shift); 2893 } 2894 2895 if (instruction->IsDiv()) { 2896 __ sub(out, temp1, ShifterOperand(temp1, ASR, 31)); 2897 } else { 2898 __ sub(temp1, temp1, ShifterOperand(temp1, ASR, 31)); 2899 // TODO: Strength reduction for mls. 2900 __ LoadImmediate(temp2, imm); 2901 __ mls(out, temp1, temp2, dividend); 2902 } 2903} 2904 2905void InstructionCodeGeneratorARM::GenerateDivRemConstantIntegral(HBinaryOperation* instruction) { 2906 DCHECK(instruction->IsDiv() || instruction->IsRem()); 2907 DCHECK(instruction->GetResultType() == Primitive::kPrimInt); 2908 2909 LocationSummary* locations = instruction->GetLocations(); 2910 Location second = locations->InAt(1); 2911 DCHECK(second.IsConstant()); 2912 2913 int32_t imm = second.GetConstant()->AsIntConstant()->GetValue(); 2914 if (imm == 0) { 2915 // Do not generate anything. DivZeroCheck would prevent any code to be executed. 2916 } else if (imm == 1 || imm == -1) { 2917 DivRemOneOrMinusOne(instruction); 2918 } else if (IsPowerOfTwo(std::abs(imm))) { 2919 DivRemByPowerOfTwo(instruction); 2920 } else { 2921 DCHECK(imm <= -2 || imm >= 2); 2922 GenerateDivRemWithAnyConstant(instruction); 2923 } 2924} 2925 2926void LocationsBuilderARM::VisitDiv(HDiv* div) { 2927 LocationSummary::CallKind call_kind = LocationSummary::kNoCall; 2928 if (div->GetResultType() == Primitive::kPrimLong) { 2929 // pLdiv runtime call. 2930 call_kind = LocationSummary::kCall; 2931 } else if (div->GetResultType() == Primitive::kPrimInt && div->InputAt(1)->IsConstant()) { 2932 // sdiv will be replaced by other instruction sequence. 2933 } else if (div->GetResultType() == Primitive::kPrimInt && 2934 !codegen_->GetInstructionSetFeatures().HasDivideInstruction()) { 2935 // pIdivmod runtime call. 2936 call_kind = LocationSummary::kCall; 2937 } 2938 2939 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(div, call_kind); 2940 2941 switch (div->GetResultType()) { 2942 case Primitive::kPrimInt: { 2943 if (div->InputAt(1)->IsConstant()) { 2944 locations->SetInAt(0, Location::RequiresRegister()); 2945 locations->SetInAt(1, Location::ConstantLocation(div->InputAt(1)->AsConstant())); 2946 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 2947 int32_t abs_imm = std::abs(div->InputAt(1)->AsIntConstant()->GetValue()); 2948 if (abs_imm <= 1) { 2949 // No temp register required. 2950 } else { 2951 locations->AddTemp(Location::RequiresRegister()); 2952 if (!IsPowerOfTwo(abs_imm)) { 2953 locations->AddTemp(Location::RequiresRegister()); 2954 } 2955 } 2956 } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) { 2957 locations->SetInAt(0, Location::RequiresRegister()); 2958 locations->SetInAt(1, Location::RequiresRegister()); 2959 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 2960 } else { 2961 InvokeRuntimeCallingConvention calling_convention; 2962 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); 2963 locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); 2964 // Note: divrem will compute both the quotient and the remainder as the pair R0 and R1, but 2965 // we only need the former. 2966 locations->SetOut(Location::RegisterLocation(R0)); 2967 } 2968 break; 2969 } 2970 case Primitive::kPrimLong: { 2971 InvokeRuntimeCallingConvention calling_convention; 2972 locations->SetInAt(0, Location::RegisterPairLocation( 2973 calling_convention.GetRegisterAt(0), calling_convention.GetRegisterAt(1))); 2974 locations->SetInAt(1, Location::RegisterPairLocation( 2975 calling_convention.GetRegisterAt(2), calling_convention.GetRegisterAt(3))); 2976 locations->SetOut(Location::RegisterPairLocation(R0, R1)); 2977 break; 2978 } 2979 case Primitive::kPrimFloat: 2980 case Primitive::kPrimDouble: { 2981 locations->SetInAt(0, Location::RequiresFpuRegister()); 2982 locations->SetInAt(1, Location::RequiresFpuRegister()); 2983 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); 2984 break; 2985 } 2986 2987 default: 2988 LOG(FATAL) << "Unexpected div type " << div->GetResultType(); 2989 } 2990} 2991 2992void InstructionCodeGeneratorARM::VisitDiv(HDiv* div) { 2993 LocationSummary* locations = div->GetLocations(); 2994 Location out = locations->Out(); 2995 Location first = locations->InAt(0); 2996 Location second = locations->InAt(1); 2997 2998 switch (div->GetResultType()) { 2999 case Primitive::kPrimInt: { 3000 if (second.IsConstant()) { 3001 GenerateDivRemConstantIntegral(div); 3002 } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) { 3003 __ sdiv(out.AsRegister<Register>(), 3004 first.AsRegister<Register>(), 3005 second.AsRegister<Register>()); 3006 } else { 3007 InvokeRuntimeCallingConvention calling_convention; 3008 DCHECK_EQ(calling_convention.GetRegisterAt(0), first.AsRegister<Register>()); 3009 DCHECK_EQ(calling_convention.GetRegisterAt(1), second.AsRegister<Register>()); 3010 DCHECK_EQ(R0, out.AsRegister<Register>()); 3011 3012 codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pIdivmod), div, div->GetDexPc(), nullptr); 3013 CheckEntrypointTypes<kQuickIdivmod, int32_t, int32_t, int32_t>(); 3014 } 3015 break; 3016 } 3017 3018 case Primitive::kPrimLong: { 3019 InvokeRuntimeCallingConvention calling_convention; 3020 DCHECK_EQ(calling_convention.GetRegisterAt(0), first.AsRegisterPairLow<Register>()); 3021 DCHECK_EQ(calling_convention.GetRegisterAt(1), first.AsRegisterPairHigh<Register>()); 3022 DCHECK_EQ(calling_convention.GetRegisterAt(2), second.AsRegisterPairLow<Register>()); 3023 DCHECK_EQ(calling_convention.GetRegisterAt(3), second.AsRegisterPairHigh<Register>()); 3024 DCHECK_EQ(R0, out.AsRegisterPairLow<Register>()); 3025 DCHECK_EQ(R1, out.AsRegisterPairHigh<Register>()); 3026 3027 codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pLdiv), div, div->GetDexPc(), nullptr); 3028 CheckEntrypointTypes<kQuickLdiv, int64_t, int64_t, int64_t>(); 3029 break; 3030 } 3031 3032 case Primitive::kPrimFloat: { 3033 __ vdivs(out.AsFpuRegister<SRegister>(), 3034 first.AsFpuRegister<SRegister>(), 3035 second.AsFpuRegister<SRegister>()); 3036 break; 3037 } 3038 3039 case Primitive::kPrimDouble: { 3040 __ vdivd(FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()), 3041 FromLowSToD(first.AsFpuRegisterPairLow<SRegister>()), 3042 FromLowSToD(second.AsFpuRegisterPairLow<SRegister>())); 3043 break; 3044 } 3045 3046 default: 3047 LOG(FATAL) << "Unexpected div type " << div->GetResultType(); 3048 } 3049} 3050 3051void LocationsBuilderARM::VisitRem(HRem* rem) { 3052 Primitive::Type type = rem->GetResultType(); 3053 3054 // Most remainders are implemented in the runtime. 3055 LocationSummary::CallKind call_kind = LocationSummary::kCall; 3056 if (rem->GetResultType() == Primitive::kPrimInt && rem->InputAt(1)->IsConstant()) { 3057 // sdiv will be replaced by other instruction sequence. 3058 call_kind = LocationSummary::kNoCall; 3059 } else if ((rem->GetResultType() == Primitive::kPrimInt) 3060 && codegen_->GetInstructionSetFeatures().HasDivideInstruction()) { 3061 // Have hardware divide instruction for int, do it with three instructions. 3062 call_kind = LocationSummary::kNoCall; 3063 } 3064 3065 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(rem, call_kind); 3066 3067 switch (type) { 3068 case Primitive::kPrimInt: { 3069 if (rem->InputAt(1)->IsConstant()) { 3070 locations->SetInAt(0, Location::RequiresRegister()); 3071 locations->SetInAt(1, Location::ConstantLocation(rem->InputAt(1)->AsConstant())); 3072 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 3073 int32_t abs_imm = std::abs(rem->InputAt(1)->AsIntConstant()->GetValue()); 3074 if (abs_imm <= 1) { 3075 // No temp register required. 3076 } else { 3077 locations->AddTemp(Location::RequiresRegister()); 3078 if (!IsPowerOfTwo(abs_imm)) { 3079 locations->AddTemp(Location::RequiresRegister()); 3080 } 3081 } 3082 } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) { 3083 locations->SetInAt(0, Location::RequiresRegister()); 3084 locations->SetInAt(1, Location::RequiresRegister()); 3085 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 3086 locations->AddTemp(Location::RequiresRegister()); 3087 } else { 3088 InvokeRuntimeCallingConvention calling_convention; 3089 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); 3090 locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); 3091 // Note: divrem will compute both the quotient and the remainder as the pair R0 and R1, but 3092 // we only need the latter. 3093 locations->SetOut(Location::RegisterLocation(R1)); 3094 } 3095 break; 3096 } 3097 case Primitive::kPrimLong: { 3098 InvokeRuntimeCallingConvention calling_convention; 3099 locations->SetInAt(0, Location::RegisterPairLocation( 3100 calling_convention.GetRegisterAt(0), calling_convention.GetRegisterAt(1))); 3101 locations->SetInAt(1, Location::RegisterPairLocation( 3102 calling_convention.GetRegisterAt(2), calling_convention.GetRegisterAt(3))); 3103 // The runtime helper puts the output in R2,R3. 3104 locations->SetOut(Location::RegisterPairLocation(R2, R3)); 3105 break; 3106 } 3107 case Primitive::kPrimFloat: { 3108 InvokeRuntimeCallingConvention calling_convention; 3109 locations->SetInAt(0, Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(0))); 3110 locations->SetInAt(1, Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(1))); 3111 locations->SetOut(Location::FpuRegisterLocation(S0)); 3112 break; 3113 } 3114 3115 case Primitive::kPrimDouble: { 3116 InvokeRuntimeCallingConvention calling_convention; 3117 locations->SetInAt(0, Location::FpuRegisterPairLocation( 3118 calling_convention.GetFpuRegisterAt(0), calling_convention.GetFpuRegisterAt(1))); 3119 locations->SetInAt(1, Location::FpuRegisterPairLocation( 3120 calling_convention.GetFpuRegisterAt(2), calling_convention.GetFpuRegisterAt(3))); 3121 locations->SetOut(Location::Location::FpuRegisterPairLocation(S0, S1)); 3122 break; 3123 } 3124 3125 default: 3126 LOG(FATAL) << "Unexpected rem type " << type; 3127 } 3128} 3129 3130void InstructionCodeGeneratorARM::VisitRem(HRem* rem) { 3131 LocationSummary* locations = rem->GetLocations(); 3132 Location out = locations->Out(); 3133 Location first = locations->InAt(0); 3134 Location second = locations->InAt(1); 3135 3136 Primitive::Type type = rem->GetResultType(); 3137 switch (type) { 3138 case Primitive::kPrimInt: { 3139 if (second.IsConstant()) { 3140 GenerateDivRemConstantIntegral(rem); 3141 } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) { 3142 Register reg1 = first.AsRegister<Register>(); 3143 Register reg2 = second.AsRegister<Register>(); 3144 Register temp = locations->GetTemp(0).AsRegister<Register>(); 3145 3146 // temp = reg1 / reg2 (integer division) 3147 // dest = reg1 - temp * reg2 3148 __ sdiv(temp, reg1, reg2); 3149 __ mls(out.AsRegister<Register>(), temp, reg2, reg1); 3150 } else { 3151 InvokeRuntimeCallingConvention calling_convention; 3152 DCHECK_EQ(calling_convention.GetRegisterAt(0), first.AsRegister<Register>()); 3153 DCHECK_EQ(calling_convention.GetRegisterAt(1), second.AsRegister<Register>()); 3154 DCHECK_EQ(R1, out.AsRegister<Register>()); 3155 3156 codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pIdivmod), rem, rem->GetDexPc(), nullptr); 3157 CheckEntrypointTypes<kQuickIdivmod, int32_t, int32_t, int32_t>(); 3158 } 3159 break; 3160 } 3161 3162 case Primitive::kPrimLong: { 3163 codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pLmod), rem, rem->GetDexPc(), nullptr); 3164 CheckEntrypointTypes<kQuickLmod, int64_t, int64_t, int64_t>(); 3165 break; 3166 } 3167 3168 case Primitive::kPrimFloat: { 3169 codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pFmodf), rem, rem->GetDexPc(), nullptr); 3170 CheckEntrypointTypes<kQuickFmodf, float, float, float>(); 3171 break; 3172 } 3173 3174 case Primitive::kPrimDouble: { 3175 codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pFmod), rem, rem->GetDexPc(), nullptr); 3176 CheckEntrypointTypes<kQuickFmod, double, double, double>(); 3177 break; 3178 } 3179 3180 default: 3181 LOG(FATAL) << "Unexpected rem type " << type; 3182 } 3183} 3184 3185void LocationsBuilderARM::VisitDivZeroCheck(HDivZeroCheck* instruction) { 3186 LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock() 3187 ? LocationSummary::kCallOnSlowPath 3188 : LocationSummary::kNoCall; 3189 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind); 3190 locations->SetInAt(0, Location::RegisterOrConstant(instruction->InputAt(0))); 3191 if (instruction->HasUses()) { 3192 locations->SetOut(Location::SameAsFirstInput()); 3193 } 3194} 3195 3196void InstructionCodeGeneratorARM::VisitDivZeroCheck(HDivZeroCheck* instruction) { 3197 SlowPathCode* slow_path = new (GetGraph()->GetArena()) DivZeroCheckSlowPathARM(instruction); 3198 codegen_->AddSlowPath(slow_path); 3199 3200 LocationSummary* locations = instruction->GetLocations(); 3201 Location value = locations->InAt(0); 3202 3203 switch (instruction->GetType()) { 3204 case Primitive::kPrimByte: 3205 case Primitive::kPrimChar: 3206 case Primitive::kPrimShort: 3207 case Primitive::kPrimInt: { 3208 if (value.IsRegister()) { 3209 __ CompareAndBranchIfZero(value.AsRegister<Register>(), slow_path->GetEntryLabel()); 3210 } else { 3211 DCHECK(value.IsConstant()) << value; 3212 if (value.GetConstant()->AsIntConstant()->GetValue() == 0) { 3213 __ b(slow_path->GetEntryLabel()); 3214 } 3215 } 3216 break; 3217 } 3218 case Primitive::kPrimLong: { 3219 if (value.IsRegisterPair()) { 3220 __ orrs(IP, 3221 value.AsRegisterPairLow<Register>(), 3222 ShifterOperand(value.AsRegisterPairHigh<Register>())); 3223 __ b(slow_path->GetEntryLabel(), EQ); 3224 } else { 3225 DCHECK(value.IsConstant()) << value; 3226 if (value.GetConstant()->AsLongConstant()->GetValue() == 0) { 3227 __ b(slow_path->GetEntryLabel()); 3228 } 3229 } 3230 break; 3231 default: 3232 LOG(FATAL) << "Unexpected type for HDivZeroCheck " << instruction->GetType(); 3233 } 3234 } 3235} 3236 3237void LocationsBuilderARM::HandleShift(HBinaryOperation* op) { 3238 DCHECK(op->IsShl() || op->IsShr() || op->IsUShr()); 3239 3240 LocationSummary* locations = 3241 new (GetGraph()->GetArena()) LocationSummary(op, LocationSummary::kNoCall); 3242 3243 switch (op->GetResultType()) { 3244 case Primitive::kPrimInt: { 3245 locations->SetInAt(0, Location::RequiresRegister()); 3246 if (op->InputAt(1)->IsConstant()) { 3247 locations->SetInAt(1, Location::ConstantLocation(op->InputAt(1)->AsConstant())); 3248 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 3249 } else { 3250 locations->SetInAt(1, Location::RequiresRegister()); 3251 // Make the output overlap, as it will be used to hold the masked 3252 // second input. 3253 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); 3254 } 3255 break; 3256 } 3257 case Primitive::kPrimLong: { 3258 locations->SetInAt(0, Location::RequiresRegister()); 3259 if (op->InputAt(1)->IsConstant()) { 3260 locations->SetInAt(1, Location::ConstantLocation(op->InputAt(1)->AsConstant())); 3261 // For simplicity, use kOutputOverlap even though we only require that low registers 3262 // don't clash with high registers which the register allocator currently guarantees. 3263 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); 3264 } else { 3265 locations->SetInAt(1, Location::RequiresRegister()); 3266 locations->AddTemp(Location::RequiresRegister()); 3267 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); 3268 } 3269 break; 3270 } 3271 default: 3272 LOG(FATAL) << "Unexpected operation type " << op->GetResultType(); 3273 } 3274} 3275 3276void InstructionCodeGeneratorARM::HandleShift(HBinaryOperation* op) { 3277 DCHECK(op->IsShl() || op->IsShr() || op->IsUShr()); 3278 3279 LocationSummary* locations = op->GetLocations(); 3280 Location out = locations->Out(); 3281 Location first = locations->InAt(0); 3282 Location second = locations->InAt(1); 3283 3284 Primitive::Type type = op->GetResultType(); 3285 switch (type) { 3286 case Primitive::kPrimInt: { 3287 Register out_reg = out.AsRegister<Register>(); 3288 Register first_reg = first.AsRegister<Register>(); 3289 if (second.IsRegister()) { 3290 Register second_reg = second.AsRegister<Register>(); 3291 // Arm doesn't mask the shift count so we need to do it ourselves. 3292 __ and_(out_reg, second_reg, ShifterOperand(kMaxIntShiftValue)); 3293 if (op->IsShl()) { 3294 __ Lsl(out_reg, first_reg, out_reg); 3295 } else if (op->IsShr()) { 3296 __ Asr(out_reg, first_reg, out_reg); 3297 } else { 3298 __ Lsr(out_reg, first_reg, out_reg); 3299 } 3300 } else { 3301 int32_t cst = second.GetConstant()->AsIntConstant()->GetValue(); 3302 uint32_t shift_value = static_cast<uint32_t>(cst & kMaxIntShiftValue); 3303 if (shift_value == 0) { // arm does not support shifting with 0 immediate. 3304 __ Mov(out_reg, first_reg); 3305 } else if (op->IsShl()) { 3306 __ Lsl(out_reg, first_reg, shift_value); 3307 } else if (op->IsShr()) { 3308 __ Asr(out_reg, first_reg, shift_value); 3309 } else { 3310 __ Lsr(out_reg, first_reg, shift_value); 3311 } 3312 } 3313 break; 3314 } 3315 case Primitive::kPrimLong: { 3316 Register o_h = out.AsRegisterPairHigh<Register>(); 3317 Register o_l = out.AsRegisterPairLow<Register>(); 3318 3319 Register high = first.AsRegisterPairHigh<Register>(); 3320 Register low = first.AsRegisterPairLow<Register>(); 3321 3322 if (second.IsRegister()) { 3323 Register temp = locations->GetTemp(0).AsRegister<Register>(); 3324 3325 Register second_reg = second.AsRegister<Register>(); 3326 3327 if (op->IsShl()) { 3328 __ and_(o_l, second_reg, ShifterOperand(kMaxLongShiftValue)); 3329 // Shift the high part 3330 __ Lsl(o_h, high, o_l); 3331 // Shift the low part and `or` what overflew on the high part 3332 __ rsb(temp, o_l, ShifterOperand(kArmBitsPerWord)); 3333 __ Lsr(temp, low, temp); 3334 __ orr(o_h, o_h, ShifterOperand(temp)); 3335 // If the shift is > 32 bits, override the high part 3336 __ subs(temp, o_l, ShifterOperand(kArmBitsPerWord)); 3337 __ it(PL); 3338 __ Lsl(o_h, low, temp, PL); 3339 // Shift the low part 3340 __ Lsl(o_l, low, o_l); 3341 } else if (op->IsShr()) { 3342 __ and_(o_h, second_reg, ShifterOperand(kMaxLongShiftValue)); 3343 // Shift the low part 3344 __ Lsr(o_l, low, o_h); 3345 // Shift the high part and `or` what underflew on the low part 3346 __ rsb(temp, o_h, ShifterOperand(kArmBitsPerWord)); 3347 __ Lsl(temp, high, temp); 3348 __ orr(o_l, o_l, ShifterOperand(temp)); 3349 // If the shift is > 32 bits, override the low part 3350 __ subs(temp, o_h, ShifterOperand(kArmBitsPerWord)); 3351 __ it(PL); 3352 __ Asr(o_l, high, temp, PL); 3353 // Shift the high part 3354 __ Asr(o_h, high, o_h); 3355 } else { 3356 __ and_(o_h, second_reg, ShifterOperand(kMaxLongShiftValue)); 3357 // same as Shr except we use `Lsr`s and not `Asr`s 3358 __ Lsr(o_l, low, o_h); 3359 __ rsb(temp, o_h, ShifterOperand(kArmBitsPerWord)); 3360 __ Lsl(temp, high, temp); 3361 __ orr(o_l, o_l, ShifterOperand(temp)); 3362 __ subs(temp, o_h, ShifterOperand(kArmBitsPerWord)); 3363 __ it(PL); 3364 __ Lsr(o_l, high, temp, PL); 3365 __ Lsr(o_h, high, o_h); 3366 } 3367 } else { 3368 // Register allocator doesn't create partial overlap. 3369 DCHECK_NE(o_l, high); 3370 DCHECK_NE(o_h, low); 3371 int32_t cst = second.GetConstant()->AsIntConstant()->GetValue(); 3372 uint32_t shift_value = static_cast<uint32_t>(cst & kMaxLongShiftValue); 3373 if (shift_value > 32) { 3374 if (op->IsShl()) { 3375 __ Lsl(o_h, low, shift_value - 32); 3376 __ LoadImmediate(o_l, 0); 3377 } else if (op->IsShr()) { 3378 __ Asr(o_l, high, shift_value - 32); 3379 __ Asr(o_h, high, 31); 3380 } else { 3381 __ Lsr(o_l, high, shift_value - 32); 3382 __ LoadImmediate(o_h, 0); 3383 } 3384 } else if (shift_value == 32) { 3385 if (op->IsShl()) { 3386 __ mov(o_h, ShifterOperand(low)); 3387 __ LoadImmediate(o_l, 0); 3388 } else if (op->IsShr()) { 3389 __ mov(o_l, ShifterOperand(high)); 3390 __ Asr(o_h, high, 31); 3391 } else { 3392 __ mov(o_l, ShifterOperand(high)); 3393 __ LoadImmediate(o_h, 0); 3394 } 3395 } else if (shift_value == 1) { 3396 if (op->IsShl()) { 3397 __ Lsls(o_l, low, 1); 3398 __ adc(o_h, high, ShifterOperand(high)); 3399 } else if (op->IsShr()) { 3400 __ Asrs(o_h, high, 1); 3401 __ Rrx(o_l, low); 3402 } else { 3403 __ Lsrs(o_h, high, 1); 3404 __ Rrx(o_l, low); 3405 } 3406 } else { 3407 DCHECK(2 <= shift_value && shift_value < 32) << shift_value; 3408 if (op->IsShl()) { 3409 __ Lsl(o_h, high, shift_value); 3410 __ orr(o_h, o_h, ShifterOperand(low, LSR, 32 - shift_value)); 3411 __ Lsl(o_l, low, shift_value); 3412 } else if (op->IsShr()) { 3413 __ Lsr(o_l, low, shift_value); 3414 __ orr(o_l, o_l, ShifterOperand(high, LSL, 32 - shift_value)); 3415 __ Asr(o_h, high, shift_value); 3416 } else { 3417 __ Lsr(o_l, low, shift_value); 3418 __ orr(o_l, o_l, ShifterOperand(high, LSL, 32 - shift_value)); 3419 __ Lsr(o_h, high, shift_value); 3420 } 3421 } 3422 } 3423 break; 3424 } 3425 default: 3426 LOG(FATAL) << "Unexpected operation type " << type; 3427 UNREACHABLE(); 3428 } 3429} 3430 3431void LocationsBuilderARM::VisitShl(HShl* shl) { 3432 HandleShift(shl); 3433} 3434 3435void InstructionCodeGeneratorARM::VisitShl(HShl* shl) { 3436 HandleShift(shl); 3437} 3438 3439void LocationsBuilderARM::VisitShr(HShr* shr) { 3440 HandleShift(shr); 3441} 3442 3443void InstructionCodeGeneratorARM::VisitShr(HShr* shr) { 3444 HandleShift(shr); 3445} 3446 3447void LocationsBuilderARM::VisitUShr(HUShr* ushr) { 3448 HandleShift(ushr); 3449} 3450 3451void InstructionCodeGeneratorARM::VisitUShr(HUShr* ushr) { 3452 HandleShift(ushr); 3453} 3454 3455void LocationsBuilderARM::VisitNewInstance(HNewInstance* instruction) { 3456 LocationSummary* locations = 3457 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall); 3458 InvokeRuntimeCallingConvention calling_convention; 3459 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); 3460 locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); 3461 locations->SetOut(Location::RegisterLocation(R0)); 3462} 3463 3464void InstructionCodeGeneratorARM::VisitNewInstance(HNewInstance* instruction) { 3465 // Note: if heap poisoning is enabled, the entry point takes cares 3466 // of poisoning the reference. 3467 codegen_->InvokeRuntime(instruction->GetEntrypoint(), 3468 instruction, 3469 instruction->GetDexPc(), 3470 nullptr); 3471 CheckEntrypointTypes<kQuickAllocObjectWithAccessCheck, void*, uint32_t, ArtMethod*>(); 3472} 3473 3474void LocationsBuilderARM::VisitNewArray(HNewArray* instruction) { 3475 LocationSummary* locations = 3476 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall); 3477 InvokeRuntimeCallingConvention calling_convention; 3478 locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0))); 3479 locations->SetOut(Location::RegisterLocation(R0)); 3480 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); 3481 locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(2))); 3482} 3483 3484void InstructionCodeGeneratorARM::VisitNewArray(HNewArray* instruction) { 3485 InvokeRuntimeCallingConvention calling_convention; 3486 __ LoadImmediate(calling_convention.GetRegisterAt(0), instruction->GetTypeIndex()); 3487 // Note: if heap poisoning is enabled, the entry point takes cares 3488 // of poisoning the reference. 3489 codegen_->InvokeRuntime(instruction->GetEntrypoint(), 3490 instruction, 3491 instruction->GetDexPc(), 3492 nullptr); 3493 CheckEntrypointTypes<kQuickAllocArrayWithAccessCheck, void*, uint32_t, int32_t, ArtMethod*>(); 3494} 3495 3496void LocationsBuilderARM::VisitParameterValue(HParameterValue* instruction) { 3497 LocationSummary* locations = 3498 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); 3499 Location location = parameter_visitor_.GetNextLocation(instruction->GetType()); 3500 if (location.IsStackSlot()) { 3501 location = Location::StackSlot(location.GetStackIndex() + codegen_->GetFrameSize()); 3502 } else if (location.IsDoubleStackSlot()) { 3503 location = Location::DoubleStackSlot(location.GetStackIndex() + codegen_->GetFrameSize()); 3504 } 3505 locations->SetOut(location); 3506} 3507 3508void InstructionCodeGeneratorARM::VisitParameterValue( 3509 HParameterValue* instruction ATTRIBUTE_UNUSED) { 3510 // Nothing to do, the parameter is already at its location. 3511} 3512 3513void LocationsBuilderARM::VisitCurrentMethod(HCurrentMethod* instruction) { 3514 LocationSummary* locations = 3515 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); 3516 locations->SetOut(Location::RegisterLocation(kMethodRegisterArgument)); 3517} 3518 3519void InstructionCodeGeneratorARM::VisitCurrentMethod(HCurrentMethod* instruction ATTRIBUTE_UNUSED) { 3520 // Nothing to do, the method is already at its location. 3521} 3522 3523void LocationsBuilderARM::VisitNot(HNot* not_) { 3524 LocationSummary* locations = 3525 new (GetGraph()->GetArena()) LocationSummary(not_, LocationSummary::kNoCall); 3526 locations->SetInAt(0, Location::RequiresRegister()); 3527 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 3528} 3529 3530void InstructionCodeGeneratorARM::VisitNot(HNot* not_) { 3531 LocationSummary* locations = not_->GetLocations(); 3532 Location out = locations->Out(); 3533 Location in = locations->InAt(0); 3534 switch (not_->GetResultType()) { 3535 case Primitive::kPrimInt: 3536 __ mvn(out.AsRegister<Register>(), ShifterOperand(in.AsRegister<Register>())); 3537 break; 3538 3539 case Primitive::kPrimLong: 3540 __ mvn(out.AsRegisterPairLow<Register>(), 3541 ShifterOperand(in.AsRegisterPairLow<Register>())); 3542 __ mvn(out.AsRegisterPairHigh<Register>(), 3543 ShifterOperand(in.AsRegisterPairHigh<Register>())); 3544 break; 3545 3546 default: 3547 LOG(FATAL) << "Unimplemented type for not operation " << not_->GetResultType(); 3548 } 3549} 3550 3551void LocationsBuilderARM::VisitBooleanNot(HBooleanNot* bool_not) { 3552 LocationSummary* locations = 3553 new (GetGraph()->GetArena()) LocationSummary(bool_not, LocationSummary::kNoCall); 3554 locations->SetInAt(0, Location::RequiresRegister()); 3555 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 3556} 3557 3558void InstructionCodeGeneratorARM::VisitBooleanNot(HBooleanNot* bool_not) { 3559 LocationSummary* locations = bool_not->GetLocations(); 3560 Location out = locations->Out(); 3561 Location in = locations->InAt(0); 3562 __ eor(out.AsRegister<Register>(), in.AsRegister<Register>(), ShifterOperand(1)); 3563} 3564 3565void LocationsBuilderARM::VisitCompare(HCompare* compare) { 3566 LocationSummary* locations = 3567 new (GetGraph()->GetArena()) LocationSummary(compare, LocationSummary::kNoCall); 3568 switch (compare->InputAt(0)->GetType()) { 3569 case Primitive::kPrimLong: { 3570 locations->SetInAt(0, Location::RequiresRegister()); 3571 locations->SetInAt(1, Location::RequiresRegister()); 3572 // Output overlaps because it is written before doing the low comparison. 3573 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); 3574 break; 3575 } 3576 case Primitive::kPrimFloat: 3577 case Primitive::kPrimDouble: { 3578 locations->SetInAt(0, Location::RequiresFpuRegister()); 3579 locations->SetInAt(1, Location::RequiresFpuRegister()); 3580 locations->SetOut(Location::RequiresRegister()); 3581 break; 3582 } 3583 default: 3584 LOG(FATAL) << "Unexpected type for compare operation " << compare->InputAt(0)->GetType(); 3585 } 3586} 3587 3588void InstructionCodeGeneratorARM::VisitCompare(HCompare* compare) { 3589 LocationSummary* locations = compare->GetLocations(); 3590 Register out = locations->Out().AsRegister<Register>(); 3591 Location left = locations->InAt(0); 3592 Location right = locations->InAt(1); 3593 3594 Label less, greater, done; 3595 Primitive::Type type = compare->InputAt(0)->GetType(); 3596 switch (type) { 3597 case Primitive::kPrimLong: { 3598 __ cmp(left.AsRegisterPairHigh<Register>(), 3599 ShifterOperand(right.AsRegisterPairHigh<Register>())); // Signed compare. 3600 __ b(&less, LT); 3601 __ b(&greater, GT); 3602 // Do LoadImmediate before the last `cmp`, as LoadImmediate might affect the status flags. 3603 __ LoadImmediate(out, 0); 3604 __ cmp(left.AsRegisterPairLow<Register>(), 3605 ShifterOperand(right.AsRegisterPairLow<Register>())); // Unsigned compare. 3606 break; 3607 } 3608 case Primitive::kPrimFloat: 3609 case Primitive::kPrimDouble: { 3610 __ LoadImmediate(out, 0); 3611 if (type == Primitive::kPrimFloat) { 3612 __ vcmps(left.AsFpuRegister<SRegister>(), right.AsFpuRegister<SRegister>()); 3613 } else { 3614 __ vcmpd(FromLowSToD(left.AsFpuRegisterPairLow<SRegister>()), 3615 FromLowSToD(right.AsFpuRegisterPairLow<SRegister>())); 3616 } 3617 __ vmstat(); // transfer FP status register to ARM APSR. 3618 __ b(compare->IsGtBias() ? &greater : &less, VS); // VS for unordered. 3619 break; 3620 } 3621 default: 3622 LOG(FATAL) << "Unexpected compare type " << type; 3623 } 3624 __ b(&done, EQ); 3625 __ b(&less, LO); // LO is for both: unsigned compare for longs and 'less than' for floats. 3626 3627 __ Bind(&greater); 3628 __ LoadImmediate(out, 1); 3629 __ b(&done); 3630 3631 __ Bind(&less); 3632 __ LoadImmediate(out, -1); 3633 3634 __ Bind(&done); 3635} 3636 3637void LocationsBuilderARM::VisitPhi(HPhi* instruction) { 3638 LocationSummary* locations = 3639 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); 3640 for (size_t i = 0, e = instruction->InputCount(); i < e; ++i) { 3641 locations->SetInAt(i, Location::Any()); 3642 } 3643 locations->SetOut(Location::Any()); 3644} 3645 3646void InstructionCodeGeneratorARM::VisitPhi(HPhi* instruction ATTRIBUTE_UNUSED) { 3647 LOG(FATAL) << "Unreachable"; 3648} 3649 3650void InstructionCodeGeneratorARM::GenerateMemoryBarrier(MemBarrierKind kind) { 3651 // TODO (ported from quick): revisit Arm barrier kinds 3652 DmbOptions flavor = DmbOptions::ISH; // quiet c++ warnings 3653 switch (kind) { 3654 case MemBarrierKind::kAnyStore: 3655 case MemBarrierKind::kLoadAny: 3656 case MemBarrierKind::kAnyAny: { 3657 flavor = DmbOptions::ISH; 3658 break; 3659 } 3660 case MemBarrierKind::kStoreStore: { 3661 flavor = DmbOptions::ISHST; 3662 break; 3663 } 3664 default: 3665 LOG(FATAL) << "Unexpected memory barrier " << kind; 3666 } 3667 __ dmb(flavor); 3668} 3669 3670void InstructionCodeGeneratorARM::GenerateWideAtomicLoad(Register addr, 3671 uint32_t offset, 3672 Register out_lo, 3673 Register out_hi) { 3674 if (offset != 0) { 3675 // Ensure `out_lo` is different from `addr`, so that loading 3676 // `offset` into `out_lo` does not clutter `addr`. 3677 DCHECK_NE(out_lo, addr); 3678 __ LoadImmediate(out_lo, offset); 3679 __ add(IP, addr, ShifterOperand(out_lo)); 3680 addr = IP; 3681 } 3682 __ ldrexd(out_lo, out_hi, addr); 3683} 3684 3685void InstructionCodeGeneratorARM::GenerateWideAtomicStore(Register addr, 3686 uint32_t offset, 3687 Register value_lo, 3688 Register value_hi, 3689 Register temp1, 3690 Register temp2, 3691 HInstruction* instruction) { 3692 Label fail; 3693 if (offset != 0) { 3694 __ LoadImmediate(temp1, offset); 3695 __ add(IP, addr, ShifterOperand(temp1)); 3696 addr = IP; 3697 } 3698 __ Bind(&fail); 3699 // We need a load followed by store. (The address used in a STREX instruction must 3700 // be the same as the address in the most recently executed LDREX instruction.) 3701 __ ldrexd(temp1, temp2, addr); 3702 codegen_->MaybeRecordImplicitNullCheck(instruction); 3703 __ strexd(temp1, value_lo, value_hi, addr); 3704 __ CompareAndBranchIfNonZero(temp1, &fail); 3705} 3706 3707void LocationsBuilderARM::HandleFieldSet(HInstruction* instruction, const FieldInfo& field_info) { 3708 DCHECK(instruction->IsInstanceFieldSet() || instruction->IsStaticFieldSet()); 3709 3710 LocationSummary* locations = 3711 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); 3712 locations->SetInAt(0, Location::RequiresRegister()); 3713 3714 Primitive::Type field_type = field_info.GetFieldType(); 3715 if (Primitive::IsFloatingPointType(field_type)) { 3716 locations->SetInAt(1, Location::RequiresFpuRegister()); 3717 } else { 3718 locations->SetInAt(1, Location::RequiresRegister()); 3719 } 3720 3721 bool is_wide = field_type == Primitive::kPrimLong || field_type == Primitive::kPrimDouble; 3722 bool generate_volatile = field_info.IsVolatile() 3723 && is_wide 3724 && !codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd(); 3725 bool needs_write_barrier = 3726 CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1)); 3727 // Temporary registers for the write barrier. 3728 // TODO: consider renaming StoreNeedsWriteBarrier to StoreNeedsGCMark. 3729 if (needs_write_barrier) { 3730 locations->AddTemp(Location::RequiresRegister()); // Possibly used for reference poisoning too. 3731 locations->AddTemp(Location::RequiresRegister()); 3732 } else if (generate_volatile) { 3733 // Arm encoding have some additional constraints for ldrexd/strexd: 3734 // - registers need to be consecutive 3735 // - the first register should be even but not R14. 3736 // We don't test for Arm yet, and the assertion makes sure that we revisit this if we ever 3737 // enable Arm encoding. 3738 DCHECK_EQ(InstructionSet::kThumb2, codegen_->GetInstructionSet()); 3739 3740 locations->AddTemp(Location::RequiresRegister()); 3741 locations->AddTemp(Location::RequiresRegister()); 3742 if (field_type == Primitive::kPrimDouble) { 3743 // For doubles we need two more registers to copy the value. 3744 locations->AddTemp(Location::RegisterLocation(R2)); 3745 locations->AddTemp(Location::RegisterLocation(R3)); 3746 } 3747 } 3748} 3749 3750void InstructionCodeGeneratorARM::HandleFieldSet(HInstruction* instruction, 3751 const FieldInfo& field_info, 3752 bool value_can_be_null) { 3753 DCHECK(instruction->IsInstanceFieldSet() || instruction->IsStaticFieldSet()); 3754 3755 LocationSummary* locations = instruction->GetLocations(); 3756 Register base = locations->InAt(0).AsRegister<Register>(); 3757 Location value = locations->InAt(1); 3758 3759 bool is_volatile = field_info.IsVolatile(); 3760 bool atomic_ldrd_strd = codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd(); 3761 Primitive::Type field_type = field_info.GetFieldType(); 3762 uint32_t offset = field_info.GetFieldOffset().Uint32Value(); 3763 bool needs_write_barrier = 3764 CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1)); 3765 3766 if (is_volatile) { 3767 GenerateMemoryBarrier(MemBarrierKind::kAnyStore); 3768 } 3769 3770 switch (field_type) { 3771 case Primitive::kPrimBoolean: 3772 case Primitive::kPrimByte: { 3773 __ StoreToOffset(kStoreByte, value.AsRegister<Register>(), base, offset); 3774 break; 3775 } 3776 3777 case Primitive::kPrimShort: 3778 case Primitive::kPrimChar: { 3779 __ StoreToOffset(kStoreHalfword, value.AsRegister<Register>(), base, offset); 3780 break; 3781 } 3782 3783 case Primitive::kPrimInt: 3784 case Primitive::kPrimNot: { 3785 if (kPoisonHeapReferences && needs_write_barrier) { 3786 // Note that in the case where `value` is a null reference, 3787 // we do not enter this block, as a null reference does not 3788 // need poisoning. 3789 DCHECK_EQ(field_type, Primitive::kPrimNot); 3790 Register temp = locations->GetTemp(0).AsRegister<Register>(); 3791 __ Mov(temp, value.AsRegister<Register>()); 3792 __ PoisonHeapReference(temp); 3793 __ StoreToOffset(kStoreWord, temp, base, offset); 3794 } else { 3795 __ StoreToOffset(kStoreWord, value.AsRegister<Register>(), base, offset); 3796 } 3797 break; 3798 } 3799 3800 case Primitive::kPrimLong: { 3801 if (is_volatile && !atomic_ldrd_strd) { 3802 GenerateWideAtomicStore(base, offset, 3803 value.AsRegisterPairLow<Register>(), 3804 value.AsRegisterPairHigh<Register>(), 3805 locations->GetTemp(0).AsRegister<Register>(), 3806 locations->GetTemp(1).AsRegister<Register>(), 3807 instruction); 3808 } else { 3809 __ StoreToOffset(kStoreWordPair, value.AsRegisterPairLow<Register>(), base, offset); 3810 codegen_->MaybeRecordImplicitNullCheck(instruction); 3811 } 3812 break; 3813 } 3814 3815 case Primitive::kPrimFloat: { 3816 __ StoreSToOffset(value.AsFpuRegister<SRegister>(), base, offset); 3817 break; 3818 } 3819 3820 case Primitive::kPrimDouble: { 3821 DRegister value_reg = FromLowSToD(value.AsFpuRegisterPairLow<SRegister>()); 3822 if (is_volatile && !atomic_ldrd_strd) { 3823 Register value_reg_lo = locations->GetTemp(0).AsRegister<Register>(); 3824 Register value_reg_hi = locations->GetTemp(1).AsRegister<Register>(); 3825 3826 __ vmovrrd(value_reg_lo, value_reg_hi, value_reg); 3827 3828 GenerateWideAtomicStore(base, offset, 3829 value_reg_lo, 3830 value_reg_hi, 3831 locations->GetTemp(2).AsRegister<Register>(), 3832 locations->GetTemp(3).AsRegister<Register>(), 3833 instruction); 3834 } else { 3835 __ StoreDToOffset(value_reg, base, offset); 3836 codegen_->MaybeRecordImplicitNullCheck(instruction); 3837 } 3838 break; 3839 } 3840 3841 case Primitive::kPrimVoid: 3842 LOG(FATAL) << "Unreachable type " << field_type; 3843 UNREACHABLE(); 3844 } 3845 3846 // Longs and doubles are handled in the switch. 3847 if (field_type != Primitive::kPrimLong && field_type != Primitive::kPrimDouble) { 3848 codegen_->MaybeRecordImplicitNullCheck(instruction); 3849 } 3850 3851 if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1))) { 3852 Register temp = locations->GetTemp(0).AsRegister<Register>(); 3853 Register card = locations->GetTemp(1).AsRegister<Register>(); 3854 codegen_->MarkGCCard( 3855 temp, card, base, value.AsRegister<Register>(), value_can_be_null); 3856 } 3857 3858 if (is_volatile) { 3859 GenerateMemoryBarrier(MemBarrierKind::kAnyAny); 3860 } 3861} 3862 3863void LocationsBuilderARM::HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info) { 3864 DCHECK(instruction->IsInstanceFieldGet() || instruction->IsStaticFieldGet()); 3865 3866 bool object_field_get_with_read_barrier = 3867 kEmitCompilerReadBarrier && (field_info.GetFieldType() == Primitive::kPrimNot); 3868 LocationSummary* locations = 3869 new (GetGraph()->GetArena()) LocationSummary(instruction, 3870 object_field_get_with_read_barrier ? 3871 LocationSummary::kCallOnSlowPath : 3872 LocationSummary::kNoCall); 3873 locations->SetInAt(0, Location::RequiresRegister()); 3874 3875 bool volatile_for_double = field_info.IsVolatile() 3876 && (field_info.GetFieldType() == Primitive::kPrimDouble) 3877 && !codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd(); 3878 // The output overlaps in case of volatile long: we don't want the 3879 // code generated by GenerateWideAtomicLoad to overwrite the 3880 // object's location. Likewise, in the case of an object field get 3881 // with read barriers enabled, we do not want the load to overwrite 3882 // the object's location, as we need it to emit the read barrier. 3883 bool overlap = (field_info.IsVolatile() && (field_info.GetFieldType() == Primitive::kPrimLong)) || 3884 object_field_get_with_read_barrier; 3885 3886 if (Primitive::IsFloatingPointType(instruction->GetType())) { 3887 locations->SetOut(Location::RequiresFpuRegister()); 3888 } else { 3889 locations->SetOut(Location::RequiresRegister(), 3890 (overlap ? Location::kOutputOverlap : Location::kNoOutputOverlap)); 3891 } 3892 if (volatile_for_double) { 3893 // Arm encoding have some additional constraints for ldrexd/strexd: 3894 // - registers need to be consecutive 3895 // - the first register should be even but not R14. 3896 // We don't test for Arm yet, and the assertion makes sure that we revisit this if we ever 3897 // enable Arm encoding. 3898 DCHECK_EQ(InstructionSet::kThumb2, codegen_->GetInstructionSet()); 3899 locations->AddTemp(Location::RequiresRegister()); 3900 locations->AddTemp(Location::RequiresRegister()); 3901 } 3902} 3903 3904Location LocationsBuilderARM::ArmEncodableConstantOrRegister(HInstruction* constant, 3905 Opcode opcode) { 3906 DCHECK(!Primitive::IsFloatingPointType(constant->GetType())); 3907 if (constant->IsConstant() && 3908 CanEncodeConstantAsImmediate(constant->AsConstant(), opcode)) { 3909 return Location::ConstantLocation(constant->AsConstant()); 3910 } 3911 return Location::RequiresRegister(); 3912} 3913 3914bool LocationsBuilderARM::CanEncodeConstantAsImmediate(HConstant* input_cst, 3915 Opcode opcode) { 3916 uint64_t value = static_cast<uint64_t>(Int64FromConstant(input_cst)); 3917 if (Primitive::Is64BitType(input_cst->GetType())) { 3918 return CanEncodeConstantAsImmediate(Low32Bits(value), opcode) && 3919 CanEncodeConstantAsImmediate(High32Bits(value), opcode); 3920 } else { 3921 return CanEncodeConstantAsImmediate(Low32Bits(value), opcode); 3922 } 3923} 3924 3925bool LocationsBuilderARM::CanEncodeConstantAsImmediate(uint32_t value, Opcode opcode) { 3926 ShifterOperand so; 3927 ArmAssembler* assembler = codegen_->GetAssembler(); 3928 if (assembler->ShifterOperandCanHold(kNoRegister, kNoRegister, opcode, value, &so)) { 3929 return true; 3930 } 3931 Opcode neg_opcode = kNoOperand; 3932 switch (opcode) { 3933 case AND: 3934 neg_opcode = BIC; 3935 break; 3936 case ORR: 3937 neg_opcode = ORN; 3938 break; 3939 default: 3940 return false; 3941 } 3942 return assembler->ShifterOperandCanHold(kNoRegister, kNoRegister, neg_opcode, ~value, &so); 3943} 3944 3945void InstructionCodeGeneratorARM::HandleFieldGet(HInstruction* instruction, 3946 const FieldInfo& field_info) { 3947 DCHECK(instruction->IsInstanceFieldGet() || instruction->IsStaticFieldGet()); 3948 3949 LocationSummary* locations = instruction->GetLocations(); 3950 Location base_loc = locations->InAt(0); 3951 Register base = base_loc.AsRegister<Register>(); 3952 Location out = locations->Out(); 3953 bool is_volatile = field_info.IsVolatile(); 3954 bool atomic_ldrd_strd = codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd(); 3955 Primitive::Type field_type = field_info.GetFieldType(); 3956 uint32_t offset = field_info.GetFieldOffset().Uint32Value(); 3957 3958 switch (field_type) { 3959 case Primitive::kPrimBoolean: { 3960 __ LoadFromOffset(kLoadUnsignedByte, out.AsRegister<Register>(), base, offset); 3961 break; 3962 } 3963 3964 case Primitive::kPrimByte: { 3965 __ LoadFromOffset(kLoadSignedByte, out.AsRegister<Register>(), base, offset); 3966 break; 3967 } 3968 3969 case Primitive::kPrimShort: { 3970 __ LoadFromOffset(kLoadSignedHalfword, out.AsRegister<Register>(), base, offset); 3971 break; 3972 } 3973 3974 case Primitive::kPrimChar: { 3975 __ LoadFromOffset(kLoadUnsignedHalfword, out.AsRegister<Register>(), base, offset); 3976 break; 3977 } 3978 3979 case Primitive::kPrimInt: 3980 case Primitive::kPrimNot: { 3981 __ LoadFromOffset(kLoadWord, out.AsRegister<Register>(), base, offset); 3982 break; 3983 } 3984 3985 case Primitive::kPrimLong: { 3986 if (is_volatile && !atomic_ldrd_strd) { 3987 GenerateWideAtomicLoad(base, offset, 3988 out.AsRegisterPairLow<Register>(), 3989 out.AsRegisterPairHigh<Register>()); 3990 } else { 3991 __ LoadFromOffset(kLoadWordPair, out.AsRegisterPairLow<Register>(), base, offset); 3992 } 3993 break; 3994 } 3995 3996 case Primitive::kPrimFloat: { 3997 __ LoadSFromOffset(out.AsFpuRegister<SRegister>(), base, offset); 3998 break; 3999 } 4000 4001 case Primitive::kPrimDouble: { 4002 DRegister out_reg = FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()); 4003 if (is_volatile && !atomic_ldrd_strd) { 4004 Register lo = locations->GetTemp(0).AsRegister<Register>(); 4005 Register hi = locations->GetTemp(1).AsRegister<Register>(); 4006 GenerateWideAtomicLoad(base, offset, lo, hi); 4007 codegen_->MaybeRecordImplicitNullCheck(instruction); 4008 __ vmovdrr(out_reg, lo, hi); 4009 } else { 4010 __ LoadDFromOffset(out_reg, base, offset); 4011 codegen_->MaybeRecordImplicitNullCheck(instruction); 4012 } 4013 break; 4014 } 4015 4016 case Primitive::kPrimVoid: 4017 LOG(FATAL) << "Unreachable type " << field_type; 4018 UNREACHABLE(); 4019 } 4020 4021 // Doubles are handled in the switch. 4022 if (field_type != Primitive::kPrimDouble) { 4023 codegen_->MaybeRecordImplicitNullCheck(instruction); 4024 } 4025 4026 if (is_volatile) { 4027 GenerateMemoryBarrier(MemBarrierKind::kLoadAny); 4028 } 4029 4030 if (field_type == Primitive::kPrimNot) { 4031 codegen_->MaybeGenerateReadBarrier(instruction, out, out, base_loc, offset); 4032 } 4033} 4034 4035void LocationsBuilderARM::VisitInstanceFieldSet(HInstanceFieldSet* instruction) { 4036 HandleFieldSet(instruction, instruction->GetFieldInfo()); 4037} 4038 4039void InstructionCodeGeneratorARM::VisitInstanceFieldSet(HInstanceFieldSet* instruction) { 4040 HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull()); 4041} 4042 4043void LocationsBuilderARM::VisitInstanceFieldGet(HInstanceFieldGet* instruction) { 4044 HandleFieldGet(instruction, instruction->GetFieldInfo()); 4045} 4046 4047void InstructionCodeGeneratorARM::VisitInstanceFieldGet(HInstanceFieldGet* instruction) { 4048 HandleFieldGet(instruction, instruction->GetFieldInfo()); 4049} 4050 4051void LocationsBuilderARM::VisitStaticFieldGet(HStaticFieldGet* instruction) { 4052 HandleFieldGet(instruction, instruction->GetFieldInfo()); 4053} 4054 4055void InstructionCodeGeneratorARM::VisitStaticFieldGet(HStaticFieldGet* instruction) { 4056 HandleFieldGet(instruction, instruction->GetFieldInfo()); 4057} 4058 4059void LocationsBuilderARM::VisitStaticFieldSet(HStaticFieldSet* instruction) { 4060 HandleFieldSet(instruction, instruction->GetFieldInfo()); 4061} 4062 4063void InstructionCodeGeneratorARM::VisitStaticFieldSet(HStaticFieldSet* instruction) { 4064 HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull()); 4065} 4066 4067void LocationsBuilderARM::VisitUnresolvedInstanceFieldGet( 4068 HUnresolvedInstanceFieldGet* instruction) { 4069 FieldAccessCallingConventionARM calling_convention; 4070 codegen_->CreateUnresolvedFieldLocationSummary( 4071 instruction, instruction->GetFieldType(), calling_convention); 4072} 4073 4074void InstructionCodeGeneratorARM::VisitUnresolvedInstanceFieldGet( 4075 HUnresolvedInstanceFieldGet* instruction) { 4076 FieldAccessCallingConventionARM calling_convention; 4077 codegen_->GenerateUnresolvedFieldAccess(instruction, 4078 instruction->GetFieldType(), 4079 instruction->GetFieldIndex(), 4080 instruction->GetDexPc(), 4081 calling_convention); 4082} 4083 4084void LocationsBuilderARM::VisitUnresolvedInstanceFieldSet( 4085 HUnresolvedInstanceFieldSet* instruction) { 4086 FieldAccessCallingConventionARM calling_convention; 4087 codegen_->CreateUnresolvedFieldLocationSummary( 4088 instruction, instruction->GetFieldType(), calling_convention); 4089} 4090 4091void InstructionCodeGeneratorARM::VisitUnresolvedInstanceFieldSet( 4092 HUnresolvedInstanceFieldSet* instruction) { 4093 FieldAccessCallingConventionARM calling_convention; 4094 codegen_->GenerateUnresolvedFieldAccess(instruction, 4095 instruction->GetFieldType(), 4096 instruction->GetFieldIndex(), 4097 instruction->GetDexPc(), 4098 calling_convention); 4099} 4100 4101void LocationsBuilderARM::VisitUnresolvedStaticFieldGet( 4102 HUnresolvedStaticFieldGet* instruction) { 4103 FieldAccessCallingConventionARM calling_convention; 4104 codegen_->CreateUnresolvedFieldLocationSummary( 4105 instruction, instruction->GetFieldType(), calling_convention); 4106} 4107 4108void InstructionCodeGeneratorARM::VisitUnresolvedStaticFieldGet( 4109 HUnresolvedStaticFieldGet* instruction) { 4110 FieldAccessCallingConventionARM calling_convention; 4111 codegen_->GenerateUnresolvedFieldAccess(instruction, 4112 instruction->GetFieldType(), 4113 instruction->GetFieldIndex(), 4114 instruction->GetDexPc(), 4115 calling_convention); 4116} 4117 4118void LocationsBuilderARM::VisitUnresolvedStaticFieldSet( 4119 HUnresolvedStaticFieldSet* instruction) { 4120 FieldAccessCallingConventionARM calling_convention; 4121 codegen_->CreateUnresolvedFieldLocationSummary( 4122 instruction, instruction->GetFieldType(), calling_convention); 4123} 4124 4125void InstructionCodeGeneratorARM::VisitUnresolvedStaticFieldSet( 4126 HUnresolvedStaticFieldSet* instruction) { 4127 FieldAccessCallingConventionARM calling_convention; 4128 codegen_->GenerateUnresolvedFieldAccess(instruction, 4129 instruction->GetFieldType(), 4130 instruction->GetFieldIndex(), 4131 instruction->GetDexPc(), 4132 calling_convention); 4133} 4134 4135void LocationsBuilderARM::VisitNullCheck(HNullCheck* instruction) { 4136 LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock() 4137 ? LocationSummary::kCallOnSlowPath 4138 : LocationSummary::kNoCall; 4139 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind); 4140 locations->SetInAt(0, Location::RequiresRegister()); 4141 if (instruction->HasUses()) { 4142 locations->SetOut(Location::SameAsFirstInput()); 4143 } 4144} 4145 4146void InstructionCodeGeneratorARM::GenerateImplicitNullCheck(HNullCheck* instruction) { 4147 if (codegen_->CanMoveNullCheckToUser(instruction)) { 4148 return; 4149 } 4150 Location obj = instruction->GetLocations()->InAt(0); 4151 4152 __ LoadFromOffset(kLoadWord, IP, obj.AsRegister<Register>(), 0); 4153 codegen_->RecordPcInfo(instruction, instruction->GetDexPc()); 4154} 4155 4156void InstructionCodeGeneratorARM::GenerateExplicitNullCheck(HNullCheck* instruction) { 4157 SlowPathCode* slow_path = new (GetGraph()->GetArena()) NullCheckSlowPathARM(instruction); 4158 codegen_->AddSlowPath(slow_path); 4159 4160 LocationSummary* locations = instruction->GetLocations(); 4161 Location obj = locations->InAt(0); 4162 4163 __ CompareAndBranchIfZero(obj.AsRegister<Register>(), slow_path->GetEntryLabel()); 4164} 4165 4166void InstructionCodeGeneratorARM::VisitNullCheck(HNullCheck* instruction) { 4167 if (codegen_->IsImplicitNullCheckAllowed(instruction)) { 4168 GenerateImplicitNullCheck(instruction); 4169 } else { 4170 GenerateExplicitNullCheck(instruction); 4171 } 4172} 4173 4174void LocationsBuilderARM::VisitArrayGet(HArrayGet* instruction) { 4175 bool object_array_get_with_read_barrier = 4176 kEmitCompilerReadBarrier && (instruction->GetType() == Primitive::kPrimNot); 4177 LocationSummary* locations = 4178 new (GetGraph()->GetArena()) LocationSummary(instruction, 4179 object_array_get_with_read_barrier ? 4180 LocationSummary::kCallOnSlowPath : 4181 LocationSummary::kNoCall); 4182 locations->SetInAt(0, Location::RequiresRegister()); 4183 locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1))); 4184 if (Primitive::IsFloatingPointType(instruction->GetType())) { 4185 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); 4186 } else { 4187 // The output overlaps in the case of an object array get with 4188 // read barriers enabled: we do not want the move to overwrite the 4189 // array's location, as we need it to emit the read barrier. 4190 locations->SetOut( 4191 Location::RequiresRegister(), 4192 object_array_get_with_read_barrier ? Location::kOutputOverlap : Location::kNoOutputOverlap); 4193 } 4194} 4195 4196void InstructionCodeGeneratorARM::VisitArrayGet(HArrayGet* instruction) { 4197 LocationSummary* locations = instruction->GetLocations(); 4198 Location obj_loc = locations->InAt(0); 4199 Register obj = obj_loc.AsRegister<Register>(); 4200 Location index = locations->InAt(1); 4201 Primitive::Type type = instruction->GetType(); 4202 4203 switch (type) { 4204 case Primitive::kPrimBoolean: { 4205 uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value(); 4206 Register out = locations->Out().AsRegister<Register>(); 4207 if (index.IsConstant()) { 4208 size_t offset = 4209 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset; 4210 __ LoadFromOffset(kLoadUnsignedByte, out, obj, offset); 4211 } else { 4212 __ add(IP, obj, ShifterOperand(index.AsRegister<Register>())); 4213 __ LoadFromOffset(kLoadUnsignedByte, out, IP, data_offset); 4214 } 4215 break; 4216 } 4217 4218 case Primitive::kPrimByte: { 4219 uint32_t data_offset = mirror::Array::DataOffset(sizeof(int8_t)).Uint32Value(); 4220 Register out = locations->Out().AsRegister<Register>(); 4221 if (index.IsConstant()) { 4222 size_t offset = 4223 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset; 4224 __ LoadFromOffset(kLoadSignedByte, out, obj, offset); 4225 } else { 4226 __ add(IP, obj, ShifterOperand(index.AsRegister<Register>())); 4227 __ LoadFromOffset(kLoadSignedByte, out, IP, data_offset); 4228 } 4229 break; 4230 } 4231 4232 case Primitive::kPrimShort: { 4233 uint32_t data_offset = mirror::Array::DataOffset(sizeof(int16_t)).Uint32Value(); 4234 Register out = locations->Out().AsRegister<Register>(); 4235 if (index.IsConstant()) { 4236 size_t offset = 4237 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset; 4238 __ LoadFromOffset(kLoadSignedHalfword, out, obj, offset); 4239 } else { 4240 __ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_2)); 4241 __ LoadFromOffset(kLoadSignedHalfword, out, IP, data_offset); 4242 } 4243 break; 4244 } 4245 4246 case Primitive::kPrimChar: { 4247 uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value(); 4248 Register out = locations->Out().AsRegister<Register>(); 4249 if (index.IsConstant()) { 4250 size_t offset = 4251 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset; 4252 __ LoadFromOffset(kLoadUnsignedHalfword, out, obj, offset); 4253 } else { 4254 __ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_2)); 4255 __ LoadFromOffset(kLoadUnsignedHalfword, out, IP, data_offset); 4256 } 4257 break; 4258 } 4259 4260 case Primitive::kPrimInt: 4261 case Primitive::kPrimNot: { 4262 static_assert( 4263 sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t), 4264 "art::mirror::HeapReference<mirror::Object> and int32_t have different sizes."); 4265 uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value(); 4266 Register out = locations->Out().AsRegister<Register>(); 4267 if (index.IsConstant()) { 4268 size_t offset = 4269 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; 4270 __ LoadFromOffset(kLoadWord, out, obj, offset); 4271 } else { 4272 __ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4)); 4273 __ LoadFromOffset(kLoadWord, out, IP, data_offset); 4274 } 4275 break; 4276 } 4277 4278 case Primitive::kPrimLong: { 4279 uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value(); 4280 Location out = locations->Out(); 4281 if (index.IsConstant()) { 4282 size_t offset = 4283 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset; 4284 __ LoadFromOffset(kLoadWordPair, out.AsRegisterPairLow<Register>(), obj, offset); 4285 } else { 4286 __ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_8)); 4287 __ LoadFromOffset(kLoadWordPair, out.AsRegisterPairLow<Register>(), IP, data_offset); 4288 } 4289 break; 4290 } 4291 4292 case Primitive::kPrimFloat: { 4293 uint32_t data_offset = mirror::Array::DataOffset(sizeof(float)).Uint32Value(); 4294 Location out = locations->Out(); 4295 DCHECK(out.IsFpuRegister()); 4296 if (index.IsConstant()) { 4297 size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; 4298 __ LoadSFromOffset(out.AsFpuRegister<SRegister>(), obj, offset); 4299 } else { 4300 __ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4)); 4301 __ LoadSFromOffset(out.AsFpuRegister<SRegister>(), IP, data_offset); 4302 } 4303 break; 4304 } 4305 4306 case Primitive::kPrimDouble: { 4307 uint32_t data_offset = mirror::Array::DataOffset(sizeof(double)).Uint32Value(); 4308 Location out = locations->Out(); 4309 DCHECK(out.IsFpuRegisterPair()); 4310 if (index.IsConstant()) { 4311 size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset; 4312 __ LoadDFromOffset(FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()), obj, offset); 4313 } else { 4314 __ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_8)); 4315 __ LoadDFromOffset(FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()), IP, data_offset); 4316 } 4317 break; 4318 } 4319 4320 case Primitive::kPrimVoid: 4321 LOG(FATAL) << "Unreachable type " << type; 4322 UNREACHABLE(); 4323 } 4324 codegen_->MaybeRecordImplicitNullCheck(instruction); 4325 4326 if (type == Primitive::kPrimNot) { 4327 static_assert( 4328 sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t), 4329 "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes."); 4330 uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value(); 4331 Location out = locations->Out(); 4332 if (index.IsConstant()) { 4333 uint32_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; 4334 codegen_->MaybeGenerateReadBarrier(instruction, out, out, obj_loc, offset); 4335 } else { 4336 codegen_->MaybeGenerateReadBarrier(instruction, out, out, obj_loc, data_offset, index); 4337 } 4338 } 4339} 4340 4341void LocationsBuilderARM::VisitArraySet(HArraySet* instruction) { 4342 Primitive::Type value_type = instruction->GetComponentType(); 4343 4344 bool needs_write_barrier = 4345 CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue()); 4346 bool may_need_runtime_call_for_type_check = instruction->NeedsTypeCheck(); 4347 bool object_array_set_with_read_barrier = 4348 kEmitCompilerReadBarrier && (value_type == Primitive::kPrimNot); 4349 4350 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary( 4351 instruction, 4352 (may_need_runtime_call_for_type_check || object_array_set_with_read_barrier) ? 4353 LocationSummary::kCallOnSlowPath : 4354 LocationSummary::kNoCall); 4355 4356 locations->SetInAt(0, Location::RequiresRegister()); 4357 locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1))); 4358 if (Primitive::IsFloatingPointType(value_type)) { 4359 locations->SetInAt(2, Location::RequiresFpuRegister()); 4360 } else { 4361 locations->SetInAt(2, Location::RequiresRegister()); 4362 } 4363 if (needs_write_barrier) { 4364 // Temporary registers for the write barrier. 4365 locations->AddTemp(Location::RequiresRegister()); // Possibly used for ref. poisoning too. 4366 locations->AddTemp(Location::RequiresRegister()); 4367 } 4368} 4369 4370void InstructionCodeGeneratorARM::VisitArraySet(HArraySet* instruction) { 4371 LocationSummary* locations = instruction->GetLocations(); 4372 Location array_loc = locations->InAt(0); 4373 Register array = array_loc.AsRegister<Register>(); 4374 Location index = locations->InAt(1); 4375 Primitive::Type value_type = instruction->GetComponentType(); 4376 bool may_need_runtime_call_for_type_check = instruction->NeedsTypeCheck(); 4377 bool needs_write_barrier = 4378 CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue()); 4379 4380 switch (value_type) { 4381 case Primitive::kPrimBoolean: 4382 case Primitive::kPrimByte: { 4383 uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value(); 4384 Register value = locations->InAt(2).AsRegister<Register>(); 4385 if (index.IsConstant()) { 4386 size_t offset = 4387 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset; 4388 __ StoreToOffset(kStoreByte, value, array, offset); 4389 } else { 4390 __ add(IP, array, ShifterOperand(index.AsRegister<Register>())); 4391 __ StoreToOffset(kStoreByte, value, IP, data_offset); 4392 } 4393 break; 4394 } 4395 4396 case Primitive::kPrimShort: 4397 case Primitive::kPrimChar: { 4398 uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value(); 4399 Register value = locations->InAt(2).AsRegister<Register>(); 4400 if (index.IsConstant()) { 4401 size_t offset = 4402 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset; 4403 __ StoreToOffset(kStoreHalfword, value, array, offset); 4404 } else { 4405 __ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_2)); 4406 __ StoreToOffset(kStoreHalfword, value, IP, data_offset); 4407 } 4408 break; 4409 } 4410 4411 case Primitive::kPrimNot: { 4412 uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value(); 4413 Location value_loc = locations->InAt(2); 4414 Register value = value_loc.AsRegister<Register>(); 4415 Register source = value; 4416 4417 if (instruction->InputAt(2)->IsNullConstant()) { 4418 // Just setting null. 4419 if (index.IsConstant()) { 4420 size_t offset = 4421 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; 4422 __ StoreToOffset(kStoreWord, source, array, offset); 4423 } else { 4424 DCHECK(index.IsRegister()) << index; 4425 __ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4)); 4426 __ StoreToOffset(kStoreWord, source, IP, data_offset); 4427 } 4428 DCHECK(!needs_write_barrier); 4429 DCHECK(!may_need_runtime_call_for_type_check); 4430 break; 4431 } 4432 4433 DCHECK(needs_write_barrier); 4434 Register temp1 = locations->GetTemp(0).AsRegister<Register>(); 4435 Register temp2 = locations->GetTemp(1).AsRegister<Register>(); 4436 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value(); 4437 uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value(); 4438 uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value(); 4439 Label done; 4440 SlowPathCode* slow_path = nullptr; 4441 4442 if (may_need_runtime_call_for_type_check) { 4443 slow_path = new (GetGraph()->GetArena()) ArraySetSlowPathARM(instruction); 4444 codegen_->AddSlowPath(slow_path); 4445 if (instruction->GetValueCanBeNull()) { 4446 Label non_zero; 4447 __ CompareAndBranchIfNonZero(value, &non_zero); 4448 if (index.IsConstant()) { 4449 size_t offset = 4450 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; 4451 __ StoreToOffset(kStoreWord, value, array, offset); 4452 } else { 4453 DCHECK(index.IsRegister()) << index; 4454 __ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4)); 4455 __ StoreToOffset(kStoreWord, value, IP, data_offset); 4456 } 4457 codegen_->MaybeRecordImplicitNullCheck(instruction); 4458 __ b(&done); 4459 __ Bind(&non_zero); 4460 } 4461 4462 if (kEmitCompilerReadBarrier) { 4463 // When read barriers are enabled, the type checking 4464 // instrumentation requires two read barriers: 4465 // 4466 // __ Mov(temp2, temp1); 4467 // // /* HeapReference<Class> */ temp1 = temp1->component_type_ 4468 // __ LoadFromOffset(kLoadWord, temp1, temp1, component_offset); 4469 // codegen_->GenerateReadBarrier( 4470 // instruction, temp1_loc, temp1_loc, temp2_loc, component_offset); 4471 // 4472 // // /* HeapReference<Class> */ temp2 = value->klass_ 4473 // __ LoadFromOffset(kLoadWord, temp2, value, class_offset); 4474 // codegen_->GenerateReadBarrier( 4475 // instruction, temp2_loc, temp2_loc, value_loc, class_offset, temp1_loc); 4476 // 4477 // __ cmp(temp1, ShifterOperand(temp2)); 4478 // 4479 // However, the second read barrier may trash `temp`, as it 4480 // is a temporary register, and as such would not be saved 4481 // along with live registers before calling the runtime (nor 4482 // restored afterwards). So in this case, we bail out and 4483 // delegate the work to the array set slow path. 4484 // 4485 // TODO: Extend the register allocator to support a new 4486 // "(locally) live temp" location so as to avoid always 4487 // going into the slow path when read barriers are enabled. 4488 __ b(slow_path->GetEntryLabel()); 4489 } else { 4490 // /* HeapReference<Class> */ temp1 = array->klass_ 4491 __ LoadFromOffset(kLoadWord, temp1, array, class_offset); 4492 codegen_->MaybeRecordImplicitNullCheck(instruction); 4493 __ MaybeUnpoisonHeapReference(temp1); 4494 4495 // /* HeapReference<Class> */ temp1 = temp1->component_type_ 4496 __ LoadFromOffset(kLoadWord, temp1, temp1, component_offset); 4497 // /* HeapReference<Class> */ temp2 = value->klass_ 4498 __ LoadFromOffset(kLoadWord, temp2, value, class_offset); 4499 // If heap poisoning is enabled, no need to unpoison `temp1` 4500 // nor `temp2`, as we are comparing two poisoned references. 4501 __ cmp(temp1, ShifterOperand(temp2)); 4502 4503 if (instruction->StaticTypeOfArrayIsObjectArray()) { 4504 Label do_put; 4505 __ b(&do_put, EQ); 4506 // If heap poisoning is enabled, the `temp1` reference has 4507 // not been unpoisoned yet; unpoison it now. 4508 __ MaybeUnpoisonHeapReference(temp1); 4509 4510 // /* HeapReference<Class> */ temp1 = temp1->super_class_ 4511 __ LoadFromOffset(kLoadWord, temp1, temp1, super_offset); 4512 // If heap poisoning is enabled, no need to unpoison 4513 // `temp1`, as we are comparing against null below. 4514 __ CompareAndBranchIfNonZero(temp1, slow_path->GetEntryLabel()); 4515 __ Bind(&do_put); 4516 } else { 4517 __ b(slow_path->GetEntryLabel(), NE); 4518 } 4519 } 4520 } 4521 4522 if (kPoisonHeapReferences) { 4523 // Note that in the case where `value` is a null reference, 4524 // we do not enter this block, as a null reference does not 4525 // need poisoning. 4526 DCHECK_EQ(value_type, Primitive::kPrimNot); 4527 __ Mov(temp1, value); 4528 __ PoisonHeapReference(temp1); 4529 source = temp1; 4530 } 4531 4532 if (index.IsConstant()) { 4533 size_t offset = 4534 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; 4535 __ StoreToOffset(kStoreWord, source, array, offset); 4536 } else { 4537 DCHECK(index.IsRegister()) << index; 4538 __ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4)); 4539 __ StoreToOffset(kStoreWord, source, IP, data_offset); 4540 } 4541 4542 if (!may_need_runtime_call_for_type_check) { 4543 codegen_->MaybeRecordImplicitNullCheck(instruction); 4544 } 4545 4546 codegen_->MarkGCCard(temp1, temp2, array, value, instruction->GetValueCanBeNull()); 4547 4548 if (done.IsLinked()) { 4549 __ Bind(&done); 4550 } 4551 4552 if (slow_path != nullptr) { 4553 __ Bind(slow_path->GetExitLabel()); 4554 } 4555 4556 break; 4557 } 4558 4559 case Primitive::kPrimInt: { 4560 uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value(); 4561 Register value = locations->InAt(2).AsRegister<Register>(); 4562 if (index.IsConstant()) { 4563 size_t offset = 4564 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; 4565 __ StoreToOffset(kStoreWord, value, array, offset); 4566 } else { 4567 DCHECK(index.IsRegister()) << index; 4568 __ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4)); 4569 __ StoreToOffset(kStoreWord, value, IP, data_offset); 4570 } 4571 4572 codegen_->MaybeRecordImplicitNullCheck(instruction); 4573 break; 4574 } 4575 4576 case Primitive::kPrimLong: { 4577 uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value(); 4578 Location value = locations->InAt(2); 4579 if (index.IsConstant()) { 4580 size_t offset = 4581 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset; 4582 __ StoreToOffset(kStoreWordPair, value.AsRegisterPairLow<Register>(), array, offset); 4583 } else { 4584 __ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_8)); 4585 __ StoreToOffset(kStoreWordPair, value.AsRegisterPairLow<Register>(), IP, data_offset); 4586 } 4587 break; 4588 } 4589 4590 case Primitive::kPrimFloat: { 4591 uint32_t data_offset = mirror::Array::DataOffset(sizeof(float)).Uint32Value(); 4592 Location value = locations->InAt(2); 4593 DCHECK(value.IsFpuRegister()); 4594 if (index.IsConstant()) { 4595 size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; 4596 __ StoreSToOffset(value.AsFpuRegister<SRegister>(), array, offset); 4597 } else { 4598 __ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4)); 4599 __ StoreSToOffset(value.AsFpuRegister<SRegister>(), IP, data_offset); 4600 } 4601 break; 4602 } 4603 4604 case Primitive::kPrimDouble: { 4605 uint32_t data_offset = mirror::Array::DataOffset(sizeof(double)).Uint32Value(); 4606 Location value = locations->InAt(2); 4607 DCHECK(value.IsFpuRegisterPair()); 4608 if (index.IsConstant()) { 4609 size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset; 4610 __ StoreDToOffset(FromLowSToD(value.AsFpuRegisterPairLow<SRegister>()), array, offset); 4611 } else { 4612 __ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_8)); 4613 __ StoreDToOffset(FromLowSToD(value.AsFpuRegisterPairLow<SRegister>()), IP, data_offset); 4614 } 4615 4616 break; 4617 } 4618 4619 case Primitive::kPrimVoid: 4620 LOG(FATAL) << "Unreachable type " << value_type; 4621 UNREACHABLE(); 4622 } 4623 4624 // Ints and objects are handled in the switch. 4625 if (value_type != Primitive::kPrimInt && value_type != Primitive::kPrimNot) { 4626 codegen_->MaybeRecordImplicitNullCheck(instruction); 4627 } 4628} 4629 4630void LocationsBuilderARM::VisitArrayLength(HArrayLength* instruction) { 4631 LocationSummary* locations = 4632 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); 4633 locations->SetInAt(0, Location::RequiresRegister()); 4634 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 4635} 4636 4637void InstructionCodeGeneratorARM::VisitArrayLength(HArrayLength* instruction) { 4638 LocationSummary* locations = instruction->GetLocations(); 4639 uint32_t offset = mirror::Array::LengthOffset().Uint32Value(); 4640 Register obj = locations->InAt(0).AsRegister<Register>(); 4641 Register out = locations->Out().AsRegister<Register>(); 4642 __ LoadFromOffset(kLoadWord, out, obj, offset); 4643 codegen_->MaybeRecordImplicitNullCheck(instruction); 4644} 4645 4646void LocationsBuilderARM::VisitBoundsCheck(HBoundsCheck* instruction) { 4647 LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock() 4648 ? LocationSummary::kCallOnSlowPath 4649 : LocationSummary::kNoCall; 4650 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind); 4651 locations->SetInAt(0, Location::RequiresRegister()); 4652 locations->SetInAt(1, Location::RequiresRegister()); 4653 if (instruction->HasUses()) { 4654 locations->SetOut(Location::SameAsFirstInput()); 4655 } 4656} 4657 4658void InstructionCodeGeneratorARM::VisitBoundsCheck(HBoundsCheck* instruction) { 4659 LocationSummary* locations = instruction->GetLocations(); 4660 SlowPathCode* slow_path = 4661 new (GetGraph()->GetArena()) BoundsCheckSlowPathARM(instruction); 4662 codegen_->AddSlowPath(slow_path); 4663 4664 Register index = locations->InAt(0).AsRegister<Register>(); 4665 Register length = locations->InAt(1).AsRegister<Register>(); 4666 4667 __ cmp(index, ShifterOperand(length)); 4668 __ b(slow_path->GetEntryLabel(), HS); 4669} 4670 4671void CodeGeneratorARM::MarkGCCard(Register temp, 4672 Register card, 4673 Register object, 4674 Register value, 4675 bool can_be_null) { 4676 Label is_null; 4677 if (can_be_null) { 4678 __ CompareAndBranchIfZero(value, &is_null); 4679 } 4680 __ LoadFromOffset(kLoadWord, card, TR, Thread::CardTableOffset<kArmWordSize>().Int32Value()); 4681 __ Lsr(temp, object, gc::accounting::CardTable::kCardShift); 4682 __ strb(card, Address(card, temp)); 4683 if (can_be_null) { 4684 __ Bind(&is_null); 4685 } 4686} 4687 4688void LocationsBuilderARM::VisitTemporary(HTemporary* temp) { 4689 temp->SetLocations(nullptr); 4690} 4691 4692void InstructionCodeGeneratorARM::VisitTemporary(HTemporary* temp ATTRIBUTE_UNUSED) { 4693 // Nothing to do, this is driven by the code generator. 4694} 4695 4696void LocationsBuilderARM::VisitParallelMove(HParallelMove* instruction ATTRIBUTE_UNUSED) { 4697 LOG(FATAL) << "Unreachable"; 4698} 4699 4700void InstructionCodeGeneratorARM::VisitParallelMove(HParallelMove* instruction) { 4701 codegen_->GetMoveResolver()->EmitNativeCode(instruction); 4702} 4703 4704void LocationsBuilderARM::VisitSuspendCheck(HSuspendCheck* instruction) { 4705 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnSlowPath); 4706} 4707 4708void InstructionCodeGeneratorARM::VisitSuspendCheck(HSuspendCheck* instruction) { 4709 HBasicBlock* block = instruction->GetBlock(); 4710 if (block->GetLoopInformation() != nullptr) { 4711 DCHECK(block->GetLoopInformation()->GetSuspendCheck() == instruction); 4712 // The back edge will generate the suspend check. 4713 return; 4714 } 4715 if (block->IsEntryBlock() && instruction->GetNext()->IsGoto()) { 4716 // The goto will generate the suspend check. 4717 return; 4718 } 4719 GenerateSuspendCheck(instruction, nullptr); 4720} 4721 4722void InstructionCodeGeneratorARM::GenerateSuspendCheck(HSuspendCheck* instruction, 4723 HBasicBlock* successor) { 4724 SuspendCheckSlowPathARM* slow_path = 4725 down_cast<SuspendCheckSlowPathARM*>(instruction->GetSlowPath()); 4726 if (slow_path == nullptr) { 4727 slow_path = new (GetGraph()->GetArena()) SuspendCheckSlowPathARM(instruction, successor); 4728 instruction->SetSlowPath(slow_path); 4729 codegen_->AddSlowPath(slow_path); 4730 if (successor != nullptr) { 4731 DCHECK(successor->IsLoopHeader()); 4732 codegen_->ClearSpillSlotsFromLoopPhisInStackMap(instruction); 4733 } 4734 } else { 4735 DCHECK_EQ(slow_path->GetSuccessor(), successor); 4736 } 4737 4738 __ LoadFromOffset( 4739 kLoadUnsignedHalfword, IP, TR, Thread::ThreadFlagsOffset<kArmWordSize>().Int32Value()); 4740 if (successor == nullptr) { 4741 __ CompareAndBranchIfNonZero(IP, slow_path->GetEntryLabel()); 4742 __ Bind(slow_path->GetReturnLabel()); 4743 } else { 4744 __ CompareAndBranchIfZero(IP, codegen_->GetLabelOf(successor)); 4745 __ b(slow_path->GetEntryLabel()); 4746 } 4747} 4748 4749ArmAssembler* ParallelMoveResolverARM::GetAssembler() const { 4750 return codegen_->GetAssembler(); 4751} 4752 4753void ParallelMoveResolverARM::EmitMove(size_t index) { 4754 MoveOperands* move = moves_[index]; 4755 Location source = move->GetSource(); 4756 Location destination = move->GetDestination(); 4757 4758 if (source.IsRegister()) { 4759 if (destination.IsRegister()) { 4760 __ Mov(destination.AsRegister<Register>(), source.AsRegister<Register>()); 4761 } else { 4762 DCHECK(destination.IsStackSlot()); 4763 __ StoreToOffset(kStoreWord, source.AsRegister<Register>(), 4764 SP, destination.GetStackIndex()); 4765 } 4766 } else if (source.IsStackSlot()) { 4767 if (destination.IsRegister()) { 4768 __ LoadFromOffset(kLoadWord, destination.AsRegister<Register>(), 4769 SP, source.GetStackIndex()); 4770 } else if (destination.IsFpuRegister()) { 4771 __ LoadSFromOffset(destination.AsFpuRegister<SRegister>(), SP, source.GetStackIndex()); 4772 } else { 4773 DCHECK(destination.IsStackSlot()); 4774 __ LoadFromOffset(kLoadWord, IP, SP, source.GetStackIndex()); 4775 __ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex()); 4776 } 4777 } else if (source.IsFpuRegister()) { 4778 if (destination.IsFpuRegister()) { 4779 __ vmovs(destination.AsFpuRegister<SRegister>(), source.AsFpuRegister<SRegister>()); 4780 } else { 4781 DCHECK(destination.IsStackSlot()); 4782 __ StoreSToOffset(source.AsFpuRegister<SRegister>(), SP, destination.GetStackIndex()); 4783 } 4784 } else if (source.IsDoubleStackSlot()) { 4785 if (destination.IsDoubleStackSlot()) { 4786 __ LoadDFromOffset(DTMP, SP, source.GetStackIndex()); 4787 __ StoreDToOffset(DTMP, SP, destination.GetStackIndex()); 4788 } else if (destination.IsRegisterPair()) { 4789 DCHECK(ExpectedPairLayout(destination)); 4790 __ LoadFromOffset( 4791 kLoadWordPair, destination.AsRegisterPairLow<Register>(), SP, source.GetStackIndex()); 4792 } else { 4793 DCHECK(destination.IsFpuRegisterPair()) << destination; 4794 __ LoadDFromOffset(FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()), 4795 SP, 4796 source.GetStackIndex()); 4797 } 4798 } else if (source.IsRegisterPair()) { 4799 if (destination.IsRegisterPair()) { 4800 __ Mov(destination.AsRegisterPairLow<Register>(), source.AsRegisterPairLow<Register>()); 4801 __ Mov(destination.AsRegisterPairHigh<Register>(), source.AsRegisterPairHigh<Register>()); 4802 } else { 4803 DCHECK(destination.IsDoubleStackSlot()) << destination; 4804 DCHECK(ExpectedPairLayout(source)); 4805 __ StoreToOffset( 4806 kStoreWordPair, source.AsRegisterPairLow<Register>(), SP, destination.GetStackIndex()); 4807 } 4808 } else if (source.IsFpuRegisterPair()) { 4809 if (destination.IsFpuRegisterPair()) { 4810 __ vmovd(FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()), 4811 FromLowSToD(source.AsFpuRegisterPairLow<SRegister>())); 4812 } else { 4813 DCHECK(destination.IsDoubleStackSlot()) << destination; 4814 __ StoreDToOffset(FromLowSToD(source.AsFpuRegisterPairLow<SRegister>()), 4815 SP, 4816 destination.GetStackIndex()); 4817 } 4818 } else { 4819 DCHECK(source.IsConstant()) << source; 4820 HConstant* constant = source.GetConstant(); 4821 if (constant->IsIntConstant() || constant->IsNullConstant()) { 4822 int32_t value = CodeGenerator::GetInt32ValueOf(constant); 4823 if (destination.IsRegister()) { 4824 __ LoadImmediate(destination.AsRegister<Register>(), value); 4825 } else { 4826 DCHECK(destination.IsStackSlot()); 4827 __ LoadImmediate(IP, value); 4828 __ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex()); 4829 } 4830 } else if (constant->IsLongConstant()) { 4831 int64_t value = constant->AsLongConstant()->GetValue(); 4832 if (destination.IsRegisterPair()) { 4833 __ LoadImmediate(destination.AsRegisterPairLow<Register>(), Low32Bits(value)); 4834 __ LoadImmediate(destination.AsRegisterPairHigh<Register>(), High32Bits(value)); 4835 } else { 4836 DCHECK(destination.IsDoubleStackSlot()) << destination; 4837 __ LoadImmediate(IP, Low32Bits(value)); 4838 __ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex()); 4839 __ LoadImmediate(IP, High32Bits(value)); 4840 __ StoreToOffset(kStoreWord, IP, SP, destination.GetHighStackIndex(kArmWordSize)); 4841 } 4842 } else if (constant->IsDoubleConstant()) { 4843 double value = constant->AsDoubleConstant()->GetValue(); 4844 if (destination.IsFpuRegisterPair()) { 4845 __ LoadDImmediate(FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()), value); 4846 } else { 4847 DCHECK(destination.IsDoubleStackSlot()) << destination; 4848 uint64_t int_value = bit_cast<uint64_t, double>(value); 4849 __ LoadImmediate(IP, Low32Bits(int_value)); 4850 __ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex()); 4851 __ LoadImmediate(IP, High32Bits(int_value)); 4852 __ StoreToOffset(kStoreWord, IP, SP, destination.GetHighStackIndex(kArmWordSize)); 4853 } 4854 } else { 4855 DCHECK(constant->IsFloatConstant()) << constant->DebugName(); 4856 float value = constant->AsFloatConstant()->GetValue(); 4857 if (destination.IsFpuRegister()) { 4858 __ LoadSImmediate(destination.AsFpuRegister<SRegister>(), value); 4859 } else { 4860 DCHECK(destination.IsStackSlot()); 4861 __ LoadImmediate(IP, bit_cast<int32_t, float>(value)); 4862 __ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex()); 4863 } 4864 } 4865 } 4866} 4867 4868void ParallelMoveResolverARM::Exchange(Register reg, int mem) { 4869 __ Mov(IP, reg); 4870 __ LoadFromOffset(kLoadWord, reg, SP, mem); 4871 __ StoreToOffset(kStoreWord, IP, SP, mem); 4872} 4873 4874void ParallelMoveResolverARM::Exchange(int mem1, int mem2) { 4875 ScratchRegisterScope ensure_scratch(this, IP, R0, codegen_->GetNumberOfCoreRegisters()); 4876 int stack_offset = ensure_scratch.IsSpilled() ? kArmWordSize : 0; 4877 __ LoadFromOffset(kLoadWord, static_cast<Register>(ensure_scratch.GetRegister()), 4878 SP, mem1 + stack_offset); 4879 __ LoadFromOffset(kLoadWord, IP, SP, mem2 + stack_offset); 4880 __ StoreToOffset(kStoreWord, static_cast<Register>(ensure_scratch.GetRegister()), 4881 SP, mem2 + stack_offset); 4882 __ StoreToOffset(kStoreWord, IP, SP, mem1 + stack_offset); 4883} 4884 4885void ParallelMoveResolverARM::EmitSwap(size_t index) { 4886 MoveOperands* move = moves_[index]; 4887 Location source = move->GetSource(); 4888 Location destination = move->GetDestination(); 4889 4890 if (source.IsRegister() && destination.IsRegister()) { 4891 DCHECK_NE(source.AsRegister<Register>(), IP); 4892 DCHECK_NE(destination.AsRegister<Register>(), IP); 4893 __ Mov(IP, source.AsRegister<Register>()); 4894 __ Mov(source.AsRegister<Register>(), destination.AsRegister<Register>()); 4895 __ Mov(destination.AsRegister<Register>(), IP); 4896 } else if (source.IsRegister() && destination.IsStackSlot()) { 4897 Exchange(source.AsRegister<Register>(), destination.GetStackIndex()); 4898 } else if (source.IsStackSlot() && destination.IsRegister()) { 4899 Exchange(destination.AsRegister<Register>(), source.GetStackIndex()); 4900 } else if (source.IsStackSlot() && destination.IsStackSlot()) { 4901 Exchange(source.GetStackIndex(), destination.GetStackIndex()); 4902 } else if (source.IsFpuRegister() && destination.IsFpuRegister()) { 4903 __ vmovrs(IP, source.AsFpuRegister<SRegister>()); 4904 __ vmovs(source.AsFpuRegister<SRegister>(), destination.AsFpuRegister<SRegister>()); 4905 __ vmovsr(destination.AsFpuRegister<SRegister>(), IP); 4906 } else if (source.IsRegisterPair() && destination.IsRegisterPair()) { 4907 __ vmovdrr(DTMP, source.AsRegisterPairLow<Register>(), source.AsRegisterPairHigh<Register>()); 4908 __ Mov(source.AsRegisterPairLow<Register>(), destination.AsRegisterPairLow<Register>()); 4909 __ Mov(source.AsRegisterPairHigh<Register>(), destination.AsRegisterPairHigh<Register>()); 4910 __ vmovrrd(destination.AsRegisterPairLow<Register>(), 4911 destination.AsRegisterPairHigh<Register>(), 4912 DTMP); 4913 } else if (source.IsRegisterPair() || destination.IsRegisterPair()) { 4914 Register low_reg = source.IsRegisterPair() 4915 ? source.AsRegisterPairLow<Register>() 4916 : destination.AsRegisterPairLow<Register>(); 4917 int mem = source.IsRegisterPair() 4918 ? destination.GetStackIndex() 4919 : source.GetStackIndex(); 4920 DCHECK(ExpectedPairLayout(source.IsRegisterPair() ? source : destination)); 4921 __ vmovdrr(DTMP, low_reg, static_cast<Register>(low_reg + 1)); 4922 __ LoadFromOffset(kLoadWordPair, low_reg, SP, mem); 4923 __ StoreDToOffset(DTMP, SP, mem); 4924 } else if (source.IsFpuRegisterPair() && destination.IsFpuRegisterPair()) { 4925 DRegister first = FromLowSToD(source.AsFpuRegisterPairLow<SRegister>()); 4926 DRegister second = FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()); 4927 __ vmovd(DTMP, first); 4928 __ vmovd(first, second); 4929 __ vmovd(second, DTMP); 4930 } else if (source.IsFpuRegisterPair() || destination.IsFpuRegisterPair()) { 4931 DRegister reg = source.IsFpuRegisterPair() 4932 ? FromLowSToD(source.AsFpuRegisterPairLow<SRegister>()) 4933 : FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()); 4934 int mem = source.IsFpuRegisterPair() 4935 ? destination.GetStackIndex() 4936 : source.GetStackIndex(); 4937 __ vmovd(DTMP, reg); 4938 __ LoadDFromOffset(reg, SP, mem); 4939 __ StoreDToOffset(DTMP, SP, mem); 4940 } else if (source.IsFpuRegister() || destination.IsFpuRegister()) { 4941 SRegister reg = source.IsFpuRegister() ? source.AsFpuRegister<SRegister>() 4942 : destination.AsFpuRegister<SRegister>(); 4943 int mem = source.IsFpuRegister() 4944 ? destination.GetStackIndex() 4945 : source.GetStackIndex(); 4946 4947 __ vmovrs(IP, reg); 4948 __ LoadSFromOffset(reg, SP, mem); 4949 __ StoreToOffset(kStoreWord, IP, SP, mem); 4950 } else if (source.IsDoubleStackSlot() && destination.IsDoubleStackSlot()) { 4951 Exchange(source.GetStackIndex(), destination.GetStackIndex()); 4952 Exchange(source.GetHighStackIndex(kArmWordSize), destination.GetHighStackIndex(kArmWordSize)); 4953 } else { 4954 LOG(FATAL) << "Unimplemented" << source << " <-> " << destination; 4955 } 4956} 4957 4958void ParallelMoveResolverARM::SpillScratch(int reg) { 4959 __ Push(static_cast<Register>(reg)); 4960} 4961 4962void ParallelMoveResolverARM::RestoreScratch(int reg) { 4963 __ Pop(static_cast<Register>(reg)); 4964} 4965 4966void LocationsBuilderARM::VisitLoadClass(HLoadClass* cls) { 4967 InvokeRuntimeCallingConvention calling_convention; 4968 CodeGenerator::CreateLoadClassLocationSummary( 4969 cls, 4970 Location::RegisterLocation(calling_convention.GetRegisterAt(0)), 4971 Location::RegisterLocation(R0), 4972 /* code_generator_supports_read_barrier */ true); 4973} 4974 4975void InstructionCodeGeneratorARM::VisitLoadClass(HLoadClass* cls) { 4976 LocationSummary* locations = cls->GetLocations(); 4977 if (cls->NeedsAccessCheck()) { 4978 codegen_->MoveConstant(locations->GetTemp(0), cls->GetTypeIndex()); 4979 codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pInitializeTypeAndVerifyAccess), 4980 cls, 4981 cls->GetDexPc(), 4982 nullptr); 4983 CheckEntrypointTypes<kQuickInitializeTypeAndVerifyAccess, void*, uint32_t>(); 4984 return; 4985 } 4986 4987 Location out_loc = locations->Out(); 4988 Register out = out_loc.AsRegister<Register>(); 4989 Register current_method = locations->InAt(0).AsRegister<Register>(); 4990 4991 if (cls->IsReferrersClass()) { 4992 DCHECK(!cls->CanCallRuntime()); 4993 DCHECK(!cls->MustGenerateClinitCheck()); 4994 uint32_t declaring_class_offset = ArtMethod::DeclaringClassOffset().Int32Value(); 4995 if (kEmitCompilerReadBarrier) { 4996 // /* GcRoot<mirror::Class>* */ out = &(current_method->declaring_class_) 4997 __ AddConstant(out, current_method, declaring_class_offset); 4998 // /* mirror::Class* */ out = out->Read() 4999 codegen_->GenerateReadBarrierForRoot(cls, out_loc, out_loc); 5000 } else { 5001 // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_ 5002 __ LoadFromOffset(kLoadWord, out, current_method, declaring_class_offset); 5003 } 5004 } else { 5005 // /* GcRoot<mirror::Class>[] */ out = 5006 // current_method.ptr_sized_fields_->dex_cache_resolved_types_ 5007 __ LoadFromOffset(kLoadWord, 5008 out, 5009 current_method, 5010 ArtMethod::DexCacheResolvedTypesOffset(kArmPointerSize).Int32Value()); 5011 5012 size_t cache_offset = CodeGenerator::GetCacheOffset(cls->GetTypeIndex()); 5013 if (kEmitCompilerReadBarrier) { 5014 // /* GcRoot<mirror::Class>* */ out = &out[type_index] 5015 __ AddConstant(out, out, cache_offset); 5016 // /* mirror::Class* */ out = out->Read() 5017 codegen_->GenerateReadBarrierForRoot(cls, out_loc, out_loc); 5018 } else { 5019 // /* GcRoot<mirror::Class> */ out = out[type_index] 5020 __ LoadFromOffset(kLoadWord, out, out, cache_offset); 5021 } 5022 5023 if (!cls->IsInDexCache() || cls->MustGenerateClinitCheck()) { 5024 DCHECK(cls->CanCallRuntime()); 5025 SlowPathCode* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathARM( 5026 cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck()); 5027 codegen_->AddSlowPath(slow_path); 5028 if (!cls->IsInDexCache()) { 5029 __ CompareAndBranchIfZero(out, slow_path->GetEntryLabel()); 5030 } 5031 if (cls->MustGenerateClinitCheck()) { 5032 GenerateClassInitializationCheck(slow_path, out); 5033 } else { 5034 __ Bind(slow_path->GetExitLabel()); 5035 } 5036 } 5037 } 5038} 5039 5040void LocationsBuilderARM::VisitClinitCheck(HClinitCheck* check) { 5041 LocationSummary* locations = 5042 new (GetGraph()->GetArena()) LocationSummary(check, LocationSummary::kCallOnSlowPath); 5043 locations->SetInAt(0, Location::RequiresRegister()); 5044 if (check->HasUses()) { 5045 locations->SetOut(Location::SameAsFirstInput()); 5046 } 5047} 5048 5049void InstructionCodeGeneratorARM::VisitClinitCheck(HClinitCheck* check) { 5050 // We assume the class is not null. 5051 SlowPathCode* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathARM( 5052 check->GetLoadClass(), check, check->GetDexPc(), true); 5053 codegen_->AddSlowPath(slow_path); 5054 GenerateClassInitializationCheck(slow_path, 5055 check->GetLocations()->InAt(0).AsRegister<Register>()); 5056} 5057 5058void InstructionCodeGeneratorARM::GenerateClassInitializationCheck( 5059 SlowPathCode* slow_path, Register class_reg) { 5060 __ LoadFromOffset(kLoadWord, IP, class_reg, mirror::Class::StatusOffset().Int32Value()); 5061 __ cmp(IP, ShifterOperand(mirror::Class::kStatusInitialized)); 5062 __ b(slow_path->GetEntryLabel(), LT); 5063 // Even if the initialized flag is set, we may be in a situation where caches are not synced 5064 // properly. Therefore, we do a memory fence. 5065 __ dmb(ISH); 5066 __ Bind(slow_path->GetExitLabel()); 5067} 5068 5069void LocationsBuilderARM::VisitLoadString(HLoadString* load) { 5070 LocationSummary* locations = 5071 new (GetGraph()->GetArena()) LocationSummary(load, LocationSummary::kCallOnSlowPath); 5072 locations->SetInAt(0, Location::RequiresRegister()); 5073 locations->SetOut(Location::RequiresRegister()); 5074} 5075 5076void InstructionCodeGeneratorARM::VisitLoadString(HLoadString* load) { 5077 SlowPathCode* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathARM(load); 5078 codegen_->AddSlowPath(slow_path); 5079 5080 LocationSummary* locations = load->GetLocations(); 5081 Location out_loc = locations->Out(); 5082 Register out = out_loc.AsRegister<Register>(); 5083 Register current_method = locations->InAt(0).AsRegister<Register>(); 5084 5085 uint32_t declaring_class_offset = ArtMethod::DeclaringClassOffset().Int32Value(); 5086 if (kEmitCompilerReadBarrier) { 5087 // /* GcRoot<mirror::Class>* */ out = &(current_method->declaring_class_) 5088 __ AddConstant(out, current_method, declaring_class_offset); 5089 // /* mirror::Class* */ out = out->Read() 5090 codegen_->GenerateReadBarrierForRoot(load, out_loc, out_loc); 5091 } else { 5092 // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_ 5093 __ LoadFromOffset(kLoadWord, out, current_method, declaring_class_offset); 5094 } 5095 5096 // /* GcRoot<mirror::String>[] */ out = out->dex_cache_strings_ 5097 __ LoadFromOffset(kLoadWord, out, out, mirror::Class::DexCacheStringsOffset().Int32Value()); 5098 5099 size_t cache_offset = CodeGenerator::GetCacheOffset(load->GetStringIndex()); 5100 if (kEmitCompilerReadBarrier) { 5101 // /* GcRoot<mirror::String>* */ out = &out[string_index] 5102 __ AddConstant(out, out, cache_offset); 5103 // /* mirror::String* */ out = out->Read() 5104 codegen_->GenerateReadBarrierForRoot(load, out_loc, out_loc); 5105 } else { 5106 // /* GcRoot<mirror::String> */ out = out[string_index] 5107 __ LoadFromOffset(kLoadWord, out, out, cache_offset); 5108 } 5109 5110 __ CompareAndBranchIfZero(out, slow_path->GetEntryLabel()); 5111 __ Bind(slow_path->GetExitLabel()); 5112} 5113 5114static int32_t GetExceptionTlsOffset() { 5115 return Thread::ExceptionOffset<kArmWordSize>().Int32Value(); 5116} 5117 5118void LocationsBuilderARM::VisitLoadException(HLoadException* load) { 5119 LocationSummary* locations = 5120 new (GetGraph()->GetArena()) LocationSummary(load, LocationSummary::kNoCall); 5121 locations->SetOut(Location::RequiresRegister()); 5122} 5123 5124void InstructionCodeGeneratorARM::VisitLoadException(HLoadException* load) { 5125 Register out = load->GetLocations()->Out().AsRegister<Register>(); 5126 __ LoadFromOffset(kLoadWord, out, TR, GetExceptionTlsOffset()); 5127} 5128 5129void LocationsBuilderARM::VisitClearException(HClearException* clear) { 5130 new (GetGraph()->GetArena()) LocationSummary(clear, LocationSummary::kNoCall); 5131} 5132 5133void InstructionCodeGeneratorARM::VisitClearException(HClearException* clear ATTRIBUTE_UNUSED) { 5134 __ LoadImmediate(IP, 0); 5135 __ StoreToOffset(kStoreWord, IP, TR, GetExceptionTlsOffset()); 5136} 5137 5138void LocationsBuilderARM::VisitThrow(HThrow* instruction) { 5139 LocationSummary* locations = 5140 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall); 5141 InvokeRuntimeCallingConvention calling_convention; 5142 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); 5143} 5144 5145void InstructionCodeGeneratorARM::VisitThrow(HThrow* instruction) { 5146 codegen_->InvokeRuntime( 5147 QUICK_ENTRY_POINT(pDeliverException), instruction, instruction->GetDexPc(), nullptr); 5148 CheckEntrypointTypes<kQuickDeliverException, void, mirror::Object*>(); 5149} 5150 5151void LocationsBuilderARM::VisitInstanceOf(HInstanceOf* instruction) { 5152 LocationSummary::CallKind call_kind = LocationSummary::kNoCall; 5153 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind(); 5154 switch (type_check_kind) { 5155 case TypeCheckKind::kExactCheck: 5156 case TypeCheckKind::kAbstractClassCheck: 5157 case TypeCheckKind::kClassHierarchyCheck: 5158 case TypeCheckKind::kArrayObjectCheck: 5159 call_kind = 5160 kEmitCompilerReadBarrier ? LocationSummary::kCallOnSlowPath : LocationSummary::kNoCall; 5161 break; 5162 case TypeCheckKind::kArrayCheck: 5163 case TypeCheckKind::kUnresolvedCheck: 5164 case TypeCheckKind::kInterfaceCheck: 5165 call_kind = LocationSummary::kCallOnSlowPath; 5166 break; 5167 } 5168 5169 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind); 5170 locations->SetInAt(0, Location::RequiresRegister()); 5171 locations->SetInAt(1, Location::RequiresRegister()); 5172 // The "out" register is used as a temporary, so it overlaps with the inputs. 5173 // Note that TypeCheckSlowPathARM uses this register too. 5174 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); 5175 // When read barriers are enabled, we need a temporary register for 5176 // some cases. 5177 if (kEmitCompilerReadBarrier && 5178 (type_check_kind == TypeCheckKind::kAbstractClassCheck || 5179 type_check_kind == TypeCheckKind::kClassHierarchyCheck || 5180 type_check_kind == TypeCheckKind::kArrayObjectCheck)) { 5181 locations->AddTemp(Location::RequiresRegister()); 5182 } 5183} 5184 5185void InstructionCodeGeneratorARM::VisitInstanceOf(HInstanceOf* instruction) { 5186 LocationSummary* locations = instruction->GetLocations(); 5187 Location obj_loc = locations->InAt(0); 5188 Register obj = obj_loc.AsRegister<Register>(); 5189 Register cls = locations->InAt(1).AsRegister<Register>(); 5190 Location out_loc = locations->Out(); 5191 Register out = out_loc.AsRegister<Register>(); 5192 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value(); 5193 uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value(); 5194 uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value(); 5195 uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value(); 5196 Label done, zero; 5197 SlowPathCode* slow_path = nullptr; 5198 5199 // Return 0 if `obj` is null. 5200 // avoid null check if we know obj is not null. 5201 if (instruction->MustDoNullCheck()) { 5202 __ CompareAndBranchIfZero(obj, &zero); 5203 } 5204 5205 // /* HeapReference<Class> */ out = obj->klass_ 5206 __ LoadFromOffset(kLoadWord, out, obj, class_offset); 5207 codegen_->MaybeGenerateReadBarrier(instruction, out_loc, out_loc, obj_loc, class_offset); 5208 5209 switch (instruction->GetTypeCheckKind()) { 5210 case TypeCheckKind::kExactCheck: { 5211 __ cmp(out, ShifterOperand(cls)); 5212 // Classes must be equal for the instanceof to succeed. 5213 __ b(&zero, NE); 5214 __ LoadImmediate(out, 1); 5215 __ b(&done); 5216 break; 5217 } 5218 5219 case TypeCheckKind::kAbstractClassCheck: { 5220 // If the class is abstract, we eagerly fetch the super class of the 5221 // object to avoid doing a comparison we know will fail. 5222 Label loop; 5223 __ Bind(&loop); 5224 Location temp_loc = kEmitCompilerReadBarrier ? locations->GetTemp(0) : Location::NoLocation(); 5225 if (kEmitCompilerReadBarrier) { 5226 // Save the value of `out` into `temp` before overwriting it 5227 // in the following move operation, as we will need it for the 5228 // read barrier below. 5229 Register temp = temp_loc.AsRegister<Register>(); 5230 __ Mov(temp, out); 5231 } 5232 // /* HeapReference<Class> */ out = out->super_class_ 5233 __ LoadFromOffset(kLoadWord, out, out, super_offset); 5234 codegen_->MaybeGenerateReadBarrier(instruction, out_loc, out_loc, temp_loc, super_offset); 5235 // If `out` is null, we use it for the result, and jump to `done`. 5236 __ CompareAndBranchIfZero(out, &done); 5237 __ cmp(out, ShifterOperand(cls)); 5238 __ b(&loop, NE); 5239 __ LoadImmediate(out, 1); 5240 if (zero.IsLinked()) { 5241 __ b(&done); 5242 } 5243 break; 5244 } 5245 5246 case TypeCheckKind::kClassHierarchyCheck: { 5247 // Walk over the class hierarchy to find a match. 5248 Label loop, success; 5249 __ Bind(&loop); 5250 __ cmp(out, ShifterOperand(cls)); 5251 __ b(&success, EQ); 5252 Location temp_loc = kEmitCompilerReadBarrier ? locations->GetTemp(0) : Location::NoLocation(); 5253 if (kEmitCompilerReadBarrier) { 5254 // Save the value of `out` into `temp` before overwriting it 5255 // in the following move operation, as we will need it for the 5256 // read barrier below. 5257 Register temp = temp_loc.AsRegister<Register>(); 5258 __ Mov(temp, out); 5259 } 5260 // /* HeapReference<Class> */ out = out->super_class_ 5261 __ LoadFromOffset(kLoadWord, out, out, super_offset); 5262 codegen_->MaybeGenerateReadBarrier(instruction, out_loc, out_loc, temp_loc, super_offset); 5263 __ CompareAndBranchIfNonZero(out, &loop); 5264 // If `out` is null, we use it for the result, and jump to `done`. 5265 __ b(&done); 5266 __ Bind(&success); 5267 __ LoadImmediate(out, 1); 5268 if (zero.IsLinked()) { 5269 __ b(&done); 5270 } 5271 break; 5272 } 5273 5274 case TypeCheckKind::kArrayObjectCheck: { 5275 // Do an exact check. 5276 Label exact_check; 5277 __ cmp(out, ShifterOperand(cls)); 5278 __ b(&exact_check, EQ); 5279 // Otherwise, we need to check that the object's class is a non-primitive array. 5280 Location temp_loc = kEmitCompilerReadBarrier ? locations->GetTemp(0) : Location::NoLocation(); 5281 if (kEmitCompilerReadBarrier) { 5282 // Save the value of `out` into `temp` before overwriting it 5283 // in the following move operation, as we will need it for the 5284 // read barrier below. 5285 Register temp = temp_loc.AsRegister<Register>(); 5286 __ Mov(temp, out); 5287 } 5288 // /* HeapReference<Class> */ out = out->component_type_ 5289 __ LoadFromOffset(kLoadWord, out, out, component_offset); 5290 codegen_->MaybeGenerateReadBarrier(instruction, out_loc, out_loc, temp_loc, component_offset); 5291 // If `out` is null, we use it for the result, and jump to `done`. 5292 __ CompareAndBranchIfZero(out, &done); 5293 __ LoadFromOffset(kLoadUnsignedHalfword, out, out, primitive_offset); 5294 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot"); 5295 __ CompareAndBranchIfNonZero(out, &zero); 5296 __ Bind(&exact_check); 5297 __ LoadImmediate(out, 1); 5298 __ b(&done); 5299 break; 5300 } 5301 5302 case TypeCheckKind::kArrayCheck: { 5303 __ cmp(out, ShifterOperand(cls)); 5304 DCHECK(locations->OnlyCallsOnSlowPath()); 5305 slow_path = new (GetGraph()->GetArena()) TypeCheckSlowPathARM(instruction, 5306 /* is_fatal */ false); 5307 codegen_->AddSlowPath(slow_path); 5308 __ b(slow_path->GetEntryLabel(), NE); 5309 __ LoadImmediate(out, 1); 5310 if (zero.IsLinked()) { 5311 __ b(&done); 5312 } 5313 break; 5314 } 5315 5316 case TypeCheckKind::kUnresolvedCheck: 5317 case TypeCheckKind::kInterfaceCheck: { 5318 // Note that we indeed only call on slow path, but we always go 5319 // into the slow path for the unresolved & interface check 5320 // cases. 5321 // 5322 // We cannot directly call the InstanceofNonTrivial runtime 5323 // entry point without resorting to a type checking slow path 5324 // here (i.e. by calling InvokeRuntime directly), as it would 5325 // require to assign fixed registers for the inputs of this 5326 // HInstanceOf instruction (following the runtime calling 5327 // convention), which might be cluttered by the potential first 5328 // read barrier emission at the beginning of this method. 5329 DCHECK(locations->OnlyCallsOnSlowPath()); 5330 slow_path = new (GetGraph()->GetArena()) TypeCheckSlowPathARM(instruction, 5331 /* is_fatal */ false); 5332 codegen_->AddSlowPath(slow_path); 5333 __ b(slow_path->GetEntryLabel()); 5334 if (zero.IsLinked()) { 5335 __ b(&done); 5336 } 5337 break; 5338 } 5339 } 5340 5341 if (zero.IsLinked()) { 5342 __ Bind(&zero); 5343 __ LoadImmediate(out, 0); 5344 } 5345 5346 if (done.IsLinked()) { 5347 __ Bind(&done); 5348 } 5349 5350 if (slow_path != nullptr) { 5351 __ Bind(slow_path->GetExitLabel()); 5352 } 5353} 5354 5355void LocationsBuilderARM::VisitCheckCast(HCheckCast* instruction) { 5356 LocationSummary::CallKind call_kind = LocationSummary::kNoCall; 5357 bool throws_into_catch = instruction->CanThrowIntoCatchBlock(); 5358 5359 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind(); 5360 switch (type_check_kind) { 5361 case TypeCheckKind::kExactCheck: 5362 case TypeCheckKind::kAbstractClassCheck: 5363 case TypeCheckKind::kClassHierarchyCheck: 5364 case TypeCheckKind::kArrayObjectCheck: 5365 call_kind = (throws_into_catch || kEmitCompilerReadBarrier) ? 5366 LocationSummary::kCallOnSlowPath : 5367 LocationSummary::kNoCall; // In fact, call on a fatal (non-returning) slow path. 5368 break; 5369 case TypeCheckKind::kArrayCheck: 5370 case TypeCheckKind::kUnresolvedCheck: 5371 case TypeCheckKind::kInterfaceCheck: 5372 call_kind = LocationSummary::kCallOnSlowPath; 5373 break; 5374 } 5375 5376 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind); 5377 locations->SetInAt(0, Location::RequiresRegister()); 5378 locations->SetInAt(1, Location::RequiresRegister()); 5379 // Note that TypeCheckSlowPathARM uses this "temp" register too. 5380 locations->AddTemp(Location::RequiresRegister()); 5381 // When read barriers are enabled, we need an additional temporary 5382 // register for some cases. 5383 if (kEmitCompilerReadBarrier && 5384 (type_check_kind == TypeCheckKind::kAbstractClassCheck || 5385 type_check_kind == TypeCheckKind::kClassHierarchyCheck || 5386 type_check_kind == TypeCheckKind::kArrayObjectCheck)) { 5387 locations->AddTemp(Location::RequiresRegister()); 5388 } 5389} 5390 5391void InstructionCodeGeneratorARM::VisitCheckCast(HCheckCast* instruction) { 5392 LocationSummary* locations = instruction->GetLocations(); 5393 Location obj_loc = locations->InAt(0); 5394 Register obj = obj_loc.AsRegister<Register>(); 5395 Register cls = locations->InAt(1).AsRegister<Register>(); 5396 Location temp_loc = locations->GetTemp(0); 5397 Register temp = temp_loc.AsRegister<Register>(); 5398 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value(); 5399 uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value(); 5400 uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value(); 5401 uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value(); 5402 5403 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind(); 5404 bool is_type_check_slow_path_fatal = 5405 (type_check_kind == TypeCheckKind::kExactCheck || 5406 type_check_kind == TypeCheckKind::kAbstractClassCheck || 5407 type_check_kind == TypeCheckKind::kClassHierarchyCheck || 5408 type_check_kind == TypeCheckKind::kArrayObjectCheck) && 5409 !instruction->CanThrowIntoCatchBlock(); 5410 SlowPathCode* type_check_slow_path = 5411 new (GetGraph()->GetArena()) TypeCheckSlowPathARM(instruction, 5412 is_type_check_slow_path_fatal); 5413 codegen_->AddSlowPath(type_check_slow_path); 5414 5415 Label done; 5416 // Avoid null check if we know obj is not null. 5417 if (instruction->MustDoNullCheck()) { 5418 __ CompareAndBranchIfZero(obj, &done); 5419 } 5420 5421 // /* HeapReference<Class> */ temp = obj->klass_ 5422 __ LoadFromOffset(kLoadWord, temp, obj, class_offset); 5423 codegen_->MaybeGenerateReadBarrier(instruction, temp_loc, temp_loc, obj_loc, class_offset); 5424 5425 switch (type_check_kind) { 5426 case TypeCheckKind::kExactCheck: 5427 case TypeCheckKind::kArrayCheck: { 5428 __ cmp(temp, ShifterOperand(cls)); 5429 // Jump to slow path for throwing the exception or doing a 5430 // more involved array check. 5431 __ b(type_check_slow_path->GetEntryLabel(), NE); 5432 break; 5433 } 5434 5435 case TypeCheckKind::kAbstractClassCheck: { 5436 // If the class is abstract, we eagerly fetch the super class of the 5437 // object to avoid doing a comparison we know will fail. 5438 Label loop, compare_classes; 5439 __ Bind(&loop); 5440 Location temp2_loc = 5441 kEmitCompilerReadBarrier ? locations->GetTemp(1) : Location::NoLocation(); 5442 if (kEmitCompilerReadBarrier) { 5443 // Save the value of `temp` into `temp2` before overwriting it 5444 // in the following move operation, as we will need it for the 5445 // read barrier below. 5446 Register temp2 = temp2_loc.AsRegister<Register>(); 5447 __ Mov(temp2, temp); 5448 } 5449 // /* HeapReference<Class> */ temp = temp->super_class_ 5450 __ LoadFromOffset(kLoadWord, temp, temp, super_offset); 5451 codegen_->MaybeGenerateReadBarrier(instruction, temp_loc, temp_loc, temp2_loc, super_offset); 5452 5453 // If the class reference currently in `temp` is not null, jump 5454 // to the `compare_classes` label to compare it with the checked 5455 // class. 5456 __ CompareAndBranchIfNonZero(temp, &compare_classes); 5457 // Otherwise, jump to the slow path to throw the exception. 5458 // 5459 // But before, move back the object's class into `temp` before 5460 // going into the slow path, as it has been overwritten in the 5461 // meantime. 5462 // /* HeapReference<Class> */ temp = obj->klass_ 5463 __ LoadFromOffset(kLoadWord, temp, obj, class_offset); 5464 codegen_->MaybeGenerateReadBarrier(instruction, temp_loc, temp_loc, obj_loc, class_offset); 5465 __ b(type_check_slow_path->GetEntryLabel()); 5466 5467 __ Bind(&compare_classes); 5468 __ cmp(temp, ShifterOperand(cls)); 5469 __ b(&loop, NE); 5470 break; 5471 } 5472 5473 case TypeCheckKind::kClassHierarchyCheck: { 5474 // Walk over the class hierarchy to find a match. 5475 Label loop; 5476 __ Bind(&loop); 5477 __ cmp(temp, ShifterOperand(cls)); 5478 __ b(&done, EQ); 5479 5480 Location temp2_loc = 5481 kEmitCompilerReadBarrier ? locations->GetTemp(1) : Location::NoLocation(); 5482 if (kEmitCompilerReadBarrier) { 5483 // Save the value of `temp` into `temp2` before overwriting it 5484 // in the following move operation, as we will need it for the 5485 // read barrier below. 5486 Register temp2 = temp2_loc.AsRegister<Register>(); 5487 __ Mov(temp2, temp); 5488 } 5489 // /* HeapReference<Class> */ temp = temp->super_class_ 5490 __ LoadFromOffset(kLoadWord, temp, temp, super_offset); 5491 codegen_->MaybeGenerateReadBarrier(instruction, temp_loc, temp_loc, temp2_loc, super_offset); 5492 5493 // If the class reference currently in `temp` is not null, jump 5494 // back at the beginning of the loop. 5495 __ CompareAndBranchIfNonZero(temp, &loop); 5496 // Otherwise, jump to the slow path to throw the exception. 5497 // 5498 // But before, move back the object's class into `temp` before 5499 // going into the slow path, as it has been overwritten in the 5500 // meantime. 5501 // /* HeapReference<Class> */ temp = obj->klass_ 5502 __ LoadFromOffset(kLoadWord, temp, obj, class_offset); 5503 codegen_->MaybeGenerateReadBarrier(instruction, temp_loc, temp_loc, obj_loc, class_offset); 5504 __ b(type_check_slow_path->GetEntryLabel()); 5505 break; 5506 } 5507 5508 case TypeCheckKind::kArrayObjectCheck: { 5509 // Do an exact check. 5510 Label check_non_primitive_component_type; 5511 __ cmp(temp, ShifterOperand(cls)); 5512 __ b(&done, EQ); 5513 5514 // Otherwise, we need to check that the object's class is a non-primitive array. 5515 Location temp2_loc = 5516 kEmitCompilerReadBarrier ? locations->GetTemp(1) : Location::NoLocation(); 5517 if (kEmitCompilerReadBarrier) { 5518 // Save the value of `temp` into `temp2` before overwriting it 5519 // in the following move operation, as we will need it for the 5520 // read barrier below. 5521 Register temp2 = temp2_loc.AsRegister<Register>(); 5522 __ Mov(temp2, temp); 5523 } 5524 // /* HeapReference<Class> */ temp = temp->component_type_ 5525 __ LoadFromOffset(kLoadWord, temp, temp, component_offset); 5526 codegen_->MaybeGenerateReadBarrier( 5527 instruction, temp_loc, temp_loc, temp2_loc, component_offset); 5528 5529 // If the component type is not null (i.e. the object is indeed 5530 // an array), jump to label `check_non_primitive_component_type` 5531 // to further check that this component type is not a primitive 5532 // type. 5533 __ CompareAndBranchIfNonZero(temp, &check_non_primitive_component_type); 5534 // Otherwise, jump to the slow path to throw the exception. 5535 // 5536 // But before, move back the object's class into `temp` before 5537 // going into the slow path, as it has been overwritten in the 5538 // meantime. 5539 // /* HeapReference<Class> */ temp = obj->klass_ 5540 __ LoadFromOffset(kLoadWord, temp, obj, class_offset); 5541 codegen_->MaybeGenerateReadBarrier(instruction, temp_loc, temp_loc, obj_loc, class_offset); 5542 __ b(type_check_slow_path->GetEntryLabel()); 5543 5544 __ Bind(&check_non_primitive_component_type); 5545 __ LoadFromOffset(kLoadUnsignedHalfword, temp, temp, primitive_offset); 5546 static_assert(Primitive::kPrimNot == 0, "Expected 0 for art::Primitive::kPrimNot"); 5547 __ CompareAndBranchIfZero(temp, &done); 5548 // Same comment as above regarding `temp` and the slow path. 5549 // /* HeapReference<Class> */ temp = obj->klass_ 5550 __ LoadFromOffset(kLoadWord, temp, obj, class_offset); 5551 codegen_->MaybeGenerateReadBarrier(instruction, temp_loc, temp_loc, obj_loc, class_offset); 5552 __ b(type_check_slow_path->GetEntryLabel()); 5553 break; 5554 } 5555 5556 case TypeCheckKind::kUnresolvedCheck: 5557 case TypeCheckKind::kInterfaceCheck: 5558 // We always go into the type check slow path for the unresolved & 5559 // interface check cases. 5560 // 5561 // We cannot directly call the CheckCast runtime entry point 5562 // without resorting to a type checking slow path here (i.e. by 5563 // calling InvokeRuntime directly), as it would require to 5564 // assign fixed registers for the inputs of this HInstanceOf 5565 // instruction (following the runtime calling convention), which 5566 // might be cluttered by the potential first read barrier 5567 // emission at the beginning of this method. 5568 __ b(type_check_slow_path->GetEntryLabel()); 5569 break; 5570 } 5571 __ Bind(&done); 5572 5573 __ Bind(type_check_slow_path->GetExitLabel()); 5574} 5575 5576void LocationsBuilderARM::VisitMonitorOperation(HMonitorOperation* instruction) { 5577 LocationSummary* locations = 5578 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall); 5579 InvokeRuntimeCallingConvention calling_convention; 5580 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); 5581} 5582 5583void InstructionCodeGeneratorARM::VisitMonitorOperation(HMonitorOperation* instruction) { 5584 codegen_->InvokeRuntime(instruction->IsEnter() 5585 ? QUICK_ENTRY_POINT(pLockObject) : QUICK_ENTRY_POINT(pUnlockObject), 5586 instruction, 5587 instruction->GetDexPc(), 5588 nullptr); 5589 if (instruction->IsEnter()) { 5590 CheckEntrypointTypes<kQuickLockObject, void, mirror::Object*>(); 5591 } else { 5592 CheckEntrypointTypes<kQuickUnlockObject, void, mirror::Object*>(); 5593 } 5594} 5595 5596void LocationsBuilderARM::VisitAnd(HAnd* instruction) { HandleBitwiseOperation(instruction, AND); } 5597void LocationsBuilderARM::VisitOr(HOr* instruction) { HandleBitwiseOperation(instruction, ORR); } 5598void LocationsBuilderARM::VisitXor(HXor* instruction) { HandleBitwiseOperation(instruction, EOR); } 5599 5600void LocationsBuilderARM::HandleBitwiseOperation(HBinaryOperation* instruction, Opcode opcode) { 5601 LocationSummary* locations = 5602 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); 5603 DCHECK(instruction->GetResultType() == Primitive::kPrimInt 5604 || instruction->GetResultType() == Primitive::kPrimLong); 5605 // Note: GVN reorders commutative operations to have the constant on the right hand side. 5606 locations->SetInAt(0, Location::RequiresRegister()); 5607 locations->SetInAt(1, ArmEncodableConstantOrRegister(instruction->InputAt(1), opcode)); 5608 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 5609} 5610 5611void InstructionCodeGeneratorARM::VisitAnd(HAnd* instruction) { 5612 HandleBitwiseOperation(instruction); 5613} 5614 5615void InstructionCodeGeneratorARM::VisitOr(HOr* instruction) { 5616 HandleBitwiseOperation(instruction); 5617} 5618 5619void InstructionCodeGeneratorARM::VisitXor(HXor* instruction) { 5620 HandleBitwiseOperation(instruction); 5621} 5622 5623void InstructionCodeGeneratorARM::GenerateAndConst(Register out, Register first, uint32_t value) { 5624 // Optimize special cases for individual halfs of `and-long` (`and` is simplified earlier). 5625 if (value == 0xffffffffu) { 5626 if (out != first) { 5627 __ mov(out, ShifterOperand(first)); 5628 } 5629 return; 5630 } 5631 if (value == 0u) { 5632 __ mov(out, ShifterOperand(0)); 5633 return; 5634 } 5635 ShifterOperand so; 5636 if (__ ShifterOperandCanHold(kNoRegister, kNoRegister, AND, value, &so)) { 5637 __ and_(out, first, so); 5638 } else { 5639 DCHECK(__ ShifterOperandCanHold(kNoRegister, kNoRegister, BIC, ~value, &so)); 5640 __ bic(out, first, ShifterOperand(~value)); 5641 } 5642} 5643 5644void InstructionCodeGeneratorARM::GenerateOrrConst(Register out, Register first, uint32_t value) { 5645 // Optimize special cases for individual halfs of `or-long` (`or` is simplified earlier). 5646 if (value == 0u) { 5647 if (out != first) { 5648 __ mov(out, ShifterOperand(first)); 5649 } 5650 return; 5651 } 5652 if (value == 0xffffffffu) { 5653 __ mvn(out, ShifterOperand(0)); 5654 return; 5655 } 5656 ShifterOperand so; 5657 if (__ ShifterOperandCanHold(kNoRegister, kNoRegister, ORR, value, &so)) { 5658 __ orr(out, first, so); 5659 } else { 5660 DCHECK(__ ShifterOperandCanHold(kNoRegister, kNoRegister, ORN, ~value, &so)); 5661 __ orn(out, first, ShifterOperand(~value)); 5662 } 5663} 5664 5665void InstructionCodeGeneratorARM::GenerateEorConst(Register out, Register first, uint32_t value) { 5666 // Optimize special case for individual halfs of `xor-long` (`xor` is simplified earlier). 5667 if (value == 0u) { 5668 if (out != first) { 5669 __ mov(out, ShifterOperand(first)); 5670 } 5671 return; 5672 } 5673 __ eor(out, first, ShifterOperand(value)); 5674} 5675 5676void InstructionCodeGeneratorARM::HandleBitwiseOperation(HBinaryOperation* instruction) { 5677 LocationSummary* locations = instruction->GetLocations(); 5678 Location first = locations->InAt(0); 5679 Location second = locations->InAt(1); 5680 Location out = locations->Out(); 5681 5682 if (second.IsConstant()) { 5683 uint64_t value = static_cast<uint64_t>(Int64FromConstant(second.GetConstant())); 5684 uint32_t value_low = Low32Bits(value); 5685 if (instruction->GetResultType() == Primitive::kPrimInt) { 5686 Register first_reg = first.AsRegister<Register>(); 5687 Register out_reg = out.AsRegister<Register>(); 5688 if (instruction->IsAnd()) { 5689 GenerateAndConst(out_reg, first_reg, value_low); 5690 } else if (instruction->IsOr()) { 5691 GenerateOrrConst(out_reg, first_reg, value_low); 5692 } else { 5693 DCHECK(instruction->IsXor()); 5694 GenerateEorConst(out_reg, first_reg, value_low); 5695 } 5696 } else { 5697 DCHECK_EQ(instruction->GetResultType(), Primitive::kPrimLong); 5698 uint32_t value_high = High32Bits(value); 5699 Register first_low = first.AsRegisterPairLow<Register>(); 5700 Register first_high = first.AsRegisterPairHigh<Register>(); 5701 Register out_low = out.AsRegisterPairLow<Register>(); 5702 Register out_high = out.AsRegisterPairHigh<Register>(); 5703 if (instruction->IsAnd()) { 5704 GenerateAndConst(out_low, first_low, value_low); 5705 GenerateAndConst(out_high, first_high, value_high); 5706 } else if (instruction->IsOr()) { 5707 GenerateOrrConst(out_low, first_low, value_low); 5708 GenerateOrrConst(out_high, first_high, value_high); 5709 } else { 5710 DCHECK(instruction->IsXor()); 5711 GenerateEorConst(out_low, first_low, value_low); 5712 GenerateEorConst(out_high, first_high, value_high); 5713 } 5714 } 5715 return; 5716 } 5717 5718 if (instruction->GetResultType() == Primitive::kPrimInt) { 5719 Register first_reg = first.AsRegister<Register>(); 5720 ShifterOperand second_reg(second.AsRegister<Register>()); 5721 Register out_reg = out.AsRegister<Register>(); 5722 if (instruction->IsAnd()) { 5723 __ and_(out_reg, first_reg, second_reg); 5724 } else if (instruction->IsOr()) { 5725 __ orr(out_reg, first_reg, second_reg); 5726 } else { 5727 DCHECK(instruction->IsXor()); 5728 __ eor(out_reg, first_reg, second_reg); 5729 } 5730 } else { 5731 DCHECK_EQ(instruction->GetResultType(), Primitive::kPrimLong); 5732 Register first_low = first.AsRegisterPairLow<Register>(); 5733 Register first_high = first.AsRegisterPairHigh<Register>(); 5734 ShifterOperand second_low(second.AsRegisterPairLow<Register>()); 5735 ShifterOperand second_high(second.AsRegisterPairHigh<Register>()); 5736 Register out_low = out.AsRegisterPairLow<Register>(); 5737 Register out_high = out.AsRegisterPairHigh<Register>(); 5738 if (instruction->IsAnd()) { 5739 __ and_(out_low, first_low, second_low); 5740 __ and_(out_high, first_high, second_high); 5741 } else if (instruction->IsOr()) { 5742 __ orr(out_low, first_low, second_low); 5743 __ orr(out_high, first_high, second_high); 5744 } else { 5745 DCHECK(instruction->IsXor()); 5746 __ eor(out_low, first_low, second_low); 5747 __ eor(out_high, first_high, second_high); 5748 } 5749 } 5750} 5751 5752void CodeGeneratorARM::GenerateReadBarrier(HInstruction* instruction, 5753 Location out, 5754 Location ref, 5755 Location obj, 5756 uint32_t offset, 5757 Location index) { 5758 DCHECK(kEmitCompilerReadBarrier); 5759 5760 // If heap poisoning is enabled, the unpoisoning of the loaded 5761 // reference will be carried out by the runtime within the slow 5762 // path. 5763 // 5764 // Note that `ref` currently does not get unpoisoned (when heap 5765 // poisoning is enabled), which is alright as the `ref` argument is 5766 // not used by the artReadBarrierSlow entry point. 5767 // 5768 // TODO: Unpoison `ref` when it is used by artReadBarrierSlow. 5769 SlowPathCode* slow_path = new (GetGraph()->GetArena()) 5770 ReadBarrierForHeapReferenceSlowPathARM(instruction, out, ref, obj, offset, index); 5771 AddSlowPath(slow_path); 5772 5773 // TODO: When read barrier has a fast path, add it here. 5774 /* Currently the read barrier call is inserted after the original load. 5775 * However, if we have a fast path, we need to perform the load of obj.LockWord *before* the 5776 * original load. This load-load ordering is required by the read barrier. 5777 * The fast path/slow path (for Baker's algorithm) should look like: 5778 * 5779 * bool isGray = obj.LockWord & kReadBarrierMask; 5780 * lfence; // load fence or artificial data dependence to prevent load-load reordering 5781 * ref = obj.field; // this is the original load 5782 * if (isGray) { 5783 * ref = Mark(ref); // ideally the slow path just does Mark(ref) 5784 * } 5785 */ 5786 5787 __ b(slow_path->GetEntryLabel()); 5788 __ Bind(slow_path->GetExitLabel()); 5789} 5790 5791void CodeGeneratorARM::MaybeGenerateReadBarrier(HInstruction* instruction, 5792 Location out, 5793 Location ref, 5794 Location obj, 5795 uint32_t offset, 5796 Location index) { 5797 if (kEmitCompilerReadBarrier) { 5798 // If heap poisoning is enabled, unpoisoning will be taken care of 5799 // by the runtime within the slow path. 5800 GenerateReadBarrier(instruction, out, ref, obj, offset, index); 5801 } else if (kPoisonHeapReferences) { 5802 __ UnpoisonHeapReference(out.AsRegister<Register>()); 5803 } 5804} 5805 5806void CodeGeneratorARM::GenerateReadBarrierForRoot(HInstruction* instruction, 5807 Location out, 5808 Location root) { 5809 DCHECK(kEmitCompilerReadBarrier); 5810 5811 // Note that GC roots are not affected by heap poisoning, so we do 5812 // not need to do anything special for this here. 5813 SlowPathCode* slow_path = 5814 new (GetGraph()->GetArena()) ReadBarrierForRootSlowPathARM(instruction, out, root); 5815 AddSlowPath(slow_path); 5816 5817 // TODO: Implement a fast path for ReadBarrierForRoot, performing 5818 // the following operation (for Baker's algorithm): 5819 // 5820 // if (thread.tls32_.is_gc_marking) { 5821 // root = Mark(root); 5822 // } 5823 5824 __ b(slow_path->GetEntryLabel()); 5825 __ Bind(slow_path->GetExitLabel()); 5826} 5827 5828HInvokeStaticOrDirect::DispatchInfo CodeGeneratorARM::GetSupportedInvokeStaticOrDirectDispatch( 5829 const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info, 5830 MethodReference target_method) { 5831 if (desired_dispatch_info.code_ptr_location == 5832 HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative) { 5833 const DexFile& outer_dex_file = GetGraph()->GetDexFile(); 5834 if (&outer_dex_file != target_method.dex_file) { 5835 // Calls across dex files are more likely to exceed the available BL range, 5836 // so use absolute patch with fixup if available and kCallArtMethod otherwise. 5837 HInvokeStaticOrDirect::CodePtrLocation code_ptr_location = 5838 (desired_dispatch_info.method_load_kind == 5839 HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup) 5840 ? HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup 5841 : HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod; 5842 return HInvokeStaticOrDirect::DispatchInfo { 5843 desired_dispatch_info.method_load_kind, 5844 code_ptr_location, 5845 desired_dispatch_info.method_load_data, 5846 0u 5847 }; 5848 } 5849 } 5850 return desired_dispatch_info; 5851} 5852 5853Register CodeGeneratorARM::GetInvokeStaticOrDirectExtraParameter(HInvokeStaticOrDirect* invoke, 5854 Register temp) { 5855 DCHECK_EQ(invoke->InputCount(), invoke->GetNumberOfArguments() + 1u); 5856 Location location = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex()); 5857 if (!invoke->GetLocations()->Intrinsified()) { 5858 return location.AsRegister<Register>(); 5859 } 5860 // For intrinsics we allow any location, so it may be on the stack. 5861 if (!location.IsRegister()) { 5862 __ LoadFromOffset(kLoadWord, temp, SP, location.GetStackIndex()); 5863 return temp; 5864 } 5865 // For register locations, check if the register was saved. If so, get it from the stack. 5866 // Note: There is a chance that the register was saved but not overwritten, so we could 5867 // save one load. However, since this is just an intrinsic slow path we prefer this 5868 // simple and more robust approach rather that trying to determine if that's the case. 5869 SlowPathCode* slow_path = GetCurrentSlowPath(); 5870 DCHECK(slow_path != nullptr); // For intrinsified invokes the call is emitted on the slow path. 5871 if (slow_path->IsCoreRegisterSaved(location.AsRegister<Register>())) { 5872 int stack_offset = slow_path->GetStackOffsetOfCoreRegister(location.AsRegister<Register>()); 5873 __ LoadFromOffset(kLoadWord, temp, SP, stack_offset); 5874 return temp; 5875 } 5876 return location.AsRegister<Register>(); 5877} 5878 5879void CodeGeneratorARM::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp) { 5880 // For better instruction scheduling we load the direct code pointer before the method pointer. 5881 switch (invoke->GetCodePtrLocation()) { 5882 case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup: 5883 // LR = code address from literal pool with link-time patch. 5884 __ LoadLiteral(LR, DeduplicateMethodCodeLiteral(invoke->GetTargetMethod())); 5885 break; 5886 case HInvokeStaticOrDirect::CodePtrLocation::kCallDirect: 5887 // LR = invoke->GetDirectCodePtr(); 5888 __ LoadImmediate(LR, invoke->GetDirectCodePtr()); 5889 break; 5890 default: 5891 break; 5892 } 5893 5894 Location callee_method = temp; // For all kinds except kRecursive, callee will be in temp. 5895 switch (invoke->GetMethodLoadKind()) { 5896 case HInvokeStaticOrDirect::MethodLoadKind::kStringInit: 5897 // temp = thread->string_init_entrypoint 5898 __ LoadFromOffset(kLoadWord, temp.AsRegister<Register>(), TR, invoke->GetStringInitOffset()); 5899 break; 5900 case HInvokeStaticOrDirect::MethodLoadKind::kRecursive: 5901 callee_method = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex()); 5902 break; 5903 case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress: 5904 __ LoadImmediate(temp.AsRegister<Register>(), invoke->GetMethodAddress()); 5905 break; 5906 case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup: 5907 __ LoadLiteral(temp.AsRegister<Register>(), 5908 DeduplicateMethodAddressLiteral(invoke->GetTargetMethod())); 5909 break; 5910 case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative: { 5911 HArmDexCacheArraysBase* base = 5912 invoke->InputAt(invoke->GetSpecialInputIndex())->AsArmDexCacheArraysBase(); 5913 Register base_reg = GetInvokeStaticOrDirectExtraParameter(invoke, 5914 temp.AsRegister<Register>()); 5915 int32_t offset = invoke->GetDexCacheArrayOffset() - base->GetElementOffset(); 5916 __ LoadFromOffset(kLoadWord, temp.AsRegister<Register>(), base_reg, offset); 5917 break; 5918 } 5919 case HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod: { 5920 Location current_method = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex()); 5921 Register method_reg; 5922 Register reg = temp.AsRegister<Register>(); 5923 if (current_method.IsRegister()) { 5924 method_reg = current_method.AsRegister<Register>(); 5925 } else { 5926 DCHECK(invoke->GetLocations()->Intrinsified()); 5927 DCHECK(!current_method.IsValid()); 5928 method_reg = reg; 5929 __ LoadFromOffset(kLoadWord, reg, SP, kCurrentMethodStackOffset); 5930 } 5931 // /* ArtMethod*[] */ temp = temp.ptr_sized_fields_->dex_cache_resolved_methods_; 5932 __ LoadFromOffset(kLoadWord, 5933 reg, 5934 method_reg, 5935 ArtMethod::DexCacheResolvedMethodsOffset(kArmPointerSize).Int32Value()); 5936 // temp = temp[index_in_cache] 5937 uint32_t index_in_cache = invoke->GetTargetMethod().dex_method_index; 5938 __ LoadFromOffset(kLoadWord, reg, reg, CodeGenerator::GetCachePointerOffset(index_in_cache)); 5939 break; 5940 } 5941 } 5942 5943 switch (invoke->GetCodePtrLocation()) { 5944 case HInvokeStaticOrDirect::CodePtrLocation::kCallSelf: 5945 __ bl(GetFrameEntryLabel()); 5946 break; 5947 case HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative: 5948 relative_call_patches_.emplace_back(invoke->GetTargetMethod()); 5949 __ BindTrackedLabel(&relative_call_patches_.back().label); 5950 // Arbitrarily branch to the BL itself, override at link time. 5951 __ bl(&relative_call_patches_.back().label); 5952 break; 5953 case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup: 5954 case HInvokeStaticOrDirect::CodePtrLocation::kCallDirect: 5955 // LR prepared above for better instruction scheduling. 5956 // LR() 5957 __ blx(LR); 5958 break; 5959 case HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod: 5960 // LR = callee_method->entry_point_from_quick_compiled_code_ 5961 __ LoadFromOffset( 5962 kLoadWord, LR, callee_method.AsRegister<Register>(), 5963 ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmWordSize).Int32Value()); 5964 // LR() 5965 __ blx(LR); 5966 break; 5967 } 5968 5969 DCHECK(!IsLeafMethod()); 5970} 5971 5972void CodeGeneratorARM::GenerateVirtualCall(HInvokeVirtual* invoke, Location temp_location) { 5973 Register temp = temp_location.AsRegister<Register>(); 5974 uint32_t method_offset = mirror::Class::EmbeddedVTableEntryOffset( 5975 invoke->GetVTableIndex(), kArmPointerSize).Uint32Value(); 5976 LocationSummary* locations = invoke->GetLocations(); 5977 Location receiver = locations->InAt(0); 5978 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value(); 5979 DCHECK(receiver.IsRegister()); 5980 // /* HeapReference<Class> */ temp = receiver->klass_ 5981 __ LoadFromOffset(kLoadWord, temp, receiver.AsRegister<Register>(), class_offset); 5982 MaybeRecordImplicitNullCheck(invoke); 5983 // Instead of simply (possibly) unpoisoning `temp` here, we should 5984 // emit a read barrier for the previous class reference load. 5985 // However this is not required in practice, as this is an 5986 // intermediate/temporary reference and because the current 5987 // concurrent copying collector keeps the from-space memory 5988 // intact/accessible until the end of the marking phase (the 5989 // concurrent copying collector may not in the future). 5990 __ MaybeUnpoisonHeapReference(temp); 5991 // temp = temp->GetMethodAt(method_offset); 5992 uint32_t entry_point = ArtMethod::EntryPointFromQuickCompiledCodeOffset( 5993 kArmWordSize).Int32Value(); 5994 __ LoadFromOffset(kLoadWord, temp, temp, method_offset); 5995 // LR = temp->GetEntryPoint(); 5996 __ LoadFromOffset(kLoadWord, LR, temp, entry_point); 5997 // LR(); 5998 __ blx(LR); 5999} 6000 6001void CodeGeneratorARM::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) { 6002 DCHECK(linker_patches->empty()); 6003 size_t size = 6004 method_patches_.size() + 6005 call_patches_.size() + 6006 relative_call_patches_.size() + 6007 /* MOVW+MOVT for each base */ 2u * dex_cache_arrays_base_labels_.size(); 6008 linker_patches->reserve(size); 6009 for (const auto& entry : method_patches_) { 6010 const MethodReference& target_method = entry.first; 6011 Literal* literal = entry.second; 6012 DCHECK(literal->GetLabel()->IsBound()); 6013 uint32_t literal_offset = literal->GetLabel()->Position(); 6014 linker_patches->push_back(LinkerPatch::MethodPatch(literal_offset, 6015 target_method.dex_file, 6016 target_method.dex_method_index)); 6017 } 6018 for (const auto& entry : call_patches_) { 6019 const MethodReference& target_method = entry.first; 6020 Literal* literal = entry.second; 6021 DCHECK(literal->GetLabel()->IsBound()); 6022 uint32_t literal_offset = literal->GetLabel()->Position(); 6023 linker_patches->push_back(LinkerPatch::CodePatch(literal_offset, 6024 target_method.dex_file, 6025 target_method.dex_method_index)); 6026 } 6027 for (const MethodPatchInfo<Label>& info : relative_call_patches_) { 6028 uint32_t literal_offset = info.label.Position(); 6029 linker_patches->push_back(LinkerPatch::RelativeCodePatch(literal_offset, 6030 info.target_method.dex_file, 6031 info.target_method.dex_method_index)); 6032 } 6033 for (const auto& pair : dex_cache_arrays_base_labels_) { 6034 HArmDexCacheArraysBase* base = pair.first; 6035 const DexCacheArraysBaseLabels* labels = &pair.second; 6036 const DexFile& dex_file = base->GetDexFile(); 6037 size_t base_element_offset = base->GetElementOffset(); 6038 DCHECK(labels->add_pc_label.IsBound()); 6039 uint32_t add_pc_offset = dchecked_integral_cast<uint32_t>(labels->add_pc_label.Position()); 6040 // Add MOVW patch. 6041 DCHECK(labels->movw_label.IsBound()); 6042 uint32_t movw_offset = dchecked_integral_cast<uint32_t>(labels->movw_label.Position()); 6043 linker_patches->push_back(LinkerPatch::DexCacheArrayPatch(movw_offset, 6044 &dex_file, 6045 add_pc_offset, 6046 base_element_offset)); 6047 // Add MOVT patch. 6048 DCHECK(labels->movt_label.IsBound()); 6049 uint32_t movt_offset = dchecked_integral_cast<uint32_t>(labels->movt_label.Position()); 6050 linker_patches->push_back(LinkerPatch::DexCacheArrayPatch(movt_offset, 6051 &dex_file, 6052 add_pc_offset, 6053 base_element_offset)); 6054 } 6055} 6056 6057Literal* CodeGeneratorARM::DeduplicateMethodLiteral(MethodReference target_method, 6058 MethodToLiteralMap* map) { 6059 // Look up the literal for target_method. 6060 auto lb = map->lower_bound(target_method); 6061 if (lb != map->end() && !map->key_comp()(target_method, lb->first)) { 6062 return lb->second; 6063 } 6064 // We don't have a literal for this method yet, insert a new one. 6065 Literal* literal = __ NewLiteral<uint32_t>(0u); 6066 map->PutBefore(lb, target_method, literal); 6067 return literal; 6068} 6069 6070Literal* CodeGeneratorARM::DeduplicateMethodAddressLiteral(MethodReference target_method) { 6071 return DeduplicateMethodLiteral(target_method, &method_patches_); 6072} 6073 6074Literal* CodeGeneratorARM::DeduplicateMethodCodeLiteral(MethodReference target_method) { 6075 return DeduplicateMethodLiteral(target_method, &call_patches_); 6076} 6077 6078void LocationsBuilderARM::VisitBoundType(HBoundType* instruction ATTRIBUTE_UNUSED) { 6079 // Nothing to do, this should be removed during prepare for register allocator. 6080 LOG(FATAL) << "Unreachable"; 6081} 6082 6083void InstructionCodeGeneratorARM::VisitBoundType(HBoundType* instruction ATTRIBUTE_UNUSED) { 6084 // Nothing to do, this should be removed during prepare for register allocator. 6085 LOG(FATAL) << "Unreachable"; 6086} 6087 6088void LocationsBuilderARM::VisitFakeString(HFakeString* instruction) { 6089 DCHECK(codegen_->IsBaseline()); 6090 LocationSummary* locations = 6091 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); 6092 locations->SetOut(Location::ConstantLocation(GetGraph()->GetNullConstant())); 6093} 6094 6095void InstructionCodeGeneratorARM::VisitFakeString(HFakeString* instruction ATTRIBUTE_UNUSED) { 6096 DCHECK(codegen_->IsBaseline()); 6097 // Will be generated at use site. 6098} 6099 6100// Simple implementation of packed switch - generate cascaded compare/jumps. 6101void LocationsBuilderARM::VisitPackedSwitch(HPackedSwitch* switch_instr) { 6102 LocationSummary* locations = 6103 new (GetGraph()->GetArena()) LocationSummary(switch_instr, LocationSummary::kNoCall); 6104 locations->SetInAt(0, Location::RequiresRegister()); 6105 if (switch_instr->GetNumEntries() >= kPackedSwitchJumpTableThreshold && 6106 codegen_->GetAssembler()->IsThumb()) { 6107 locations->AddTemp(Location::RequiresRegister()); // We need a temp for the table base. 6108 if (switch_instr->GetStartValue() != 0) { 6109 locations->AddTemp(Location::RequiresRegister()); // We need a temp for the bias. 6110 } 6111 } 6112} 6113 6114void InstructionCodeGeneratorARM::VisitPackedSwitch(HPackedSwitch* switch_instr) { 6115 int32_t lower_bound = switch_instr->GetStartValue(); 6116 uint32_t num_entries = switch_instr->GetNumEntries(); 6117 LocationSummary* locations = switch_instr->GetLocations(); 6118 Register value_reg = locations->InAt(0).AsRegister<Register>(); 6119 HBasicBlock* default_block = switch_instr->GetDefaultBlock(); 6120 6121 if (num_entries < kPackedSwitchJumpTableThreshold || !codegen_->GetAssembler()->IsThumb()) { 6122 // Create a series of compare/jumps. 6123 const ArenaVector<HBasicBlock*>& successors = switch_instr->GetBlock()->GetSuccessors(); 6124 for (uint32_t i = 0; i < num_entries; i++) { 6125 GenerateCompareWithImmediate(value_reg, lower_bound + i); 6126 __ b(codegen_->GetLabelOf(successors[i]), EQ); 6127 } 6128 6129 // And the default for any other value. 6130 if (!codegen_->GoesToNextBlock(switch_instr->GetBlock(), default_block)) { 6131 __ b(codegen_->GetLabelOf(default_block)); 6132 } 6133 } else { 6134 // Create a table lookup. 6135 Register temp_reg = locations->GetTemp(0).AsRegister<Register>(); 6136 6137 // Materialize a pointer to the switch table 6138 std::vector<Label*> labels(num_entries); 6139 const ArenaVector<HBasicBlock*>& successors = switch_instr->GetBlock()->GetSuccessors(); 6140 for (uint32_t i = 0; i < num_entries; i++) { 6141 labels[i] = codegen_->GetLabelOf(successors[i]); 6142 } 6143 JumpTable* table = __ CreateJumpTable(std::move(labels), temp_reg); 6144 6145 // Remove the bias. 6146 Register key_reg; 6147 if (lower_bound != 0) { 6148 key_reg = locations->GetTemp(1).AsRegister<Register>(); 6149 __ AddConstant(key_reg, value_reg, -lower_bound); 6150 } else { 6151 key_reg = value_reg; 6152 } 6153 6154 // Check whether the value is in the table, jump to default block if not. 6155 __ CmpConstant(key_reg, num_entries - 1); 6156 __ b(codegen_->GetLabelOf(default_block), Condition::HI); 6157 6158 // Load the displacement from the table. 6159 __ ldr(temp_reg, Address(temp_reg, key_reg, Shift::LSL, 2)); 6160 6161 // Dispatch is a direct add to the PC (for Thumb2). 6162 __ EmitJumpTableDispatch(table, temp_reg); 6163 } 6164} 6165 6166void LocationsBuilderARM::VisitArmDexCacheArraysBase(HArmDexCacheArraysBase* base) { 6167 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(base); 6168 locations->SetOut(Location::RequiresRegister()); 6169 codegen_->AddDexCacheArraysBase(base); 6170} 6171 6172void InstructionCodeGeneratorARM::VisitArmDexCacheArraysBase(HArmDexCacheArraysBase* base) { 6173 Register base_reg = base->GetLocations()->Out().AsRegister<Register>(); 6174 CodeGeneratorARM::DexCacheArraysBaseLabels* labels = codegen_->GetDexCacheArraysBaseLabels(base); 6175 __ BindTrackedLabel(&labels->movw_label); 6176 __ movw(base_reg, 0u); 6177 __ BindTrackedLabel(&labels->movt_label); 6178 __ movt(base_reg, 0u); 6179 __ BindTrackedLabel(&labels->add_pc_label); 6180 __ add(base_reg, base_reg, ShifterOperand(PC)); 6181} 6182 6183void CodeGeneratorARM::MoveFromReturnRegister(Location trg, Primitive::Type type) { 6184 if (!trg.IsValid()) { 6185 DCHECK(type == Primitive::kPrimVoid); 6186 return; 6187 } 6188 6189 DCHECK_NE(type, Primitive::kPrimVoid); 6190 6191 Location return_loc = InvokeDexCallingConventionVisitorARM().GetReturnLocation(type); 6192 if (return_loc.Equals(trg)) { 6193 return; 6194 } 6195 6196 // TODO: Consider pairs in the parallel move resolver, then this could be nicely merged 6197 // with the last branch. 6198 if (type == Primitive::kPrimLong) { 6199 HParallelMove parallel_move(GetGraph()->GetArena()); 6200 parallel_move.AddMove(return_loc.ToLow(), trg.ToLow(), Primitive::kPrimInt, nullptr); 6201 parallel_move.AddMove(return_loc.ToHigh(), trg.ToHigh(), Primitive::kPrimInt, nullptr); 6202 GetMoveResolver()->EmitNativeCode(¶llel_move); 6203 } else if (type == Primitive::kPrimDouble) { 6204 HParallelMove parallel_move(GetGraph()->GetArena()); 6205 parallel_move.AddMove(return_loc.ToLow(), trg.ToLow(), Primitive::kPrimFloat, nullptr); 6206 parallel_move.AddMove(return_loc.ToHigh(), trg.ToHigh(), Primitive::kPrimFloat, nullptr); 6207 GetMoveResolver()->EmitNativeCode(¶llel_move); 6208 } else { 6209 // Let the parallel move resolver take care of all of this. 6210 HParallelMove parallel_move(GetGraph()->GetArena()); 6211 parallel_move.AddMove(return_loc, trg, type, nullptr); 6212 GetMoveResolver()->EmitNativeCode(¶llel_move); 6213 } 6214} 6215 6216#undef __ 6217#undef QUICK_ENTRY_POINT 6218 6219} // namespace arm 6220} // namespace art 6221