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