code_generator_arm.cc revision 4a0dad67867f389e01a5a6c0fe381d210f687c0d
1/* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include "code_generator_arm.h" 18 19#include "arch/arm/instruction_set_features_arm.h" 20#include "art_method.h" 21#include "code_generator_utils.h" 22#include "compiled_method.h" 23#include "entrypoints/quick/quick_entrypoints.h" 24#include "gc/accounting/card_table.h" 25#include "intrinsics.h" 26#include "intrinsics_arm.h" 27#include "mirror/array-inl.h" 28#include "mirror/class-inl.h" 29#include "thread.h" 30#include "utils/arm/assembler_arm.h" 31#include "utils/arm/managed_register_arm.h" 32#include "utils/assembler.h" 33#include "utils/stack_checks.h" 34 35namespace art { 36 37template<class MirrorType> 38class GcRoot; 39 40namespace arm { 41 42static bool ExpectedPairLayout(Location location) { 43 // We expected this for both core and fpu register pairs. 44 return ((location.low() & 1) == 0) && (location.low() + 1 == location.high()); 45} 46 47static constexpr int kCurrentMethodStackOffset = 0; 48static constexpr Register kMethodRegisterArgument = R0; 49 50static constexpr Register kCoreAlwaysSpillRegister = R5; 51static constexpr Register kCoreCalleeSaves[] = 52 { R5, R6, R7, R8, R10, R11, LR }; 53static constexpr SRegister kFpuCalleeSaves[] = 54 { S16, S17, S18, S19, S20, S21, S22, S23, S24, S25, S26, S27, S28, S29, S30, S31 }; 55 56// D31 cannot be split into two S registers, and the register allocator only works on 57// S registers. Therefore there is no need to block it. 58static constexpr DRegister DTMP = D31; 59 60static constexpr uint32_t kPackedSwitchCompareJumpThreshold = 7; 61 62#define __ down_cast<ArmAssembler*>(codegen->GetAssembler())-> 63#define QUICK_ENTRY_POINT(x) QUICK_ENTRYPOINT_OFFSET(kArmWordSize, x).Int32Value() 64 65class NullCheckSlowPathARM : public SlowPathCode { 66 public: 67 explicit NullCheckSlowPathARM(HNullCheck* instruction) : SlowPathCode(instruction) {} 68 69 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { 70 CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen); 71 __ Bind(GetEntryLabel()); 72 if (instruction_->CanThrowIntoCatchBlock()) { 73 // Live registers will be restored in the catch block if caught. 74 SaveLiveRegisters(codegen, instruction_->GetLocations()); 75 } 76 arm_codegen->InvokeRuntime( 77 QUICK_ENTRY_POINT(pThrowNullPointer), instruction_, instruction_->GetDexPc(), this); 78 CheckEntrypointTypes<kQuickThrowNullPointer, void, void>(); 79 } 80 81 bool IsFatal() const OVERRIDE { return true; } 82 83 const char* GetDescription() const OVERRIDE { return "NullCheckSlowPathARM"; } 84 85 private: 86 DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathARM); 87}; 88 89class DivZeroCheckSlowPathARM : public SlowPathCode { 90 public: 91 explicit DivZeroCheckSlowPathARM(HDivZeroCheck* instruction) : SlowPathCode(instruction) {} 92 93 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { 94 CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen); 95 __ Bind(GetEntryLabel()); 96 if (instruction_->CanThrowIntoCatchBlock()) { 97 // Live registers will be restored in the catch block if caught. 98 SaveLiveRegisters(codegen, instruction_->GetLocations()); 99 } 100 arm_codegen->InvokeRuntime( 101 QUICK_ENTRY_POINT(pThrowDivZero), instruction_, instruction_->GetDexPc(), this); 102 CheckEntrypointTypes<kQuickThrowDivZero, void, void>(); 103 } 104 105 bool IsFatal() const OVERRIDE { return true; } 106 107 const char* GetDescription() const OVERRIDE { return "DivZeroCheckSlowPathARM"; } 108 109 private: 110 DISALLOW_COPY_AND_ASSIGN(DivZeroCheckSlowPathARM); 111}; 112 113class SuspendCheckSlowPathARM : public SlowPathCode { 114 public: 115 SuspendCheckSlowPathARM(HSuspendCheck* instruction, HBasicBlock* successor) 116 : SlowPathCode(instruction), successor_(successor) {} 117 118 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { 119 CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen); 120 __ Bind(GetEntryLabel()); 121 SaveLiveRegisters(codegen, instruction_->GetLocations()); 122 arm_codegen->InvokeRuntime( 123 QUICK_ENTRY_POINT(pTestSuspend), instruction_, instruction_->GetDexPc(), this); 124 CheckEntrypointTypes<kQuickTestSuspend, void, void>(); 125 RestoreLiveRegisters(codegen, instruction_->GetLocations()); 126 if (successor_ == nullptr) { 127 __ b(GetReturnLabel()); 128 } else { 129 __ b(arm_codegen->GetLabelOf(successor_)); 130 } 131 } 132 133 Label* GetReturnLabel() { 134 DCHECK(successor_ == nullptr); 135 return &return_label_; 136 } 137 138 HBasicBlock* GetSuccessor() const { 139 return successor_; 140 } 141 142 const char* GetDescription() const OVERRIDE { return "SuspendCheckSlowPathARM"; } 143 144 private: 145 // If not null, the block to branch to after the suspend check. 146 HBasicBlock* const successor_; 147 148 // If `successor_` is null, the label to branch to after the suspend check. 149 Label return_label_; 150 151 DISALLOW_COPY_AND_ASSIGN(SuspendCheckSlowPathARM); 152}; 153 154class BoundsCheckSlowPathARM : public SlowPathCode { 155 public: 156 explicit BoundsCheckSlowPathARM(HBoundsCheck* instruction) 157 : SlowPathCode(instruction) {} 158 159 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { 160 CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen); 161 LocationSummary* locations = instruction_->GetLocations(); 162 163 __ Bind(GetEntryLabel()); 164 if (instruction_->CanThrowIntoCatchBlock()) { 165 // Live registers will be restored in the catch block if caught. 166 SaveLiveRegisters(codegen, instruction_->GetLocations()); 167 } 168 // We're moving two locations to locations that could overlap, so we need a parallel 169 // move resolver. 170 InvokeRuntimeCallingConvention calling_convention; 171 codegen->EmitParallelMoves( 172 locations->InAt(0), 173 Location::RegisterLocation(calling_convention.GetRegisterAt(0)), 174 Primitive::kPrimInt, 175 locations->InAt(1), 176 Location::RegisterLocation(calling_convention.GetRegisterAt(1)), 177 Primitive::kPrimInt); 178 arm_codegen->InvokeRuntime( 179 QUICK_ENTRY_POINT(pThrowArrayBounds), instruction_, instruction_->GetDexPc(), this); 180 CheckEntrypointTypes<kQuickThrowArrayBounds, void, int32_t, int32_t>(); 181 } 182 183 bool IsFatal() const OVERRIDE { return true; } 184 185 const char* GetDescription() const OVERRIDE { return "BoundsCheckSlowPathARM"; } 186 187 private: 188 DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathARM); 189}; 190 191class LoadClassSlowPathARM : public SlowPathCode { 192 public: 193 LoadClassSlowPathARM(HLoadClass* cls, 194 HInstruction* at, 195 uint32_t dex_pc, 196 bool do_clinit) 197 : SlowPathCode(at), cls_(cls), at_(at), dex_pc_(dex_pc), do_clinit_(do_clinit) { 198 DCHECK(at->IsLoadClass() || at->IsClinitCheck()); 199 } 200 201 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { 202 LocationSummary* locations = at_->GetLocations(); 203 204 CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen); 205 __ Bind(GetEntryLabel()); 206 SaveLiveRegisters(codegen, locations); 207 208 InvokeRuntimeCallingConvention calling_convention; 209 __ LoadImmediate(calling_convention.GetRegisterAt(0), cls_->GetTypeIndex()); 210 int32_t entry_point_offset = do_clinit_ 211 ? QUICK_ENTRY_POINT(pInitializeStaticStorage) 212 : QUICK_ENTRY_POINT(pInitializeType); 213 arm_codegen->InvokeRuntime(entry_point_offset, at_, dex_pc_, this); 214 if (do_clinit_) { 215 CheckEntrypointTypes<kQuickInitializeStaticStorage, void*, uint32_t>(); 216 } else { 217 CheckEntrypointTypes<kQuickInitializeType, void*, uint32_t>(); 218 } 219 220 // Move the class to the desired location. 221 Location out = locations->Out(); 222 if (out.IsValid()) { 223 DCHECK(out.IsRegister() && !locations->GetLiveRegisters()->ContainsCoreRegister(out.reg())); 224 arm_codegen->Move32(locations->Out(), Location::RegisterLocation(R0)); 225 } 226 RestoreLiveRegisters(codegen, locations); 227 __ b(GetExitLabel()); 228 } 229 230 const char* GetDescription() const OVERRIDE { return "LoadClassSlowPathARM"; } 231 232 private: 233 // The class this slow path will load. 234 HLoadClass* const cls_; 235 236 // The instruction where this slow path is happening. 237 // (Might be the load class or an initialization check). 238 HInstruction* const at_; 239 240 // The dex PC of `at_`. 241 const uint32_t dex_pc_; 242 243 // Whether to initialize the class. 244 const bool do_clinit_; 245 246 DISALLOW_COPY_AND_ASSIGN(LoadClassSlowPathARM); 247}; 248 249class LoadStringSlowPathARM : public SlowPathCode { 250 public: 251 explicit LoadStringSlowPathARM(HLoadString* instruction) : SlowPathCode(instruction) {} 252 253 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { 254 LocationSummary* locations = instruction_->GetLocations(); 255 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg())); 256 257 CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen); 258 __ Bind(GetEntryLabel()); 259 SaveLiveRegisters(codegen, locations); 260 261 InvokeRuntimeCallingConvention calling_convention; 262 const uint32_t string_index = instruction_->AsLoadString()->GetStringIndex(); 263 __ LoadImmediate(calling_convention.GetRegisterAt(0), string_index); 264 arm_codegen->InvokeRuntime( 265 QUICK_ENTRY_POINT(pResolveString), instruction_, instruction_->GetDexPc(), this); 266 CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>(); 267 arm_codegen->Move32(locations->Out(), Location::RegisterLocation(R0)); 268 269 RestoreLiveRegisters(codegen, locations); 270 __ b(GetExitLabel()); 271 } 272 273 const char* GetDescription() const OVERRIDE { return "LoadStringSlowPathARM"; } 274 275 private: 276 DISALLOW_COPY_AND_ASSIGN(LoadStringSlowPathARM); 277}; 278 279class TypeCheckSlowPathARM : public SlowPathCode { 280 public: 281 TypeCheckSlowPathARM(HInstruction* instruction, bool is_fatal) 282 : SlowPathCode(instruction), is_fatal_(is_fatal) {} 283 284 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { 285 LocationSummary* locations = instruction_->GetLocations(); 286 Location object_class = instruction_->IsCheckCast() ? locations->GetTemp(0) 287 : locations->Out(); 288 DCHECK(instruction_->IsCheckCast() 289 || !locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg())); 290 291 CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen); 292 __ Bind(GetEntryLabel()); 293 294 if (!is_fatal_) { 295 SaveLiveRegisters(codegen, locations); 296 } 297 298 // We're moving two locations to locations that could overlap, so we need a parallel 299 // move resolver. 300 InvokeRuntimeCallingConvention calling_convention; 301 codegen->EmitParallelMoves( 302 locations->InAt(1), 303 Location::RegisterLocation(calling_convention.GetRegisterAt(0)), 304 Primitive::kPrimNot, 305 object_class, 306 Location::RegisterLocation(calling_convention.GetRegisterAt(1)), 307 Primitive::kPrimNot); 308 309 if (instruction_->IsInstanceOf()) { 310 arm_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pInstanceofNonTrivial), 311 instruction_, 312 instruction_->GetDexPc(), 313 this); 314 CheckEntrypointTypes< 315 kQuickInstanceofNonTrivial, uint32_t, const mirror::Class*, const mirror::Class*>(); 316 arm_codegen->Move32(locations->Out(), Location::RegisterLocation(R0)); 317 } else { 318 DCHECK(instruction_->IsCheckCast()); 319 arm_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pCheckCast), 320 instruction_, 321 instruction_->GetDexPc(), 322 this); 323 CheckEntrypointTypes<kQuickCheckCast, void, const mirror::Class*, const mirror::Class*>(); 324 } 325 326 if (!is_fatal_) { 327 RestoreLiveRegisters(codegen, locations); 328 __ b(GetExitLabel()); 329 } 330 } 331 332 const char* GetDescription() const OVERRIDE { return "TypeCheckSlowPathARM"; } 333 334 bool IsFatal() const OVERRIDE { return is_fatal_; } 335 336 private: 337 const bool is_fatal_; 338 339 DISALLOW_COPY_AND_ASSIGN(TypeCheckSlowPathARM); 340}; 341 342class DeoptimizationSlowPathARM : public SlowPathCode { 343 public: 344 explicit DeoptimizationSlowPathARM(HDeoptimize* instruction) 345 : SlowPathCode(instruction) {} 346 347 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { 348 CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen); 349 __ Bind(GetEntryLabel()); 350 SaveLiveRegisters(codegen, instruction_->GetLocations()); 351 arm_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pDeoptimize), 352 instruction_, 353 instruction_->GetDexPc(), 354 this); 355 CheckEntrypointTypes<kQuickDeoptimize, void, void>(); 356 } 357 358 const char* GetDescription() const OVERRIDE { return "DeoptimizationSlowPathARM"; } 359 360 private: 361 DISALLOW_COPY_AND_ASSIGN(DeoptimizationSlowPathARM); 362}; 363 364class ArraySetSlowPathARM : public SlowPathCode { 365 public: 366 explicit ArraySetSlowPathARM(HInstruction* instruction) : SlowPathCode(instruction) {} 367 368 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { 369 LocationSummary* locations = instruction_->GetLocations(); 370 __ Bind(GetEntryLabel()); 371 SaveLiveRegisters(codegen, locations); 372 373 InvokeRuntimeCallingConvention calling_convention; 374 HParallelMove parallel_move(codegen->GetGraph()->GetArena()); 375 parallel_move.AddMove( 376 locations->InAt(0), 377 Location::RegisterLocation(calling_convention.GetRegisterAt(0)), 378 Primitive::kPrimNot, 379 nullptr); 380 parallel_move.AddMove( 381 locations->InAt(1), 382 Location::RegisterLocation(calling_convention.GetRegisterAt(1)), 383 Primitive::kPrimInt, 384 nullptr); 385 parallel_move.AddMove( 386 locations->InAt(2), 387 Location::RegisterLocation(calling_convention.GetRegisterAt(2)), 388 Primitive::kPrimNot, 389 nullptr); 390 codegen->GetMoveResolver()->EmitNativeCode(¶llel_move); 391 392 CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen); 393 arm_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pAputObject), 394 instruction_, 395 instruction_->GetDexPc(), 396 this); 397 CheckEntrypointTypes<kQuickAputObject, void, mirror::Array*, int32_t, mirror::Object*>(); 398 RestoreLiveRegisters(codegen, locations); 399 __ b(GetExitLabel()); 400 } 401 402 const char* GetDescription() const OVERRIDE { return "ArraySetSlowPathARM"; } 403 404 private: 405 DISALLOW_COPY_AND_ASSIGN(ArraySetSlowPathARM); 406}; 407 408// Slow path marking an object during a read barrier. 409class ReadBarrierMarkSlowPathARM : public SlowPathCode { 410 public: 411 ReadBarrierMarkSlowPathARM(HInstruction* instruction, Location out, Location obj) 412 : SlowPathCode(instruction), out_(out), obj_(obj) { 413 DCHECK(kEmitCompilerReadBarrier); 414 } 415 416 const char* GetDescription() const OVERRIDE { return "ReadBarrierMarkSlowPathARM"; } 417 418 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { 419 LocationSummary* locations = instruction_->GetLocations(); 420 Register reg_out = out_.AsRegister<Register>(); 421 DCHECK(locations->CanCall()); 422 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(reg_out)); 423 DCHECK(instruction_->IsInstanceFieldGet() || 424 instruction_->IsStaticFieldGet() || 425 instruction_->IsArrayGet() || 426 instruction_->IsLoadClass() || 427 instruction_->IsLoadString() || 428 instruction_->IsInstanceOf() || 429 instruction_->IsCheckCast()) 430 << "Unexpected instruction in read barrier marking slow path: " 431 << instruction_->DebugName(); 432 433 __ Bind(GetEntryLabel()); 434 SaveLiveRegisters(codegen, locations); 435 436 InvokeRuntimeCallingConvention calling_convention; 437 CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen); 438 arm_codegen->Move32(Location::RegisterLocation(calling_convention.GetRegisterAt(0)), obj_); 439 arm_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pReadBarrierMark), 440 instruction_, 441 instruction_->GetDexPc(), 442 this); 443 CheckEntrypointTypes<kQuickReadBarrierMark, mirror::Object*, mirror::Object*>(); 444 arm_codegen->Move32(out_, Location::RegisterLocation(R0)); 445 446 RestoreLiveRegisters(codegen, locations); 447 __ b(GetExitLabel()); 448 } 449 450 private: 451 const Location out_; 452 const Location obj_; 453 454 DISALLOW_COPY_AND_ASSIGN(ReadBarrierMarkSlowPathARM); 455}; 456 457// Slow path generating a read barrier for a heap reference. 458class ReadBarrierForHeapReferenceSlowPathARM : public SlowPathCode { 459 public: 460 ReadBarrierForHeapReferenceSlowPathARM(HInstruction* instruction, 461 Location out, 462 Location ref, 463 Location obj, 464 uint32_t offset, 465 Location index) 466 : SlowPathCode(instruction), 467 out_(out), 468 ref_(ref), 469 obj_(obj), 470 offset_(offset), 471 index_(index) { 472 DCHECK(kEmitCompilerReadBarrier); 473 // If `obj` is equal to `out` or `ref`, it means the initial object 474 // has been overwritten by (or after) the heap object reference load 475 // to be instrumented, e.g.: 476 // 477 // __ LoadFromOffset(kLoadWord, out, out, offset); 478 // codegen_->GenerateReadBarrierSlow(instruction, out_loc, out_loc, out_loc, offset); 479 // 480 // In that case, we have lost the information about the original 481 // object, and the emitted read barrier cannot work properly. 482 DCHECK(!obj.Equals(out)) << "obj=" << obj << " out=" << out; 483 DCHECK(!obj.Equals(ref)) << "obj=" << obj << " ref=" << ref; 484 } 485 486 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { 487 CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen); 488 LocationSummary* locations = instruction_->GetLocations(); 489 Register reg_out = out_.AsRegister<Register>(); 490 DCHECK(locations->CanCall()); 491 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(reg_out)); 492 DCHECK(!instruction_->IsInvoke() || 493 (instruction_->IsInvokeStaticOrDirect() && 494 instruction_->GetLocations()->Intrinsified())) 495 << "Unexpected instruction in read barrier for heap reference slow path: " 496 << instruction_->DebugName(); 497 498 __ Bind(GetEntryLabel()); 499 SaveLiveRegisters(codegen, locations); 500 501 // We may have to change the index's value, but as `index_` is a 502 // constant member (like other "inputs" of this slow path), 503 // introduce a copy of it, `index`. 504 Location index = index_; 505 if (index_.IsValid()) { 506 // Handle `index_` for HArrayGet and intrinsic UnsafeGetObject. 507 if (instruction_->IsArrayGet()) { 508 // Compute the actual memory offset and store it in `index`. 509 Register index_reg = index_.AsRegister<Register>(); 510 DCHECK(locations->GetLiveRegisters()->ContainsCoreRegister(index_reg)); 511 if (codegen->IsCoreCalleeSaveRegister(index_reg)) { 512 // We are about to change the value of `index_reg` (see the 513 // calls to art::arm::Thumb2Assembler::Lsl and 514 // art::arm::Thumb2Assembler::AddConstant below), but it has 515 // not been saved by the previous call to 516 // art::SlowPathCode::SaveLiveRegisters, as it is a 517 // callee-save register -- 518 // art::SlowPathCode::SaveLiveRegisters does not consider 519 // callee-save registers, as it has been designed with the 520 // assumption that callee-save registers are supposed to be 521 // handled by the called function. So, as a callee-save 522 // register, `index_reg` _would_ eventually be saved onto 523 // the stack, but it would be too late: we would have 524 // changed its value earlier. Therefore, we manually save 525 // it here into another freely available register, 526 // `free_reg`, chosen of course among the caller-save 527 // registers (as a callee-save `free_reg` register would 528 // exhibit the same problem). 529 // 530 // Note we could have requested a temporary register from 531 // the register allocator instead; but we prefer not to, as 532 // this is a slow path, and we know we can find a 533 // caller-save register that is available. 534 Register free_reg = FindAvailableCallerSaveRegister(codegen); 535 __ Mov(free_reg, index_reg); 536 index_reg = free_reg; 537 index = Location::RegisterLocation(index_reg); 538 } else { 539 // The initial register stored in `index_` has already been 540 // saved in the call to art::SlowPathCode::SaveLiveRegisters 541 // (as it is not a callee-save register), so we can freely 542 // use it. 543 } 544 // Shifting the index value contained in `index_reg` by the scale 545 // factor (2) cannot overflow in practice, as the runtime is 546 // unable to allocate object arrays with a size larger than 547 // 2^26 - 1 (that is, 2^28 - 4 bytes). 548 __ Lsl(index_reg, index_reg, TIMES_4); 549 static_assert( 550 sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t), 551 "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes."); 552 __ AddConstant(index_reg, index_reg, offset_); 553 } else { 554 DCHECK(instruction_->IsInvoke()); 555 DCHECK(instruction_->GetLocations()->Intrinsified()); 556 DCHECK((instruction_->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObject) || 557 (instruction_->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile)) 558 << instruction_->AsInvoke()->GetIntrinsic(); 559 DCHECK_EQ(offset_, 0U); 560 DCHECK(index_.IsRegisterPair()); 561 // UnsafeGet's offset location is a register pair, the low 562 // part contains the correct offset. 563 index = index_.ToLow(); 564 } 565 } 566 567 // We're moving two or three locations to locations that could 568 // overlap, so we need a parallel move resolver. 569 InvokeRuntimeCallingConvention calling_convention; 570 HParallelMove parallel_move(codegen->GetGraph()->GetArena()); 571 parallel_move.AddMove(ref_, 572 Location::RegisterLocation(calling_convention.GetRegisterAt(0)), 573 Primitive::kPrimNot, 574 nullptr); 575 parallel_move.AddMove(obj_, 576 Location::RegisterLocation(calling_convention.GetRegisterAt(1)), 577 Primitive::kPrimNot, 578 nullptr); 579 if (index.IsValid()) { 580 parallel_move.AddMove(index, 581 Location::RegisterLocation(calling_convention.GetRegisterAt(2)), 582 Primitive::kPrimInt, 583 nullptr); 584 codegen->GetMoveResolver()->EmitNativeCode(¶llel_move); 585 } else { 586 codegen->GetMoveResolver()->EmitNativeCode(¶llel_move); 587 __ LoadImmediate(calling_convention.GetRegisterAt(2), offset_); 588 } 589 arm_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pReadBarrierSlow), 590 instruction_, 591 instruction_->GetDexPc(), 592 this); 593 CheckEntrypointTypes< 594 kQuickReadBarrierSlow, mirror::Object*, mirror::Object*, mirror::Object*, uint32_t>(); 595 arm_codegen->Move32(out_, Location::RegisterLocation(R0)); 596 597 RestoreLiveRegisters(codegen, locations); 598 __ b(GetExitLabel()); 599 } 600 601 const char* GetDescription() const OVERRIDE { return "ReadBarrierForHeapReferenceSlowPathARM"; } 602 603 private: 604 Register FindAvailableCallerSaveRegister(CodeGenerator* codegen) { 605 size_t ref = static_cast<int>(ref_.AsRegister<Register>()); 606 size_t obj = static_cast<int>(obj_.AsRegister<Register>()); 607 for (size_t i = 0, e = codegen->GetNumberOfCoreRegisters(); i < e; ++i) { 608 if (i != ref && i != obj && !codegen->IsCoreCalleeSaveRegister(i)) { 609 return static_cast<Register>(i); 610 } 611 } 612 // We shall never fail to find a free caller-save register, as 613 // there are more than two core caller-save registers on ARM 614 // (meaning it is possible to find one which is different from 615 // `ref` and `obj`). 616 DCHECK_GT(codegen->GetNumberOfCoreCallerSaveRegisters(), 2u); 617 LOG(FATAL) << "Could not find a free caller-save register"; 618 UNREACHABLE(); 619 } 620 621 const Location out_; 622 const Location ref_; 623 const Location obj_; 624 const uint32_t offset_; 625 // An additional location containing an index to an array. 626 // Only used for HArrayGet and the UnsafeGetObject & 627 // UnsafeGetObjectVolatile intrinsics. 628 const Location index_; 629 630 DISALLOW_COPY_AND_ASSIGN(ReadBarrierForHeapReferenceSlowPathARM); 631}; 632 633// Slow path generating a read barrier for a GC root. 634class ReadBarrierForRootSlowPathARM : public SlowPathCode { 635 public: 636 ReadBarrierForRootSlowPathARM(HInstruction* instruction, Location out, Location root) 637 : SlowPathCode(instruction), out_(out), root_(root) { 638 DCHECK(kEmitCompilerReadBarrier); 639 } 640 641 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { 642 LocationSummary* locations = instruction_->GetLocations(); 643 Register reg_out = out_.AsRegister<Register>(); 644 DCHECK(locations->CanCall()); 645 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(reg_out)); 646 DCHECK(instruction_->IsLoadClass() || instruction_->IsLoadString()) 647 << "Unexpected instruction in read barrier for GC root slow path: " 648 << instruction_->DebugName(); 649 650 __ Bind(GetEntryLabel()); 651 SaveLiveRegisters(codegen, locations); 652 653 InvokeRuntimeCallingConvention calling_convention; 654 CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen); 655 arm_codegen->Move32(Location::RegisterLocation(calling_convention.GetRegisterAt(0)), root_); 656 arm_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pReadBarrierForRootSlow), 657 instruction_, 658 instruction_->GetDexPc(), 659 this); 660 CheckEntrypointTypes<kQuickReadBarrierForRootSlow, mirror::Object*, GcRoot<mirror::Object>*>(); 661 arm_codegen->Move32(out_, Location::RegisterLocation(R0)); 662 663 RestoreLiveRegisters(codegen, locations); 664 __ b(GetExitLabel()); 665 } 666 667 const char* GetDescription() const OVERRIDE { return "ReadBarrierForRootSlowPathARM"; } 668 669 private: 670 const Location out_; 671 const Location root_; 672 673 DISALLOW_COPY_AND_ASSIGN(ReadBarrierForRootSlowPathARM); 674}; 675 676#undef __ 677#define __ down_cast<ArmAssembler*>(GetAssembler())-> 678 679inline Condition ARMCondition(IfCondition cond) { 680 switch (cond) { 681 case kCondEQ: return EQ; 682 case kCondNE: return NE; 683 case kCondLT: return LT; 684 case kCondLE: return LE; 685 case kCondGT: return GT; 686 case kCondGE: return GE; 687 case kCondB: return LO; 688 case kCondBE: return LS; 689 case kCondA: return HI; 690 case kCondAE: return HS; 691 } 692 LOG(FATAL) << "Unreachable"; 693 UNREACHABLE(); 694} 695 696// Maps signed condition to unsigned condition. 697inline Condition ARMUnsignedCondition(IfCondition cond) { 698 switch (cond) { 699 case kCondEQ: return EQ; 700 case kCondNE: return NE; 701 // Signed to unsigned. 702 case kCondLT: return LO; 703 case kCondLE: return LS; 704 case kCondGT: return HI; 705 case kCondGE: return HS; 706 // Unsigned remain unchanged. 707 case kCondB: return LO; 708 case kCondBE: return LS; 709 case kCondA: return HI; 710 case kCondAE: return HS; 711 } 712 LOG(FATAL) << "Unreachable"; 713 UNREACHABLE(); 714} 715 716inline Condition ARMFPCondition(IfCondition cond, bool gt_bias) { 717 // The ARM condition codes can express all the necessary branches, see the 718 // "Meaning (floating-point)" column in the table A8-1 of the ARMv7 reference manual. 719 // There is no dex instruction or HIR that would need the missing conditions 720 // "equal or unordered" or "not equal". 721 switch (cond) { 722 case kCondEQ: return EQ; 723 case kCondNE: return NE /* unordered */; 724 case kCondLT: return gt_bias ? CC : LT /* unordered */; 725 case kCondLE: return gt_bias ? LS : LE /* unordered */; 726 case kCondGT: return gt_bias ? HI /* unordered */ : GT; 727 case kCondGE: return gt_bias ? CS /* unordered */ : GE; 728 default: 729 LOG(FATAL) << "UNREACHABLE"; 730 UNREACHABLE(); 731 } 732} 733 734void CodeGeneratorARM::DumpCoreRegister(std::ostream& stream, int reg) const { 735 stream << Register(reg); 736} 737 738void CodeGeneratorARM::DumpFloatingPointRegister(std::ostream& stream, int reg) const { 739 stream << SRegister(reg); 740} 741 742size_t CodeGeneratorARM::SaveCoreRegister(size_t stack_index, uint32_t reg_id) { 743 __ StoreToOffset(kStoreWord, static_cast<Register>(reg_id), SP, stack_index); 744 return kArmWordSize; 745} 746 747size_t CodeGeneratorARM::RestoreCoreRegister(size_t stack_index, uint32_t reg_id) { 748 __ LoadFromOffset(kLoadWord, static_cast<Register>(reg_id), SP, stack_index); 749 return kArmWordSize; 750} 751 752size_t CodeGeneratorARM::SaveFloatingPointRegister(size_t stack_index, uint32_t reg_id) { 753 __ StoreSToOffset(static_cast<SRegister>(reg_id), SP, stack_index); 754 return kArmWordSize; 755} 756 757size_t CodeGeneratorARM::RestoreFloatingPointRegister(size_t stack_index, uint32_t reg_id) { 758 __ LoadSFromOffset(static_cast<SRegister>(reg_id), SP, stack_index); 759 return kArmWordSize; 760} 761 762CodeGeneratorARM::CodeGeneratorARM(HGraph* graph, 763 const ArmInstructionSetFeatures& isa_features, 764 const CompilerOptions& compiler_options, 765 OptimizingCompilerStats* stats) 766 : CodeGenerator(graph, 767 kNumberOfCoreRegisters, 768 kNumberOfSRegisters, 769 kNumberOfRegisterPairs, 770 ComputeRegisterMask(reinterpret_cast<const int*>(kCoreCalleeSaves), 771 arraysize(kCoreCalleeSaves)), 772 ComputeRegisterMask(reinterpret_cast<const int*>(kFpuCalleeSaves), 773 arraysize(kFpuCalleeSaves)), 774 compiler_options, 775 stats), 776 block_labels_(nullptr), 777 location_builder_(graph, this), 778 instruction_visitor_(graph, this), 779 move_resolver_(graph->GetArena(), this), 780 assembler_(), 781 isa_features_(isa_features), 782 method_patches_(MethodReferenceComparator(), 783 graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), 784 call_patches_(MethodReferenceComparator(), 785 graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), 786 relative_call_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), 787 dex_cache_arrays_base_labels_(std::less<HArmDexCacheArraysBase*>(), 788 graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) { 789 // Always save the LR register to mimic Quick. 790 AddAllocatedRegister(Location::RegisterLocation(LR)); 791} 792 793void CodeGeneratorARM::Finalize(CodeAllocator* allocator) { 794 // Ensure that we fix up branches and literal loads and emit the literal pool. 795 __ FinalizeCode(); 796 797 // Adjust native pc offsets in stack maps. 798 for (size_t i = 0, num = stack_map_stream_.GetNumberOfStackMaps(); i != num; ++i) { 799 uint32_t old_position = stack_map_stream_.GetStackMap(i).native_pc_offset; 800 uint32_t new_position = __ GetAdjustedPosition(old_position); 801 stack_map_stream_.SetStackMapNativePcOffset(i, new_position); 802 } 803 // Adjust pc offsets for the disassembly information. 804 if (disasm_info_ != nullptr) { 805 GeneratedCodeInterval* frame_entry_interval = disasm_info_->GetFrameEntryInterval(); 806 frame_entry_interval->start = __ GetAdjustedPosition(frame_entry_interval->start); 807 frame_entry_interval->end = __ GetAdjustedPosition(frame_entry_interval->end); 808 for (auto& it : *disasm_info_->GetInstructionIntervals()) { 809 it.second.start = __ GetAdjustedPosition(it.second.start); 810 it.second.end = __ GetAdjustedPosition(it.second.end); 811 } 812 for (auto& it : *disasm_info_->GetSlowPathIntervals()) { 813 it.code_interval.start = __ GetAdjustedPosition(it.code_interval.start); 814 it.code_interval.end = __ GetAdjustedPosition(it.code_interval.end); 815 } 816 } 817 818 CodeGenerator::Finalize(allocator); 819} 820 821void CodeGeneratorARM::SetupBlockedRegisters() const { 822 // Don't allocate the dalvik style register pair passing. 823 blocked_register_pairs_[R1_R2] = true; 824 825 // Stack register, LR and PC are always reserved. 826 blocked_core_registers_[SP] = true; 827 blocked_core_registers_[LR] = true; 828 blocked_core_registers_[PC] = true; 829 830 // Reserve thread register. 831 blocked_core_registers_[TR] = true; 832 833 // Reserve temp register. 834 blocked_core_registers_[IP] = true; 835 836 if (GetGraph()->IsDebuggable()) { 837 // Stubs do not save callee-save floating point registers. If the graph 838 // is debuggable, we need to deal with these registers differently. For 839 // now, just block them. 840 for (size_t i = 0; i < arraysize(kFpuCalleeSaves); ++i) { 841 blocked_fpu_registers_[kFpuCalleeSaves[i]] = true; 842 } 843 } 844 845 UpdateBlockedPairRegisters(); 846} 847 848void CodeGeneratorARM::UpdateBlockedPairRegisters() const { 849 for (int i = 0; i < kNumberOfRegisterPairs; i++) { 850 ArmManagedRegister current = 851 ArmManagedRegister::FromRegisterPair(static_cast<RegisterPair>(i)); 852 if (blocked_core_registers_[current.AsRegisterPairLow()] 853 || blocked_core_registers_[current.AsRegisterPairHigh()]) { 854 blocked_register_pairs_[i] = true; 855 } 856 } 857} 858 859InstructionCodeGeneratorARM::InstructionCodeGeneratorARM(HGraph* graph, CodeGeneratorARM* codegen) 860 : InstructionCodeGenerator(graph, codegen), 861 assembler_(codegen->GetAssembler()), 862 codegen_(codegen) {} 863 864void CodeGeneratorARM::ComputeSpillMask() { 865 core_spill_mask_ = allocated_registers_.GetCoreRegisters() & core_callee_save_mask_; 866 DCHECK_NE(core_spill_mask_, 0u) << "At least the return address register must be saved"; 867 // There is no easy instruction to restore just the PC on thumb2. We spill and 868 // restore another arbitrary register. 869 core_spill_mask_ |= (1 << kCoreAlwaysSpillRegister); 870 fpu_spill_mask_ = allocated_registers_.GetFloatingPointRegisters() & fpu_callee_save_mask_; 871 // We use vpush and vpop for saving and restoring floating point registers, which take 872 // a SRegister and the number of registers to save/restore after that SRegister. We 873 // therefore update the `fpu_spill_mask_` to also contain those registers not allocated, 874 // but in the range. 875 if (fpu_spill_mask_ != 0) { 876 uint32_t least_significant_bit = LeastSignificantBit(fpu_spill_mask_); 877 uint32_t most_significant_bit = MostSignificantBit(fpu_spill_mask_); 878 for (uint32_t i = least_significant_bit + 1 ; i < most_significant_bit; ++i) { 879 fpu_spill_mask_ |= (1 << i); 880 } 881 } 882} 883 884static dwarf::Reg DWARFReg(Register reg) { 885 return dwarf::Reg::ArmCore(static_cast<int>(reg)); 886} 887 888static dwarf::Reg DWARFReg(SRegister reg) { 889 return dwarf::Reg::ArmFp(static_cast<int>(reg)); 890} 891 892void CodeGeneratorARM::GenerateFrameEntry() { 893 bool skip_overflow_check = 894 IsLeafMethod() && !FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kArm); 895 DCHECK(GetCompilerOptions().GetImplicitStackOverflowChecks()); 896 __ Bind(&frame_entry_label_); 897 898 if (HasEmptyFrame()) { 899 return; 900 } 901 902 if (!skip_overflow_check) { 903 __ AddConstant(IP, SP, -static_cast<int32_t>(GetStackOverflowReservedBytes(kArm))); 904 __ LoadFromOffset(kLoadWord, IP, IP, 0); 905 RecordPcInfo(nullptr, 0); 906 } 907 908 __ PushList(core_spill_mask_); 909 __ cfi().AdjustCFAOffset(kArmWordSize * POPCOUNT(core_spill_mask_)); 910 __ cfi().RelOffsetForMany(DWARFReg(kMethodRegisterArgument), 0, core_spill_mask_, kArmWordSize); 911 if (fpu_spill_mask_ != 0) { 912 SRegister start_register = SRegister(LeastSignificantBit(fpu_spill_mask_)); 913 __ vpushs(start_register, POPCOUNT(fpu_spill_mask_)); 914 __ cfi().AdjustCFAOffset(kArmWordSize * POPCOUNT(fpu_spill_mask_)); 915 __ cfi().RelOffsetForMany(DWARFReg(S0), 0, fpu_spill_mask_, kArmWordSize); 916 } 917 int adjust = GetFrameSize() - FrameEntrySpillSize(); 918 __ AddConstant(SP, -adjust); 919 __ cfi().AdjustCFAOffset(adjust); 920 __ StoreToOffset(kStoreWord, kMethodRegisterArgument, SP, 0); 921} 922 923void CodeGeneratorARM::GenerateFrameExit() { 924 if (HasEmptyFrame()) { 925 __ bx(LR); 926 return; 927 } 928 __ cfi().RememberState(); 929 int adjust = GetFrameSize() - FrameEntrySpillSize(); 930 __ AddConstant(SP, adjust); 931 __ cfi().AdjustCFAOffset(-adjust); 932 if (fpu_spill_mask_ != 0) { 933 SRegister start_register = SRegister(LeastSignificantBit(fpu_spill_mask_)); 934 __ vpops(start_register, POPCOUNT(fpu_spill_mask_)); 935 __ cfi().AdjustCFAOffset(-kArmPointerSize * POPCOUNT(fpu_spill_mask_)); 936 __ cfi().RestoreMany(DWARFReg(SRegister(0)), fpu_spill_mask_); 937 } 938 // Pop LR into PC to return. 939 DCHECK_NE(core_spill_mask_ & (1 << LR), 0U); 940 uint32_t pop_mask = (core_spill_mask_ & (~(1 << LR))) | 1 << PC; 941 __ PopList(pop_mask); 942 __ cfi().RestoreState(); 943 __ cfi().DefCFAOffset(GetFrameSize()); 944} 945 946void CodeGeneratorARM::Bind(HBasicBlock* block) { 947 Label* label = GetLabelOf(block); 948 __ BindTrackedLabel(label); 949} 950 951Location CodeGeneratorARM::GetStackLocation(HLoadLocal* load) const { 952 switch (load->GetType()) { 953 case Primitive::kPrimLong: 954 case Primitive::kPrimDouble: 955 return Location::DoubleStackSlot(GetStackSlot(load->GetLocal())); 956 957 case Primitive::kPrimInt: 958 case Primitive::kPrimNot: 959 case Primitive::kPrimFloat: 960 return Location::StackSlot(GetStackSlot(load->GetLocal())); 961 962 case Primitive::kPrimBoolean: 963 case Primitive::kPrimByte: 964 case Primitive::kPrimChar: 965 case Primitive::kPrimShort: 966 case Primitive::kPrimVoid: 967 LOG(FATAL) << "Unexpected type " << load->GetType(); 968 UNREACHABLE(); 969 } 970 971 LOG(FATAL) << "Unreachable"; 972 UNREACHABLE(); 973} 974 975Location InvokeDexCallingConventionVisitorARM::GetNextLocation(Primitive::Type type) { 976 switch (type) { 977 case Primitive::kPrimBoolean: 978 case Primitive::kPrimByte: 979 case Primitive::kPrimChar: 980 case Primitive::kPrimShort: 981 case Primitive::kPrimInt: 982 case Primitive::kPrimNot: { 983 uint32_t index = gp_index_++; 984 uint32_t stack_index = stack_index_++; 985 if (index < calling_convention.GetNumberOfRegisters()) { 986 return Location::RegisterLocation(calling_convention.GetRegisterAt(index)); 987 } else { 988 return Location::StackSlot(calling_convention.GetStackOffsetOf(stack_index)); 989 } 990 } 991 992 case Primitive::kPrimLong: { 993 uint32_t index = gp_index_; 994 uint32_t stack_index = stack_index_; 995 gp_index_ += 2; 996 stack_index_ += 2; 997 if (index + 1 < calling_convention.GetNumberOfRegisters()) { 998 if (calling_convention.GetRegisterAt(index) == R1) { 999 // Skip R1, and use R2_R3 instead. 1000 gp_index_++; 1001 index++; 1002 } 1003 } 1004 if (index + 1 < calling_convention.GetNumberOfRegisters()) { 1005 DCHECK_EQ(calling_convention.GetRegisterAt(index) + 1, 1006 calling_convention.GetRegisterAt(index + 1)); 1007 1008 return Location::RegisterPairLocation(calling_convention.GetRegisterAt(index), 1009 calling_convention.GetRegisterAt(index + 1)); 1010 } else { 1011 return Location::DoubleStackSlot(calling_convention.GetStackOffsetOf(stack_index)); 1012 } 1013 } 1014 1015 case Primitive::kPrimFloat: { 1016 uint32_t stack_index = stack_index_++; 1017 if (float_index_ % 2 == 0) { 1018 float_index_ = std::max(double_index_, float_index_); 1019 } 1020 if (float_index_ < calling_convention.GetNumberOfFpuRegisters()) { 1021 return Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(float_index_++)); 1022 } else { 1023 return Location::StackSlot(calling_convention.GetStackOffsetOf(stack_index)); 1024 } 1025 } 1026 1027 case Primitive::kPrimDouble: { 1028 double_index_ = std::max(double_index_, RoundUp(float_index_, 2)); 1029 uint32_t stack_index = stack_index_; 1030 stack_index_ += 2; 1031 if (double_index_ + 1 < calling_convention.GetNumberOfFpuRegisters()) { 1032 uint32_t index = double_index_; 1033 double_index_ += 2; 1034 Location result = Location::FpuRegisterPairLocation( 1035 calling_convention.GetFpuRegisterAt(index), 1036 calling_convention.GetFpuRegisterAt(index + 1)); 1037 DCHECK(ExpectedPairLayout(result)); 1038 return result; 1039 } else { 1040 return Location::DoubleStackSlot(calling_convention.GetStackOffsetOf(stack_index)); 1041 } 1042 } 1043 1044 case Primitive::kPrimVoid: 1045 LOG(FATAL) << "Unexpected parameter type " << type; 1046 break; 1047 } 1048 return Location::NoLocation(); 1049} 1050 1051Location InvokeDexCallingConventionVisitorARM::GetReturnLocation(Primitive::Type type) const { 1052 switch (type) { 1053 case Primitive::kPrimBoolean: 1054 case Primitive::kPrimByte: 1055 case Primitive::kPrimChar: 1056 case Primitive::kPrimShort: 1057 case Primitive::kPrimInt: 1058 case Primitive::kPrimNot: { 1059 return Location::RegisterLocation(R0); 1060 } 1061 1062 case Primitive::kPrimFloat: { 1063 return Location::FpuRegisterLocation(S0); 1064 } 1065 1066 case Primitive::kPrimLong: { 1067 return Location::RegisterPairLocation(R0, R1); 1068 } 1069 1070 case Primitive::kPrimDouble: { 1071 return Location::FpuRegisterPairLocation(S0, S1); 1072 } 1073 1074 case Primitive::kPrimVoid: 1075 return Location::NoLocation(); 1076 } 1077 1078 UNREACHABLE(); 1079} 1080 1081Location InvokeDexCallingConventionVisitorARM::GetMethodLocation() const { 1082 return Location::RegisterLocation(kMethodRegisterArgument); 1083} 1084 1085void CodeGeneratorARM::Move32(Location destination, Location source) { 1086 if (source.Equals(destination)) { 1087 return; 1088 } 1089 if (destination.IsRegister()) { 1090 if (source.IsRegister()) { 1091 __ Mov(destination.AsRegister<Register>(), source.AsRegister<Register>()); 1092 } else if (source.IsFpuRegister()) { 1093 __ vmovrs(destination.AsRegister<Register>(), source.AsFpuRegister<SRegister>()); 1094 } else { 1095 __ LoadFromOffset(kLoadWord, destination.AsRegister<Register>(), SP, source.GetStackIndex()); 1096 } 1097 } else if (destination.IsFpuRegister()) { 1098 if (source.IsRegister()) { 1099 __ vmovsr(destination.AsFpuRegister<SRegister>(), source.AsRegister<Register>()); 1100 } else if (source.IsFpuRegister()) { 1101 __ vmovs(destination.AsFpuRegister<SRegister>(), source.AsFpuRegister<SRegister>()); 1102 } else { 1103 __ LoadSFromOffset(destination.AsFpuRegister<SRegister>(), SP, source.GetStackIndex()); 1104 } 1105 } else { 1106 DCHECK(destination.IsStackSlot()) << destination; 1107 if (source.IsRegister()) { 1108 __ StoreToOffset(kStoreWord, source.AsRegister<Register>(), SP, destination.GetStackIndex()); 1109 } else if (source.IsFpuRegister()) { 1110 __ StoreSToOffset(source.AsFpuRegister<SRegister>(), SP, destination.GetStackIndex()); 1111 } else { 1112 DCHECK(source.IsStackSlot()) << source; 1113 __ LoadFromOffset(kLoadWord, IP, SP, source.GetStackIndex()); 1114 __ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex()); 1115 } 1116 } 1117} 1118 1119void CodeGeneratorARM::Move64(Location destination, Location source) { 1120 if (source.Equals(destination)) { 1121 return; 1122 } 1123 if (destination.IsRegisterPair()) { 1124 if (source.IsRegisterPair()) { 1125 EmitParallelMoves( 1126 Location::RegisterLocation(source.AsRegisterPairHigh<Register>()), 1127 Location::RegisterLocation(destination.AsRegisterPairHigh<Register>()), 1128 Primitive::kPrimInt, 1129 Location::RegisterLocation(source.AsRegisterPairLow<Register>()), 1130 Location::RegisterLocation(destination.AsRegisterPairLow<Register>()), 1131 Primitive::kPrimInt); 1132 } else if (source.IsFpuRegister()) { 1133 UNIMPLEMENTED(FATAL); 1134 } else if (source.IsFpuRegisterPair()) { 1135 __ vmovrrd(destination.AsRegisterPairLow<Register>(), 1136 destination.AsRegisterPairHigh<Register>(), 1137 FromLowSToD(source.AsFpuRegisterPairLow<SRegister>())); 1138 } else { 1139 DCHECK(source.IsDoubleStackSlot()); 1140 DCHECK(ExpectedPairLayout(destination)); 1141 __ LoadFromOffset(kLoadWordPair, destination.AsRegisterPairLow<Register>(), 1142 SP, source.GetStackIndex()); 1143 } 1144 } else if (destination.IsFpuRegisterPair()) { 1145 if (source.IsDoubleStackSlot()) { 1146 __ LoadDFromOffset(FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()), 1147 SP, 1148 source.GetStackIndex()); 1149 } else if (source.IsRegisterPair()) { 1150 __ vmovdrr(FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()), 1151 source.AsRegisterPairLow<Register>(), 1152 source.AsRegisterPairHigh<Register>()); 1153 } else { 1154 UNIMPLEMENTED(FATAL); 1155 } 1156 } else { 1157 DCHECK(destination.IsDoubleStackSlot()); 1158 if (source.IsRegisterPair()) { 1159 // No conflict possible, so just do the moves. 1160 if (source.AsRegisterPairLow<Register>() == R1) { 1161 DCHECK_EQ(source.AsRegisterPairHigh<Register>(), R2); 1162 __ StoreToOffset(kStoreWord, R1, SP, destination.GetStackIndex()); 1163 __ StoreToOffset(kStoreWord, R2, SP, destination.GetHighStackIndex(kArmWordSize)); 1164 } else { 1165 __ StoreToOffset(kStoreWordPair, source.AsRegisterPairLow<Register>(), 1166 SP, destination.GetStackIndex()); 1167 } 1168 } else if (source.IsFpuRegisterPair()) { 1169 __ StoreDToOffset(FromLowSToD(source.AsFpuRegisterPairLow<SRegister>()), 1170 SP, 1171 destination.GetStackIndex()); 1172 } else { 1173 DCHECK(source.IsDoubleStackSlot()); 1174 EmitParallelMoves( 1175 Location::StackSlot(source.GetStackIndex()), 1176 Location::StackSlot(destination.GetStackIndex()), 1177 Primitive::kPrimInt, 1178 Location::StackSlot(source.GetHighStackIndex(kArmWordSize)), 1179 Location::StackSlot(destination.GetHighStackIndex(kArmWordSize)), 1180 Primitive::kPrimInt); 1181 } 1182 } 1183} 1184 1185void CodeGeneratorARM::MoveConstant(Location location, int32_t value) { 1186 DCHECK(location.IsRegister()); 1187 __ LoadImmediate(location.AsRegister<Register>(), value); 1188} 1189 1190void CodeGeneratorARM::MoveLocation(Location dst, Location src, Primitive::Type dst_type) { 1191 HParallelMove move(GetGraph()->GetArena()); 1192 move.AddMove(src, dst, dst_type, nullptr); 1193 GetMoveResolver()->EmitNativeCode(&move); 1194} 1195 1196void CodeGeneratorARM::AddLocationAsTemp(Location location, LocationSummary* locations) { 1197 if (location.IsRegister()) { 1198 locations->AddTemp(location); 1199 } else if (location.IsRegisterPair()) { 1200 locations->AddTemp(Location::RegisterLocation(location.AsRegisterPairLow<Register>())); 1201 locations->AddTemp(Location::RegisterLocation(location.AsRegisterPairHigh<Register>())); 1202 } else { 1203 UNIMPLEMENTED(FATAL) << "AddLocationAsTemp not implemented for location " << location; 1204 } 1205} 1206 1207void CodeGeneratorARM::InvokeRuntime(QuickEntrypointEnum entrypoint, 1208 HInstruction* instruction, 1209 uint32_t dex_pc, 1210 SlowPathCode* slow_path) { 1211 InvokeRuntime(GetThreadOffset<kArmWordSize>(entrypoint).Int32Value(), 1212 instruction, 1213 dex_pc, 1214 slow_path); 1215} 1216 1217void CodeGeneratorARM::InvokeRuntime(int32_t entry_point_offset, 1218 HInstruction* instruction, 1219 uint32_t dex_pc, 1220 SlowPathCode* slow_path) { 1221 ValidateInvokeRuntime(instruction, slow_path); 1222 __ LoadFromOffset(kLoadWord, LR, TR, entry_point_offset); 1223 __ blx(LR); 1224 RecordPcInfo(instruction, dex_pc, slow_path); 1225} 1226 1227void InstructionCodeGeneratorARM::HandleGoto(HInstruction* got, HBasicBlock* successor) { 1228 DCHECK(!successor->IsExitBlock()); 1229 1230 HBasicBlock* block = got->GetBlock(); 1231 HInstruction* previous = got->GetPrevious(); 1232 1233 HLoopInformation* info = block->GetLoopInformation(); 1234 if (info != nullptr && info->IsBackEdge(*block) && info->HasSuspendCheck()) { 1235 codegen_->ClearSpillSlotsFromLoopPhisInStackMap(info->GetSuspendCheck()); 1236 GenerateSuspendCheck(info->GetSuspendCheck(), successor); 1237 return; 1238 } 1239 1240 if (block->IsEntryBlock() && (previous != nullptr) && previous->IsSuspendCheck()) { 1241 GenerateSuspendCheck(previous->AsSuspendCheck(), nullptr); 1242 } 1243 if (!codegen_->GoesToNextBlock(got->GetBlock(), successor)) { 1244 __ b(codegen_->GetLabelOf(successor)); 1245 } 1246} 1247 1248void LocationsBuilderARM::VisitGoto(HGoto* got) { 1249 got->SetLocations(nullptr); 1250} 1251 1252void InstructionCodeGeneratorARM::VisitGoto(HGoto* got) { 1253 HandleGoto(got, got->GetSuccessor()); 1254} 1255 1256void LocationsBuilderARM::VisitTryBoundary(HTryBoundary* try_boundary) { 1257 try_boundary->SetLocations(nullptr); 1258} 1259 1260void InstructionCodeGeneratorARM::VisitTryBoundary(HTryBoundary* try_boundary) { 1261 HBasicBlock* successor = try_boundary->GetNormalFlowSuccessor(); 1262 if (!successor->IsExitBlock()) { 1263 HandleGoto(try_boundary, successor); 1264 } 1265} 1266 1267void LocationsBuilderARM::VisitExit(HExit* exit) { 1268 exit->SetLocations(nullptr); 1269} 1270 1271void InstructionCodeGeneratorARM::VisitExit(HExit* exit ATTRIBUTE_UNUSED) { 1272} 1273 1274void InstructionCodeGeneratorARM::GenerateFPJumps(HCondition* cond, 1275 Label* true_label, 1276 Label* false_label ATTRIBUTE_UNUSED) { 1277 __ vmstat(); // transfer FP status register to ARM APSR. 1278 __ b(true_label, ARMFPCondition(cond->GetCondition(), cond->IsGtBias())); 1279} 1280 1281void InstructionCodeGeneratorARM::GenerateLongComparesAndJumps(HCondition* cond, 1282 Label* true_label, 1283 Label* false_label) { 1284 LocationSummary* locations = cond->GetLocations(); 1285 Location left = locations->InAt(0); 1286 Location right = locations->InAt(1); 1287 IfCondition if_cond = cond->GetCondition(); 1288 1289 Register left_high = left.AsRegisterPairHigh<Register>(); 1290 Register left_low = left.AsRegisterPairLow<Register>(); 1291 IfCondition true_high_cond = if_cond; 1292 IfCondition false_high_cond = cond->GetOppositeCondition(); 1293 Condition final_condition = ARMUnsignedCondition(if_cond); // unsigned on lower part 1294 1295 // Set the conditions for the test, remembering that == needs to be 1296 // decided using the low words. 1297 // TODO: consider avoiding jumps with temporary and CMP low+SBC high 1298 switch (if_cond) { 1299 case kCondEQ: 1300 case kCondNE: 1301 // Nothing to do. 1302 break; 1303 case kCondLT: 1304 false_high_cond = kCondGT; 1305 break; 1306 case kCondLE: 1307 true_high_cond = kCondLT; 1308 break; 1309 case kCondGT: 1310 false_high_cond = kCondLT; 1311 break; 1312 case kCondGE: 1313 true_high_cond = kCondGT; 1314 break; 1315 case kCondB: 1316 false_high_cond = kCondA; 1317 break; 1318 case kCondBE: 1319 true_high_cond = kCondB; 1320 break; 1321 case kCondA: 1322 false_high_cond = kCondB; 1323 break; 1324 case kCondAE: 1325 true_high_cond = kCondA; 1326 break; 1327 } 1328 if (right.IsConstant()) { 1329 int64_t value = right.GetConstant()->AsLongConstant()->GetValue(); 1330 int32_t val_low = Low32Bits(value); 1331 int32_t val_high = High32Bits(value); 1332 1333 __ CmpConstant(left_high, val_high); 1334 if (if_cond == kCondNE) { 1335 __ b(true_label, ARMCondition(true_high_cond)); 1336 } else if (if_cond == kCondEQ) { 1337 __ b(false_label, ARMCondition(false_high_cond)); 1338 } else { 1339 __ b(true_label, ARMCondition(true_high_cond)); 1340 __ b(false_label, ARMCondition(false_high_cond)); 1341 } 1342 // Must be equal high, so compare the lows. 1343 __ CmpConstant(left_low, val_low); 1344 } else { 1345 Register right_high = right.AsRegisterPairHigh<Register>(); 1346 Register right_low = right.AsRegisterPairLow<Register>(); 1347 1348 __ cmp(left_high, ShifterOperand(right_high)); 1349 if (if_cond == kCondNE) { 1350 __ b(true_label, ARMCondition(true_high_cond)); 1351 } else if (if_cond == kCondEQ) { 1352 __ b(false_label, ARMCondition(false_high_cond)); 1353 } else { 1354 __ b(true_label, ARMCondition(true_high_cond)); 1355 __ b(false_label, ARMCondition(false_high_cond)); 1356 } 1357 // Must be equal high, so compare the lows. 1358 __ cmp(left_low, ShifterOperand(right_low)); 1359 } 1360 // The last comparison might be unsigned. 1361 // TODO: optimize cases where this is always true/false 1362 __ b(true_label, final_condition); 1363} 1364 1365void InstructionCodeGeneratorARM::GenerateCompareTestAndBranch(HCondition* condition, 1366 Label* true_target_in, 1367 Label* false_target_in) { 1368 // Generated branching requires both targets to be explicit. If either of the 1369 // targets is nullptr (fallthrough) use and bind `fallthrough_target` instead. 1370 Label fallthrough_target; 1371 Label* true_target = true_target_in == nullptr ? &fallthrough_target : true_target_in; 1372 Label* false_target = false_target_in == nullptr ? &fallthrough_target : false_target_in; 1373 1374 LocationSummary* locations = condition->GetLocations(); 1375 Location left = locations->InAt(0); 1376 Location right = locations->InAt(1); 1377 1378 Primitive::Type type = condition->InputAt(0)->GetType(); 1379 switch (type) { 1380 case Primitive::kPrimLong: 1381 GenerateLongComparesAndJumps(condition, true_target, false_target); 1382 break; 1383 case Primitive::kPrimFloat: 1384 __ vcmps(left.AsFpuRegister<SRegister>(), right.AsFpuRegister<SRegister>()); 1385 GenerateFPJumps(condition, true_target, false_target); 1386 break; 1387 case Primitive::kPrimDouble: 1388 __ vcmpd(FromLowSToD(left.AsFpuRegisterPairLow<SRegister>()), 1389 FromLowSToD(right.AsFpuRegisterPairLow<SRegister>())); 1390 GenerateFPJumps(condition, true_target, false_target); 1391 break; 1392 default: 1393 LOG(FATAL) << "Unexpected compare type " << type; 1394 } 1395 1396 if (false_target != &fallthrough_target) { 1397 __ b(false_target); 1398 } 1399 1400 if (fallthrough_target.IsLinked()) { 1401 __ Bind(&fallthrough_target); 1402 } 1403} 1404 1405void InstructionCodeGeneratorARM::GenerateTestAndBranch(HInstruction* instruction, 1406 size_t condition_input_index, 1407 Label* true_target, 1408 Label* false_target) { 1409 HInstruction* cond = instruction->InputAt(condition_input_index); 1410 1411 if (true_target == nullptr && false_target == nullptr) { 1412 // Nothing to do. The code always falls through. 1413 return; 1414 } else if (cond->IsIntConstant()) { 1415 // Constant condition, statically compared against 1. 1416 if (cond->AsIntConstant()->IsOne()) { 1417 if (true_target != nullptr) { 1418 __ b(true_target); 1419 } 1420 } else { 1421 DCHECK(cond->AsIntConstant()->IsZero()); 1422 if (false_target != nullptr) { 1423 __ b(false_target); 1424 } 1425 } 1426 return; 1427 } 1428 1429 // The following code generates these patterns: 1430 // (1) true_target == nullptr && false_target != nullptr 1431 // - opposite condition true => branch to false_target 1432 // (2) true_target != nullptr && false_target == nullptr 1433 // - condition true => branch to true_target 1434 // (3) true_target != nullptr && false_target != nullptr 1435 // - condition true => branch to true_target 1436 // - branch to false_target 1437 if (IsBooleanValueOrMaterializedCondition(cond)) { 1438 // Condition has been materialized, compare the output to 0. 1439 Location cond_val = instruction->GetLocations()->InAt(condition_input_index); 1440 DCHECK(cond_val.IsRegister()); 1441 if (true_target == nullptr) { 1442 __ CompareAndBranchIfZero(cond_val.AsRegister<Register>(), false_target); 1443 } else { 1444 __ CompareAndBranchIfNonZero(cond_val.AsRegister<Register>(), true_target); 1445 } 1446 } else { 1447 // Condition has not been materialized. Use its inputs as the comparison and 1448 // its condition as the branch condition. 1449 HCondition* condition = cond->AsCondition(); 1450 1451 // If this is a long or FP comparison that has been folded into 1452 // the HCondition, generate the comparison directly. 1453 Primitive::Type type = condition->InputAt(0)->GetType(); 1454 if (type == Primitive::kPrimLong || Primitive::IsFloatingPointType(type)) { 1455 GenerateCompareTestAndBranch(condition, true_target, false_target); 1456 return; 1457 } 1458 1459 LocationSummary* locations = cond->GetLocations(); 1460 DCHECK(locations->InAt(0).IsRegister()); 1461 Register left = locations->InAt(0).AsRegister<Register>(); 1462 Location right = locations->InAt(1); 1463 if (right.IsRegister()) { 1464 __ cmp(left, ShifterOperand(right.AsRegister<Register>())); 1465 } else { 1466 DCHECK(right.IsConstant()); 1467 __ CmpConstant(left, CodeGenerator::GetInt32ValueOf(right.GetConstant())); 1468 } 1469 if (true_target == nullptr) { 1470 __ b(false_target, ARMCondition(condition->GetOppositeCondition())); 1471 } else { 1472 __ b(true_target, ARMCondition(condition->GetCondition())); 1473 } 1474 } 1475 1476 // If neither branch falls through (case 3), the conditional branch to `true_target` 1477 // was already emitted (case 2) and we need to emit a jump to `false_target`. 1478 if (true_target != nullptr && false_target != nullptr) { 1479 __ b(false_target); 1480 } 1481} 1482 1483void LocationsBuilderARM::VisitIf(HIf* if_instr) { 1484 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(if_instr); 1485 if (IsBooleanValueOrMaterializedCondition(if_instr->InputAt(0))) { 1486 locations->SetInAt(0, Location::RequiresRegister()); 1487 } 1488} 1489 1490void InstructionCodeGeneratorARM::VisitIf(HIf* if_instr) { 1491 HBasicBlock* true_successor = if_instr->IfTrueSuccessor(); 1492 HBasicBlock* false_successor = if_instr->IfFalseSuccessor(); 1493 Label* true_target = codegen_->GoesToNextBlock(if_instr->GetBlock(), true_successor) ? 1494 nullptr : codegen_->GetLabelOf(true_successor); 1495 Label* false_target = codegen_->GoesToNextBlock(if_instr->GetBlock(), false_successor) ? 1496 nullptr : codegen_->GetLabelOf(false_successor); 1497 GenerateTestAndBranch(if_instr, /* condition_input_index */ 0, true_target, false_target); 1498} 1499 1500void LocationsBuilderARM::VisitDeoptimize(HDeoptimize* deoptimize) { 1501 LocationSummary* locations = new (GetGraph()->GetArena()) 1502 LocationSummary(deoptimize, LocationSummary::kCallOnSlowPath); 1503 if (IsBooleanValueOrMaterializedCondition(deoptimize->InputAt(0))) { 1504 locations->SetInAt(0, Location::RequiresRegister()); 1505 } 1506} 1507 1508void InstructionCodeGeneratorARM::VisitDeoptimize(HDeoptimize* deoptimize) { 1509 SlowPathCode* slow_path = deopt_slow_paths_.NewSlowPath<DeoptimizationSlowPathARM>(deoptimize); 1510 GenerateTestAndBranch(deoptimize, 1511 /* condition_input_index */ 0, 1512 slow_path->GetEntryLabel(), 1513 /* false_target */ nullptr); 1514} 1515 1516void LocationsBuilderARM::VisitSelect(HSelect* select) { 1517 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(select); 1518 if (Primitive::IsFloatingPointType(select->GetType())) { 1519 locations->SetInAt(0, Location::RequiresFpuRegister()); 1520 locations->SetInAt(1, Location::RequiresFpuRegister()); 1521 } else { 1522 locations->SetInAt(0, Location::RequiresRegister()); 1523 locations->SetInAt(1, Location::RequiresRegister()); 1524 } 1525 if (IsBooleanValueOrMaterializedCondition(select->GetCondition())) { 1526 locations->SetInAt(2, Location::RequiresRegister()); 1527 } 1528 locations->SetOut(Location::SameAsFirstInput()); 1529} 1530 1531void InstructionCodeGeneratorARM::VisitSelect(HSelect* select) { 1532 LocationSummary* locations = select->GetLocations(); 1533 Label false_target; 1534 GenerateTestAndBranch(select, 1535 /* condition_input_index */ 2, 1536 /* true_target */ nullptr, 1537 &false_target); 1538 codegen_->MoveLocation(locations->Out(), locations->InAt(1), select->GetType()); 1539 __ Bind(&false_target); 1540} 1541 1542void LocationsBuilderARM::VisitNativeDebugInfo(HNativeDebugInfo* info) { 1543 new (GetGraph()->GetArena()) LocationSummary(info); 1544} 1545 1546void InstructionCodeGeneratorARM::VisitNativeDebugInfo(HNativeDebugInfo* 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::kPrimByte: 3155 case Primitive::kPrimChar: 3156 case Primitive::kPrimShort: 3157 case Primitive::kPrimInt: { 3158 if (value.IsRegister()) { 3159 __ CompareAndBranchIfZero(value.AsRegister<Register>(), slow_path->GetEntryLabel()); 3160 } else { 3161 DCHECK(value.IsConstant()) << value; 3162 if (value.GetConstant()->AsIntConstant()->GetValue() == 0) { 3163 __ b(slow_path->GetEntryLabel()); 3164 } 3165 } 3166 break; 3167 } 3168 case Primitive::kPrimLong: { 3169 if (value.IsRegisterPair()) { 3170 __ orrs(IP, 3171 value.AsRegisterPairLow<Register>(), 3172 ShifterOperand(value.AsRegisterPairHigh<Register>())); 3173 __ b(slow_path->GetEntryLabel(), EQ); 3174 } else { 3175 DCHECK(value.IsConstant()) << value; 3176 if (value.GetConstant()->AsLongConstant()->GetValue() == 0) { 3177 __ b(slow_path->GetEntryLabel()); 3178 } 3179 } 3180 break; 3181 default: 3182 LOG(FATAL) << "Unexpected type for HDivZeroCheck " << instruction->GetType(); 3183 } 3184 } 3185} 3186 3187void InstructionCodeGeneratorARM::HandleIntegerRotate(LocationSummary* locations) { 3188 Register in = locations->InAt(0).AsRegister<Register>(); 3189 Location rhs = locations->InAt(1); 3190 Register out = locations->Out().AsRegister<Register>(); 3191 3192 if (rhs.IsConstant()) { 3193 // Arm32 and Thumb2 assemblers require a rotation on the interval [1,31], 3194 // so map all rotations to a +ve. equivalent in that range. 3195 // (e.g. left *or* right by -2 bits == 30 bits in the same direction.) 3196 uint32_t rot = CodeGenerator::GetInt32ValueOf(rhs.GetConstant()) & 0x1F; 3197 if (rot) { 3198 // Rotate, mapping left rotations to right equivalents if necessary. 3199 // (e.g. left by 2 bits == right by 30.) 3200 __ Ror(out, in, rot); 3201 } else if (out != in) { 3202 __ Mov(out, in); 3203 } 3204 } else { 3205 __ Ror(out, in, rhs.AsRegister<Register>()); 3206 } 3207} 3208 3209// Gain some speed by mapping all Long rotates onto equivalent pairs of Integer 3210// rotates by swapping input regs (effectively rotating by the first 32-bits of 3211// a larger rotation) or flipping direction (thus treating larger right/left 3212// rotations as sub-word sized rotations in the other direction) as appropriate. 3213void InstructionCodeGeneratorARM::HandleLongRotate(LocationSummary* locations) { 3214 Register in_reg_lo = locations->InAt(0).AsRegisterPairLow<Register>(); 3215 Register in_reg_hi = locations->InAt(0).AsRegisterPairHigh<Register>(); 3216 Location rhs = locations->InAt(1); 3217 Register out_reg_lo = locations->Out().AsRegisterPairLow<Register>(); 3218 Register out_reg_hi = locations->Out().AsRegisterPairHigh<Register>(); 3219 3220 if (rhs.IsConstant()) { 3221 uint64_t rot = CodeGenerator::GetInt64ValueOf(rhs.GetConstant()); 3222 // Map all rotations to +ve. equivalents on the interval [0,63]. 3223 rot &= kMaxLongShiftValue; 3224 // For rotates over a word in size, 'pre-rotate' by 32-bits to keep rotate 3225 // logic below to a simple pair of binary orr. 3226 // (e.g. 34 bits == in_reg swap + 2 bits right.) 3227 if (rot >= kArmBitsPerWord) { 3228 rot -= kArmBitsPerWord; 3229 std::swap(in_reg_hi, in_reg_lo); 3230 } 3231 // Rotate, or mov to out for zero or word size rotations. 3232 if (rot != 0u) { 3233 __ Lsr(out_reg_hi, in_reg_hi, rot); 3234 __ orr(out_reg_hi, out_reg_hi, ShifterOperand(in_reg_lo, arm::LSL, kArmBitsPerWord - rot)); 3235 __ Lsr(out_reg_lo, in_reg_lo, rot); 3236 __ orr(out_reg_lo, out_reg_lo, ShifterOperand(in_reg_hi, arm::LSL, kArmBitsPerWord - rot)); 3237 } else { 3238 __ Mov(out_reg_lo, in_reg_lo); 3239 __ Mov(out_reg_hi, in_reg_hi); 3240 } 3241 } else { 3242 Register shift_right = locations->GetTemp(0).AsRegister<Register>(); 3243 Register shift_left = locations->GetTemp(1).AsRegister<Register>(); 3244 Label end; 3245 Label shift_by_32_plus_shift_right; 3246 3247 __ and_(shift_right, rhs.AsRegister<Register>(), ShifterOperand(0x1F)); 3248 __ Lsrs(shift_left, rhs.AsRegister<Register>(), 6); 3249 __ rsb(shift_left, shift_right, ShifterOperand(kArmBitsPerWord), AL, kCcKeep); 3250 __ b(&shift_by_32_plus_shift_right, CC); 3251 3252 // out_reg_hi = (reg_hi << shift_left) | (reg_lo >> shift_right). 3253 // out_reg_lo = (reg_lo << shift_left) | (reg_hi >> shift_right). 3254 __ Lsl(out_reg_hi, in_reg_hi, shift_left); 3255 __ Lsr(out_reg_lo, in_reg_lo, shift_right); 3256 __ add(out_reg_hi, out_reg_hi, ShifterOperand(out_reg_lo)); 3257 __ Lsl(out_reg_lo, in_reg_lo, shift_left); 3258 __ Lsr(shift_left, in_reg_hi, shift_right); 3259 __ add(out_reg_lo, out_reg_lo, ShifterOperand(shift_left)); 3260 __ b(&end); 3261 3262 __ Bind(&shift_by_32_plus_shift_right); // Shift by 32+shift_right. 3263 // out_reg_hi = (reg_hi >> shift_right) | (reg_lo << shift_left). 3264 // out_reg_lo = (reg_lo >> shift_right) | (reg_hi << shift_left). 3265 __ Lsr(out_reg_hi, in_reg_hi, shift_right); 3266 __ Lsl(out_reg_lo, in_reg_lo, shift_left); 3267 __ add(out_reg_hi, out_reg_hi, ShifterOperand(out_reg_lo)); 3268 __ Lsr(out_reg_lo, in_reg_lo, shift_right); 3269 __ Lsl(shift_right, in_reg_hi, shift_left); 3270 __ add(out_reg_lo, out_reg_lo, ShifterOperand(shift_right)); 3271 3272 __ Bind(&end); 3273 } 3274} 3275void LocationsBuilderARM::HandleRotate(HRor* ror) { 3276 LocationSummary* locations = 3277 new (GetGraph()->GetArena()) LocationSummary(ror, LocationSummary::kNoCall); 3278 switch (ror->GetResultType()) { 3279 case Primitive::kPrimInt: { 3280 locations->SetInAt(0, Location::RequiresRegister()); 3281 locations->SetInAt(1, Location::RegisterOrConstant(ror->InputAt(1))); 3282 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 3283 break; 3284 } 3285 case Primitive::kPrimLong: { 3286 locations->SetInAt(0, Location::RequiresRegister()); 3287 if (ror->InputAt(1)->IsConstant()) { 3288 locations->SetInAt(1, Location::ConstantLocation(ror->InputAt(1)->AsConstant())); 3289 } else { 3290 locations->SetInAt(1, Location::RequiresRegister()); 3291 locations->AddTemp(Location::RequiresRegister()); 3292 locations->AddTemp(Location::RequiresRegister()); 3293 } 3294 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); 3295 break; 3296 } 3297 default: 3298 LOG(FATAL) << "Unexpected operation type " << ror->GetResultType(); 3299 } 3300} 3301 3302void InstructionCodeGeneratorARM::HandleRotate(HRor* ror) { 3303 LocationSummary* locations = ror->GetLocations(); 3304 Primitive::Type type = ror->GetResultType(); 3305 switch (type) { 3306 case Primitive::kPrimInt: { 3307 HandleIntegerRotate(locations); 3308 break; 3309 } 3310 case Primitive::kPrimLong: { 3311 HandleLongRotate(locations); 3312 break; 3313 } 3314 default: 3315 LOG(FATAL) << "Unexpected operation type " << type; 3316 UNREACHABLE(); 3317 } 3318} 3319 3320void LocationsBuilderARM::VisitRor(HRor* op) { 3321 HandleRotate(op); 3322} 3323 3324void InstructionCodeGeneratorARM::VisitRor(HRor* op) { 3325 HandleRotate(op); 3326} 3327 3328void LocationsBuilderARM::HandleShift(HBinaryOperation* op) { 3329 DCHECK(op->IsShl() || op->IsShr() || op->IsUShr()); 3330 3331 LocationSummary* locations = 3332 new (GetGraph()->GetArena()) LocationSummary(op, LocationSummary::kNoCall); 3333 3334 switch (op->GetResultType()) { 3335 case Primitive::kPrimInt: { 3336 locations->SetInAt(0, Location::RequiresRegister()); 3337 if (op->InputAt(1)->IsConstant()) { 3338 locations->SetInAt(1, Location::ConstantLocation(op->InputAt(1)->AsConstant())); 3339 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 3340 } else { 3341 locations->SetInAt(1, Location::RequiresRegister()); 3342 // Make the output overlap, as it will be used to hold the masked 3343 // second input. 3344 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); 3345 } 3346 break; 3347 } 3348 case Primitive::kPrimLong: { 3349 locations->SetInAt(0, Location::RequiresRegister()); 3350 if (op->InputAt(1)->IsConstant()) { 3351 locations->SetInAt(1, Location::ConstantLocation(op->InputAt(1)->AsConstant())); 3352 // For simplicity, use kOutputOverlap even though we only require that low registers 3353 // don't clash with high registers which the register allocator currently guarantees. 3354 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); 3355 } else { 3356 locations->SetInAt(1, Location::RequiresRegister()); 3357 locations->AddTemp(Location::RequiresRegister()); 3358 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); 3359 } 3360 break; 3361 } 3362 default: 3363 LOG(FATAL) << "Unexpected operation type " << op->GetResultType(); 3364 } 3365} 3366 3367void InstructionCodeGeneratorARM::HandleShift(HBinaryOperation* op) { 3368 DCHECK(op->IsShl() || op->IsShr() || op->IsUShr()); 3369 3370 LocationSummary* locations = op->GetLocations(); 3371 Location out = locations->Out(); 3372 Location first = locations->InAt(0); 3373 Location second = locations->InAt(1); 3374 3375 Primitive::Type type = op->GetResultType(); 3376 switch (type) { 3377 case Primitive::kPrimInt: { 3378 Register out_reg = out.AsRegister<Register>(); 3379 Register first_reg = first.AsRegister<Register>(); 3380 if (second.IsRegister()) { 3381 Register second_reg = second.AsRegister<Register>(); 3382 // ARM doesn't mask the shift count so we need to do it ourselves. 3383 __ and_(out_reg, second_reg, ShifterOperand(kMaxIntShiftValue)); 3384 if (op->IsShl()) { 3385 __ Lsl(out_reg, first_reg, out_reg); 3386 } else if (op->IsShr()) { 3387 __ Asr(out_reg, first_reg, out_reg); 3388 } else { 3389 __ Lsr(out_reg, first_reg, out_reg); 3390 } 3391 } else { 3392 int32_t cst = second.GetConstant()->AsIntConstant()->GetValue(); 3393 uint32_t shift_value = static_cast<uint32_t>(cst & kMaxIntShiftValue); 3394 if (shift_value == 0) { // ARM does not support shifting with 0 immediate. 3395 __ Mov(out_reg, first_reg); 3396 } else if (op->IsShl()) { 3397 __ Lsl(out_reg, first_reg, shift_value); 3398 } else if (op->IsShr()) { 3399 __ Asr(out_reg, first_reg, shift_value); 3400 } else { 3401 __ Lsr(out_reg, first_reg, shift_value); 3402 } 3403 } 3404 break; 3405 } 3406 case Primitive::kPrimLong: { 3407 Register o_h = out.AsRegisterPairHigh<Register>(); 3408 Register o_l = out.AsRegisterPairLow<Register>(); 3409 3410 Register high = first.AsRegisterPairHigh<Register>(); 3411 Register low = first.AsRegisterPairLow<Register>(); 3412 3413 if (second.IsRegister()) { 3414 Register temp = locations->GetTemp(0).AsRegister<Register>(); 3415 3416 Register second_reg = second.AsRegister<Register>(); 3417 3418 if (op->IsShl()) { 3419 __ and_(o_l, second_reg, ShifterOperand(kMaxLongShiftValue)); 3420 // Shift the high part 3421 __ Lsl(o_h, high, o_l); 3422 // Shift the low part and `or` what overflew on the high part 3423 __ rsb(temp, o_l, ShifterOperand(kArmBitsPerWord)); 3424 __ Lsr(temp, low, temp); 3425 __ orr(o_h, o_h, ShifterOperand(temp)); 3426 // If the shift is > 32 bits, override the high part 3427 __ subs(temp, o_l, ShifterOperand(kArmBitsPerWord)); 3428 __ it(PL); 3429 __ Lsl(o_h, low, temp, PL); 3430 // Shift the low part 3431 __ Lsl(o_l, low, o_l); 3432 } else if (op->IsShr()) { 3433 __ and_(o_h, second_reg, ShifterOperand(kMaxLongShiftValue)); 3434 // Shift the low part 3435 __ Lsr(o_l, low, o_h); 3436 // Shift the high part and `or` what underflew on the low part 3437 __ rsb(temp, o_h, ShifterOperand(kArmBitsPerWord)); 3438 __ Lsl(temp, high, temp); 3439 __ orr(o_l, o_l, ShifterOperand(temp)); 3440 // If the shift is > 32 bits, override the low part 3441 __ subs(temp, o_h, ShifterOperand(kArmBitsPerWord)); 3442 __ it(PL); 3443 __ Asr(o_l, high, temp, PL); 3444 // Shift the high part 3445 __ Asr(o_h, high, o_h); 3446 } else { 3447 __ and_(o_h, second_reg, ShifterOperand(kMaxLongShiftValue)); 3448 // same as Shr except we use `Lsr`s and not `Asr`s 3449 __ Lsr(o_l, low, o_h); 3450 __ rsb(temp, o_h, ShifterOperand(kArmBitsPerWord)); 3451 __ Lsl(temp, high, temp); 3452 __ orr(o_l, o_l, ShifterOperand(temp)); 3453 __ subs(temp, o_h, ShifterOperand(kArmBitsPerWord)); 3454 __ it(PL); 3455 __ Lsr(o_l, high, temp, PL); 3456 __ Lsr(o_h, high, o_h); 3457 } 3458 } else { 3459 // Register allocator doesn't create partial overlap. 3460 DCHECK_NE(o_l, high); 3461 DCHECK_NE(o_h, low); 3462 int32_t cst = second.GetConstant()->AsIntConstant()->GetValue(); 3463 uint32_t shift_value = static_cast<uint32_t>(cst & kMaxLongShiftValue); 3464 if (shift_value > 32) { 3465 if (op->IsShl()) { 3466 __ Lsl(o_h, low, shift_value - 32); 3467 __ LoadImmediate(o_l, 0); 3468 } else if (op->IsShr()) { 3469 __ Asr(o_l, high, shift_value - 32); 3470 __ Asr(o_h, high, 31); 3471 } else { 3472 __ Lsr(o_l, high, shift_value - 32); 3473 __ LoadImmediate(o_h, 0); 3474 } 3475 } else if (shift_value == 32) { 3476 if (op->IsShl()) { 3477 __ mov(o_h, ShifterOperand(low)); 3478 __ LoadImmediate(o_l, 0); 3479 } else if (op->IsShr()) { 3480 __ mov(o_l, ShifterOperand(high)); 3481 __ Asr(o_h, high, 31); 3482 } else { 3483 __ mov(o_l, ShifterOperand(high)); 3484 __ LoadImmediate(o_h, 0); 3485 } 3486 } else if (shift_value == 1) { 3487 if (op->IsShl()) { 3488 __ Lsls(o_l, low, 1); 3489 __ adc(o_h, high, ShifterOperand(high)); 3490 } else if (op->IsShr()) { 3491 __ Asrs(o_h, high, 1); 3492 __ Rrx(o_l, low); 3493 } else { 3494 __ Lsrs(o_h, high, 1); 3495 __ Rrx(o_l, low); 3496 } 3497 } else { 3498 DCHECK(2 <= shift_value && shift_value < 32) << shift_value; 3499 if (op->IsShl()) { 3500 __ Lsl(o_h, high, shift_value); 3501 __ orr(o_h, o_h, ShifterOperand(low, LSR, 32 - shift_value)); 3502 __ Lsl(o_l, low, shift_value); 3503 } else if (op->IsShr()) { 3504 __ Lsr(o_l, low, shift_value); 3505 __ orr(o_l, o_l, ShifterOperand(high, LSL, 32 - shift_value)); 3506 __ Asr(o_h, high, shift_value); 3507 } else { 3508 __ Lsr(o_l, low, shift_value); 3509 __ orr(o_l, o_l, ShifterOperand(high, LSL, 32 - shift_value)); 3510 __ Lsr(o_h, high, shift_value); 3511 } 3512 } 3513 } 3514 break; 3515 } 3516 default: 3517 LOG(FATAL) << "Unexpected operation type " << type; 3518 UNREACHABLE(); 3519 } 3520} 3521 3522void LocationsBuilderARM::VisitShl(HShl* shl) { 3523 HandleShift(shl); 3524} 3525 3526void InstructionCodeGeneratorARM::VisitShl(HShl* shl) { 3527 HandleShift(shl); 3528} 3529 3530void LocationsBuilderARM::VisitShr(HShr* shr) { 3531 HandleShift(shr); 3532} 3533 3534void InstructionCodeGeneratorARM::VisitShr(HShr* shr) { 3535 HandleShift(shr); 3536} 3537 3538void LocationsBuilderARM::VisitUShr(HUShr* ushr) { 3539 HandleShift(ushr); 3540} 3541 3542void InstructionCodeGeneratorARM::VisitUShr(HUShr* ushr) { 3543 HandleShift(ushr); 3544} 3545 3546void LocationsBuilderARM::VisitNewInstance(HNewInstance* instruction) { 3547 LocationSummary* locations = 3548 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall); 3549 if (instruction->IsStringAlloc()) { 3550 locations->AddTemp(Location::RegisterLocation(kMethodRegisterArgument)); 3551 } else { 3552 InvokeRuntimeCallingConvention calling_convention; 3553 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); 3554 locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); 3555 } 3556 locations->SetOut(Location::RegisterLocation(R0)); 3557} 3558 3559void InstructionCodeGeneratorARM::VisitNewInstance(HNewInstance* instruction) { 3560 // Note: if heap poisoning is enabled, the entry point takes cares 3561 // of poisoning the reference. 3562 if (instruction->IsStringAlloc()) { 3563 // String is allocated through StringFactory. Call NewEmptyString entry point. 3564 Register temp = instruction->GetLocations()->GetTemp(0).AsRegister<Register>(); 3565 MemberOffset code_offset = ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmWordSize); 3566 __ LoadFromOffset(kLoadWord, temp, TR, QUICK_ENTRY_POINT(pNewEmptyString)); 3567 __ LoadFromOffset(kLoadWord, LR, temp, code_offset.Int32Value()); 3568 __ blx(LR); 3569 codegen_->RecordPcInfo(instruction, instruction->GetDexPc()); 3570 } else { 3571 codegen_->InvokeRuntime(instruction->GetEntrypoint(), 3572 instruction, 3573 instruction->GetDexPc(), 3574 nullptr); 3575 CheckEntrypointTypes<kQuickAllocObjectWithAccessCheck, void*, uint32_t, ArtMethod*>(); 3576 } 3577} 3578 3579void LocationsBuilderARM::VisitNewArray(HNewArray* instruction) { 3580 LocationSummary* locations = 3581 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall); 3582 InvokeRuntimeCallingConvention calling_convention; 3583 locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0))); 3584 locations->SetOut(Location::RegisterLocation(R0)); 3585 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); 3586 locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(2))); 3587} 3588 3589void InstructionCodeGeneratorARM::VisitNewArray(HNewArray* instruction) { 3590 InvokeRuntimeCallingConvention calling_convention; 3591 __ LoadImmediate(calling_convention.GetRegisterAt(0), instruction->GetTypeIndex()); 3592 // Note: if heap poisoning is enabled, the entry point takes cares 3593 // of poisoning the reference. 3594 codegen_->InvokeRuntime(instruction->GetEntrypoint(), 3595 instruction, 3596 instruction->GetDexPc(), 3597 nullptr); 3598 CheckEntrypointTypes<kQuickAllocArrayWithAccessCheck, void*, uint32_t, int32_t, ArtMethod*>(); 3599} 3600 3601void LocationsBuilderARM::VisitParameterValue(HParameterValue* instruction) { 3602 LocationSummary* locations = 3603 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); 3604 Location location = parameter_visitor_.GetNextLocation(instruction->GetType()); 3605 if (location.IsStackSlot()) { 3606 location = Location::StackSlot(location.GetStackIndex() + codegen_->GetFrameSize()); 3607 } else if (location.IsDoubleStackSlot()) { 3608 location = Location::DoubleStackSlot(location.GetStackIndex() + codegen_->GetFrameSize()); 3609 } 3610 locations->SetOut(location); 3611} 3612 3613void InstructionCodeGeneratorARM::VisitParameterValue( 3614 HParameterValue* instruction ATTRIBUTE_UNUSED) { 3615 // Nothing to do, the parameter is already at its location. 3616} 3617 3618void LocationsBuilderARM::VisitCurrentMethod(HCurrentMethod* instruction) { 3619 LocationSummary* locations = 3620 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); 3621 locations->SetOut(Location::RegisterLocation(kMethodRegisterArgument)); 3622} 3623 3624void InstructionCodeGeneratorARM::VisitCurrentMethod(HCurrentMethod* instruction ATTRIBUTE_UNUSED) { 3625 // Nothing to do, the method is already at its location. 3626} 3627 3628void LocationsBuilderARM::VisitNot(HNot* not_) { 3629 LocationSummary* locations = 3630 new (GetGraph()->GetArena()) LocationSummary(not_, LocationSummary::kNoCall); 3631 locations->SetInAt(0, Location::RequiresRegister()); 3632 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 3633} 3634 3635void InstructionCodeGeneratorARM::VisitNot(HNot* not_) { 3636 LocationSummary* locations = not_->GetLocations(); 3637 Location out = locations->Out(); 3638 Location in = locations->InAt(0); 3639 switch (not_->GetResultType()) { 3640 case Primitive::kPrimInt: 3641 __ mvn(out.AsRegister<Register>(), ShifterOperand(in.AsRegister<Register>())); 3642 break; 3643 3644 case Primitive::kPrimLong: 3645 __ mvn(out.AsRegisterPairLow<Register>(), 3646 ShifterOperand(in.AsRegisterPairLow<Register>())); 3647 __ mvn(out.AsRegisterPairHigh<Register>(), 3648 ShifterOperand(in.AsRegisterPairHigh<Register>())); 3649 break; 3650 3651 default: 3652 LOG(FATAL) << "Unimplemented type for not operation " << not_->GetResultType(); 3653 } 3654} 3655 3656void LocationsBuilderARM::VisitBooleanNot(HBooleanNot* bool_not) { 3657 LocationSummary* locations = 3658 new (GetGraph()->GetArena()) LocationSummary(bool_not, LocationSummary::kNoCall); 3659 locations->SetInAt(0, Location::RequiresRegister()); 3660 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 3661} 3662 3663void InstructionCodeGeneratorARM::VisitBooleanNot(HBooleanNot* bool_not) { 3664 LocationSummary* locations = bool_not->GetLocations(); 3665 Location out = locations->Out(); 3666 Location in = locations->InAt(0); 3667 __ eor(out.AsRegister<Register>(), in.AsRegister<Register>(), ShifterOperand(1)); 3668} 3669 3670void LocationsBuilderARM::VisitCompare(HCompare* compare) { 3671 LocationSummary* locations = 3672 new (GetGraph()->GetArena()) LocationSummary(compare, LocationSummary::kNoCall); 3673 switch (compare->InputAt(0)->GetType()) { 3674 case Primitive::kPrimInt: 3675 case Primitive::kPrimLong: { 3676 locations->SetInAt(0, Location::RequiresRegister()); 3677 locations->SetInAt(1, Location::RequiresRegister()); 3678 // Output overlaps because it is written before doing the low comparison. 3679 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); 3680 break; 3681 } 3682 case Primitive::kPrimFloat: 3683 case Primitive::kPrimDouble: { 3684 locations->SetInAt(0, Location::RequiresFpuRegister()); 3685 locations->SetInAt(1, Location::RequiresFpuRegister()); 3686 locations->SetOut(Location::RequiresRegister()); 3687 break; 3688 } 3689 default: 3690 LOG(FATAL) << "Unexpected type for compare operation " << compare->InputAt(0)->GetType(); 3691 } 3692} 3693 3694void InstructionCodeGeneratorARM::VisitCompare(HCompare* compare) { 3695 LocationSummary* locations = compare->GetLocations(); 3696 Register out = locations->Out().AsRegister<Register>(); 3697 Location left = locations->InAt(0); 3698 Location right = locations->InAt(1); 3699 3700 Label less, greater, done; 3701 Primitive::Type type = compare->InputAt(0)->GetType(); 3702 Condition less_cond; 3703 switch (type) { 3704 case Primitive::kPrimInt: { 3705 __ LoadImmediate(out, 0); 3706 __ cmp(left.AsRegister<Register>(), 3707 ShifterOperand(right.AsRegister<Register>())); // Signed compare. 3708 less_cond = LT; 3709 break; 3710 } 3711 case Primitive::kPrimLong: { 3712 __ cmp(left.AsRegisterPairHigh<Register>(), 3713 ShifterOperand(right.AsRegisterPairHigh<Register>())); // Signed compare. 3714 __ b(&less, LT); 3715 __ b(&greater, GT); 3716 // Do LoadImmediate before the last `cmp`, as LoadImmediate might affect the status flags. 3717 __ LoadImmediate(out, 0); 3718 __ cmp(left.AsRegisterPairLow<Register>(), 3719 ShifterOperand(right.AsRegisterPairLow<Register>())); // Unsigned compare. 3720 less_cond = LO; 3721 break; 3722 } 3723 case Primitive::kPrimFloat: 3724 case Primitive::kPrimDouble: { 3725 __ LoadImmediate(out, 0); 3726 if (type == Primitive::kPrimFloat) { 3727 __ vcmps(left.AsFpuRegister<SRegister>(), right.AsFpuRegister<SRegister>()); 3728 } else { 3729 __ vcmpd(FromLowSToD(left.AsFpuRegisterPairLow<SRegister>()), 3730 FromLowSToD(right.AsFpuRegisterPairLow<SRegister>())); 3731 } 3732 __ vmstat(); // transfer FP status register to ARM APSR. 3733 less_cond = ARMFPCondition(kCondLT, compare->IsGtBias()); 3734 break; 3735 } 3736 default: 3737 LOG(FATAL) << "Unexpected compare type " << type; 3738 UNREACHABLE(); 3739 } 3740 3741 __ b(&done, EQ); 3742 __ b(&less, less_cond); 3743 3744 __ Bind(&greater); 3745 __ LoadImmediate(out, 1); 3746 __ b(&done); 3747 3748 __ Bind(&less); 3749 __ LoadImmediate(out, -1); 3750 3751 __ Bind(&done); 3752} 3753 3754void LocationsBuilderARM::VisitPhi(HPhi* instruction) { 3755 LocationSummary* locations = 3756 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); 3757 for (size_t i = 0, e = instruction->InputCount(); i < e; ++i) { 3758 locations->SetInAt(i, Location::Any()); 3759 } 3760 locations->SetOut(Location::Any()); 3761} 3762 3763void InstructionCodeGeneratorARM::VisitPhi(HPhi* instruction ATTRIBUTE_UNUSED) { 3764 LOG(FATAL) << "Unreachable"; 3765} 3766 3767void CodeGeneratorARM::GenerateMemoryBarrier(MemBarrierKind kind) { 3768 // TODO (ported from quick): revisit ARM barrier kinds. 3769 DmbOptions flavor = DmbOptions::ISH; // Quiet C++ warnings. 3770 switch (kind) { 3771 case MemBarrierKind::kAnyStore: 3772 case MemBarrierKind::kLoadAny: 3773 case MemBarrierKind::kAnyAny: { 3774 flavor = DmbOptions::ISH; 3775 break; 3776 } 3777 case MemBarrierKind::kStoreStore: { 3778 flavor = DmbOptions::ISHST; 3779 break; 3780 } 3781 default: 3782 LOG(FATAL) << "Unexpected memory barrier " << kind; 3783 } 3784 __ dmb(flavor); 3785} 3786 3787void InstructionCodeGeneratorARM::GenerateWideAtomicLoad(Register addr, 3788 uint32_t offset, 3789 Register out_lo, 3790 Register out_hi) { 3791 if (offset != 0) { 3792 // Ensure `out_lo` is different from `addr`, so that loading 3793 // `offset` into `out_lo` does not clutter `addr`. 3794 DCHECK_NE(out_lo, addr); 3795 __ LoadImmediate(out_lo, offset); 3796 __ add(IP, addr, ShifterOperand(out_lo)); 3797 addr = IP; 3798 } 3799 __ ldrexd(out_lo, out_hi, addr); 3800} 3801 3802void InstructionCodeGeneratorARM::GenerateWideAtomicStore(Register addr, 3803 uint32_t offset, 3804 Register value_lo, 3805 Register value_hi, 3806 Register temp1, 3807 Register temp2, 3808 HInstruction* instruction) { 3809 Label fail; 3810 if (offset != 0) { 3811 __ LoadImmediate(temp1, offset); 3812 __ add(IP, addr, ShifterOperand(temp1)); 3813 addr = IP; 3814 } 3815 __ Bind(&fail); 3816 // We need a load followed by store. (The address used in a STREX instruction must 3817 // be the same as the address in the most recently executed LDREX instruction.) 3818 __ ldrexd(temp1, temp2, addr); 3819 codegen_->MaybeRecordImplicitNullCheck(instruction); 3820 __ strexd(temp1, value_lo, value_hi, addr); 3821 __ CompareAndBranchIfNonZero(temp1, &fail); 3822} 3823 3824void LocationsBuilderARM::HandleFieldSet(HInstruction* instruction, const FieldInfo& field_info) { 3825 DCHECK(instruction->IsInstanceFieldSet() || instruction->IsStaticFieldSet()); 3826 3827 LocationSummary* locations = 3828 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); 3829 locations->SetInAt(0, Location::RequiresRegister()); 3830 3831 Primitive::Type field_type = field_info.GetFieldType(); 3832 if (Primitive::IsFloatingPointType(field_type)) { 3833 locations->SetInAt(1, Location::RequiresFpuRegister()); 3834 } else { 3835 locations->SetInAt(1, Location::RequiresRegister()); 3836 } 3837 3838 bool is_wide = field_type == Primitive::kPrimLong || field_type == Primitive::kPrimDouble; 3839 bool generate_volatile = field_info.IsVolatile() 3840 && is_wide 3841 && !codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd(); 3842 bool needs_write_barrier = 3843 CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1)); 3844 // Temporary registers for the write barrier. 3845 // TODO: consider renaming StoreNeedsWriteBarrier to StoreNeedsGCMark. 3846 if (needs_write_barrier) { 3847 locations->AddTemp(Location::RequiresRegister()); // Possibly used for reference poisoning too. 3848 locations->AddTemp(Location::RequiresRegister()); 3849 } else if (generate_volatile) { 3850 // ARM encoding have some additional constraints for ldrexd/strexd: 3851 // - registers need to be consecutive 3852 // - the first register should be even but not R14. 3853 // We don't test for ARM yet, and the assertion makes sure that we 3854 // revisit this if we ever enable ARM encoding. 3855 DCHECK_EQ(InstructionSet::kThumb2, codegen_->GetInstructionSet()); 3856 3857 locations->AddTemp(Location::RequiresRegister()); 3858 locations->AddTemp(Location::RequiresRegister()); 3859 if (field_type == Primitive::kPrimDouble) { 3860 // For doubles we need two more registers to copy the value. 3861 locations->AddTemp(Location::RegisterLocation(R2)); 3862 locations->AddTemp(Location::RegisterLocation(R3)); 3863 } 3864 } 3865} 3866 3867void InstructionCodeGeneratorARM::HandleFieldSet(HInstruction* instruction, 3868 const FieldInfo& field_info, 3869 bool value_can_be_null) { 3870 DCHECK(instruction->IsInstanceFieldSet() || instruction->IsStaticFieldSet()); 3871 3872 LocationSummary* locations = instruction->GetLocations(); 3873 Register base = locations->InAt(0).AsRegister<Register>(); 3874 Location value = locations->InAt(1); 3875 3876 bool is_volatile = field_info.IsVolatile(); 3877 bool atomic_ldrd_strd = codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd(); 3878 Primitive::Type field_type = field_info.GetFieldType(); 3879 uint32_t offset = field_info.GetFieldOffset().Uint32Value(); 3880 bool needs_write_barrier = 3881 CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1)); 3882 3883 if (is_volatile) { 3884 codegen_->GenerateMemoryBarrier(MemBarrierKind::kAnyStore); 3885 } 3886 3887 switch (field_type) { 3888 case Primitive::kPrimBoolean: 3889 case Primitive::kPrimByte: { 3890 __ StoreToOffset(kStoreByte, value.AsRegister<Register>(), base, offset); 3891 break; 3892 } 3893 3894 case Primitive::kPrimShort: 3895 case Primitive::kPrimChar: { 3896 __ StoreToOffset(kStoreHalfword, value.AsRegister<Register>(), base, offset); 3897 break; 3898 } 3899 3900 case Primitive::kPrimInt: 3901 case Primitive::kPrimNot: { 3902 if (kPoisonHeapReferences && needs_write_barrier) { 3903 // Note that in the case where `value` is a null reference, 3904 // we do not enter this block, as a null reference does not 3905 // need poisoning. 3906 DCHECK_EQ(field_type, Primitive::kPrimNot); 3907 Register temp = locations->GetTemp(0).AsRegister<Register>(); 3908 __ Mov(temp, value.AsRegister<Register>()); 3909 __ PoisonHeapReference(temp); 3910 __ StoreToOffset(kStoreWord, temp, base, offset); 3911 } else { 3912 __ StoreToOffset(kStoreWord, value.AsRegister<Register>(), base, offset); 3913 } 3914 break; 3915 } 3916 3917 case Primitive::kPrimLong: { 3918 if (is_volatile && !atomic_ldrd_strd) { 3919 GenerateWideAtomicStore(base, offset, 3920 value.AsRegisterPairLow<Register>(), 3921 value.AsRegisterPairHigh<Register>(), 3922 locations->GetTemp(0).AsRegister<Register>(), 3923 locations->GetTemp(1).AsRegister<Register>(), 3924 instruction); 3925 } else { 3926 __ StoreToOffset(kStoreWordPair, value.AsRegisterPairLow<Register>(), base, offset); 3927 codegen_->MaybeRecordImplicitNullCheck(instruction); 3928 } 3929 break; 3930 } 3931 3932 case Primitive::kPrimFloat: { 3933 __ StoreSToOffset(value.AsFpuRegister<SRegister>(), base, offset); 3934 break; 3935 } 3936 3937 case Primitive::kPrimDouble: { 3938 DRegister value_reg = FromLowSToD(value.AsFpuRegisterPairLow<SRegister>()); 3939 if (is_volatile && !atomic_ldrd_strd) { 3940 Register value_reg_lo = locations->GetTemp(0).AsRegister<Register>(); 3941 Register value_reg_hi = locations->GetTemp(1).AsRegister<Register>(); 3942 3943 __ vmovrrd(value_reg_lo, value_reg_hi, value_reg); 3944 3945 GenerateWideAtomicStore(base, offset, 3946 value_reg_lo, 3947 value_reg_hi, 3948 locations->GetTemp(2).AsRegister<Register>(), 3949 locations->GetTemp(3).AsRegister<Register>(), 3950 instruction); 3951 } else { 3952 __ StoreDToOffset(value_reg, base, offset); 3953 codegen_->MaybeRecordImplicitNullCheck(instruction); 3954 } 3955 break; 3956 } 3957 3958 case Primitive::kPrimVoid: 3959 LOG(FATAL) << "Unreachable type " << field_type; 3960 UNREACHABLE(); 3961 } 3962 3963 // Longs and doubles are handled in the switch. 3964 if (field_type != Primitive::kPrimLong && field_type != Primitive::kPrimDouble) { 3965 codegen_->MaybeRecordImplicitNullCheck(instruction); 3966 } 3967 3968 if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1))) { 3969 Register temp = locations->GetTemp(0).AsRegister<Register>(); 3970 Register card = locations->GetTemp(1).AsRegister<Register>(); 3971 codegen_->MarkGCCard( 3972 temp, card, base, value.AsRegister<Register>(), value_can_be_null); 3973 } 3974 3975 if (is_volatile) { 3976 codegen_->GenerateMemoryBarrier(MemBarrierKind::kAnyAny); 3977 } 3978} 3979 3980void LocationsBuilderARM::HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info) { 3981 DCHECK(instruction->IsInstanceFieldGet() || instruction->IsStaticFieldGet()); 3982 3983 bool object_field_get_with_read_barrier = 3984 kEmitCompilerReadBarrier && (field_info.GetFieldType() == Primitive::kPrimNot); 3985 LocationSummary* locations = 3986 new (GetGraph()->GetArena()) LocationSummary(instruction, 3987 object_field_get_with_read_barrier ? 3988 LocationSummary::kCallOnSlowPath : 3989 LocationSummary::kNoCall); 3990 locations->SetInAt(0, Location::RequiresRegister()); 3991 3992 bool volatile_for_double = field_info.IsVolatile() 3993 && (field_info.GetFieldType() == Primitive::kPrimDouble) 3994 && !codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd(); 3995 // The output overlaps in case of volatile long: we don't want the 3996 // code generated by GenerateWideAtomicLoad to overwrite the 3997 // object's location. Likewise, in the case of an object field get 3998 // with read barriers enabled, we do not want the load to overwrite 3999 // the object's location, as we need it to emit the read barrier. 4000 bool overlap = (field_info.IsVolatile() && (field_info.GetFieldType() == Primitive::kPrimLong)) || 4001 object_field_get_with_read_barrier; 4002 4003 if (Primitive::IsFloatingPointType(instruction->GetType())) { 4004 locations->SetOut(Location::RequiresFpuRegister()); 4005 } else { 4006 locations->SetOut(Location::RequiresRegister(), 4007 (overlap ? Location::kOutputOverlap : Location::kNoOutputOverlap)); 4008 } 4009 if (volatile_for_double) { 4010 // ARM encoding have some additional constraints for ldrexd/strexd: 4011 // - registers need to be consecutive 4012 // - the first register should be even but not R14. 4013 // We don't test for ARM yet, and the assertion makes sure that we 4014 // revisit this if we ever enable ARM encoding. 4015 DCHECK_EQ(InstructionSet::kThumb2, codegen_->GetInstructionSet()); 4016 locations->AddTemp(Location::RequiresRegister()); 4017 locations->AddTemp(Location::RequiresRegister()); 4018 } else if (object_field_get_with_read_barrier && kUseBakerReadBarrier) { 4019 // We need a temporary register for the read barrier marking slow 4020 // path in CodeGeneratorARM::GenerateFieldLoadWithBakerReadBarrier. 4021 locations->AddTemp(Location::RequiresRegister()); 4022 } 4023} 4024 4025Location LocationsBuilderARM::ArmEncodableConstantOrRegister(HInstruction* constant, 4026 Opcode opcode) { 4027 DCHECK(!Primitive::IsFloatingPointType(constant->GetType())); 4028 if (constant->IsConstant() && 4029 CanEncodeConstantAsImmediate(constant->AsConstant(), opcode)) { 4030 return Location::ConstantLocation(constant->AsConstant()); 4031 } 4032 return Location::RequiresRegister(); 4033} 4034 4035bool LocationsBuilderARM::CanEncodeConstantAsImmediate(HConstant* input_cst, 4036 Opcode opcode) { 4037 uint64_t value = static_cast<uint64_t>(Int64FromConstant(input_cst)); 4038 if (Primitive::Is64BitType(input_cst->GetType())) { 4039 return CanEncodeConstantAsImmediate(Low32Bits(value), opcode) && 4040 CanEncodeConstantAsImmediate(High32Bits(value), opcode); 4041 } else { 4042 return CanEncodeConstantAsImmediate(Low32Bits(value), opcode); 4043 } 4044} 4045 4046bool LocationsBuilderARM::CanEncodeConstantAsImmediate(uint32_t value, Opcode opcode) { 4047 ShifterOperand so; 4048 ArmAssembler* assembler = codegen_->GetAssembler(); 4049 if (assembler->ShifterOperandCanHold(kNoRegister, kNoRegister, opcode, value, &so)) { 4050 return true; 4051 } 4052 Opcode neg_opcode = kNoOperand; 4053 switch (opcode) { 4054 case AND: 4055 neg_opcode = BIC; 4056 break; 4057 case ORR: 4058 neg_opcode = ORN; 4059 break; 4060 default: 4061 return false; 4062 } 4063 return assembler->ShifterOperandCanHold(kNoRegister, kNoRegister, neg_opcode, ~value, &so); 4064} 4065 4066void InstructionCodeGeneratorARM::HandleFieldGet(HInstruction* instruction, 4067 const FieldInfo& field_info) { 4068 DCHECK(instruction->IsInstanceFieldGet() || instruction->IsStaticFieldGet()); 4069 4070 LocationSummary* locations = instruction->GetLocations(); 4071 Location base_loc = locations->InAt(0); 4072 Register base = base_loc.AsRegister<Register>(); 4073 Location out = locations->Out(); 4074 bool is_volatile = field_info.IsVolatile(); 4075 bool atomic_ldrd_strd = codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd(); 4076 Primitive::Type field_type = field_info.GetFieldType(); 4077 uint32_t offset = field_info.GetFieldOffset().Uint32Value(); 4078 4079 switch (field_type) { 4080 case Primitive::kPrimBoolean: 4081 __ LoadFromOffset(kLoadUnsignedByte, out.AsRegister<Register>(), base, offset); 4082 break; 4083 4084 case Primitive::kPrimByte: 4085 __ LoadFromOffset(kLoadSignedByte, out.AsRegister<Register>(), base, offset); 4086 break; 4087 4088 case Primitive::kPrimShort: 4089 __ LoadFromOffset(kLoadSignedHalfword, out.AsRegister<Register>(), base, offset); 4090 break; 4091 4092 case Primitive::kPrimChar: 4093 __ LoadFromOffset(kLoadUnsignedHalfword, out.AsRegister<Register>(), base, offset); 4094 break; 4095 4096 case Primitive::kPrimInt: 4097 __ LoadFromOffset(kLoadWord, out.AsRegister<Register>(), base, offset); 4098 break; 4099 4100 case Primitive::kPrimNot: { 4101 // /* HeapReference<Object> */ out = *(base + offset) 4102 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) { 4103 Location temp_loc = locations->GetTemp(0); 4104 // Note that a potential implicit null check is handled in this 4105 // CodeGeneratorARM::GenerateFieldLoadWithBakerReadBarrier call. 4106 codegen_->GenerateFieldLoadWithBakerReadBarrier( 4107 instruction, out, base, offset, temp_loc, /* needs_null_check */ true); 4108 if (is_volatile) { 4109 codegen_->GenerateMemoryBarrier(MemBarrierKind::kLoadAny); 4110 } 4111 } else { 4112 __ LoadFromOffset(kLoadWord, out.AsRegister<Register>(), base, offset); 4113 codegen_->MaybeRecordImplicitNullCheck(instruction); 4114 if (is_volatile) { 4115 codegen_->GenerateMemoryBarrier(MemBarrierKind::kLoadAny); 4116 } 4117 // If read barriers are enabled, emit read barriers other than 4118 // Baker's using a slow path (and also unpoison the loaded 4119 // reference, if heap poisoning is enabled). 4120 codegen_->MaybeGenerateReadBarrierSlow(instruction, out, out, base_loc, offset); 4121 } 4122 break; 4123 } 4124 4125 case Primitive::kPrimLong: 4126 if (is_volatile && !atomic_ldrd_strd) { 4127 GenerateWideAtomicLoad(base, offset, 4128 out.AsRegisterPairLow<Register>(), 4129 out.AsRegisterPairHigh<Register>()); 4130 } else { 4131 __ LoadFromOffset(kLoadWordPair, out.AsRegisterPairLow<Register>(), base, offset); 4132 } 4133 break; 4134 4135 case Primitive::kPrimFloat: 4136 __ LoadSFromOffset(out.AsFpuRegister<SRegister>(), base, offset); 4137 break; 4138 4139 case Primitive::kPrimDouble: { 4140 DRegister out_reg = FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()); 4141 if (is_volatile && !atomic_ldrd_strd) { 4142 Register lo = locations->GetTemp(0).AsRegister<Register>(); 4143 Register hi = locations->GetTemp(1).AsRegister<Register>(); 4144 GenerateWideAtomicLoad(base, offset, lo, hi); 4145 codegen_->MaybeRecordImplicitNullCheck(instruction); 4146 __ vmovdrr(out_reg, lo, hi); 4147 } else { 4148 __ LoadDFromOffset(out_reg, base, offset); 4149 codegen_->MaybeRecordImplicitNullCheck(instruction); 4150 } 4151 break; 4152 } 4153 4154 case Primitive::kPrimVoid: 4155 LOG(FATAL) << "Unreachable type " << field_type; 4156 UNREACHABLE(); 4157 } 4158 4159 if (field_type == Primitive::kPrimNot || field_type == Primitive::kPrimDouble) { 4160 // Potential implicit null checks, in the case of reference or 4161 // double fields, are handled in the previous switch statement. 4162 } else { 4163 codegen_->MaybeRecordImplicitNullCheck(instruction); 4164 } 4165 4166 if (is_volatile) { 4167 if (field_type == Primitive::kPrimNot) { 4168 // Memory barriers, in the case of references, are also handled 4169 // in the previous switch statement. 4170 } else { 4171 codegen_->GenerateMemoryBarrier(MemBarrierKind::kLoadAny); 4172 } 4173 } 4174} 4175 4176void LocationsBuilderARM::VisitInstanceFieldSet(HInstanceFieldSet* instruction) { 4177 HandleFieldSet(instruction, instruction->GetFieldInfo()); 4178} 4179 4180void InstructionCodeGeneratorARM::VisitInstanceFieldSet(HInstanceFieldSet* instruction) { 4181 HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull()); 4182} 4183 4184void LocationsBuilderARM::VisitInstanceFieldGet(HInstanceFieldGet* instruction) { 4185 HandleFieldGet(instruction, instruction->GetFieldInfo()); 4186} 4187 4188void InstructionCodeGeneratorARM::VisitInstanceFieldGet(HInstanceFieldGet* instruction) { 4189 HandleFieldGet(instruction, instruction->GetFieldInfo()); 4190} 4191 4192void LocationsBuilderARM::VisitStaticFieldGet(HStaticFieldGet* instruction) { 4193 HandleFieldGet(instruction, instruction->GetFieldInfo()); 4194} 4195 4196void InstructionCodeGeneratorARM::VisitStaticFieldGet(HStaticFieldGet* instruction) { 4197 HandleFieldGet(instruction, instruction->GetFieldInfo()); 4198} 4199 4200void LocationsBuilderARM::VisitStaticFieldSet(HStaticFieldSet* instruction) { 4201 HandleFieldSet(instruction, instruction->GetFieldInfo()); 4202} 4203 4204void InstructionCodeGeneratorARM::VisitStaticFieldSet(HStaticFieldSet* instruction) { 4205 HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull()); 4206} 4207 4208void LocationsBuilderARM::VisitUnresolvedInstanceFieldGet( 4209 HUnresolvedInstanceFieldGet* instruction) { 4210 FieldAccessCallingConventionARM calling_convention; 4211 codegen_->CreateUnresolvedFieldLocationSummary( 4212 instruction, instruction->GetFieldType(), calling_convention); 4213} 4214 4215void InstructionCodeGeneratorARM::VisitUnresolvedInstanceFieldGet( 4216 HUnresolvedInstanceFieldGet* instruction) { 4217 FieldAccessCallingConventionARM calling_convention; 4218 codegen_->GenerateUnresolvedFieldAccess(instruction, 4219 instruction->GetFieldType(), 4220 instruction->GetFieldIndex(), 4221 instruction->GetDexPc(), 4222 calling_convention); 4223} 4224 4225void LocationsBuilderARM::VisitUnresolvedInstanceFieldSet( 4226 HUnresolvedInstanceFieldSet* instruction) { 4227 FieldAccessCallingConventionARM calling_convention; 4228 codegen_->CreateUnresolvedFieldLocationSummary( 4229 instruction, instruction->GetFieldType(), calling_convention); 4230} 4231 4232void InstructionCodeGeneratorARM::VisitUnresolvedInstanceFieldSet( 4233 HUnresolvedInstanceFieldSet* instruction) { 4234 FieldAccessCallingConventionARM calling_convention; 4235 codegen_->GenerateUnresolvedFieldAccess(instruction, 4236 instruction->GetFieldType(), 4237 instruction->GetFieldIndex(), 4238 instruction->GetDexPc(), 4239 calling_convention); 4240} 4241 4242void LocationsBuilderARM::VisitUnresolvedStaticFieldGet( 4243 HUnresolvedStaticFieldGet* instruction) { 4244 FieldAccessCallingConventionARM calling_convention; 4245 codegen_->CreateUnresolvedFieldLocationSummary( 4246 instruction, instruction->GetFieldType(), calling_convention); 4247} 4248 4249void InstructionCodeGeneratorARM::VisitUnresolvedStaticFieldGet( 4250 HUnresolvedStaticFieldGet* instruction) { 4251 FieldAccessCallingConventionARM calling_convention; 4252 codegen_->GenerateUnresolvedFieldAccess(instruction, 4253 instruction->GetFieldType(), 4254 instruction->GetFieldIndex(), 4255 instruction->GetDexPc(), 4256 calling_convention); 4257} 4258 4259void LocationsBuilderARM::VisitUnresolvedStaticFieldSet( 4260 HUnresolvedStaticFieldSet* instruction) { 4261 FieldAccessCallingConventionARM calling_convention; 4262 codegen_->CreateUnresolvedFieldLocationSummary( 4263 instruction, instruction->GetFieldType(), calling_convention); 4264} 4265 4266void InstructionCodeGeneratorARM::VisitUnresolvedStaticFieldSet( 4267 HUnresolvedStaticFieldSet* instruction) { 4268 FieldAccessCallingConventionARM calling_convention; 4269 codegen_->GenerateUnresolvedFieldAccess(instruction, 4270 instruction->GetFieldType(), 4271 instruction->GetFieldIndex(), 4272 instruction->GetDexPc(), 4273 calling_convention); 4274} 4275 4276void LocationsBuilderARM::VisitNullCheck(HNullCheck* instruction) { 4277 LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock() 4278 ? LocationSummary::kCallOnSlowPath 4279 : LocationSummary::kNoCall; 4280 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind); 4281 locations->SetInAt(0, Location::RequiresRegister()); 4282 if (instruction->HasUses()) { 4283 locations->SetOut(Location::SameAsFirstInput()); 4284 } 4285} 4286 4287void InstructionCodeGeneratorARM::GenerateImplicitNullCheck(HNullCheck* instruction) { 4288 if (codegen_->CanMoveNullCheckToUser(instruction)) { 4289 return; 4290 } 4291 Location obj = instruction->GetLocations()->InAt(0); 4292 4293 __ LoadFromOffset(kLoadWord, IP, obj.AsRegister<Register>(), 0); 4294 codegen_->RecordPcInfo(instruction, instruction->GetDexPc()); 4295} 4296 4297void InstructionCodeGeneratorARM::GenerateExplicitNullCheck(HNullCheck* instruction) { 4298 SlowPathCode* slow_path = new (GetGraph()->GetArena()) NullCheckSlowPathARM(instruction); 4299 codegen_->AddSlowPath(slow_path); 4300 4301 LocationSummary* locations = instruction->GetLocations(); 4302 Location obj = locations->InAt(0); 4303 4304 __ CompareAndBranchIfZero(obj.AsRegister<Register>(), slow_path->GetEntryLabel()); 4305} 4306 4307void InstructionCodeGeneratorARM::VisitNullCheck(HNullCheck* instruction) { 4308 if (codegen_->IsImplicitNullCheckAllowed(instruction)) { 4309 GenerateImplicitNullCheck(instruction); 4310 } else { 4311 GenerateExplicitNullCheck(instruction); 4312 } 4313} 4314 4315void LocationsBuilderARM::VisitArrayGet(HArrayGet* instruction) { 4316 bool object_array_get_with_read_barrier = 4317 kEmitCompilerReadBarrier && (instruction->GetType() == Primitive::kPrimNot); 4318 LocationSummary* locations = 4319 new (GetGraph()->GetArena()) LocationSummary(instruction, 4320 object_array_get_with_read_barrier ? 4321 LocationSummary::kCallOnSlowPath : 4322 LocationSummary::kNoCall); 4323 locations->SetInAt(0, Location::RequiresRegister()); 4324 locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1))); 4325 if (Primitive::IsFloatingPointType(instruction->GetType())) { 4326 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); 4327 } else { 4328 // The output overlaps in the case of an object array get with 4329 // read barriers enabled: we do not want the move to overwrite the 4330 // array's location, as we need it to emit the read barrier. 4331 locations->SetOut( 4332 Location::RequiresRegister(), 4333 object_array_get_with_read_barrier ? Location::kOutputOverlap : Location::kNoOutputOverlap); 4334 } 4335 // We need a temporary register for the read barrier marking slow 4336 // path in CodeGeneratorARM::GenerateArrayLoadWithBakerReadBarrier. 4337 if (object_array_get_with_read_barrier && kUseBakerReadBarrier) { 4338 locations->AddTemp(Location::RequiresRegister()); 4339 } 4340} 4341 4342void InstructionCodeGeneratorARM::VisitArrayGet(HArrayGet* instruction) { 4343 LocationSummary* locations = instruction->GetLocations(); 4344 Location obj_loc = locations->InAt(0); 4345 Register obj = obj_loc.AsRegister<Register>(); 4346 Location index = locations->InAt(1); 4347 Location out_loc = locations->Out(); 4348 4349 Primitive::Type type = instruction->GetType(); 4350 switch (type) { 4351 case Primitive::kPrimBoolean: { 4352 uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value(); 4353 Register out = out_loc.AsRegister<Register>(); 4354 if (index.IsConstant()) { 4355 size_t offset = 4356 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset; 4357 __ LoadFromOffset(kLoadUnsignedByte, out, obj, offset); 4358 } else { 4359 __ add(IP, obj, ShifterOperand(index.AsRegister<Register>())); 4360 __ LoadFromOffset(kLoadUnsignedByte, out, IP, data_offset); 4361 } 4362 break; 4363 } 4364 4365 case Primitive::kPrimByte: { 4366 uint32_t data_offset = mirror::Array::DataOffset(sizeof(int8_t)).Uint32Value(); 4367 Register out = out_loc.AsRegister<Register>(); 4368 if (index.IsConstant()) { 4369 size_t offset = 4370 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset; 4371 __ LoadFromOffset(kLoadSignedByte, out, obj, offset); 4372 } else { 4373 __ add(IP, obj, ShifterOperand(index.AsRegister<Register>())); 4374 __ LoadFromOffset(kLoadSignedByte, out, IP, data_offset); 4375 } 4376 break; 4377 } 4378 4379 case Primitive::kPrimShort: { 4380 uint32_t data_offset = mirror::Array::DataOffset(sizeof(int16_t)).Uint32Value(); 4381 Register out = out_loc.AsRegister<Register>(); 4382 if (index.IsConstant()) { 4383 size_t offset = 4384 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset; 4385 __ LoadFromOffset(kLoadSignedHalfword, out, obj, offset); 4386 } else { 4387 __ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_2)); 4388 __ LoadFromOffset(kLoadSignedHalfword, out, IP, data_offset); 4389 } 4390 break; 4391 } 4392 4393 case Primitive::kPrimChar: { 4394 uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value(); 4395 Register out = out_loc.AsRegister<Register>(); 4396 if (index.IsConstant()) { 4397 size_t offset = 4398 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset; 4399 __ LoadFromOffset(kLoadUnsignedHalfword, out, obj, offset); 4400 } else { 4401 __ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_2)); 4402 __ LoadFromOffset(kLoadUnsignedHalfword, out, IP, data_offset); 4403 } 4404 break; 4405 } 4406 4407 case Primitive::kPrimInt: { 4408 uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value(); 4409 Register out = out_loc.AsRegister<Register>(); 4410 if (index.IsConstant()) { 4411 size_t offset = 4412 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; 4413 __ LoadFromOffset(kLoadWord, out, obj, offset); 4414 } else { 4415 __ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4)); 4416 __ LoadFromOffset(kLoadWord, out, IP, data_offset); 4417 } 4418 break; 4419 } 4420 4421 case Primitive::kPrimNot: { 4422 static_assert( 4423 sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t), 4424 "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes."); 4425 uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value(); 4426 // /* HeapReference<Object> */ out = 4427 // *(obj + data_offset + index * sizeof(HeapReference<Object>)) 4428 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) { 4429 Location temp = locations->GetTemp(0); 4430 // Note that a potential implicit null check is handled in this 4431 // CodeGeneratorARM::GenerateArrayLoadWithBakerReadBarrier call. 4432 codegen_->GenerateArrayLoadWithBakerReadBarrier( 4433 instruction, out_loc, obj, data_offset, index, temp, /* needs_null_check */ true); 4434 } else { 4435 Register out = out_loc.AsRegister<Register>(); 4436 if (index.IsConstant()) { 4437 size_t offset = 4438 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; 4439 __ LoadFromOffset(kLoadWord, out, obj, offset); 4440 codegen_->MaybeRecordImplicitNullCheck(instruction); 4441 // If read barriers are enabled, emit read barriers other than 4442 // Baker's using a slow path (and also unpoison the loaded 4443 // reference, if heap poisoning is enabled). 4444 codegen_->MaybeGenerateReadBarrierSlow(instruction, out_loc, out_loc, obj_loc, offset); 4445 } else { 4446 __ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4)); 4447 __ LoadFromOffset(kLoadWord, out, IP, data_offset); 4448 codegen_->MaybeRecordImplicitNullCheck(instruction); 4449 // If read barriers are enabled, emit read barriers other than 4450 // Baker's using a slow path (and also unpoison the loaded 4451 // reference, if heap poisoning is enabled). 4452 codegen_->MaybeGenerateReadBarrierSlow( 4453 instruction, out_loc, out_loc, obj_loc, data_offset, index); 4454 } 4455 } 4456 break; 4457 } 4458 4459 case Primitive::kPrimLong: { 4460 uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value(); 4461 if (index.IsConstant()) { 4462 size_t offset = 4463 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset; 4464 __ LoadFromOffset(kLoadWordPair, out_loc.AsRegisterPairLow<Register>(), obj, offset); 4465 } else { 4466 __ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_8)); 4467 __ LoadFromOffset(kLoadWordPair, out_loc.AsRegisterPairLow<Register>(), IP, data_offset); 4468 } 4469 break; 4470 } 4471 4472 case Primitive::kPrimFloat: { 4473 uint32_t data_offset = mirror::Array::DataOffset(sizeof(float)).Uint32Value(); 4474 SRegister out = out_loc.AsFpuRegister<SRegister>(); 4475 if (index.IsConstant()) { 4476 size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; 4477 __ LoadSFromOffset(out, obj, offset); 4478 } else { 4479 __ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4)); 4480 __ LoadSFromOffset(out, IP, data_offset); 4481 } 4482 break; 4483 } 4484 4485 case Primitive::kPrimDouble: { 4486 uint32_t data_offset = mirror::Array::DataOffset(sizeof(double)).Uint32Value(); 4487 SRegister out = out_loc.AsFpuRegisterPairLow<SRegister>(); 4488 if (index.IsConstant()) { 4489 size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset; 4490 __ LoadDFromOffset(FromLowSToD(out), obj, offset); 4491 } else { 4492 __ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_8)); 4493 __ LoadDFromOffset(FromLowSToD(out), IP, data_offset); 4494 } 4495 break; 4496 } 4497 4498 case Primitive::kPrimVoid: 4499 LOG(FATAL) << "Unreachable type " << type; 4500 UNREACHABLE(); 4501 } 4502 4503 if (type == Primitive::kPrimNot) { 4504 // Potential implicit null checks, in the case of reference 4505 // arrays, are handled in the previous switch statement. 4506 } else { 4507 codegen_->MaybeRecordImplicitNullCheck(instruction); 4508 } 4509} 4510 4511void LocationsBuilderARM::VisitArraySet(HArraySet* instruction) { 4512 Primitive::Type value_type = instruction->GetComponentType(); 4513 4514 bool needs_write_barrier = 4515 CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue()); 4516 bool may_need_runtime_call_for_type_check = instruction->NeedsTypeCheck(); 4517 bool object_array_set_with_read_barrier = 4518 kEmitCompilerReadBarrier && (value_type == Primitive::kPrimNot); 4519 4520 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary( 4521 instruction, 4522 (may_need_runtime_call_for_type_check || object_array_set_with_read_barrier) ? 4523 LocationSummary::kCallOnSlowPath : 4524 LocationSummary::kNoCall); 4525 4526 locations->SetInAt(0, Location::RequiresRegister()); 4527 locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1))); 4528 if (Primitive::IsFloatingPointType(value_type)) { 4529 locations->SetInAt(2, Location::RequiresFpuRegister()); 4530 } else { 4531 locations->SetInAt(2, Location::RequiresRegister()); 4532 } 4533 if (needs_write_barrier) { 4534 // Temporary registers for the write barrier. 4535 locations->AddTemp(Location::RequiresRegister()); // Possibly used for ref. poisoning too. 4536 locations->AddTemp(Location::RequiresRegister()); 4537 } 4538} 4539 4540void InstructionCodeGeneratorARM::VisitArraySet(HArraySet* instruction) { 4541 LocationSummary* locations = instruction->GetLocations(); 4542 Location array_loc = locations->InAt(0); 4543 Register array = array_loc.AsRegister<Register>(); 4544 Location index = locations->InAt(1); 4545 Primitive::Type value_type = instruction->GetComponentType(); 4546 bool may_need_runtime_call_for_type_check = instruction->NeedsTypeCheck(); 4547 bool needs_write_barrier = 4548 CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue()); 4549 4550 switch (value_type) { 4551 case Primitive::kPrimBoolean: 4552 case Primitive::kPrimByte: { 4553 uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value(); 4554 Register value = locations->InAt(2).AsRegister<Register>(); 4555 if (index.IsConstant()) { 4556 size_t offset = 4557 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset; 4558 __ StoreToOffset(kStoreByte, value, array, offset); 4559 } else { 4560 __ add(IP, array, ShifterOperand(index.AsRegister<Register>())); 4561 __ StoreToOffset(kStoreByte, value, IP, data_offset); 4562 } 4563 break; 4564 } 4565 4566 case Primitive::kPrimShort: 4567 case Primitive::kPrimChar: { 4568 uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value(); 4569 Register value = locations->InAt(2).AsRegister<Register>(); 4570 if (index.IsConstant()) { 4571 size_t offset = 4572 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset; 4573 __ StoreToOffset(kStoreHalfword, value, array, offset); 4574 } else { 4575 __ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_2)); 4576 __ StoreToOffset(kStoreHalfword, value, IP, data_offset); 4577 } 4578 break; 4579 } 4580 4581 case Primitive::kPrimNot: { 4582 uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value(); 4583 Location value_loc = locations->InAt(2); 4584 Register value = value_loc.AsRegister<Register>(); 4585 Register source = value; 4586 4587 if (instruction->InputAt(2)->IsNullConstant()) { 4588 // Just setting null. 4589 if (index.IsConstant()) { 4590 size_t offset = 4591 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; 4592 __ StoreToOffset(kStoreWord, source, array, offset); 4593 } else { 4594 DCHECK(index.IsRegister()) << index; 4595 __ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4)); 4596 __ StoreToOffset(kStoreWord, source, IP, data_offset); 4597 } 4598 codegen_->MaybeRecordImplicitNullCheck(instruction); 4599 DCHECK(!needs_write_barrier); 4600 DCHECK(!may_need_runtime_call_for_type_check); 4601 break; 4602 } 4603 4604 DCHECK(needs_write_barrier); 4605 Register temp1 = locations->GetTemp(0).AsRegister<Register>(); 4606 Register temp2 = locations->GetTemp(1).AsRegister<Register>(); 4607 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value(); 4608 uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value(); 4609 uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value(); 4610 Label done; 4611 SlowPathCode* slow_path = nullptr; 4612 4613 if (may_need_runtime_call_for_type_check) { 4614 slow_path = new (GetGraph()->GetArena()) ArraySetSlowPathARM(instruction); 4615 codegen_->AddSlowPath(slow_path); 4616 if (instruction->GetValueCanBeNull()) { 4617 Label non_zero; 4618 __ CompareAndBranchIfNonZero(value, &non_zero); 4619 if (index.IsConstant()) { 4620 size_t offset = 4621 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; 4622 __ StoreToOffset(kStoreWord, value, array, offset); 4623 } else { 4624 DCHECK(index.IsRegister()) << index; 4625 __ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4)); 4626 __ StoreToOffset(kStoreWord, value, IP, data_offset); 4627 } 4628 codegen_->MaybeRecordImplicitNullCheck(instruction); 4629 __ b(&done); 4630 __ Bind(&non_zero); 4631 } 4632 4633 if (kEmitCompilerReadBarrier) { 4634 // When read barriers are enabled, the type checking 4635 // instrumentation requires two read barriers: 4636 // 4637 // __ Mov(temp2, temp1); 4638 // // /* HeapReference<Class> */ temp1 = temp1->component_type_ 4639 // __ LoadFromOffset(kLoadWord, temp1, temp1, component_offset); 4640 // codegen_->GenerateReadBarrierSlow( 4641 // instruction, temp1_loc, temp1_loc, temp2_loc, component_offset); 4642 // 4643 // // /* HeapReference<Class> */ temp2 = value->klass_ 4644 // __ LoadFromOffset(kLoadWord, temp2, value, class_offset); 4645 // codegen_->GenerateReadBarrierSlow( 4646 // instruction, temp2_loc, temp2_loc, value_loc, class_offset, temp1_loc); 4647 // 4648 // __ cmp(temp1, ShifterOperand(temp2)); 4649 // 4650 // However, the second read barrier may trash `temp`, as it 4651 // is a temporary register, and as such would not be saved 4652 // along with live registers before calling the runtime (nor 4653 // restored afterwards). So in this case, we bail out and 4654 // delegate the work to the array set slow path. 4655 // 4656 // TODO: Extend the register allocator to support a new 4657 // "(locally) live temp" location so as to avoid always 4658 // going into the slow path when read barriers are enabled. 4659 __ b(slow_path->GetEntryLabel()); 4660 } else { 4661 // /* HeapReference<Class> */ temp1 = array->klass_ 4662 __ LoadFromOffset(kLoadWord, temp1, array, class_offset); 4663 codegen_->MaybeRecordImplicitNullCheck(instruction); 4664 __ MaybeUnpoisonHeapReference(temp1); 4665 4666 // /* HeapReference<Class> */ temp1 = temp1->component_type_ 4667 __ LoadFromOffset(kLoadWord, temp1, temp1, component_offset); 4668 // /* HeapReference<Class> */ temp2 = value->klass_ 4669 __ LoadFromOffset(kLoadWord, temp2, value, class_offset); 4670 // If heap poisoning is enabled, no need to unpoison `temp1` 4671 // nor `temp2`, as we are comparing two poisoned references. 4672 __ cmp(temp1, ShifterOperand(temp2)); 4673 4674 if (instruction->StaticTypeOfArrayIsObjectArray()) { 4675 Label do_put; 4676 __ b(&do_put, EQ); 4677 // If heap poisoning is enabled, the `temp1` reference has 4678 // not been unpoisoned yet; unpoison it now. 4679 __ MaybeUnpoisonHeapReference(temp1); 4680 4681 // /* HeapReference<Class> */ temp1 = temp1->super_class_ 4682 __ LoadFromOffset(kLoadWord, temp1, temp1, super_offset); 4683 // If heap poisoning is enabled, no need to unpoison 4684 // `temp1`, as we are comparing against null below. 4685 __ CompareAndBranchIfNonZero(temp1, slow_path->GetEntryLabel()); 4686 __ Bind(&do_put); 4687 } else { 4688 __ b(slow_path->GetEntryLabel(), NE); 4689 } 4690 } 4691 } 4692 4693 if (kPoisonHeapReferences) { 4694 // Note that in the case where `value` is a null reference, 4695 // we do not enter this block, as a null reference does not 4696 // need poisoning. 4697 DCHECK_EQ(value_type, Primitive::kPrimNot); 4698 __ Mov(temp1, value); 4699 __ PoisonHeapReference(temp1); 4700 source = temp1; 4701 } 4702 4703 if (index.IsConstant()) { 4704 size_t offset = 4705 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; 4706 __ StoreToOffset(kStoreWord, source, array, offset); 4707 } else { 4708 DCHECK(index.IsRegister()) << index; 4709 __ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4)); 4710 __ StoreToOffset(kStoreWord, source, IP, data_offset); 4711 } 4712 4713 if (!may_need_runtime_call_for_type_check) { 4714 codegen_->MaybeRecordImplicitNullCheck(instruction); 4715 } 4716 4717 codegen_->MarkGCCard(temp1, temp2, array, value, instruction->GetValueCanBeNull()); 4718 4719 if (done.IsLinked()) { 4720 __ Bind(&done); 4721 } 4722 4723 if (slow_path != nullptr) { 4724 __ Bind(slow_path->GetExitLabel()); 4725 } 4726 4727 break; 4728 } 4729 4730 case Primitive::kPrimInt: { 4731 uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value(); 4732 Register value = locations->InAt(2).AsRegister<Register>(); 4733 if (index.IsConstant()) { 4734 size_t offset = 4735 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; 4736 __ StoreToOffset(kStoreWord, value, array, offset); 4737 } else { 4738 DCHECK(index.IsRegister()) << index; 4739 __ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4)); 4740 __ StoreToOffset(kStoreWord, value, IP, data_offset); 4741 } 4742 break; 4743 } 4744 4745 case Primitive::kPrimLong: { 4746 uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value(); 4747 Location value = locations->InAt(2); 4748 if (index.IsConstant()) { 4749 size_t offset = 4750 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset; 4751 __ StoreToOffset(kStoreWordPair, value.AsRegisterPairLow<Register>(), array, offset); 4752 } else { 4753 __ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_8)); 4754 __ StoreToOffset(kStoreWordPair, value.AsRegisterPairLow<Register>(), IP, data_offset); 4755 } 4756 break; 4757 } 4758 4759 case Primitive::kPrimFloat: { 4760 uint32_t data_offset = mirror::Array::DataOffset(sizeof(float)).Uint32Value(); 4761 Location value = locations->InAt(2); 4762 DCHECK(value.IsFpuRegister()); 4763 if (index.IsConstant()) { 4764 size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; 4765 __ StoreSToOffset(value.AsFpuRegister<SRegister>(), array, offset); 4766 } else { 4767 __ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4)); 4768 __ StoreSToOffset(value.AsFpuRegister<SRegister>(), IP, data_offset); 4769 } 4770 break; 4771 } 4772 4773 case Primitive::kPrimDouble: { 4774 uint32_t data_offset = mirror::Array::DataOffset(sizeof(double)).Uint32Value(); 4775 Location value = locations->InAt(2); 4776 DCHECK(value.IsFpuRegisterPair()); 4777 if (index.IsConstant()) { 4778 size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset; 4779 __ StoreDToOffset(FromLowSToD(value.AsFpuRegisterPairLow<SRegister>()), array, offset); 4780 } else { 4781 __ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_8)); 4782 __ StoreDToOffset(FromLowSToD(value.AsFpuRegisterPairLow<SRegister>()), IP, data_offset); 4783 } 4784 4785 break; 4786 } 4787 4788 case Primitive::kPrimVoid: 4789 LOG(FATAL) << "Unreachable type " << value_type; 4790 UNREACHABLE(); 4791 } 4792 4793 // Objects are handled in the switch. 4794 if (value_type != Primitive::kPrimNot) { 4795 codegen_->MaybeRecordImplicitNullCheck(instruction); 4796 } 4797} 4798 4799void LocationsBuilderARM::VisitArrayLength(HArrayLength* instruction) { 4800 LocationSummary* locations = 4801 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); 4802 locations->SetInAt(0, Location::RequiresRegister()); 4803 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 4804} 4805 4806void InstructionCodeGeneratorARM::VisitArrayLength(HArrayLength* instruction) { 4807 LocationSummary* locations = instruction->GetLocations(); 4808 uint32_t offset = mirror::Array::LengthOffset().Uint32Value(); 4809 Register obj = locations->InAt(0).AsRegister<Register>(); 4810 Register out = locations->Out().AsRegister<Register>(); 4811 __ LoadFromOffset(kLoadWord, out, obj, offset); 4812 codegen_->MaybeRecordImplicitNullCheck(instruction); 4813} 4814 4815void LocationsBuilderARM::VisitBoundsCheck(HBoundsCheck* instruction) { 4816 LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock() 4817 ? LocationSummary::kCallOnSlowPath 4818 : LocationSummary::kNoCall; 4819 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind); 4820 locations->SetInAt(0, Location::RequiresRegister()); 4821 locations->SetInAt(1, Location::RequiresRegister()); 4822 if (instruction->HasUses()) { 4823 locations->SetOut(Location::SameAsFirstInput()); 4824 } 4825} 4826 4827void InstructionCodeGeneratorARM::VisitBoundsCheck(HBoundsCheck* instruction) { 4828 LocationSummary* locations = instruction->GetLocations(); 4829 SlowPathCode* slow_path = 4830 new (GetGraph()->GetArena()) BoundsCheckSlowPathARM(instruction); 4831 codegen_->AddSlowPath(slow_path); 4832 4833 Register index = locations->InAt(0).AsRegister<Register>(); 4834 Register length = locations->InAt(1).AsRegister<Register>(); 4835 4836 __ cmp(index, ShifterOperand(length)); 4837 __ b(slow_path->GetEntryLabel(), HS); 4838} 4839 4840void CodeGeneratorARM::MarkGCCard(Register temp, 4841 Register card, 4842 Register object, 4843 Register value, 4844 bool can_be_null) { 4845 Label is_null; 4846 if (can_be_null) { 4847 __ CompareAndBranchIfZero(value, &is_null); 4848 } 4849 __ LoadFromOffset(kLoadWord, card, TR, Thread::CardTableOffset<kArmWordSize>().Int32Value()); 4850 __ Lsr(temp, object, gc::accounting::CardTable::kCardShift); 4851 __ strb(card, Address(card, temp)); 4852 if (can_be_null) { 4853 __ Bind(&is_null); 4854 } 4855} 4856 4857void LocationsBuilderARM::VisitParallelMove(HParallelMove* instruction ATTRIBUTE_UNUSED) { 4858 LOG(FATAL) << "Unreachable"; 4859} 4860 4861void InstructionCodeGeneratorARM::VisitParallelMove(HParallelMove* instruction) { 4862 codegen_->GetMoveResolver()->EmitNativeCode(instruction); 4863} 4864 4865void LocationsBuilderARM::VisitSuspendCheck(HSuspendCheck* instruction) { 4866 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnSlowPath); 4867} 4868 4869void InstructionCodeGeneratorARM::VisitSuspendCheck(HSuspendCheck* instruction) { 4870 HBasicBlock* block = instruction->GetBlock(); 4871 if (block->GetLoopInformation() != nullptr) { 4872 DCHECK(block->GetLoopInformation()->GetSuspendCheck() == instruction); 4873 // The back edge will generate the suspend check. 4874 return; 4875 } 4876 if (block->IsEntryBlock() && instruction->GetNext()->IsGoto()) { 4877 // The goto will generate the suspend check. 4878 return; 4879 } 4880 GenerateSuspendCheck(instruction, nullptr); 4881} 4882 4883void InstructionCodeGeneratorARM::GenerateSuspendCheck(HSuspendCheck* instruction, 4884 HBasicBlock* successor) { 4885 SuspendCheckSlowPathARM* slow_path = 4886 down_cast<SuspendCheckSlowPathARM*>(instruction->GetSlowPath()); 4887 if (slow_path == nullptr) { 4888 slow_path = new (GetGraph()->GetArena()) SuspendCheckSlowPathARM(instruction, successor); 4889 instruction->SetSlowPath(slow_path); 4890 codegen_->AddSlowPath(slow_path); 4891 if (successor != nullptr) { 4892 DCHECK(successor->IsLoopHeader()); 4893 codegen_->ClearSpillSlotsFromLoopPhisInStackMap(instruction); 4894 } 4895 } else { 4896 DCHECK_EQ(slow_path->GetSuccessor(), successor); 4897 } 4898 4899 __ LoadFromOffset( 4900 kLoadUnsignedHalfword, IP, TR, Thread::ThreadFlagsOffset<kArmWordSize>().Int32Value()); 4901 if (successor == nullptr) { 4902 __ CompareAndBranchIfNonZero(IP, slow_path->GetEntryLabel()); 4903 __ Bind(slow_path->GetReturnLabel()); 4904 } else { 4905 __ CompareAndBranchIfZero(IP, codegen_->GetLabelOf(successor)); 4906 __ b(slow_path->GetEntryLabel()); 4907 } 4908} 4909 4910ArmAssembler* ParallelMoveResolverARM::GetAssembler() const { 4911 return codegen_->GetAssembler(); 4912} 4913 4914void ParallelMoveResolverARM::EmitMove(size_t index) { 4915 MoveOperands* move = moves_[index]; 4916 Location source = move->GetSource(); 4917 Location destination = move->GetDestination(); 4918 4919 if (source.IsRegister()) { 4920 if (destination.IsRegister()) { 4921 __ Mov(destination.AsRegister<Register>(), source.AsRegister<Register>()); 4922 } else if (destination.IsFpuRegister()) { 4923 __ vmovsr(destination.AsFpuRegister<SRegister>(), source.AsRegister<Register>()); 4924 } else { 4925 DCHECK(destination.IsStackSlot()); 4926 __ StoreToOffset(kStoreWord, source.AsRegister<Register>(), 4927 SP, destination.GetStackIndex()); 4928 } 4929 } else if (source.IsStackSlot()) { 4930 if (destination.IsRegister()) { 4931 __ LoadFromOffset(kLoadWord, destination.AsRegister<Register>(), 4932 SP, source.GetStackIndex()); 4933 } else if (destination.IsFpuRegister()) { 4934 __ LoadSFromOffset(destination.AsFpuRegister<SRegister>(), SP, source.GetStackIndex()); 4935 } else { 4936 DCHECK(destination.IsStackSlot()); 4937 __ LoadFromOffset(kLoadWord, IP, SP, source.GetStackIndex()); 4938 __ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex()); 4939 } 4940 } else if (source.IsFpuRegister()) { 4941 if (destination.IsRegister()) { 4942 __ vmovrs(destination.AsRegister<Register>(), source.AsFpuRegister<SRegister>()); 4943 } else if (destination.IsFpuRegister()) { 4944 __ vmovs(destination.AsFpuRegister<SRegister>(), source.AsFpuRegister<SRegister>()); 4945 } else { 4946 DCHECK(destination.IsStackSlot()); 4947 __ StoreSToOffset(source.AsFpuRegister<SRegister>(), SP, destination.GetStackIndex()); 4948 } 4949 } else if (source.IsDoubleStackSlot()) { 4950 if (destination.IsDoubleStackSlot()) { 4951 __ LoadDFromOffset(DTMP, SP, source.GetStackIndex()); 4952 __ StoreDToOffset(DTMP, SP, destination.GetStackIndex()); 4953 } else if (destination.IsRegisterPair()) { 4954 DCHECK(ExpectedPairLayout(destination)); 4955 __ LoadFromOffset( 4956 kLoadWordPair, destination.AsRegisterPairLow<Register>(), SP, source.GetStackIndex()); 4957 } else { 4958 DCHECK(destination.IsFpuRegisterPair()) << destination; 4959 __ LoadDFromOffset(FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()), 4960 SP, 4961 source.GetStackIndex()); 4962 } 4963 } else if (source.IsRegisterPair()) { 4964 if (destination.IsRegisterPair()) { 4965 __ Mov(destination.AsRegisterPairLow<Register>(), source.AsRegisterPairLow<Register>()); 4966 __ Mov(destination.AsRegisterPairHigh<Register>(), source.AsRegisterPairHigh<Register>()); 4967 } else if (destination.IsFpuRegisterPair()) { 4968 __ vmovdrr(FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()), 4969 source.AsRegisterPairLow<Register>(), 4970 source.AsRegisterPairHigh<Register>()); 4971 } else { 4972 DCHECK(destination.IsDoubleStackSlot()) << destination; 4973 DCHECK(ExpectedPairLayout(source)); 4974 __ StoreToOffset( 4975 kStoreWordPair, source.AsRegisterPairLow<Register>(), SP, destination.GetStackIndex()); 4976 } 4977 } else if (source.IsFpuRegisterPair()) { 4978 if (destination.IsRegisterPair()) { 4979 __ vmovrrd(destination.AsRegisterPairLow<Register>(), 4980 destination.AsRegisterPairHigh<Register>(), 4981 FromLowSToD(source.AsFpuRegisterPairLow<SRegister>())); 4982 } else if (destination.IsFpuRegisterPair()) { 4983 __ vmovd(FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()), 4984 FromLowSToD(source.AsFpuRegisterPairLow<SRegister>())); 4985 } else { 4986 DCHECK(destination.IsDoubleStackSlot()) << destination; 4987 __ StoreDToOffset(FromLowSToD(source.AsFpuRegisterPairLow<SRegister>()), 4988 SP, 4989 destination.GetStackIndex()); 4990 } 4991 } else { 4992 DCHECK(source.IsConstant()) << source; 4993 HConstant* constant = source.GetConstant(); 4994 if (constant->IsIntConstant() || constant->IsNullConstant()) { 4995 int32_t value = CodeGenerator::GetInt32ValueOf(constant); 4996 if (destination.IsRegister()) { 4997 __ LoadImmediate(destination.AsRegister<Register>(), value); 4998 } else { 4999 DCHECK(destination.IsStackSlot()); 5000 __ LoadImmediate(IP, value); 5001 __ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex()); 5002 } 5003 } else if (constant->IsLongConstant()) { 5004 int64_t value = constant->AsLongConstant()->GetValue(); 5005 if (destination.IsRegisterPair()) { 5006 __ LoadImmediate(destination.AsRegisterPairLow<Register>(), Low32Bits(value)); 5007 __ LoadImmediate(destination.AsRegisterPairHigh<Register>(), High32Bits(value)); 5008 } else { 5009 DCHECK(destination.IsDoubleStackSlot()) << destination; 5010 __ LoadImmediate(IP, Low32Bits(value)); 5011 __ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex()); 5012 __ LoadImmediate(IP, High32Bits(value)); 5013 __ StoreToOffset(kStoreWord, IP, SP, destination.GetHighStackIndex(kArmWordSize)); 5014 } 5015 } else if (constant->IsDoubleConstant()) { 5016 double value = constant->AsDoubleConstant()->GetValue(); 5017 if (destination.IsFpuRegisterPair()) { 5018 __ LoadDImmediate(FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()), value); 5019 } else { 5020 DCHECK(destination.IsDoubleStackSlot()) << destination; 5021 uint64_t int_value = bit_cast<uint64_t, double>(value); 5022 __ LoadImmediate(IP, Low32Bits(int_value)); 5023 __ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex()); 5024 __ LoadImmediate(IP, High32Bits(int_value)); 5025 __ StoreToOffset(kStoreWord, IP, SP, destination.GetHighStackIndex(kArmWordSize)); 5026 } 5027 } else { 5028 DCHECK(constant->IsFloatConstant()) << constant->DebugName(); 5029 float value = constant->AsFloatConstant()->GetValue(); 5030 if (destination.IsFpuRegister()) { 5031 __ LoadSImmediate(destination.AsFpuRegister<SRegister>(), value); 5032 } else { 5033 DCHECK(destination.IsStackSlot()); 5034 __ LoadImmediate(IP, bit_cast<int32_t, float>(value)); 5035 __ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex()); 5036 } 5037 } 5038 } 5039} 5040 5041void ParallelMoveResolverARM::Exchange(Register reg, int mem) { 5042 __ Mov(IP, reg); 5043 __ LoadFromOffset(kLoadWord, reg, SP, mem); 5044 __ StoreToOffset(kStoreWord, IP, SP, mem); 5045} 5046 5047void ParallelMoveResolverARM::Exchange(int mem1, int mem2) { 5048 ScratchRegisterScope ensure_scratch(this, IP, R0, codegen_->GetNumberOfCoreRegisters()); 5049 int stack_offset = ensure_scratch.IsSpilled() ? kArmWordSize : 0; 5050 __ LoadFromOffset(kLoadWord, static_cast<Register>(ensure_scratch.GetRegister()), 5051 SP, mem1 + stack_offset); 5052 __ LoadFromOffset(kLoadWord, IP, SP, mem2 + stack_offset); 5053 __ StoreToOffset(kStoreWord, static_cast<Register>(ensure_scratch.GetRegister()), 5054 SP, mem2 + stack_offset); 5055 __ StoreToOffset(kStoreWord, IP, SP, mem1 + stack_offset); 5056} 5057 5058void ParallelMoveResolverARM::EmitSwap(size_t index) { 5059 MoveOperands* move = moves_[index]; 5060 Location source = move->GetSource(); 5061 Location destination = move->GetDestination(); 5062 5063 if (source.IsRegister() && destination.IsRegister()) { 5064 DCHECK_NE(source.AsRegister<Register>(), IP); 5065 DCHECK_NE(destination.AsRegister<Register>(), IP); 5066 __ Mov(IP, source.AsRegister<Register>()); 5067 __ Mov(source.AsRegister<Register>(), destination.AsRegister<Register>()); 5068 __ Mov(destination.AsRegister<Register>(), IP); 5069 } else if (source.IsRegister() && destination.IsStackSlot()) { 5070 Exchange(source.AsRegister<Register>(), destination.GetStackIndex()); 5071 } else if (source.IsStackSlot() && destination.IsRegister()) { 5072 Exchange(destination.AsRegister<Register>(), source.GetStackIndex()); 5073 } else if (source.IsStackSlot() && destination.IsStackSlot()) { 5074 Exchange(source.GetStackIndex(), destination.GetStackIndex()); 5075 } else if (source.IsFpuRegister() && destination.IsFpuRegister()) { 5076 __ vmovrs(IP, source.AsFpuRegister<SRegister>()); 5077 __ vmovs(source.AsFpuRegister<SRegister>(), destination.AsFpuRegister<SRegister>()); 5078 __ vmovsr(destination.AsFpuRegister<SRegister>(), IP); 5079 } else if (source.IsRegisterPair() && destination.IsRegisterPair()) { 5080 __ vmovdrr(DTMP, source.AsRegisterPairLow<Register>(), source.AsRegisterPairHigh<Register>()); 5081 __ Mov(source.AsRegisterPairLow<Register>(), destination.AsRegisterPairLow<Register>()); 5082 __ Mov(source.AsRegisterPairHigh<Register>(), destination.AsRegisterPairHigh<Register>()); 5083 __ vmovrrd(destination.AsRegisterPairLow<Register>(), 5084 destination.AsRegisterPairHigh<Register>(), 5085 DTMP); 5086 } else if (source.IsRegisterPair() || destination.IsRegisterPair()) { 5087 Register low_reg = source.IsRegisterPair() 5088 ? source.AsRegisterPairLow<Register>() 5089 : destination.AsRegisterPairLow<Register>(); 5090 int mem = source.IsRegisterPair() 5091 ? destination.GetStackIndex() 5092 : source.GetStackIndex(); 5093 DCHECK(ExpectedPairLayout(source.IsRegisterPair() ? source : destination)); 5094 __ vmovdrr(DTMP, low_reg, static_cast<Register>(low_reg + 1)); 5095 __ LoadFromOffset(kLoadWordPair, low_reg, SP, mem); 5096 __ StoreDToOffset(DTMP, SP, mem); 5097 } else if (source.IsFpuRegisterPair() && destination.IsFpuRegisterPair()) { 5098 DRegister first = FromLowSToD(source.AsFpuRegisterPairLow<SRegister>()); 5099 DRegister second = FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()); 5100 __ vmovd(DTMP, first); 5101 __ vmovd(first, second); 5102 __ vmovd(second, DTMP); 5103 } else if (source.IsFpuRegisterPair() || destination.IsFpuRegisterPair()) { 5104 DRegister reg = source.IsFpuRegisterPair() 5105 ? FromLowSToD(source.AsFpuRegisterPairLow<SRegister>()) 5106 : FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()); 5107 int mem = source.IsFpuRegisterPair() 5108 ? destination.GetStackIndex() 5109 : source.GetStackIndex(); 5110 __ vmovd(DTMP, reg); 5111 __ LoadDFromOffset(reg, SP, mem); 5112 __ StoreDToOffset(DTMP, SP, mem); 5113 } else if (source.IsFpuRegister() || destination.IsFpuRegister()) { 5114 SRegister reg = source.IsFpuRegister() ? source.AsFpuRegister<SRegister>() 5115 : destination.AsFpuRegister<SRegister>(); 5116 int mem = source.IsFpuRegister() 5117 ? destination.GetStackIndex() 5118 : source.GetStackIndex(); 5119 5120 __ vmovrs(IP, reg); 5121 __ LoadSFromOffset(reg, SP, mem); 5122 __ StoreToOffset(kStoreWord, IP, SP, mem); 5123 } else if (source.IsDoubleStackSlot() && destination.IsDoubleStackSlot()) { 5124 Exchange(source.GetStackIndex(), destination.GetStackIndex()); 5125 Exchange(source.GetHighStackIndex(kArmWordSize), destination.GetHighStackIndex(kArmWordSize)); 5126 } else { 5127 LOG(FATAL) << "Unimplemented" << source << " <-> " << destination; 5128 } 5129} 5130 5131void ParallelMoveResolverARM::SpillScratch(int reg) { 5132 __ Push(static_cast<Register>(reg)); 5133} 5134 5135void ParallelMoveResolverARM::RestoreScratch(int reg) { 5136 __ Pop(static_cast<Register>(reg)); 5137} 5138 5139void LocationsBuilderARM::VisitLoadClass(HLoadClass* cls) { 5140 InvokeRuntimeCallingConvention calling_convention; 5141 CodeGenerator::CreateLoadClassLocationSummary( 5142 cls, 5143 Location::RegisterLocation(calling_convention.GetRegisterAt(0)), 5144 Location::RegisterLocation(R0), 5145 /* code_generator_supports_read_barrier */ true); 5146} 5147 5148void InstructionCodeGeneratorARM::VisitLoadClass(HLoadClass* cls) { 5149 LocationSummary* locations = cls->GetLocations(); 5150 if (cls->NeedsAccessCheck()) { 5151 codegen_->MoveConstant(locations->GetTemp(0), cls->GetTypeIndex()); 5152 codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pInitializeTypeAndVerifyAccess), 5153 cls, 5154 cls->GetDexPc(), 5155 nullptr); 5156 CheckEntrypointTypes<kQuickInitializeTypeAndVerifyAccess, void*, uint32_t>(); 5157 return; 5158 } 5159 5160 Location out_loc = locations->Out(); 5161 Register out = out_loc.AsRegister<Register>(); 5162 Register current_method = locations->InAt(0).AsRegister<Register>(); 5163 5164 if (cls->IsReferrersClass()) { 5165 DCHECK(!cls->CanCallRuntime()); 5166 DCHECK(!cls->MustGenerateClinitCheck()); 5167 // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_ 5168 GenerateGcRootFieldLoad( 5169 cls, out_loc, current_method, ArtMethod::DeclaringClassOffset().Int32Value()); 5170 } else { 5171 // /* GcRoot<mirror::Class>[] */ out = 5172 // current_method.ptr_sized_fields_->dex_cache_resolved_types_ 5173 __ LoadFromOffset(kLoadWord, 5174 out, 5175 current_method, 5176 ArtMethod::DexCacheResolvedTypesOffset(kArmPointerSize).Int32Value()); 5177 // /* GcRoot<mirror::Class> */ out = out[type_index] 5178 GenerateGcRootFieldLoad(cls, out_loc, out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex())); 5179 5180 if (!cls->IsInDexCache() || cls->MustGenerateClinitCheck()) { 5181 DCHECK(cls->CanCallRuntime()); 5182 SlowPathCode* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathARM( 5183 cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck()); 5184 codegen_->AddSlowPath(slow_path); 5185 if (!cls->IsInDexCache()) { 5186 __ CompareAndBranchIfZero(out, slow_path->GetEntryLabel()); 5187 } 5188 if (cls->MustGenerateClinitCheck()) { 5189 GenerateClassInitializationCheck(slow_path, out); 5190 } else { 5191 __ Bind(slow_path->GetExitLabel()); 5192 } 5193 } 5194 } 5195} 5196 5197void LocationsBuilderARM::VisitClinitCheck(HClinitCheck* check) { 5198 LocationSummary* locations = 5199 new (GetGraph()->GetArena()) LocationSummary(check, LocationSummary::kCallOnSlowPath); 5200 locations->SetInAt(0, Location::RequiresRegister()); 5201 if (check->HasUses()) { 5202 locations->SetOut(Location::SameAsFirstInput()); 5203 } 5204} 5205 5206void InstructionCodeGeneratorARM::VisitClinitCheck(HClinitCheck* check) { 5207 // We assume the class is not null. 5208 SlowPathCode* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathARM( 5209 check->GetLoadClass(), check, check->GetDexPc(), true); 5210 codegen_->AddSlowPath(slow_path); 5211 GenerateClassInitializationCheck(slow_path, 5212 check->GetLocations()->InAt(0).AsRegister<Register>()); 5213} 5214 5215void InstructionCodeGeneratorARM::GenerateClassInitializationCheck( 5216 SlowPathCode* slow_path, Register class_reg) { 5217 __ LoadFromOffset(kLoadWord, IP, class_reg, mirror::Class::StatusOffset().Int32Value()); 5218 __ cmp(IP, ShifterOperand(mirror::Class::kStatusInitialized)); 5219 __ b(slow_path->GetEntryLabel(), LT); 5220 // Even if the initialized flag is set, we may be in a situation where caches are not synced 5221 // properly. Therefore, we do a memory fence. 5222 __ dmb(ISH); 5223 __ Bind(slow_path->GetExitLabel()); 5224} 5225 5226void LocationsBuilderARM::VisitLoadString(HLoadString* load) { 5227 LocationSummary::CallKind call_kind = (!load->IsInDexCache() || kEmitCompilerReadBarrier) 5228 ? LocationSummary::kCallOnSlowPath 5229 : LocationSummary::kNoCall; 5230 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(load, call_kind); 5231 locations->SetInAt(0, Location::RequiresRegister()); 5232 locations->SetOut(Location::RequiresRegister()); 5233} 5234 5235void InstructionCodeGeneratorARM::VisitLoadString(HLoadString* load) { 5236 LocationSummary* locations = load->GetLocations(); 5237 Location out_loc = locations->Out(); 5238 Register out = out_loc.AsRegister<Register>(); 5239 Register current_method = locations->InAt(0).AsRegister<Register>(); 5240 5241 // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_ 5242 GenerateGcRootFieldLoad( 5243 load, out_loc, current_method, ArtMethod::DeclaringClassOffset().Int32Value()); 5244 // /* GcRoot<mirror::String>[] */ out = out->dex_cache_strings_ 5245 __ LoadFromOffset(kLoadWord, out, out, mirror::Class::DexCacheStringsOffset().Int32Value()); 5246 // /* GcRoot<mirror::String> */ out = out[string_index] 5247 GenerateGcRootFieldLoad( 5248 load, out_loc, out, CodeGenerator::GetCacheOffset(load->GetStringIndex())); 5249 5250 if (!load->IsInDexCache()) { 5251 SlowPathCode* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathARM(load); 5252 codegen_->AddSlowPath(slow_path); 5253 __ CompareAndBranchIfZero(out, slow_path->GetEntryLabel()); 5254 __ Bind(slow_path->GetExitLabel()); 5255 } 5256} 5257 5258static int32_t GetExceptionTlsOffset() { 5259 return Thread::ExceptionOffset<kArmWordSize>().Int32Value(); 5260} 5261 5262void LocationsBuilderARM::VisitLoadException(HLoadException* load) { 5263 LocationSummary* locations = 5264 new (GetGraph()->GetArena()) LocationSummary(load, LocationSummary::kNoCall); 5265 locations->SetOut(Location::RequiresRegister()); 5266} 5267 5268void InstructionCodeGeneratorARM::VisitLoadException(HLoadException* load) { 5269 Register out = load->GetLocations()->Out().AsRegister<Register>(); 5270 __ LoadFromOffset(kLoadWord, out, TR, GetExceptionTlsOffset()); 5271} 5272 5273void LocationsBuilderARM::VisitClearException(HClearException* clear) { 5274 new (GetGraph()->GetArena()) LocationSummary(clear, LocationSummary::kNoCall); 5275} 5276 5277void InstructionCodeGeneratorARM::VisitClearException(HClearException* clear ATTRIBUTE_UNUSED) { 5278 __ LoadImmediate(IP, 0); 5279 __ StoreToOffset(kStoreWord, IP, TR, GetExceptionTlsOffset()); 5280} 5281 5282void LocationsBuilderARM::VisitThrow(HThrow* instruction) { 5283 LocationSummary* locations = 5284 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall); 5285 InvokeRuntimeCallingConvention calling_convention; 5286 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); 5287} 5288 5289void InstructionCodeGeneratorARM::VisitThrow(HThrow* instruction) { 5290 codegen_->InvokeRuntime( 5291 QUICK_ENTRY_POINT(pDeliverException), instruction, instruction->GetDexPc(), nullptr); 5292 CheckEntrypointTypes<kQuickDeliverException, void, mirror::Object*>(); 5293} 5294 5295static bool TypeCheckNeedsATemporary(TypeCheckKind type_check_kind) { 5296 return kEmitCompilerReadBarrier && 5297 (kUseBakerReadBarrier || 5298 type_check_kind == TypeCheckKind::kAbstractClassCheck || 5299 type_check_kind == TypeCheckKind::kClassHierarchyCheck || 5300 type_check_kind == TypeCheckKind::kArrayObjectCheck); 5301} 5302 5303void LocationsBuilderARM::VisitInstanceOf(HInstanceOf* instruction) { 5304 LocationSummary::CallKind call_kind = LocationSummary::kNoCall; 5305 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind(); 5306 switch (type_check_kind) { 5307 case TypeCheckKind::kExactCheck: 5308 case TypeCheckKind::kAbstractClassCheck: 5309 case TypeCheckKind::kClassHierarchyCheck: 5310 case TypeCheckKind::kArrayObjectCheck: 5311 call_kind = 5312 kEmitCompilerReadBarrier ? LocationSummary::kCallOnSlowPath : LocationSummary::kNoCall; 5313 break; 5314 case TypeCheckKind::kArrayCheck: 5315 case TypeCheckKind::kUnresolvedCheck: 5316 case TypeCheckKind::kInterfaceCheck: 5317 call_kind = LocationSummary::kCallOnSlowPath; 5318 break; 5319 } 5320 5321 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind); 5322 locations->SetInAt(0, Location::RequiresRegister()); 5323 locations->SetInAt(1, Location::RequiresRegister()); 5324 // The "out" register is used as a temporary, so it overlaps with the inputs. 5325 // Note that TypeCheckSlowPathARM uses this register too. 5326 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); 5327 // When read barriers are enabled, we need a temporary register for 5328 // some cases. 5329 if (TypeCheckNeedsATemporary(type_check_kind)) { 5330 locations->AddTemp(Location::RequiresRegister()); 5331 } 5332} 5333 5334void InstructionCodeGeneratorARM::VisitInstanceOf(HInstanceOf* instruction) { 5335 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind(); 5336 LocationSummary* locations = instruction->GetLocations(); 5337 Location obj_loc = locations->InAt(0); 5338 Register obj = obj_loc.AsRegister<Register>(); 5339 Register cls = locations->InAt(1).AsRegister<Register>(); 5340 Location out_loc = locations->Out(); 5341 Register out = out_loc.AsRegister<Register>(); 5342 Location maybe_temp_loc = TypeCheckNeedsATemporary(type_check_kind) ? 5343 locations->GetTemp(0) : 5344 Location::NoLocation(); 5345 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value(); 5346 uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value(); 5347 uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value(); 5348 uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value(); 5349 Label done, zero; 5350 SlowPathCode* slow_path = nullptr; 5351 5352 // Return 0 if `obj` is null. 5353 // avoid null check if we know obj is not null. 5354 if (instruction->MustDoNullCheck()) { 5355 __ CompareAndBranchIfZero(obj, &zero); 5356 } 5357 5358 // /* HeapReference<Class> */ out = obj->klass_ 5359 GenerateReferenceLoadTwoRegisters(instruction, out_loc, obj_loc, class_offset, maybe_temp_loc); 5360 5361 switch (type_check_kind) { 5362 case TypeCheckKind::kExactCheck: { 5363 __ cmp(out, ShifterOperand(cls)); 5364 // Classes must be equal for the instanceof to succeed. 5365 __ b(&zero, NE); 5366 __ LoadImmediate(out, 1); 5367 __ b(&done); 5368 break; 5369 } 5370 5371 case TypeCheckKind::kAbstractClassCheck: { 5372 // If the class is abstract, we eagerly fetch the super class of the 5373 // object to avoid doing a comparison we know will fail. 5374 Label loop; 5375 __ Bind(&loop); 5376 // /* HeapReference<Class> */ out = out->super_class_ 5377 GenerateReferenceLoadOneRegister(instruction, out_loc, super_offset, maybe_temp_loc); 5378 // If `out` is null, we use it for the result, and jump to `done`. 5379 __ CompareAndBranchIfZero(out, &done); 5380 __ cmp(out, ShifterOperand(cls)); 5381 __ b(&loop, NE); 5382 __ LoadImmediate(out, 1); 5383 if (zero.IsLinked()) { 5384 __ b(&done); 5385 } 5386 break; 5387 } 5388 5389 case TypeCheckKind::kClassHierarchyCheck: { 5390 // Walk over the class hierarchy to find a match. 5391 Label loop, success; 5392 __ Bind(&loop); 5393 __ cmp(out, ShifterOperand(cls)); 5394 __ b(&success, EQ); 5395 // /* HeapReference<Class> */ out = out->super_class_ 5396 GenerateReferenceLoadOneRegister(instruction, out_loc, super_offset, maybe_temp_loc); 5397 __ CompareAndBranchIfNonZero(out, &loop); 5398 // If `out` is null, we use it for the result, and jump to `done`. 5399 __ b(&done); 5400 __ Bind(&success); 5401 __ LoadImmediate(out, 1); 5402 if (zero.IsLinked()) { 5403 __ b(&done); 5404 } 5405 break; 5406 } 5407 5408 case TypeCheckKind::kArrayObjectCheck: { 5409 // Do an exact check. 5410 Label exact_check; 5411 __ cmp(out, ShifterOperand(cls)); 5412 __ b(&exact_check, EQ); 5413 // Otherwise, we need to check that the object's class is a non-primitive array. 5414 // /* HeapReference<Class> */ out = out->component_type_ 5415 GenerateReferenceLoadOneRegister(instruction, out_loc, component_offset, maybe_temp_loc); 5416 // If `out` is null, we use it for the result, and jump to `done`. 5417 __ CompareAndBranchIfZero(out, &done); 5418 __ LoadFromOffset(kLoadUnsignedHalfword, out, out, primitive_offset); 5419 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot"); 5420 __ CompareAndBranchIfNonZero(out, &zero); 5421 __ Bind(&exact_check); 5422 __ LoadImmediate(out, 1); 5423 __ b(&done); 5424 break; 5425 } 5426 5427 case TypeCheckKind::kArrayCheck: { 5428 __ cmp(out, ShifterOperand(cls)); 5429 DCHECK(locations->OnlyCallsOnSlowPath()); 5430 slow_path = new (GetGraph()->GetArena()) TypeCheckSlowPathARM(instruction, 5431 /* is_fatal */ false); 5432 codegen_->AddSlowPath(slow_path); 5433 __ b(slow_path->GetEntryLabel(), NE); 5434 __ LoadImmediate(out, 1); 5435 if (zero.IsLinked()) { 5436 __ b(&done); 5437 } 5438 break; 5439 } 5440 5441 case TypeCheckKind::kUnresolvedCheck: 5442 case TypeCheckKind::kInterfaceCheck: { 5443 // Note that we indeed only call on slow path, but we always go 5444 // into the slow path for the unresolved and interface check 5445 // cases. 5446 // 5447 // We cannot directly call the InstanceofNonTrivial runtime 5448 // entry point without resorting to a type checking slow path 5449 // here (i.e. by calling InvokeRuntime directly), as it would 5450 // require to assign fixed registers for the inputs of this 5451 // HInstanceOf instruction (following the runtime calling 5452 // convention), which might be cluttered by the potential first 5453 // read barrier emission at the beginning of this method. 5454 // 5455 // TODO: Introduce a new runtime entry point taking the object 5456 // to test (instead of its class) as argument, and let it deal 5457 // with the read barrier issues. This will let us refactor this 5458 // case of the `switch` code as it was previously (with a direct 5459 // call to the runtime not using a type checking slow path). 5460 // This should also be beneficial for the other cases above. 5461 DCHECK(locations->OnlyCallsOnSlowPath()); 5462 slow_path = new (GetGraph()->GetArena()) TypeCheckSlowPathARM(instruction, 5463 /* is_fatal */ false); 5464 codegen_->AddSlowPath(slow_path); 5465 __ b(slow_path->GetEntryLabel()); 5466 if (zero.IsLinked()) { 5467 __ b(&done); 5468 } 5469 break; 5470 } 5471 } 5472 5473 if (zero.IsLinked()) { 5474 __ Bind(&zero); 5475 __ LoadImmediate(out, 0); 5476 } 5477 5478 if (done.IsLinked()) { 5479 __ Bind(&done); 5480 } 5481 5482 if (slow_path != nullptr) { 5483 __ Bind(slow_path->GetExitLabel()); 5484 } 5485} 5486 5487void LocationsBuilderARM::VisitCheckCast(HCheckCast* instruction) { 5488 LocationSummary::CallKind call_kind = LocationSummary::kNoCall; 5489 bool throws_into_catch = instruction->CanThrowIntoCatchBlock(); 5490 5491 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind(); 5492 switch (type_check_kind) { 5493 case TypeCheckKind::kExactCheck: 5494 case TypeCheckKind::kAbstractClassCheck: 5495 case TypeCheckKind::kClassHierarchyCheck: 5496 case TypeCheckKind::kArrayObjectCheck: 5497 call_kind = (throws_into_catch || kEmitCompilerReadBarrier) ? 5498 LocationSummary::kCallOnSlowPath : 5499 LocationSummary::kNoCall; // In fact, call on a fatal (non-returning) slow path. 5500 break; 5501 case TypeCheckKind::kArrayCheck: 5502 case TypeCheckKind::kUnresolvedCheck: 5503 case TypeCheckKind::kInterfaceCheck: 5504 call_kind = LocationSummary::kCallOnSlowPath; 5505 break; 5506 } 5507 5508 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind); 5509 locations->SetInAt(0, Location::RequiresRegister()); 5510 locations->SetInAt(1, Location::RequiresRegister()); 5511 // Note that TypeCheckSlowPathARM uses this "temp" register too. 5512 locations->AddTemp(Location::RequiresRegister()); 5513 // When read barriers are enabled, we need an additional temporary 5514 // register for some cases. 5515 if (TypeCheckNeedsATemporary(type_check_kind)) { 5516 locations->AddTemp(Location::RequiresRegister()); 5517 } 5518} 5519 5520void InstructionCodeGeneratorARM::VisitCheckCast(HCheckCast* instruction) { 5521 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind(); 5522 LocationSummary* locations = instruction->GetLocations(); 5523 Location obj_loc = locations->InAt(0); 5524 Register obj = obj_loc.AsRegister<Register>(); 5525 Register cls = locations->InAt(1).AsRegister<Register>(); 5526 Location temp_loc = locations->GetTemp(0); 5527 Register temp = temp_loc.AsRegister<Register>(); 5528 Location maybe_temp2_loc = TypeCheckNeedsATemporary(type_check_kind) ? 5529 locations->GetTemp(1) : 5530 Location::NoLocation(); 5531 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value(); 5532 uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value(); 5533 uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value(); 5534 uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value(); 5535 5536 bool is_type_check_slow_path_fatal = 5537 (type_check_kind == TypeCheckKind::kExactCheck || 5538 type_check_kind == TypeCheckKind::kAbstractClassCheck || 5539 type_check_kind == TypeCheckKind::kClassHierarchyCheck || 5540 type_check_kind == TypeCheckKind::kArrayObjectCheck) && 5541 !instruction->CanThrowIntoCatchBlock(); 5542 SlowPathCode* type_check_slow_path = 5543 new (GetGraph()->GetArena()) TypeCheckSlowPathARM(instruction, 5544 is_type_check_slow_path_fatal); 5545 codegen_->AddSlowPath(type_check_slow_path); 5546 5547 Label done; 5548 // Avoid null check if we know obj is not null. 5549 if (instruction->MustDoNullCheck()) { 5550 __ CompareAndBranchIfZero(obj, &done); 5551 } 5552 5553 // /* HeapReference<Class> */ temp = obj->klass_ 5554 GenerateReferenceLoadTwoRegisters(instruction, temp_loc, obj_loc, class_offset, maybe_temp2_loc); 5555 5556 switch (type_check_kind) { 5557 case TypeCheckKind::kExactCheck: 5558 case TypeCheckKind::kArrayCheck: { 5559 __ cmp(temp, ShifterOperand(cls)); 5560 // Jump to slow path for throwing the exception or doing a 5561 // more involved array check. 5562 __ b(type_check_slow_path->GetEntryLabel(), NE); 5563 break; 5564 } 5565 5566 case TypeCheckKind::kAbstractClassCheck: { 5567 // If the class is abstract, we eagerly fetch the super class of the 5568 // object to avoid doing a comparison we know will fail. 5569 Label loop, compare_classes; 5570 __ Bind(&loop); 5571 // /* HeapReference<Class> */ temp = temp->super_class_ 5572 GenerateReferenceLoadOneRegister(instruction, temp_loc, super_offset, maybe_temp2_loc); 5573 5574 // If the class reference currently in `temp` is not null, jump 5575 // to the `compare_classes` label to compare it with the checked 5576 // class. 5577 __ CompareAndBranchIfNonZero(temp, &compare_classes); 5578 // Otherwise, jump to the slow path to throw the exception. 5579 // 5580 // But before, move back the object's class into `temp` before 5581 // going into the slow path, as it has been overwritten in the 5582 // meantime. 5583 // /* HeapReference<Class> */ temp = obj->klass_ 5584 GenerateReferenceLoadTwoRegisters( 5585 instruction, temp_loc, obj_loc, class_offset, maybe_temp2_loc); 5586 __ b(type_check_slow_path->GetEntryLabel()); 5587 5588 __ Bind(&compare_classes); 5589 __ cmp(temp, ShifterOperand(cls)); 5590 __ b(&loop, NE); 5591 break; 5592 } 5593 5594 case TypeCheckKind::kClassHierarchyCheck: { 5595 // Walk over the class hierarchy to find a match. 5596 Label loop; 5597 __ Bind(&loop); 5598 __ cmp(temp, ShifterOperand(cls)); 5599 __ b(&done, EQ); 5600 5601 // /* HeapReference<Class> */ temp = temp->super_class_ 5602 GenerateReferenceLoadOneRegister(instruction, temp_loc, super_offset, maybe_temp2_loc); 5603 5604 // If the class reference currently in `temp` is not null, jump 5605 // back at the beginning of the loop. 5606 __ CompareAndBranchIfNonZero(temp, &loop); 5607 // Otherwise, jump to the slow path to throw the exception. 5608 // 5609 // But before, move back the object's class into `temp` before 5610 // going into the slow path, as it has been overwritten in the 5611 // meantime. 5612 // /* HeapReference<Class> */ temp = obj->klass_ 5613 GenerateReferenceLoadTwoRegisters( 5614 instruction, temp_loc, obj_loc, class_offset, maybe_temp2_loc); 5615 __ b(type_check_slow_path->GetEntryLabel()); 5616 break; 5617 } 5618 5619 case TypeCheckKind::kArrayObjectCheck: { 5620 // Do an exact check. 5621 Label check_non_primitive_component_type; 5622 __ cmp(temp, ShifterOperand(cls)); 5623 __ b(&done, EQ); 5624 5625 // Otherwise, we need to check that the object's class is a non-primitive array. 5626 // /* HeapReference<Class> */ temp = temp->component_type_ 5627 GenerateReferenceLoadOneRegister(instruction, temp_loc, component_offset, maybe_temp2_loc); 5628 5629 // If the component type is not null (i.e. the object is indeed 5630 // an array), jump to label `check_non_primitive_component_type` 5631 // to further check that this component type is not a primitive 5632 // type. 5633 __ CompareAndBranchIfNonZero(temp, &check_non_primitive_component_type); 5634 // Otherwise, jump to the slow path to throw the exception. 5635 // 5636 // But before, move back the object's class into `temp` before 5637 // going into the slow path, as it has been overwritten in the 5638 // meantime. 5639 // /* HeapReference<Class> */ temp = obj->klass_ 5640 GenerateReferenceLoadTwoRegisters( 5641 instruction, temp_loc, obj_loc, class_offset, maybe_temp2_loc); 5642 __ b(type_check_slow_path->GetEntryLabel()); 5643 5644 __ Bind(&check_non_primitive_component_type); 5645 __ LoadFromOffset(kLoadUnsignedHalfword, temp, temp, primitive_offset); 5646 static_assert(Primitive::kPrimNot == 0, "Expected 0 for art::Primitive::kPrimNot"); 5647 __ CompareAndBranchIfZero(temp, &done); 5648 // Same comment as above regarding `temp` and the slow path. 5649 // /* HeapReference<Class> */ temp = obj->klass_ 5650 GenerateReferenceLoadTwoRegisters( 5651 instruction, temp_loc, obj_loc, class_offset, maybe_temp2_loc); 5652 __ b(type_check_slow_path->GetEntryLabel()); 5653 break; 5654 } 5655 5656 case TypeCheckKind::kUnresolvedCheck: 5657 case TypeCheckKind::kInterfaceCheck: 5658 // We always go into the type check slow path for the unresolved 5659 // and interface check cases. 5660 // 5661 // We cannot directly call the CheckCast runtime entry point 5662 // without resorting to a type checking slow path here (i.e. by 5663 // calling InvokeRuntime directly), as it would require to 5664 // assign fixed registers for the inputs of this HInstanceOf 5665 // instruction (following the runtime calling convention), which 5666 // might be cluttered by the potential first read barrier 5667 // emission at the beginning of this method. 5668 // 5669 // TODO: Introduce a new runtime entry point taking the object 5670 // to test (instead of its class) as argument, and let it deal 5671 // with the read barrier issues. This will let us refactor this 5672 // case of the `switch` code as it was previously (with a direct 5673 // call to the runtime not using a type checking slow path). 5674 // This should also be beneficial for the other cases above. 5675 __ b(type_check_slow_path->GetEntryLabel()); 5676 break; 5677 } 5678 __ Bind(&done); 5679 5680 __ Bind(type_check_slow_path->GetExitLabel()); 5681} 5682 5683void LocationsBuilderARM::VisitMonitorOperation(HMonitorOperation* instruction) { 5684 LocationSummary* locations = 5685 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall); 5686 InvokeRuntimeCallingConvention calling_convention; 5687 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); 5688} 5689 5690void InstructionCodeGeneratorARM::VisitMonitorOperation(HMonitorOperation* instruction) { 5691 codegen_->InvokeRuntime(instruction->IsEnter() 5692 ? QUICK_ENTRY_POINT(pLockObject) : QUICK_ENTRY_POINT(pUnlockObject), 5693 instruction, 5694 instruction->GetDexPc(), 5695 nullptr); 5696 if (instruction->IsEnter()) { 5697 CheckEntrypointTypes<kQuickLockObject, void, mirror::Object*>(); 5698 } else { 5699 CheckEntrypointTypes<kQuickUnlockObject, void, mirror::Object*>(); 5700 } 5701} 5702 5703void LocationsBuilderARM::VisitAnd(HAnd* instruction) { HandleBitwiseOperation(instruction, AND); } 5704void LocationsBuilderARM::VisitOr(HOr* instruction) { HandleBitwiseOperation(instruction, ORR); } 5705void LocationsBuilderARM::VisitXor(HXor* instruction) { HandleBitwiseOperation(instruction, EOR); } 5706 5707void LocationsBuilderARM::HandleBitwiseOperation(HBinaryOperation* instruction, Opcode opcode) { 5708 LocationSummary* locations = 5709 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); 5710 DCHECK(instruction->GetResultType() == Primitive::kPrimInt 5711 || instruction->GetResultType() == Primitive::kPrimLong); 5712 // Note: GVN reorders commutative operations to have the constant on the right hand side. 5713 locations->SetInAt(0, Location::RequiresRegister()); 5714 locations->SetInAt(1, ArmEncodableConstantOrRegister(instruction->InputAt(1), opcode)); 5715 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 5716} 5717 5718void InstructionCodeGeneratorARM::VisitAnd(HAnd* instruction) { 5719 HandleBitwiseOperation(instruction); 5720} 5721 5722void InstructionCodeGeneratorARM::VisitOr(HOr* instruction) { 5723 HandleBitwiseOperation(instruction); 5724} 5725 5726void InstructionCodeGeneratorARM::VisitXor(HXor* instruction) { 5727 HandleBitwiseOperation(instruction); 5728} 5729 5730void InstructionCodeGeneratorARM::GenerateAndConst(Register out, Register first, uint32_t value) { 5731 // Optimize special cases for individual halfs of `and-long` (`and` is simplified earlier). 5732 if (value == 0xffffffffu) { 5733 if (out != first) { 5734 __ mov(out, ShifterOperand(first)); 5735 } 5736 return; 5737 } 5738 if (value == 0u) { 5739 __ mov(out, ShifterOperand(0)); 5740 return; 5741 } 5742 ShifterOperand so; 5743 if (__ ShifterOperandCanHold(kNoRegister, kNoRegister, AND, value, &so)) { 5744 __ and_(out, first, so); 5745 } else { 5746 DCHECK(__ ShifterOperandCanHold(kNoRegister, kNoRegister, BIC, ~value, &so)); 5747 __ bic(out, first, ShifterOperand(~value)); 5748 } 5749} 5750 5751void InstructionCodeGeneratorARM::GenerateOrrConst(Register out, Register first, uint32_t value) { 5752 // Optimize special cases for individual halfs of `or-long` (`or` is simplified earlier). 5753 if (value == 0u) { 5754 if (out != first) { 5755 __ mov(out, ShifterOperand(first)); 5756 } 5757 return; 5758 } 5759 if (value == 0xffffffffu) { 5760 __ mvn(out, ShifterOperand(0)); 5761 return; 5762 } 5763 ShifterOperand so; 5764 if (__ ShifterOperandCanHold(kNoRegister, kNoRegister, ORR, value, &so)) { 5765 __ orr(out, first, so); 5766 } else { 5767 DCHECK(__ ShifterOperandCanHold(kNoRegister, kNoRegister, ORN, ~value, &so)); 5768 __ orn(out, first, ShifterOperand(~value)); 5769 } 5770} 5771 5772void InstructionCodeGeneratorARM::GenerateEorConst(Register out, Register first, uint32_t value) { 5773 // Optimize special case for individual halfs of `xor-long` (`xor` is simplified earlier). 5774 if (value == 0u) { 5775 if (out != first) { 5776 __ mov(out, ShifterOperand(first)); 5777 } 5778 return; 5779 } 5780 __ eor(out, first, ShifterOperand(value)); 5781} 5782 5783void InstructionCodeGeneratorARM::HandleBitwiseOperation(HBinaryOperation* instruction) { 5784 LocationSummary* locations = instruction->GetLocations(); 5785 Location first = locations->InAt(0); 5786 Location second = locations->InAt(1); 5787 Location out = locations->Out(); 5788 5789 if (second.IsConstant()) { 5790 uint64_t value = static_cast<uint64_t>(Int64FromConstant(second.GetConstant())); 5791 uint32_t value_low = Low32Bits(value); 5792 if (instruction->GetResultType() == Primitive::kPrimInt) { 5793 Register first_reg = first.AsRegister<Register>(); 5794 Register out_reg = out.AsRegister<Register>(); 5795 if (instruction->IsAnd()) { 5796 GenerateAndConst(out_reg, first_reg, value_low); 5797 } else if (instruction->IsOr()) { 5798 GenerateOrrConst(out_reg, first_reg, value_low); 5799 } else { 5800 DCHECK(instruction->IsXor()); 5801 GenerateEorConst(out_reg, first_reg, value_low); 5802 } 5803 } else { 5804 DCHECK_EQ(instruction->GetResultType(), Primitive::kPrimLong); 5805 uint32_t value_high = High32Bits(value); 5806 Register first_low = first.AsRegisterPairLow<Register>(); 5807 Register first_high = first.AsRegisterPairHigh<Register>(); 5808 Register out_low = out.AsRegisterPairLow<Register>(); 5809 Register out_high = out.AsRegisterPairHigh<Register>(); 5810 if (instruction->IsAnd()) { 5811 GenerateAndConst(out_low, first_low, value_low); 5812 GenerateAndConst(out_high, first_high, value_high); 5813 } else if (instruction->IsOr()) { 5814 GenerateOrrConst(out_low, first_low, value_low); 5815 GenerateOrrConst(out_high, first_high, value_high); 5816 } else { 5817 DCHECK(instruction->IsXor()); 5818 GenerateEorConst(out_low, first_low, value_low); 5819 GenerateEorConst(out_high, first_high, value_high); 5820 } 5821 } 5822 return; 5823 } 5824 5825 if (instruction->GetResultType() == Primitive::kPrimInt) { 5826 Register first_reg = first.AsRegister<Register>(); 5827 ShifterOperand second_reg(second.AsRegister<Register>()); 5828 Register out_reg = out.AsRegister<Register>(); 5829 if (instruction->IsAnd()) { 5830 __ and_(out_reg, first_reg, second_reg); 5831 } else if (instruction->IsOr()) { 5832 __ orr(out_reg, first_reg, second_reg); 5833 } else { 5834 DCHECK(instruction->IsXor()); 5835 __ eor(out_reg, first_reg, second_reg); 5836 } 5837 } else { 5838 DCHECK_EQ(instruction->GetResultType(), Primitive::kPrimLong); 5839 Register first_low = first.AsRegisterPairLow<Register>(); 5840 Register first_high = first.AsRegisterPairHigh<Register>(); 5841 ShifterOperand second_low(second.AsRegisterPairLow<Register>()); 5842 ShifterOperand second_high(second.AsRegisterPairHigh<Register>()); 5843 Register out_low = out.AsRegisterPairLow<Register>(); 5844 Register out_high = out.AsRegisterPairHigh<Register>(); 5845 if (instruction->IsAnd()) { 5846 __ and_(out_low, first_low, second_low); 5847 __ and_(out_high, first_high, second_high); 5848 } else if (instruction->IsOr()) { 5849 __ orr(out_low, first_low, second_low); 5850 __ orr(out_high, first_high, second_high); 5851 } else { 5852 DCHECK(instruction->IsXor()); 5853 __ eor(out_low, first_low, second_low); 5854 __ eor(out_high, first_high, second_high); 5855 } 5856 } 5857} 5858 5859void InstructionCodeGeneratorARM::GenerateReferenceLoadOneRegister(HInstruction* instruction, 5860 Location out, 5861 uint32_t offset, 5862 Location maybe_temp) { 5863 Register out_reg = out.AsRegister<Register>(); 5864 if (kEmitCompilerReadBarrier) { 5865 DCHECK(maybe_temp.IsRegister()) << maybe_temp; 5866 if (kUseBakerReadBarrier) { 5867 // Load with fast path based Baker's read barrier. 5868 // /* HeapReference<Object> */ out = *(out + offset) 5869 codegen_->GenerateFieldLoadWithBakerReadBarrier( 5870 instruction, out, out_reg, offset, maybe_temp, /* needs_null_check */ false); 5871 } else { 5872 // Load with slow path based read barrier. 5873 // Save the value of `out` into `maybe_temp` before overwriting it 5874 // in the following move operation, as we will need it for the 5875 // read barrier below. 5876 __ Mov(maybe_temp.AsRegister<Register>(), out_reg); 5877 // /* HeapReference<Object> */ out = *(out + offset) 5878 __ LoadFromOffset(kLoadWord, out_reg, out_reg, offset); 5879 codegen_->GenerateReadBarrierSlow(instruction, out, out, maybe_temp, offset); 5880 } 5881 } else { 5882 // Plain load with no read barrier. 5883 // /* HeapReference<Object> */ out = *(out + offset) 5884 __ LoadFromOffset(kLoadWord, out_reg, out_reg, offset); 5885 __ MaybeUnpoisonHeapReference(out_reg); 5886 } 5887} 5888 5889void InstructionCodeGeneratorARM::GenerateReferenceLoadTwoRegisters(HInstruction* instruction, 5890 Location out, 5891 Location obj, 5892 uint32_t offset, 5893 Location maybe_temp) { 5894 Register out_reg = out.AsRegister<Register>(); 5895 Register obj_reg = obj.AsRegister<Register>(); 5896 if (kEmitCompilerReadBarrier) { 5897 if (kUseBakerReadBarrier) { 5898 DCHECK(maybe_temp.IsRegister()) << maybe_temp; 5899 // Load with fast path based Baker's read barrier. 5900 // /* HeapReference<Object> */ out = *(obj + offset) 5901 codegen_->GenerateFieldLoadWithBakerReadBarrier( 5902 instruction, out, obj_reg, offset, maybe_temp, /* needs_null_check */ false); 5903 } else { 5904 // Load with slow path based read barrier. 5905 // /* HeapReference<Object> */ out = *(obj + offset) 5906 __ LoadFromOffset(kLoadWord, out_reg, obj_reg, offset); 5907 codegen_->GenerateReadBarrierSlow(instruction, out, out, obj, offset); 5908 } 5909 } else { 5910 // Plain load with no read barrier. 5911 // /* HeapReference<Object> */ out = *(obj + offset) 5912 __ LoadFromOffset(kLoadWord, out_reg, obj_reg, offset); 5913 __ MaybeUnpoisonHeapReference(out_reg); 5914 } 5915} 5916 5917void InstructionCodeGeneratorARM::GenerateGcRootFieldLoad(HInstruction* instruction, 5918 Location root, 5919 Register obj, 5920 uint32_t offset) { 5921 Register root_reg = root.AsRegister<Register>(); 5922 if (kEmitCompilerReadBarrier) { 5923 if (kUseBakerReadBarrier) { 5924 // Fast path implementation of art::ReadBarrier::BarrierForRoot when 5925 // Baker's read barrier are used: 5926 // 5927 // root = obj.field; 5928 // if (Thread::Current()->GetIsGcMarking()) { 5929 // root = ReadBarrier::Mark(root) 5930 // } 5931 5932 // /* GcRoot<mirror::Object> */ root = *(obj + offset) 5933 __ LoadFromOffset(kLoadWord, root_reg, obj, offset); 5934 static_assert( 5935 sizeof(mirror::CompressedReference<mirror::Object>) == sizeof(GcRoot<mirror::Object>), 5936 "art::mirror::CompressedReference<mirror::Object> and art::GcRoot<mirror::Object> " 5937 "have different sizes."); 5938 static_assert(sizeof(mirror::CompressedReference<mirror::Object>) == sizeof(int32_t), 5939 "art::mirror::CompressedReference<mirror::Object> and int32_t " 5940 "have different sizes."); 5941 5942 // Slow path used to mark the GC root `root`. 5943 SlowPathCode* slow_path = 5944 new (GetGraph()->GetArena()) ReadBarrierMarkSlowPathARM(instruction, root, root); 5945 codegen_->AddSlowPath(slow_path); 5946 5947 // IP = Thread::Current()->GetIsGcMarking() 5948 __ LoadFromOffset( 5949 kLoadWord, IP, TR, Thread::IsGcMarkingOffset<kArmWordSize>().Int32Value()); 5950 __ CompareAndBranchIfNonZero(IP, slow_path->GetEntryLabel()); 5951 __ Bind(slow_path->GetExitLabel()); 5952 } else { 5953 // GC root loaded through a slow path for read barriers other 5954 // than Baker's. 5955 // /* GcRoot<mirror::Object>* */ root = obj + offset 5956 __ AddConstant(root_reg, obj, offset); 5957 // /* mirror::Object* */ root = root->Read() 5958 codegen_->GenerateReadBarrierForRootSlow(instruction, root, root); 5959 } 5960 } else { 5961 // Plain GC root load with no read barrier. 5962 // /* GcRoot<mirror::Object> */ root = *(obj + offset) 5963 __ LoadFromOffset(kLoadWord, root_reg, obj, offset); 5964 // Note that GC roots are not affected by heap poisoning, thus we 5965 // do not have to unpoison `root_reg` here. 5966 } 5967} 5968 5969void CodeGeneratorARM::GenerateFieldLoadWithBakerReadBarrier(HInstruction* instruction, 5970 Location ref, 5971 Register obj, 5972 uint32_t offset, 5973 Location temp, 5974 bool needs_null_check) { 5975 DCHECK(kEmitCompilerReadBarrier); 5976 DCHECK(kUseBakerReadBarrier); 5977 5978 // /* HeapReference<Object> */ ref = *(obj + offset) 5979 Location no_index = Location::NoLocation(); 5980 GenerateReferenceLoadWithBakerReadBarrier( 5981 instruction, ref, obj, offset, no_index, temp, needs_null_check); 5982} 5983 5984void CodeGeneratorARM::GenerateArrayLoadWithBakerReadBarrier(HInstruction* instruction, 5985 Location ref, 5986 Register obj, 5987 uint32_t data_offset, 5988 Location index, 5989 Location temp, 5990 bool needs_null_check) { 5991 DCHECK(kEmitCompilerReadBarrier); 5992 DCHECK(kUseBakerReadBarrier); 5993 5994 // /* HeapReference<Object> */ ref = 5995 // *(obj + data_offset + index * sizeof(HeapReference<Object>)) 5996 GenerateReferenceLoadWithBakerReadBarrier( 5997 instruction, ref, obj, data_offset, index, temp, needs_null_check); 5998} 5999 6000void CodeGeneratorARM::GenerateReferenceLoadWithBakerReadBarrier(HInstruction* instruction, 6001 Location ref, 6002 Register obj, 6003 uint32_t offset, 6004 Location index, 6005 Location temp, 6006 bool needs_null_check) { 6007 DCHECK(kEmitCompilerReadBarrier); 6008 DCHECK(kUseBakerReadBarrier); 6009 6010 // In slow path based read barriers, the read barrier call is 6011 // inserted after the original load. However, in fast path based 6012 // Baker's read barriers, we need to perform the load of 6013 // mirror::Object::monitor_ *before* the original reference load. 6014 // This load-load ordering is required by the read barrier. 6015 // The fast path/slow path (for Baker's algorithm) should look like: 6016 // 6017 // uint32_t rb_state = Lockword(obj->monitor_).ReadBarrierState(); 6018 // lfence; // Load fence or artificial data dependency to prevent load-load reordering 6019 // HeapReference<Object> ref = *src; // Original reference load. 6020 // bool is_gray = (rb_state == ReadBarrier::gray_ptr_); 6021 // if (is_gray) { 6022 // ref = ReadBarrier::Mark(ref); // Performed by runtime entrypoint slow path. 6023 // } 6024 // 6025 // Note: the original implementation in ReadBarrier::Barrier is 6026 // slightly more complex as it performs additional checks that we do 6027 // not do here for performance reasons. 6028 6029 Register ref_reg = ref.AsRegister<Register>(); 6030 Register temp_reg = temp.AsRegister<Register>(); 6031 uint32_t monitor_offset = mirror::Object::MonitorOffset().Int32Value(); 6032 6033 // /* int32_t */ monitor = obj->monitor_ 6034 __ LoadFromOffset(kLoadWord, temp_reg, obj, monitor_offset); 6035 if (needs_null_check) { 6036 MaybeRecordImplicitNullCheck(instruction); 6037 } 6038 // /* LockWord */ lock_word = LockWord(monitor) 6039 static_assert(sizeof(LockWord) == sizeof(int32_t), 6040 "art::LockWord and int32_t have different sizes."); 6041 // /* uint32_t */ rb_state = lock_word.ReadBarrierState() 6042 __ Lsr(temp_reg, temp_reg, LockWord::kReadBarrierStateShift); 6043 __ and_(temp_reg, temp_reg, ShifterOperand(LockWord::kReadBarrierStateMask)); 6044 static_assert( 6045 LockWord::kReadBarrierStateMask == ReadBarrier::rb_ptr_mask_, 6046 "art::LockWord::kReadBarrierStateMask is not equal to art::ReadBarrier::rb_ptr_mask_."); 6047 6048 // Introduce a dependency on the high bits of rb_state, which shall 6049 // be all zeroes, to prevent load-load reordering, and without using 6050 // a memory barrier (which would be more expensive). 6051 // IP = rb_state & ~LockWord::kReadBarrierStateMask = 0 6052 __ bic(IP, temp_reg, ShifterOperand(LockWord::kReadBarrierStateMask)); 6053 // obj is unchanged by this operation, but its value now depends on 6054 // IP, which depends on temp_reg. 6055 __ add(obj, obj, ShifterOperand(IP)); 6056 6057 // The actual reference load. 6058 if (index.IsValid()) { 6059 static_assert( 6060 sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t), 6061 "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes."); 6062 // /* HeapReference<Object> */ ref = 6063 // *(obj + offset + index * sizeof(HeapReference<Object>)) 6064 if (index.IsConstant()) { 6065 size_t computed_offset = 6066 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + offset; 6067 __ LoadFromOffset(kLoadWord, ref_reg, obj, computed_offset); 6068 } else { 6069 __ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4)); 6070 __ LoadFromOffset(kLoadWord, ref_reg, IP, offset); 6071 } 6072 } else { 6073 // /* HeapReference<Object> */ ref = *(obj + offset) 6074 __ LoadFromOffset(kLoadWord, ref_reg, obj, offset); 6075 } 6076 6077 // Object* ref = ref_addr->AsMirrorPtr() 6078 __ MaybeUnpoisonHeapReference(ref_reg); 6079 6080 // Slow path used to mark the object `ref` when it is gray. 6081 SlowPathCode* slow_path = 6082 new (GetGraph()->GetArena()) ReadBarrierMarkSlowPathARM(instruction, ref, ref); 6083 AddSlowPath(slow_path); 6084 6085 // if (rb_state == ReadBarrier::gray_ptr_) 6086 // ref = ReadBarrier::Mark(ref); 6087 __ cmp(temp_reg, ShifterOperand(ReadBarrier::gray_ptr_)); 6088 __ b(slow_path->GetEntryLabel(), EQ); 6089 __ Bind(slow_path->GetExitLabel()); 6090} 6091 6092void CodeGeneratorARM::GenerateReadBarrierSlow(HInstruction* instruction, 6093 Location out, 6094 Location ref, 6095 Location obj, 6096 uint32_t offset, 6097 Location index) { 6098 DCHECK(kEmitCompilerReadBarrier); 6099 6100 // Insert a slow path based read barrier *after* the reference load. 6101 // 6102 // If heap poisoning is enabled, the unpoisoning of the loaded 6103 // reference will be carried out by the runtime within the slow 6104 // path. 6105 // 6106 // Note that `ref` currently does not get unpoisoned (when heap 6107 // poisoning is enabled), which is alright as the `ref` argument is 6108 // not used by the artReadBarrierSlow entry point. 6109 // 6110 // TODO: Unpoison `ref` when it is used by artReadBarrierSlow. 6111 SlowPathCode* slow_path = new (GetGraph()->GetArena()) 6112 ReadBarrierForHeapReferenceSlowPathARM(instruction, out, ref, obj, offset, index); 6113 AddSlowPath(slow_path); 6114 6115 __ b(slow_path->GetEntryLabel()); 6116 __ Bind(slow_path->GetExitLabel()); 6117} 6118 6119void CodeGeneratorARM::MaybeGenerateReadBarrierSlow(HInstruction* instruction, 6120 Location out, 6121 Location ref, 6122 Location obj, 6123 uint32_t offset, 6124 Location index) { 6125 if (kEmitCompilerReadBarrier) { 6126 // Baker's read barriers shall be handled by the fast path 6127 // (CodeGeneratorARM::GenerateReferenceLoadWithBakerReadBarrier). 6128 DCHECK(!kUseBakerReadBarrier); 6129 // If heap poisoning is enabled, unpoisoning will be taken care of 6130 // by the runtime within the slow path. 6131 GenerateReadBarrierSlow(instruction, out, ref, obj, offset, index); 6132 } else if (kPoisonHeapReferences) { 6133 __ UnpoisonHeapReference(out.AsRegister<Register>()); 6134 } 6135} 6136 6137void CodeGeneratorARM::GenerateReadBarrierForRootSlow(HInstruction* instruction, 6138 Location out, 6139 Location root) { 6140 DCHECK(kEmitCompilerReadBarrier); 6141 6142 // Insert a slow path based read barrier *after* the GC root load. 6143 // 6144 // Note that GC roots are not affected by heap poisoning, so we do 6145 // not need to do anything special for this here. 6146 SlowPathCode* slow_path = 6147 new (GetGraph()->GetArena()) ReadBarrierForRootSlowPathARM(instruction, out, root); 6148 AddSlowPath(slow_path); 6149 6150 __ b(slow_path->GetEntryLabel()); 6151 __ Bind(slow_path->GetExitLabel()); 6152} 6153 6154HInvokeStaticOrDirect::DispatchInfo CodeGeneratorARM::GetSupportedInvokeStaticOrDirectDispatch( 6155 const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info, 6156 MethodReference target_method) { 6157 HInvokeStaticOrDirect::DispatchInfo dispatch_info = desired_dispatch_info; 6158 // We disable pc-relative load when there is an irreducible loop, as the optimization 6159 // is incompatible with it. 6160 if (GetGraph()->HasIrreducibleLoops() && 6161 (dispatch_info.method_load_kind == 6162 HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative)) { 6163 dispatch_info.method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod; 6164 } 6165 6166 if (dispatch_info.code_ptr_location == HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative) { 6167 const DexFile& outer_dex_file = GetGraph()->GetDexFile(); 6168 if (&outer_dex_file != target_method.dex_file) { 6169 // Calls across dex files are more likely to exceed the available BL range, 6170 // so use absolute patch with fixup if available and kCallArtMethod otherwise. 6171 HInvokeStaticOrDirect::CodePtrLocation code_ptr_location = 6172 (desired_dispatch_info.method_load_kind == 6173 HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup) 6174 ? HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup 6175 : HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod; 6176 return HInvokeStaticOrDirect::DispatchInfo { 6177 dispatch_info.method_load_kind, 6178 code_ptr_location, 6179 dispatch_info.method_load_data, 6180 0u 6181 }; 6182 } 6183 } 6184 return dispatch_info; 6185} 6186 6187Register CodeGeneratorARM::GetInvokeStaticOrDirectExtraParameter(HInvokeStaticOrDirect* invoke, 6188 Register temp) { 6189 DCHECK_EQ(invoke->InputCount(), invoke->GetNumberOfArguments() + 1u); 6190 Location location = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex()); 6191 if (!invoke->GetLocations()->Intrinsified()) { 6192 return location.AsRegister<Register>(); 6193 } 6194 // For intrinsics we allow any location, so it may be on the stack. 6195 if (!location.IsRegister()) { 6196 __ LoadFromOffset(kLoadWord, temp, SP, location.GetStackIndex()); 6197 return temp; 6198 } 6199 // For register locations, check if the register was saved. If so, get it from the stack. 6200 // Note: There is a chance that the register was saved but not overwritten, so we could 6201 // save one load. However, since this is just an intrinsic slow path we prefer this 6202 // simple and more robust approach rather that trying to determine if that's the case. 6203 SlowPathCode* slow_path = GetCurrentSlowPath(); 6204 DCHECK(slow_path != nullptr); // For intrinsified invokes the call is emitted on the slow path. 6205 if (slow_path->IsCoreRegisterSaved(location.AsRegister<Register>())) { 6206 int stack_offset = slow_path->GetStackOffsetOfCoreRegister(location.AsRegister<Register>()); 6207 __ LoadFromOffset(kLoadWord, temp, SP, stack_offset); 6208 return temp; 6209 } 6210 return location.AsRegister<Register>(); 6211} 6212 6213void CodeGeneratorARM::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp) { 6214 // For better instruction scheduling we load the direct code pointer before the method pointer. 6215 switch (invoke->GetCodePtrLocation()) { 6216 case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup: 6217 // LR = code address from literal pool with link-time patch. 6218 __ LoadLiteral(LR, DeduplicateMethodCodeLiteral(invoke->GetTargetMethod())); 6219 break; 6220 case HInvokeStaticOrDirect::CodePtrLocation::kCallDirect: 6221 // LR = invoke->GetDirectCodePtr(); 6222 __ LoadImmediate(LR, invoke->GetDirectCodePtr()); 6223 break; 6224 default: 6225 break; 6226 } 6227 6228 Location callee_method = temp; // For all kinds except kRecursive, callee will be in temp. 6229 switch (invoke->GetMethodLoadKind()) { 6230 case HInvokeStaticOrDirect::MethodLoadKind::kStringInit: 6231 // temp = thread->string_init_entrypoint 6232 __ LoadFromOffset(kLoadWord, temp.AsRegister<Register>(), TR, invoke->GetStringInitOffset()); 6233 break; 6234 case HInvokeStaticOrDirect::MethodLoadKind::kRecursive: 6235 callee_method = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex()); 6236 break; 6237 case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress: 6238 __ LoadImmediate(temp.AsRegister<Register>(), invoke->GetMethodAddress()); 6239 break; 6240 case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup: 6241 __ LoadLiteral(temp.AsRegister<Register>(), 6242 DeduplicateMethodAddressLiteral(invoke->GetTargetMethod())); 6243 break; 6244 case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative: { 6245 HArmDexCacheArraysBase* base = 6246 invoke->InputAt(invoke->GetSpecialInputIndex())->AsArmDexCacheArraysBase(); 6247 Register base_reg = GetInvokeStaticOrDirectExtraParameter(invoke, 6248 temp.AsRegister<Register>()); 6249 int32_t offset = invoke->GetDexCacheArrayOffset() - base->GetElementOffset(); 6250 __ LoadFromOffset(kLoadWord, temp.AsRegister<Register>(), base_reg, offset); 6251 break; 6252 } 6253 case HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod: { 6254 Location current_method = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex()); 6255 Register method_reg; 6256 Register reg = temp.AsRegister<Register>(); 6257 if (current_method.IsRegister()) { 6258 method_reg = current_method.AsRegister<Register>(); 6259 } else { 6260 DCHECK(invoke->GetLocations()->Intrinsified()); 6261 DCHECK(!current_method.IsValid()); 6262 method_reg = reg; 6263 __ LoadFromOffset(kLoadWord, reg, SP, kCurrentMethodStackOffset); 6264 } 6265 // /* ArtMethod*[] */ temp = temp.ptr_sized_fields_->dex_cache_resolved_methods_; 6266 __ LoadFromOffset(kLoadWord, 6267 reg, 6268 method_reg, 6269 ArtMethod::DexCacheResolvedMethodsOffset(kArmPointerSize).Int32Value()); 6270 // temp = temp[index_in_cache] 6271 uint32_t index_in_cache = invoke->GetTargetMethod().dex_method_index; 6272 __ LoadFromOffset(kLoadWord, reg, reg, CodeGenerator::GetCachePointerOffset(index_in_cache)); 6273 break; 6274 } 6275 } 6276 6277 switch (invoke->GetCodePtrLocation()) { 6278 case HInvokeStaticOrDirect::CodePtrLocation::kCallSelf: 6279 __ bl(GetFrameEntryLabel()); 6280 break; 6281 case HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative: 6282 relative_call_patches_.emplace_back(invoke->GetTargetMethod()); 6283 __ BindTrackedLabel(&relative_call_patches_.back().label); 6284 // Arbitrarily branch to the BL itself, override at link time. 6285 __ bl(&relative_call_patches_.back().label); 6286 break; 6287 case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup: 6288 case HInvokeStaticOrDirect::CodePtrLocation::kCallDirect: 6289 // LR prepared above for better instruction scheduling. 6290 // LR() 6291 __ blx(LR); 6292 break; 6293 case HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod: 6294 // LR = callee_method->entry_point_from_quick_compiled_code_ 6295 __ LoadFromOffset( 6296 kLoadWord, LR, callee_method.AsRegister<Register>(), 6297 ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmWordSize).Int32Value()); 6298 // LR() 6299 __ blx(LR); 6300 break; 6301 } 6302 6303 DCHECK(!IsLeafMethod()); 6304} 6305 6306void CodeGeneratorARM::GenerateVirtualCall(HInvokeVirtual* invoke, Location temp_location) { 6307 Register temp = temp_location.AsRegister<Register>(); 6308 uint32_t method_offset = mirror::Class::EmbeddedVTableEntryOffset( 6309 invoke->GetVTableIndex(), kArmPointerSize).Uint32Value(); 6310 6311 // Use the calling convention instead of the location of the receiver, as 6312 // intrinsics may have put the receiver in a different register. In the intrinsics 6313 // slow path, the arguments have been moved to the right place, so here we are 6314 // guaranteed that the receiver is the first register of the calling convention. 6315 InvokeDexCallingConvention calling_convention; 6316 Register receiver = calling_convention.GetRegisterAt(0); 6317 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value(); 6318 // /* HeapReference<Class> */ temp = receiver->klass_ 6319 __ LoadFromOffset(kLoadWord, temp, receiver, class_offset); 6320 MaybeRecordImplicitNullCheck(invoke); 6321 // Instead of simply (possibly) unpoisoning `temp` here, we should 6322 // emit a read barrier for the previous class reference load. 6323 // However this is not required in practice, as this is an 6324 // intermediate/temporary reference and because the current 6325 // concurrent copying collector keeps the from-space memory 6326 // intact/accessible until the end of the marking phase (the 6327 // concurrent copying collector may not in the future). 6328 __ MaybeUnpoisonHeapReference(temp); 6329 // temp = temp->GetMethodAt(method_offset); 6330 uint32_t entry_point = ArtMethod::EntryPointFromQuickCompiledCodeOffset( 6331 kArmWordSize).Int32Value(); 6332 __ LoadFromOffset(kLoadWord, temp, temp, method_offset); 6333 // LR = temp->GetEntryPoint(); 6334 __ LoadFromOffset(kLoadWord, LR, temp, entry_point); 6335 // LR(); 6336 __ blx(LR); 6337} 6338 6339void CodeGeneratorARM::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) { 6340 DCHECK(linker_patches->empty()); 6341 size_t size = 6342 method_patches_.size() + 6343 call_patches_.size() + 6344 relative_call_patches_.size() + 6345 /* MOVW+MOVT for each base */ 2u * dex_cache_arrays_base_labels_.size(); 6346 linker_patches->reserve(size); 6347 for (const auto& entry : method_patches_) { 6348 const MethodReference& target_method = entry.first; 6349 Literal* literal = entry.second; 6350 DCHECK(literal->GetLabel()->IsBound()); 6351 uint32_t literal_offset = literal->GetLabel()->Position(); 6352 linker_patches->push_back(LinkerPatch::MethodPatch(literal_offset, 6353 target_method.dex_file, 6354 target_method.dex_method_index)); 6355 } 6356 for (const auto& entry : call_patches_) { 6357 const MethodReference& target_method = entry.first; 6358 Literal* literal = entry.second; 6359 DCHECK(literal->GetLabel()->IsBound()); 6360 uint32_t literal_offset = literal->GetLabel()->Position(); 6361 linker_patches->push_back(LinkerPatch::CodePatch(literal_offset, 6362 target_method.dex_file, 6363 target_method.dex_method_index)); 6364 } 6365 for (const MethodPatchInfo<Label>& info : relative_call_patches_) { 6366 uint32_t literal_offset = info.label.Position(); 6367 linker_patches->push_back(LinkerPatch::RelativeCodePatch(literal_offset, 6368 info.target_method.dex_file, 6369 info.target_method.dex_method_index)); 6370 } 6371 for (const auto& pair : dex_cache_arrays_base_labels_) { 6372 HArmDexCacheArraysBase* base = pair.first; 6373 const DexCacheArraysBaseLabels* labels = &pair.second; 6374 const DexFile& dex_file = base->GetDexFile(); 6375 size_t base_element_offset = base->GetElementOffset(); 6376 DCHECK(labels->add_pc_label.IsBound()); 6377 uint32_t add_pc_offset = dchecked_integral_cast<uint32_t>(labels->add_pc_label.Position()); 6378 // Add MOVW patch. 6379 DCHECK(labels->movw_label.IsBound()); 6380 uint32_t movw_offset = dchecked_integral_cast<uint32_t>(labels->movw_label.Position()); 6381 linker_patches->push_back(LinkerPatch::DexCacheArrayPatch(movw_offset, 6382 &dex_file, 6383 add_pc_offset, 6384 base_element_offset)); 6385 // Add MOVT patch. 6386 DCHECK(labels->movt_label.IsBound()); 6387 uint32_t movt_offset = dchecked_integral_cast<uint32_t>(labels->movt_label.Position()); 6388 linker_patches->push_back(LinkerPatch::DexCacheArrayPatch(movt_offset, 6389 &dex_file, 6390 add_pc_offset, 6391 base_element_offset)); 6392 } 6393} 6394 6395Literal* CodeGeneratorARM::DeduplicateMethodLiteral(MethodReference target_method, 6396 MethodToLiteralMap* map) { 6397 // Look up the literal for target_method. 6398 auto lb = map->lower_bound(target_method); 6399 if (lb != map->end() && !map->key_comp()(target_method, lb->first)) { 6400 return lb->second; 6401 } 6402 // We don't have a literal for this method yet, insert a new one. 6403 Literal* literal = __ NewLiteral<uint32_t>(0u); 6404 map->PutBefore(lb, target_method, literal); 6405 return literal; 6406} 6407 6408Literal* CodeGeneratorARM::DeduplicateMethodAddressLiteral(MethodReference target_method) { 6409 return DeduplicateMethodLiteral(target_method, &method_patches_); 6410} 6411 6412Literal* CodeGeneratorARM::DeduplicateMethodCodeLiteral(MethodReference target_method) { 6413 return DeduplicateMethodLiteral(target_method, &call_patches_); 6414} 6415 6416void LocationsBuilderARM::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) { 6417 LocationSummary* locations = 6418 new (GetGraph()->GetArena()) LocationSummary(instr, LocationSummary::kNoCall); 6419 locations->SetInAt(HMultiplyAccumulate::kInputAccumulatorIndex, 6420 Location::RequiresRegister()); 6421 locations->SetInAt(HMultiplyAccumulate::kInputMulLeftIndex, Location::RequiresRegister()); 6422 locations->SetInAt(HMultiplyAccumulate::kInputMulRightIndex, Location::RequiresRegister()); 6423 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 6424} 6425 6426void InstructionCodeGeneratorARM::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) { 6427 LocationSummary* locations = instr->GetLocations(); 6428 Register res = locations->Out().AsRegister<Register>(); 6429 Register accumulator = 6430 locations->InAt(HMultiplyAccumulate::kInputAccumulatorIndex).AsRegister<Register>(); 6431 Register mul_left = 6432 locations->InAt(HMultiplyAccumulate::kInputMulLeftIndex).AsRegister<Register>(); 6433 Register mul_right = 6434 locations->InAt(HMultiplyAccumulate::kInputMulRightIndex).AsRegister<Register>(); 6435 6436 if (instr->GetOpKind() == HInstruction::kAdd) { 6437 __ mla(res, mul_left, mul_right, accumulator); 6438 } else { 6439 __ mls(res, mul_left, mul_right, accumulator); 6440 } 6441} 6442 6443void LocationsBuilderARM::VisitBoundType(HBoundType* instruction ATTRIBUTE_UNUSED) { 6444 // Nothing to do, this should be removed during prepare for register allocator. 6445 LOG(FATAL) << "Unreachable"; 6446} 6447 6448void InstructionCodeGeneratorARM::VisitBoundType(HBoundType* instruction ATTRIBUTE_UNUSED) { 6449 // Nothing to do, this should be removed during prepare for register allocator. 6450 LOG(FATAL) << "Unreachable"; 6451} 6452 6453// Simple implementation of packed switch - generate cascaded compare/jumps. 6454void LocationsBuilderARM::VisitPackedSwitch(HPackedSwitch* switch_instr) { 6455 LocationSummary* locations = 6456 new (GetGraph()->GetArena()) LocationSummary(switch_instr, LocationSummary::kNoCall); 6457 locations->SetInAt(0, Location::RequiresRegister()); 6458 if (switch_instr->GetNumEntries() > kPackedSwitchCompareJumpThreshold && 6459 codegen_->GetAssembler()->IsThumb()) { 6460 locations->AddTemp(Location::RequiresRegister()); // We need a temp for the table base. 6461 if (switch_instr->GetStartValue() != 0) { 6462 locations->AddTemp(Location::RequiresRegister()); // We need a temp for the bias. 6463 } 6464 } 6465} 6466 6467void InstructionCodeGeneratorARM::VisitPackedSwitch(HPackedSwitch* switch_instr) { 6468 int32_t lower_bound = switch_instr->GetStartValue(); 6469 uint32_t num_entries = switch_instr->GetNumEntries(); 6470 LocationSummary* locations = switch_instr->GetLocations(); 6471 Register value_reg = locations->InAt(0).AsRegister<Register>(); 6472 HBasicBlock* default_block = switch_instr->GetDefaultBlock(); 6473 6474 if (num_entries <= kPackedSwitchCompareJumpThreshold || !codegen_->GetAssembler()->IsThumb()) { 6475 // Create a series of compare/jumps. 6476 Register temp_reg = IP; 6477 // Note: It is fine for the below AddConstantSetFlags() using IP register to temporarily store 6478 // the immediate, because IP is used as the destination register. For the other 6479 // AddConstantSetFlags() and GenerateCompareWithImmediate(), the immediate values are constant, 6480 // and they can be encoded in the instruction without making use of IP register. 6481 __ AddConstantSetFlags(temp_reg, value_reg, -lower_bound); 6482 6483 const ArenaVector<HBasicBlock*>& successors = switch_instr->GetBlock()->GetSuccessors(); 6484 // Jump to successors[0] if value == lower_bound. 6485 __ b(codegen_->GetLabelOf(successors[0]), EQ); 6486 int32_t last_index = 0; 6487 for (; num_entries - last_index > 2; last_index += 2) { 6488 __ AddConstantSetFlags(temp_reg, temp_reg, -2); 6489 // Jump to successors[last_index + 1] if value < case_value[last_index + 2]. 6490 __ b(codegen_->GetLabelOf(successors[last_index + 1]), LO); 6491 // Jump to successors[last_index + 2] if value == case_value[last_index + 2]. 6492 __ b(codegen_->GetLabelOf(successors[last_index + 2]), EQ); 6493 } 6494 if (num_entries - last_index == 2) { 6495 // The last missing case_value. 6496 __ CmpConstant(temp_reg, 1); 6497 __ b(codegen_->GetLabelOf(successors[last_index + 1]), EQ); 6498 } 6499 6500 // And the default for any other value. 6501 if (!codegen_->GoesToNextBlock(switch_instr->GetBlock(), default_block)) { 6502 __ b(codegen_->GetLabelOf(default_block)); 6503 } 6504 } else { 6505 // Create a table lookup. 6506 Register temp_reg = locations->GetTemp(0).AsRegister<Register>(); 6507 6508 // Materialize a pointer to the switch table 6509 std::vector<Label*> labels(num_entries); 6510 const ArenaVector<HBasicBlock*>& successors = switch_instr->GetBlock()->GetSuccessors(); 6511 for (uint32_t i = 0; i < num_entries; i++) { 6512 labels[i] = codegen_->GetLabelOf(successors[i]); 6513 } 6514 JumpTable* table = __ CreateJumpTable(std::move(labels), temp_reg); 6515 6516 // Remove the bias. 6517 Register key_reg; 6518 if (lower_bound != 0) { 6519 key_reg = locations->GetTemp(1).AsRegister<Register>(); 6520 __ AddConstant(key_reg, value_reg, -lower_bound); 6521 } else { 6522 key_reg = value_reg; 6523 } 6524 6525 // Check whether the value is in the table, jump to default block if not. 6526 __ CmpConstant(key_reg, num_entries - 1); 6527 __ b(codegen_->GetLabelOf(default_block), Condition::HI); 6528 6529 // Load the displacement from the table. 6530 __ ldr(temp_reg, Address(temp_reg, key_reg, Shift::LSL, 2)); 6531 6532 // Dispatch is a direct add to the PC (for Thumb2). 6533 __ EmitJumpTableDispatch(table, temp_reg); 6534 } 6535} 6536 6537void LocationsBuilderARM::VisitArmDexCacheArraysBase(HArmDexCacheArraysBase* base) { 6538 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(base); 6539 locations->SetOut(Location::RequiresRegister()); 6540 codegen_->AddDexCacheArraysBase(base); 6541} 6542 6543void InstructionCodeGeneratorARM::VisitArmDexCacheArraysBase(HArmDexCacheArraysBase* base) { 6544 Register base_reg = base->GetLocations()->Out().AsRegister<Register>(); 6545 CodeGeneratorARM::DexCacheArraysBaseLabels* labels = codegen_->GetDexCacheArraysBaseLabels(base); 6546 __ BindTrackedLabel(&labels->movw_label); 6547 __ movw(base_reg, 0u); 6548 __ BindTrackedLabel(&labels->movt_label); 6549 __ movt(base_reg, 0u); 6550 __ BindTrackedLabel(&labels->add_pc_label); 6551 __ add(base_reg, base_reg, ShifterOperand(PC)); 6552} 6553 6554void CodeGeneratorARM::MoveFromReturnRegister(Location trg, Primitive::Type type) { 6555 if (!trg.IsValid()) { 6556 DCHECK_EQ(type, Primitive::kPrimVoid); 6557 return; 6558 } 6559 6560 DCHECK_NE(type, Primitive::kPrimVoid); 6561 6562 Location return_loc = InvokeDexCallingConventionVisitorARM().GetReturnLocation(type); 6563 if (return_loc.Equals(trg)) { 6564 return; 6565 } 6566 6567 // TODO: Consider pairs in the parallel move resolver, then this could be nicely merged 6568 // with the last branch. 6569 if (type == Primitive::kPrimLong) { 6570 HParallelMove parallel_move(GetGraph()->GetArena()); 6571 parallel_move.AddMove(return_loc.ToLow(), trg.ToLow(), Primitive::kPrimInt, nullptr); 6572 parallel_move.AddMove(return_loc.ToHigh(), trg.ToHigh(), Primitive::kPrimInt, nullptr); 6573 GetMoveResolver()->EmitNativeCode(¶llel_move); 6574 } else if (type == Primitive::kPrimDouble) { 6575 HParallelMove parallel_move(GetGraph()->GetArena()); 6576 parallel_move.AddMove(return_loc.ToLow(), trg.ToLow(), Primitive::kPrimFloat, nullptr); 6577 parallel_move.AddMove(return_loc.ToHigh(), trg.ToHigh(), Primitive::kPrimFloat, nullptr); 6578 GetMoveResolver()->EmitNativeCode(¶llel_move); 6579 } else { 6580 // Let the parallel move resolver take care of all of this. 6581 HParallelMove parallel_move(GetGraph()->GetArena()); 6582 parallel_move.AddMove(return_loc, trg, type, nullptr); 6583 GetMoveResolver()->EmitNativeCode(¶llel_move); 6584 } 6585} 6586 6587void LocationsBuilderARM::VisitClassTableGet(HClassTableGet* instruction) { 6588 LocationSummary* locations = 6589 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); 6590 locations->SetInAt(0, Location::RequiresRegister()); 6591 locations->SetOut(Location::RequiresRegister()); 6592} 6593 6594void InstructionCodeGeneratorARM::VisitClassTableGet(HClassTableGet* instruction) { 6595 LocationSummary* locations = instruction->GetLocations(); 6596 uint32_t method_offset = 0; 6597 if (instruction->GetTableKind() == HClassTableGet::kVTable) { 6598 method_offset = mirror::Class::EmbeddedVTableEntryOffset( 6599 instruction->GetIndex(), kArmPointerSize).SizeValue(); 6600 } else { 6601 method_offset = mirror::Class::EmbeddedImTableEntryOffset( 6602 instruction->GetIndex() % mirror::Class::kImtSize, kArmPointerSize).Uint32Value(); 6603 } 6604 __ LoadFromOffset(kLoadWord, 6605 locations->Out().AsRegister<Register>(), 6606 locations->InAt(0).AsRegister<Register>(), 6607 method_offset); 6608} 6609 6610#undef __ 6611#undef QUICK_ENTRY_POINT 6612 6613} // namespace arm 6614} // namespace art 6615