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