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