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