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