code_generator_arm.cc revision 580b609cd6cfef46108156457df42254d11e72a7
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 37namespace arm { 38 39static bool ExpectedPairLayout(Location location) { 40 // We expected this for both core and fpu register pairs. 41 return ((location.low() & 1) == 0) && (location.low() + 1 == location.high()); 42} 43 44static constexpr int kCurrentMethodStackOffset = 0; 45static constexpr Register kMethodRegisterArgument = R0; 46 47// We unconditionally allocate R5 to ensure we can do long operations 48// with baseline. 49static constexpr Register kCoreSavedRegisterForBaseline = R5; 50static constexpr Register kCoreCalleeSaves[] = 51 { R5, R6, R7, R8, R10, R11, LR }; 52static constexpr SRegister kFpuCalleeSaves[] = 53 { S16, S17, S18, S19, S20, S21, S22, S23, S24, S25, S26, S27, S28, S29, S30, S31 }; 54 55// D31 cannot be split into two S registers, and the register allocator only works on 56// S registers. Therefore there is no need to block it. 57static constexpr DRegister DTMP = D31; 58 59#define __ down_cast<ArmAssembler*>(codegen->GetAssembler())-> 60#define QUICK_ENTRY_POINT(x) QUICK_ENTRYPOINT_OFFSET(kArmWordSize, x).Int32Value() 61 62class NullCheckSlowPathARM : public SlowPathCode { 63 public: 64 explicit NullCheckSlowPathARM(HNullCheck* instruction) : instruction_(instruction) {} 65 66 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { 67 CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen); 68 __ Bind(GetEntryLabel()); 69 if (instruction_->CanThrowIntoCatchBlock()) { 70 // Live registers will be restored in the catch block if caught. 71 SaveLiveRegisters(codegen, instruction_->GetLocations()); 72 } 73 arm_codegen->InvokeRuntime( 74 QUICK_ENTRY_POINT(pThrowNullPointer), instruction_, instruction_->GetDexPc(), this); 75 } 76 77 bool IsFatal() const OVERRIDE { return true; } 78 79 const char* GetDescription() const OVERRIDE { return "NullCheckSlowPathARM"; } 80 81 private: 82 HNullCheck* const instruction_; 83 DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathARM); 84}; 85 86class DivZeroCheckSlowPathARM : public SlowPathCode { 87 public: 88 explicit DivZeroCheckSlowPathARM(HDivZeroCheck* instruction) : instruction_(instruction) {} 89 90 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { 91 CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen); 92 __ Bind(GetEntryLabel()); 93 if (instruction_->CanThrowIntoCatchBlock()) { 94 // Live registers will be restored in the catch block if caught. 95 SaveLiveRegisters(codegen, instruction_->GetLocations()); 96 } 97 arm_codegen->InvokeRuntime( 98 QUICK_ENTRY_POINT(pThrowDivZero), instruction_, instruction_->GetDexPc(), this); 99 } 100 101 bool IsFatal() const OVERRIDE { return true; } 102 103 const char* GetDescription() const OVERRIDE { return "DivZeroCheckSlowPathARM"; } 104 105 private: 106 HDivZeroCheck* const instruction_; 107 DISALLOW_COPY_AND_ASSIGN(DivZeroCheckSlowPathARM); 108}; 109 110class SuspendCheckSlowPathARM : public SlowPathCode { 111 public: 112 SuspendCheckSlowPathARM(HSuspendCheck* instruction, HBasicBlock* successor) 113 : instruction_(instruction), successor_(successor) {} 114 115 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { 116 CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen); 117 __ Bind(GetEntryLabel()); 118 SaveLiveRegisters(codegen, instruction_->GetLocations()); 119 arm_codegen->InvokeRuntime( 120 QUICK_ENTRY_POINT(pTestSuspend), instruction_, instruction_->GetDexPc(), this); 121 RestoreLiveRegisters(codegen, instruction_->GetLocations()); 122 if (successor_ == nullptr) { 123 __ b(GetReturnLabel()); 124 } else { 125 __ b(arm_codegen->GetLabelOf(successor_)); 126 } 127 } 128 129 Label* GetReturnLabel() { 130 DCHECK(successor_ == nullptr); 131 return &return_label_; 132 } 133 134 HBasicBlock* GetSuccessor() const { 135 return successor_; 136 } 137 138 const char* GetDescription() const OVERRIDE { return "SuspendCheckSlowPathARM"; } 139 140 private: 141 HSuspendCheck* const instruction_; 142 // If not null, the block to branch to after the suspend check. 143 HBasicBlock* const successor_; 144 145 // If `successor_` is null, the label to branch to after the suspend check. 146 Label return_label_; 147 148 DISALLOW_COPY_AND_ASSIGN(SuspendCheckSlowPathARM); 149}; 150 151class BoundsCheckSlowPathARM : public SlowPathCode { 152 public: 153 explicit BoundsCheckSlowPathARM(HBoundsCheck* instruction) 154 : instruction_(instruction) {} 155 156 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { 157 CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen); 158 LocationSummary* locations = instruction_->GetLocations(); 159 160 __ Bind(GetEntryLabel()); 161 if (instruction_->CanThrowIntoCatchBlock()) { 162 // Live registers will be restored in the catch block if caught. 163 SaveLiveRegisters(codegen, instruction_->GetLocations()); 164 } 165 // We're moving two locations to locations that could overlap, so we need a parallel 166 // move resolver. 167 InvokeRuntimeCallingConvention calling_convention; 168 codegen->EmitParallelMoves( 169 locations->InAt(0), 170 Location::RegisterLocation(calling_convention.GetRegisterAt(0)), 171 Primitive::kPrimInt, 172 locations->InAt(1), 173 Location::RegisterLocation(calling_convention.GetRegisterAt(1)), 174 Primitive::kPrimInt); 175 arm_codegen->InvokeRuntime( 176 QUICK_ENTRY_POINT(pThrowArrayBounds), instruction_, instruction_->GetDexPc(), this); 177 } 178 179 bool IsFatal() const OVERRIDE { return true; } 180 181 const char* GetDescription() const OVERRIDE { return "BoundsCheckSlowPathARM"; } 182 183 private: 184 HBoundsCheck* const instruction_; 185 186 DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathARM); 187}; 188 189class LoadClassSlowPathARM : public SlowPathCode { 190 public: 191 LoadClassSlowPathARM(HLoadClass* cls, 192 HInstruction* at, 193 uint32_t dex_pc, 194 bool do_clinit) 195 : cls_(cls), at_(at), dex_pc_(dex_pc), do_clinit_(do_clinit) { 196 DCHECK(at->IsLoadClass() || at->IsClinitCheck()); 197 } 198 199 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { 200 LocationSummary* locations = at_->GetLocations(); 201 202 CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen); 203 __ Bind(GetEntryLabel()); 204 SaveLiveRegisters(codegen, locations); 205 206 InvokeRuntimeCallingConvention calling_convention; 207 __ LoadImmediate(calling_convention.GetRegisterAt(0), cls_->GetTypeIndex()); 208 int32_t entry_point_offset = do_clinit_ 209 ? QUICK_ENTRY_POINT(pInitializeStaticStorage) 210 : QUICK_ENTRY_POINT(pInitializeType); 211 arm_codegen->InvokeRuntime(entry_point_offset, at_, dex_pc_, this); 212 213 // Move the class to the desired location. 214 Location out = locations->Out(); 215 if (out.IsValid()) { 216 DCHECK(out.IsRegister() && !locations->GetLiveRegisters()->ContainsCoreRegister(out.reg())); 217 arm_codegen->Move32(locations->Out(), Location::RegisterLocation(R0)); 218 } 219 RestoreLiveRegisters(codegen, locations); 220 __ b(GetExitLabel()); 221 } 222 223 const char* GetDescription() const OVERRIDE { return "LoadClassSlowPathARM"; } 224 225 private: 226 // The class this slow path will load. 227 HLoadClass* const cls_; 228 229 // The instruction where this slow path is happening. 230 // (Might be the load class or an initialization check). 231 HInstruction* const at_; 232 233 // The dex PC of `at_`. 234 const uint32_t dex_pc_; 235 236 // Whether to initialize the class. 237 const bool do_clinit_; 238 239 DISALLOW_COPY_AND_ASSIGN(LoadClassSlowPathARM); 240}; 241 242class LoadStringSlowPathARM : public SlowPathCode { 243 public: 244 explicit LoadStringSlowPathARM(HLoadString* instruction) : instruction_(instruction) {} 245 246 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { 247 LocationSummary* locations = instruction_->GetLocations(); 248 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg())); 249 250 CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen); 251 __ Bind(GetEntryLabel()); 252 SaveLiveRegisters(codegen, locations); 253 254 InvokeRuntimeCallingConvention calling_convention; 255 __ LoadImmediate(calling_convention.GetRegisterAt(0), instruction_->GetStringIndex()); 256 arm_codegen->InvokeRuntime( 257 QUICK_ENTRY_POINT(pResolveString), instruction_, instruction_->GetDexPc(), this); 258 arm_codegen->Move32(locations->Out(), Location::RegisterLocation(R0)); 259 260 RestoreLiveRegisters(codegen, locations); 261 __ b(GetExitLabel()); 262 } 263 264 const char* GetDescription() const OVERRIDE { return "LoadStringSlowPathARM"; } 265 266 private: 267 HLoadString* const instruction_; 268 269 DISALLOW_COPY_AND_ASSIGN(LoadStringSlowPathARM); 270}; 271 272class TypeCheckSlowPathARM : public SlowPathCode { 273 public: 274 TypeCheckSlowPathARM(HInstruction* instruction, bool is_fatal) 275 : instruction_(instruction), is_fatal_(is_fatal) {} 276 277 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { 278 LocationSummary* locations = instruction_->GetLocations(); 279 Location object_class = instruction_->IsCheckCast() ? locations->GetTemp(0) 280 : locations->Out(); 281 DCHECK(instruction_->IsCheckCast() 282 || !locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg())); 283 284 CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen); 285 __ Bind(GetEntryLabel()); 286 287 if (instruction_->IsCheckCast()) { 288 // The codegen for the instruction overwrites `temp`, so put it back in place. 289 Register obj = locations->InAt(0).AsRegister<Register>(); 290 Register temp = locations->GetTemp(0).AsRegister<Register>(); 291 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value(); 292 __ LoadFromOffset(kLoadWord, temp, obj, class_offset); 293 __ MaybeUnpoisonHeapReference(temp); 294 } 295 296 if (!is_fatal_) { 297 SaveLiveRegisters(codegen, locations); 298 } 299 300 // We're moving two locations to locations that could overlap, so we need a parallel 301 // move resolver. 302 InvokeRuntimeCallingConvention calling_convention; 303 codegen->EmitParallelMoves( 304 locations->InAt(1), 305 Location::RegisterLocation(calling_convention.GetRegisterAt(0)), 306 Primitive::kPrimNot, 307 object_class, 308 Location::RegisterLocation(calling_convention.GetRegisterAt(1)), 309 Primitive::kPrimNot); 310 311 if (instruction_->IsInstanceOf()) { 312 arm_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pInstanceofNonTrivial), 313 instruction_, 314 instruction_->GetDexPc(), 315 this); 316 arm_codegen->Move32(locations->Out(), Location::RegisterLocation(R0)); 317 } else { 318 DCHECK(instruction_->IsCheckCast()); 319 arm_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pCheckCast), 320 instruction_, 321 instruction_->GetDexPc(), 322 this); 323 } 324 325 if (!is_fatal_) { 326 RestoreLiveRegisters(codegen, locations); 327 __ b(GetExitLabel()); 328 } 329 } 330 331 const char* GetDescription() const OVERRIDE { return "TypeCheckSlowPathARM"; } 332 333 bool IsFatal() const OVERRIDE { return is_fatal_; } 334 335 private: 336 HInstruction* const instruction_; 337 const bool is_fatal_; 338 339 DISALLOW_COPY_AND_ASSIGN(TypeCheckSlowPathARM); 340}; 341 342class DeoptimizationSlowPathARM : public SlowPathCode { 343 public: 344 explicit DeoptimizationSlowPathARM(HInstruction* instruction) 345 : instruction_(instruction) {} 346 347 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { 348 __ Bind(GetEntryLabel()); 349 SaveLiveRegisters(codegen, instruction_->GetLocations()); 350 DCHECK(instruction_->IsDeoptimize()); 351 HDeoptimize* deoptimize = instruction_->AsDeoptimize(); 352 uint32_t dex_pc = deoptimize->GetDexPc(); 353 CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen); 354 arm_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pDeoptimize), instruction_, dex_pc, this); 355 } 356 357 const char* GetDescription() const OVERRIDE { return "DeoptimizationSlowPathARM"; } 358 359 private: 360 HInstruction* const instruction_; 361 DISALLOW_COPY_AND_ASSIGN(DeoptimizationSlowPathARM); 362}; 363 364class ArraySetSlowPathARM : public SlowPathCode { 365 public: 366 explicit ArraySetSlowPathARM(HInstruction* instruction) : instruction_(instruction) {} 367 368 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { 369 LocationSummary* locations = instruction_->GetLocations(); 370 __ Bind(GetEntryLabel()); 371 SaveLiveRegisters(codegen, locations); 372 373 InvokeRuntimeCallingConvention calling_convention; 374 HParallelMove parallel_move(codegen->GetGraph()->GetArena()); 375 parallel_move.AddMove( 376 locations->InAt(0), 377 Location::RegisterLocation(calling_convention.GetRegisterAt(0)), 378 Primitive::kPrimNot, 379 nullptr); 380 parallel_move.AddMove( 381 locations->InAt(1), 382 Location::RegisterLocation(calling_convention.GetRegisterAt(1)), 383 Primitive::kPrimInt, 384 nullptr); 385 parallel_move.AddMove( 386 locations->InAt(2), 387 Location::RegisterLocation(calling_convention.GetRegisterAt(2)), 388 Primitive::kPrimNot, 389 nullptr); 390 codegen->GetMoveResolver()->EmitNativeCode(¶llel_move); 391 392 CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen); 393 arm_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pAputObject), 394 instruction_, 395 instruction_->GetDexPc(), 396 this); 397 RestoreLiveRegisters(codegen, locations); 398 __ b(GetExitLabel()); 399 } 400 401 const char* GetDescription() const OVERRIDE { return "ArraySetSlowPathARM"; } 402 403 private: 404 HInstruction* const instruction_; 405 406 DISALLOW_COPY_AND_ASSIGN(ArraySetSlowPathARM); 407}; 408 409#undef __ 410#define __ down_cast<ArmAssembler*>(GetAssembler())-> 411 412inline Condition ARMSignedOrFPCondition(IfCondition cond) { 413 switch (cond) { 414 case kCondEQ: return EQ; 415 case kCondNE: return NE; 416 case kCondLT: return LT; 417 case kCondLE: return LE; 418 case kCondGT: return GT; 419 case kCondGE: return GE; 420 } 421 LOG(FATAL) << "Unreachable"; 422 UNREACHABLE(); 423} 424 425inline Condition ARMUnsignedCondition(IfCondition cond) { 426 switch (cond) { 427 case kCondEQ: return EQ; 428 case kCondNE: return NE; 429 case kCondLT: return LO; 430 case kCondLE: return LS; 431 case kCondGT: return HI; 432 case kCondGE: return HS; 433 } 434 LOG(FATAL) << "Unreachable"; 435 UNREACHABLE(); 436} 437 438void CodeGeneratorARM::DumpCoreRegister(std::ostream& stream, int reg) const { 439 stream << Register(reg); 440} 441 442void CodeGeneratorARM::DumpFloatingPointRegister(std::ostream& stream, int reg) const { 443 stream << SRegister(reg); 444} 445 446size_t CodeGeneratorARM::SaveCoreRegister(size_t stack_index, uint32_t reg_id) { 447 __ StoreToOffset(kStoreWord, static_cast<Register>(reg_id), SP, stack_index); 448 return kArmWordSize; 449} 450 451size_t CodeGeneratorARM::RestoreCoreRegister(size_t stack_index, uint32_t reg_id) { 452 __ LoadFromOffset(kLoadWord, static_cast<Register>(reg_id), SP, stack_index); 453 return kArmWordSize; 454} 455 456size_t CodeGeneratorARM::SaveFloatingPointRegister(size_t stack_index, uint32_t reg_id) { 457 __ StoreSToOffset(static_cast<SRegister>(reg_id), SP, stack_index); 458 return kArmWordSize; 459} 460 461size_t CodeGeneratorARM::RestoreFloatingPointRegister(size_t stack_index, uint32_t reg_id) { 462 __ LoadSFromOffset(static_cast<SRegister>(reg_id), SP, stack_index); 463 return kArmWordSize; 464} 465 466CodeGeneratorARM::CodeGeneratorARM(HGraph* graph, 467 const ArmInstructionSetFeatures& isa_features, 468 const CompilerOptions& compiler_options, 469 OptimizingCompilerStats* stats) 470 : CodeGenerator(graph, 471 kNumberOfCoreRegisters, 472 kNumberOfSRegisters, 473 kNumberOfRegisterPairs, 474 ComputeRegisterMask(reinterpret_cast<const int*>(kCoreCalleeSaves), 475 arraysize(kCoreCalleeSaves)), 476 ComputeRegisterMask(reinterpret_cast<const int*>(kFpuCalleeSaves), 477 arraysize(kFpuCalleeSaves)), 478 compiler_options, 479 stats), 480 block_labels_(nullptr), 481 location_builder_(graph, this), 482 instruction_visitor_(graph, this), 483 move_resolver_(graph->GetArena(), this), 484 assembler_(), 485 isa_features_(isa_features), 486 method_patches_(MethodReferenceComparator(), 487 graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), 488 call_patches_(MethodReferenceComparator(), 489 graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), 490 relative_call_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) { 491 // Always save the LR register to mimic Quick. 492 AddAllocatedRegister(Location::RegisterLocation(LR)); 493} 494 495void CodeGeneratorARM::Finalize(CodeAllocator* allocator) { 496 // Ensure that we fix up branches and literal loads and emit the literal pool. 497 __ FinalizeCode(); 498 499 // Adjust native pc offsets in stack maps. 500 for (size_t i = 0, num = stack_map_stream_.GetNumberOfStackMaps(); i != num; ++i) { 501 uint32_t old_position = stack_map_stream_.GetStackMap(i).native_pc_offset; 502 uint32_t new_position = __ GetAdjustedPosition(old_position); 503 stack_map_stream_.SetStackMapNativePcOffset(i, new_position); 504 } 505 // Adjust native pc offsets of block labels. 506 for (HBasicBlock* block : *block_order_) { 507 // Get the label directly from block_labels_ rather than through GetLabelOf() to avoid 508 // FirstNonEmptyBlock() which could lead to adjusting a label more than once. 509 DCHECK_LT(block->GetBlockId(), GetGraph()->GetBlocks().size()); 510 Label* block_label = &block_labels_[block->GetBlockId()]; 511 DCHECK_EQ(block_label->IsBound(), !block->IsSingleJump()); 512 if (block_label->IsBound()) { 513 __ AdjustLabelPosition(block_label); 514 } 515 } 516 // Adjust pc offsets for the disassembly information. 517 if (disasm_info_ != nullptr) { 518 GeneratedCodeInterval* frame_entry_interval = disasm_info_->GetFrameEntryInterval(); 519 frame_entry_interval->start = __ GetAdjustedPosition(frame_entry_interval->start); 520 frame_entry_interval->end = __ GetAdjustedPosition(frame_entry_interval->end); 521 for (auto& it : *disasm_info_->GetInstructionIntervals()) { 522 it.second.start = __ GetAdjustedPosition(it.second.start); 523 it.second.end = __ GetAdjustedPosition(it.second.end); 524 } 525 for (auto& it : *disasm_info_->GetSlowPathIntervals()) { 526 it.code_interval.start = __ GetAdjustedPosition(it.code_interval.start); 527 it.code_interval.end = __ GetAdjustedPosition(it.code_interval.end); 528 } 529 } 530 // Adjust pc offsets for relative call patches. 531 for (MethodPatchInfo<Label>& info : relative_call_patches_) { 532 __ AdjustLabelPosition(&info.label); 533 } 534 535 CodeGenerator::Finalize(allocator); 536} 537 538Location CodeGeneratorARM::AllocateFreeRegister(Primitive::Type type) const { 539 switch (type) { 540 case Primitive::kPrimLong: { 541 size_t reg = FindFreeEntry(blocked_register_pairs_, kNumberOfRegisterPairs); 542 ArmManagedRegister pair = 543 ArmManagedRegister::FromRegisterPair(static_cast<RegisterPair>(reg)); 544 DCHECK(!blocked_core_registers_[pair.AsRegisterPairLow()]); 545 DCHECK(!blocked_core_registers_[pair.AsRegisterPairHigh()]); 546 547 blocked_core_registers_[pair.AsRegisterPairLow()] = true; 548 blocked_core_registers_[pair.AsRegisterPairHigh()] = true; 549 UpdateBlockedPairRegisters(); 550 return Location::RegisterPairLocation(pair.AsRegisterPairLow(), pair.AsRegisterPairHigh()); 551 } 552 553 case Primitive::kPrimByte: 554 case Primitive::kPrimBoolean: 555 case Primitive::kPrimChar: 556 case Primitive::kPrimShort: 557 case Primitive::kPrimInt: 558 case Primitive::kPrimNot: { 559 int reg = FindFreeEntry(blocked_core_registers_, kNumberOfCoreRegisters); 560 // Block all register pairs that contain `reg`. 561 for (int i = 0; i < kNumberOfRegisterPairs; i++) { 562 ArmManagedRegister current = 563 ArmManagedRegister::FromRegisterPair(static_cast<RegisterPair>(i)); 564 if (current.AsRegisterPairLow() == reg || current.AsRegisterPairHigh() == reg) { 565 blocked_register_pairs_[i] = true; 566 } 567 } 568 return Location::RegisterLocation(reg); 569 } 570 571 case Primitive::kPrimFloat: { 572 int reg = FindFreeEntry(blocked_fpu_registers_, kNumberOfSRegisters); 573 return Location::FpuRegisterLocation(reg); 574 } 575 576 case Primitive::kPrimDouble: { 577 int reg = FindTwoFreeConsecutiveAlignedEntries(blocked_fpu_registers_, kNumberOfSRegisters); 578 DCHECK_EQ(reg % 2, 0); 579 return Location::FpuRegisterPairLocation(reg, reg + 1); 580 } 581 582 case Primitive::kPrimVoid: 583 LOG(FATAL) << "Unreachable type " << type; 584 } 585 586 return Location(); 587} 588 589void CodeGeneratorARM::SetupBlockedRegisters(bool is_baseline) const { 590 // Don't allocate the dalvik style register pair passing. 591 blocked_register_pairs_[R1_R2] = true; 592 593 // Stack register, LR and PC are always reserved. 594 blocked_core_registers_[SP] = true; 595 blocked_core_registers_[LR] = true; 596 blocked_core_registers_[PC] = true; 597 598 // Reserve thread register. 599 blocked_core_registers_[TR] = true; 600 601 // Reserve temp register. 602 blocked_core_registers_[IP] = true; 603 604 if (is_baseline) { 605 for (size_t i = 0; i < arraysize(kCoreCalleeSaves); ++i) { 606 blocked_core_registers_[kCoreCalleeSaves[i]] = true; 607 } 608 609 blocked_core_registers_[kCoreSavedRegisterForBaseline] = false; 610 } 611 612 if (is_baseline || GetGraph()->IsDebuggable()) { 613 // Stubs do not save callee-save floating point registers. If the graph 614 // is debuggable, we need to deal with these registers differently. For 615 // now, just block them. 616 for (size_t i = 0; i < arraysize(kFpuCalleeSaves); ++i) { 617 blocked_fpu_registers_[kFpuCalleeSaves[i]] = true; 618 } 619 } 620 621 UpdateBlockedPairRegisters(); 622} 623 624void CodeGeneratorARM::UpdateBlockedPairRegisters() const { 625 for (int i = 0; i < kNumberOfRegisterPairs; i++) { 626 ArmManagedRegister current = 627 ArmManagedRegister::FromRegisterPair(static_cast<RegisterPair>(i)); 628 if (blocked_core_registers_[current.AsRegisterPairLow()] 629 || blocked_core_registers_[current.AsRegisterPairHigh()]) { 630 blocked_register_pairs_[i] = true; 631 } 632 } 633} 634 635InstructionCodeGeneratorARM::InstructionCodeGeneratorARM(HGraph* graph, CodeGeneratorARM* codegen) 636 : HGraphVisitor(graph), 637 assembler_(codegen->GetAssembler()), 638 codegen_(codegen) {} 639 640void CodeGeneratorARM::ComputeSpillMask() { 641 core_spill_mask_ = allocated_registers_.GetCoreRegisters() & core_callee_save_mask_; 642 // Save one extra register for baseline. Note that on thumb2, there is no easy 643 // instruction to restore just the PC, so this actually helps both baseline 644 // and non-baseline to save and restore at least two registers at entry and exit. 645 core_spill_mask_ |= (1 << kCoreSavedRegisterForBaseline); 646 DCHECK_NE(core_spill_mask_, 0u) << "At least the return address register must be saved"; 647 fpu_spill_mask_ = allocated_registers_.GetFloatingPointRegisters() & fpu_callee_save_mask_; 648 // We use vpush and vpop for saving and restoring floating point registers, which take 649 // a SRegister and the number of registers to save/restore after that SRegister. We 650 // therefore update the `fpu_spill_mask_` to also contain those registers not allocated, 651 // but in the range. 652 if (fpu_spill_mask_ != 0) { 653 uint32_t least_significant_bit = LeastSignificantBit(fpu_spill_mask_); 654 uint32_t most_significant_bit = MostSignificantBit(fpu_spill_mask_); 655 for (uint32_t i = least_significant_bit + 1 ; i < most_significant_bit; ++i) { 656 fpu_spill_mask_ |= (1 << i); 657 } 658 } 659} 660 661static dwarf::Reg DWARFReg(Register reg) { 662 return dwarf::Reg::ArmCore(static_cast<int>(reg)); 663} 664 665static dwarf::Reg DWARFReg(SRegister reg) { 666 return dwarf::Reg::ArmFp(static_cast<int>(reg)); 667} 668 669void CodeGeneratorARM::GenerateFrameEntry() { 670 bool skip_overflow_check = 671 IsLeafMethod() && !FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kArm); 672 DCHECK(GetCompilerOptions().GetImplicitStackOverflowChecks()); 673 __ Bind(&frame_entry_label_); 674 675 if (HasEmptyFrame()) { 676 return; 677 } 678 679 if (!skip_overflow_check) { 680 __ AddConstant(IP, SP, -static_cast<int32_t>(GetStackOverflowReservedBytes(kArm))); 681 __ LoadFromOffset(kLoadWord, IP, IP, 0); 682 RecordPcInfo(nullptr, 0); 683 } 684 685 __ PushList(core_spill_mask_); 686 __ cfi().AdjustCFAOffset(kArmWordSize * POPCOUNT(core_spill_mask_)); 687 __ cfi().RelOffsetForMany(DWARFReg(kMethodRegisterArgument), 0, core_spill_mask_, kArmWordSize); 688 if (fpu_spill_mask_ != 0) { 689 SRegister start_register = SRegister(LeastSignificantBit(fpu_spill_mask_)); 690 __ vpushs(start_register, POPCOUNT(fpu_spill_mask_)); 691 __ cfi().AdjustCFAOffset(kArmWordSize * POPCOUNT(fpu_spill_mask_)); 692 __ cfi().RelOffsetForMany(DWARFReg(S0), 0, fpu_spill_mask_, kArmWordSize); 693 } 694 int adjust = GetFrameSize() - FrameEntrySpillSize(); 695 __ AddConstant(SP, -adjust); 696 __ cfi().AdjustCFAOffset(adjust); 697 __ StoreToOffset(kStoreWord, kMethodRegisterArgument, SP, 0); 698} 699 700void CodeGeneratorARM::GenerateFrameExit() { 701 if (HasEmptyFrame()) { 702 __ bx(LR); 703 return; 704 } 705 __ cfi().RememberState(); 706 int adjust = GetFrameSize() - FrameEntrySpillSize(); 707 __ AddConstant(SP, adjust); 708 __ cfi().AdjustCFAOffset(-adjust); 709 if (fpu_spill_mask_ != 0) { 710 SRegister start_register = SRegister(LeastSignificantBit(fpu_spill_mask_)); 711 __ vpops(start_register, POPCOUNT(fpu_spill_mask_)); 712 __ cfi().AdjustCFAOffset(-kArmPointerSize * POPCOUNT(fpu_spill_mask_)); 713 __ cfi().RestoreMany(DWARFReg(SRegister(0)), fpu_spill_mask_); 714 } 715 // Pop LR into PC to return. 716 DCHECK_NE(core_spill_mask_ & (1 << LR), 0U); 717 uint32_t pop_mask = (core_spill_mask_ & (~(1 << LR))) | 1 << PC; 718 __ PopList(pop_mask); 719 __ cfi().RestoreState(); 720 __ cfi().DefCFAOffset(GetFrameSize()); 721} 722 723void CodeGeneratorARM::Bind(HBasicBlock* block) { 724 __ Bind(GetLabelOf(block)); 725} 726 727Location CodeGeneratorARM::GetStackLocation(HLoadLocal* load) const { 728 switch (load->GetType()) { 729 case Primitive::kPrimLong: 730 case Primitive::kPrimDouble: 731 return Location::DoubleStackSlot(GetStackSlot(load->GetLocal())); 732 733 case Primitive::kPrimInt: 734 case Primitive::kPrimNot: 735 case Primitive::kPrimFloat: 736 return Location::StackSlot(GetStackSlot(load->GetLocal())); 737 738 case Primitive::kPrimBoolean: 739 case Primitive::kPrimByte: 740 case Primitive::kPrimChar: 741 case Primitive::kPrimShort: 742 case Primitive::kPrimVoid: 743 LOG(FATAL) << "Unexpected type " << load->GetType(); 744 UNREACHABLE(); 745 } 746 747 LOG(FATAL) << "Unreachable"; 748 UNREACHABLE(); 749} 750 751Location InvokeDexCallingConventionVisitorARM::GetNextLocation(Primitive::Type type) { 752 switch (type) { 753 case Primitive::kPrimBoolean: 754 case Primitive::kPrimByte: 755 case Primitive::kPrimChar: 756 case Primitive::kPrimShort: 757 case Primitive::kPrimInt: 758 case Primitive::kPrimNot: { 759 uint32_t index = gp_index_++; 760 uint32_t stack_index = stack_index_++; 761 if (index < calling_convention.GetNumberOfRegisters()) { 762 return Location::RegisterLocation(calling_convention.GetRegisterAt(index)); 763 } else { 764 return Location::StackSlot(calling_convention.GetStackOffsetOf(stack_index)); 765 } 766 } 767 768 case Primitive::kPrimLong: { 769 uint32_t index = gp_index_; 770 uint32_t stack_index = stack_index_; 771 gp_index_ += 2; 772 stack_index_ += 2; 773 if (index + 1 < calling_convention.GetNumberOfRegisters()) { 774 if (calling_convention.GetRegisterAt(index) == R1) { 775 // Skip R1, and use R2_R3 instead. 776 gp_index_++; 777 index++; 778 } 779 } 780 if (index + 1 < calling_convention.GetNumberOfRegisters()) { 781 DCHECK_EQ(calling_convention.GetRegisterAt(index) + 1, 782 calling_convention.GetRegisterAt(index + 1)); 783 784 return Location::RegisterPairLocation(calling_convention.GetRegisterAt(index), 785 calling_convention.GetRegisterAt(index + 1)); 786 } else { 787 return Location::DoubleStackSlot(calling_convention.GetStackOffsetOf(stack_index)); 788 } 789 } 790 791 case Primitive::kPrimFloat: { 792 uint32_t stack_index = stack_index_++; 793 if (float_index_ % 2 == 0) { 794 float_index_ = std::max(double_index_, float_index_); 795 } 796 if (float_index_ < calling_convention.GetNumberOfFpuRegisters()) { 797 return Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(float_index_++)); 798 } else { 799 return Location::StackSlot(calling_convention.GetStackOffsetOf(stack_index)); 800 } 801 } 802 803 case Primitive::kPrimDouble: { 804 double_index_ = std::max(double_index_, RoundUp(float_index_, 2)); 805 uint32_t stack_index = stack_index_; 806 stack_index_ += 2; 807 if (double_index_ + 1 < calling_convention.GetNumberOfFpuRegisters()) { 808 uint32_t index = double_index_; 809 double_index_ += 2; 810 Location result = Location::FpuRegisterPairLocation( 811 calling_convention.GetFpuRegisterAt(index), 812 calling_convention.GetFpuRegisterAt(index + 1)); 813 DCHECK(ExpectedPairLayout(result)); 814 return result; 815 } else { 816 return Location::DoubleStackSlot(calling_convention.GetStackOffsetOf(stack_index)); 817 } 818 } 819 820 case Primitive::kPrimVoid: 821 LOG(FATAL) << "Unexpected parameter type " << type; 822 break; 823 } 824 return Location(); 825} 826 827Location InvokeDexCallingConventionVisitorARM::GetReturnLocation(Primitive::Type type) const { 828 switch (type) { 829 case Primitive::kPrimBoolean: 830 case Primitive::kPrimByte: 831 case Primitive::kPrimChar: 832 case Primitive::kPrimShort: 833 case Primitive::kPrimInt: 834 case Primitive::kPrimNot: { 835 return Location::RegisterLocation(R0); 836 } 837 838 case Primitive::kPrimFloat: { 839 return Location::FpuRegisterLocation(S0); 840 } 841 842 case Primitive::kPrimLong: { 843 return Location::RegisterPairLocation(R0, R1); 844 } 845 846 case Primitive::kPrimDouble: { 847 return Location::FpuRegisterPairLocation(S0, S1); 848 } 849 850 case Primitive::kPrimVoid: 851 return Location(); 852 } 853 854 UNREACHABLE(); 855} 856 857Location InvokeDexCallingConventionVisitorARM::GetMethodLocation() const { 858 return Location::RegisterLocation(kMethodRegisterArgument); 859} 860 861void CodeGeneratorARM::Move32(Location destination, Location source) { 862 if (source.Equals(destination)) { 863 return; 864 } 865 if (destination.IsRegister()) { 866 if (source.IsRegister()) { 867 __ Mov(destination.AsRegister<Register>(), source.AsRegister<Register>()); 868 } else if (source.IsFpuRegister()) { 869 __ vmovrs(destination.AsRegister<Register>(), source.AsFpuRegister<SRegister>()); 870 } else { 871 __ LoadFromOffset(kLoadWord, destination.AsRegister<Register>(), SP, source.GetStackIndex()); 872 } 873 } else if (destination.IsFpuRegister()) { 874 if (source.IsRegister()) { 875 __ vmovsr(destination.AsFpuRegister<SRegister>(), source.AsRegister<Register>()); 876 } else if (source.IsFpuRegister()) { 877 __ vmovs(destination.AsFpuRegister<SRegister>(), source.AsFpuRegister<SRegister>()); 878 } else { 879 __ LoadSFromOffset(destination.AsFpuRegister<SRegister>(), SP, source.GetStackIndex()); 880 } 881 } else { 882 DCHECK(destination.IsStackSlot()) << destination; 883 if (source.IsRegister()) { 884 __ StoreToOffset(kStoreWord, source.AsRegister<Register>(), SP, destination.GetStackIndex()); 885 } else if (source.IsFpuRegister()) { 886 __ StoreSToOffset(source.AsFpuRegister<SRegister>(), SP, destination.GetStackIndex()); 887 } else { 888 DCHECK(source.IsStackSlot()) << source; 889 __ LoadFromOffset(kLoadWord, IP, SP, source.GetStackIndex()); 890 __ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex()); 891 } 892 } 893} 894 895void CodeGeneratorARM::Move64(Location destination, Location source) { 896 if (source.Equals(destination)) { 897 return; 898 } 899 if (destination.IsRegisterPair()) { 900 if (source.IsRegisterPair()) { 901 EmitParallelMoves( 902 Location::RegisterLocation(source.AsRegisterPairHigh<Register>()), 903 Location::RegisterLocation(destination.AsRegisterPairHigh<Register>()), 904 Primitive::kPrimInt, 905 Location::RegisterLocation(source.AsRegisterPairLow<Register>()), 906 Location::RegisterLocation(destination.AsRegisterPairLow<Register>()), 907 Primitive::kPrimInt); 908 } else if (source.IsFpuRegister()) { 909 UNIMPLEMENTED(FATAL); 910 } else if (source.IsFpuRegisterPair()) { 911 __ vmovrrd(destination.AsRegisterPairLow<Register>(), 912 destination.AsRegisterPairHigh<Register>(), 913 FromLowSToD(source.AsFpuRegisterPairLow<SRegister>())); 914 } else { 915 DCHECK(source.IsDoubleStackSlot()); 916 DCHECK(ExpectedPairLayout(destination)); 917 __ LoadFromOffset(kLoadWordPair, destination.AsRegisterPairLow<Register>(), 918 SP, source.GetStackIndex()); 919 } 920 } else if (destination.IsFpuRegisterPair()) { 921 if (source.IsDoubleStackSlot()) { 922 __ LoadDFromOffset(FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()), 923 SP, 924 source.GetStackIndex()); 925 } else if (source.IsRegisterPair()) { 926 __ vmovdrr(FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()), 927 source.AsRegisterPairLow<Register>(), 928 source.AsRegisterPairHigh<Register>()); 929 } else { 930 UNIMPLEMENTED(FATAL); 931 } 932 } else { 933 DCHECK(destination.IsDoubleStackSlot()); 934 if (source.IsRegisterPair()) { 935 // No conflict possible, so just do the moves. 936 if (source.AsRegisterPairLow<Register>() == R1) { 937 DCHECK_EQ(source.AsRegisterPairHigh<Register>(), R2); 938 __ StoreToOffset(kStoreWord, R1, SP, destination.GetStackIndex()); 939 __ StoreToOffset(kStoreWord, R2, SP, destination.GetHighStackIndex(kArmWordSize)); 940 } else { 941 __ StoreToOffset(kStoreWordPair, source.AsRegisterPairLow<Register>(), 942 SP, destination.GetStackIndex()); 943 } 944 } else if (source.IsFpuRegisterPair()) { 945 __ StoreDToOffset(FromLowSToD(source.AsFpuRegisterPairLow<SRegister>()), 946 SP, 947 destination.GetStackIndex()); 948 } else { 949 DCHECK(source.IsDoubleStackSlot()); 950 EmitParallelMoves( 951 Location::StackSlot(source.GetStackIndex()), 952 Location::StackSlot(destination.GetStackIndex()), 953 Primitive::kPrimInt, 954 Location::StackSlot(source.GetHighStackIndex(kArmWordSize)), 955 Location::StackSlot(destination.GetHighStackIndex(kArmWordSize)), 956 Primitive::kPrimInt); 957 } 958 } 959} 960 961void CodeGeneratorARM::Move(HInstruction* instruction, Location location, HInstruction* move_for) { 962 LocationSummary* locations = instruction->GetLocations(); 963 if (instruction->IsCurrentMethod()) { 964 Move32(location, Location::StackSlot(kCurrentMethodStackOffset)); 965 } else if (locations != nullptr && locations->Out().Equals(location)) { 966 return; 967 } else if (locations != nullptr && locations->Out().IsConstant()) { 968 HConstant* const_to_move = locations->Out().GetConstant(); 969 if (const_to_move->IsIntConstant() || const_to_move->IsNullConstant()) { 970 int32_t value = GetInt32ValueOf(const_to_move); 971 if (location.IsRegister()) { 972 __ LoadImmediate(location.AsRegister<Register>(), value); 973 } else { 974 DCHECK(location.IsStackSlot()); 975 __ LoadImmediate(IP, value); 976 __ StoreToOffset(kStoreWord, IP, SP, location.GetStackIndex()); 977 } 978 } else { 979 DCHECK(const_to_move->IsLongConstant()) << const_to_move->DebugName(); 980 int64_t value = const_to_move->AsLongConstant()->GetValue(); 981 if (location.IsRegisterPair()) { 982 __ LoadImmediate(location.AsRegisterPairLow<Register>(), Low32Bits(value)); 983 __ LoadImmediate(location.AsRegisterPairHigh<Register>(), High32Bits(value)); 984 } else { 985 DCHECK(location.IsDoubleStackSlot()); 986 __ LoadImmediate(IP, Low32Bits(value)); 987 __ StoreToOffset(kStoreWord, IP, SP, location.GetStackIndex()); 988 __ LoadImmediate(IP, High32Bits(value)); 989 __ StoreToOffset(kStoreWord, IP, SP, location.GetHighStackIndex(kArmWordSize)); 990 } 991 } 992 } else if (instruction->IsLoadLocal()) { 993 uint32_t stack_slot = GetStackSlot(instruction->AsLoadLocal()->GetLocal()); 994 switch (instruction->GetType()) { 995 case Primitive::kPrimBoolean: 996 case Primitive::kPrimByte: 997 case Primitive::kPrimChar: 998 case Primitive::kPrimShort: 999 case Primitive::kPrimInt: 1000 case Primitive::kPrimNot: 1001 case Primitive::kPrimFloat: 1002 Move32(location, Location::StackSlot(stack_slot)); 1003 break; 1004 1005 case Primitive::kPrimLong: 1006 case Primitive::kPrimDouble: 1007 Move64(location, Location::DoubleStackSlot(stack_slot)); 1008 break; 1009 1010 default: 1011 LOG(FATAL) << "Unexpected type " << instruction->GetType(); 1012 } 1013 } else if (instruction->IsTemporary()) { 1014 Location temp_location = GetTemporaryLocation(instruction->AsTemporary()); 1015 if (temp_location.IsStackSlot()) { 1016 Move32(location, temp_location); 1017 } else { 1018 DCHECK(temp_location.IsDoubleStackSlot()); 1019 Move64(location, temp_location); 1020 } 1021 } else { 1022 DCHECK((instruction->GetNext() == move_for) || instruction->GetNext()->IsTemporary()); 1023 switch (instruction->GetType()) { 1024 case Primitive::kPrimBoolean: 1025 case Primitive::kPrimByte: 1026 case Primitive::kPrimChar: 1027 case Primitive::kPrimShort: 1028 case Primitive::kPrimNot: 1029 case Primitive::kPrimInt: 1030 case Primitive::kPrimFloat: 1031 Move32(location, locations->Out()); 1032 break; 1033 1034 case Primitive::kPrimLong: 1035 case Primitive::kPrimDouble: 1036 Move64(location, locations->Out()); 1037 break; 1038 1039 default: 1040 LOG(FATAL) << "Unexpected type " << instruction->GetType(); 1041 } 1042 } 1043} 1044 1045void CodeGeneratorARM::MoveConstant(Location location, int32_t value) { 1046 DCHECK(location.IsRegister()); 1047 __ LoadImmediate(location.AsRegister<Register>(), value); 1048} 1049 1050void CodeGeneratorARM::MoveLocation(Location dst, Location src, Primitive::Type dst_type) { 1051 if (Primitive::Is64BitType(dst_type)) { 1052 Move64(dst, src); 1053 } else { 1054 Move32(dst, src); 1055 } 1056} 1057 1058void CodeGeneratorARM::AddLocationAsTemp(Location location, LocationSummary* locations) { 1059 if (location.IsRegister()) { 1060 locations->AddTemp(location); 1061 } else if (location.IsRegisterPair()) { 1062 locations->AddTemp(Location::RegisterLocation(location.AsRegisterPairLow<Register>())); 1063 locations->AddTemp(Location::RegisterLocation(location.AsRegisterPairHigh<Register>())); 1064 } else { 1065 UNIMPLEMENTED(FATAL) << "AddLocationAsTemp not implemented for location " << location; 1066 } 1067} 1068 1069void CodeGeneratorARM::InvokeRuntime(QuickEntrypointEnum entrypoint, 1070 HInstruction* instruction, 1071 uint32_t dex_pc, 1072 SlowPathCode* slow_path) { 1073 InvokeRuntime(GetThreadOffset<kArmWordSize>(entrypoint).Int32Value(), 1074 instruction, 1075 dex_pc, 1076 slow_path); 1077} 1078 1079void CodeGeneratorARM::InvokeRuntime(int32_t entry_point_offset, 1080 HInstruction* instruction, 1081 uint32_t dex_pc, 1082 SlowPathCode* slow_path) { 1083 ValidateInvokeRuntime(instruction, slow_path); 1084 __ LoadFromOffset(kLoadWord, LR, TR, entry_point_offset); 1085 __ blx(LR); 1086 RecordPcInfo(instruction, dex_pc, slow_path); 1087} 1088 1089void InstructionCodeGeneratorARM::HandleGoto(HInstruction* got, HBasicBlock* successor) { 1090 DCHECK(!successor->IsExitBlock()); 1091 1092 HBasicBlock* block = got->GetBlock(); 1093 HInstruction* previous = got->GetPrevious(); 1094 1095 HLoopInformation* info = block->GetLoopInformation(); 1096 if (info != nullptr && info->IsBackEdge(*block) && info->HasSuspendCheck()) { 1097 codegen_->ClearSpillSlotsFromLoopPhisInStackMap(info->GetSuspendCheck()); 1098 GenerateSuspendCheck(info->GetSuspendCheck(), successor); 1099 return; 1100 } 1101 1102 if (block->IsEntryBlock() && (previous != nullptr) && previous->IsSuspendCheck()) { 1103 GenerateSuspendCheck(previous->AsSuspendCheck(), nullptr); 1104 } 1105 if (!codegen_->GoesToNextBlock(got->GetBlock(), successor)) { 1106 __ b(codegen_->GetLabelOf(successor)); 1107 } 1108} 1109 1110void LocationsBuilderARM::VisitGoto(HGoto* got) { 1111 got->SetLocations(nullptr); 1112} 1113 1114void InstructionCodeGeneratorARM::VisitGoto(HGoto* got) { 1115 HandleGoto(got, got->GetSuccessor()); 1116} 1117 1118void LocationsBuilderARM::VisitTryBoundary(HTryBoundary* try_boundary) { 1119 try_boundary->SetLocations(nullptr); 1120} 1121 1122void InstructionCodeGeneratorARM::VisitTryBoundary(HTryBoundary* try_boundary) { 1123 HBasicBlock* successor = try_boundary->GetNormalFlowSuccessor(); 1124 if (!successor->IsExitBlock()) { 1125 HandleGoto(try_boundary, successor); 1126 } 1127} 1128 1129void LocationsBuilderARM::VisitExit(HExit* exit) { 1130 exit->SetLocations(nullptr); 1131} 1132 1133void InstructionCodeGeneratorARM::VisitExit(HExit* exit) { 1134 UNUSED(exit); 1135} 1136 1137void InstructionCodeGeneratorARM::GenerateCompareWithImmediate(Register left, int32_t right) { 1138 ShifterOperand operand; 1139 if (GetAssembler()->ShifterOperandCanHold(R0, left, CMP, right, &operand)) { 1140 __ cmp(left, operand); 1141 } else { 1142 Register temp = IP; 1143 __ LoadImmediate(temp, right); 1144 __ cmp(left, ShifterOperand(temp)); 1145 } 1146} 1147 1148void InstructionCodeGeneratorARM::GenerateFPJumps(HCondition* cond, 1149 Label* true_label, 1150 Label* false_label) { 1151 __ vmstat(); // transfer FP status register to ARM APSR. 1152 if (cond->IsFPConditionTrueIfNaN()) { 1153 __ b(true_label, VS); // VS for unordered. 1154 } else if (cond->IsFPConditionFalseIfNaN()) { 1155 __ b(false_label, VS); // VS for unordered. 1156 } 1157 __ b(true_label, ARMSignedOrFPCondition(cond->GetCondition())); 1158} 1159 1160void InstructionCodeGeneratorARM::GenerateLongComparesAndJumps(HCondition* cond, 1161 Label* true_label, 1162 Label* false_label) { 1163 LocationSummary* locations = cond->GetLocations(); 1164 Location left = locations->InAt(0); 1165 Location right = locations->InAt(1); 1166 IfCondition if_cond = cond->GetCondition(); 1167 1168 Register left_high = left.AsRegisterPairHigh<Register>(); 1169 Register left_low = left.AsRegisterPairLow<Register>(); 1170 IfCondition true_high_cond = if_cond; 1171 IfCondition false_high_cond = cond->GetOppositeCondition(); 1172 Condition final_condition = ARMUnsignedCondition(if_cond); 1173 1174 // Set the conditions for the test, remembering that == needs to be 1175 // decided using the low words. 1176 switch (if_cond) { 1177 case kCondEQ: 1178 case kCondNE: 1179 // Nothing to do. 1180 break; 1181 case kCondLT: 1182 false_high_cond = kCondGT; 1183 break; 1184 case kCondLE: 1185 true_high_cond = kCondLT; 1186 break; 1187 case kCondGT: 1188 false_high_cond = kCondLT; 1189 break; 1190 case kCondGE: 1191 true_high_cond = kCondGT; 1192 break; 1193 } 1194 if (right.IsConstant()) { 1195 int64_t value = right.GetConstant()->AsLongConstant()->GetValue(); 1196 int32_t val_low = Low32Bits(value); 1197 int32_t val_high = High32Bits(value); 1198 1199 GenerateCompareWithImmediate(left_high, val_high); 1200 if (if_cond == kCondNE) { 1201 __ b(true_label, ARMSignedOrFPCondition(true_high_cond)); 1202 } else if (if_cond == kCondEQ) { 1203 __ b(false_label, ARMSignedOrFPCondition(false_high_cond)); 1204 } else { 1205 __ b(true_label, ARMSignedOrFPCondition(true_high_cond)); 1206 __ b(false_label, ARMSignedOrFPCondition(false_high_cond)); 1207 } 1208 // Must be equal high, so compare the lows. 1209 GenerateCompareWithImmediate(left_low, val_low); 1210 } else { 1211 Register right_high = right.AsRegisterPairHigh<Register>(); 1212 Register right_low = right.AsRegisterPairLow<Register>(); 1213 1214 __ cmp(left_high, ShifterOperand(right_high)); 1215 if (if_cond == kCondNE) { 1216 __ b(true_label, ARMSignedOrFPCondition(true_high_cond)); 1217 } else if (if_cond == kCondEQ) { 1218 __ b(false_label, ARMSignedOrFPCondition(false_high_cond)); 1219 } else { 1220 __ b(true_label, ARMSignedOrFPCondition(true_high_cond)); 1221 __ b(false_label, ARMSignedOrFPCondition(false_high_cond)); 1222 } 1223 // Must be equal high, so compare the lows. 1224 __ cmp(left_low, ShifterOperand(right_low)); 1225 } 1226 // The last comparison might be unsigned. 1227 __ b(true_label, final_condition); 1228} 1229 1230void InstructionCodeGeneratorARM::GenerateCompareTestAndBranch(HIf* if_instr, 1231 HCondition* condition, 1232 Label* true_target, 1233 Label* false_target, 1234 Label* always_true_target) { 1235 LocationSummary* locations = condition->GetLocations(); 1236 Location left = locations->InAt(0); 1237 Location right = locations->InAt(1); 1238 1239 // We don't want true_target as a nullptr. 1240 if (true_target == nullptr) { 1241 true_target = always_true_target; 1242 } 1243 bool falls_through = (false_target == nullptr); 1244 1245 // FP compares don't like null false_targets. 1246 if (false_target == nullptr) { 1247 false_target = codegen_->GetLabelOf(if_instr->IfFalseSuccessor()); 1248 } 1249 1250 Primitive::Type type = condition->InputAt(0)->GetType(); 1251 switch (type) { 1252 case Primitive::kPrimLong: 1253 GenerateLongComparesAndJumps(condition, true_target, false_target); 1254 break; 1255 case Primitive::kPrimFloat: 1256 __ vcmps(left.AsFpuRegister<SRegister>(), right.AsFpuRegister<SRegister>()); 1257 GenerateFPJumps(condition, true_target, false_target); 1258 break; 1259 case Primitive::kPrimDouble: 1260 __ vcmpd(FromLowSToD(left.AsFpuRegisterPairLow<SRegister>()), 1261 FromLowSToD(right.AsFpuRegisterPairLow<SRegister>())); 1262 GenerateFPJumps(condition, true_target, false_target); 1263 break; 1264 default: 1265 LOG(FATAL) << "Unexpected compare type " << type; 1266 } 1267 1268 if (!falls_through) { 1269 __ b(false_target); 1270 } 1271} 1272 1273void InstructionCodeGeneratorARM::GenerateTestAndBranch(HInstruction* instruction, 1274 Label* true_target, 1275 Label* false_target, 1276 Label* always_true_target) { 1277 HInstruction* cond = instruction->InputAt(0); 1278 if (cond->IsIntConstant()) { 1279 // Constant condition, statically compared against 1. 1280 int32_t cond_value = cond->AsIntConstant()->GetValue(); 1281 if (cond_value == 1) { 1282 if (always_true_target != nullptr) { 1283 __ b(always_true_target); 1284 } 1285 return; 1286 } else { 1287 DCHECK_EQ(cond_value, 0); 1288 } 1289 } else { 1290 if (!cond->IsCondition() || cond->AsCondition()->NeedsMaterialization()) { 1291 // Condition has been materialized, compare the output to 0 1292 DCHECK(instruction->GetLocations()->InAt(0).IsRegister()); 1293 __ CompareAndBranchIfNonZero(instruction->GetLocations()->InAt(0).AsRegister<Register>(), 1294 true_target); 1295 } else { 1296 // Condition has not been materialized, use its inputs as the 1297 // comparison and its condition as the branch condition. 1298 Primitive::Type type = 1299 cond->IsCondition() ? cond->InputAt(0)->GetType() : Primitive::kPrimInt; 1300 // Is this a long or FP comparison that has been folded into the HCondition? 1301 if (type == Primitive::kPrimLong || Primitive::IsFloatingPointType(type)) { 1302 // Generate the comparison directly. 1303 GenerateCompareTestAndBranch(instruction->AsIf(), cond->AsCondition(), 1304 true_target, false_target, always_true_target); 1305 return; 1306 } 1307 1308 LocationSummary* locations = cond->GetLocations(); 1309 DCHECK(locations->InAt(0).IsRegister()) << locations->InAt(0); 1310 Register left = locations->InAt(0).AsRegister<Register>(); 1311 Location right = locations->InAt(1); 1312 if (right.IsRegister()) { 1313 __ cmp(left, ShifterOperand(right.AsRegister<Register>())); 1314 } else { 1315 DCHECK(right.IsConstant()); 1316 GenerateCompareWithImmediate(left, CodeGenerator::GetInt32ValueOf(right.GetConstant())); 1317 } 1318 __ b(true_target, ARMSignedOrFPCondition(cond->AsCondition()->GetCondition())); 1319 } 1320 } 1321 if (false_target != nullptr) { 1322 __ b(false_target); 1323 } 1324} 1325 1326void LocationsBuilderARM::VisitIf(HIf* if_instr) { 1327 LocationSummary* locations = 1328 new (GetGraph()->GetArena()) LocationSummary(if_instr, LocationSummary::kNoCall); 1329 HInstruction* cond = if_instr->InputAt(0); 1330 if (!cond->IsCondition() || cond->AsCondition()->NeedsMaterialization()) { 1331 locations->SetInAt(0, Location::RequiresRegister()); 1332 } 1333} 1334 1335void InstructionCodeGeneratorARM::VisitIf(HIf* if_instr) { 1336 Label* true_target = codegen_->GetLabelOf(if_instr->IfTrueSuccessor()); 1337 Label* false_target = codegen_->GetLabelOf(if_instr->IfFalseSuccessor()); 1338 Label* always_true_target = true_target; 1339 if (codegen_->GoesToNextBlock(if_instr->GetBlock(), 1340 if_instr->IfTrueSuccessor())) { 1341 always_true_target = nullptr; 1342 } 1343 if (codegen_->GoesToNextBlock(if_instr->GetBlock(), 1344 if_instr->IfFalseSuccessor())) { 1345 false_target = nullptr; 1346 } 1347 GenerateTestAndBranch(if_instr, true_target, false_target, always_true_target); 1348} 1349 1350void LocationsBuilderARM::VisitDeoptimize(HDeoptimize* deoptimize) { 1351 LocationSummary* locations = new (GetGraph()->GetArena()) 1352 LocationSummary(deoptimize, LocationSummary::kCallOnSlowPath); 1353 HInstruction* cond = deoptimize->InputAt(0); 1354 DCHECK(cond->IsCondition()); 1355 if (cond->AsCondition()->NeedsMaterialization()) { 1356 locations->SetInAt(0, Location::RequiresRegister()); 1357 } 1358} 1359 1360void InstructionCodeGeneratorARM::VisitDeoptimize(HDeoptimize* deoptimize) { 1361 SlowPathCode* slow_path = new (GetGraph()->GetArena()) 1362 DeoptimizationSlowPathARM(deoptimize); 1363 codegen_->AddSlowPath(slow_path); 1364 Label* slow_path_entry = slow_path->GetEntryLabel(); 1365 GenerateTestAndBranch(deoptimize, slow_path_entry, nullptr, slow_path_entry); 1366} 1367 1368void LocationsBuilderARM::VisitCondition(HCondition* cond) { 1369 LocationSummary* locations = 1370 new (GetGraph()->GetArena()) LocationSummary(cond, LocationSummary::kNoCall); 1371 // Handle the long/FP comparisons made in instruction simplification. 1372 switch (cond->InputAt(0)->GetType()) { 1373 case Primitive::kPrimLong: 1374 locations->SetInAt(0, Location::RequiresRegister()); 1375 locations->SetInAt(1, Location::RegisterOrConstant(cond->InputAt(1))); 1376 if (cond->NeedsMaterialization()) { 1377 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); 1378 } 1379 break; 1380 1381 case Primitive::kPrimFloat: 1382 case Primitive::kPrimDouble: 1383 locations->SetInAt(0, Location::RequiresFpuRegister()); 1384 locations->SetInAt(1, Location::RequiresFpuRegister()); 1385 if (cond->NeedsMaterialization()) { 1386 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 1387 } 1388 break; 1389 1390 default: 1391 locations->SetInAt(0, Location::RequiresRegister()); 1392 locations->SetInAt(1, Location::RegisterOrConstant(cond->InputAt(1))); 1393 if (cond->NeedsMaterialization()) { 1394 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 1395 } 1396 } 1397} 1398 1399void InstructionCodeGeneratorARM::VisitCondition(HCondition* cond) { 1400 if (!cond->NeedsMaterialization()) { 1401 return; 1402 } 1403 1404 LocationSummary* locations = cond->GetLocations(); 1405 Location left = locations->InAt(0); 1406 Location right = locations->InAt(1); 1407 Register out = locations->Out().AsRegister<Register>(); 1408 Label true_label, false_label; 1409 1410 switch (cond->InputAt(0)->GetType()) { 1411 default: { 1412 // Integer case. 1413 if (right.IsRegister()) { 1414 __ cmp(left.AsRegister<Register>(), ShifterOperand(right.AsRegister<Register>())); 1415 } else { 1416 DCHECK(right.IsConstant()); 1417 GenerateCompareWithImmediate(left.AsRegister<Register>(), 1418 CodeGenerator::GetInt32ValueOf(right.GetConstant())); 1419 } 1420 __ it(ARMSignedOrFPCondition(cond->GetCondition()), kItElse); 1421 __ mov(locations->Out().AsRegister<Register>(), ShifterOperand(1), 1422 ARMSignedOrFPCondition(cond->GetCondition())); 1423 __ mov(locations->Out().AsRegister<Register>(), ShifterOperand(0), 1424 ARMSignedOrFPCondition(cond->GetOppositeCondition())); 1425 return; 1426 } 1427 case Primitive::kPrimLong: 1428 GenerateLongComparesAndJumps(cond, &true_label, &false_label); 1429 break; 1430 case Primitive::kPrimFloat: 1431 __ vcmps(left.AsFpuRegister<SRegister>(), right.AsFpuRegister<SRegister>()); 1432 GenerateFPJumps(cond, &true_label, &false_label); 1433 break; 1434 case Primitive::kPrimDouble: 1435 __ vcmpd(FromLowSToD(left.AsFpuRegisterPairLow<SRegister>()), 1436 FromLowSToD(right.AsFpuRegisterPairLow<SRegister>())); 1437 GenerateFPJumps(cond, &true_label, &false_label); 1438 break; 1439 } 1440 1441 // Convert the jumps into the result. 1442 Label done_label; 1443 1444 // False case: result = 0. 1445 __ Bind(&false_label); 1446 __ LoadImmediate(out, 0); 1447 __ b(&done_label); 1448 1449 // True case: result = 1. 1450 __ Bind(&true_label); 1451 __ LoadImmediate(out, 1); 1452 __ Bind(&done_label); 1453} 1454 1455void LocationsBuilderARM::VisitEqual(HEqual* comp) { 1456 VisitCondition(comp); 1457} 1458 1459void InstructionCodeGeneratorARM::VisitEqual(HEqual* comp) { 1460 VisitCondition(comp); 1461} 1462 1463void LocationsBuilderARM::VisitNotEqual(HNotEqual* comp) { 1464 VisitCondition(comp); 1465} 1466 1467void InstructionCodeGeneratorARM::VisitNotEqual(HNotEqual* comp) { 1468 VisitCondition(comp); 1469} 1470 1471void LocationsBuilderARM::VisitLessThan(HLessThan* comp) { 1472 VisitCondition(comp); 1473} 1474 1475void InstructionCodeGeneratorARM::VisitLessThan(HLessThan* comp) { 1476 VisitCondition(comp); 1477} 1478 1479void LocationsBuilderARM::VisitLessThanOrEqual(HLessThanOrEqual* comp) { 1480 VisitCondition(comp); 1481} 1482 1483void InstructionCodeGeneratorARM::VisitLessThanOrEqual(HLessThanOrEqual* comp) { 1484 VisitCondition(comp); 1485} 1486 1487void LocationsBuilderARM::VisitGreaterThan(HGreaterThan* comp) { 1488 VisitCondition(comp); 1489} 1490 1491void InstructionCodeGeneratorARM::VisitGreaterThan(HGreaterThan* comp) { 1492 VisitCondition(comp); 1493} 1494 1495void LocationsBuilderARM::VisitGreaterThanOrEqual(HGreaterThanOrEqual* comp) { 1496 VisitCondition(comp); 1497} 1498 1499void InstructionCodeGeneratorARM::VisitGreaterThanOrEqual(HGreaterThanOrEqual* comp) { 1500 VisitCondition(comp); 1501} 1502 1503void LocationsBuilderARM::VisitLocal(HLocal* local) { 1504 local->SetLocations(nullptr); 1505} 1506 1507void InstructionCodeGeneratorARM::VisitLocal(HLocal* local) { 1508 DCHECK_EQ(local->GetBlock(), GetGraph()->GetEntryBlock()); 1509} 1510 1511void LocationsBuilderARM::VisitLoadLocal(HLoadLocal* load) { 1512 load->SetLocations(nullptr); 1513} 1514 1515void InstructionCodeGeneratorARM::VisitLoadLocal(HLoadLocal* load) { 1516 // Nothing to do, this is driven by the code generator. 1517 UNUSED(load); 1518} 1519 1520void LocationsBuilderARM::VisitStoreLocal(HStoreLocal* store) { 1521 LocationSummary* locations = 1522 new (GetGraph()->GetArena()) LocationSummary(store, LocationSummary::kNoCall); 1523 switch (store->InputAt(1)->GetType()) { 1524 case Primitive::kPrimBoolean: 1525 case Primitive::kPrimByte: 1526 case Primitive::kPrimChar: 1527 case Primitive::kPrimShort: 1528 case Primitive::kPrimInt: 1529 case Primitive::kPrimNot: 1530 case Primitive::kPrimFloat: 1531 locations->SetInAt(1, Location::StackSlot(codegen_->GetStackSlot(store->GetLocal()))); 1532 break; 1533 1534 case Primitive::kPrimLong: 1535 case Primitive::kPrimDouble: 1536 locations->SetInAt(1, Location::DoubleStackSlot(codegen_->GetStackSlot(store->GetLocal()))); 1537 break; 1538 1539 default: 1540 LOG(FATAL) << "Unexpected local type " << store->InputAt(1)->GetType(); 1541 } 1542} 1543 1544void InstructionCodeGeneratorARM::VisitStoreLocal(HStoreLocal* store) { 1545 UNUSED(store); 1546} 1547 1548void LocationsBuilderARM::VisitIntConstant(HIntConstant* constant) { 1549 LocationSummary* locations = 1550 new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall); 1551 locations->SetOut(Location::ConstantLocation(constant)); 1552} 1553 1554void InstructionCodeGeneratorARM::VisitIntConstant(HIntConstant* constant) { 1555 // Will be generated at use site. 1556 UNUSED(constant); 1557} 1558 1559void LocationsBuilderARM::VisitNullConstant(HNullConstant* constant) { 1560 LocationSummary* locations = 1561 new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall); 1562 locations->SetOut(Location::ConstantLocation(constant)); 1563} 1564 1565void InstructionCodeGeneratorARM::VisitNullConstant(HNullConstant* constant) { 1566 // Will be generated at use site. 1567 UNUSED(constant); 1568} 1569 1570void LocationsBuilderARM::VisitLongConstant(HLongConstant* constant) { 1571 LocationSummary* locations = 1572 new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall); 1573 locations->SetOut(Location::ConstantLocation(constant)); 1574} 1575 1576void InstructionCodeGeneratorARM::VisitLongConstant(HLongConstant* constant) { 1577 // Will be generated at use site. 1578 UNUSED(constant); 1579} 1580 1581void LocationsBuilderARM::VisitFloatConstant(HFloatConstant* constant) { 1582 LocationSummary* locations = 1583 new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall); 1584 locations->SetOut(Location::ConstantLocation(constant)); 1585} 1586 1587void InstructionCodeGeneratorARM::VisitFloatConstant(HFloatConstant* constant) { 1588 // Will be generated at use site. 1589 UNUSED(constant); 1590} 1591 1592void LocationsBuilderARM::VisitDoubleConstant(HDoubleConstant* constant) { 1593 LocationSummary* locations = 1594 new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall); 1595 locations->SetOut(Location::ConstantLocation(constant)); 1596} 1597 1598void InstructionCodeGeneratorARM::VisitDoubleConstant(HDoubleConstant* constant) { 1599 // Will be generated at use site. 1600 UNUSED(constant); 1601} 1602 1603void LocationsBuilderARM::VisitMemoryBarrier(HMemoryBarrier* memory_barrier) { 1604 memory_barrier->SetLocations(nullptr); 1605} 1606 1607void InstructionCodeGeneratorARM::VisitMemoryBarrier(HMemoryBarrier* memory_barrier) { 1608 GenerateMemoryBarrier(memory_barrier->GetBarrierKind()); 1609} 1610 1611void LocationsBuilderARM::VisitReturnVoid(HReturnVoid* ret) { 1612 ret->SetLocations(nullptr); 1613} 1614 1615void InstructionCodeGeneratorARM::VisitReturnVoid(HReturnVoid* ret) { 1616 UNUSED(ret); 1617 codegen_->GenerateFrameExit(); 1618} 1619 1620void LocationsBuilderARM::VisitReturn(HReturn* ret) { 1621 LocationSummary* locations = 1622 new (GetGraph()->GetArena()) LocationSummary(ret, LocationSummary::kNoCall); 1623 locations->SetInAt(0, parameter_visitor_.GetReturnLocation(ret->InputAt(0)->GetType())); 1624} 1625 1626void InstructionCodeGeneratorARM::VisitReturn(HReturn* ret) { 1627 UNUSED(ret); 1628 codegen_->GenerateFrameExit(); 1629} 1630 1631void LocationsBuilderARM::VisitInvokeUnresolved(HInvokeUnresolved* invoke) { 1632 // The trampoline uses the same calling convention as dex calling conventions, 1633 // except instead of loading arg0/r0 with the target Method*, arg0/r0 will contain 1634 // the method_idx. 1635 HandleInvoke(invoke); 1636} 1637 1638void InstructionCodeGeneratorARM::VisitInvokeUnresolved(HInvokeUnresolved* invoke) { 1639 codegen_->GenerateInvokeUnresolvedRuntimeCall(invoke); 1640} 1641 1642void LocationsBuilderARM::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) { 1643 // When we do not run baseline, explicit clinit checks triggered by static 1644 // invokes must have been pruned by art::PrepareForRegisterAllocation. 1645 DCHECK(codegen_->IsBaseline() || !invoke->IsStaticWithExplicitClinitCheck()); 1646 1647 IntrinsicLocationsBuilderARM intrinsic(GetGraph()->GetArena(), 1648 codegen_->GetInstructionSetFeatures()); 1649 if (intrinsic.TryDispatch(invoke)) { 1650 return; 1651 } 1652 1653 HandleInvoke(invoke); 1654} 1655 1656static bool TryGenerateIntrinsicCode(HInvoke* invoke, CodeGeneratorARM* codegen) { 1657 if (invoke->GetLocations()->Intrinsified()) { 1658 IntrinsicCodeGeneratorARM intrinsic(codegen); 1659 intrinsic.Dispatch(invoke); 1660 return true; 1661 } 1662 return false; 1663} 1664 1665void InstructionCodeGeneratorARM::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) { 1666 // When we do not run baseline, explicit clinit checks triggered by static 1667 // invokes must have been pruned by art::PrepareForRegisterAllocation. 1668 DCHECK(codegen_->IsBaseline() || !invoke->IsStaticWithExplicitClinitCheck()); 1669 1670 if (TryGenerateIntrinsicCode(invoke, codegen_)) { 1671 return; 1672 } 1673 1674 LocationSummary* locations = invoke->GetLocations(); 1675 codegen_->GenerateStaticOrDirectCall( 1676 invoke, locations->HasTemps() ? locations->GetTemp(0) : Location::NoLocation()); 1677 codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); 1678} 1679 1680void LocationsBuilderARM::HandleInvoke(HInvoke* invoke) { 1681 InvokeDexCallingConventionVisitorARM calling_convention_visitor; 1682 CodeGenerator::CreateCommonInvokeLocationSummary(invoke, &calling_convention_visitor); 1683} 1684 1685void LocationsBuilderARM::VisitInvokeVirtual(HInvokeVirtual* invoke) { 1686 IntrinsicLocationsBuilderARM intrinsic(GetGraph()->GetArena(), 1687 codegen_->GetInstructionSetFeatures()); 1688 if (intrinsic.TryDispatch(invoke)) { 1689 return; 1690 } 1691 1692 HandleInvoke(invoke); 1693} 1694 1695void InstructionCodeGeneratorARM::VisitInvokeVirtual(HInvokeVirtual* invoke) { 1696 if (TryGenerateIntrinsicCode(invoke, codegen_)) { 1697 return; 1698 } 1699 1700 codegen_->GenerateVirtualCall(invoke, invoke->GetLocations()->GetTemp(0)); 1701 DCHECK(!codegen_->IsLeafMethod()); 1702 codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); 1703} 1704 1705void LocationsBuilderARM::VisitInvokeInterface(HInvokeInterface* invoke) { 1706 HandleInvoke(invoke); 1707 // Add the hidden argument. 1708 invoke->GetLocations()->AddTemp(Location::RegisterLocation(R12)); 1709} 1710 1711void InstructionCodeGeneratorARM::VisitInvokeInterface(HInvokeInterface* invoke) { 1712 // TODO: b/18116999, our IMTs can miss an IncompatibleClassChangeError. 1713 Register temp = invoke->GetLocations()->GetTemp(0).AsRegister<Register>(); 1714 uint32_t method_offset = mirror::Class::EmbeddedImTableEntryOffset( 1715 invoke->GetImtIndex() % mirror::Class::kImtSize, kArmPointerSize).Uint32Value(); 1716 LocationSummary* locations = invoke->GetLocations(); 1717 Location receiver = locations->InAt(0); 1718 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value(); 1719 1720 // Set the hidden argument. 1721 __ LoadImmediate(invoke->GetLocations()->GetTemp(1).AsRegister<Register>(), 1722 invoke->GetDexMethodIndex()); 1723 1724 // temp = object->GetClass(); 1725 if (receiver.IsStackSlot()) { 1726 __ LoadFromOffset(kLoadWord, temp, SP, receiver.GetStackIndex()); 1727 __ LoadFromOffset(kLoadWord, temp, temp, class_offset); 1728 } else { 1729 __ LoadFromOffset(kLoadWord, temp, receiver.AsRegister<Register>(), class_offset); 1730 } 1731 codegen_->MaybeRecordImplicitNullCheck(invoke); 1732 __ MaybeUnpoisonHeapReference(temp); 1733 // temp = temp->GetImtEntryAt(method_offset); 1734 uint32_t entry_point = ArtMethod::EntryPointFromQuickCompiledCodeOffset( 1735 kArmWordSize).Int32Value(); 1736 __ LoadFromOffset(kLoadWord, temp, temp, method_offset); 1737 // LR = temp->GetEntryPoint(); 1738 __ LoadFromOffset(kLoadWord, LR, temp, entry_point); 1739 // LR(); 1740 __ blx(LR); 1741 DCHECK(!codegen_->IsLeafMethod()); 1742 codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); 1743} 1744 1745void LocationsBuilderARM::VisitNeg(HNeg* neg) { 1746 LocationSummary* locations = 1747 new (GetGraph()->GetArena()) LocationSummary(neg, LocationSummary::kNoCall); 1748 switch (neg->GetResultType()) { 1749 case Primitive::kPrimInt: { 1750 locations->SetInAt(0, Location::RequiresRegister()); 1751 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 1752 break; 1753 } 1754 case Primitive::kPrimLong: { 1755 locations->SetInAt(0, Location::RequiresRegister()); 1756 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); 1757 break; 1758 } 1759 1760 case Primitive::kPrimFloat: 1761 case Primitive::kPrimDouble: 1762 locations->SetInAt(0, Location::RequiresFpuRegister()); 1763 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); 1764 break; 1765 1766 default: 1767 LOG(FATAL) << "Unexpected neg type " << neg->GetResultType(); 1768 } 1769} 1770 1771void InstructionCodeGeneratorARM::VisitNeg(HNeg* neg) { 1772 LocationSummary* locations = neg->GetLocations(); 1773 Location out = locations->Out(); 1774 Location in = locations->InAt(0); 1775 switch (neg->GetResultType()) { 1776 case Primitive::kPrimInt: 1777 DCHECK(in.IsRegister()); 1778 __ rsb(out.AsRegister<Register>(), in.AsRegister<Register>(), ShifterOperand(0)); 1779 break; 1780 1781 case Primitive::kPrimLong: 1782 DCHECK(in.IsRegisterPair()); 1783 // out.lo = 0 - in.lo (and update the carry/borrow (C) flag) 1784 __ rsbs(out.AsRegisterPairLow<Register>(), 1785 in.AsRegisterPairLow<Register>(), 1786 ShifterOperand(0)); 1787 // We cannot emit an RSC (Reverse Subtract with Carry) 1788 // instruction here, as it does not exist in the Thumb-2 1789 // instruction set. We use the following approach 1790 // using SBC and SUB instead. 1791 // 1792 // out.hi = -C 1793 __ sbc(out.AsRegisterPairHigh<Register>(), 1794 out.AsRegisterPairHigh<Register>(), 1795 ShifterOperand(out.AsRegisterPairHigh<Register>())); 1796 // out.hi = out.hi - in.hi 1797 __ sub(out.AsRegisterPairHigh<Register>(), 1798 out.AsRegisterPairHigh<Register>(), 1799 ShifterOperand(in.AsRegisterPairHigh<Register>())); 1800 break; 1801 1802 case Primitive::kPrimFloat: 1803 DCHECK(in.IsFpuRegister()); 1804 __ vnegs(out.AsFpuRegister<SRegister>(), in.AsFpuRegister<SRegister>()); 1805 break; 1806 1807 case Primitive::kPrimDouble: 1808 DCHECK(in.IsFpuRegisterPair()); 1809 __ vnegd(FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()), 1810 FromLowSToD(in.AsFpuRegisterPairLow<SRegister>())); 1811 break; 1812 1813 default: 1814 LOG(FATAL) << "Unexpected neg type " << neg->GetResultType(); 1815 } 1816} 1817 1818void LocationsBuilderARM::VisitTypeConversion(HTypeConversion* conversion) { 1819 Primitive::Type result_type = conversion->GetResultType(); 1820 Primitive::Type input_type = conversion->GetInputType(); 1821 DCHECK_NE(result_type, input_type); 1822 1823 // The float-to-long, double-to-long and long-to-float type conversions 1824 // rely on a call to the runtime. 1825 LocationSummary::CallKind call_kind = 1826 (((input_type == Primitive::kPrimFloat || input_type == Primitive::kPrimDouble) 1827 && result_type == Primitive::kPrimLong) 1828 || (input_type == Primitive::kPrimLong && result_type == Primitive::kPrimFloat)) 1829 ? LocationSummary::kCall 1830 : LocationSummary::kNoCall; 1831 LocationSummary* locations = 1832 new (GetGraph()->GetArena()) LocationSummary(conversion, call_kind); 1833 1834 // The Java language does not allow treating boolean as an integral type but 1835 // our bit representation makes it safe. 1836 1837 switch (result_type) { 1838 case Primitive::kPrimByte: 1839 switch (input_type) { 1840 case Primitive::kPrimBoolean: 1841 // Boolean input is a result of code transformations. 1842 case Primitive::kPrimShort: 1843 case Primitive::kPrimInt: 1844 case Primitive::kPrimChar: 1845 // Processing a Dex `int-to-byte' instruction. 1846 locations->SetInAt(0, Location::RequiresRegister()); 1847 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 1848 break; 1849 1850 default: 1851 LOG(FATAL) << "Unexpected type conversion from " << input_type 1852 << " to " << result_type; 1853 } 1854 break; 1855 1856 case Primitive::kPrimShort: 1857 switch (input_type) { 1858 case Primitive::kPrimBoolean: 1859 // Boolean input is a result of code transformations. 1860 case Primitive::kPrimByte: 1861 case Primitive::kPrimInt: 1862 case Primitive::kPrimChar: 1863 // Processing a Dex `int-to-short' instruction. 1864 locations->SetInAt(0, Location::RequiresRegister()); 1865 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 1866 break; 1867 1868 default: 1869 LOG(FATAL) << "Unexpected type conversion from " << input_type 1870 << " to " << result_type; 1871 } 1872 break; 1873 1874 case Primitive::kPrimInt: 1875 switch (input_type) { 1876 case Primitive::kPrimLong: 1877 // Processing a Dex `long-to-int' instruction. 1878 locations->SetInAt(0, Location::Any()); 1879 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 1880 break; 1881 1882 case Primitive::kPrimFloat: 1883 // Processing a Dex `float-to-int' instruction. 1884 locations->SetInAt(0, Location::RequiresFpuRegister()); 1885 locations->SetOut(Location::RequiresRegister()); 1886 locations->AddTemp(Location::RequiresFpuRegister()); 1887 break; 1888 1889 case Primitive::kPrimDouble: 1890 // Processing a Dex `double-to-int' instruction. 1891 locations->SetInAt(0, Location::RequiresFpuRegister()); 1892 locations->SetOut(Location::RequiresRegister()); 1893 locations->AddTemp(Location::RequiresFpuRegister()); 1894 break; 1895 1896 default: 1897 LOG(FATAL) << "Unexpected type conversion from " << input_type 1898 << " to " << result_type; 1899 } 1900 break; 1901 1902 case Primitive::kPrimLong: 1903 switch (input_type) { 1904 case Primitive::kPrimBoolean: 1905 // Boolean input is a result of code transformations. 1906 case Primitive::kPrimByte: 1907 case Primitive::kPrimShort: 1908 case Primitive::kPrimInt: 1909 case Primitive::kPrimChar: 1910 // Processing a Dex `int-to-long' instruction. 1911 locations->SetInAt(0, Location::RequiresRegister()); 1912 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 1913 break; 1914 1915 case Primitive::kPrimFloat: { 1916 // Processing a Dex `float-to-long' instruction. 1917 InvokeRuntimeCallingConvention calling_convention; 1918 locations->SetInAt(0, Location::FpuRegisterLocation( 1919 calling_convention.GetFpuRegisterAt(0))); 1920 locations->SetOut(Location::RegisterPairLocation(R0, R1)); 1921 break; 1922 } 1923 1924 case Primitive::kPrimDouble: { 1925 // Processing a Dex `double-to-long' instruction. 1926 InvokeRuntimeCallingConvention calling_convention; 1927 locations->SetInAt(0, Location::FpuRegisterPairLocation( 1928 calling_convention.GetFpuRegisterAt(0), 1929 calling_convention.GetFpuRegisterAt(1))); 1930 locations->SetOut(Location::RegisterPairLocation(R0, R1)); 1931 break; 1932 } 1933 1934 default: 1935 LOG(FATAL) << "Unexpected type conversion from " << input_type 1936 << " to " << result_type; 1937 } 1938 break; 1939 1940 case Primitive::kPrimChar: 1941 switch (input_type) { 1942 case Primitive::kPrimBoolean: 1943 // Boolean input is a result of code transformations. 1944 case Primitive::kPrimByte: 1945 case Primitive::kPrimShort: 1946 case Primitive::kPrimInt: 1947 // Processing a Dex `int-to-char' instruction. 1948 locations->SetInAt(0, Location::RequiresRegister()); 1949 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 1950 break; 1951 1952 default: 1953 LOG(FATAL) << "Unexpected type conversion from " << input_type 1954 << " to " << result_type; 1955 } 1956 break; 1957 1958 case Primitive::kPrimFloat: 1959 switch (input_type) { 1960 case Primitive::kPrimBoolean: 1961 // Boolean input is a result of code transformations. 1962 case Primitive::kPrimByte: 1963 case Primitive::kPrimShort: 1964 case Primitive::kPrimInt: 1965 case Primitive::kPrimChar: 1966 // Processing a Dex `int-to-float' instruction. 1967 locations->SetInAt(0, Location::RequiresRegister()); 1968 locations->SetOut(Location::RequiresFpuRegister()); 1969 break; 1970 1971 case Primitive::kPrimLong: { 1972 // Processing a Dex `long-to-float' instruction. 1973 InvokeRuntimeCallingConvention calling_convention; 1974 locations->SetInAt(0, Location::RegisterPairLocation( 1975 calling_convention.GetRegisterAt(0), calling_convention.GetRegisterAt(1))); 1976 locations->SetOut(Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(0))); 1977 break; 1978 } 1979 1980 case Primitive::kPrimDouble: 1981 // Processing a Dex `double-to-float' instruction. 1982 locations->SetInAt(0, Location::RequiresFpuRegister()); 1983 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); 1984 break; 1985 1986 default: 1987 LOG(FATAL) << "Unexpected type conversion from " << input_type 1988 << " to " << result_type; 1989 }; 1990 break; 1991 1992 case Primitive::kPrimDouble: 1993 switch (input_type) { 1994 case Primitive::kPrimBoolean: 1995 // Boolean input is a result of code transformations. 1996 case Primitive::kPrimByte: 1997 case Primitive::kPrimShort: 1998 case Primitive::kPrimInt: 1999 case Primitive::kPrimChar: 2000 // Processing a Dex `int-to-double' instruction. 2001 locations->SetInAt(0, Location::RequiresRegister()); 2002 locations->SetOut(Location::RequiresFpuRegister()); 2003 break; 2004 2005 case Primitive::kPrimLong: 2006 // Processing a Dex `long-to-double' instruction. 2007 locations->SetInAt(0, Location::RequiresRegister()); 2008 locations->SetOut(Location::RequiresFpuRegister()); 2009 locations->AddTemp(Location::RequiresFpuRegister()); 2010 locations->AddTemp(Location::RequiresFpuRegister()); 2011 break; 2012 2013 case Primitive::kPrimFloat: 2014 // Processing a Dex `float-to-double' instruction. 2015 locations->SetInAt(0, Location::RequiresFpuRegister()); 2016 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); 2017 break; 2018 2019 default: 2020 LOG(FATAL) << "Unexpected type conversion from " << input_type 2021 << " to " << result_type; 2022 }; 2023 break; 2024 2025 default: 2026 LOG(FATAL) << "Unexpected type conversion from " << input_type 2027 << " to " << result_type; 2028 } 2029} 2030 2031void InstructionCodeGeneratorARM::VisitTypeConversion(HTypeConversion* conversion) { 2032 LocationSummary* locations = conversion->GetLocations(); 2033 Location out = locations->Out(); 2034 Location in = locations->InAt(0); 2035 Primitive::Type result_type = conversion->GetResultType(); 2036 Primitive::Type input_type = conversion->GetInputType(); 2037 DCHECK_NE(result_type, input_type); 2038 switch (result_type) { 2039 case Primitive::kPrimByte: 2040 switch (input_type) { 2041 case Primitive::kPrimBoolean: 2042 // Boolean input is a result of code transformations. 2043 case Primitive::kPrimShort: 2044 case Primitive::kPrimInt: 2045 case Primitive::kPrimChar: 2046 // Processing a Dex `int-to-byte' instruction. 2047 __ sbfx(out.AsRegister<Register>(), in.AsRegister<Register>(), 0, 8); 2048 break; 2049 2050 default: 2051 LOG(FATAL) << "Unexpected type conversion from " << input_type 2052 << " to " << result_type; 2053 } 2054 break; 2055 2056 case Primitive::kPrimShort: 2057 switch (input_type) { 2058 case Primitive::kPrimBoolean: 2059 // Boolean input is a result of code transformations. 2060 case Primitive::kPrimByte: 2061 case Primitive::kPrimInt: 2062 case Primitive::kPrimChar: 2063 // Processing a Dex `int-to-short' instruction. 2064 __ sbfx(out.AsRegister<Register>(), in.AsRegister<Register>(), 0, 16); 2065 break; 2066 2067 default: 2068 LOG(FATAL) << "Unexpected type conversion from " << input_type 2069 << " to " << result_type; 2070 } 2071 break; 2072 2073 case Primitive::kPrimInt: 2074 switch (input_type) { 2075 case Primitive::kPrimLong: 2076 // Processing a Dex `long-to-int' instruction. 2077 DCHECK(out.IsRegister()); 2078 if (in.IsRegisterPair()) { 2079 __ Mov(out.AsRegister<Register>(), in.AsRegisterPairLow<Register>()); 2080 } else if (in.IsDoubleStackSlot()) { 2081 __ LoadFromOffset(kLoadWord, out.AsRegister<Register>(), SP, in.GetStackIndex()); 2082 } else { 2083 DCHECK(in.IsConstant()); 2084 DCHECK(in.GetConstant()->IsLongConstant()); 2085 int64_t value = in.GetConstant()->AsLongConstant()->GetValue(); 2086 __ LoadImmediate(out.AsRegister<Register>(), static_cast<int32_t>(value)); 2087 } 2088 break; 2089 2090 case Primitive::kPrimFloat: { 2091 // Processing a Dex `float-to-int' instruction. 2092 SRegister temp = locations->GetTemp(0).AsFpuRegisterPairLow<SRegister>(); 2093 __ vmovs(temp, in.AsFpuRegister<SRegister>()); 2094 __ vcvtis(temp, temp); 2095 __ vmovrs(out.AsRegister<Register>(), temp); 2096 break; 2097 } 2098 2099 case Primitive::kPrimDouble: { 2100 // Processing a Dex `double-to-int' instruction. 2101 SRegister temp_s = locations->GetTemp(0).AsFpuRegisterPairLow<SRegister>(); 2102 DRegister temp_d = FromLowSToD(temp_s); 2103 __ vmovd(temp_d, FromLowSToD(in.AsFpuRegisterPairLow<SRegister>())); 2104 __ vcvtid(temp_s, temp_d); 2105 __ vmovrs(out.AsRegister<Register>(), temp_s); 2106 break; 2107 } 2108 2109 default: 2110 LOG(FATAL) << "Unexpected type conversion from " << input_type 2111 << " to " << result_type; 2112 } 2113 break; 2114 2115 case Primitive::kPrimLong: 2116 switch (input_type) { 2117 case Primitive::kPrimBoolean: 2118 // Boolean input is a result of code transformations. 2119 case Primitive::kPrimByte: 2120 case Primitive::kPrimShort: 2121 case Primitive::kPrimInt: 2122 case Primitive::kPrimChar: 2123 // Processing a Dex `int-to-long' instruction. 2124 DCHECK(out.IsRegisterPair()); 2125 DCHECK(in.IsRegister()); 2126 __ Mov(out.AsRegisterPairLow<Register>(), in.AsRegister<Register>()); 2127 // Sign extension. 2128 __ Asr(out.AsRegisterPairHigh<Register>(), 2129 out.AsRegisterPairLow<Register>(), 2130 31); 2131 break; 2132 2133 case Primitive::kPrimFloat: 2134 // Processing a Dex `float-to-long' instruction. 2135 codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pF2l), 2136 conversion, 2137 conversion->GetDexPc(), 2138 nullptr); 2139 break; 2140 2141 case Primitive::kPrimDouble: 2142 // Processing a Dex `double-to-long' instruction. 2143 codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pD2l), 2144 conversion, 2145 conversion->GetDexPc(), 2146 nullptr); 2147 break; 2148 2149 default: 2150 LOG(FATAL) << "Unexpected type conversion from " << input_type 2151 << " to " << result_type; 2152 } 2153 break; 2154 2155 case Primitive::kPrimChar: 2156 switch (input_type) { 2157 case Primitive::kPrimBoolean: 2158 // Boolean input is a result of code transformations. 2159 case Primitive::kPrimByte: 2160 case Primitive::kPrimShort: 2161 case Primitive::kPrimInt: 2162 // Processing a Dex `int-to-char' instruction. 2163 __ ubfx(out.AsRegister<Register>(), in.AsRegister<Register>(), 0, 16); 2164 break; 2165 2166 default: 2167 LOG(FATAL) << "Unexpected type conversion from " << input_type 2168 << " to " << result_type; 2169 } 2170 break; 2171 2172 case Primitive::kPrimFloat: 2173 switch (input_type) { 2174 case Primitive::kPrimBoolean: 2175 // Boolean input is a result of code transformations. 2176 case Primitive::kPrimByte: 2177 case Primitive::kPrimShort: 2178 case Primitive::kPrimInt: 2179 case Primitive::kPrimChar: { 2180 // Processing a Dex `int-to-float' instruction. 2181 __ vmovsr(out.AsFpuRegister<SRegister>(), in.AsRegister<Register>()); 2182 __ vcvtsi(out.AsFpuRegister<SRegister>(), out.AsFpuRegister<SRegister>()); 2183 break; 2184 } 2185 2186 case Primitive::kPrimLong: 2187 // Processing a Dex `long-to-float' instruction. 2188 codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pL2f), 2189 conversion, 2190 conversion->GetDexPc(), 2191 nullptr); 2192 break; 2193 2194 case Primitive::kPrimDouble: 2195 // Processing a Dex `double-to-float' instruction. 2196 __ vcvtsd(out.AsFpuRegister<SRegister>(), 2197 FromLowSToD(in.AsFpuRegisterPairLow<SRegister>())); 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::kPrimDouble: 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-double' instruction. 2215 __ vmovsr(out.AsFpuRegisterPairLow<SRegister>(), in.AsRegister<Register>()); 2216 __ vcvtdi(FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()), 2217 out.AsFpuRegisterPairLow<SRegister>()); 2218 break; 2219 } 2220 2221 case Primitive::kPrimLong: { 2222 // Processing a Dex `long-to-double' instruction. 2223 Register low = in.AsRegisterPairLow<Register>(); 2224 Register high = in.AsRegisterPairHigh<Register>(); 2225 SRegister out_s = out.AsFpuRegisterPairLow<SRegister>(); 2226 DRegister out_d = FromLowSToD(out_s); 2227 SRegister temp_s = locations->GetTemp(0).AsFpuRegisterPairLow<SRegister>(); 2228 DRegister temp_d = FromLowSToD(temp_s); 2229 SRegister constant_s = locations->GetTemp(1).AsFpuRegisterPairLow<SRegister>(); 2230 DRegister constant_d = FromLowSToD(constant_s); 2231 2232 // temp_d = int-to-double(high) 2233 __ vmovsr(temp_s, high); 2234 __ vcvtdi(temp_d, temp_s); 2235 // constant_d = k2Pow32EncodingForDouble 2236 __ LoadDImmediate(constant_d, bit_cast<double, int64_t>(k2Pow32EncodingForDouble)); 2237 // out_d = unsigned-to-double(low) 2238 __ vmovsr(out_s, low); 2239 __ vcvtdu(out_d, out_s); 2240 // out_d += temp_d * constant_d 2241 __ vmlad(out_d, temp_d, constant_d); 2242 break; 2243 } 2244 2245 case Primitive::kPrimFloat: 2246 // Processing a Dex `float-to-double' instruction. 2247 __ vcvtds(FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()), 2248 in.AsFpuRegister<SRegister>()); 2249 break; 2250 2251 default: 2252 LOG(FATAL) << "Unexpected type conversion from " << input_type 2253 << " to " << result_type; 2254 }; 2255 break; 2256 2257 default: 2258 LOG(FATAL) << "Unexpected type conversion from " << input_type 2259 << " to " << result_type; 2260 } 2261} 2262 2263void LocationsBuilderARM::VisitAdd(HAdd* add) { 2264 LocationSummary* locations = 2265 new (GetGraph()->GetArena()) LocationSummary(add, LocationSummary::kNoCall); 2266 switch (add->GetResultType()) { 2267 case Primitive::kPrimInt: { 2268 locations->SetInAt(0, Location::RequiresRegister()); 2269 locations->SetInAt(1, Location::RegisterOrConstant(add->InputAt(1))); 2270 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 2271 break; 2272 } 2273 2274 case Primitive::kPrimLong: { 2275 locations->SetInAt(0, Location::RequiresRegister()); 2276 locations->SetInAt(1, Location::RequiresRegister()); 2277 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 2278 break; 2279 } 2280 2281 case Primitive::kPrimFloat: 2282 case Primitive::kPrimDouble: { 2283 locations->SetInAt(0, Location::RequiresFpuRegister()); 2284 locations->SetInAt(1, Location::RequiresFpuRegister()); 2285 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); 2286 break; 2287 } 2288 2289 default: 2290 LOG(FATAL) << "Unexpected add type " << add->GetResultType(); 2291 } 2292} 2293 2294void InstructionCodeGeneratorARM::VisitAdd(HAdd* add) { 2295 LocationSummary* locations = add->GetLocations(); 2296 Location out = locations->Out(); 2297 Location first = locations->InAt(0); 2298 Location second = locations->InAt(1); 2299 switch (add->GetResultType()) { 2300 case Primitive::kPrimInt: 2301 if (second.IsRegister()) { 2302 __ add(out.AsRegister<Register>(), 2303 first.AsRegister<Register>(), 2304 ShifterOperand(second.AsRegister<Register>())); 2305 } else { 2306 __ AddConstant(out.AsRegister<Register>(), 2307 first.AsRegister<Register>(), 2308 second.GetConstant()->AsIntConstant()->GetValue()); 2309 } 2310 break; 2311 2312 case Primitive::kPrimLong: { 2313 DCHECK(second.IsRegisterPair()); 2314 __ adds(out.AsRegisterPairLow<Register>(), 2315 first.AsRegisterPairLow<Register>(), 2316 ShifterOperand(second.AsRegisterPairLow<Register>())); 2317 __ adc(out.AsRegisterPairHigh<Register>(), 2318 first.AsRegisterPairHigh<Register>(), 2319 ShifterOperand(second.AsRegisterPairHigh<Register>())); 2320 break; 2321 } 2322 2323 case Primitive::kPrimFloat: 2324 __ vadds(out.AsFpuRegister<SRegister>(), 2325 first.AsFpuRegister<SRegister>(), 2326 second.AsFpuRegister<SRegister>()); 2327 break; 2328 2329 case Primitive::kPrimDouble: 2330 __ vaddd(FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()), 2331 FromLowSToD(first.AsFpuRegisterPairLow<SRegister>()), 2332 FromLowSToD(second.AsFpuRegisterPairLow<SRegister>())); 2333 break; 2334 2335 default: 2336 LOG(FATAL) << "Unexpected add type " << add->GetResultType(); 2337 } 2338} 2339 2340void LocationsBuilderARM::VisitSub(HSub* sub) { 2341 LocationSummary* locations = 2342 new (GetGraph()->GetArena()) LocationSummary(sub, LocationSummary::kNoCall); 2343 switch (sub->GetResultType()) { 2344 case Primitive::kPrimInt: { 2345 locations->SetInAt(0, Location::RequiresRegister()); 2346 locations->SetInAt(1, Location::RegisterOrConstant(sub->InputAt(1))); 2347 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 2348 break; 2349 } 2350 2351 case Primitive::kPrimLong: { 2352 locations->SetInAt(0, Location::RequiresRegister()); 2353 locations->SetInAt(1, Location::RequiresRegister()); 2354 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 2355 break; 2356 } 2357 case Primitive::kPrimFloat: 2358 case Primitive::kPrimDouble: { 2359 locations->SetInAt(0, Location::RequiresFpuRegister()); 2360 locations->SetInAt(1, Location::RequiresFpuRegister()); 2361 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); 2362 break; 2363 } 2364 default: 2365 LOG(FATAL) << "Unexpected sub type " << sub->GetResultType(); 2366 } 2367} 2368 2369void InstructionCodeGeneratorARM::VisitSub(HSub* sub) { 2370 LocationSummary* locations = sub->GetLocations(); 2371 Location out = locations->Out(); 2372 Location first = locations->InAt(0); 2373 Location second = locations->InAt(1); 2374 switch (sub->GetResultType()) { 2375 case Primitive::kPrimInt: { 2376 if (second.IsRegister()) { 2377 __ sub(out.AsRegister<Register>(), 2378 first.AsRegister<Register>(), 2379 ShifterOperand(second.AsRegister<Register>())); 2380 } else { 2381 __ AddConstant(out.AsRegister<Register>(), 2382 first.AsRegister<Register>(), 2383 -second.GetConstant()->AsIntConstant()->GetValue()); 2384 } 2385 break; 2386 } 2387 2388 case Primitive::kPrimLong: { 2389 DCHECK(second.IsRegisterPair()); 2390 __ subs(out.AsRegisterPairLow<Register>(), 2391 first.AsRegisterPairLow<Register>(), 2392 ShifterOperand(second.AsRegisterPairLow<Register>())); 2393 __ sbc(out.AsRegisterPairHigh<Register>(), 2394 first.AsRegisterPairHigh<Register>(), 2395 ShifterOperand(second.AsRegisterPairHigh<Register>())); 2396 break; 2397 } 2398 2399 case Primitive::kPrimFloat: { 2400 __ vsubs(out.AsFpuRegister<SRegister>(), 2401 first.AsFpuRegister<SRegister>(), 2402 second.AsFpuRegister<SRegister>()); 2403 break; 2404 } 2405 2406 case Primitive::kPrimDouble: { 2407 __ vsubd(FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()), 2408 FromLowSToD(first.AsFpuRegisterPairLow<SRegister>()), 2409 FromLowSToD(second.AsFpuRegisterPairLow<SRegister>())); 2410 break; 2411 } 2412 2413 2414 default: 2415 LOG(FATAL) << "Unexpected sub type " << sub->GetResultType(); 2416 } 2417} 2418 2419void LocationsBuilderARM::VisitMul(HMul* mul) { 2420 LocationSummary* locations = 2421 new (GetGraph()->GetArena()) LocationSummary(mul, LocationSummary::kNoCall); 2422 switch (mul->GetResultType()) { 2423 case Primitive::kPrimInt: 2424 case Primitive::kPrimLong: { 2425 locations->SetInAt(0, Location::RequiresRegister()); 2426 locations->SetInAt(1, Location::RequiresRegister()); 2427 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 2428 break; 2429 } 2430 2431 case Primitive::kPrimFloat: 2432 case Primitive::kPrimDouble: { 2433 locations->SetInAt(0, Location::RequiresFpuRegister()); 2434 locations->SetInAt(1, Location::RequiresFpuRegister()); 2435 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); 2436 break; 2437 } 2438 2439 default: 2440 LOG(FATAL) << "Unexpected mul type " << mul->GetResultType(); 2441 } 2442} 2443 2444void InstructionCodeGeneratorARM::VisitMul(HMul* mul) { 2445 LocationSummary* locations = mul->GetLocations(); 2446 Location out = locations->Out(); 2447 Location first = locations->InAt(0); 2448 Location second = locations->InAt(1); 2449 switch (mul->GetResultType()) { 2450 case Primitive::kPrimInt: { 2451 __ mul(out.AsRegister<Register>(), 2452 first.AsRegister<Register>(), 2453 second.AsRegister<Register>()); 2454 break; 2455 } 2456 case Primitive::kPrimLong: { 2457 Register out_hi = out.AsRegisterPairHigh<Register>(); 2458 Register out_lo = out.AsRegisterPairLow<Register>(); 2459 Register in1_hi = first.AsRegisterPairHigh<Register>(); 2460 Register in1_lo = first.AsRegisterPairLow<Register>(); 2461 Register in2_hi = second.AsRegisterPairHigh<Register>(); 2462 Register in2_lo = second.AsRegisterPairLow<Register>(); 2463 2464 // Extra checks to protect caused by the existence of R1_R2. 2465 // The algorithm is wrong if out.hi is either in1.lo or in2.lo: 2466 // (e.g. in1=r0_r1, in2=r2_r3 and out=r1_r2); 2467 DCHECK_NE(out_hi, in1_lo); 2468 DCHECK_NE(out_hi, in2_lo); 2469 2470 // input: in1 - 64 bits, in2 - 64 bits 2471 // output: out 2472 // formula: out.hi : out.lo = (in1.lo * in2.hi + in1.hi * in2.lo)* 2^32 + in1.lo * in2.lo 2473 // parts: out.hi = in1.lo * in2.hi + in1.hi * in2.lo + (in1.lo * in2.lo)[63:32] 2474 // parts: out.lo = (in1.lo * in2.lo)[31:0] 2475 2476 // IP <- in1.lo * in2.hi 2477 __ mul(IP, in1_lo, in2_hi); 2478 // out.hi <- in1.lo * in2.hi + in1.hi * in2.lo 2479 __ mla(out_hi, in1_hi, in2_lo, IP); 2480 // out.lo <- (in1.lo * in2.lo)[31:0]; 2481 __ umull(out_lo, IP, in1_lo, in2_lo); 2482 // out.hi <- in2.hi * in1.lo + in2.lo * in1.hi + (in1.lo * in2.lo)[63:32] 2483 __ add(out_hi, out_hi, ShifterOperand(IP)); 2484 break; 2485 } 2486 2487 case Primitive::kPrimFloat: { 2488 __ vmuls(out.AsFpuRegister<SRegister>(), 2489 first.AsFpuRegister<SRegister>(), 2490 second.AsFpuRegister<SRegister>()); 2491 break; 2492 } 2493 2494 case Primitive::kPrimDouble: { 2495 __ vmuld(FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()), 2496 FromLowSToD(first.AsFpuRegisterPairLow<SRegister>()), 2497 FromLowSToD(second.AsFpuRegisterPairLow<SRegister>())); 2498 break; 2499 } 2500 2501 default: 2502 LOG(FATAL) << "Unexpected mul type " << mul->GetResultType(); 2503 } 2504} 2505 2506void InstructionCodeGeneratorARM::DivRemOneOrMinusOne(HBinaryOperation* instruction) { 2507 DCHECK(instruction->IsDiv() || instruction->IsRem()); 2508 DCHECK(instruction->GetResultType() == Primitive::kPrimInt); 2509 2510 LocationSummary* locations = instruction->GetLocations(); 2511 Location second = locations->InAt(1); 2512 DCHECK(second.IsConstant()); 2513 2514 Register out = locations->Out().AsRegister<Register>(); 2515 Register dividend = locations->InAt(0).AsRegister<Register>(); 2516 int32_t imm = second.GetConstant()->AsIntConstant()->GetValue(); 2517 DCHECK(imm == 1 || imm == -1); 2518 2519 if (instruction->IsRem()) { 2520 __ LoadImmediate(out, 0); 2521 } else { 2522 if (imm == 1) { 2523 __ Mov(out, dividend); 2524 } else { 2525 __ rsb(out, dividend, ShifterOperand(0)); 2526 } 2527 } 2528} 2529 2530void InstructionCodeGeneratorARM::DivRemByPowerOfTwo(HBinaryOperation* instruction) { 2531 DCHECK(instruction->IsDiv() || instruction->IsRem()); 2532 DCHECK(instruction->GetResultType() == Primitive::kPrimInt); 2533 2534 LocationSummary* locations = instruction->GetLocations(); 2535 Location second = locations->InAt(1); 2536 DCHECK(second.IsConstant()); 2537 2538 Register out = locations->Out().AsRegister<Register>(); 2539 Register dividend = locations->InAt(0).AsRegister<Register>(); 2540 Register temp = locations->GetTemp(0).AsRegister<Register>(); 2541 int32_t imm = second.GetConstant()->AsIntConstant()->GetValue(); 2542 uint32_t abs_imm = static_cast<uint32_t>(std::abs(imm)); 2543 DCHECK(IsPowerOfTwo(abs_imm)); 2544 int ctz_imm = CTZ(abs_imm); 2545 2546 if (ctz_imm == 1) { 2547 __ Lsr(temp, dividend, 32 - ctz_imm); 2548 } else { 2549 __ Asr(temp, dividend, 31); 2550 __ Lsr(temp, temp, 32 - ctz_imm); 2551 } 2552 __ add(out, temp, ShifterOperand(dividend)); 2553 2554 if (instruction->IsDiv()) { 2555 __ Asr(out, out, ctz_imm); 2556 if (imm < 0) { 2557 __ rsb(out, out, ShifterOperand(0)); 2558 } 2559 } else { 2560 __ ubfx(out, out, 0, ctz_imm); 2561 __ sub(out, out, ShifterOperand(temp)); 2562 } 2563} 2564 2565void InstructionCodeGeneratorARM::GenerateDivRemWithAnyConstant(HBinaryOperation* instruction) { 2566 DCHECK(instruction->IsDiv() || instruction->IsRem()); 2567 DCHECK(instruction->GetResultType() == Primitive::kPrimInt); 2568 2569 LocationSummary* locations = instruction->GetLocations(); 2570 Location second = locations->InAt(1); 2571 DCHECK(second.IsConstant()); 2572 2573 Register out = locations->Out().AsRegister<Register>(); 2574 Register dividend = locations->InAt(0).AsRegister<Register>(); 2575 Register temp1 = locations->GetTemp(0).AsRegister<Register>(); 2576 Register temp2 = locations->GetTemp(1).AsRegister<Register>(); 2577 int64_t imm = second.GetConstant()->AsIntConstant()->GetValue(); 2578 2579 int64_t magic; 2580 int shift; 2581 CalculateMagicAndShiftForDivRem(imm, false /* is_long */, &magic, &shift); 2582 2583 __ LoadImmediate(temp1, magic); 2584 __ smull(temp2, temp1, dividend, temp1); 2585 2586 if (imm > 0 && magic < 0) { 2587 __ add(temp1, temp1, ShifterOperand(dividend)); 2588 } else if (imm < 0 && magic > 0) { 2589 __ sub(temp1, temp1, ShifterOperand(dividend)); 2590 } 2591 2592 if (shift != 0) { 2593 __ Asr(temp1, temp1, shift); 2594 } 2595 2596 if (instruction->IsDiv()) { 2597 __ sub(out, temp1, ShifterOperand(temp1, ASR, 31)); 2598 } else { 2599 __ sub(temp1, temp1, ShifterOperand(temp1, ASR, 31)); 2600 // TODO: Strength reduction for mls. 2601 __ LoadImmediate(temp2, imm); 2602 __ mls(out, temp1, temp2, dividend); 2603 } 2604} 2605 2606void InstructionCodeGeneratorARM::GenerateDivRemConstantIntegral(HBinaryOperation* instruction) { 2607 DCHECK(instruction->IsDiv() || instruction->IsRem()); 2608 DCHECK(instruction->GetResultType() == Primitive::kPrimInt); 2609 2610 LocationSummary* locations = instruction->GetLocations(); 2611 Location second = locations->InAt(1); 2612 DCHECK(second.IsConstant()); 2613 2614 int32_t imm = second.GetConstant()->AsIntConstant()->GetValue(); 2615 if (imm == 0) { 2616 // Do not generate anything. DivZeroCheck would prevent any code to be executed. 2617 } else if (imm == 1 || imm == -1) { 2618 DivRemOneOrMinusOne(instruction); 2619 } else if (IsPowerOfTwo(std::abs(imm))) { 2620 DivRemByPowerOfTwo(instruction); 2621 } else { 2622 DCHECK(imm <= -2 || imm >= 2); 2623 GenerateDivRemWithAnyConstant(instruction); 2624 } 2625} 2626 2627void LocationsBuilderARM::VisitDiv(HDiv* div) { 2628 LocationSummary::CallKind call_kind = LocationSummary::kNoCall; 2629 if (div->GetResultType() == Primitive::kPrimLong) { 2630 // pLdiv runtime call. 2631 call_kind = LocationSummary::kCall; 2632 } else if (div->GetResultType() == Primitive::kPrimInt && div->InputAt(1)->IsConstant()) { 2633 // sdiv will be replaced by other instruction sequence. 2634 } else if (div->GetResultType() == Primitive::kPrimInt && 2635 !codegen_->GetInstructionSetFeatures().HasDivideInstruction()) { 2636 // pIdivmod runtime call. 2637 call_kind = LocationSummary::kCall; 2638 } 2639 2640 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(div, call_kind); 2641 2642 switch (div->GetResultType()) { 2643 case Primitive::kPrimInt: { 2644 if (div->InputAt(1)->IsConstant()) { 2645 locations->SetInAt(0, Location::RequiresRegister()); 2646 locations->SetInAt(1, Location::RegisterOrConstant(div->InputAt(1))); 2647 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 2648 int32_t abs_imm = std::abs(div->InputAt(1)->AsIntConstant()->GetValue()); 2649 if (abs_imm <= 1) { 2650 // No temp register required. 2651 } else { 2652 locations->AddTemp(Location::RequiresRegister()); 2653 if (!IsPowerOfTwo(abs_imm)) { 2654 locations->AddTemp(Location::RequiresRegister()); 2655 } 2656 } 2657 } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) { 2658 locations->SetInAt(0, Location::RequiresRegister()); 2659 locations->SetInAt(1, Location::RequiresRegister()); 2660 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 2661 } else { 2662 InvokeRuntimeCallingConvention calling_convention; 2663 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); 2664 locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); 2665 // Note: divrem will compute both the quotient and the remainder as the pair R0 and R1, but 2666 // we only need the former. 2667 locations->SetOut(Location::RegisterLocation(R0)); 2668 } 2669 break; 2670 } 2671 case Primitive::kPrimLong: { 2672 InvokeRuntimeCallingConvention calling_convention; 2673 locations->SetInAt(0, Location::RegisterPairLocation( 2674 calling_convention.GetRegisterAt(0), calling_convention.GetRegisterAt(1))); 2675 locations->SetInAt(1, Location::RegisterPairLocation( 2676 calling_convention.GetRegisterAt(2), calling_convention.GetRegisterAt(3))); 2677 locations->SetOut(Location::RegisterPairLocation(R0, R1)); 2678 break; 2679 } 2680 case Primitive::kPrimFloat: 2681 case Primitive::kPrimDouble: { 2682 locations->SetInAt(0, Location::RequiresFpuRegister()); 2683 locations->SetInAt(1, Location::RequiresFpuRegister()); 2684 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); 2685 break; 2686 } 2687 2688 default: 2689 LOG(FATAL) << "Unexpected div type " << div->GetResultType(); 2690 } 2691} 2692 2693void InstructionCodeGeneratorARM::VisitDiv(HDiv* div) { 2694 LocationSummary* locations = div->GetLocations(); 2695 Location out = locations->Out(); 2696 Location first = locations->InAt(0); 2697 Location second = locations->InAt(1); 2698 2699 switch (div->GetResultType()) { 2700 case Primitive::kPrimInt: { 2701 if (second.IsConstant()) { 2702 GenerateDivRemConstantIntegral(div); 2703 } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) { 2704 __ sdiv(out.AsRegister<Register>(), 2705 first.AsRegister<Register>(), 2706 second.AsRegister<Register>()); 2707 } else { 2708 InvokeRuntimeCallingConvention calling_convention; 2709 DCHECK_EQ(calling_convention.GetRegisterAt(0), first.AsRegister<Register>()); 2710 DCHECK_EQ(calling_convention.GetRegisterAt(1), second.AsRegister<Register>()); 2711 DCHECK_EQ(R0, out.AsRegister<Register>()); 2712 2713 codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pIdivmod), div, div->GetDexPc(), nullptr); 2714 } 2715 break; 2716 } 2717 2718 case Primitive::kPrimLong: { 2719 InvokeRuntimeCallingConvention calling_convention; 2720 DCHECK_EQ(calling_convention.GetRegisterAt(0), first.AsRegisterPairLow<Register>()); 2721 DCHECK_EQ(calling_convention.GetRegisterAt(1), first.AsRegisterPairHigh<Register>()); 2722 DCHECK_EQ(calling_convention.GetRegisterAt(2), second.AsRegisterPairLow<Register>()); 2723 DCHECK_EQ(calling_convention.GetRegisterAt(3), second.AsRegisterPairHigh<Register>()); 2724 DCHECK_EQ(R0, out.AsRegisterPairLow<Register>()); 2725 DCHECK_EQ(R1, out.AsRegisterPairHigh<Register>()); 2726 2727 codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pLdiv), div, div->GetDexPc(), nullptr); 2728 break; 2729 } 2730 2731 case Primitive::kPrimFloat: { 2732 __ vdivs(out.AsFpuRegister<SRegister>(), 2733 first.AsFpuRegister<SRegister>(), 2734 second.AsFpuRegister<SRegister>()); 2735 break; 2736 } 2737 2738 case Primitive::kPrimDouble: { 2739 __ vdivd(FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()), 2740 FromLowSToD(first.AsFpuRegisterPairLow<SRegister>()), 2741 FromLowSToD(second.AsFpuRegisterPairLow<SRegister>())); 2742 break; 2743 } 2744 2745 default: 2746 LOG(FATAL) << "Unexpected div type " << div->GetResultType(); 2747 } 2748} 2749 2750void LocationsBuilderARM::VisitRem(HRem* rem) { 2751 Primitive::Type type = rem->GetResultType(); 2752 2753 // Most remainders are implemented in the runtime. 2754 LocationSummary::CallKind call_kind = LocationSummary::kCall; 2755 if (rem->GetResultType() == Primitive::kPrimInt && rem->InputAt(1)->IsConstant()) { 2756 // sdiv will be replaced by other instruction sequence. 2757 call_kind = LocationSummary::kNoCall; 2758 } else if ((rem->GetResultType() == Primitive::kPrimInt) 2759 && codegen_->GetInstructionSetFeatures().HasDivideInstruction()) { 2760 // Have hardware divide instruction for int, do it with three instructions. 2761 call_kind = LocationSummary::kNoCall; 2762 } 2763 2764 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(rem, call_kind); 2765 2766 switch (type) { 2767 case Primitive::kPrimInt: { 2768 if (rem->InputAt(1)->IsConstant()) { 2769 locations->SetInAt(0, Location::RequiresRegister()); 2770 locations->SetInAt(1, Location::RegisterOrConstant(rem->InputAt(1))); 2771 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 2772 int32_t abs_imm = std::abs(rem->InputAt(1)->AsIntConstant()->GetValue()); 2773 if (abs_imm <= 1) { 2774 // No temp register required. 2775 } else { 2776 locations->AddTemp(Location::RequiresRegister()); 2777 if (!IsPowerOfTwo(abs_imm)) { 2778 locations->AddTemp(Location::RequiresRegister()); 2779 } 2780 } 2781 } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) { 2782 locations->SetInAt(0, Location::RequiresRegister()); 2783 locations->SetInAt(1, Location::RequiresRegister()); 2784 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 2785 locations->AddTemp(Location::RequiresRegister()); 2786 } else { 2787 InvokeRuntimeCallingConvention calling_convention; 2788 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); 2789 locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); 2790 // Note: divrem will compute both the quotient and the remainder as the pair R0 and R1, but 2791 // we only need the latter. 2792 locations->SetOut(Location::RegisterLocation(R1)); 2793 } 2794 break; 2795 } 2796 case Primitive::kPrimLong: { 2797 InvokeRuntimeCallingConvention calling_convention; 2798 locations->SetInAt(0, Location::RegisterPairLocation( 2799 calling_convention.GetRegisterAt(0), calling_convention.GetRegisterAt(1))); 2800 locations->SetInAt(1, Location::RegisterPairLocation( 2801 calling_convention.GetRegisterAt(2), calling_convention.GetRegisterAt(3))); 2802 // The runtime helper puts the output in R2,R3. 2803 locations->SetOut(Location::RegisterPairLocation(R2, R3)); 2804 break; 2805 } 2806 case Primitive::kPrimFloat: { 2807 InvokeRuntimeCallingConvention calling_convention; 2808 locations->SetInAt(0, Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(0))); 2809 locations->SetInAt(1, Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(1))); 2810 locations->SetOut(Location::FpuRegisterLocation(S0)); 2811 break; 2812 } 2813 2814 case Primitive::kPrimDouble: { 2815 InvokeRuntimeCallingConvention calling_convention; 2816 locations->SetInAt(0, Location::FpuRegisterPairLocation( 2817 calling_convention.GetFpuRegisterAt(0), calling_convention.GetFpuRegisterAt(1))); 2818 locations->SetInAt(1, Location::FpuRegisterPairLocation( 2819 calling_convention.GetFpuRegisterAt(2), calling_convention.GetFpuRegisterAt(3))); 2820 locations->SetOut(Location::Location::FpuRegisterPairLocation(S0, S1)); 2821 break; 2822 } 2823 2824 default: 2825 LOG(FATAL) << "Unexpected rem type " << type; 2826 } 2827} 2828 2829void InstructionCodeGeneratorARM::VisitRem(HRem* rem) { 2830 LocationSummary* locations = rem->GetLocations(); 2831 Location out = locations->Out(); 2832 Location first = locations->InAt(0); 2833 Location second = locations->InAt(1); 2834 2835 Primitive::Type type = rem->GetResultType(); 2836 switch (type) { 2837 case Primitive::kPrimInt: { 2838 if (second.IsConstant()) { 2839 GenerateDivRemConstantIntegral(rem); 2840 } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) { 2841 Register reg1 = first.AsRegister<Register>(); 2842 Register reg2 = second.AsRegister<Register>(); 2843 Register temp = locations->GetTemp(0).AsRegister<Register>(); 2844 2845 // temp = reg1 / reg2 (integer division) 2846 // dest = reg1 - temp * reg2 2847 __ sdiv(temp, reg1, reg2); 2848 __ mls(out.AsRegister<Register>(), temp, reg2, reg1); 2849 } else { 2850 InvokeRuntimeCallingConvention calling_convention; 2851 DCHECK_EQ(calling_convention.GetRegisterAt(0), first.AsRegister<Register>()); 2852 DCHECK_EQ(calling_convention.GetRegisterAt(1), second.AsRegister<Register>()); 2853 DCHECK_EQ(R1, out.AsRegister<Register>()); 2854 2855 codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pIdivmod), rem, rem->GetDexPc(), nullptr); 2856 } 2857 break; 2858 } 2859 2860 case Primitive::kPrimLong: { 2861 codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pLmod), rem, rem->GetDexPc(), nullptr); 2862 break; 2863 } 2864 2865 case Primitive::kPrimFloat: { 2866 codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pFmodf), rem, rem->GetDexPc(), nullptr); 2867 break; 2868 } 2869 2870 case Primitive::kPrimDouble: { 2871 codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pFmod), rem, rem->GetDexPc(), nullptr); 2872 break; 2873 } 2874 2875 default: 2876 LOG(FATAL) << "Unexpected rem type " << type; 2877 } 2878} 2879 2880void LocationsBuilderARM::VisitDivZeroCheck(HDivZeroCheck* instruction) { 2881 LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock() 2882 ? LocationSummary::kCallOnSlowPath 2883 : LocationSummary::kNoCall; 2884 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind); 2885 locations->SetInAt(0, Location::RegisterOrConstant(instruction->InputAt(0))); 2886 if (instruction->HasUses()) { 2887 locations->SetOut(Location::SameAsFirstInput()); 2888 } 2889} 2890 2891void InstructionCodeGeneratorARM::VisitDivZeroCheck(HDivZeroCheck* instruction) { 2892 SlowPathCode* slow_path = new (GetGraph()->GetArena()) DivZeroCheckSlowPathARM(instruction); 2893 codegen_->AddSlowPath(slow_path); 2894 2895 LocationSummary* locations = instruction->GetLocations(); 2896 Location value = locations->InAt(0); 2897 2898 switch (instruction->GetType()) { 2899 case Primitive::kPrimByte: 2900 case Primitive::kPrimChar: 2901 case Primitive::kPrimShort: 2902 case Primitive::kPrimInt: { 2903 if (value.IsRegister()) { 2904 __ CompareAndBranchIfZero(value.AsRegister<Register>(), slow_path->GetEntryLabel()); 2905 } else { 2906 DCHECK(value.IsConstant()) << value; 2907 if (value.GetConstant()->AsIntConstant()->GetValue() == 0) { 2908 __ b(slow_path->GetEntryLabel()); 2909 } 2910 } 2911 break; 2912 } 2913 case Primitive::kPrimLong: { 2914 if (value.IsRegisterPair()) { 2915 __ orrs(IP, 2916 value.AsRegisterPairLow<Register>(), 2917 ShifterOperand(value.AsRegisterPairHigh<Register>())); 2918 __ b(slow_path->GetEntryLabel(), EQ); 2919 } else { 2920 DCHECK(value.IsConstant()) << value; 2921 if (value.GetConstant()->AsLongConstant()->GetValue() == 0) { 2922 __ b(slow_path->GetEntryLabel()); 2923 } 2924 } 2925 break; 2926 default: 2927 LOG(FATAL) << "Unexpected type for HDivZeroCheck " << instruction->GetType(); 2928 } 2929 } 2930} 2931 2932void LocationsBuilderARM::HandleShift(HBinaryOperation* op) { 2933 DCHECK(op->IsShl() || op->IsShr() || op->IsUShr()); 2934 2935 LocationSummary* locations = 2936 new (GetGraph()->GetArena()) LocationSummary(op, LocationSummary::kNoCall); 2937 2938 switch (op->GetResultType()) { 2939 case Primitive::kPrimInt: { 2940 locations->SetInAt(0, Location::RequiresRegister()); 2941 locations->SetInAt(1, Location::RegisterOrConstant(op->InputAt(1))); 2942 // Make the output overlap, as it will be used to hold the masked 2943 // second input. 2944 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); 2945 break; 2946 } 2947 case Primitive::kPrimLong: { 2948 locations->SetInAt(0, Location::RequiresRegister()); 2949 locations->SetInAt(1, Location::RequiresRegister()); 2950 locations->AddTemp(Location::RequiresRegister()); 2951 locations->SetOut(Location::RequiresRegister()); 2952 break; 2953 } 2954 default: 2955 LOG(FATAL) << "Unexpected operation type " << op->GetResultType(); 2956 } 2957} 2958 2959void InstructionCodeGeneratorARM::HandleShift(HBinaryOperation* op) { 2960 DCHECK(op->IsShl() || op->IsShr() || op->IsUShr()); 2961 2962 LocationSummary* locations = op->GetLocations(); 2963 Location out = locations->Out(); 2964 Location first = locations->InAt(0); 2965 Location second = locations->InAt(1); 2966 2967 Primitive::Type type = op->GetResultType(); 2968 switch (type) { 2969 case Primitive::kPrimInt: { 2970 Register out_reg = out.AsRegister<Register>(); 2971 Register first_reg = first.AsRegister<Register>(); 2972 // Arm doesn't mask the shift count so we need to do it ourselves. 2973 if (second.IsRegister()) { 2974 Register second_reg = second.AsRegister<Register>(); 2975 __ and_(out_reg, second_reg, ShifterOperand(kMaxIntShiftValue)); 2976 if (op->IsShl()) { 2977 __ Lsl(out_reg, first_reg, out_reg); 2978 } else if (op->IsShr()) { 2979 __ Asr(out_reg, first_reg, out_reg); 2980 } else { 2981 __ Lsr(out_reg, first_reg, out_reg); 2982 } 2983 } else { 2984 int32_t cst = second.GetConstant()->AsIntConstant()->GetValue(); 2985 uint32_t shift_value = static_cast<uint32_t>(cst & kMaxIntShiftValue); 2986 if (shift_value == 0) { // arm does not support shifting with 0 immediate. 2987 __ Mov(out_reg, first_reg); 2988 } else if (op->IsShl()) { 2989 __ Lsl(out_reg, first_reg, shift_value); 2990 } else if (op->IsShr()) { 2991 __ Asr(out_reg, first_reg, shift_value); 2992 } else { 2993 __ Lsr(out_reg, first_reg, shift_value); 2994 } 2995 } 2996 break; 2997 } 2998 case Primitive::kPrimLong: { 2999 Register o_h = out.AsRegisterPairHigh<Register>(); 3000 Register o_l = out.AsRegisterPairLow<Register>(); 3001 3002 Register temp = locations->GetTemp(0).AsRegister<Register>(); 3003 3004 Register high = first.AsRegisterPairHigh<Register>(); 3005 Register low = first.AsRegisterPairLow<Register>(); 3006 3007 Register second_reg = second.AsRegister<Register>(); 3008 3009 if (op->IsShl()) { 3010 __ and_(o_l, second_reg, ShifterOperand(kMaxLongShiftValue)); 3011 // Shift the high part 3012 __ Lsl(o_h, high, o_l); 3013 // Shift the low part and `or` what overflew on the high part 3014 __ rsb(temp, o_l, ShifterOperand(kArmBitsPerWord)); 3015 __ Lsr(temp, low, temp); 3016 __ orr(o_h, o_h, ShifterOperand(temp)); 3017 // If the shift is > 32 bits, override the high part 3018 __ subs(temp, o_l, ShifterOperand(kArmBitsPerWord)); 3019 __ it(PL); 3020 __ Lsl(o_h, low, temp, PL); 3021 // Shift the low part 3022 __ Lsl(o_l, low, o_l); 3023 } else if (op->IsShr()) { 3024 __ and_(o_h, second_reg, ShifterOperand(kMaxLongShiftValue)); 3025 // Shift the low part 3026 __ Lsr(o_l, low, o_h); 3027 // Shift the high part and `or` what underflew on the low part 3028 __ rsb(temp, o_h, ShifterOperand(kArmBitsPerWord)); 3029 __ Lsl(temp, high, temp); 3030 __ orr(o_l, o_l, ShifterOperand(temp)); 3031 // If the shift is > 32 bits, override the low part 3032 __ subs(temp, o_h, ShifterOperand(kArmBitsPerWord)); 3033 __ it(PL); 3034 __ Asr(o_l, high, temp, PL); 3035 // Shift the high part 3036 __ Asr(o_h, high, o_h); 3037 } else { 3038 __ and_(o_h, second_reg, ShifterOperand(kMaxLongShiftValue)); 3039 // same as Shr except we use `Lsr`s and not `Asr`s 3040 __ Lsr(o_l, low, o_h); 3041 __ rsb(temp, o_h, ShifterOperand(kArmBitsPerWord)); 3042 __ Lsl(temp, high, temp); 3043 __ orr(o_l, o_l, ShifterOperand(temp)); 3044 __ subs(temp, o_h, ShifterOperand(kArmBitsPerWord)); 3045 __ it(PL); 3046 __ Lsr(o_l, high, temp, PL); 3047 __ Lsr(o_h, high, o_h); 3048 } 3049 break; 3050 } 3051 default: 3052 LOG(FATAL) << "Unexpected operation type " << type; 3053 } 3054} 3055 3056void LocationsBuilderARM::VisitShl(HShl* shl) { 3057 HandleShift(shl); 3058} 3059 3060void InstructionCodeGeneratorARM::VisitShl(HShl* shl) { 3061 HandleShift(shl); 3062} 3063 3064void LocationsBuilderARM::VisitShr(HShr* shr) { 3065 HandleShift(shr); 3066} 3067 3068void InstructionCodeGeneratorARM::VisitShr(HShr* shr) { 3069 HandleShift(shr); 3070} 3071 3072void LocationsBuilderARM::VisitUShr(HUShr* ushr) { 3073 HandleShift(ushr); 3074} 3075 3076void InstructionCodeGeneratorARM::VisitUShr(HUShr* ushr) { 3077 HandleShift(ushr); 3078} 3079 3080void LocationsBuilderARM::VisitNewInstance(HNewInstance* instruction) { 3081 LocationSummary* locations = 3082 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall); 3083 InvokeRuntimeCallingConvention calling_convention; 3084 locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0))); 3085 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); 3086 locations->SetOut(Location::RegisterLocation(R0)); 3087} 3088 3089void InstructionCodeGeneratorARM::VisitNewInstance(HNewInstance* instruction) { 3090 InvokeRuntimeCallingConvention calling_convention; 3091 __ LoadImmediate(calling_convention.GetRegisterAt(0), instruction->GetTypeIndex()); 3092 // Note: if heap poisoning is enabled, the entry point takes cares 3093 // of poisoning the reference. 3094 codegen_->InvokeRuntime(instruction->GetEntrypoint(), 3095 instruction, 3096 instruction->GetDexPc(), 3097 nullptr); 3098} 3099 3100void LocationsBuilderARM::VisitNewArray(HNewArray* instruction) { 3101 LocationSummary* locations = 3102 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall); 3103 InvokeRuntimeCallingConvention calling_convention; 3104 locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0))); 3105 locations->SetOut(Location::RegisterLocation(R0)); 3106 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); 3107 locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(2))); 3108} 3109 3110void InstructionCodeGeneratorARM::VisitNewArray(HNewArray* instruction) { 3111 InvokeRuntimeCallingConvention calling_convention; 3112 __ LoadImmediate(calling_convention.GetRegisterAt(0), instruction->GetTypeIndex()); 3113 // Note: if heap poisoning is enabled, the entry point takes cares 3114 // of poisoning the reference. 3115 codegen_->InvokeRuntime(instruction->GetEntrypoint(), 3116 instruction, 3117 instruction->GetDexPc(), 3118 nullptr); 3119} 3120 3121void LocationsBuilderARM::VisitParameterValue(HParameterValue* instruction) { 3122 LocationSummary* locations = 3123 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); 3124 Location location = parameter_visitor_.GetNextLocation(instruction->GetType()); 3125 if (location.IsStackSlot()) { 3126 location = Location::StackSlot(location.GetStackIndex() + codegen_->GetFrameSize()); 3127 } else if (location.IsDoubleStackSlot()) { 3128 location = Location::DoubleStackSlot(location.GetStackIndex() + codegen_->GetFrameSize()); 3129 } 3130 locations->SetOut(location); 3131} 3132 3133void InstructionCodeGeneratorARM::VisitParameterValue( 3134 HParameterValue* instruction ATTRIBUTE_UNUSED) { 3135 // Nothing to do, the parameter is already at its location. 3136} 3137 3138void LocationsBuilderARM::VisitCurrentMethod(HCurrentMethod* instruction) { 3139 LocationSummary* locations = 3140 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); 3141 locations->SetOut(Location::RegisterLocation(kMethodRegisterArgument)); 3142} 3143 3144void InstructionCodeGeneratorARM::VisitCurrentMethod(HCurrentMethod* instruction ATTRIBUTE_UNUSED) { 3145 // Nothing to do, the method is already at its location. 3146} 3147 3148void LocationsBuilderARM::VisitNot(HNot* not_) { 3149 LocationSummary* locations = 3150 new (GetGraph()->GetArena()) LocationSummary(not_, LocationSummary::kNoCall); 3151 locations->SetInAt(0, Location::RequiresRegister()); 3152 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 3153} 3154 3155void InstructionCodeGeneratorARM::VisitNot(HNot* not_) { 3156 LocationSummary* locations = not_->GetLocations(); 3157 Location out = locations->Out(); 3158 Location in = locations->InAt(0); 3159 switch (not_->GetResultType()) { 3160 case Primitive::kPrimInt: 3161 __ mvn(out.AsRegister<Register>(), ShifterOperand(in.AsRegister<Register>())); 3162 break; 3163 3164 case Primitive::kPrimLong: 3165 __ mvn(out.AsRegisterPairLow<Register>(), 3166 ShifterOperand(in.AsRegisterPairLow<Register>())); 3167 __ mvn(out.AsRegisterPairHigh<Register>(), 3168 ShifterOperand(in.AsRegisterPairHigh<Register>())); 3169 break; 3170 3171 default: 3172 LOG(FATAL) << "Unimplemented type for not operation " << not_->GetResultType(); 3173 } 3174} 3175 3176void LocationsBuilderARM::VisitBooleanNot(HBooleanNot* bool_not) { 3177 LocationSummary* locations = 3178 new (GetGraph()->GetArena()) LocationSummary(bool_not, LocationSummary::kNoCall); 3179 locations->SetInAt(0, Location::RequiresRegister()); 3180 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 3181} 3182 3183void InstructionCodeGeneratorARM::VisitBooleanNot(HBooleanNot* bool_not) { 3184 LocationSummary* locations = bool_not->GetLocations(); 3185 Location out = locations->Out(); 3186 Location in = locations->InAt(0); 3187 __ eor(out.AsRegister<Register>(), in.AsRegister<Register>(), ShifterOperand(1)); 3188} 3189 3190void LocationsBuilderARM::VisitCompare(HCompare* compare) { 3191 LocationSummary* locations = 3192 new (GetGraph()->GetArena()) LocationSummary(compare, LocationSummary::kNoCall); 3193 switch (compare->InputAt(0)->GetType()) { 3194 case Primitive::kPrimLong: { 3195 locations->SetInAt(0, Location::RequiresRegister()); 3196 locations->SetInAt(1, Location::RequiresRegister()); 3197 // Output overlaps because it is written before doing the low comparison. 3198 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); 3199 break; 3200 } 3201 case Primitive::kPrimFloat: 3202 case Primitive::kPrimDouble: { 3203 locations->SetInAt(0, Location::RequiresFpuRegister()); 3204 locations->SetInAt(1, Location::RequiresFpuRegister()); 3205 locations->SetOut(Location::RequiresRegister()); 3206 break; 3207 } 3208 default: 3209 LOG(FATAL) << "Unexpected type for compare operation " << compare->InputAt(0)->GetType(); 3210 } 3211} 3212 3213void InstructionCodeGeneratorARM::VisitCompare(HCompare* compare) { 3214 LocationSummary* locations = compare->GetLocations(); 3215 Register out = locations->Out().AsRegister<Register>(); 3216 Location left = locations->InAt(0); 3217 Location right = locations->InAt(1); 3218 3219 Label less, greater, done; 3220 Primitive::Type type = compare->InputAt(0)->GetType(); 3221 switch (type) { 3222 case Primitive::kPrimLong: { 3223 __ cmp(left.AsRegisterPairHigh<Register>(), 3224 ShifterOperand(right.AsRegisterPairHigh<Register>())); // Signed compare. 3225 __ b(&less, LT); 3226 __ b(&greater, GT); 3227 // Do LoadImmediate before the last `cmp`, as LoadImmediate might affect the status flags. 3228 __ LoadImmediate(out, 0); 3229 __ cmp(left.AsRegisterPairLow<Register>(), 3230 ShifterOperand(right.AsRegisterPairLow<Register>())); // Unsigned compare. 3231 break; 3232 } 3233 case Primitive::kPrimFloat: 3234 case Primitive::kPrimDouble: { 3235 __ LoadImmediate(out, 0); 3236 if (type == Primitive::kPrimFloat) { 3237 __ vcmps(left.AsFpuRegister<SRegister>(), right.AsFpuRegister<SRegister>()); 3238 } else { 3239 __ vcmpd(FromLowSToD(left.AsFpuRegisterPairLow<SRegister>()), 3240 FromLowSToD(right.AsFpuRegisterPairLow<SRegister>())); 3241 } 3242 __ vmstat(); // transfer FP status register to ARM APSR. 3243 __ b(compare->IsGtBias() ? &greater : &less, VS); // VS for unordered. 3244 break; 3245 } 3246 default: 3247 LOG(FATAL) << "Unexpected compare type " << type; 3248 } 3249 __ b(&done, EQ); 3250 __ b(&less, LO); // LO is for both: unsigned compare for longs and 'less than' for floats. 3251 3252 __ Bind(&greater); 3253 __ LoadImmediate(out, 1); 3254 __ b(&done); 3255 3256 __ Bind(&less); 3257 __ LoadImmediate(out, -1); 3258 3259 __ Bind(&done); 3260} 3261 3262void LocationsBuilderARM::VisitPhi(HPhi* instruction) { 3263 LocationSummary* locations = 3264 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); 3265 for (size_t i = 0, e = instruction->InputCount(); i < e; ++i) { 3266 locations->SetInAt(i, Location::Any()); 3267 } 3268 locations->SetOut(Location::Any()); 3269} 3270 3271void InstructionCodeGeneratorARM::VisitPhi(HPhi* instruction) { 3272 UNUSED(instruction); 3273 LOG(FATAL) << "Unreachable"; 3274} 3275 3276void InstructionCodeGeneratorARM::GenerateMemoryBarrier(MemBarrierKind kind) { 3277 // TODO (ported from quick): revisit Arm barrier kinds 3278 DmbOptions flavor = DmbOptions::ISH; // quiet c++ warnings 3279 switch (kind) { 3280 case MemBarrierKind::kAnyStore: 3281 case MemBarrierKind::kLoadAny: 3282 case MemBarrierKind::kAnyAny: { 3283 flavor = DmbOptions::ISH; 3284 break; 3285 } 3286 case MemBarrierKind::kStoreStore: { 3287 flavor = DmbOptions::ISHST; 3288 break; 3289 } 3290 default: 3291 LOG(FATAL) << "Unexpected memory barrier " << kind; 3292 } 3293 __ dmb(flavor); 3294} 3295 3296void InstructionCodeGeneratorARM::GenerateWideAtomicLoad(Register addr, 3297 uint32_t offset, 3298 Register out_lo, 3299 Register out_hi) { 3300 if (offset != 0) { 3301 __ LoadImmediate(out_lo, offset); 3302 __ add(IP, addr, ShifterOperand(out_lo)); 3303 addr = IP; 3304 } 3305 __ ldrexd(out_lo, out_hi, addr); 3306} 3307 3308void InstructionCodeGeneratorARM::GenerateWideAtomicStore(Register addr, 3309 uint32_t offset, 3310 Register value_lo, 3311 Register value_hi, 3312 Register temp1, 3313 Register temp2, 3314 HInstruction* instruction) { 3315 Label fail; 3316 if (offset != 0) { 3317 __ LoadImmediate(temp1, offset); 3318 __ add(IP, addr, ShifterOperand(temp1)); 3319 addr = IP; 3320 } 3321 __ Bind(&fail); 3322 // We need a load followed by store. (The address used in a STREX instruction must 3323 // be the same as the address in the most recently executed LDREX instruction.) 3324 __ ldrexd(temp1, temp2, addr); 3325 codegen_->MaybeRecordImplicitNullCheck(instruction); 3326 __ strexd(temp1, value_lo, value_hi, addr); 3327 __ CompareAndBranchIfNonZero(temp1, &fail); 3328} 3329 3330void LocationsBuilderARM::HandleFieldSet(HInstruction* instruction, const FieldInfo& field_info) { 3331 DCHECK(instruction->IsInstanceFieldSet() || instruction->IsStaticFieldSet()); 3332 3333 LocationSummary* locations = 3334 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); 3335 locations->SetInAt(0, Location::RequiresRegister()); 3336 3337 Primitive::Type field_type = field_info.GetFieldType(); 3338 if (Primitive::IsFloatingPointType(field_type)) { 3339 locations->SetInAt(1, Location::RequiresFpuRegister()); 3340 } else { 3341 locations->SetInAt(1, Location::RequiresRegister()); 3342 } 3343 3344 bool is_wide = field_type == Primitive::kPrimLong || field_type == Primitive::kPrimDouble; 3345 bool generate_volatile = field_info.IsVolatile() 3346 && is_wide 3347 && !codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd(); 3348 bool needs_write_barrier = 3349 CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1)); 3350 // Temporary registers for the write barrier. 3351 // TODO: consider renaming StoreNeedsWriteBarrier to StoreNeedsGCMark. 3352 if (needs_write_barrier) { 3353 locations->AddTemp(Location::RequiresRegister()); // Possibly used for reference poisoning too. 3354 locations->AddTemp(Location::RequiresRegister()); 3355 } else if (generate_volatile) { 3356 // Arm encoding have some additional constraints for ldrexd/strexd: 3357 // - registers need to be consecutive 3358 // - the first register should be even but not R14. 3359 // We don't test for Arm yet, and the assertion makes sure that we revisit this if we ever 3360 // enable Arm encoding. 3361 DCHECK_EQ(InstructionSet::kThumb2, codegen_->GetInstructionSet()); 3362 3363 locations->AddTemp(Location::RequiresRegister()); 3364 locations->AddTemp(Location::RequiresRegister()); 3365 if (field_type == Primitive::kPrimDouble) { 3366 // For doubles we need two more registers to copy the value. 3367 locations->AddTemp(Location::RegisterLocation(R2)); 3368 locations->AddTemp(Location::RegisterLocation(R3)); 3369 } 3370 } 3371} 3372 3373void InstructionCodeGeneratorARM::HandleFieldSet(HInstruction* instruction, 3374 const FieldInfo& field_info, 3375 bool value_can_be_null) { 3376 DCHECK(instruction->IsInstanceFieldSet() || instruction->IsStaticFieldSet()); 3377 3378 LocationSummary* locations = instruction->GetLocations(); 3379 Register base = locations->InAt(0).AsRegister<Register>(); 3380 Location value = locations->InAt(1); 3381 3382 bool is_volatile = field_info.IsVolatile(); 3383 bool atomic_ldrd_strd = codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd(); 3384 Primitive::Type field_type = field_info.GetFieldType(); 3385 uint32_t offset = field_info.GetFieldOffset().Uint32Value(); 3386 bool needs_write_barrier = 3387 CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1)); 3388 3389 if (is_volatile) { 3390 GenerateMemoryBarrier(MemBarrierKind::kAnyStore); 3391 } 3392 3393 switch (field_type) { 3394 case Primitive::kPrimBoolean: 3395 case Primitive::kPrimByte: { 3396 __ StoreToOffset(kStoreByte, value.AsRegister<Register>(), base, offset); 3397 break; 3398 } 3399 3400 case Primitive::kPrimShort: 3401 case Primitive::kPrimChar: { 3402 __ StoreToOffset(kStoreHalfword, value.AsRegister<Register>(), base, offset); 3403 break; 3404 } 3405 3406 case Primitive::kPrimInt: 3407 case Primitive::kPrimNot: { 3408 if (kPoisonHeapReferences && needs_write_barrier) { 3409 // Note that in the case where `value` is a null reference, 3410 // we do not enter this block, as a null reference does not 3411 // need poisoning. 3412 DCHECK_EQ(field_type, Primitive::kPrimNot); 3413 Register temp = locations->GetTemp(0).AsRegister<Register>(); 3414 __ Mov(temp, value.AsRegister<Register>()); 3415 __ PoisonHeapReference(temp); 3416 __ StoreToOffset(kStoreWord, temp, base, offset); 3417 } else { 3418 __ StoreToOffset(kStoreWord, value.AsRegister<Register>(), base, offset); 3419 } 3420 break; 3421 } 3422 3423 case Primitive::kPrimLong: { 3424 if (is_volatile && !atomic_ldrd_strd) { 3425 GenerateWideAtomicStore(base, offset, 3426 value.AsRegisterPairLow<Register>(), 3427 value.AsRegisterPairHigh<Register>(), 3428 locations->GetTemp(0).AsRegister<Register>(), 3429 locations->GetTemp(1).AsRegister<Register>(), 3430 instruction); 3431 } else { 3432 __ StoreToOffset(kStoreWordPair, value.AsRegisterPairLow<Register>(), base, offset); 3433 codegen_->MaybeRecordImplicitNullCheck(instruction); 3434 } 3435 break; 3436 } 3437 3438 case Primitive::kPrimFloat: { 3439 __ StoreSToOffset(value.AsFpuRegister<SRegister>(), base, offset); 3440 break; 3441 } 3442 3443 case Primitive::kPrimDouble: { 3444 DRegister value_reg = FromLowSToD(value.AsFpuRegisterPairLow<SRegister>()); 3445 if (is_volatile && !atomic_ldrd_strd) { 3446 Register value_reg_lo = locations->GetTemp(0).AsRegister<Register>(); 3447 Register value_reg_hi = locations->GetTemp(1).AsRegister<Register>(); 3448 3449 __ vmovrrd(value_reg_lo, value_reg_hi, value_reg); 3450 3451 GenerateWideAtomicStore(base, offset, 3452 value_reg_lo, 3453 value_reg_hi, 3454 locations->GetTemp(2).AsRegister<Register>(), 3455 locations->GetTemp(3).AsRegister<Register>(), 3456 instruction); 3457 } else { 3458 __ StoreDToOffset(value_reg, base, offset); 3459 codegen_->MaybeRecordImplicitNullCheck(instruction); 3460 } 3461 break; 3462 } 3463 3464 case Primitive::kPrimVoid: 3465 LOG(FATAL) << "Unreachable type " << field_type; 3466 UNREACHABLE(); 3467 } 3468 3469 // Longs and doubles are handled in the switch. 3470 if (field_type != Primitive::kPrimLong && field_type != Primitive::kPrimDouble) { 3471 codegen_->MaybeRecordImplicitNullCheck(instruction); 3472 } 3473 3474 if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1))) { 3475 Register temp = locations->GetTemp(0).AsRegister<Register>(); 3476 Register card = locations->GetTemp(1).AsRegister<Register>(); 3477 codegen_->MarkGCCard( 3478 temp, card, base, value.AsRegister<Register>(), value_can_be_null); 3479 } 3480 3481 if (is_volatile) { 3482 GenerateMemoryBarrier(MemBarrierKind::kAnyAny); 3483 } 3484} 3485 3486void LocationsBuilderARM::HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info) { 3487 DCHECK(instruction->IsInstanceFieldGet() || instruction->IsStaticFieldGet()); 3488 LocationSummary* locations = 3489 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); 3490 locations->SetInAt(0, Location::RequiresRegister()); 3491 3492 bool volatile_for_double = field_info.IsVolatile() 3493 && (field_info.GetFieldType() == Primitive::kPrimDouble) 3494 && !codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd(); 3495 bool overlap = field_info.IsVolatile() && (field_info.GetFieldType() == Primitive::kPrimLong); 3496 3497 if (Primitive::IsFloatingPointType(instruction->GetType())) { 3498 locations->SetOut(Location::RequiresFpuRegister()); 3499 } else { 3500 locations->SetOut(Location::RequiresRegister(), 3501 (overlap ? Location::kOutputOverlap : Location::kNoOutputOverlap)); 3502 } 3503 if (volatile_for_double) { 3504 // Arm encoding have some additional constraints for ldrexd/strexd: 3505 // - registers need to be consecutive 3506 // - the first register should be even but not R14. 3507 // We don't test for Arm yet, and the assertion makes sure that we revisit this if we ever 3508 // enable Arm encoding. 3509 DCHECK_EQ(InstructionSet::kThumb2, codegen_->GetInstructionSet()); 3510 locations->AddTemp(Location::RequiresRegister()); 3511 locations->AddTemp(Location::RequiresRegister()); 3512 } 3513} 3514 3515void InstructionCodeGeneratorARM::HandleFieldGet(HInstruction* instruction, 3516 const FieldInfo& field_info) { 3517 DCHECK(instruction->IsInstanceFieldGet() || instruction->IsStaticFieldGet()); 3518 3519 LocationSummary* locations = instruction->GetLocations(); 3520 Register base = locations->InAt(0).AsRegister<Register>(); 3521 Location out = locations->Out(); 3522 bool is_volatile = field_info.IsVolatile(); 3523 bool atomic_ldrd_strd = codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd(); 3524 Primitive::Type field_type = field_info.GetFieldType(); 3525 uint32_t offset = field_info.GetFieldOffset().Uint32Value(); 3526 3527 switch (field_type) { 3528 case Primitive::kPrimBoolean: { 3529 __ LoadFromOffset(kLoadUnsignedByte, out.AsRegister<Register>(), base, offset); 3530 break; 3531 } 3532 3533 case Primitive::kPrimByte: { 3534 __ LoadFromOffset(kLoadSignedByte, out.AsRegister<Register>(), base, offset); 3535 break; 3536 } 3537 3538 case Primitive::kPrimShort: { 3539 __ LoadFromOffset(kLoadSignedHalfword, out.AsRegister<Register>(), base, offset); 3540 break; 3541 } 3542 3543 case Primitive::kPrimChar: { 3544 __ LoadFromOffset(kLoadUnsignedHalfword, out.AsRegister<Register>(), base, offset); 3545 break; 3546 } 3547 3548 case Primitive::kPrimInt: 3549 case Primitive::kPrimNot: { 3550 __ LoadFromOffset(kLoadWord, out.AsRegister<Register>(), base, offset); 3551 break; 3552 } 3553 3554 case Primitive::kPrimLong: { 3555 if (is_volatile && !atomic_ldrd_strd) { 3556 GenerateWideAtomicLoad(base, offset, 3557 out.AsRegisterPairLow<Register>(), 3558 out.AsRegisterPairHigh<Register>()); 3559 } else { 3560 __ LoadFromOffset(kLoadWordPair, out.AsRegisterPairLow<Register>(), base, offset); 3561 } 3562 break; 3563 } 3564 3565 case Primitive::kPrimFloat: { 3566 __ LoadSFromOffset(out.AsFpuRegister<SRegister>(), base, offset); 3567 break; 3568 } 3569 3570 case Primitive::kPrimDouble: { 3571 DRegister out_reg = FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()); 3572 if (is_volatile && !atomic_ldrd_strd) { 3573 Register lo = locations->GetTemp(0).AsRegister<Register>(); 3574 Register hi = locations->GetTemp(1).AsRegister<Register>(); 3575 GenerateWideAtomicLoad(base, offset, lo, hi); 3576 codegen_->MaybeRecordImplicitNullCheck(instruction); 3577 __ vmovdrr(out_reg, lo, hi); 3578 } else { 3579 __ LoadDFromOffset(out_reg, base, offset); 3580 codegen_->MaybeRecordImplicitNullCheck(instruction); 3581 } 3582 break; 3583 } 3584 3585 case Primitive::kPrimVoid: 3586 LOG(FATAL) << "Unreachable type " << field_type; 3587 UNREACHABLE(); 3588 } 3589 3590 // Doubles are handled in the switch. 3591 if (field_type != Primitive::kPrimDouble) { 3592 codegen_->MaybeRecordImplicitNullCheck(instruction); 3593 } 3594 3595 if (is_volatile) { 3596 GenerateMemoryBarrier(MemBarrierKind::kLoadAny); 3597 } 3598 3599 if (field_type == Primitive::kPrimNot) { 3600 __ MaybeUnpoisonHeapReference(out.AsRegister<Register>()); 3601 } 3602} 3603 3604void LocationsBuilderARM::VisitInstanceFieldSet(HInstanceFieldSet* instruction) { 3605 HandleFieldSet(instruction, instruction->GetFieldInfo()); 3606} 3607 3608void InstructionCodeGeneratorARM::VisitInstanceFieldSet(HInstanceFieldSet* instruction) { 3609 HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull()); 3610} 3611 3612void LocationsBuilderARM::VisitInstanceFieldGet(HInstanceFieldGet* instruction) { 3613 HandleFieldGet(instruction, instruction->GetFieldInfo()); 3614} 3615 3616void InstructionCodeGeneratorARM::VisitInstanceFieldGet(HInstanceFieldGet* instruction) { 3617 HandleFieldGet(instruction, instruction->GetFieldInfo()); 3618} 3619 3620void LocationsBuilderARM::VisitStaticFieldGet(HStaticFieldGet* instruction) { 3621 HandleFieldGet(instruction, instruction->GetFieldInfo()); 3622} 3623 3624void InstructionCodeGeneratorARM::VisitStaticFieldGet(HStaticFieldGet* instruction) { 3625 HandleFieldGet(instruction, instruction->GetFieldInfo()); 3626} 3627 3628void LocationsBuilderARM::VisitStaticFieldSet(HStaticFieldSet* instruction) { 3629 HandleFieldSet(instruction, instruction->GetFieldInfo()); 3630} 3631 3632void InstructionCodeGeneratorARM::VisitStaticFieldSet(HStaticFieldSet* instruction) { 3633 HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull()); 3634} 3635 3636void LocationsBuilderARM::VisitUnresolvedInstanceFieldGet( 3637 HUnresolvedInstanceFieldGet* instruction) { 3638 FieldAccessCallingConventionARM calling_convention; 3639 codegen_->CreateUnresolvedFieldLocationSummary( 3640 instruction, instruction->GetFieldType(), calling_convention); 3641} 3642 3643void InstructionCodeGeneratorARM::VisitUnresolvedInstanceFieldGet( 3644 HUnresolvedInstanceFieldGet* instruction) { 3645 FieldAccessCallingConventionARM calling_convention; 3646 codegen_->GenerateUnresolvedFieldAccess(instruction, 3647 instruction->GetFieldType(), 3648 instruction->GetFieldIndex(), 3649 instruction->GetDexPc(), 3650 calling_convention); 3651} 3652 3653void LocationsBuilderARM::VisitUnresolvedInstanceFieldSet( 3654 HUnresolvedInstanceFieldSet* instruction) { 3655 FieldAccessCallingConventionARM calling_convention; 3656 codegen_->CreateUnresolvedFieldLocationSummary( 3657 instruction, instruction->GetFieldType(), calling_convention); 3658} 3659 3660void InstructionCodeGeneratorARM::VisitUnresolvedInstanceFieldSet( 3661 HUnresolvedInstanceFieldSet* instruction) { 3662 FieldAccessCallingConventionARM calling_convention; 3663 codegen_->GenerateUnresolvedFieldAccess(instruction, 3664 instruction->GetFieldType(), 3665 instruction->GetFieldIndex(), 3666 instruction->GetDexPc(), 3667 calling_convention); 3668} 3669 3670void LocationsBuilderARM::VisitUnresolvedStaticFieldGet( 3671 HUnresolvedStaticFieldGet* instruction) { 3672 FieldAccessCallingConventionARM calling_convention; 3673 codegen_->CreateUnresolvedFieldLocationSummary( 3674 instruction, instruction->GetFieldType(), calling_convention); 3675} 3676 3677void InstructionCodeGeneratorARM::VisitUnresolvedStaticFieldGet( 3678 HUnresolvedStaticFieldGet* instruction) { 3679 FieldAccessCallingConventionARM calling_convention; 3680 codegen_->GenerateUnresolvedFieldAccess(instruction, 3681 instruction->GetFieldType(), 3682 instruction->GetFieldIndex(), 3683 instruction->GetDexPc(), 3684 calling_convention); 3685} 3686 3687void LocationsBuilderARM::VisitUnresolvedStaticFieldSet( 3688 HUnresolvedStaticFieldSet* instruction) { 3689 FieldAccessCallingConventionARM calling_convention; 3690 codegen_->CreateUnresolvedFieldLocationSummary( 3691 instruction, instruction->GetFieldType(), calling_convention); 3692} 3693 3694void InstructionCodeGeneratorARM::VisitUnresolvedStaticFieldSet( 3695 HUnresolvedStaticFieldSet* instruction) { 3696 FieldAccessCallingConventionARM calling_convention; 3697 codegen_->GenerateUnresolvedFieldAccess(instruction, 3698 instruction->GetFieldType(), 3699 instruction->GetFieldIndex(), 3700 instruction->GetDexPc(), 3701 calling_convention); 3702} 3703 3704void LocationsBuilderARM::VisitNullCheck(HNullCheck* instruction) { 3705 LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock() 3706 ? LocationSummary::kCallOnSlowPath 3707 : LocationSummary::kNoCall; 3708 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind); 3709 locations->SetInAt(0, Location::RequiresRegister()); 3710 if (instruction->HasUses()) { 3711 locations->SetOut(Location::SameAsFirstInput()); 3712 } 3713} 3714 3715void InstructionCodeGeneratorARM::GenerateImplicitNullCheck(HNullCheck* instruction) { 3716 if (codegen_->CanMoveNullCheckToUser(instruction)) { 3717 return; 3718 } 3719 Location obj = instruction->GetLocations()->InAt(0); 3720 3721 __ LoadFromOffset(kLoadWord, IP, obj.AsRegister<Register>(), 0); 3722 codegen_->RecordPcInfo(instruction, instruction->GetDexPc()); 3723} 3724 3725void InstructionCodeGeneratorARM::GenerateExplicitNullCheck(HNullCheck* instruction) { 3726 SlowPathCode* slow_path = new (GetGraph()->GetArena()) NullCheckSlowPathARM(instruction); 3727 codegen_->AddSlowPath(slow_path); 3728 3729 LocationSummary* locations = instruction->GetLocations(); 3730 Location obj = locations->InAt(0); 3731 3732 __ CompareAndBranchIfZero(obj.AsRegister<Register>(), slow_path->GetEntryLabel()); 3733} 3734 3735void InstructionCodeGeneratorARM::VisitNullCheck(HNullCheck* instruction) { 3736 if (codegen_->IsImplicitNullCheckAllowed(instruction)) { 3737 GenerateImplicitNullCheck(instruction); 3738 } else { 3739 GenerateExplicitNullCheck(instruction); 3740 } 3741} 3742 3743void LocationsBuilderARM::VisitArrayGet(HArrayGet* instruction) { 3744 LocationSummary* locations = 3745 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); 3746 locations->SetInAt(0, Location::RequiresRegister()); 3747 locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1))); 3748 if (Primitive::IsFloatingPointType(instruction->GetType())) { 3749 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); 3750 } else { 3751 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 3752 } 3753} 3754 3755void InstructionCodeGeneratorARM::VisitArrayGet(HArrayGet* instruction) { 3756 LocationSummary* locations = instruction->GetLocations(); 3757 Register obj = locations->InAt(0).AsRegister<Register>(); 3758 Location index = locations->InAt(1); 3759 Primitive::Type type = instruction->GetType(); 3760 3761 switch (type) { 3762 case Primitive::kPrimBoolean: { 3763 uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value(); 3764 Register out = locations->Out().AsRegister<Register>(); 3765 if (index.IsConstant()) { 3766 size_t offset = 3767 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset; 3768 __ LoadFromOffset(kLoadUnsignedByte, out, obj, offset); 3769 } else { 3770 __ add(IP, obj, ShifterOperand(index.AsRegister<Register>())); 3771 __ LoadFromOffset(kLoadUnsignedByte, out, IP, data_offset); 3772 } 3773 break; 3774 } 3775 3776 case Primitive::kPrimByte: { 3777 uint32_t data_offset = mirror::Array::DataOffset(sizeof(int8_t)).Uint32Value(); 3778 Register out = locations->Out().AsRegister<Register>(); 3779 if (index.IsConstant()) { 3780 size_t offset = 3781 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset; 3782 __ LoadFromOffset(kLoadSignedByte, out, obj, offset); 3783 } else { 3784 __ add(IP, obj, ShifterOperand(index.AsRegister<Register>())); 3785 __ LoadFromOffset(kLoadSignedByte, out, IP, data_offset); 3786 } 3787 break; 3788 } 3789 3790 case Primitive::kPrimShort: { 3791 uint32_t data_offset = mirror::Array::DataOffset(sizeof(int16_t)).Uint32Value(); 3792 Register out = locations->Out().AsRegister<Register>(); 3793 if (index.IsConstant()) { 3794 size_t offset = 3795 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset; 3796 __ LoadFromOffset(kLoadSignedHalfword, out, obj, offset); 3797 } else { 3798 __ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_2)); 3799 __ LoadFromOffset(kLoadSignedHalfword, out, IP, data_offset); 3800 } 3801 break; 3802 } 3803 3804 case Primitive::kPrimChar: { 3805 uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value(); 3806 Register out = locations->Out().AsRegister<Register>(); 3807 if (index.IsConstant()) { 3808 size_t offset = 3809 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset; 3810 __ LoadFromOffset(kLoadUnsignedHalfword, out, obj, offset); 3811 } else { 3812 __ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_2)); 3813 __ LoadFromOffset(kLoadUnsignedHalfword, out, IP, data_offset); 3814 } 3815 break; 3816 } 3817 3818 case Primitive::kPrimInt: 3819 case Primitive::kPrimNot: { 3820 static_assert(sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t), 3821 "art::mirror::HeapReference<mirror::Object> and int32_t have different sizes."); 3822 uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value(); 3823 Register out = locations->Out().AsRegister<Register>(); 3824 if (index.IsConstant()) { 3825 size_t offset = 3826 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; 3827 __ LoadFromOffset(kLoadWord, out, obj, offset); 3828 } else { 3829 __ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4)); 3830 __ LoadFromOffset(kLoadWord, out, IP, data_offset); 3831 } 3832 break; 3833 } 3834 3835 case Primitive::kPrimLong: { 3836 uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value(); 3837 Location out = locations->Out(); 3838 if (index.IsConstant()) { 3839 size_t offset = 3840 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset; 3841 __ LoadFromOffset(kLoadWordPair, out.AsRegisterPairLow<Register>(), obj, offset); 3842 } else { 3843 __ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_8)); 3844 __ LoadFromOffset(kLoadWordPair, out.AsRegisterPairLow<Register>(), IP, data_offset); 3845 } 3846 break; 3847 } 3848 3849 case Primitive::kPrimFloat: { 3850 uint32_t data_offset = mirror::Array::DataOffset(sizeof(float)).Uint32Value(); 3851 Location out = locations->Out(); 3852 DCHECK(out.IsFpuRegister()); 3853 if (index.IsConstant()) { 3854 size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; 3855 __ LoadSFromOffset(out.AsFpuRegister<SRegister>(), obj, offset); 3856 } else { 3857 __ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4)); 3858 __ LoadSFromOffset(out.AsFpuRegister<SRegister>(), IP, data_offset); 3859 } 3860 break; 3861 } 3862 3863 case Primitive::kPrimDouble: { 3864 uint32_t data_offset = mirror::Array::DataOffset(sizeof(double)).Uint32Value(); 3865 Location out = locations->Out(); 3866 DCHECK(out.IsFpuRegisterPair()); 3867 if (index.IsConstant()) { 3868 size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset; 3869 __ LoadDFromOffset(FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()), obj, offset); 3870 } else { 3871 __ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_8)); 3872 __ LoadDFromOffset(FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()), IP, data_offset); 3873 } 3874 break; 3875 } 3876 3877 case Primitive::kPrimVoid: 3878 LOG(FATAL) << "Unreachable type " << type; 3879 UNREACHABLE(); 3880 } 3881 codegen_->MaybeRecordImplicitNullCheck(instruction); 3882 3883 if (type == Primitive::kPrimNot) { 3884 Register out = locations->Out().AsRegister<Register>(); 3885 __ MaybeUnpoisonHeapReference(out); 3886 } 3887} 3888 3889void LocationsBuilderARM::VisitArraySet(HArraySet* instruction) { 3890 Primitive::Type value_type = instruction->GetComponentType(); 3891 3892 bool needs_write_barrier = 3893 CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue()); 3894 bool may_need_runtime_call = instruction->NeedsTypeCheck(); 3895 3896 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary( 3897 instruction, 3898 may_need_runtime_call ? LocationSummary::kCallOnSlowPath : LocationSummary::kNoCall); 3899 locations->SetInAt(0, Location::RequiresRegister()); 3900 locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1))); 3901 if (Primitive::IsFloatingPointType(value_type)) { 3902 locations->SetInAt(2, Location::RequiresFpuRegister()); 3903 } else { 3904 locations->SetInAt(2, Location::RequiresRegister()); 3905 } 3906 3907 if (needs_write_barrier) { 3908 // Temporary registers for the write barrier. 3909 locations->AddTemp(Location::RequiresRegister()); // Possibly used for ref. poisoning too. 3910 locations->AddTemp(Location::RequiresRegister()); 3911 } 3912} 3913 3914void InstructionCodeGeneratorARM::VisitArraySet(HArraySet* instruction) { 3915 LocationSummary* locations = instruction->GetLocations(); 3916 Register array = locations->InAt(0).AsRegister<Register>(); 3917 Location index = locations->InAt(1); 3918 Primitive::Type value_type = instruction->GetComponentType(); 3919 bool may_need_runtime_call = locations->CanCall(); 3920 bool needs_write_barrier = 3921 CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue()); 3922 3923 switch (value_type) { 3924 case Primitive::kPrimBoolean: 3925 case Primitive::kPrimByte: { 3926 uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value(); 3927 Register value = locations->InAt(2).AsRegister<Register>(); 3928 if (index.IsConstant()) { 3929 size_t offset = 3930 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset; 3931 __ StoreToOffset(kStoreByte, value, array, offset); 3932 } else { 3933 __ add(IP, array, ShifterOperand(index.AsRegister<Register>())); 3934 __ StoreToOffset(kStoreByte, value, IP, data_offset); 3935 } 3936 break; 3937 } 3938 3939 case Primitive::kPrimShort: 3940 case Primitive::kPrimChar: { 3941 uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value(); 3942 Register value = locations->InAt(2).AsRegister<Register>(); 3943 if (index.IsConstant()) { 3944 size_t offset = 3945 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset; 3946 __ StoreToOffset(kStoreHalfword, value, array, offset); 3947 } else { 3948 __ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_2)); 3949 __ StoreToOffset(kStoreHalfword, value, IP, data_offset); 3950 } 3951 break; 3952 } 3953 3954 case Primitive::kPrimNot: { 3955 uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value(); 3956 Register value = locations->InAt(2).AsRegister<Register>(); 3957 Register source = value; 3958 3959 if (instruction->InputAt(2)->IsNullConstant()) { 3960 // Just setting null. 3961 if (index.IsConstant()) { 3962 size_t offset = 3963 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; 3964 __ StoreToOffset(kStoreWord, source, array, offset); 3965 } else { 3966 DCHECK(index.IsRegister()) << index; 3967 __ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4)); 3968 __ StoreToOffset(kStoreWord, source, IP, data_offset); 3969 } 3970 break; 3971 } 3972 3973 DCHECK(needs_write_barrier); 3974 Register temp1 = locations->GetTemp(0).AsRegister<Register>(); 3975 Register temp2 = locations->GetTemp(1).AsRegister<Register>(); 3976 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value(); 3977 uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value(); 3978 uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value(); 3979 Label done; 3980 SlowPathCode* slow_path = nullptr; 3981 3982 if (may_need_runtime_call) { 3983 slow_path = new (GetGraph()->GetArena()) ArraySetSlowPathARM(instruction); 3984 codegen_->AddSlowPath(slow_path); 3985 if (instruction->GetValueCanBeNull()) { 3986 Label non_zero; 3987 __ CompareAndBranchIfNonZero(value, &non_zero); 3988 if (index.IsConstant()) { 3989 size_t offset = 3990 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; 3991 __ StoreToOffset(kStoreWord, value, array, offset); 3992 } else { 3993 DCHECK(index.IsRegister()) << index; 3994 __ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4)); 3995 __ StoreToOffset(kStoreWord, value, IP, data_offset); 3996 } 3997 codegen_->MaybeRecordImplicitNullCheck(instruction); 3998 __ b(&done); 3999 __ Bind(&non_zero); 4000 } 4001 4002 __ LoadFromOffset(kLoadWord, temp1, array, class_offset); 4003 codegen_->MaybeRecordImplicitNullCheck(instruction); 4004 __ MaybeUnpoisonHeapReference(temp1); 4005 __ LoadFromOffset(kLoadWord, temp1, temp1, component_offset); 4006 __ LoadFromOffset(kLoadWord, temp2, value, class_offset); 4007 // No need to poison/unpoison, we're comparing two poisoined references. 4008 __ cmp(temp1, ShifterOperand(temp2)); 4009 if (instruction->StaticTypeOfArrayIsObjectArray()) { 4010 Label do_put; 4011 __ b(&do_put, EQ); 4012 __ MaybeUnpoisonHeapReference(temp1); 4013 __ LoadFromOffset(kLoadWord, temp1, temp1, super_offset); 4014 // No need to poison/unpoison, we're comparing against null. 4015 __ CompareAndBranchIfNonZero(temp1, slow_path->GetEntryLabel()); 4016 __ Bind(&do_put); 4017 } else { 4018 __ b(slow_path->GetEntryLabel(), NE); 4019 } 4020 } 4021 4022 if (kPoisonHeapReferences) { 4023 // Note that in the case where `value` is a null reference, 4024 // we do not enter this block, as a null reference does not 4025 // need poisoning. 4026 DCHECK_EQ(value_type, Primitive::kPrimNot); 4027 __ Mov(temp1, value); 4028 __ PoisonHeapReference(temp1); 4029 source = temp1; 4030 } 4031 4032 if (index.IsConstant()) { 4033 size_t offset = 4034 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; 4035 __ StoreToOffset(kStoreWord, source, array, offset); 4036 } else { 4037 DCHECK(index.IsRegister()) << index; 4038 __ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4)); 4039 __ StoreToOffset(kStoreWord, source, IP, data_offset); 4040 } 4041 4042 if (!may_need_runtime_call) { 4043 codegen_->MaybeRecordImplicitNullCheck(instruction); 4044 } 4045 4046 codegen_->MarkGCCard(temp1, temp2, array, value, instruction->GetValueCanBeNull()); 4047 4048 if (done.IsLinked()) { 4049 __ Bind(&done); 4050 } 4051 4052 if (slow_path != nullptr) { 4053 __ Bind(slow_path->GetExitLabel()); 4054 } 4055 4056 break; 4057 } 4058 4059 case Primitive::kPrimInt: { 4060 uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value(); 4061 Register value = locations->InAt(2).AsRegister<Register>(); 4062 if (index.IsConstant()) { 4063 size_t offset = 4064 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; 4065 __ StoreToOffset(kStoreWord, value, array, offset); 4066 } else { 4067 DCHECK(index.IsRegister()) << index; 4068 __ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4)); 4069 __ StoreToOffset(kStoreWord, value, IP, data_offset); 4070 } 4071 4072 codegen_->MaybeRecordImplicitNullCheck(instruction); 4073 break; 4074 } 4075 4076 case Primitive::kPrimLong: { 4077 uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value(); 4078 Location value = locations->InAt(2); 4079 if (index.IsConstant()) { 4080 size_t offset = 4081 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset; 4082 __ StoreToOffset(kStoreWordPair, value.AsRegisterPairLow<Register>(), array, offset); 4083 } else { 4084 __ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_8)); 4085 __ StoreToOffset(kStoreWordPair, value.AsRegisterPairLow<Register>(), IP, data_offset); 4086 } 4087 break; 4088 } 4089 4090 case Primitive::kPrimFloat: { 4091 uint32_t data_offset = mirror::Array::DataOffset(sizeof(float)).Uint32Value(); 4092 Location value = locations->InAt(2); 4093 DCHECK(value.IsFpuRegister()); 4094 if (index.IsConstant()) { 4095 size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; 4096 __ StoreSToOffset(value.AsFpuRegister<SRegister>(), array, offset); 4097 } else { 4098 __ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4)); 4099 __ StoreSToOffset(value.AsFpuRegister<SRegister>(), IP, data_offset); 4100 } 4101 break; 4102 } 4103 4104 case Primitive::kPrimDouble: { 4105 uint32_t data_offset = mirror::Array::DataOffset(sizeof(double)).Uint32Value(); 4106 Location value = locations->InAt(2); 4107 DCHECK(value.IsFpuRegisterPair()); 4108 if (index.IsConstant()) { 4109 size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset; 4110 __ StoreDToOffset(FromLowSToD(value.AsFpuRegisterPairLow<SRegister>()), array, offset); 4111 } else { 4112 __ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_8)); 4113 __ StoreDToOffset(FromLowSToD(value.AsFpuRegisterPairLow<SRegister>()), IP, data_offset); 4114 } 4115 4116 break; 4117 } 4118 4119 case Primitive::kPrimVoid: 4120 LOG(FATAL) << "Unreachable type " << value_type; 4121 UNREACHABLE(); 4122 } 4123 4124 // Ints and objects are handled in the switch. 4125 if (value_type != Primitive::kPrimInt && value_type != Primitive::kPrimNot) { 4126 codegen_->MaybeRecordImplicitNullCheck(instruction); 4127 } 4128} 4129 4130void LocationsBuilderARM::VisitArrayLength(HArrayLength* instruction) { 4131 LocationSummary* locations = 4132 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); 4133 locations->SetInAt(0, Location::RequiresRegister()); 4134 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 4135} 4136 4137void InstructionCodeGeneratorARM::VisitArrayLength(HArrayLength* instruction) { 4138 LocationSummary* locations = instruction->GetLocations(); 4139 uint32_t offset = mirror::Array::LengthOffset().Uint32Value(); 4140 Register obj = locations->InAt(0).AsRegister<Register>(); 4141 Register out = locations->Out().AsRegister<Register>(); 4142 __ LoadFromOffset(kLoadWord, out, obj, offset); 4143 codegen_->MaybeRecordImplicitNullCheck(instruction); 4144} 4145 4146void LocationsBuilderARM::VisitBoundsCheck(HBoundsCheck* instruction) { 4147 LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock() 4148 ? LocationSummary::kCallOnSlowPath 4149 : LocationSummary::kNoCall; 4150 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind); 4151 locations->SetInAt(0, Location::RequiresRegister()); 4152 locations->SetInAt(1, Location::RequiresRegister()); 4153 if (instruction->HasUses()) { 4154 locations->SetOut(Location::SameAsFirstInput()); 4155 } 4156} 4157 4158void InstructionCodeGeneratorARM::VisitBoundsCheck(HBoundsCheck* instruction) { 4159 LocationSummary* locations = instruction->GetLocations(); 4160 SlowPathCode* slow_path = 4161 new (GetGraph()->GetArena()) BoundsCheckSlowPathARM(instruction); 4162 codegen_->AddSlowPath(slow_path); 4163 4164 Register index = locations->InAt(0).AsRegister<Register>(); 4165 Register length = locations->InAt(1).AsRegister<Register>(); 4166 4167 __ cmp(index, ShifterOperand(length)); 4168 __ b(slow_path->GetEntryLabel(), HS); 4169} 4170 4171void CodeGeneratorARM::MarkGCCard(Register temp, 4172 Register card, 4173 Register object, 4174 Register value, 4175 bool can_be_null) { 4176 Label is_null; 4177 if (can_be_null) { 4178 __ CompareAndBranchIfZero(value, &is_null); 4179 } 4180 __ LoadFromOffset(kLoadWord, card, TR, Thread::CardTableOffset<kArmWordSize>().Int32Value()); 4181 __ Lsr(temp, object, gc::accounting::CardTable::kCardShift); 4182 __ strb(card, Address(card, temp)); 4183 if (can_be_null) { 4184 __ Bind(&is_null); 4185 } 4186} 4187 4188void LocationsBuilderARM::VisitTemporary(HTemporary* temp) { 4189 temp->SetLocations(nullptr); 4190} 4191 4192void InstructionCodeGeneratorARM::VisitTemporary(HTemporary* temp) { 4193 // Nothing to do, this is driven by the code generator. 4194 UNUSED(temp); 4195} 4196 4197void LocationsBuilderARM::VisitParallelMove(HParallelMove* instruction) { 4198 UNUSED(instruction); 4199 LOG(FATAL) << "Unreachable"; 4200} 4201 4202void InstructionCodeGeneratorARM::VisitParallelMove(HParallelMove* instruction) { 4203 codegen_->GetMoveResolver()->EmitNativeCode(instruction); 4204} 4205 4206void LocationsBuilderARM::VisitSuspendCheck(HSuspendCheck* instruction) { 4207 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnSlowPath); 4208} 4209 4210void InstructionCodeGeneratorARM::VisitSuspendCheck(HSuspendCheck* instruction) { 4211 HBasicBlock* block = instruction->GetBlock(); 4212 if (block->GetLoopInformation() != nullptr) { 4213 DCHECK(block->GetLoopInformation()->GetSuspendCheck() == instruction); 4214 // The back edge will generate the suspend check. 4215 return; 4216 } 4217 if (block->IsEntryBlock() && instruction->GetNext()->IsGoto()) { 4218 // The goto will generate the suspend check. 4219 return; 4220 } 4221 GenerateSuspendCheck(instruction, nullptr); 4222} 4223 4224void InstructionCodeGeneratorARM::GenerateSuspendCheck(HSuspendCheck* instruction, 4225 HBasicBlock* successor) { 4226 SuspendCheckSlowPathARM* slow_path = 4227 down_cast<SuspendCheckSlowPathARM*>(instruction->GetSlowPath()); 4228 if (slow_path == nullptr) { 4229 slow_path = new (GetGraph()->GetArena()) SuspendCheckSlowPathARM(instruction, successor); 4230 instruction->SetSlowPath(slow_path); 4231 codegen_->AddSlowPath(slow_path); 4232 if (successor != nullptr) { 4233 DCHECK(successor->IsLoopHeader()); 4234 codegen_->ClearSpillSlotsFromLoopPhisInStackMap(instruction); 4235 } 4236 } else { 4237 DCHECK_EQ(slow_path->GetSuccessor(), successor); 4238 } 4239 4240 __ LoadFromOffset( 4241 kLoadUnsignedHalfword, IP, TR, Thread::ThreadFlagsOffset<kArmWordSize>().Int32Value()); 4242 if (successor == nullptr) { 4243 __ CompareAndBranchIfNonZero(IP, slow_path->GetEntryLabel()); 4244 __ Bind(slow_path->GetReturnLabel()); 4245 } else { 4246 __ CompareAndBranchIfZero(IP, codegen_->GetLabelOf(successor)); 4247 __ b(slow_path->GetEntryLabel()); 4248 } 4249} 4250 4251ArmAssembler* ParallelMoveResolverARM::GetAssembler() const { 4252 return codegen_->GetAssembler(); 4253} 4254 4255void ParallelMoveResolverARM::EmitMove(size_t index) { 4256 DCHECK_LT(index, moves_.size()); 4257 MoveOperands* move = moves_[index]; 4258 Location source = move->GetSource(); 4259 Location destination = move->GetDestination(); 4260 4261 if (source.IsRegister()) { 4262 if (destination.IsRegister()) { 4263 __ Mov(destination.AsRegister<Register>(), source.AsRegister<Register>()); 4264 } else { 4265 DCHECK(destination.IsStackSlot()); 4266 __ StoreToOffset(kStoreWord, source.AsRegister<Register>(), 4267 SP, destination.GetStackIndex()); 4268 } 4269 } else if (source.IsStackSlot()) { 4270 if (destination.IsRegister()) { 4271 __ LoadFromOffset(kLoadWord, destination.AsRegister<Register>(), 4272 SP, source.GetStackIndex()); 4273 } else if (destination.IsFpuRegister()) { 4274 __ LoadSFromOffset(destination.AsFpuRegister<SRegister>(), SP, source.GetStackIndex()); 4275 } else { 4276 DCHECK(destination.IsStackSlot()); 4277 __ LoadFromOffset(kLoadWord, IP, SP, source.GetStackIndex()); 4278 __ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex()); 4279 } 4280 } else if (source.IsFpuRegister()) { 4281 if (destination.IsFpuRegister()) { 4282 __ vmovs(destination.AsFpuRegister<SRegister>(), source.AsFpuRegister<SRegister>()); 4283 } else { 4284 DCHECK(destination.IsStackSlot()); 4285 __ StoreSToOffset(source.AsFpuRegister<SRegister>(), SP, destination.GetStackIndex()); 4286 } 4287 } else if (source.IsDoubleStackSlot()) { 4288 if (destination.IsDoubleStackSlot()) { 4289 __ LoadDFromOffset(DTMP, SP, source.GetStackIndex()); 4290 __ StoreDToOffset(DTMP, SP, destination.GetStackIndex()); 4291 } else if (destination.IsRegisterPair()) { 4292 DCHECK(ExpectedPairLayout(destination)); 4293 __ LoadFromOffset( 4294 kLoadWordPair, destination.AsRegisterPairLow<Register>(), SP, source.GetStackIndex()); 4295 } else { 4296 DCHECK(destination.IsFpuRegisterPair()) << destination; 4297 __ LoadDFromOffset(FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()), 4298 SP, 4299 source.GetStackIndex()); 4300 } 4301 } else if (source.IsRegisterPair()) { 4302 if (destination.IsRegisterPair()) { 4303 __ Mov(destination.AsRegisterPairLow<Register>(), source.AsRegisterPairLow<Register>()); 4304 __ Mov(destination.AsRegisterPairHigh<Register>(), source.AsRegisterPairHigh<Register>()); 4305 } else { 4306 DCHECK(destination.IsDoubleStackSlot()) << destination; 4307 DCHECK(ExpectedPairLayout(source)); 4308 __ StoreToOffset( 4309 kStoreWordPair, source.AsRegisterPairLow<Register>(), SP, destination.GetStackIndex()); 4310 } 4311 } else if (source.IsFpuRegisterPair()) { 4312 if (destination.IsFpuRegisterPair()) { 4313 __ vmovd(FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()), 4314 FromLowSToD(source.AsFpuRegisterPairLow<SRegister>())); 4315 } else { 4316 DCHECK(destination.IsDoubleStackSlot()) << destination; 4317 __ StoreDToOffset(FromLowSToD(source.AsFpuRegisterPairLow<SRegister>()), 4318 SP, 4319 destination.GetStackIndex()); 4320 } 4321 } else { 4322 DCHECK(source.IsConstant()) << source; 4323 HConstant* constant = source.GetConstant(); 4324 if (constant->IsIntConstant() || constant->IsNullConstant()) { 4325 int32_t value = CodeGenerator::GetInt32ValueOf(constant); 4326 if (destination.IsRegister()) { 4327 __ LoadImmediate(destination.AsRegister<Register>(), value); 4328 } else { 4329 DCHECK(destination.IsStackSlot()); 4330 __ LoadImmediate(IP, value); 4331 __ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex()); 4332 } 4333 } else if (constant->IsLongConstant()) { 4334 int64_t value = constant->AsLongConstant()->GetValue(); 4335 if (destination.IsRegisterPair()) { 4336 __ LoadImmediate(destination.AsRegisterPairLow<Register>(), Low32Bits(value)); 4337 __ LoadImmediate(destination.AsRegisterPairHigh<Register>(), High32Bits(value)); 4338 } else { 4339 DCHECK(destination.IsDoubleStackSlot()) << destination; 4340 __ LoadImmediate(IP, Low32Bits(value)); 4341 __ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex()); 4342 __ LoadImmediate(IP, High32Bits(value)); 4343 __ StoreToOffset(kStoreWord, IP, SP, destination.GetHighStackIndex(kArmWordSize)); 4344 } 4345 } else if (constant->IsDoubleConstant()) { 4346 double value = constant->AsDoubleConstant()->GetValue(); 4347 if (destination.IsFpuRegisterPair()) { 4348 __ LoadDImmediate(FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()), value); 4349 } else { 4350 DCHECK(destination.IsDoubleStackSlot()) << destination; 4351 uint64_t int_value = bit_cast<uint64_t, double>(value); 4352 __ LoadImmediate(IP, Low32Bits(int_value)); 4353 __ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex()); 4354 __ LoadImmediate(IP, High32Bits(int_value)); 4355 __ StoreToOffset(kStoreWord, IP, SP, destination.GetHighStackIndex(kArmWordSize)); 4356 } 4357 } else { 4358 DCHECK(constant->IsFloatConstant()) << constant->DebugName(); 4359 float value = constant->AsFloatConstant()->GetValue(); 4360 if (destination.IsFpuRegister()) { 4361 __ LoadSImmediate(destination.AsFpuRegister<SRegister>(), value); 4362 } else { 4363 DCHECK(destination.IsStackSlot()); 4364 __ LoadImmediate(IP, bit_cast<int32_t, float>(value)); 4365 __ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex()); 4366 } 4367 } 4368 } 4369} 4370 4371void ParallelMoveResolverARM::Exchange(Register reg, int mem) { 4372 __ Mov(IP, reg); 4373 __ LoadFromOffset(kLoadWord, reg, SP, mem); 4374 __ StoreToOffset(kStoreWord, IP, SP, mem); 4375} 4376 4377void ParallelMoveResolverARM::Exchange(int mem1, int mem2) { 4378 ScratchRegisterScope ensure_scratch(this, IP, R0, codegen_->GetNumberOfCoreRegisters()); 4379 int stack_offset = ensure_scratch.IsSpilled() ? kArmWordSize : 0; 4380 __ LoadFromOffset(kLoadWord, static_cast<Register>(ensure_scratch.GetRegister()), 4381 SP, mem1 + stack_offset); 4382 __ LoadFromOffset(kLoadWord, IP, SP, mem2 + stack_offset); 4383 __ StoreToOffset(kStoreWord, static_cast<Register>(ensure_scratch.GetRegister()), 4384 SP, mem2 + stack_offset); 4385 __ StoreToOffset(kStoreWord, IP, SP, mem1 + stack_offset); 4386} 4387 4388void ParallelMoveResolverARM::EmitSwap(size_t index) { 4389 DCHECK_LT(index, moves_.size()); 4390 MoveOperands* move = moves_[index]; 4391 Location source = move->GetSource(); 4392 Location destination = move->GetDestination(); 4393 4394 if (source.IsRegister() && destination.IsRegister()) { 4395 DCHECK_NE(source.AsRegister<Register>(), IP); 4396 DCHECK_NE(destination.AsRegister<Register>(), IP); 4397 __ Mov(IP, source.AsRegister<Register>()); 4398 __ Mov(source.AsRegister<Register>(), destination.AsRegister<Register>()); 4399 __ Mov(destination.AsRegister<Register>(), IP); 4400 } else if (source.IsRegister() && destination.IsStackSlot()) { 4401 Exchange(source.AsRegister<Register>(), destination.GetStackIndex()); 4402 } else if (source.IsStackSlot() && destination.IsRegister()) { 4403 Exchange(destination.AsRegister<Register>(), source.GetStackIndex()); 4404 } else if (source.IsStackSlot() && destination.IsStackSlot()) { 4405 Exchange(source.GetStackIndex(), destination.GetStackIndex()); 4406 } else if (source.IsFpuRegister() && destination.IsFpuRegister()) { 4407 __ vmovrs(IP, source.AsFpuRegister<SRegister>()); 4408 __ vmovs(source.AsFpuRegister<SRegister>(), destination.AsFpuRegister<SRegister>()); 4409 __ vmovsr(destination.AsFpuRegister<SRegister>(), IP); 4410 } else if (source.IsRegisterPair() && destination.IsRegisterPair()) { 4411 __ vmovdrr(DTMP, source.AsRegisterPairLow<Register>(), source.AsRegisterPairHigh<Register>()); 4412 __ Mov(source.AsRegisterPairLow<Register>(), destination.AsRegisterPairLow<Register>()); 4413 __ Mov(source.AsRegisterPairHigh<Register>(), destination.AsRegisterPairHigh<Register>()); 4414 __ vmovrrd(destination.AsRegisterPairLow<Register>(), 4415 destination.AsRegisterPairHigh<Register>(), 4416 DTMP); 4417 } else if (source.IsRegisterPair() || destination.IsRegisterPair()) { 4418 Register low_reg = source.IsRegisterPair() 4419 ? source.AsRegisterPairLow<Register>() 4420 : destination.AsRegisterPairLow<Register>(); 4421 int mem = source.IsRegisterPair() 4422 ? destination.GetStackIndex() 4423 : source.GetStackIndex(); 4424 DCHECK(ExpectedPairLayout(source.IsRegisterPair() ? source : destination)); 4425 __ vmovdrr(DTMP, low_reg, static_cast<Register>(low_reg + 1)); 4426 __ LoadFromOffset(kLoadWordPair, low_reg, SP, mem); 4427 __ StoreDToOffset(DTMP, SP, mem); 4428 } else if (source.IsFpuRegisterPair() && destination.IsFpuRegisterPair()) { 4429 DRegister first = FromLowSToD(source.AsFpuRegisterPairLow<SRegister>()); 4430 DRegister second = FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()); 4431 __ vmovd(DTMP, first); 4432 __ vmovd(first, second); 4433 __ vmovd(second, DTMP); 4434 } else if (source.IsFpuRegisterPair() || destination.IsFpuRegisterPair()) { 4435 DRegister reg = source.IsFpuRegisterPair() 4436 ? FromLowSToD(source.AsFpuRegisterPairLow<SRegister>()) 4437 : FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()); 4438 int mem = source.IsFpuRegisterPair() 4439 ? destination.GetStackIndex() 4440 : source.GetStackIndex(); 4441 __ vmovd(DTMP, reg); 4442 __ LoadDFromOffset(reg, SP, mem); 4443 __ StoreDToOffset(DTMP, SP, mem); 4444 } else if (source.IsFpuRegister() || destination.IsFpuRegister()) { 4445 SRegister reg = source.IsFpuRegister() ? source.AsFpuRegister<SRegister>() 4446 : destination.AsFpuRegister<SRegister>(); 4447 int mem = source.IsFpuRegister() 4448 ? destination.GetStackIndex() 4449 : source.GetStackIndex(); 4450 4451 __ vmovrs(IP, reg); 4452 __ LoadSFromOffset(reg, SP, mem); 4453 __ StoreToOffset(kStoreWord, IP, SP, mem); 4454 } else if (source.IsDoubleStackSlot() && destination.IsDoubleStackSlot()) { 4455 Exchange(source.GetStackIndex(), destination.GetStackIndex()); 4456 Exchange(source.GetHighStackIndex(kArmWordSize), destination.GetHighStackIndex(kArmWordSize)); 4457 } else { 4458 LOG(FATAL) << "Unimplemented" << source << " <-> " << destination; 4459 } 4460} 4461 4462void ParallelMoveResolverARM::SpillScratch(int reg) { 4463 __ Push(static_cast<Register>(reg)); 4464} 4465 4466void ParallelMoveResolverARM::RestoreScratch(int reg) { 4467 __ Pop(static_cast<Register>(reg)); 4468} 4469 4470void LocationsBuilderARM::VisitLoadClass(HLoadClass* cls) { 4471 InvokeRuntimeCallingConvention calling_convention; 4472 CodeGenerator::CreateLoadClassLocationSummary( 4473 cls, 4474 Location::RegisterLocation(calling_convention.GetRegisterAt(0)), 4475 Location::RegisterLocation(R0)); 4476} 4477 4478void InstructionCodeGeneratorARM::VisitLoadClass(HLoadClass* cls) { 4479 LocationSummary* locations = cls->GetLocations(); 4480 if (cls->NeedsAccessCheck()) { 4481 codegen_->MoveConstant(locations->GetTemp(0), cls->GetTypeIndex()); 4482 codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pInitializeTypeAndVerifyAccess), 4483 cls, 4484 cls->GetDexPc(), 4485 nullptr); 4486 return; 4487 } 4488 4489 Register out = locations->Out().AsRegister<Register>(); 4490 Register current_method = locations->InAt(0).AsRegister<Register>(); 4491 if (cls->IsReferrersClass()) { 4492 DCHECK(!cls->CanCallRuntime()); 4493 DCHECK(!cls->MustGenerateClinitCheck()); 4494 __ LoadFromOffset( 4495 kLoadWord, out, current_method, ArtMethod::DeclaringClassOffset().Int32Value()); 4496 } else { 4497 DCHECK(cls->CanCallRuntime()); 4498 __ LoadFromOffset(kLoadWord, 4499 out, 4500 current_method, 4501 ArtMethod::DexCacheResolvedTypesOffset(kArmPointerSize).Int32Value()); 4502 __ LoadFromOffset(kLoadWord, out, out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex())); 4503 // TODO: We will need a read barrier here. 4504 4505 SlowPathCode* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathARM( 4506 cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck()); 4507 codegen_->AddSlowPath(slow_path); 4508 __ CompareAndBranchIfZero(out, slow_path->GetEntryLabel()); 4509 if (cls->MustGenerateClinitCheck()) { 4510 GenerateClassInitializationCheck(slow_path, out); 4511 } else { 4512 __ Bind(slow_path->GetExitLabel()); 4513 } 4514 } 4515} 4516 4517void LocationsBuilderARM::VisitClinitCheck(HClinitCheck* check) { 4518 LocationSummary* locations = 4519 new (GetGraph()->GetArena()) LocationSummary(check, LocationSummary::kCallOnSlowPath); 4520 locations->SetInAt(0, Location::RequiresRegister()); 4521 if (check->HasUses()) { 4522 locations->SetOut(Location::SameAsFirstInput()); 4523 } 4524} 4525 4526void InstructionCodeGeneratorARM::VisitClinitCheck(HClinitCheck* check) { 4527 // We assume the class is not null. 4528 SlowPathCode* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathARM( 4529 check->GetLoadClass(), check, check->GetDexPc(), true); 4530 codegen_->AddSlowPath(slow_path); 4531 GenerateClassInitializationCheck(slow_path, 4532 check->GetLocations()->InAt(0).AsRegister<Register>()); 4533} 4534 4535void InstructionCodeGeneratorARM::GenerateClassInitializationCheck( 4536 SlowPathCode* slow_path, Register class_reg) { 4537 __ LoadFromOffset(kLoadWord, IP, class_reg, mirror::Class::StatusOffset().Int32Value()); 4538 __ cmp(IP, ShifterOperand(mirror::Class::kStatusInitialized)); 4539 __ b(slow_path->GetEntryLabel(), LT); 4540 // Even if the initialized flag is set, we may be in a situation where caches are not synced 4541 // properly. Therefore, we do a memory fence. 4542 __ dmb(ISH); 4543 __ Bind(slow_path->GetExitLabel()); 4544} 4545 4546void LocationsBuilderARM::VisitLoadString(HLoadString* load) { 4547 LocationSummary* locations = 4548 new (GetGraph()->GetArena()) LocationSummary(load, LocationSummary::kCallOnSlowPath); 4549 locations->SetInAt(0, Location::RequiresRegister()); 4550 locations->SetOut(Location::RequiresRegister()); 4551} 4552 4553void InstructionCodeGeneratorARM::VisitLoadString(HLoadString* load) { 4554 SlowPathCode* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathARM(load); 4555 codegen_->AddSlowPath(slow_path); 4556 4557 LocationSummary* locations = load->GetLocations(); 4558 Register out = locations->Out().AsRegister<Register>(); 4559 Register current_method = locations->InAt(0).AsRegister<Register>(); 4560 __ LoadFromOffset( 4561 kLoadWord, out, current_method, ArtMethod::DeclaringClassOffset().Int32Value()); 4562 __ LoadFromOffset(kLoadWord, out, out, mirror::Class::DexCacheStringsOffset().Int32Value()); 4563 __ LoadFromOffset(kLoadWord, out, out, CodeGenerator::GetCacheOffset(load->GetStringIndex())); 4564 // TODO: We will need a read barrier here. 4565 __ CompareAndBranchIfZero(out, slow_path->GetEntryLabel()); 4566 __ Bind(slow_path->GetExitLabel()); 4567} 4568 4569static int32_t GetExceptionTlsOffset() { 4570 return Thread::ExceptionOffset<kArmWordSize>().Int32Value(); 4571} 4572 4573void LocationsBuilderARM::VisitLoadException(HLoadException* load) { 4574 LocationSummary* locations = 4575 new (GetGraph()->GetArena()) LocationSummary(load, LocationSummary::kNoCall); 4576 locations->SetOut(Location::RequiresRegister()); 4577} 4578 4579void InstructionCodeGeneratorARM::VisitLoadException(HLoadException* load) { 4580 Register out = load->GetLocations()->Out().AsRegister<Register>(); 4581 __ LoadFromOffset(kLoadWord, out, TR, GetExceptionTlsOffset()); 4582} 4583 4584void LocationsBuilderARM::VisitClearException(HClearException* clear) { 4585 new (GetGraph()->GetArena()) LocationSummary(clear, LocationSummary::kNoCall); 4586} 4587 4588void InstructionCodeGeneratorARM::VisitClearException(HClearException* clear ATTRIBUTE_UNUSED) { 4589 __ LoadImmediate(IP, 0); 4590 __ StoreToOffset(kStoreWord, IP, TR, GetExceptionTlsOffset()); 4591} 4592 4593void LocationsBuilderARM::VisitThrow(HThrow* instruction) { 4594 LocationSummary* locations = 4595 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall); 4596 InvokeRuntimeCallingConvention calling_convention; 4597 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); 4598} 4599 4600void InstructionCodeGeneratorARM::VisitThrow(HThrow* instruction) { 4601 codegen_->InvokeRuntime( 4602 QUICK_ENTRY_POINT(pDeliverException), instruction, instruction->GetDexPc(), nullptr); 4603} 4604 4605void LocationsBuilderARM::VisitInstanceOf(HInstanceOf* instruction) { 4606 LocationSummary::CallKind call_kind = LocationSummary::kNoCall; 4607 switch (instruction->GetTypeCheckKind()) { 4608 case TypeCheckKind::kExactCheck: 4609 case TypeCheckKind::kAbstractClassCheck: 4610 case TypeCheckKind::kClassHierarchyCheck: 4611 case TypeCheckKind::kArrayObjectCheck: 4612 call_kind = LocationSummary::kNoCall; 4613 break; 4614 case TypeCheckKind::kUnresolvedCheck: 4615 case TypeCheckKind::kInterfaceCheck: 4616 call_kind = LocationSummary::kCall; 4617 break; 4618 case TypeCheckKind::kArrayCheck: 4619 call_kind = LocationSummary::kCallOnSlowPath; 4620 break; 4621 } 4622 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind); 4623 if (call_kind != LocationSummary::kCall) { 4624 locations->SetInAt(0, Location::RequiresRegister()); 4625 locations->SetInAt(1, Location::RequiresRegister()); 4626 // The out register is used as a temporary, so it overlaps with the inputs. 4627 // Note that TypeCheckSlowPathARM uses this register too. 4628 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); 4629 } else { 4630 InvokeRuntimeCallingConvention calling_convention; 4631 locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); 4632 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); 4633 locations->SetOut(Location::RegisterLocation(R0)); 4634 } 4635} 4636 4637void InstructionCodeGeneratorARM::VisitInstanceOf(HInstanceOf* instruction) { 4638 LocationSummary* locations = instruction->GetLocations(); 4639 Register obj = locations->InAt(0).AsRegister<Register>(); 4640 Register cls = locations->InAt(1).AsRegister<Register>(); 4641 Register out = locations->Out().AsRegister<Register>(); 4642 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value(); 4643 uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value(); 4644 uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value(); 4645 uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value(); 4646 Label done, zero; 4647 SlowPathCode* slow_path = nullptr; 4648 4649 // Return 0 if `obj` is null. 4650 // avoid null check if we know obj is not null. 4651 if (instruction->MustDoNullCheck()) { 4652 __ CompareAndBranchIfZero(obj, &zero); 4653 } 4654 4655 // In case of an interface/unresolved check, we put the object class into the object register. 4656 // This is safe, as the register is caller-save, and the object must be in another 4657 // register if it survives the runtime call. 4658 Register target = (instruction->GetTypeCheckKind() == TypeCheckKind::kInterfaceCheck) || 4659 (instruction->GetTypeCheckKind() == TypeCheckKind::kUnresolvedCheck) 4660 ? obj 4661 : out; 4662 __ LoadFromOffset(kLoadWord, target, obj, class_offset); 4663 __ MaybeUnpoisonHeapReference(target); 4664 4665 switch (instruction->GetTypeCheckKind()) { 4666 case TypeCheckKind::kExactCheck: { 4667 __ cmp(out, ShifterOperand(cls)); 4668 // Classes must be equal for the instanceof to succeed. 4669 __ b(&zero, NE); 4670 __ LoadImmediate(out, 1); 4671 __ b(&done); 4672 break; 4673 } 4674 case TypeCheckKind::kAbstractClassCheck: { 4675 // If the class is abstract, we eagerly fetch the super class of the 4676 // object to avoid doing a comparison we know will fail. 4677 Label loop; 4678 __ Bind(&loop); 4679 __ LoadFromOffset(kLoadWord, out, out, super_offset); 4680 __ MaybeUnpoisonHeapReference(out); 4681 // If `out` is null, we use it for the result, and jump to `done`. 4682 __ CompareAndBranchIfZero(out, &done); 4683 __ cmp(out, ShifterOperand(cls)); 4684 __ b(&loop, NE); 4685 __ LoadImmediate(out, 1); 4686 if (zero.IsLinked()) { 4687 __ b(&done); 4688 } 4689 break; 4690 } 4691 case TypeCheckKind::kClassHierarchyCheck: { 4692 // Walk over the class hierarchy to find a match. 4693 Label loop, success; 4694 __ Bind(&loop); 4695 __ cmp(out, ShifterOperand(cls)); 4696 __ b(&success, EQ); 4697 __ LoadFromOffset(kLoadWord, out, out, super_offset); 4698 __ MaybeUnpoisonHeapReference(out); 4699 __ CompareAndBranchIfNonZero(out, &loop); 4700 // If `out` is null, we use it for the result, and jump to `done`. 4701 __ b(&done); 4702 __ Bind(&success); 4703 __ LoadImmediate(out, 1); 4704 if (zero.IsLinked()) { 4705 __ b(&done); 4706 } 4707 break; 4708 } 4709 case TypeCheckKind::kArrayObjectCheck: { 4710 // Do an exact check. 4711 Label exact_check; 4712 __ cmp(out, ShifterOperand(cls)); 4713 __ b(&exact_check, EQ); 4714 // Otherwise, we need to check that the object's class is a non primitive array. 4715 __ LoadFromOffset(kLoadWord, out, out, component_offset); 4716 __ MaybeUnpoisonHeapReference(out); 4717 // If `out` is null, we use it for the result, and jump to `done`. 4718 __ CompareAndBranchIfZero(out, &done); 4719 __ LoadFromOffset(kLoadUnsignedHalfword, out, out, primitive_offset); 4720 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot"); 4721 __ CompareAndBranchIfNonZero(out, &zero); 4722 __ Bind(&exact_check); 4723 __ LoadImmediate(out, 1); 4724 __ b(&done); 4725 break; 4726 } 4727 case TypeCheckKind::kArrayCheck: { 4728 __ cmp(out, ShifterOperand(cls)); 4729 DCHECK(locations->OnlyCallsOnSlowPath()); 4730 slow_path = new (GetGraph()->GetArena()) TypeCheckSlowPathARM( 4731 instruction, /* is_fatal */ false); 4732 codegen_->AddSlowPath(slow_path); 4733 __ b(slow_path->GetEntryLabel(), NE); 4734 __ LoadImmediate(out, 1); 4735 if (zero.IsLinked()) { 4736 __ b(&done); 4737 } 4738 break; 4739 } 4740 case TypeCheckKind::kUnresolvedCheck: 4741 case TypeCheckKind::kInterfaceCheck: 4742 default: { 4743 codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pInstanceofNonTrivial), 4744 instruction, 4745 instruction->GetDexPc(), 4746 nullptr); 4747 if (zero.IsLinked()) { 4748 __ b(&done); 4749 } 4750 break; 4751 } 4752 } 4753 4754 if (zero.IsLinked()) { 4755 __ Bind(&zero); 4756 __ LoadImmediate(out, 0); 4757 } 4758 4759 if (done.IsLinked()) { 4760 __ Bind(&done); 4761 } 4762 4763 if (slow_path != nullptr) { 4764 __ Bind(slow_path->GetExitLabel()); 4765 } 4766} 4767 4768void LocationsBuilderARM::VisitCheckCast(HCheckCast* instruction) { 4769 LocationSummary::CallKind call_kind = LocationSummary::kNoCall; 4770 bool throws_into_catch = instruction->CanThrowIntoCatchBlock(); 4771 4772 switch (instruction->GetTypeCheckKind()) { 4773 case TypeCheckKind::kExactCheck: 4774 case TypeCheckKind::kAbstractClassCheck: 4775 case TypeCheckKind::kClassHierarchyCheck: 4776 case TypeCheckKind::kArrayObjectCheck: 4777 call_kind = throws_into_catch 4778 ? LocationSummary::kCallOnSlowPath 4779 : LocationSummary::kNoCall; 4780 break; 4781 case TypeCheckKind::kUnresolvedCheck: 4782 case TypeCheckKind::kInterfaceCheck: 4783 call_kind = LocationSummary::kCall; 4784 break; 4785 case TypeCheckKind::kArrayCheck: 4786 call_kind = LocationSummary::kCallOnSlowPath; 4787 break; 4788 } 4789 4790 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary( 4791 instruction, call_kind); 4792 if (call_kind != LocationSummary::kCall) { 4793 locations->SetInAt(0, Location::RequiresRegister()); 4794 locations->SetInAt(1, Location::RequiresRegister()); 4795 // Note that TypeCheckSlowPathARM uses this register too. 4796 locations->AddTemp(Location::RequiresRegister()); 4797 } else { 4798 InvokeRuntimeCallingConvention calling_convention; 4799 locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); 4800 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); 4801 } 4802} 4803 4804void InstructionCodeGeneratorARM::VisitCheckCast(HCheckCast* instruction) { 4805 LocationSummary* locations = instruction->GetLocations(); 4806 Register obj = locations->InAt(0).AsRegister<Register>(); 4807 Register cls = locations->InAt(1).AsRegister<Register>(); 4808 Register temp = locations->WillCall() 4809 ? Register(kNoRegister) 4810 : locations->GetTemp(0).AsRegister<Register>(); 4811 4812 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value(); 4813 uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value(); 4814 uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value(); 4815 uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value(); 4816 SlowPathCode* slow_path = nullptr; 4817 4818 if (!locations->WillCall()) { 4819 slow_path = new (GetGraph()->GetArena()) TypeCheckSlowPathARM( 4820 instruction, !locations->CanCall()); 4821 codegen_->AddSlowPath(slow_path); 4822 } 4823 4824 Label done; 4825 // Avoid null check if we know obj is not null. 4826 if (instruction->MustDoNullCheck()) { 4827 __ CompareAndBranchIfZero(obj, &done); 4828 } 4829 4830 if (locations->WillCall()) { 4831 __ LoadFromOffset(kLoadWord, obj, obj, class_offset); 4832 __ MaybeUnpoisonHeapReference(obj); 4833 } else { 4834 __ LoadFromOffset(kLoadWord, temp, obj, class_offset); 4835 __ MaybeUnpoisonHeapReference(temp); 4836 } 4837 4838 switch (instruction->GetTypeCheckKind()) { 4839 case TypeCheckKind::kExactCheck: 4840 case TypeCheckKind::kArrayCheck: { 4841 __ cmp(temp, ShifterOperand(cls)); 4842 // Jump to slow path for throwing the exception or doing a 4843 // more involved array check. 4844 __ b(slow_path->GetEntryLabel(), NE); 4845 break; 4846 } 4847 case TypeCheckKind::kAbstractClassCheck: { 4848 // If the class is abstract, we eagerly fetch the super class of the 4849 // object to avoid doing a comparison we know will fail. 4850 Label loop; 4851 __ Bind(&loop); 4852 __ LoadFromOffset(kLoadWord, temp, temp, super_offset); 4853 __ MaybeUnpoisonHeapReference(temp); 4854 // Jump to the slow path to throw the exception. 4855 __ CompareAndBranchIfZero(temp, slow_path->GetEntryLabel()); 4856 __ cmp(temp, ShifterOperand(cls)); 4857 __ b(&loop, NE); 4858 break; 4859 } 4860 case TypeCheckKind::kClassHierarchyCheck: { 4861 // Walk over the class hierarchy to find a match. 4862 Label loop; 4863 __ Bind(&loop); 4864 __ cmp(temp, ShifterOperand(cls)); 4865 __ b(&done, EQ); 4866 __ LoadFromOffset(kLoadWord, temp, temp, super_offset); 4867 __ MaybeUnpoisonHeapReference(temp); 4868 __ CompareAndBranchIfNonZero(temp, &loop); 4869 // Jump to the slow path to throw the exception. 4870 __ b(slow_path->GetEntryLabel()); 4871 break; 4872 } 4873 case TypeCheckKind::kArrayObjectCheck: { 4874 // Do an exact check. 4875 __ cmp(temp, ShifterOperand(cls)); 4876 __ b(&done, EQ); 4877 // Otherwise, we need to check that the object's class is a non primitive array. 4878 __ LoadFromOffset(kLoadWord, temp, temp, component_offset); 4879 __ MaybeUnpoisonHeapReference(temp); 4880 __ CompareAndBranchIfZero(temp, slow_path->GetEntryLabel()); 4881 __ LoadFromOffset(kLoadUnsignedHalfword, temp, temp, primitive_offset); 4882 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot"); 4883 __ CompareAndBranchIfNonZero(temp, slow_path->GetEntryLabel()); 4884 break; 4885 } 4886 case TypeCheckKind::kUnresolvedCheck: 4887 case TypeCheckKind::kInterfaceCheck: 4888 default: 4889 codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pCheckCast), 4890 instruction, 4891 instruction->GetDexPc(), 4892 nullptr); 4893 break; 4894 } 4895 __ Bind(&done); 4896 4897 if (slow_path != nullptr) { 4898 __ Bind(slow_path->GetExitLabel()); 4899 } 4900} 4901 4902void LocationsBuilderARM::VisitMonitorOperation(HMonitorOperation* instruction) { 4903 LocationSummary* locations = 4904 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall); 4905 InvokeRuntimeCallingConvention calling_convention; 4906 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); 4907} 4908 4909void InstructionCodeGeneratorARM::VisitMonitorOperation(HMonitorOperation* instruction) { 4910 codegen_->InvokeRuntime(instruction->IsEnter() 4911 ? QUICK_ENTRY_POINT(pLockObject) : QUICK_ENTRY_POINT(pUnlockObject), 4912 instruction, 4913 instruction->GetDexPc(), 4914 nullptr); 4915} 4916 4917void LocationsBuilderARM::VisitAnd(HAnd* instruction) { HandleBitwiseOperation(instruction); } 4918void LocationsBuilderARM::VisitOr(HOr* instruction) { HandleBitwiseOperation(instruction); } 4919void LocationsBuilderARM::VisitXor(HXor* instruction) { HandleBitwiseOperation(instruction); } 4920 4921void LocationsBuilderARM::HandleBitwiseOperation(HBinaryOperation* instruction) { 4922 LocationSummary* locations = 4923 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); 4924 DCHECK(instruction->GetResultType() == Primitive::kPrimInt 4925 || instruction->GetResultType() == Primitive::kPrimLong); 4926 locations->SetInAt(0, Location::RequiresRegister()); 4927 locations->SetInAt(1, Location::RequiresRegister()); 4928 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 4929} 4930 4931void InstructionCodeGeneratorARM::VisitAnd(HAnd* instruction) { 4932 HandleBitwiseOperation(instruction); 4933} 4934 4935void InstructionCodeGeneratorARM::VisitOr(HOr* instruction) { 4936 HandleBitwiseOperation(instruction); 4937} 4938 4939void InstructionCodeGeneratorARM::VisitXor(HXor* instruction) { 4940 HandleBitwiseOperation(instruction); 4941} 4942 4943void InstructionCodeGeneratorARM::HandleBitwiseOperation(HBinaryOperation* instruction) { 4944 LocationSummary* locations = instruction->GetLocations(); 4945 4946 if (instruction->GetResultType() == Primitive::kPrimInt) { 4947 Register first = locations->InAt(0).AsRegister<Register>(); 4948 Register second = locations->InAt(1).AsRegister<Register>(); 4949 Register out = locations->Out().AsRegister<Register>(); 4950 if (instruction->IsAnd()) { 4951 __ and_(out, first, ShifterOperand(second)); 4952 } else if (instruction->IsOr()) { 4953 __ orr(out, first, ShifterOperand(second)); 4954 } else { 4955 DCHECK(instruction->IsXor()); 4956 __ eor(out, first, ShifterOperand(second)); 4957 } 4958 } else { 4959 DCHECK_EQ(instruction->GetResultType(), Primitive::kPrimLong); 4960 Location first = locations->InAt(0); 4961 Location second = locations->InAt(1); 4962 Location out = locations->Out(); 4963 if (instruction->IsAnd()) { 4964 __ and_(out.AsRegisterPairLow<Register>(), 4965 first.AsRegisterPairLow<Register>(), 4966 ShifterOperand(second.AsRegisterPairLow<Register>())); 4967 __ and_(out.AsRegisterPairHigh<Register>(), 4968 first.AsRegisterPairHigh<Register>(), 4969 ShifterOperand(second.AsRegisterPairHigh<Register>())); 4970 } else if (instruction->IsOr()) { 4971 __ orr(out.AsRegisterPairLow<Register>(), 4972 first.AsRegisterPairLow<Register>(), 4973 ShifterOperand(second.AsRegisterPairLow<Register>())); 4974 __ orr(out.AsRegisterPairHigh<Register>(), 4975 first.AsRegisterPairHigh<Register>(), 4976 ShifterOperand(second.AsRegisterPairHigh<Register>())); 4977 } else { 4978 DCHECK(instruction->IsXor()); 4979 __ eor(out.AsRegisterPairLow<Register>(), 4980 first.AsRegisterPairLow<Register>(), 4981 ShifterOperand(second.AsRegisterPairLow<Register>())); 4982 __ eor(out.AsRegisterPairHigh<Register>(), 4983 first.AsRegisterPairHigh<Register>(), 4984 ShifterOperand(second.AsRegisterPairHigh<Register>())); 4985 } 4986 } 4987} 4988 4989void CodeGeneratorARM::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp) { 4990 // For better instruction scheduling we load the direct code pointer before the method pointer. 4991 bool direct_code_loaded = false; 4992 switch (invoke->GetCodePtrLocation()) { 4993 case HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative: 4994 if (IsSameDexFile(*invoke->GetTargetMethod().dex_file, GetGraph()->GetDexFile())) { 4995 break; 4996 } 4997 // Calls across dex files are more likely to exceed the available BL range, 4998 // so use absolute patch by falling through to kDirectCodeFixup. 4999 FALLTHROUGH_INTENDED; 5000 case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup: 5001 // LR = code address from literal pool with link-time patch. 5002 __ LoadLiteral(LR, DeduplicateMethodCodeLiteral(invoke->GetTargetMethod())); 5003 direct_code_loaded = true; 5004 break; 5005 case HInvokeStaticOrDirect::CodePtrLocation::kCallDirect: 5006 // LR = invoke->GetDirectCodePtr(); 5007 __ LoadImmediate(LR, invoke->GetDirectCodePtr()); 5008 direct_code_loaded = true; 5009 break; 5010 default: 5011 break; 5012 } 5013 5014 Location callee_method = temp; // For all kinds except kRecursive, callee will be in temp. 5015 switch (invoke->GetMethodLoadKind()) { 5016 case HInvokeStaticOrDirect::MethodLoadKind::kStringInit: 5017 // temp = thread->string_init_entrypoint 5018 __ LoadFromOffset(kLoadWord, temp.AsRegister<Register>(), TR, invoke->GetStringInitOffset()); 5019 break; 5020 case HInvokeStaticOrDirect::MethodLoadKind::kRecursive: 5021 callee_method = invoke->GetLocations()->InAt(invoke->GetCurrentMethodInputIndex()); 5022 break; 5023 case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress: 5024 __ LoadImmediate(temp.AsRegister<Register>(), invoke->GetMethodAddress()); 5025 break; 5026 case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup: 5027 __ LoadLiteral(temp.AsRegister<Register>(), 5028 DeduplicateMethodAddressLiteral(invoke->GetTargetMethod())); 5029 break; 5030 case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative: 5031 // TODO: Implement this type. For the moment, we fall back to kDexCacheViaMethod. 5032 FALLTHROUGH_INTENDED; 5033 case HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod: { 5034 Location current_method = invoke->GetLocations()->InAt(invoke->GetCurrentMethodInputIndex()); 5035 Register method_reg; 5036 Register reg = temp.AsRegister<Register>(); 5037 if (current_method.IsRegister()) { 5038 method_reg = current_method.AsRegister<Register>(); 5039 } else { 5040 DCHECK(invoke->GetLocations()->Intrinsified()); 5041 DCHECK(!current_method.IsValid()); 5042 method_reg = reg; 5043 __ LoadFromOffset(kLoadWord, reg, SP, kCurrentMethodStackOffset); 5044 } 5045 // temp = current_method->dex_cache_resolved_methods_; 5046 __ LoadFromOffset( 5047 kLoadWord, reg, method_reg, ArtMethod::DexCacheResolvedMethodsOffset( 5048 kArmPointerSize).Int32Value()); 5049 // temp = temp[index_in_cache] 5050 uint32_t index_in_cache = invoke->GetTargetMethod().dex_method_index; 5051 __ LoadFromOffset(kLoadWord, reg, reg, CodeGenerator::GetCachePointerOffset(index_in_cache)); 5052 break; 5053 } 5054 } 5055 5056 switch (invoke->GetCodePtrLocation()) { 5057 case HInvokeStaticOrDirect::CodePtrLocation::kCallSelf: 5058 __ bl(GetFrameEntryLabel()); 5059 break; 5060 case HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative: 5061 if (!direct_code_loaded) { 5062 relative_call_patches_.emplace_back(invoke->GetTargetMethod()); 5063 __ Bind(&relative_call_patches_.back().label); 5064 Label label; 5065 __ bl(&label); // Arbitrarily branch to the instruction after BL, override at link time. 5066 __ Bind(&label); 5067 break; 5068 } 5069 // If we loaded the direct code above, fall through. 5070 FALLTHROUGH_INTENDED; 5071 case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup: 5072 case HInvokeStaticOrDirect::CodePtrLocation::kCallDirect: 5073 // LR prepared above for better instruction scheduling. 5074 DCHECK(direct_code_loaded); 5075 // LR() 5076 __ blx(LR); 5077 break; 5078 case HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod: 5079 // LR = callee_method->entry_point_from_quick_compiled_code_ 5080 __ LoadFromOffset( 5081 kLoadWord, LR, callee_method.AsRegister<Register>(), 5082 ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmWordSize).Int32Value()); 5083 // LR() 5084 __ blx(LR); 5085 break; 5086 } 5087 5088 DCHECK(!IsLeafMethod()); 5089} 5090 5091void CodeGeneratorARM::GenerateVirtualCall(HInvokeVirtual* invoke, Location temp_location) { 5092 Register temp = temp_location.AsRegister<Register>(); 5093 uint32_t method_offset = mirror::Class::EmbeddedVTableEntryOffset( 5094 invoke->GetVTableIndex(), kArmPointerSize).Uint32Value(); 5095 LocationSummary* locations = invoke->GetLocations(); 5096 Location receiver = locations->InAt(0); 5097 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value(); 5098 // temp = object->GetClass(); 5099 DCHECK(receiver.IsRegister()); 5100 __ LoadFromOffset(kLoadWord, temp, receiver.AsRegister<Register>(), class_offset); 5101 MaybeRecordImplicitNullCheck(invoke); 5102 __ MaybeUnpoisonHeapReference(temp); 5103 // temp = temp->GetMethodAt(method_offset); 5104 uint32_t entry_point = ArtMethod::EntryPointFromQuickCompiledCodeOffset( 5105 kArmWordSize).Int32Value(); 5106 __ LoadFromOffset(kLoadWord, temp, temp, method_offset); 5107 // LR = temp->GetEntryPoint(); 5108 __ LoadFromOffset(kLoadWord, LR, temp, entry_point); 5109 // LR(); 5110 __ blx(LR); 5111} 5112 5113void CodeGeneratorARM::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) { 5114 DCHECK(linker_patches->empty()); 5115 size_t size = method_patches_.size() + call_patches_.size() + relative_call_patches_.size(); 5116 linker_patches->reserve(size); 5117 for (const auto& entry : method_patches_) { 5118 const MethodReference& target_method = entry.first; 5119 Literal* literal = entry.second; 5120 DCHECK(literal->GetLabel()->IsBound()); 5121 uint32_t literal_offset = literal->GetLabel()->Position(); 5122 linker_patches->push_back(LinkerPatch::MethodPatch(literal_offset, 5123 target_method.dex_file, 5124 target_method.dex_method_index)); 5125 } 5126 for (const auto& entry : call_patches_) { 5127 const MethodReference& target_method = entry.first; 5128 Literal* literal = entry.second; 5129 DCHECK(literal->GetLabel()->IsBound()); 5130 uint32_t literal_offset = literal->GetLabel()->Position(); 5131 linker_patches->push_back(LinkerPatch::CodePatch(literal_offset, 5132 target_method.dex_file, 5133 target_method.dex_method_index)); 5134 } 5135 for (const MethodPatchInfo<Label>& info : relative_call_patches_) { 5136 uint32_t literal_offset = info.label.Position(); 5137 linker_patches->push_back(LinkerPatch::RelativeCodePatch(literal_offset, 5138 info.target_method.dex_file, 5139 info.target_method.dex_method_index)); 5140 } 5141} 5142 5143Literal* CodeGeneratorARM::DeduplicateMethodLiteral(MethodReference target_method, 5144 MethodToLiteralMap* map) { 5145 // Look up the literal for target_method. 5146 auto lb = map->lower_bound(target_method); 5147 if (lb != map->end() && !map->key_comp()(target_method, lb->first)) { 5148 return lb->second; 5149 } 5150 // We don't have a literal for this method yet, insert a new one. 5151 Literal* literal = __ NewLiteral<uint32_t>(0u); 5152 map->PutBefore(lb, target_method, literal); 5153 return literal; 5154} 5155 5156Literal* CodeGeneratorARM::DeduplicateMethodAddressLiteral(MethodReference target_method) { 5157 return DeduplicateMethodLiteral(target_method, &method_patches_); 5158} 5159 5160Literal* CodeGeneratorARM::DeduplicateMethodCodeLiteral(MethodReference target_method) { 5161 return DeduplicateMethodLiteral(target_method, &call_patches_); 5162} 5163 5164void LocationsBuilderARM::VisitBoundType(HBoundType* instruction) { 5165 // Nothing to do, this should be removed during prepare for register allocator. 5166 UNUSED(instruction); 5167 LOG(FATAL) << "Unreachable"; 5168} 5169 5170void InstructionCodeGeneratorARM::VisitBoundType(HBoundType* instruction) { 5171 // Nothing to do, this should be removed during prepare for register allocator. 5172 UNUSED(instruction); 5173 LOG(FATAL) << "Unreachable"; 5174} 5175 5176void LocationsBuilderARM::VisitFakeString(HFakeString* instruction) { 5177 DCHECK(codegen_->IsBaseline()); 5178 LocationSummary* locations = 5179 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); 5180 locations->SetOut(Location::ConstantLocation(GetGraph()->GetNullConstant())); 5181} 5182 5183void InstructionCodeGeneratorARM::VisitFakeString(HFakeString* instruction ATTRIBUTE_UNUSED) { 5184 DCHECK(codegen_->IsBaseline()); 5185 // Will be generated at use site. 5186} 5187 5188// Simple implementation of packed switch - generate cascaded compare/jumps. 5189void LocationsBuilderARM::VisitPackedSwitch(HPackedSwitch* switch_instr) { 5190 LocationSummary* locations = 5191 new (GetGraph()->GetArena()) LocationSummary(switch_instr, LocationSummary::kNoCall); 5192 locations->SetInAt(0, Location::RequiresRegister()); 5193} 5194 5195void InstructionCodeGeneratorARM::VisitPackedSwitch(HPackedSwitch* switch_instr) { 5196 int32_t lower_bound = switch_instr->GetStartValue(); 5197 int32_t num_entries = switch_instr->GetNumEntries(); 5198 LocationSummary* locations = switch_instr->GetLocations(); 5199 Register value_reg = locations->InAt(0).AsRegister<Register>(); 5200 HBasicBlock* default_block = switch_instr->GetDefaultBlock(); 5201 5202 // Create a series of compare/jumps. 5203 const ArenaVector<HBasicBlock*>& successors = switch_instr->GetBlock()->GetSuccessors(); 5204 for (int32_t i = 0; i < num_entries; i++) { 5205 GenerateCompareWithImmediate(value_reg, lower_bound + i); 5206 __ b(codegen_->GetLabelOf(successors.at(i)), EQ); 5207 } 5208 5209 // And the default for any other value. 5210 if (!codegen_->GoesToNextBlock(switch_instr->GetBlock(), default_block)) { 5211 __ b(codegen_->GetLabelOf(default_block)); 5212 } 5213} 5214 5215void CodeGeneratorARM::MoveFromReturnRegister(Location trg, Primitive::Type type) { 5216 if (!trg.IsValid()) { 5217 DCHECK(type == Primitive::kPrimVoid); 5218 return; 5219 } 5220 5221 DCHECK_NE(type, Primitive::kPrimVoid); 5222 5223 Location return_loc = InvokeDexCallingConventionVisitorARM().GetReturnLocation(type); 5224 if (return_loc.Equals(trg)) { 5225 return; 5226 } 5227 5228 // TODO: Consider pairs in the parallel move resolver, then this could be nicely merged 5229 // with the last branch. 5230 if (type == Primitive::kPrimLong) { 5231 HParallelMove parallel_move(GetGraph()->GetArena()); 5232 parallel_move.AddMove(return_loc.ToLow(), trg.ToLow(), Primitive::kPrimInt, nullptr); 5233 parallel_move.AddMove(return_loc.ToHigh(), trg.ToHigh(), Primitive::kPrimInt, nullptr); 5234 GetMoveResolver()->EmitNativeCode(¶llel_move); 5235 } else if (type == Primitive::kPrimDouble) { 5236 HParallelMove parallel_move(GetGraph()->GetArena()); 5237 parallel_move.AddMove(return_loc.ToLow(), trg.ToLow(), Primitive::kPrimFloat, nullptr); 5238 parallel_move.AddMove(return_loc.ToHigh(), trg.ToHigh(), Primitive::kPrimFloat, nullptr); 5239 GetMoveResolver()->EmitNativeCode(¶llel_move); 5240 } else { 5241 // Let the parallel move resolver take care of all of this. 5242 HParallelMove parallel_move(GetGraph()->GetArena()); 5243 parallel_move.AddMove(return_loc, trg, type, nullptr); 5244 GetMoveResolver()->EmitNativeCode(¶llel_move); 5245 } 5246} 5247 5248#undef __ 5249#undef QUICK_ENTRY_POINT 5250 5251} // namespace arm 5252} // namespace art 5253