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