code_generator_arm64.cc revision 6dc01748c61a7ad41d4ab701d3e27897bd39a899
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_arm64.h" 18 19#include "arch/arm64/instruction_set_features_arm64.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 "entrypoints/quick/quick_entrypoints_enum.h" 25#include "gc/accounting/card_table.h" 26#include "intrinsics.h" 27#include "intrinsics_arm64.h" 28#include "mirror/array-inl.h" 29#include "mirror/class-inl.h" 30#include "offsets.h" 31#include "thread.h" 32#include "utils/arm64/assembler_arm64.h" 33#include "utils/assembler.h" 34#include "utils/stack_checks.h" 35 36 37using namespace vixl; // NOLINT(build/namespaces) 38 39#ifdef __ 40#error "ARM64 Codegen VIXL macro-assembler macro already defined." 41#endif 42 43namespace art { 44 45namespace arm64 { 46 47using helpers::CPURegisterFrom; 48using helpers::DRegisterFrom; 49using helpers::FPRegisterFrom; 50using helpers::HeapOperand; 51using helpers::HeapOperandFrom; 52using helpers::InputCPURegisterAt; 53using helpers::InputFPRegisterAt; 54using helpers::InputRegisterAt; 55using helpers::InputOperandAt; 56using helpers::Int64ConstantFrom; 57using helpers::LocationFrom; 58using helpers::OperandFromMemOperand; 59using helpers::OutputCPURegister; 60using helpers::OutputFPRegister; 61using helpers::OutputRegister; 62using helpers::RegisterFrom; 63using helpers::StackOperandFrom; 64using helpers::VIXLRegCodeFromART; 65using helpers::WRegisterFrom; 66using helpers::XRegisterFrom; 67using helpers::ARM64EncodableConstantOrRegister; 68using helpers::ArtVixlRegCodeCoherentForRegSet; 69 70static constexpr int kCurrentMethodStackOffset = 0; 71 72inline Condition ARM64Condition(IfCondition cond) { 73 switch (cond) { 74 case kCondEQ: return eq; 75 case kCondNE: return ne; 76 case kCondLT: return lt; 77 case kCondLE: return le; 78 case kCondGT: return gt; 79 case kCondGE: return ge; 80 case kCondB: return lo; 81 case kCondBE: return ls; 82 case kCondA: return hi; 83 case kCondAE: return hs; 84 } 85 LOG(FATAL) << "Unreachable"; 86 UNREACHABLE(); 87} 88 89Location ARM64ReturnLocation(Primitive::Type return_type) { 90 // Note that in practice, `LocationFrom(x0)` and `LocationFrom(w0)` create the 91 // same Location object, and so do `LocationFrom(d0)` and `LocationFrom(s0)`, 92 // but we use the exact registers for clarity. 93 if (return_type == Primitive::kPrimFloat) { 94 return LocationFrom(s0); 95 } else if (return_type == Primitive::kPrimDouble) { 96 return LocationFrom(d0); 97 } else if (return_type == Primitive::kPrimLong) { 98 return LocationFrom(x0); 99 } else if (return_type == Primitive::kPrimVoid) { 100 return Location::NoLocation(); 101 } else { 102 return LocationFrom(w0); 103 } 104} 105 106Location InvokeRuntimeCallingConvention::GetReturnLocation(Primitive::Type return_type) { 107 return ARM64ReturnLocation(return_type); 108} 109 110#define __ down_cast<CodeGeneratorARM64*>(codegen)->GetVIXLAssembler()-> 111#define QUICK_ENTRY_POINT(x) QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, x).Int32Value() 112 113// Calculate memory accessing operand for save/restore live registers. 114static void SaveRestoreLiveRegistersHelper(CodeGenerator* codegen, 115 RegisterSet* register_set, 116 int64_t spill_offset, 117 bool is_save) { 118 DCHECK(ArtVixlRegCodeCoherentForRegSet(register_set->GetCoreRegisters(), 119 codegen->GetNumberOfCoreRegisters(), 120 register_set->GetFloatingPointRegisters(), 121 codegen->GetNumberOfFloatingPointRegisters())); 122 123 CPURegList core_list = CPURegList(CPURegister::kRegister, kXRegSize, 124 register_set->GetCoreRegisters() & (~callee_saved_core_registers.list())); 125 CPURegList fp_list = CPURegList(CPURegister::kFPRegister, kDRegSize, 126 register_set->GetFloatingPointRegisters() & (~callee_saved_fp_registers.list())); 127 128 MacroAssembler* masm = down_cast<CodeGeneratorARM64*>(codegen)->GetVIXLAssembler(); 129 UseScratchRegisterScope temps(masm); 130 131 Register base = masm->StackPointer(); 132 int64_t core_spill_size = core_list.TotalSizeInBytes(); 133 int64_t fp_spill_size = fp_list.TotalSizeInBytes(); 134 int64_t reg_size = kXRegSizeInBytes; 135 int64_t max_ls_pair_offset = spill_offset + core_spill_size + fp_spill_size - 2 * reg_size; 136 uint32_t ls_access_size = WhichPowerOf2(reg_size); 137 if (((core_list.Count() > 1) || (fp_list.Count() > 1)) && 138 !masm->IsImmLSPair(max_ls_pair_offset, ls_access_size)) { 139 // If the offset does not fit in the instruction's immediate field, use an alternate register 140 // to compute the base address(float point registers spill base address). 141 Register new_base = temps.AcquireSameSizeAs(base); 142 __ Add(new_base, base, Operand(spill_offset + core_spill_size)); 143 base = new_base; 144 spill_offset = -core_spill_size; 145 int64_t new_max_ls_pair_offset = fp_spill_size - 2 * reg_size; 146 DCHECK(masm->IsImmLSPair(spill_offset, ls_access_size)); 147 DCHECK(masm->IsImmLSPair(new_max_ls_pair_offset, ls_access_size)); 148 } 149 150 if (is_save) { 151 __ StoreCPURegList(core_list, MemOperand(base, spill_offset)); 152 __ StoreCPURegList(fp_list, MemOperand(base, spill_offset + core_spill_size)); 153 } else { 154 __ LoadCPURegList(core_list, MemOperand(base, spill_offset)); 155 __ LoadCPURegList(fp_list, MemOperand(base, spill_offset + core_spill_size)); 156 } 157} 158 159void SlowPathCodeARM64::SaveLiveRegisters(CodeGenerator* codegen, LocationSummary* locations) { 160 RegisterSet* register_set = locations->GetLiveRegisters(); 161 size_t stack_offset = codegen->GetFirstRegisterSlotInSlowPath(); 162 for (size_t i = 0, e = codegen->GetNumberOfCoreRegisters(); i < e; ++i) { 163 if (!codegen->IsCoreCalleeSaveRegister(i) && register_set->ContainsCoreRegister(i)) { 164 // If the register holds an object, update the stack mask. 165 if (locations->RegisterContainsObject(i)) { 166 locations->SetStackBit(stack_offset / kVRegSize); 167 } 168 DCHECK_LT(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize()); 169 DCHECK_LT(i, kMaximumNumberOfExpectedRegisters); 170 saved_core_stack_offsets_[i] = stack_offset; 171 stack_offset += kXRegSizeInBytes; 172 } 173 } 174 175 for (size_t i = 0, e = codegen->GetNumberOfFloatingPointRegisters(); i < e; ++i) { 176 if (!codegen->IsFloatingPointCalleeSaveRegister(i) && 177 register_set->ContainsFloatingPointRegister(i)) { 178 DCHECK_LT(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize()); 179 DCHECK_LT(i, kMaximumNumberOfExpectedRegisters); 180 saved_fpu_stack_offsets_[i] = stack_offset; 181 stack_offset += kDRegSizeInBytes; 182 } 183 } 184 185 SaveRestoreLiveRegistersHelper(codegen, register_set, 186 codegen->GetFirstRegisterSlotInSlowPath(), true /* is_save */); 187} 188 189void SlowPathCodeARM64::RestoreLiveRegisters(CodeGenerator* codegen, LocationSummary* locations) { 190 RegisterSet* register_set = locations->GetLiveRegisters(); 191 SaveRestoreLiveRegistersHelper(codegen, register_set, 192 codegen->GetFirstRegisterSlotInSlowPath(), false /* is_save */); 193} 194 195class BoundsCheckSlowPathARM64 : public SlowPathCodeARM64 { 196 public: 197 explicit BoundsCheckSlowPathARM64(HBoundsCheck* instruction) : instruction_(instruction) {} 198 199 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { 200 LocationSummary* locations = instruction_->GetLocations(); 201 CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen); 202 203 __ Bind(GetEntryLabel()); 204 if (instruction_->CanThrowIntoCatchBlock()) { 205 // Live registers will be restored in the catch block if caught. 206 SaveLiveRegisters(codegen, instruction_->GetLocations()); 207 } 208 // We're moving two locations to locations that could overlap, so we need a parallel 209 // move resolver. 210 InvokeRuntimeCallingConvention calling_convention; 211 codegen->EmitParallelMoves( 212 locations->InAt(0), LocationFrom(calling_convention.GetRegisterAt(0)), Primitive::kPrimInt, 213 locations->InAt(1), LocationFrom(calling_convention.GetRegisterAt(1)), Primitive::kPrimInt); 214 arm64_codegen->InvokeRuntime( 215 QUICK_ENTRY_POINT(pThrowArrayBounds), instruction_, instruction_->GetDexPc(), this); 216 CheckEntrypointTypes<kQuickThrowArrayBounds, void, int32_t, int32_t>(); 217 } 218 219 bool IsFatal() const OVERRIDE { return true; } 220 221 const char* GetDescription() const OVERRIDE { return "BoundsCheckSlowPathARM64"; } 222 223 private: 224 HBoundsCheck* const instruction_; 225 226 DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathARM64); 227}; 228 229class DivZeroCheckSlowPathARM64 : public SlowPathCodeARM64 { 230 public: 231 explicit DivZeroCheckSlowPathARM64(HDivZeroCheck* instruction) : instruction_(instruction) {} 232 233 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { 234 CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen); 235 __ Bind(GetEntryLabel()); 236 if (instruction_->CanThrowIntoCatchBlock()) { 237 // Live registers will be restored in the catch block if caught. 238 SaveLiveRegisters(codegen, instruction_->GetLocations()); 239 } 240 arm64_codegen->InvokeRuntime( 241 QUICK_ENTRY_POINT(pThrowDivZero), instruction_, instruction_->GetDexPc(), this); 242 CheckEntrypointTypes<kQuickThrowDivZero, void, void>(); 243 } 244 245 bool IsFatal() const OVERRIDE { return true; } 246 247 const char* GetDescription() const OVERRIDE { return "DivZeroCheckSlowPathARM64"; } 248 249 private: 250 HDivZeroCheck* const instruction_; 251 DISALLOW_COPY_AND_ASSIGN(DivZeroCheckSlowPathARM64); 252}; 253 254class LoadClassSlowPathARM64 : public SlowPathCodeARM64 { 255 public: 256 LoadClassSlowPathARM64(HLoadClass* cls, 257 HInstruction* at, 258 uint32_t dex_pc, 259 bool do_clinit) 260 : cls_(cls), at_(at), dex_pc_(dex_pc), do_clinit_(do_clinit) { 261 DCHECK(at->IsLoadClass() || at->IsClinitCheck()); 262 } 263 264 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { 265 LocationSummary* locations = at_->GetLocations(); 266 CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen); 267 268 __ Bind(GetEntryLabel()); 269 SaveLiveRegisters(codegen, locations); 270 271 InvokeRuntimeCallingConvention calling_convention; 272 __ Mov(calling_convention.GetRegisterAt(0).W(), cls_->GetTypeIndex()); 273 int32_t entry_point_offset = do_clinit_ ? QUICK_ENTRY_POINT(pInitializeStaticStorage) 274 : QUICK_ENTRY_POINT(pInitializeType); 275 arm64_codegen->InvokeRuntime(entry_point_offset, at_, dex_pc_, this); 276 if (do_clinit_) { 277 CheckEntrypointTypes<kQuickInitializeStaticStorage, void*, uint32_t>(); 278 } else { 279 CheckEntrypointTypes<kQuickInitializeType, void*, uint32_t>(); 280 } 281 282 // Move the class to the desired location. 283 Location out = locations->Out(); 284 if (out.IsValid()) { 285 DCHECK(out.IsRegister() && !locations->GetLiveRegisters()->ContainsCoreRegister(out.reg())); 286 Primitive::Type type = at_->GetType(); 287 arm64_codegen->MoveLocation(out, calling_convention.GetReturnLocation(type), type); 288 } 289 290 RestoreLiveRegisters(codegen, locations); 291 __ B(GetExitLabel()); 292 } 293 294 const char* GetDescription() const OVERRIDE { return "LoadClassSlowPathARM64"; } 295 296 private: 297 // The class this slow path will load. 298 HLoadClass* const cls_; 299 300 // The instruction where this slow path is happening. 301 // (Might be the load class or an initialization check). 302 HInstruction* const at_; 303 304 // The dex PC of `at_`. 305 const uint32_t dex_pc_; 306 307 // Whether to initialize the class. 308 const bool do_clinit_; 309 310 DISALLOW_COPY_AND_ASSIGN(LoadClassSlowPathARM64); 311}; 312 313class LoadStringSlowPathARM64 : public SlowPathCodeARM64 { 314 public: 315 explicit LoadStringSlowPathARM64(HLoadString* instruction) : instruction_(instruction) {} 316 317 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { 318 LocationSummary* locations = instruction_->GetLocations(); 319 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg())); 320 CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen); 321 322 __ Bind(GetEntryLabel()); 323 SaveLiveRegisters(codegen, locations); 324 325 InvokeRuntimeCallingConvention calling_convention; 326 __ Mov(calling_convention.GetRegisterAt(0).W(), instruction_->GetStringIndex()); 327 arm64_codegen->InvokeRuntime( 328 QUICK_ENTRY_POINT(pResolveString), instruction_, instruction_->GetDexPc(), this); 329 CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>(); 330 Primitive::Type type = instruction_->GetType(); 331 arm64_codegen->MoveLocation(locations->Out(), calling_convention.GetReturnLocation(type), type); 332 333 RestoreLiveRegisters(codegen, locations); 334 __ B(GetExitLabel()); 335 } 336 337 const char* GetDescription() const OVERRIDE { return "LoadStringSlowPathARM64"; } 338 339 private: 340 HLoadString* const instruction_; 341 342 DISALLOW_COPY_AND_ASSIGN(LoadStringSlowPathARM64); 343}; 344 345class NullCheckSlowPathARM64 : public SlowPathCodeARM64 { 346 public: 347 explicit NullCheckSlowPathARM64(HNullCheck* instr) : instruction_(instr) {} 348 349 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { 350 CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen); 351 __ Bind(GetEntryLabel()); 352 if (instruction_->CanThrowIntoCatchBlock()) { 353 // Live registers will be restored in the catch block if caught. 354 SaveLiveRegisters(codegen, instruction_->GetLocations()); 355 } 356 arm64_codegen->InvokeRuntime( 357 QUICK_ENTRY_POINT(pThrowNullPointer), instruction_, instruction_->GetDexPc(), this); 358 CheckEntrypointTypes<kQuickThrowNullPointer, void, void>(); 359 } 360 361 bool IsFatal() const OVERRIDE { return true; } 362 363 const char* GetDescription() const OVERRIDE { return "NullCheckSlowPathARM64"; } 364 365 private: 366 HNullCheck* const instruction_; 367 368 DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathARM64); 369}; 370 371class SuspendCheckSlowPathARM64 : public SlowPathCodeARM64 { 372 public: 373 SuspendCheckSlowPathARM64(HSuspendCheck* instruction, HBasicBlock* successor) 374 : instruction_(instruction), successor_(successor) {} 375 376 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { 377 CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen); 378 __ Bind(GetEntryLabel()); 379 SaveLiveRegisters(codegen, instruction_->GetLocations()); 380 arm64_codegen->InvokeRuntime( 381 QUICK_ENTRY_POINT(pTestSuspend), instruction_, instruction_->GetDexPc(), this); 382 CheckEntrypointTypes<kQuickTestSuspend, void, void>(); 383 RestoreLiveRegisters(codegen, instruction_->GetLocations()); 384 if (successor_ == nullptr) { 385 __ B(GetReturnLabel()); 386 } else { 387 __ B(arm64_codegen->GetLabelOf(successor_)); 388 } 389 } 390 391 vixl::Label* GetReturnLabel() { 392 DCHECK(successor_ == nullptr); 393 return &return_label_; 394 } 395 396 HBasicBlock* GetSuccessor() const { 397 return successor_; 398 } 399 400 const char* GetDescription() const OVERRIDE { return "SuspendCheckSlowPathARM64"; } 401 402 private: 403 HSuspendCheck* const instruction_; 404 // If not null, the block to branch to after the suspend check. 405 HBasicBlock* const successor_; 406 407 // If `successor_` is null, the label to branch to after the suspend check. 408 vixl::Label return_label_; 409 410 DISALLOW_COPY_AND_ASSIGN(SuspendCheckSlowPathARM64); 411}; 412 413class TypeCheckSlowPathARM64 : public SlowPathCodeARM64 { 414 public: 415 TypeCheckSlowPathARM64(HInstruction* instruction, bool is_fatal) 416 : instruction_(instruction), is_fatal_(is_fatal) {} 417 418 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { 419 LocationSummary* locations = instruction_->GetLocations(); 420 Location class_to_check = locations->InAt(1); 421 Location object_class = instruction_->IsCheckCast() ? locations->GetTemp(0) 422 : locations->Out(); 423 DCHECK(instruction_->IsCheckCast() 424 || !locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg())); 425 CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen); 426 uint32_t dex_pc = instruction_->GetDexPc(); 427 428 __ Bind(GetEntryLabel()); 429 430 if (instruction_->IsCheckCast()) { 431 // The codegen for the instruction overwrites `temp`, so put it back in place. 432 Register obj = InputRegisterAt(instruction_, 0); 433 Register temp = WRegisterFrom(locations->GetTemp(0)); 434 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value(); 435 __ Ldr(temp, HeapOperand(obj, class_offset)); 436 arm64_codegen->GetAssembler()->MaybeUnpoisonHeapReference(temp); 437 } 438 439 if (!is_fatal_) { 440 SaveLiveRegisters(codegen, locations); 441 } 442 443 // We're moving two locations to locations that could overlap, so we need a parallel 444 // move resolver. 445 InvokeRuntimeCallingConvention calling_convention; 446 codegen->EmitParallelMoves( 447 class_to_check, LocationFrom(calling_convention.GetRegisterAt(0)), Primitive::kPrimNot, 448 object_class, LocationFrom(calling_convention.GetRegisterAt(1)), Primitive::kPrimNot); 449 450 if (instruction_->IsInstanceOf()) { 451 arm64_codegen->InvokeRuntime( 452 QUICK_ENTRY_POINT(pInstanceofNonTrivial), instruction_, dex_pc, this); 453 Primitive::Type ret_type = instruction_->GetType(); 454 Location ret_loc = calling_convention.GetReturnLocation(ret_type); 455 arm64_codegen->MoveLocation(locations->Out(), ret_loc, ret_type); 456 CheckEntrypointTypes<kQuickInstanceofNonTrivial, uint32_t, 457 const mirror::Class*, const mirror::Class*>(); 458 } else { 459 DCHECK(instruction_->IsCheckCast()); 460 arm64_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pCheckCast), instruction_, dex_pc, this); 461 CheckEntrypointTypes<kQuickCheckCast, void, const mirror::Class*, const mirror::Class*>(); 462 } 463 464 if (!is_fatal_) { 465 RestoreLiveRegisters(codegen, locations); 466 __ B(GetExitLabel()); 467 } 468 } 469 470 const char* GetDescription() const OVERRIDE { return "TypeCheckSlowPathARM64"; } 471 bool IsFatal() const { return is_fatal_; } 472 473 private: 474 HInstruction* const instruction_; 475 const bool is_fatal_; 476 477 DISALLOW_COPY_AND_ASSIGN(TypeCheckSlowPathARM64); 478}; 479 480class DeoptimizationSlowPathARM64 : public SlowPathCodeARM64 { 481 public: 482 explicit DeoptimizationSlowPathARM64(HInstruction* instruction) 483 : instruction_(instruction) {} 484 485 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { 486 __ Bind(GetEntryLabel()); 487 SaveLiveRegisters(codegen, instruction_->GetLocations()); 488 DCHECK(instruction_->IsDeoptimize()); 489 HDeoptimize* deoptimize = instruction_->AsDeoptimize(); 490 uint32_t dex_pc = deoptimize->GetDexPc(); 491 CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen); 492 arm64_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pDeoptimize), instruction_, dex_pc, this); 493 } 494 495 const char* GetDescription() const OVERRIDE { return "DeoptimizationSlowPathARM64"; } 496 497 private: 498 HInstruction* const instruction_; 499 DISALLOW_COPY_AND_ASSIGN(DeoptimizationSlowPathARM64); 500}; 501 502class ArraySetSlowPathARM64 : public SlowPathCodeARM64 { 503 public: 504 explicit ArraySetSlowPathARM64(HInstruction* instruction) : instruction_(instruction) {} 505 506 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { 507 LocationSummary* locations = instruction_->GetLocations(); 508 __ Bind(GetEntryLabel()); 509 SaveLiveRegisters(codegen, locations); 510 511 InvokeRuntimeCallingConvention calling_convention; 512 HParallelMove parallel_move(codegen->GetGraph()->GetArena()); 513 parallel_move.AddMove( 514 locations->InAt(0), 515 LocationFrom(calling_convention.GetRegisterAt(0)), 516 Primitive::kPrimNot, 517 nullptr); 518 parallel_move.AddMove( 519 locations->InAt(1), 520 LocationFrom(calling_convention.GetRegisterAt(1)), 521 Primitive::kPrimInt, 522 nullptr); 523 parallel_move.AddMove( 524 locations->InAt(2), 525 LocationFrom(calling_convention.GetRegisterAt(2)), 526 Primitive::kPrimNot, 527 nullptr); 528 codegen->GetMoveResolver()->EmitNativeCode(¶llel_move); 529 530 CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen); 531 arm64_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pAputObject), 532 instruction_, 533 instruction_->GetDexPc(), 534 this); 535 CheckEntrypointTypes<kQuickAputObject, void, mirror::Array*, int32_t, mirror::Object*>(); 536 RestoreLiveRegisters(codegen, locations); 537 __ B(GetExitLabel()); 538 } 539 540 const char* GetDescription() const OVERRIDE { return "ArraySetSlowPathARM64"; } 541 542 private: 543 HInstruction* const instruction_; 544 545 DISALLOW_COPY_AND_ASSIGN(ArraySetSlowPathARM64); 546}; 547 548#undef __ 549 550Location InvokeDexCallingConventionVisitorARM64::GetNextLocation(Primitive::Type type) { 551 Location next_location; 552 if (type == Primitive::kPrimVoid) { 553 LOG(FATAL) << "Unreachable type " << type; 554 } 555 556 if (Primitive::IsFloatingPointType(type) && 557 (float_index_ < calling_convention.GetNumberOfFpuRegisters())) { 558 next_location = LocationFrom(calling_convention.GetFpuRegisterAt(float_index_++)); 559 } else if (!Primitive::IsFloatingPointType(type) && 560 (gp_index_ < calling_convention.GetNumberOfRegisters())) { 561 next_location = LocationFrom(calling_convention.GetRegisterAt(gp_index_++)); 562 } else { 563 size_t stack_offset = calling_convention.GetStackOffsetOf(stack_index_); 564 next_location = Primitive::Is64BitType(type) ? Location::DoubleStackSlot(stack_offset) 565 : Location::StackSlot(stack_offset); 566 } 567 568 // Space on the stack is reserved for all arguments. 569 stack_index_ += Primitive::Is64BitType(type) ? 2 : 1; 570 return next_location; 571} 572 573Location InvokeDexCallingConventionVisitorARM64::GetMethodLocation() const { 574 return LocationFrom(kArtMethodRegister); 575} 576 577CodeGeneratorARM64::CodeGeneratorARM64(HGraph* graph, 578 const Arm64InstructionSetFeatures& isa_features, 579 const CompilerOptions& compiler_options, 580 OptimizingCompilerStats* stats) 581 : CodeGenerator(graph, 582 kNumberOfAllocatableRegisters, 583 kNumberOfAllocatableFPRegisters, 584 kNumberOfAllocatableRegisterPairs, 585 callee_saved_core_registers.list(), 586 callee_saved_fp_registers.list(), 587 compiler_options, 588 stats), 589 block_labels_(nullptr), 590 location_builder_(graph, this), 591 instruction_visitor_(graph, this), 592 move_resolver_(graph->GetArena(), this), 593 isa_features_(isa_features), 594 uint64_literals_(std::less<uint64_t>(), 595 graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), 596 method_patches_(MethodReferenceComparator(), 597 graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), 598 call_patches_(MethodReferenceComparator(), 599 graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), 600 relative_call_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), 601 pc_relative_dex_cache_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) { 602 // Save the link register (containing the return address) to mimic Quick. 603 AddAllocatedRegister(LocationFrom(lr)); 604} 605 606#undef __ 607#define __ GetVIXLAssembler()-> 608 609void CodeGeneratorARM64::Finalize(CodeAllocator* allocator) { 610 // Ensure we emit the literal pool. 611 __ FinalizeCode(); 612 613 CodeGenerator::Finalize(allocator); 614} 615 616void ParallelMoveResolverARM64::PrepareForEmitNativeCode() { 617 // Note: There are 6 kinds of moves: 618 // 1. constant -> GPR/FPR (non-cycle) 619 // 2. constant -> stack (non-cycle) 620 // 3. GPR/FPR -> GPR/FPR 621 // 4. GPR/FPR -> stack 622 // 5. stack -> GPR/FPR 623 // 6. stack -> stack (non-cycle) 624 // Case 1, 2 and 6 should never be included in a dependency cycle on ARM64. For case 3, 4, and 5 625 // VIXL uses at most 1 GPR. VIXL has 2 GPR and 1 FPR temps, and there should be no intersecting 626 // cycles on ARM64, so we always have 1 GPR and 1 FPR available VIXL temps to resolve the 627 // dependency. 628 vixl_temps_.Open(GetVIXLAssembler()); 629} 630 631void ParallelMoveResolverARM64::FinishEmitNativeCode() { 632 vixl_temps_.Close(); 633} 634 635Location ParallelMoveResolverARM64::AllocateScratchLocationFor(Location::Kind kind) { 636 DCHECK(kind == Location::kRegister || kind == Location::kFpuRegister || 637 kind == Location::kStackSlot || kind == Location::kDoubleStackSlot); 638 kind = (kind == Location::kFpuRegister) ? Location::kFpuRegister : Location::kRegister; 639 Location scratch = GetScratchLocation(kind); 640 if (!scratch.Equals(Location::NoLocation())) { 641 return scratch; 642 } 643 // Allocate from VIXL temp registers. 644 if (kind == Location::kRegister) { 645 scratch = LocationFrom(vixl_temps_.AcquireX()); 646 } else { 647 DCHECK(kind == Location::kFpuRegister); 648 scratch = LocationFrom(vixl_temps_.AcquireD()); 649 } 650 AddScratchLocation(scratch); 651 return scratch; 652} 653 654void ParallelMoveResolverARM64::FreeScratchLocation(Location loc) { 655 if (loc.IsRegister()) { 656 vixl_temps_.Release(XRegisterFrom(loc)); 657 } else { 658 DCHECK(loc.IsFpuRegister()); 659 vixl_temps_.Release(DRegisterFrom(loc)); 660 } 661 RemoveScratchLocation(loc); 662} 663 664void ParallelMoveResolverARM64::EmitMove(size_t index) { 665 MoveOperands* move = moves_[index]; 666 codegen_->MoveLocation(move->GetDestination(), move->GetSource(), Primitive::kPrimVoid); 667} 668 669void CodeGeneratorARM64::GenerateFrameEntry() { 670 MacroAssembler* masm = GetVIXLAssembler(); 671 BlockPoolsScope block_pools(masm); 672 __ Bind(&frame_entry_label_); 673 674 bool do_overflow_check = FrameNeedsStackCheck(GetFrameSize(), kArm64) || !IsLeafMethod(); 675 if (do_overflow_check) { 676 UseScratchRegisterScope temps(masm); 677 Register temp = temps.AcquireX(); 678 DCHECK(GetCompilerOptions().GetImplicitStackOverflowChecks()); 679 __ Sub(temp, sp, static_cast<int32_t>(GetStackOverflowReservedBytes(kArm64))); 680 __ Ldr(wzr, MemOperand(temp, 0)); 681 RecordPcInfo(nullptr, 0); 682 } 683 684 if (!HasEmptyFrame()) { 685 int frame_size = GetFrameSize(); 686 // Stack layout: 687 // sp[frame_size - 8] : lr. 688 // ... : other preserved core registers. 689 // ... : other preserved fp registers. 690 // ... : reserved frame space. 691 // sp[0] : current method. 692 __ Str(kArtMethodRegister, MemOperand(sp, -frame_size, PreIndex)); 693 GetAssembler()->cfi().AdjustCFAOffset(frame_size); 694 GetAssembler()->SpillRegisters(GetFramePreservedCoreRegisters(), 695 frame_size - GetCoreSpillSize()); 696 GetAssembler()->SpillRegisters(GetFramePreservedFPRegisters(), 697 frame_size - FrameEntrySpillSize()); 698 } 699} 700 701void CodeGeneratorARM64::GenerateFrameExit() { 702 BlockPoolsScope block_pools(GetVIXLAssembler()); 703 GetAssembler()->cfi().RememberState(); 704 if (!HasEmptyFrame()) { 705 int frame_size = GetFrameSize(); 706 GetAssembler()->UnspillRegisters(GetFramePreservedFPRegisters(), 707 frame_size - FrameEntrySpillSize()); 708 GetAssembler()->UnspillRegisters(GetFramePreservedCoreRegisters(), 709 frame_size - GetCoreSpillSize()); 710 __ Drop(frame_size); 711 GetAssembler()->cfi().AdjustCFAOffset(-frame_size); 712 } 713 __ Ret(); 714 GetAssembler()->cfi().RestoreState(); 715 GetAssembler()->cfi().DefCFAOffset(GetFrameSize()); 716} 717 718vixl::CPURegList CodeGeneratorARM64::GetFramePreservedCoreRegisters() const { 719 DCHECK(ArtVixlRegCodeCoherentForRegSet(core_spill_mask_, GetNumberOfCoreRegisters(), 0, 0)); 720 return vixl::CPURegList(vixl::CPURegister::kRegister, vixl::kXRegSize, 721 core_spill_mask_); 722} 723 724vixl::CPURegList CodeGeneratorARM64::GetFramePreservedFPRegisters() const { 725 DCHECK(ArtVixlRegCodeCoherentForRegSet(0, 0, fpu_spill_mask_, 726 GetNumberOfFloatingPointRegisters())); 727 return vixl::CPURegList(vixl::CPURegister::kFPRegister, vixl::kDRegSize, 728 fpu_spill_mask_); 729} 730 731void CodeGeneratorARM64::Bind(HBasicBlock* block) { 732 __ Bind(GetLabelOf(block)); 733} 734 735void CodeGeneratorARM64::Move(HInstruction* instruction, 736 Location location, 737 HInstruction* move_for) { 738 LocationSummary* locations = instruction->GetLocations(); 739 Primitive::Type type = instruction->GetType(); 740 DCHECK_NE(type, Primitive::kPrimVoid); 741 742 if (instruction->IsFakeString()) { 743 // The fake string is an alias for null. 744 DCHECK(IsBaseline()); 745 instruction = locations->Out().GetConstant(); 746 DCHECK(instruction->IsNullConstant()) << instruction->DebugName(); 747 } 748 749 if (instruction->IsCurrentMethod()) { 750 MoveLocation(location, 751 Location::DoubleStackSlot(kCurrentMethodStackOffset), 752 Primitive::kPrimVoid); 753 } else if (locations != nullptr && locations->Out().Equals(location)) { 754 return; 755 } else if (instruction->IsIntConstant() 756 || instruction->IsLongConstant() 757 || instruction->IsNullConstant()) { 758 int64_t value = GetInt64ValueOf(instruction->AsConstant()); 759 if (location.IsRegister()) { 760 Register dst = RegisterFrom(location, type); 761 DCHECK(((instruction->IsIntConstant() || instruction->IsNullConstant()) && dst.Is32Bits()) || 762 (instruction->IsLongConstant() && dst.Is64Bits())); 763 __ Mov(dst, value); 764 } else { 765 DCHECK(location.IsStackSlot() || location.IsDoubleStackSlot()); 766 UseScratchRegisterScope temps(GetVIXLAssembler()); 767 Register temp = (instruction->IsIntConstant() || instruction->IsNullConstant()) 768 ? temps.AcquireW() 769 : temps.AcquireX(); 770 __ Mov(temp, value); 771 __ Str(temp, StackOperandFrom(location)); 772 } 773 } else if (instruction->IsTemporary()) { 774 Location temp_location = GetTemporaryLocation(instruction->AsTemporary()); 775 MoveLocation(location, temp_location, type); 776 } else if (instruction->IsLoadLocal()) { 777 uint32_t stack_slot = GetStackSlot(instruction->AsLoadLocal()->GetLocal()); 778 if (Primitive::Is64BitType(type)) { 779 MoveLocation(location, Location::DoubleStackSlot(stack_slot), type); 780 } else { 781 MoveLocation(location, Location::StackSlot(stack_slot), type); 782 } 783 784 } else { 785 DCHECK((instruction->GetNext() == move_for) || instruction->GetNext()->IsTemporary()); 786 MoveLocation(location, locations->Out(), type); 787 } 788} 789 790void CodeGeneratorARM64::MoveConstant(Location location, int32_t value) { 791 DCHECK(location.IsRegister()); 792 __ Mov(RegisterFrom(location, Primitive::kPrimInt), value); 793} 794 795void CodeGeneratorARM64::AddLocationAsTemp(Location location, LocationSummary* locations) { 796 if (location.IsRegister()) { 797 locations->AddTemp(location); 798 } else { 799 UNIMPLEMENTED(FATAL) << "AddLocationAsTemp not implemented for location " << location; 800 } 801} 802 803Location CodeGeneratorARM64::GetStackLocation(HLoadLocal* load) const { 804 Primitive::Type type = load->GetType(); 805 806 switch (type) { 807 case Primitive::kPrimNot: 808 case Primitive::kPrimInt: 809 case Primitive::kPrimFloat: 810 return Location::StackSlot(GetStackSlot(load->GetLocal())); 811 812 case Primitive::kPrimLong: 813 case Primitive::kPrimDouble: 814 return Location::DoubleStackSlot(GetStackSlot(load->GetLocal())); 815 816 case Primitive::kPrimBoolean: 817 case Primitive::kPrimByte: 818 case Primitive::kPrimChar: 819 case Primitive::kPrimShort: 820 case Primitive::kPrimVoid: 821 LOG(FATAL) << "Unexpected type " << type; 822 } 823 824 LOG(FATAL) << "Unreachable"; 825 return Location::NoLocation(); 826} 827 828void CodeGeneratorARM64::MarkGCCard(Register object, Register value, bool value_can_be_null) { 829 UseScratchRegisterScope temps(GetVIXLAssembler()); 830 Register card = temps.AcquireX(); 831 Register temp = temps.AcquireW(); // Index within the CardTable - 32bit. 832 vixl::Label done; 833 if (value_can_be_null) { 834 __ Cbz(value, &done); 835 } 836 __ Ldr(card, MemOperand(tr, Thread::CardTableOffset<kArm64WordSize>().Int32Value())); 837 __ Lsr(temp, object, gc::accounting::CardTable::kCardShift); 838 __ Strb(card, MemOperand(card, temp.X())); 839 if (value_can_be_null) { 840 __ Bind(&done); 841 } 842} 843 844void CodeGeneratorARM64::SetupBlockedRegisters(bool is_baseline) const { 845 // Blocked core registers: 846 // lr : Runtime reserved. 847 // tr : Runtime reserved. 848 // xSuspend : Runtime reserved. TODO: Unblock this when the runtime stops using it. 849 // ip1 : VIXL core temp. 850 // ip0 : VIXL core temp. 851 // 852 // Blocked fp registers: 853 // d31 : VIXL fp temp. 854 CPURegList reserved_core_registers = vixl_reserved_core_registers; 855 reserved_core_registers.Combine(runtime_reserved_core_registers); 856 while (!reserved_core_registers.IsEmpty()) { 857 blocked_core_registers_[reserved_core_registers.PopLowestIndex().code()] = true; 858 } 859 860 CPURegList reserved_fp_registers = vixl_reserved_fp_registers; 861 while (!reserved_fp_registers.IsEmpty()) { 862 blocked_fpu_registers_[reserved_fp_registers.PopLowestIndex().code()] = true; 863 } 864 865 if (is_baseline) { 866 CPURegList reserved_core_baseline_registers = callee_saved_core_registers; 867 while (!reserved_core_baseline_registers.IsEmpty()) { 868 blocked_core_registers_[reserved_core_baseline_registers.PopLowestIndex().code()] = true; 869 } 870 } 871 872 if (is_baseline || GetGraph()->IsDebuggable()) { 873 // Stubs do not save callee-save floating point registers. If the graph 874 // is debuggable, we need to deal with these registers differently. For 875 // now, just block them. 876 CPURegList reserved_fp_baseline_registers = callee_saved_fp_registers; 877 while (!reserved_fp_baseline_registers.IsEmpty()) { 878 blocked_fpu_registers_[reserved_fp_baseline_registers.PopLowestIndex().code()] = true; 879 } 880 } 881} 882 883Location CodeGeneratorARM64::AllocateFreeRegister(Primitive::Type type) const { 884 if (type == Primitive::kPrimVoid) { 885 LOG(FATAL) << "Unreachable type " << type; 886 } 887 888 if (Primitive::IsFloatingPointType(type)) { 889 ssize_t reg = FindFreeEntry(blocked_fpu_registers_, kNumberOfAllocatableFPRegisters); 890 DCHECK_NE(reg, -1); 891 return Location::FpuRegisterLocation(reg); 892 } else { 893 ssize_t reg = FindFreeEntry(blocked_core_registers_, kNumberOfAllocatableRegisters); 894 DCHECK_NE(reg, -1); 895 return Location::RegisterLocation(reg); 896 } 897} 898 899size_t CodeGeneratorARM64::SaveCoreRegister(size_t stack_index, uint32_t reg_id) { 900 Register reg = Register(VIXLRegCodeFromART(reg_id), kXRegSize); 901 __ Str(reg, MemOperand(sp, stack_index)); 902 return kArm64WordSize; 903} 904 905size_t CodeGeneratorARM64::RestoreCoreRegister(size_t stack_index, uint32_t reg_id) { 906 Register reg = Register(VIXLRegCodeFromART(reg_id), kXRegSize); 907 __ Ldr(reg, MemOperand(sp, stack_index)); 908 return kArm64WordSize; 909} 910 911size_t CodeGeneratorARM64::SaveFloatingPointRegister(size_t stack_index, uint32_t reg_id) { 912 FPRegister reg = FPRegister(reg_id, kDRegSize); 913 __ Str(reg, MemOperand(sp, stack_index)); 914 return kArm64WordSize; 915} 916 917size_t CodeGeneratorARM64::RestoreFloatingPointRegister(size_t stack_index, uint32_t reg_id) { 918 FPRegister reg = FPRegister(reg_id, kDRegSize); 919 __ Ldr(reg, MemOperand(sp, stack_index)); 920 return kArm64WordSize; 921} 922 923void CodeGeneratorARM64::DumpCoreRegister(std::ostream& stream, int reg) const { 924 stream << XRegister(reg); 925} 926 927void CodeGeneratorARM64::DumpFloatingPointRegister(std::ostream& stream, int reg) const { 928 stream << DRegister(reg); 929} 930 931void CodeGeneratorARM64::MoveConstant(CPURegister destination, HConstant* constant) { 932 if (constant->IsIntConstant()) { 933 __ Mov(Register(destination), constant->AsIntConstant()->GetValue()); 934 } else if (constant->IsLongConstant()) { 935 __ Mov(Register(destination), constant->AsLongConstant()->GetValue()); 936 } else if (constant->IsNullConstant()) { 937 __ Mov(Register(destination), 0); 938 } else if (constant->IsFloatConstant()) { 939 __ Fmov(FPRegister(destination), constant->AsFloatConstant()->GetValue()); 940 } else { 941 DCHECK(constant->IsDoubleConstant()); 942 __ Fmov(FPRegister(destination), constant->AsDoubleConstant()->GetValue()); 943 } 944} 945 946 947static bool CoherentConstantAndType(Location constant, Primitive::Type type) { 948 DCHECK(constant.IsConstant()); 949 HConstant* cst = constant.GetConstant(); 950 return (cst->IsIntConstant() && type == Primitive::kPrimInt) || 951 // Null is mapped to a core W register, which we associate with kPrimInt. 952 (cst->IsNullConstant() && type == Primitive::kPrimInt) || 953 (cst->IsLongConstant() && type == Primitive::kPrimLong) || 954 (cst->IsFloatConstant() && type == Primitive::kPrimFloat) || 955 (cst->IsDoubleConstant() && type == Primitive::kPrimDouble); 956} 957 958void CodeGeneratorARM64::MoveLocation(Location destination, 959 Location source, 960 Primitive::Type dst_type) { 961 if (source.Equals(destination)) { 962 return; 963 } 964 965 // A valid move can always be inferred from the destination and source 966 // locations. When moving from and to a register, the argument type can be 967 // used to generate 32bit instead of 64bit moves. In debug mode we also 968 // checks the coherency of the locations and the type. 969 bool unspecified_type = (dst_type == Primitive::kPrimVoid); 970 971 if (destination.IsRegister() || destination.IsFpuRegister()) { 972 if (unspecified_type) { 973 HConstant* src_cst = source.IsConstant() ? source.GetConstant() : nullptr; 974 if (source.IsStackSlot() || 975 (src_cst != nullptr && (src_cst->IsIntConstant() 976 || src_cst->IsFloatConstant() 977 || src_cst->IsNullConstant()))) { 978 // For stack slots and 32bit constants, a 64bit type is appropriate. 979 dst_type = destination.IsRegister() ? Primitive::kPrimInt : Primitive::kPrimFloat; 980 } else { 981 // If the source is a double stack slot or a 64bit constant, a 64bit 982 // type is appropriate. Else the source is a register, and since the 983 // type has not been specified, we chose a 64bit type to force a 64bit 984 // move. 985 dst_type = destination.IsRegister() ? Primitive::kPrimLong : Primitive::kPrimDouble; 986 } 987 } 988 DCHECK((destination.IsFpuRegister() && Primitive::IsFloatingPointType(dst_type)) || 989 (destination.IsRegister() && !Primitive::IsFloatingPointType(dst_type))); 990 CPURegister dst = CPURegisterFrom(destination, dst_type); 991 if (source.IsStackSlot() || source.IsDoubleStackSlot()) { 992 DCHECK(dst.Is64Bits() == source.IsDoubleStackSlot()); 993 __ Ldr(dst, StackOperandFrom(source)); 994 } else if (source.IsConstant()) { 995 DCHECK(CoherentConstantAndType(source, dst_type)); 996 MoveConstant(dst, source.GetConstant()); 997 } else if (source.IsRegister()) { 998 if (destination.IsRegister()) { 999 __ Mov(Register(dst), RegisterFrom(source, dst_type)); 1000 } else { 1001 DCHECK(destination.IsFpuRegister()); 1002 Primitive::Type source_type = Primitive::Is64BitType(dst_type) 1003 ? Primitive::kPrimLong 1004 : Primitive::kPrimInt; 1005 __ Fmov(FPRegisterFrom(destination, dst_type), RegisterFrom(source, source_type)); 1006 } 1007 } else { 1008 DCHECK(source.IsFpuRegister()); 1009 if (destination.IsRegister()) { 1010 Primitive::Type source_type = Primitive::Is64BitType(dst_type) 1011 ? Primitive::kPrimDouble 1012 : Primitive::kPrimFloat; 1013 __ Fmov(RegisterFrom(destination, dst_type), FPRegisterFrom(source, source_type)); 1014 } else { 1015 DCHECK(destination.IsFpuRegister()); 1016 __ Fmov(FPRegister(dst), FPRegisterFrom(source, dst_type)); 1017 } 1018 } 1019 } else { // The destination is not a register. It must be a stack slot. 1020 DCHECK(destination.IsStackSlot() || destination.IsDoubleStackSlot()); 1021 if (source.IsRegister() || source.IsFpuRegister()) { 1022 if (unspecified_type) { 1023 if (source.IsRegister()) { 1024 dst_type = destination.IsStackSlot() ? Primitive::kPrimInt : Primitive::kPrimLong; 1025 } else { 1026 dst_type = destination.IsStackSlot() ? Primitive::kPrimFloat : Primitive::kPrimDouble; 1027 } 1028 } 1029 DCHECK((destination.IsDoubleStackSlot() == Primitive::Is64BitType(dst_type)) && 1030 (source.IsFpuRegister() == Primitive::IsFloatingPointType(dst_type))); 1031 __ Str(CPURegisterFrom(source, dst_type), StackOperandFrom(destination)); 1032 } else if (source.IsConstant()) { 1033 DCHECK(unspecified_type || CoherentConstantAndType(source, dst_type)) 1034 << source << " " << dst_type; 1035 UseScratchRegisterScope temps(GetVIXLAssembler()); 1036 HConstant* src_cst = source.GetConstant(); 1037 CPURegister temp; 1038 if (src_cst->IsIntConstant() || src_cst->IsNullConstant()) { 1039 temp = temps.AcquireW(); 1040 } else if (src_cst->IsLongConstant()) { 1041 temp = temps.AcquireX(); 1042 } else if (src_cst->IsFloatConstant()) { 1043 temp = temps.AcquireS(); 1044 } else { 1045 DCHECK(src_cst->IsDoubleConstant()); 1046 temp = temps.AcquireD(); 1047 } 1048 MoveConstant(temp, src_cst); 1049 __ Str(temp, StackOperandFrom(destination)); 1050 } else { 1051 DCHECK(source.IsStackSlot() || source.IsDoubleStackSlot()); 1052 DCHECK(source.IsDoubleStackSlot() == destination.IsDoubleStackSlot()); 1053 UseScratchRegisterScope temps(GetVIXLAssembler()); 1054 // There is generally less pressure on FP registers. 1055 FPRegister temp = destination.IsDoubleStackSlot() ? temps.AcquireD() : temps.AcquireS(); 1056 __ Ldr(temp, StackOperandFrom(source)); 1057 __ Str(temp, StackOperandFrom(destination)); 1058 } 1059 } 1060} 1061 1062void CodeGeneratorARM64::Load(Primitive::Type type, 1063 CPURegister dst, 1064 const MemOperand& src) { 1065 switch (type) { 1066 case Primitive::kPrimBoolean: 1067 __ Ldrb(Register(dst), src); 1068 break; 1069 case Primitive::kPrimByte: 1070 __ Ldrsb(Register(dst), src); 1071 break; 1072 case Primitive::kPrimShort: 1073 __ Ldrsh(Register(dst), src); 1074 break; 1075 case Primitive::kPrimChar: 1076 __ Ldrh(Register(dst), src); 1077 break; 1078 case Primitive::kPrimInt: 1079 case Primitive::kPrimNot: 1080 case Primitive::kPrimLong: 1081 case Primitive::kPrimFloat: 1082 case Primitive::kPrimDouble: 1083 DCHECK_EQ(dst.Is64Bits(), Primitive::Is64BitType(type)); 1084 __ Ldr(dst, src); 1085 break; 1086 case Primitive::kPrimVoid: 1087 LOG(FATAL) << "Unreachable type " << type; 1088 } 1089} 1090 1091void CodeGeneratorARM64::LoadAcquire(HInstruction* instruction, 1092 CPURegister dst, 1093 const MemOperand& src) { 1094 MacroAssembler* masm = GetVIXLAssembler(); 1095 BlockPoolsScope block_pools(masm); 1096 UseScratchRegisterScope temps(masm); 1097 Register temp_base = temps.AcquireX(); 1098 Primitive::Type type = instruction->GetType(); 1099 1100 DCHECK(!src.IsPreIndex()); 1101 DCHECK(!src.IsPostIndex()); 1102 1103 // TODO(vixl): Let the MacroAssembler handle MemOperand. 1104 __ Add(temp_base, src.base(), OperandFromMemOperand(src)); 1105 MemOperand base = MemOperand(temp_base); 1106 switch (type) { 1107 case Primitive::kPrimBoolean: 1108 __ Ldarb(Register(dst), base); 1109 MaybeRecordImplicitNullCheck(instruction); 1110 break; 1111 case Primitive::kPrimByte: 1112 __ Ldarb(Register(dst), base); 1113 MaybeRecordImplicitNullCheck(instruction); 1114 __ Sbfx(Register(dst), Register(dst), 0, Primitive::ComponentSize(type) * kBitsPerByte); 1115 break; 1116 case Primitive::kPrimChar: 1117 __ Ldarh(Register(dst), base); 1118 MaybeRecordImplicitNullCheck(instruction); 1119 break; 1120 case Primitive::kPrimShort: 1121 __ Ldarh(Register(dst), base); 1122 MaybeRecordImplicitNullCheck(instruction); 1123 __ Sbfx(Register(dst), Register(dst), 0, Primitive::ComponentSize(type) * kBitsPerByte); 1124 break; 1125 case Primitive::kPrimInt: 1126 case Primitive::kPrimNot: 1127 case Primitive::kPrimLong: 1128 DCHECK_EQ(dst.Is64Bits(), Primitive::Is64BitType(type)); 1129 __ Ldar(Register(dst), base); 1130 MaybeRecordImplicitNullCheck(instruction); 1131 break; 1132 case Primitive::kPrimFloat: 1133 case Primitive::kPrimDouble: { 1134 DCHECK(dst.IsFPRegister()); 1135 DCHECK_EQ(dst.Is64Bits(), Primitive::Is64BitType(type)); 1136 1137 Register temp = dst.Is64Bits() ? temps.AcquireX() : temps.AcquireW(); 1138 __ Ldar(temp, base); 1139 MaybeRecordImplicitNullCheck(instruction); 1140 __ Fmov(FPRegister(dst), temp); 1141 break; 1142 } 1143 case Primitive::kPrimVoid: 1144 LOG(FATAL) << "Unreachable type " << type; 1145 } 1146} 1147 1148void CodeGeneratorARM64::Store(Primitive::Type type, 1149 CPURegister src, 1150 const MemOperand& dst) { 1151 switch (type) { 1152 case Primitive::kPrimBoolean: 1153 case Primitive::kPrimByte: 1154 __ Strb(Register(src), dst); 1155 break; 1156 case Primitive::kPrimChar: 1157 case Primitive::kPrimShort: 1158 __ Strh(Register(src), dst); 1159 break; 1160 case Primitive::kPrimInt: 1161 case Primitive::kPrimNot: 1162 case Primitive::kPrimLong: 1163 case Primitive::kPrimFloat: 1164 case Primitive::kPrimDouble: 1165 DCHECK_EQ(src.Is64Bits(), Primitive::Is64BitType(type)); 1166 __ Str(src, dst); 1167 break; 1168 case Primitive::kPrimVoid: 1169 LOG(FATAL) << "Unreachable type " << type; 1170 } 1171} 1172 1173void CodeGeneratorARM64::StoreRelease(Primitive::Type type, 1174 CPURegister src, 1175 const MemOperand& dst) { 1176 UseScratchRegisterScope temps(GetVIXLAssembler()); 1177 Register temp_base = temps.AcquireX(); 1178 1179 DCHECK(!dst.IsPreIndex()); 1180 DCHECK(!dst.IsPostIndex()); 1181 1182 // TODO(vixl): Let the MacroAssembler handle this. 1183 Operand op = OperandFromMemOperand(dst); 1184 __ Add(temp_base, dst.base(), op); 1185 MemOperand base = MemOperand(temp_base); 1186 switch (type) { 1187 case Primitive::kPrimBoolean: 1188 case Primitive::kPrimByte: 1189 __ Stlrb(Register(src), base); 1190 break; 1191 case Primitive::kPrimChar: 1192 case Primitive::kPrimShort: 1193 __ Stlrh(Register(src), base); 1194 break; 1195 case Primitive::kPrimInt: 1196 case Primitive::kPrimNot: 1197 case Primitive::kPrimLong: 1198 DCHECK_EQ(src.Is64Bits(), Primitive::Is64BitType(type)); 1199 __ Stlr(Register(src), base); 1200 break; 1201 case Primitive::kPrimFloat: 1202 case Primitive::kPrimDouble: { 1203 DCHECK(src.IsFPRegister()); 1204 DCHECK_EQ(src.Is64Bits(), Primitive::Is64BitType(type)); 1205 1206 Register temp = src.Is64Bits() ? temps.AcquireX() : temps.AcquireW(); 1207 __ Fmov(temp, FPRegister(src)); 1208 __ Stlr(temp, base); 1209 break; 1210 } 1211 case Primitive::kPrimVoid: 1212 LOG(FATAL) << "Unreachable type " << type; 1213 } 1214} 1215 1216void CodeGeneratorARM64::InvokeRuntime(QuickEntrypointEnum entrypoint, 1217 HInstruction* instruction, 1218 uint32_t dex_pc, 1219 SlowPathCode* slow_path) { 1220 InvokeRuntime(GetThreadOffset<kArm64WordSize>(entrypoint).Int32Value(), 1221 instruction, 1222 dex_pc, 1223 slow_path); 1224} 1225 1226void CodeGeneratorARM64::InvokeRuntime(int32_t entry_point_offset, 1227 HInstruction* instruction, 1228 uint32_t dex_pc, 1229 SlowPathCode* slow_path) { 1230 ValidateInvokeRuntime(instruction, slow_path); 1231 BlockPoolsScope block_pools(GetVIXLAssembler()); 1232 __ Ldr(lr, MemOperand(tr, entry_point_offset)); 1233 __ Blr(lr); 1234 RecordPcInfo(instruction, dex_pc, slow_path); 1235} 1236 1237void InstructionCodeGeneratorARM64::GenerateClassInitializationCheck(SlowPathCodeARM64* slow_path, 1238 vixl::Register class_reg) { 1239 UseScratchRegisterScope temps(GetVIXLAssembler()); 1240 Register temp = temps.AcquireW(); 1241 size_t status_offset = mirror::Class::StatusOffset().SizeValue(); 1242 bool use_acquire_release = codegen_->GetInstructionSetFeatures().PreferAcquireRelease(); 1243 1244 // Even if the initialized flag is set, we need to ensure consistent memory ordering. 1245 if (use_acquire_release) { 1246 // TODO(vixl): Let the MacroAssembler handle MemOperand. 1247 __ Add(temp, class_reg, status_offset); 1248 __ Ldar(temp, HeapOperand(temp)); 1249 __ Cmp(temp, mirror::Class::kStatusInitialized); 1250 __ B(lt, slow_path->GetEntryLabel()); 1251 } else { 1252 __ Ldr(temp, HeapOperand(class_reg, status_offset)); 1253 __ Cmp(temp, mirror::Class::kStatusInitialized); 1254 __ B(lt, slow_path->GetEntryLabel()); 1255 __ Dmb(InnerShareable, BarrierReads); 1256 } 1257 __ Bind(slow_path->GetExitLabel()); 1258} 1259 1260void InstructionCodeGeneratorARM64::GenerateMemoryBarrier(MemBarrierKind kind) { 1261 BarrierType type = BarrierAll; 1262 1263 switch (kind) { 1264 case MemBarrierKind::kAnyAny: 1265 case MemBarrierKind::kAnyStore: { 1266 type = BarrierAll; 1267 break; 1268 } 1269 case MemBarrierKind::kLoadAny: { 1270 type = BarrierReads; 1271 break; 1272 } 1273 case MemBarrierKind::kStoreStore: { 1274 type = BarrierWrites; 1275 break; 1276 } 1277 default: 1278 LOG(FATAL) << "Unexpected memory barrier " << kind; 1279 } 1280 __ Dmb(InnerShareable, type); 1281} 1282 1283void InstructionCodeGeneratorARM64::GenerateSuspendCheck(HSuspendCheck* instruction, 1284 HBasicBlock* successor) { 1285 SuspendCheckSlowPathARM64* slow_path = 1286 down_cast<SuspendCheckSlowPathARM64*>(instruction->GetSlowPath()); 1287 if (slow_path == nullptr) { 1288 slow_path = new (GetGraph()->GetArena()) SuspendCheckSlowPathARM64(instruction, successor); 1289 instruction->SetSlowPath(slow_path); 1290 codegen_->AddSlowPath(slow_path); 1291 if (successor != nullptr) { 1292 DCHECK(successor->IsLoopHeader()); 1293 codegen_->ClearSpillSlotsFromLoopPhisInStackMap(instruction); 1294 } 1295 } else { 1296 DCHECK_EQ(slow_path->GetSuccessor(), successor); 1297 } 1298 1299 UseScratchRegisterScope temps(codegen_->GetVIXLAssembler()); 1300 Register temp = temps.AcquireW(); 1301 1302 __ Ldrh(temp, MemOperand(tr, Thread::ThreadFlagsOffset<kArm64WordSize>().SizeValue())); 1303 if (successor == nullptr) { 1304 __ Cbnz(temp, slow_path->GetEntryLabel()); 1305 __ Bind(slow_path->GetReturnLabel()); 1306 } else { 1307 __ Cbz(temp, codegen_->GetLabelOf(successor)); 1308 __ B(slow_path->GetEntryLabel()); 1309 // slow_path will return to GetLabelOf(successor). 1310 } 1311} 1312 1313InstructionCodeGeneratorARM64::InstructionCodeGeneratorARM64(HGraph* graph, 1314 CodeGeneratorARM64* codegen) 1315 : HGraphVisitor(graph), 1316 assembler_(codegen->GetAssembler()), 1317 codegen_(codegen) {} 1318 1319#define FOR_EACH_UNIMPLEMENTED_INSTRUCTION(M) \ 1320 /* No unimplemented IR. */ 1321 1322#define UNIMPLEMENTED_INSTRUCTION_BREAK_CODE(name) name##UnimplementedInstructionBreakCode 1323 1324enum UnimplementedInstructionBreakCode { 1325 // Using a base helps identify when we hit such breakpoints. 1326 UnimplementedInstructionBreakCodeBaseCode = 0x900, 1327#define ENUM_UNIMPLEMENTED_INSTRUCTION(name) UNIMPLEMENTED_INSTRUCTION_BREAK_CODE(name), 1328 FOR_EACH_UNIMPLEMENTED_INSTRUCTION(ENUM_UNIMPLEMENTED_INSTRUCTION) 1329#undef ENUM_UNIMPLEMENTED_INSTRUCTION 1330}; 1331 1332#define DEFINE_UNIMPLEMENTED_INSTRUCTION_VISITORS(name) \ 1333 void InstructionCodeGeneratorARM64::Visit##name(H##name* instr ATTRIBUTE_UNUSED) { \ 1334 __ Brk(UNIMPLEMENTED_INSTRUCTION_BREAK_CODE(name)); \ 1335 } \ 1336 void LocationsBuilderARM64::Visit##name(H##name* instr) { \ 1337 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instr); \ 1338 locations->SetOut(Location::Any()); \ 1339 } 1340 FOR_EACH_UNIMPLEMENTED_INSTRUCTION(DEFINE_UNIMPLEMENTED_INSTRUCTION_VISITORS) 1341#undef DEFINE_UNIMPLEMENTED_INSTRUCTION_VISITORS 1342 1343#undef UNIMPLEMENTED_INSTRUCTION_BREAK_CODE 1344#undef FOR_EACH_UNIMPLEMENTED_INSTRUCTION 1345 1346void LocationsBuilderARM64::HandleBinaryOp(HBinaryOperation* instr) { 1347 DCHECK_EQ(instr->InputCount(), 2U); 1348 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instr); 1349 Primitive::Type type = instr->GetResultType(); 1350 switch (type) { 1351 case Primitive::kPrimInt: 1352 case Primitive::kPrimLong: 1353 locations->SetInAt(0, Location::RequiresRegister()); 1354 locations->SetInAt(1, ARM64EncodableConstantOrRegister(instr->InputAt(1), instr)); 1355 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 1356 break; 1357 1358 case Primitive::kPrimFloat: 1359 case Primitive::kPrimDouble: 1360 locations->SetInAt(0, Location::RequiresFpuRegister()); 1361 locations->SetInAt(1, Location::RequiresFpuRegister()); 1362 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); 1363 break; 1364 1365 default: 1366 LOG(FATAL) << "Unexpected " << instr->DebugName() << " type " << type; 1367 } 1368} 1369 1370void LocationsBuilderARM64::HandleFieldGet(HInstruction* instruction) { 1371 LocationSummary* locations = 1372 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); 1373 locations->SetInAt(0, Location::RequiresRegister()); 1374 if (Primitive::IsFloatingPointType(instruction->GetType())) { 1375 locations->SetOut(Location::RequiresFpuRegister()); 1376 } else { 1377 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 1378 } 1379} 1380 1381void InstructionCodeGeneratorARM64::HandleFieldGet(HInstruction* instruction, 1382 const FieldInfo& field_info) { 1383 DCHECK(instruction->IsInstanceFieldGet() || instruction->IsStaticFieldGet()); 1384 Primitive::Type field_type = field_info.GetFieldType(); 1385 BlockPoolsScope block_pools(GetVIXLAssembler()); 1386 1387 MemOperand field = HeapOperand(InputRegisterAt(instruction, 0), field_info.GetFieldOffset()); 1388 bool use_acquire_release = codegen_->GetInstructionSetFeatures().PreferAcquireRelease(); 1389 1390 if (field_info.IsVolatile()) { 1391 if (use_acquire_release) { 1392 // NB: LoadAcquire will record the pc info if needed. 1393 codegen_->LoadAcquire(instruction, OutputCPURegister(instruction), field); 1394 } else { 1395 codegen_->Load(field_type, OutputCPURegister(instruction), field); 1396 codegen_->MaybeRecordImplicitNullCheck(instruction); 1397 // For IRIW sequential consistency kLoadAny is not sufficient. 1398 GenerateMemoryBarrier(MemBarrierKind::kAnyAny); 1399 } 1400 } else { 1401 codegen_->Load(field_type, OutputCPURegister(instruction), field); 1402 codegen_->MaybeRecordImplicitNullCheck(instruction); 1403 } 1404 1405 if (field_type == Primitive::kPrimNot) { 1406 GetAssembler()->MaybeUnpoisonHeapReference(OutputCPURegister(instruction).W()); 1407 } 1408} 1409 1410void LocationsBuilderARM64::HandleFieldSet(HInstruction* instruction) { 1411 LocationSummary* locations = 1412 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); 1413 locations->SetInAt(0, Location::RequiresRegister()); 1414 if (Primitive::IsFloatingPointType(instruction->InputAt(1)->GetType())) { 1415 locations->SetInAt(1, Location::RequiresFpuRegister()); 1416 } else { 1417 locations->SetInAt(1, Location::RequiresRegister()); 1418 } 1419} 1420 1421void InstructionCodeGeneratorARM64::HandleFieldSet(HInstruction* instruction, 1422 const FieldInfo& field_info, 1423 bool value_can_be_null) { 1424 DCHECK(instruction->IsInstanceFieldSet() || instruction->IsStaticFieldSet()); 1425 BlockPoolsScope block_pools(GetVIXLAssembler()); 1426 1427 Register obj = InputRegisterAt(instruction, 0); 1428 CPURegister value = InputCPURegisterAt(instruction, 1); 1429 CPURegister source = value; 1430 Offset offset = field_info.GetFieldOffset(); 1431 Primitive::Type field_type = field_info.GetFieldType(); 1432 bool use_acquire_release = codegen_->GetInstructionSetFeatures().PreferAcquireRelease(); 1433 1434 { 1435 // We use a block to end the scratch scope before the write barrier, thus 1436 // freeing the temporary registers so they can be used in `MarkGCCard`. 1437 UseScratchRegisterScope temps(GetVIXLAssembler()); 1438 1439 if (kPoisonHeapReferences && field_type == Primitive::kPrimNot) { 1440 DCHECK(value.IsW()); 1441 Register temp = temps.AcquireW(); 1442 __ Mov(temp, value.W()); 1443 GetAssembler()->PoisonHeapReference(temp.W()); 1444 source = temp; 1445 } 1446 1447 if (field_info.IsVolatile()) { 1448 if (use_acquire_release) { 1449 codegen_->StoreRelease(field_type, source, HeapOperand(obj, offset)); 1450 codegen_->MaybeRecordImplicitNullCheck(instruction); 1451 } else { 1452 GenerateMemoryBarrier(MemBarrierKind::kAnyStore); 1453 codegen_->Store(field_type, source, HeapOperand(obj, offset)); 1454 codegen_->MaybeRecordImplicitNullCheck(instruction); 1455 GenerateMemoryBarrier(MemBarrierKind::kAnyAny); 1456 } 1457 } else { 1458 codegen_->Store(field_type, source, HeapOperand(obj, offset)); 1459 codegen_->MaybeRecordImplicitNullCheck(instruction); 1460 } 1461 } 1462 1463 if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1))) { 1464 codegen_->MarkGCCard(obj, Register(value), value_can_be_null); 1465 } 1466} 1467 1468void InstructionCodeGeneratorARM64::HandleBinaryOp(HBinaryOperation* instr) { 1469 Primitive::Type type = instr->GetType(); 1470 1471 switch (type) { 1472 case Primitive::kPrimInt: 1473 case Primitive::kPrimLong: { 1474 Register dst = OutputRegister(instr); 1475 Register lhs = InputRegisterAt(instr, 0); 1476 Operand rhs = InputOperandAt(instr, 1); 1477 if (instr->IsAdd()) { 1478 __ Add(dst, lhs, rhs); 1479 } else if (instr->IsAnd()) { 1480 __ And(dst, lhs, rhs); 1481 } else if (instr->IsOr()) { 1482 __ Orr(dst, lhs, rhs); 1483 } else if (instr->IsSub()) { 1484 __ Sub(dst, lhs, rhs); 1485 } else { 1486 DCHECK(instr->IsXor()); 1487 __ Eor(dst, lhs, rhs); 1488 } 1489 break; 1490 } 1491 case Primitive::kPrimFloat: 1492 case Primitive::kPrimDouble: { 1493 FPRegister dst = OutputFPRegister(instr); 1494 FPRegister lhs = InputFPRegisterAt(instr, 0); 1495 FPRegister rhs = InputFPRegisterAt(instr, 1); 1496 if (instr->IsAdd()) { 1497 __ Fadd(dst, lhs, rhs); 1498 } else if (instr->IsSub()) { 1499 __ Fsub(dst, lhs, rhs); 1500 } else { 1501 LOG(FATAL) << "Unexpected floating-point binary operation"; 1502 } 1503 break; 1504 } 1505 default: 1506 LOG(FATAL) << "Unexpected binary operation type " << type; 1507 } 1508} 1509 1510void LocationsBuilderARM64::HandleShift(HBinaryOperation* instr) { 1511 DCHECK(instr->IsShl() || instr->IsShr() || instr->IsUShr()); 1512 1513 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instr); 1514 Primitive::Type type = instr->GetResultType(); 1515 switch (type) { 1516 case Primitive::kPrimInt: 1517 case Primitive::kPrimLong: { 1518 locations->SetInAt(0, Location::RequiresRegister()); 1519 locations->SetInAt(1, Location::RegisterOrConstant(instr->InputAt(1))); 1520 locations->SetOut(Location::RequiresRegister()); 1521 break; 1522 } 1523 default: 1524 LOG(FATAL) << "Unexpected shift type " << type; 1525 } 1526} 1527 1528void InstructionCodeGeneratorARM64::HandleShift(HBinaryOperation* instr) { 1529 DCHECK(instr->IsShl() || instr->IsShr() || instr->IsUShr()); 1530 1531 Primitive::Type type = instr->GetType(); 1532 switch (type) { 1533 case Primitive::kPrimInt: 1534 case Primitive::kPrimLong: { 1535 Register dst = OutputRegister(instr); 1536 Register lhs = InputRegisterAt(instr, 0); 1537 Operand rhs = InputOperandAt(instr, 1); 1538 if (rhs.IsImmediate()) { 1539 uint32_t shift_value = (type == Primitive::kPrimInt) 1540 ? static_cast<uint32_t>(rhs.immediate() & kMaxIntShiftValue) 1541 : static_cast<uint32_t>(rhs.immediate() & kMaxLongShiftValue); 1542 if (instr->IsShl()) { 1543 __ Lsl(dst, lhs, shift_value); 1544 } else if (instr->IsShr()) { 1545 __ Asr(dst, lhs, shift_value); 1546 } else { 1547 __ Lsr(dst, lhs, shift_value); 1548 } 1549 } else { 1550 Register rhs_reg = dst.IsX() ? rhs.reg().X() : rhs.reg().W(); 1551 1552 if (instr->IsShl()) { 1553 __ Lsl(dst, lhs, rhs_reg); 1554 } else if (instr->IsShr()) { 1555 __ Asr(dst, lhs, rhs_reg); 1556 } else { 1557 __ Lsr(dst, lhs, rhs_reg); 1558 } 1559 } 1560 break; 1561 } 1562 default: 1563 LOG(FATAL) << "Unexpected shift operation type " << type; 1564 } 1565} 1566 1567void LocationsBuilderARM64::VisitAdd(HAdd* instruction) { 1568 HandleBinaryOp(instruction); 1569} 1570 1571void InstructionCodeGeneratorARM64::VisitAdd(HAdd* instruction) { 1572 HandleBinaryOp(instruction); 1573} 1574 1575void LocationsBuilderARM64::VisitAnd(HAnd* instruction) { 1576 HandleBinaryOp(instruction); 1577} 1578 1579void InstructionCodeGeneratorARM64::VisitAnd(HAnd* instruction) { 1580 HandleBinaryOp(instruction); 1581} 1582 1583void LocationsBuilderARM64::VisitArm64IntermediateAddress(HArm64IntermediateAddress* instruction) { 1584 LocationSummary* locations = 1585 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); 1586 locations->SetInAt(0, Location::RequiresRegister()); 1587 locations->SetInAt(1, ARM64EncodableConstantOrRegister(instruction->GetOffset(), instruction)); 1588 locations->SetOut(Location::RequiresRegister()); 1589} 1590 1591void InstructionCodeGeneratorARM64::VisitArm64IntermediateAddress( 1592 HArm64IntermediateAddress* instruction) { 1593 __ Add(OutputRegister(instruction), 1594 InputRegisterAt(instruction, 0), 1595 Operand(InputOperandAt(instruction, 1))); 1596} 1597 1598void LocationsBuilderARM64::VisitArrayGet(HArrayGet* instruction) { 1599 LocationSummary* locations = 1600 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); 1601 locations->SetInAt(0, Location::RequiresRegister()); 1602 locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1))); 1603 if (Primitive::IsFloatingPointType(instruction->GetType())) { 1604 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); 1605 } else { 1606 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 1607 } 1608} 1609 1610void InstructionCodeGeneratorARM64::VisitArrayGet(HArrayGet* instruction) { 1611 Primitive::Type type = instruction->GetType(); 1612 Register obj = InputRegisterAt(instruction, 0); 1613 Location index = instruction->GetLocations()->InAt(1); 1614 size_t offset = mirror::Array::DataOffset(Primitive::ComponentSize(type)).Uint32Value(); 1615 MemOperand source = HeapOperand(obj); 1616 CPURegister dest = OutputCPURegister(instruction); 1617 1618 MacroAssembler* masm = GetVIXLAssembler(); 1619 UseScratchRegisterScope temps(masm); 1620 // Block pools between `Load` and `MaybeRecordImplicitNullCheck`. 1621 BlockPoolsScope block_pools(masm); 1622 1623 if (index.IsConstant()) { 1624 offset += Int64ConstantFrom(index) << Primitive::ComponentSizeShift(type); 1625 source = HeapOperand(obj, offset); 1626 } else { 1627 Register temp = temps.AcquireSameSizeAs(obj); 1628 if (instruction->GetArray()->IsArm64IntermediateAddress()) { 1629 // We do not need to compute the intermediate address from the array: the 1630 // input instruction has done it already. See the comment in 1631 // `InstructionSimplifierArm64::TryExtractArrayAccessAddress()`. 1632 if (kIsDebugBuild) { 1633 HArm64IntermediateAddress* tmp = instruction->GetArray()->AsArm64IntermediateAddress(); 1634 DCHECK(tmp->GetOffset()->AsIntConstant()->GetValueAsUint64() == offset); 1635 } 1636 temp = obj; 1637 } else { 1638 __ Add(temp, obj, offset); 1639 } 1640 source = HeapOperand(temp, XRegisterFrom(index), LSL, Primitive::ComponentSizeShift(type)); 1641 } 1642 1643 codegen_->Load(type, dest, source); 1644 codegen_->MaybeRecordImplicitNullCheck(instruction); 1645 1646 if (instruction->GetType() == Primitive::kPrimNot) { 1647 GetAssembler()->MaybeUnpoisonHeapReference(dest.W()); 1648 } 1649} 1650 1651void LocationsBuilderARM64::VisitArrayLength(HArrayLength* instruction) { 1652 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); 1653 locations->SetInAt(0, Location::RequiresRegister()); 1654 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 1655} 1656 1657void InstructionCodeGeneratorARM64::VisitArrayLength(HArrayLength* instruction) { 1658 BlockPoolsScope block_pools(GetVIXLAssembler()); 1659 __ Ldr(OutputRegister(instruction), 1660 HeapOperand(InputRegisterAt(instruction, 0), mirror::Array::LengthOffset())); 1661 codegen_->MaybeRecordImplicitNullCheck(instruction); 1662} 1663 1664void LocationsBuilderARM64::VisitArraySet(HArraySet* instruction) { 1665 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary( 1666 instruction, 1667 instruction->NeedsTypeCheck() ? LocationSummary::kCallOnSlowPath : LocationSummary::kNoCall); 1668 locations->SetInAt(0, Location::RequiresRegister()); 1669 locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1))); 1670 if (Primitive::IsFloatingPointType(instruction->InputAt(2)->GetType())) { 1671 locations->SetInAt(2, Location::RequiresFpuRegister()); 1672 } else { 1673 locations->SetInAt(2, Location::RequiresRegister()); 1674 } 1675} 1676 1677void InstructionCodeGeneratorARM64::VisitArraySet(HArraySet* instruction) { 1678 Primitive::Type value_type = instruction->GetComponentType(); 1679 LocationSummary* locations = instruction->GetLocations(); 1680 bool may_need_runtime_call = locations->CanCall(); 1681 bool needs_write_barrier = 1682 CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue()); 1683 1684 Register array = InputRegisterAt(instruction, 0); 1685 CPURegister value = InputCPURegisterAt(instruction, 2); 1686 CPURegister source = value; 1687 Location index = locations->InAt(1); 1688 size_t offset = mirror::Array::DataOffset(Primitive::ComponentSize(value_type)).Uint32Value(); 1689 MemOperand destination = HeapOperand(array); 1690 MacroAssembler* masm = GetVIXLAssembler(); 1691 BlockPoolsScope block_pools(masm); 1692 1693 if (!needs_write_barrier) { 1694 DCHECK(!may_need_runtime_call); 1695 if (index.IsConstant()) { 1696 offset += Int64ConstantFrom(index) << Primitive::ComponentSizeShift(value_type); 1697 destination = HeapOperand(array, offset); 1698 } else { 1699 UseScratchRegisterScope temps(masm); 1700 Register temp = temps.AcquireSameSizeAs(array); 1701 if (instruction->GetArray()->IsArm64IntermediateAddress()) { 1702 // We do not need to compute the intermediate address from the array: the 1703 // input instruction has done it already. See the comment in 1704 // `InstructionSimplifierArm64::TryExtractArrayAccessAddress()`. 1705 if (kIsDebugBuild) { 1706 HArm64IntermediateAddress* tmp = instruction->GetArray()->AsArm64IntermediateAddress(); 1707 DCHECK(tmp->GetOffset()->AsIntConstant()->GetValueAsUint64() == offset); 1708 } 1709 temp = array; 1710 } else { 1711 __ Add(temp, array, offset); 1712 } 1713 destination = HeapOperand(temp, 1714 XRegisterFrom(index), 1715 LSL, 1716 Primitive::ComponentSizeShift(value_type)); 1717 } 1718 codegen_->Store(value_type, value, destination); 1719 codegen_->MaybeRecordImplicitNullCheck(instruction); 1720 } else { 1721 DCHECK(needs_write_barrier); 1722 DCHECK(!instruction->GetArray()->IsArm64IntermediateAddress()); 1723 vixl::Label done; 1724 SlowPathCodeARM64* slow_path = nullptr; 1725 { 1726 // We use a block to end the scratch scope before the write barrier, thus 1727 // freeing the temporary registers so they can be used in `MarkGCCard`. 1728 UseScratchRegisterScope temps(masm); 1729 Register temp = temps.AcquireSameSizeAs(array); 1730 if (index.IsConstant()) { 1731 offset += Int64ConstantFrom(index) << Primitive::ComponentSizeShift(value_type); 1732 destination = HeapOperand(array, offset); 1733 } else { 1734 destination = HeapOperand(temp, 1735 XRegisterFrom(index), 1736 LSL, 1737 Primitive::ComponentSizeShift(value_type)); 1738 } 1739 1740 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value(); 1741 uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value(); 1742 uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value(); 1743 1744 if (may_need_runtime_call) { 1745 slow_path = new (GetGraph()->GetArena()) ArraySetSlowPathARM64(instruction); 1746 codegen_->AddSlowPath(slow_path); 1747 if (instruction->GetValueCanBeNull()) { 1748 vixl::Label non_zero; 1749 __ Cbnz(Register(value), &non_zero); 1750 if (!index.IsConstant()) { 1751 __ Add(temp, array, offset); 1752 } 1753 __ Str(wzr, destination); 1754 codegen_->MaybeRecordImplicitNullCheck(instruction); 1755 __ B(&done); 1756 __ Bind(&non_zero); 1757 } 1758 1759 Register temp2 = temps.AcquireSameSizeAs(array); 1760 __ Ldr(temp, HeapOperand(array, class_offset)); 1761 codegen_->MaybeRecordImplicitNullCheck(instruction); 1762 GetAssembler()->MaybeUnpoisonHeapReference(temp); 1763 __ Ldr(temp, HeapOperand(temp, component_offset)); 1764 __ Ldr(temp2, HeapOperand(Register(value), class_offset)); 1765 // No need to poison/unpoison, we're comparing two poisoned references. 1766 __ Cmp(temp, temp2); 1767 if (instruction->StaticTypeOfArrayIsObjectArray()) { 1768 vixl::Label do_put; 1769 __ B(eq, &do_put); 1770 GetAssembler()->MaybeUnpoisonHeapReference(temp); 1771 __ Ldr(temp, HeapOperand(temp, super_offset)); 1772 // No need to unpoison, we're comparing against null. 1773 __ Cbnz(temp, slow_path->GetEntryLabel()); 1774 __ Bind(&do_put); 1775 } else { 1776 __ B(ne, slow_path->GetEntryLabel()); 1777 } 1778 temps.Release(temp2); 1779 } 1780 1781 if (kPoisonHeapReferences) { 1782 Register temp2 = temps.AcquireSameSizeAs(array); 1783 DCHECK(value.IsW()); 1784 __ Mov(temp2, value.W()); 1785 GetAssembler()->PoisonHeapReference(temp2); 1786 source = temp2; 1787 } 1788 1789 if (!index.IsConstant()) { 1790 __ Add(temp, array, offset); 1791 } 1792 __ Str(source, destination); 1793 1794 if (!may_need_runtime_call) { 1795 codegen_->MaybeRecordImplicitNullCheck(instruction); 1796 } 1797 } 1798 1799 codegen_->MarkGCCard(array, value.W(), instruction->GetValueCanBeNull()); 1800 1801 if (done.IsLinked()) { 1802 __ Bind(&done); 1803 } 1804 1805 if (slow_path != nullptr) { 1806 __ Bind(slow_path->GetExitLabel()); 1807 } 1808 } 1809} 1810 1811void LocationsBuilderARM64::VisitBoundsCheck(HBoundsCheck* instruction) { 1812 LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock() 1813 ? LocationSummary::kCallOnSlowPath 1814 : LocationSummary::kNoCall; 1815 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind); 1816 locations->SetInAt(0, Location::RequiresRegister()); 1817 locations->SetInAt(1, ARM64EncodableConstantOrRegister(instruction->InputAt(1), instruction)); 1818 if (instruction->HasUses()) { 1819 locations->SetOut(Location::SameAsFirstInput()); 1820 } 1821} 1822 1823void InstructionCodeGeneratorARM64::VisitBoundsCheck(HBoundsCheck* instruction) { 1824 BoundsCheckSlowPathARM64* slow_path = 1825 new (GetGraph()->GetArena()) BoundsCheckSlowPathARM64(instruction); 1826 codegen_->AddSlowPath(slow_path); 1827 1828 __ Cmp(InputRegisterAt(instruction, 0), InputOperandAt(instruction, 1)); 1829 __ B(slow_path->GetEntryLabel(), hs); 1830} 1831 1832void LocationsBuilderARM64::VisitClinitCheck(HClinitCheck* check) { 1833 LocationSummary* locations = 1834 new (GetGraph()->GetArena()) LocationSummary(check, LocationSummary::kCallOnSlowPath); 1835 locations->SetInAt(0, Location::RequiresRegister()); 1836 if (check->HasUses()) { 1837 locations->SetOut(Location::SameAsFirstInput()); 1838 } 1839} 1840 1841void InstructionCodeGeneratorARM64::VisitClinitCheck(HClinitCheck* check) { 1842 // We assume the class is not null. 1843 SlowPathCodeARM64* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathARM64( 1844 check->GetLoadClass(), check, check->GetDexPc(), true); 1845 codegen_->AddSlowPath(slow_path); 1846 GenerateClassInitializationCheck(slow_path, InputRegisterAt(check, 0)); 1847} 1848 1849static bool IsFloatingPointZeroConstant(HInstruction* instruction) { 1850 return (instruction->IsFloatConstant() && (instruction->AsFloatConstant()->GetValue() == 0.0f)) 1851 || (instruction->IsDoubleConstant() && (instruction->AsDoubleConstant()->GetValue() == 0.0)); 1852} 1853 1854void LocationsBuilderARM64::VisitCompare(HCompare* compare) { 1855 LocationSummary* locations = 1856 new (GetGraph()->GetArena()) LocationSummary(compare, LocationSummary::kNoCall); 1857 Primitive::Type in_type = compare->InputAt(0)->GetType(); 1858 switch (in_type) { 1859 case Primitive::kPrimLong: { 1860 locations->SetInAt(0, Location::RequiresRegister()); 1861 locations->SetInAt(1, ARM64EncodableConstantOrRegister(compare->InputAt(1), compare)); 1862 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 1863 break; 1864 } 1865 case Primitive::kPrimFloat: 1866 case Primitive::kPrimDouble: { 1867 locations->SetInAt(0, Location::RequiresFpuRegister()); 1868 locations->SetInAt(1, 1869 IsFloatingPointZeroConstant(compare->InputAt(1)) 1870 ? Location::ConstantLocation(compare->InputAt(1)->AsConstant()) 1871 : Location::RequiresFpuRegister()); 1872 locations->SetOut(Location::RequiresRegister()); 1873 break; 1874 } 1875 default: 1876 LOG(FATAL) << "Unexpected type for compare operation " << in_type; 1877 } 1878} 1879 1880void InstructionCodeGeneratorARM64::VisitCompare(HCompare* compare) { 1881 Primitive::Type in_type = compare->InputAt(0)->GetType(); 1882 1883 // 0 if: left == right 1884 // 1 if: left > right 1885 // -1 if: left < right 1886 switch (in_type) { 1887 case Primitive::kPrimLong: { 1888 Register result = OutputRegister(compare); 1889 Register left = InputRegisterAt(compare, 0); 1890 Operand right = InputOperandAt(compare, 1); 1891 1892 __ Cmp(left, right); 1893 __ Cset(result, ne); 1894 __ Cneg(result, result, lt); 1895 break; 1896 } 1897 case Primitive::kPrimFloat: 1898 case Primitive::kPrimDouble: { 1899 Register result = OutputRegister(compare); 1900 FPRegister left = InputFPRegisterAt(compare, 0); 1901 if (compare->GetLocations()->InAt(1).IsConstant()) { 1902 DCHECK(IsFloatingPointZeroConstant(compare->GetLocations()->InAt(1).GetConstant())); 1903 // 0.0 is the only immediate that can be encoded directly in an FCMP instruction. 1904 __ Fcmp(left, 0.0); 1905 } else { 1906 __ Fcmp(left, InputFPRegisterAt(compare, 1)); 1907 } 1908 if (compare->IsGtBias()) { 1909 __ Cset(result, ne); 1910 } else { 1911 __ Csetm(result, ne); 1912 } 1913 __ Cneg(result, result, compare->IsGtBias() ? mi : gt); 1914 break; 1915 } 1916 default: 1917 LOG(FATAL) << "Unimplemented compare type " << in_type; 1918 } 1919} 1920 1921void LocationsBuilderARM64::VisitCondition(HCondition* instruction) { 1922 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); 1923 1924 if (Primitive::IsFloatingPointType(instruction->InputAt(0)->GetType())) { 1925 locations->SetInAt(0, Location::RequiresFpuRegister()); 1926 locations->SetInAt(1, 1927 IsFloatingPointZeroConstant(instruction->InputAt(1)) 1928 ? Location::ConstantLocation(instruction->InputAt(1)->AsConstant()) 1929 : Location::RequiresFpuRegister()); 1930 } else { 1931 // Integer cases. 1932 locations->SetInAt(0, Location::RequiresRegister()); 1933 locations->SetInAt(1, ARM64EncodableConstantOrRegister(instruction->InputAt(1), instruction)); 1934 } 1935 1936 if (instruction->NeedsMaterialization()) { 1937 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 1938 } 1939} 1940 1941void InstructionCodeGeneratorARM64::VisitCondition(HCondition* instruction) { 1942 if (!instruction->NeedsMaterialization()) { 1943 return; 1944 } 1945 1946 LocationSummary* locations = instruction->GetLocations(); 1947 Register res = RegisterFrom(locations->Out(), instruction->GetType()); 1948 IfCondition if_cond = instruction->GetCondition(); 1949 Condition arm64_cond = ARM64Condition(if_cond); 1950 1951 if (Primitive::IsFloatingPointType(instruction->InputAt(0)->GetType())) { 1952 FPRegister lhs = InputFPRegisterAt(instruction, 0); 1953 if (locations->InAt(1).IsConstant()) { 1954 DCHECK(IsFloatingPointZeroConstant(locations->InAt(1).GetConstant())); 1955 // 0.0 is the only immediate that can be encoded directly in an FCMP instruction. 1956 __ Fcmp(lhs, 0.0); 1957 } else { 1958 __ Fcmp(lhs, InputFPRegisterAt(instruction, 1)); 1959 } 1960 __ Cset(res, arm64_cond); 1961 if (instruction->IsFPConditionTrueIfNaN()) { 1962 // res = IsUnordered(arm64_cond) ? 1 : res <=> res = IsNotUnordered(arm64_cond) ? res : 1 1963 __ Csel(res, res, Operand(1), vc); // VC for "not unordered". 1964 } else if (instruction->IsFPConditionFalseIfNaN()) { 1965 // res = IsUnordered(arm64_cond) ? 0 : res <=> res = IsNotUnordered(arm64_cond) ? res : 0 1966 __ Csel(res, res, Operand(0), vc); // VC for "not unordered". 1967 } 1968 } else { 1969 // Integer cases. 1970 Register lhs = InputRegisterAt(instruction, 0); 1971 Operand rhs = InputOperandAt(instruction, 1); 1972 __ Cmp(lhs, rhs); 1973 __ Cset(res, arm64_cond); 1974 } 1975} 1976 1977#define FOR_EACH_CONDITION_INSTRUCTION(M) \ 1978 M(Equal) \ 1979 M(NotEqual) \ 1980 M(LessThan) \ 1981 M(LessThanOrEqual) \ 1982 M(GreaterThan) \ 1983 M(GreaterThanOrEqual) \ 1984 M(Below) \ 1985 M(BelowOrEqual) \ 1986 M(Above) \ 1987 M(AboveOrEqual) 1988#define DEFINE_CONDITION_VISITORS(Name) \ 1989void LocationsBuilderARM64::Visit##Name(H##Name* comp) { VisitCondition(comp); } \ 1990void InstructionCodeGeneratorARM64::Visit##Name(H##Name* comp) { VisitCondition(comp); } 1991FOR_EACH_CONDITION_INSTRUCTION(DEFINE_CONDITION_VISITORS) 1992#undef DEFINE_CONDITION_VISITORS 1993#undef FOR_EACH_CONDITION_INSTRUCTION 1994 1995void InstructionCodeGeneratorARM64::DivRemOneOrMinusOne(HBinaryOperation* instruction) { 1996 DCHECK(instruction->IsDiv() || instruction->IsRem()); 1997 1998 LocationSummary* locations = instruction->GetLocations(); 1999 Location second = locations->InAt(1); 2000 DCHECK(second.IsConstant()); 2001 2002 Register out = OutputRegister(instruction); 2003 Register dividend = InputRegisterAt(instruction, 0); 2004 int64_t imm = Int64FromConstant(second.GetConstant()); 2005 DCHECK(imm == 1 || imm == -1); 2006 2007 if (instruction->IsRem()) { 2008 __ Mov(out, 0); 2009 } else { 2010 if (imm == 1) { 2011 __ Mov(out, dividend); 2012 } else { 2013 __ Neg(out, dividend); 2014 } 2015 } 2016} 2017 2018void InstructionCodeGeneratorARM64::DivRemByPowerOfTwo(HBinaryOperation* instruction) { 2019 DCHECK(instruction->IsDiv() || instruction->IsRem()); 2020 2021 LocationSummary* locations = instruction->GetLocations(); 2022 Location second = locations->InAt(1); 2023 DCHECK(second.IsConstant()); 2024 2025 Register out = OutputRegister(instruction); 2026 Register dividend = InputRegisterAt(instruction, 0); 2027 int64_t imm = Int64FromConstant(second.GetConstant()); 2028 uint64_t abs_imm = static_cast<uint64_t>(std::abs(imm)); 2029 DCHECK(IsPowerOfTwo(abs_imm)); 2030 int ctz_imm = CTZ(abs_imm); 2031 2032 UseScratchRegisterScope temps(GetVIXLAssembler()); 2033 Register temp = temps.AcquireSameSizeAs(out); 2034 2035 if (instruction->IsDiv()) { 2036 __ Add(temp, dividend, abs_imm - 1); 2037 __ Cmp(dividend, 0); 2038 __ Csel(out, temp, dividend, lt); 2039 if (imm > 0) { 2040 __ Asr(out, out, ctz_imm); 2041 } else { 2042 __ Neg(out, Operand(out, ASR, ctz_imm)); 2043 } 2044 } else { 2045 int bits = instruction->GetResultType() == Primitive::kPrimInt ? 32 : 64; 2046 __ Asr(temp, dividend, bits - 1); 2047 __ Lsr(temp, temp, bits - ctz_imm); 2048 __ Add(out, dividend, temp); 2049 __ And(out, out, abs_imm - 1); 2050 __ Sub(out, out, temp); 2051 } 2052} 2053 2054void InstructionCodeGeneratorARM64::GenerateDivRemWithAnyConstant(HBinaryOperation* instruction) { 2055 DCHECK(instruction->IsDiv() || instruction->IsRem()); 2056 2057 LocationSummary* locations = instruction->GetLocations(); 2058 Location second = locations->InAt(1); 2059 DCHECK(second.IsConstant()); 2060 2061 Register out = OutputRegister(instruction); 2062 Register dividend = InputRegisterAt(instruction, 0); 2063 int64_t imm = Int64FromConstant(second.GetConstant()); 2064 2065 Primitive::Type type = instruction->GetResultType(); 2066 DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong); 2067 2068 int64_t magic; 2069 int shift; 2070 CalculateMagicAndShiftForDivRem(imm, type == Primitive::kPrimLong /* is_long */, &magic, &shift); 2071 2072 UseScratchRegisterScope temps(GetVIXLAssembler()); 2073 Register temp = temps.AcquireSameSizeAs(out); 2074 2075 // temp = get_high(dividend * magic) 2076 __ Mov(temp, magic); 2077 if (type == Primitive::kPrimLong) { 2078 __ Smulh(temp, dividend, temp); 2079 } else { 2080 __ Smull(temp.X(), dividend, temp); 2081 __ Lsr(temp.X(), temp.X(), 32); 2082 } 2083 2084 if (imm > 0 && magic < 0) { 2085 __ Add(temp, temp, dividend); 2086 } else if (imm < 0 && magic > 0) { 2087 __ Sub(temp, temp, dividend); 2088 } 2089 2090 if (shift != 0) { 2091 __ Asr(temp, temp, shift); 2092 } 2093 2094 if (instruction->IsDiv()) { 2095 __ Sub(out, temp, Operand(temp, ASR, type == Primitive::kPrimLong ? 63 : 31)); 2096 } else { 2097 __ Sub(temp, temp, Operand(temp, ASR, type == Primitive::kPrimLong ? 63 : 31)); 2098 // TODO: Strength reduction for msub. 2099 Register temp_imm = temps.AcquireSameSizeAs(out); 2100 __ Mov(temp_imm, imm); 2101 __ Msub(out, temp, temp_imm, dividend); 2102 } 2103} 2104 2105void InstructionCodeGeneratorARM64::GenerateDivRemIntegral(HBinaryOperation* instruction) { 2106 DCHECK(instruction->IsDiv() || instruction->IsRem()); 2107 Primitive::Type type = instruction->GetResultType(); 2108 DCHECK(type == Primitive::kPrimInt || Primitive::kPrimLong); 2109 2110 LocationSummary* locations = instruction->GetLocations(); 2111 Register out = OutputRegister(instruction); 2112 Location second = locations->InAt(1); 2113 2114 if (second.IsConstant()) { 2115 int64_t imm = Int64FromConstant(second.GetConstant()); 2116 2117 if (imm == 0) { 2118 // Do not generate anything. DivZeroCheck would prevent any code to be executed. 2119 } else if (imm == 1 || imm == -1) { 2120 DivRemOneOrMinusOne(instruction); 2121 } else if (IsPowerOfTwo(std::abs(imm))) { 2122 DivRemByPowerOfTwo(instruction); 2123 } else { 2124 DCHECK(imm <= -2 || imm >= 2); 2125 GenerateDivRemWithAnyConstant(instruction); 2126 } 2127 } else { 2128 Register dividend = InputRegisterAt(instruction, 0); 2129 Register divisor = InputRegisterAt(instruction, 1); 2130 if (instruction->IsDiv()) { 2131 __ Sdiv(out, dividend, divisor); 2132 } else { 2133 UseScratchRegisterScope temps(GetVIXLAssembler()); 2134 Register temp = temps.AcquireSameSizeAs(out); 2135 __ Sdiv(temp, dividend, divisor); 2136 __ Msub(out, temp, divisor, dividend); 2137 } 2138 } 2139} 2140 2141void LocationsBuilderARM64::VisitDiv(HDiv* div) { 2142 LocationSummary* locations = 2143 new (GetGraph()->GetArena()) LocationSummary(div, LocationSummary::kNoCall); 2144 switch (div->GetResultType()) { 2145 case Primitive::kPrimInt: 2146 case Primitive::kPrimLong: 2147 locations->SetInAt(0, Location::RequiresRegister()); 2148 locations->SetInAt(1, Location::RegisterOrConstant(div->InputAt(1))); 2149 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 2150 break; 2151 2152 case Primitive::kPrimFloat: 2153 case Primitive::kPrimDouble: 2154 locations->SetInAt(0, Location::RequiresFpuRegister()); 2155 locations->SetInAt(1, Location::RequiresFpuRegister()); 2156 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); 2157 break; 2158 2159 default: 2160 LOG(FATAL) << "Unexpected div type " << div->GetResultType(); 2161 } 2162} 2163 2164void InstructionCodeGeneratorARM64::VisitDiv(HDiv* div) { 2165 Primitive::Type type = div->GetResultType(); 2166 switch (type) { 2167 case Primitive::kPrimInt: 2168 case Primitive::kPrimLong: 2169 GenerateDivRemIntegral(div); 2170 break; 2171 2172 case Primitive::kPrimFloat: 2173 case Primitive::kPrimDouble: 2174 __ Fdiv(OutputFPRegister(div), InputFPRegisterAt(div, 0), InputFPRegisterAt(div, 1)); 2175 break; 2176 2177 default: 2178 LOG(FATAL) << "Unexpected div type " << type; 2179 } 2180} 2181 2182void LocationsBuilderARM64::VisitDivZeroCheck(HDivZeroCheck* instruction) { 2183 LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock() 2184 ? LocationSummary::kCallOnSlowPath 2185 : LocationSummary::kNoCall; 2186 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind); 2187 locations->SetInAt(0, Location::RegisterOrConstant(instruction->InputAt(0))); 2188 if (instruction->HasUses()) { 2189 locations->SetOut(Location::SameAsFirstInput()); 2190 } 2191} 2192 2193void InstructionCodeGeneratorARM64::VisitDivZeroCheck(HDivZeroCheck* instruction) { 2194 SlowPathCodeARM64* slow_path = 2195 new (GetGraph()->GetArena()) DivZeroCheckSlowPathARM64(instruction); 2196 codegen_->AddSlowPath(slow_path); 2197 Location value = instruction->GetLocations()->InAt(0); 2198 2199 Primitive::Type type = instruction->GetType(); 2200 2201 if ((type == Primitive::kPrimBoolean) || !Primitive::IsIntegralType(type)) { 2202 LOG(FATAL) << "Unexpected type " << type << " for DivZeroCheck."; 2203 return; 2204 } 2205 2206 if (value.IsConstant()) { 2207 int64_t divisor = Int64ConstantFrom(value); 2208 if (divisor == 0) { 2209 __ B(slow_path->GetEntryLabel()); 2210 } else { 2211 // A division by a non-null constant is valid. We don't need to perform 2212 // any check, so simply fall through. 2213 } 2214 } else { 2215 __ Cbz(InputRegisterAt(instruction, 0), slow_path->GetEntryLabel()); 2216 } 2217} 2218 2219void LocationsBuilderARM64::VisitDoubleConstant(HDoubleConstant* constant) { 2220 LocationSummary* locations = 2221 new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall); 2222 locations->SetOut(Location::ConstantLocation(constant)); 2223} 2224 2225void InstructionCodeGeneratorARM64::VisitDoubleConstant( 2226 HDoubleConstant* constant ATTRIBUTE_UNUSED) { 2227 // Will be generated at use site. 2228} 2229 2230void LocationsBuilderARM64::VisitExit(HExit* exit) { 2231 exit->SetLocations(nullptr); 2232} 2233 2234void InstructionCodeGeneratorARM64::VisitExit(HExit* exit ATTRIBUTE_UNUSED) { 2235} 2236 2237void LocationsBuilderARM64::VisitFloatConstant(HFloatConstant* constant) { 2238 LocationSummary* locations = 2239 new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall); 2240 locations->SetOut(Location::ConstantLocation(constant)); 2241} 2242 2243void InstructionCodeGeneratorARM64::VisitFloatConstant(HFloatConstant* constant ATTRIBUTE_UNUSED) { 2244 // Will be generated at use site. 2245} 2246 2247void InstructionCodeGeneratorARM64::HandleGoto(HInstruction* got, HBasicBlock* successor) { 2248 DCHECK(!successor->IsExitBlock()); 2249 HBasicBlock* block = got->GetBlock(); 2250 HInstruction* previous = got->GetPrevious(); 2251 HLoopInformation* info = block->GetLoopInformation(); 2252 2253 if (info != nullptr && info->IsBackEdge(*block) && info->HasSuspendCheck()) { 2254 codegen_->ClearSpillSlotsFromLoopPhisInStackMap(info->GetSuspendCheck()); 2255 GenerateSuspendCheck(info->GetSuspendCheck(), successor); 2256 return; 2257 } 2258 if (block->IsEntryBlock() && (previous != nullptr) && previous->IsSuspendCheck()) { 2259 GenerateSuspendCheck(previous->AsSuspendCheck(), nullptr); 2260 } 2261 if (!codegen_->GoesToNextBlock(block, successor)) { 2262 __ B(codegen_->GetLabelOf(successor)); 2263 } 2264} 2265 2266void LocationsBuilderARM64::VisitGoto(HGoto* got) { 2267 got->SetLocations(nullptr); 2268} 2269 2270void InstructionCodeGeneratorARM64::VisitGoto(HGoto* got) { 2271 HandleGoto(got, got->GetSuccessor()); 2272} 2273 2274void LocationsBuilderARM64::VisitTryBoundary(HTryBoundary* try_boundary) { 2275 try_boundary->SetLocations(nullptr); 2276} 2277 2278void InstructionCodeGeneratorARM64::VisitTryBoundary(HTryBoundary* try_boundary) { 2279 HBasicBlock* successor = try_boundary->GetNormalFlowSuccessor(); 2280 if (!successor->IsExitBlock()) { 2281 HandleGoto(try_boundary, successor); 2282 } 2283} 2284 2285void InstructionCodeGeneratorARM64::GenerateTestAndBranch(HInstruction* instruction, 2286 vixl::Label* true_target, 2287 vixl::Label* false_target, 2288 vixl::Label* always_true_target) { 2289 HInstruction* cond = instruction->InputAt(0); 2290 HCondition* condition = cond->AsCondition(); 2291 2292 if (cond->IsIntConstant()) { 2293 int32_t cond_value = cond->AsIntConstant()->GetValue(); 2294 if (cond_value == 1) { 2295 if (always_true_target != nullptr) { 2296 __ B(always_true_target); 2297 } 2298 return; 2299 } else { 2300 DCHECK_EQ(cond_value, 0); 2301 } 2302 } else if (!cond->IsCondition() || condition->NeedsMaterialization()) { 2303 // The condition instruction has been materialized, compare the output to 0. 2304 Location cond_val = instruction->GetLocations()->InAt(0); 2305 DCHECK(cond_val.IsRegister()); 2306 __ Cbnz(InputRegisterAt(instruction, 0), true_target); 2307 } else { 2308 // The condition instruction has not been materialized, use its inputs as 2309 // the comparison and its condition as the branch condition. 2310 Primitive::Type type = 2311 cond->IsCondition() ? cond->InputAt(0)->GetType() : Primitive::kPrimInt; 2312 2313 if (Primitive::IsFloatingPointType(type)) { 2314 // FP compares don't like null false_targets. 2315 if (false_target == nullptr) { 2316 false_target = codegen_->GetLabelOf(instruction->AsIf()->IfFalseSuccessor()); 2317 } 2318 FPRegister lhs = InputFPRegisterAt(condition, 0); 2319 if (condition->GetLocations()->InAt(1).IsConstant()) { 2320 DCHECK(IsFloatingPointZeroConstant(condition->GetLocations()->InAt(1).GetConstant())); 2321 // 0.0 is the only immediate that can be encoded directly in an FCMP instruction. 2322 __ Fcmp(lhs, 0.0); 2323 } else { 2324 __ Fcmp(lhs, InputFPRegisterAt(condition, 1)); 2325 } 2326 if (condition->IsFPConditionTrueIfNaN()) { 2327 __ B(vs, true_target); // VS for unordered. 2328 } else if (condition->IsFPConditionFalseIfNaN()) { 2329 __ B(vs, false_target); // VS for unordered. 2330 } 2331 __ B(ARM64Condition(condition->GetCondition()), true_target); 2332 } else { 2333 // Integer cases. 2334 Register lhs = InputRegisterAt(condition, 0); 2335 Operand rhs = InputOperandAt(condition, 1); 2336 Condition arm64_cond = ARM64Condition(condition->GetCondition()); 2337 if ((arm64_cond != gt && arm64_cond != le) && rhs.IsImmediate() && (rhs.immediate() == 0)) { 2338 switch (arm64_cond) { 2339 case eq: 2340 __ Cbz(lhs, true_target); 2341 break; 2342 case ne: 2343 __ Cbnz(lhs, true_target); 2344 break; 2345 case lt: 2346 // Test the sign bit and branch accordingly. 2347 __ Tbnz(lhs, (lhs.IsX() ? kXRegSize : kWRegSize) - 1, true_target); 2348 break; 2349 case ge: 2350 // Test the sign bit and branch accordingly. 2351 __ Tbz(lhs, (lhs.IsX() ? kXRegSize : kWRegSize) - 1, true_target); 2352 break; 2353 default: 2354 // Without the `static_cast` the compiler throws an error for 2355 // `-Werror=sign-promo`. 2356 LOG(FATAL) << "Unexpected condition: " << static_cast<int>(arm64_cond); 2357 } 2358 } else { 2359 __ Cmp(lhs, rhs); 2360 __ B(arm64_cond, true_target); 2361 } 2362 } 2363 } 2364 if (false_target != nullptr) { 2365 __ B(false_target); 2366 } 2367} 2368 2369void LocationsBuilderARM64::VisitIf(HIf* if_instr) { 2370 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(if_instr); 2371 HInstruction* cond = if_instr->InputAt(0); 2372 if (!cond->IsCondition() || cond->AsCondition()->NeedsMaterialization()) { 2373 locations->SetInAt(0, Location::RequiresRegister()); 2374 } 2375} 2376 2377void InstructionCodeGeneratorARM64::VisitIf(HIf* if_instr) { 2378 vixl::Label* true_target = codegen_->GetLabelOf(if_instr->IfTrueSuccessor()); 2379 vixl::Label* false_target = codegen_->GetLabelOf(if_instr->IfFalseSuccessor()); 2380 vixl::Label* always_true_target = true_target; 2381 if (codegen_->GoesToNextBlock(if_instr->GetBlock(), 2382 if_instr->IfTrueSuccessor())) { 2383 always_true_target = nullptr; 2384 } 2385 if (codegen_->GoesToNextBlock(if_instr->GetBlock(), 2386 if_instr->IfFalseSuccessor())) { 2387 false_target = nullptr; 2388 } 2389 GenerateTestAndBranch(if_instr, true_target, false_target, always_true_target); 2390} 2391 2392void LocationsBuilderARM64::VisitDeoptimize(HDeoptimize* deoptimize) { 2393 LocationSummary* locations = new (GetGraph()->GetArena()) 2394 LocationSummary(deoptimize, LocationSummary::kCallOnSlowPath); 2395 HInstruction* cond = deoptimize->InputAt(0); 2396 if (!cond->IsCondition() || cond->AsCondition()->NeedsMaterialization()) { 2397 locations->SetInAt(0, Location::RequiresRegister()); 2398 } 2399} 2400 2401void InstructionCodeGeneratorARM64::VisitDeoptimize(HDeoptimize* deoptimize) { 2402 SlowPathCodeARM64* slow_path = new (GetGraph()->GetArena()) 2403 DeoptimizationSlowPathARM64(deoptimize); 2404 codegen_->AddSlowPath(slow_path); 2405 vixl::Label* slow_path_entry = slow_path->GetEntryLabel(); 2406 GenerateTestAndBranch(deoptimize, slow_path_entry, nullptr, slow_path_entry); 2407} 2408 2409void LocationsBuilderARM64::VisitInstanceFieldGet(HInstanceFieldGet* instruction) { 2410 HandleFieldGet(instruction); 2411} 2412 2413void InstructionCodeGeneratorARM64::VisitInstanceFieldGet(HInstanceFieldGet* instruction) { 2414 HandleFieldGet(instruction, instruction->GetFieldInfo()); 2415} 2416 2417void LocationsBuilderARM64::VisitInstanceFieldSet(HInstanceFieldSet* instruction) { 2418 HandleFieldSet(instruction); 2419} 2420 2421void InstructionCodeGeneratorARM64::VisitInstanceFieldSet(HInstanceFieldSet* instruction) { 2422 HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull()); 2423} 2424 2425void LocationsBuilderARM64::VisitInstanceOf(HInstanceOf* instruction) { 2426 LocationSummary::CallKind call_kind = LocationSummary::kNoCall; 2427 switch (instruction->GetTypeCheckKind()) { 2428 case TypeCheckKind::kExactCheck: 2429 case TypeCheckKind::kAbstractClassCheck: 2430 case TypeCheckKind::kClassHierarchyCheck: 2431 case TypeCheckKind::kArrayObjectCheck: 2432 call_kind = LocationSummary::kNoCall; 2433 break; 2434 case TypeCheckKind::kUnresolvedCheck: 2435 case TypeCheckKind::kInterfaceCheck: 2436 call_kind = LocationSummary::kCall; 2437 break; 2438 case TypeCheckKind::kArrayCheck: 2439 call_kind = LocationSummary::kCallOnSlowPath; 2440 break; 2441 } 2442 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind); 2443 if (call_kind != LocationSummary::kCall) { 2444 locations->SetInAt(0, Location::RequiresRegister()); 2445 locations->SetInAt(1, Location::RequiresRegister()); 2446 // The out register is used as a temporary, so it overlaps with the inputs. 2447 // Note that TypeCheckSlowPathARM64 uses this register too. 2448 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); 2449 } else { 2450 InvokeRuntimeCallingConvention calling_convention; 2451 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(0))); 2452 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(1))); 2453 locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimInt)); 2454 } 2455} 2456 2457void InstructionCodeGeneratorARM64::VisitInstanceOf(HInstanceOf* instruction) { 2458 LocationSummary* locations = instruction->GetLocations(); 2459 Register obj = InputRegisterAt(instruction, 0); 2460 Register cls = InputRegisterAt(instruction, 1); 2461 Register out = OutputRegister(instruction); 2462 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value(); 2463 uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value(); 2464 uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value(); 2465 uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value(); 2466 2467 vixl::Label done, zero; 2468 SlowPathCodeARM64* slow_path = nullptr; 2469 2470 // Return 0 if `obj` is null. 2471 // Avoid null check if we know `obj` is not null. 2472 if (instruction->MustDoNullCheck()) { 2473 __ Cbz(obj, &zero); 2474 } 2475 2476 // In case of an interface/unresolved check, we put the object class into the object register. 2477 // This is safe, as the register is caller-save, and the object must be in another 2478 // register if it survives the runtime call. 2479 Register target = (instruction->GetTypeCheckKind() == TypeCheckKind::kInterfaceCheck) || 2480 (instruction->GetTypeCheckKind() == TypeCheckKind::kUnresolvedCheck) 2481 ? obj 2482 : out; 2483 __ Ldr(target, HeapOperand(obj.W(), class_offset)); 2484 GetAssembler()->MaybeUnpoisonHeapReference(target); 2485 2486 switch (instruction->GetTypeCheckKind()) { 2487 case TypeCheckKind::kExactCheck: { 2488 __ Cmp(out, cls); 2489 __ Cset(out, eq); 2490 if (zero.IsLinked()) { 2491 __ B(&done); 2492 } 2493 break; 2494 } 2495 case TypeCheckKind::kAbstractClassCheck: { 2496 // If the class is abstract, we eagerly fetch the super class of the 2497 // object to avoid doing a comparison we know will fail. 2498 vixl::Label loop, success; 2499 __ Bind(&loop); 2500 __ Ldr(out, HeapOperand(out, super_offset)); 2501 GetAssembler()->MaybeUnpoisonHeapReference(out); 2502 // If `out` is null, we use it for the result, and jump to `done`. 2503 __ Cbz(out, &done); 2504 __ Cmp(out, cls); 2505 __ B(ne, &loop); 2506 __ Mov(out, 1); 2507 if (zero.IsLinked()) { 2508 __ B(&done); 2509 } 2510 break; 2511 } 2512 case TypeCheckKind::kClassHierarchyCheck: { 2513 // Walk over the class hierarchy to find a match. 2514 vixl::Label loop, success; 2515 __ Bind(&loop); 2516 __ Cmp(out, cls); 2517 __ B(eq, &success); 2518 __ Ldr(out, HeapOperand(out, super_offset)); 2519 GetAssembler()->MaybeUnpoisonHeapReference(out); 2520 __ Cbnz(out, &loop); 2521 // If `out` is null, we use it for the result, and jump to `done`. 2522 __ B(&done); 2523 __ Bind(&success); 2524 __ Mov(out, 1); 2525 if (zero.IsLinked()) { 2526 __ B(&done); 2527 } 2528 break; 2529 } 2530 case TypeCheckKind::kArrayObjectCheck: { 2531 // Do an exact check. 2532 vixl::Label exact_check; 2533 __ Cmp(out, cls); 2534 __ B(eq, &exact_check); 2535 // Otherwise, we need to check that the object's class is a non primitive array. 2536 __ Ldr(out, HeapOperand(out, component_offset)); 2537 GetAssembler()->MaybeUnpoisonHeapReference(out); 2538 // If `out` is null, we use it for the result, and jump to `done`. 2539 __ Cbz(out, &done); 2540 __ Ldrh(out, HeapOperand(out, primitive_offset)); 2541 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot"); 2542 __ Cbnz(out, &zero); 2543 __ Bind(&exact_check); 2544 __ Mov(out, 1); 2545 __ B(&done); 2546 break; 2547 } 2548 case TypeCheckKind::kArrayCheck: { 2549 __ Cmp(out, cls); 2550 DCHECK(locations->OnlyCallsOnSlowPath()); 2551 slow_path = new (GetGraph()->GetArena()) TypeCheckSlowPathARM64( 2552 instruction, /* is_fatal */ false); 2553 codegen_->AddSlowPath(slow_path); 2554 __ B(ne, slow_path->GetEntryLabel()); 2555 __ Mov(out, 1); 2556 if (zero.IsLinked()) { 2557 __ B(&done); 2558 } 2559 break; 2560 } 2561 case TypeCheckKind::kUnresolvedCheck: 2562 case TypeCheckKind::kInterfaceCheck: 2563 default: { 2564 codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pInstanceofNonTrivial), 2565 instruction, 2566 instruction->GetDexPc(), 2567 nullptr); 2568 if (zero.IsLinked()) { 2569 __ B(&done); 2570 } 2571 break; 2572 } 2573 } 2574 2575 if (zero.IsLinked()) { 2576 __ Bind(&zero); 2577 __ Mov(out, 0); 2578 } 2579 2580 if (done.IsLinked()) { 2581 __ Bind(&done); 2582 } 2583 2584 if (slow_path != nullptr) { 2585 __ Bind(slow_path->GetExitLabel()); 2586 } 2587} 2588 2589void LocationsBuilderARM64::VisitCheckCast(HCheckCast* instruction) { 2590 LocationSummary::CallKind call_kind = LocationSummary::kNoCall; 2591 bool throws_into_catch = instruction->CanThrowIntoCatchBlock(); 2592 2593 switch (instruction->GetTypeCheckKind()) { 2594 case TypeCheckKind::kExactCheck: 2595 case TypeCheckKind::kAbstractClassCheck: 2596 case TypeCheckKind::kClassHierarchyCheck: 2597 case TypeCheckKind::kArrayObjectCheck: 2598 call_kind = throws_into_catch 2599 ? LocationSummary::kCallOnSlowPath 2600 : LocationSummary::kNoCall; 2601 break; 2602 case TypeCheckKind::kUnresolvedCheck: 2603 case TypeCheckKind::kInterfaceCheck: 2604 call_kind = LocationSummary::kCall; 2605 break; 2606 case TypeCheckKind::kArrayCheck: 2607 call_kind = LocationSummary::kCallOnSlowPath; 2608 break; 2609 } 2610 2611 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary( 2612 instruction, call_kind); 2613 if (call_kind != LocationSummary::kCall) { 2614 locations->SetInAt(0, Location::RequiresRegister()); 2615 locations->SetInAt(1, Location::RequiresRegister()); 2616 // Note that TypeCheckSlowPathARM64 uses this register too. 2617 locations->AddTemp(Location::RequiresRegister()); 2618 } else { 2619 InvokeRuntimeCallingConvention calling_convention; 2620 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(0))); 2621 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(1))); 2622 } 2623} 2624 2625void InstructionCodeGeneratorARM64::VisitCheckCast(HCheckCast* instruction) { 2626 LocationSummary* locations = instruction->GetLocations(); 2627 Register obj = InputRegisterAt(instruction, 0); 2628 Register cls = InputRegisterAt(instruction, 1); 2629 Register temp; 2630 if (!locations->WillCall()) { 2631 temp = WRegisterFrom(instruction->GetLocations()->GetTemp(0)); 2632 } 2633 2634 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value(); 2635 uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value(); 2636 uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value(); 2637 uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value(); 2638 SlowPathCodeARM64* slow_path = nullptr; 2639 2640 if (!locations->WillCall()) { 2641 slow_path = new (GetGraph()->GetArena()) TypeCheckSlowPathARM64( 2642 instruction, !locations->CanCall()); 2643 codegen_->AddSlowPath(slow_path); 2644 } 2645 2646 vixl::Label done; 2647 // Avoid null check if we know obj is not null. 2648 if (instruction->MustDoNullCheck()) { 2649 __ Cbz(obj, &done); 2650 } 2651 2652 if (locations->WillCall()) { 2653 __ Ldr(obj, HeapOperand(obj, class_offset)); 2654 GetAssembler()->MaybeUnpoisonHeapReference(obj); 2655 } else { 2656 __ Ldr(temp, HeapOperand(obj, class_offset)); 2657 GetAssembler()->MaybeUnpoisonHeapReference(temp); 2658 } 2659 2660 switch (instruction->GetTypeCheckKind()) { 2661 case TypeCheckKind::kExactCheck: 2662 case TypeCheckKind::kArrayCheck: { 2663 __ Cmp(temp, cls); 2664 // Jump to slow path for throwing the exception or doing a 2665 // more involved array check. 2666 __ B(ne, slow_path->GetEntryLabel()); 2667 break; 2668 } 2669 case TypeCheckKind::kAbstractClassCheck: { 2670 // If the class is abstract, we eagerly fetch the super class of the 2671 // object to avoid doing a comparison we know will fail. 2672 vixl::Label loop; 2673 __ Bind(&loop); 2674 __ Ldr(temp, HeapOperand(temp, super_offset)); 2675 GetAssembler()->MaybeUnpoisonHeapReference(temp); 2676 // Jump to the slow path to throw the exception. 2677 __ Cbz(temp, slow_path->GetEntryLabel()); 2678 __ Cmp(temp, cls); 2679 __ B(ne, &loop); 2680 break; 2681 } 2682 case TypeCheckKind::kClassHierarchyCheck: { 2683 // Walk over the class hierarchy to find a match. 2684 vixl::Label loop; 2685 __ Bind(&loop); 2686 __ Cmp(temp, cls); 2687 __ B(eq, &done); 2688 __ Ldr(temp, HeapOperand(temp, super_offset)); 2689 GetAssembler()->MaybeUnpoisonHeapReference(temp); 2690 __ Cbnz(temp, &loop); 2691 // Jump to the slow path to throw the exception. 2692 __ B(slow_path->GetEntryLabel()); 2693 break; 2694 } 2695 case TypeCheckKind::kArrayObjectCheck: { 2696 // Do an exact check. 2697 __ Cmp(temp, cls); 2698 __ B(eq, &done); 2699 // Otherwise, we need to check that the object's class is a non primitive array. 2700 __ Ldr(temp, HeapOperand(temp, component_offset)); 2701 GetAssembler()->MaybeUnpoisonHeapReference(temp); 2702 __ Cbz(temp, slow_path->GetEntryLabel()); 2703 __ Ldrh(temp, HeapOperand(temp, primitive_offset)); 2704 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot"); 2705 __ Cbnz(temp, slow_path->GetEntryLabel()); 2706 break; 2707 } 2708 case TypeCheckKind::kUnresolvedCheck: 2709 case TypeCheckKind::kInterfaceCheck: 2710 default: 2711 codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pCheckCast), 2712 instruction, 2713 instruction->GetDexPc(), 2714 nullptr); 2715 break; 2716 } 2717 __ Bind(&done); 2718 2719 if (slow_path != nullptr) { 2720 __ Bind(slow_path->GetExitLabel()); 2721 } 2722} 2723 2724void LocationsBuilderARM64::VisitIntConstant(HIntConstant* constant) { 2725 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(constant); 2726 locations->SetOut(Location::ConstantLocation(constant)); 2727} 2728 2729void InstructionCodeGeneratorARM64::VisitIntConstant(HIntConstant* constant ATTRIBUTE_UNUSED) { 2730 // Will be generated at use site. 2731} 2732 2733void LocationsBuilderARM64::VisitNullConstant(HNullConstant* constant) { 2734 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(constant); 2735 locations->SetOut(Location::ConstantLocation(constant)); 2736} 2737 2738void InstructionCodeGeneratorARM64::VisitNullConstant(HNullConstant* constant ATTRIBUTE_UNUSED) { 2739 // Will be generated at use site. 2740} 2741 2742void LocationsBuilderARM64::VisitInvokeUnresolved(HInvokeUnresolved* invoke) { 2743 // The trampoline uses the same calling convention as dex calling conventions, 2744 // except instead of loading arg0/r0 with the target Method*, arg0/r0 will contain 2745 // the method_idx. 2746 HandleInvoke(invoke); 2747} 2748 2749void InstructionCodeGeneratorARM64::VisitInvokeUnresolved(HInvokeUnresolved* invoke) { 2750 codegen_->GenerateInvokeUnresolvedRuntimeCall(invoke); 2751} 2752 2753void LocationsBuilderARM64::HandleInvoke(HInvoke* invoke) { 2754 InvokeDexCallingConventionVisitorARM64 calling_convention_visitor; 2755 CodeGenerator::CreateCommonInvokeLocationSummary(invoke, &calling_convention_visitor); 2756} 2757 2758void LocationsBuilderARM64::VisitInvokeInterface(HInvokeInterface* invoke) { 2759 HandleInvoke(invoke); 2760} 2761 2762void InstructionCodeGeneratorARM64::VisitInvokeInterface(HInvokeInterface* invoke) { 2763 // TODO: b/18116999, our IMTs can miss an IncompatibleClassChangeError. 2764 Register temp = XRegisterFrom(invoke->GetLocations()->GetTemp(0)); 2765 uint32_t method_offset = mirror::Class::EmbeddedImTableEntryOffset( 2766 invoke->GetImtIndex() % mirror::Class::kImtSize, kArm64PointerSize).Uint32Value(); 2767 Location receiver = invoke->GetLocations()->InAt(0); 2768 Offset class_offset = mirror::Object::ClassOffset(); 2769 Offset entry_point = ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArm64WordSize); 2770 2771 // The register ip1 is required to be used for the hidden argument in 2772 // art_quick_imt_conflict_trampoline, so prevent VIXL from using it. 2773 MacroAssembler* masm = GetVIXLAssembler(); 2774 UseScratchRegisterScope scratch_scope(masm); 2775 BlockPoolsScope block_pools(masm); 2776 scratch_scope.Exclude(ip1); 2777 __ Mov(ip1, invoke->GetDexMethodIndex()); 2778 2779 // temp = object->GetClass(); 2780 if (receiver.IsStackSlot()) { 2781 __ Ldr(temp.W(), StackOperandFrom(receiver)); 2782 __ Ldr(temp.W(), HeapOperand(temp.W(), class_offset)); 2783 } else { 2784 __ Ldr(temp.W(), HeapOperandFrom(receiver, class_offset)); 2785 } 2786 codegen_->MaybeRecordImplicitNullCheck(invoke); 2787 GetAssembler()->MaybeUnpoisonHeapReference(temp.W()); 2788 // temp = temp->GetImtEntryAt(method_offset); 2789 __ Ldr(temp, MemOperand(temp, method_offset)); 2790 // lr = temp->GetEntryPoint(); 2791 __ Ldr(lr, MemOperand(temp, entry_point.Int32Value())); 2792 // lr(); 2793 __ Blr(lr); 2794 DCHECK(!codegen_->IsLeafMethod()); 2795 codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); 2796} 2797 2798void LocationsBuilderARM64::VisitInvokeVirtual(HInvokeVirtual* invoke) { 2799 IntrinsicLocationsBuilderARM64 intrinsic(GetGraph()->GetArena()); 2800 if (intrinsic.TryDispatch(invoke)) { 2801 return; 2802 } 2803 2804 HandleInvoke(invoke); 2805} 2806 2807void LocationsBuilderARM64::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) { 2808 // When we do not run baseline, explicit clinit checks triggered by static 2809 // invokes must have been pruned by art::PrepareForRegisterAllocation. 2810 DCHECK(codegen_->IsBaseline() || !invoke->IsStaticWithExplicitClinitCheck()); 2811 2812 IntrinsicLocationsBuilderARM64 intrinsic(GetGraph()->GetArena()); 2813 if (intrinsic.TryDispatch(invoke)) { 2814 return; 2815 } 2816 2817 HandleInvoke(invoke); 2818} 2819 2820static bool TryGenerateIntrinsicCode(HInvoke* invoke, CodeGeneratorARM64* codegen) { 2821 if (invoke->GetLocations()->Intrinsified()) { 2822 IntrinsicCodeGeneratorARM64 intrinsic(codegen); 2823 intrinsic.Dispatch(invoke); 2824 return true; 2825 } 2826 return false; 2827} 2828 2829HInvokeStaticOrDirect::DispatchInfo CodeGeneratorARM64::GetSupportedInvokeStaticOrDirectDispatch( 2830 const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info, 2831 MethodReference target_method ATTRIBUTE_UNUSED) { 2832 // On arm64 we support all dispatch types. 2833 return desired_dispatch_info; 2834} 2835 2836void CodeGeneratorARM64::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp) { 2837 // For better instruction scheduling we load the direct code pointer before the method pointer. 2838 bool direct_code_loaded = false; 2839 switch (invoke->GetCodePtrLocation()) { 2840 case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup: 2841 // LR = code address from literal pool with link-time patch. 2842 __ Ldr(lr, DeduplicateMethodCodeLiteral(invoke->GetTargetMethod())); 2843 direct_code_loaded = true; 2844 break; 2845 case HInvokeStaticOrDirect::CodePtrLocation::kCallDirect: 2846 // LR = invoke->GetDirectCodePtr(); 2847 __ Ldr(lr, DeduplicateUint64Literal(invoke->GetDirectCodePtr())); 2848 direct_code_loaded = true; 2849 break; 2850 default: 2851 break; 2852 } 2853 2854 // Make sure that ArtMethod* is passed in kArtMethodRegister as per the calling convention. 2855 Location callee_method = temp; // For all kinds except kRecursive, callee will be in temp. 2856 switch (invoke->GetMethodLoadKind()) { 2857 case HInvokeStaticOrDirect::MethodLoadKind::kStringInit: 2858 // temp = thread->string_init_entrypoint 2859 __ Ldr(XRegisterFrom(temp), MemOperand(tr, invoke->GetStringInitOffset())); 2860 break; 2861 case HInvokeStaticOrDirect::MethodLoadKind::kRecursive: 2862 callee_method = invoke->GetLocations()->InAt(invoke->GetCurrentMethodInputIndex()); 2863 break; 2864 case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress: 2865 // Load method address from literal pool. 2866 __ Ldr(XRegisterFrom(temp), DeduplicateUint64Literal(invoke->GetMethodAddress())); 2867 break; 2868 case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup: 2869 // Load method address from literal pool with a link-time patch. 2870 __ Ldr(XRegisterFrom(temp), 2871 DeduplicateMethodAddressLiteral(invoke->GetTargetMethod())); 2872 break; 2873 case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative: { 2874 // Add ADRP with its PC-relative DexCache access patch. 2875 pc_relative_dex_cache_patches_.emplace_back(*invoke->GetTargetMethod().dex_file, 2876 invoke->GetDexCacheArrayOffset()); 2877 vixl::Label* pc_insn_label = &pc_relative_dex_cache_patches_.back().label; 2878 { 2879 vixl::SingleEmissionCheckScope guard(GetVIXLAssembler()); 2880 __ Bind(pc_insn_label); 2881 __ adrp(XRegisterFrom(temp), 0); 2882 } 2883 pc_relative_dex_cache_patches_.back().pc_insn_label = pc_insn_label; 2884 // Add LDR with its PC-relative DexCache access patch. 2885 pc_relative_dex_cache_patches_.emplace_back(*invoke->GetTargetMethod().dex_file, 2886 invoke->GetDexCacheArrayOffset()); 2887 { 2888 vixl::SingleEmissionCheckScope guard(GetVIXLAssembler()); 2889 __ Bind(&pc_relative_dex_cache_patches_.back().label); 2890 __ ldr(XRegisterFrom(temp), MemOperand(XRegisterFrom(temp), 0)); 2891 pc_relative_dex_cache_patches_.back().pc_insn_label = pc_insn_label; 2892 } 2893 break; 2894 } 2895 case HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod: { 2896 Location current_method = invoke->GetLocations()->InAt(invoke->GetCurrentMethodInputIndex()); 2897 Register reg = XRegisterFrom(temp); 2898 Register method_reg; 2899 if (current_method.IsRegister()) { 2900 method_reg = XRegisterFrom(current_method); 2901 } else { 2902 DCHECK(invoke->GetLocations()->Intrinsified()); 2903 DCHECK(!current_method.IsValid()); 2904 method_reg = reg; 2905 __ Ldr(reg.X(), MemOperand(sp, kCurrentMethodStackOffset)); 2906 } 2907 2908 // temp = current_method->dex_cache_resolved_methods_; 2909 __ Ldr(reg.X(), 2910 MemOperand(method_reg.X(), 2911 ArtMethod::DexCacheResolvedMethodsOffset(kArm64WordSize).Int32Value())); 2912 // temp = temp[index_in_cache]; 2913 uint32_t index_in_cache = invoke->GetTargetMethod().dex_method_index; 2914 __ Ldr(reg.X(), MemOperand(reg.X(), GetCachePointerOffset(index_in_cache))); 2915 break; 2916 } 2917 } 2918 2919 switch (invoke->GetCodePtrLocation()) { 2920 case HInvokeStaticOrDirect::CodePtrLocation::kCallSelf: 2921 __ Bl(&frame_entry_label_); 2922 break; 2923 case HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative: { 2924 relative_call_patches_.emplace_back(invoke->GetTargetMethod()); 2925 vixl::Label* label = &relative_call_patches_.back().label; 2926 vixl::SingleEmissionCheckScope guard(GetVIXLAssembler()); 2927 __ Bind(label); 2928 __ bl(0); // Branch and link to itself. This will be overriden at link time. 2929 break; 2930 } 2931 case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup: 2932 case HInvokeStaticOrDirect::CodePtrLocation::kCallDirect: 2933 // LR prepared above for better instruction scheduling. 2934 DCHECK(direct_code_loaded); 2935 // lr() 2936 __ Blr(lr); 2937 break; 2938 case HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod: 2939 // LR = callee_method->entry_point_from_quick_compiled_code_; 2940 __ Ldr(lr, MemOperand( 2941 XRegisterFrom(callee_method), 2942 ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArm64WordSize).Int32Value())); 2943 // lr() 2944 __ Blr(lr); 2945 break; 2946 } 2947 2948 DCHECK(!IsLeafMethod()); 2949} 2950 2951void CodeGeneratorARM64::GenerateVirtualCall(HInvokeVirtual* invoke, Location temp_in) { 2952 LocationSummary* locations = invoke->GetLocations(); 2953 Location receiver = locations->InAt(0); 2954 Register temp = XRegisterFrom(temp_in); 2955 size_t method_offset = mirror::Class::EmbeddedVTableEntryOffset( 2956 invoke->GetVTableIndex(), kArm64PointerSize).SizeValue(); 2957 Offset class_offset = mirror::Object::ClassOffset(); 2958 Offset entry_point = ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArm64WordSize); 2959 2960 BlockPoolsScope block_pools(GetVIXLAssembler()); 2961 2962 DCHECK(receiver.IsRegister()); 2963 __ Ldr(temp.W(), HeapOperandFrom(receiver, class_offset)); 2964 MaybeRecordImplicitNullCheck(invoke); 2965 GetAssembler()->MaybeUnpoisonHeapReference(temp.W()); 2966 // temp = temp->GetMethodAt(method_offset); 2967 __ Ldr(temp, MemOperand(temp, method_offset)); 2968 // lr = temp->GetEntryPoint(); 2969 __ Ldr(lr, MemOperand(temp, entry_point.SizeValue())); 2970 // lr(); 2971 __ Blr(lr); 2972} 2973 2974void CodeGeneratorARM64::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) { 2975 DCHECK(linker_patches->empty()); 2976 size_t size = 2977 method_patches_.size() + 2978 call_patches_.size() + 2979 relative_call_patches_.size() + 2980 pc_relative_dex_cache_patches_.size(); 2981 linker_patches->reserve(size); 2982 for (const auto& entry : method_patches_) { 2983 const MethodReference& target_method = entry.first; 2984 vixl::Literal<uint64_t>* literal = entry.second; 2985 linker_patches->push_back(LinkerPatch::MethodPatch(literal->offset(), 2986 target_method.dex_file, 2987 target_method.dex_method_index)); 2988 } 2989 for (const auto& entry : call_patches_) { 2990 const MethodReference& target_method = entry.first; 2991 vixl::Literal<uint64_t>* literal = entry.second; 2992 linker_patches->push_back(LinkerPatch::CodePatch(literal->offset(), 2993 target_method.dex_file, 2994 target_method.dex_method_index)); 2995 } 2996 for (const MethodPatchInfo<vixl::Label>& info : relative_call_patches_) { 2997 linker_patches->push_back(LinkerPatch::RelativeCodePatch(info.label.location(), 2998 info.target_method.dex_file, 2999 info.target_method.dex_method_index)); 3000 } 3001 for (const PcRelativeDexCacheAccessInfo& info : pc_relative_dex_cache_patches_) { 3002 linker_patches->push_back(LinkerPatch::DexCacheArrayPatch(info.label.location(), 3003 &info.target_dex_file, 3004 info.pc_insn_label->location(), 3005 info.element_offset)); 3006 } 3007} 3008 3009vixl::Literal<uint64_t>* CodeGeneratorARM64::DeduplicateUint64Literal(uint64_t value) { 3010 // Look up the literal for value. 3011 auto lb = uint64_literals_.lower_bound(value); 3012 if (lb != uint64_literals_.end() && !uint64_literals_.key_comp()(value, lb->first)) { 3013 return lb->second; 3014 } 3015 // We don't have a literal for this value, insert a new one. 3016 vixl::Literal<uint64_t>* literal = __ CreateLiteralDestroyedWithPool<uint64_t>(value); 3017 uint64_literals_.PutBefore(lb, value, literal); 3018 return literal; 3019} 3020 3021vixl::Literal<uint64_t>* CodeGeneratorARM64::DeduplicateMethodLiteral( 3022 MethodReference target_method, 3023 MethodToLiteralMap* map) { 3024 // Look up the literal for target_method. 3025 auto lb = map->lower_bound(target_method); 3026 if (lb != map->end() && !map->key_comp()(target_method, lb->first)) { 3027 return lb->second; 3028 } 3029 // We don't have a literal for this method yet, insert a new one. 3030 vixl::Literal<uint64_t>* literal = __ CreateLiteralDestroyedWithPool<uint64_t>(0u); 3031 map->PutBefore(lb, target_method, literal); 3032 return literal; 3033} 3034 3035vixl::Literal<uint64_t>* CodeGeneratorARM64::DeduplicateMethodAddressLiteral( 3036 MethodReference target_method) { 3037 return DeduplicateMethodLiteral(target_method, &method_patches_); 3038} 3039 3040vixl::Literal<uint64_t>* CodeGeneratorARM64::DeduplicateMethodCodeLiteral( 3041 MethodReference target_method) { 3042 return DeduplicateMethodLiteral(target_method, &call_patches_); 3043} 3044 3045 3046void InstructionCodeGeneratorARM64::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) { 3047 // When we do not run baseline, explicit clinit checks triggered by static 3048 // invokes must have been pruned by art::PrepareForRegisterAllocation. 3049 DCHECK(codegen_->IsBaseline() || !invoke->IsStaticWithExplicitClinitCheck()); 3050 3051 if (TryGenerateIntrinsicCode(invoke, codegen_)) { 3052 return; 3053 } 3054 3055 BlockPoolsScope block_pools(GetVIXLAssembler()); 3056 LocationSummary* locations = invoke->GetLocations(); 3057 codegen_->GenerateStaticOrDirectCall( 3058 invoke, locations->HasTemps() ? locations->GetTemp(0) : Location::NoLocation()); 3059 codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); 3060} 3061 3062void InstructionCodeGeneratorARM64::VisitInvokeVirtual(HInvokeVirtual* invoke) { 3063 if (TryGenerateIntrinsicCode(invoke, codegen_)) { 3064 return; 3065 } 3066 3067 codegen_->GenerateVirtualCall(invoke, invoke->GetLocations()->GetTemp(0)); 3068 DCHECK(!codegen_->IsLeafMethod()); 3069 codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); 3070} 3071 3072void LocationsBuilderARM64::VisitLoadClass(HLoadClass* cls) { 3073 InvokeRuntimeCallingConvention calling_convention; 3074 CodeGenerator::CreateLoadClassLocationSummary( 3075 cls, 3076 LocationFrom(calling_convention.GetRegisterAt(0)), 3077 LocationFrom(vixl::x0)); 3078} 3079 3080void InstructionCodeGeneratorARM64::VisitLoadClass(HLoadClass* cls) { 3081 if (cls->NeedsAccessCheck()) { 3082 codegen_->MoveConstant(cls->GetLocations()->GetTemp(0), cls->GetTypeIndex()); 3083 codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pInitializeTypeAndVerifyAccess), 3084 cls, 3085 cls->GetDexPc(), 3086 nullptr); 3087 return; 3088 } 3089 3090 Register out = OutputRegister(cls); 3091 Register current_method = InputRegisterAt(cls, 0); 3092 if (cls->IsReferrersClass()) { 3093 DCHECK(!cls->CanCallRuntime()); 3094 DCHECK(!cls->MustGenerateClinitCheck()); 3095 __ Ldr(out, MemOperand(current_method, ArtMethod::DeclaringClassOffset().Int32Value())); 3096 } else { 3097 DCHECK(cls->CanCallRuntime()); 3098 MemberOffset resolved_types_offset = ArtMethod::DexCacheResolvedTypesOffset(kArm64PointerSize); 3099 __ Ldr(out.X(), MemOperand(current_method, resolved_types_offset.Int32Value())); 3100 __ Ldr(out, MemOperand(out.X(), CodeGenerator::GetCacheOffset(cls->GetTypeIndex()))); 3101 // TODO: We will need a read barrier here. 3102 3103 SlowPathCodeARM64* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathARM64( 3104 cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck()); 3105 codegen_->AddSlowPath(slow_path); 3106 __ Cbz(out, slow_path->GetEntryLabel()); 3107 if (cls->MustGenerateClinitCheck()) { 3108 GenerateClassInitializationCheck(slow_path, out); 3109 } else { 3110 __ Bind(slow_path->GetExitLabel()); 3111 } 3112 } 3113} 3114 3115static MemOperand GetExceptionTlsAddress() { 3116 return MemOperand(tr, Thread::ExceptionOffset<kArm64WordSize>().Int32Value()); 3117} 3118 3119void LocationsBuilderARM64::VisitLoadException(HLoadException* load) { 3120 LocationSummary* locations = 3121 new (GetGraph()->GetArena()) LocationSummary(load, LocationSummary::kNoCall); 3122 locations->SetOut(Location::RequiresRegister()); 3123} 3124 3125void InstructionCodeGeneratorARM64::VisitLoadException(HLoadException* instruction) { 3126 __ Ldr(OutputRegister(instruction), GetExceptionTlsAddress()); 3127} 3128 3129void LocationsBuilderARM64::VisitClearException(HClearException* clear) { 3130 new (GetGraph()->GetArena()) LocationSummary(clear, LocationSummary::kNoCall); 3131} 3132 3133void InstructionCodeGeneratorARM64::VisitClearException(HClearException* clear ATTRIBUTE_UNUSED) { 3134 __ Str(wzr, GetExceptionTlsAddress()); 3135} 3136 3137void LocationsBuilderARM64::VisitLoadLocal(HLoadLocal* load) { 3138 load->SetLocations(nullptr); 3139} 3140 3141void InstructionCodeGeneratorARM64::VisitLoadLocal(HLoadLocal* load ATTRIBUTE_UNUSED) { 3142 // Nothing to do, this is driven by the code generator. 3143} 3144 3145void LocationsBuilderARM64::VisitLoadString(HLoadString* load) { 3146 LocationSummary* locations = 3147 new (GetGraph()->GetArena()) LocationSummary(load, LocationSummary::kCallOnSlowPath); 3148 locations->SetInAt(0, Location::RequiresRegister()); 3149 locations->SetOut(Location::RequiresRegister()); 3150} 3151 3152void InstructionCodeGeneratorARM64::VisitLoadString(HLoadString* load) { 3153 SlowPathCodeARM64* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathARM64(load); 3154 codegen_->AddSlowPath(slow_path); 3155 3156 Register out = OutputRegister(load); 3157 Register current_method = InputRegisterAt(load, 0); 3158 __ Ldr(out, MemOperand(current_method, ArtMethod::DeclaringClassOffset().Int32Value())); 3159 __ Ldr(out.X(), HeapOperand(out, mirror::Class::DexCacheStringsOffset())); 3160 __ Ldr(out, MemOperand(out.X(), CodeGenerator::GetCacheOffset(load->GetStringIndex()))); 3161 // TODO: We will need a read barrier here. 3162 __ Cbz(out, slow_path->GetEntryLabel()); 3163 __ Bind(slow_path->GetExitLabel()); 3164} 3165 3166void LocationsBuilderARM64::VisitLocal(HLocal* local) { 3167 local->SetLocations(nullptr); 3168} 3169 3170void InstructionCodeGeneratorARM64::VisitLocal(HLocal* local) { 3171 DCHECK_EQ(local->GetBlock(), GetGraph()->GetEntryBlock()); 3172} 3173 3174void LocationsBuilderARM64::VisitLongConstant(HLongConstant* constant) { 3175 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(constant); 3176 locations->SetOut(Location::ConstantLocation(constant)); 3177} 3178 3179void InstructionCodeGeneratorARM64::VisitLongConstant(HLongConstant* constant ATTRIBUTE_UNUSED) { 3180 // Will be generated at use site. 3181} 3182 3183void LocationsBuilderARM64::VisitMonitorOperation(HMonitorOperation* instruction) { 3184 LocationSummary* locations = 3185 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall); 3186 InvokeRuntimeCallingConvention calling_convention; 3187 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0))); 3188} 3189 3190void InstructionCodeGeneratorARM64::VisitMonitorOperation(HMonitorOperation* instruction) { 3191 codegen_->InvokeRuntime(instruction->IsEnter() 3192 ? QUICK_ENTRY_POINT(pLockObject) : QUICK_ENTRY_POINT(pUnlockObject), 3193 instruction, 3194 instruction->GetDexPc(), 3195 nullptr); 3196 CheckEntrypointTypes<kQuickLockObject, void, mirror::Object*>(); 3197} 3198 3199void LocationsBuilderARM64::VisitMul(HMul* mul) { 3200 LocationSummary* locations = 3201 new (GetGraph()->GetArena()) LocationSummary(mul, LocationSummary::kNoCall); 3202 switch (mul->GetResultType()) { 3203 case Primitive::kPrimInt: 3204 case Primitive::kPrimLong: 3205 locations->SetInAt(0, Location::RequiresRegister()); 3206 locations->SetInAt(1, Location::RequiresRegister()); 3207 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 3208 break; 3209 3210 case Primitive::kPrimFloat: 3211 case Primitive::kPrimDouble: 3212 locations->SetInAt(0, Location::RequiresFpuRegister()); 3213 locations->SetInAt(1, Location::RequiresFpuRegister()); 3214 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); 3215 break; 3216 3217 default: 3218 LOG(FATAL) << "Unexpected mul type " << mul->GetResultType(); 3219 } 3220} 3221 3222void InstructionCodeGeneratorARM64::VisitMul(HMul* mul) { 3223 switch (mul->GetResultType()) { 3224 case Primitive::kPrimInt: 3225 case Primitive::kPrimLong: 3226 __ Mul(OutputRegister(mul), InputRegisterAt(mul, 0), InputRegisterAt(mul, 1)); 3227 break; 3228 3229 case Primitive::kPrimFloat: 3230 case Primitive::kPrimDouble: 3231 __ Fmul(OutputFPRegister(mul), InputFPRegisterAt(mul, 0), InputFPRegisterAt(mul, 1)); 3232 break; 3233 3234 default: 3235 LOG(FATAL) << "Unexpected mul type " << mul->GetResultType(); 3236 } 3237} 3238 3239void LocationsBuilderARM64::VisitNeg(HNeg* neg) { 3240 LocationSummary* locations = 3241 new (GetGraph()->GetArena()) LocationSummary(neg, LocationSummary::kNoCall); 3242 switch (neg->GetResultType()) { 3243 case Primitive::kPrimInt: 3244 case Primitive::kPrimLong: 3245 locations->SetInAt(0, ARM64EncodableConstantOrRegister(neg->InputAt(0), neg)); 3246 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 3247 break; 3248 3249 case Primitive::kPrimFloat: 3250 case Primitive::kPrimDouble: 3251 locations->SetInAt(0, Location::RequiresFpuRegister()); 3252 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); 3253 break; 3254 3255 default: 3256 LOG(FATAL) << "Unexpected neg type " << neg->GetResultType(); 3257 } 3258} 3259 3260void InstructionCodeGeneratorARM64::VisitNeg(HNeg* neg) { 3261 switch (neg->GetResultType()) { 3262 case Primitive::kPrimInt: 3263 case Primitive::kPrimLong: 3264 __ Neg(OutputRegister(neg), InputOperandAt(neg, 0)); 3265 break; 3266 3267 case Primitive::kPrimFloat: 3268 case Primitive::kPrimDouble: 3269 __ Fneg(OutputFPRegister(neg), InputFPRegisterAt(neg, 0)); 3270 break; 3271 3272 default: 3273 LOG(FATAL) << "Unexpected neg type " << neg->GetResultType(); 3274 } 3275} 3276 3277void LocationsBuilderARM64::VisitNewArray(HNewArray* instruction) { 3278 LocationSummary* locations = 3279 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall); 3280 InvokeRuntimeCallingConvention calling_convention; 3281 locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(0))); 3282 locations->SetOut(LocationFrom(x0)); 3283 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(1))); 3284 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(2))); 3285 CheckEntrypointTypes<kQuickAllocArrayWithAccessCheck, 3286 void*, uint32_t, int32_t, ArtMethod*>(); 3287} 3288 3289void InstructionCodeGeneratorARM64::VisitNewArray(HNewArray* instruction) { 3290 LocationSummary* locations = instruction->GetLocations(); 3291 InvokeRuntimeCallingConvention calling_convention; 3292 Register type_index = RegisterFrom(locations->GetTemp(0), Primitive::kPrimInt); 3293 DCHECK(type_index.Is(w0)); 3294 __ Mov(type_index, instruction->GetTypeIndex()); 3295 // Note: if heap poisoning is enabled, the entry point takes cares 3296 // of poisoning the reference. 3297 codegen_->InvokeRuntime(instruction->GetEntrypoint(), 3298 instruction, 3299 instruction->GetDexPc(), 3300 nullptr); 3301 CheckEntrypointTypes<kQuickAllocArrayWithAccessCheck, void*, uint32_t, int32_t, ArtMethod*>(); 3302} 3303 3304void LocationsBuilderARM64::VisitNewInstance(HNewInstance* instruction) { 3305 LocationSummary* locations = 3306 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall); 3307 InvokeRuntimeCallingConvention calling_convention; 3308 locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(0))); 3309 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(1))); 3310 locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimNot)); 3311 CheckEntrypointTypes<kQuickAllocObjectWithAccessCheck, void*, uint32_t, ArtMethod*>(); 3312} 3313 3314void InstructionCodeGeneratorARM64::VisitNewInstance(HNewInstance* instruction) { 3315 LocationSummary* locations = instruction->GetLocations(); 3316 Register type_index = RegisterFrom(locations->GetTemp(0), Primitive::kPrimInt); 3317 DCHECK(type_index.Is(w0)); 3318 __ Mov(type_index, instruction->GetTypeIndex()); 3319 // Note: if heap poisoning is enabled, the entry point takes cares 3320 // of poisoning the reference. 3321 codegen_->InvokeRuntime(instruction->GetEntrypoint(), 3322 instruction, 3323 instruction->GetDexPc(), 3324 nullptr); 3325 CheckEntrypointTypes<kQuickAllocObjectWithAccessCheck, void*, uint32_t, ArtMethod*>(); 3326} 3327 3328void LocationsBuilderARM64::VisitNot(HNot* instruction) { 3329 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); 3330 locations->SetInAt(0, Location::RequiresRegister()); 3331 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 3332} 3333 3334void InstructionCodeGeneratorARM64::VisitNot(HNot* instruction) { 3335 switch (instruction->GetResultType()) { 3336 case Primitive::kPrimInt: 3337 case Primitive::kPrimLong: 3338 __ Mvn(OutputRegister(instruction), InputOperandAt(instruction, 0)); 3339 break; 3340 3341 default: 3342 LOG(FATAL) << "Unexpected type for not operation " << instruction->GetResultType(); 3343 } 3344} 3345 3346void LocationsBuilderARM64::VisitBooleanNot(HBooleanNot* instruction) { 3347 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); 3348 locations->SetInAt(0, Location::RequiresRegister()); 3349 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 3350} 3351 3352void InstructionCodeGeneratorARM64::VisitBooleanNot(HBooleanNot* instruction) { 3353 __ Eor(OutputRegister(instruction), InputRegisterAt(instruction, 0), vixl::Operand(1)); 3354} 3355 3356void LocationsBuilderARM64::VisitNullCheck(HNullCheck* instruction) { 3357 LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock() 3358 ? LocationSummary::kCallOnSlowPath 3359 : LocationSummary::kNoCall; 3360 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind); 3361 locations->SetInAt(0, Location::RequiresRegister()); 3362 if (instruction->HasUses()) { 3363 locations->SetOut(Location::SameAsFirstInput()); 3364 } 3365} 3366 3367void InstructionCodeGeneratorARM64::GenerateImplicitNullCheck(HNullCheck* instruction) { 3368 if (codegen_->CanMoveNullCheckToUser(instruction)) { 3369 return; 3370 } 3371 3372 BlockPoolsScope block_pools(GetVIXLAssembler()); 3373 Location obj = instruction->GetLocations()->InAt(0); 3374 __ Ldr(wzr, HeapOperandFrom(obj, Offset(0))); 3375 codegen_->RecordPcInfo(instruction, instruction->GetDexPc()); 3376} 3377 3378void InstructionCodeGeneratorARM64::GenerateExplicitNullCheck(HNullCheck* instruction) { 3379 SlowPathCodeARM64* slow_path = new (GetGraph()->GetArena()) NullCheckSlowPathARM64(instruction); 3380 codegen_->AddSlowPath(slow_path); 3381 3382 LocationSummary* locations = instruction->GetLocations(); 3383 Location obj = locations->InAt(0); 3384 3385 __ Cbz(RegisterFrom(obj, instruction->InputAt(0)->GetType()), slow_path->GetEntryLabel()); 3386} 3387 3388void InstructionCodeGeneratorARM64::VisitNullCheck(HNullCheck* instruction) { 3389 if (codegen_->IsImplicitNullCheckAllowed(instruction)) { 3390 GenerateImplicitNullCheck(instruction); 3391 } else { 3392 GenerateExplicitNullCheck(instruction); 3393 } 3394} 3395 3396void LocationsBuilderARM64::VisitOr(HOr* instruction) { 3397 HandleBinaryOp(instruction); 3398} 3399 3400void InstructionCodeGeneratorARM64::VisitOr(HOr* instruction) { 3401 HandleBinaryOp(instruction); 3402} 3403 3404void LocationsBuilderARM64::VisitParallelMove(HParallelMove* instruction ATTRIBUTE_UNUSED) { 3405 LOG(FATAL) << "Unreachable"; 3406} 3407 3408void InstructionCodeGeneratorARM64::VisitParallelMove(HParallelMove* instruction) { 3409 codegen_->GetMoveResolver()->EmitNativeCode(instruction); 3410} 3411 3412void LocationsBuilderARM64::VisitParameterValue(HParameterValue* instruction) { 3413 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); 3414 Location location = parameter_visitor_.GetNextLocation(instruction->GetType()); 3415 if (location.IsStackSlot()) { 3416 location = Location::StackSlot(location.GetStackIndex() + codegen_->GetFrameSize()); 3417 } else if (location.IsDoubleStackSlot()) { 3418 location = Location::DoubleStackSlot(location.GetStackIndex() + codegen_->GetFrameSize()); 3419 } 3420 locations->SetOut(location); 3421} 3422 3423void InstructionCodeGeneratorARM64::VisitParameterValue( 3424 HParameterValue* instruction ATTRIBUTE_UNUSED) { 3425 // Nothing to do, the parameter is already at its location. 3426} 3427 3428void LocationsBuilderARM64::VisitCurrentMethod(HCurrentMethod* instruction) { 3429 LocationSummary* locations = 3430 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); 3431 locations->SetOut(LocationFrom(kArtMethodRegister)); 3432} 3433 3434void InstructionCodeGeneratorARM64::VisitCurrentMethod( 3435 HCurrentMethod* instruction ATTRIBUTE_UNUSED) { 3436 // Nothing to do, the method is already at its location. 3437} 3438 3439void LocationsBuilderARM64::VisitPhi(HPhi* instruction) { 3440 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); 3441 for (size_t i = 0, e = instruction->InputCount(); i < e; ++i) { 3442 locations->SetInAt(i, Location::Any()); 3443 } 3444 locations->SetOut(Location::Any()); 3445} 3446 3447void InstructionCodeGeneratorARM64::VisitPhi(HPhi* instruction ATTRIBUTE_UNUSED) { 3448 LOG(FATAL) << "Unreachable"; 3449} 3450 3451void LocationsBuilderARM64::VisitRem(HRem* rem) { 3452 Primitive::Type type = rem->GetResultType(); 3453 LocationSummary::CallKind call_kind = 3454 Primitive::IsFloatingPointType(type) ? LocationSummary::kCall : LocationSummary::kNoCall; 3455 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(rem, call_kind); 3456 3457 switch (type) { 3458 case Primitive::kPrimInt: 3459 case Primitive::kPrimLong: 3460 locations->SetInAt(0, Location::RequiresRegister()); 3461 locations->SetInAt(1, Location::RegisterOrConstant(rem->InputAt(1))); 3462 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 3463 break; 3464 3465 case Primitive::kPrimFloat: 3466 case Primitive::kPrimDouble: { 3467 InvokeRuntimeCallingConvention calling_convention; 3468 locations->SetInAt(0, LocationFrom(calling_convention.GetFpuRegisterAt(0))); 3469 locations->SetInAt(1, LocationFrom(calling_convention.GetFpuRegisterAt(1))); 3470 locations->SetOut(calling_convention.GetReturnLocation(type)); 3471 3472 break; 3473 } 3474 3475 default: 3476 LOG(FATAL) << "Unexpected rem type " << type; 3477 } 3478} 3479 3480void InstructionCodeGeneratorARM64::VisitRem(HRem* rem) { 3481 Primitive::Type type = rem->GetResultType(); 3482 3483 switch (type) { 3484 case Primitive::kPrimInt: 3485 case Primitive::kPrimLong: { 3486 GenerateDivRemIntegral(rem); 3487 break; 3488 } 3489 3490 case Primitive::kPrimFloat: 3491 case Primitive::kPrimDouble: { 3492 int32_t entry_offset = (type == Primitive::kPrimFloat) ? QUICK_ENTRY_POINT(pFmodf) 3493 : QUICK_ENTRY_POINT(pFmod); 3494 codegen_->InvokeRuntime(entry_offset, rem, rem->GetDexPc(), nullptr); 3495 break; 3496 } 3497 3498 default: 3499 LOG(FATAL) << "Unexpected rem type " << type; 3500 } 3501} 3502 3503void LocationsBuilderARM64::VisitMemoryBarrier(HMemoryBarrier* memory_barrier) { 3504 memory_barrier->SetLocations(nullptr); 3505} 3506 3507void InstructionCodeGeneratorARM64::VisitMemoryBarrier(HMemoryBarrier* memory_barrier) { 3508 GenerateMemoryBarrier(memory_barrier->GetBarrierKind()); 3509} 3510 3511void LocationsBuilderARM64::VisitReturn(HReturn* instruction) { 3512 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); 3513 Primitive::Type return_type = instruction->InputAt(0)->GetType(); 3514 locations->SetInAt(0, ARM64ReturnLocation(return_type)); 3515} 3516 3517void InstructionCodeGeneratorARM64::VisitReturn(HReturn* instruction ATTRIBUTE_UNUSED) { 3518 codegen_->GenerateFrameExit(); 3519} 3520 3521void LocationsBuilderARM64::VisitReturnVoid(HReturnVoid* instruction) { 3522 instruction->SetLocations(nullptr); 3523} 3524 3525void InstructionCodeGeneratorARM64::VisitReturnVoid(HReturnVoid* instruction ATTRIBUTE_UNUSED) { 3526 codegen_->GenerateFrameExit(); 3527} 3528 3529void LocationsBuilderARM64::VisitShl(HShl* shl) { 3530 HandleShift(shl); 3531} 3532 3533void InstructionCodeGeneratorARM64::VisitShl(HShl* shl) { 3534 HandleShift(shl); 3535} 3536 3537void LocationsBuilderARM64::VisitShr(HShr* shr) { 3538 HandleShift(shr); 3539} 3540 3541void InstructionCodeGeneratorARM64::VisitShr(HShr* shr) { 3542 HandleShift(shr); 3543} 3544 3545void LocationsBuilderARM64::VisitStoreLocal(HStoreLocal* store) { 3546 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(store); 3547 Primitive::Type field_type = store->InputAt(1)->GetType(); 3548 switch (field_type) { 3549 case Primitive::kPrimNot: 3550 case Primitive::kPrimBoolean: 3551 case Primitive::kPrimByte: 3552 case Primitive::kPrimChar: 3553 case Primitive::kPrimShort: 3554 case Primitive::kPrimInt: 3555 case Primitive::kPrimFloat: 3556 locations->SetInAt(1, Location::StackSlot(codegen_->GetStackSlot(store->GetLocal()))); 3557 break; 3558 3559 case Primitive::kPrimLong: 3560 case Primitive::kPrimDouble: 3561 locations->SetInAt(1, Location::DoubleStackSlot(codegen_->GetStackSlot(store->GetLocal()))); 3562 break; 3563 3564 default: 3565 LOG(FATAL) << "Unimplemented local type " << field_type; 3566 } 3567} 3568 3569void InstructionCodeGeneratorARM64::VisitStoreLocal(HStoreLocal* store ATTRIBUTE_UNUSED) { 3570} 3571 3572void LocationsBuilderARM64::VisitSub(HSub* instruction) { 3573 HandleBinaryOp(instruction); 3574} 3575 3576void InstructionCodeGeneratorARM64::VisitSub(HSub* instruction) { 3577 HandleBinaryOp(instruction); 3578} 3579 3580void LocationsBuilderARM64::VisitStaticFieldGet(HStaticFieldGet* instruction) { 3581 HandleFieldGet(instruction); 3582} 3583 3584void InstructionCodeGeneratorARM64::VisitStaticFieldGet(HStaticFieldGet* instruction) { 3585 HandleFieldGet(instruction, instruction->GetFieldInfo()); 3586} 3587 3588void LocationsBuilderARM64::VisitStaticFieldSet(HStaticFieldSet* instruction) { 3589 HandleFieldSet(instruction); 3590} 3591 3592void InstructionCodeGeneratorARM64::VisitStaticFieldSet(HStaticFieldSet* instruction) { 3593 HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull()); 3594} 3595 3596void LocationsBuilderARM64::VisitUnresolvedInstanceFieldGet( 3597 HUnresolvedInstanceFieldGet* instruction) { 3598 FieldAccessCallingConventionARM64 calling_convention; 3599 codegen_->CreateUnresolvedFieldLocationSummary( 3600 instruction, instruction->GetFieldType(), calling_convention); 3601} 3602 3603void InstructionCodeGeneratorARM64::VisitUnresolvedInstanceFieldGet( 3604 HUnresolvedInstanceFieldGet* instruction) { 3605 FieldAccessCallingConventionARM64 calling_convention; 3606 codegen_->GenerateUnresolvedFieldAccess(instruction, 3607 instruction->GetFieldType(), 3608 instruction->GetFieldIndex(), 3609 instruction->GetDexPc(), 3610 calling_convention); 3611} 3612 3613void LocationsBuilderARM64::VisitUnresolvedInstanceFieldSet( 3614 HUnresolvedInstanceFieldSet* instruction) { 3615 FieldAccessCallingConventionARM64 calling_convention; 3616 codegen_->CreateUnresolvedFieldLocationSummary( 3617 instruction, instruction->GetFieldType(), calling_convention); 3618} 3619 3620void InstructionCodeGeneratorARM64::VisitUnresolvedInstanceFieldSet( 3621 HUnresolvedInstanceFieldSet* instruction) { 3622 FieldAccessCallingConventionARM64 calling_convention; 3623 codegen_->GenerateUnresolvedFieldAccess(instruction, 3624 instruction->GetFieldType(), 3625 instruction->GetFieldIndex(), 3626 instruction->GetDexPc(), 3627 calling_convention); 3628} 3629 3630void LocationsBuilderARM64::VisitUnresolvedStaticFieldGet( 3631 HUnresolvedStaticFieldGet* instruction) { 3632 FieldAccessCallingConventionARM64 calling_convention; 3633 codegen_->CreateUnresolvedFieldLocationSummary( 3634 instruction, instruction->GetFieldType(), calling_convention); 3635} 3636 3637void InstructionCodeGeneratorARM64::VisitUnresolvedStaticFieldGet( 3638 HUnresolvedStaticFieldGet* instruction) { 3639 FieldAccessCallingConventionARM64 calling_convention; 3640 codegen_->GenerateUnresolvedFieldAccess(instruction, 3641 instruction->GetFieldType(), 3642 instruction->GetFieldIndex(), 3643 instruction->GetDexPc(), 3644 calling_convention); 3645} 3646 3647void LocationsBuilderARM64::VisitUnresolvedStaticFieldSet( 3648 HUnresolvedStaticFieldSet* instruction) { 3649 FieldAccessCallingConventionARM64 calling_convention; 3650 codegen_->CreateUnresolvedFieldLocationSummary( 3651 instruction, instruction->GetFieldType(), calling_convention); 3652} 3653 3654void InstructionCodeGeneratorARM64::VisitUnresolvedStaticFieldSet( 3655 HUnresolvedStaticFieldSet* instruction) { 3656 FieldAccessCallingConventionARM64 calling_convention; 3657 codegen_->GenerateUnresolvedFieldAccess(instruction, 3658 instruction->GetFieldType(), 3659 instruction->GetFieldIndex(), 3660 instruction->GetDexPc(), 3661 calling_convention); 3662} 3663 3664void LocationsBuilderARM64::VisitSuspendCheck(HSuspendCheck* instruction) { 3665 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnSlowPath); 3666} 3667 3668void InstructionCodeGeneratorARM64::VisitSuspendCheck(HSuspendCheck* instruction) { 3669 HBasicBlock* block = instruction->GetBlock(); 3670 if (block->GetLoopInformation() != nullptr) { 3671 DCHECK(block->GetLoopInformation()->GetSuspendCheck() == instruction); 3672 // The back edge will generate the suspend check. 3673 return; 3674 } 3675 if (block->IsEntryBlock() && instruction->GetNext()->IsGoto()) { 3676 // The goto will generate the suspend check. 3677 return; 3678 } 3679 GenerateSuspendCheck(instruction, nullptr); 3680} 3681 3682void LocationsBuilderARM64::VisitTemporary(HTemporary* temp) { 3683 temp->SetLocations(nullptr); 3684} 3685 3686void InstructionCodeGeneratorARM64::VisitTemporary(HTemporary* temp ATTRIBUTE_UNUSED) { 3687 // Nothing to do, this is driven by the code generator. 3688} 3689 3690void LocationsBuilderARM64::VisitThrow(HThrow* instruction) { 3691 LocationSummary* locations = 3692 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall); 3693 InvokeRuntimeCallingConvention calling_convention; 3694 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0))); 3695} 3696 3697void InstructionCodeGeneratorARM64::VisitThrow(HThrow* instruction) { 3698 codegen_->InvokeRuntime( 3699 QUICK_ENTRY_POINT(pDeliverException), instruction, instruction->GetDexPc(), nullptr); 3700 CheckEntrypointTypes<kQuickDeliverException, void, mirror::Object*>(); 3701} 3702 3703void LocationsBuilderARM64::VisitTypeConversion(HTypeConversion* conversion) { 3704 LocationSummary* locations = 3705 new (GetGraph()->GetArena()) LocationSummary(conversion, LocationSummary::kNoCall); 3706 Primitive::Type input_type = conversion->GetInputType(); 3707 Primitive::Type result_type = conversion->GetResultType(); 3708 DCHECK_NE(input_type, result_type); 3709 if ((input_type == Primitive::kPrimNot) || (input_type == Primitive::kPrimVoid) || 3710 (result_type == Primitive::kPrimNot) || (result_type == Primitive::kPrimVoid)) { 3711 LOG(FATAL) << "Unexpected type conversion from " << input_type << " to " << result_type; 3712 } 3713 3714 if (Primitive::IsFloatingPointType(input_type)) { 3715 locations->SetInAt(0, Location::RequiresFpuRegister()); 3716 } else { 3717 locations->SetInAt(0, Location::RequiresRegister()); 3718 } 3719 3720 if (Primitive::IsFloatingPointType(result_type)) { 3721 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); 3722 } else { 3723 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 3724 } 3725} 3726 3727void InstructionCodeGeneratorARM64::VisitTypeConversion(HTypeConversion* conversion) { 3728 Primitive::Type result_type = conversion->GetResultType(); 3729 Primitive::Type input_type = conversion->GetInputType(); 3730 3731 DCHECK_NE(input_type, result_type); 3732 3733 if (Primitive::IsIntegralType(result_type) && Primitive::IsIntegralType(input_type)) { 3734 int result_size = Primitive::ComponentSize(result_type); 3735 int input_size = Primitive::ComponentSize(input_type); 3736 int min_size = std::min(result_size, input_size); 3737 Register output = OutputRegister(conversion); 3738 Register source = InputRegisterAt(conversion, 0); 3739 if ((result_type == Primitive::kPrimChar) && (input_size < result_size)) { 3740 __ Ubfx(output, source, 0, result_size * kBitsPerByte); 3741 } else if (result_type == Primitive::kPrimInt && input_type == Primitive::kPrimLong) { 3742 // 'int' values are used directly as W registers, discarding the top 3743 // bits, so we don't need to sign-extend and can just perform a move. 3744 // We do not pass the `kDiscardForSameWReg` argument to force clearing the 3745 // top 32 bits of the target register. We theoretically could leave those 3746 // bits unchanged, but we would have to make sure that no code uses a 3747 // 32bit input value as a 64bit value assuming that the top 32 bits are 3748 // zero. 3749 __ Mov(output.W(), source.W()); 3750 } else if ((result_type == Primitive::kPrimChar) || 3751 ((input_type == Primitive::kPrimChar) && (result_size > input_size))) { 3752 __ Ubfx(output, output.IsX() ? source.X() : source.W(), 0, min_size * kBitsPerByte); 3753 } else { 3754 __ Sbfx(output, output.IsX() ? source.X() : source.W(), 0, min_size * kBitsPerByte); 3755 } 3756 } else if (Primitive::IsFloatingPointType(result_type) && Primitive::IsIntegralType(input_type)) { 3757 __ Scvtf(OutputFPRegister(conversion), InputRegisterAt(conversion, 0)); 3758 } else if (Primitive::IsIntegralType(result_type) && Primitive::IsFloatingPointType(input_type)) { 3759 CHECK(result_type == Primitive::kPrimInt || result_type == Primitive::kPrimLong); 3760 __ Fcvtzs(OutputRegister(conversion), InputFPRegisterAt(conversion, 0)); 3761 } else if (Primitive::IsFloatingPointType(result_type) && 3762 Primitive::IsFloatingPointType(input_type)) { 3763 __ Fcvt(OutputFPRegister(conversion), InputFPRegisterAt(conversion, 0)); 3764 } else { 3765 LOG(FATAL) << "Unexpected or unimplemented type conversion from " << input_type 3766 << " to " << result_type; 3767 } 3768} 3769 3770void LocationsBuilderARM64::VisitUShr(HUShr* ushr) { 3771 HandleShift(ushr); 3772} 3773 3774void InstructionCodeGeneratorARM64::VisitUShr(HUShr* ushr) { 3775 HandleShift(ushr); 3776} 3777 3778void LocationsBuilderARM64::VisitXor(HXor* instruction) { 3779 HandleBinaryOp(instruction); 3780} 3781 3782void InstructionCodeGeneratorARM64::VisitXor(HXor* instruction) { 3783 HandleBinaryOp(instruction); 3784} 3785 3786void LocationsBuilderARM64::VisitBoundType(HBoundType* instruction ATTRIBUTE_UNUSED) { 3787 // Nothing to do, this should be removed during prepare for register allocator. 3788 LOG(FATAL) << "Unreachable"; 3789} 3790 3791void InstructionCodeGeneratorARM64::VisitBoundType(HBoundType* instruction ATTRIBUTE_UNUSED) { 3792 // Nothing to do, this should be removed during prepare for register allocator. 3793 LOG(FATAL) << "Unreachable"; 3794} 3795 3796void LocationsBuilderARM64::VisitFakeString(HFakeString* instruction) { 3797 DCHECK(codegen_->IsBaseline()); 3798 LocationSummary* locations = 3799 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); 3800 locations->SetOut(Location::ConstantLocation(GetGraph()->GetNullConstant())); 3801} 3802 3803void InstructionCodeGeneratorARM64::VisitFakeString(HFakeString* instruction ATTRIBUTE_UNUSED) { 3804 DCHECK(codegen_->IsBaseline()); 3805 // Will be generated at use site. 3806} 3807 3808// Simple implementation of packed switch - generate cascaded compare/jumps. 3809void LocationsBuilderARM64::VisitPackedSwitch(HPackedSwitch* switch_instr) { 3810 LocationSummary* locations = 3811 new (GetGraph()->GetArena()) LocationSummary(switch_instr, LocationSummary::kNoCall); 3812 locations->SetInAt(0, Location::RequiresRegister()); 3813} 3814 3815void InstructionCodeGeneratorARM64::VisitPackedSwitch(HPackedSwitch* switch_instr) { 3816 int32_t lower_bound = switch_instr->GetStartValue(); 3817 int32_t num_entries = switch_instr->GetNumEntries(); 3818 Register value_reg = InputRegisterAt(switch_instr, 0); 3819 HBasicBlock* default_block = switch_instr->GetDefaultBlock(); 3820 3821 // Create a series of compare/jumps. 3822 const ArenaVector<HBasicBlock*>& successors = switch_instr->GetBlock()->GetSuccessors(); 3823 for (int32_t i = 0; i < num_entries; i++) { 3824 int32_t case_value = lower_bound + i; 3825 vixl::Label* succ = codegen_->GetLabelOf(successors[i]); 3826 if (case_value == 0) { 3827 __ Cbz(value_reg, succ); 3828 } else { 3829 __ Cmp(value_reg, vixl::Operand(case_value)); 3830 __ B(eq, succ); 3831 } 3832 } 3833 3834 // And the default for any other value. 3835 if (!codegen_->GoesToNextBlock(switch_instr->GetBlock(), default_block)) { 3836 __ B(codegen_->GetLabelOf(default_block)); 3837 } 3838} 3839 3840#undef __ 3841#undef QUICK_ENTRY_POINT 3842 3843} // namespace arm64 3844} // namespace art 3845