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