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