code_generator_arm64.cc revision 42249c3602c3d0243396ee3627ffb5906aa77c1e
1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "code_generator_arm64.h"
18
19#include "arch/arm64/instruction_set_features_arm64.h"
20#include "art_method.h"
21#include "code_generator_utils.h"
22#include "compiled_method.h"
23#include "entrypoints/quick/quick_entrypoints.h"
24#include "entrypoints/quick/quick_entrypoints_enum.h"
25#include "gc/accounting/card_table.h"
26#include "intrinsics.h"
27#include "intrinsics_arm64.h"
28#include "mirror/array-inl.h"
29#include "mirror/class-inl.h"
30#include "offsets.h"
31#include "thread.h"
32#include "utils/arm64/assembler_arm64.h"
33#include "utils/assembler.h"
34#include "utils/stack_checks.h"
35
36
37using namespace vixl;   // NOLINT(build/namespaces)
38
39#ifdef __
40#error "ARM64 Codegen VIXL macro-assembler macro already defined."
41#endif
42
43namespace art {
44
45template<class MirrorType>
46class GcRoot;
47
48namespace arm64 {
49
50using helpers::CPURegisterFrom;
51using helpers::DRegisterFrom;
52using helpers::FPRegisterFrom;
53using helpers::HeapOperand;
54using helpers::HeapOperandFrom;
55using helpers::InputCPURegisterAt;
56using helpers::InputFPRegisterAt;
57using helpers::InputRegisterAt;
58using helpers::InputOperandAt;
59using helpers::Int64ConstantFrom;
60using helpers::LocationFrom;
61using helpers::OperandFromMemOperand;
62using helpers::OutputCPURegister;
63using helpers::OutputFPRegister;
64using helpers::OutputRegister;
65using helpers::RegisterFrom;
66using helpers::StackOperandFrom;
67using helpers::VIXLRegCodeFromART;
68using helpers::WRegisterFrom;
69using helpers::XRegisterFrom;
70using helpers::ARM64EncodableConstantOrRegister;
71using helpers::ArtVixlRegCodeCoherentForRegSet;
72
73static constexpr int kCurrentMethodStackOffset = 0;
74// The compare/jump sequence will generate about (1.5 * num_entries + 3) instructions. While jump
75// table version generates 7 instructions and num_entries literals. Compare/jump sequence will
76// generates less code/data with a small num_entries.
77static constexpr uint32_t kPackedSwitchCompareJumpThreshold = 7;
78
79inline Condition ARM64Condition(IfCondition cond) {
80  switch (cond) {
81    case kCondEQ: return eq;
82    case kCondNE: return ne;
83    case kCondLT: return lt;
84    case kCondLE: return le;
85    case kCondGT: return gt;
86    case kCondGE: return ge;
87    case kCondB:  return lo;
88    case kCondBE: return ls;
89    case kCondA:  return hi;
90    case kCondAE: return hs;
91  }
92  LOG(FATAL) << "Unreachable";
93  UNREACHABLE();
94}
95
96Location ARM64ReturnLocation(Primitive::Type return_type) {
97  // Note that in practice, `LocationFrom(x0)` and `LocationFrom(w0)` create the
98  // same Location object, and so do `LocationFrom(d0)` and `LocationFrom(s0)`,
99  // but we use the exact registers for clarity.
100  if (return_type == Primitive::kPrimFloat) {
101    return LocationFrom(s0);
102  } else if (return_type == Primitive::kPrimDouble) {
103    return LocationFrom(d0);
104  } else if (return_type == Primitive::kPrimLong) {
105    return LocationFrom(x0);
106  } else if (return_type == Primitive::kPrimVoid) {
107    return Location::NoLocation();
108  } else {
109    return LocationFrom(w0);
110  }
111}
112
113Location InvokeRuntimeCallingConvention::GetReturnLocation(Primitive::Type return_type) {
114  return ARM64ReturnLocation(return_type);
115}
116
117#define __ down_cast<CodeGeneratorARM64*>(codegen)->GetVIXLAssembler()->
118#define QUICK_ENTRY_POINT(x) QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, x).Int32Value()
119
120// Calculate memory accessing operand for save/restore live registers.
121static void SaveRestoreLiveRegistersHelper(CodeGenerator* codegen,
122                                           RegisterSet* register_set,
123                                           int64_t spill_offset,
124                                           bool is_save) {
125  DCHECK(ArtVixlRegCodeCoherentForRegSet(register_set->GetCoreRegisters(),
126                                         codegen->GetNumberOfCoreRegisters(),
127                                         register_set->GetFloatingPointRegisters(),
128                                         codegen->GetNumberOfFloatingPointRegisters()));
129
130  CPURegList core_list = CPURegList(CPURegister::kRegister, kXRegSize,
131      register_set->GetCoreRegisters() & (~callee_saved_core_registers.list()));
132  CPURegList fp_list = CPURegList(CPURegister::kFPRegister, kDRegSize,
133      register_set->GetFloatingPointRegisters() & (~callee_saved_fp_registers.list()));
134
135  MacroAssembler* masm = down_cast<CodeGeneratorARM64*>(codegen)->GetVIXLAssembler();
136  UseScratchRegisterScope temps(masm);
137
138  Register base = masm->StackPointer();
139  int64_t core_spill_size = core_list.TotalSizeInBytes();
140  int64_t fp_spill_size = fp_list.TotalSizeInBytes();
141  int64_t reg_size = kXRegSizeInBytes;
142  int64_t max_ls_pair_offset = spill_offset + core_spill_size + fp_spill_size - 2 * reg_size;
143  uint32_t ls_access_size = WhichPowerOf2(reg_size);
144  if (((core_list.Count() > 1) || (fp_list.Count() > 1)) &&
145      !masm->IsImmLSPair(max_ls_pair_offset, ls_access_size)) {
146    // If the offset does not fit in the instruction's immediate field, use an alternate register
147    // to compute the base address(float point registers spill base address).
148    Register new_base = temps.AcquireSameSizeAs(base);
149    __ Add(new_base, base, Operand(spill_offset + core_spill_size));
150    base = new_base;
151    spill_offset = -core_spill_size;
152    int64_t new_max_ls_pair_offset = fp_spill_size - 2 * reg_size;
153    DCHECK(masm->IsImmLSPair(spill_offset, ls_access_size));
154    DCHECK(masm->IsImmLSPair(new_max_ls_pair_offset, ls_access_size));
155  }
156
157  if (is_save) {
158    __ StoreCPURegList(core_list, MemOperand(base, spill_offset));
159    __ StoreCPURegList(fp_list, MemOperand(base, spill_offset + core_spill_size));
160  } else {
161    __ LoadCPURegList(core_list, MemOperand(base, spill_offset));
162    __ LoadCPURegList(fp_list, MemOperand(base, spill_offset + core_spill_size));
163  }
164}
165
166void SlowPathCodeARM64::SaveLiveRegisters(CodeGenerator* codegen, LocationSummary* locations) {
167  RegisterSet* register_set = locations->GetLiveRegisters();
168  size_t stack_offset = codegen->GetFirstRegisterSlotInSlowPath();
169  for (size_t i = 0, e = codegen->GetNumberOfCoreRegisters(); i < e; ++i) {
170    if (!codegen->IsCoreCalleeSaveRegister(i) && register_set->ContainsCoreRegister(i)) {
171      // If the register holds an object, update the stack mask.
172      if (locations->RegisterContainsObject(i)) {
173        locations->SetStackBit(stack_offset / kVRegSize);
174      }
175      DCHECK_LT(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
176      DCHECK_LT(i, kMaximumNumberOfExpectedRegisters);
177      saved_core_stack_offsets_[i] = stack_offset;
178      stack_offset += kXRegSizeInBytes;
179    }
180  }
181
182  for (size_t i = 0, e = codegen->GetNumberOfFloatingPointRegisters(); i < e; ++i) {
183    if (!codegen->IsFloatingPointCalleeSaveRegister(i) &&
184        register_set->ContainsFloatingPointRegister(i)) {
185      DCHECK_LT(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
186      DCHECK_LT(i, kMaximumNumberOfExpectedRegisters);
187      saved_fpu_stack_offsets_[i] = stack_offset;
188      stack_offset += kDRegSizeInBytes;
189    }
190  }
191
192  SaveRestoreLiveRegistersHelper(codegen, register_set,
193                                 codegen->GetFirstRegisterSlotInSlowPath(), true /* is_save */);
194}
195
196void SlowPathCodeARM64::RestoreLiveRegisters(CodeGenerator* codegen, LocationSummary* locations) {
197  RegisterSet* register_set = locations->GetLiveRegisters();
198  SaveRestoreLiveRegistersHelper(codegen, register_set,
199                                 codegen->GetFirstRegisterSlotInSlowPath(), false /* is_save */);
200}
201
202class BoundsCheckSlowPathARM64 : public SlowPathCodeARM64 {
203 public:
204  explicit BoundsCheckSlowPathARM64(HBoundsCheck* instruction) : instruction_(instruction) {}
205
206  void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
207    LocationSummary* locations = instruction_->GetLocations();
208    CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen);
209
210    __ Bind(GetEntryLabel());
211    if (instruction_->CanThrowIntoCatchBlock()) {
212      // Live registers will be restored in the catch block if caught.
213      SaveLiveRegisters(codegen, instruction_->GetLocations());
214    }
215    // We're moving two locations to locations that could overlap, so we need a parallel
216    // move resolver.
217    InvokeRuntimeCallingConvention calling_convention;
218    codegen->EmitParallelMoves(
219        locations->InAt(0), LocationFrom(calling_convention.GetRegisterAt(0)), Primitive::kPrimInt,
220        locations->InAt(1), LocationFrom(calling_convention.GetRegisterAt(1)), Primitive::kPrimInt);
221    arm64_codegen->InvokeRuntime(
222        QUICK_ENTRY_POINT(pThrowArrayBounds), instruction_, instruction_->GetDexPc(), this);
223    CheckEntrypointTypes<kQuickThrowArrayBounds, void, int32_t, int32_t>();
224  }
225
226  bool IsFatal() const OVERRIDE { return true; }
227
228  const char* GetDescription() const OVERRIDE { return "BoundsCheckSlowPathARM64"; }
229
230 private:
231  HBoundsCheck* const instruction_;
232
233  DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathARM64);
234};
235
236class DivZeroCheckSlowPathARM64 : public SlowPathCodeARM64 {
237 public:
238  explicit DivZeroCheckSlowPathARM64(HDivZeroCheck* instruction) : instruction_(instruction) {}
239
240  void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
241    CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen);
242    __ Bind(GetEntryLabel());
243    if (instruction_->CanThrowIntoCatchBlock()) {
244      // Live registers will be restored in the catch block if caught.
245      SaveLiveRegisters(codegen, instruction_->GetLocations());
246    }
247    arm64_codegen->InvokeRuntime(
248        QUICK_ENTRY_POINT(pThrowDivZero), instruction_, instruction_->GetDexPc(), this);
249    CheckEntrypointTypes<kQuickThrowDivZero, void, void>();
250  }
251
252  bool IsFatal() const OVERRIDE { return true; }
253
254  const char* GetDescription() const OVERRIDE { return "DivZeroCheckSlowPathARM64"; }
255
256 private:
257  HDivZeroCheck* const instruction_;
258  DISALLOW_COPY_AND_ASSIGN(DivZeroCheckSlowPathARM64);
259};
260
261class LoadClassSlowPathARM64 : public SlowPathCodeARM64 {
262 public:
263  LoadClassSlowPathARM64(HLoadClass* cls,
264                         HInstruction* at,
265                         uint32_t dex_pc,
266                         bool do_clinit)
267      : cls_(cls), at_(at), dex_pc_(dex_pc), do_clinit_(do_clinit) {
268    DCHECK(at->IsLoadClass() || at->IsClinitCheck());
269  }
270
271  void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
272    LocationSummary* locations = at_->GetLocations();
273    CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen);
274
275    __ Bind(GetEntryLabel());
276    SaveLiveRegisters(codegen, locations);
277
278    InvokeRuntimeCallingConvention calling_convention;
279    __ Mov(calling_convention.GetRegisterAt(0).W(), cls_->GetTypeIndex());
280    int32_t entry_point_offset = do_clinit_ ? QUICK_ENTRY_POINT(pInitializeStaticStorage)
281                                            : QUICK_ENTRY_POINT(pInitializeType);
282    arm64_codegen->InvokeRuntime(entry_point_offset, at_, dex_pc_, this);
283    if (do_clinit_) {
284      CheckEntrypointTypes<kQuickInitializeStaticStorage, void*, uint32_t>();
285    } else {
286      CheckEntrypointTypes<kQuickInitializeType, void*, uint32_t>();
287    }
288
289    // Move the class to the desired location.
290    Location out = locations->Out();
291    if (out.IsValid()) {
292      DCHECK(out.IsRegister() && !locations->GetLiveRegisters()->ContainsCoreRegister(out.reg()));
293      Primitive::Type type = at_->GetType();
294      arm64_codegen->MoveLocation(out, calling_convention.GetReturnLocation(type), type);
295    }
296
297    RestoreLiveRegisters(codegen, locations);
298    __ B(GetExitLabel());
299  }
300
301  const char* GetDescription() const OVERRIDE { return "LoadClassSlowPathARM64"; }
302
303 private:
304  // The class this slow path will load.
305  HLoadClass* const cls_;
306
307  // The instruction where this slow path is happening.
308  // (Might be the load class or an initialization check).
309  HInstruction* const at_;
310
311  // The dex PC of `at_`.
312  const uint32_t dex_pc_;
313
314  // Whether to initialize the class.
315  const bool do_clinit_;
316
317  DISALLOW_COPY_AND_ASSIGN(LoadClassSlowPathARM64);
318};
319
320class LoadStringSlowPathARM64 : public SlowPathCodeARM64 {
321 public:
322  explicit LoadStringSlowPathARM64(HLoadString* instruction) : instruction_(instruction) {}
323
324  void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
325    LocationSummary* locations = instruction_->GetLocations();
326    DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg()));
327    CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen);
328
329    __ Bind(GetEntryLabel());
330    SaveLiveRegisters(codegen, locations);
331
332    InvokeRuntimeCallingConvention calling_convention;
333    __ Mov(calling_convention.GetRegisterAt(0).W(), instruction_->GetStringIndex());
334    arm64_codegen->InvokeRuntime(
335        QUICK_ENTRY_POINT(pResolveString), instruction_, instruction_->GetDexPc(), this);
336    CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>();
337    Primitive::Type type = instruction_->GetType();
338    arm64_codegen->MoveLocation(locations->Out(), calling_convention.GetReturnLocation(type), type);
339
340    RestoreLiveRegisters(codegen, locations);
341    __ B(GetExitLabel());
342  }
343
344  const char* GetDescription() const OVERRIDE { return "LoadStringSlowPathARM64"; }
345
346 private:
347  HLoadString* const instruction_;
348
349  DISALLOW_COPY_AND_ASSIGN(LoadStringSlowPathARM64);
350};
351
352class NullCheckSlowPathARM64 : public SlowPathCodeARM64 {
353 public:
354  explicit NullCheckSlowPathARM64(HNullCheck* instr) : instruction_(instr) {}
355
356  void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
357    CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen);
358    __ Bind(GetEntryLabel());
359    if (instruction_->CanThrowIntoCatchBlock()) {
360      // Live registers will be restored in the catch block if caught.
361      SaveLiveRegisters(codegen, instruction_->GetLocations());
362    }
363    arm64_codegen->InvokeRuntime(
364        QUICK_ENTRY_POINT(pThrowNullPointer), instruction_, instruction_->GetDexPc(), this);
365    CheckEntrypointTypes<kQuickThrowNullPointer, void, void>();
366  }
367
368  bool IsFatal() const OVERRIDE { return true; }
369
370  const char* GetDescription() const OVERRIDE { return "NullCheckSlowPathARM64"; }
371
372 private:
373  HNullCheck* const instruction_;
374
375  DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathARM64);
376};
377
378class SuspendCheckSlowPathARM64 : public SlowPathCodeARM64 {
379 public:
380  SuspendCheckSlowPathARM64(HSuspendCheck* instruction, HBasicBlock* successor)
381      : instruction_(instruction), successor_(successor) {}
382
383  void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
384    CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen);
385    __ Bind(GetEntryLabel());
386    SaveLiveRegisters(codegen, instruction_->GetLocations());
387    arm64_codegen->InvokeRuntime(
388        QUICK_ENTRY_POINT(pTestSuspend), instruction_, instruction_->GetDexPc(), this);
389    CheckEntrypointTypes<kQuickTestSuspend, void, void>();
390    RestoreLiveRegisters(codegen, instruction_->GetLocations());
391    if (successor_ == nullptr) {
392      __ B(GetReturnLabel());
393    } else {
394      __ B(arm64_codegen->GetLabelOf(successor_));
395    }
396  }
397
398  vixl::Label* GetReturnLabel() {
399    DCHECK(successor_ == nullptr);
400    return &return_label_;
401  }
402
403  HBasicBlock* GetSuccessor() const {
404    return successor_;
405  }
406
407  const char* GetDescription() const OVERRIDE { return "SuspendCheckSlowPathARM64"; }
408
409 private:
410  HSuspendCheck* const instruction_;
411  // If not null, the block to branch to after the suspend check.
412  HBasicBlock* const successor_;
413
414  // If `successor_` is null, the label to branch to after the suspend check.
415  vixl::Label return_label_;
416
417  DISALLOW_COPY_AND_ASSIGN(SuspendCheckSlowPathARM64);
418};
419
420class TypeCheckSlowPathARM64 : public SlowPathCodeARM64 {
421 public:
422  TypeCheckSlowPathARM64(HInstruction* instruction, bool is_fatal)
423      : instruction_(instruction), is_fatal_(is_fatal) {}
424
425  void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
426    LocationSummary* locations = instruction_->GetLocations();
427    Location class_to_check = locations->InAt(1);
428    Location object_class = instruction_->IsCheckCast() ? locations->GetTemp(0)
429                                                        : locations->Out();
430    DCHECK(instruction_->IsCheckCast()
431           || !locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg()));
432    CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen);
433    uint32_t dex_pc = instruction_->GetDexPc();
434
435    __ Bind(GetEntryLabel());
436
437    if (!is_fatal_) {
438      SaveLiveRegisters(codegen, locations);
439    }
440
441    // We're moving two locations to locations that could overlap, so we need a parallel
442    // move resolver.
443    InvokeRuntimeCallingConvention calling_convention;
444    codegen->EmitParallelMoves(
445        class_to_check, LocationFrom(calling_convention.GetRegisterAt(0)), Primitive::kPrimNot,
446        object_class, LocationFrom(calling_convention.GetRegisterAt(1)), Primitive::kPrimNot);
447
448    if (instruction_->IsInstanceOf()) {
449      arm64_codegen->InvokeRuntime(
450          QUICK_ENTRY_POINT(pInstanceofNonTrivial), instruction_, dex_pc, this);
451      CheckEntrypointTypes<kQuickInstanceofNonTrivial, uint32_t,
452                           const mirror::Class*, const mirror::Class*>();
453      Primitive::Type ret_type = instruction_->GetType();
454      Location ret_loc = calling_convention.GetReturnLocation(ret_type);
455      arm64_codegen->MoveLocation(locations->Out(), ret_loc, ret_type);
456    } else {
457      DCHECK(instruction_->IsCheckCast());
458      arm64_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pCheckCast), instruction_, dex_pc, this);
459      CheckEntrypointTypes<kQuickCheckCast, void, const mirror::Class*, const mirror::Class*>();
460    }
461
462    if (!is_fatal_) {
463      RestoreLiveRegisters(codegen, locations);
464      __ B(GetExitLabel());
465    }
466  }
467
468  const char* GetDescription() const OVERRIDE { return "TypeCheckSlowPathARM64"; }
469  bool IsFatal() const { return is_fatal_; }
470
471 private:
472  HInstruction* const instruction_;
473  const bool is_fatal_;
474
475  DISALLOW_COPY_AND_ASSIGN(TypeCheckSlowPathARM64);
476};
477
478class DeoptimizationSlowPathARM64 : public SlowPathCodeARM64 {
479 public:
480  explicit DeoptimizationSlowPathARM64(HDeoptimize* instruction)
481      : instruction_(instruction) {}
482
483  void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
484    CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen);
485    __ Bind(GetEntryLabel());
486    SaveLiveRegisters(codegen, instruction_->GetLocations());
487    arm64_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pDeoptimize),
488                                 instruction_,
489                                 instruction_->GetDexPc(),
490                                 this);
491    CheckEntrypointTypes<kQuickDeoptimize, void, void>();
492  }
493
494  const char* GetDescription() const OVERRIDE { return "DeoptimizationSlowPathARM64"; }
495
496 private:
497  HDeoptimize* const instruction_;
498  DISALLOW_COPY_AND_ASSIGN(DeoptimizationSlowPathARM64);
499};
500
501class ArraySetSlowPathARM64 : public SlowPathCodeARM64 {
502 public:
503  explicit ArraySetSlowPathARM64(HInstruction* instruction) : instruction_(instruction) {}
504
505  void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
506    LocationSummary* locations = instruction_->GetLocations();
507    __ Bind(GetEntryLabel());
508    SaveLiveRegisters(codegen, locations);
509
510    InvokeRuntimeCallingConvention calling_convention;
511    HParallelMove parallel_move(codegen->GetGraph()->GetArena());
512    parallel_move.AddMove(
513        locations->InAt(0),
514        LocationFrom(calling_convention.GetRegisterAt(0)),
515        Primitive::kPrimNot,
516        nullptr);
517    parallel_move.AddMove(
518        locations->InAt(1),
519        LocationFrom(calling_convention.GetRegisterAt(1)),
520        Primitive::kPrimInt,
521        nullptr);
522    parallel_move.AddMove(
523        locations->InAt(2),
524        LocationFrom(calling_convention.GetRegisterAt(2)),
525        Primitive::kPrimNot,
526        nullptr);
527    codegen->GetMoveResolver()->EmitNativeCode(&parallel_move);
528
529    CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen);
530    arm64_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pAputObject),
531                                 instruction_,
532                                 instruction_->GetDexPc(),
533                                 this);
534    CheckEntrypointTypes<kQuickAputObject, void, mirror::Array*, int32_t, mirror::Object*>();
535    RestoreLiveRegisters(codegen, locations);
536    __ B(GetExitLabel());
537  }
538
539  const char* GetDescription() const OVERRIDE { return "ArraySetSlowPathARM64"; }
540
541 private:
542  HInstruction* const instruction_;
543
544  DISALLOW_COPY_AND_ASSIGN(ArraySetSlowPathARM64);
545};
546
547void JumpTableARM64::EmitTable(CodeGeneratorARM64* codegen) {
548  uint32_t num_entries = switch_instr_->GetNumEntries();
549  DCHECK_GE(num_entries, kPackedSwitchCompareJumpThreshold);
550
551  // We are about to use the assembler to place literals directly. Make sure we have enough
552  // underlying code buffer and we have generated the jump table with right size.
553  CodeBufferCheckScope scope(codegen->GetVIXLAssembler(), num_entries * sizeof(int32_t),
554                             CodeBufferCheckScope::kCheck, CodeBufferCheckScope::kExactSize);
555
556  __ Bind(&table_start_);
557  const ArenaVector<HBasicBlock*>& successors = switch_instr_->GetBlock()->GetSuccessors();
558  for (uint32_t i = 0; i < num_entries; i++) {
559    vixl::Label* target_label = codegen->GetLabelOf(successors[i]);
560    DCHECK(target_label->IsBound());
561    ptrdiff_t jump_offset = target_label->location() - table_start_.location();
562    DCHECK_GT(jump_offset, std::numeric_limits<int32_t>::min());
563    DCHECK_LE(jump_offset, std::numeric_limits<int32_t>::max());
564    Literal<int32_t> literal(jump_offset);
565    __ place(&literal);
566  }
567}
568
569// Slow path generating a read barrier for a heap reference.
570class ReadBarrierForHeapReferenceSlowPathARM64 : public SlowPathCodeARM64 {
571 public:
572  ReadBarrierForHeapReferenceSlowPathARM64(HInstruction* instruction,
573                                           Location out,
574                                           Location ref,
575                                           Location obj,
576                                           uint32_t offset,
577                                           Location index)
578      : instruction_(instruction),
579        out_(out),
580        ref_(ref),
581        obj_(obj),
582        offset_(offset),
583        index_(index) {
584    DCHECK(kEmitCompilerReadBarrier);
585    // If `obj` is equal to `out` or `ref`, it means the initial object
586    // has been overwritten by (or after) the heap object reference load
587    // to be instrumented, e.g.:
588    //
589    //   __ Ldr(out, HeapOperand(out, class_offset);
590    //   codegen_->GenerateReadBarrier(instruction, out_loc, out_loc, out_loc, offset);
591    //
592    // In that case, we have lost the information about the original
593    // object, and the emitted read barrier cannot work properly.
594    DCHECK(!obj.Equals(out)) << "obj=" << obj << " out=" << out;
595    DCHECK(!obj.Equals(ref)) << "obj=" << obj << " ref=" << ref;
596  }
597
598  void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
599    CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen);
600    LocationSummary* locations = instruction_->GetLocations();
601    Primitive::Type type = Primitive::kPrimNot;
602    DCHECK(locations->CanCall());
603    DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(out_.reg()));
604    DCHECK(!instruction_->IsInvoke() ||
605           (instruction_->IsInvokeStaticOrDirect() &&
606            instruction_->GetLocations()->Intrinsified()));
607
608    __ Bind(GetEntryLabel());
609
610    // Note: In the case of a HArrayGet instruction, when the base
611    // address is a HArm64IntermediateAddress instruction, it does not
612    // point to the array object itself, but to an offset within this
613    // object. However, the read barrier entry point needs the array
614    // object address to be passed as first argument. So we
615    // temporarily set back `obj_` to that address, and restore its
616    // initial value later.
617    if (instruction_->IsArrayGet() &&
618        instruction_->AsArrayGet()->GetArray()->IsArm64IntermediateAddress()) {
619      if (kIsDebugBuild) {
620        HArm64IntermediateAddress* intermediate_address =
621            instruction_->AsArrayGet()->GetArray()->AsArm64IntermediateAddress();
622        uint32_t intermediate_address_offset =
623            intermediate_address->GetOffset()->AsIntConstant()->GetValueAsUint64();
624        DCHECK_EQ(intermediate_address_offset, offset_);
625        DCHECK_EQ(mirror::Array::DataOffset(Primitive::ComponentSize(type)).Uint32Value(), offset_);
626      }
627      Register obj_reg = RegisterFrom(obj_, Primitive::kPrimInt);
628      __ Sub(obj_reg, obj_reg, offset_);
629    }
630
631    SaveLiveRegisters(codegen, locations);
632
633    // We may have to change the index's value, but as `index_` is a
634    // constant member (like other "inputs" of this slow path),
635    // introduce a copy of it, `index`.
636    Location index = index_;
637    if (index_.IsValid()) {
638      // Handle `index_` for HArrayGet and intrinsic UnsafeGetObject.
639      if (instruction_->IsArrayGet()) {
640        // Compute the actual memory offset and store it in `index`.
641        Register index_reg = RegisterFrom(index_, Primitive::kPrimInt);
642        DCHECK(locations->GetLiveRegisters()->ContainsCoreRegister(index_.reg()));
643        if (codegen->IsCoreCalleeSaveRegister(index_.reg())) {
644          // We are about to change the value of `index_reg` (see the
645          // calls to vixl::MacroAssembler::Lsl and
646          // vixl::MacroAssembler::Mov below), but it has
647          // not been saved by the previous call to
648          // art::SlowPathCode::SaveLiveRegisters, as it is a
649          // callee-save register --
650          // art::SlowPathCode::SaveLiveRegisters does not consider
651          // callee-save registers, as it has been designed with the
652          // assumption that callee-save registers are supposed to be
653          // handled by the called function.  So, as a callee-save
654          // register, `index_reg` _would_ eventually be saved onto
655          // the stack, but it would be too late: we would have
656          // changed its value earlier.  Therefore, we manually save
657          // it here into another freely available register,
658          // `free_reg`, chosen of course among the caller-save
659          // registers (as a callee-save `free_reg` register would
660          // exhibit the same problem).
661          //
662          // Note we could have requested a temporary register from
663          // the register allocator instead; but we prefer not to, as
664          // this is a slow path, and we know we can find a
665          // caller-save register that is available.
666          Register free_reg = FindAvailableCallerSaveRegister(codegen);
667          __ Mov(free_reg.W(), index_reg);
668          index_reg = free_reg;
669          index = LocationFrom(index_reg);
670        } else {
671          // The initial register stored in `index_` has already been
672          // saved in the call to art::SlowPathCode::SaveLiveRegisters
673          // (as it is not a callee-save register), so we can freely
674          // use it.
675        }
676        // Shifting the index value contained in `index_reg` by the scale
677        // factor (2) cannot overflow in practice, as the runtime is
678        // unable to allocate object arrays with a size larger than
679        // 2^26 - 1 (that is, 2^28 - 4 bytes).
680        __ Lsl(index_reg, index_reg, Primitive::ComponentSizeShift(type));
681        static_assert(
682            sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
683            "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
684        __ Add(index_reg, index_reg, Operand(offset_));
685      } else {
686        DCHECK(instruction_->IsInvoke());
687        DCHECK(instruction_->GetLocations()->Intrinsified());
688        DCHECK((instruction_->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObject) ||
689               (instruction_->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile))
690            << instruction_->AsInvoke()->GetIntrinsic();
691        DCHECK_EQ(offset_, 0U);
692        DCHECK(index_.IsRegisterPair());
693        // UnsafeGet's offset location is a register pair, the low
694        // part contains the correct offset.
695        index = index_.ToLow();
696      }
697    }
698
699    // We're moving two or three locations to locations that could
700    // overlap, so we need a parallel move resolver.
701    InvokeRuntimeCallingConvention calling_convention;
702    HParallelMove parallel_move(codegen->GetGraph()->GetArena());
703    parallel_move.AddMove(ref_,
704                          LocationFrom(calling_convention.GetRegisterAt(0)),
705                          type,
706                          nullptr);
707    parallel_move.AddMove(obj_,
708                          LocationFrom(calling_convention.GetRegisterAt(1)),
709                          type,
710                          nullptr);
711    if (index.IsValid()) {
712      parallel_move.AddMove(index,
713                            LocationFrom(calling_convention.GetRegisterAt(2)),
714                            Primitive::kPrimInt,
715                            nullptr);
716      codegen->GetMoveResolver()->EmitNativeCode(&parallel_move);
717    } else {
718      codegen->GetMoveResolver()->EmitNativeCode(&parallel_move);
719      arm64_codegen->MoveConstant(LocationFrom(calling_convention.GetRegisterAt(2)), offset_);
720    }
721    arm64_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pReadBarrierSlow),
722                                 instruction_,
723                                 instruction_->GetDexPc(),
724                                 this);
725    CheckEntrypointTypes<
726        kQuickReadBarrierSlow, mirror::Object*, mirror::Object*, mirror::Object*, uint32_t>();
727    arm64_codegen->MoveLocation(out_, calling_convention.GetReturnLocation(type), type);
728
729    RestoreLiveRegisters(codegen, locations);
730
731    // Restore the value of `obj_` when it corresponds to a
732    // HArm64IntermediateAddress instruction.
733    if (instruction_->IsArrayGet() &&
734        instruction_->AsArrayGet()->GetArray()->IsArm64IntermediateAddress()) {
735      if (kIsDebugBuild) {
736        HArm64IntermediateAddress* intermediate_address =
737            instruction_->AsArrayGet()->GetArray()->AsArm64IntermediateAddress();
738        uint32_t intermediate_address_offset =
739            intermediate_address->GetOffset()->AsIntConstant()->GetValueAsUint64();
740        DCHECK_EQ(intermediate_address_offset, offset_);
741        DCHECK_EQ(mirror::Array::DataOffset(Primitive::ComponentSize(type)).Uint32Value(), offset_);
742      }
743      Register obj_reg = RegisterFrom(obj_, Primitive::kPrimInt);
744      __ Add(obj_reg, obj_reg, offset_);
745    }
746
747    __ B(GetExitLabel());
748  }
749
750  const char* GetDescription() const OVERRIDE { return "ReadBarrierForHeapReferenceSlowPathARM64"; }
751
752 private:
753  Register FindAvailableCallerSaveRegister(CodeGenerator* codegen) {
754    size_t ref = static_cast<int>(XRegisterFrom(ref_).code());
755    size_t obj = static_cast<int>(XRegisterFrom(obj_).code());
756    for (size_t i = 0, e = codegen->GetNumberOfCoreRegisters(); i < e; ++i) {
757      if (i != ref && i != obj && !codegen->IsCoreCalleeSaveRegister(i)) {
758        return Register(VIXLRegCodeFromART(i), kXRegSize);
759      }
760    }
761    // We shall never fail to find a free caller-save register, as
762    // there are more than two core caller-save registers on ARM64
763    // (meaning it is possible to find one which is different from
764    // `ref` and `obj`).
765    DCHECK_GT(codegen->GetNumberOfCoreCallerSaveRegisters(), 2u);
766    LOG(FATAL) << "Could not find a free register";
767    UNREACHABLE();
768  }
769
770  HInstruction* const instruction_;
771  const Location out_;
772  const Location ref_;
773  const Location obj_;
774  const uint32_t offset_;
775  // An additional location containing an index to an array.
776  // Only used for HArrayGet and the UnsafeGetObject &
777  // UnsafeGetObjectVolatile intrinsics.
778  const Location index_;
779
780  DISALLOW_COPY_AND_ASSIGN(ReadBarrierForHeapReferenceSlowPathARM64);
781};
782
783// Slow path generating a read barrier for a GC root.
784class ReadBarrierForRootSlowPathARM64 : public SlowPathCodeARM64 {
785 public:
786  ReadBarrierForRootSlowPathARM64(HInstruction* instruction, Location out, Location root)
787      : instruction_(instruction), out_(out), root_(root) {}
788
789  void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
790    LocationSummary* locations = instruction_->GetLocations();
791    Primitive::Type type = Primitive::kPrimNot;
792    DCHECK(locations->CanCall());
793    DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(out_.reg()));
794    DCHECK(instruction_->IsLoadClass() || instruction_->IsLoadString());
795
796    __ Bind(GetEntryLabel());
797    SaveLiveRegisters(codegen, locations);
798
799    InvokeRuntimeCallingConvention calling_convention;
800    CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen);
801    // The argument of the ReadBarrierForRootSlow is not a managed
802    // reference (`mirror::Object*`), but a `GcRoot<mirror::Object>*`;
803    // thus we need a 64-bit move here, and we cannot use
804    //
805    //   arm64_codegen->MoveLocation(
806    //       LocationFrom(calling_convention.GetRegisterAt(0)),
807    //       root_,
808    //       type);
809    //
810    // which would emit a 32-bit move, as `type` is a (32-bit wide)
811    // reference type (`Primitive::kPrimNot`).
812    __ Mov(calling_convention.GetRegisterAt(0), XRegisterFrom(out_));
813    arm64_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pReadBarrierForRootSlow),
814                                 instruction_,
815                                 instruction_->GetDexPc(),
816                                 this);
817    CheckEntrypointTypes<kQuickReadBarrierForRootSlow, mirror::Object*, GcRoot<mirror::Object>*>();
818    arm64_codegen->MoveLocation(out_, calling_convention.GetReturnLocation(type), type);
819
820    RestoreLiveRegisters(codegen, locations);
821    __ B(GetExitLabel());
822  }
823
824  const char* GetDescription() const OVERRIDE { return "ReadBarrierForRootSlowPathARM64"; }
825
826 private:
827  HInstruction* const instruction_;
828  const Location out_;
829  const Location root_;
830
831  DISALLOW_COPY_AND_ASSIGN(ReadBarrierForRootSlowPathARM64);
832};
833
834#undef __
835
836Location InvokeDexCallingConventionVisitorARM64::GetNextLocation(Primitive::Type type) {
837  Location next_location;
838  if (type == Primitive::kPrimVoid) {
839    LOG(FATAL) << "Unreachable type " << type;
840  }
841
842  if (Primitive::IsFloatingPointType(type) &&
843      (float_index_ < calling_convention.GetNumberOfFpuRegisters())) {
844    next_location = LocationFrom(calling_convention.GetFpuRegisterAt(float_index_++));
845  } else if (!Primitive::IsFloatingPointType(type) &&
846             (gp_index_ < calling_convention.GetNumberOfRegisters())) {
847    next_location = LocationFrom(calling_convention.GetRegisterAt(gp_index_++));
848  } else {
849    size_t stack_offset = calling_convention.GetStackOffsetOf(stack_index_);
850    next_location = Primitive::Is64BitType(type) ? Location::DoubleStackSlot(stack_offset)
851                                                 : Location::StackSlot(stack_offset);
852  }
853
854  // Space on the stack is reserved for all arguments.
855  stack_index_ += Primitive::Is64BitType(type) ? 2 : 1;
856  return next_location;
857}
858
859Location InvokeDexCallingConventionVisitorARM64::GetMethodLocation() const {
860  return LocationFrom(kArtMethodRegister);
861}
862
863CodeGeneratorARM64::CodeGeneratorARM64(HGraph* graph,
864                                       const Arm64InstructionSetFeatures& isa_features,
865                                       const CompilerOptions& compiler_options,
866                                       OptimizingCompilerStats* stats)
867    : CodeGenerator(graph,
868                    kNumberOfAllocatableRegisters,
869                    kNumberOfAllocatableFPRegisters,
870                    kNumberOfAllocatableRegisterPairs,
871                    callee_saved_core_registers.list(),
872                    callee_saved_fp_registers.list(),
873                    compiler_options,
874                    stats),
875      block_labels_(nullptr),
876      jump_tables_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
877      location_builder_(graph, this),
878      instruction_visitor_(graph, this),
879      move_resolver_(graph->GetArena(), this),
880      isa_features_(isa_features),
881      uint64_literals_(std::less<uint64_t>(),
882                       graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
883      method_patches_(MethodReferenceComparator(),
884                      graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
885      call_patches_(MethodReferenceComparator(),
886                    graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
887      relative_call_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
888      pc_relative_dex_cache_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) {
889  // Save the link register (containing the return address) to mimic Quick.
890  AddAllocatedRegister(LocationFrom(lr));
891}
892
893#define __ GetVIXLAssembler()->
894
895void CodeGeneratorARM64::EmitJumpTables() {
896  for (auto jump_table : jump_tables_) {
897    jump_table->EmitTable(this);
898  }
899}
900
901void CodeGeneratorARM64::Finalize(CodeAllocator* allocator) {
902  EmitJumpTables();
903  // Ensure we emit the literal pool.
904  __ FinalizeCode();
905
906  CodeGenerator::Finalize(allocator);
907}
908
909void ParallelMoveResolverARM64::PrepareForEmitNativeCode() {
910  // Note: There are 6 kinds of moves:
911  // 1. constant -> GPR/FPR (non-cycle)
912  // 2. constant -> stack (non-cycle)
913  // 3. GPR/FPR -> GPR/FPR
914  // 4. GPR/FPR -> stack
915  // 5. stack -> GPR/FPR
916  // 6. stack -> stack (non-cycle)
917  // Case 1, 2 and 6 should never be included in a dependency cycle on ARM64. For case 3, 4, and 5
918  // VIXL uses at most 1 GPR. VIXL has 2 GPR and 1 FPR temps, and there should be no intersecting
919  // cycles on ARM64, so we always have 1 GPR and 1 FPR available VIXL temps to resolve the
920  // dependency.
921  vixl_temps_.Open(GetVIXLAssembler());
922}
923
924void ParallelMoveResolverARM64::FinishEmitNativeCode() {
925  vixl_temps_.Close();
926}
927
928Location ParallelMoveResolverARM64::AllocateScratchLocationFor(Location::Kind kind) {
929  DCHECK(kind == Location::kRegister || kind == Location::kFpuRegister ||
930         kind == Location::kStackSlot || kind == Location::kDoubleStackSlot);
931  kind = (kind == Location::kFpuRegister) ? Location::kFpuRegister : Location::kRegister;
932  Location scratch = GetScratchLocation(kind);
933  if (!scratch.Equals(Location::NoLocation())) {
934    return scratch;
935  }
936  // Allocate from VIXL temp registers.
937  if (kind == Location::kRegister) {
938    scratch = LocationFrom(vixl_temps_.AcquireX());
939  } else {
940    DCHECK(kind == Location::kFpuRegister);
941    scratch = LocationFrom(vixl_temps_.AcquireD());
942  }
943  AddScratchLocation(scratch);
944  return scratch;
945}
946
947void ParallelMoveResolverARM64::FreeScratchLocation(Location loc) {
948  if (loc.IsRegister()) {
949    vixl_temps_.Release(XRegisterFrom(loc));
950  } else {
951    DCHECK(loc.IsFpuRegister());
952    vixl_temps_.Release(DRegisterFrom(loc));
953  }
954  RemoveScratchLocation(loc);
955}
956
957void ParallelMoveResolverARM64::EmitMove(size_t index) {
958  MoveOperands* move = moves_[index];
959  codegen_->MoveLocation(move->GetDestination(), move->GetSource(), Primitive::kPrimVoid);
960}
961
962void CodeGeneratorARM64::GenerateFrameEntry() {
963  MacroAssembler* masm = GetVIXLAssembler();
964  BlockPoolsScope block_pools(masm);
965  __ Bind(&frame_entry_label_);
966
967  bool do_overflow_check = FrameNeedsStackCheck(GetFrameSize(), kArm64) || !IsLeafMethod();
968  if (do_overflow_check) {
969    UseScratchRegisterScope temps(masm);
970    Register temp = temps.AcquireX();
971    DCHECK(GetCompilerOptions().GetImplicitStackOverflowChecks());
972    __ Sub(temp, sp, static_cast<int32_t>(GetStackOverflowReservedBytes(kArm64)));
973    __ Ldr(wzr, MemOperand(temp, 0));
974    RecordPcInfo(nullptr, 0);
975  }
976
977  if (!HasEmptyFrame()) {
978    int frame_size = GetFrameSize();
979    // Stack layout:
980    //      sp[frame_size - 8]        : lr.
981    //      ...                       : other preserved core registers.
982    //      ...                       : other preserved fp registers.
983    //      ...                       : reserved frame space.
984    //      sp[0]                     : current method.
985    __ Str(kArtMethodRegister, MemOperand(sp, -frame_size, PreIndex));
986    GetAssembler()->cfi().AdjustCFAOffset(frame_size);
987    GetAssembler()->SpillRegisters(GetFramePreservedCoreRegisters(),
988        frame_size - GetCoreSpillSize());
989    GetAssembler()->SpillRegisters(GetFramePreservedFPRegisters(),
990        frame_size - FrameEntrySpillSize());
991  }
992}
993
994void CodeGeneratorARM64::GenerateFrameExit() {
995  BlockPoolsScope block_pools(GetVIXLAssembler());
996  GetAssembler()->cfi().RememberState();
997  if (!HasEmptyFrame()) {
998    int frame_size = GetFrameSize();
999    GetAssembler()->UnspillRegisters(GetFramePreservedFPRegisters(),
1000        frame_size - FrameEntrySpillSize());
1001    GetAssembler()->UnspillRegisters(GetFramePreservedCoreRegisters(),
1002        frame_size - GetCoreSpillSize());
1003    __ Drop(frame_size);
1004    GetAssembler()->cfi().AdjustCFAOffset(-frame_size);
1005  }
1006  __ Ret();
1007  GetAssembler()->cfi().RestoreState();
1008  GetAssembler()->cfi().DefCFAOffset(GetFrameSize());
1009}
1010
1011vixl::CPURegList CodeGeneratorARM64::GetFramePreservedCoreRegisters() const {
1012  DCHECK(ArtVixlRegCodeCoherentForRegSet(core_spill_mask_, GetNumberOfCoreRegisters(), 0, 0));
1013  return vixl::CPURegList(vixl::CPURegister::kRegister, vixl::kXRegSize,
1014                          core_spill_mask_);
1015}
1016
1017vixl::CPURegList CodeGeneratorARM64::GetFramePreservedFPRegisters() const {
1018  DCHECK(ArtVixlRegCodeCoherentForRegSet(0, 0, fpu_spill_mask_,
1019                                         GetNumberOfFloatingPointRegisters()));
1020  return vixl::CPURegList(vixl::CPURegister::kFPRegister, vixl::kDRegSize,
1021                          fpu_spill_mask_);
1022}
1023
1024void CodeGeneratorARM64::Bind(HBasicBlock* block) {
1025  __ Bind(GetLabelOf(block));
1026}
1027
1028void CodeGeneratorARM64::Move(HInstruction* instruction,
1029                              Location location,
1030                              HInstruction* move_for) {
1031  LocationSummary* locations = instruction->GetLocations();
1032  Primitive::Type type = instruction->GetType();
1033  DCHECK_NE(type, Primitive::kPrimVoid);
1034
1035  if (instruction->IsFakeString()) {
1036    // The fake string is an alias for null.
1037    DCHECK(IsBaseline());
1038    instruction = locations->Out().GetConstant();
1039    DCHECK(instruction->IsNullConstant()) << instruction->DebugName();
1040  }
1041
1042  if (instruction->IsCurrentMethod()) {
1043    MoveLocation(location,
1044                 Location::DoubleStackSlot(kCurrentMethodStackOffset),
1045                 Primitive::kPrimVoid);
1046  } else if (locations != nullptr && locations->Out().Equals(location)) {
1047    return;
1048  } else if (instruction->IsIntConstant()
1049             || instruction->IsLongConstant()
1050             || instruction->IsNullConstant()) {
1051    int64_t value = GetInt64ValueOf(instruction->AsConstant());
1052    if (location.IsRegister()) {
1053      Register dst = RegisterFrom(location, type);
1054      DCHECK(((instruction->IsIntConstant() || instruction->IsNullConstant()) && dst.Is32Bits()) ||
1055             (instruction->IsLongConstant() && dst.Is64Bits()));
1056      __ Mov(dst, value);
1057    } else {
1058      DCHECK(location.IsStackSlot() || location.IsDoubleStackSlot());
1059      UseScratchRegisterScope temps(GetVIXLAssembler());
1060      Register temp = (instruction->IsIntConstant() || instruction->IsNullConstant())
1061          ? temps.AcquireW()
1062          : temps.AcquireX();
1063      __ Mov(temp, value);
1064      __ Str(temp, StackOperandFrom(location));
1065    }
1066  } else if (instruction->IsTemporary()) {
1067    Location temp_location = GetTemporaryLocation(instruction->AsTemporary());
1068    MoveLocation(location, temp_location, type);
1069  } else if (instruction->IsLoadLocal()) {
1070    uint32_t stack_slot = GetStackSlot(instruction->AsLoadLocal()->GetLocal());
1071    if (Primitive::Is64BitType(type)) {
1072      MoveLocation(location, Location::DoubleStackSlot(stack_slot), type);
1073    } else {
1074      MoveLocation(location, Location::StackSlot(stack_slot), type);
1075    }
1076
1077  } else {
1078    DCHECK((instruction->GetNext() == move_for) || instruction->GetNext()->IsTemporary());
1079    MoveLocation(location, locations->Out(), type);
1080  }
1081}
1082
1083void CodeGeneratorARM64::MoveConstant(Location location, int32_t value) {
1084  DCHECK(location.IsRegister());
1085  __ Mov(RegisterFrom(location, Primitive::kPrimInt), value);
1086}
1087
1088void CodeGeneratorARM64::AddLocationAsTemp(Location location, LocationSummary* locations) {
1089  if (location.IsRegister()) {
1090    locations->AddTemp(location);
1091  } else {
1092    UNIMPLEMENTED(FATAL) << "AddLocationAsTemp not implemented for location " << location;
1093  }
1094}
1095
1096Location CodeGeneratorARM64::GetStackLocation(HLoadLocal* load) const {
1097  Primitive::Type type = load->GetType();
1098
1099  switch (type) {
1100    case Primitive::kPrimNot:
1101    case Primitive::kPrimInt:
1102    case Primitive::kPrimFloat:
1103      return Location::StackSlot(GetStackSlot(load->GetLocal()));
1104
1105    case Primitive::kPrimLong:
1106    case Primitive::kPrimDouble:
1107      return Location::DoubleStackSlot(GetStackSlot(load->GetLocal()));
1108
1109    case Primitive::kPrimBoolean:
1110    case Primitive::kPrimByte:
1111    case Primitive::kPrimChar:
1112    case Primitive::kPrimShort:
1113    case Primitive::kPrimVoid:
1114      LOG(FATAL) << "Unexpected type " << type;
1115  }
1116
1117  LOG(FATAL) << "Unreachable";
1118  return Location::NoLocation();
1119}
1120
1121void CodeGeneratorARM64::MarkGCCard(Register object, Register value, bool value_can_be_null) {
1122  UseScratchRegisterScope temps(GetVIXLAssembler());
1123  Register card = temps.AcquireX();
1124  Register temp = temps.AcquireW();   // Index within the CardTable - 32bit.
1125  vixl::Label done;
1126  if (value_can_be_null) {
1127    __ Cbz(value, &done);
1128  }
1129  __ Ldr(card, MemOperand(tr, Thread::CardTableOffset<kArm64WordSize>().Int32Value()));
1130  __ Lsr(temp, object, gc::accounting::CardTable::kCardShift);
1131  __ Strb(card, MemOperand(card, temp.X()));
1132  if (value_can_be_null) {
1133    __ Bind(&done);
1134  }
1135}
1136
1137void CodeGeneratorARM64::SetupBlockedRegisters(bool is_baseline) const {
1138  // Blocked core registers:
1139  //      lr        : Runtime reserved.
1140  //      tr        : Runtime reserved.
1141  //      xSuspend  : Runtime reserved. TODO: Unblock this when the runtime stops using it.
1142  //      ip1       : VIXL core temp.
1143  //      ip0       : VIXL core temp.
1144  //
1145  // Blocked fp registers:
1146  //      d31       : VIXL fp temp.
1147  CPURegList reserved_core_registers = vixl_reserved_core_registers;
1148  reserved_core_registers.Combine(runtime_reserved_core_registers);
1149  while (!reserved_core_registers.IsEmpty()) {
1150    blocked_core_registers_[reserved_core_registers.PopLowestIndex().code()] = true;
1151  }
1152
1153  CPURegList reserved_fp_registers = vixl_reserved_fp_registers;
1154  while (!reserved_fp_registers.IsEmpty()) {
1155    blocked_fpu_registers_[reserved_fp_registers.PopLowestIndex().code()] = true;
1156  }
1157
1158  if (is_baseline) {
1159    CPURegList reserved_core_baseline_registers = callee_saved_core_registers;
1160    while (!reserved_core_baseline_registers.IsEmpty()) {
1161      blocked_core_registers_[reserved_core_baseline_registers.PopLowestIndex().code()] = true;
1162    }
1163  }
1164
1165  if (is_baseline || GetGraph()->IsDebuggable()) {
1166    // Stubs do not save callee-save floating point registers. If the graph
1167    // is debuggable, we need to deal with these registers differently. For
1168    // now, just block them.
1169    CPURegList reserved_fp_baseline_registers = callee_saved_fp_registers;
1170    while (!reserved_fp_baseline_registers.IsEmpty()) {
1171      blocked_fpu_registers_[reserved_fp_baseline_registers.PopLowestIndex().code()] = true;
1172    }
1173  }
1174}
1175
1176Location CodeGeneratorARM64::AllocateFreeRegister(Primitive::Type type) const {
1177  if (type == Primitive::kPrimVoid) {
1178    LOG(FATAL) << "Unreachable type " << type;
1179  }
1180
1181  if (Primitive::IsFloatingPointType(type)) {
1182    ssize_t reg = FindFreeEntry(blocked_fpu_registers_, kNumberOfAllocatableFPRegisters);
1183    DCHECK_NE(reg, -1);
1184    return Location::FpuRegisterLocation(reg);
1185  } else {
1186    ssize_t reg = FindFreeEntry(blocked_core_registers_, kNumberOfAllocatableRegisters);
1187    DCHECK_NE(reg, -1);
1188    return Location::RegisterLocation(reg);
1189  }
1190}
1191
1192size_t CodeGeneratorARM64::SaveCoreRegister(size_t stack_index, uint32_t reg_id) {
1193  Register reg = Register(VIXLRegCodeFromART(reg_id), kXRegSize);
1194  __ Str(reg, MemOperand(sp, stack_index));
1195  return kArm64WordSize;
1196}
1197
1198size_t CodeGeneratorARM64::RestoreCoreRegister(size_t stack_index, uint32_t reg_id) {
1199  Register reg = Register(VIXLRegCodeFromART(reg_id), kXRegSize);
1200  __ Ldr(reg, MemOperand(sp, stack_index));
1201  return kArm64WordSize;
1202}
1203
1204size_t CodeGeneratorARM64::SaveFloatingPointRegister(size_t stack_index, uint32_t reg_id) {
1205  FPRegister reg = FPRegister(reg_id, kDRegSize);
1206  __ Str(reg, MemOperand(sp, stack_index));
1207  return kArm64WordSize;
1208}
1209
1210size_t CodeGeneratorARM64::RestoreFloatingPointRegister(size_t stack_index, uint32_t reg_id) {
1211  FPRegister reg = FPRegister(reg_id, kDRegSize);
1212  __ Ldr(reg, MemOperand(sp, stack_index));
1213  return kArm64WordSize;
1214}
1215
1216void CodeGeneratorARM64::DumpCoreRegister(std::ostream& stream, int reg) const {
1217  stream << XRegister(reg);
1218}
1219
1220void CodeGeneratorARM64::DumpFloatingPointRegister(std::ostream& stream, int reg) const {
1221  stream << DRegister(reg);
1222}
1223
1224void CodeGeneratorARM64::MoveConstant(CPURegister destination, HConstant* constant) {
1225  if (constant->IsIntConstant()) {
1226    __ Mov(Register(destination), constant->AsIntConstant()->GetValue());
1227  } else if (constant->IsLongConstant()) {
1228    __ Mov(Register(destination), constant->AsLongConstant()->GetValue());
1229  } else if (constant->IsNullConstant()) {
1230    __ Mov(Register(destination), 0);
1231  } else if (constant->IsFloatConstant()) {
1232    __ Fmov(FPRegister(destination), constant->AsFloatConstant()->GetValue());
1233  } else {
1234    DCHECK(constant->IsDoubleConstant());
1235    __ Fmov(FPRegister(destination), constant->AsDoubleConstant()->GetValue());
1236  }
1237}
1238
1239
1240static bool CoherentConstantAndType(Location constant, Primitive::Type type) {
1241  DCHECK(constant.IsConstant());
1242  HConstant* cst = constant.GetConstant();
1243  return (cst->IsIntConstant() && type == Primitive::kPrimInt) ||
1244         // Null is mapped to a core W register, which we associate with kPrimInt.
1245         (cst->IsNullConstant() && type == Primitive::kPrimInt) ||
1246         (cst->IsLongConstant() && type == Primitive::kPrimLong) ||
1247         (cst->IsFloatConstant() && type == Primitive::kPrimFloat) ||
1248         (cst->IsDoubleConstant() && type == Primitive::kPrimDouble);
1249}
1250
1251void CodeGeneratorARM64::MoveLocation(Location destination,
1252                                      Location source,
1253                                      Primitive::Type dst_type) {
1254  if (source.Equals(destination)) {
1255    return;
1256  }
1257
1258  // A valid move can always be inferred from the destination and source
1259  // locations. When moving from and to a register, the argument type can be
1260  // used to generate 32bit instead of 64bit moves. In debug mode we also
1261  // checks the coherency of the locations and the type.
1262  bool unspecified_type = (dst_type == Primitive::kPrimVoid);
1263
1264  if (destination.IsRegister() || destination.IsFpuRegister()) {
1265    if (unspecified_type) {
1266      HConstant* src_cst = source.IsConstant() ? source.GetConstant() : nullptr;
1267      if (source.IsStackSlot() ||
1268          (src_cst != nullptr && (src_cst->IsIntConstant()
1269                                  || src_cst->IsFloatConstant()
1270                                  || src_cst->IsNullConstant()))) {
1271        // For stack slots and 32bit constants, a 64bit type is appropriate.
1272        dst_type = destination.IsRegister() ? Primitive::kPrimInt : Primitive::kPrimFloat;
1273      } else {
1274        // If the source is a double stack slot or a 64bit constant, a 64bit
1275        // type is appropriate. Else the source is a register, and since the
1276        // type has not been specified, we chose a 64bit type to force a 64bit
1277        // move.
1278        dst_type = destination.IsRegister() ? Primitive::kPrimLong : Primitive::kPrimDouble;
1279      }
1280    }
1281    DCHECK((destination.IsFpuRegister() && Primitive::IsFloatingPointType(dst_type)) ||
1282           (destination.IsRegister() && !Primitive::IsFloatingPointType(dst_type)));
1283    CPURegister dst = CPURegisterFrom(destination, dst_type);
1284    if (source.IsStackSlot() || source.IsDoubleStackSlot()) {
1285      DCHECK(dst.Is64Bits() == source.IsDoubleStackSlot());
1286      __ Ldr(dst, StackOperandFrom(source));
1287    } else if (source.IsConstant()) {
1288      DCHECK(CoherentConstantAndType(source, dst_type));
1289      MoveConstant(dst, source.GetConstant());
1290    } else if (source.IsRegister()) {
1291      if (destination.IsRegister()) {
1292        __ Mov(Register(dst), RegisterFrom(source, dst_type));
1293      } else {
1294        DCHECK(destination.IsFpuRegister());
1295        Primitive::Type source_type = Primitive::Is64BitType(dst_type)
1296            ? Primitive::kPrimLong
1297            : Primitive::kPrimInt;
1298        __ Fmov(FPRegisterFrom(destination, dst_type), RegisterFrom(source, source_type));
1299      }
1300    } else {
1301      DCHECK(source.IsFpuRegister());
1302      if (destination.IsRegister()) {
1303        Primitive::Type source_type = Primitive::Is64BitType(dst_type)
1304            ? Primitive::kPrimDouble
1305            : Primitive::kPrimFloat;
1306        __ Fmov(RegisterFrom(destination, dst_type), FPRegisterFrom(source, source_type));
1307      } else {
1308        DCHECK(destination.IsFpuRegister());
1309        __ Fmov(FPRegister(dst), FPRegisterFrom(source, dst_type));
1310      }
1311    }
1312  } else {  // The destination is not a register. It must be a stack slot.
1313    DCHECK(destination.IsStackSlot() || destination.IsDoubleStackSlot());
1314    if (source.IsRegister() || source.IsFpuRegister()) {
1315      if (unspecified_type) {
1316        if (source.IsRegister()) {
1317          dst_type = destination.IsStackSlot() ? Primitive::kPrimInt : Primitive::kPrimLong;
1318        } else {
1319          dst_type = destination.IsStackSlot() ? Primitive::kPrimFloat : Primitive::kPrimDouble;
1320        }
1321      }
1322      DCHECK((destination.IsDoubleStackSlot() == Primitive::Is64BitType(dst_type)) &&
1323             (source.IsFpuRegister() == Primitive::IsFloatingPointType(dst_type)));
1324      __ Str(CPURegisterFrom(source, dst_type), StackOperandFrom(destination));
1325    } else if (source.IsConstant()) {
1326      DCHECK(unspecified_type || CoherentConstantAndType(source, dst_type))
1327          << source << " " << dst_type;
1328      UseScratchRegisterScope temps(GetVIXLAssembler());
1329      HConstant* src_cst = source.GetConstant();
1330      CPURegister temp;
1331      if (src_cst->IsIntConstant() || src_cst->IsNullConstant()) {
1332        temp = temps.AcquireW();
1333      } else if (src_cst->IsLongConstant()) {
1334        temp = temps.AcquireX();
1335      } else if (src_cst->IsFloatConstant()) {
1336        temp = temps.AcquireS();
1337      } else {
1338        DCHECK(src_cst->IsDoubleConstant());
1339        temp = temps.AcquireD();
1340      }
1341      MoveConstant(temp, src_cst);
1342      __ Str(temp, StackOperandFrom(destination));
1343    } else {
1344      DCHECK(source.IsStackSlot() || source.IsDoubleStackSlot());
1345      DCHECK(source.IsDoubleStackSlot() == destination.IsDoubleStackSlot());
1346      UseScratchRegisterScope temps(GetVIXLAssembler());
1347      // There is generally less pressure on FP registers.
1348      FPRegister temp = destination.IsDoubleStackSlot() ? temps.AcquireD() : temps.AcquireS();
1349      __ Ldr(temp, StackOperandFrom(source));
1350      __ Str(temp, StackOperandFrom(destination));
1351    }
1352  }
1353}
1354
1355void CodeGeneratorARM64::Load(Primitive::Type type,
1356                              CPURegister dst,
1357                              const MemOperand& src) {
1358  switch (type) {
1359    case Primitive::kPrimBoolean:
1360      __ Ldrb(Register(dst), src);
1361      break;
1362    case Primitive::kPrimByte:
1363      __ Ldrsb(Register(dst), src);
1364      break;
1365    case Primitive::kPrimShort:
1366      __ Ldrsh(Register(dst), src);
1367      break;
1368    case Primitive::kPrimChar:
1369      __ Ldrh(Register(dst), src);
1370      break;
1371    case Primitive::kPrimInt:
1372    case Primitive::kPrimNot:
1373    case Primitive::kPrimLong:
1374    case Primitive::kPrimFloat:
1375    case Primitive::kPrimDouble:
1376      DCHECK_EQ(dst.Is64Bits(), Primitive::Is64BitType(type));
1377      __ Ldr(dst, src);
1378      break;
1379    case Primitive::kPrimVoid:
1380      LOG(FATAL) << "Unreachable type " << type;
1381  }
1382}
1383
1384void CodeGeneratorARM64::LoadAcquire(HInstruction* instruction,
1385                                     CPURegister dst,
1386                                     const MemOperand& src) {
1387  MacroAssembler* masm = GetVIXLAssembler();
1388  BlockPoolsScope block_pools(masm);
1389  UseScratchRegisterScope temps(masm);
1390  Register temp_base = temps.AcquireX();
1391  Primitive::Type type = instruction->GetType();
1392
1393  DCHECK(!src.IsPreIndex());
1394  DCHECK(!src.IsPostIndex());
1395
1396  // TODO(vixl): Let the MacroAssembler handle MemOperand.
1397  __ Add(temp_base, src.base(), OperandFromMemOperand(src));
1398  MemOperand base = MemOperand(temp_base);
1399  switch (type) {
1400    case Primitive::kPrimBoolean:
1401      __ Ldarb(Register(dst), base);
1402      MaybeRecordImplicitNullCheck(instruction);
1403      break;
1404    case Primitive::kPrimByte:
1405      __ Ldarb(Register(dst), base);
1406      MaybeRecordImplicitNullCheck(instruction);
1407      __ Sbfx(Register(dst), Register(dst), 0, Primitive::ComponentSize(type) * kBitsPerByte);
1408      break;
1409    case Primitive::kPrimChar:
1410      __ Ldarh(Register(dst), base);
1411      MaybeRecordImplicitNullCheck(instruction);
1412      break;
1413    case Primitive::kPrimShort:
1414      __ Ldarh(Register(dst), base);
1415      MaybeRecordImplicitNullCheck(instruction);
1416      __ Sbfx(Register(dst), Register(dst), 0, Primitive::ComponentSize(type) * kBitsPerByte);
1417      break;
1418    case Primitive::kPrimInt:
1419    case Primitive::kPrimNot:
1420    case Primitive::kPrimLong:
1421      DCHECK_EQ(dst.Is64Bits(), Primitive::Is64BitType(type));
1422      __ Ldar(Register(dst), base);
1423      MaybeRecordImplicitNullCheck(instruction);
1424      break;
1425    case Primitive::kPrimFloat:
1426    case Primitive::kPrimDouble: {
1427      DCHECK(dst.IsFPRegister());
1428      DCHECK_EQ(dst.Is64Bits(), Primitive::Is64BitType(type));
1429
1430      Register temp = dst.Is64Bits() ? temps.AcquireX() : temps.AcquireW();
1431      __ Ldar(temp, base);
1432      MaybeRecordImplicitNullCheck(instruction);
1433      __ Fmov(FPRegister(dst), temp);
1434      break;
1435    }
1436    case Primitive::kPrimVoid:
1437      LOG(FATAL) << "Unreachable type " << type;
1438  }
1439}
1440
1441void CodeGeneratorARM64::Store(Primitive::Type type,
1442                               CPURegister src,
1443                               const MemOperand& dst) {
1444  switch (type) {
1445    case Primitive::kPrimBoolean:
1446    case Primitive::kPrimByte:
1447      __ Strb(Register(src), dst);
1448      break;
1449    case Primitive::kPrimChar:
1450    case Primitive::kPrimShort:
1451      __ Strh(Register(src), dst);
1452      break;
1453    case Primitive::kPrimInt:
1454    case Primitive::kPrimNot:
1455    case Primitive::kPrimLong:
1456    case Primitive::kPrimFloat:
1457    case Primitive::kPrimDouble:
1458      DCHECK_EQ(src.Is64Bits(), Primitive::Is64BitType(type));
1459      __ Str(src, dst);
1460      break;
1461    case Primitive::kPrimVoid:
1462      LOG(FATAL) << "Unreachable type " << type;
1463  }
1464}
1465
1466void CodeGeneratorARM64::StoreRelease(Primitive::Type type,
1467                                      CPURegister src,
1468                                      const MemOperand& dst) {
1469  UseScratchRegisterScope temps(GetVIXLAssembler());
1470  Register temp_base = temps.AcquireX();
1471
1472  DCHECK(!dst.IsPreIndex());
1473  DCHECK(!dst.IsPostIndex());
1474
1475  // TODO(vixl): Let the MacroAssembler handle this.
1476  Operand op = OperandFromMemOperand(dst);
1477  __ Add(temp_base, dst.base(), op);
1478  MemOperand base = MemOperand(temp_base);
1479  switch (type) {
1480    case Primitive::kPrimBoolean:
1481    case Primitive::kPrimByte:
1482      __ Stlrb(Register(src), base);
1483      break;
1484    case Primitive::kPrimChar:
1485    case Primitive::kPrimShort:
1486      __ Stlrh(Register(src), base);
1487      break;
1488    case Primitive::kPrimInt:
1489    case Primitive::kPrimNot:
1490    case Primitive::kPrimLong:
1491      DCHECK_EQ(src.Is64Bits(), Primitive::Is64BitType(type));
1492      __ Stlr(Register(src), base);
1493      break;
1494    case Primitive::kPrimFloat:
1495    case Primitive::kPrimDouble: {
1496      DCHECK(src.IsFPRegister());
1497      DCHECK_EQ(src.Is64Bits(), Primitive::Is64BitType(type));
1498
1499      Register temp = src.Is64Bits() ? temps.AcquireX() : temps.AcquireW();
1500      __ Fmov(temp, FPRegister(src));
1501      __ Stlr(temp, base);
1502      break;
1503    }
1504    case Primitive::kPrimVoid:
1505      LOG(FATAL) << "Unreachable type " << type;
1506  }
1507}
1508
1509void CodeGeneratorARM64::InvokeRuntime(QuickEntrypointEnum entrypoint,
1510                                       HInstruction* instruction,
1511                                       uint32_t dex_pc,
1512                                       SlowPathCode* slow_path) {
1513  InvokeRuntime(GetThreadOffset<kArm64WordSize>(entrypoint).Int32Value(),
1514                instruction,
1515                dex_pc,
1516                slow_path);
1517}
1518
1519void CodeGeneratorARM64::InvokeRuntime(int32_t entry_point_offset,
1520                                       HInstruction* instruction,
1521                                       uint32_t dex_pc,
1522                                       SlowPathCode* slow_path) {
1523  ValidateInvokeRuntime(instruction, slow_path);
1524  BlockPoolsScope block_pools(GetVIXLAssembler());
1525  __ Ldr(lr, MemOperand(tr, entry_point_offset));
1526  __ Blr(lr);
1527  RecordPcInfo(instruction, dex_pc, slow_path);
1528}
1529
1530void InstructionCodeGeneratorARM64::GenerateClassInitializationCheck(SlowPathCodeARM64* slow_path,
1531                                                                     vixl::Register class_reg) {
1532  UseScratchRegisterScope temps(GetVIXLAssembler());
1533  Register temp = temps.AcquireW();
1534  size_t status_offset = mirror::Class::StatusOffset().SizeValue();
1535  bool use_acquire_release = codegen_->GetInstructionSetFeatures().PreferAcquireRelease();
1536
1537  // Even if the initialized flag is set, we need to ensure consistent memory ordering.
1538  if (use_acquire_release) {
1539    // TODO(vixl): Let the MacroAssembler handle MemOperand.
1540    __ Add(temp, class_reg, status_offset);
1541    __ Ldar(temp, HeapOperand(temp));
1542    __ Cmp(temp, mirror::Class::kStatusInitialized);
1543    __ B(lt, slow_path->GetEntryLabel());
1544  } else {
1545    __ Ldr(temp, HeapOperand(class_reg, status_offset));
1546    __ Cmp(temp, mirror::Class::kStatusInitialized);
1547    __ B(lt, slow_path->GetEntryLabel());
1548    __ Dmb(InnerShareable, BarrierReads);
1549  }
1550  __ Bind(slow_path->GetExitLabel());
1551}
1552
1553void InstructionCodeGeneratorARM64::GenerateMemoryBarrier(MemBarrierKind kind) {
1554  BarrierType type = BarrierAll;
1555
1556  switch (kind) {
1557    case MemBarrierKind::kAnyAny:
1558    case MemBarrierKind::kAnyStore: {
1559      type = BarrierAll;
1560      break;
1561    }
1562    case MemBarrierKind::kLoadAny: {
1563      type = BarrierReads;
1564      break;
1565    }
1566    case MemBarrierKind::kStoreStore: {
1567      type = BarrierWrites;
1568      break;
1569    }
1570    default:
1571      LOG(FATAL) << "Unexpected memory barrier " << kind;
1572  }
1573  __ Dmb(InnerShareable, type);
1574}
1575
1576void InstructionCodeGeneratorARM64::GenerateSuspendCheck(HSuspendCheck* instruction,
1577                                                         HBasicBlock* successor) {
1578  SuspendCheckSlowPathARM64* slow_path =
1579      down_cast<SuspendCheckSlowPathARM64*>(instruction->GetSlowPath());
1580  if (slow_path == nullptr) {
1581    slow_path = new (GetGraph()->GetArena()) SuspendCheckSlowPathARM64(instruction, successor);
1582    instruction->SetSlowPath(slow_path);
1583    codegen_->AddSlowPath(slow_path);
1584    if (successor != nullptr) {
1585      DCHECK(successor->IsLoopHeader());
1586      codegen_->ClearSpillSlotsFromLoopPhisInStackMap(instruction);
1587    }
1588  } else {
1589    DCHECK_EQ(slow_path->GetSuccessor(), successor);
1590  }
1591
1592  UseScratchRegisterScope temps(codegen_->GetVIXLAssembler());
1593  Register temp = temps.AcquireW();
1594
1595  __ Ldrh(temp, MemOperand(tr, Thread::ThreadFlagsOffset<kArm64WordSize>().SizeValue()));
1596  if (successor == nullptr) {
1597    __ Cbnz(temp, slow_path->GetEntryLabel());
1598    __ Bind(slow_path->GetReturnLabel());
1599  } else {
1600    __ Cbz(temp, codegen_->GetLabelOf(successor));
1601    __ B(slow_path->GetEntryLabel());
1602    // slow_path will return to GetLabelOf(successor).
1603  }
1604}
1605
1606InstructionCodeGeneratorARM64::InstructionCodeGeneratorARM64(HGraph* graph,
1607                                                             CodeGeneratorARM64* codegen)
1608      : InstructionCodeGenerator(graph, codegen),
1609        assembler_(codegen->GetAssembler()),
1610        codegen_(codegen) {}
1611
1612#define FOR_EACH_UNIMPLEMENTED_INSTRUCTION(M)              \
1613  /* No unimplemented IR. */
1614
1615#define UNIMPLEMENTED_INSTRUCTION_BREAK_CODE(name) name##UnimplementedInstructionBreakCode
1616
1617enum UnimplementedInstructionBreakCode {
1618  // Using a base helps identify when we hit such breakpoints.
1619  UnimplementedInstructionBreakCodeBaseCode = 0x900,
1620#define ENUM_UNIMPLEMENTED_INSTRUCTION(name) UNIMPLEMENTED_INSTRUCTION_BREAK_CODE(name),
1621  FOR_EACH_UNIMPLEMENTED_INSTRUCTION(ENUM_UNIMPLEMENTED_INSTRUCTION)
1622#undef ENUM_UNIMPLEMENTED_INSTRUCTION
1623};
1624
1625#define DEFINE_UNIMPLEMENTED_INSTRUCTION_VISITORS(name)                               \
1626  void InstructionCodeGeneratorARM64::Visit##name(H##name* instr ATTRIBUTE_UNUSED) {  \
1627    __ Brk(UNIMPLEMENTED_INSTRUCTION_BREAK_CODE(name));                               \
1628  }                                                                                   \
1629  void LocationsBuilderARM64::Visit##name(H##name* instr) {                           \
1630    LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instr); \
1631    locations->SetOut(Location::Any());                                               \
1632  }
1633  FOR_EACH_UNIMPLEMENTED_INSTRUCTION(DEFINE_UNIMPLEMENTED_INSTRUCTION_VISITORS)
1634#undef DEFINE_UNIMPLEMENTED_INSTRUCTION_VISITORS
1635
1636#undef UNIMPLEMENTED_INSTRUCTION_BREAK_CODE
1637#undef FOR_EACH_UNIMPLEMENTED_INSTRUCTION
1638
1639void LocationsBuilderARM64::HandleBinaryOp(HBinaryOperation* instr) {
1640  DCHECK_EQ(instr->InputCount(), 2U);
1641  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instr);
1642  Primitive::Type type = instr->GetResultType();
1643  switch (type) {
1644    case Primitive::kPrimInt:
1645    case Primitive::kPrimLong:
1646      locations->SetInAt(0, Location::RequiresRegister());
1647      locations->SetInAt(1, ARM64EncodableConstantOrRegister(instr->InputAt(1), instr));
1648      locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
1649      break;
1650
1651    case Primitive::kPrimFloat:
1652    case Primitive::kPrimDouble:
1653      locations->SetInAt(0, Location::RequiresFpuRegister());
1654      locations->SetInAt(1, Location::RequiresFpuRegister());
1655      locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
1656      break;
1657
1658    default:
1659      LOG(FATAL) << "Unexpected " << instr->DebugName() << " type " << type;
1660  }
1661}
1662
1663void LocationsBuilderARM64::HandleFieldGet(HInstruction* instruction) {
1664  DCHECK(instruction->IsInstanceFieldGet() || instruction->IsStaticFieldGet());
1665
1666  bool object_field_get_with_read_barrier =
1667      kEmitCompilerReadBarrier && (instruction->GetType() == Primitive::kPrimNot);
1668  LocationSummary* locations =
1669      new (GetGraph()->GetArena()) LocationSummary(instruction,
1670                                                   object_field_get_with_read_barrier ?
1671                                                       LocationSummary::kCallOnSlowPath :
1672                                                       LocationSummary::kNoCall);
1673  locations->SetInAt(0, Location::RequiresRegister());
1674  if (Primitive::IsFloatingPointType(instruction->GetType())) {
1675    locations->SetOut(Location::RequiresFpuRegister());
1676  } else {
1677    // The output overlaps for an object field get when read barriers
1678    // are enabled: we do not want the load to overwrite the object's
1679    // location, as we need it to emit the read barrier.
1680    locations->SetOut(
1681        Location::RequiresRegister(),
1682        object_field_get_with_read_barrier ? Location::kOutputOverlap : Location::kNoOutputOverlap);
1683  }
1684}
1685
1686void InstructionCodeGeneratorARM64::HandleFieldGet(HInstruction* instruction,
1687                                                   const FieldInfo& field_info) {
1688  DCHECK(instruction->IsInstanceFieldGet() || instruction->IsStaticFieldGet());
1689  Primitive::Type field_type = field_info.GetFieldType();
1690  BlockPoolsScope block_pools(GetVIXLAssembler());
1691
1692  MemOperand field = HeapOperand(InputRegisterAt(instruction, 0), field_info.GetFieldOffset());
1693  bool use_acquire_release = codegen_->GetInstructionSetFeatures().PreferAcquireRelease();
1694
1695  if (field_info.IsVolatile()) {
1696    if (use_acquire_release) {
1697      // NB: LoadAcquire will record the pc info if needed.
1698      codegen_->LoadAcquire(instruction, OutputCPURegister(instruction), field);
1699    } else {
1700      codegen_->Load(field_type, OutputCPURegister(instruction), field);
1701      codegen_->MaybeRecordImplicitNullCheck(instruction);
1702      // For IRIW sequential consistency kLoadAny is not sufficient.
1703      GenerateMemoryBarrier(MemBarrierKind::kAnyAny);
1704    }
1705  } else {
1706    codegen_->Load(field_type, OutputCPURegister(instruction), field);
1707    codegen_->MaybeRecordImplicitNullCheck(instruction);
1708  }
1709
1710  if (field_type == Primitive::kPrimNot) {
1711    LocationSummary* locations = instruction->GetLocations();
1712    Location base = locations->InAt(0);
1713    Location out = locations->Out();
1714    uint32_t offset = field_info.GetFieldOffset().Uint32Value();
1715    codegen_->MaybeGenerateReadBarrier(instruction, out, out, base, offset);
1716  }
1717}
1718
1719void LocationsBuilderARM64::HandleFieldSet(HInstruction* instruction) {
1720  LocationSummary* locations =
1721      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
1722  locations->SetInAt(0, Location::RequiresRegister());
1723  if (Primitive::IsFloatingPointType(instruction->InputAt(1)->GetType())) {
1724    locations->SetInAt(1, Location::RequiresFpuRegister());
1725  } else {
1726    locations->SetInAt(1, Location::RequiresRegister());
1727  }
1728}
1729
1730void InstructionCodeGeneratorARM64::HandleFieldSet(HInstruction* instruction,
1731                                                   const FieldInfo& field_info,
1732                                                   bool value_can_be_null) {
1733  DCHECK(instruction->IsInstanceFieldSet() || instruction->IsStaticFieldSet());
1734  BlockPoolsScope block_pools(GetVIXLAssembler());
1735
1736  Register obj = InputRegisterAt(instruction, 0);
1737  CPURegister value = InputCPURegisterAt(instruction, 1);
1738  CPURegister source = value;
1739  Offset offset = field_info.GetFieldOffset();
1740  Primitive::Type field_type = field_info.GetFieldType();
1741  bool use_acquire_release = codegen_->GetInstructionSetFeatures().PreferAcquireRelease();
1742
1743  {
1744    // We use a block to end the scratch scope before the write barrier, thus
1745    // freeing the temporary registers so they can be used in `MarkGCCard`.
1746    UseScratchRegisterScope temps(GetVIXLAssembler());
1747
1748    if (kPoisonHeapReferences && field_type == Primitive::kPrimNot) {
1749      DCHECK(value.IsW());
1750      Register temp = temps.AcquireW();
1751      __ Mov(temp, value.W());
1752      GetAssembler()->PoisonHeapReference(temp.W());
1753      source = temp;
1754    }
1755
1756    if (field_info.IsVolatile()) {
1757      if (use_acquire_release) {
1758        codegen_->StoreRelease(field_type, source, HeapOperand(obj, offset));
1759        codegen_->MaybeRecordImplicitNullCheck(instruction);
1760      } else {
1761        GenerateMemoryBarrier(MemBarrierKind::kAnyStore);
1762        codegen_->Store(field_type, source, HeapOperand(obj, offset));
1763        codegen_->MaybeRecordImplicitNullCheck(instruction);
1764        GenerateMemoryBarrier(MemBarrierKind::kAnyAny);
1765      }
1766    } else {
1767      codegen_->Store(field_type, source, HeapOperand(obj, offset));
1768      codegen_->MaybeRecordImplicitNullCheck(instruction);
1769    }
1770  }
1771
1772  if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1))) {
1773    codegen_->MarkGCCard(obj, Register(value), value_can_be_null);
1774  }
1775}
1776
1777void InstructionCodeGeneratorARM64::HandleBinaryOp(HBinaryOperation* instr) {
1778  Primitive::Type type = instr->GetType();
1779
1780  switch (type) {
1781    case Primitive::kPrimInt:
1782    case Primitive::kPrimLong: {
1783      Register dst = OutputRegister(instr);
1784      Register lhs = InputRegisterAt(instr, 0);
1785      Operand rhs = InputOperandAt(instr, 1);
1786      if (instr->IsAdd()) {
1787        __ Add(dst, lhs, rhs);
1788      } else if (instr->IsAnd()) {
1789        __ And(dst, lhs, rhs);
1790      } else if (instr->IsOr()) {
1791        __ Orr(dst, lhs, rhs);
1792      } else if (instr->IsSub()) {
1793        __ Sub(dst, lhs, rhs);
1794      } else if (instr->IsRor()) {
1795        if (rhs.IsImmediate()) {
1796          uint32_t shift = rhs.immediate() & (lhs.SizeInBits() - 1);
1797          __ Ror(dst, lhs, shift);
1798        } else {
1799          // Ensure shift distance is in the same size register as the result. If
1800          // we are rotating a long and the shift comes in a w register originally,
1801          // we don't need to sxtw for use as an x since the shift distances are
1802          // all & reg_bits - 1.
1803          __ Ror(dst, lhs, RegisterFrom(instr->GetLocations()->InAt(1), type));
1804        }
1805      } else {
1806        DCHECK(instr->IsXor());
1807        __ Eor(dst, lhs, rhs);
1808      }
1809      break;
1810    }
1811    case Primitive::kPrimFloat:
1812    case Primitive::kPrimDouble: {
1813      FPRegister dst = OutputFPRegister(instr);
1814      FPRegister lhs = InputFPRegisterAt(instr, 0);
1815      FPRegister rhs = InputFPRegisterAt(instr, 1);
1816      if (instr->IsAdd()) {
1817        __ Fadd(dst, lhs, rhs);
1818      } else if (instr->IsSub()) {
1819        __ Fsub(dst, lhs, rhs);
1820      } else {
1821        LOG(FATAL) << "Unexpected floating-point binary operation";
1822      }
1823      break;
1824    }
1825    default:
1826      LOG(FATAL) << "Unexpected binary operation type " << type;
1827  }
1828}
1829
1830void LocationsBuilderARM64::HandleShift(HBinaryOperation* instr) {
1831  DCHECK(instr->IsShl() || instr->IsShr() || instr->IsUShr());
1832
1833  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instr);
1834  Primitive::Type type = instr->GetResultType();
1835  switch (type) {
1836    case Primitive::kPrimInt:
1837    case Primitive::kPrimLong: {
1838      locations->SetInAt(0, Location::RequiresRegister());
1839      locations->SetInAt(1, Location::RegisterOrConstant(instr->InputAt(1)));
1840      locations->SetOut(Location::RequiresRegister());
1841      break;
1842    }
1843    default:
1844      LOG(FATAL) << "Unexpected shift type " << type;
1845  }
1846}
1847
1848void InstructionCodeGeneratorARM64::HandleShift(HBinaryOperation* instr) {
1849  DCHECK(instr->IsShl() || instr->IsShr() || instr->IsUShr());
1850
1851  Primitive::Type type = instr->GetType();
1852  switch (type) {
1853    case Primitive::kPrimInt:
1854    case Primitive::kPrimLong: {
1855      Register dst = OutputRegister(instr);
1856      Register lhs = InputRegisterAt(instr, 0);
1857      Operand rhs = InputOperandAt(instr, 1);
1858      if (rhs.IsImmediate()) {
1859        uint32_t shift_value = (type == Primitive::kPrimInt)
1860          ? static_cast<uint32_t>(rhs.immediate() & kMaxIntShiftValue)
1861          : static_cast<uint32_t>(rhs.immediate() & kMaxLongShiftValue);
1862        if (instr->IsShl()) {
1863          __ Lsl(dst, lhs, shift_value);
1864        } else if (instr->IsShr()) {
1865          __ Asr(dst, lhs, shift_value);
1866        } else {
1867          __ Lsr(dst, lhs, shift_value);
1868        }
1869      } else {
1870        Register rhs_reg = dst.IsX() ? rhs.reg().X() : rhs.reg().W();
1871
1872        if (instr->IsShl()) {
1873          __ Lsl(dst, lhs, rhs_reg);
1874        } else if (instr->IsShr()) {
1875          __ Asr(dst, lhs, rhs_reg);
1876        } else {
1877          __ Lsr(dst, lhs, rhs_reg);
1878        }
1879      }
1880      break;
1881    }
1882    default:
1883      LOG(FATAL) << "Unexpected shift operation type " << type;
1884  }
1885}
1886
1887void LocationsBuilderARM64::VisitAdd(HAdd* instruction) {
1888  HandleBinaryOp(instruction);
1889}
1890
1891void InstructionCodeGeneratorARM64::VisitAdd(HAdd* instruction) {
1892  HandleBinaryOp(instruction);
1893}
1894
1895void LocationsBuilderARM64::VisitAnd(HAnd* instruction) {
1896  HandleBinaryOp(instruction);
1897}
1898
1899void InstructionCodeGeneratorARM64::VisitAnd(HAnd* instruction) {
1900  HandleBinaryOp(instruction);
1901}
1902
1903void LocationsBuilderARM64::VisitArm64DataProcWithShifterOp(
1904    HArm64DataProcWithShifterOp* instruction) {
1905  DCHECK(instruction->GetType() == Primitive::kPrimInt ||
1906         instruction->GetType() == Primitive::kPrimLong);
1907  LocationSummary* locations =
1908      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
1909  if (instruction->GetInstrKind() == HInstruction::kNeg) {
1910    locations->SetInAt(0, Location::ConstantLocation(instruction->InputAt(0)->AsConstant()));
1911  } else {
1912    locations->SetInAt(0, Location::RequiresRegister());
1913  }
1914  locations->SetInAt(1, Location::RequiresRegister());
1915  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
1916}
1917
1918void InstructionCodeGeneratorARM64::VisitArm64DataProcWithShifterOp(
1919    HArm64DataProcWithShifterOp* instruction) {
1920  Primitive::Type type = instruction->GetType();
1921  HInstruction::InstructionKind kind = instruction->GetInstrKind();
1922  DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong);
1923  Register out = OutputRegister(instruction);
1924  Register left;
1925  if (kind != HInstruction::kNeg) {
1926    left = InputRegisterAt(instruction, 0);
1927  }
1928  // If this `HArm64DataProcWithShifterOp` was created by merging a type conversion as the
1929  // shifter operand operation, the IR generating `right_reg` (input to the type
1930  // conversion) can have a different type from the current instruction's type,
1931  // so we manually indicate the type.
1932  Register right_reg = RegisterFrom(instruction->GetLocations()->InAt(1), type);
1933  int64_t shift_amount = (type == Primitive::kPrimInt)
1934    ? static_cast<uint32_t>(instruction->GetShiftAmount() & kMaxIntShiftValue)
1935    : static_cast<uint32_t>(instruction->GetShiftAmount() & kMaxLongShiftValue);
1936
1937  Operand right_operand(0);
1938
1939  HArm64DataProcWithShifterOp::OpKind op_kind = instruction->GetOpKind();
1940  if (HArm64DataProcWithShifterOp::IsExtensionOp(op_kind)) {
1941    right_operand = Operand(right_reg, helpers::ExtendFromOpKind(op_kind));
1942  } else {
1943    right_operand = Operand(right_reg, helpers::ShiftFromOpKind(op_kind), shift_amount);
1944  }
1945
1946  // Logical binary operations do not support extension operations in the
1947  // operand. Note that VIXL would still manage if it was passed by generating
1948  // the extension as a separate instruction.
1949  // `HNeg` also does not support extension. See comments in `ShifterOperandSupportsExtension()`.
1950  DCHECK(!right_operand.IsExtendedRegister() ||
1951         (kind != HInstruction::kAnd && kind != HInstruction::kOr && kind != HInstruction::kXor &&
1952          kind != HInstruction::kNeg));
1953  switch (kind) {
1954    case HInstruction::kAdd:
1955      __ Add(out, left, right_operand);
1956      break;
1957    case HInstruction::kAnd:
1958      __ And(out, left, right_operand);
1959      break;
1960    case HInstruction::kNeg:
1961      DCHECK(instruction->InputAt(0)->AsConstant()->IsZero());
1962      __ Neg(out, right_operand);
1963      break;
1964    case HInstruction::kOr:
1965      __ Orr(out, left, right_operand);
1966      break;
1967    case HInstruction::kSub:
1968      __ Sub(out, left, right_operand);
1969      break;
1970    case HInstruction::kXor:
1971      __ Eor(out, left, right_operand);
1972      break;
1973    default:
1974      LOG(FATAL) << "Unexpected operation kind: " << kind;
1975      UNREACHABLE();
1976  }
1977}
1978
1979void LocationsBuilderARM64::VisitArm64IntermediateAddress(HArm64IntermediateAddress* instruction) {
1980  LocationSummary* locations =
1981      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
1982  locations->SetInAt(0, Location::RequiresRegister());
1983  locations->SetInAt(1, ARM64EncodableConstantOrRegister(instruction->GetOffset(), instruction));
1984  locations->SetOut(Location::RequiresRegister());
1985}
1986
1987void InstructionCodeGeneratorARM64::VisitArm64IntermediateAddress(
1988    HArm64IntermediateAddress* instruction) {
1989  __ Add(OutputRegister(instruction),
1990         InputRegisterAt(instruction, 0),
1991         Operand(InputOperandAt(instruction, 1)));
1992}
1993
1994void LocationsBuilderARM64::VisitArm64MultiplyAccumulate(HArm64MultiplyAccumulate* instr) {
1995  LocationSummary* locations =
1996      new (GetGraph()->GetArena()) LocationSummary(instr, LocationSummary::kNoCall);
1997  locations->SetInAt(HArm64MultiplyAccumulate::kInputAccumulatorIndex,
1998                     Location::RequiresRegister());
1999  locations->SetInAt(HArm64MultiplyAccumulate::kInputMulLeftIndex, Location::RequiresRegister());
2000  locations->SetInAt(HArm64MultiplyAccumulate::kInputMulRightIndex, Location::RequiresRegister());
2001  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2002}
2003
2004void InstructionCodeGeneratorARM64::VisitArm64MultiplyAccumulate(HArm64MultiplyAccumulate* instr) {
2005  Register res = OutputRegister(instr);
2006  Register accumulator = InputRegisterAt(instr, HArm64MultiplyAccumulate::kInputAccumulatorIndex);
2007  Register mul_left = InputRegisterAt(instr, HArm64MultiplyAccumulate::kInputMulLeftIndex);
2008  Register mul_right = InputRegisterAt(instr, HArm64MultiplyAccumulate::kInputMulRightIndex);
2009
2010  // Avoid emitting code that could trigger Cortex A53's erratum 835769.
2011  // This fixup should be carried out for all multiply-accumulate instructions:
2012  // madd, msub, smaddl, smsubl, umaddl and umsubl.
2013  if (instr->GetType() == Primitive::kPrimLong &&
2014      codegen_->GetInstructionSetFeatures().NeedFixCortexA53_835769()) {
2015    MacroAssembler* masm = down_cast<CodeGeneratorARM64*>(codegen_)->GetVIXLAssembler();
2016    vixl::Instruction* prev = masm->GetCursorAddress<vixl::Instruction*>() - vixl::kInstructionSize;
2017    if (prev->IsLoadOrStore()) {
2018      // Make sure we emit only exactly one nop.
2019      vixl::CodeBufferCheckScope scope(masm,
2020                                       vixl::kInstructionSize,
2021                                       vixl::CodeBufferCheckScope::kCheck,
2022                                       vixl::CodeBufferCheckScope::kExactSize);
2023      __ nop();
2024    }
2025  }
2026
2027  if (instr->GetOpKind() == HInstruction::kAdd) {
2028    __ Madd(res, mul_left, mul_right, accumulator);
2029  } else {
2030    DCHECK(instr->GetOpKind() == HInstruction::kSub);
2031    __ Msub(res, mul_left, mul_right, accumulator);
2032  }
2033}
2034
2035void LocationsBuilderARM64::VisitArrayGet(HArrayGet* instruction) {
2036  bool object_array_get_with_read_barrier =
2037      kEmitCompilerReadBarrier && (instruction->GetType() == Primitive::kPrimNot);
2038  LocationSummary* locations =
2039      new (GetGraph()->GetArena()) LocationSummary(instruction,
2040                                                   object_array_get_with_read_barrier ?
2041                                                       LocationSummary::kCallOnSlowPath :
2042                                                       LocationSummary::kNoCall);
2043  locations->SetInAt(0, Location::RequiresRegister());
2044  locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
2045  if (Primitive::IsFloatingPointType(instruction->GetType())) {
2046    locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
2047  } else {
2048    // The output overlaps in the case of an object array get with
2049    // read barriers enabled: we do not want the move to overwrite the
2050    // array's location, as we need it to emit the read barrier.
2051    locations->SetOut(
2052        Location::RequiresRegister(),
2053        object_array_get_with_read_barrier ? Location::kOutputOverlap : Location::kNoOutputOverlap);
2054  }
2055}
2056
2057void InstructionCodeGeneratorARM64::VisitArrayGet(HArrayGet* instruction) {
2058  Primitive::Type type = instruction->GetType();
2059  Register obj = InputRegisterAt(instruction, 0);
2060  LocationSummary* locations = instruction->GetLocations();
2061  Location index = locations->InAt(1);
2062  uint32_t offset = mirror::Array::DataOffset(Primitive::ComponentSize(type)).Uint32Value();
2063  MemOperand source = HeapOperand(obj);
2064  CPURegister dest = OutputCPURegister(instruction);
2065
2066  MacroAssembler* masm = GetVIXLAssembler();
2067  UseScratchRegisterScope temps(masm);
2068  // Block pools between `Load` and `MaybeRecordImplicitNullCheck`.
2069  BlockPoolsScope block_pools(masm);
2070
2071  if (index.IsConstant()) {
2072    offset += Int64ConstantFrom(index) << Primitive::ComponentSizeShift(type);
2073    source = HeapOperand(obj, offset);
2074  } else {
2075    Register temp = temps.AcquireSameSizeAs(obj);
2076    if (instruction->GetArray()->IsArm64IntermediateAddress()) {
2077      // We do not need to compute the intermediate address from the array: the
2078      // input instruction has done it already. See the comment in
2079      // `InstructionSimplifierArm64::TryExtractArrayAccessAddress()`.
2080      if (kIsDebugBuild) {
2081        HArm64IntermediateAddress* tmp = instruction->GetArray()->AsArm64IntermediateAddress();
2082        DCHECK(tmp->GetOffset()->AsIntConstant()->GetValueAsUint64() == offset);
2083      }
2084      temp = obj;
2085    } else {
2086      __ Add(temp, obj, offset);
2087    }
2088    source = HeapOperand(temp, XRegisterFrom(index), LSL, Primitive::ComponentSizeShift(type));
2089  }
2090
2091  codegen_->Load(type, dest, source);
2092  codegen_->MaybeRecordImplicitNullCheck(instruction);
2093
2094  if (type == Primitive::kPrimNot) {
2095    static_assert(
2096        sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
2097        "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
2098    Location obj_loc = locations->InAt(0);
2099    Location out = locations->Out();
2100    if (index.IsConstant()) {
2101      codegen_->MaybeGenerateReadBarrier(instruction, out, out, obj_loc, offset);
2102    } else {
2103      // Note: when `obj_loc` is a HArm64IntermediateAddress, it does
2104      // not contain the base address of the array object, which is
2105      // needed by the read barrier entry point. So the read barrier
2106      // slow path will temporarily set back `obj_loc` to the right
2107      // address (see ReadBarrierForHeapReferenceSlowPathARM64::EmitNativeCode).
2108      codegen_->MaybeGenerateReadBarrier(instruction, out, out, obj_loc, offset, index);
2109    }
2110  }
2111}
2112
2113void LocationsBuilderARM64::VisitArrayLength(HArrayLength* instruction) {
2114  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
2115  locations->SetInAt(0, Location::RequiresRegister());
2116  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2117}
2118
2119void InstructionCodeGeneratorARM64::VisitArrayLength(HArrayLength* instruction) {
2120  BlockPoolsScope block_pools(GetVIXLAssembler());
2121  __ Ldr(OutputRegister(instruction),
2122         HeapOperand(InputRegisterAt(instruction, 0), mirror::Array::LengthOffset()));
2123  codegen_->MaybeRecordImplicitNullCheck(instruction);
2124}
2125
2126void LocationsBuilderARM64::VisitArraySet(HArraySet* instruction) {
2127  Primitive::Type value_type = instruction->GetComponentType();
2128
2129  bool may_need_runtime_call_for_type_check = instruction->NeedsTypeCheck();
2130  bool object_array_set_with_read_barrier =
2131      kEmitCompilerReadBarrier && (value_type == Primitive::kPrimNot);
2132  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(
2133      instruction,
2134      (may_need_runtime_call_for_type_check  || object_array_set_with_read_barrier) ?
2135          LocationSummary::kCallOnSlowPath :
2136          LocationSummary::kNoCall);
2137  locations->SetInAt(0, Location::RequiresRegister());
2138  locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
2139  if (Primitive::IsFloatingPointType(value_type)) {
2140    locations->SetInAt(2, Location::RequiresFpuRegister());
2141  } else {
2142    locations->SetInAt(2, Location::RequiresRegister());
2143  }
2144}
2145
2146void InstructionCodeGeneratorARM64::VisitArraySet(HArraySet* instruction) {
2147  Primitive::Type value_type = instruction->GetComponentType();
2148  LocationSummary* locations = instruction->GetLocations();
2149  bool may_need_runtime_call_for_type_check = instruction->NeedsTypeCheck();
2150  bool needs_write_barrier =
2151      CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue());
2152
2153  Register array = InputRegisterAt(instruction, 0);
2154  CPURegister value = InputCPURegisterAt(instruction, 2);
2155  CPURegister source = value;
2156  Location index = locations->InAt(1);
2157  size_t offset = mirror::Array::DataOffset(Primitive::ComponentSize(value_type)).Uint32Value();
2158  MemOperand destination = HeapOperand(array);
2159  MacroAssembler* masm = GetVIXLAssembler();
2160  BlockPoolsScope block_pools(masm);
2161
2162  if (!needs_write_barrier) {
2163    DCHECK(!may_need_runtime_call_for_type_check);
2164    if (index.IsConstant()) {
2165      offset += Int64ConstantFrom(index) << Primitive::ComponentSizeShift(value_type);
2166      destination = HeapOperand(array, offset);
2167    } else {
2168      UseScratchRegisterScope temps(masm);
2169      Register temp = temps.AcquireSameSizeAs(array);
2170      if (instruction->GetArray()->IsArm64IntermediateAddress()) {
2171        // We do not need to compute the intermediate address from the array: the
2172        // input instruction has done it already. See the comment in
2173        // `InstructionSimplifierArm64::TryExtractArrayAccessAddress()`.
2174        if (kIsDebugBuild) {
2175          HArm64IntermediateAddress* tmp = instruction->GetArray()->AsArm64IntermediateAddress();
2176          DCHECK(tmp->GetOffset()->AsIntConstant()->GetValueAsUint64() == offset);
2177        }
2178        temp = array;
2179      } else {
2180        __ Add(temp, array, offset);
2181      }
2182      destination = HeapOperand(temp,
2183                                XRegisterFrom(index),
2184                                LSL,
2185                                Primitive::ComponentSizeShift(value_type));
2186    }
2187    codegen_->Store(value_type, value, destination);
2188    codegen_->MaybeRecordImplicitNullCheck(instruction);
2189  } else {
2190    DCHECK(needs_write_barrier);
2191    DCHECK(!instruction->GetArray()->IsArm64IntermediateAddress());
2192    vixl::Label done;
2193    SlowPathCodeARM64* slow_path = nullptr;
2194    {
2195      // We use a block to end the scratch scope before the write barrier, thus
2196      // freeing the temporary registers so they can be used in `MarkGCCard`.
2197      UseScratchRegisterScope temps(masm);
2198      Register temp = temps.AcquireSameSizeAs(array);
2199      if (index.IsConstant()) {
2200        offset += Int64ConstantFrom(index) << Primitive::ComponentSizeShift(value_type);
2201        destination = HeapOperand(array, offset);
2202      } else {
2203        destination = HeapOperand(temp,
2204                                  XRegisterFrom(index),
2205                                  LSL,
2206                                  Primitive::ComponentSizeShift(value_type));
2207      }
2208
2209      uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
2210      uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
2211      uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
2212
2213      if (may_need_runtime_call_for_type_check) {
2214        slow_path = new (GetGraph()->GetArena()) ArraySetSlowPathARM64(instruction);
2215        codegen_->AddSlowPath(slow_path);
2216        if (instruction->GetValueCanBeNull()) {
2217          vixl::Label non_zero;
2218          __ Cbnz(Register(value), &non_zero);
2219          if (!index.IsConstant()) {
2220            __ Add(temp, array, offset);
2221          }
2222          __ Str(wzr, destination);
2223          codegen_->MaybeRecordImplicitNullCheck(instruction);
2224          __ B(&done);
2225          __ Bind(&non_zero);
2226        }
2227
2228        if (kEmitCompilerReadBarrier) {
2229          // When read barriers are enabled, the type checking
2230          // instrumentation requires two read barriers:
2231          //
2232          //   __ Mov(temp2, temp);
2233          //   // /* HeapReference<Class> */ temp = temp->component_type_
2234          //   __ Ldr(temp, HeapOperand(temp, component_offset));
2235          //   codegen_->GenerateReadBarrier(
2236          //       instruction, temp_loc, temp_loc, temp2_loc, component_offset);
2237          //
2238          //   // /* HeapReference<Class> */ temp2 = value->klass_
2239          //   __ Ldr(temp2, HeapOperand(Register(value), class_offset));
2240          //   codegen_->GenerateReadBarrier(
2241          //       instruction, temp2_loc, temp2_loc, value_loc, class_offset, temp_loc);
2242          //
2243          //   __ Cmp(temp, temp2);
2244          //
2245          // However, the second read barrier may trash `temp`, as it
2246          // is a temporary register, and as such would not be saved
2247          // along with live registers before calling the runtime (nor
2248          // restored afterwards).  So in this case, we bail out and
2249          // delegate the work to the array set slow path.
2250          //
2251          // TODO: Extend the register allocator to support a new
2252          // "(locally) live temp" location so as to avoid always
2253          // going into the slow path when read barriers are enabled.
2254          __ B(slow_path->GetEntryLabel());
2255        } else {
2256          Register temp2 = temps.AcquireSameSizeAs(array);
2257          // /* HeapReference<Class> */ temp = array->klass_
2258          __ Ldr(temp, HeapOperand(array, class_offset));
2259          codegen_->MaybeRecordImplicitNullCheck(instruction);
2260          GetAssembler()->MaybeUnpoisonHeapReference(temp);
2261
2262          // /* HeapReference<Class> */ temp = temp->component_type_
2263          __ Ldr(temp, HeapOperand(temp, component_offset));
2264          // /* HeapReference<Class> */ temp2 = value->klass_
2265          __ Ldr(temp2, HeapOperand(Register(value), class_offset));
2266          // If heap poisoning is enabled, no need to unpoison `temp`
2267          // nor `temp2`, as we are comparing two poisoned references.
2268          __ Cmp(temp, temp2);
2269
2270          if (instruction->StaticTypeOfArrayIsObjectArray()) {
2271            vixl::Label do_put;
2272            __ B(eq, &do_put);
2273            // If heap poisoning is enabled, the `temp` reference has
2274            // not been unpoisoned yet; unpoison it now.
2275            GetAssembler()->MaybeUnpoisonHeapReference(temp);
2276
2277            // /* HeapReference<Class> */ temp = temp->super_class_
2278            __ Ldr(temp, HeapOperand(temp, super_offset));
2279            // If heap poisoning is enabled, no need to unpoison
2280            // `temp`, as we are comparing against null below.
2281            __ Cbnz(temp, slow_path->GetEntryLabel());
2282            __ Bind(&do_put);
2283          } else {
2284            __ B(ne, slow_path->GetEntryLabel());
2285          }
2286          temps.Release(temp2);
2287        }
2288      }
2289
2290      if (kPoisonHeapReferences) {
2291        Register temp2 = temps.AcquireSameSizeAs(array);
2292          DCHECK(value.IsW());
2293        __ Mov(temp2, value.W());
2294        GetAssembler()->PoisonHeapReference(temp2);
2295        source = temp2;
2296      }
2297
2298      if (!index.IsConstant()) {
2299        __ Add(temp, array, offset);
2300      }
2301      __ Str(source, destination);
2302
2303      if (!may_need_runtime_call_for_type_check) {
2304        codegen_->MaybeRecordImplicitNullCheck(instruction);
2305      }
2306    }
2307
2308    codegen_->MarkGCCard(array, value.W(), instruction->GetValueCanBeNull());
2309
2310    if (done.IsLinked()) {
2311      __ Bind(&done);
2312    }
2313
2314    if (slow_path != nullptr) {
2315      __ Bind(slow_path->GetExitLabel());
2316    }
2317  }
2318}
2319
2320void LocationsBuilderARM64::VisitBoundsCheck(HBoundsCheck* instruction) {
2321  LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock()
2322      ? LocationSummary::kCallOnSlowPath
2323      : LocationSummary::kNoCall;
2324  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
2325  locations->SetInAt(0, Location::RequiresRegister());
2326  locations->SetInAt(1, ARM64EncodableConstantOrRegister(instruction->InputAt(1), instruction));
2327  if (instruction->HasUses()) {
2328    locations->SetOut(Location::SameAsFirstInput());
2329  }
2330}
2331
2332void InstructionCodeGeneratorARM64::VisitBoundsCheck(HBoundsCheck* instruction) {
2333  BoundsCheckSlowPathARM64* slow_path =
2334      new (GetGraph()->GetArena()) BoundsCheckSlowPathARM64(instruction);
2335  codegen_->AddSlowPath(slow_path);
2336
2337  __ Cmp(InputRegisterAt(instruction, 0), InputOperandAt(instruction, 1));
2338  __ B(slow_path->GetEntryLabel(), hs);
2339}
2340
2341void LocationsBuilderARM64::VisitClinitCheck(HClinitCheck* check) {
2342  LocationSummary* locations =
2343      new (GetGraph()->GetArena()) LocationSummary(check, LocationSummary::kCallOnSlowPath);
2344  locations->SetInAt(0, Location::RequiresRegister());
2345  if (check->HasUses()) {
2346    locations->SetOut(Location::SameAsFirstInput());
2347  }
2348}
2349
2350void InstructionCodeGeneratorARM64::VisitClinitCheck(HClinitCheck* check) {
2351  // We assume the class is not null.
2352  SlowPathCodeARM64* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathARM64(
2353      check->GetLoadClass(), check, check->GetDexPc(), true);
2354  codegen_->AddSlowPath(slow_path);
2355  GenerateClassInitializationCheck(slow_path, InputRegisterAt(check, 0));
2356}
2357
2358static bool IsFloatingPointZeroConstant(HInstruction* instruction) {
2359  return (instruction->IsFloatConstant() && (instruction->AsFloatConstant()->GetValue() == 0.0f))
2360      || (instruction->IsDoubleConstant() && (instruction->AsDoubleConstant()->GetValue() == 0.0));
2361}
2362
2363void LocationsBuilderARM64::VisitCompare(HCompare* compare) {
2364  LocationSummary* locations =
2365      new (GetGraph()->GetArena()) LocationSummary(compare, LocationSummary::kNoCall);
2366  Primitive::Type in_type = compare->InputAt(0)->GetType();
2367  switch (in_type) {
2368    case Primitive::kPrimLong: {
2369      locations->SetInAt(0, Location::RequiresRegister());
2370      locations->SetInAt(1, ARM64EncodableConstantOrRegister(compare->InputAt(1), compare));
2371      locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2372      break;
2373    }
2374    case Primitive::kPrimFloat:
2375    case Primitive::kPrimDouble: {
2376      locations->SetInAt(0, Location::RequiresFpuRegister());
2377      locations->SetInAt(1,
2378                         IsFloatingPointZeroConstant(compare->InputAt(1))
2379                             ? Location::ConstantLocation(compare->InputAt(1)->AsConstant())
2380                             : Location::RequiresFpuRegister());
2381      locations->SetOut(Location::RequiresRegister());
2382      break;
2383    }
2384    default:
2385      LOG(FATAL) << "Unexpected type for compare operation " << in_type;
2386  }
2387}
2388
2389void InstructionCodeGeneratorARM64::VisitCompare(HCompare* compare) {
2390  Primitive::Type in_type = compare->InputAt(0)->GetType();
2391
2392  //  0 if: left == right
2393  //  1 if: left  > right
2394  // -1 if: left  < right
2395  switch (in_type) {
2396    case Primitive::kPrimLong: {
2397      Register result = OutputRegister(compare);
2398      Register left = InputRegisterAt(compare, 0);
2399      Operand right = InputOperandAt(compare, 1);
2400
2401      __ Cmp(left, right);
2402      __ Cset(result, ne);
2403      __ Cneg(result, result, lt);
2404      break;
2405    }
2406    case Primitive::kPrimFloat:
2407    case Primitive::kPrimDouble: {
2408      Register result = OutputRegister(compare);
2409      FPRegister left = InputFPRegisterAt(compare, 0);
2410      if (compare->GetLocations()->InAt(1).IsConstant()) {
2411        DCHECK(IsFloatingPointZeroConstant(compare->GetLocations()->InAt(1).GetConstant()));
2412        // 0.0 is the only immediate that can be encoded directly in an FCMP instruction.
2413        __ Fcmp(left, 0.0);
2414      } else {
2415        __ Fcmp(left, InputFPRegisterAt(compare, 1));
2416      }
2417      if (compare->IsGtBias()) {
2418        __ Cset(result, ne);
2419      } else {
2420        __ Csetm(result, ne);
2421      }
2422      __ Cneg(result, result, compare->IsGtBias() ? mi : gt);
2423      break;
2424    }
2425    default:
2426      LOG(FATAL) << "Unimplemented compare type " << in_type;
2427  }
2428}
2429
2430void LocationsBuilderARM64::HandleCondition(HCondition* instruction) {
2431  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
2432
2433  if (Primitive::IsFloatingPointType(instruction->InputAt(0)->GetType())) {
2434    locations->SetInAt(0, Location::RequiresFpuRegister());
2435    locations->SetInAt(1,
2436                       IsFloatingPointZeroConstant(instruction->InputAt(1))
2437                           ? Location::ConstantLocation(instruction->InputAt(1)->AsConstant())
2438                           : Location::RequiresFpuRegister());
2439  } else {
2440    // Integer cases.
2441    locations->SetInAt(0, Location::RequiresRegister());
2442    locations->SetInAt(1, ARM64EncodableConstantOrRegister(instruction->InputAt(1), instruction));
2443  }
2444
2445  if (instruction->NeedsMaterialization()) {
2446    locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2447  }
2448}
2449
2450void InstructionCodeGeneratorARM64::HandleCondition(HCondition* instruction) {
2451  if (!instruction->NeedsMaterialization()) {
2452    return;
2453  }
2454
2455  LocationSummary* locations = instruction->GetLocations();
2456  Register res = RegisterFrom(locations->Out(), instruction->GetType());
2457  IfCondition if_cond = instruction->GetCondition();
2458  Condition arm64_cond = ARM64Condition(if_cond);
2459
2460  if (Primitive::IsFloatingPointType(instruction->InputAt(0)->GetType())) {
2461    FPRegister lhs = InputFPRegisterAt(instruction, 0);
2462    if (locations->InAt(1).IsConstant()) {
2463      DCHECK(IsFloatingPointZeroConstant(locations->InAt(1).GetConstant()));
2464      // 0.0 is the only immediate that can be encoded directly in an FCMP instruction.
2465      __ Fcmp(lhs, 0.0);
2466    } else {
2467      __ Fcmp(lhs, InputFPRegisterAt(instruction, 1));
2468    }
2469    __ Cset(res, arm64_cond);
2470    if (instruction->IsFPConditionTrueIfNaN()) {
2471      // res = IsUnordered(arm64_cond) ? 1 : res  <=>  res = IsNotUnordered(arm64_cond) ? res : 1
2472      __ Csel(res, res, Operand(1), vc);  // VC for "not unordered".
2473    } else if (instruction->IsFPConditionFalseIfNaN()) {
2474      // res = IsUnordered(arm64_cond) ? 0 : res  <=>  res = IsNotUnordered(arm64_cond) ? res : 0
2475      __ Csel(res, res, Operand(0), vc);  // VC for "not unordered".
2476    }
2477  } else {
2478    // Integer cases.
2479    Register lhs = InputRegisterAt(instruction, 0);
2480    Operand rhs = InputOperandAt(instruction, 1);
2481    __ Cmp(lhs, rhs);
2482    __ Cset(res, arm64_cond);
2483  }
2484}
2485
2486#define FOR_EACH_CONDITION_INSTRUCTION(M)                                                \
2487  M(Equal)                                                                               \
2488  M(NotEqual)                                                                            \
2489  M(LessThan)                                                                            \
2490  M(LessThanOrEqual)                                                                     \
2491  M(GreaterThan)                                                                         \
2492  M(GreaterThanOrEqual)                                                                  \
2493  M(Below)                                                                               \
2494  M(BelowOrEqual)                                                                        \
2495  M(Above)                                                                               \
2496  M(AboveOrEqual)
2497#define DEFINE_CONDITION_VISITORS(Name)                                                  \
2498void LocationsBuilderARM64::Visit##Name(H##Name* comp) { HandleCondition(comp); }         \
2499void InstructionCodeGeneratorARM64::Visit##Name(H##Name* comp) { HandleCondition(comp); }
2500FOR_EACH_CONDITION_INSTRUCTION(DEFINE_CONDITION_VISITORS)
2501#undef DEFINE_CONDITION_VISITORS
2502#undef FOR_EACH_CONDITION_INSTRUCTION
2503
2504void InstructionCodeGeneratorARM64::DivRemOneOrMinusOne(HBinaryOperation* instruction) {
2505  DCHECK(instruction->IsDiv() || instruction->IsRem());
2506
2507  LocationSummary* locations = instruction->GetLocations();
2508  Location second = locations->InAt(1);
2509  DCHECK(second.IsConstant());
2510
2511  Register out = OutputRegister(instruction);
2512  Register dividend = InputRegisterAt(instruction, 0);
2513  int64_t imm = Int64FromConstant(second.GetConstant());
2514  DCHECK(imm == 1 || imm == -1);
2515
2516  if (instruction->IsRem()) {
2517    __ Mov(out, 0);
2518  } else {
2519    if (imm == 1) {
2520      __ Mov(out, dividend);
2521    } else {
2522      __ Neg(out, dividend);
2523    }
2524  }
2525}
2526
2527void InstructionCodeGeneratorARM64::DivRemByPowerOfTwo(HBinaryOperation* instruction) {
2528  DCHECK(instruction->IsDiv() || instruction->IsRem());
2529
2530  LocationSummary* locations = instruction->GetLocations();
2531  Location second = locations->InAt(1);
2532  DCHECK(second.IsConstant());
2533
2534  Register out = OutputRegister(instruction);
2535  Register dividend = InputRegisterAt(instruction, 0);
2536  int64_t imm = Int64FromConstant(second.GetConstant());
2537  uint64_t abs_imm = static_cast<uint64_t>(AbsOrMin(imm));
2538  int ctz_imm = CTZ(abs_imm);
2539
2540  UseScratchRegisterScope temps(GetVIXLAssembler());
2541  Register temp = temps.AcquireSameSizeAs(out);
2542
2543  if (instruction->IsDiv()) {
2544    __ Add(temp, dividend, abs_imm - 1);
2545    __ Cmp(dividend, 0);
2546    __ Csel(out, temp, dividend, lt);
2547    if (imm > 0) {
2548      __ Asr(out, out, ctz_imm);
2549    } else {
2550      __ Neg(out, Operand(out, ASR, ctz_imm));
2551    }
2552  } else {
2553    int bits = instruction->GetResultType() == Primitive::kPrimInt ? 32 : 64;
2554    __ Asr(temp, dividend, bits - 1);
2555    __ Lsr(temp, temp, bits - ctz_imm);
2556    __ Add(out, dividend, temp);
2557    __ And(out, out, abs_imm - 1);
2558    __ Sub(out, out, temp);
2559  }
2560}
2561
2562void InstructionCodeGeneratorARM64::GenerateDivRemWithAnyConstant(HBinaryOperation* instruction) {
2563  DCHECK(instruction->IsDiv() || instruction->IsRem());
2564
2565  LocationSummary* locations = instruction->GetLocations();
2566  Location second = locations->InAt(1);
2567  DCHECK(second.IsConstant());
2568
2569  Register out = OutputRegister(instruction);
2570  Register dividend = InputRegisterAt(instruction, 0);
2571  int64_t imm = Int64FromConstant(second.GetConstant());
2572
2573  Primitive::Type type = instruction->GetResultType();
2574  DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong);
2575
2576  int64_t magic;
2577  int shift;
2578  CalculateMagicAndShiftForDivRem(imm, type == Primitive::kPrimLong /* is_long */, &magic, &shift);
2579
2580  UseScratchRegisterScope temps(GetVIXLAssembler());
2581  Register temp = temps.AcquireSameSizeAs(out);
2582
2583  // temp = get_high(dividend * magic)
2584  __ Mov(temp, magic);
2585  if (type == Primitive::kPrimLong) {
2586    __ Smulh(temp, dividend, temp);
2587  } else {
2588    __ Smull(temp.X(), dividend, temp);
2589    __ Lsr(temp.X(), temp.X(), 32);
2590  }
2591
2592  if (imm > 0 && magic < 0) {
2593    __ Add(temp, temp, dividend);
2594  } else if (imm < 0 && magic > 0) {
2595    __ Sub(temp, temp, dividend);
2596  }
2597
2598  if (shift != 0) {
2599    __ Asr(temp, temp, shift);
2600  }
2601
2602  if (instruction->IsDiv()) {
2603    __ Sub(out, temp, Operand(temp, ASR, type == Primitive::kPrimLong ? 63 : 31));
2604  } else {
2605    __ Sub(temp, temp, Operand(temp, ASR, type == Primitive::kPrimLong ? 63 : 31));
2606    // TODO: Strength reduction for msub.
2607    Register temp_imm = temps.AcquireSameSizeAs(out);
2608    __ Mov(temp_imm, imm);
2609    __ Msub(out, temp, temp_imm, dividend);
2610  }
2611}
2612
2613void InstructionCodeGeneratorARM64::GenerateDivRemIntegral(HBinaryOperation* instruction) {
2614  DCHECK(instruction->IsDiv() || instruction->IsRem());
2615  Primitive::Type type = instruction->GetResultType();
2616  DCHECK(type == Primitive::kPrimInt || Primitive::kPrimLong);
2617
2618  LocationSummary* locations = instruction->GetLocations();
2619  Register out = OutputRegister(instruction);
2620  Location second = locations->InAt(1);
2621
2622  if (second.IsConstant()) {
2623    int64_t imm = Int64FromConstant(second.GetConstant());
2624
2625    if (imm == 0) {
2626      // Do not generate anything. DivZeroCheck would prevent any code to be executed.
2627    } else if (imm == 1 || imm == -1) {
2628      DivRemOneOrMinusOne(instruction);
2629    } else if (IsPowerOfTwo(AbsOrMin(imm))) {
2630      DivRemByPowerOfTwo(instruction);
2631    } else {
2632      DCHECK(imm <= -2 || imm >= 2);
2633      GenerateDivRemWithAnyConstant(instruction);
2634    }
2635  } else {
2636    Register dividend = InputRegisterAt(instruction, 0);
2637    Register divisor = InputRegisterAt(instruction, 1);
2638    if (instruction->IsDiv()) {
2639      __ Sdiv(out, dividend, divisor);
2640    } else {
2641      UseScratchRegisterScope temps(GetVIXLAssembler());
2642      Register temp = temps.AcquireSameSizeAs(out);
2643      __ Sdiv(temp, dividend, divisor);
2644      __ Msub(out, temp, divisor, dividend);
2645    }
2646  }
2647}
2648
2649void LocationsBuilderARM64::VisitDiv(HDiv* div) {
2650  LocationSummary* locations =
2651      new (GetGraph()->GetArena()) LocationSummary(div, LocationSummary::kNoCall);
2652  switch (div->GetResultType()) {
2653    case Primitive::kPrimInt:
2654    case Primitive::kPrimLong:
2655      locations->SetInAt(0, Location::RequiresRegister());
2656      locations->SetInAt(1, Location::RegisterOrConstant(div->InputAt(1)));
2657      locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2658      break;
2659
2660    case Primitive::kPrimFloat:
2661    case Primitive::kPrimDouble:
2662      locations->SetInAt(0, Location::RequiresFpuRegister());
2663      locations->SetInAt(1, Location::RequiresFpuRegister());
2664      locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
2665      break;
2666
2667    default:
2668      LOG(FATAL) << "Unexpected div type " << div->GetResultType();
2669  }
2670}
2671
2672void InstructionCodeGeneratorARM64::VisitDiv(HDiv* div) {
2673  Primitive::Type type = div->GetResultType();
2674  switch (type) {
2675    case Primitive::kPrimInt:
2676    case Primitive::kPrimLong:
2677      GenerateDivRemIntegral(div);
2678      break;
2679
2680    case Primitive::kPrimFloat:
2681    case Primitive::kPrimDouble:
2682      __ Fdiv(OutputFPRegister(div), InputFPRegisterAt(div, 0), InputFPRegisterAt(div, 1));
2683      break;
2684
2685    default:
2686      LOG(FATAL) << "Unexpected div type " << type;
2687  }
2688}
2689
2690void LocationsBuilderARM64::VisitDivZeroCheck(HDivZeroCheck* instruction) {
2691  LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock()
2692      ? LocationSummary::kCallOnSlowPath
2693      : LocationSummary::kNoCall;
2694  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
2695  locations->SetInAt(0, Location::RegisterOrConstant(instruction->InputAt(0)));
2696  if (instruction->HasUses()) {
2697    locations->SetOut(Location::SameAsFirstInput());
2698  }
2699}
2700
2701void InstructionCodeGeneratorARM64::VisitDivZeroCheck(HDivZeroCheck* instruction) {
2702  SlowPathCodeARM64* slow_path =
2703      new (GetGraph()->GetArena()) DivZeroCheckSlowPathARM64(instruction);
2704  codegen_->AddSlowPath(slow_path);
2705  Location value = instruction->GetLocations()->InAt(0);
2706
2707  Primitive::Type type = instruction->GetType();
2708
2709  if ((type == Primitive::kPrimBoolean) || !Primitive::IsIntegralType(type)) {
2710      LOG(FATAL) << "Unexpected type " << type << " for DivZeroCheck.";
2711    return;
2712  }
2713
2714  if (value.IsConstant()) {
2715    int64_t divisor = Int64ConstantFrom(value);
2716    if (divisor == 0) {
2717      __ B(slow_path->GetEntryLabel());
2718    } else {
2719      // A division by a non-null constant is valid. We don't need to perform
2720      // any check, so simply fall through.
2721    }
2722  } else {
2723    __ Cbz(InputRegisterAt(instruction, 0), slow_path->GetEntryLabel());
2724  }
2725}
2726
2727void LocationsBuilderARM64::VisitDoubleConstant(HDoubleConstant* constant) {
2728  LocationSummary* locations =
2729      new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
2730  locations->SetOut(Location::ConstantLocation(constant));
2731}
2732
2733void InstructionCodeGeneratorARM64::VisitDoubleConstant(
2734    HDoubleConstant* constant ATTRIBUTE_UNUSED) {
2735  // Will be generated at use site.
2736}
2737
2738void LocationsBuilderARM64::VisitExit(HExit* exit) {
2739  exit->SetLocations(nullptr);
2740}
2741
2742void InstructionCodeGeneratorARM64::VisitExit(HExit* exit ATTRIBUTE_UNUSED) {
2743}
2744
2745void LocationsBuilderARM64::VisitFloatConstant(HFloatConstant* constant) {
2746  LocationSummary* locations =
2747      new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
2748  locations->SetOut(Location::ConstantLocation(constant));
2749}
2750
2751void InstructionCodeGeneratorARM64::VisitFloatConstant(HFloatConstant* constant ATTRIBUTE_UNUSED) {
2752  // Will be generated at use site.
2753}
2754
2755void InstructionCodeGeneratorARM64::HandleGoto(HInstruction* got, HBasicBlock* successor) {
2756  DCHECK(!successor->IsExitBlock());
2757  HBasicBlock* block = got->GetBlock();
2758  HInstruction* previous = got->GetPrevious();
2759  HLoopInformation* info = block->GetLoopInformation();
2760
2761  if (info != nullptr && info->IsBackEdge(*block) && info->HasSuspendCheck()) {
2762    codegen_->ClearSpillSlotsFromLoopPhisInStackMap(info->GetSuspendCheck());
2763    GenerateSuspendCheck(info->GetSuspendCheck(), successor);
2764    return;
2765  }
2766  if (block->IsEntryBlock() && (previous != nullptr) && previous->IsSuspendCheck()) {
2767    GenerateSuspendCheck(previous->AsSuspendCheck(), nullptr);
2768  }
2769  if (!codegen_->GoesToNextBlock(block, successor)) {
2770    __ B(codegen_->GetLabelOf(successor));
2771  }
2772}
2773
2774void LocationsBuilderARM64::VisitGoto(HGoto* got) {
2775  got->SetLocations(nullptr);
2776}
2777
2778void InstructionCodeGeneratorARM64::VisitGoto(HGoto* got) {
2779  HandleGoto(got, got->GetSuccessor());
2780}
2781
2782void LocationsBuilderARM64::VisitTryBoundary(HTryBoundary* try_boundary) {
2783  try_boundary->SetLocations(nullptr);
2784}
2785
2786void InstructionCodeGeneratorARM64::VisitTryBoundary(HTryBoundary* try_boundary) {
2787  HBasicBlock* successor = try_boundary->GetNormalFlowSuccessor();
2788  if (!successor->IsExitBlock()) {
2789    HandleGoto(try_boundary, successor);
2790  }
2791}
2792
2793void InstructionCodeGeneratorARM64::GenerateTestAndBranch(HInstruction* instruction,
2794                                                          size_t condition_input_index,
2795                                                          vixl::Label* true_target,
2796                                                          vixl::Label* false_target) {
2797  // FP branching requires both targets to be explicit. If either of the targets
2798  // is nullptr (fallthrough) use and bind `fallthrough_target` instead.
2799  vixl::Label fallthrough_target;
2800  HInstruction* cond = instruction->InputAt(condition_input_index);
2801
2802  if (true_target == nullptr && false_target == nullptr) {
2803    // Nothing to do. The code always falls through.
2804    return;
2805  } else if (cond->IsIntConstant()) {
2806    // Constant condition, statically compared against 1.
2807    if (cond->AsIntConstant()->IsOne()) {
2808      if (true_target != nullptr) {
2809        __ B(true_target);
2810      }
2811    } else {
2812      DCHECK(cond->AsIntConstant()->IsZero());
2813      if (false_target != nullptr) {
2814        __ B(false_target);
2815      }
2816    }
2817    return;
2818  }
2819
2820  // The following code generates these patterns:
2821  //  (1) true_target == nullptr && false_target != nullptr
2822  //        - opposite condition true => branch to false_target
2823  //  (2) true_target != nullptr && false_target == nullptr
2824  //        - condition true => branch to true_target
2825  //  (3) true_target != nullptr && false_target != nullptr
2826  //        - condition true => branch to true_target
2827  //        - branch to false_target
2828  if (IsBooleanValueOrMaterializedCondition(cond)) {
2829    // The condition instruction has been materialized, compare the output to 0.
2830    Location cond_val = instruction->GetLocations()->InAt(condition_input_index);
2831    DCHECK(cond_val.IsRegister());
2832      if (true_target == nullptr) {
2833      __ Cbz(InputRegisterAt(instruction, condition_input_index), false_target);
2834    } else {
2835      __ Cbnz(InputRegisterAt(instruction, condition_input_index), true_target);
2836    }
2837  } else {
2838    // The condition instruction has not been materialized, use its inputs as
2839    // the comparison and its condition as the branch condition.
2840    HCondition* condition = cond->AsCondition();
2841
2842    Primitive::Type type = condition->InputAt(0)->GetType();
2843    if (Primitive::IsFloatingPointType(type)) {
2844      FPRegister lhs = InputFPRegisterAt(condition, 0);
2845      if (condition->GetLocations()->InAt(1).IsConstant()) {
2846        DCHECK(IsFloatingPointZeroConstant(condition->GetLocations()->InAt(1).GetConstant()));
2847        // 0.0 is the only immediate that can be encoded directly in an FCMP instruction.
2848        __ Fcmp(lhs, 0.0);
2849      } else {
2850        __ Fcmp(lhs, InputFPRegisterAt(condition, 1));
2851      }
2852      if (condition->IsFPConditionTrueIfNaN()) {
2853        __ B(vs, true_target == nullptr ? &fallthrough_target : true_target);
2854      } else if (condition->IsFPConditionFalseIfNaN()) {
2855        __ B(vs, false_target == nullptr ? &fallthrough_target : false_target);
2856      }
2857      if (true_target == nullptr) {
2858        __ B(ARM64Condition(condition->GetOppositeCondition()), false_target);
2859      } else {
2860        __ B(ARM64Condition(condition->GetCondition()), true_target);
2861      }
2862    } else {
2863      // Integer cases.
2864      Register lhs = InputRegisterAt(condition, 0);
2865      Operand rhs = InputOperandAt(condition, 1);
2866
2867      Condition arm64_cond;
2868      vixl::Label* non_fallthrough_target;
2869      if (true_target == nullptr) {
2870        arm64_cond = ARM64Condition(condition->GetOppositeCondition());
2871        non_fallthrough_target = false_target;
2872      } else {
2873        arm64_cond = ARM64Condition(condition->GetCondition());
2874        non_fallthrough_target = true_target;
2875      }
2876
2877      if ((arm64_cond != gt && arm64_cond != le) && rhs.IsImmediate() && (rhs.immediate() == 0)) {
2878        switch (arm64_cond) {
2879          case eq:
2880            __ Cbz(lhs, non_fallthrough_target);
2881            break;
2882          case ne:
2883            __ Cbnz(lhs, non_fallthrough_target);
2884            break;
2885          case lt:
2886            // Test the sign bit and branch accordingly.
2887            __ Tbnz(lhs, (lhs.IsX() ? kXRegSize : kWRegSize) - 1, non_fallthrough_target);
2888            break;
2889          case ge:
2890            // Test the sign bit and branch accordingly.
2891            __ Tbz(lhs, (lhs.IsX() ? kXRegSize : kWRegSize) - 1, non_fallthrough_target);
2892            break;
2893          default:
2894            // Without the `static_cast` the compiler throws an error for
2895            // `-Werror=sign-promo`.
2896            LOG(FATAL) << "Unexpected condition: " << static_cast<int>(arm64_cond);
2897        }
2898      } else {
2899        __ Cmp(lhs, rhs);
2900        __ B(arm64_cond, non_fallthrough_target);
2901      }
2902    }
2903  }
2904
2905  // If neither branch falls through (case 3), the conditional branch to `true_target`
2906  // was already emitted (case 2) and we need to emit a jump to `false_target`.
2907  if (true_target != nullptr && false_target != nullptr) {
2908    __ B(false_target);
2909  }
2910
2911  if (fallthrough_target.IsLinked()) {
2912    __ Bind(&fallthrough_target);
2913  }
2914}
2915
2916void LocationsBuilderARM64::VisitIf(HIf* if_instr) {
2917  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(if_instr);
2918  if (IsBooleanValueOrMaterializedCondition(if_instr->InputAt(0))) {
2919    locations->SetInAt(0, Location::RequiresRegister());
2920  }
2921}
2922
2923void InstructionCodeGeneratorARM64::VisitIf(HIf* if_instr) {
2924  HBasicBlock* true_successor = if_instr->IfTrueSuccessor();
2925  HBasicBlock* false_successor = if_instr->IfFalseSuccessor();
2926  vixl::Label* true_target = codegen_->GoesToNextBlock(if_instr->GetBlock(), true_successor) ?
2927      nullptr : codegen_->GetLabelOf(true_successor);
2928  vixl::Label* false_target = codegen_->GoesToNextBlock(if_instr->GetBlock(), false_successor) ?
2929      nullptr : codegen_->GetLabelOf(false_successor);
2930  GenerateTestAndBranch(if_instr, /* condition_input_index */ 0, true_target, false_target);
2931}
2932
2933void LocationsBuilderARM64::VisitDeoptimize(HDeoptimize* deoptimize) {
2934  LocationSummary* locations = new (GetGraph()->GetArena())
2935      LocationSummary(deoptimize, LocationSummary::kCallOnSlowPath);
2936  if (IsBooleanValueOrMaterializedCondition(deoptimize->InputAt(0))) {
2937    locations->SetInAt(0, Location::RequiresRegister());
2938  }
2939}
2940
2941void InstructionCodeGeneratorARM64::VisitDeoptimize(HDeoptimize* deoptimize) {
2942  SlowPathCodeARM64* slow_path =
2943      deopt_slow_paths_.NewSlowPath<DeoptimizationSlowPathARM64>(deoptimize);
2944  GenerateTestAndBranch(deoptimize,
2945                        /* condition_input_index */ 0,
2946                        slow_path->GetEntryLabel(),
2947                        /* false_target */ nullptr);
2948}
2949
2950void LocationsBuilderARM64::VisitNativeDebugInfo(HNativeDebugInfo* info) {
2951  new (GetGraph()->GetArena()) LocationSummary(info);
2952}
2953
2954void InstructionCodeGeneratorARM64::VisitNativeDebugInfo(HNativeDebugInfo* info) {
2955  if (codegen_->HasStackMapAtCurrentPc()) {
2956    // Ensure that we do not collide with the stack map of the previous instruction.
2957    __ Nop();
2958  }
2959  codegen_->RecordPcInfo(info, info->GetDexPc());
2960}
2961
2962void LocationsBuilderARM64::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
2963  HandleFieldGet(instruction);
2964}
2965
2966void InstructionCodeGeneratorARM64::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
2967  HandleFieldGet(instruction, instruction->GetFieldInfo());
2968}
2969
2970void LocationsBuilderARM64::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
2971  HandleFieldSet(instruction);
2972}
2973
2974void InstructionCodeGeneratorARM64::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
2975  HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
2976}
2977
2978void LocationsBuilderARM64::VisitInstanceOf(HInstanceOf* instruction) {
2979  LocationSummary::CallKind call_kind = LocationSummary::kNoCall;
2980  TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
2981  switch (type_check_kind) {
2982    case TypeCheckKind::kExactCheck:
2983    case TypeCheckKind::kAbstractClassCheck:
2984    case TypeCheckKind::kClassHierarchyCheck:
2985    case TypeCheckKind::kArrayObjectCheck:
2986      call_kind =
2987          kEmitCompilerReadBarrier ? LocationSummary::kCallOnSlowPath : LocationSummary::kNoCall;
2988      break;
2989    case TypeCheckKind::kArrayCheck:
2990    case TypeCheckKind::kUnresolvedCheck:
2991    case TypeCheckKind::kInterfaceCheck:
2992      call_kind = LocationSummary::kCallOnSlowPath;
2993      break;
2994  }
2995
2996  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
2997  locations->SetInAt(0, Location::RequiresRegister());
2998  locations->SetInAt(1, Location::RequiresRegister());
2999  // The "out" register is used as a temporary, so it overlaps with the inputs.
3000  // Note that TypeCheckSlowPathARM64 uses this register too.
3001  locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
3002  // When read barriers are enabled, we need a temporary register for
3003  // some cases.
3004  if (kEmitCompilerReadBarrier &&
3005      (type_check_kind == TypeCheckKind::kAbstractClassCheck ||
3006       type_check_kind == TypeCheckKind::kClassHierarchyCheck ||
3007       type_check_kind == TypeCheckKind::kArrayObjectCheck)) {
3008    locations->AddTemp(Location::RequiresRegister());
3009  }
3010}
3011
3012void InstructionCodeGeneratorARM64::VisitInstanceOf(HInstanceOf* instruction) {
3013  LocationSummary* locations = instruction->GetLocations();
3014  Location obj_loc = locations->InAt(0);
3015  Register obj = InputRegisterAt(instruction, 0);
3016  Register cls = InputRegisterAt(instruction, 1);
3017  Location out_loc = locations->Out();
3018  Register out = OutputRegister(instruction);
3019  uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
3020  uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
3021  uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
3022  uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value();
3023
3024  vixl::Label done, zero;
3025  SlowPathCodeARM64* slow_path = nullptr;
3026
3027  // Return 0 if `obj` is null.
3028  // Avoid null check if we know `obj` is not null.
3029  if (instruction->MustDoNullCheck()) {
3030    __ Cbz(obj, &zero);
3031  }
3032
3033  // /* HeapReference<Class> */ out = obj->klass_
3034  __ Ldr(out, HeapOperand(obj.W(), class_offset));
3035  codegen_->MaybeGenerateReadBarrier(instruction, out_loc, out_loc, obj_loc, class_offset);
3036
3037  switch (instruction->GetTypeCheckKind()) {
3038    case TypeCheckKind::kExactCheck: {
3039      __ Cmp(out, cls);
3040      __ Cset(out, eq);
3041      if (zero.IsLinked()) {
3042        __ B(&done);
3043      }
3044      break;
3045    }
3046
3047    case TypeCheckKind::kAbstractClassCheck: {
3048      // If the class is abstract, we eagerly fetch the super class of the
3049      // object to avoid doing a comparison we know will fail.
3050      vixl::Label loop, success;
3051      __ Bind(&loop);
3052      Location temp_loc = kEmitCompilerReadBarrier ? locations->GetTemp(0) : Location::NoLocation();
3053      if (kEmitCompilerReadBarrier) {
3054        // Save the value of `out` into `temp` before overwriting it
3055        // in the following move operation, as we will need it for the
3056        // read barrier below.
3057        Register temp = WRegisterFrom(temp_loc);
3058        __ Mov(temp, out);
3059      }
3060      // /* HeapReference<Class> */ out = out->super_class_
3061      __ Ldr(out, HeapOperand(out, super_offset));
3062      codegen_->MaybeGenerateReadBarrier(instruction, out_loc, out_loc, temp_loc, super_offset);
3063      // If `out` is null, we use it for the result, and jump to `done`.
3064      __ Cbz(out, &done);
3065      __ Cmp(out, cls);
3066      __ B(ne, &loop);
3067      __ Mov(out, 1);
3068      if (zero.IsLinked()) {
3069        __ B(&done);
3070      }
3071      break;
3072    }
3073
3074    case TypeCheckKind::kClassHierarchyCheck: {
3075      // Walk over the class hierarchy to find a match.
3076      vixl::Label loop, success;
3077      __ Bind(&loop);
3078      __ Cmp(out, cls);
3079      __ B(eq, &success);
3080      Location temp_loc = kEmitCompilerReadBarrier ? locations->GetTemp(0) : Location::NoLocation();
3081      if (kEmitCompilerReadBarrier) {
3082        // Save the value of `out` into `temp` before overwriting it
3083        // in the following move operation, as we will need it for the
3084        // read barrier below.
3085        Register temp = WRegisterFrom(temp_loc);
3086        __ Mov(temp, out);
3087      }
3088      // /* HeapReference<Class> */ out = out->super_class_
3089      __ Ldr(out, HeapOperand(out, super_offset));
3090      codegen_->MaybeGenerateReadBarrier(instruction, out_loc, out_loc, temp_loc, super_offset);
3091      __ Cbnz(out, &loop);
3092      // If `out` is null, we use it for the result, and jump to `done`.
3093      __ B(&done);
3094      __ Bind(&success);
3095      __ Mov(out, 1);
3096      if (zero.IsLinked()) {
3097        __ B(&done);
3098      }
3099      break;
3100    }
3101
3102    case TypeCheckKind::kArrayObjectCheck: {
3103      // Do an exact check.
3104      vixl::Label exact_check;
3105      __ Cmp(out, cls);
3106      __ B(eq, &exact_check);
3107      // Otherwise, we need to check that the object's class is a non-primitive array.
3108      Location temp_loc = kEmitCompilerReadBarrier ? locations->GetTemp(0) : Location::NoLocation();
3109      if (kEmitCompilerReadBarrier) {
3110        // Save the value of `out` into `temp` before overwriting it
3111        // in the following move operation, as we will need it for the
3112        // read barrier below.
3113        Register temp = WRegisterFrom(temp_loc);
3114        __ Mov(temp, out);
3115      }
3116      // /* HeapReference<Class> */ out = out->component_type_
3117      __ Ldr(out, HeapOperand(out, component_offset));
3118      codegen_->MaybeGenerateReadBarrier(instruction, out_loc, out_loc, temp_loc, component_offset);
3119      // If `out` is null, we use it for the result, and jump to `done`.
3120      __ Cbz(out, &done);
3121      __ Ldrh(out, HeapOperand(out, primitive_offset));
3122      static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
3123      __ Cbnz(out, &zero);
3124      __ Bind(&exact_check);
3125      __ Mov(out, 1);
3126      __ B(&done);
3127      break;
3128    }
3129
3130    case TypeCheckKind::kArrayCheck: {
3131      __ Cmp(out, cls);
3132      DCHECK(locations->OnlyCallsOnSlowPath());
3133      slow_path = new (GetGraph()->GetArena()) TypeCheckSlowPathARM64(instruction,
3134                                                                      /* is_fatal */ false);
3135      codegen_->AddSlowPath(slow_path);
3136      __ B(ne, slow_path->GetEntryLabel());
3137      __ Mov(out, 1);
3138      if (zero.IsLinked()) {
3139        __ B(&done);
3140      }
3141      break;
3142    }
3143
3144    case TypeCheckKind::kUnresolvedCheck:
3145    case TypeCheckKind::kInterfaceCheck: {
3146      // Note that we indeed only call on slow path, but we always go
3147      // into the slow path for the unresolved and interface check
3148      // cases.
3149      //
3150      // We cannot directly call the InstanceofNonTrivial runtime
3151      // entry point without resorting to a type checking slow path
3152      // here (i.e. by calling InvokeRuntime directly), as it would
3153      // require to assign fixed registers for the inputs of this
3154      // HInstanceOf instruction (following the runtime calling
3155      // convention), which might be cluttered by the potential first
3156      // read barrier emission at the beginning of this method.
3157      DCHECK(locations->OnlyCallsOnSlowPath());
3158      slow_path = new (GetGraph()->GetArena()) TypeCheckSlowPathARM64(instruction,
3159                                                                      /* is_fatal */ false);
3160      codegen_->AddSlowPath(slow_path);
3161      __ B(slow_path->GetEntryLabel());
3162      if (zero.IsLinked()) {
3163        __ B(&done);
3164      }
3165      break;
3166    }
3167  }
3168
3169  if (zero.IsLinked()) {
3170    __ Bind(&zero);
3171    __ Mov(out, 0);
3172  }
3173
3174  if (done.IsLinked()) {
3175    __ Bind(&done);
3176  }
3177
3178  if (slow_path != nullptr) {
3179    __ Bind(slow_path->GetExitLabel());
3180  }
3181}
3182
3183void LocationsBuilderARM64::VisitCheckCast(HCheckCast* instruction) {
3184  LocationSummary::CallKind call_kind = LocationSummary::kNoCall;
3185  bool throws_into_catch = instruction->CanThrowIntoCatchBlock();
3186
3187  TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
3188  switch (type_check_kind) {
3189    case TypeCheckKind::kExactCheck:
3190    case TypeCheckKind::kAbstractClassCheck:
3191    case TypeCheckKind::kClassHierarchyCheck:
3192    case TypeCheckKind::kArrayObjectCheck:
3193      call_kind = (throws_into_catch || kEmitCompilerReadBarrier) ?
3194          LocationSummary::kCallOnSlowPath :
3195          LocationSummary::kNoCall;  // In fact, call on a fatal (non-returning) slow path.
3196      break;
3197    case TypeCheckKind::kArrayCheck:
3198    case TypeCheckKind::kUnresolvedCheck:
3199    case TypeCheckKind::kInterfaceCheck:
3200      call_kind = LocationSummary::kCallOnSlowPath;
3201      break;
3202  }
3203
3204  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
3205  locations->SetInAt(0, Location::RequiresRegister());
3206  locations->SetInAt(1, Location::RequiresRegister());
3207  // Note that TypeCheckSlowPathARM64 uses this "temp" register too.
3208  locations->AddTemp(Location::RequiresRegister());
3209  locations->AddTemp(Location::RequiresRegister());
3210  // When read barriers are enabled, we need an additional temporary
3211  // register for some cases.
3212  if (kEmitCompilerReadBarrier &&
3213      (type_check_kind == TypeCheckKind::kAbstractClassCheck ||
3214       type_check_kind == TypeCheckKind::kClassHierarchyCheck ||
3215       type_check_kind == TypeCheckKind::kArrayObjectCheck)) {
3216     locations->AddTemp(Location::RequiresRegister());
3217  }
3218}
3219
3220void InstructionCodeGeneratorARM64::VisitCheckCast(HCheckCast* instruction) {
3221  LocationSummary* locations = instruction->GetLocations();
3222  Location obj_loc = locations->InAt(0);
3223  Register obj = InputRegisterAt(instruction, 0);
3224  Register cls = InputRegisterAt(instruction, 1);
3225  Location temp_loc = locations->GetTemp(0);
3226  Register temp = WRegisterFrom(temp_loc);
3227  uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
3228  uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
3229  uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
3230  uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value();
3231
3232  TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
3233  bool is_type_check_slow_path_fatal =
3234      (type_check_kind == TypeCheckKind::kExactCheck ||
3235       type_check_kind == TypeCheckKind::kAbstractClassCheck ||
3236       type_check_kind == TypeCheckKind::kClassHierarchyCheck ||
3237       type_check_kind == TypeCheckKind::kArrayObjectCheck) &&
3238      !instruction->CanThrowIntoCatchBlock();
3239  SlowPathCodeARM64* type_check_slow_path =
3240      new (GetGraph()->GetArena()) TypeCheckSlowPathARM64(instruction,
3241                                                          is_type_check_slow_path_fatal);
3242  codegen_->AddSlowPath(type_check_slow_path);
3243
3244  vixl::Label done;
3245  // Avoid null check if we know obj is not null.
3246  if (instruction->MustDoNullCheck()) {
3247    __ Cbz(obj, &done);
3248  }
3249
3250  // /* HeapReference<Class> */ temp = obj->klass_
3251  __ Ldr(temp, HeapOperand(obj, class_offset));
3252  codegen_->MaybeGenerateReadBarrier(instruction, temp_loc, temp_loc, obj_loc, class_offset);
3253
3254  switch (type_check_kind) {
3255    case TypeCheckKind::kExactCheck:
3256    case TypeCheckKind::kArrayCheck: {
3257      __ Cmp(temp, cls);
3258      // Jump to slow path for throwing the exception or doing a
3259      // more involved array check.
3260      __ B(ne, type_check_slow_path->GetEntryLabel());
3261      break;
3262    }
3263
3264    case TypeCheckKind::kAbstractClassCheck: {
3265      // If the class is abstract, we eagerly fetch the super class of the
3266      // object to avoid doing a comparison we know will fail.
3267      vixl::Label loop, compare_classes;
3268      __ Bind(&loop);
3269      Location temp2_loc =
3270          kEmitCompilerReadBarrier ? locations->GetTemp(1) : Location::NoLocation();
3271      if (kEmitCompilerReadBarrier) {
3272        // Save the value of `temp` into `temp2` before overwriting it
3273        // in the following move operation, as we will need it for the
3274        // read barrier below.
3275        Register temp2 = WRegisterFrom(temp2_loc);
3276        __ Mov(temp2, temp);
3277      }
3278      // /* HeapReference<Class> */ temp = temp->super_class_
3279      __ Ldr(temp, HeapOperand(temp, super_offset));
3280      codegen_->MaybeGenerateReadBarrier(instruction, temp_loc, temp_loc, temp2_loc, super_offset);
3281
3282      // If the class reference currently in `temp` is not null, jump
3283      // to the `compare_classes` label to compare it with the checked
3284      // class.
3285      __ Cbnz(temp, &compare_classes);
3286      // Otherwise, jump to the slow path to throw the exception.
3287      //
3288      // But before, move back the object's class into `temp` before
3289      // going into the slow path, as it has been overwritten in the
3290      // meantime.
3291      // /* HeapReference<Class> */ temp = obj->klass_
3292      __ Ldr(temp, HeapOperand(obj, class_offset));
3293      codegen_->MaybeGenerateReadBarrier(instruction, temp_loc, temp_loc, obj_loc, class_offset);
3294      __ B(type_check_slow_path->GetEntryLabel());
3295
3296      __ Bind(&compare_classes);
3297      __ Cmp(temp, cls);
3298      __ B(ne, &loop);
3299      break;
3300    }
3301
3302    case TypeCheckKind::kClassHierarchyCheck: {
3303      // Walk over the class hierarchy to find a match.
3304      vixl::Label loop;
3305      __ Bind(&loop);
3306      __ Cmp(temp, cls);
3307      __ B(eq, &done);
3308
3309      Location temp2_loc =
3310          kEmitCompilerReadBarrier ? locations->GetTemp(1) : Location::NoLocation();
3311      if (kEmitCompilerReadBarrier) {
3312        // Save the value of `temp` into `temp2` before overwriting it
3313        // in the following move operation, as we will need it for the
3314        // read barrier below.
3315        Register temp2 = WRegisterFrom(temp2_loc);
3316        __ Mov(temp2, temp);
3317      }
3318      // /* HeapReference<Class> */ temp = temp->super_class_
3319      __ Ldr(temp, HeapOperand(temp, super_offset));
3320      codegen_->MaybeGenerateReadBarrier(instruction, temp_loc, temp_loc, temp2_loc, super_offset);
3321
3322      // If the class reference currently in `temp` is not null, jump
3323      // back at the beginning of the loop.
3324      __ Cbnz(temp, &loop);
3325      // Otherwise, jump to the slow path to throw the exception.
3326      //
3327      // But before, move back the object's class into `temp` before
3328      // going into the slow path, as it has been overwritten in the
3329      // meantime.
3330      // /* HeapReference<Class> */ temp = obj->klass_
3331      __ Ldr(temp, HeapOperand(obj, class_offset));
3332      codegen_->MaybeGenerateReadBarrier(instruction, temp_loc, temp_loc, obj_loc, class_offset);
3333      __ B(type_check_slow_path->GetEntryLabel());
3334      break;
3335    }
3336
3337    case TypeCheckKind::kArrayObjectCheck: {
3338      // Do an exact check.
3339      vixl::Label check_non_primitive_component_type;
3340      __ Cmp(temp, cls);
3341      __ B(eq, &done);
3342
3343      // Otherwise, we need to check that the object's class is a non-primitive array.
3344      Location temp2_loc =
3345          kEmitCompilerReadBarrier ? locations->GetTemp(1) : Location::NoLocation();
3346      if (kEmitCompilerReadBarrier) {
3347        // Save the value of `temp` into `temp2` before overwriting it
3348        // in the following move operation, as we will need it for the
3349        // read barrier below.
3350        Register temp2 = WRegisterFrom(temp2_loc);
3351        __ Mov(temp2, temp);
3352      }
3353      // /* HeapReference<Class> */ temp = temp->component_type_
3354      __ Ldr(temp, HeapOperand(temp, component_offset));
3355      codegen_->MaybeGenerateReadBarrier(
3356          instruction, temp_loc, temp_loc, temp2_loc, component_offset);
3357
3358      // If the component type is not null (i.e. the object is indeed
3359      // an array), jump to label `check_non_primitive_component_type`
3360      // to further check that this component type is not a primitive
3361      // type.
3362      __ Cbnz(temp, &check_non_primitive_component_type);
3363      // Otherwise, jump to the slow path to throw the exception.
3364      //
3365      // But before, move back the object's class into `temp` before
3366      // going into the slow path, as it has been overwritten in the
3367      // meantime.
3368      // /* HeapReference<Class> */ temp = obj->klass_
3369      __ Ldr(temp, HeapOperand(obj, class_offset));
3370      codegen_->MaybeGenerateReadBarrier(instruction, temp_loc, temp_loc, obj_loc, class_offset);
3371      __ B(type_check_slow_path->GetEntryLabel());
3372
3373      __ Bind(&check_non_primitive_component_type);
3374      __ Ldrh(temp, HeapOperand(temp, primitive_offset));
3375      static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
3376      __ Cbz(temp, &done);
3377      // Same comment as above regarding `temp` and the slow path.
3378      // /* HeapReference<Class> */ temp = obj->klass_
3379      __ Ldr(temp, HeapOperand(obj, class_offset));
3380      codegen_->MaybeGenerateReadBarrier(instruction, temp_loc, temp_loc, obj_loc, class_offset);
3381      __ B(type_check_slow_path->GetEntryLabel());
3382      break;
3383    }
3384
3385    case TypeCheckKind::kUnresolvedCheck:
3386    case TypeCheckKind::kInterfaceCheck:
3387      // We always go into the type check slow path for the unresolved
3388      // and interface check cases.
3389      //
3390      // We cannot directly call the CheckCast runtime entry point
3391      // without resorting to a type checking slow path here (i.e. by
3392      // calling InvokeRuntime directly), as it would require to
3393      // assign fixed registers for the inputs of this HInstanceOf
3394      // instruction (following the runtime calling convention), which
3395      // might be cluttered by the potential first read barrier
3396      // emission at the beginning of this method.
3397      __ B(type_check_slow_path->GetEntryLabel());
3398      break;
3399  }
3400  __ Bind(&done);
3401
3402  __ Bind(type_check_slow_path->GetExitLabel());
3403}
3404
3405void LocationsBuilderARM64::VisitIntConstant(HIntConstant* constant) {
3406  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(constant);
3407  locations->SetOut(Location::ConstantLocation(constant));
3408}
3409
3410void InstructionCodeGeneratorARM64::VisitIntConstant(HIntConstant* constant ATTRIBUTE_UNUSED) {
3411  // Will be generated at use site.
3412}
3413
3414void LocationsBuilderARM64::VisitNullConstant(HNullConstant* constant) {
3415  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(constant);
3416  locations->SetOut(Location::ConstantLocation(constant));
3417}
3418
3419void InstructionCodeGeneratorARM64::VisitNullConstant(HNullConstant* constant ATTRIBUTE_UNUSED) {
3420  // Will be generated at use site.
3421}
3422
3423void LocationsBuilderARM64::VisitInvokeUnresolved(HInvokeUnresolved* invoke) {
3424  // The trampoline uses the same calling convention as dex calling conventions,
3425  // except instead of loading arg0/r0 with the target Method*, arg0/r0 will contain
3426  // the method_idx.
3427  HandleInvoke(invoke);
3428}
3429
3430void InstructionCodeGeneratorARM64::VisitInvokeUnresolved(HInvokeUnresolved* invoke) {
3431  codegen_->GenerateInvokeUnresolvedRuntimeCall(invoke);
3432}
3433
3434void LocationsBuilderARM64::HandleInvoke(HInvoke* invoke) {
3435  InvokeDexCallingConventionVisitorARM64 calling_convention_visitor;
3436  CodeGenerator::CreateCommonInvokeLocationSummary(invoke, &calling_convention_visitor);
3437}
3438
3439void LocationsBuilderARM64::VisitInvokeInterface(HInvokeInterface* invoke) {
3440  HandleInvoke(invoke);
3441}
3442
3443void InstructionCodeGeneratorARM64::VisitInvokeInterface(HInvokeInterface* invoke) {
3444  // TODO: b/18116999, our IMTs can miss an IncompatibleClassChangeError.
3445  LocationSummary* locations = invoke->GetLocations();
3446  Register temp = XRegisterFrom(locations->GetTemp(0));
3447  uint32_t method_offset = mirror::Class::EmbeddedImTableEntryOffset(
3448      invoke->GetImtIndex() % mirror::Class::kImtSize, kArm64PointerSize).Uint32Value();
3449  Location receiver = locations->InAt(0);
3450  Offset class_offset = mirror::Object::ClassOffset();
3451  Offset entry_point = ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArm64WordSize);
3452
3453  // The register ip1 is required to be used for the hidden argument in
3454  // art_quick_imt_conflict_trampoline, so prevent VIXL from using it.
3455  MacroAssembler* masm = GetVIXLAssembler();
3456  UseScratchRegisterScope scratch_scope(masm);
3457  BlockPoolsScope block_pools(masm);
3458  scratch_scope.Exclude(ip1);
3459  __ Mov(ip1, invoke->GetDexMethodIndex());
3460
3461  if (receiver.IsStackSlot()) {
3462    __ Ldr(temp.W(), StackOperandFrom(receiver));
3463    // /* HeapReference<Class> */ temp = temp->klass_
3464    __ Ldr(temp.W(), HeapOperand(temp.W(), class_offset));
3465  } else {
3466    // /* HeapReference<Class> */ temp = receiver->klass_
3467    __ Ldr(temp.W(), HeapOperandFrom(receiver, class_offset));
3468  }
3469  codegen_->MaybeRecordImplicitNullCheck(invoke);
3470  // Instead of simply (possibly) unpoisoning `temp` here, we should
3471  // emit a read barrier for the previous class reference load.
3472  // However this is not required in practice, as this is an
3473  // intermediate/temporary reference and because the current
3474  // concurrent copying collector keeps the from-space memory
3475  // intact/accessible until the end of the marking phase (the
3476  // concurrent copying collector may not in the future).
3477  GetAssembler()->MaybeUnpoisonHeapReference(temp.W());
3478  // temp = temp->GetImtEntryAt(method_offset);
3479  __ Ldr(temp, MemOperand(temp, method_offset));
3480  // lr = temp->GetEntryPoint();
3481  __ Ldr(lr, MemOperand(temp, entry_point.Int32Value()));
3482  // lr();
3483  __ Blr(lr);
3484  DCHECK(!codegen_->IsLeafMethod());
3485  codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
3486}
3487
3488void LocationsBuilderARM64::VisitInvokeVirtual(HInvokeVirtual* invoke) {
3489  IntrinsicLocationsBuilderARM64 intrinsic(GetGraph()->GetArena());
3490  if (intrinsic.TryDispatch(invoke)) {
3491    return;
3492  }
3493
3494  HandleInvoke(invoke);
3495}
3496
3497void LocationsBuilderARM64::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
3498  // When we do not run baseline, explicit clinit checks triggered by static
3499  // invokes must have been pruned by art::PrepareForRegisterAllocation.
3500  DCHECK(codegen_->IsBaseline() || !invoke->IsStaticWithExplicitClinitCheck());
3501
3502  IntrinsicLocationsBuilderARM64 intrinsic(GetGraph()->GetArena());
3503  if (intrinsic.TryDispatch(invoke)) {
3504    return;
3505  }
3506
3507  HandleInvoke(invoke);
3508}
3509
3510static bool TryGenerateIntrinsicCode(HInvoke* invoke, CodeGeneratorARM64* codegen) {
3511  if (invoke->GetLocations()->Intrinsified()) {
3512    IntrinsicCodeGeneratorARM64 intrinsic(codegen);
3513    intrinsic.Dispatch(invoke);
3514    return true;
3515  }
3516  return false;
3517}
3518
3519HInvokeStaticOrDirect::DispatchInfo CodeGeneratorARM64::GetSupportedInvokeStaticOrDirectDispatch(
3520      const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
3521      MethodReference target_method ATTRIBUTE_UNUSED) {
3522  // On arm64 we support all dispatch types.
3523  return desired_dispatch_info;
3524}
3525
3526void CodeGeneratorARM64::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp) {
3527  // For better instruction scheduling we load the direct code pointer before the method pointer.
3528  bool direct_code_loaded = false;
3529  switch (invoke->GetCodePtrLocation()) {
3530    case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup:
3531      // LR = code address from literal pool with link-time patch.
3532      __ Ldr(lr, DeduplicateMethodCodeLiteral(invoke->GetTargetMethod()));
3533      direct_code_loaded = true;
3534      break;
3535    case HInvokeStaticOrDirect::CodePtrLocation::kCallDirect:
3536      // LR = invoke->GetDirectCodePtr();
3537      __ Ldr(lr, DeduplicateUint64Literal(invoke->GetDirectCodePtr()));
3538      direct_code_loaded = true;
3539      break;
3540    default:
3541      break;
3542  }
3543
3544  // Make sure that ArtMethod* is passed in kArtMethodRegister as per the calling convention.
3545  Location callee_method = temp;  // For all kinds except kRecursive, callee will be in temp.
3546  switch (invoke->GetMethodLoadKind()) {
3547    case HInvokeStaticOrDirect::MethodLoadKind::kStringInit:
3548      // temp = thread->string_init_entrypoint
3549      __ Ldr(XRegisterFrom(temp), MemOperand(tr, invoke->GetStringInitOffset()));
3550      break;
3551    case HInvokeStaticOrDirect::MethodLoadKind::kRecursive:
3552      callee_method = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex());
3553      break;
3554    case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress:
3555      // Load method address from literal pool.
3556      __ Ldr(XRegisterFrom(temp), DeduplicateUint64Literal(invoke->GetMethodAddress()));
3557      break;
3558    case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup:
3559      // Load method address from literal pool with a link-time patch.
3560      __ Ldr(XRegisterFrom(temp),
3561             DeduplicateMethodAddressLiteral(invoke->GetTargetMethod()));
3562      break;
3563    case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative: {
3564      // Add ADRP with its PC-relative DexCache access patch.
3565      pc_relative_dex_cache_patches_.emplace_back(*invoke->GetTargetMethod().dex_file,
3566                                                  invoke->GetDexCacheArrayOffset());
3567      vixl::Label* pc_insn_label = &pc_relative_dex_cache_patches_.back().label;
3568      {
3569        vixl::SingleEmissionCheckScope guard(GetVIXLAssembler());
3570        __ Bind(pc_insn_label);
3571        __ adrp(XRegisterFrom(temp), 0);
3572      }
3573      pc_relative_dex_cache_patches_.back().pc_insn_label = pc_insn_label;
3574      // Add LDR with its PC-relative DexCache access patch.
3575      pc_relative_dex_cache_patches_.emplace_back(*invoke->GetTargetMethod().dex_file,
3576                                                  invoke->GetDexCacheArrayOffset());
3577      {
3578        vixl::SingleEmissionCheckScope guard(GetVIXLAssembler());
3579        __ Bind(&pc_relative_dex_cache_patches_.back().label);
3580        __ ldr(XRegisterFrom(temp), MemOperand(XRegisterFrom(temp), 0));
3581        pc_relative_dex_cache_patches_.back().pc_insn_label = pc_insn_label;
3582      }
3583      break;
3584    }
3585    case HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod: {
3586      Location current_method = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex());
3587      Register reg = XRegisterFrom(temp);
3588      Register method_reg;
3589      if (current_method.IsRegister()) {
3590        method_reg = XRegisterFrom(current_method);
3591      } else {
3592        DCHECK(invoke->GetLocations()->Intrinsified());
3593        DCHECK(!current_method.IsValid());
3594        method_reg = reg;
3595        __ Ldr(reg.X(), MemOperand(sp, kCurrentMethodStackOffset));
3596      }
3597
3598      // /* ArtMethod*[] */ temp = temp.ptr_sized_fields_->dex_cache_resolved_methods_;
3599      __ Ldr(reg.X(),
3600             MemOperand(method_reg.X(),
3601                        ArtMethod::DexCacheResolvedMethodsOffset(kArm64WordSize).Int32Value()));
3602      // temp = temp[index_in_cache];
3603      uint32_t index_in_cache = invoke->GetTargetMethod().dex_method_index;
3604    __ Ldr(reg.X(), MemOperand(reg.X(), GetCachePointerOffset(index_in_cache)));
3605      break;
3606    }
3607  }
3608
3609  switch (invoke->GetCodePtrLocation()) {
3610    case HInvokeStaticOrDirect::CodePtrLocation::kCallSelf:
3611      __ Bl(&frame_entry_label_);
3612      break;
3613    case HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative: {
3614      relative_call_patches_.emplace_back(invoke->GetTargetMethod());
3615      vixl::Label* label = &relative_call_patches_.back().label;
3616      vixl::SingleEmissionCheckScope guard(GetVIXLAssembler());
3617      __ Bind(label);
3618      __ bl(0);  // Branch and link to itself. This will be overriden at link time.
3619      break;
3620    }
3621    case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup:
3622    case HInvokeStaticOrDirect::CodePtrLocation::kCallDirect:
3623      // LR prepared above for better instruction scheduling.
3624      DCHECK(direct_code_loaded);
3625      // lr()
3626      __ Blr(lr);
3627      break;
3628    case HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod:
3629      // LR = callee_method->entry_point_from_quick_compiled_code_;
3630      __ Ldr(lr, MemOperand(
3631          XRegisterFrom(callee_method),
3632          ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArm64WordSize).Int32Value()));
3633      // lr()
3634      __ Blr(lr);
3635      break;
3636  }
3637
3638  DCHECK(!IsLeafMethod());
3639}
3640
3641void CodeGeneratorARM64::GenerateVirtualCall(HInvokeVirtual* invoke, Location temp_in) {
3642  // Use the calling convention instead of the location of the receiver, as
3643  // intrinsics may have put the receiver in a different register. In the intrinsics
3644  // slow path, the arguments have been moved to the right place, so here we are
3645  // guaranteed that the receiver is the first register of the calling convention.
3646  InvokeDexCallingConvention calling_convention;
3647  Register receiver = calling_convention.GetRegisterAt(0);
3648  Register temp = XRegisterFrom(temp_in);
3649  size_t method_offset = mirror::Class::EmbeddedVTableEntryOffset(
3650      invoke->GetVTableIndex(), kArm64PointerSize).SizeValue();
3651  Offset class_offset = mirror::Object::ClassOffset();
3652  Offset entry_point = ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArm64WordSize);
3653
3654  BlockPoolsScope block_pools(GetVIXLAssembler());
3655
3656  DCHECK(receiver.IsRegister());
3657  // /* HeapReference<Class> */ temp = receiver->klass_
3658  __ Ldr(temp.W(), HeapOperandFrom(LocationFrom(receiver), class_offset));
3659  MaybeRecordImplicitNullCheck(invoke);
3660  // Instead of simply (possibly) unpoisoning `temp` here, we should
3661  // emit a read barrier for the previous class reference load.
3662  // intermediate/temporary reference and because the current
3663  // concurrent copying collector keeps the from-space memory
3664  // intact/accessible until the end of the marking phase (the
3665  // concurrent copying collector may not in the future).
3666  GetAssembler()->MaybeUnpoisonHeapReference(temp.W());
3667  // temp = temp->GetMethodAt(method_offset);
3668  __ Ldr(temp, MemOperand(temp, method_offset));
3669  // lr = temp->GetEntryPoint();
3670  __ Ldr(lr, MemOperand(temp, entry_point.SizeValue()));
3671  // lr();
3672  __ Blr(lr);
3673}
3674
3675void CodeGeneratorARM64::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) {
3676  DCHECK(linker_patches->empty());
3677  size_t size =
3678      method_patches_.size() +
3679      call_patches_.size() +
3680      relative_call_patches_.size() +
3681      pc_relative_dex_cache_patches_.size();
3682  linker_patches->reserve(size);
3683  for (const auto& entry : method_patches_) {
3684    const MethodReference& target_method = entry.first;
3685    vixl::Literal<uint64_t>* literal = entry.second;
3686    linker_patches->push_back(LinkerPatch::MethodPatch(literal->offset(),
3687                                                       target_method.dex_file,
3688                                                       target_method.dex_method_index));
3689  }
3690  for (const auto& entry : call_patches_) {
3691    const MethodReference& target_method = entry.first;
3692    vixl::Literal<uint64_t>* literal = entry.second;
3693    linker_patches->push_back(LinkerPatch::CodePatch(literal->offset(),
3694                                                     target_method.dex_file,
3695                                                     target_method.dex_method_index));
3696  }
3697  for (const MethodPatchInfo<vixl::Label>& info : relative_call_patches_) {
3698    linker_patches->push_back(LinkerPatch::RelativeCodePatch(info.label.location(),
3699                                                             info.target_method.dex_file,
3700                                                             info.target_method.dex_method_index));
3701  }
3702  for (const PcRelativeDexCacheAccessInfo& info : pc_relative_dex_cache_patches_) {
3703    linker_patches->push_back(LinkerPatch::DexCacheArrayPatch(info.label.location(),
3704                                                              &info.target_dex_file,
3705                                                              info.pc_insn_label->location(),
3706                                                              info.element_offset));
3707  }
3708}
3709
3710vixl::Literal<uint64_t>* CodeGeneratorARM64::DeduplicateUint64Literal(uint64_t value) {
3711  // Look up the literal for value.
3712  auto lb = uint64_literals_.lower_bound(value);
3713  if (lb != uint64_literals_.end() && !uint64_literals_.key_comp()(value, lb->first)) {
3714    return lb->second;
3715  }
3716  // We don't have a literal for this value, insert a new one.
3717  vixl::Literal<uint64_t>* literal = __ CreateLiteralDestroyedWithPool<uint64_t>(value);
3718  uint64_literals_.PutBefore(lb, value, literal);
3719  return literal;
3720}
3721
3722vixl::Literal<uint64_t>* CodeGeneratorARM64::DeduplicateMethodLiteral(
3723    MethodReference target_method,
3724    MethodToLiteralMap* map) {
3725  // Look up the literal for target_method.
3726  auto lb = map->lower_bound(target_method);
3727  if (lb != map->end() && !map->key_comp()(target_method, lb->first)) {
3728    return lb->second;
3729  }
3730  // We don't have a literal for this method yet, insert a new one.
3731  vixl::Literal<uint64_t>* literal = __ CreateLiteralDestroyedWithPool<uint64_t>(0u);
3732  map->PutBefore(lb, target_method, literal);
3733  return literal;
3734}
3735
3736vixl::Literal<uint64_t>* CodeGeneratorARM64::DeduplicateMethodAddressLiteral(
3737    MethodReference target_method) {
3738  return DeduplicateMethodLiteral(target_method, &method_patches_);
3739}
3740
3741vixl::Literal<uint64_t>* CodeGeneratorARM64::DeduplicateMethodCodeLiteral(
3742    MethodReference target_method) {
3743  return DeduplicateMethodLiteral(target_method, &call_patches_);
3744}
3745
3746
3747void InstructionCodeGeneratorARM64::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
3748  // When we do not run baseline, explicit clinit checks triggered by static
3749  // invokes must have been pruned by art::PrepareForRegisterAllocation.
3750  DCHECK(codegen_->IsBaseline() || !invoke->IsStaticWithExplicitClinitCheck());
3751
3752  if (TryGenerateIntrinsicCode(invoke, codegen_)) {
3753    return;
3754  }
3755
3756  BlockPoolsScope block_pools(GetVIXLAssembler());
3757  LocationSummary* locations = invoke->GetLocations();
3758  codegen_->GenerateStaticOrDirectCall(
3759      invoke, locations->HasTemps() ? locations->GetTemp(0) : Location::NoLocation());
3760  codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
3761}
3762
3763void InstructionCodeGeneratorARM64::VisitInvokeVirtual(HInvokeVirtual* invoke) {
3764  if (TryGenerateIntrinsicCode(invoke, codegen_)) {
3765    return;
3766  }
3767
3768  codegen_->GenerateVirtualCall(invoke, invoke->GetLocations()->GetTemp(0));
3769  DCHECK(!codegen_->IsLeafMethod());
3770  codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
3771}
3772
3773void LocationsBuilderARM64::VisitLoadClass(HLoadClass* cls) {
3774  InvokeRuntimeCallingConvention calling_convention;
3775  CodeGenerator::CreateLoadClassLocationSummary(
3776      cls,
3777      LocationFrom(calling_convention.GetRegisterAt(0)),
3778      LocationFrom(vixl::x0),
3779      /* code_generator_supports_read_barrier */ true);
3780}
3781
3782void InstructionCodeGeneratorARM64::VisitLoadClass(HLoadClass* cls) {
3783  if (cls->NeedsAccessCheck()) {
3784    codegen_->MoveConstant(cls->GetLocations()->GetTemp(0), cls->GetTypeIndex());
3785    codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pInitializeTypeAndVerifyAccess),
3786                            cls,
3787                            cls->GetDexPc(),
3788                            nullptr);
3789    CheckEntrypointTypes<kQuickInitializeTypeAndVerifyAccess, void*, uint32_t>();
3790    return;
3791  }
3792
3793  Location out_loc = cls->GetLocations()->Out();
3794  Register out = OutputRegister(cls);
3795  Register current_method = InputRegisterAt(cls, 0);
3796  if (cls->IsReferrersClass()) {
3797    DCHECK(!cls->CanCallRuntime());
3798    DCHECK(!cls->MustGenerateClinitCheck());
3799    uint32_t declaring_class_offset = ArtMethod::DeclaringClassOffset().Int32Value();
3800    if (kEmitCompilerReadBarrier) {
3801      // /* GcRoot<mirror::Class>* */ out = &(current_method->declaring_class_)
3802      __ Add(out.X(), current_method.X(), declaring_class_offset);
3803      // /* mirror::Class* */ out = out->Read()
3804      codegen_->GenerateReadBarrierForRoot(cls, out_loc, out_loc);
3805    } else {
3806      // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_
3807      __ Ldr(out, MemOperand(current_method, declaring_class_offset));
3808    }
3809  } else {
3810    MemberOffset resolved_types_offset = ArtMethod::DexCacheResolvedTypesOffset(kArm64PointerSize);
3811    // /* GcRoot<mirror::Class>[] */ out =
3812    //        current_method.ptr_sized_fields_->dex_cache_resolved_types_
3813    __ Ldr(out.X(), MemOperand(current_method, resolved_types_offset.Int32Value()));
3814
3815    size_t cache_offset = CodeGenerator::GetCacheOffset(cls->GetTypeIndex());
3816    if (kEmitCompilerReadBarrier) {
3817      // /* GcRoot<mirror::Class>* */ out = &out[type_index]
3818      __ Add(out.X(), out.X(), cache_offset);
3819      // /* mirror::Class* */ out = out->Read()
3820      codegen_->GenerateReadBarrierForRoot(cls, out_loc, out_loc);
3821    } else {
3822      // /* GcRoot<mirror::Class> */ out = out[type_index]
3823      __ Ldr(out, MemOperand(out.X(), cache_offset));
3824    }
3825
3826    if (!cls->IsInDexCache() || cls->MustGenerateClinitCheck()) {
3827      DCHECK(cls->CanCallRuntime());
3828      SlowPathCodeARM64* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathARM64(
3829          cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck());
3830      codegen_->AddSlowPath(slow_path);
3831      if (!cls->IsInDexCache()) {
3832        __ Cbz(out, slow_path->GetEntryLabel());
3833      }
3834      if (cls->MustGenerateClinitCheck()) {
3835        GenerateClassInitializationCheck(slow_path, out);
3836      } else {
3837        __ Bind(slow_path->GetExitLabel());
3838      }
3839    }
3840  }
3841}
3842
3843static MemOperand GetExceptionTlsAddress() {
3844  return MemOperand(tr, Thread::ExceptionOffset<kArm64WordSize>().Int32Value());
3845}
3846
3847void LocationsBuilderARM64::VisitLoadException(HLoadException* load) {
3848  LocationSummary* locations =
3849      new (GetGraph()->GetArena()) LocationSummary(load, LocationSummary::kNoCall);
3850  locations->SetOut(Location::RequiresRegister());
3851}
3852
3853void InstructionCodeGeneratorARM64::VisitLoadException(HLoadException* instruction) {
3854  __ Ldr(OutputRegister(instruction), GetExceptionTlsAddress());
3855}
3856
3857void LocationsBuilderARM64::VisitClearException(HClearException* clear) {
3858  new (GetGraph()->GetArena()) LocationSummary(clear, LocationSummary::kNoCall);
3859}
3860
3861void InstructionCodeGeneratorARM64::VisitClearException(HClearException* clear ATTRIBUTE_UNUSED) {
3862  __ Str(wzr, GetExceptionTlsAddress());
3863}
3864
3865void LocationsBuilderARM64::VisitLoadLocal(HLoadLocal* load) {
3866  load->SetLocations(nullptr);
3867}
3868
3869void InstructionCodeGeneratorARM64::VisitLoadLocal(HLoadLocal* load ATTRIBUTE_UNUSED) {
3870  // Nothing to do, this is driven by the code generator.
3871}
3872
3873void LocationsBuilderARM64::VisitLoadString(HLoadString* load) {
3874  LocationSummary::CallKind call_kind = (!load->IsInDexCache() || kEmitCompilerReadBarrier)
3875      ? LocationSummary::kCallOnSlowPath
3876      : LocationSummary::kNoCall;
3877  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(load, call_kind);
3878  locations->SetInAt(0, Location::RequiresRegister());
3879  locations->SetOut(Location::RequiresRegister());
3880}
3881
3882void InstructionCodeGeneratorARM64::VisitLoadString(HLoadString* load) {
3883  Location out_loc = load->GetLocations()->Out();
3884  Register out = OutputRegister(load);
3885  Register current_method = InputRegisterAt(load, 0);
3886
3887  uint32_t declaring_class_offset = ArtMethod::DeclaringClassOffset().Int32Value();
3888  if (kEmitCompilerReadBarrier) {
3889    // /* GcRoot<mirror::Class>* */ out = &(current_method->declaring_class_)
3890    __ Add(out.X(), current_method.X(), declaring_class_offset);
3891    // /* mirror::Class* */ out = out->Read()
3892    codegen_->GenerateReadBarrierForRoot(load, out_loc, out_loc);
3893  } else {
3894    // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_
3895    __ Ldr(out, MemOperand(current_method, declaring_class_offset));
3896  }
3897
3898  // /* GcRoot<mirror::String>[] */ out = out->dex_cache_strings_
3899  __ Ldr(out.X(), HeapOperand(out, mirror::Class::DexCacheStringsOffset().Uint32Value()));
3900
3901  size_t cache_offset = CodeGenerator::GetCacheOffset(load->GetStringIndex());
3902  if (kEmitCompilerReadBarrier) {
3903    // /* GcRoot<mirror::String>* */ out = &out[string_index]
3904    __ Add(out.X(), out.X(), cache_offset);
3905    // /* mirror::String* */ out = out->Read()
3906    codegen_->GenerateReadBarrierForRoot(load, out_loc, out_loc);
3907  } else {
3908    // /* GcRoot<mirror::String> */ out = out[string_index]
3909    __ Ldr(out, MemOperand(out.X(), cache_offset));
3910  }
3911
3912  if (!load->IsInDexCache()) {
3913    SlowPathCodeARM64* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathARM64(load);
3914    codegen_->AddSlowPath(slow_path);
3915    __ Cbz(out, slow_path->GetEntryLabel());
3916    __ Bind(slow_path->GetExitLabel());
3917  }
3918}
3919
3920void LocationsBuilderARM64::VisitLocal(HLocal* local) {
3921  local->SetLocations(nullptr);
3922}
3923
3924void InstructionCodeGeneratorARM64::VisitLocal(HLocal* local) {
3925  DCHECK_EQ(local->GetBlock(), GetGraph()->GetEntryBlock());
3926}
3927
3928void LocationsBuilderARM64::VisitLongConstant(HLongConstant* constant) {
3929  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(constant);
3930  locations->SetOut(Location::ConstantLocation(constant));
3931}
3932
3933void InstructionCodeGeneratorARM64::VisitLongConstant(HLongConstant* constant ATTRIBUTE_UNUSED) {
3934  // Will be generated at use site.
3935}
3936
3937void LocationsBuilderARM64::VisitMonitorOperation(HMonitorOperation* instruction) {
3938  LocationSummary* locations =
3939      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall);
3940  InvokeRuntimeCallingConvention calling_convention;
3941  locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
3942}
3943
3944void InstructionCodeGeneratorARM64::VisitMonitorOperation(HMonitorOperation* instruction) {
3945  codegen_->InvokeRuntime(instruction->IsEnter()
3946        ? QUICK_ENTRY_POINT(pLockObject) : QUICK_ENTRY_POINT(pUnlockObject),
3947      instruction,
3948      instruction->GetDexPc(),
3949      nullptr);
3950  if (instruction->IsEnter()) {
3951    CheckEntrypointTypes<kQuickLockObject, void, mirror::Object*>();
3952  } else {
3953    CheckEntrypointTypes<kQuickUnlockObject, void, mirror::Object*>();
3954  }
3955}
3956
3957void LocationsBuilderARM64::VisitMul(HMul* mul) {
3958  LocationSummary* locations =
3959      new (GetGraph()->GetArena()) LocationSummary(mul, LocationSummary::kNoCall);
3960  switch (mul->GetResultType()) {
3961    case Primitive::kPrimInt:
3962    case Primitive::kPrimLong:
3963      locations->SetInAt(0, Location::RequiresRegister());
3964      locations->SetInAt(1, Location::RequiresRegister());
3965      locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3966      break;
3967
3968    case Primitive::kPrimFloat:
3969    case Primitive::kPrimDouble:
3970      locations->SetInAt(0, Location::RequiresFpuRegister());
3971      locations->SetInAt(1, Location::RequiresFpuRegister());
3972      locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
3973      break;
3974
3975    default:
3976      LOG(FATAL) << "Unexpected mul type " << mul->GetResultType();
3977  }
3978}
3979
3980void InstructionCodeGeneratorARM64::VisitMul(HMul* mul) {
3981  switch (mul->GetResultType()) {
3982    case Primitive::kPrimInt:
3983    case Primitive::kPrimLong:
3984      __ Mul(OutputRegister(mul), InputRegisterAt(mul, 0), InputRegisterAt(mul, 1));
3985      break;
3986
3987    case Primitive::kPrimFloat:
3988    case Primitive::kPrimDouble:
3989      __ Fmul(OutputFPRegister(mul), InputFPRegisterAt(mul, 0), InputFPRegisterAt(mul, 1));
3990      break;
3991
3992    default:
3993      LOG(FATAL) << "Unexpected mul type " << mul->GetResultType();
3994  }
3995}
3996
3997void LocationsBuilderARM64::VisitNeg(HNeg* neg) {
3998  LocationSummary* locations =
3999      new (GetGraph()->GetArena()) LocationSummary(neg, LocationSummary::kNoCall);
4000  switch (neg->GetResultType()) {
4001    case Primitive::kPrimInt:
4002    case Primitive::kPrimLong:
4003      locations->SetInAt(0, ARM64EncodableConstantOrRegister(neg->InputAt(0), neg));
4004      locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4005      break;
4006
4007    case Primitive::kPrimFloat:
4008    case Primitive::kPrimDouble:
4009      locations->SetInAt(0, Location::RequiresFpuRegister());
4010      locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
4011      break;
4012
4013    default:
4014      LOG(FATAL) << "Unexpected neg type " << neg->GetResultType();
4015  }
4016}
4017
4018void InstructionCodeGeneratorARM64::VisitNeg(HNeg* neg) {
4019  switch (neg->GetResultType()) {
4020    case Primitive::kPrimInt:
4021    case Primitive::kPrimLong:
4022      __ Neg(OutputRegister(neg), InputOperandAt(neg, 0));
4023      break;
4024
4025    case Primitive::kPrimFloat:
4026    case Primitive::kPrimDouble:
4027      __ Fneg(OutputFPRegister(neg), InputFPRegisterAt(neg, 0));
4028      break;
4029
4030    default:
4031      LOG(FATAL) << "Unexpected neg type " << neg->GetResultType();
4032  }
4033}
4034
4035void LocationsBuilderARM64::VisitNewArray(HNewArray* instruction) {
4036  LocationSummary* locations =
4037      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall);
4038  InvokeRuntimeCallingConvention calling_convention;
4039  locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(0)));
4040  locations->SetOut(LocationFrom(x0));
4041  locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(1)));
4042  locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(2)));
4043}
4044
4045void InstructionCodeGeneratorARM64::VisitNewArray(HNewArray* instruction) {
4046  LocationSummary* locations = instruction->GetLocations();
4047  InvokeRuntimeCallingConvention calling_convention;
4048  Register type_index = RegisterFrom(locations->GetTemp(0), Primitive::kPrimInt);
4049  DCHECK(type_index.Is(w0));
4050  __ Mov(type_index, instruction->GetTypeIndex());
4051  // Note: if heap poisoning is enabled, the entry point takes cares
4052  // of poisoning the reference.
4053  codegen_->InvokeRuntime(instruction->GetEntrypoint(),
4054                          instruction,
4055                          instruction->GetDexPc(),
4056                          nullptr);
4057  CheckEntrypointTypes<kQuickAllocArrayWithAccessCheck, void*, uint32_t, int32_t, ArtMethod*>();
4058}
4059
4060void LocationsBuilderARM64::VisitNewInstance(HNewInstance* instruction) {
4061  LocationSummary* locations =
4062      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall);
4063  InvokeRuntimeCallingConvention calling_convention;
4064  locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
4065  locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
4066  locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimNot));
4067}
4068
4069void InstructionCodeGeneratorARM64::VisitNewInstance(HNewInstance* instruction) {
4070  // Note: if heap poisoning is enabled, the entry point takes cares
4071  // of poisoning the reference.
4072  codegen_->InvokeRuntime(instruction->GetEntrypoint(),
4073                          instruction,
4074                          instruction->GetDexPc(),
4075                          nullptr);
4076  CheckEntrypointTypes<kQuickAllocObjectWithAccessCheck, void*, uint32_t, ArtMethod*>();
4077}
4078
4079void LocationsBuilderARM64::VisitNot(HNot* instruction) {
4080  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
4081  locations->SetInAt(0, Location::RequiresRegister());
4082  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4083}
4084
4085void InstructionCodeGeneratorARM64::VisitNot(HNot* instruction) {
4086  switch (instruction->GetResultType()) {
4087    case Primitive::kPrimInt:
4088    case Primitive::kPrimLong:
4089      __ Mvn(OutputRegister(instruction), InputOperandAt(instruction, 0));
4090      break;
4091
4092    default:
4093      LOG(FATAL) << "Unexpected type for not operation " << instruction->GetResultType();
4094  }
4095}
4096
4097void LocationsBuilderARM64::VisitBooleanNot(HBooleanNot* instruction) {
4098  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
4099  locations->SetInAt(0, Location::RequiresRegister());
4100  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4101}
4102
4103void InstructionCodeGeneratorARM64::VisitBooleanNot(HBooleanNot* instruction) {
4104  __ Eor(OutputRegister(instruction), InputRegisterAt(instruction, 0), vixl::Operand(1));
4105}
4106
4107void LocationsBuilderARM64::VisitNullCheck(HNullCheck* instruction) {
4108  LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock()
4109      ? LocationSummary::kCallOnSlowPath
4110      : LocationSummary::kNoCall;
4111  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
4112  locations->SetInAt(0, Location::RequiresRegister());
4113  if (instruction->HasUses()) {
4114    locations->SetOut(Location::SameAsFirstInput());
4115  }
4116}
4117
4118void InstructionCodeGeneratorARM64::GenerateImplicitNullCheck(HNullCheck* instruction) {
4119  if (codegen_->CanMoveNullCheckToUser(instruction)) {
4120    return;
4121  }
4122
4123  BlockPoolsScope block_pools(GetVIXLAssembler());
4124  Location obj = instruction->GetLocations()->InAt(0);
4125  __ Ldr(wzr, HeapOperandFrom(obj, Offset(0)));
4126  codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
4127}
4128
4129void InstructionCodeGeneratorARM64::GenerateExplicitNullCheck(HNullCheck* instruction) {
4130  SlowPathCodeARM64* slow_path = new (GetGraph()->GetArena()) NullCheckSlowPathARM64(instruction);
4131  codegen_->AddSlowPath(slow_path);
4132
4133  LocationSummary* locations = instruction->GetLocations();
4134  Location obj = locations->InAt(0);
4135
4136  __ Cbz(RegisterFrom(obj, instruction->InputAt(0)->GetType()), slow_path->GetEntryLabel());
4137}
4138
4139void InstructionCodeGeneratorARM64::VisitNullCheck(HNullCheck* instruction) {
4140  if (codegen_->IsImplicitNullCheckAllowed(instruction)) {
4141    GenerateImplicitNullCheck(instruction);
4142  } else {
4143    GenerateExplicitNullCheck(instruction);
4144  }
4145}
4146
4147void LocationsBuilderARM64::VisitOr(HOr* instruction) {
4148  HandleBinaryOp(instruction);
4149}
4150
4151void InstructionCodeGeneratorARM64::VisitOr(HOr* instruction) {
4152  HandleBinaryOp(instruction);
4153}
4154
4155void LocationsBuilderARM64::VisitParallelMove(HParallelMove* instruction ATTRIBUTE_UNUSED) {
4156  LOG(FATAL) << "Unreachable";
4157}
4158
4159void InstructionCodeGeneratorARM64::VisitParallelMove(HParallelMove* instruction) {
4160  codegen_->GetMoveResolver()->EmitNativeCode(instruction);
4161}
4162
4163void LocationsBuilderARM64::VisitParameterValue(HParameterValue* instruction) {
4164  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
4165  Location location = parameter_visitor_.GetNextLocation(instruction->GetType());
4166  if (location.IsStackSlot()) {
4167    location = Location::StackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
4168  } else if (location.IsDoubleStackSlot()) {
4169    location = Location::DoubleStackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
4170  }
4171  locations->SetOut(location);
4172}
4173
4174void InstructionCodeGeneratorARM64::VisitParameterValue(
4175    HParameterValue* instruction ATTRIBUTE_UNUSED) {
4176  // Nothing to do, the parameter is already at its location.
4177}
4178
4179void LocationsBuilderARM64::VisitCurrentMethod(HCurrentMethod* instruction) {
4180  LocationSummary* locations =
4181      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
4182  locations->SetOut(LocationFrom(kArtMethodRegister));
4183}
4184
4185void InstructionCodeGeneratorARM64::VisitCurrentMethod(
4186    HCurrentMethod* instruction ATTRIBUTE_UNUSED) {
4187  // Nothing to do, the method is already at its location.
4188}
4189
4190void LocationsBuilderARM64::VisitPhi(HPhi* instruction) {
4191  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
4192  for (size_t i = 0, e = instruction->InputCount(); i < e; ++i) {
4193    locations->SetInAt(i, Location::Any());
4194  }
4195  locations->SetOut(Location::Any());
4196}
4197
4198void InstructionCodeGeneratorARM64::VisitPhi(HPhi* instruction ATTRIBUTE_UNUSED) {
4199  LOG(FATAL) << "Unreachable";
4200}
4201
4202void LocationsBuilderARM64::VisitRem(HRem* rem) {
4203  Primitive::Type type = rem->GetResultType();
4204  LocationSummary::CallKind call_kind =
4205      Primitive::IsFloatingPointType(type) ? LocationSummary::kCall : LocationSummary::kNoCall;
4206  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(rem, call_kind);
4207
4208  switch (type) {
4209    case Primitive::kPrimInt:
4210    case Primitive::kPrimLong:
4211      locations->SetInAt(0, Location::RequiresRegister());
4212      locations->SetInAt(1, Location::RegisterOrConstant(rem->InputAt(1)));
4213      locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4214      break;
4215
4216    case Primitive::kPrimFloat:
4217    case Primitive::kPrimDouble: {
4218      InvokeRuntimeCallingConvention calling_convention;
4219      locations->SetInAt(0, LocationFrom(calling_convention.GetFpuRegisterAt(0)));
4220      locations->SetInAt(1, LocationFrom(calling_convention.GetFpuRegisterAt(1)));
4221      locations->SetOut(calling_convention.GetReturnLocation(type));
4222
4223      break;
4224    }
4225
4226    default:
4227      LOG(FATAL) << "Unexpected rem type " << type;
4228  }
4229}
4230
4231void InstructionCodeGeneratorARM64::VisitRem(HRem* rem) {
4232  Primitive::Type type = rem->GetResultType();
4233
4234  switch (type) {
4235    case Primitive::kPrimInt:
4236    case Primitive::kPrimLong: {
4237      GenerateDivRemIntegral(rem);
4238      break;
4239    }
4240
4241    case Primitive::kPrimFloat:
4242    case Primitive::kPrimDouble: {
4243      int32_t entry_offset = (type == Primitive::kPrimFloat) ? QUICK_ENTRY_POINT(pFmodf)
4244                                                             : QUICK_ENTRY_POINT(pFmod);
4245      codegen_->InvokeRuntime(entry_offset, rem, rem->GetDexPc(), nullptr);
4246      if (type == Primitive::kPrimFloat) {
4247        CheckEntrypointTypes<kQuickFmodf, float, float, float>();
4248      } else {
4249        CheckEntrypointTypes<kQuickFmod, double, double, double>();
4250      }
4251      break;
4252    }
4253
4254    default:
4255      LOG(FATAL) << "Unexpected rem type " << type;
4256      UNREACHABLE();
4257  }
4258}
4259
4260void LocationsBuilderARM64::VisitMemoryBarrier(HMemoryBarrier* memory_barrier) {
4261  memory_barrier->SetLocations(nullptr);
4262}
4263
4264void InstructionCodeGeneratorARM64::VisitMemoryBarrier(HMemoryBarrier* memory_barrier) {
4265  GenerateMemoryBarrier(memory_barrier->GetBarrierKind());
4266}
4267
4268void LocationsBuilderARM64::VisitReturn(HReturn* instruction) {
4269  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
4270  Primitive::Type return_type = instruction->InputAt(0)->GetType();
4271  locations->SetInAt(0, ARM64ReturnLocation(return_type));
4272}
4273
4274void InstructionCodeGeneratorARM64::VisitReturn(HReturn* instruction ATTRIBUTE_UNUSED) {
4275  codegen_->GenerateFrameExit();
4276}
4277
4278void LocationsBuilderARM64::VisitReturnVoid(HReturnVoid* instruction) {
4279  instruction->SetLocations(nullptr);
4280}
4281
4282void InstructionCodeGeneratorARM64::VisitReturnVoid(HReturnVoid* instruction ATTRIBUTE_UNUSED) {
4283  codegen_->GenerateFrameExit();
4284}
4285
4286void LocationsBuilderARM64::VisitRor(HRor* ror) {
4287  HandleBinaryOp(ror);
4288}
4289
4290void InstructionCodeGeneratorARM64::VisitRor(HRor* ror) {
4291  HandleBinaryOp(ror);
4292}
4293
4294void LocationsBuilderARM64::VisitShl(HShl* shl) {
4295  HandleShift(shl);
4296}
4297
4298void InstructionCodeGeneratorARM64::VisitShl(HShl* shl) {
4299  HandleShift(shl);
4300}
4301
4302void LocationsBuilderARM64::VisitShr(HShr* shr) {
4303  HandleShift(shr);
4304}
4305
4306void InstructionCodeGeneratorARM64::VisitShr(HShr* shr) {
4307  HandleShift(shr);
4308}
4309
4310void LocationsBuilderARM64::VisitStoreLocal(HStoreLocal* store) {
4311  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(store);
4312  Primitive::Type field_type = store->InputAt(1)->GetType();
4313  switch (field_type) {
4314    case Primitive::kPrimNot:
4315    case Primitive::kPrimBoolean:
4316    case Primitive::kPrimByte:
4317    case Primitive::kPrimChar:
4318    case Primitive::kPrimShort:
4319    case Primitive::kPrimInt:
4320    case Primitive::kPrimFloat:
4321      locations->SetInAt(1, Location::StackSlot(codegen_->GetStackSlot(store->GetLocal())));
4322      break;
4323
4324    case Primitive::kPrimLong:
4325    case Primitive::kPrimDouble:
4326      locations->SetInAt(1, Location::DoubleStackSlot(codegen_->GetStackSlot(store->GetLocal())));
4327      break;
4328
4329    default:
4330      LOG(FATAL) << "Unimplemented local type " << field_type;
4331      UNREACHABLE();
4332  }
4333}
4334
4335void InstructionCodeGeneratorARM64::VisitStoreLocal(HStoreLocal* store ATTRIBUTE_UNUSED) {
4336}
4337
4338void LocationsBuilderARM64::VisitSub(HSub* instruction) {
4339  HandleBinaryOp(instruction);
4340}
4341
4342void InstructionCodeGeneratorARM64::VisitSub(HSub* instruction) {
4343  HandleBinaryOp(instruction);
4344}
4345
4346void LocationsBuilderARM64::VisitStaticFieldGet(HStaticFieldGet* instruction) {
4347  HandleFieldGet(instruction);
4348}
4349
4350void InstructionCodeGeneratorARM64::VisitStaticFieldGet(HStaticFieldGet* instruction) {
4351  HandleFieldGet(instruction, instruction->GetFieldInfo());
4352}
4353
4354void LocationsBuilderARM64::VisitStaticFieldSet(HStaticFieldSet* instruction) {
4355  HandleFieldSet(instruction);
4356}
4357
4358void InstructionCodeGeneratorARM64::VisitStaticFieldSet(HStaticFieldSet* instruction) {
4359  HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
4360}
4361
4362void LocationsBuilderARM64::VisitUnresolvedInstanceFieldGet(
4363    HUnresolvedInstanceFieldGet* instruction) {
4364  FieldAccessCallingConventionARM64 calling_convention;
4365  codegen_->CreateUnresolvedFieldLocationSummary(
4366      instruction, instruction->GetFieldType(), calling_convention);
4367}
4368
4369void InstructionCodeGeneratorARM64::VisitUnresolvedInstanceFieldGet(
4370    HUnresolvedInstanceFieldGet* instruction) {
4371  FieldAccessCallingConventionARM64 calling_convention;
4372  codegen_->GenerateUnresolvedFieldAccess(instruction,
4373                                          instruction->GetFieldType(),
4374                                          instruction->GetFieldIndex(),
4375                                          instruction->GetDexPc(),
4376                                          calling_convention);
4377}
4378
4379void LocationsBuilderARM64::VisitUnresolvedInstanceFieldSet(
4380    HUnresolvedInstanceFieldSet* instruction) {
4381  FieldAccessCallingConventionARM64 calling_convention;
4382  codegen_->CreateUnresolvedFieldLocationSummary(
4383      instruction, instruction->GetFieldType(), calling_convention);
4384}
4385
4386void InstructionCodeGeneratorARM64::VisitUnresolvedInstanceFieldSet(
4387    HUnresolvedInstanceFieldSet* instruction) {
4388  FieldAccessCallingConventionARM64 calling_convention;
4389  codegen_->GenerateUnresolvedFieldAccess(instruction,
4390                                          instruction->GetFieldType(),
4391                                          instruction->GetFieldIndex(),
4392                                          instruction->GetDexPc(),
4393                                          calling_convention);
4394}
4395
4396void LocationsBuilderARM64::VisitUnresolvedStaticFieldGet(
4397    HUnresolvedStaticFieldGet* instruction) {
4398  FieldAccessCallingConventionARM64 calling_convention;
4399  codegen_->CreateUnresolvedFieldLocationSummary(
4400      instruction, instruction->GetFieldType(), calling_convention);
4401}
4402
4403void InstructionCodeGeneratorARM64::VisitUnresolvedStaticFieldGet(
4404    HUnresolvedStaticFieldGet* instruction) {
4405  FieldAccessCallingConventionARM64 calling_convention;
4406  codegen_->GenerateUnresolvedFieldAccess(instruction,
4407                                          instruction->GetFieldType(),
4408                                          instruction->GetFieldIndex(),
4409                                          instruction->GetDexPc(),
4410                                          calling_convention);
4411}
4412
4413void LocationsBuilderARM64::VisitUnresolvedStaticFieldSet(
4414    HUnresolvedStaticFieldSet* instruction) {
4415  FieldAccessCallingConventionARM64 calling_convention;
4416  codegen_->CreateUnresolvedFieldLocationSummary(
4417      instruction, instruction->GetFieldType(), calling_convention);
4418}
4419
4420void InstructionCodeGeneratorARM64::VisitUnresolvedStaticFieldSet(
4421    HUnresolvedStaticFieldSet* instruction) {
4422  FieldAccessCallingConventionARM64 calling_convention;
4423  codegen_->GenerateUnresolvedFieldAccess(instruction,
4424                                          instruction->GetFieldType(),
4425                                          instruction->GetFieldIndex(),
4426                                          instruction->GetDexPc(),
4427                                          calling_convention);
4428}
4429
4430void LocationsBuilderARM64::VisitSuspendCheck(HSuspendCheck* instruction) {
4431  new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnSlowPath);
4432}
4433
4434void InstructionCodeGeneratorARM64::VisitSuspendCheck(HSuspendCheck* instruction) {
4435  HBasicBlock* block = instruction->GetBlock();
4436  if (block->GetLoopInformation() != nullptr) {
4437    DCHECK(block->GetLoopInformation()->GetSuspendCheck() == instruction);
4438    // The back edge will generate the suspend check.
4439    return;
4440  }
4441  if (block->IsEntryBlock() && instruction->GetNext()->IsGoto()) {
4442    // The goto will generate the suspend check.
4443    return;
4444  }
4445  GenerateSuspendCheck(instruction, nullptr);
4446}
4447
4448void LocationsBuilderARM64::VisitTemporary(HTemporary* temp) {
4449  temp->SetLocations(nullptr);
4450}
4451
4452void InstructionCodeGeneratorARM64::VisitTemporary(HTemporary* temp ATTRIBUTE_UNUSED) {
4453  // Nothing to do, this is driven by the code generator.
4454}
4455
4456void LocationsBuilderARM64::VisitThrow(HThrow* instruction) {
4457  LocationSummary* locations =
4458      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall);
4459  InvokeRuntimeCallingConvention calling_convention;
4460  locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
4461}
4462
4463void InstructionCodeGeneratorARM64::VisitThrow(HThrow* instruction) {
4464  codegen_->InvokeRuntime(
4465      QUICK_ENTRY_POINT(pDeliverException), instruction, instruction->GetDexPc(), nullptr);
4466  CheckEntrypointTypes<kQuickDeliverException, void, mirror::Object*>();
4467}
4468
4469void LocationsBuilderARM64::VisitTypeConversion(HTypeConversion* conversion) {
4470  LocationSummary* locations =
4471      new (GetGraph()->GetArena()) LocationSummary(conversion, LocationSummary::kNoCall);
4472  Primitive::Type input_type = conversion->GetInputType();
4473  Primitive::Type result_type = conversion->GetResultType();
4474  DCHECK_NE(input_type, result_type);
4475  if ((input_type == Primitive::kPrimNot) || (input_type == Primitive::kPrimVoid) ||
4476      (result_type == Primitive::kPrimNot) || (result_type == Primitive::kPrimVoid)) {
4477    LOG(FATAL) << "Unexpected type conversion from " << input_type << " to " << result_type;
4478  }
4479
4480  if (Primitive::IsFloatingPointType(input_type)) {
4481    locations->SetInAt(0, Location::RequiresFpuRegister());
4482  } else {
4483    locations->SetInAt(0, Location::RequiresRegister());
4484  }
4485
4486  if (Primitive::IsFloatingPointType(result_type)) {
4487    locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
4488  } else {
4489    locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4490  }
4491}
4492
4493void InstructionCodeGeneratorARM64::VisitTypeConversion(HTypeConversion* conversion) {
4494  Primitive::Type result_type = conversion->GetResultType();
4495  Primitive::Type input_type = conversion->GetInputType();
4496
4497  DCHECK_NE(input_type, result_type);
4498
4499  if (Primitive::IsIntegralType(result_type) && Primitive::IsIntegralType(input_type)) {
4500    int result_size = Primitive::ComponentSize(result_type);
4501    int input_size = Primitive::ComponentSize(input_type);
4502    int min_size = std::min(result_size, input_size);
4503    Register output = OutputRegister(conversion);
4504    Register source = InputRegisterAt(conversion, 0);
4505    if (result_type == Primitive::kPrimInt && input_type == Primitive::kPrimLong) {
4506      // 'int' values are used directly as W registers, discarding the top
4507      // bits, so we don't need to sign-extend and can just perform a move.
4508      // We do not pass the `kDiscardForSameWReg` argument to force clearing the
4509      // top 32 bits of the target register. We theoretically could leave those
4510      // bits unchanged, but we would have to make sure that no code uses a
4511      // 32bit input value as a 64bit value assuming that the top 32 bits are
4512      // zero.
4513      __ Mov(output.W(), source.W());
4514    } else if (result_type == Primitive::kPrimChar ||
4515               (input_type == Primitive::kPrimChar && input_size < result_size)) {
4516      __ Ubfx(output,
4517              output.IsX() ? source.X() : source.W(),
4518              0, Primitive::ComponentSize(Primitive::kPrimChar) * kBitsPerByte);
4519    } else {
4520      __ Sbfx(output, output.IsX() ? source.X() : source.W(), 0, min_size * kBitsPerByte);
4521    }
4522  } else if (Primitive::IsFloatingPointType(result_type) && Primitive::IsIntegralType(input_type)) {
4523    __ Scvtf(OutputFPRegister(conversion), InputRegisterAt(conversion, 0));
4524  } else if (Primitive::IsIntegralType(result_type) && Primitive::IsFloatingPointType(input_type)) {
4525    CHECK(result_type == Primitive::kPrimInt || result_type == Primitive::kPrimLong);
4526    __ Fcvtzs(OutputRegister(conversion), InputFPRegisterAt(conversion, 0));
4527  } else if (Primitive::IsFloatingPointType(result_type) &&
4528             Primitive::IsFloatingPointType(input_type)) {
4529    __ Fcvt(OutputFPRegister(conversion), InputFPRegisterAt(conversion, 0));
4530  } else {
4531    LOG(FATAL) << "Unexpected or unimplemented type conversion from " << input_type
4532                << " to " << result_type;
4533  }
4534}
4535
4536void LocationsBuilderARM64::VisitUShr(HUShr* ushr) {
4537  HandleShift(ushr);
4538}
4539
4540void InstructionCodeGeneratorARM64::VisitUShr(HUShr* ushr) {
4541  HandleShift(ushr);
4542}
4543
4544void LocationsBuilderARM64::VisitXor(HXor* instruction) {
4545  HandleBinaryOp(instruction);
4546}
4547
4548void InstructionCodeGeneratorARM64::VisitXor(HXor* instruction) {
4549  HandleBinaryOp(instruction);
4550}
4551
4552void LocationsBuilderARM64::VisitBoundType(HBoundType* instruction ATTRIBUTE_UNUSED) {
4553  // Nothing to do, this should be removed during prepare for register allocator.
4554  LOG(FATAL) << "Unreachable";
4555}
4556
4557void InstructionCodeGeneratorARM64::VisitBoundType(HBoundType* instruction ATTRIBUTE_UNUSED) {
4558  // Nothing to do, this should be removed during prepare for register allocator.
4559  LOG(FATAL) << "Unreachable";
4560}
4561
4562void LocationsBuilderARM64::VisitFakeString(HFakeString* instruction) {
4563  DCHECK(codegen_->IsBaseline());
4564  LocationSummary* locations =
4565      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
4566  locations->SetOut(Location::ConstantLocation(GetGraph()->GetNullConstant()));
4567}
4568
4569void InstructionCodeGeneratorARM64::VisitFakeString(HFakeString* instruction ATTRIBUTE_UNUSED) {
4570  DCHECK(codegen_->IsBaseline());
4571  // Will be generated at use site.
4572}
4573
4574// Simple implementation of packed switch - generate cascaded compare/jumps.
4575void LocationsBuilderARM64::VisitPackedSwitch(HPackedSwitch* switch_instr) {
4576  LocationSummary* locations =
4577      new (GetGraph()->GetArena()) LocationSummary(switch_instr, LocationSummary::kNoCall);
4578  locations->SetInAt(0, Location::RequiresRegister());
4579}
4580
4581void InstructionCodeGeneratorARM64::VisitPackedSwitch(HPackedSwitch* switch_instr) {
4582  int32_t lower_bound = switch_instr->GetStartValue();
4583  uint32_t num_entries = switch_instr->GetNumEntries();
4584  Register value_reg = InputRegisterAt(switch_instr, 0);
4585  HBasicBlock* default_block = switch_instr->GetDefaultBlock();
4586
4587  // Roughly set 16 as max average assemblies generated per HIR in a graph.
4588  static constexpr int32_t kMaxExpectedSizePerHInstruction = 16 * vixl::kInstructionSize;
4589  // ADR has a limited range(+/-1MB), so we set a threshold for the number of HIRs in the graph to
4590  // make sure we don't emit it if the target may run out of range.
4591  // TODO: Instead of emitting all jump tables at the end of the code, we could keep track of ADR
4592  // ranges and emit the tables only as required.
4593  static constexpr int32_t kJumpTableInstructionThreshold = 1* MB / kMaxExpectedSizePerHInstruction;
4594
4595  if (num_entries <= kPackedSwitchCompareJumpThreshold ||
4596      // Current instruction id is an upper bound of the number of HIRs in the graph.
4597      GetGraph()->GetCurrentInstructionId() > kJumpTableInstructionThreshold) {
4598    // Create a series of compare/jumps.
4599    UseScratchRegisterScope temps(codegen_->GetVIXLAssembler());
4600    Register temp = temps.AcquireW();
4601    __ Subs(temp, value_reg, Operand(lower_bound));
4602
4603    const ArenaVector<HBasicBlock*>& successors = switch_instr->GetBlock()->GetSuccessors();
4604    // Jump to successors[0] if value == lower_bound.
4605    __ B(eq, codegen_->GetLabelOf(successors[0]));
4606    int32_t last_index = 0;
4607    for (; num_entries - last_index > 2; last_index += 2) {
4608      __ Subs(temp, temp, Operand(2));
4609      // Jump to successors[last_index + 1] if value < case_value[last_index + 2].
4610      __ B(lo, codegen_->GetLabelOf(successors[last_index + 1]));
4611      // Jump to successors[last_index + 2] if value == case_value[last_index + 2].
4612      __ B(eq, codegen_->GetLabelOf(successors[last_index + 2]));
4613    }
4614    if (num_entries - last_index == 2) {
4615      // The last missing case_value.
4616      __ Cmp(temp, Operand(1));
4617      __ B(eq, codegen_->GetLabelOf(successors[last_index + 1]));
4618    }
4619
4620    // And the default for any other value.
4621    if (!codegen_->GoesToNextBlock(switch_instr->GetBlock(), default_block)) {
4622      __ B(codegen_->GetLabelOf(default_block));
4623    }
4624  } else {
4625    JumpTableARM64* jump_table = new (GetGraph()->GetArena()) JumpTableARM64(switch_instr);
4626    codegen_->AddJumpTable(jump_table);
4627
4628    UseScratchRegisterScope temps(codegen_->GetVIXLAssembler());
4629
4630    // Below instructions should use at most one blocked register. Since there are two blocked
4631    // registers, we are free to block one.
4632    Register temp_w = temps.AcquireW();
4633    Register index;
4634    // Remove the bias.
4635    if (lower_bound != 0) {
4636      index = temp_w;
4637      __ Sub(index, value_reg, Operand(lower_bound));
4638    } else {
4639      index = value_reg;
4640    }
4641
4642    // Jump to default block if index is out of the range.
4643    __ Cmp(index, Operand(num_entries));
4644    __ B(hs, codegen_->GetLabelOf(default_block));
4645
4646    // In current VIXL implementation, it won't require any blocked registers to encode the
4647    // immediate value for Adr. So we are free to use both VIXL blocked registers to reduce the
4648    // register pressure.
4649    Register table_base = temps.AcquireX();
4650    // Load jump offset from the table.
4651    __ Adr(table_base, jump_table->GetTableStartLabel());
4652    Register jump_offset = temp_w;
4653    __ Ldr(jump_offset, MemOperand(table_base, index, UXTW, 2));
4654
4655    // Jump to target block by branching to table_base(pc related) + offset.
4656    Register target_address = table_base;
4657    __ Add(target_address, table_base, Operand(jump_offset, SXTW));
4658    __ Br(target_address);
4659  }
4660}
4661
4662void CodeGeneratorARM64::GenerateReadBarrier(HInstruction* instruction,
4663                                             Location out,
4664                                             Location ref,
4665                                             Location obj,
4666                                             uint32_t offset,
4667                                             Location index) {
4668  DCHECK(kEmitCompilerReadBarrier);
4669
4670  // If heap poisoning is enabled, the unpoisoning of the loaded
4671  // reference will be carried out by the runtime within the slow
4672  // path.
4673  //
4674  // Note that `ref` currently does not get unpoisoned (when heap
4675  // poisoning is enabled), which is alright as the `ref` argument is
4676  // not used by the artReadBarrierSlow entry point.
4677  //
4678  // TODO: Unpoison `ref` when it is used by artReadBarrierSlow.
4679  SlowPathCodeARM64* slow_path = new (GetGraph()->GetArena())
4680      ReadBarrierForHeapReferenceSlowPathARM64(instruction, out, ref, obj, offset, index);
4681  AddSlowPath(slow_path);
4682
4683  // TODO: When read barrier has a fast path, add it here.
4684  /* Currently the read barrier call is inserted after the original load.
4685   * However, if we have a fast path, we need to perform the load of obj.LockWord *before* the
4686   * original load. This load-load ordering is required by the read barrier.
4687   * The fast path/slow path (for Baker's algorithm) should look like:
4688   *
4689   * bool isGray = obj.LockWord & kReadBarrierMask;
4690   * lfence;  // load fence or artificial data dependence to prevent load-load reordering
4691   * ref = obj.field;    // this is the original load
4692   * if (isGray) {
4693   *   ref = Mark(ref);  // ideally the slow path just does Mark(ref)
4694   * }
4695   */
4696
4697  __ B(slow_path->GetEntryLabel());
4698  __ Bind(slow_path->GetExitLabel());
4699}
4700
4701void CodeGeneratorARM64::MaybeGenerateReadBarrier(HInstruction* instruction,
4702                                                  Location out,
4703                                                  Location ref,
4704                                                  Location obj,
4705                                                  uint32_t offset,
4706                                                  Location index) {
4707  if (kEmitCompilerReadBarrier) {
4708    // If heap poisoning is enabled, unpoisoning will be taken care of
4709    // by the runtime within the slow path.
4710    GenerateReadBarrier(instruction, out, ref, obj, offset, index);
4711  } else if (kPoisonHeapReferences) {
4712    GetAssembler()->UnpoisonHeapReference(WRegisterFrom(out));
4713  }
4714}
4715
4716void CodeGeneratorARM64::GenerateReadBarrierForRoot(HInstruction* instruction,
4717                                                    Location out,
4718                                                    Location root) {
4719  DCHECK(kEmitCompilerReadBarrier);
4720
4721  // Note that GC roots are not affected by heap poisoning, so we do
4722  // not need to do anything special for this here.
4723  SlowPathCodeARM64* slow_path =
4724      new (GetGraph()->GetArena()) ReadBarrierForRootSlowPathARM64(instruction, out, root);
4725  AddSlowPath(slow_path);
4726
4727  // TODO: Implement a fast path for ReadBarrierForRoot, performing
4728  // the following operation (for Baker's algorithm):
4729  //
4730  //   if (thread.tls32_.is_gc_marking) {
4731  //     root = Mark(root);
4732  //   }
4733
4734  __ B(slow_path->GetEntryLabel());
4735  __ Bind(slow_path->GetExitLabel());
4736}
4737
4738#undef __
4739#undef QUICK_ENTRY_POINT
4740
4741}  // namespace arm64
4742}  // namespace art
4743