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