code_generator_arm.cc revision 1a65388f1d86bb232c2e44fecb44cebe13105d2e
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 "true" (integer value 1). 1416 if (cond->AsIntConstant()->IsTrue()) { 1417 if (true_target != nullptr) { 1418 __ b(true_target); 1419 } 1420 } else { 1421 DCHECK(cond->AsIntConstant()->IsFalse()) << cond->AsIntConstant()->GetValue(); 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* info) { 1547 codegen_->MaybeRecordNativeDebugInfo(info, info->GetDexPc()); 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 &= kMaxLongShiftValue; 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} 3276void LocationsBuilderARM::HandleRotate(HRor* ror) { 3277 LocationSummary* locations = 3278 new (GetGraph()->GetArena()) LocationSummary(ror, LocationSummary::kNoCall); 3279 switch (ror->GetResultType()) { 3280 case Primitive::kPrimInt: { 3281 locations->SetInAt(0, Location::RequiresRegister()); 3282 locations->SetInAt(1, Location::RegisterOrConstant(ror->InputAt(1))); 3283 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 3284 break; 3285 } 3286 case Primitive::kPrimLong: { 3287 locations->SetInAt(0, Location::RequiresRegister()); 3288 if (ror->InputAt(1)->IsConstant()) { 3289 locations->SetInAt(1, Location::ConstantLocation(ror->InputAt(1)->AsConstant())); 3290 } else { 3291 locations->SetInAt(1, Location::RequiresRegister()); 3292 locations->AddTemp(Location::RequiresRegister()); 3293 locations->AddTemp(Location::RequiresRegister()); 3294 } 3295 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); 3296 break; 3297 } 3298 default: 3299 LOG(FATAL) << "Unexpected operation type " << ror->GetResultType(); 3300 } 3301} 3302 3303void InstructionCodeGeneratorARM::HandleRotate(HRor* ror) { 3304 LocationSummary* locations = ror->GetLocations(); 3305 Primitive::Type type = ror->GetResultType(); 3306 switch (type) { 3307 case Primitive::kPrimInt: { 3308 HandleIntegerRotate(locations); 3309 break; 3310 } 3311 case Primitive::kPrimLong: { 3312 HandleLongRotate(locations); 3313 break; 3314 } 3315 default: 3316 LOG(FATAL) << "Unexpected operation type " << type; 3317 UNREACHABLE(); 3318 } 3319} 3320 3321void LocationsBuilderARM::VisitRor(HRor* op) { 3322 HandleRotate(op); 3323} 3324 3325void InstructionCodeGeneratorARM::VisitRor(HRor* op) { 3326 HandleRotate(op); 3327} 3328 3329void LocationsBuilderARM::HandleShift(HBinaryOperation* op) { 3330 DCHECK(op->IsShl() || op->IsShr() || op->IsUShr()); 3331 3332 LocationSummary* locations = 3333 new (GetGraph()->GetArena()) LocationSummary(op, LocationSummary::kNoCall); 3334 3335 switch (op->GetResultType()) { 3336 case Primitive::kPrimInt: { 3337 locations->SetInAt(0, Location::RequiresRegister()); 3338 if (op->InputAt(1)->IsConstant()) { 3339 locations->SetInAt(1, Location::ConstantLocation(op->InputAt(1)->AsConstant())); 3340 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 3341 } else { 3342 locations->SetInAt(1, Location::RequiresRegister()); 3343 // Make the output overlap, as it will be used to hold the masked 3344 // second input. 3345 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); 3346 } 3347 break; 3348 } 3349 case Primitive::kPrimLong: { 3350 locations->SetInAt(0, Location::RequiresRegister()); 3351 if (op->InputAt(1)->IsConstant()) { 3352 locations->SetInAt(1, Location::ConstantLocation(op->InputAt(1)->AsConstant())); 3353 // For simplicity, use kOutputOverlap even though we only require that low registers 3354 // don't clash with high registers which the register allocator currently guarantees. 3355 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); 3356 } else { 3357 locations->SetInAt(1, Location::RequiresRegister()); 3358 locations->AddTemp(Location::RequiresRegister()); 3359 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); 3360 } 3361 break; 3362 } 3363 default: 3364 LOG(FATAL) << "Unexpected operation type " << op->GetResultType(); 3365 } 3366} 3367 3368void InstructionCodeGeneratorARM::HandleShift(HBinaryOperation* op) { 3369 DCHECK(op->IsShl() || op->IsShr() || op->IsUShr()); 3370 3371 LocationSummary* locations = op->GetLocations(); 3372 Location out = locations->Out(); 3373 Location first = locations->InAt(0); 3374 Location second = locations->InAt(1); 3375 3376 Primitive::Type type = op->GetResultType(); 3377 switch (type) { 3378 case Primitive::kPrimInt: { 3379 Register out_reg = out.AsRegister<Register>(); 3380 Register first_reg = first.AsRegister<Register>(); 3381 if (second.IsRegister()) { 3382 Register second_reg = second.AsRegister<Register>(); 3383 // ARM doesn't mask the shift count so we need to do it ourselves. 3384 __ and_(out_reg, second_reg, ShifterOperand(kMaxIntShiftValue)); 3385 if (op->IsShl()) { 3386 __ Lsl(out_reg, first_reg, out_reg); 3387 } else if (op->IsShr()) { 3388 __ Asr(out_reg, first_reg, out_reg); 3389 } else { 3390 __ Lsr(out_reg, first_reg, out_reg); 3391 } 3392 } else { 3393 int32_t cst = second.GetConstant()->AsIntConstant()->GetValue(); 3394 uint32_t shift_value = static_cast<uint32_t>(cst & kMaxIntShiftValue); 3395 if (shift_value == 0) { // ARM does not support shifting with 0 immediate. 3396 __ Mov(out_reg, first_reg); 3397 } else if (op->IsShl()) { 3398 __ Lsl(out_reg, first_reg, shift_value); 3399 } else if (op->IsShr()) { 3400 __ Asr(out_reg, first_reg, shift_value); 3401 } else { 3402 __ Lsr(out_reg, first_reg, shift_value); 3403 } 3404 } 3405 break; 3406 } 3407 case Primitive::kPrimLong: { 3408 Register o_h = out.AsRegisterPairHigh<Register>(); 3409 Register o_l = out.AsRegisterPairLow<Register>(); 3410 3411 Register high = first.AsRegisterPairHigh<Register>(); 3412 Register low = first.AsRegisterPairLow<Register>(); 3413 3414 if (second.IsRegister()) { 3415 Register temp = locations->GetTemp(0).AsRegister<Register>(); 3416 3417 Register second_reg = second.AsRegister<Register>(); 3418 3419 if (op->IsShl()) { 3420 __ and_(o_l, second_reg, ShifterOperand(kMaxLongShiftValue)); 3421 // Shift the high part 3422 __ Lsl(o_h, high, o_l); 3423 // Shift the low part and `or` what overflew on the high part 3424 __ rsb(temp, o_l, ShifterOperand(kArmBitsPerWord)); 3425 __ Lsr(temp, low, temp); 3426 __ orr(o_h, o_h, ShifterOperand(temp)); 3427 // If the shift is > 32 bits, override the high part 3428 __ subs(temp, o_l, ShifterOperand(kArmBitsPerWord)); 3429 __ it(PL); 3430 __ Lsl(o_h, low, temp, PL); 3431 // Shift the low part 3432 __ Lsl(o_l, low, o_l); 3433 } else if (op->IsShr()) { 3434 __ and_(o_h, second_reg, ShifterOperand(kMaxLongShiftValue)); 3435 // Shift the low part 3436 __ Lsr(o_l, low, o_h); 3437 // Shift the high part and `or` what underflew on the low part 3438 __ rsb(temp, o_h, ShifterOperand(kArmBitsPerWord)); 3439 __ Lsl(temp, high, temp); 3440 __ orr(o_l, o_l, ShifterOperand(temp)); 3441 // If the shift is > 32 bits, override the low part 3442 __ subs(temp, o_h, ShifterOperand(kArmBitsPerWord)); 3443 __ it(PL); 3444 __ Asr(o_l, high, temp, PL); 3445 // Shift the high part 3446 __ Asr(o_h, high, o_h); 3447 } else { 3448 __ and_(o_h, second_reg, ShifterOperand(kMaxLongShiftValue)); 3449 // same as Shr except we use `Lsr`s and not `Asr`s 3450 __ Lsr(o_l, low, o_h); 3451 __ rsb(temp, o_h, ShifterOperand(kArmBitsPerWord)); 3452 __ Lsl(temp, high, temp); 3453 __ orr(o_l, o_l, ShifterOperand(temp)); 3454 __ subs(temp, o_h, ShifterOperand(kArmBitsPerWord)); 3455 __ it(PL); 3456 __ Lsr(o_l, high, temp, PL); 3457 __ Lsr(o_h, high, o_h); 3458 } 3459 } else { 3460 // Register allocator doesn't create partial overlap. 3461 DCHECK_NE(o_l, high); 3462 DCHECK_NE(o_h, low); 3463 int32_t cst = second.GetConstant()->AsIntConstant()->GetValue(); 3464 uint32_t shift_value = static_cast<uint32_t>(cst & kMaxLongShiftValue); 3465 if (shift_value > 32) { 3466 if (op->IsShl()) { 3467 __ Lsl(o_h, low, shift_value - 32); 3468 __ LoadImmediate(o_l, 0); 3469 } else if (op->IsShr()) { 3470 __ Asr(o_l, high, shift_value - 32); 3471 __ Asr(o_h, high, 31); 3472 } else { 3473 __ Lsr(o_l, high, shift_value - 32); 3474 __ LoadImmediate(o_h, 0); 3475 } 3476 } else if (shift_value == 32) { 3477 if (op->IsShl()) { 3478 __ mov(o_h, ShifterOperand(low)); 3479 __ LoadImmediate(o_l, 0); 3480 } else if (op->IsShr()) { 3481 __ mov(o_l, ShifterOperand(high)); 3482 __ Asr(o_h, high, 31); 3483 } else { 3484 __ mov(o_l, ShifterOperand(high)); 3485 __ LoadImmediate(o_h, 0); 3486 } 3487 } else if (shift_value == 1) { 3488 if (op->IsShl()) { 3489 __ Lsls(o_l, low, 1); 3490 __ adc(o_h, high, ShifterOperand(high)); 3491 } else if (op->IsShr()) { 3492 __ Asrs(o_h, high, 1); 3493 __ Rrx(o_l, low); 3494 } else { 3495 __ Lsrs(o_h, high, 1); 3496 __ Rrx(o_l, low); 3497 } 3498 } else { 3499 DCHECK(2 <= shift_value && shift_value < 32) << shift_value; 3500 if (op->IsShl()) { 3501 __ Lsl(o_h, high, shift_value); 3502 __ orr(o_h, o_h, ShifterOperand(low, LSR, 32 - shift_value)); 3503 __ Lsl(o_l, low, shift_value); 3504 } else if (op->IsShr()) { 3505 __ Lsr(o_l, low, shift_value); 3506 __ orr(o_l, o_l, ShifterOperand(high, LSL, 32 - shift_value)); 3507 __ Asr(o_h, high, shift_value); 3508 } else { 3509 __ Lsr(o_l, low, shift_value); 3510 __ orr(o_l, o_l, ShifterOperand(high, LSL, 32 - shift_value)); 3511 __ Lsr(o_h, high, shift_value); 3512 } 3513 } 3514 } 3515 break; 3516 } 3517 default: 3518 LOG(FATAL) << "Unexpected operation type " << type; 3519 UNREACHABLE(); 3520 } 3521} 3522 3523void LocationsBuilderARM::VisitShl(HShl* shl) { 3524 HandleShift(shl); 3525} 3526 3527void InstructionCodeGeneratorARM::VisitShl(HShl* shl) { 3528 HandleShift(shl); 3529} 3530 3531void LocationsBuilderARM::VisitShr(HShr* shr) { 3532 HandleShift(shr); 3533} 3534 3535void InstructionCodeGeneratorARM::VisitShr(HShr* shr) { 3536 HandleShift(shr); 3537} 3538 3539void LocationsBuilderARM::VisitUShr(HUShr* ushr) { 3540 HandleShift(ushr); 3541} 3542 3543void InstructionCodeGeneratorARM::VisitUShr(HUShr* ushr) { 3544 HandleShift(ushr); 3545} 3546 3547void LocationsBuilderARM::VisitNewInstance(HNewInstance* instruction) { 3548 LocationSummary* locations = 3549 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall); 3550 if (instruction->IsStringAlloc()) { 3551 locations->AddTemp(Location::RegisterLocation(kMethodRegisterArgument)); 3552 } else { 3553 InvokeRuntimeCallingConvention calling_convention; 3554 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); 3555 locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); 3556 } 3557 locations->SetOut(Location::RegisterLocation(R0)); 3558} 3559 3560void InstructionCodeGeneratorARM::VisitNewInstance(HNewInstance* instruction) { 3561 // Note: if heap poisoning is enabled, the entry point takes cares 3562 // of poisoning the reference. 3563 if (instruction->IsStringAlloc()) { 3564 // String is allocated through StringFactory. Call NewEmptyString entry point. 3565 Register temp = instruction->GetLocations()->GetTemp(0).AsRegister<Register>(); 3566 MemberOffset code_offset = ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmWordSize); 3567 __ LoadFromOffset(kLoadWord, temp, TR, QUICK_ENTRY_POINT(pNewEmptyString)); 3568 __ LoadFromOffset(kLoadWord, LR, temp, code_offset.Int32Value()); 3569 __ blx(LR); 3570 codegen_->RecordPcInfo(instruction, instruction->GetDexPc()); 3571 } else { 3572 codegen_->InvokeRuntime(instruction->GetEntrypoint(), 3573 instruction, 3574 instruction->GetDexPc(), 3575 nullptr); 3576 CheckEntrypointTypes<kQuickAllocObjectWithAccessCheck, void*, uint32_t, ArtMethod*>(); 3577 } 3578} 3579 3580void LocationsBuilderARM::VisitNewArray(HNewArray* instruction) { 3581 LocationSummary* locations = 3582 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall); 3583 InvokeRuntimeCallingConvention calling_convention; 3584 locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0))); 3585 locations->SetOut(Location::RegisterLocation(R0)); 3586 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); 3587 locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(2))); 3588} 3589 3590void InstructionCodeGeneratorARM::VisitNewArray(HNewArray* instruction) { 3591 InvokeRuntimeCallingConvention calling_convention; 3592 __ LoadImmediate(calling_convention.GetRegisterAt(0), instruction->GetTypeIndex()); 3593 // Note: if heap poisoning is enabled, the entry point takes cares 3594 // of poisoning the reference. 3595 codegen_->InvokeRuntime(instruction->GetEntrypoint(), 3596 instruction, 3597 instruction->GetDexPc(), 3598 nullptr); 3599 CheckEntrypointTypes<kQuickAllocArrayWithAccessCheck, void*, uint32_t, int32_t, ArtMethod*>(); 3600} 3601 3602void LocationsBuilderARM::VisitParameterValue(HParameterValue* instruction) { 3603 LocationSummary* locations = 3604 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); 3605 Location location = parameter_visitor_.GetNextLocation(instruction->GetType()); 3606 if (location.IsStackSlot()) { 3607 location = Location::StackSlot(location.GetStackIndex() + codegen_->GetFrameSize()); 3608 } else if (location.IsDoubleStackSlot()) { 3609 location = Location::DoubleStackSlot(location.GetStackIndex() + codegen_->GetFrameSize()); 3610 } 3611 locations->SetOut(location); 3612} 3613 3614void InstructionCodeGeneratorARM::VisitParameterValue( 3615 HParameterValue* instruction ATTRIBUTE_UNUSED) { 3616 // Nothing to do, the parameter is already at its location. 3617} 3618 3619void LocationsBuilderARM::VisitCurrentMethod(HCurrentMethod* instruction) { 3620 LocationSummary* locations = 3621 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); 3622 locations->SetOut(Location::RegisterLocation(kMethodRegisterArgument)); 3623} 3624 3625void InstructionCodeGeneratorARM::VisitCurrentMethod(HCurrentMethod* instruction ATTRIBUTE_UNUSED) { 3626 // Nothing to do, the method is already at its location. 3627} 3628 3629void LocationsBuilderARM::VisitNot(HNot* not_) { 3630 LocationSummary* locations = 3631 new (GetGraph()->GetArena()) LocationSummary(not_, LocationSummary::kNoCall); 3632 locations->SetInAt(0, Location::RequiresRegister()); 3633 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 3634} 3635 3636void InstructionCodeGeneratorARM::VisitNot(HNot* not_) { 3637 LocationSummary* locations = not_->GetLocations(); 3638 Location out = locations->Out(); 3639 Location in = locations->InAt(0); 3640 switch (not_->GetResultType()) { 3641 case Primitive::kPrimInt: 3642 __ mvn(out.AsRegister<Register>(), ShifterOperand(in.AsRegister<Register>())); 3643 break; 3644 3645 case Primitive::kPrimLong: 3646 __ mvn(out.AsRegisterPairLow<Register>(), 3647 ShifterOperand(in.AsRegisterPairLow<Register>())); 3648 __ mvn(out.AsRegisterPairHigh<Register>(), 3649 ShifterOperand(in.AsRegisterPairHigh<Register>())); 3650 break; 3651 3652 default: 3653 LOG(FATAL) << "Unimplemented type for not operation " << not_->GetResultType(); 3654 } 3655} 3656 3657void LocationsBuilderARM::VisitBooleanNot(HBooleanNot* bool_not) { 3658 LocationSummary* locations = 3659 new (GetGraph()->GetArena()) LocationSummary(bool_not, LocationSummary::kNoCall); 3660 locations->SetInAt(0, Location::RequiresRegister()); 3661 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 3662} 3663 3664void InstructionCodeGeneratorARM::VisitBooleanNot(HBooleanNot* bool_not) { 3665 LocationSummary* locations = bool_not->GetLocations(); 3666 Location out = locations->Out(); 3667 Location in = locations->InAt(0); 3668 __ eor(out.AsRegister<Register>(), in.AsRegister<Register>(), ShifterOperand(1)); 3669} 3670 3671void LocationsBuilderARM::VisitCompare(HCompare* compare) { 3672 LocationSummary* locations = 3673 new (GetGraph()->GetArena()) LocationSummary(compare, LocationSummary::kNoCall); 3674 switch (compare->InputAt(0)->GetType()) { 3675 case Primitive::kPrimInt: 3676 case Primitive::kPrimLong: { 3677 locations->SetInAt(0, Location::RequiresRegister()); 3678 locations->SetInAt(1, Location::RequiresRegister()); 3679 // Output overlaps because it is written before doing the low comparison. 3680 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); 3681 break; 3682 } 3683 case Primitive::kPrimFloat: 3684 case Primitive::kPrimDouble: { 3685 locations->SetInAt(0, Location::RequiresFpuRegister()); 3686 locations->SetInAt(1, Location::RequiresFpuRegister()); 3687 locations->SetOut(Location::RequiresRegister()); 3688 break; 3689 } 3690 default: 3691 LOG(FATAL) << "Unexpected type for compare operation " << compare->InputAt(0)->GetType(); 3692 } 3693} 3694 3695void InstructionCodeGeneratorARM::VisitCompare(HCompare* compare) { 3696 LocationSummary* locations = compare->GetLocations(); 3697 Register out = locations->Out().AsRegister<Register>(); 3698 Location left = locations->InAt(0); 3699 Location right = locations->InAt(1); 3700 3701 Label less, greater, done; 3702 Primitive::Type type = compare->InputAt(0)->GetType(); 3703 Condition less_cond; 3704 switch (type) { 3705 case Primitive::kPrimInt: { 3706 __ LoadImmediate(out, 0); 3707 __ cmp(left.AsRegister<Register>(), 3708 ShifterOperand(right.AsRegister<Register>())); // Signed compare. 3709 less_cond = LT; 3710 break; 3711 } 3712 case Primitive::kPrimLong: { 3713 __ cmp(left.AsRegisterPairHigh<Register>(), 3714 ShifterOperand(right.AsRegisterPairHigh<Register>())); // Signed compare. 3715 __ b(&less, LT); 3716 __ b(&greater, GT); 3717 // Do LoadImmediate before the last `cmp`, as LoadImmediate might affect the status flags. 3718 __ LoadImmediate(out, 0); 3719 __ cmp(left.AsRegisterPairLow<Register>(), 3720 ShifterOperand(right.AsRegisterPairLow<Register>())); // Unsigned compare. 3721 less_cond = LO; 3722 break; 3723 } 3724 case Primitive::kPrimFloat: 3725 case Primitive::kPrimDouble: { 3726 __ LoadImmediate(out, 0); 3727 if (type == Primitive::kPrimFloat) { 3728 __ vcmps(left.AsFpuRegister<SRegister>(), right.AsFpuRegister<SRegister>()); 3729 } else { 3730 __ vcmpd(FromLowSToD(left.AsFpuRegisterPairLow<SRegister>()), 3731 FromLowSToD(right.AsFpuRegisterPairLow<SRegister>())); 3732 } 3733 __ vmstat(); // transfer FP status register to ARM APSR. 3734 less_cond = ARMFPCondition(kCondLT, compare->IsGtBias()); 3735 break; 3736 } 3737 default: 3738 LOG(FATAL) << "Unexpected compare type " << type; 3739 UNREACHABLE(); 3740 } 3741 3742 __ b(&done, EQ); 3743 __ b(&less, less_cond); 3744 3745 __ Bind(&greater); 3746 __ LoadImmediate(out, 1); 3747 __ b(&done); 3748 3749 __ Bind(&less); 3750 __ LoadImmediate(out, -1); 3751 3752 __ Bind(&done); 3753} 3754 3755void LocationsBuilderARM::VisitPhi(HPhi* instruction) { 3756 LocationSummary* locations = 3757 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); 3758 for (size_t i = 0, e = instruction->InputCount(); i < e; ++i) { 3759 locations->SetInAt(i, Location::Any()); 3760 } 3761 locations->SetOut(Location::Any()); 3762} 3763 3764void InstructionCodeGeneratorARM::VisitPhi(HPhi* instruction ATTRIBUTE_UNUSED) { 3765 LOG(FATAL) << "Unreachable"; 3766} 3767 3768void CodeGeneratorARM::GenerateMemoryBarrier(MemBarrierKind kind) { 3769 // TODO (ported from quick): revisit ARM barrier kinds. 3770 DmbOptions flavor = DmbOptions::ISH; // Quiet C++ warnings. 3771 switch (kind) { 3772 case MemBarrierKind::kAnyStore: 3773 case MemBarrierKind::kLoadAny: 3774 case MemBarrierKind::kAnyAny: { 3775 flavor = DmbOptions::ISH; 3776 break; 3777 } 3778 case MemBarrierKind::kStoreStore: { 3779 flavor = DmbOptions::ISHST; 3780 break; 3781 } 3782 default: 3783 LOG(FATAL) << "Unexpected memory barrier " << kind; 3784 } 3785 __ dmb(flavor); 3786} 3787 3788void InstructionCodeGeneratorARM::GenerateWideAtomicLoad(Register addr, 3789 uint32_t offset, 3790 Register out_lo, 3791 Register out_hi) { 3792 if (offset != 0) { 3793 // Ensure `out_lo` is different from `addr`, so that loading 3794 // `offset` into `out_lo` does not clutter `addr`. 3795 DCHECK_NE(out_lo, addr); 3796 __ LoadImmediate(out_lo, offset); 3797 __ add(IP, addr, ShifterOperand(out_lo)); 3798 addr = IP; 3799 } 3800 __ ldrexd(out_lo, out_hi, addr); 3801} 3802 3803void InstructionCodeGeneratorARM::GenerateWideAtomicStore(Register addr, 3804 uint32_t offset, 3805 Register value_lo, 3806 Register value_hi, 3807 Register temp1, 3808 Register temp2, 3809 HInstruction* instruction) { 3810 Label fail; 3811 if (offset != 0) { 3812 __ LoadImmediate(temp1, offset); 3813 __ add(IP, addr, ShifterOperand(temp1)); 3814 addr = IP; 3815 } 3816 __ Bind(&fail); 3817 // We need a load followed by store. (The address used in a STREX instruction must 3818 // be the same as the address in the most recently executed LDREX instruction.) 3819 __ ldrexd(temp1, temp2, addr); 3820 codegen_->MaybeRecordImplicitNullCheck(instruction); 3821 __ strexd(temp1, value_lo, value_hi, addr); 3822 __ CompareAndBranchIfNonZero(temp1, &fail); 3823} 3824 3825void LocationsBuilderARM::HandleFieldSet(HInstruction* instruction, const FieldInfo& field_info) { 3826 DCHECK(instruction->IsInstanceFieldSet() || instruction->IsStaticFieldSet()); 3827 3828 LocationSummary* locations = 3829 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); 3830 locations->SetInAt(0, Location::RequiresRegister()); 3831 3832 Primitive::Type field_type = field_info.GetFieldType(); 3833 if (Primitive::IsFloatingPointType(field_type)) { 3834 locations->SetInAt(1, Location::RequiresFpuRegister()); 3835 } else { 3836 locations->SetInAt(1, Location::RequiresRegister()); 3837 } 3838 3839 bool is_wide = field_type == Primitive::kPrimLong || field_type == Primitive::kPrimDouble; 3840 bool generate_volatile = field_info.IsVolatile() 3841 && is_wide 3842 && !codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd(); 3843 bool needs_write_barrier = 3844 CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1)); 3845 // Temporary registers for the write barrier. 3846 // TODO: consider renaming StoreNeedsWriteBarrier to StoreNeedsGCMark. 3847 if (needs_write_barrier) { 3848 locations->AddTemp(Location::RequiresRegister()); // Possibly used for reference poisoning too. 3849 locations->AddTemp(Location::RequiresRegister()); 3850 } else if (generate_volatile) { 3851 // ARM encoding have some additional constraints for ldrexd/strexd: 3852 // - registers need to be consecutive 3853 // - the first register should be even but not R14. 3854 // We don't test for ARM yet, and the assertion makes sure that we 3855 // revisit this if we ever enable ARM encoding. 3856 DCHECK_EQ(InstructionSet::kThumb2, codegen_->GetInstructionSet()); 3857 3858 locations->AddTemp(Location::RequiresRegister()); 3859 locations->AddTemp(Location::RequiresRegister()); 3860 if (field_type == Primitive::kPrimDouble) { 3861 // For doubles we need two more registers to copy the value. 3862 locations->AddTemp(Location::RegisterLocation(R2)); 3863 locations->AddTemp(Location::RegisterLocation(R3)); 3864 } 3865 } 3866} 3867 3868void InstructionCodeGeneratorARM::HandleFieldSet(HInstruction* instruction, 3869 const FieldInfo& field_info, 3870 bool value_can_be_null) { 3871 DCHECK(instruction->IsInstanceFieldSet() || instruction->IsStaticFieldSet()); 3872 3873 LocationSummary* locations = instruction->GetLocations(); 3874 Register base = locations->InAt(0).AsRegister<Register>(); 3875 Location value = locations->InAt(1); 3876 3877 bool is_volatile = field_info.IsVolatile(); 3878 bool atomic_ldrd_strd = codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd(); 3879 Primitive::Type field_type = field_info.GetFieldType(); 3880 uint32_t offset = field_info.GetFieldOffset().Uint32Value(); 3881 bool needs_write_barrier = 3882 CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1)); 3883 3884 if (is_volatile) { 3885 codegen_->GenerateMemoryBarrier(MemBarrierKind::kAnyStore); 3886 } 3887 3888 switch (field_type) { 3889 case Primitive::kPrimBoolean: 3890 case Primitive::kPrimByte: { 3891 __ StoreToOffset(kStoreByte, value.AsRegister<Register>(), base, offset); 3892 break; 3893 } 3894 3895 case Primitive::kPrimShort: 3896 case Primitive::kPrimChar: { 3897 __ StoreToOffset(kStoreHalfword, value.AsRegister<Register>(), base, offset); 3898 break; 3899 } 3900 3901 case Primitive::kPrimInt: 3902 case Primitive::kPrimNot: { 3903 if (kPoisonHeapReferences && needs_write_barrier) { 3904 // Note that in the case where `value` is a null reference, 3905 // we do not enter this block, as a null reference does not 3906 // need poisoning. 3907 DCHECK_EQ(field_type, Primitive::kPrimNot); 3908 Register temp = locations->GetTemp(0).AsRegister<Register>(); 3909 __ Mov(temp, value.AsRegister<Register>()); 3910 __ PoisonHeapReference(temp); 3911 __ StoreToOffset(kStoreWord, temp, base, offset); 3912 } else { 3913 __ StoreToOffset(kStoreWord, value.AsRegister<Register>(), base, offset); 3914 } 3915 break; 3916 } 3917 3918 case Primitive::kPrimLong: { 3919 if (is_volatile && !atomic_ldrd_strd) { 3920 GenerateWideAtomicStore(base, offset, 3921 value.AsRegisterPairLow<Register>(), 3922 value.AsRegisterPairHigh<Register>(), 3923 locations->GetTemp(0).AsRegister<Register>(), 3924 locations->GetTemp(1).AsRegister<Register>(), 3925 instruction); 3926 } else { 3927 __ StoreToOffset(kStoreWordPair, value.AsRegisterPairLow<Register>(), base, offset); 3928 codegen_->MaybeRecordImplicitNullCheck(instruction); 3929 } 3930 break; 3931 } 3932 3933 case Primitive::kPrimFloat: { 3934 __ StoreSToOffset(value.AsFpuRegister<SRegister>(), base, offset); 3935 break; 3936 } 3937 3938 case Primitive::kPrimDouble: { 3939 DRegister value_reg = FromLowSToD(value.AsFpuRegisterPairLow<SRegister>()); 3940 if (is_volatile && !atomic_ldrd_strd) { 3941 Register value_reg_lo = locations->GetTemp(0).AsRegister<Register>(); 3942 Register value_reg_hi = locations->GetTemp(1).AsRegister<Register>(); 3943 3944 __ vmovrrd(value_reg_lo, value_reg_hi, value_reg); 3945 3946 GenerateWideAtomicStore(base, offset, 3947 value_reg_lo, 3948 value_reg_hi, 3949 locations->GetTemp(2).AsRegister<Register>(), 3950 locations->GetTemp(3).AsRegister<Register>(), 3951 instruction); 3952 } else { 3953 __ StoreDToOffset(value_reg, base, offset); 3954 codegen_->MaybeRecordImplicitNullCheck(instruction); 3955 } 3956 break; 3957 } 3958 3959 case Primitive::kPrimVoid: 3960 LOG(FATAL) << "Unreachable type " << field_type; 3961 UNREACHABLE(); 3962 } 3963 3964 // Longs and doubles are handled in the switch. 3965 if (field_type != Primitive::kPrimLong && field_type != Primitive::kPrimDouble) { 3966 codegen_->MaybeRecordImplicitNullCheck(instruction); 3967 } 3968 3969 if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1))) { 3970 Register temp = locations->GetTemp(0).AsRegister<Register>(); 3971 Register card = locations->GetTemp(1).AsRegister<Register>(); 3972 codegen_->MarkGCCard( 3973 temp, card, base, value.AsRegister<Register>(), value_can_be_null); 3974 } 3975 3976 if (is_volatile) { 3977 codegen_->GenerateMemoryBarrier(MemBarrierKind::kAnyAny); 3978 } 3979} 3980 3981void LocationsBuilderARM::HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info) { 3982 DCHECK(instruction->IsInstanceFieldGet() || instruction->IsStaticFieldGet()); 3983 3984 bool object_field_get_with_read_barrier = 3985 kEmitCompilerReadBarrier && (field_info.GetFieldType() == Primitive::kPrimNot); 3986 LocationSummary* locations = 3987 new (GetGraph()->GetArena()) LocationSummary(instruction, 3988 object_field_get_with_read_barrier ? 3989 LocationSummary::kCallOnSlowPath : 3990 LocationSummary::kNoCall); 3991 locations->SetInAt(0, Location::RequiresRegister()); 3992 3993 bool volatile_for_double = field_info.IsVolatile() 3994 && (field_info.GetFieldType() == Primitive::kPrimDouble) 3995 && !codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd(); 3996 // The output overlaps in case of volatile long: we don't want the 3997 // code generated by GenerateWideAtomicLoad to overwrite the 3998 // object's location. Likewise, in the case of an object field get 3999 // with read barriers enabled, we do not want the load to overwrite 4000 // the object's location, as we need it to emit the read barrier. 4001 bool overlap = (field_info.IsVolatile() && (field_info.GetFieldType() == Primitive::kPrimLong)) || 4002 object_field_get_with_read_barrier; 4003 4004 if (Primitive::IsFloatingPointType(instruction->GetType())) { 4005 locations->SetOut(Location::RequiresFpuRegister()); 4006 } else { 4007 locations->SetOut(Location::RequiresRegister(), 4008 (overlap ? Location::kOutputOverlap : Location::kNoOutputOverlap)); 4009 } 4010 if (volatile_for_double) { 4011 // ARM encoding have some additional constraints for ldrexd/strexd: 4012 // - registers need to be consecutive 4013 // - the first register should be even but not R14. 4014 // We don't test for ARM yet, and the assertion makes sure that we 4015 // revisit this if we ever enable ARM encoding. 4016 DCHECK_EQ(InstructionSet::kThumb2, codegen_->GetInstructionSet()); 4017 locations->AddTemp(Location::RequiresRegister()); 4018 locations->AddTemp(Location::RequiresRegister()); 4019 } else if (object_field_get_with_read_barrier && kUseBakerReadBarrier) { 4020 // We need a temporary register for the read barrier marking slow 4021 // path in CodeGeneratorARM::GenerateFieldLoadWithBakerReadBarrier. 4022 locations->AddTemp(Location::RequiresRegister()); 4023 } 4024} 4025 4026Location LocationsBuilderARM::ArmEncodableConstantOrRegister(HInstruction* constant, 4027 Opcode opcode) { 4028 DCHECK(!Primitive::IsFloatingPointType(constant->GetType())); 4029 if (constant->IsConstant() && 4030 CanEncodeConstantAsImmediate(constant->AsConstant(), opcode)) { 4031 return Location::ConstantLocation(constant->AsConstant()); 4032 } 4033 return Location::RequiresRegister(); 4034} 4035 4036bool LocationsBuilderARM::CanEncodeConstantAsImmediate(HConstant* input_cst, 4037 Opcode opcode) { 4038 uint64_t value = static_cast<uint64_t>(Int64FromConstant(input_cst)); 4039 if (Primitive::Is64BitType(input_cst->GetType())) { 4040 return CanEncodeConstantAsImmediate(Low32Bits(value), opcode) && 4041 CanEncodeConstantAsImmediate(High32Bits(value), opcode); 4042 } else { 4043 return CanEncodeConstantAsImmediate(Low32Bits(value), opcode); 4044 } 4045} 4046 4047bool LocationsBuilderARM::CanEncodeConstantAsImmediate(uint32_t value, Opcode opcode) { 4048 ShifterOperand so; 4049 ArmAssembler* assembler = codegen_->GetAssembler(); 4050 if (assembler->ShifterOperandCanHold(kNoRegister, kNoRegister, opcode, value, &so)) { 4051 return true; 4052 } 4053 Opcode neg_opcode = kNoOperand; 4054 switch (opcode) { 4055 case AND: 4056 neg_opcode = BIC; 4057 break; 4058 case ORR: 4059 neg_opcode = ORN; 4060 break; 4061 default: 4062 return false; 4063 } 4064 return assembler->ShifterOperandCanHold(kNoRegister, kNoRegister, neg_opcode, ~value, &so); 4065} 4066 4067void InstructionCodeGeneratorARM::HandleFieldGet(HInstruction* instruction, 4068 const FieldInfo& field_info) { 4069 DCHECK(instruction->IsInstanceFieldGet() || instruction->IsStaticFieldGet()); 4070 4071 LocationSummary* locations = instruction->GetLocations(); 4072 Location base_loc = locations->InAt(0); 4073 Register base = base_loc.AsRegister<Register>(); 4074 Location out = locations->Out(); 4075 bool is_volatile = field_info.IsVolatile(); 4076 bool atomic_ldrd_strd = codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd(); 4077 Primitive::Type field_type = field_info.GetFieldType(); 4078 uint32_t offset = field_info.GetFieldOffset().Uint32Value(); 4079 4080 switch (field_type) { 4081 case Primitive::kPrimBoolean: 4082 __ LoadFromOffset(kLoadUnsignedByte, out.AsRegister<Register>(), base, offset); 4083 break; 4084 4085 case Primitive::kPrimByte: 4086 __ LoadFromOffset(kLoadSignedByte, out.AsRegister<Register>(), base, offset); 4087 break; 4088 4089 case Primitive::kPrimShort: 4090 __ LoadFromOffset(kLoadSignedHalfword, out.AsRegister<Register>(), base, offset); 4091 break; 4092 4093 case Primitive::kPrimChar: 4094 __ LoadFromOffset(kLoadUnsignedHalfword, out.AsRegister<Register>(), base, offset); 4095 break; 4096 4097 case Primitive::kPrimInt: 4098 __ LoadFromOffset(kLoadWord, out.AsRegister<Register>(), base, offset); 4099 break; 4100 4101 case Primitive::kPrimNot: { 4102 // /* HeapReference<Object> */ out = *(base + offset) 4103 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) { 4104 Location temp_loc = locations->GetTemp(0); 4105 // Note that a potential implicit null check is handled in this 4106 // CodeGeneratorARM::GenerateFieldLoadWithBakerReadBarrier call. 4107 codegen_->GenerateFieldLoadWithBakerReadBarrier( 4108 instruction, out, base, offset, temp_loc, /* needs_null_check */ true); 4109 if (is_volatile) { 4110 codegen_->GenerateMemoryBarrier(MemBarrierKind::kLoadAny); 4111 } 4112 } else { 4113 __ LoadFromOffset(kLoadWord, out.AsRegister<Register>(), base, offset); 4114 codegen_->MaybeRecordImplicitNullCheck(instruction); 4115 if (is_volatile) { 4116 codegen_->GenerateMemoryBarrier(MemBarrierKind::kLoadAny); 4117 } 4118 // If read barriers are enabled, emit read barriers other than 4119 // Baker's using a slow path (and also unpoison the loaded 4120 // reference, if heap poisoning is enabled). 4121 codegen_->MaybeGenerateReadBarrierSlow(instruction, out, out, base_loc, offset); 4122 } 4123 break; 4124 } 4125 4126 case Primitive::kPrimLong: 4127 if (is_volatile && !atomic_ldrd_strd) { 4128 GenerateWideAtomicLoad(base, offset, 4129 out.AsRegisterPairLow<Register>(), 4130 out.AsRegisterPairHigh<Register>()); 4131 } else { 4132 __ LoadFromOffset(kLoadWordPair, out.AsRegisterPairLow<Register>(), base, offset); 4133 } 4134 break; 4135 4136 case Primitive::kPrimFloat: 4137 __ LoadSFromOffset(out.AsFpuRegister<SRegister>(), base, offset); 4138 break; 4139 4140 case Primitive::kPrimDouble: { 4141 DRegister out_reg = FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()); 4142 if (is_volatile && !atomic_ldrd_strd) { 4143 Register lo = locations->GetTemp(0).AsRegister<Register>(); 4144 Register hi = locations->GetTemp(1).AsRegister<Register>(); 4145 GenerateWideAtomicLoad(base, offset, lo, hi); 4146 codegen_->MaybeRecordImplicitNullCheck(instruction); 4147 __ vmovdrr(out_reg, lo, hi); 4148 } else { 4149 __ LoadDFromOffset(out_reg, base, offset); 4150 codegen_->MaybeRecordImplicitNullCheck(instruction); 4151 } 4152 break; 4153 } 4154 4155 case Primitive::kPrimVoid: 4156 LOG(FATAL) << "Unreachable type " << field_type; 4157 UNREACHABLE(); 4158 } 4159 4160 if (field_type == Primitive::kPrimNot || field_type == Primitive::kPrimDouble) { 4161 // Potential implicit null checks, in the case of reference or 4162 // double fields, are handled in the previous switch statement. 4163 } else { 4164 codegen_->MaybeRecordImplicitNullCheck(instruction); 4165 } 4166 4167 if (is_volatile) { 4168 if (field_type == Primitive::kPrimNot) { 4169 // Memory barriers, in the case of references, are also handled 4170 // in the previous switch statement. 4171 } else { 4172 codegen_->GenerateMemoryBarrier(MemBarrierKind::kLoadAny); 4173 } 4174 } 4175} 4176 4177void LocationsBuilderARM::VisitInstanceFieldSet(HInstanceFieldSet* instruction) { 4178 HandleFieldSet(instruction, instruction->GetFieldInfo()); 4179} 4180 4181void InstructionCodeGeneratorARM::VisitInstanceFieldSet(HInstanceFieldSet* instruction) { 4182 HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull()); 4183} 4184 4185void LocationsBuilderARM::VisitInstanceFieldGet(HInstanceFieldGet* instruction) { 4186 HandleFieldGet(instruction, instruction->GetFieldInfo()); 4187} 4188 4189void InstructionCodeGeneratorARM::VisitInstanceFieldGet(HInstanceFieldGet* instruction) { 4190 HandleFieldGet(instruction, instruction->GetFieldInfo()); 4191} 4192 4193void LocationsBuilderARM::VisitStaticFieldGet(HStaticFieldGet* instruction) { 4194 HandleFieldGet(instruction, instruction->GetFieldInfo()); 4195} 4196 4197void InstructionCodeGeneratorARM::VisitStaticFieldGet(HStaticFieldGet* instruction) { 4198 HandleFieldGet(instruction, instruction->GetFieldInfo()); 4199} 4200 4201void LocationsBuilderARM::VisitStaticFieldSet(HStaticFieldSet* instruction) { 4202 HandleFieldSet(instruction, instruction->GetFieldInfo()); 4203} 4204 4205void InstructionCodeGeneratorARM::VisitStaticFieldSet(HStaticFieldSet* instruction) { 4206 HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull()); 4207} 4208 4209void LocationsBuilderARM::VisitUnresolvedInstanceFieldGet( 4210 HUnresolvedInstanceFieldGet* instruction) { 4211 FieldAccessCallingConventionARM calling_convention; 4212 codegen_->CreateUnresolvedFieldLocationSummary( 4213 instruction, instruction->GetFieldType(), calling_convention); 4214} 4215 4216void InstructionCodeGeneratorARM::VisitUnresolvedInstanceFieldGet( 4217 HUnresolvedInstanceFieldGet* instruction) { 4218 FieldAccessCallingConventionARM calling_convention; 4219 codegen_->GenerateUnresolvedFieldAccess(instruction, 4220 instruction->GetFieldType(), 4221 instruction->GetFieldIndex(), 4222 instruction->GetDexPc(), 4223 calling_convention); 4224} 4225 4226void LocationsBuilderARM::VisitUnresolvedInstanceFieldSet( 4227 HUnresolvedInstanceFieldSet* instruction) { 4228 FieldAccessCallingConventionARM calling_convention; 4229 codegen_->CreateUnresolvedFieldLocationSummary( 4230 instruction, instruction->GetFieldType(), calling_convention); 4231} 4232 4233void InstructionCodeGeneratorARM::VisitUnresolvedInstanceFieldSet( 4234 HUnresolvedInstanceFieldSet* instruction) { 4235 FieldAccessCallingConventionARM calling_convention; 4236 codegen_->GenerateUnresolvedFieldAccess(instruction, 4237 instruction->GetFieldType(), 4238 instruction->GetFieldIndex(), 4239 instruction->GetDexPc(), 4240 calling_convention); 4241} 4242 4243void LocationsBuilderARM::VisitUnresolvedStaticFieldGet( 4244 HUnresolvedStaticFieldGet* instruction) { 4245 FieldAccessCallingConventionARM calling_convention; 4246 codegen_->CreateUnresolvedFieldLocationSummary( 4247 instruction, instruction->GetFieldType(), calling_convention); 4248} 4249 4250void InstructionCodeGeneratorARM::VisitUnresolvedStaticFieldGet( 4251 HUnresolvedStaticFieldGet* instruction) { 4252 FieldAccessCallingConventionARM calling_convention; 4253 codegen_->GenerateUnresolvedFieldAccess(instruction, 4254 instruction->GetFieldType(), 4255 instruction->GetFieldIndex(), 4256 instruction->GetDexPc(), 4257 calling_convention); 4258} 4259 4260void LocationsBuilderARM::VisitUnresolvedStaticFieldSet( 4261 HUnresolvedStaticFieldSet* instruction) { 4262 FieldAccessCallingConventionARM calling_convention; 4263 codegen_->CreateUnresolvedFieldLocationSummary( 4264 instruction, instruction->GetFieldType(), calling_convention); 4265} 4266 4267void InstructionCodeGeneratorARM::VisitUnresolvedStaticFieldSet( 4268 HUnresolvedStaticFieldSet* instruction) { 4269 FieldAccessCallingConventionARM calling_convention; 4270 codegen_->GenerateUnresolvedFieldAccess(instruction, 4271 instruction->GetFieldType(), 4272 instruction->GetFieldIndex(), 4273 instruction->GetDexPc(), 4274 calling_convention); 4275} 4276 4277void LocationsBuilderARM::VisitNullCheck(HNullCheck* instruction) { 4278 LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock() 4279 ? LocationSummary::kCallOnSlowPath 4280 : LocationSummary::kNoCall; 4281 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind); 4282 locations->SetInAt(0, Location::RequiresRegister()); 4283 if (instruction->HasUses()) { 4284 locations->SetOut(Location::SameAsFirstInput()); 4285 } 4286} 4287 4288void InstructionCodeGeneratorARM::GenerateImplicitNullCheck(HNullCheck* instruction) { 4289 if (codegen_->CanMoveNullCheckToUser(instruction)) { 4290 return; 4291 } 4292 Location obj = instruction->GetLocations()->InAt(0); 4293 4294 __ LoadFromOffset(kLoadWord, IP, obj.AsRegister<Register>(), 0); 4295 codegen_->RecordPcInfo(instruction, instruction->GetDexPc()); 4296} 4297 4298void InstructionCodeGeneratorARM::GenerateExplicitNullCheck(HNullCheck* instruction) { 4299 SlowPathCode* slow_path = new (GetGraph()->GetArena()) NullCheckSlowPathARM(instruction); 4300 codegen_->AddSlowPath(slow_path); 4301 4302 LocationSummary* locations = instruction->GetLocations(); 4303 Location obj = locations->InAt(0); 4304 4305 __ CompareAndBranchIfZero(obj.AsRegister<Register>(), slow_path->GetEntryLabel()); 4306} 4307 4308void InstructionCodeGeneratorARM::VisitNullCheck(HNullCheck* instruction) { 4309 if (codegen_->IsImplicitNullCheckAllowed(instruction)) { 4310 GenerateImplicitNullCheck(instruction); 4311 } else { 4312 GenerateExplicitNullCheck(instruction); 4313 } 4314} 4315 4316void LocationsBuilderARM::VisitArrayGet(HArrayGet* instruction) { 4317 bool object_array_get_with_read_barrier = 4318 kEmitCompilerReadBarrier && (instruction->GetType() == Primitive::kPrimNot); 4319 LocationSummary* locations = 4320 new (GetGraph()->GetArena()) LocationSummary(instruction, 4321 object_array_get_with_read_barrier ? 4322 LocationSummary::kCallOnSlowPath : 4323 LocationSummary::kNoCall); 4324 locations->SetInAt(0, Location::RequiresRegister()); 4325 locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1))); 4326 if (Primitive::IsFloatingPointType(instruction->GetType())) { 4327 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); 4328 } else { 4329 // The output overlaps in the case of an object array get with 4330 // read barriers enabled: we do not want the move to overwrite the 4331 // array's location, as we need it to emit the read barrier. 4332 locations->SetOut( 4333 Location::RequiresRegister(), 4334 object_array_get_with_read_barrier ? Location::kOutputOverlap : Location::kNoOutputOverlap); 4335 } 4336 // We need a temporary register for the read barrier marking slow 4337 // path in CodeGeneratorARM::GenerateArrayLoadWithBakerReadBarrier. 4338 if (object_array_get_with_read_barrier && kUseBakerReadBarrier) { 4339 locations->AddTemp(Location::RequiresRegister()); 4340 } 4341} 4342 4343void InstructionCodeGeneratorARM::VisitArrayGet(HArrayGet* instruction) { 4344 LocationSummary* locations = instruction->GetLocations(); 4345 Location obj_loc = locations->InAt(0); 4346 Register obj = obj_loc.AsRegister<Register>(); 4347 Location index = locations->InAt(1); 4348 Location out_loc = locations->Out(); 4349 4350 Primitive::Type type = instruction->GetType(); 4351 switch (type) { 4352 case Primitive::kPrimBoolean: { 4353 uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value(); 4354 Register out = out_loc.AsRegister<Register>(); 4355 if (index.IsConstant()) { 4356 size_t offset = 4357 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset; 4358 __ LoadFromOffset(kLoadUnsignedByte, out, obj, offset); 4359 } else { 4360 __ add(IP, obj, ShifterOperand(index.AsRegister<Register>())); 4361 __ LoadFromOffset(kLoadUnsignedByte, out, IP, data_offset); 4362 } 4363 break; 4364 } 4365 4366 case Primitive::kPrimByte: { 4367 uint32_t data_offset = mirror::Array::DataOffset(sizeof(int8_t)).Uint32Value(); 4368 Register out = out_loc.AsRegister<Register>(); 4369 if (index.IsConstant()) { 4370 size_t offset = 4371 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset; 4372 __ LoadFromOffset(kLoadSignedByte, out, obj, offset); 4373 } else { 4374 __ add(IP, obj, ShifterOperand(index.AsRegister<Register>())); 4375 __ LoadFromOffset(kLoadSignedByte, out, IP, data_offset); 4376 } 4377 break; 4378 } 4379 4380 case Primitive::kPrimShort: { 4381 uint32_t data_offset = mirror::Array::DataOffset(sizeof(int16_t)).Uint32Value(); 4382 Register out = out_loc.AsRegister<Register>(); 4383 if (index.IsConstant()) { 4384 size_t offset = 4385 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset; 4386 __ LoadFromOffset(kLoadSignedHalfword, out, obj, offset); 4387 } else { 4388 __ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_2)); 4389 __ LoadFromOffset(kLoadSignedHalfword, out, IP, data_offset); 4390 } 4391 break; 4392 } 4393 4394 case Primitive::kPrimChar: { 4395 uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value(); 4396 Register out = out_loc.AsRegister<Register>(); 4397 if (index.IsConstant()) { 4398 size_t offset = 4399 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset; 4400 __ LoadFromOffset(kLoadUnsignedHalfword, out, obj, offset); 4401 } else { 4402 __ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_2)); 4403 __ LoadFromOffset(kLoadUnsignedHalfword, out, IP, data_offset); 4404 } 4405 break; 4406 } 4407 4408 case Primitive::kPrimInt: { 4409 uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value(); 4410 Register out = out_loc.AsRegister<Register>(); 4411 if (index.IsConstant()) { 4412 size_t offset = 4413 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; 4414 __ LoadFromOffset(kLoadWord, out, obj, offset); 4415 } else { 4416 __ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4)); 4417 __ LoadFromOffset(kLoadWord, out, IP, data_offset); 4418 } 4419 break; 4420 } 4421 4422 case Primitive::kPrimNot: { 4423 static_assert( 4424 sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t), 4425 "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes."); 4426 uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value(); 4427 // /* HeapReference<Object> */ out = 4428 // *(obj + data_offset + index * sizeof(HeapReference<Object>)) 4429 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) { 4430 Location temp = locations->GetTemp(0); 4431 // Note that a potential implicit null check is handled in this 4432 // CodeGeneratorARM::GenerateArrayLoadWithBakerReadBarrier call. 4433 codegen_->GenerateArrayLoadWithBakerReadBarrier( 4434 instruction, out_loc, obj, data_offset, index, temp, /* needs_null_check */ true); 4435 } else { 4436 Register out = out_loc.AsRegister<Register>(); 4437 if (index.IsConstant()) { 4438 size_t offset = 4439 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; 4440 __ LoadFromOffset(kLoadWord, out, obj, offset); 4441 codegen_->MaybeRecordImplicitNullCheck(instruction); 4442 // If read barriers are enabled, emit read barriers other than 4443 // Baker's using a slow path (and also unpoison the loaded 4444 // reference, if heap poisoning is enabled). 4445 codegen_->MaybeGenerateReadBarrierSlow(instruction, out_loc, out_loc, obj_loc, offset); 4446 } else { 4447 __ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4)); 4448 __ LoadFromOffset(kLoadWord, out, IP, data_offset); 4449 codegen_->MaybeRecordImplicitNullCheck(instruction); 4450 // If read barriers are enabled, emit read barriers other than 4451 // Baker's using a slow path (and also unpoison the loaded 4452 // reference, if heap poisoning is enabled). 4453 codegen_->MaybeGenerateReadBarrierSlow( 4454 instruction, out_loc, out_loc, obj_loc, data_offset, index); 4455 } 4456 } 4457 break; 4458 } 4459 4460 case Primitive::kPrimLong: { 4461 uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value(); 4462 if (index.IsConstant()) { 4463 size_t offset = 4464 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset; 4465 __ LoadFromOffset(kLoadWordPair, out_loc.AsRegisterPairLow<Register>(), obj, offset); 4466 } else { 4467 __ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_8)); 4468 __ LoadFromOffset(kLoadWordPair, out_loc.AsRegisterPairLow<Register>(), IP, data_offset); 4469 } 4470 break; 4471 } 4472 4473 case Primitive::kPrimFloat: { 4474 uint32_t data_offset = mirror::Array::DataOffset(sizeof(float)).Uint32Value(); 4475 SRegister out = out_loc.AsFpuRegister<SRegister>(); 4476 if (index.IsConstant()) { 4477 size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; 4478 __ LoadSFromOffset(out, obj, offset); 4479 } else { 4480 __ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4)); 4481 __ LoadSFromOffset(out, IP, data_offset); 4482 } 4483 break; 4484 } 4485 4486 case Primitive::kPrimDouble: { 4487 uint32_t data_offset = mirror::Array::DataOffset(sizeof(double)).Uint32Value(); 4488 SRegister out = out_loc.AsFpuRegisterPairLow<SRegister>(); 4489 if (index.IsConstant()) { 4490 size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset; 4491 __ LoadDFromOffset(FromLowSToD(out), obj, offset); 4492 } else { 4493 __ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_8)); 4494 __ LoadDFromOffset(FromLowSToD(out), IP, data_offset); 4495 } 4496 break; 4497 } 4498 4499 case Primitive::kPrimVoid: 4500 LOG(FATAL) << "Unreachable type " << type; 4501 UNREACHABLE(); 4502 } 4503 4504 if (type == Primitive::kPrimNot) { 4505 // Potential implicit null checks, in the case of reference 4506 // arrays, are handled in the previous switch statement. 4507 } else { 4508 codegen_->MaybeRecordImplicitNullCheck(instruction); 4509 } 4510} 4511 4512void LocationsBuilderARM::VisitArraySet(HArraySet* instruction) { 4513 Primitive::Type value_type = instruction->GetComponentType(); 4514 4515 bool needs_write_barrier = 4516 CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue()); 4517 bool may_need_runtime_call_for_type_check = instruction->NeedsTypeCheck(); 4518 bool object_array_set_with_read_barrier = 4519 kEmitCompilerReadBarrier && (value_type == Primitive::kPrimNot); 4520 4521 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary( 4522 instruction, 4523 (may_need_runtime_call_for_type_check || object_array_set_with_read_barrier) ? 4524 LocationSummary::kCallOnSlowPath : 4525 LocationSummary::kNoCall); 4526 4527 locations->SetInAt(0, Location::RequiresRegister()); 4528 locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1))); 4529 if (Primitive::IsFloatingPointType(value_type)) { 4530 locations->SetInAt(2, Location::RequiresFpuRegister()); 4531 } else { 4532 locations->SetInAt(2, Location::RequiresRegister()); 4533 } 4534 if (needs_write_barrier) { 4535 // Temporary registers for the write barrier. 4536 locations->AddTemp(Location::RequiresRegister()); // Possibly used for ref. poisoning too. 4537 locations->AddTemp(Location::RequiresRegister()); 4538 } 4539} 4540 4541void InstructionCodeGeneratorARM::VisitArraySet(HArraySet* instruction) { 4542 LocationSummary* locations = instruction->GetLocations(); 4543 Location array_loc = locations->InAt(0); 4544 Register array = array_loc.AsRegister<Register>(); 4545 Location index = locations->InAt(1); 4546 Primitive::Type value_type = instruction->GetComponentType(); 4547 bool may_need_runtime_call_for_type_check = instruction->NeedsTypeCheck(); 4548 bool needs_write_barrier = 4549 CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue()); 4550 4551 switch (value_type) { 4552 case Primitive::kPrimBoolean: 4553 case Primitive::kPrimByte: { 4554 uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value(); 4555 Register value = locations->InAt(2).AsRegister<Register>(); 4556 if (index.IsConstant()) { 4557 size_t offset = 4558 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset; 4559 __ StoreToOffset(kStoreByte, value, array, offset); 4560 } else { 4561 __ add(IP, array, ShifterOperand(index.AsRegister<Register>())); 4562 __ StoreToOffset(kStoreByte, value, IP, data_offset); 4563 } 4564 break; 4565 } 4566 4567 case Primitive::kPrimShort: 4568 case Primitive::kPrimChar: { 4569 uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value(); 4570 Register value = locations->InAt(2).AsRegister<Register>(); 4571 if (index.IsConstant()) { 4572 size_t offset = 4573 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset; 4574 __ StoreToOffset(kStoreHalfword, value, array, offset); 4575 } else { 4576 __ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_2)); 4577 __ StoreToOffset(kStoreHalfword, value, IP, data_offset); 4578 } 4579 break; 4580 } 4581 4582 case Primitive::kPrimNot: { 4583 uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value(); 4584 Location value_loc = locations->InAt(2); 4585 Register value = value_loc.AsRegister<Register>(); 4586 Register source = value; 4587 4588 if (instruction->InputAt(2)->IsNullConstant()) { 4589 // Just setting null. 4590 if (index.IsConstant()) { 4591 size_t offset = 4592 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; 4593 __ StoreToOffset(kStoreWord, source, array, offset); 4594 } else { 4595 DCHECK(index.IsRegister()) << index; 4596 __ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4)); 4597 __ StoreToOffset(kStoreWord, source, IP, data_offset); 4598 } 4599 codegen_->MaybeRecordImplicitNullCheck(instruction); 4600 DCHECK(!needs_write_barrier); 4601 DCHECK(!may_need_runtime_call_for_type_check); 4602 break; 4603 } 4604 4605 DCHECK(needs_write_barrier); 4606 Register temp1 = locations->GetTemp(0).AsRegister<Register>(); 4607 Register temp2 = locations->GetTemp(1).AsRegister<Register>(); 4608 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value(); 4609 uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value(); 4610 uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value(); 4611 Label done; 4612 SlowPathCode* slow_path = nullptr; 4613 4614 if (may_need_runtime_call_for_type_check) { 4615 slow_path = new (GetGraph()->GetArena()) ArraySetSlowPathARM(instruction); 4616 codegen_->AddSlowPath(slow_path); 4617 if (instruction->GetValueCanBeNull()) { 4618 Label non_zero; 4619 __ CompareAndBranchIfNonZero(value, &non_zero); 4620 if (index.IsConstant()) { 4621 size_t offset = 4622 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; 4623 __ StoreToOffset(kStoreWord, value, array, offset); 4624 } else { 4625 DCHECK(index.IsRegister()) << index; 4626 __ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4)); 4627 __ StoreToOffset(kStoreWord, value, IP, data_offset); 4628 } 4629 codegen_->MaybeRecordImplicitNullCheck(instruction); 4630 __ b(&done); 4631 __ Bind(&non_zero); 4632 } 4633 4634 if (kEmitCompilerReadBarrier) { 4635 // When read barriers are enabled, the type checking 4636 // instrumentation requires two read barriers: 4637 // 4638 // __ Mov(temp2, temp1); 4639 // // /* HeapReference<Class> */ temp1 = temp1->component_type_ 4640 // __ LoadFromOffset(kLoadWord, temp1, temp1, component_offset); 4641 // codegen_->GenerateReadBarrierSlow( 4642 // instruction, temp1_loc, temp1_loc, temp2_loc, component_offset); 4643 // 4644 // // /* HeapReference<Class> */ temp2 = value->klass_ 4645 // __ LoadFromOffset(kLoadWord, temp2, value, class_offset); 4646 // codegen_->GenerateReadBarrierSlow( 4647 // instruction, temp2_loc, temp2_loc, value_loc, class_offset, temp1_loc); 4648 // 4649 // __ cmp(temp1, ShifterOperand(temp2)); 4650 // 4651 // However, the second read barrier may trash `temp`, as it 4652 // is a temporary register, and as such would not be saved 4653 // along with live registers before calling the runtime (nor 4654 // restored afterwards). So in this case, we bail out and 4655 // delegate the work to the array set slow path. 4656 // 4657 // TODO: Extend the register allocator to support a new 4658 // "(locally) live temp" location so as to avoid always 4659 // going into the slow path when read barriers are enabled. 4660 __ b(slow_path->GetEntryLabel()); 4661 } else { 4662 // /* HeapReference<Class> */ temp1 = array->klass_ 4663 __ LoadFromOffset(kLoadWord, temp1, array, class_offset); 4664 codegen_->MaybeRecordImplicitNullCheck(instruction); 4665 __ MaybeUnpoisonHeapReference(temp1); 4666 4667 // /* HeapReference<Class> */ temp1 = temp1->component_type_ 4668 __ LoadFromOffset(kLoadWord, temp1, temp1, component_offset); 4669 // /* HeapReference<Class> */ temp2 = value->klass_ 4670 __ LoadFromOffset(kLoadWord, temp2, value, class_offset); 4671 // If heap poisoning is enabled, no need to unpoison `temp1` 4672 // nor `temp2`, as we are comparing two poisoned references. 4673 __ cmp(temp1, ShifterOperand(temp2)); 4674 4675 if (instruction->StaticTypeOfArrayIsObjectArray()) { 4676 Label do_put; 4677 __ b(&do_put, EQ); 4678 // If heap poisoning is enabled, the `temp1` reference has 4679 // not been unpoisoned yet; unpoison it now. 4680 __ MaybeUnpoisonHeapReference(temp1); 4681 4682 // /* HeapReference<Class> */ temp1 = temp1->super_class_ 4683 __ LoadFromOffset(kLoadWord, temp1, temp1, super_offset); 4684 // If heap poisoning is enabled, no need to unpoison 4685 // `temp1`, as we are comparing against null below. 4686 __ CompareAndBranchIfNonZero(temp1, slow_path->GetEntryLabel()); 4687 __ Bind(&do_put); 4688 } else { 4689 __ b(slow_path->GetEntryLabel(), NE); 4690 } 4691 } 4692 } 4693 4694 if (kPoisonHeapReferences) { 4695 // Note that in the case where `value` is a null reference, 4696 // we do not enter this block, as a null reference does not 4697 // need poisoning. 4698 DCHECK_EQ(value_type, Primitive::kPrimNot); 4699 __ Mov(temp1, value); 4700 __ PoisonHeapReference(temp1); 4701 source = temp1; 4702 } 4703 4704 if (index.IsConstant()) { 4705 size_t offset = 4706 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; 4707 __ StoreToOffset(kStoreWord, source, array, offset); 4708 } else { 4709 DCHECK(index.IsRegister()) << index; 4710 __ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4)); 4711 __ StoreToOffset(kStoreWord, source, IP, data_offset); 4712 } 4713 4714 if (!may_need_runtime_call_for_type_check) { 4715 codegen_->MaybeRecordImplicitNullCheck(instruction); 4716 } 4717 4718 codegen_->MarkGCCard(temp1, temp2, array, value, instruction->GetValueCanBeNull()); 4719 4720 if (done.IsLinked()) { 4721 __ Bind(&done); 4722 } 4723 4724 if (slow_path != nullptr) { 4725 __ Bind(slow_path->GetExitLabel()); 4726 } 4727 4728 break; 4729 } 4730 4731 case Primitive::kPrimInt: { 4732 uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value(); 4733 Register value = locations->InAt(2).AsRegister<Register>(); 4734 if (index.IsConstant()) { 4735 size_t offset = 4736 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; 4737 __ StoreToOffset(kStoreWord, value, array, offset); 4738 } else { 4739 DCHECK(index.IsRegister()) << index; 4740 __ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4)); 4741 __ StoreToOffset(kStoreWord, value, IP, data_offset); 4742 } 4743 break; 4744 } 4745 4746 case Primitive::kPrimLong: { 4747 uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value(); 4748 Location value = locations->InAt(2); 4749 if (index.IsConstant()) { 4750 size_t offset = 4751 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset; 4752 __ StoreToOffset(kStoreWordPair, value.AsRegisterPairLow<Register>(), array, offset); 4753 } else { 4754 __ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_8)); 4755 __ StoreToOffset(kStoreWordPair, value.AsRegisterPairLow<Register>(), IP, data_offset); 4756 } 4757 break; 4758 } 4759 4760 case Primitive::kPrimFloat: { 4761 uint32_t data_offset = mirror::Array::DataOffset(sizeof(float)).Uint32Value(); 4762 Location value = locations->InAt(2); 4763 DCHECK(value.IsFpuRegister()); 4764 if (index.IsConstant()) { 4765 size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; 4766 __ StoreSToOffset(value.AsFpuRegister<SRegister>(), array, offset); 4767 } else { 4768 __ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4)); 4769 __ StoreSToOffset(value.AsFpuRegister<SRegister>(), IP, data_offset); 4770 } 4771 break; 4772 } 4773 4774 case Primitive::kPrimDouble: { 4775 uint32_t data_offset = mirror::Array::DataOffset(sizeof(double)).Uint32Value(); 4776 Location value = locations->InAt(2); 4777 DCHECK(value.IsFpuRegisterPair()); 4778 if (index.IsConstant()) { 4779 size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset; 4780 __ StoreDToOffset(FromLowSToD(value.AsFpuRegisterPairLow<SRegister>()), array, offset); 4781 } else { 4782 __ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_8)); 4783 __ StoreDToOffset(FromLowSToD(value.AsFpuRegisterPairLow<SRegister>()), IP, data_offset); 4784 } 4785 4786 break; 4787 } 4788 4789 case Primitive::kPrimVoid: 4790 LOG(FATAL) << "Unreachable type " << value_type; 4791 UNREACHABLE(); 4792 } 4793 4794 // Objects are handled in the switch. 4795 if (value_type != Primitive::kPrimNot) { 4796 codegen_->MaybeRecordImplicitNullCheck(instruction); 4797 } 4798} 4799 4800void LocationsBuilderARM::VisitArrayLength(HArrayLength* instruction) { 4801 LocationSummary* locations = 4802 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); 4803 locations->SetInAt(0, Location::RequiresRegister()); 4804 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 4805} 4806 4807void InstructionCodeGeneratorARM::VisitArrayLength(HArrayLength* instruction) { 4808 LocationSummary* locations = instruction->GetLocations(); 4809 uint32_t offset = mirror::Array::LengthOffset().Uint32Value(); 4810 Register obj = locations->InAt(0).AsRegister<Register>(); 4811 Register out = locations->Out().AsRegister<Register>(); 4812 __ LoadFromOffset(kLoadWord, out, obj, offset); 4813 codegen_->MaybeRecordImplicitNullCheck(instruction); 4814} 4815 4816void LocationsBuilderARM::VisitBoundsCheck(HBoundsCheck* instruction) { 4817 LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock() 4818 ? LocationSummary::kCallOnSlowPath 4819 : LocationSummary::kNoCall; 4820 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind); 4821 locations->SetInAt(0, Location::RequiresRegister()); 4822 locations->SetInAt(1, Location::RequiresRegister()); 4823 if (instruction->HasUses()) { 4824 locations->SetOut(Location::SameAsFirstInput()); 4825 } 4826} 4827 4828void InstructionCodeGeneratorARM::VisitBoundsCheck(HBoundsCheck* instruction) { 4829 LocationSummary* locations = instruction->GetLocations(); 4830 SlowPathCode* slow_path = 4831 new (GetGraph()->GetArena()) BoundsCheckSlowPathARM(instruction); 4832 codegen_->AddSlowPath(slow_path); 4833 4834 Register index = locations->InAt(0).AsRegister<Register>(); 4835 Register length = locations->InAt(1).AsRegister<Register>(); 4836 4837 __ cmp(index, ShifterOperand(length)); 4838 __ b(slow_path->GetEntryLabel(), HS); 4839} 4840 4841void CodeGeneratorARM::MarkGCCard(Register temp, 4842 Register card, 4843 Register object, 4844 Register value, 4845 bool can_be_null) { 4846 Label is_null; 4847 if (can_be_null) { 4848 __ CompareAndBranchIfZero(value, &is_null); 4849 } 4850 __ LoadFromOffset(kLoadWord, card, TR, Thread::CardTableOffset<kArmWordSize>().Int32Value()); 4851 __ Lsr(temp, object, gc::accounting::CardTable::kCardShift); 4852 __ strb(card, Address(card, temp)); 4853 if (can_be_null) { 4854 __ Bind(&is_null); 4855 } 4856} 4857 4858void LocationsBuilderARM::VisitParallelMove(HParallelMove* instruction ATTRIBUTE_UNUSED) { 4859 LOG(FATAL) << "Unreachable"; 4860} 4861 4862void InstructionCodeGeneratorARM::VisitParallelMove(HParallelMove* instruction) { 4863 codegen_->GetMoveResolver()->EmitNativeCode(instruction); 4864} 4865 4866void LocationsBuilderARM::VisitSuspendCheck(HSuspendCheck* instruction) { 4867 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnSlowPath); 4868} 4869 4870void InstructionCodeGeneratorARM::VisitSuspendCheck(HSuspendCheck* instruction) { 4871 HBasicBlock* block = instruction->GetBlock(); 4872 if (block->GetLoopInformation() != nullptr) { 4873 DCHECK(block->GetLoopInformation()->GetSuspendCheck() == instruction); 4874 // The back edge will generate the suspend check. 4875 return; 4876 } 4877 if (block->IsEntryBlock() && instruction->GetNext()->IsGoto()) { 4878 // The goto will generate the suspend check. 4879 return; 4880 } 4881 GenerateSuspendCheck(instruction, nullptr); 4882} 4883 4884void InstructionCodeGeneratorARM::GenerateSuspendCheck(HSuspendCheck* instruction, 4885 HBasicBlock* successor) { 4886 SuspendCheckSlowPathARM* slow_path = 4887 down_cast<SuspendCheckSlowPathARM*>(instruction->GetSlowPath()); 4888 if (slow_path == nullptr) { 4889 slow_path = new (GetGraph()->GetArena()) SuspendCheckSlowPathARM(instruction, successor); 4890 instruction->SetSlowPath(slow_path); 4891 codegen_->AddSlowPath(slow_path); 4892 if (successor != nullptr) { 4893 DCHECK(successor->IsLoopHeader()); 4894 codegen_->ClearSpillSlotsFromLoopPhisInStackMap(instruction); 4895 } 4896 } else { 4897 DCHECK_EQ(slow_path->GetSuccessor(), successor); 4898 } 4899 4900 __ LoadFromOffset( 4901 kLoadUnsignedHalfword, IP, TR, Thread::ThreadFlagsOffset<kArmWordSize>().Int32Value()); 4902 if (successor == nullptr) { 4903 __ CompareAndBranchIfNonZero(IP, slow_path->GetEntryLabel()); 4904 __ Bind(slow_path->GetReturnLabel()); 4905 } else { 4906 __ CompareAndBranchIfZero(IP, codegen_->GetLabelOf(successor)); 4907 __ b(slow_path->GetEntryLabel()); 4908 } 4909} 4910 4911ArmAssembler* ParallelMoveResolverARM::GetAssembler() const { 4912 return codegen_->GetAssembler(); 4913} 4914 4915void ParallelMoveResolverARM::EmitMove(size_t index) { 4916 MoveOperands* move = moves_[index]; 4917 Location source = move->GetSource(); 4918 Location destination = move->GetDestination(); 4919 4920 if (source.IsRegister()) { 4921 if (destination.IsRegister()) { 4922 __ Mov(destination.AsRegister<Register>(), source.AsRegister<Register>()); 4923 } else if (destination.IsFpuRegister()) { 4924 __ vmovsr(destination.AsFpuRegister<SRegister>(), source.AsRegister<Register>()); 4925 } else { 4926 DCHECK(destination.IsStackSlot()); 4927 __ StoreToOffset(kStoreWord, source.AsRegister<Register>(), 4928 SP, destination.GetStackIndex()); 4929 } 4930 } else if (source.IsStackSlot()) { 4931 if (destination.IsRegister()) { 4932 __ LoadFromOffset(kLoadWord, destination.AsRegister<Register>(), 4933 SP, source.GetStackIndex()); 4934 } else if (destination.IsFpuRegister()) { 4935 __ LoadSFromOffset(destination.AsFpuRegister<SRegister>(), SP, source.GetStackIndex()); 4936 } else { 4937 DCHECK(destination.IsStackSlot()); 4938 __ LoadFromOffset(kLoadWord, IP, SP, source.GetStackIndex()); 4939 __ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex()); 4940 } 4941 } else if (source.IsFpuRegister()) { 4942 if (destination.IsRegister()) { 4943 __ vmovrs(destination.AsRegister<Register>(), source.AsFpuRegister<SRegister>()); 4944 } else if (destination.IsFpuRegister()) { 4945 __ vmovs(destination.AsFpuRegister<SRegister>(), source.AsFpuRegister<SRegister>()); 4946 } else { 4947 DCHECK(destination.IsStackSlot()); 4948 __ StoreSToOffset(source.AsFpuRegister<SRegister>(), SP, destination.GetStackIndex()); 4949 } 4950 } else if (source.IsDoubleStackSlot()) { 4951 if (destination.IsDoubleStackSlot()) { 4952 __ LoadDFromOffset(DTMP, SP, source.GetStackIndex()); 4953 __ StoreDToOffset(DTMP, SP, destination.GetStackIndex()); 4954 } else if (destination.IsRegisterPair()) { 4955 DCHECK(ExpectedPairLayout(destination)); 4956 __ LoadFromOffset( 4957 kLoadWordPair, destination.AsRegisterPairLow<Register>(), SP, source.GetStackIndex()); 4958 } else { 4959 DCHECK(destination.IsFpuRegisterPair()) << destination; 4960 __ LoadDFromOffset(FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()), 4961 SP, 4962 source.GetStackIndex()); 4963 } 4964 } else if (source.IsRegisterPair()) { 4965 if (destination.IsRegisterPair()) { 4966 __ Mov(destination.AsRegisterPairLow<Register>(), source.AsRegisterPairLow<Register>()); 4967 __ Mov(destination.AsRegisterPairHigh<Register>(), source.AsRegisterPairHigh<Register>()); 4968 } else if (destination.IsFpuRegisterPair()) { 4969 __ vmovdrr(FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()), 4970 source.AsRegisterPairLow<Register>(), 4971 source.AsRegisterPairHigh<Register>()); 4972 } else { 4973 DCHECK(destination.IsDoubleStackSlot()) << destination; 4974 DCHECK(ExpectedPairLayout(source)); 4975 __ StoreToOffset( 4976 kStoreWordPair, source.AsRegisterPairLow<Register>(), SP, destination.GetStackIndex()); 4977 } 4978 } else if (source.IsFpuRegisterPair()) { 4979 if (destination.IsRegisterPair()) { 4980 __ vmovrrd(destination.AsRegisterPairLow<Register>(), 4981 destination.AsRegisterPairHigh<Register>(), 4982 FromLowSToD(source.AsFpuRegisterPairLow<SRegister>())); 4983 } else if (destination.IsFpuRegisterPair()) { 4984 __ vmovd(FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()), 4985 FromLowSToD(source.AsFpuRegisterPairLow<SRegister>())); 4986 } else { 4987 DCHECK(destination.IsDoubleStackSlot()) << destination; 4988 __ StoreDToOffset(FromLowSToD(source.AsFpuRegisterPairLow<SRegister>()), 4989 SP, 4990 destination.GetStackIndex()); 4991 } 4992 } else { 4993 DCHECK(source.IsConstant()) << source; 4994 HConstant* constant = source.GetConstant(); 4995 if (constant->IsIntConstant() || constant->IsNullConstant()) { 4996 int32_t value = CodeGenerator::GetInt32ValueOf(constant); 4997 if (destination.IsRegister()) { 4998 __ LoadImmediate(destination.AsRegister<Register>(), value); 4999 } else { 5000 DCHECK(destination.IsStackSlot()); 5001 __ LoadImmediate(IP, value); 5002 __ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex()); 5003 } 5004 } else if (constant->IsLongConstant()) { 5005 int64_t value = constant->AsLongConstant()->GetValue(); 5006 if (destination.IsRegisterPair()) { 5007 __ LoadImmediate(destination.AsRegisterPairLow<Register>(), Low32Bits(value)); 5008 __ LoadImmediate(destination.AsRegisterPairHigh<Register>(), High32Bits(value)); 5009 } else { 5010 DCHECK(destination.IsDoubleStackSlot()) << destination; 5011 __ LoadImmediate(IP, Low32Bits(value)); 5012 __ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex()); 5013 __ LoadImmediate(IP, High32Bits(value)); 5014 __ StoreToOffset(kStoreWord, IP, SP, destination.GetHighStackIndex(kArmWordSize)); 5015 } 5016 } else if (constant->IsDoubleConstant()) { 5017 double value = constant->AsDoubleConstant()->GetValue(); 5018 if (destination.IsFpuRegisterPair()) { 5019 __ LoadDImmediate(FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()), value); 5020 } else { 5021 DCHECK(destination.IsDoubleStackSlot()) << destination; 5022 uint64_t int_value = bit_cast<uint64_t, double>(value); 5023 __ LoadImmediate(IP, Low32Bits(int_value)); 5024 __ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex()); 5025 __ LoadImmediate(IP, High32Bits(int_value)); 5026 __ StoreToOffset(kStoreWord, IP, SP, destination.GetHighStackIndex(kArmWordSize)); 5027 } 5028 } else { 5029 DCHECK(constant->IsFloatConstant()) << constant->DebugName(); 5030 float value = constant->AsFloatConstant()->GetValue(); 5031 if (destination.IsFpuRegister()) { 5032 __ LoadSImmediate(destination.AsFpuRegister<SRegister>(), value); 5033 } else { 5034 DCHECK(destination.IsStackSlot()); 5035 __ LoadImmediate(IP, bit_cast<int32_t, float>(value)); 5036 __ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex()); 5037 } 5038 } 5039 } 5040} 5041 5042void ParallelMoveResolverARM::Exchange(Register reg, int mem) { 5043 __ Mov(IP, reg); 5044 __ LoadFromOffset(kLoadWord, reg, SP, mem); 5045 __ StoreToOffset(kStoreWord, IP, SP, mem); 5046} 5047 5048void ParallelMoveResolverARM::Exchange(int mem1, int mem2) { 5049 ScratchRegisterScope ensure_scratch(this, IP, R0, codegen_->GetNumberOfCoreRegisters()); 5050 int stack_offset = ensure_scratch.IsSpilled() ? kArmWordSize : 0; 5051 __ LoadFromOffset(kLoadWord, static_cast<Register>(ensure_scratch.GetRegister()), 5052 SP, mem1 + stack_offset); 5053 __ LoadFromOffset(kLoadWord, IP, SP, mem2 + stack_offset); 5054 __ StoreToOffset(kStoreWord, static_cast<Register>(ensure_scratch.GetRegister()), 5055 SP, mem2 + stack_offset); 5056 __ StoreToOffset(kStoreWord, IP, SP, mem1 + stack_offset); 5057} 5058 5059void ParallelMoveResolverARM::EmitSwap(size_t index) { 5060 MoveOperands* move = moves_[index]; 5061 Location source = move->GetSource(); 5062 Location destination = move->GetDestination(); 5063 5064 if (source.IsRegister() && destination.IsRegister()) { 5065 DCHECK_NE(source.AsRegister<Register>(), IP); 5066 DCHECK_NE(destination.AsRegister<Register>(), IP); 5067 __ Mov(IP, source.AsRegister<Register>()); 5068 __ Mov(source.AsRegister<Register>(), destination.AsRegister<Register>()); 5069 __ Mov(destination.AsRegister<Register>(), IP); 5070 } else if (source.IsRegister() && destination.IsStackSlot()) { 5071 Exchange(source.AsRegister<Register>(), destination.GetStackIndex()); 5072 } else if (source.IsStackSlot() && destination.IsRegister()) { 5073 Exchange(destination.AsRegister<Register>(), source.GetStackIndex()); 5074 } else if (source.IsStackSlot() && destination.IsStackSlot()) { 5075 Exchange(source.GetStackIndex(), destination.GetStackIndex()); 5076 } else if (source.IsFpuRegister() && destination.IsFpuRegister()) { 5077 __ vmovrs(IP, source.AsFpuRegister<SRegister>()); 5078 __ vmovs(source.AsFpuRegister<SRegister>(), destination.AsFpuRegister<SRegister>()); 5079 __ vmovsr(destination.AsFpuRegister<SRegister>(), IP); 5080 } else if (source.IsRegisterPair() && destination.IsRegisterPair()) { 5081 __ vmovdrr(DTMP, source.AsRegisterPairLow<Register>(), source.AsRegisterPairHigh<Register>()); 5082 __ Mov(source.AsRegisterPairLow<Register>(), destination.AsRegisterPairLow<Register>()); 5083 __ Mov(source.AsRegisterPairHigh<Register>(), destination.AsRegisterPairHigh<Register>()); 5084 __ vmovrrd(destination.AsRegisterPairLow<Register>(), 5085 destination.AsRegisterPairHigh<Register>(), 5086 DTMP); 5087 } else if (source.IsRegisterPair() || destination.IsRegisterPair()) { 5088 Register low_reg = source.IsRegisterPair() 5089 ? source.AsRegisterPairLow<Register>() 5090 : destination.AsRegisterPairLow<Register>(); 5091 int mem = source.IsRegisterPair() 5092 ? destination.GetStackIndex() 5093 : source.GetStackIndex(); 5094 DCHECK(ExpectedPairLayout(source.IsRegisterPair() ? source : destination)); 5095 __ vmovdrr(DTMP, low_reg, static_cast<Register>(low_reg + 1)); 5096 __ LoadFromOffset(kLoadWordPair, low_reg, SP, mem); 5097 __ StoreDToOffset(DTMP, SP, mem); 5098 } else if (source.IsFpuRegisterPair() && destination.IsFpuRegisterPair()) { 5099 DRegister first = FromLowSToD(source.AsFpuRegisterPairLow<SRegister>()); 5100 DRegister second = FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()); 5101 __ vmovd(DTMP, first); 5102 __ vmovd(first, second); 5103 __ vmovd(second, DTMP); 5104 } else if (source.IsFpuRegisterPair() || destination.IsFpuRegisterPair()) { 5105 DRegister reg = source.IsFpuRegisterPair() 5106 ? FromLowSToD(source.AsFpuRegisterPairLow<SRegister>()) 5107 : FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()); 5108 int mem = source.IsFpuRegisterPair() 5109 ? destination.GetStackIndex() 5110 : source.GetStackIndex(); 5111 __ vmovd(DTMP, reg); 5112 __ LoadDFromOffset(reg, SP, mem); 5113 __ StoreDToOffset(DTMP, SP, mem); 5114 } else if (source.IsFpuRegister() || destination.IsFpuRegister()) { 5115 SRegister reg = source.IsFpuRegister() ? source.AsFpuRegister<SRegister>() 5116 : destination.AsFpuRegister<SRegister>(); 5117 int mem = source.IsFpuRegister() 5118 ? destination.GetStackIndex() 5119 : source.GetStackIndex(); 5120 5121 __ vmovrs(IP, reg); 5122 __ LoadSFromOffset(reg, SP, mem); 5123 __ StoreToOffset(kStoreWord, IP, SP, mem); 5124 } else if (source.IsDoubleStackSlot() && destination.IsDoubleStackSlot()) { 5125 Exchange(source.GetStackIndex(), destination.GetStackIndex()); 5126 Exchange(source.GetHighStackIndex(kArmWordSize), destination.GetHighStackIndex(kArmWordSize)); 5127 } else { 5128 LOG(FATAL) << "Unimplemented" << source << " <-> " << destination; 5129 } 5130} 5131 5132void ParallelMoveResolverARM::SpillScratch(int reg) { 5133 __ Push(static_cast<Register>(reg)); 5134} 5135 5136void ParallelMoveResolverARM::RestoreScratch(int reg) { 5137 __ Pop(static_cast<Register>(reg)); 5138} 5139 5140void LocationsBuilderARM::VisitLoadClass(HLoadClass* cls) { 5141 InvokeRuntimeCallingConvention calling_convention; 5142 CodeGenerator::CreateLoadClassLocationSummary( 5143 cls, 5144 Location::RegisterLocation(calling_convention.GetRegisterAt(0)), 5145 Location::RegisterLocation(R0), 5146 /* code_generator_supports_read_barrier */ true); 5147} 5148 5149void InstructionCodeGeneratorARM::VisitLoadClass(HLoadClass* cls) { 5150 LocationSummary* locations = cls->GetLocations(); 5151 if (cls->NeedsAccessCheck()) { 5152 codegen_->MoveConstant(locations->GetTemp(0), cls->GetTypeIndex()); 5153 codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pInitializeTypeAndVerifyAccess), 5154 cls, 5155 cls->GetDexPc(), 5156 nullptr); 5157 CheckEntrypointTypes<kQuickInitializeTypeAndVerifyAccess, void*, uint32_t>(); 5158 return; 5159 } 5160 5161 Location out_loc = locations->Out(); 5162 Register out = out_loc.AsRegister<Register>(); 5163 Register current_method = locations->InAt(0).AsRegister<Register>(); 5164 5165 if (cls->IsReferrersClass()) { 5166 DCHECK(!cls->CanCallRuntime()); 5167 DCHECK(!cls->MustGenerateClinitCheck()); 5168 // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_ 5169 GenerateGcRootFieldLoad( 5170 cls, out_loc, current_method, ArtMethod::DeclaringClassOffset().Int32Value()); 5171 } else { 5172 // /* GcRoot<mirror::Class>[] */ out = 5173 // current_method.ptr_sized_fields_->dex_cache_resolved_types_ 5174 __ LoadFromOffset(kLoadWord, 5175 out, 5176 current_method, 5177 ArtMethod::DexCacheResolvedTypesOffset(kArmPointerSize).Int32Value()); 5178 // /* GcRoot<mirror::Class> */ out = out[type_index] 5179 GenerateGcRootFieldLoad(cls, out_loc, out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex())); 5180 5181 if (!cls->IsInDexCache() || cls->MustGenerateClinitCheck()) { 5182 DCHECK(cls->CanCallRuntime()); 5183 SlowPathCode* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathARM( 5184 cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck()); 5185 codegen_->AddSlowPath(slow_path); 5186 if (!cls->IsInDexCache()) { 5187 __ CompareAndBranchIfZero(out, slow_path->GetEntryLabel()); 5188 } 5189 if (cls->MustGenerateClinitCheck()) { 5190 GenerateClassInitializationCheck(slow_path, out); 5191 } else { 5192 __ Bind(slow_path->GetExitLabel()); 5193 } 5194 } 5195 } 5196} 5197 5198void LocationsBuilderARM::VisitClinitCheck(HClinitCheck* check) { 5199 LocationSummary* locations = 5200 new (GetGraph()->GetArena()) LocationSummary(check, LocationSummary::kCallOnSlowPath); 5201 locations->SetInAt(0, Location::RequiresRegister()); 5202 if (check->HasUses()) { 5203 locations->SetOut(Location::SameAsFirstInput()); 5204 } 5205} 5206 5207void InstructionCodeGeneratorARM::VisitClinitCheck(HClinitCheck* check) { 5208 // We assume the class is not null. 5209 SlowPathCode* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathARM( 5210 check->GetLoadClass(), check, check->GetDexPc(), true); 5211 codegen_->AddSlowPath(slow_path); 5212 GenerateClassInitializationCheck(slow_path, 5213 check->GetLocations()->InAt(0).AsRegister<Register>()); 5214} 5215 5216void InstructionCodeGeneratorARM::GenerateClassInitializationCheck( 5217 SlowPathCode* slow_path, Register class_reg) { 5218 __ LoadFromOffset(kLoadWord, IP, class_reg, mirror::Class::StatusOffset().Int32Value()); 5219 __ cmp(IP, ShifterOperand(mirror::Class::kStatusInitialized)); 5220 __ b(slow_path->GetEntryLabel(), LT); 5221 // Even if the initialized flag is set, we may be in a situation where caches are not synced 5222 // properly. Therefore, we do a memory fence. 5223 __ dmb(ISH); 5224 __ Bind(slow_path->GetExitLabel()); 5225} 5226 5227void LocationsBuilderARM::VisitLoadString(HLoadString* load) { 5228 LocationSummary::CallKind call_kind = (!load->IsInDexCache() || kEmitCompilerReadBarrier) 5229 ? LocationSummary::kCallOnSlowPath 5230 : LocationSummary::kNoCall; 5231 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(load, call_kind); 5232 locations->SetInAt(0, Location::RequiresRegister()); 5233 locations->SetOut(Location::RequiresRegister()); 5234} 5235 5236void InstructionCodeGeneratorARM::VisitLoadString(HLoadString* load) { 5237 LocationSummary* locations = load->GetLocations(); 5238 Location out_loc = locations->Out(); 5239 Register out = out_loc.AsRegister<Register>(); 5240 Register current_method = locations->InAt(0).AsRegister<Register>(); 5241 5242 // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_ 5243 GenerateGcRootFieldLoad( 5244 load, out_loc, current_method, ArtMethod::DeclaringClassOffset().Int32Value()); 5245 // /* GcRoot<mirror::String>[] */ out = out->dex_cache_strings_ 5246 __ LoadFromOffset(kLoadWord, out, out, mirror::Class::DexCacheStringsOffset().Int32Value()); 5247 // /* GcRoot<mirror::String> */ out = out[string_index] 5248 GenerateGcRootFieldLoad( 5249 load, out_loc, out, CodeGenerator::GetCacheOffset(load->GetStringIndex())); 5250 5251 if (!load->IsInDexCache()) { 5252 SlowPathCode* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathARM(load); 5253 codegen_->AddSlowPath(slow_path); 5254 __ CompareAndBranchIfZero(out, slow_path->GetEntryLabel()); 5255 __ Bind(slow_path->GetExitLabel()); 5256 } 5257} 5258 5259static int32_t GetExceptionTlsOffset() { 5260 return Thread::ExceptionOffset<kArmWordSize>().Int32Value(); 5261} 5262 5263void LocationsBuilderARM::VisitLoadException(HLoadException* load) { 5264 LocationSummary* locations = 5265 new (GetGraph()->GetArena()) LocationSummary(load, LocationSummary::kNoCall); 5266 locations->SetOut(Location::RequiresRegister()); 5267} 5268 5269void InstructionCodeGeneratorARM::VisitLoadException(HLoadException* load) { 5270 Register out = load->GetLocations()->Out().AsRegister<Register>(); 5271 __ LoadFromOffset(kLoadWord, out, TR, GetExceptionTlsOffset()); 5272} 5273 5274void LocationsBuilderARM::VisitClearException(HClearException* clear) { 5275 new (GetGraph()->GetArena()) LocationSummary(clear, LocationSummary::kNoCall); 5276} 5277 5278void InstructionCodeGeneratorARM::VisitClearException(HClearException* clear ATTRIBUTE_UNUSED) { 5279 __ LoadImmediate(IP, 0); 5280 __ StoreToOffset(kStoreWord, IP, TR, GetExceptionTlsOffset()); 5281} 5282 5283void LocationsBuilderARM::VisitThrow(HThrow* instruction) { 5284 LocationSummary* locations = 5285 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall); 5286 InvokeRuntimeCallingConvention calling_convention; 5287 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); 5288} 5289 5290void InstructionCodeGeneratorARM::VisitThrow(HThrow* instruction) { 5291 codegen_->InvokeRuntime( 5292 QUICK_ENTRY_POINT(pDeliverException), instruction, instruction->GetDexPc(), nullptr); 5293 CheckEntrypointTypes<kQuickDeliverException, void, mirror::Object*>(); 5294} 5295 5296static bool TypeCheckNeedsATemporary(TypeCheckKind type_check_kind) { 5297 return kEmitCompilerReadBarrier && 5298 (kUseBakerReadBarrier || 5299 type_check_kind == TypeCheckKind::kAbstractClassCheck || 5300 type_check_kind == TypeCheckKind::kClassHierarchyCheck || 5301 type_check_kind == TypeCheckKind::kArrayObjectCheck); 5302} 5303 5304void LocationsBuilderARM::VisitInstanceOf(HInstanceOf* instruction) { 5305 LocationSummary::CallKind call_kind = LocationSummary::kNoCall; 5306 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind(); 5307 switch (type_check_kind) { 5308 case TypeCheckKind::kExactCheck: 5309 case TypeCheckKind::kAbstractClassCheck: 5310 case TypeCheckKind::kClassHierarchyCheck: 5311 case TypeCheckKind::kArrayObjectCheck: 5312 call_kind = 5313 kEmitCompilerReadBarrier ? LocationSummary::kCallOnSlowPath : LocationSummary::kNoCall; 5314 break; 5315 case TypeCheckKind::kArrayCheck: 5316 case TypeCheckKind::kUnresolvedCheck: 5317 case TypeCheckKind::kInterfaceCheck: 5318 call_kind = LocationSummary::kCallOnSlowPath; 5319 break; 5320 } 5321 5322 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind); 5323 locations->SetInAt(0, Location::RequiresRegister()); 5324 locations->SetInAt(1, Location::RequiresRegister()); 5325 // The "out" register is used as a temporary, so it overlaps with the inputs. 5326 // Note that TypeCheckSlowPathARM uses this register too. 5327 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); 5328 // When read barriers are enabled, we need a temporary register for 5329 // some cases. 5330 if (TypeCheckNeedsATemporary(type_check_kind)) { 5331 locations->AddTemp(Location::RequiresRegister()); 5332 } 5333} 5334 5335void InstructionCodeGeneratorARM::VisitInstanceOf(HInstanceOf* instruction) { 5336 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind(); 5337 LocationSummary* locations = instruction->GetLocations(); 5338 Location obj_loc = locations->InAt(0); 5339 Register obj = obj_loc.AsRegister<Register>(); 5340 Register cls = locations->InAt(1).AsRegister<Register>(); 5341 Location out_loc = locations->Out(); 5342 Register out = out_loc.AsRegister<Register>(); 5343 Location maybe_temp_loc = TypeCheckNeedsATemporary(type_check_kind) ? 5344 locations->GetTemp(0) : 5345 Location::NoLocation(); 5346 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value(); 5347 uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value(); 5348 uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value(); 5349 uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value(); 5350 Label done, zero; 5351 SlowPathCode* slow_path = nullptr; 5352 5353 // Return 0 if `obj` is null. 5354 // avoid null check if we know obj is not null. 5355 if (instruction->MustDoNullCheck()) { 5356 __ CompareAndBranchIfZero(obj, &zero); 5357 } 5358 5359 // /* HeapReference<Class> */ out = obj->klass_ 5360 GenerateReferenceLoadTwoRegisters(instruction, out_loc, obj_loc, class_offset, maybe_temp_loc); 5361 5362 switch (type_check_kind) { 5363 case TypeCheckKind::kExactCheck: { 5364 __ cmp(out, ShifterOperand(cls)); 5365 // Classes must be equal for the instanceof to succeed. 5366 __ b(&zero, NE); 5367 __ LoadImmediate(out, 1); 5368 __ b(&done); 5369 break; 5370 } 5371 5372 case TypeCheckKind::kAbstractClassCheck: { 5373 // If the class is abstract, we eagerly fetch the super class of the 5374 // object to avoid doing a comparison we know will fail. 5375 Label loop; 5376 __ Bind(&loop); 5377 // /* HeapReference<Class> */ out = out->super_class_ 5378 GenerateReferenceLoadOneRegister(instruction, out_loc, super_offset, maybe_temp_loc); 5379 // If `out` is null, we use it for the result, and jump to `done`. 5380 __ CompareAndBranchIfZero(out, &done); 5381 __ cmp(out, ShifterOperand(cls)); 5382 __ b(&loop, NE); 5383 __ LoadImmediate(out, 1); 5384 if (zero.IsLinked()) { 5385 __ b(&done); 5386 } 5387 break; 5388 } 5389 5390 case TypeCheckKind::kClassHierarchyCheck: { 5391 // Walk over the class hierarchy to find a match. 5392 Label loop, success; 5393 __ Bind(&loop); 5394 __ cmp(out, ShifterOperand(cls)); 5395 __ b(&success, EQ); 5396 // /* HeapReference<Class> */ out = out->super_class_ 5397 GenerateReferenceLoadOneRegister(instruction, out_loc, super_offset, maybe_temp_loc); 5398 __ CompareAndBranchIfNonZero(out, &loop); 5399 // If `out` is null, we use it for the result, and jump to `done`. 5400 __ b(&done); 5401 __ Bind(&success); 5402 __ LoadImmediate(out, 1); 5403 if (zero.IsLinked()) { 5404 __ b(&done); 5405 } 5406 break; 5407 } 5408 5409 case TypeCheckKind::kArrayObjectCheck: { 5410 // Do an exact check. 5411 Label exact_check; 5412 __ cmp(out, ShifterOperand(cls)); 5413 __ b(&exact_check, EQ); 5414 // Otherwise, we need to check that the object's class is a non-primitive array. 5415 // /* HeapReference<Class> */ out = out->component_type_ 5416 GenerateReferenceLoadOneRegister(instruction, out_loc, component_offset, maybe_temp_loc); 5417 // If `out` is null, we use it for the result, and jump to `done`. 5418 __ CompareAndBranchIfZero(out, &done); 5419 __ LoadFromOffset(kLoadUnsignedHalfword, out, out, primitive_offset); 5420 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot"); 5421 __ CompareAndBranchIfNonZero(out, &zero); 5422 __ Bind(&exact_check); 5423 __ LoadImmediate(out, 1); 5424 __ b(&done); 5425 break; 5426 } 5427 5428 case TypeCheckKind::kArrayCheck: { 5429 __ cmp(out, ShifterOperand(cls)); 5430 DCHECK(locations->OnlyCallsOnSlowPath()); 5431 slow_path = new (GetGraph()->GetArena()) TypeCheckSlowPathARM(instruction, 5432 /* is_fatal */ false); 5433 codegen_->AddSlowPath(slow_path); 5434 __ b(slow_path->GetEntryLabel(), NE); 5435 __ LoadImmediate(out, 1); 5436 if (zero.IsLinked()) { 5437 __ b(&done); 5438 } 5439 break; 5440 } 5441 5442 case TypeCheckKind::kUnresolvedCheck: 5443 case TypeCheckKind::kInterfaceCheck: { 5444 // Note that we indeed only call on slow path, but we always go 5445 // into the slow path for the unresolved and interface check 5446 // cases. 5447 // 5448 // We cannot directly call the InstanceofNonTrivial runtime 5449 // entry point without resorting to a type checking slow path 5450 // here (i.e. by calling InvokeRuntime directly), as it would 5451 // require to assign fixed registers for the inputs of this 5452 // HInstanceOf instruction (following the runtime calling 5453 // convention), which might be cluttered by the potential first 5454 // read barrier emission at the beginning of this method. 5455 // 5456 // TODO: Introduce a new runtime entry point taking the object 5457 // to test (instead of its class) as argument, and let it deal 5458 // with the read barrier issues. This will let us refactor this 5459 // case of the `switch` code as it was previously (with a direct 5460 // call to the runtime not using a type checking slow path). 5461 // This should also be beneficial for the other cases above. 5462 DCHECK(locations->OnlyCallsOnSlowPath()); 5463 slow_path = new (GetGraph()->GetArena()) TypeCheckSlowPathARM(instruction, 5464 /* is_fatal */ false); 5465 codegen_->AddSlowPath(slow_path); 5466 __ b(slow_path->GetEntryLabel()); 5467 if (zero.IsLinked()) { 5468 __ b(&done); 5469 } 5470 break; 5471 } 5472 } 5473 5474 if (zero.IsLinked()) { 5475 __ Bind(&zero); 5476 __ LoadImmediate(out, 0); 5477 } 5478 5479 if (done.IsLinked()) { 5480 __ Bind(&done); 5481 } 5482 5483 if (slow_path != nullptr) { 5484 __ Bind(slow_path->GetExitLabel()); 5485 } 5486} 5487 5488void LocationsBuilderARM::VisitCheckCast(HCheckCast* instruction) { 5489 LocationSummary::CallKind call_kind = LocationSummary::kNoCall; 5490 bool throws_into_catch = instruction->CanThrowIntoCatchBlock(); 5491 5492 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind(); 5493 switch (type_check_kind) { 5494 case TypeCheckKind::kExactCheck: 5495 case TypeCheckKind::kAbstractClassCheck: 5496 case TypeCheckKind::kClassHierarchyCheck: 5497 case TypeCheckKind::kArrayObjectCheck: 5498 call_kind = (throws_into_catch || kEmitCompilerReadBarrier) ? 5499 LocationSummary::kCallOnSlowPath : 5500 LocationSummary::kNoCall; // In fact, call on a fatal (non-returning) slow path. 5501 break; 5502 case TypeCheckKind::kArrayCheck: 5503 case TypeCheckKind::kUnresolvedCheck: 5504 case TypeCheckKind::kInterfaceCheck: 5505 call_kind = LocationSummary::kCallOnSlowPath; 5506 break; 5507 } 5508 5509 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind); 5510 locations->SetInAt(0, Location::RequiresRegister()); 5511 locations->SetInAt(1, Location::RequiresRegister()); 5512 // Note that TypeCheckSlowPathARM uses this "temp" register too. 5513 locations->AddTemp(Location::RequiresRegister()); 5514 // When read barriers are enabled, we need an additional temporary 5515 // register for some cases. 5516 if (TypeCheckNeedsATemporary(type_check_kind)) { 5517 locations->AddTemp(Location::RequiresRegister()); 5518 } 5519} 5520 5521void InstructionCodeGeneratorARM::VisitCheckCast(HCheckCast* instruction) { 5522 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind(); 5523 LocationSummary* locations = instruction->GetLocations(); 5524 Location obj_loc = locations->InAt(0); 5525 Register obj = obj_loc.AsRegister<Register>(); 5526 Register cls = locations->InAt(1).AsRegister<Register>(); 5527 Location temp_loc = locations->GetTemp(0); 5528 Register temp = temp_loc.AsRegister<Register>(); 5529 Location maybe_temp2_loc = TypeCheckNeedsATemporary(type_check_kind) ? 5530 locations->GetTemp(1) : 5531 Location::NoLocation(); 5532 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value(); 5533 uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value(); 5534 uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value(); 5535 uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value(); 5536 5537 bool is_type_check_slow_path_fatal = 5538 (type_check_kind == TypeCheckKind::kExactCheck || 5539 type_check_kind == TypeCheckKind::kAbstractClassCheck || 5540 type_check_kind == TypeCheckKind::kClassHierarchyCheck || 5541 type_check_kind == TypeCheckKind::kArrayObjectCheck) && 5542 !instruction->CanThrowIntoCatchBlock(); 5543 SlowPathCode* type_check_slow_path = 5544 new (GetGraph()->GetArena()) TypeCheckSlowPathARM(instruction, 5545 is_type_check_slow_path_fatal); 5546 codegen_->AddSlowPath(type_check_slow_path); 5547 5548 Label done; 5549 // Avoid null check if we know obj is not null. 5550 if (instruction->MustDoNullCheck()) { 5551 __ CompareAndBranchIfZero(obj, &done); 5552 } 5553 5554 // /* HeapReference<Class> */ temp = obj->klass_ 5555 GenerateReferenceLoadTwoRegisters(instruction, temp_loc, obj_loc, class_offset, maybe_temp2_loc); 5556 5557 switch (type_check_kind) { 5558 case TypeCheckKind::kExactCheck: 5559 case TypeCheckKind::kArrayCheck: { 5560 __ cmp(temp, ShifterOperand(cls)); 5561 // Jump to slow path for throwing the exception or doing a 5562 // more involved array check. 5563 __ b(type_check_slow_path->GetEntryLabel(), NE); 5564 break; 5565 } 5566 5567 case TypeCheckKind::kAbstractClassCheck: { 5568 // If the class is abstract, we eagerly fetch the super class of the 5569 // object to avoid doing a comparison we know will fail. 5570 Label loop, compare_classes; 5571 __ Bind(&loop); 5572 // /* HeapReference<Class> */ temp = temp->super_class_ 5573 GenerateReferenceLoadOneRegister(instruction, temp_loc, super_offset, maybe_temp2_loc); 5574 5575 // If the class reference currently in `temp` is not null, jump 5576 // to the `compare_classes` label to compare it with the checked 5577 // class. 5578 __ CompareAndBranchIfNonZero(temp, &compare_classes); 5579 // Otherwise, jump to the slow path to throw the exception. 5580 // 5581 // But before, move back the object's class into `temp` before 5582 // going into the slow path, as it has been overwritten in the 5583 // meantime. 5584 // /* HeapReference<Class> */ temp = obj->klass_ 5585 GenerateReferenceLoadTwoRegisters( 5586 instruction, temp_loc, obj_loc, class_offset, maybe_temp2_loc); 5587 __ b(type_check_slow_path->GetEntryLabel()); 5588 5589 __ Bind(&compare_classes); 5590 __ cmp(temp, ShifterOperand(cls)); 5591 __ b(&loop, NE); 5592 break; 5593 } 5594 5595 case TypeCheckKind::kClassHierarchyCheck: { 5596 // Walk over the class hierarchy to find a match. 5597 Label loop; 5598 __ Bind(&loop); 5599 __ cmp(temp, ShifterOperand(cls)); 5600 __ b(&done, EQ); 5601 5602 // /* HeapReference<Class> */ temp = temp->super_class_ 5603 GenerateReferenceLoadOneRegister(instruction, temp_loc, super_offset, maybe_temp2_loc); 5604 5605 // If the class reference currently in `temp` is not null, jump 5606 // back at the beginning of the loop. 5607 __ CompareAndBranchIfNonZero(temp, &loop); 5608 // Otherwise, jump to the slow path to throw the exception. 5609 // 5610 // But before, move back the object's class into `temp` before 5611 // going into the slow path, as it has been overwritten in the 5612 // meantime. 5613 // /* HeapReference<Class> */ temp = obj->klass_ 5614 GenerateReferenceLoadTwoRegisters( 5615 instruction, temp_loc, obj_loc, class_offset, maybe_temp2_loc); 5616 __ b(type_check_slow_path->GetEntryLabel()); 5617 break; 5618 } 5619 5620 case TypeCheckKind::kArrayObjectCheck: { 5621 // Do an exact check. 5622 Label check_non_primitive_component_type; 5623 __ cmp(temp, ShifterOperand(cls)); 5624 __ b(&done, EQ); 5625 5626 // Otherwise, we need to check that the object's class is a non-primitive array. 5627 // /* HeapReference<Class> */ temp = temp->component_type_ 5628 GenerateReferenceLoadOneRegister(instruction, temp_loc, component_offset, maybe_temp2_loc); 5629 5630 // If the component type is not null (i.e. the object is indeed 5631 // an array), jump to label `check_non_primitive_component_type` 5632 // to further check that this component type is not a primitive 5633 // type. 5634 __ CompareAndBranchIfNonZero(temp, &check_non_primitive_component_type); 5635 // Otherwise, jump to the slow path to throw the exception. 5636 // 5637 // But before, move back the object's class into `temp` before 5638 // going into the slow path, as it has been overwritten in the 5639 // meantime. 5640 // /* HeapReference<Class> */ temp = obj->klass_ 5641 GenerateReferenceLoadTwoRegisters( 5642 instruction, temp_loc, obj_loc, class_offset, maybe_temp2_loc); 5643 __ b(type_check_slow_path->GetEntryLabel()); 5644 5645 __ Bind(&check_non_primitive_component_type); 5646 __ LoadFromOffset(kLoadUnsignedHalfword, temp, temp, primitive_offset); 5647 static_assert(Primitive::kPrimNot == 0, "Expected 0 for art::Primitive::kPrimNot"); 5648 __ CompareAndBranchIfZero(temp, &done); 5649 // Same comment as above regarding `temp` and the slow path. 5650 // /* HeapReference<Class> */ temp = obj->klass_ 5651 GenerateReferenceLoadTwoRegisters( 5652 instruction, temp_loc, obj_loc, class_offset, maybe_temp2_loc); 5653 __ b(type_check_slow_path->GetEntryLabel()); 5654 break; 5655 } 5656 5657 case TypeCheckKind::kUnresolvedCheck: 5658 case TypeCheckKind::kInterfaceCheck: 5659 // We always go into the type check slow path for the unresolved 5660 // and interface check cases. 5661 // 5662 // We cannot directly call the CheckCast runtime entry point 5663 // without resorting to a type checking slow path here (i.e. by 5664 // calling InvokeRuntime directly), as it would require to 5665 // assign fixed registers for the inputs of this HInstanceOf 5666 // instruction (following the runtime calling convention), which 5667 // might be cluttered by the potential first read barrier 5668 // emission at the beginning of this method. 5669 // 5670 // TODO: Introduce a new runtime entry point taking the object 5671 // to test (instead of its class) as argument, and let it deal 5672 // with the read barrier issues. This will let us refactor this 5673 // case of the `switch` code as it was previously (with a direct 5674 // call to the runtime not using a type checking slow path). 5675 // This should also be beneficial for the other cases above. 5676 __ b(type_check_slow_path->GetEntryLabel()); 5677 break; 5678 } 5679 __ Bind(&done); 5680 5681 __ Bind(type_check_slow_path->GetExitLabel()); 5682} 5683 5684void LocationsBuilderARM::VisitMonitorOperation(HMonitorOperation* instruction) { 5685 LocationSummary* locations = 5686 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall); 5687 InvokeRuntimeCallingConvention calling_convention; 5688 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); 5689} 5690 5691void InstructionCodeGeneratorARM::VisitMonitorOperation(HMonitorOperation* instruction) { 5692 codegen_->InvokeRuntime(instruction->IsEnter() 5693 ? QUICK_ENTRY_POINT(pLockObject) : QUICK_ENTRY_POINT(pUnlockObject), 5694 instruction, 5695 instruction->GetDexPc(), 5696 nullptr); 5697 if (instruction->IsEnter()) { 5698 CheckEntrypointTypes<kQuickLockObject, void, mirror::Object*>(); 5699 } else { 5700 CheckEntrypointTypes<kQuickUnlockObject, void, mirror::Object*>(); 5701 } 5702} 5703 5704void LocationsBuilderARM::VisitAnd(HAnd* instruction) { HandleBitwiseOperation(instruction, AND); } 5705void LocationsBuilderARM::VisitOr(HOr* instruction) { HandleBitwiseOperation(instruction, ORR); } 5706void LocationsBuilderARM::VisitXor(HXor* instruction) { HandleBitwiseOperation(instruction, EOR); } 5707 5708void LocationsBuilderARM::HandleBitwiseOperation(HBinaryOperation* instruction, Opcode opcode) { 5709 LocationSummary* locations = 5710 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); 5711 DCHECK(instruction->GetResultType() == Primitive::kPrimInt 5712 || instruction->GetResultType() == Primitive::kPrimLong); 5713 // Note: GVN reorders commutative operations to have the constant on the right hand side. 5714 locations->SetInAt(0, Location::RequiresRegister()); 5715 locations->SetInAt(1, ArmEncodableConstantOrRegister(instruction->InputAt(1), opcode)); 5716 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 5717} 5718 5719void InstructionCodeGeneratorARM::VisitAnd(HAnd* instruction) { 5720 HandleBitwiseOperation(instruction); 5721} 5722 5723void InstructionCodeGeneratorARM::VisitOr(HOr* instruction) { 5724 HandleBitwiseOperation(instruction); 5725} 5726 5727void InstructionCodeGeneratorARM::VisitXor(HXor* instruction) { 5728 HandleBitwiseOperation(instruction); 5729} 5730 5731 5732void LocationsBuilderARM::VisitBitwiseNegatedRight(HBitwiseNegatedRight* instruction) { 5733 LocationSummary* locations = 5734 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); 5735 DCHECK(instruction->GetResultType() == Primitive::kPrimInt 5736 || instruction->GetResultType() == Primitive::kPrimLong); 5737 5738 locations->SetInAt(0, Location::RequiresRegister()); 5739 locations->SetInAt(1, Location::RequiresRegister()); 5740 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 5741} 5742 5743void InstructionCodeGeneratorARM::VisitBitwiseNegatedRight(HBitwiseNegatedRight* instruction) { 5744 LocationSummary* locations = instruction->GetLocations(); 5745 Location first = locations->InAt(0); 5746 Location second = locations->InAt(1); 5747 Location out = locations->Out(); 5748 5749 if (instruction->GetResultType() == Primitive::kPrimInt) { 5750 Register first_reg = first.AsRegister<Register>(); 5751 ShifterOperand second_reg(second.AsRegister<Register>()); 5752 Register out_reg = out.AsRegister<Register>(); 5753 5754 switch (instruction->GetOpKind()) { 5755 case HInstruction::kAnd: 5756 __ bic(out_reg, first_reg, second_reg); 5757 break; 5758 case HInstruction::kOr: 5759 __ orn(out_reg, first_reg, second_reg); 5760 break; 5761 // There is no EON on arm. 5762 case HInstruction::kXor: 5763 default: 5764 LOG(FATAL) << "Unexpected instruction " << instruction->DebugName(); 5765 UNREACHABLE(); 5766 } 5767 return; 5768 5769 } else { 5770 DCHECK_EQ(instruction->GetResultType(), Primitive::kPrimLong); 5771 Register first_low = first.AsRegisterPairLow<Register>(); 5772 Register first_high = first.AsRegisterPairHigh<Register>(); 5773 ShifterOperand second_low(second.AsRegisterPairLow<Register>()); 5774 ShifterOperand second_high(second.AsRegisterPairHigh<Register>()); 5775 Register out_low = out.AsRegisterPairLow<Register>(); 5776 Register out_high = out.AsRegisterPairHigh<Register>(); 5777 5778 switch (instruction->GetOpKind()) { 5779 case HInstruction::kAnd: 5780 __ bic(out_low, first_low, second_low); 5781 __ bic(out_high, first_high, second_high); 5782 break; 5783 case HInstruction::kOr: 5784 __ orn(out_low, first_low, second_low); 5785 __ orn(out_high, first_high, second_high); 5786 break; 5787 // There is no EON on arm. 5788 case HInstruction::kXor: 5789 default: 5790 LOG(FATAL) << "Unexpected instruction " << instruction->DebugName(); 5791 UNREACHABLE(); 5792 } 5793 } 5794} 5795 5796void InstructionCodeGeneratorARM::GenerateAndConst(Register out, Register first, uint32_t value) { 5797 // Optimize special cases for individual halfs of `and-long` (`and` is simplified earlier). 5798 if (value == 0xffffffffu) { 5799 if (out != first) { 5800 __ mov(out, ShifterOperand(first)); 5801 } 5802 return; 5803 } 5804 if (value == 0u) { 5805 __ mov(out, ShifterOperand(0)); 5806 return; 5807 } 5808 ShifterOperand so; 5809 if (__ ShifterOperandCanHold(kNoRegister, kNoRegister, AND, value, &so)) { 5810 __ and_(out, first, so); 5811 } else { 5812 DCHECK(__ ShifterOperandCanHold(kNoRegister, kNoRegister, BIC, ~value, &so)); 5813 __ bic(out, first, ShifterOperand(~value)); 5814 } 5815} 5816 5817void InstructionCodeGeneratorARM::GenerateOrrConst(Register out, Register first, uint32_t value) { 5818 // Optimize special cases for individual halfs of `or-long` (`or` is simplified earlier). 5819 if (value == 0u) { 5820 if (out != first) { 5821 __ mov(out, ShifterOperand(first)); 5822 } 5823 return; 5824 } 5825 if (value == 0xffffffffu) { 5826 __ mvn(out, ShifterOperand(0)); 5827 return; 5828 } 5829 ShifterOperand so; 5830 if (__ ShifterOperandCanHold(kNoRegister, kNoRegister, ORR, value, &so)) { 5831 __ orr(out, first, so); 5832 } else { 5833 DCHECK(__ ShifterOperandCanHold(kNoRegister, kNoRegister, ORN, ~value, &so)); 5834 __ orn(out, first, ShifterOperand(~value)); 5835 } 5836} 5837 5838void InstructionCodeGeneratorARM::GenerateEorConst(Register out, Register first, uint32_t value) { 5839 // Optimize special case for individual halfs of `xor-long` (`xor` is simplified earlier). 5840 if (value == 0u) { 5841 if (out != first) { 5842 __ mov(out, ShifterOperand(first)); 5843 } 5844 return; 5845 } 5846 __ eor(out, first, ShifterOperand(value)); 5847} 5848 5849void InstructionCodeGeneratorARM::HandleBitwiseOperation(HBinaryOperation* instruction) { 5850 LocationSummary* locations = instruction->GetLocations(); 5851 Location first = locations->InAt(0); 5852 Location second = locations->InAt(1); 5853 Location out = locations->Out(); 5854 5855 if (second.IsConstant()) { 5856 uint64_t value = static_cast<uint64_t>(Int64FromConstant(second.GetConstant())); 5857 uint32_t value_low = Low32Bits(value); 5858 if (instruction->GetResultType() == Primitive::kPrimInt) { 5859 Register first_reg = first.AsRegister<Register>(); 5860 Register out_reg = out.AsRegister<Register>(); 5861 if (instruction->IsAnd()) { 5862 GenerateAndConst(out_reg, first_reg, value_low); 5863 } else if (instruction->IsOr()) { 5864 GenerateOrrConst(out_reg, first_reg, value_low); 5865 } else { 5866 DCHECK(instruction->IsXor()); 5867 GenerateEorConst(out_reg, first_reg, value_low); 5868 } 5869 } else { 5870 DCHECK_EQ(instruction->GetResultType(), Primitive::kPrimLong); 5871 uint32_t value_high = High32Bits(value); 5872 Register first_low = first.AsRegisterPairLow<Register>(); 5873 Register first_high = first.AsRegisterPairHigh<Register>(); 5874 Register out_low = out.AsRegisterPairLow<Register>(); 5875 Register out_high = out.AsRegisterPairHigh<Register>(); 5876 if (instruction->IsAnd()) { 5877 GenerateAndConst(out_low, first_low, value_low); 5878 GenerateAndConst(out_high, first_high, value_high); 5879 } else if (instruction->IsOr()) { 5880 GenerateOrrConst(out_low, first_low, value_low); 5881 GenerateOrrConst(out_high, first_high, value_high); 5882 } else { 5883 DCHECK(instruction->IsXor()); 5884 GenerateEorConst(out_low, first_low, value_low); 5885 GenerateEorConst(out_high, first_high, value_high); 5886 } 5887 } 5888 return; 5889 } 5890 5891 if (instruction->GetResultType() == Primitive::kPrimInt) { 5892 Register first_reg = first.AsRegister<Register>(); 5893 ShifterOperand second_reg(second.AsRegister<Register>()); 5894 Register out_reg = out.AsRegister<Register>(); 5895 if (instruction->IsAnd()) { 5896 __ and_(out_reg, first_reg, second_reg); 5897 } else if (instruction->IsOr()) { 5898 __ orr(out_reg, first_reg, second_reg); 5899 } else { 5900 DCHECK(instruction->IsXor()); 5901 __ eor(out_reg, first_reg, second_reg); 5902 } 5903 } else { 5904 DCHECK_EQ(instruction->GetResultType(), Primitive::kPrimLong); 5905 Register first_low = first.AsRegisterPairLow<Register>(); 5906 Register first_high = first.AsRegisterPairHigh<Register>(); 5907 ShifterOperand second_low(second.AsRegisterPairLow<Register>()); 5908 ShifterOperand second_high(second.AsRegisterPairHigh<Register>()); 5909 Register out_low = out.AsRegisterPairLow<Register>(); 5910 Register out_high = out.AsRegisterPairHigh<Register>(); 5911 if (instruction->IsAnd()) { 5912 __ and_(out_low, first_low, second_low); 5913 __ and_(out_high, first_high, second_high); 5914 } else if (instruction->IsOr()) { 5915 __ orr(out_low, first_low, second_low); 5916 __ orr(out_high, first_high, second_high); 5917 } else { 5918 DCHECK(instruction->IsXor()); 5919 __ eor(out_low, first_low, second_low); 5920 __ eor(out_high, first_high, second_high); 5921 } 5922 } 5923} 5924 5925void InstructionCodeGeneratorARM::GenerateReferenceLoadOneRegister(HInstruction* instruction, 5926 Location out, 5927 uint32_t offset, 5928 Location maybe_temp) { 5929 Register out_reg = out.AsRegister<Register>(); 5930 if (kEmitCompilerReadBarrier) { 5931 DCHECK(maybe_temp.IsRegister()) << maybe_temp; 5932 if (kUseBakerReadBarrier) { 5933 // Load with fast path based Baker's read barrier. 5934 // /* HeapReference<Object> */ out = *(out + offset) 5935 codegen_->GenerateFieldLoadWithBakerReadBarrier( 5936 instruction, out, out_reg, offset, maybe_temp, /* needs_null_check */ false); 5937 } else { 5938 // Load with slow path based read barrier. 5939 // Save the value of `out` into `maybe_temp` before overwriting it 5940 // in the following move operation, as we will need it for the 5941 // read barrier below. 5942 __ Mov(maybe_temp.AsRegister<Register>(), out_reg); 5943 // /* HeapReference<Object> */ out = *(out + offset) 5944 __ LoadFromOffset(kLoadWord, out_reg, out_reg, offset); 5945 codegen_->GenerateReadBarrierSlow(instruction, out, out, maybe_temp, offset); 5946 } 5947 } else { 5948 // Plain load with no read barrier. 5949 // /* HeapReference<Object> */ out = *(out + offset) 5950 __ LoadFromOffset(kLoadWord, out_reg, out_reg, offset); 5951 __ MaybeUnpoisonHeapReference(out_reg); 5952 } 5953} 5954 5955void InstructionCodeGeneratorARM::GenerateReferenceLoadTwoRegisters(HInstruction* instruction, 5956 Location out, 5957 Location obj, 5958 uint32_t offset, 5959 Location maybe_temp) { 5960 Register out_reg = out.AsRegister<Register>(); 5961 Register obj_reg = obj.AsRegister<Register>(); 5962 if (kEmitCompilerReadBarrier) { 5963 if (kUseBakerReadBarrier) { 5964 DCHECK(maybe_temp.IsRegister()) << maybe_temp; 5965 // Load with fast path based Baker's read barrier. 5966 // /* HeapReference<Object> */ out = *(obj + offset) 5967 codegen_->GenerateFieldLoadWithBakerReadBarrier( 5968 instruction, out, obj_reg, offset, maybe_temp, /* needs_null_check */ false); 5969 } else { 5970 // Load with slow path based read barrier. 5971 // /* HeapReference<Object> */ out = *(obj + offset) 5972 __ LoadFromOffset(kLoadWord, out_reg, obj_reg, offset); 5973 codegen_->GenerateReadBarrierSlow(instruction, out, out, obj, offset); 5974 } 5975 } else { 5976 // Plain load with no read barrier. 5977 // /* HeapReference<Object> */ out = *(obj + offset) 5978 __ LoadFromOffset(kLoadWord, out_reg, obj_reg, offset); 5979 __ MaybeUnpoisonHeapReference(out_reg); 5980 } 5981} 5982 5983void InstructionCodeGeneratorARM::GenerateGcRootFieldLoad(HInstruction* instruction, 5984 Location root, 5985 Register obj, 5986 uint32_t offset) { 5987 Register root_reg = root.AsRegister<Register>(); 5988 if (kEmitCompilerReadBarrier) { 5989 if (kUseBakerReadBarrier) { 5990 // Fast path implementation of art::ReadBarrier::BarrierForRoot when 5991 // Baker's read barrier are used: 5992 // 5993 // root = obj.field; 5994 // if (Thread::Current()->GetIsGcMarking()) { 5995 // root = ReadBarrier::Mark(root) 5996 // } 5997 5998 // /* GcRoot<mirror::Object> */ root = *(obj + offset) 5999 __ LoadFromOffset(kLoadWord, root_reg, obj, offset); 6000 static_assert( 6001 sizeof(mirror::CompressedReference<mirror::Object>) == sizeof(GcRoot<mirror::Object>), 6002 "art::mirror::CompressedReference<mirror::Object> and art::GcRoot<mirror::Object> " 6003 "have different sizes."); 6004 static_assert(sizeof(mirror::CompressedReference<mirror::Object>) == sizeof(int32_t), 6005 "art::mirror::CompressedReference<mirror::Object> and int32_t " 6006 "have different sizes."); 6007 6008 // Slow path used to mark the GC root `root`. 6009 SlowPathCode* slow_path = 6010 new (GetGraph()->GetArena()) ReadBarrierMarkSlowPathARM(instruction, root, root); 6011 codegen_->AddSlowPath(slow_path); 6012 6013 // IP = Thread::Current()->GetIsGcMarking() 6014 __ LoadFromOffset( 6015 kLoadWord, IP, TR, Thread::IsGcMarkingOffset<kArmWordSize>().Int32Value()); 6016 __ CompareAndBranchIfNonZero(IP, slow_path->GetEntryLabel()); 6017 __ Bind(slow_path->GetExitLabel()); 6018 } else { 6019 // GC root loaded through a slow path for read barriers other 6020 // than Baker's. 6021 // /* GcRoot<mirror::Object>* */ root = obj + offset 6022 __ AddConstant(root_reg, obj, offset); 6023 // /* mirror::Object* */ root = root->Read() 6024 codegen_->GenerateReadBarrierForRootSlow(instruction, root, root); 6025 } 6026 } else { 6027 // Plain GC root load with no read barrier. 6028 // /* GcRoot<mirror::Object> */ root = *(obj + offset) 6029 __ LoadFromOffset(kLoadWord, root_reg, obj, offset); 6030 // Note that GC roots are not affected by heap poisoning, thus we 6031 // do not have to unpoison `root_reg` here. 6032 } 6033} 6034 6035void CodeGeneratorARM::GenerateFieldLoadWithBakerReadBarrier(HInstruction* instruction, 6036 Location ref, 6037 Register obj, 6038 uint32_t offset, 6039 Location temp, 6040 bool needs_null_check) { 6041 DCHECK(kEmitCompilerReadBarrier); 6042 DCHECK(kUseBakerReadBarrier); 6043 6044 // /* HeapReference<Object> */ ref = *(obj + offset) 6045 Location no_index = Location::NoLocation(); 6046 GenerateReferenceLoadWithBakerReadBarrier( 6047 instruction, ref, obj, offset, no_index, temp, needs_null_check); 6048} 6049 6050void CodeGeneratorARM::GenerateArrayLoadWithBakerReadBarrier(HInstruction* instruction, 6051 Location ref, 6052 Register obj, 6053 uint32_t data_offset, 6054 Location index, 6055 Location temp, 6056 bool needs_null_check) { 6057 DCHECK(kEmitCompilerReadBarrier); 6058 DCHECK(kUseBakerReadBarrier); 6059 6060 // /* HeapReference<Object> */ ref = 6061 // *(obj + data_offset + index * sizeof(HeapReference<Object>)) 6062 GenerateReferenceLoadWithBakerReadBarrier( 6063 instruction, ref, obj, data_offset, index, temp, needs_null_check); 6064} 6065 6066void CodeGeneratorARM::GenerateReferenceLoadWithBakerReadBarrier(HInstruction* instruction, 6067 Location ref, 6068 Register obj, 6069 uint32_t offset, 6070 Location index, 6071 Location temp, 6072 bool needs_null_check) { 6073 DCHECK(kEmitCompilerReadBarrier); 6074 DCHECK(kUseBakerReadBarrier); 6075 6076 // In slow path based read barriers, the read barrier call is 6077 // inserted after the original load. However, in fast path based 6078 // Baker's read barriers, we need to perform the load of 6079 // mirror::Object::monitor_ *before* the original reference load. 6080 // This load-load ordering is required by the read barrier. 6081 // The fast path/slow path (for Baker's algorithm) should look like: 6082 // 6083 // uint32_t rb_state = Lockword(obj->monitor_).ReadBarrierState(); 6084 // lfence; // Load fence or artificial data dependency to prevent load-load reordering 6085 // HeapReference<Object> ref = *src; // Original reference load. 6086 // bool is_gray = (rb_state == ReadBarrier::gray_ptr_); 6087 // if (is_gray) { 6088 // ref = ReadBarrier::Mark(ref); // Performed by runtime entrypoint slow path. 6089 // } 6090 // 6091 // Note: the original implementation in ReadBarrier::Barrier is 6092 // slightly more complex as it performs additional checks that we do 6093 // not do here for performance reasons. 6094 6095 Register ref_reg = ref.AsRegister<Register>(); 6096 Register temp_reg = temp.AsRegister<Register>(); 6097 uint32_t monitor_offset = mirror::Object::MonitorOffset().Int32Value(); 6098 6099 // /* int32_t */ monitor = obj->monitor_ 6100 __ LoadFromOffset(kLoadWord, temp_reg, obj, monitor_offset); 6101 if (needs_null_check) { 6102 MaybeRecordImplicitNullCheck(instruction); 6103 } 6104 // /* LockWord */ lock_word = LockWord(monitor) 6105 static_assert(sizeof(LockWord) == sizeof(int32_t), 6106 "art::LockWord and int32_t have different sizes."); 6107 // /* uint32_t */ rb_state = lock_word.ReadBarrierState() 6108 __ Lsr(temp_reg, temp_reg, LockWord::kReadBarrierStateShift); 6109 __ and_(temp_reg, temp_reg, ShifterOperand(LockWord::kReadBarrierStateMask)); 6110 static_assert( 6111 LockWord::kReadBarrierStateMask == ReadBarrier::rb_ptr_mask_, 6112 "art::LockWord::kReadBarrierStateMask is not equal to art::ReadBarrier::rb_ptr_mask_."); 6113 6114 // Introduce a dependency on the high bits of rb_state, which shall 6115 // be all zeroes, to prevent load-load reordering, and without using 6116 // a memory barrier (which would be more expensive). 6117 // IP = rb_state & ~LockWord::kReadBarrierStateMask = 0 6118 __ bic(IP, temp_reg, ShifterOperand(LockWord::kReadBarrierStateMask)); 6119 // obj is unchanged by this operation, but its value now depends on 6120 // IP, which depends on temp_reg. 6121 __ add(obj, obj, ShifterOperand(IP)); 6122 6123 // The actual reference load. 6124 if (index.IsValid()) { 6125 static_assert( 6126 sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t), 6127 "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes."); 6128 // /* HeapReference<Object> */ ref = 6129 // *(obj + offset + index * sizeof(HeapReference<Object>)) 6130 if (index.IsConstant()) { 6131 size_t computed_offset = 6132 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + offset; 6133 __ LoadFromOffset(kLoadWord, ref_reg, obj, computed_offset); 6134 } else { 6135 __ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4)); 6136 __ LoadFromOffset(kLoadWord, ref_reg, IP, offset); 6137 } 6138 } else { 6139 // /* HeapReference<Object> */ ref = *(obj + offset) 6140 __ LoadFromOffset(kLoadWord, ref_reg, obj, offset); 6141 } 6142 6143 // Object* ref = ref_addr->AsMirrorPtr() 6144 __ MaybeUnpoisonHeapReference(ref_reg); 6145 6146 // Slow path used to mark the object `ref` when it is gray. 6147 SlowPathCode* slow_path = 6148 new (GetGraph()->GetArena()) ReadBarrierMarkSlowPathARM(instruction, ref, ref); 6149 AddSlowPath(slow_path); 6150 6151 // if (rb_state == ReadBarrier::gray_ptr_) 6152 // ref = ReadBarrier::Mark(ref); 6153 __ cmp(temp_reg, ShifterOperand(ReadBarrier::gray_ptr_)); 6154 __ b(slow_path->GetEntryLabel(), EQ); 6155 __ Bind(slow_path->GetExitLabel()); 6156} 6157 6158void CodeGeneratorARM::GenerateReadBarrierSlow(HInstruction* instruction, 6159 Location out, 6160 Location ref, 6161 Location obj, 6162 uint32_t offset, 6163 Location index) { 6164 DCHECK(kEmitCompilerReadBarrier); 6165 6166 // Insert a slow path based read barrier *after* the reference load. 6167 // 6168 // If heap poisoning is enabled, the unpoisoning of the loaded 6169 // reference will be carried out by the runtime within the slow 6170 // path. 6171 // 6172 // Note that `ref` currently does not get unpoisoned (when heap 6173 // poisoning is enabled), which is alright as the `ref` argument is 6174 // not used by the artReadBarrierSlow entry point. 6175 // 6176 // TODO: Unpoison `ref` when it is used by artReadBarrierSlow. 6177 SlowPathCode* slow_path = new (GetGraph()->GetArena()) 6178 ReadBarrierForHeapReferenceSlowPathARM(instruction, out, ref, obj, offset, index); 6179 AddSlowPath(slow_path); 6180 6181 __ b(slow_path->GetEntryLabel()); 6182 __ Bind(slow_path->GetExitLabel()); 6183} 6184 6185void CodeGeneratorARM::MaybeGenerateReadBarrierSlow(HInstruction* instruction, 6186 Location out, 6187 Location ref, 6188 Location obj, 6189 uint32_t offset, 6190 Location index) { 6191 if (kEmitCompilerReadBarrier) { 6192 // Baker's read barriers shall be handled by the fast path 6193 // (CodeGeneratorARM::GenerateReferenceLoadWithBakerReadBarrier). 6194 DCHECK(!kUseBakerReadBarrier); 6195 // If heap poisoning is enabled, unpoisoning will be taken care of 6196 // by the runtime within the slow path. 6197 GenerateReadBarrierSlow(instruction, out, ref, obj, offset, index); 6198 } else if (kPoisonHeapReferences) { 6199 __ UnpoisonHeapReference(out.AsRegister<Register>()); 6200 } 6201} 6202 6203void CodeGeneratorARM::GenerateReadBarrierForRootSlow(HInstruction* instruction, 6204 Location out, 6205 Location root) { 6206 DCHECK(kEmitCompilerReadBarrier); 6207 6208 // Insert a slow path based read barrier *after* the GC root load. 6209 // 6210 // Note that GC roots are not affected by heap poisoning, so we do 6211 // not need to do anything special for this here. 6212 SlowPathCode* slow_path = 6213 new (GetGraph()->GetArena()) ReadBarrierForRootSlowPathARM(instruction, out, root); 6214 AddSlowPath(slow_path); 6215 6216 __ b(slow_path->GetEntryLabel()); 6217 __ Bind(slow_path->GetExitLabel()); 6218} 6219 6220HInvokeStaticOrDirect::DispatchInfo CodeGeneratorARM::GetSupportedInvokeStaticOrDirectDispatch( 6221 const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info, 6222 MethodReference target_method) { 6223 HInvokeStaticOrDirect::DispatchInfo dispatch_info = desired_dispatch_info; 6224 // We disable pc-relative load when there is an irreducible loop, as the optimization 6225 // is incompatible with it. 6226 if (GetGraph()->HasIrreducibleLoops() && 6227 (dispatch_info.method_load_kind == 6228 HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative)) { 6229 dispatch_info.method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod; 6230 } 6231 6232 if (dispatch_info.code_ptr_location == HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative) { 6233 const DexFile& outer_dex_file = GetGraph()->GetDexFile(); 6234 if (&outer_dex_file != target_method.dex_file) { 6235 // Calls across dex files are more likely to exceed the available BL range, 6236 // so use absolute patch with fixup if available and kCallArtMethod otherwise. 6237 HInvokeStaticOrDirect::CodePtrLocation code_ptr_location = 6238 (desired_dispatch_info.method_load_kind == 6239 HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup) 6240 ? HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup 6241 : HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod; 6242 return HInvokeStaticOrDirect::DispatchInfo { 6243 dispatch_info.method_load_kind, 6244 code_ptr_location, 6245 dispatch_info.method_load_data, 6246 0u 6247 }; 6248 } 6249 } 6250 return dispatch_info; 6251} 6252 6253Register CodeGeneratorARM::GetInvokeStaticOrDirectExtraParameter(HInvokeStaticOrDirect* invoke, 6254 Register temp) { 6255 DCHECK_EQ(invoke->InputCount(), invoke->GetNumberOfArguments() + 1u); 6256 Location location = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex()); 6257 if (!invoke->GetLocations()->Intrinsified()) { 6258 return location.AsRegister<Register>(); 6259 } 6260 // For intrinsics we allow any location, so it may be on the stack. 6261 if (!location.IsRegister()) { 6262 __ LoadFromOffset(kLoadWord, temp, SP, location.GetStackIndex()); 6263 return temp; 6264 } 6265 // For register locations, check if the register was saved. If so, get it from the stack. 6266 // Note: There is a chance that the register was saved but not overwritten, so we could 6267 // save one load. However, since this is just an intrinsic slow path we prefer this 6268 // simple and more robust approach rather that trying to determine if that's the case. 6269 SlowPathCode* slow_path = GetCurrentSlowPath(); 6270 DCHECK(slow_path != nullptr); // For intrinsified invokes the call is emitted on the slow path. 6271 if (slow_path->IsCoreRegisterSaved(location.AsRegister<Register>())) { 6272 int stack_offset = slow_path->GetStackOffsetOfCoreRegister(location.AsRegister<Register>()); 6273 __ LoadFromOffset(kLoadWord, temp, SP, stack_offset); 6274 return temp; 6275 } 6276 return location.AsRegister<Register>(); 6277} 6278 6279void CodeGeneratorARM::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp) { 6280 // For better instruction scheduling we load the direct code pointer before the method pointer. 6281 switch (invoke->GetCodePtrLocation()) { 6282 case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup: 6283 // LR = code address from literal pool with link-time patch. 6284 __ LoadLiteral(LR, DeduplicateMethodCodeLiteral(invoke->GetTargetMethod())); 6285 break; 6286 case HInvokeStaticOrDirect::CodePtrLocation::kCallDirect: 6287 // LR = invoke->GetDirectCodePtr(); 6288 __ LoadImmediate(LR, invoke->GetDirectCodePtr()); 6289 break; 6290 default: 6291 break; 6292 } 6293 6294 Location callee_method = temp; // For all kinds except kRecursive, callee will be in temp. 6295 switch (invoke->GetMethodLoadKind()) { 6296 case HInvokeStaticOrDirect::MethodLoadKind::kStringInit: 6297 // temp = thread->string_init_entrypoint 6298 __ LoadFromOffset(kLoadWord, temp.AsRegister<Register>(), TR, invoke->GetStringInitOffset()); 6299 break; 6300 case HInvokeStaticOrDirect::MethodLoadKind::kRecursive: 6301 callee_method = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex()); 6302 break; 6303 case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress: 6304 __ LoadImmediate(temp.AsRegister<Register>(), invoke->GetMethodAddress()); 6305 break; 6306 case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup: 6307 __ LoadLiteral(temp.AsRegister<Register>(), 6308 DeduplicateMethodAddressLiteral(invoke->GetTargetMethod())); 6309 break; 6310 case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative: { 6311 HArmDexCacheArraysBase* base = 6312 invoke->InputAt(invoke->GetSpecialInputIndex())->AsArmDexCacheArraysBase(); 6313 Register base_reg = GetInvokeStaticOrDirectExtraParameter(invoke, 6314 temp.AsRegister<Register>()); 6315 int32_t offset = invoke->GetDexCacheArrayOffset() - base->GetElementOffset(); 6316 __ LoadFromOffset(kLoadWord, temp.AsRegister<Register>(), base_reg, offset); 6317 break; 6318 } 6319 case HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod: { 6320 Location current_method = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex()); 6321 Register method_reg; 6322 Register reg = temp.AsRegister<Register>(); 6323 if (current_method.IsRegister()) { 6324 method_reg = current_method.AsRegister<Register>(); 6325 } else { 6326 DCHECK(invoke->GetLocations()->Intrinsified()); 6327 DCHECK(!current_method.IsValid()); 6328 method_reg = reg; 6329 __ LoadFromOffset(kLoadWord, reg, SP, kCurrentMethodStackOffset); 6330 } 6331 // /* ArtMethod*[] */ temp = temp.ptr_sized_fields_->dex_cache_resolved_methods_; 6332 __ LoadFromOffset(kLoadWord, 6333 reg, 6334 method_reg, 6335 ArtMethod::DexCacheResolvedMethodsOffset(kArmPointerSize).Int32Value()); 6336 // temp = temp[index_in_cache] 6337 uint32_t index_in_cache = invoke->GetTargetMethod().dex_method_index; 6338 __ LoadFromOffset(kLoadWord, reg, reg, CodeGenerator::GetCachePointerOffset(index_in_cache)); 6339 break; 6340 } 6341 } 6342 6343 switch (invoke->GetCodePtrLocation()) { 6344 case HInvokeStaticOrDirect::CodePtrLocation::kCallSelf: 6345 __ bl(GetFrameEntryLabel()); 6346 break; 6347 case HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative: 6348 relative_call_patches_.emplace_back(invoke->GetTargetMethod()); 6349 __ BindTrackedLabel(&relative_call_patches_.back().label); 6350 // Arbitrarily branch to the BL itself, override at link time. 6351 __ bl(&relative_call_patches_.back().label); 6352 break; 6353 case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup: 6354 case HInvokeStaticOrDirect::CodePtrLocation::kCallDirect: 6355 // LR prepared above for better instruction scheduling. 6356 // LR() 6357 __ blx(LR); 6358 break; 6359 case HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod: 6360 // LR = callee_method->entry_point_from_quick_compiled_code_ 6361 __ LoadFromOffset( 6362 kLoadWord, LR, callee_method.AsRegister<Register>(), 6363 ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmWordSize).Int32Value()); 6364 // LR() 6365 __ blx(LR); 6366 break; 6367 } 6368 6369 DCHECK(!IsLeafMethod()); 6370} 6371 6372void CodeGeneratorARM::GenerateVirtualCall(HInvokeVirtual* invoke, Location temp_location) { 6373 Register temp = temp_location.AsRegister<Register>(); 6374 uint32_t method_offset = mirror::Class::EmbeddedVTableEntryOffset( 6375 invoke->GetVTableIndex(), kArmPointerSize).Uint32Value(); 6376 6377 // Use the calling convention instead of the location of the receiver, as 6378 // intrinsics may have put the receiver in a different register. In the intrinsics 6379 // slow path, the arguments have been moved to the right place, so here we are 6380 // guaranteed that the receiver is the first register of the calling convention. 6381 InvokeDexCallingConvention calling_convention; 6382 Register receiver = calling_convention.GetRegisterAt(0); 6383 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value(); 6384 // /* HeapReference<Class> */ temp = receiver->klass_ 6385 __ LoadFromOffset(kLoadWord, temp, receiver, class_offset); 6386 MaybeRecordImplicitNullCheck(invoke); 6387 // Instead of simply (possibly) unpoisoning `temp` here, we should 6388 // emit a read barrier for the previous class reference load. 6389 // However this is not required in practice, as this is an 6390 // intermediate/temporary reference and because the current 6391 // concurrent copying collector keeps the from-space memory 6392 // intact/accessible until the end of the marking phase (the 6393 // concurrent copying collector may not in the future). 6394 __ MaybeUnpoisonHeapReference(temp); 6395 // temp = temp->GetMethodAt(method_offset); 6396 uint32_t entry_point = ArtMethod::EntryPointFromQuickCompiledCodeOffset( 6397 kArmWordSize).Int32Value(); 6398 __ LoadFromOffset(kLoadWord, temp, temp, method_offset); 6399 // LR = temp->GetEntryPoint(); 6400 __ LoadFromOffset(kLoadWord, LR, temp, entry_point); 6401 // LR(); 6402 __ blx(LR); 6403} 6404 6405void CodeGeneratorARM::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) { 6406 DCHECK(linker_patches->empty()); 6407 size_t size = 6408 method_patches_.size() + 6409 call_patches_.size() + 6410 relative_call_patches_.size() + 6411 /* MOVW+MOVT for each base */ 2u * dex_cache_arrays_base_labels_.size(); 6412 linker_patches->reserve(size); 6413 for (const auto& entry : method_patches_) { 6414 const MethodReference& target_method = entry.first; 6415 Literal* literal = entry.second; 6416 DCHECK(literal->GetLabel()->IsBound()); 6417 uint32_t literal_offset = literal->GetLabel()->Position(); 6418 linker_patches->push_back(LinkerPatch::MethodPatch(literal_offset, 6419 target_method.dex_file, 6420 target_method.dex_method_index)); 6421 } 6422 for (const auto& entry : call_patches_) { 6423 const MethodReference& target_method = entry.first; 6424 Literal* literal = entry.second; 6425 DCHECK(literal->GetLabel()->IsBound()); 6426 uint32_t literal_offset = literal->GetLabel()->Position(); 6427 linker_patches->push_back(LinkerPatch::CodePatch(literal_offset, 6428 target_method.dex_file, 6429 target_method.dex_method_index)); 6430 } 6431 for (const MethodPatchInfo<Label>& info : relative_call_patches_) { 6432 uint32_t literal_offset = info.label.Position(); 6433 linker_patches->push_back(LinkerPatch::RelativeCodePatch(literal_offset, 6434 info.target_method.dex_file, 6435 info.target_method.dex_method_index)); 6436 } 6437 for (const auto& pair : dex_cache_arrays_base_labels_) { 6438 HArmDexCacheArraysBase* base = pair.first; 6439 const DexCacheArraysBaseLabels* labels = &pair.second; 6440 const DexFile& dex_file = base->GetDexFile(); 6441 size_t base_element_offset = base->GetElementOffset(); 6442 DCHECK(labels->add_pc_label.IsBound()); 6443 uint32_t add_pc_offset = dchecked_integral_cast<uint32_t>(labels->add_pc_label.Position()); 6444 // Add MOVW patch. 6445 DCHECK(labels->movw_label.IsBound()); 6446 uint32_t movw_offset = dchecked_integral_cast<uint32_t>(labels->movw_label.Position()); 6447 linker_patches->push_back(LinkerPatch::DexCacheArrayPatch(movw_offset, 6448 &dex_file, 6449 add_pc_offset, 6450 base_element_offset)); 6451 // Add MOVT patch. 6452 DCHECK(labels->movt_label.IsBound()); 6453 uint32_t movt_offset = dchecked_integral_cast<uint32_t>(labels->movt_label.Position()); 6454 linker_patches->push_back(LinkerPatch::DexCacheArrayPatch(movt_offset, 6455 &dex_file, 6456 add_pc_offset, 6457 base_element_offset)); 6458 } 6459} 6460 6461Literal* CodeGeneratorARM::DeduplicateMethodLiteral(MethodReference target_method, 6462 MethodToLiteralMap* map) { 6463 // Look up the literal for target_method. 6464 auto lb = map->lower_bound(target_method); 6465 if (lb != map->end() && !map->key_comp()(target_method, lb->first)) { 6466 return lb->second; 6467 } 6468 // We don't have a literal for this method yet, insert a new one. 6469 Literal* literal = __ NewLiteral<uint32_t>(0u); 6470 map->PutBefore(lb, target_method, literal); 6471 return literal; 6472} 6473 6474Literal* CodeGeneratorARM::DeduplicateMethodAddressLiteral(MethodReference target_method) { 6475 return DeduplicateMethodLiteral(target_method, &method_patches_); 6476} 6477 6478Literal* CodeGeneratorARM::DeduplicateMethodCodeLiteral(MethodReference target_method) { 6479 return DeduplicateMethodLiteral(target_method, &call_patches_); 6480} 6481 6482void LocationsBuilderARM::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) { 6483 LocationSummary* locations = 6484 new (GetGraph()->GetArena()) LocationSummary(instr, LocationSummary::kNoCall); 6485 locations->SetInAt(HMultiplyAccumulate::kInputAccumulatorIndex, 6486 Location::RequiresRegister()); 6487 locations->SetInAt(HMultiplyAccumulate::kInputMulLeftIndex, Location::RequiresRegister()); 6488 locations->SetInAt(HMultiplyAccumulate::kInputMulRightIndex, Location::RequiresRegister()); 6489 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 6490} 6491 6492void InstructionCodeGeneratorARM::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) { 6493 LocationSummary* locations = instr->GetLocations(); 6494 Register res = locations->Out().AsRegister<Register>(); 6495 Register accumulator = 6496 locations->InAt(HMultiplyAccumulate::kInputAccumulatorIndex).AsRegister<Register>(); 6497 Register mul_left = 6498 locations->InAt(HMultiplyAccumulate::kInputMulLeftIndex).AsRegister<Register>(); 6499 Register mul_right = 6500 locations->InAt(HMultiplyAccumulate::kInputMulRightIndex).AsRegister<Register>(); 6501 6502 if (instr->GetOpKind() == HInstruction::kAdd) { 6503 __ mla(res, mul_left, mul_right, accumulator); 6504 } else { 6505 __ mls(res, mul_left, mul_right, accumulator); 6506 } 6507} 6508 6509void LocationsBuilderARM::VisitBoundType(HBoundType* instruction ATTRIBUTE_UNUSED) { 6510 // Nothing to do, this should be removed during prepare for register allocator. 6511 LOG(FATAL) << "Unreachable"; 6512} 6513 6514void InstructionCodeGeneratorARM::VisitBoundType(HBoundType* instruction ATTRIBUTE_UNUSED) { 6515 // Nothing to do, this should be removed during prepare for register allocator. 6516 LOG(FATAL) << "Unreachable"; 6517} 6518 6519// Simple implementation of packed switch - generate cascaded compare/jumps. 6520void LocationsBuilderARM::VisitPackedSwitch(HPackedSwitch* switch_instr) { 6521 LocationSummary* locations = 6522 new (GetGraph()->GetArena()) LocationSummary(switch_instr, LocationSummary::kNoCall); 6523 locations->SetInAt(0, Location::RequiresRegister()); 6524 if (switch_instr->GetNumEntries() > kPackedSwitchCompareJumpThreshold && 6525 codegen_->GetAssembler()->IsThumb()) { 6526 locations->AddTemp(Location::RequiresRegister()); // We need a temp for the table base. 6527 if (switch_instr->GetStartValue() != 0) { 6528 locations->AddTemp(Location::RequiresRegister()); // We need a temp for the bias. 6529 } 6530 } 6531} 6532 6533void InstructionCodeGeneratorARM::VisitPackedSwitch(HPackedSwitch* switch_instr) { 6534 int32_t lower_bound = switch_instr->GetStartValue(); 6535 uint32_t num_entries = switch_instr->GetNumEntries(); 6536 LocationSummary* locations = switch_instr->GetLocations(); 6537 Register value_reg = locations->InAt(0).AsRegister<Register>(); 6538 HBasicBlock* default_block = switch_instr->GetDefaultBlock(); 6539 6540 if (num_entries <= kPackedSwitchCompareJumpThreshold || !codegen_->GetAssembler()->IsThumb()) { 6541 // Create a series of compare/jumps. 6542 Register temp_reg = IP; 6543 // Note: It is fine for the below AddConstantSetFlags() using IP register to temporarily store 6544 // the immediate, because IP is used as the destination register. For the other 6545 // AddConstantSetFlags() and GenerateCompareWithImmediate(), the immediate values are constant, 6546 // and they can be encoded in the instruction without making use of IP register. 6547 __ AddConstantSetFlags(temp_reg, value_reg, -lower_bound); 6548 6549 const ArenaVector<HBasicBlock*>& successors = switch_instr->GetBlock()->GetSuccessors(); 6550 // Jump to successors[0] if value == lower_bound. 6551 __ b(codegen_->GetLabelOf(successors[0]), EQ); 6552 int32_t last_index = 0; 6553 for (; num_entries - last_index > 2; last_index += 2) { 6554 __ AddConstantSetFlags(temp_reg, temp_reg, -2); 6555 // Jump to successors[last_index + 1] if value < case_value[last_index + 2]. 6556 __ b(codegen_->GetLabelOf(successors[last_index + 1]), LO); 6557 // Jump to successors[last_index + 2] if value == case_value[last_index + 2]. 6558 __ b(codegen_->GetLabelOf(successors[last_index + 2]), EQ); 6559 } 6560 if (num_entries - last_index == 2) { 6561 // The last missing case_value. 6562 __ CmpConstant(temp_reg, 1); 6563 __ b(codegen_->GetLabelOf(successors[last_index + 1]), EQ); 6564 } 6565 6566 // And the default for any other value. 6567 if (!codegen_->GoesToNextBlock(switch_instr->GetBlock(), default_block)) { 6568 __ b(codegen_->GetLabelOf(default_block)); 6569 } 6570 } else { 6571 // Create a table lookup. 6572 Register temp_reg = locations->GetTemp(0).AsRegister<Register>(); 6573 6574 // Materialize a pointer to the switch table 6575 std::vector<Label*> labels(num_entries); 6576 const ArenaVector<HBasicBlock*>& successors = switch_instr->GetBlock()->GetSuccessors(); 6577 for (uint32_t i = 0; i < num_entries; i++) { 6578 labels[i] = codegen_->GetLabelOf(successors[i]); 6579 } 6580 JumpTable* table = __ CreateJumpTable(std::move(labels), temp_reg); 6581 6582 // Remove the bias. 6583 Register key_reg; 6584 if (lower_bound != 0) { 6585 key_reg = locations->GetTemp(1).AsRegister<Register>(); 6586 __ AddConstant(key_reg, value_reg, -lower_bound); 6587 } else { 6588 key_reg = value_reg; 6589 } 6590 6591 // Check whether the value is in the table, jump to default block if not. 6592 __ CmpConstant(key_reg, num_entries - 1); 6593 __ b(codegen_->GetLabelOf(default_block), Condition::HI); 6594 6595 // Load the displacement from the table. 6596 __ ldr(temp_reg, Address(temp_reg, key_reg, Shift::LSL, 2)); 6597 6598 // Dispatch is a direct add to the PC (for Thumb2). 6599 __ EmitJumpTableDispatch(table, temp_reg); 6600 } 6601} 6602 6603void LocationsBuilderARM::VisitArmDexCacheArraysBase(HArmDexCacheArraysBase* base) { 6604 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(base); 6605 locations->SetOut(Location::RequiresRegister()); 6606 codegen_->AddDexCacheArraysBase(base); 6607} 6608 6609void InstructionCodeGeneratorARM::VisitArmDexCacheArraysBase(HArmDexCacheArraysBase* base) { 6610 Register base_reg = base->GetLocations()->Out().AsRegister<Register>(); 6611 CodeGeneratorARM::DexCacheArraysBaseLabels* labels = codegen_->GetDexCacheArraysBaseLabels(base); 6612 __ BindTrackedLabel(&labels->movw_label); 6613 __ movw(base_reg, 0u); 6614 __ BindTrackedLabel(&labels->movt_label); 6615 __ movt(base_reg, 0u); 6616 __ BindTrackedLabel(&labels->add_pc_label); 6617 __ add(base_reg, base_reg, ShifterOperand(PC)); 6618} 6619 6620void CodeGeneratorARM::MoveFromReturnRegister(Location trg, Primitive::Type type) { 6621 if (!trg.IsValid()) { 6622 DCHECK_EQ(type, Primitive::kPrimVoid); 6623 return; 6624 } 6625 6626 DCHECK_NE(type, Primitive::kPrimVoid); 6627 6628 Location return_loc = InvokeDexCallingConventionVisitorARM().GetReturnLocation(type); 6629 if (return_loc.Equals(trg)) { 6630 return; 6631 } 6632 6633 // TODO: Consider pairs in the parallel move resolver, then this could be nicely merged 6634 // with the last branch. 6635 if (type == Primitive::kPrimLong) { 6636 HParallelMove parallel_move(GetGraph()->GetArena()); 6637 parallel_move.AddMove(return_loc.ToLow(), trg.ToLow(), Primitive::kPrimInt, nullptr); 6638 parallel_move.AddMove(return_loc.ToHigh(), trg.ToHigh(), Primitive::kPrimInt, nullptr); 6639 GetMoveResolver()->EmitNativeCode(¶llel_move); 6640 } else if (type == Primitive::kPrimDouble) { 6641 HParallelMove parallel_move(GetGraph()->GetArena()); 6642 parallel_move.AddMove(return_loc.ToLow(), trg.ToLow(), Primitive::kPrimFloat, nullptr); 6643 parallel_move.AddMove(return_loc.ToHigh(), trg.ToHigh(), Primitive::kPrimFloat, nullptr); 6644 GetMoveResolver()->EmitNativeCode(¶llel_move); 6645 } else { 6646 // Let the parallel move resolver take care of all of this. 6647 HParallelMove parallel_move(GetGraph()->GetArena()); 6648 parallel_move.AddMove(return_loc, trg, type, nullptr); 6649 GetMoveResolver()->EmitNativeCode(¶llel_move); 6650 } 6651} 6652 6653void LocationsBuilderARM::VisitClassTableGet(HClassTableGet* instruction) { 6654 LocationSummary* locations = 6655 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); 6656 locations->SetInAt(0, Location::RequiresRegister()); 6657 locations->SetOut(Location::RequiresRegister()); 6658} 6659 6660void InstructionCodeGeneratorARM::VisitClassTableGet(HClassTableGet* instruction) { 6661 LocationSummary* locations = instruction->GetLocations(); 6662 uint32_t method_offset = 0; 6663 if (instruction->GetTableKind() == HClassTableGet::TableKind::kVTable) { 6664 method_offset = mirror::Class::EmbeddedVTableEntryOffset( 6665 instruction->GetIndex(), kArmPointerSize).SizeValue(); 6666 } else { 6667 method_offset = mirror::Class::EmbeddedImTableEntryOffset( 6668 instruction->GetIndex() % mirror::Class::kImtSize, kArmPointerSize).Uint32Value(); 6669 } 6670 __ LoadFromOffset(kLoadWord, 6671 locations->Out().AsRegister<Register>(), 6672 locations->InAt(0).AsRegister<Register>(), 6673 method_offset); 6674} 6675 6676#undef __ 6677#undef QUICK_ENTRY_POINT 6678 6679} // namespace arm 6680} // namespace art 6681