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