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