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