• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 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_mips64.h"
18 
19 #include "arch/mips64/asm_support_mips64.h"
20 #include "art_method.h"
21 #include "class_table.h"
22 #include "code_generator_utils.h"
23 #include "compiled_method.h"
24 #include "entrypoints/quick/quick_entrypoints.h"
25 #include "entrypoints/quick/quick_entrypoints_enum.h"
26 #include "gc/accounting/card_table.h"
27 #include "heap_poisoning.h"
28 #include "intrinsics.h"
29 #include "intrinsics_mips64.h"
30 #include "linker/linker_patch.h"
31 #include "mirror/array-inl.h"
32 #include "mirror/class-inl.h"
33 #include "offsets.h"
34 #include "stack_map_stream.h"
35 #include "thread.h"
36 #include "utils/assembler.h"
37 #include "utils/mips64/assembler_mips64.h"
38 #include "utils/stack_checks.h"
39 
40 namespace art {
41 namespace mips64 {
42 
43 static constexpr int kCurrentMethodStackOffset = 0;
44 static constexpr GpuRegister kMethodRegisterArgument = A0;
45 
46 // Flags controlling the use of thunks for Baker read barriers.
47 constexpr bool kBakerReadBarrierThunksEnableForFields = true;
48 constexpr bool kBakerReadBarrierThunksEnableForArrays = true;
49 constexpr bool kBakerReadBarrierThunksEnableForGcRoots = true;
50 
Mips64ReturnLocation(DataType::Type return_type)51 Location Mips64ReturnLocation(DataType::Type return_type) {
52   switch (return_type) {
53     case DataType::Type::kBool:
54     case DataType::Type::kUint8:
55     case DataType::Type::kInt8:
56     case DataType::Type::kUint16:
57     case DataType::Type::kInt16:
58     case DataType::Type::kUint32:
59     case DataType::Type::kInt32:
60     case DataType::Type::kReference:
61     case DataType::Type::kUint64:
62     case DataType::Type::kInt64:
63       return Location::RegisterLocation(V0);
64 
65     case DataType::Type::kFloat32:
66     case DataType::Type::kFloat64:
67       return Location::FpuRegisterLocation(F0);
68 
69     case DataType::Type::kVoid:
70       return Location();
71   }
72   UNREACHABLE();
73 }
74 
GetReturnLocation(DataType::Type type) const75 Location InvokeDexCallingConventionVisitorMIPS64::GetReturnLocation(DataType::Type type) const {
76   return Mips64ReturnLocation(type);
77 }
78 
GetMethodLocation() const79 Location InvokeDexCallingConventionVisitorMIPS64::GetMethodLocation() const {
80   return Location::RegisterLocation(kMethodRegisterArgument);
81 }
82 
GetNextLocation(DataType::Type type)83 Location InvokeDexCallingConventionVisitorMIPS64::GetNextLocation(DataType::Type type) {
84   Location next_location;
85   if (type == DataType::Type::kVoid) {
86     LOG(FATAL) << "Unexpected parameter type " << type;
87   }
88 
89   if (DataType::IsFloatingPointType(type) &&
90       (float_index_ < calling_convention.GetNumberOfFpuRegisters())) {
91     next_location = Location::FpuRegisterLocation(
92         calling_convention.GetFpuRegisterAt(float_index_++));
93     gp_index_++;
94   } else if (!DataType::IsFloatingPointType(type) &&
95              (gp_index_ < calling_convention.GetNumberOfRegisters())) {
96     next_location = Location::RegisterLocation(calling_convention.GetRegisterAt(gp_index_++));
97     float_index_++;
98   } else {
99     size_t stack_offset = calling_convention.GetStackOffsetOf(stack_index_);
100     next_location = DataType::Is64BitType(type) ? Location::DoubleStackSlot(stack_offset)
101                                                 : Location::StackSlot(stack_offset);
102   }
103 
104   // Space on the stack is reserved for all arguments.
105   stack_index_ += DataType::Is64BitType(type) ? 2 : 1;
106 
107   return next_location;
108 }
109 
GetReturnLocation(DataType::Type type)110 Location InvokeRuntimeCallingConvention::GetReturnLocation(DataType::Type type) {
111   return Mips64ReturnLocation(type);
112 }
113 
114 // NOLINT on __ macro to suppress wrong warning/fix (misc-macro-parentheses) from clang-tidy.
115 #define __ down_cast<CodeGeneratorMIPS64*>(codegen)->GetAssembler()->  // NOLINT
116 #define QUICK_ENTRY_POINT(x) QUICK_ENTRYPOINT_OFFSET(kMips64PointerSize, x).Int32Value()
117 
118 class BoundsCheckSlowPathMIPS64 : public SlowPathCodeMIPS64 {
119  public:
BoundsCheckSlowPathMIPS64(HBoundsCheck * instruction)120   explicit BoundsCheckSlowPathMIPS64(HBoundsCheck* instruction) : SlowPathCodeMIPS64(instruction) {}
121 
EmitNativeCode(CodeGenerator * codegen)122   void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
123     LocationSummary* locations = instruction_->GetLocations();
124     CodeGeneratorMIPS64* mips64_codegen = down_cast<CodeGeneratorMIPS64*>(codegen);
125     __ Bind(GetEntryLabel());
126     if (instruction_->CanThrowIntoCatchBlock()) {
127       // Live registers will be restored in the catch block if caught.
128       SaveLiveRegisters(codegen, instruction_->GetLocations());
129     }
130     // We're moving two locations to locations that could overlap, so we need a parallel
131     // move resolver.
132     InvokeRuntimeCallingConvention calling_convention;
133     codegen->EmitParallelMoves(locations->InAt(0),
134                                Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
135                                DataType::Type::kInt32,
136                                locations->InAt(1),
137                                Location::RegisterLocation(calling_convention.GetRegisterAt(1)),
138                                DataType::Type::kInt32);
139     QuickEntrypointEnum entrypoint = instruction_->AsBoundsCheck()->IsStringCharAt()
140         ? kQuickThrowStringBounds
141         : kQuickThrowArrayBounds;
142     mips64_codegen->InvokeRuntime(entrypoint, instruction_, instruction_->GetDexPc(), this);
143     CheckEntrypointTypes<kQuickThrowStringBounds, void, int32_t, int32_t>();
144     CheckEntrypointTypes<kQuickThrowArrayBounds, void, int32_t, int32_t>();
145   }
146 
IsFatal() const147   bool IsFatal() const OVERRIDE { return true; }
148 
GetDescription() const149   const char* GetDescription() const OVERRIDE { return "BoundsCheckSlowPathMIPS64"; }
150 
151  private:
152   DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathMIPS64);
153 };
154 
155 class DivZeroCheckSlowPathMIPS64 : public SlowPathCodeMIPS64 {
156  public:
DivZeroCheckSlowPathMIPS64(HDivZeroCheck * instruction)157   explicit DivZeroCheckSlowPathMIPS64(HDivZeroCheck* instruction)
158       : SlowPathCodeMIPS64(instruction) {}
159 
EmitNativeCode(CodeGenerator * codegen)160   void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
161     CodeGeneratorMIPS64* mips64_codegen = down_cast<CodeGeneratorMIPS64*>(codegen);
162     __ Bind(GetEntryLabel());
163     mips64_codegen->InvokeRuntime(kQuickThrowDivZero, instruction_, instruction_->GetDexPc(), this);
164     CheckEntrypointTypes<kQuickThrowDivZero, void, void>();
165   }
166 
IsFatal() const167   bool IsFatal() const OVERRIDE { return true; }
168 
GetDescription() const169   const char* GetDescription() const OVERRIDE { return "DivZeroCheckSlowPathMIPS64"; }
170 
171  private:
172   DISALLOW_COPY_AND_ASSIGN(DivZeroCheckSlowPathMIPS64);
173 };
174 
175 class LoadClassSlowPathMIPS64 : public SlowPathCodeMIPS64 {
176  public:
LoadClassSlowPathMIPS64(HLoadClass * cls,HInstruction * at,uint32_t dex_pc,bool do_clinit)177   LoadClassSlowPathMIPS64(HLoadClass* cls,
178                           HInstruction* at,
179                           uint32_t dex_pc,
180                           bool do_clinit)
181       : SlowPathCodeMIPS64(at),
182         cls_(cls),
183         dex_pc_(dex_pc),
184         do_clinit_(do_clinit) {
185     DCHECK(at->IsLoadClass() || at->IsClinitCheck());
186   }
187 
EmitNativeCode(CodeGenerator * codegen)188   void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
189     LocationSummary* locations = instruction_->GetLocations();
190     Location out = locations->Out();
191     CodeGeneratorMIPS64* mips64_codegen = down_cast<CodeGeneratorMIPS64*>(codegen);
192     InvokeRuntimeCallingConvention calling_convention;
193     DCHECK_EQ(instruction_->IsLoadClass(), cls_ == instruction_);
194     __ Bind(GetEntryLabel());
195     SaveLiveRegisters(codegen, locations);
196 
197     dex::TypeIndex type_index = cls_->GetTypeIndex();
198     __ LoadConst32(calling_convention.GetRegisterAt(0), type_index.index_);
199     QuickEntrypointEnum entrypoint = do_clinit_ ? kQuickInitializeStaticStorage
200                                                 : kQuickInitializeType;
201     mips64_codegen->InvokeRuntime(entrypoint, instruction_, dex_pc_, this);
202     if (do_clinit_) {
203       CheckEntrypointTypes<kQuickInitializeStaticStorage, void*, uint32_t>();
204     } else {
205       CheckEntrypointTypes<kQuickInitializeType, void*, uint32_t>();
206     }
207 
208     // Move the class to the desired location.
209     if (out.IsValid()) {
210       DCHECK(out.IsRegister() && !locations->GetLiveRegisters()->ContainsCoreRegister(out.reg()));
211       DataType::Type type = instruction_->GetType();
212       mips64_codegen->MoveLocation(out,
213                                    Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
214                                    type);
215     }
216     RestoreLiveRegisters(codegen, locations);
217 
218     __ Bc(GetExitLabel());
219   }
220 
GetDescription() const221   const char* GetDescription() const OVERRIDE { return "LoadClassSlowPathMIPS64"; }
222 
223  private:
224   // The class this slow path will load.
225   HLoadClass* const cls_;
226 
227   // The dex PC of `at_`.
228   const uint32_t dex_pc_;
229 
230   // Whether to initialize the class.
231   const bool do_clinit_;
232 
233   DISALLOW_COPY_AND_ASSIGN(LoadClassSlowPathMIPS64);
234 };
235 
236 class LoadStringSlowPathMIPS64 : public SlowPathCodeMIPS64 {
237  public:
LoadStringSlowPathMIPS64(HLoadString * instruction)238   explicit LoadStringSlowPathMIPS64(HLoadString* instruction)
239       : SlowPathCodeMIPS64(instruction) {}
240 
EmitNativeCode(CodeGenerator * codegen)241   void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
242     DCHECK(instruction_->IsLoadString());
243     DCHECK_EQ(instruction_->AsLoadString()->GetLoadKind(), HLoadString::LoadKind::kBssEntry);
244     LocationSummary* locations = instruction_->GetLocations();
245     DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg()));
246     const dex::StringIndex string_index = instruction_->AsLoadString()->GetStringIndex();
247     CodeGeneratorMIPS64* mips64_codegen = down_cast<CodeGeneratorMIPS64*>(codegen);
248     InvokeRuntimeCallingConvention calling_convention;
249     __ Bind(GetEntryLabel());
250     SaveLiveRegisters(codegen, locations);
251 
252     __ LoadConst32(calling_convention.GetRegisterAt(0), string_index.index_);
253     mips64_codegen->InvokeRuntime(kQuickResolveString,
254                                   instruction_,
255                                   instruction_->GetDexPc(),
256                                   this);
257     CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>();
258 
259     DataType::Type type = instruction_->GetType();
260     mips64_codegen->MoveLocation(locations->Out(),
261                                  Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
262                                  type);
263     RestoreLiveRegisters(codegen, locations);
264 
265     __ Bc(GetExitLabel());
266   }
267 
GetDescription() const268   const char* GetDescription() const OVERRIDE { return "LoadStringSlowPathMIPS64"; }
269 
270  private:
271   DISALLOW_COPY_AND_ASSIGN(LoadStringSlowPathMIPS64);
272 };
273 
274 class NullCheckSlowPathMIPS64 : public SlowPathCodeMIPS64 {
275  public:
NullCheckSlowPathMIPS64(HNullCheck * instr)276   explicit NullCheckSlowPathMIPS64(HNullCheck* instr) : SlowPathCodeMIPS64(instr) {}
277 
EmitNativeCode(CodeGenerator * codegen)278   void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
279     CodeGeneratorMIPS64* mips64_codegen = down_cast<CodeGeneratorMIPS64*>(codegen);
280     __ Bind(GetEntryLabel());
281     if (instruction_->CanThrowIntoCatchBlock()) {
282       // Live registers will be restored in the catch block if caught.
283       SaveLiveRegisters(codegen, instruction_->GetLocations());
284     }
285     mips64_codegen->InvokeRuntime(kQuickThrowNullPointer,
286                                   instruction_,
287                                   instruction_->GetDexPc(),
288                                   this);
289     CheckEntrypointTypes<kQuickThrowNullPointer, void, void>();
290   }
291 
IsFatal() const292   bool IsFatal() const OVERRIDE { return true; }
293 
GetDescription() const294   const char* GetDescription() const OVERRIDE { return "NullCheckSlowPathMIPS64"; }
295 
296  private:
297   DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathMIPS64);
298 };
299 
300 class SuspendCheckSlowPathMIPS64 : public SlowPathCodeMIPS64 {
301  public:
SuspendCheckSlowPathMIPS64(HSuspendCheck * instruction,HBasicBlock * successor)302   SuspendCheckSlowPathMIPS64(HSuspendCheck* instruction, HBasicBlock* successor)
303       : SlowPathCodeMIPS64(instruction), successor_(successor) {}
304 
EmitNativeCode(CodeGenerator * codegen)305   void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
306     LocationSummary* locations = instruction_->GetLocations();
307     CodeGeneratorMIPS64* mips64_codegen = down_cast<CodeGeneratorMIPS64*>(codegen);
308     __ Bind(GetEntryLabel());
309     SaveLiveRegisters(codegen, locations);     // Only saves live vector registers for SIMD.
310     mips64_codegen->InvokeRuntime(kQuickTestSuspend, instruction_, instruction_->GetDexPc(), this);
311     CheckEntrypointTypes<kQuickTestSuspend, void, void>();
312     RestoreLiveRegisters(codegen, locations);  // Only restores live vector registers for SIMD.
313     if (successor_ == nullptr) {
314       __ Bc(GetReturnLabel());
315     } else {
316       __ Bc(mips64_codegen->GetLabelOf(successor_));
317     }
318   }
319 
GetReturnLabel()320   Mips64Label* GetReturnLabel() {
321     DCHECK(successor_ == nullptr);
322     return &return_label_;
323   }
324 
GetDescription() const325   const char* GetDescription() const OVERRIDE { return "SuspendCheckSlowPathMIPS64"; }
326 
GetSuccessor() const327   HBasicBlock* GetSuccessor() const {
328     return successor_;
329   }
330 
331  private:
332   // If not null, the block to branch to after the suspend check.
333   HBasicBlock* const successor_;
334 
335   // If `successor_` is null, the label to branch to after the suspend check.
336   Mips64Label return_label_;
337 
338   DISALLOW_COPY_AND_ASSIGN(SuspendCheckSlowPathMIPS64);
339 };
340 
341 class TypeCheckSlowPathMIPS64 : public SlowPathCodeMIPS64 {
342  public:
TypeCheckSlowPathMIPS64(HInstruction * instruction,bool is_fatal)343   explicit TypeCheckSlowPathMIPS64(HInstruction* instruction, bool is_fatal)
344       : SlowPathCodeMIPS64(instruction), is_fatal_(is_fatal) {}
345 
EmitNativeCode(CodeGenerator * codegen)346   void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
347     LocationSummary* locations = instruction_->GetLocations();
348 
349     uint32_t dex_pc = instruction_->GetDexPc();
350     DCHECK(instruction_->IsCheckCast()
351            || !locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg()));
352     CodeGeneratorMIPS64* mips64_codegen = down_cast<CodeGeneratorMIPS64*>(codegen);
353 
354     __ Bind(GetEntryLabel());
355     if (!is_fatal_ || instruction_->CanThrowIntoCatchBlock()) {
356       SaveLiveRegisters(codegen, locations);
357     }
358 
359     // We're moving two locations to locations that could overlap, so we need a parallel
360     // move resolver.
361     InvokeRuntimeCallingConvention calling_convention;
362     codegen->EmitParallelMoves(locations->InAt(0),
363                                Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
364                                DataType::Type::kReference,
365                                locations->InAt(1),
366                                Location::RegisterLocation(calling_convention.GetRegisterAt(1)),
367                                DataType::Type::kReference);
368     if (instruction_->IsInstanceOf()) {
369       mips64_codegen->InvokeRuntime(kQuickInstanceofNonTrivial, instruction_, dex_pc, this);
370       CheckEntrypointTypes<kQuickInstanceofNonTrivial, size_t, mirror::Object*, mirror::Class*>();
371       DataType::Type ret_type = instruction_->GetType();
372       Location ret_loc = calling_convention.GetReturnLocation(ret_type);
373       mips64_codegen->MoveLocation(locations->Out(), ret_loc, ret_type);
374     } else {
375       DCHECK(instruction_->IsCheckCast());
376       mips64_codegen->InvokeRuntime(kQuickCheckInstanceOf, instruction_, dex_pc, this);
377       CheckEntrypointTypes<kQuickCheckInstanceOf, void, mirror::Object*, mirror::Class*>();
378     }
379 
380     if (!is_fatal_) {
381       RestoreLiveRegisters(codegen, locations);
382       __ Bc(GetExitLabel());
383     }
384   }
385 
GetDescription() const386   const char* GetDescription() const OVERRIDE { return "TypeCheckSlowPathMIPS64"; }
387 
IsFatal() const388   bool IsFatal() const OVERRIDE { return is_fatal_; }
389 
390  private:
391   const bool is_fatal_;
392 
393   DISALLOW_COPY_AND_ASSIGN(TypeCheckSlowPathMIPS64);
394 };
395 
396 class DeoptimizationSlowPathMIPS64 : public SlowPathCodeMIPS64 {
397  public:
DeoptimizationSlowPathMIPS64(HDeoptimize * instruction)398   explicit DeoptimizationSlowPathMIPS64(HDeoptimize* instruction)
399     : SlowPathCodeMIPS64(instruction) {}
400 
EmitNativeCode(CodeGenerator * codegen)401   void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
402     CodeGeneratorMIPS64* mips64_codegen = down_cast<CodeGeneratorMIPS64*>(codegen);
403     __ Bind(GetEntryLabel());
404       LocationSummary* locations = instruction_->GetLocations();
405     SaveLiveRegisters(codegen, locations);
406     InvokeRuntimeCallingConvention calling_convention;
407     __ LoadConst32(calling_convention.GetRegisterAt(0),
408                    static_cast<uint32_t>(instruction_->AsDeoptimize()->GetDeoptimizationKind()));
409     mips64_codegen->InvokeRuntime(kQuickDeoptimize, instruction_, instruction_->GetDexPc(), this);
410     CheckEntrypointTypes<kQuickDeoptimize, void, DeoptimizationKind>();
411   }
412 
GetDescription() const413   const char* GetDescription() const OVERRIDE { return "DeoptimizationSlowPathMIPS64"; }
414 
415  private:
416   DISALLOW_COPY_AND_ASSIGN(DeoptimizationSlowPathMIPS64);
417 };
418 
419 class ArraySetSlowPathMIPS64 : public SlowPathCodeMIPS64 {
420  public:
ArraySetSlowPathMIPS64(HInstruction * instruction)421   explicit ArraySetSlowPathMIPS64(HInstruction* instruction) : SlowPathCodeMIPS64(instruction) {}
422 
EmitNativeCode(CodeGenerator * codegen)423   void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
424     LocationSummary* locations = instruction_->GetLocations();
425     __ Bind(GetEntryLabel());
426     SaveLiveRegisters(codegen, locations);
427 
428     InvokeRuntimeCallingConvention calling_convention;
429     HParallelMove parallel_move(codegen->GetGraph()->GetAllocator());
430     parallel_move.AddMove(
431         locations->InAt(0),
432         Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
433         DataType::Type::kReference,
434         nullptr);
435     parallel_move.AddMove(
436         locations->InAt(1),
437         Location::RegisterLocation(calling_convention.GetRegisterAt(1)),
438         DataType::Type::kInt32,
439         nullptr);
440     parallel_move.AddMove(
441         locations->InAt(2),
442         Location::RegisterLocation(calling_convention.GetRegisterAt(2)),
443         DataType::Type::kReference,
444         nullptr);
445     codegen->GetMoveResolver()->EmitNativeCode(&parallel_move);
446 
447     CodeGeneratorMIPS64* mips64_codegen = down_cast<CodeGeneratorMIPS64*>(codegen);
448     mips64_codegen->InvokeRuntime(kQuickAputObject, instruction_, instruction_->GetDexPc(), this);
449     CheckEntrypointTypes<kQuickAputObject, void, mirror::Array*, int32_t, mirror::Object*>();
450     RestoreLiveRegisters(codegen, locations);
451     __ Bc(GetExitLabel());
452   }
453 
GetDescription() const454   const char* GetDescription() const OVERRIDE { return "ArraySetSlowPathMIPS64"; }
455 
456  private:
457   DISALLOW_COPY_AND_ASSIGN(ArraySetSlowPathMIPS64);
458 };
459 
460 // Slow path marking an object reference `ref` during a read
461 // barrier. The field `obj.field` in the object `obj` holding this
462 // reference does not get updated by this slow path after marking (see
463 // ReadBarrierMarkAndUpdateFieldSlowPathMIPS64 below for that).
464 //
465 // This means that after the execution of this slow path, `ref` will
466 // always be up-to-date, but `obj.field` may not; i.e., after the
467 // flip, `ref` will be a to-space reference, but `obj.field` will
468 // probably still be a from-space reference (unless it gets updated by
469 // another thread, or if another thread installed another object
470 // reference (different from `ref`) in `obj.field`).
471 //
472 // If `entrypoint` is a valid location it is assumed to already be
473 // holding the entrypoint. The case where the entrypoint is passed in
474 // is for the GcRoot read barrier.
475 class ReadBarrierMarkSlowPathMIPS64 : public SlowPathCodeMIPS64 {
476  public:
ReadBarrierMarkSlowPathMIPS64(HInstruction * instruction,Location ref,Location entrypoint=Location::NoLocation ())477   ReadBarrierMarkSlowPathMIPS64(HInstruction* instruction,
478                                 Location ref,
479                                 Location entrypoint = Location::NoLocation())
480       : SlowPathCodeMIPS64(instruction), ref_(ref), entrypoint_(entrypoint) {
481     DCHECK(kEmitCompilerReadBarrier);
482   }
483 
GetDescription() const484   const char* GetDescription() const OVERRIDE { return "ReadBarrierMarkSlowPathMIPS"; }
485 
EmitNativeCode(CodeGenerator * codegen)486   void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
487     LocationSummary* locations = instruction_->GetLocations();
488     GpuRegister ref_reg = ref_.AsRegister<GpuRegister>();
489     DCHECK(locations->CanCall());
490     DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(ref_reg)) << ref_reg;
491     DCHECK(instruction_->IsInstanceFieldGet() ||
492            instruction_->IsStaticFieldGet() ||
493            instruction_->IsArrayGet() ||
494            instruction_->IsArraySet() ||
495            instruction_->IsLoadClass() ||
496            instruction_->IsLoadString() ||
497            instruction_->IsInstanceOf() ||
498            instruction_->IsCheckCast() ||
499            (instruction_->IsInvokeVirtual() && instruction_->GetLocations()->Intrinsified()) ||
500            (instruction_->IsInvokeStaticOrDirect() && instruction_->GetLocations()->Intrinsified()))
501         << "Unexpected instruction in read barrier marking slow path: "
502         << instruction_->DebugName();
503 
504     __ Bind(GetEntryLabel());
505     // No need to save live registers; it's taken care of by the
506     // entrypoint. Also, there is no need to update the stack mask,
507     // as this runtime call will not trigger a garbage collection.
508     CodeGeneratorMIPS64* mips64_codegen = down_cast<CodeGeneratorMIPS64*>(codegen);
509     DCHECK((V0 <= ref_reg && ref_reg <= T2) ||
510            (S2 <= ref_reg && ref_reg <= S7) ||
511            (ref_reg == S8)) << ref_reg;
512     // "Compact" slow path, saving two moves.
513     //
514     // Instead of using the standard runtime calling convention (input
515     // and output in A0 and V0 respectively):
516     //
517     //   A0 <- ref
518     //   V0 <- ReadBarrierMark(A0)
519     //   ref <- V0
520     //
521     // we just use rX (the register containing `ref`) as input and output
522     // of a dedicated entrypoint:
523     //
524     //   rX <- ReadBarrierMarkRegX(rX)
525     //
526     if (entrypoint_.IsValid()) {
527       mips64_codegen->ValidateInvokeRuntimeWithoutRecordingPcInfo(instruction_, this);
528       DCHECK_EQ(entrypoint_.AsRegister<GpuRegister>(), T9);
529       __ Jalr(entrypoint_.AsRegister<GpuRegister>());
530       __ Nop();
531     } else {
532       int32_t entry_point_offset =
533           Thread::ReadBarrierMarkEntryPointsOffset<kMips64PointerSize>(ref_reg - 1);
534       // This runtime call does not require a stack map.
535       mips64_codegen->InvokeRuntimeWithoutRecordingPcInfo(entry_point_offset,
536                                                           instruction_,
537                                                           this);
538     }
539     __ Bc(GetExitLabel());
540   }
541 
542  private:
543   // The location (register) of the marked object reference.
544   const Location ref_;
545 
546   // The location of the entrypoint if already loaded.
547   const Location entrypoint_;
548 
549   DISALLOW_COPY_AND_ASSIGN(ReadBarrierMarkSlowPathMIPS64);
550 };
551 
552 // Slow path marking an object reference `ref` during a read barrier,
553 // and if needed, atomically updating the field `obj.field` in the
554 // object `obj` holding this reference after marking (contrary to
555 // ReadBarrierMarkSlowPathMIPS64 above, which never tries to update
556 // `obj.field`).
557 //
558 // This means that after the execution of this slow path, both `ref`
559 // and `obj.field` will be up-to-date; i.e., after the flip, both will
560 // hold the same to-space reference (unless another thread installed
561 // another object reference (different from `ref`) in `obj.field`).
562 class ReadBarrierMarkAndUpdateFieldSlowPathMIPS64 : public SlowPathCodeMIPS64 {
563  public:
ReadBarrierMarkAndUpdateFieldSlowPathMIPS64(HInstruction * instruction,Location ref,GpuRegister obj,Location field_offset,GpuRegister temp1)564   ReadBarrierMarkAndUpdateFieldSlowPathMIPS64(HInstruction* instruction,
565                                               Location ref,
566                                               GpuRegister obj,
567                                               Location field_offset,
568                                               GpuRegister temp1)
569       : SlowPathCodeMIPS64(instruction),
570         ref_(ref),
571         obj_(obj),
572         field_offset_(field_offset),
573         temp1_(temp1) {
574     DCHECK(kEmitCompilerReadBarrier);
575   }
576 
GetDescription() const577   const char* GetDescription() const OVERRIDE {
578     return "ReadBarrierMarkAndUpdateFieldSlowPathMIPS64";
579   }
580 
EmitNativeCode(CodeGenerator * codegen)581   void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
582     LocationSummary* locations = instruction_->GetLocations();
583     GpuRegister ref_reg = ref_.AsRegister<GpuRegister>();
584     DCHECK(locations->CanCall());
585     DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(ref_reg)) << ref_reg;
586     // This slow path is only used by the UnsafeCASObject intrinsic.
587     DCHECK((instruction_->IsInvokeVirtual() && instruction_->GetLocations()->Intrinsified()))
588         << "Unexpected instruction in read barrier marking and field updating slow path: "
589         << instruction_->DebugName();
590     DCHECK(instruction_->GetLocations()->Intrinsified());
591     DCHECK_EQ(instruction_->AsInvoke()->GetIntrinsic(), Intrinsics::kUnsafeCASObject);
592     DCHECK(field_offset_.IsRegister()) << field_offset_;
593 
594     __ Bind(GetEntryLabel());
595 
596     // Save the old reference.
597     // Note that we cannot use AT or TMP to save the old reference, as those
598     // are used by the code that follows, but we need the old reference after
599     // the call to the ReadBarrierMarkRegX entry point.
600     DCHECK_NE(temp1_, AT);
601     DCHECK_NE(temp1_, TMP);
602     __ Move(temp1_, ref_reg);
603 
604     // No need to save live registers; it's taken care of by the
605     // entrypoint. Also, there is no need to update the stack mask,
606     // as this runtime call will not trigger a garbage collection.
607     CodeGeneratorMIPS64* mips64_codegen = down_cast<CodeGeneratorMIPS64*>(codegen);
608     DCHECK((V0 <= ref_reg && ref_reg <= T2) ||
609            (S2 <= ref_reg && ref_reg <= S7) ||
610            (ref_reg == S8)) << ref_reg;
611     // "Compact" slow path, saving two moves.
612     //
613     // Instead of using the standard runtime calling convention (input
614     // and output in A0 and V0 respectively):
615     //
616     //   A0 <- ref
617     //   V0 <- ReadBarrierMark(A0)
618     //   ref <- V0
619     //
620     // we just use rX (the register containing `ref`) as input and output
621     // of a dedicated entrypoint:
622     //
623     //   rX <- ReadBarrierMarkRegX(rX)
624     //
625     int32_t entry_point_offset =
626         Thread::ReadBarrierMarkEntryPointsOffset<kMips64PointerSize>(ref_reg - 1);
627     // This runtime call does not require a stack map.
628     mips64_codegen->InvokeRuntimeWithoutRecordingPcInfo(entry_point_offset,
629                                                         instruction_,
630                                                         this);
631 
632     // If the new reference is different from the old reference,
633     // update the field in the holder (`*(obj_ + field_offset_)`).
634     //
635     // Note that this field could also hold a different object, if
636     // another thread had concurrently changed it. In that case, the
637     // the compare-and-set (CAS) loop below would abort, leaving the
638     // field as-is.
639     Mips64Label done;
640     __ Beqc(temp1_, ref_reg, &done);
641 
642     // Update the the holder's field atomically.  This may fail if
643     // mutator updates before us, but it's OK.  This is achieved
644     // using a strong compare-and-set (CAS) operation with relaxed
645     // memory synchronization ordering, where the expected value is
646     // the old reference and the desired value is the new reference.
647 
648     // Convenience aliases.
649     GpuRegister base = obj_;
650     GpuRegister offset = field_offset_.AsRegister<GpuRegister>();
651     GpuRegister expected = temp1_;
652     GpuRegister value = ref_reg;
653     GpuRegister tmp_ptr = TMP;      // Pointer to actual memory.
654     GpuRegister tmp = AT;           // Value in memory.
655 
656     __ Daddu(tmp_ptr, base, offset);
657 
658     if (kPoisonHeapReferences) {
659       __ PoisonHeapReference(expected);
660       // Do not poison `value` if it is the same register as
661       // `expected`, which has just been poisoned.
662       if (value != expected) {
663         __ PoisonHeapReference(value);
664       }
665     }
666 
667     // do {
668     //   tmp = [r_ptr] - expected;
669     // } while (tmp == 0 && failure([r_ptr] <- r_new_value));
670 
671     Mips64Label loop_head, exit_loop;
672     __ Bind(&loop_head);
673     __ Ll(tmp, tmp_ptr);
674     // The LL instruction sign-extends the 32-bit value, but
675     // 32-bit references must be zero-extended. Zero-extend `tmp`.
676     __ Dext(tmp, tmp, 0, 32);
677     __ Bnec(tmp, expected, &exit_loop);
678     __ Move(tmp, value);
679     __ Sc(tmp, tmp_ptr);
680     __ Beqzc(tmp, &loop_head);
681     __ Bind(&exit_loop);
682 
683     if (kPoisonHeapReferences) {
684       __ UnpoisonHeapReference(expected);
685       // Do not unpoison `value` if it is the same register as
686       // `expected`, which has just been unpoisoned.
687       if (value != expected) {
688         __ UnpoisonHeapReference(value);
689       }
690     }
691 
692     __ Bind(&done);
693     __ Bc(GetExitLabel());
694   }
695 
696  private:
697   // The location (register) of the marked object reference.
698   const Location ref_;
699   // The register containing the object holding the marked object reference field.
700   const GpuRegister obj_;
701   // The location of the offset of the marked reference field within `obj_`.
702   Location field_offset_;
703 
704   const GpuRegister temp1_;
705 
706   DISALLOW_COPY_AND_ASSIGN(ReadBarrierMarkAndUpdateFieldSlowPathMIPS64);
707 };
708 
709 // Slow path generating a read barrier for a heap reference.
710 class ReadBarrierForHeapReferenceSlowPathMIPS64 : public SlowPathCodeMIPS64 {
711  public:
ReadBarrierForHeapReferenceSlowPathMIPS64(HInstruction * instruction,Location out,Location ref,Location obj,uint32_t offset,Location index)712   ReadBarrierForHeapReferenceSlowPathMIPS64(HInstruction* instruction,
713                                             Location out,
714                                             Location ref,
715                                             Location obj,
716                                             uint32_t offset,
717                                             Location index)
718       : SlowPathCodeMIPS64(instruction),
719         out_(out),
720         ref_(ref),
721         obj_(obj),
722         offset_(offset),
723         index_(index) {
724     DCHECK(kEmitCompilerReadBarrier);
725     // If `obj` is equal to `out` or `ref`, it means the initial object
726     // has been overwritten by (or after) the heap object reference load
727     // to be instrumented, e.g.:
728     //
729     //   __ LoadFromOffset(kLoadWord, out, out, offset);
730     //   codegen_->GenerateReadBarrierSlow(instruction, out_loc, out_loc, out_loc, offset);
731     //
732     // In that case, we have lost the information about the original
733     // object, and the emitted read barrier cannot work properly.
734     DCHECK(!obj.Equals(out)) << "obj=" << obj << " out=" << out;
735     DCHECK(!obj.Equals(ref)) << "obj=" << obj << " ref=" << ref;
736   }
737 
EmitNativeCode(CodeGenerator * codegen)738   void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
739     CodeGeneratorMIPS64* mips64_codegen = down_cast<CodeGeneratorMIPS64*>(codegen);
740     LocationSummary* locations = instruction_->GetLocations();
741     DataType::Type type = DataType::Type::kReference;
742     GpuRegister reg_out = out_.AsRegister<GpuRegister>();
743     DCHECK(locations->CanCall());
744     DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(reg_out));
745     DCHECK(instruction_->IsInstanceFieldGet() ||
746            instruction_->IsStaticFieldGet() ||
747            instruction_->IsArrayGet() ||
748            instruction_->IsInstanceOf() ||
749            instruction_->IsCheckCast() ||
750            (instruction_->IsInvokeVirtual() && instruction_->GetLocations()->Intrinsified()))
751         << "Unexpected instruction in read barrier for heap reference slow path: "
752         << instruction_->DebugName();
753 
754     __ Bind(GetEntryLabel());
755     SaveLiveRegisters(codegen, locations);
756 
757     // We may have to change the index's value, but as `index_` is a
758     // constant member (like other "inputs" of this slow path),
759     // introduce a copy of it, `index`.
760     Location index = index_;
761     if (index_.IsValid()) {
762       // Handle `index_` for HArrayGet and UnsafeGetObject/UnsafeGetObjectVolatile intrinsics.
763       if (instruction_->IsArrayGet()) {
764         // Compute the actual memory offset and store it in `index`.
765         GpuRegister index_reg = index_.AsRegister<GpuRegister>();
766         DCHECK(locations->GetLiveRegisters()->ContainsCoreRegister(index_reg));
767         if (codegen->IsCoreCalleeSaveRegister(index_reg)) {
768           // We are about to change the value of `index_reg` (see the
769           // calls to art::mips64::Mips64Assembler::Sll and
770           // art::mips64::MipsAssembler::Addiu32 below), but it has
771           // not been saved by the previous call to
772           // art::SlowPathCode::SaveLiveRegisters, as it is a
773           // callee-save register --
774           // art::SlowPathCode::SaveLiveRegisters does not consider
775           // callee-save registers, as it has been designed with the
776           // assumption that callee-save registers are supposed to be
777           // handled by the called function.  So, as a callee-save
778           // register, `index_reg` _would_ eventually be saved onto
779           // the stack, but it would be too late: we would have
780           // changed its value earlier.  Therefore, we manually save
781           // it here into another freely available register,
782           // `free_reg`, chosen of course among the caller-save
783           // registers (as a callee-save `free_reg` register would
784           // exhibit the same problem).
785           //
786           // Note we could have requested a temporary register from
787           // the register allocator instead; but we prefer not to, as
788           // this is a slow path, and we know we can find a
789           // caller-save register that is available.
790           GpuRegister free_reg = FindAvailableCallerSaveRegister(codegen);
791           __ Move(free_reg, index_reg);
792           index_reg = free_reg;
793           index = Location::RegisterLocation(index_reg);
794         } else {
795           // The initial register stored in `index_` has already been
796           // saved in the call to art::SlowPathCode::SaveLiveRegisters
797           // (as it is not a callee-save register), so we can freely
798           // use it.
799         }
800         // Shifting the index value contained in `index_reg` by the scale
801         // factor (2) cannot overflow in practice, as the runtime is
802         // unable to allocate object arrays with a size larger than
803         // 2^26 - 1 (that is, 2^28 - 4 bytes).
804         __ Sll(index_reg, index_reg, TIMES_4);
805         static_assert(
806             sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
807             "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
808         __ Addiu32(index_reg, index_reg, offset_);
809       } else {
810         // In the case of the UnsafeGetObject/UnsafeGetObjectVolatile
811         // intrinsics, `index_` is not shifted by a scale factor of 2
812         // (as in the case of ArrayGet), as it is actually an offset
813         // to an object field within an object.
814         DCHECK(instruction_->IsInvoke()) << instruction_->DebugName();
815         DCHECK(instruction_->GetLocations()->Intrinsified());
816         DCHECK((instruction_->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObject) ||
817                (instruction_->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile))
818             << instruction_->AsInvoke()->GetIntrinsic();
819         DCHECK_EQ(offset_, 0U);
820         DCHECK(index_.IsRegister());
821       }
822     }
823 
824     // We're moving two or three locations to locations that could
825     // overlap, so we need a parallel move resolver.
826     InvokeRuntimeCallingConvention calling_convention;
827     HParallelMove parallel_move(codegen->GetGraph()->GetAllocator());
828     parallel_move.AddMove(ref_,
829                           Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
830                           DataType::Type::kReference,
831                           nullptr);
832     parallel_move.AddMove(obj_,
833                           Location::RegisterLocation(calling_convention.GetRegisterAt(1)),
834                           DataType::Type::kReference,
835                           nullptr);
836     if (index.IsValid()) {
837       parallel_move.AddMove(index,
838                             Location::RegisterLocation(calling_convention.GetRegisterAt(2)),
839                             DataType::Type::kInt32,
840                             nullptr);
841       codegen->GetMoveResolver()->EmitNativeCode(&parallel_move);
842     } else {
843       codegen->GetMoveResolver()->EmitNativeCode(&parallel_move);
844       __ LoadConst32(calling_convention.GetRegisterAt(2), offset_);
845     }
846     mips64_codegen->InvokeRuntime(kQuickReadBarrierSlow,
847                                   instruction_,
848                                   instruction_->GetDexPc(),
849                                   this);
850     CheckEntrypointTypes<
851         kQuickReadBarrierSlow, mirror::Object*, mirror::Object*, mirror::Object*, uint32_t>();
852     mips64_codegen->MoveLocation(out_, calling_convention.GetReturnLocation(type), type);
853 
854     RestoreLiveRegisters(codegen, locations);
855     __ Bc(GetExitLabel());
856   }
857 
GetDescription() const858   const char* GetDescription() const OVERRIDE {
859     return "ReadBarrierForHeapReferenceSlowPathMIPS64";
860   }
861 
862  private:
FindAvailableCallerSaveRegister(CodeGenerator * codegen)863   GpuRegister FindAvailableCallerSaveRegister(CodeGenerator* codegen) {
864     size_t ref = static_cast<int>(ref_.AsRegister<GpuRegister>());
865     size_t obj = static_cast<int>(obj_.AsRegister<GpuRegister>());
866     for (size_t i = 0, e = codegen->GetNumberOfCoreRegisters(); i < e; ++i) {
867       if (i != ref &&
868           i != obj &&
869           !codegen->IsCoreCalleeSaveRegister(i) &&
870           !codegen->IsBlockedCoreRegister(i)) {
871         return static_cast<GpuRegister>(i);
872       }
873     }
874     // We shall never fail to find a free caller-save register, as
875     // there are more than two core caller-save registers on MIPS64
876     // (meaning it is possible to find one which is different from
877     // `ref` and `obj`).
878     DCHECK_GT(codegen->GetNumberOfCoreCallerSaveRegisters(), 2u);
879     LOG(FATAL) << "Could not find a free caller-save register";
880     UNREACHABLE();
881   }
882 
883   const Location out_;
884   const Location ref_;
885   const Location obj_;
886   const uint32_t offset_;
887   // An additional location containing an index to an array.
888   // Only used for HArrayGet and the UnsafeGetObject &
889   // UnsafeGetObjectVolatile intrinsics.
890   const Location index_;
891 
892   DISALLOW_COPY_AND_ASSIGN(ReadBarrierForHeapReferenceSlowPathMIPS64);
893 };
894 
895 // Slow path generating a read barrier for a GC root.
896 class ReadBarrierForRootSlowPathMIPS64 : public SlowPathCodeMIPS64 {
897  public:
ReadBarrierForRootSlowPathMIPS64(HInstruction * instruction,Location out,Location root)898   ReadBarrierForRootSlowPathMIPS64(HInstruction* instruction, Location out, Location root)
899       : SlowPathCodeMIPS64(instruction), out_(out), root_(root) {
900     DCHECK(kEmitCompilerReadBarrier);
901   }
902 
EmitNativeCode(CodeGenerator * codegen)903   void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
904     LocationSummary* locations = instruction_->GetLocations();
905     DataType::Type type = DataType::Type::kReference;
906     GpuRegister reg_out = out_.AsRegister<GpuRegister>();
907     DCHECK(locations->CanCall());
908     DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(reg_out));
909     DCHECK(instruction_->IsLoadClass() || instruction_->IsLoadString())
910         << "Unexpected instruction in read barrier for GC root slow path: "
911         << instruction_->DebugName();
912 
913     __ Bind(GetEntryLabel());
914     SaveLiveRegisters(codegen, locations);
915 
916     InvokeRuntimeCallingConvention calling_convention;
917     CodeGeneratorMIPS64* mips64_codegen = down_cast<CodeGeneratorMIPS64*>(codegen);
918     mips64_codegen->MoveLocation(Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
919                                  root_,
920                                  DataType::Type::kReference);
921     mips64_codegen->InvokeRuntime(kQuickReadBarrierForRootSlow,
922                                   instruction_,
923                                   instruction_->GetDexPc(),
924                                   this);
925     CheckEntrypointTypes<kQuickReadBarrierForRootSlow, mirror::Object*, GcRoot<mirror::Object>*>();
926     mips64_codegen->MoveLocation(out_, calling_convention.GetReturnLocation(type), type);
927 
928     RestoreLiveRegisters(codegen, locations);
929     __ Bc(GetExitLabel());
930   }
931 
GetDescription() const932   const char* GetDescription() const OVERRIDE { return "ReadBarrierForRootSlowPathMIPS64"; }
933 
934  private:
935   const Location out_;
936   const Location root_;
937 
938   DISALLOW_COPY_AND_ASSIGN(ReadBarrierForRootSlowPathMIPS64);
939 };
940 
CodeGeneratorMIPS64(HGraph * graph,const Mips64InstructionSetFeatures & isa_features,const CompilerOptions & compiler_options,OptimizingCompilerStats * stats)941 CodeGeneratorMIPS64::CodeGeneratorMIPS64(HGraph* graph,
942                                          const Mips64InstructionSetFeatures& isa_features,
943                                          const CompilerOptions& compiler_options,
944                                          OptimizingCompilerStats* stats)
945     : CodeGenerator(graph,
946                     kNumberOfGpuRegisters,
947                     kNumberOfFpuRegisters,
948                     /* number_of_register_pairs */ 0,
949                     ComputeRegisterMask(reinterpret_cast<const int*>(kCoreCalleeSaves),
950                                         arraysize(kCoreCalleeSaves)),
951                     ComputeRegisterMask(reinterpret_cast<const int*>(kFpuCalleeSaves),
952                                         arraysize(kFpuCalleeSaves)),
953                     compiler_options,
954                     stats),
955       block_labels_(nullptr),
956       location_builder_(graph, this),
957       instruction_visitor_(graph, this),
958       move_resolver_(graph->GetAllocator(), this),
959       assembler_(graph->GetAllocator(), &isa_features),
960       isa_features_(isa_features),
961       uint32_literals_(std::less<uint32_t>(),
962                        graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
963       uint64_literals_(std::less<uint64_t>(),
964                        graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
965       boot_image_method_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
966       method_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
967       boot_image_type_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
968       type_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
969       boot_image_string_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
970       string_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
971       jit_string_patches_(StringReferenceValueComparator(),
972                           graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
973       jit_class_patches_(TypeReferenceValueComparator(),
974                          graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)) {
975   // Save RA (containing the return address) to mimic Quick.
976   AddAllocatedRegister(Location::RegisterLocation(RA));
977 }
978 
979 #undef __
980 // NOLINT on __ macro to suppress wrong warning/fix (misc-macro-parentheses) from clang-tidy.
981 #define __ down_cast<Mips64Assembler*>(GetAssembler())->  // NOLINT
982 #define QUICK_ENTRY_POINT(x) QUICK_ENTRYPOINT_OFFSET(kMips64PointerSize, x).Int32Value()
983 
Finalize(CodeAllocator * allocator)984 void CodeGeneratorMIPS64::Finalize(CodeAllocator* allocator) {
985   // Ensure that we fix up branches.
986   __ FinalizeCode();
987 
988   // Adjust native pc offsets in stack maps.
989   StackMapStream* stack_map_stream = GetStackMapStream();
990   for (size_t i = 0, num = stack_map_stream->GetNumberOfStackMaps(); i != num; ++i) {
991     uint32_t old_position =
992         stack_map_stream->GetStackMap(i).native_pc_code_offset.Uint32Value(InstructionSet::kMips64);
993     uint32_t new_position = __ GetAdjustedPosition(old_position);
994     DCHECK_GE(new_position, old_position);
995     stack_map_stream->SetStackMapNativePcOffset(i, new_position);
996   }
997 
998   // Adjust pc offsets for the disassembly information.
999   if (disasm_info_ != nullptr) {
1000     GeneratedCodeInterval* frame_entry_interval = disasm_info_->GetFrameEntryInterval();
1001     frame_entry_interval->start = __ GetAdjustedPosition(frame_entry_interval->start);
1002     frame_entry_interval->end = __ GetAdjustedPosition(frame_entry_interval->end);
1003     for (auto& it : *disasm_info_->GetInstructionIntervals()) {
1004       it.second.start = __ GetAdjustedPosition(it.second.start);
1005       it.second.end = __ GetAdjustedPosition(it.second.end);
1006     }
1007     for (auto& it : *disasm_info_->GetSlowPathIntervals()) {
1008       it.code_interval.start = __ GetAdjustedPosition(it.code_interval.start);
1009       it.code_interval.end = __ GetAdjustedPosition(it.code_interval.end);
1010     }
1011   }
1012 
1013   CodeGenerator::Finalize(allocator);
1014 }
1015 
GetAssembler() const1016 Mips64Assembler* ParallelMoveResolverMIPS64::GetAssembler() const {
1017   return codegen_->GetAssembler();
1018 }
1019 
EmitMove(size_t index)1020 void ParallelMoveResolverMIPS64::EmitMove(size_t index) {
1021   MoveOperands* move = moves_[index];
1022   codegen_->MoveLocation(move->GetDestination(), move->GetSource(), move->GetType());
1023 }
1024 
EmitSwap(size_t index)1025 void ParallelMoveResolverMIPS64::EmitSwap(size_t index) {
1026   MoveOperands* move = moves_[index];
1027   codegen_->SwapLocations(move->GetDestination(), move->GetSource(), move->GetType());
1028 }
1029 
RestoreScratch(int reg)1030 void ParallelMoveResolverMIPS64::RestoreScratch(int reg) {
1031   // Pop reg
1032   __ Ld(GpuRegister(reg), SP, 0);
1033   __ DecreaseFrameSize(kMips64DoublewordSize);
1034 }
1035 
SpillScratch(int reg)1036 void ParallelMoveResolverMIPS64::SpillScratch(int reg) {
1037   // Push reg
1038   __ IncreaseFrameSize(kMips64DoublewordSize);
1039   __ Sd(GpuRegister(reg), SP, 0);
1040 }
1041 
Exchange(int index1,int index2,bool double_slot)1042 void ParallelMoveResolverMIPS64::Exchange(int index1, int index2, bool double_slot) {
1043   LoadOperandType load_type = double_slot ? kLoadDoubleword : kLoadWord;
1044   StoreOperandType store_type = double_slot ? kStoreDoubleword : kStoreWord;
1045   // Allocate a scratch register other than TMP, if available.
1046   // Else, spill V0 (arbitrary choice) and use it as a scratch register (it will be
1047   // automatically unspilled when the scratch scope object is destroyed).
1048   ScratchRegisterScope ensure_scratch(this, TMP, V0, codegen_->GetNumberOfCoreRegisters());
1049   // If V0 spills onto the stack, SP-relative offsets need to be adjusted.
1050   int stack_offset = ensure_scratch.IsSpilled() ? kMips64DoublewordSize : 0;
1051   __ LoadFromOffset(load_type,
1052                     GpuRegister(ensure_scratch.GetRegister()),
1053                     SP,
1054                     index1 + stack_offset);
1055   __ LoadFromOffset(load_type,
1056                     TMP,
1057                     SP,
1058                     index2 + stack_offset);
1059   __ StoreToOffset(store_type,
1060                    GpuRegister(ensure_scratch.GetRegister()),
1061                    SP,
1062                    index2 + stack_offset);
1063   __ StoreToOffset(store_type, TMP, SP, index1 + stack_offset);
1064 }
1065 
ExchangeQuadSlots(int index1,int index2)1066 void ParallelMoveResolverMIPS64::ExchangeQuadSlots(int index1, int index2) {
1067   __ LoadFpuFromOffset(kLoadQuadword, FTMP, SP, index1);
1068   __ LoadFpuFromOffset(kLoadQuadword, FTMP2, SP, index2);
1069   __ StoreFpuToOffset(kStoreQuadword, FTMP, SP, index2);
1070   __ StoreFpuToOffset(kStoreQuadword, FTMP2, SP, index1);
1071 }
1072 
DWARFReg(GpuRegister reg)1073 static dwarf::Reg DWARFReg(GpuRegister reg) {
1074   return dwarf::Reg::Mips64Core(static_cast<int>(reg));
1075 }
1076 
DWARFReg(FpuRegister reg)1077 static dwarf::Reg DWARFReg(FpuRegister reg) {
1078   return dwarf::Reg::Mips64Fp(static_cast<int>(reg));
1079 }
1080 
GenerateFrameEntry()1081 void CodeGeneratorMIPS64::GenerateFrameEntry() {
1082   __ Bind(&frame_entry_label_);
1083 
1084   if (GetCompilerOptions().CountHotnessInCompiledCode()) {
1085     __ Lhu(TMP, kMethodRegisterArgument, ArtMethod::HotnessCountOffset().Int32Value());
1086     __ Addiu(TMP, TMP, 1);
1087     __ Sh(TMP, kMethodRegisterArgument, ArtMethod::HotnessCountOffset().Int32Value());
1088   }
1089 
1090   bool do_overflow_check =
1091       FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kMips64) || !IsLeafMethod();
1092 
1093   if (do_overflow_check) {
1094     __ LoadFromOffset(
1095         kLoadWord,
1096         ZERO,
1097         SP,
1098         -static_cast<int32_t>(GetStackOverflowReservedBytes(InstructionSet::kMips64)));
1099     RecordPcInfo(nullptr, 0);
1100   }
1101 
1102   if (HasEmptyFrame()) {
1103     return;
1104   }
1105 
1106   // Make sure the frame size isn't unreasonably large.
1107   if (GetFrameSize() > GetStackOverflowReservedBytes(InstructionSet::kMips64)) {
1108     LOG(FATAL) << "Stack frame larger than "
1109         << GetStackOverflowReservedBytes(InstructionSet::kMips64) << " bytes";
1110   }
1111 
1112   // Spill callee-saved registers.
1113 
1114   uint32_t ofs = GetFrameSize();
1115   __ IncreaseFrameSize(ofs);
1116 
1117   for (int i = arraysize(kCoreCalleeSaves) - 1; i >= 0; --i) {
1118     GpuRegister reg = kCoreCalleeSaves[i];
1119     if (allocated_registers_.ContainsCoreRegister(reg)) {
1120       ofs -= kMips64DoublewordSize;
1121       __ StoreToOffset(kStoreDoubleword, reg, SP, ofs);
1122       __ cfi().RelOffset(DWARFReg(reg), ofs);
1123     }
1124   }
1125 
1126   for (int i = arraysize(kFpuCalleeSaves) - 1; i >= 0; --i) {
1127     FpuRegister reg = kFpuCalleeSaves[i];
1128     if (allocated_registers_.ContainsFloatingPointRegister(reg)) {
1129       ofs -= kMips64DoublewordSize;
1130       __ StoreFpuToOffset(kStoreDoubleword, reg, SP, ofs);
1131       __ cfi().RelOffset(DWARFReg(reg), ofs);
1132     }
1133   }
1134 
1135   // Save the current method if we need it. Note that we do not
1136   // do this in HCurrentMethod, as the instruction might have been removed
1137   // in the SSA graph.
1138   if (RequiresCurrentMethod()) {
1139     __ StoreToOffset(kStoreDoubleword, kMethodRegisterArgument, SP, kCurrentMethodStackOffset);
1140   }
1141 
1142   if (GetGraph()->HasShouldDeoptimizeFlag()) {
1143     // Initialize should_deoptimize flag to 0.
1144     __ StoreToOffset(kStoreWord, ZERO, SP, GetStackOffsetOfShouldDeoptimizeFlag());
1145   }
1146 }
1147 
GenerateFrameExit()1148 void CodeGeneratorMIPS64::GenerateFrameExit() {
1149   __ cfi().RememberState();
1150 
1151   if (!HasEmptyFrame()) {
1152     // Restore callee-saved registers.
1153 
1154     // For better instruction scheduling restore RA before other registers.
1155     uint32_t ofs = GetFrameSize();
1156     for (int i = arraysize(kCoreCalleeSaves) - 1; i >= 0; --i) {
1157       GpuRegister reg = kCoreCalleeSaves[i];
1158       if (allocated_registers_.ContainsCoreRegister(reg)) {
1159         ofs -= kMips64DoublewordSize;
1160         __ LoadFromOffset(kLoadDoubleword, reg, SP, ofs);
1161         __ cfi().Restore(DWARFReg(reg));
1162       }
1163     }
1164 
1165     for (int i = arraysize(kFpuCalleeSaves) - 1; i >= 0; --i) {
1166       FpuRegister reg = kFpuCalleeSaves[i];
1167       if (allocated_registers_.ContainsFloatingPointRegister(reg)) {
1168         ofs -= kMips64DoublewordSize;
1169         __ LoadFpuFromOffset(kLoadDoubleword, reg, SP, ofs);
1170         __ cfi().Restore(DWARFReg(reg));
1171       }
1172     }
1173 
1174     __ DecreaseFrameSize(GetFrameSize());
1175   }
1176 
1177   __ Jic(RA, 0);
1178 
1179   __ cfi().RestoreState();
1180   __ cfi().DefCFAOffset(GetFrameSize());
1181 }
1182 
Bind(HBasicBlock * block)1183 void CodeGeneratorMIPS64::Bind(HBasicBlock* block) {
1184   __ Bind(GetLabelOf(block));
1185 }
1186 
MoveLocation(Location destination,Location source,DataType::Type dst_type)1187 void CodeGeneratorMIPS64::MoveLocation(Location destination,
1188                                        Location source,
1189                                        DataType::Type dst_type) {
1190   if (source.Equals(destination)) {
1191     return;
1192   }
1193 
1194   // A valid move can always be inferred from the destination and source
1195   // locations. When moving from and to a register, the argument type can be
1196   // used to generate 32bit instead of 64bit moves.
1197   bool unspecified_type = (dst_type == DataType::Type::kVoid);
1198   DCHECK_EQ(unspecified_type, false);
1199 
1200   if (destination.IsRegister() || destination.IsFpuRegister()) {
1201     if (unspecified_type) {
1202       HConstant* src_cst = source.IsConstant() ? source.GetConstant() : nullptr;
1203       if (source.IsStackSlot() ||
1204           (src_cst != nullptr && (src_cst->IsIntConstant()
1205                                   || src_cst->IsFloatConstant()
1206                                   || src_cst->IsNullConstant()))) {
1207         // For stack slots and 32bit constants, a 64bit type is appropriate.
1208         dst_type = destination.IsRegister() ? DataType::Type::kInt32 : DataType::Type::kFloat32;
1209       } else {
1210         // If the source is a double stack slot or a 64bit constant, a 64bit
1211         // type is appropriate. Else the source is a register, and since the
1212         // type has not been specified, we chose a 64bit type to force a 64bit
1213         // move.
1214         dst_type = destination.IsRegister() ? DataType::Type::kInt64 : DataType::Type::kFloat64;
1215       }
1216     }
1217     DCHECK((destination.IsFpuRegister() && DataType::IsFloatingPointType(dst_type)) ||
1218            (destination.IsRegister() && !DataType::IsFloatingPointType(dst_type)));
1219     if (source.IsStackSlot() || source.IsDoubleStackSlot()) {
1220       // Move to GPR/FPR from stack
1221       LoadOperandType load_type = source.IsStackSlot() ? kLoadWord : kLoadDoubleword;
1222       if (DataType::IsFloatingPointType(dst_type)) {
1223         __ LoadFpuFromOffset(load_type,
1224                              destination.AsFpuRegister<FpuRegister>(),
1225                              SP,
1226                              source.GetStackIndex());
1227       } else {
1228         // TODO: use load_type = kLoadUnsignedWord when type == DataType::Type::kReference.
1229         __ LoadFromOffset(load_type,
1230                           destination.AsRegister<GpuRegister>(),
1231                           SP,
1232                           source.GetStackIndex());
1233       }
1234     } else if (source.IsSIMDStackSlot()) {
1235       __ LoadFpuFromOffset(kLoadQuadword,
1236                            destination.AsFpuRegister<FpuRegister>(),
1237                            SP,
1238                            source.GetStackIndex());
1239     } else if (source.IsConstant()) {
1240       // Move to GPR/FPR from constant
1241       GpuRegister gpr = AT;
1242       if (!DataType::IsFloatingPointType(dst_type)) {
1243         gpr = destination.AsRegister<GpuRegister>();
1244       }
1245       if (dst_type == DataType::Type::kInt32 || dst_type == DataType::Type::kFloat32) {
1246         int32_t value = GetInt32ValueOf(source.GetConstant()->AsConstant());
1247         if (DataType::IsFloatingPointType(dst_type) && value == 0) {
1248           gpr = ZERO;
1249         } else {
1250           __ LoadConst32(gpr, value);
1251         }
1252       } else {
1253         int64_t value = GetInt64ValueOf(source.GetConstant()->AsConstant());
1254         if (DataType::IsFloatingPointType(dst_type) && value == 0) {
1255           gpr = ZERO;
1256         } else {
1257           __ LoadConst64(gpr, value);
1258         }
1259       }
1260       if (dst_type == DataType::Type::kFloat32) {
1261         __ Mtc1(gpr, destination.AsFpuRegister<FpuRegister>());
1262       } else if (dst_type == DataType::Type::kFloat64) {
1263         __ Dmtc1(gpr, destination.AsFpuRegister<FpuRegister>());
1264       }
1265     } else if (source.IsRegister()) {
1266       if (destination.IsRegister()) {
1267         // Move to GPR from GPR
1268         __ Move(destination.AsRegister<GpuRegister>(), source.AsRegister<GpuRegister>());
1269       } else {
1270         DCHECK(destination.IsFpuRegister());
1271         if (DataType::Is64BitType(dst_type)) {
1272           __ Dmtc1(source.AsRegister<GpuRegister>(), destination.AsFpuRegister<FpuRegister>());
1273         } else {
1274           __ Mtc1(source.AsRegister<GpuRegister>(), destination.AsFpuRegister<FpuRegister>());
1275         }
1276       }
1277     } else if (source.IsFpuRegister()) {
1278       if (destination.IsFpuRegister()) {
1279         if (GetGraph()->HasSIMD()) {
1280           __ MoveV(VectorRegisterFrom(destination),
1281                    VectorRegisterFrom(source));
1282         } else {
1283           // Move to FPR from FPR
1284           if (dst_type == DataType::Type::kFloat32) {
1285             __ MovS(destination.AsFpuRegister<FpuRegister>(), source.AsFpuRegister<FpuRegister>());
1286           } else {
1287             DCHECK_EQ(dst_type, DataType::Type::kFloat64);
1288             __ MovD(destination.AsFpuRegister<FpuRegister>(), source.AsFpuRegister<FpuRegister>());
1289           }
1290         }
1291       } else {
1292         DCHECK(destination.IsRegister());
1293         if (DataType::Is64BitType(dst_type)) {
1294           __ Dmfc1(destination.AsRegister<GpuRegister>(), source.AsFpuRegister<FpuRegister>());
1295         } else {
1296           __ Mfc1(destination.AsRegister<GpuRegister>(), source.AsFpuRegister<FpuRegister>());
1297         }
1298       }
1299     }
1300   } else if (destination.IsSIMDStackSlot()) {
1301     if (source.IsFpuRegister()) {
1302       __ StoreFpuToOffset(kStoreQuadword,
1303                           source.AsFpuRegister<FpuRegister>(),
1304                           SP,
1305                           destination.GetStackIndex());
1306     } else {
1307       DCHECK(source.IsSIMDStackSlot());
1308       __ LoadFpuFromOffset(kLoadQuadword,
1309                            FTMP,
1310                            SP,
1311                            source.GetStackIndex());
1312       __ StoreFpuToOffset(kStoreQuadword,
1313                           FTMP,
1314                           SP,
1315                           destination.GetStackIndex());
1316     }
1317   } else {  // The destination is not a register. It must be a stack slot.
1318     DCHECK(destination.IsStackSlot() || destination.IsDoubleStackSlot());
1319     if (source.IsRegister() || source.IsFpuRegister()) {
1320       if (unspecified_type) {
1321         if (source.IsRegister()) {
1322           dst_type = destination.IsStackSlot() ? DataType::Type::kInt32 : DataType::Type::kInt64;
1323         } else {
1324           dst_type =
1325               destination.IsStackSlot() ? DataType::Type::kFloat32 : DataType::Type::kFloat64;
1326         }
1327       }
1328       DCHECK((destination.IsDoubleStackSlot() == DataType::Is64BitType(dst_type)) &&
1329              (source.IsFpuRegister() == DataType::IsFloatingPointType(dst_type)));
1330       // Move to stack from GPR/FPR
1331       StoreOperandType store_type = destination.IsStackSlot() ? kStoreWord : kStoreDoubleword;
1332       if (source.IsRegister()) {
1333         __ StoreToOffset(store_type,
1334                          source.AsRegister<GpuRegister>(),
1335                          SP,
1336                          destination.GetStackIndex());
1337       } else {
1338         __ StoreFpuToOffset(store_type,
1339                             source.AsFpuRegister<FpuRegister>(),
1340                             SP,
1341                             destination.GetStackIndex());
1342       }
1343     } else if (source.IsConstant()) {
1344       // Move to stack from constant
1345       HConstant* src_cst = source.GetConstant();
1346       StoreOperandType store_type = destination.IsStackSlot() ? kStoreWord : kStoreDoubleword;
1347       GpuRegister gpr = ZERO;
1348       if (destination.IsStackSlot()) {
1349         int32_t value = GetInt32ValueOf(src_cst->AsConstant());
1350         if (value != 0) {
1351           gpr = TMP;
1352           __ LoadConst32(gpr, value);
1353         }
1354       } else {
1355         DCHECK(destination.IsDoubleStackSlot());
1356         int64_t value = GetInt64ValueOf(src_cst->AsConstant());
1357         if (value != 0) {
1358           gpr = TMP;
1359           __ LoadConst64(gpr, value);
1360         }
1361       }
1362       __ StoreToOffset(store_type, gpr, SP, destination.GetStackIndex());
1363     } else {
1364       DCHECK(source.IsStackSlot() || source.IsDoubleStackSlot());
1365       DCHECK_EQ(source.IsDoubleStackSlot(), destination.IsDoubleStackSlot());
1366       // Move to stack from stack
1367       if (destination.IsStackSlot()) {
1368         __ LoadFromOffset(kLoadWord, TMP, SP, source.GetStackIndex());
1369         __ StoreToOffset(kStoreWord, TMP, SP, destination.GetStackIndex());
1370       } else {
1371         __ LoadFromOffset(kLoadDoubleword, TMP, SP, source.GetStackIndex());
1372         __ StoreToOffset(kStoreDoubleword, TMP, SP, destination.GetStackIndex());
1373       }
1374     }
1375   }
1376 }
1377 
SwapLocations(Location loc1,Location loc2,DataType::Type type)1378 void CodeGeneratorMIPS64::SwapLocations(Location loc1, Location loc2, DataType::Type type) {
1379   DCHECK(!loc1.IsConstant());
1380   DCHECK(!loc2.IsConstant());
1381 
1382   if (loc1.Equals(loc2)) {
1383     return;
1384   }
1385 
1386   bool is_slot1 = loc1.IsStackSlot() || loc1.IsDoubleStackSlot();
1387   bool is_slot2 = loc2.IsStackSlot() || loc2.IsDoubleStackSlot();
1388   bool is_simd1 = loc1.IsSIMDStackSlot();
1389   bool is_simd2 = loc2.IsSIMDStackSlot();
1390   bool is_fp_reg1 = loc1.IsFpuRegister();
1391   bool is_fp_reg2 = loc2.IsFpuRegister();
1392 
1393   if (loc2.IsRegister() && loc1.IsRegister()) {
1394     // Swap 2 GPRs
1395     GpuRegister r1 = loc1.AsRegister<GpuRegister>();
1396     GpuRegister r2 = loc2.AsRegister<GpuRegister>();
1397     __ Move(TMP, r2);
1398     __ Move(r2, r1);
1399     __ Move(r1, TMP);
1400   } else if (is_fp_reg2 && is_fp_reg1) {
1401     // Swap 2 FPRs
1402     if (GetGraph()->HasSIMD()) {
1403       __ MoveV(static_cast<VectorRegister>(FTMP), VectorRegisterFrom(loc1));
1404       __ MoveV(VectorRegisterFrom(loc1), VectorRegisterFrom(loc2));
1405       __ MoveV(VectorRegisterFrom(loc2), static_cast<VectorRegister>(FTMP));
1406     } else {
1407       FpuRegister r1 = loc1.AsFpuRegister<FpuRegister>();
1408       FpuRegister r2 = loc2.AsFpuRegister<FpuRegister>();
1409       if (type == DataType::Type::kFloat32) {
1410         __ MovS(FTMP, r1);
1411         __ MovS(r1, r2);
1412         __ MovS(r2, FTMP);
1413       } else {
1414         DCHECK_EQ(type, DataType::Type::kFloat64);
1415         __ MovD(FTMP, r1);
1416         __ MovD(r1, r2);
1417         __ MovD(r2, FTMP);
1418       }
1419     }
1420   } else if (is_slot1 != is_slot2) {
1421     // Swap GPR/FPR and stack slot
1422     Location reg_loc = is_slot1 ? loc2 : loc1;
1423     Location mem_loc = is_slot1 ? loc1 : loc2;
1424     LoadOperandType load_type = mem_loc.IsStackSlot() ? kLoadWord : kLoadDoubleword;
1425     StoreOperandType store_type = mem_loc.IsStackSlot() ? kStoreWord : kStoreDoubleword;
1426     // TODO: use load_type = kLoadUnsignedWord when type == DataType::Type::kReference.
1427     __ LoadFromOffset(load_type, TMP, SP, mem_loc.GetStackIndex());
1428     if (reg_loc.IsFpuRegister()) {
1429       __ StoreFpuToOffset(store_type,
1430                           reg_loc.AsFpuRegister<FpuRegister>(),
1431                           SP,
1432                           mem_loc.GetStackIndex());
1433       if (mem_loc.IsStackSlot()) {
1434         __ Mtc1(TMP, reg_loc.AsFpuRegister<FpuRegister>());
1435       } else {
1436         DCHECK(mem_loc.IsDoubleStackSlot());
1437         __ Dmtc1(TMP, reg_loc.AsFpuRegister<FpuRegister>());
1438       }
1439     } else {
1440       __ StoreToOffset(store_type, reg_loc.AsRegister<GpuRegister>(), SP, mem_loc.GetStackIndex());
1441       __ Move(reg_loc.AsRegister<GpuRegister>(), TMP);
1442     }
1443   } else if (is_slot1 && is_slot2) {
1444     move_resolver_.Exchange(loc1.GetStackIndex(),
1445                             loc2.GetStackIndex(),
1446                             loc1.IsDoubleStackSlot());
1447   } else if (is_simd1 && is_simd2) {
1448     move_resolver_.ExchangeQuadSlots(loc1.GetStackIndex(), loc2.GetStackIndex());
1449   } else if ((is_fp_reg1 && is_simd2) || (is_fp_reg2 && is_simd1)) {
1450     Location fp_reg_loc = is_fp_reg1 ? loc1 : loc2;
1451     Location mem_loc = is_fp_reg1 ? loc2 : loc1;
1452     __ LoadFpuFromOffset(kLoadQuadword, FTMP, SP, mem_loc.GetStackIndex());
1453     __ StoreFpuToOffset(kStoreQuadword,
1454                         fp_reg_loc.AsFpuRegister<FpuRegister>(),
1455                         SP,
1456                         mem_loc.GetStackIndex());
1457     __ MoveV(VectorRegisterFrom(fp_reg_loc), static_cast<VectorRegister>(FTMP));
1458   } else {
1459     LOG(FATAL) << "Unimplemented swap between locations " << loc1 << " and " << loc2;
1460   }
1461 }
1462 
MoveConstant(Location location,int32_t value)1463 void CodeGeneratorMIPS64::MoveConstant(Location location, int32_t value) {
1464   DCHECK(location.IsRegister());
1465   __ LoadConst32(location.AsRegister<GpuRegister>(), value);
1466 }
1467 
AddLocationAsTemp(Location location,LocationSummary * locations)1468 void CodeGeneratorMIPS64::AddLocationAsTemp(Location location, LocationSummary* locations) {
1469   if (location.IsRegister()) {
1470     locations->AddTemp(location);
1471   } else {
1472     UNIMPLEMENTED(FATAL) << "AddLocationAsTemp not implemented for location " << location;
1473   }
1474 }
1475 
MarkGCCard(GpuRegister object,GpuRegister value,bool value_can_be_null)1476 void CodeGeneratorMIPS64::MarkGCCard(GpuRegister object,
1477                                      GpuRegister value,
1478                                      bool value_can_be_null) {
1479   Mips64Label done;
1480   GpuRegister card = AT;
1481   GpuRegister temp = TMP;
1482   if (value_can_be_null) {
1483     __ Beqzc(value, &done);
1484   }
1485   __ LoadFromOffset(kLoadDoubleword,
1486                     card,
1487                     TR,
1488                     Thread::CardTableOffset<kMips64PointerSize>().Int32Value());
1489   __ Dsrl(temp, object, gc::accounting::CardTable::kCardShift);
1490   __ Daddu(temp, card, temp);
1491   __ Sb(card, temp, 0);
1492   if (value_can_be_null) {
1493     __ Bind(&done);
1494   }
1495 }
1496 
1497 template <linker::LinkerPatch (*Factory)(size_t, const DexFile*, uint32_t, uint32_t)>
EmitPcRelativeLinkerPatches(const ArenaDeque<PcRelativePatchInfo> & infos,ArenaVector<linker::LinkerPatch> * linker_patches)1498 inline void CodeGeneratorMIPS64::EmitPcRelativeLinkerPatches(
1499     const ArenaDeque<PcRelativePatchInfo>& infos,
1500     ArenaVector<linker::LinkerPatch>* linker_patches) {
1501   for (const PcRelativePatchInfo& info : infos) {
1502     const DexFile* dex_file = info.target_dex_file;
1503     size_t offset_or_index = info.offset_or_index;
1504     DCHECK(info.label.IsBound());
1505     uint32_t literal_offset = __ GetLabelLocation(&info.label);
1506     const PcRelativePatchInfo& info_high = info.patch_info_high ? *info.patch_info_high : info;
1507     uint32_t pc_rel_offset = __ GetLabelLocation(&info_high.label);
1508     linker_patches->push_back(Factory(literal_offset, dex_file, pc_rel_offset, offset_or_index));
1509   }
1510 }
1511 
EmitLinkerPatches(ArenaVector<linker::LinkerPatch> * linker_patches)1512 void CodeGeneratorMIPS64::EmitLinkerPatches(ArenaVector<linker::LinkerPatch>* linker_patches) {
1513   DCHECK(linker_patches->empty());
1514   size_t size =
1515       boot_image_method_patches_.size() +
1516       method_bss_entry_patches_.size() +
1517       boot_image_type_patches_.size() +
1518       type_bss_entry_patches_.size() +
1519       boot_image_string_patches_.size() +
1520       string_bss_entry_patches_.size();
1521   linker_patches->reserve(size);
1522   if (GetCompilerOptions().IsBootImage()) {
1523     EmitPcRelativeLinkerPatches<linker::LinkerPatch::RelativeMethodPatch>(
1524         boot_image_method_patches_, linker_patches);
1525     EmitPcRelativeLinkerPatches<linker::LinkerPatch::RelativeTypePatch>(
1526         boot_image_type_patches_, linker_patches);
1527     EmitPcRelativeLinkerPatches<linker::LinkerPatch::RelativeStringPatch>(
1528         boot_image_string_patches_, linker_patches);
1529   } else {
1530     DCHECK(boot_image_method_patches_.empty());
1531     EmitPcRelativeLinkerPatches<linker::LinkerPatch::TypeClassTablePatch>(
1532         boot_image_type_patches_, linker_patches);
1533     EmitPcRelativeLinkerPatches<linker::LinkerPatch::StringInternTablePatch>(
1534         boot_image_string_patches_, linker_patches);
1535   }
1536   EmitPcRelativeLinkerPatches<linker::LinkerPatch::MethodBssEntryPatch>(
1537       method_bss_entry_patches_, linker_patches);
1538   EmitPcRelativeLinkerPatches<linker::LinkerPatch::TypeBssEntryPatch>(
1539       type_bss_entry_patches_, linker_patches);
1540   EmitPcRelativeLinkerPatches<linker::LinkerPatch::StringBssEntryPatch>(
1541       string_bss_entry_patches_, linker_patches);
1542   DCHECK_EQ(size, linker_patches->size());
1543 }
1544 
NewBootImageMethodPatch(MethodReference target_method,const PcRelativePatchInfo * info_high)1545 CodeGeneratorMIPS64::PcRelativePatchInfo* CodeGeneratorMIPS64::NewBootImageMethodPatch(
1546     MethodReference target_method,
1547     const PcRelativePatchInfo* info_high) {
1548   return NewPcRelativePatch(
1549       target_method.dex_file, target_method.index, info_high, &boot_image_method_patches_);
1550 }
1551 
NewMethodBssEntryPatch(MethodReference target_method,const PcRelativePatchInfo * info_high)1552 CodeGeneratorMIPS64::PcRelativePatchInfo* CodeGeneratorMIPS64::NewMethodBssEntryPatch(
1553     MethodReference target_method,
1554     const PcRelativePatchInfo* info_high) {
1555   return NewPcRelativePatch(
1556       target_method.dex_file, target_method.index, info_high, &method_bss_entry_patches_);
1557 }
1558 
NewBootImageTypePatch(const DexFile & dex_file,dex::TypeIndex type_index,const PcRelativePatchInfo * info_high)1559 CodeGeneratorMIPS64::PcRelativePatchInfo* CodeGeneratorMIPS64::NewBootImageTypePatch(
1560     const DexFile& dex_file,
1561     dex::TypeIndex type_index,
1562     const PcRelativePatchInfo* info_high) {
1563   return NewPcRelativePatch(&dex_file, type_index.index_, info_high, &boot_image_type_patches_);
1564 }
1565 
NewTypeBssEntryPatch(const DexFile & dex_file,dex::TypeIndex type_index,const PcRelativePatchInfo * info_high)1566 CodeGeneratorMIPS64::PcRelativePatchInfo* CodeGeneratorMIPS64::NewTypeBssEntryPatch(
1567     const DexFile& dex_file,
1568     dex::TypeIndex type_index,
1569     const PcRelativePatchInfo* info_high) {
1570   return NewPcRelativePatch(&dex_file, type_index.index_, info_high, &type_bss_entry_patches_);
1571 }
1572 
NewBootImageStringPatch(const DexFile & dex_file,dex::StringIndex string_index,const PcRelativePatchInfo * info_high)1573 CodeGeneratorMIPS64::PcRelativePatchInfo* CodeGeneratorMIPS64::NewBootImageStringPatch(
1574     const DexFile& dex_file,
1575     dex::StringIndex string_index,
1576     const PcRelativePatchInfo* info_high) {
1577   return NewPcRelativePatch(
1578       &dex_file, string_index.index_, info_high, &boot_image_string_patches_);
1579 }
1580 
NewStringBssEntryPatch(const DexFile & dex_file,dex::StringIndex string_index,const PcRelativePatchInfo * info_high)1581 CodeGeneratorMIPS64::PcRelativePatchInfo* CodeGeneratorMIPS64::NewStringBssEntryPatch(
1582     const DexFile& dex_file,
1583     dex::StringIndex string_index,
1584     const PcRelativePatchInfo* info_high) {
1585   return NewPcRelativePatch(&dex_file, string_index.index_, info_high, &string_bss_entry_patches_);
1586 }
1587 
NewPcRelativePatch(const DexFile * dex_file,uint32_t offset_or_index,const PcRelativePatchInfo * info_high,ArenaDeque<PcRelativePatchInfo> * patches)1588 CodeGeneratorMIPS64::PcRelativePatchInfo* CodeGeneratorMIPS64::NewPcRelativePatch(
1589     const DexFile* dex_file,
1590     uint32_t offset_or_index,
1591     const PcRelativePatchInfo* info_high,
1592     ArenaDeque<PcRelativePatchInfo>* patches) {
1593   patches->emplace_back(dex_file, offset_or_index, info_high);
1594   return &patches->back();
1595 }
1596 
DeduplicateUint32Literal(uint32_t value,Uint32ToLiteralMap * map)1597 Literal* CodeGeneratorMIPS64::DeduplicateUint32Literal(uint32_t value, Uint32ToLiteralMap* map) {
1598   return map->GetOrCreate(
1599       value,
1600       [this, value]() { return __ NewLiteral<uint32_t>(value); });
1601 }
1602 
DeduplicateUint64Literal(uint64_t value)1603 Literal* CodeGeneratorMIPS64::DeduplicateUint64Literal(uint64_t value) {
1604   return uint64_literals_.GetOrCreate(
1605       value,
1606       [this, value]() { return __ NewLiteral<uint64_t>(value); });
1607 }
1608 
DeduplicateBootImageAddressLiteral(uint64_t address)1609 Literal* CodeGeneratorMIPS64::DeduplicateBootImageAddressLiteral(uint64_t address) {
1610   return DeduplicateUint32Literal(dchecked_integral_cast<uint32_t>(address), &uint32_literals_);
1611 }
1612 
EmitPcRelativeAddressPlaceholderHigh(PcRelativePatchInfo * info_high,GpuRegister out,PcRelativePatchInfo * info_low)1613 void CodeGeneratorMIPS64::EmitPcRelativeAddressPlaceholderHigh(PcRelativePatchInfo* info_high,
1614                                                                GpuRegister out,
1615                                                                PcRelativePatchInfo* info_low) {
1616   DCHECK(!info_high->patch_info_high);
1617   __ Bind(&info_high->label);
1618   // Add the high half of a 32-bit offset to PC.
1619   __ Auipc(out, /* placeholder */ 0x1234);
1620   // A following instruction will add the sign-extended low half of the 32-bit
1621   // offset to `out` (e.g. ld, jialc, daddiu).
1622   if (info_low != nullptr) {
1623     DCHECK_EQ(info_low->patch_info_high, info_high);
1624     __ Bind(&info_low->label);
1625   }
1626 }
1627 
DeduplicateJitStringLiteral(const DexFile & dex_file,dex::StringIndex string_index,Handle<mirror::String> handle)1628 Literal* CodeGeneratorMIPS64::DeduplicateJitStringLiteral(const DexFile& dex_file,
1629                                                           dex::StringIndex string_index,
1630                                                           Handle<mirror::String> handle) {
1631   ReserveJitStringRoot(StringReference(&dex_file, string_index), handle);
1632   return jit_string_patches_.GetOrCreate(
1633       StringReference(&dex_file, string_index),
1634       [this]() { return __ NewLiteral<uint32_t>(/* placeholder */ 0u); });
1635 }
1636 
DeduplicateJitClassLiteral(const DexFile & dex_file,dex::TypeIndex type_index,Handle<mirror::Class> handle)1637 Literal* CodeGeneratorMIPS64::DeduplicateJitClassLiteral(const DexFile& dex_file,
1638                                                          dex::TypeIndex type_index,
1639                                                          Handle<mirror::Class> handle) {
1640   ReserveJitClassRoot(TypeReference(&dex_file, type_index), handle);
1641   return jit_class_patches_.GetOrCreate(
1642       TypeReference(&dex_file, type_index),
1643       [this]() { return __ NewLiteral<uint32_t>(/* placeholder */ 0u); });
1644 }
1645 
PatchJitRootUse(uint8_t * code,const uint8_t * roots_data,const Literal * literal,uint64_t index_in_table) const1646 void CodeGeneratorMIPS64::PatchJitRootUse(uint8_t* code,
1647                                           const uint8_t* roots_data,
1648                                           const Literal* literal,
1649                                           uint64_t index_in_table) const {
1650   uint32_t literal_offset = GetAssembler().GetLabelLocation(literal->GetLabel());
1651   uintptr_t address =
1652       reinterpret_cast<uintptr_t>(roots_data) + index_in_table * sizeof(GcRoot<mirror::Object>);
1653   reinterpret_cast<uint32_t*>(code + literal_offset)[0] = dchecked_integral_cast<uint32_t>(address);
1654 }
1655 
EmitJitRootPatches(uint8_t * code,const uint8_t * roots_data)1656 void CodeGeneratorMIPS64::EmitJitRootPatches(uint8_t* code, const uint8_t* roots_data) {
1657   for (const auto& entry : jit_string_patches_) {
1658     const StringReference& string_reference = entry.first;
1659     Literal* table_entry_literal = entry.second;
1660     uint64_t index_in_table = GetJitStringRootIndex(string_reference);
1661     PatchJitRootUse(code, roots_data, table_entry_literal, index_in_table);
1662   }
1663   for (const auto& entry : jit_class_patches_) {
1664     const TypeReference& type_reference = entry.first;
1665     Literal* table_entry_literal = entry.second;
1666     uint64_t index_in_table = GetJitClassRootIndex(type_reference);
1667     PatchJitRootUse(code, roots_data, table_entry_literal, index_in_table);
1668   }
1669 }
1670 
SetupBlockedRegisters() const1671 void CodeGeneratorMIPS64::SetupBlockedRegisters() const {
1672   // ZERO, K0, K1, GP, SP, RA are always reserved and can't be allocated.
1673   blocked_core_registers_[ZERO] = true;
1674   blocked_core_registers_[K0] = true;
1675   blocked_core_registers_[K1] = true;
1676   blocked_core_registers_[GP] = true;
1677   blocked_core_registers_[SP] = true;
1678   blocked_core_registers_[RA] = true;
1679 
1680   // AT, TMP(T8) and TMP2(T3) are used as temporary/scratch
1681   // registers (similar to how AT is used by MIPS assemblers).
1682   blocked_core_registers_[AT] = true;
1683   blocked_core_registers_[TMP] = true;
1684   blocked_core_registers_[TMP2] = true;
1685   blocked_fpu_registers_[FTMP] = true;
1686 
1687   if (GetInstructionSetFeatures().HasMsa()) {
1688     // To be used just for MSA instructions.
1689     blocked_fpu_registers_[FTMP2] = true;
1690   }
1691 
1692   // Reserve suspend and thread registers.
1693   blocked_core_registers_[S0] = true;
1694   blocked_core_registers_[TR] = true;
1695 
1696   // Reserve T9 for function calls
1697   blocked_core_registers_[T9] = true;
1698 
1699   if (GetGraph()->IsDebuggable()) {
1700     // Stubs do not save callee-save floating point registers. If the graph
1701     // is debuggable, we need to deal with these registers differently. For
1702     // now, just block them.
1703     for (size_t i = 0; i < arraysize(kFpuCalleeSaves); ++i) {
1704       blocked_fpu_registers_[kFpuCalleeSaves[i]] = true;
1705     }
1706   }
1707 }
1708 
SaveCoreRegister(size_t stack_index,uint32_t reg_id)1709 size_t CodeGeneratorMIPS64::SaveCoreRegister(size_t stack_index, uint32_t reg_id) {
1710   __ StoreToOffset(kStoreDoubleword, GpuRegister(reg_id), SP, stack_index);
1711   return kMips64DoublewordSize;
1712 }
1713 
RestoreCoreRegister(size_t stack_index,uint32_t reg_id)1714 size_t CodeGeneratorMIPS64::RestoreCoreRegister(size_t stack_index, uint32_t reg_id) {
1715   __ LoadFromOffset(kLoadDoubleword, GpuRegister(reg_id), SP, stack_index);
1716   return kMips64DoublewordSize;
1717 }
1718 
SaveFloatingPointRegister(size_t stack_index,uint32_t reg_id)1719 size_t CodeGeneratorMIPS64::SaveFloatingPointRegister(size_t stack_index, uint32_t reg_id) {
1720   __ StoreFpuToOffset(GetGraph()->HasSIMD() ? kStoreQuadword : kStoreDoubleword,
1721                       FpuRegister(reg_id),
1722                       SP,
1723                       stack_index);
1724   return GetFloatingPointSpillSlotSize();
1725 }
1726 
RestoreFloatingPointRegister(size_t stack_index,uint32_t reg_id)1727 size_t CodeGeneratorMIPS64::RestoreFloatingPointRegister(size_t stack_index, uint32_t reg_id) {
1728   __ LoadFpuFromOffset(GetGraph()->HasSIMD() ? kLoadQuadword : kLoadDoubleword,
1729                        FpuRegister(reg_id),
1730                        SP,
1731                        stack_index);
1732   return GetFloatingPointSpillSlotSize();
1733 }
1734 
DumpCoreRegister(std::ostream & stream,int reg) const1735 void CodeGeneratorMIPS64::DumpCoreRegister(std::ostream& stream, int reg) const {
1736   stream << GpuRegister(reg);
1737 }
1738 
DumpFloatingPointRegister(std::ostream & stream,int reg) const1739 void CodeGeneratorMIPS64::DumpFloatingPointRegister(std::ostream& stream, int reg) const {
1740   stream << FpuRegister(reg);
1741 }
1742 
InvokeRuntime(QuickEntrypointEnum entrypoint,HInstruction * instruction,uint32_t dex_pc,SlowPathCode * slow_path)1743 void CodeGeneratorMIPS64::InvokeRuntime(QuickEntrypointEnum entrypoint,
1744                                         HInstruction* instruction,
1745                                         uint32_t dex_pc,
1746                                         SlowPathCode* slow_path) {
1747   ValidateInvokeRuntime(entrypoint, instruction, slow_path);
1748   GenerateInvokeRuntime(GetThreadOffset<kMips64PointerSize>(entrypoint).Int32Value());
1749   if (EntrypointRequiresStackMap(entrypoint)) {
1750     RecordPcInfo(instruction, dex_pc, slow_path);
1751   }
1752 }
1753 
InvokeRuntimeWithoutRecordingPcInfo(int32_t entry_point_offset,HInstruction * instruction,SlowPathCode * slow_path)1754 void CodeGeneratorMIPS64::InvokeRuntimeWithoutRecordingPcInfo(int32_t entry_point_offset,
1755                                                               HInstruction* instruction,
1756                                                               SlowPathCode* slow_path) {
1757   ValidateInvokeRuntimeWithoutRecordingPcInfo(instruction, slow_path);
1758   GenerateInvokeRuntime(entry_point_offset);
1759 }
1760 
GenerateInvokeRuntime(int32_t entry_point_offset)1761 void CodeGeneratorMIPS64::GenerateInvokeRuntime(int32_t entry_point_offset) {
1762   __ LoadFromOffset(kLoadDoubleword, T9, TR, entry_point_offset);
1763   __ Jalr(T9);
1764   __ Nop();
1765 }
1766 
GenerateClassInitializationCheck(SlowPathCodeMIPS64 * slow_path,GpuRegister class_reg)1767 void InstructionCodeGeneratorMIPS64::GenerateClassInitializationCheck(SlowPathCodeMIPS64* slow_path,
1768                                                                       GpuRegister class_reg) {
1769   constexpr size_t status_lsb_position = SubtypeCheckBits::BitStructSizeOf();
1770   const size_t status_byte_offset =
1771       mirror::Class::StatusOffset().SizeValue() + (status_lsb_position / kBitsPerByte);
1772   constexpr uint32_t shifted_initialized_value =
1773       enum_cast<uint32_t>(ClassStatus::kInitialized) << (status_lsb_position % kBitsPerByte);
1774 
1775   __ LoadFromOffset(kLoadUnsignedByte, TMP, class_reg, status_byte_offset);
1776   __ Sltiu(TMP, TMP, shifted_initialized_value);
1777   __ Bnezc(TMP, slow_path->GetEntryLabel());
1778   // Even if the initialized flag is set, we need to ensure consistent memory ordering.
1779   __ Sync(0);
1780   __ Bind(slow_path->GetExitLabel());
1781 }
1782 
GenerateMemoryBarrier(MemBarrierKind kind ATTRIBUTE_UNUSED)1783 void InstructionCodeGeneratorMIPS64::GenerateMemoryBarrier(MemBarrierKind kind ATTRIBUTE_UNUSED) {
1784   __ Sync(0);  // only stype 0 is supported
1785 }
1786 
GenerateSuspendCheck(HSuspendCheck * instruction,HBasicBlock * successor)1787 void InstructionCodeGeneratorMIPS64::GenerateSuspendCheck(HSuspendCheck* instruction,
1788                                                           HBasicBlock* successor) {
1789   SuspendCheckSlowPathMIPS64* slow_path =
1790       down_cast<SuspendCheckSlowPathMIPS64*>(instruction->GetSlowPath());
1791 
1792   if (slow_path == nullptr) {
1793     slow_path =
1794         new (codegen_->GetScopedAllocator()) SuspendCheckSlowPathMIPS64(instruction, successor);
1795     instruction->SetSlowPath(slow_path);
1796     codegen_->AddSlowPath(slow_path);
1797     if (successor != nullptr) {
1798       DCHECK(successor->IsLoopHeader());
1799     }
1800   } else {
1801     DCHECK_EQ(slow_path->GetSuccessor(), successor);
1802   }
1803 
1804   __ LoadFromOffset(kLoadUnsignedHalfword,
1805                     TMP,
1806                     TR,
1807                     Thread::ThreadFlagsOffset<kMips64PointerSize>().Int32Value());
1808   if (successor == nullptr) {
1809     __ Bnezc(TMP, slow_path->GetEntryLabel());
1810     __ Bind(slow_path->GetReturnLabel());
1811   } else {
1812     __ Beqzc(TMP, codegen_->GetLabelOf(successor));
1813     __ Bc(slow_path->GetEntryLabel());
1814     // slow_path will return to GetLabelOf(successor).
1815   }
1816 }
1817 
InstructionCodeGeneratorMIPS64(HGraph * graph,CodeGeneratorMIPS64 * codegen)1818 InstructionCodeGeneratorMIPS64::InstructionCodeGeneratorMIPS64(HGraph* graph,
1819                                                                CodeGeneratorMIPS64* codegen)
1820       : InstructionCodeGenerator(graph, codegen),
1821         assembler_(codegen->GetAssembler()),
1822         codegen_(codegen) {}
1823 
HandleBinaryOp(HBinaryOperation * instruction)1824 void LocationsBuilderMIPS64::HandleBinaryOp(HBinaryOperation* instruction) {
1825   DCHECK_EQ(instruction->InputCount(), 2U);
1826   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
1827   DataType::Type type = instruction->GetResultType();
1828   switch (type) {
1829     case DataType::Type::kInt32:
1830     case DataType::Type::kInt64: {
1831       locations->SetInAt(0, Location::RequiresRegister());
1832       HInstruction* right = instruction->InputAt(1);
1833       bool can_use_imm = false;
1834       if (right->IsConstant()) {
1835         int64_t imm = CodeGenerator::GetInt64ValueOf(right->AsConstant());
1836         if (instruction->IsAnd() || instruction->IsOr() || instruction->IsXor()) {
1837           can_use_imm = IsUint<16>(imm);
1838         } else {
1839           DCHECK(instruction->IsAdd() || instruction->IsSub());
1840           bool single_use = right->GetUses().HasExactlyOneElement();
1841           if (instruction->IsSub()) {
1842             if (!(type == DataType::Type::kInt32 && imm == INT32_MIN)) {
1843               imm = -imm;
1844             }
1845           }
1846           if (type == DataType::Type::kInt32) {
1847             can_use_imm = IsInt<16>(imm) || (Low16Bits(imm) == 0) || single_use;
1848           } else {
1849             can_use_imm = IsInt<16>(imm) || (IsInt<32>(imm) && (Low16Bits(imm) == 0)) || single_use;
1850           }
1851         }
1852       }
1853       if (can_use_imm)
1854         locations->SetInAt(1, Location::ConstantLocation(right->AsConstant()));
1855       else
1856         locations->SetInAt(1, Location::RequiresRegister());
1857       locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
1858       }
1859       break;
1860 
1861     case DataType::Type::kFloat32:
1862     case DataType::Type::kFloat64:
1863       locations->SetInAt(0, Location::RequiresFpuRegister());
1864       locations->SetInAt(1, Location::RequiresFpuRegister());
1865       locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
1866       break;
1867 
1868     default:
1869       LOG(FATAL) << "Unexpected " << instruction->DebugName() << " type " << type;
1870   }
1871 }
1872 
HandleBinaryOp(HBinaryOperation * instruction)1873 void InstructionCodeGeneratorMIPS64::HandleBinaryOp(HBinaryOperation* instruction) {
1874   DataType::Type type = instruction->GetType();
1875   LocationSummary* locations = instruction->GetLocations();
1876 
1877   switch (type) {
1878     case DataType::Type::kInt32:
1879     case DataType::Type::kInt64: {
1880       GpuRegister dst = locations->Out().AsRegister<GpuRegister>();
1881       GpuRegister lhs = locations->InAt(0).AsRegister<GpuRegister>();
1882       Location rhs_location = locations->InAt(1);
1883 
1884       GpuRegister rhs_reg = ZERO;
1885       int64_t rhs_imm = 0;
1886       bool use_imm = rhs_location.IsConstant();
1887       if (use_imm) {
1888         rhs_imm = CodeGenerator::GetInt64ValueOf(rhs_location.GetConstant());
1889       } else {
1890         rhs_reg = rhs_location.AsRegister<GpuRegister>();
1891       }
1892 
1893       if (instruction->IsAnd()) {
1894         if (use_imm)
1895           __ Andi(dst, lhs, rhs_imm);
1896         else
1897           __ And(dst, lhs, rhs_reg);
1898       } else if (instruction->IsOr()) {
1899         if (use_imm)
1900           __ Ori(dst, lhs, rhs_imm);
1901         else
1902           __ Or(dst, lhs, rhs_reg);
1903       } else if (instruction->IsXor()) {
1904         if (use_imm)
1905           __ Xori(dst, lhs, rhs_imm);
1906         else
1907           __ Xor(dst, lhs, rhs_reg);
1908       } else if (instruction->IsAdd() || instruction->IsSub()) {
1909         if (instruction->IsSub()) {
1910           rhs_imm = -rhs_imm;
1911         }
1912         if (type == DataType::Type::kInt32) {
1913           if (use_imm) {
1914             if (IsInt<16>(rhs_imm)) {
1915               __ Addiu(dst, lhs, rhs_imm);
1916             } else {
1917               int16_t rhs_imm_high = High16Bits(rhs_imm);
1918               int16_t rhs_imm_low = Low16Bits(rhs_imm);
1919               if (rhs_imm_low < 0) {
1920                 rhs_imm_high += 1;
1921               }
1922               __ Aui(dst, lhs, rhs_imm_high);
1923               if (rhs_imm_low != 0) {
1924                 __ Addiu(dst, dst, rhs_imm_low);
1925               }
1926             }
1927           } else {
1928             if (instruction->IsAdd()) {
1929               __ Addu(dst, lhs, rhs_reg);
1930             } else {
1931               DCHECK(instruction->IsSub());
1932               __ Subu(dst, lhs, rhs_reg);
1933             }
1934           }
1935         } else {
1936           if (use_imm) {
1937             if (IsInt<16>(rhs_imm)) {
1938               __ Daddiu(dst, lhs, rhs_imm);
1939             } else if (IsInt<32>(rhs_imm)) {
1940               int16_t rhs_imm_high = High16Bits(rhs_imm);
1941               int16_t rhs_imm_low = Low16Bits(rhs_imm);
1942               bool overflow_hi16 = false;
1943               if (rhs_imm_low < 0) {
1944                 rhs_imm_high += 1;
1945                 overflow_hi16 = (rhs_imm_high == -32768);
1946               }
1947               __ Daui(dst, lhs, rhs_imm_high);
1948               if (rhs_imm_low != 0) {
1949                 __ Daddiu(dst, dst, rhs_imm_low);
1950               }
1951               if (overflow_hi16) {
1952                 __ Dahi(dst, 1);
1953               }
1954             } else {
1955               int16_t rhs_imm_low = Low16Bits(Low32Bits(rhs_imm));
1956               if (rhs_imm_low < 0) {
1957                 rhs_imm += (INT64_C(1) << 16);
1958               }
1959               int16_t rhs_imm_upper = High16Bits(Low32Bits(rhs_imm));
1960               if (rhs_imm_upper < 0) {
1961                 rhs_imm += (INT64_C(1) << 32);
1962               }
1963               int16_t rhs_imm_high = Low16Bits(High32Bits(rhs_imm));
1964               if (rhs_imm_high < 0) {
1965                 rhs_imm += (INT64_C(1) << 48);
1966               }
1967               int16_t rhs_imm_top = High16Bits(High32Bits(rhs_imm));
1968               GpuRegister tmp = lhs;
1969               if (rhs_imm_low != 0) {
1970                 __ Daddiu(dst, tmp, rhs_imm_low);
1971                 tmp = dst;
1972               }
1973               // Dahi and Dati must use the same input and output register, so we have to initialize
1974               // the dst register using Daddiu or Daui, even when the intermediate value is zero:
1975               // Daui(dst, lhs, 0).
1976               if ((rhs_imm_upper != 0) || (rhs_imm_low == 0)) {
1977                 __ Daui(dst, tmp, rhs_imm_upper);
1978               }
1979               if (rhs_imm_high != 0) {
1980                 __ Dahi(dst, rhs_imm_high);
1981               }
1982               if (rhs_imm_top != 0) {
1983                 __ Dati(dst, rhs_imm_top);
1984               }
1985             }
1986           } else if (instruction->IsAdd()) {
1987             __ Daddu(dst, lhs, rhs_reg);
1988           } else {
1989             DCHECK(instruction->IsSub());
1990             __ Dsubu(dst, lhs, rhs_reg);
1991           }
1992         }
1993       }
1994       break;
1995     }
1996     case DataType::Type::kFloat32:
1997     case DataType::Type::kFloat64: {
1998       FpuRegister dst = locations->Out().AsFpuRegister<FpuRegister>();
1999       FpuRegister lhs = locations->InAt(0).AsFpuRegister<FpuRegister>();
2000       FpuRegister rhs = locations->InAt(1).AsFpuRegister<FpuRegister>();
2001       if (instruction->IsAdd()) {
2002         if (type == DataType::Type::kFloat32)
2003           __ AddS(dst, lhs, rhs);
2004         else
2005           __ AddD(dst, lhs, rhs);
2006       } else if (instruction->IsSub()) {
2007         if (type == DataType::Type::kFloat32)
2008           __ SubS(dst, lhs, rhs);
2009         else
2010           __ SubD(dst, lhs, rhs);
2011       } else {
2012         LOG(FATAL) << "Unexpected floating-point binary operation";
2013       }
2014       break;
2015     }
2016     default:
2017       LOG(FATAL) << "Unexpected binary operation type " << type;
2018   }
2019 }
2020 
HandleShift(HBinaryOperation * instr)2021 void LocationsBuilderMIPS64::HandleShift(HBinaryOperation* instr) {
2022   DCHECK(instr->IsShl() || instr->IsShr() || instr->IsUShr() || instr->IsRor());
2023 
2024   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instr);
2025   DataType::Type type = instr->GetResultType();
2026   switch (type) {
2027     case DataType::Type::kInt32:
2028     case DataType::Type::kInt64: {
2029       locations->SetInAt(0, Location::RequiresRegister());
2030       locations->SetInAt(1, Location::RegisterOrConstant(instr->InputAt(1)));
2031       locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2032       break;
2033     }
2034     default:
2035       LOG(FATAL) << "Unexpected shift type " << type;
2036   }
2037 }
2038 
HandleShift(HBinaryOperation * instr)2039 void InstructionCodeGeneratorMIPS64::HandleShift(HBinaryOperation* instr) {
2040   DCHECK(instr->IsShl() || instr->IsShr() || instr->IsUShr() || instr->IsRor());
2041   LocationSummary* locations = instr->GetLocations();
2042   DataType::Type type = instr->GetType();
2043 
2044   switch (type) {
2045     case DataType::Type::kInt32:
2046     case DataType::Type::kInt64: {
2047       GpuRegister dst = locations->Out().AsRegister<GpuRegister>();
2048       GpuRegister lhs = locations->InAt(0).AsRegister<GpuRegister>();
2049       Location rhs_location = locations->InAt(1);
2050 
2051       GpuRegister rhs_reg = ZERO;
2052       int64_t rhs_imm = 0;
2053       bool use_imm = rhs_location.IsConstant();
2054       if (use_imm) {
2055         rhs_imm = CodeGenerator::GetInt64ValueOf(rhs_location.GetConstant());
2056       } else {
2057         rhs_reg = rhs_location.AsRegister<GpuRegister>();
2058       }
2059 
2060       if (use_imm) {
2061         uint32_t shift_value = rhs_imm &
2062             (type == DataType::Type::kInt32 ? kMaxIntShiftDistance : kMaxLongShiftDistance);
2063 
2064         if (shift_value == 0) {
2065           if (dst != lhs) {
2066             __ Move(dst, lhs);
2067           }
2068         } else if (type == DataType::Type::kInt32) {
2069           if (instr->IsShl()) {
2070             __ Sll(dst, lhs, shift_value);
2071           } else if (instr->IsShr()) {
2072             __ Sra(dst, lhs, shift_value);
2073           } else if (instr->IsUShr()) {
2074             __ Srl(dst, lhs, shift_value);
2075           } else {
2076             __ Rotr(dst, lhs, shift_value);
2077           }
2078         } else {
2079           if (shift_value < 32) {
2080             if (instr->IsShl()) {
2081               __ Dsll(dst, lhs, shift_value);
2082             } else if (instr->IsShr()) {
2083               __ Dsra(dst, lhs, shift_value);
2084             } else if (instr->IsUShr()) {
2085               __ Dsrl(dst, lhs, shift_value);
2086             } else {
2087               __ Drotr(dst, lhs, shift_value);
2088             }
2089           } else {
2090             shift_value -= 32;
2091             if (instr->IsShl()) {
2092               __ Dsll32(dst, lhs, shift_value);
2093             } else if (instr->IsShr()) {
2094               __ Dsra32(dst, lhs, shift_value);
2095             } else if (instr->IsUShr()) {
2096               __ Dsrl32(dst, lhs, shift_value);
2097             } else {
2098               __ Drotr32(dst, lhs, shift_value);
2099             }
2100           }
2101         }
2102       } else {
2103         if (type == DataType::Type::kInt32) {
2104           if (instr->IsShl()) {
2105             __ Sllv(dst, lhs, rhs_reg);
2106           } else if (instr->IsShr()) {
2107             __ Srav(dst, lhs, rhs_reg);
2108           } else if (instr->IsUShr()) {
2109             __ Srlv(dst, lhs, rhs_reg);
2110           } else {
2111             __ Rotrv(dst, lhs, rhs_reg);
2112           }
2113         } else {
2114           if (instr->IsShl()) {
2115             __ Dsllv(dst, lhs, rhs_reg);
2116           } else if (instr->IsShr()) {
2117             __ Dsrav(dst, lhs, rhs_reg);
2118           } else if (instr->IsUShr()) {
2119             __ Dsrlv(dst, lhs, rhs_reg);
2120           } else {
2121             __ Drotrv(dst, lhs, rhs_reg);
2122           }
2123         }
2124       }
2125       break;
2126     }
2127     default:
2128       LOG(FATAL) << "Unexpected shift operation type " << type;
2129   }
2130 }
2131 
VisitAdd(HAdd * instruction)2132 void LocationsBuilderMIPS64::VisitAdd(HAdd* instruction) {
2133   HandleBinaryOp(instruction);
2134 }
2135 
VisitAdd(HAdd * instruction)2136 void InstructionCodeGeneratorMIPS64::VisitAdd(HAdd* instruction) {
2137   HandleBinaryOp(instruction);
2138 }
2139 
VisitAnd(HAnd * instruction)2140 void LocationsBuilderMIPS64::VisitAnd(HAnd* instruction) {
2141   HandleBinaryOp(instruction);
2142 }
2143 
VisitAnd(HAnd * instruction)2144 void InstructionCodeGeneratorMIPS64::VisitAnd(HAnd* instruction) {
2145   HandleBinaryOp(instruction);
2146 }
2147 
VisitArrayGet(HArrayGet * instruction)2148 void LocationsBuilderMIPS64::VisitArrayGet(HArrayGet* instruction) {
2149   DataType::Type type = instruction->GetType();
2150   bool object_array_get_with_read_barrier =
2151       kEmitCompilerReadBarrier && (type == DataType::Type::kReference);
2152   LocationSummary* locations =
2153       new (GetGraph()->GetAllocator()) LocationSummary(instruction,
2154                                                        object_array_get_with_read_barrier
2155                                                            ? LocationSummary::kCallOnSlowPath
2156                                                            : LocationSummary::kNoCall);
2157   if (object_array_get_with_read_barrier && kUseBakerReadBarrier) {
2158     locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty());  // No caller-save registers.
2159   }
2160   locations->SetInAt(0, Location::RequiresRegister());
2161   locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
2162   if (DataType::IsFloatingPointType(type)) {
2163     locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
2164   } else {
2165     // The output overlaps in the case of an object array get with
2166     // read barriers enabled: we do not want the move to overwrite the
2167     // array's location, as we need it to emit the read barrier.
2168     locations->SetOut(Location::RequiresRegister(),
2169                       object_array_get_with_read_barrier
2170                           ? Location::kOutputOverlap
2171                           : Location::kNoOutputOverlap);
2172   }
2173   // We need a temporary register for the read barrier marking slow
2174   // path in CodeGeneratorMIPS64::GenerateArrayLoadWithBakerReadBarrier.
2175   if (object_array_get_with_read_barrier && kUseBakerReadBarrier) {
2176     bool temp_needed = instruction->GetIndex()->IsConstant()
2177         ? !kBakerReadBarrierThunksEnableForFields
2178         : !kBakerReadBarrierThunksEnableForArrays;
2179     if (temp_needed) {
2180       locations->AddTemp(Location::RequiresRegister());
2181     }
2182   }
2183 }
2184 
GetImplicitNullChecker(HInstruction * instruction,CodeGeneratorMIPS64 * codegen)2185 static auto GetImplicitNullChecker(HInstruction* instruction, CodeGeneratorMIPS64* codegen) {
2186   auto null_checker = [codegen, instruction]() {
2187     codegen->MaybeRecordImplicitNullCheck(instruction);
2188   };
2189   return null_checker;
2190 }
2191 
VisitArrayGet(HArrayGet * instruction)2192 void InstructionCodeGeneratorMIPS64::VisitArrayGet(HArrayGet* instruction) {
2193   LocationSummary* locations = instruction->GetLocations();
2194   Location obj_loc = locations->InAt(0);
2195   GpuRegister obj = obj_loc.AsRegister<GpuRegister>();
2196   Location out_loc = locations->Out();
2197   Location index = locations->InAt(1);
2198   uint32_t data_offset = CodeGenerator::GetArrayDataOffset(instruction);
2199   auto null_checker = GetImplicitNullChecker(instruction, codegen_);
2200 
2201   DataType::Type type = instruction->GetType();
2202   const bool maybe_compressed_char_at = mirror::kUseStringCompression &&
2203                                         instruction->IsStringCharAt();
2204   switch (type) {
2205     case DataType::Type::kBool:
2206     case DataType::Type::kUint8: {
2207       GpuRegister out = out_loc.AsRegister<GpuRegister>();
2208       if (index.IsConstant()) {
2209         size_t offset =
2210             (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset;
2211         __ LoadFromOffset(kLoadUnsignedByte, out, obj, offset, null_checker);
2212       } else {
2213         __ Daddu(TMP, obj, index.AsRegister<GpuRegister>());
2214         __ LoadFromOffset(kLoadUnsignedByte, out, TMP, data_offset, null_checker);
2215       }
2216       break;
2217     }
2218 
2219     case DataType::Type::kInt8: {
2220       GpuRegister out = out_loc.AsRegister<GpuRegister>();
2221       if (index.IsConstant()) {
2222         size_t offset =
2223             (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset;
2224         __ LoadFromOffset(kLoadSignedByte, out, obj, offset, null_checker);
2225       } else {
2226         __ Daddu(TMP, obj, index.AsRegister<GpuRegister>());
2227         __ LoadFromOffset(kLoadSignedByte, out, TMP, data_offset, null_checker);
2228       }
2229       break;
2230     }
2231 
2232     case DataType::Type::kUint16: {
2233       GpuRegister out = out_loc.AsRegister<GpuRegister>();
2234       if (maybe_compressed_char_at) {
2235         uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
2236         __ LoadFromOffset(kLoadWord, TMP, obj, count_offset, null_checker);
2237         __ Dext(TMP, TMP, 0, 1);
2238         static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
2239                       "Expecting 0=compressed, 1=uncompressed");
2240       }
2241       if (index.IsConstant()) {
2242         int32_t const_index = index.GetConstant()->AsIntConstant()->GetValue();
2243         if (maybe_compressed_char_at) {
2244           Mips64Label uncompressed_load, done;
2245           __ Bnezc(TMP, &uncompressed_load);
2246           __ LoadFromOffset(kLoadUnsignedByte,
2247                             out,
2248                             obj,
2249                             data_offset + (const_index << TIMES_1));
2250           __ Bc(&done);
2251           __ Bind(&uncompressed_load);
2252           __ LoadFromOffset(kLoadUnsignedHalfword,
2253                             out,
2254                             obj,
2255                             data_offset + (const_index << TIMES_2));
2256           __ Bind(&done);
2257         } else {
2258           __ LoadFromOffset(kLoadUnsignedHalfword,
2259                             out,
2260                             obj,
2261                             data_offset + (const_index << TIMES_2),
2262                             null_checker);
2263         }
2264       } else {
2265         GpuRegister index_reg = index.AsRegister<GpuRegister>();
2266         if (maybe_compressed_char_at) {
2267           Mips64Label uncompressed_load, done;
2268           __ Bnezc(TMP, &uncompressed_load);
2269           __ Daddu(TMP, obj, index_reg);
2270           __ LoadFromOffset(kLoadUnsignedByte, out, TMP, data_offset);
2271           __ Bc(&done);
2272           __ Bind(&uncompressed_load);
2273           __ Dlsa(TMP, index_reg, obj, TIMES_2);
2274           __ LoadFromOffset(kLoadUnsignedHalfword, out, TMP, data_offset);
2275           __ Bind(&done);
2276         } else {
2277           __ Dlsa(TMP, index_reg, obj, TIMES_2);
2278           __ LoadFromOffset(kLoadUnsignedHalfword, out, TMP, data_offset, null_checker);
2279         }
2280       }
2281       break;
2282     }
2283 
2284     case DataType::Type::kInt16: {
2285       GpuRegister out = out_loc.AsRegister<GpuRegister>();
2286       if (index.IsConstant()) {
2287         size_t offset =
2288             (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset;
2289         __ LoadFromOffset(kLoadSignedHalfword, out, obj, offset, null_checker);
2290       } else {
2291         __ Dlsa(TMP, index.AsRegister<GpuRegister>(), obj, TIMES_2);
2292         __ LoadFromOffset(kLoadSignedHalfword, out, TMP, data_offset, null_checker);
2293       }
2294       break;
2295     }
2296 
2297     case DataType::Type::kInt32: {
2298       DCHECK_EQ(sizeof(mirror::HeapReference<mirror::Object>), sizeof(int32_t));
2299       GpuRegister out = out_loc.AsRegister<GpuRegister>();
2300       LoadOperandType load_type =
2301           (type == DataType::Type::kReference) ? kLoadUnsignedWord : kLoadWord;
2302       if (index.IsConstant()) {
2303         size_t offset =
2304             (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
2305         __ LoadFromOffset(load_type, out, obj, offset, null_checker);
2306       } else {
2307         __ Dlsa(TMP, index.AsRegister<GpuRegister>(), obj, TIMES_4);
2308         __ LoadFromOffset(load_type, out, TMP, data_offset, null_checker);
2309       }
2310       break;
2311     }
2312 
2313     case DataType::Type::kReference: {
2314       static_assert(
2315           sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
2316           "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
2317       // /* HeapReference<Object> */ out =
2318       //     *(obj + data_offset + index * sizeof(HeapReference<Object>))
2319       if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
2320         bool temp_needed = index.IsConstant()
2321             ? !kBakerReadBarrierThunksEnableForFields
2322             : !kBakerReadBarrierThunksEnableForArrays;
2323         Location temp = temp_needed ? locations->GetTemp(0) : Location::NoLocation();
2324         // Note that a potential implicit null check is handled in this
2325         // CodeGeneratorMIPS64::GenerateArrayLoadWithBakerReadBarrier call.
2326         DCHECK(!instruction->CanDoImplicitNullCheckOn(instruction->InputAt(0)));
2327         if (index.IsConstant()) {
2328           // Array load with a constant index can be treated as a field load.
2329           size_t offset =
2330               (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
2331           codegen_->GenerateFieldLoadWithBakerReadBarrier(instruction,
2332                                                           out_loc,
2333                                                           obj,
2334                                                           offset,
2335                                                           temp,
2336                                                           /* needs_null_check */ false);
2337         } else {
2338           codegen_->GenerateArrayLoadWithBakerReadBarrier(instruction,
2339                                                           out_loc,
2340                                                           obj,
2341                                                           data_offset,
2342                                                           index,
2343                                                           temp,
2344                                                           /* needs_null_check */ false);
2345         }
2346       } else {
2347         GpuRegister out = out_loc.AsRegister<GpuRegister>();
2348         if (index.IsConstant()) {
2349           size_t offset =
2350               (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
2351           __ LoadFromOffset(kLoadUnsignedWord, out, obj, offset, null_checker);
2352           // If read barriers are enabled, emit read barriers other than
2353           // Baker's using a slow path (and also unpoison the loaded
2354           // reference, if heap poisoning is enabled).
2355           codegen_->MaybeGenerateReadBarrierSlow(instruction, out_loc, out_loc, obj_loc, offset);
2356         } else {
2357           __ Dlsa(TMP, index.AsRegister<GpuRegister>(), obj, TIMES_4);
2358           __ LoadFromOffset(kLoadUnsignedWord, out, TMP, data_offset, null_checker);
2359           // If read barriers are enabled, emit read barriers other than
2360           // Baker's using a slow path (and also unpoison the loaded
2361           // reference, if heap poisoning is enabled).
2362           codegen_->MaybeGenerateReadBarrierSlow(instruction,
2363                                                  out_loc,
2364                                                  out_loc,
2365                                                  obj_loc,
2366                                                  data_offset,
2367                                                  index);
2368         }
2369       }
2370       break;
2371     }
2372 
2373     case DataType::Type::kInt64: {
2374       GpuRegister out = out_loc.AsRegister<GpuRegister>();
2375       if (index.IsConstant()) {
2376         size_t offset =
2377             (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
2378         __ LoadFromOffset(kLoadDoubleword, out, obj, offset, null_checker);
2379       } else {
2380         __ Dlsa(TMP, index.AsRegister<GpuRegister>(), obj, TIMES_8);
2381         __ LoadFromOffset(kLoadDoubleword, out, TMP, data_offset, null_checker);
2382       }
2383       break;
2384     }
2385 
2386     case DataType::Type::kFloat32: {
2387       FpuRegister out = out_loc.AsFpuRegister<FpuRegister>();
2388       if (index.IsConstant()) {
2389         size_t offset =
2390             (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
2391         __ LoadFpuFromOffset(kLoadWord, out, obj, offset, null_checker);
2392       } else {
2393         __ Dlsa(TMP, index.AsRegister<GpuRegister>(), obj, TIMES_4);
2394         __ LoadFpuFromOffset(kLoadWord, out, TMP, data_offset, null_checker);
2395       }
2396       break;
2397     }
2398 
2399     case DataType::Type::kFloat64: {
2400       FpuRegister out = out_loc.AsFpuRegister<FpuRegister>();
2401       if (index.IsConstant()) {
2402         size_t offset =
2403             (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
2404         __ LoadFpuFromOffset(kLoadDoubleword, out, obj, offset, null_checker);
2405       } else {
2406         __ Dlsa(TMP, index.AsRegister<GpuRegister>(), obj, TIMES_8);
2407         __ LoadFpuFromOffset(kLoadDoubleword, out, TMP, data_offset, null_checker);
2408       }
2409       break;
2410     }
2411 
2412     case DataType::Type::kUint32:
2413     case DataType::Type::kUint64:
2414     case DataType::Type::kVoid:
2415       LOG(FATAL) << "Unreachable type " << instruction->GetType();
2416       UNREACHABLE();
2417   }
2418 }
2419 
VisitArrayLength(HArrayLength * instruction)2420 void LocationsBuilderMIPS64::VisitArrayLength(HArrayLength* instruction) {
2421   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
2422   locations->SetInAt(0, Location::RequiresRegister());
2423   locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2424 }
2425 
VisitArrayLength(HArrayLength * instruction)2426 void InstructionCodeGeneratorMIPS64::VisitArrayLength(HArrayLength* instruction) {
2427   LocationSummary* locations = instruction->GetLocations();
2428   uint32_t offset = CodeGenerator::GetArrayLengthOffset(instruction);
2429   GpuRegister obj = locations->InAt(0).AsRegister<GpuRegister>();
2430   GpuRegister out = locations->Out().AsRegister<GpuRegister>();
2431   __ LoadFromOffset(kLoadWord, out, obj, offset);
2432   codegen_->MaybeRecordImplicitNullCheck(instruction);
2433   // Mask out compression flag from String's array length.
2434   if (mirror::kUseStringCompression && instruction->IsStringLength()) {
2435     __ Srl(out, out, 1u);
2436   }
2437 }
2438 
RegisterOrZeroConstant(HInstruction * instruction)2439 Location LocationsBuilderMIPS64::RegisterOrZeroConstant(HInstruction* instruction) {
2440   return (instruction->IsConstant() && instruction->AsConstant()->IsZeroBitPattern())
2441       ? Location::ConstantLocation(instruction->AsConstant())
2442       : Location::RequiresRegister();
2443 }
2444 
FpuRegisterOrConstantForStore(HInstruction * instruction)2445 Location LocationsBuilderMIPS64::FpuRegisterOrConstantForStore(HInstruction* instruction) {
2446   // We can store 0.0 directly (from the ZERO register) without loading it into an FPU register.
2447   // We can store a non-zero float or double constant without first loading it into the FPU,
2448   // but we should only prefer this if the constant has a single use.
2449   if (instruction->IsConstant() &&
2450       (instruction->AsConstant()->IsZeroBitPattern() ||
2451        instruction->GetUses().HasExactlyOneElement())) {
2452     return Location::ConstantLocation(instruction->AsConstant());
2453     // Otherwise fall through and require an FPU register for the constant.
2454   }
2455   return Location::RequiresFpuRegister();
2456 }
2457 
VisitArraySet(HArraySet * instruction)2458 void LocationsBuilderMIPS64::VisitArraySet(HArraySet* instruction) {
2459   DataType::Type value_type = instruction->GetComponentType();
2460 
2461   bool needs_write_barrier =
2462       CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue());
2463   bool may_need_runtime_call_for_type_check = instruction->NeedsTypeCheck();
2464 
2465   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(
2466       instruction,
2467       may_need_runtime_call_for_type_check ?
2468           LocationSummary::kCallOnSlowPath :
2469           LocationSummary::kNoCall);
2470 
2471   locations->SetInAt(0, Location::RequiresRegister());
2472   locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
2473   if (DataType::IsFloatingPointType(instruction->InputAt(2)->GetType())) {
2474     locations->SetInAt(2, FpuRegisterOrConstantForStore(instruction->InputAt(2)));
2475   } else {
2476     locations->SetInAt(2, RegisterOrZeroConstant(instruction->InputAt(2)));
2477   }
2478   if (needs_write_barrier) {
2479     // Temporary register for the write barrier.
2480     locations->AddTemp(Location::RequiresRegister());  // Possibly used for ref. poisoning too.
2481   }
2482 }
2483 
VisitArraySet(HArraySet * instruction)2484 void InstructionCodeGeneratorMIPS64::VisitArraySet(HArraySet* instruction) {
2485   LocationSummary* locations = instruction->GetLocations();
2486   GpuRegister obj = locations->InAt(0).AsRegister<GpuRegister>();
2487   Location index = locations->InAt(1);
2488   Location value_location = locations->InAt(2);
2489   DataType::Type value_type = instruction->GetComponentType();
2490   bool may_need_runtime_call_for_type_check = instruction->NeedsTypeCheck();
2491   bool needs_write_barrier =
2492       CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue());
2493   auto null_checker = GetImplicitNullChecker(instruction, codegen_);
2494   GpuRegister base_reg = index.IsConstant() ? obj : TMP;
2495 
2496   switch (value_type) {
2497     case DataType::Type::kBool:
2498     case DataType::Type::kUint8:
2499     case DataType::Type::kInt8: {
2500       uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value();
2501       if (index.IsConstant()) {
2502         data_offset += index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1;
2503       } else {
2504         __ Daddu(base_reg, obj, index.AsRegister<GpuRegister>());
2505       }
2506       if (value_location.IsConstant()) {
2507         int32_t value = CodeGenerator::GetInt32ValueOf(value_location.GetConstant());
2508         __ StoreConstToOffset(kStoreByte, value, base_reg, data_offset, TMP, null_checker);
2509       } else {
2510         GpuRegister value = value_location.AsRegister<GpuRegister>();
2511         __ StoreToOffset(kStoreByte, value, base_reg, data_offset, null_checker);
2512       }
2513       break;
2514     }
2515 
2516     case DataType::Type::kUint16:
2517     case DataType::Type::kInt16: {
2518       uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value();
2519       if (index.IsConstant()) {
2520         data_offset += index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2;
2521       } else {
2522         __ Dlsa(base_reg, index.AsRegister<GpuRegister>(), obj, TIMES_2);
2523       }
2524       if (value_location.IsConstant()) {
2525         int32_t value = CodeGenerator::GetInt32ValueOf(value_location.GetConstant());
2526         __ StoreConstToOffset(kStoreHalfword, value, base_reg, data_offset, TMP, null_checker);
2527       } else {
2528         GpuRegister value = value_location.AsRegister<GpuRegister>();
2529         __ StoreToOffset(kStoreHalfword, value, base_reg, data_offset, null_checker);
2530       }
2531       break;
2532     }
2533 
2534     case DataType::Type::kInt32: {
2535       uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value();
2536       if (index.IsConstant()) {
2537         data_offset += index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4;
2538       } else {
2539         __ Dlsa(base_reg, index.AsRegister<GpuRegister>(), obj, TIMES_4);
2540       }
2541       if (value_location.IsConstant()) {
2542         int32_t value = CodeGenerator::GetInt32ValueOf(value_location.GetConstant());
2543         __ StoreConstToOffset(kStoreWord, value, base_reg, data_offset, TMP, null_checker);
2544       } else {
2545         GpuRegister value = value_location.AsRegister<GpuRegister>();
2546         __ StoreToOffset(kStoreWord, value, base_reg, data_offset, null_checker);
2547       }
2548       break;
2549     }
2550 
2551     case DataType::Type::kReference: {
2552       if (value_location.IsConstant()) {
2553         // Just setting null.
2554         uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value();
2555         if (index.IsConstant()) {
2556           data_offset += index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4;
2557         } else {
2558           __ Dlsa(base_reg, index.AsRegister<GpuRegister>(), obj, TIMES_4);
2559         }
2560         int32_t value = CodeGenerator::GetInt32ValueOf(value_location.GetConstant());
2561         DCHECK_EQ(value, 0);
2562         __ StoreConstToOffset(kStoreWord, value, base_reg, data_offset, TMP, null_checker);
2563         DCHECK(!needs_write_barrier);
2564         DCHECK(!may_need_runtime_call_for_type_check);
2565         break;
2566       }
2567 
2568       DCHECK(needs_write_barrier);
2569       GpuRegister value = value_location.AsRegister<GpuRegister>();
2570       GpuRegister temp1 = locations->GetTemp(0).AsRegister<GpuRegister>();
2571       GpuRegister temp2 = TMP;  // Doesn't need to survive slow path.
2572       uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
2573       uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
2574       uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
2575       Mips64Label done;
2576       SlowPathCodeMIPS64* slow_path = nullptr;
2577 
2578       if (may_need_runtime_call_for_type_check) {
2579         slow_path = new (codegen_->GetScopedAllocator()) ArraySetSlowPathMIPS64(instruction);
2580         codegen_->AddSlowPath(slow_path);
2581         if (instruction->GetValueCanBeNull()) {
2582           Mips64Label non_zero;
2583           __ Bnezc(value, &non_zero);
2584           uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value();
2585           if (index.IsConstant()) {
2586             data_offset += index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4;
2587           } else {
2588             __ Dlsa(base_reg, index.AsRegister<GpuRegister>(), obj, TIMES_4);
2589           }
2590           __ StoreToOffset(kStoreWord, value, base_reg, data_offset, null_checker);
2591           __ Bc(&done);
2592           __ Bind(&non_zero);
2593         }
2594 
2595         // Note that when read barriers are enabled, the type checks
2596         // are performed without read barriers.  This is fine, even in
2597         // the case where a class object is in the from-space after
2598         // the flip, as a comparison involving such a type would not
2599         // produce a false positive; it may of course produce a false
2600         // negative, in which case we would take the ArraySet slow
2601         // path.
2602 
2603         // /* HeapReference<Class> */ temp1 = obj->klass_
2604         __ LoadFromOffset(kLoadUnsignedWord, temp1, obj, class_offset, null_checker);
2605         __ MaybeUnpoisonHeapReference(temp1);
2606 
2607         // /* HeapReference<Class> */ temp1 = temp1->component_type_
2608         __ LoadFromOffset(kLoadUnsignedWord, temp1, temp1, component_offset);
2609         // /* HeapReference<Class> */ temp2 = value->klass_
2610         __ LoadFromOffset(kLoadUnsignedWord, temp2, value, class_offset);
2611         // If heap poisoning is enabled, no need to unpoison `temp1`
2612         // nor `temp2`, as we are comparing two poisoned references.
2613 
2614         if (instruction->StaticTypeOfArrayIsObjectArray()) {
2615           Mips64Label do_put;
2616           __ Beqc(temp1, temp2, &do_put);
2617           // If heap poisoning is enabled, the `temp1` reference has
2618           // not been unpoisoned yet; unpoison it now.
2619           __ MaybeUnpoisonHeapReference(temp1);
2620 
2621           // /* HeapReference<Class> */ temp1 = temp1->super_class_
2622           __ LoadFromOffset(kLoadUnsignedWord, temp1, temp1, super_offset);
2623           // If heap poisoning is enabled, no need to unpoison
2624           // `temp1`, as we are comparing against null below.
2625           __ Bnezc(temp1, slow_path->GetEntryLabel());
2626           __ Bind(&do_put);
2627         } else {
2628           __ Bnec(temp1, temp2, slow_path->GetEntryLabel());
2629         }
2630       }
2631 
2632       GpuRegister source = value;
2633       if (kPoisonHeapReferences) {
2634         // Note that in the case where `value` is a null reference,
2635         // we do not enter this block, as a null reference does not
2636         // need poisoning.
2637         __ Move(temp1, value);
2638         __ PoisonHeapReference(temp1);
2639         source = temp1;
2640       }
2641 
2642       uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value();
2643       if (index.IsConstant()) {
2644         data_offset += index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4;
2645       } else {
2646         __ Dlsa(base_reg, index.AsRegister<GpuRegister>(), obj, TIMES_4);
2647       }
2648       __ StoreToOffset(kStoreWord, source, base_reg, data_offset);
2649 
2650       if (!may_need_runtime_call_for_type_check) {
2651         codegen_->MaybeRecordImplicitNullCheck(instruction);
2652       }
2653 
2654       codegen_->MarkGCCard(obj, value, instruction->GetValueCanBeNull());
2655 
2656       if (done.IsLinked()) {
2657         __ Bind(&done);
2658       }
2659 
2660       if (slow_path != nullptr) {
2661         __ Bind(slow_path->GetExitLabel());
2662       }
2663       break;
2664     }
2665 
2666     case DataType::Type::kInt64: {
2667       uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value();
2668       if (index.IsConstant()) {
2669         data_offset += index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8;
2670       } else {
2671         __ Dlsa(base_reg, index.AsRegister<GpuRegister>(), obj, TIMES_8);
2672       }
2673       if (value_location.IsConstant()) {
2674         int64_t value = CodeGenerator::GetInt64ValueOf(value_location.GetConstant());
2675         __ StoreConstToOffset(kStoreDoubleword, value, base_reg, data_offset, TMP, null_checker);
2676       } else {
2677         GpuRegister value = value_location.AsRegister<GpuRegister>();
2678         __ StoreToOffset(kStoreDoubleword, value, base_reg, data_offset, null_checker);
2679       }
2680       break;
2681     }
2682 
2683     case DataType::Type::kFloat32: {
2684       uint32_t data_offset = mirror::Array::DataOffset(sizeof(float)).Uint32Value();
2685       if (index.IsConstant()) {
2686         data_offset += index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4;
2687       } else {
2688         __ Dlsa(base_reg, index.AsRegister<GpuRegister>(), obj, TIMES_4);
2689       }
2690       if (value_location.IsConstant()) {
2691         int32_t value = CodeGenerator::GetInt32ValueOf(value_location.GetConstant());
2692         __ StoreConstToOffset(kStoreWord, value, base_reg, data_offset, TMP, null_checker);
2693       } else {
2694         FpuRegister value = value_location.AsFpuRegister<FpuRegister>();
2695         __ StoreFpuToOffset(kStoreWord, value, base_reg, data_offset, null_checker);
2696       }
2697       break;
2698     }
2699 
2700     case DataType::Type::kFloat64: {
2701       uint32_t data_offset = mirror::Array::DataOffset(sizeof(double)).Uint32Value();
2702       if (index.IsConstant()) {
2703         data_offset += index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8;
2704       } else {
2705         __ Dlsa(base_reg, index.AsRegister<GpuRegister>(), obj, TIMES_8);
2706       }
2707       if (value_location.IsConstant()) {
2708         int64_t value = CodeGenerator::GetInt64ValueOf(value_location.GetConstant());
2709         __ StoreConstToOffset(kStoreDoubleword, value, base_reg, data_offset, TMP, null_checker);
2710       } else {
2711         FpuRegister value = value_location.AsFpuRegister<FpuRegister>();
2712         __ StoreFpuToOffset(kStoreDoubleword, value, base_reg, data_offset, null_checker);
2713       }
2714       break;
2715     }
2716 
2717     case DataType::Type::kUint32:
2718     case DataType::Type::kUint64:
2719     case DataType::Type::kVoid:
2720       LOG(FATAL) << "Unreachable type " << instruction->GetType();
2721       UNREACHABLE();
2722   }
2723 }
2724 
VisitBoundsCheck(HBoundsCheck * instruction)2725 void LocationsBuilderMIPS64::VisitBoundsCheck(HBoundsCheck* instruction) {
2726   RegisterSet caller_saves = RegisterSet::Empty();
2727   InvokeRuntimeCallingConvention calling_convention;
2728   caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
2729   caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
2730   LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction, caller_saves);
2731 
2732   HInstruction* index = instruction->InputAt(0);
2733   HInstruction* length = instruction->InputAt(1);
2734 
2735   bool const_index = false;
2736   bool const_length = false;
2737 
2738   if (index->IsConstant()) {
2739     if (length->IsConstant()) {
2740       const_index = true;
2741       const_length = true;
2742     } else {
2743       int32_t index_value = index->AsIntConstant()->GetValue();
2744       if (index_value < 0 || IsInt<16>(index_value + 1)) {
2745         const_index = true;
2746       }
2747     }
2748   } else if (length->IsConstant()) {
2749     int32_t length_value = length->AsIntConstant()->GetValue();
2750     if (IsUint<15>(length_value)) {
2751       const_length = true;
2752     }
2753   }
2754 
2755   locations->SetInAt(0, const_index
2756       ? Location::ConstantLocation(index->AsConstant())
2757       : Location::RequiresRegister());
2758   locations->SetInAt(1, const_length
2759       ? Location::ConstantLocation(length->AsConstant())
2760       : Location::RequiresRegister());
2761 }
2762 
VisitBoundsCheck(HBoundsCheck * instruction)2763 void InstructionCodeGeneratorMIPS64::VisitBoundsCheck(HBoundsCheck* instruction) {
2764   LocationSummary* locations = instruction->GetLocations();
2765   Location index_loc = locations->InAt(0);
2766   Location length_loc = locations->InAt(1);
2767 
2768   if (length_loc.IsConstant()) {
2769     int32_t length = length_loc.GetConstant()->AsIntConstant()->GetValue();
2770     if (index_loc.IsConstant()) {
2771       int32_t index = index_loc.GetConstant()->AsIntConstant()->GetValue();
2772       if (index < 0 || index >= length) {
2773         BoundsCheckSlowPathMIPS64* slow_path =
2774             new (codegen_->GetScopedAllocator()) BoundsCheckSlowPathMIPS64(instruction);
2775         codegen_->AddSlowPath(slow_path);
2776         __ Bc(slow_path->GetEntryLabel());
2777       } else {
2778         // Nothing to be done.
2779       }
2780       return;
2781     }
2782 
2783     BoundsCheckSlowPathMIPS64* slow_path =
2784         new (codegen_->GetScopedAllocator()) BoundsCheckSlowPathMIPS64(instruction);
2785     codegen_->AddSlowPath(slow_path);
2786     GpuRegister index = index_loc.AsRegister<GpuRegister>();
2787     if (length == 0) {
2788       __ Bc(slow_path->GetEntryLabel());
2789     } else if (length == 1) {
2790       __ Bnezc(index, slow_path->GetEntryLabel());
2791     } else {
2792       DCHECK(IsUint<15>(length)) << length;
2793       __ Sltiu(TMP, index, length);
2794       __ Beqzc(TMP, slow_path->GetEntryLabel());
2795     }
2796   } else {
2797     GpuRegister length = length_loc.AsRegister<GpuRegister>();
2798     BoundsCheckSlowPathMIPS64* slow_path =
2799         new (codegen_->GetScopedAllocator()) BoundsCheckSlowPathMIPS64(instruction);
2800     codegen_->AddSlowPath(slow_path);
2801     if (index_loc.IsConstant()) {
2802       int32_t index = index_loc.GetConstant()->AsIntConstant()->GetValue();
2803       if (index < 0) {
2804         __ Bc(slow_path->GetEntryLabel());
2805       } else if (index == 0) {
2806         __ Blezc(length, slow_path->GetEntryLabel());
2807       } else {
2808         DCHECK(IsInt<16>(index + 1)) << index;
2809         __ Sltiu(TMP, length, index + 1);
2810         __ Bnezc(TMP, slow_path->GetEntryLabel());
2811       }
2812     } else {
2813       GpuRegister index = index_loc.AsRegister<GpuRegister>();
2814       __ Bgeuc(index, length, slow_path->GetEntryLabel());
2815     }
2816   }
2817 }
2818 
2819 // Temp is used for read barrier.
NumberOfInstanceOfTemps(TypeCheckKind type_check_kind)2820 static size_t NumberOfInstanceOfTemps(TypeCheckKind type_check_kind) {
2821   if (kEmitCompilerReadBarrier &&
2822       !(kUseBakerReadBarrier && kBakerReadBarrierThunksEnableForFields) &&
2823       (kUseBakerReadBarrier ||
2824        type_check_kind == TypeCheckKind::kAbstractClassCheck ||
2825        type_check_kind == TypeCheckKind::kClassHierarchyCheck ||
2826        type_check_kind == TypeCheckKind::kArrayObjectCheck)) {
2827     return 1;
2828   }
2829   return 0;
2830 }
2831 
2832 // Extra temp is used for read barrier.
NumberOfCheckCastTemps(TypeCheckKind type_check_kind)2833 static size_t NumberOfCheckCastTemps(TypeCheckKind type_check_kind) {
2834   return 1 + NumberOfInstanceOfTemps(type_check_kind);
2835 }
2836 
VisitCheckCast(HCheckCast * instruction)2837 void LocationsBuilderMIPS64::VisitCheckCast(HCheckCast* instruction) {
2838   TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
2839   LocationSummary::CallKind call_kind = CodeGenerator::GetCheckCastCallKind(instruction);
2840   LocationSummary* locations =
2841       new (GetGraph()->GetAllocator()) LocationSummary(instruction, call_kind);
2842   locations->SetInAt(0, Location::RequiresRegister());
2843   locations->SetInAt(1, Location::RequiresRegister());
2844   locations->AddRegisterTemps(NumberOfCheckCastTemps(type_check_kind));
2845 }
2846 
VisitCheckCast(HCheckCast * instruction)2847 void InstructionCodeGeneratorMIPS64::VisitCheckCast(HCheckCast* instruction) {
2848   TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
2849   LocationSummary* locations = instruction->GetLocations();
2850   Location obj_loc = locations->InAt(0);
2851   GpuRegister obj = obj_loc.AsRegister<GpuRegister>();
2852   GpuRegister cls = locations->InAt(1).AsRegister<GpuRegister>();
2853   Location temp_loc = locations->GetTemp(0);
2854   GpuRegister temp = temp_loc.AsRegister<GpuRegister>();
2855   const size_t num_temps = NumberOfCheckCastTemps(type_check_kind);
2856   DCHECK_LE(num_temps, 2u);
2857   Location maybe_temp2_loc = (num_temps >= 2) ? locations->GetTemp(1) : Location::NoLocation();
2858   const uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
2859   const uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
2860   const uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
2861   const uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value();
2862   const uint32_t iftable_offset = mirror::Class::IfTableOffset().Uint32Value();
2863   const uint32_t array_length_offset = mirror::Array::LengthOffset().Uint32Value();
2864   const uint32_t object_array_data_offset =
2865       mirror::Array::DataOffset(kHeapReferenceSize).Uint32Value();
2866   Mips64Label done;
2867 
2868   bool is_type_check_slow_path_fatal = CodeGenerator::IsTypeCheckSlowPathFatal(instruction);
2869   SlowPathCodeMIPS64* slow_path =
2870       new (codegen_->GetScopedAllocator()) TypeCheckSlowPathMIPS64(
2871           instruction, is_type_check_slow_path_fatal);
2872   codegen_->AddSlowPath(slow_path);
2873 
2874   // Avoid this check if we know `obj` is not null.
2875   if (instruction->MustDoNullCheck()) {
2876     __ Beqzc(obj, &done);
2877   }
2878 
2879   switch (type_check_kind) {
2880     case TypeCheckKind::kExactCheck:
2881     case TypeCheckKind::kArrayCheck: {
2882       // /* HeapReference<Class> */ temp = obj->klass_
2883       GenerateReferenceLoadTwoRegisters(instruction,
2884                                         temp_loc,
2885                                         obj_loc,
2886                                         class_offset,
2887                                         maybe_temp2_loc,
2888                                         kWithoutReadBarrier);
2889       // Jump to slow path for throwing the exception or doing a
2890       // more involved array check.
2891       __ Bnec(temp, cls, slow_path->GetEntryLabel());
2892       break;
2893     }
2894 
2895     case TypeCheckKind::kAbstractClassCheck: {
2896       // /* HeapReference<Class> */ temp = obj->klass_
2897       GenerateReferenceLoadTwoRegisters(instruction,
2898                                         temp_loc,
2899                                         obj_loc,
2900                                         class_offset,
2901                                         maybe_temp2_loc,
2902                                         kWithoutReadBarrier);
2903       // If the class is abstract, we eagerly fetch the super class of the
2904       // object to avoid doing a comparison we know will fail.
2905       Mips64Label loop;
2906       __ Bind(&loop);
2907       // /* HeapReference<Class> */ temp = temp->super_class_
2908       GenerateReferenceLoadOneRegister(instruction,
2909                                        temp_loc,
2910                                        super_offset,
2911                                        maybe_temp2_loc,
2912                                        kWithoutReadBarrier);
2913       // If the class reference currently in `temp` is null, jump to the slow path to throw the
2914       // exception.
2915       __ Beqzc(temp, slow_path->GetEntryLabel());
2916       // Otherwise, compare the classes.
2917       __ Bnec(temp, cls, &loop);
2918       break;
2919     }
2920 
2921     case TypeCheckKind::kClassHierarchyCheck: {
2922       // /* HeapReference<Class> */ temp = obj->klass_
2923       GenerateReferenceLoadTwoRegisters(instruction,
2924                                         temp_loc,
2925                                         obj_loc,
2926                                         class_offset,
2927                                         maybe_temp2_loc,
2928                                         kWithoutReadBarrier);
2929       // Walk over the class hierarchy to find a match.
2930       Mips64Label loop;
2931       __ Bind(&loop);
2932       __ Beqc(temp, cls, &done);
2933       // /* HeapReference<Class> */ temp = temp->super_class_
2934       GenerateReferenceLoadOneRegister(instruction,
2935                                        temp_loc,
2936                                        super_offset,
2937                                        maybe_temp2_loc,
2938                                        kWithoutReadBarrier);
2939       // If the class reference currently in `temp` is null, jump to the slow path to throw the
2940       // exception. Otherwise, jump to the beginning of the loop.
2941       __ Bnezc(temp, &loop);
2942       __ Bc(slow_path->GetEntryLabel());
2943       break;
2944     }
2945 
2946     case TypeCheckKind::kArrayObjectCheck: {
2947       // /* HeapReference<Class> */ temp = obj->klass_
2948       GenerateReferenceLoadTwoRegisters(instruction,
2949                                         temp_loc,
2950                                         obj_loc,
2951                                         class_offset,
2952                                         maybe_temp2_loc,
2953                                         kWithoutReadBarrier);
2954       // Do an exact check.
2955       __ Beqc(temp, cls, &done);
2956       // Otherwise, we need to check that the object's class is a non-primitive array.
2957       // /* HeapReference<Class> */ temp = temp->component_type_
2958       GenerateReferenceLoadOneRegister(instruction,
2959                                        temp_loc,
2960                                        component_offset,
2961                                        maybe_temp2_loc,
2962                                        kWithoutReadBarrier);
2963       // If the component type is null, jump to the slow path to throw the exception.
2964       __ Beqzc(temp, slow_path->GetEntryLabel());
2965       // Otherwise, the object is indeed an array, further check that this component
2966       // type is not a primitive type.
2967       __ LoadFromOffset(kLoadUnsignedHalfword, temp, temp, primitive_offset);
2968       static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
2969       __ Bnezc(temp, slow_path->GetEntryLabel());
2970       break;
2971     }
2972 
2973     case TypeCheckKind::kUnresolvedCheck:
2974       // We always go into the type check slow path for the unresolved check case.
2975       // We cannot directly call the CheckCast runtime entry point
2976       // without resorting to a type checking slow path here (i.e. by
2977       // calling InvokeRuntime directly), as it would require to
2978       // assign fixed registers for the inputs of this HInstanceOf
2979       // instruction (following the runtime calling convention), which
2980       // might be cluttered by the potential first read barrier
2981       // emission at the beginning of this method.
2982       __ Bc(slow_path->GetEntryLabel());
2983       break;
2984 
2985     case TypeCheckKind::kInterfaceCheck: {
2986       // Avoid read barriers to improve performance of the fast path. We can not get false
2987       // positives by doing this.
2988       // /* HeapReference<Class> */ temp = obj->klass_
2989       GenerateReferenceLoadTwoRegisters(instruction,
2990                                         temp_loc,
2991                                         obj_loc,
2992                                         class_offset,
2993                                         maybe_temp2_loc,
2994                                         kWithoutReadBarrier);
2995       // /* HeapReference<Class> */ temp = temp->iftable_
2996       GenerateReferenceLoadTwoRegisters(instruction,
2997                                         temp_loc,
2998                                         temp_loc,
2999                                         iftable_offset,
3000                                         maybe_temp2_loc,
3001                                         kWithoutReadBarrier);
3002       // Iftable is never null.
3003       __ Lw(TMP, temp, array_length_offset);
3004       // Loop through the iftable and check if any class matches.
3005       Mips64Label loop;
3006       __ Bind(&loop);
3007       __ Beqzc(TMP, slow_path->GetEntryLabel());
3008       __ Lwu(AT, temp, object_array_data_offset);
3009       __ MaybeUnpoisonHeapReference(AT);
3010       // Go to next interface.
3011       __ Daddiu(temp, temp, 2 * kHeapReferenceSize);
3012       __ Addiu(TMP, TMP, -2);
3013       // Compare the classes and continue the loop if they do not match.
3014       __ Bnec(AT, cls, &loop);
3015       break;
3016     }
3017   }
3018 
3019   __ Bind(&done);
3020   __ Bind(slow_path->GetExitLabel());
3021 }
3022 
VisitClinitCheck(HClinitCheck * check)3023 void LocationsBuilderMIPS64::VisitClinitCheck(HClinitCheck* check) {
3024   LocationSummary* locations =
3025       new (GetGraph()->GetAllocator()) LocationSummary(check, LocationSummary::kCallOnSlowPath);
3026   locations->SetInAt(0, Location::RequiresRegister());
3027   if (check->HasUses()) {
3028     locations->SetOut(Location::SameAsFirstInput());
3029   }
3030 }
3031 
VisitClinitCheck(HClinitCheck * check)3032 void InstructionCodeGeneratorMIPS64::VisitClinitCheck(HClinitCheck* check) {
3033   // We assume the class is not null.
3034   SlowPathCodeMIPS64* slow_path = new (codegen_->GetScopedAllocator()) LoadClassSlowPathMIPS64(
3035       check->GetLoadClass(),
3036       check,
3037       check->GetDexPc(),
3038       true);
3039   codegen_->AddSlowPath(slow_path);
3040   GenerateClassInitializationCheck(slow_path,
3041                                    check->GetLocations()->InAt(0).AsRegister<GpuRegister>());
3042 }
3043 
VisitCompare(HCompare * compare)3044 void LocationsBuilderMIPS64::VisitCompare(HCompare* compare) {
3045   DataType::Type in_type = compare->InputAt(0)->GetType();
3046 
3047   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(compare);
3048 
3049   switch (in_type) {
3050     case DataType::Type::kBool:
3051     case DataType::Type::kUint8:
3052     case DataType::Type::kInt8:
3053     case DataType::Type::kUint16:
3054     case DataType::Type::kInt16:
3055     case DataType::Type::kInt32:
3056     case DataType::Type::kInt64:
3057       locations->SetInAt(0, Location::RequiresRegister());
3058       locations->SetInAt(1, Location::RegisterOrConstant(compare->InputAt(1)));
3059       locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3060       break;
3061 
3062     case DataType::Type::kFloat32:
3063     case DataType::Type::kFloat64:
3064       locations->SetInAt(0, Location::RequiresFpuRegister());
3065       locations->SetInAt(1, Location::RequiresFpuRegister());
3066       locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3067       break;
3068 
3069     default:
3070       LOG(FATAL) << "Unexpected type for compare operation " << in_type;
3071   }
3072 }
3073 
VisitCompare(HCompare * instruction)3074 void InstructionCodeGeneratorMIPS64::VisitCompare(HCompare* instruction) {
3075   LocationSummary* locations = instruction->GetLocations();
3076   GpuRegister res = locations->Out().AsRegister<GpuRegister>();
3077   DataType::Type in_type = instruction->InputAt(0)->GetType();
3078 
3079   //  0 if: left == right
3080   //  1 if: left  > right
3081   // -1 if: left  < right
3082   switch (in_type) {
3083     case DataType::Type::kBool:
3084     case DataType::Type::kUint8:
3085     case DataType::Type::kInt8:
3086     case DataType::Type::kUint16:
3087     case DataType::Type::kInt16:
3088     case DataType::Type::kInt32:
3089     case DataType::Type::kInt64: {
3090       GpuRegister lhs = locations->InAt(0).AsRegister<GpuRegister>();
3091       Location rhs_location = locations->InAt(1);
3092       bool use_imm = rhs_location.IsConstant();
3093       GpuRegister rhs = ZERO;
3094       if (use_imm) {
3095         if (in_type == DataType::Type::kInt64) {
3096           int64_t value = CodeGenerator::GetInt64ValueOf(rhs_location.GetConstant()->AsConstant());
3097           if (value != 0) {
3098             rhs = AT;
3099             __ LoadConst64(rhs, value);
3100           }
3101         } else {
3102           int32_t value = CodeGenerator::GetInt32ValueOf(rhs_location.GetConstant()->AsConstant());
3103           if (value != 0) {
3104             rhs = AT;
3105             __ LoadConst32(rhs, value);
3106           }
3107         }
3108       } else {
3109         rhs = rhs_location.AsRegister<GpuRegister>();
3110       }
3111       __ Slt(TMP, lhs, rhs);
3112       __ Slt(res, rhs, lhs);
3113       __ Subu(res, res, TMP);
3114       break;
3115     }
3116 
3117     case DataType::Type::kFloat32: {
3118       FpuRegister lhs = locations->InAt(0).AsFpuRegister<FpuRegister>();
3119       FpuRegister rhs = locations->InAt(1).AsFpuRegister<FpuRegister>();
3120       Mips64Label done;
3121       __ CmpEqS(FTMP, lhs, rhs);
3122       __ LoadConst32(res, 0);
3123       __ Bc1nez(FTMP, &done);
3124       if (instruction->IsGtBias()) {
3125         __ CmpLtS(FTMP, lhs, rhs);
3126         __ LoadConst32(res, -1);
3127         __ Bc1nez(FTMP, &done);
3128         __ LoadConst32(res, 1);
3129       } else {
3130         __ CmpLtS(FTMP, rhs, lhs);
3131         __ LoadConst32(res, 1);
3132         __ Bc1nez(FTMP, &done);
3133         __ LoadConst32(res, -1);
3134       }
3135       __ Bind(&done);
3136       break;
3137     }
3138 
3139     case DataType::Type::kFloat64: {
3140       FpuRegister lhs = locations->InAt(0).AsFpuRegister<FpuRegister>();
3141       FpuRegister rhs = locations->InAt(1).AsFpuRegister<FpuRegister>();
3142       Mips64Label done;
3143       __ CmpEqD(FTMP, lhs, rhs);
3144       __ LoadConst32(res, 0);
3145       __ Bc1nez(FTMP, &done);
3146       if (instruction->IsGtBias()) {
3147         __ CmpLtD(FTMP, lhs, rhs);
3148         __ LoadConst32(res, -1);
3149         __ Bc1nez(FTMP, &done);
3150         __ LoadConst32(res, 1);
3151       } else {
3152         __ CmpLtD(FTMP, rhs, lhs);
3153         __ LoadConst32(res, 1);
3154         __ Bc1nez(FTMP, &done);
3155         __ LoadConst32(res, -1);
3156       }
3157       __ Bind(&done);
3158       break;
3159     }
3160 
3161     default:
3162       LOG(FATAL) << "Unimplemented compare type " << in_type;
3163   }
3164 }
3165 
HandleCondition(HCondition * instruction)3166 void LocationsBuilderMIPS64::HandleCondition(HCondition* instruction) {
3167   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
3168   switch (instruction->InputAt(0)->GetType()) {
3169     default:
3170     case DataType::Type::kInt64:
3171       locations->SetInAt(0, Location::RequiresRegister());
3172       locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
3173       break;
3174 
3175     case DataType::Type::kFloat32:
3176     case DataType::Type::kFloat64:
3177       locations->SetInAt(0, Location::RequiresFpuRegister());
3178       locations->SetInAt(1, Location::RequiresFpuRegister());
3179       break;
3180   }
3181   if (!instruction->IsEmittedAtUseSite()) {
3182     locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3183   }
3184 }
3185 
HandleCondition(HCondition * instruction)3186 void InstructionCodeGeneratorMIPS64::HandleCondition(HCondition* instruction) {
3187   if (instruction->IsEmittedAtUseSite()) {
3188     return;
3189   }
3190 
3191   DataType::Type type = instruction->InputAt(0)->GetType();
3192   LocationSummary* locations = instruction->GetLocations();
3193   switch (type) {
3194     default:
3195       // Integer case.
3196       GenerateIntLongCompare(instruction->GetCondition(), /* is64bit */ false, locations);
3197       return;
3198     case DataType::Type::kInt64:
3199       GenerateIntLongCompare(instruction->GetCondition(), /* is64bit */ true, locations);
3200       return;
3201     case DataType::Type::kFloat32:
3202     case DataType::Type::kFloat64:
3203       GenerateFpCompare(instruction->GetCondition(), instruction->IsGtBias(), type, locations);
3204      return;
3205   }
3206 }
3207 
DivRemOneOrMinusOne(HBinaryOperation * instruction)3208 void InstructionCodeGeneratorMIPS64::DivRemOneOrMinusOne(HBinaryOperation* instruction) {
3209   DCHECK(instruction->IsDiv() || instruction->IsRem());
3210   DataType::Type type = instruction->GetResultType();
3211 
3212   LocationSummary* locations = instruction->GetLocations();
3213   Location second = locations->InAt(1);
3214   DCHECK(second.IsConstant());
3215 
3216   GpuRegister out = locations->Out().AsRegister<GpuRegister>();
3217   GpuRegister dividend = locations->InAt(0).AsRegister<GpuRegister>();
3218   int64_t imm = Int64FromConstant(second.GetConstant());
3219   DCHECK(imm == 1 || imm == -1);
3220 
3221   if (instruction->IsRem()) {
3222     __ Move(out, ZERO);
3223   } else {
3224     if (imm == -1) {
3225       if (type == DataType::Type::kInt32) {
3226         __ Subu(out, ZERO, dividend);
3227       } else {
3228         DCHECK_EQ(type, DataType::Type::kInt64);
3229         __ Dsubu(out, ZERO, dividend);
3230       }
3231     } else if (out != dividend) {
3232       __ Move(out, dividend);
3233     }
3234   }
3235 }
3236 
DivRemByPowerOfTwo(HBinaryOperation * instruction)3237 void InstructionCodeGeneratorMIPS64::DivRemByPowerOfTwo(HBinaryOperation* instruction) {
3238   DCHECK(instruction->IsDiv() || instruction->IsRem());
3239   DataType::Type type = instruction->GetResultType();
3240 
3241   LocationSummary* locations = instruction->GetLocations();
3242   Location second = locations->InAt(1);
3243   DCHECK(second.IsConstant());
3244 
3245   GpuRegister out = locations->Out().AsRegister<GpuRegister>();
3246   GpuRegister dividend = locations->InAt(0).AsRegister<GpuRegister>();
3247   int64_t imm = Int64FromConstant(second.GetConstant());
3248   uint64_t abs_imm = static_cast<uint64_t>(AbsOrMin(imm));
3249   int ctz_imm = CTZ(abs_imm);
3250 
3251   if (instruction->IsDiv()) {
3252     if (type == DataType::Type::kInt32) {
3253       if (ctz_imm == 1) {
3254         // Fast path for division by +/-2, which is very common.
3255         __ Srl(TMP, dividend, 31);
3256       } else {
3257         __ Sra(TMP, dividend, 31);
3258         __ Srl(TMP, TMP, 32 - ctz_imm);
3259       }
3260       __ Addu(out, dividend, TMP);
3261       __ Sra(out, out, ctz_imm);
3262       if (imm < 0) {
3263         __ Subu(out, ZERO, out);
3264       }
3265     } else {
3266       DCHECK_EQ(type, DataType::Type::kInt64);
3267       if (ctz_imm == 1) {
3268         // Fast path for division by +/-2, which is very common.
3269         __ Dsrl32(TMP, dividend, 31);
3270       } else {
3271         __ Dsra32(TMP, dividend, 31);
3272         if (ctz_imm > 32) {
3273           __ Dsrl(TMP, TMP, 64 - ctz_imm);
3274         } else {
3275           __ Dsrl32(TMP, TMP, 32 - ctz_imm);
3276         }
3277       }
3278       __ Daddu(out, dividend, TMP);
3279       if (ctz_imm < 32) {
3280         __ Dsra(out, out, ctz_imm);
3281       } else {
3282         __ Dsra32(out, out, ctz_imm - 32);
3283       }
3284       if (imm < 0) {
3285         __ Dsubu(out, ZERO, out);
3286       }
3287     }
3288   } else {
3289     if (type == DataType::Type::kInt32) {
3290       if (ctz_imm == 1) {
3291         // Fast path for modulo +/-2, which is very common.
3292         __ Sra(TMP, dividend, 31);
3293         __ Subu(out, dividend, TMP);
3294         __ Andi(out, out, 1);
3295         __ Addu(out, out, TMP);
3296       } else {
3297         __ Sra(TMP, dividend, 31);
3298         __ Srl(TMP, TMP, 32 - ctz_imm);
3299         __ Addu(out, dividend, TMP);
3300         __ Ins(out, ZERO, ctz_imm, 32 - ctz_imm);
3301         __ Subu(out, out, TMP);
3302       }
3303     } else {
3304       DCHECK_EQ(type, DataType::Type::kInt64);
3305       if (ctz_imm == 1) {
3306         // Fast path for modulo +/-2, which is very common.
3307         __ Dsra32(TMP, dividend, 31);
3308         __ Dsubu(out, dividend, TMP);
3309         __ Andi(out, out, 1);
3310         __ Daddu(out, out, TMP);
3311       } else {
3312         __ Dsra32(TMP, dividend, 31);
3313         if (ctz_imm > 32) {
3314           __ Dsrl(TMP, TMP, 64 - ctz_imm);
3315         } else {
3316           __ Dsrl32(TMP, TMP, 32 - ctz_imm);
3317         }
3318         __ Daddu(out, dividend, TMP);
3319         __ DblIns(out, ZERO, ctz_imm, 64 - ctz_imm);
3320         __ Dsubu(out, out, TMP);
3321       }
3322     }
3323   }
3324 }
3325 
GenerateDivRemWithAnyConstant(HBinaryOperation * instruction)3326 void InstructionCodeGeneratorMIPS64::GenerateDivRemWithAnyConstant(HBinaryOperation* instruction) {
3327   DCHECK(instruction->IsDiv() || instruction->IsRem());
3328 
3329   LocationSummary* locations = instruction->GetLocations();
3330   Location second = locations->InAt(1);
3331   DCHECK(second.IsConstant());
3332 
3333   GpuRegister out = locations->Out().AsRegister<GpuRegister>();
3334   GpuRegister dividend = locations->InAt(0).AsRegister<GpuRegister>();
3335   int64_t imm = Int64FromConstant(second.GetConstant());
3336 
3337   DataType::Type type = instruction->GetResultType();
3338   DCHECK(type == DataType::Type::kInt32 || type == DataType::Type::kInt64) << type;
3339 
3340   int64_t magic;
3341   int shift;
3342   CalculateMagicAndShiftForDivRem(imm,
3343                                   (type == DataType::Type::kInt64),
3344                                   &magic,
3345                                   &shift);
3346 
3347   if (type == DataType::Type::kInt32) {
3348     __ LoadConst32(TMP, magic);
3349     __ MuhR6(TMP, dividend, TMP);
3350 
3351     if (imm > 0 && magic < 0) {
3352       __ Addu(TMP, TMP, dividend);
3353     } else if (imm < 0 && magic > 0) {
3354       __ Subu(TMP, TMP, dividend);
3355     }
3356 
3357     if (shift != 0) {
3358       __ Sra(TMP, TMP, shift);
3359     }
3360 
3361     if (instruction->IsDiv()) {
3362       __ Sra(out, TMP, 31);
3363       __ Subu(out, TMP, out);
3364     } else {
3365       __ Sra(AT, TMP, 31);
3366       __ Subu(AT, TMP, AT);
3367       __ LoadConst32(TMP, imm);
3368       __ MulR6(TMP, AT, TMP);
3369       __ Subu(out, dividend, TMP);
3370     }
3371   } else {
3372     __ LoadConst64(TMP, magic);
3373     __ Dmuh(TMP, dividend, TMP);
3374 
3375     if (imm > 0 && magic < 0) {
3376       __ Daddu(TMP, TMP, dividend);
3377     } else if (imm < 0 && magic > 0) {
3378       __ Dsubu(TMP, TMP, dividend);
3379     }
3380 
3381     if (shift >= 32) {
3382       __ Dsra32(TMP, TMP, shift - 32);
3383     } else if (shift > 0) {
3384       __ Dsra(TMP, TMP, shift);
3385     }
3386 
3387     if (instruction->IsDiv()) {
3388       __ Dsra32(out, TMP, 31);
3389       __ Dsubu(out, TMP, out);
3390     } else {
3391       __ Dsra32(AT, TMP, 31);
3392       __ Dsubu(AT, TMP, AT);
3393       __ LoadConst64(TMP, imm);
3394       __ Dmul(TMP, AT, TMP);
3395       __ Dsubu(out, dividend, TMP);
3396     }
3397   }
3398 }
3399 
GenerateDivRemIntegral(HBinaryOperation * instruction)3400 void InstructionCodeGeneratorMIPS64::GenerateDivRemIntegral(HBinaryOperation* instruction) {
3401   DCHECK(instruction->IsDiv() || instruction->IsRem());
3402   DataType::Type type = instruction->GetResultType();
3403   DCHECK(type == DataType::Type::kInt32 || type == DataType::Type::kInt64) << type;
3404 
3405   LocationSummary* locations = instruction->GetLocations();
3406   GpuRegister out = locations->Out().AsRegister<GpuRegister>();
3407   Location second = locations->InAt(1);
3408 
3409   if (second.IsConstant()) {
3410     int64_t imm = Int64FromConstant(second.GetConstant());
3411     if (imm == 0) {
3412       // Do not generate anything. DivZeroCheck would prevent any code to be executed.
3413     } else if (imm == 1 || imm == -1) {
3414       DivRemOneOrMinusOne(instruction);
3415     } else if (IsPowerOfTwo(AbsOrMin(imm))) {
3416       DivRemByPowerOfTwo(instruction);
3417     } else {
3418       DCHECK(imm <= -2 || imm >= 2);
3419       GenerateDivRemWithAnyConstant(instruction);
3420     }
3421   } else {
3422     GpuRegister dividend = locations->InAt(0).AsRegister<GpuRegister>();
3423     GpuRegister divisor = second.AsRegister<GpuRegister>();
3424     if (instruction->IsDiv()) {
3425       if (type == DataType::Type::kInt32)
3426         __ DivR6(out, dividend, divisor);
3427       else
3428         __ Ddiv(out, dividend, divisor);
3429     } else {
3430       if (type == DataType::Type::kInt32)
3431         __ ModR6(out, dividend, divisor);
3432       else
3433         __ Dmod(out, dividend, divisor);
3434     }
3435   }
3436 }
3437 
VisitDiv(HDiv * div)3438 void LocationsBuilderMIPS64::VisitDiv(HDiv* div) {
3439   LocationSummary* locations =
3440       new (GetGraph()->GetAllocator()) LocationSummary(div, LocationSummary::kNoCall);
3441   switch (div->GetResultType()) {
3442     case DataType::Type::kInt32:
3443     case DataType::Type::kInt64:
3444       locations->SetInAt(0, Location::RequiresRegister());
3445       locations->SetInAt(1, Location::RegisterOrConstant(div->InputAt(1)));
3446       locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3447       break;
3448 
3449     case DataType::Type::kFloat32:
3450     case DataType::Type::kFloat64:
3451       locations->SetInAt(0, Location::RequiresFpuRegister());
3452       locations->SetInAt(1, Location::RequiresFpuRegister());
3453       locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
3454       break;
3455 
3456     default:
3457       LOG(FATAL) << "Unexpected div type " << div->GetResultType();
3458   }
3459 }
3460 
VisitDiv(HDiv * instruction)3461 void InstructionCodeGeneratorMIPS64::VisitDiv(HDiv* instruction) {
3462   DataType::Type type = instruction->GetType();
3463   LocationSummary* locations = instruction->GetLocations();
3464 
3465   switch (type) {
3466     case DataType::Type::kInt32:
3467     case DataType::Type::kInt64:
3468       GenerateDivRemIntegral(instruction);
3469       break;
3470     case DataType::Type::kFloat32:
3471     case DataType::Type::kFloat64: {
3472       FpuRegister dst = locations->Out().AsFpuRegister<FpuRegister>();
3473       FpuRegister lhs = locations->InAt(0).AsFpuRegister<FpuRegister>();
3474       FpuRegister rhs = locations->InAt(1).AsFpuRegister<FpuRegister>();
3475       if (type == DataType::Type::kFloat32)
3476         __ DivS(dst, lhs, rhs);
3477       else
3478         __ DivD(dst, lhs, rhs);
3479       break;
3480     }
3481     default:
3482       LOG(FATAL) << "Unexpected div type " << type;
3483   }
3484 }
3485 
VisitDivZeroCheck(HDivZeroCheck * instruction)3486 void LocationsBuilderMIPS64::VisitDivZeroCheck(HDivZeroCheck* instruction) {
3487   LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction);
3488   locations->SetInAt(0, Location::RegisterOrConstant(instruction->InputAt(0)));
3489 }
3490 
VisitDivZeroCheck(HDivZeroCheck * instruction)3491 void InstructionCodeGeneratorMIPS64::VisitDivZeroCheck(HDivZeroCheck* instruction) {
3492   SlowPathCodeMIPS64* slow_path =
3493       new (codegen_->GetScopedAllocator()) DivZeroCheckSlowPathMIPS64(instruction);
3494   codegen_->AddSlowPath(slow_path);
3495   Location value = instruction->GetLocations()->InAt(0);
3496 
3497   DataType::Type type = instruction->GetType();
3498 
3499   if (!DataType::IsIntegralType(type)) {
3500     LOG(FATAL) << "Unexpected type " << type << " for DivZeroCheck.";
3501     return;
3502   }
3503 
3504   if (value.IsConstant()) {
3505     int64_t divisor = codegen_->GetInt64ValueOf(value.GetConstant()->AsConstant());
3506     if (divisor == 0) {
3507       __ Bc(slow_path->GetEntryLabel());
3508     } else {
3509       // A division by a non-null constant is valid. We don't need to perform
3510       // any check, so simply fall through.
3511     }
3512   } else {
3513     __ Beqzc(value.AsRegister<GpuRegister>(), slow_path->GetEntryLabel());
3514   }
3515 }
3516 
VisitDoubleConstant(HDoubleConstant * constant)3517 void LocationsBuilderMIPS64::VisitDoubleConstant(HDoubleConstant* constant) {
3518   LocationSummary* locations =
3519       new (GetGraph()->GetAllocator()) LocationSummary(constant, LocationSummary::kNoCall);
3520   locations->SetOut(Location::ConstantLocation(constant));
3521 }
3522 
VisitDoubleConstant(HDoubleConstant * cst ATTRIBUTE_UNUSED)3523 void InstructionCodeGeneratorMIPS64::VisitDoubleConstant(HDoubleConstant* cst ATTRIBUTE_UNUSED) {
3524   // Will be generated at use site.
3525 }
3526 
VisitExit(HExit * exit)3527 void LocationsBuilderMIPS64::VisitExit(HExit* exit) {
3528   exit->SetLocations(nullptr);
3529 }
3530 
VisitExit(HExit * exit ATTRIBUTE_UNUSED)3531 void InstructionCodeGeneratorMIPS64::VisitExit(HExit* exit ATTRIBUTE_UNUSED) {
3532 }
3533 
VisitFloatConstant(HFloatConstant * constant)3534 void LocationsBuilderMIPS64::VisitFloatConstant(HFloatConstant* constant) {
3535   LocationSummary* locations =
3536       new (GetGraph()->GetAllocator()) LocationSummary(constant, LocationSummary::kNoCall);
3537   locations->SetOut(Location::ConstantLocation(constant));
3538 }
3539 
VisitFloatConstant(HFloatConstant * constant ATTRIBUTE_UNUSED)3540 void InstructionCodeGeneratorMIPS64::VisitFloatConstant(HFloatConstant* constant ATTRIBUTE_UNUSED) {
3541   // Will be generated at use site.
3542 }
3543 
HandleGoto(HInstruction * got,HBasicBlock * successor)3544 void InstructionCodeGeneratorMIPS64::HandleGoto(HInstruction* got, HBasicBlock* successor) {
3545   if (successor->IsExitBlock()) {
3546     DCHECK(got->GetPrevious()->AlwaysThrows());
3547     return;  // no code needed
3548   }
3549 
3550   HBasicBlock* block = got->GetBlock();
3551   HInstruction* previous = got->GetPrevious();
3552   HLoopInformation* info = block->GetLoopInformation();
3553 
3554   if (info != nullptr && info->IsBackEdge(*block) && info->HasSuspendCheck()) {
3555     if (codegen_->GetCompilerOptions().CountHotnessInCompiledCode()) {
3556       __ Ld(AT, SP, kCurrentMethodStackOffset);
3557       __ Lhu(TMP, AT, ArtMethod::HotnessCountOffset().Int32Value());
3558       __ Addiu(TMP, TMP, 1);
3559       __ Sh(TMP, AT, ArtMethod::HotnessCountOffset().Int32Value());
3560     }
3561     GenerateSuspendCheck(info->GetSuspendCheck(), successor);
3562     return;
3563   }
3564   if (block->IsEntryBlock() && (previous != nullptr) && previous->IsSuspendCheck()) {
3565     GenerateSuspendCheck(previous->AsSuspendCheck(), nullptr);
3566   }
3567   if (!codegen_->GoesToNextBlock(block, successor)) {
3568     __ Bc(codegen_->GetLabelOf(successor));
3569   }
3570 }
3571 
VisitGoto(HGoto * got)3572 void LocationsBuilderMIPS64::VisitGoto(HGoto* got) {
3573   got->SetLocations(nullptr);
3574 }
3575 
VisitGoto(HGoto * got)3576 void InstructionCodeGeneratorMIPS64::VisitGoto(HGoto* got) {
3577   HandleGoto(got, got->GetSuccessor());
3578 }
3579 
VisitTryBoundary(HTryBoundary * try_boundary)3580 void LocationsBuilderMIPS64::VisitTryBoundary(HTryBoundary* try_boundary) {
3581   try_boundary->SetLocations(nullptr);
3582 }
3583 
VisitTryBoundary(HTryBoundary * try_boundary)3584 void InstructionCodeGeneratorMIPS64::VisitTryBoundary(HTryBoundary* try_boundary) {
3585   HBasicBlock* successor = try_boundary->GetNormalFlowSuccessor();
3586   if (!successor->IsExitBlock()) {
3587     HandleGoto(try_boundary, successor);
3588   }
3589 }
3590 
GenerateIntLongCompare(IfCondition cond,bool is64bit,LocationSummary * locations)3591 void InstructionCodeGeneratorMIPS64::GenerateIntLongCompare(IfCondition cond,
3592                                                             bool is64bit,
3593                                                             LocationSummary* locations) {
3594   GpuRegister dst = locations->Out().AsRegister<GpuRegister>();
3595   GpuRegister lhs = locations->InAt(0).AsRegister<GpuRegister>();
3596   Location rhs_location = locations->InAt(1);
3597   GpuRegister rhs_reg = ZERO;
3598   int64_t rhs_imm = 0;
3599   bool use_imm = rhs_location.IsConstant();
3600   if (use_imm) {
3601     if (is64bit) {
3602       rhs_imm = CodeGenerator::GetInt64ValueOf(rhs_location.GetConstant());
3603     } else {
3604       rhs_imm = CodeGenerator::GetInt32ValueOf(rhs_location.GetConstant());
3605     }
3606   } else {
3607     rhs_reg = rhs_location.AsRegister<GpuRegister>();
3608   }
3609   int64_t rhs_imm_plus_one = rhs_imm + UINT64_C(1);
3610 
3611   switch (cond) {
3612     case kCondEQ:
3613     case kCondNE:
3614       if (use_imm && IsInt<16>(-rhs_imm)) {
3615         if (rhs_imm == 0) {
3616           if (cond == kCondEQ) {
3617             __ Sltiu(dst, lhs, 1);
3618           } else {
3619             __ Sltu(dst, ZERO, lhs);
3620           }
3621         } else {
3622           if (is64bit) {
3623             __ Daddiu(dst, lhs, -rhs_imm);
3624           } else {
3625             __ Addiu(dst, lhs, -rhs_imm);
3626           }
3627           if (cond == kCondEQ) {
3628             __ Sltiu(dst, dst, 1);
3629           } else {
3630             __ Sltu(dst, ZERO, dst);
3631           }
3632         }
3633       } else {
3634         if (use_imm && IsUint<16>(rhs_imm)) {
3635           __ Xori(dst, lhs, rhs_imm);
3636         } else {
3637           if (use_imm) {
3638             rhs_reg = TMP;
3639             __ LoadConst64(rhs_reg, rhs_imm);
3640           }
3641           __ Xor(dst, lhs, rhs_reg);
3642         }
3643         if (cond == kCondEQ) {
3644           __ Sltiu(dst, dst, 1);
3645         } else {
3646           __ Sltu(dst, ZERO, dst);
3647         }
3648       }
3649       break;
3650 
3651     case kCondLT:
3652     case kCondGE:
3653       if (use_imm && IsInt<16>(rhs_imm)) {
3654         __ Slti(dst, lhs, rhs_imm);
3655       } else {
3656         if (use_imm) {
3657           rhs_reg = TMP;
3658           __ LoadConst64(rhs_reg, rhs_imm);
3659         }
3660         __ Slt(dst, lhs, rhs_reg);
3661       }
3662       if (cond == kCondGE) {
3663         // Simulate lhs >= rhs via !(lhs < rhs) since there's
3664         // only the slt instruction but no sge.
3665         __ Xori(dst, dst, 1);
3666       }
3667       break;
3668 
3669     case kCondLE:
3670     case kCondGT:
3671       if (use_imm && IsInt<16>(rhs_imm_plus_one)) {
3672         // Simulate lhs <= rhs via lhs < rhs + 1.
3673         __ Slti(dst, lhs, rhs_imm_plus_one);
3674         if (cond == kCondGT) {
3675           // Simulate lhs > rhs via !(lhs <= rhs) since there's
3676           // only the slti instruction but no sgti.
3677           __ Xori(dst, dst, 1);
3678         }
3679       } else {
3680         if (use_imm) {
3681           rhs_reg = TMP;
3682           __ LoadConst64(rhs_reg, rhs_imm);
3683         }
3684         __ Slt(dst, rhs_reg, lhs);
3685         if (cond == kCondLE) {
3686           // Simulate lhs <= rhs via !(rhs < lhs) since there's
3687           // only the slt instruction but no sle.
3688           __ Xori(dst, dst, 1);
3689         }
3690       }
3691       break;
3692 
3693     case kCondB:
3694     case kCondAE:
3695       if (use_imm && IsInt<16>(rhs_imm)) {
3696         // Sltiu sign-extends its 16-bit immediate operand before
3697         // the comparison and thus lets us compare directly with
3698         // unsigned values in the ranges [0, 0x7fff] and
3699         // [0x[ffffffff]ffff8000, 0x[ffffffff]ffffffff].
3700         __ Sltiu(dst, lhs, rhs_imm);
3701       } else {
3702         if (use_imm) {
3703           rhs_reg = TMP;
3704           __ LoadConst64(rhs_reg, rhs_imm);
3705         }
3706         __ Sltu(dst, lhs, rhs_reg);
3707       }
3708       if (cond == kCondAE) {
3709         // Simulate lhs >= rhs via !(lhs < rhs) since there's
3710         // only the sltu instruction but no sgeu.
3711         __ Xori(dst, dst, 1);
3712       }
3713       break;
3714 
3715     case kCondBE:
3716     case kCondA:
3717       if (use_imm && (rhs_imm_plus_one != 0) && IsInt<16>(rhs_imm_plus_one)) {
3718         // Simulate lhs <= rhs via lhs < rhs + 1.
3719         // Note that this only works if rhs + 1 does not overflow
3720         // to 0, hence the check above.
3721         // Sltiu sign-extends its 16-bit immediate operand before
3722         // the comparison and thus lets us compare directly with
3723         // unsigned values in the ranges [0, 0x7fff] and
3724         // [0x[ffffffff]ffff8000, 0x[ffffffff]ffffffff].
3725         __ Sltiu(dst, lhs, rhs_imm_plus_one);
3726         if (cond == kCondA) {
3727           // Simulate lhs > rhs via !(lhs <= rhs) since there's
3728           // only the sltiu instruction but no sgtiu.
3729           __ Xori(dst, dst, 1);
3730         }
3731       } else {
3732         if (use_imm) {
3733           rhs_reg = TMP;
3734           __ LoadConst64(rhs_reg, rhs_imm);
3735         }
3736         __ Sltu(dst, rhs_reg, lhs);
3737         if (cond == kCondBE) {
3738           // Simulate lhs <= rhs via !(rhs < lhs) since there's
3739           // only the sltu instruction but no sleu.
3740           __ Xori(dst, dst, 1);
3741         }
3742       }
3743       break;
3744   }
3745 }
3746 
MaterializeIntLongCompare(IfCondition cond,bool is64bit,LocationSummary * input_locations,GpuRegister dst)3747 bool InstructionCodeGeneratorMIPS64::MaterializeIntLongCompare(IfCondition cond,
3748                                                                bool is64bit,
3749                                                                LocationSummary* input_locations,
3750                                                                GpuRegister dst) {
3751   GpuRegister lhs = input_locations->InAt(0).AsRegister<GpuRegister>();
3752   Location rhs_location = input_locations->InAt(1);
3753   GpuRegister rhs_reg = ZERO;
3754   int64_t rhs_imm = 0;
3755   bool use_imm = rhs_location.IsConstant();
3756   if (use_imm) {
3757     if (is64bit) {
3758       rhs_imm = CodeGenerator::GetInt64ValueOf(rhs_location.GetConstant());
3759     } else {
3760       rhs_imm = CodeGenerator::GetInt32ValueOf(rhs_location.GetConstant());
3761     }
3762   } else {
3763     rhs_reg = rhs_location.AsRegister<GpuRegister>();
3764   }
3765   int64_t rhs_imm_plus_one = rhs_imm + UINT64_C(1);
3766 
3767   switch (cond) {
3768     case kCondEQ:
3769     case kCondNE:
3770       if (use_imm && IsInt<16>(-rhs_imm)) {
3771         if (is64bit) {
3772           __ Daddiu(dst, lhs, -rhs_imm);
3773         } else {
3774           __ Addiu(dst, lhs, -rhs_imm);
3775         }
3776       } else if (use_imm && IsUint<16>(rhs_imm)) {
3777         __ Xori(dst, lhs, rhs_imm);
3778       } else {
3779         if (use_imm) {
3780           rhs_reg = TMP;
3781           __ LoadConst64(rhs_reg, rhs_imm);
3782         }
3783         __ Xor(dst, lhs, rhs_reg);
3784       }
3785       return (cond == kCondEQ);
3786 
3787     case kCondLT:
3788     case kCondGE:
3789       if (use_imm && IsInt<16>(rhs_imm)) {
3790         __ Slti(dst, lhs, rhs_imm);
3791       } else {
3792         if (use_imm) {
3793           rhs_reg = TMP;
3794           __ LoadConst64(rhs_reg, rhs_imm);
3795         }
3796         __ Slt(dst, lhs, rhs_reg);
3797       }
3798       return (cond == kCondGE);
3799 
3800     case kCondLE:
3801     case kCondGT:
3802       if (use_imm && IsInt<16>(rhs_imm_plus_one)) {
3803         // Simulate lhs <= rhs via lhs < rhs + 1.
3804         __ Slti(dst, lhs, rhs_imm_plus_one);
3805         return (cond == kCondGT);
3806       } else {
3807         if (use_imm) {
3808           rhs_reg = TMP;
3809           __ LoadConst64(rhs_reg, rhs_imm);
3810         }
3811         __ Slt(dst, rhs_reg, lhs);
3812         return (cond == kCondLE);
3813       }
3814 
3815     case kCondB:
3816     case kCondAE:
3817       if (use_imm && IsInt<16>(rhs_imm)) {
3818         // Sltiu sign-extends its 16-bit immediate operand before
3819         // the comparison and thus lets us compare directly with
3820         // unsigned values in the ranges [0, 0x7fff] and
3821         // [0x[ffffffff]ffff8000, 0x[ffffffff]ffffffff].
3822         __ Sltiu(dst, lhs, rhs_imm);
3823       } else {
3824         if (use_imm) {
3825           rhs_reg = TMP;
3826           __ LoadConst64(rhs_reg, rhs_imm);
3827         }
3828         __ Sltu(dst, lhs, rhs_reg);
3829       }
3830       return (cond == kCondAE);
3831 
3832     case kCondBE:
3833     case kCondA:
3834       if (use_imm && (rhs_imm_plus_one != 0) && IsInt<16>(rhs_imm_plus_one)) {
3835         // Simulate lhs <= rhs via lhs < rhs + 1.
3836         // Note that this only works if rhs + 1 does not overflow
3837         // to 0, hence the check above.
3838         // Sltiu sign-extends its 16-bit immediate operand before
3839         // the comparison and thus lets us compare directly with
3840         // unsigned values in the ranges [0, 0x7fff] and
3841         // [0x[ffffffff]ffff8000, 0x[ffffffff]ffffffff].
3842         __ Sltiu(dst, lhs, rhs_imm_plus_one);
3843         return (cond == kCondA);
3844       } else {
3845         if (use_imm) {
3846           rhs_reg = TMP;
3847           __ LoadConst64(rhs_reg, rhs_imm);
3848         }
3849         __ Sltu(dst, rhs_reg, lhs);
3850         return (cond == kCondBE);
3851       }
3852   }
3853 }
3854 
GenerateIntLongCompareAndBranch(IfCondition cond,bool is64bit,LocationSummary * locations,Mips64Label * label)3855 void InstructionCodeGeneratorMIPS64::GenerateIntLongCompareAndBranch(IfCondition cond,
3856                                                                      bool is64bit,
3857                                                                      LocationSummary* locations,
3858                                                                      Mips64Label* label) {
3859   GpuRegister lhs = locations->InAt(0).AsRegister<GpuRegister>();
3860   Location rhs_location = locations->InAt(1);
3861   GpuRegister rhs_reg = ZERO;
3862   int64_t rhs_imm = 0;
3863   bool use_imm = rhs_location.IsConstant();
3864   if (use_imm) {
3865     if (is64bit) {
3866       rhs_imm = CodeGenerator::GetInt64ValueOf(rhs_location.GetConstant());
3867     } else {
3868       rhs_imm = CodeGenerator::GetInt32ValueOf(rhs_location.GetConstant());
3869     }
3870   } else {
3871     rhs_reg = rhs_location.AsRegister<GpuRegister>();
3872   }
3873 
3874   if (use_imm && rhs_imm == 0) {
3875     switch (cond) {
3876       case kCondEQ:
3877       case kCondBE:  // <= 0 if zero
3878         __ Beqzc(lhs, label);
3879         break;
3880       case kCondNE:
3881       case kCondA:  // > 0 if non-zero
3882         __ Bnezc(lhs, label);
3883         break;
3884       case kCondLT:
3885         __ Bltzc(lhs, label);
3886         break;
3887       case kCondGE:
3888         __ Bgezc(lhs, label);
3889         break;
3890       case kCondLE:
3891         __ Blezc(lhs, label);
3892         break;
3893       case kCondGT:
3894         __ Bgtzc(lhs, label);
3895         break;
3896       case kCondB:  // always false
3897         break;
3898       case kCondAE:  // always true
3899         __ Bc(label);
3900         break;
3901     }
3902   } else {
3903     if (use_imm) {
3904       rhs_reg = TMP;
3905       __ LoadConst64(rhs_reg, rhs_imm);
3906     }
3907     switch (cond) {
3908       case kCondEQ:
3909         __ Beqc(lhs, rhs_reg, label);
3910         break;
3911       case kCondNE:
3912         __ Bnec(lhs, rhs_reg, label);
3913         break;
3914       case kCondLT:
3915         __ Bltc(lhs, rhs_reg, label);
3916         break;
3917       case kCondGE:
3918         __ Bgec(lhs, rhs_reg, label);
3919         break;
3920       case kCondLE:
3921         __ Bgec(rhs_reg, lhs, label);
3922         break;
3923       case kCondGT:
3924         __ Bltc(rhs_reg, lhs, label);
3925         break;
3926       case kCondB:
3927         __ Bltuc(lhs, rhs_reg, label);
3928         break;
3929       case kCondAE:
3930         __ Bgeuc(lhs, rhs_reg, label);
3931         break;
3932       case kCondBE:
3933         __ Bgeuc(rhs_reg, lhs, label);
3934         break;
3935       case kCondA:
3936         __ Bltuc(rhs_reg, lhs, label);
3937         break;
3938     }
3939   }
3940 }
3941 
GenerateFpCompare(IfCondition cond,bool gt_bias,DataType::Type type,LocationSummary * locations)3942 void InstructionCodeGeneratorMIPS64::GenerateFpCompare(IfCondition cond,
3943                                                        bool gt_bias,
3944                                                        DataType::Type type,
3945                                                        LocationSummary* locations) {
3946   GpuRegister dst = locations->Out().AsRegister<GpuRegister>();
3947   FpuRegister lhs = locations->InAt(0).AsFpuRegister<FpuRegister>();
3948   FpuRegister rhs = locations->InAt(1).AsFpuRegister<FpuRegister>();
3949   if (type == DataType::Type::kFloat32) {
3950     switch (cond) {
3951       case kCondEQ:
3952         __ CmpEqS(FTMP, lhs, rhs);
3953         __ Mfc1(dst, FTMP);
3954         __ Andi(dst, dst, 1);
3955         break;
3956       case kCondNE:
3957         __ CmpEqS(FTMP, lhs, rhs);
3958         __ Mfc1(dst, FTMP);
3959         __ Addiu(dst, dst, 1);
3960         break;
3961       case kCondLT:
3962         if (gt_bias) {
3963           __ CmpLtS(FTMP, lhs, rhs);
3964         } else {
3965           __ CmpUltS(FTMP, lhs, rhs);
3966         }
3967         __ Mfc1(dst, FTMP);
3968         __ Andi(dst, dst, 1);
3969         break;
3970       case kCondLE:
3971         if (gt_bias) {
3972           __ CmpLeS(FTMP, lhs, rhs);
3973         } else {
3974           __ CmpUleS(FTMP, lhs, rhs);
3975         }
3976         __ Mfc1(dst, FTMP);
3977         __ Andi(dst, dst, 1);
3978         break;
3979       case kCondGT:
3980         if (gt_bias) {
3981           __ CmpUltS(FTMP, rhs, lhs);
3982         } else {
3983           __ CmpLtS(FTMP, rhs, lhs);
3984         }
3985         __ Mfc1(dst, FTMP);
3986         __ Andi(dst, dst, 1);
3987         break;
3988       case kCondGE:
3989         if (gt_bias) {
3990           __ CmpUleS(FTMP, rhs, lhs);
3991         } else {
3992           __ CmpLeS(FTMP, rhs, lhs);
3993         }
3994         __ Mfc1(dst, FTMP);
3995         __ Andi(dst, dst, 1);
3996         break;
3997       default:
3998         LOG(FATAL) << "Unexpected non-floating-point condition " << cond;
3999         UNREACHABLE();
4000     }
4001   } else {
4002     DCHECK_EQ(type, DataType::Type::kFloat64);
4003     switch (cond) {
4004       case kCondEQ:
4005         __ CmpEqD(FTMP, lhs, rhs);
4006         __ Mfc1(dst, FTMP);
4007         __ Andi(dst, dst, 1);
4008         break;
4009       case kCondNE:
4010         __ CmpEqD(FTMP, lhs, rhs);
4011         __ Mfc1(dst, FTMP);
4012         __ Addiu(dst, dst, 1);
4013         break;
4014       case kCondLT:
4015         if (gt_bias) {
4016           __ CmpLtD(FTMP, lhs, rhs);
4017         } else {
4018           __ CmpUltD(FTMP, lhs, rhs);
4019         }
4020         __ Mfc1(dst, FTMP);
4021         __ Andi(dst, dst, 1);
4022         break;
4023       case kCondLE:
4024         if (gt_bias) {
4025           __ CmpLeD(FTMP, lhs, rhs);
4026         } else {
4027           __ CmpUleD(FTMP, lhs, rhs);
4028         }
4029         __ Mfc1(dst, FTMP);
4030         __ Andi(dst, dst, 1);
4031         break;
4032       case kCondGT:
4033         if (gt_bias) {
4034           __ CmpUltD(FTMP, rhs, lhs);
4035         } else {
4036           __ CmpLtD(FTMP, rhs, lhs);
4037         }
4038         __ Mfc1(dst, FTMP);
4039         __ Andi(dst, dst, 1);
4040         break;
4041       case kCondGE:
4042         if (gt_bias) {
4043           __ CmpUleD(FTMP, rhs, lhs);
4044         } else {
4045           __ CmpLeD(FTMP, rhs, lhs);
4046         }
4047         __ Mfc1(dst, FTMP);
4048         __ Andi(dst, dst, 1);
4049         break;
4050       default:
4051         LOG(FATAL) << "Unexpected non-floating-point condition " << cond;
4052         UNREACHABLE();
4053     }
4054   }
4055 }
4056 
MaterializeFpCompare(IfCondition cond,bool gt_bias,DataType::Type type,LocationSummary * input_locations,FpuRegister dst)4057 bool InstructionCodeGeneratorMIPS64::MaterializeFpCompare(IfCondition cond,
4058                                                           bool gt_bias,
4059                                                           DataType::Type type,
4060                                                           LocationSummary* input_locations,
4061                                                           FpuRegister dst) {
4062   FpuRegister lhs = input_locations->InAt(0).AsFpuRegister<FpuRegister>();
4063   FpuRegister rhs = input_locations->InAt(1).AsFpuRegister<FpuRegister>();
4064   if (type == DataType::Type::kFloat32) {
4065     switch (cond) {
4066       case kCondEQ:
4067         __ CmpEqS(dst, lhs, rhs);
4068         return false;
4069       case kCondNE:
4070         __ CmpEqS(dst, lhs, rhs);
4071         return true;
4072       case kCondLT:
4073         if (gt_bias) {
4074           __ CmpLtS(dst, lhs, rhs);
4075         } else {
4076           __ CmpUltS(dst, lhs, rhs);
4077         }
4078         return false;
4079       case kCondLE:
4080         if (gt_bias) {
4081           __ CmpLeS(dst, lhs, rhs);
4082         } else {
4083           __ CmpUleS(dst, lhs, rhs);
4084         }
4085         return false;
4086       case kCondGT:
4087         if (gt_bias) {
4088           __ CmpUltS(dst, rhs, lhs);
4089         } else {
4090           __ CmpLtS(dst, rhs, lhs);
4091         }
4092         return false;
4093       case kCondGE:
4094         if (gt_bias) {
4095           __ CmpUleS(dst, rhs, lhs);
4096         } else {
4097           __ CmpLeS(dst, rhs, lhs);
4098         }
4099         return false;
4100       default:
4101         LOG(FATAL) << "Unexpected non-floating-point condition " << cond;
4102         UNREACHABLE();
4103     }
4104   } else {
4105     DCHECK_EQ(type, DataType::Type::kFloat64);
4106     switch (cond) {
4107       case kCondEQ:
4108         __ CmpEqD(dst, lhs, rhs);
4109         return false;
4110       case kCondNE:
4111         __ CmpEqD(dst, lhs, rhs);
4112         return true;
4113       case kCondLT:
4114         if (gt_bias) {
4115           __ CmpLtD(dst, lhs, rhs);
4116         } else {
4117           __ CmpUltD(dst, lhs, rhs);
4118         }
4119         return false;
4120       case kCondLE:
4121         if (gt_bias) {
4122           __ CmpLeD(dst, lhs, rhs);
4123         } else {
4124           __ CmpUleD(dst, lhs, rhs);
4125         }
4126         return false;
4127       case kCondGT:
4128         if (gt_bias) {
4129           __ CmpUltD(dst, rhs, lhs);
4130         } else {
4131           __ CmpLtD(dst, rhs, lhs);
4132         }
4133         return false;
4134       case kCondGE:
4135         if (gt_bias) {
4136           __ CmpUleD(dst, rhs, lhs);
4137         } else {
4138           __ CmpLeD(dst, rhs, lhs);
4139         }
4140         return false;
4141       default:
4142         LOG(FATAL) << "Unexpected non-floating-point condition " << cond;
4143         UNREACHABLE();
4144     }
4145   }
4146 }
4147 
GenerateFpCompareAndBranch(IfCondition cond,bool gt_bias,DataType::Type type,LocationSummary * locations,Mips64Label * label)4148 void InstructionCodeGeneratorMIPS64::GenerateFpCompareAndBranch(IfCondition cond,
4149                                                                 bool gt_bias,
4150                                                                 DataType::Type type,
4151                                                                 LocationSummary* locations,
4152                                                                 Mips64Label* label) {
4153   FpuRegister lhs = locations->InAt(0).AsFpuRegister<FpuRegister>();
4154   FpuRegister rhs = locations->InAt(1).AsFpuRegister<FpuRegister>();
4155   if (type == DataType::Type::kFloat32) {
4156     switch (cond) {
4157       case kCondEQ:
4158         __ CmpEqS(FTMP, lhs, rhs);
4159         __ Bc1nez(FTMP, label);
4160         break;
4161       case kCondNE:
4162         __ CmpEqS(FTMP, lhs, rhs);
4163         __ Bc1eqz(FTMP, label);
4164         break;
4165       case kCondLT:
4166         if (gt_bias) {
4167           __ CmpLtS(FTMP, lhs, rhs);
4168         } else {
4169           __ CmpUltS(FTMP, lhs, rhs);
4170         }
4171         __ Bc1nez(FTMP, label);
4172         break;
4173       case kCondLE:
4174         if (gt_bias) {
4175           __ CmpLeS(FTMP, lhs, rhs);
4176         } else {
4177           __ CmpUleS(FTMP, lhs, rhs);
4178         }
4179         __ Bc1nez(FTMP, label);
4180         break;
4181       case kCondGT:
4182         if (gt_bias) {
4183           __ CmpUltS(FTMP, rhs, lhs);
4184         } else {
4185           __ CmpLtS(FTMP, rhs, lhs);
4186         }
4187         __ Bc1nez(FTMP, label);
4188         break;
4189       case kCondGE:
4190         if (gt_bias) {
4191           __ CmpUleS(FTMP, rhs, lhs);
4192         } else {
4193           __ CmpLeS(FTMP, rhs, lhs);
4194         }
4195         __ Bc1nez(FTMP, label);
4196         break;
4197       default:
4198         LOG(FATAL) << "Unexpected non-floating-point condition";
4199         UNREACHABLE();
4200     }
4201   } else {
4202     DCHECK_EQ(type, DataType::Type::kFloat64);
4203     switch (cond) {
4204       case kCondEQ:
4205         __ CmpEqD(FTMP, lhs, rhs);
4206         __ Bc1nez(FTMP, label);
4207         break;
4208       case kCondNE:
4209         __ CmpEqD(FTMP, lhs, rhs);
4210         __ Bc1eqz(FTMP, label);
4211         break;
4212       case kCondLT:
4213         if (gt_bias) {
4214           __ CmpLtD(FTMP, lhs, rhs);
4215         } else {
4216           __ CmpUltD(FTMP, lhs, rhs);
4217         }
4218         __ Bc1nez(FTMP, label);
4219         break;
4220       case kCondLE:
4221         if (gt_bias) {
4222           __ CmpLeD(FTMP, lhs, rhs);
4223         } else {
4224           __ CmpUleD(FTMP, lhs, rhs);
4225         }
4226         __ Bc1nez(FTMP, label);
4227         break;
4228       case kCondGT:
4229         if (gt_bias) {
4230           __ CmpUltD(FTMP, rhs, lhs);
4231         } else {
4232           __ CmpLtD(FTMP, rhs, lhs);
4233         }
4234         __ Bc1nez(FTMP, label);
4235         break;
4236       case kCondGE:
4237         if (gt_bias) {
4238           __ CmpUleD(FTMP, rhs, lhs);
4239         } else {
4240           __ CmpLeD(FTMP, rhs, lhs);
4241         }
4242         __ Bc1nez(FTMP, label);
4243         break;
4244       default:
4245         LOG(FATAL) << "Unexpected non-floating-point condition";
4246         UNREACHABLE();
4247     }
4248   }
4249 }
4250 
GenerateTestAndBranch(HInstruction * instruction,size_t condition_input_index,Mips64Label * true_target,Mips64Label * false_target)4251 void InstructionCodeGeneratorMIPS64::GenerateTestAndBranch(HInstruction* instruction,
4252                                                            size_t condition_input_index,
4253                                                            Mips64Label* true_target,
4254                                                            Mips64Label* false_target) {
4255   HInstruction* cond = instruction->InputAt(condition_input_index);
4256 
4257   if (true_target == nullptr && false_target == nullptr) {
4258     // Nothing to do. The code always falls through.
4259     return;
4260   } else if (cond->IsIntConstant()) {
4261     // Constant condition, statically compared against "true" (integer value 1).
4262     if (cond->AsIntConstant()->IsTrue()) {
4263       if (true_target != nullptr) {
4264         __ Bc(true_target);
4265       }
4266     } else {
4267       DCHECK(cond->AsIntConstant()->IsFalse()) << cond->AsIntConstant()->GetValue();
4268       if (false_target != nullptr) {
4269         __ Bc(false_target);
4270       }
4271     }
4272     return;
4273   }
4274 
4275   // The following code generates these patterns:
4276   //  (1) true_target == nullptr && false_target != nullptr
4277   //        - opposite condition true => branch to false_target
4278   //  (2) true_target != nullptr && false_target == nullptr
4279   //        - condition true => branch to true_target
4280   //  (3) true_target != nullptr && false_target != nullptr
4281   //        - condition true => branch to true_target
4282   //        - branch to false_target
4283   if (IsBooleanValueOrMaterializedCondition(cond)) {
4284     // The condition instruction has been materialized, compare the output to 0.
4285     Location cond_val = instruction->GetLocations()->InAt(condition_input_index);
4286     DCHECK(cond_val.IsRegister());
4287     if (true_target == nullptr) {
4288       __ Beqzc(cond_val.AsRegister<GpuRegister>(), false_target);
4289     } else {
4290       __ Bnezc(cond_val.AsRegister<GpuRegister>(), true_target);
4291     }
4292   } else {
4293     // The condition instruction has not been materialized, use its inputs as
4294     // the comparison and its condition as the branch condition.
4295     HCondition* condition = cond->AsCondition();
4296     DataType::Type type = condition->InputAt(0)->GetType();
4297     LocationSummary* locations = cond->GetLocations();
4298     IfCondition if_cond = condition->GetCondition();
4299     Mips64Label* branch_target = true_target;
4300 
4301     if (true_target == nullptr) {
4302       if_cond = condition->GetOppositeCondition();
4303       branch_target = false_target;
4304     }
4305 
4306     switch (type) {
4307       default:
4308         GenerateIntLongCompareAndBranch(if_cond, /* is64bit */ false, locations, branch_target);
4309         break;
4310       case DataType::Type::kInt64:
4311         GenerateIntLongCompareAndBranch(if_cond, /* is64bit */ true, locations, branch_target);
4312         break;
4313       case DataType::Type::kFloat32:
4314       case DataType::Type::kFloat64:
4315         GenerateFpCompareAndBranch(if_cond, condition->IsGtBias(), type, locations, branch_target);
4316         break;
4317     }
4318   }
4319 
4320   // If neither branch falls through (case 3), the conditional branch to `true_target`
4321   // was already emitted (case 2) and we need to emit a jump to `false_target`.
4322   if (true_target != nullptr && false_target != nullptr) {
4323     __ Bc(false_target);
4324   }
4325 }
4326 
VisitIf(HIf * if_instr)4327 void LocationsBuilderMIPS64::VisitIf(HIf* if_instr) {
4328   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(if_instr);
4329   if (IsBooleanValueOrMaterializedCondition(if_instr->InputAt(0))) {
4330     locations->SetInAt(0, Location::RequiresRegister());
4331   }
4332 }
4333 
VisitIf(HIf * if_instr)4334 void InstructionCodeGeneratorMIPS64::VisitIf(HIf* if_instr) {
4335   HBasicBlock* true_successor = if_instr->IfTrueSuccessor();
4336   HBasicBlock* false_successor = if_instr->IfFalseSuccessor();
4337   Mips64Label* true_target = codegen_->GoesToNextBlock(if_instr->GetBlock(), true_successor) ?
4338       nullptr : codegen_->GetLabelOf(true_successor);
4339   Mips64Label* false_target = codegen_->GoesToNextBlock(if_instr->GetBlock(), false_successor) ?
4340       nullptr : codegen_->GetLabelOf(false_successor);
4341   GenerateTestAndBranch(if_instr, /* condition_input_index */ 0, true_target, false_target);
4342 }
4343 
VisitDeoptimize(HDeoptimize * deoptimize)4344 void LocationsBuilderMIPS64::VisitDeoptimize(HDeoptimize* deoptimize) {
4345   LocationSummary* locations = new (GetGraph()->GetAllocator())
4346       LocationSummary(deoptimize, LocationSummary::kCallOnSlowPath);
4347   InvokeRuntimeCallingConvention calling_convention;
4348   RegisterSet caller_saves = RegisterSet::Empty();
4349   caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
4350   locations->SetCustomSlowPathCallerSaves(caller_saves);
4351   if (IsBooleanValueOrMaterializedCondition(deoptimize->InputAt(0))) {
4352     locations->SetInAt(0, Location::RequiresRegister());
4353   }
4354 }
4355 
VisitDeoptimize(HDeoptimize * deoptimize)4356 void InstructionCodeGeneratorMIPS64::VisitDeoptimize(HDeoptimize* deoptimize) {
4357   SlowPathCodeMIPS64* slow_path =
4358       deopt_slow_paths_.NewSlowPath<DeoptimizationSlowPathMIPS64>(deoptimize);
4359   GenerateTestAndBranch(deoptimize,
4360                         /* condition_input_index */ 0,
4361                         slow_path->GetEntryLabel(),
4362                         /* false_target */ nullptr);
4363 }
4364 
4365 // This function returns true if a conditional move can be generated for HSelect.
4366 // Otherwise it returns false and HSelect must be implemented in terms of conditonal
4367 // branches and regular moves.
4368 //
4369 // If `locations_to_set` isn't nullptr, its inputs and outputs are set for HSelect.
4370 //
4371 // While determining feasibility of a conditional move and setting inputs/outputs
4372 // are two distinct tasks, this function does both because they share quite a bit
4373 // of common logic.
CanMoveConditionally(HSelect * select,LocationSummary * locations_to_set)4374 static bool CanMoveConditionally(HSelect* select, LocationSummary* locations_to_set) {
4375   bool materialized = IsBooleanValueOrMaterializedCondition(select->GetCondition());
4376   HInstruction* cond = select->InputAt(/* condition_input_index */ 2);
4377   HCondition* condition = cond->AsCondition();
4378 
4379   DataType::Type cond_type =
4380       materialized ? DataType::Type::kInt32 : condition->InputAt(0)->GetType();
4381   DataType::Type dst_type = select->GetType();
4382 
4383   HConstant* cst_true_value = select->GetTrueValue()->AsConstant();
4384   HConstant* cst_false_value = select->GetFalseValue()->AsConstant();
4385   bool is_true_value_zero_constant =
4386       (cst_true_value != nullptr && cst_true_value->IsZeroBitPattern());
4387   bool is_false_value_zero_constant =
4388       (cst_false_value != nullptr && cst_false_value->IsZeroBitPattern());
4389 
4390   bool can_move_conditionally = false;
4391   bool use_const_for_false_in = false;
4392   bool use_const_for_true_in = false;
4393 
4394   if (!cond->IsConstant()) {
4395     if (!DataType::IsFloatingPointType(cond_type)) {
4396       if (!DataType::IsFloatingPointType(dst_type)) {
4397         // Moving int/long on int/long condition.
4398         if (is_true_value_zero_constant) {
4399           // seleqz out_reg, false_reg, cond_reg
4400           can_move_conditionally = true;
4401           use_const_for_true_in = true;
4402         } else if (is_false_value_zero_constant) {
4403           // selnez out_reg, true_reg, cond_reg
4404           can_move_conditionally = true;
4405           use_const_for_false_in = true;
4406         } else if (materialized) {
4407           // Not materializing unmaterialized int conditions
4408           // to keep the instruction count low.
4409           // selnez AT, true_reg, cond_reg
4410           // seleqz TMP, false_reg, cond_reg
4411           // or out_reg, AT, TMP
4412           can_move_conditionally = true;
4413         }
4414       } else {
4415         // Moving float/double on int/long condition.
4416         if (materialized) {
4417           // Not materializing unmaterialized int conditions
4418           // to keep the instruction count low.
4419           can_move_conditionally = true;
4420           if (is_true_value_zero_constant) {
4421             // sltu TMP, ZERO, cond_reg
4422             // mtc1 TMP, temp_cond_reg
4423             // seleqz.fmt out_reg, false_reg, temp_cond_reg
4424             use_const_for_true_in = true;
4425           } else if (is_false_value_zero_constant) {
4426             // sltu TMP, ZERO, cond_reg
4427             // mtc1 TMP, temp_cond_reg
4428             // selnez.fmt out_reg, true_reg, temp_cond_reg
4429             use_const_for_false_in = true;
4430           } else {
4431             // sltu TMP, ZERO, cond_reg
4432             // mtc1 TMP, temp_cond_reg
4433             // sel.fmt temp_cond_reg, false_reg, true_reg
4434             // mov.fmt out_reg, temp_cond_reg
4435           }
4436         }
4437       }
4438     } else {
4439       if (!DataType::IsFloatingPointType(dst_type)) {
4440         // Moving int/long on float/double condition.
4441         can_move_conditionally = true;
4442         if (is_true_value_zero_constant) {
4443           // mfc1 TMP, temp_cond_reg
4444           // seleqz out_reg, false_reg, TMP
4445           use_const_for_true_in = true;
4446         } else if (is_false_value_zero_constant) {
4447           // mfc1 TMP, temp_cond_reg
4448           // selnez out_reg, true_reg, TMP
4449           use_const_for_false_in = true;
4450         } else {
4451           // mfc1 TMP, temp_cond_reg
4452           // selnez AT, true_reg, TMP
4453           // seleqz TMP, false_reg, TMP
4454           // or out_reg, AT, TMP
4455         }
4456       } else {
4457         // Moving float/double on float/double condition.
4458         can_move_conditionally = true;
4459         if (is_true_value_zero_constant) {
4460           // seleqz.fmt out_reg, false_reg, temp_cond_reg
4461           use_const_for_true_in = true;
4462         } else if (is_false_value_zero_constant) {
4463           // selnez.fmt out_reg, true_reg, temp_cond_reg
4464           use_const_for_false_in = true;
4465         } else {
4466           // sel.fmt temp_cond_reg, false_reg, true_reg
4467           // mov.fmt out_reg, temp_cond_reg
4468         }
4469       }
4470     }
4471   }
4472 
4473   if (can_move_conditionally) {
4474     DCHECK(!use_const_for_false_in || !use_const_for_true_in);
4475   } else {
4476     DCHECK(!use_const_for_false_in);
4477     DCHECK(!use_const_for_true_in);
4478   }
4479 
4480   if (locations_to_set != nullptr) {
4481     if (use_const_for_false_in) {
4482       locations_to_set->SetInAt(0, Location::ConstantLocation(cst_false_value));
4483     } else {
4484       locations_to_set->SetInAt(0,
4485                                 DataType::IsFloatingPointType(dst_type)
4486                                     ? Location::RequiresFpuRegister()
4487                                     : Location::RequiresRegister());
4488     }
4489     if (use_const_for_true_in) {
4490       locations_to_set->SetInAt(1, Location::ConstantLocation(cst_true_value));
4491     } else {
4492       locations_to_set->SetInAt(1,
4493                                 DataType::IsFloatingPointType(dst_type)
4494                                     ? Location::RequiresFpuRegister()
4495                                     : Location::RequiresRegister());
4496     }
4497     if (materialized) {
4498       locations_to_set->SetInAt(2, Location::RequiresRegister());
4499     }
4500 
4501     if (can_move_conditionally) {
4502       locations_to_set->SetOut(DataType::IsFloatingPointType(dst_type)
4503                                    ? Location::RequiresFpuRegister()
4504                                    : Location::RequiresRegister());
4505     } else {
4506       locations_to_set->SetOut(Location::SameAsFirstInput());
4507     }
4508   }
4509 
4510   return can_move_conditionally;
4511 }
4512 
4513 
GenConditionalMove(HSelect * select)4514 void InstructionCodeGeneratorMIPS64::GenConditionalMove(HSelect* select) {
4515   LocationSummary* locations = select->GetLocations();
4516   Location dst = locations->Out();
4517   Location false_src = locations->InAt(0);
4518   Location true_src = locations->InAt(1);
4519   HInstruction* cond = select->InputAt(/* condition_input_index */ 2);
4520   GpuRegister cond_reg = TMP;
4521   FpuRegister fcond_reg = FTMP;
4522   DataType::Type cond_type = DataType::Type::kInt32;
4523   bool cond_inverted = false;
4524   DataType::Type dst_type = select->GetType();
4525 
4526   if (IsBooleanValueOrMaterializedCondition(cond)) {
4527     cond_reg = locations->InAt(/* condition_input_index */ 2).AsRegister<GpuRegister>();
4528   } else {
4529     HCondition* condition = cond->AsCondition();
4530     LocationSummary* cond_locations = cond->GetLocations();
4531     IfCondition if_cond = condition->GetCondition();
4532     cond_type = condition->InputAt(0)->GetType();
4533     switch (cond_type) {
4534       default:
4535         cond_inverted = MaterializeIntLongCompare(if_cond,
4536                                                   /* is64bit */ false,
4537                                                   cond_locations,
4538                                                   cond_reg);
4539         break;
4540       case DataType::Type::kInt64:
4541         cond_inverted = MaterializeIntLongCompare(if_cond,
4542                                                   /* is64bit */ true,
4543                                                   cond_locations,
4544                                                   cond_reg);
4545         break;
4546       case DataType::Type::kFloat32:
4547       case DataType::Type::kFloat64:
4548         cond_inverted = MaterializeFpCompare(if_cond,
4549                                              condition->IsGtBias(),
4550                                              cond_type,
4551                                              cond_locations,
4552                                              fcond_reg);
4553         break;
4554     }
4555   }
4556 
4557   if (true_src.IsConstant()) {
4558     DCHECK(true_src.GetConstant()->IsZeroBitPattern());
4559   }
4560   if (false_src.IsConstant()) {
4561     DCHECK(false_src.GetConstant()->IsZeroBitPattern());
4562   }
4563 
4564   switch (dst_type) {
4565     default:
4566       if (DataType::IsFloatingPointType(cond_type)) {
4567         __ Mfc1(cond_reg, fcond_reg);
4568       }
4569       if (true_src.IsConstant()) {
4570         if (cond_inverted) {
4571           __ Selnez(dst.AsRegister<GpuRegister>(), false_src.AsRegister<GpuRegister>(), cond_reg);
4572         } else {
4573           __ Seleqz(dst.AsRegister<GpuRegister>(), false_src.AsRegister<GpuRegister>(), cond_reg);
4574         }
4575       } else if (false_src.IsConstant()) {
4576         if (cond_inverted) {
4577           __ Seleqz(dst.AsRegister<GpuRegister>(), true_src.AsRegister<GpuRegister>(), cond_reg);
4578         } else {
4579           __ Selnez(dst.AsRegister<GpuRegister>(), true_src.AsRegister<GpuRegister>(), cond_reg);
4580         }
4581       } else {
4582         DCHECK_NE(cond_reg, AT);
4583         if (cond_inverted) {
4584           __ Seleqz(AT, true_src.AsRegister<GpuRegister>(), cond_reg);
4585           __ Selnez(TMP, false_src.AsRegister<GpuRegister>(), cond_reg);
4586         } else {
4587           __ Selnez(AT, true_src.AsRegister<GpuRegister>(), cond_reg);
4588           __ Seleqz(TMP, false_src.AsRegister<GpuRegister>(), cond_reg);
4589         }
4590         __ Or(dst.AsRegister<GpuRegister>(), AT, TMP);
4591       }
4592       break;
4593     case DataType::Type::kFloat32: {
4594       if (!DataType::IsFloatingPointType(cond_type)) {
4595         // sel*.fmt tests bit 0 of the condition register, account for that.
4596         __ Sltu(TMP, ZERO, cond_reg);
4597         __ Mtc1(TMP, fcond_reg);
4598       }
4599       FpuRegister dst_reg = dst.AsFpuRegister<FpuRegister>();
4600       if (true_src.IsConstant()) {
4601         FpuRegister src_reg = false_src.AsFpuRegister<FpuRegister>();
4602         if (cond_inverted) {
4603           __ SelnezS(dst_reg, src_reg, fcond_reg);
4604         } else {
4605           __ SeleqzS(dst_reg, src_reg, fcond_reg);
4606         }
4607       } else if (false_src.IsConstant()) {
4608         FpuRegister src_reg = true_src.AsFpuRegister<FpuRegister>();
4609         if (cond_inverted) {
4610           __ SeleqzS(dst_reg, src_reg, fcond_reg);
4611         } else {
4612           __ SelnezS(dst_reg, src_reg, fcond_reg);
4613         }
4614       } else {
4615         if (cond_inverted) {
4616           __ SelS(fcond_reg,
4617                   true_src.AsFpuRegister<FpuRegister>(),
4618                   false_src.AsFpuRegister<FpuRegister>());
4619         } else {
4620           __ SelS(fcond_reg,
4621                   false_src.AsFpuRegister<FpuRegister>(),
4622                   true_src.AsFpuRegister<FpuRegister>());
4623         }
4624         __ MovS(dst_reg, fcond_reg);
4625       }
4626       break;
4627     }
4628     case DataType::Type::kFloat64: {
4629       if (!DataType::IsFloatingPointType(cond_type)) {
4630         // sel*.fmt tests bit 0 of the condition register, account for that.
4631         __ Sltu(TMP, ZERO, cond_reg);
4632         __ Mtc1(TMP, fcond_reg);
4633       }
4634       FpuRegister dst_reg = dst.AsFpuRegister<FpuRegister>();
4635       if (true_src.IsConstant()) {
4636         FpuRegister src_reg = false_src.AsFpuRegister<FpuRegister>();
4637         if (cond_inverted) {
4638           __ SelnezD(dst_reg, src_reg, fcond_reg);
4639         } else {
4640           __ SeleqzD(dst_reg, src_reg, fcond_reg);
4641         }
4642       } else if (false_src.IsConstant()) {
4643         FpuRegister src_reg = true_src.AsFpuRegister<FpuRegister>();
4644         if (cond_inverted) {
4645           __ SeleqzD(dst_reg, src_reg, fcond_reg);
4646         } else {
4647           __ SelnezD(dst_reg, src_reg, fcond_reg);
4648         }
4649       } else {
4650         if (cond_inverted) {
4651           __ SelD(fcond_reg,
4652                   true_src.AsFpuRegister<FpuRegister>(),
4653                   false_src.AsFpuRegister<FpuRegister>());
4654         } else {
4655           __ SelD(fcond_reg,
4656                   false_src.AsFpuRegister<FpuRegister>(),
4657                   true_src.AsFpuRegister<FpuRegister>());
4658         }
4659         __ MovD(dst_reg, fcond_reg);
4660       }
4661       break;
4662     }
4663   }
4664 }
4665 
VisitShouldDeoptimizeFlag(HShouldDeoptimizeFlag * flag)4666 void LocationsBuilderMIPS64::VisitShouldDeoptimizeFlag(HShouldDeoptimizeFlag* flag) {
4667   LocationSummary* locations = new (GetGraph()->GetAllocator())
4668       LocationSummary(flag, LocationSummary::kNoCall);
4669   locations->SetOut(Location::RequiresRegister());
4670 }
4671 
VisitShouldDeoptimizeFlag(HShouldDeoptimizeFlag * flag)4672 void InstructionCodeGeneratorMIPS64::VisitShouldDeoptimizeFlag(HShouldDeoptimizeFlag* flag) {
4673   __ LoadFromOffset(kLoadWord,
4674                     flag->GetLocations()->Out().AsRegister<GpuRegister>(),
4675                     SP,
4676                     codegen_->GetStackOffsetOfShouldDeoptimizeFlag());
4677 }
4678 
VisitSelect(HSelect * select)4679 void LocationsBuilderMIPS64::VisitSelect(HSelect* select) {
4680   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(select);
4681   CanMoveConditionally(select, locations);
4682 }
4683 
VisitSelect(HSelect * select)4684 void InstructionCodeGeneratorMIPS64::VisitSelect(HSelect* select) {
4685   if (CanMoveConditionally(select, /* locations_to_set */ nullptr)) {
4686     GenConditionalMove(select);
4687   } else {
4688     LocationSummary* locations = select->GetLocations();
4689     Mips64Label false_target;
4690     GenerateTestAndBranch(select,
4691                           /* condition_input_index */ 2,
4692                           /* true_target */ nullptr,
4693                           &false_target);
4694     codegen_->MoveLocation(locations->Out(), locations->InAt(1), select->GetType());
4695     __ Bind(&false_target);
4696   }
4697 }
4698 
VisitNativeDebugInfo(HNativeDebugInfo * info)4699 void LocationsBuilderMIPS64::VisitNativeDebugInfo(HNativeDebugInfo* info) {
4700   new (GetGraph()->GetAllocator()) LocationSummary(info);
4701 }
4702 
VisitNativeDebugInfo(HNativeDebugInfo *)4703 void InstructionCodeGeneratorMIPS64::VisitNativeDebugInfo(HNativeDebugInfo*) {
4704   // MaybeRecordNativeDebugInfo is already called implicitly in CodeGenerator::Compile.
4705 }
4706 
GenerateNop()4707 void CodeGeneratorMIPS64::GenerateNop() {
4708   __ Nop();
4709 }
4710 
HandleFieldGet(HInstruction * instruction,const FieldInfo & field_info)4711 void LocationsBuilderMIPS64::HandleFieldGet(HInstruction* instruction,
4712                                             const FieldInfo& field_info) {
4713   DataType::Type field_type = field_info.GetFieldType();
4714   bool object_field_get_with_read_barrier =
4715       kEmitCompilerReadBarrier && (field_type == DataType::Type::kReference);
4716   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(
4717       instruction,
4718       object_field_get_with_read_barrier
4719           ? LocationSummary::kCallOnSlowPath
4720           : LocationSummary::kNoCall);
4721   if (object_field_get_with_read_barrier && kUseBakerReadBarrier) {
4722     locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty());  // No caller-save registers.
4723   }
4724   locations->SetInAt(0, Location::RequiresRegister());
4725   if (DataType::IsFloatingPointType(instruction->GetType())) {
4726     locations->SetOut(Location::RequiresFpuRegister());
4727   } else {
4728     // The output overlaps in the case of an object field get with
4729     // read barriers enabled: we do not want the move to overwrite the
4730     // object's location, as we need it to emit the read barrier.
4731     locations->SetOut(Location::RequiresRegister(),
4732                       object_field_get_with_read_barrier
4733                           ? Location::kOutputOverlap
4734                           : Location::kNoOutputOverlap);
4735   }
4736   if (object_field_get_with_read_barrier && kUseBakerReadBarrier) {
4737     // We need a temporary register for the read barrier marking slow
4738     // path in CodeGeneratorMIPS64::GenerateFieldLoadWithBakerReadBarrier.
4739     if (!kBakerReadBarrierThunksEnableForFields) {
4740       locations->AddTemp(Location::RequiresRegister());
4741     }
4742   }
4743 }
4744 
HandleFieldGet(HInstruction * instruction,const FieldInfo & field_info)4745 void InstructionCodeGeneratorMIPS64::HandleFieldGet(HInstruction* instruction,
4746                                                     const FieldInfo& field_info) {
4747   DCHECK_EQ(DataType::Size(field_info.GetFieldType()), DataType::Size(instruction->GetType()));
4748   DataType::Type type = instruction->GetType();
4749   LocationSummary* locations = instruction->GetLocations();
4750   Location obj_loc = locations->InAt(0);
4751   GpuRegister obj = obj_loc.AsRegister<GpuRegister>();
4752   Location dst_loc = locations->Out();
4753   LoadOperandType load_type = kLoadUnsignedByte;
4754   bool is_volatile = field_info.IsVolatile();
4755   uint32_t offset = field_info.GetFieldOffset().Uint32Value();
4756   auto null_checker = GetImplicitNullChecker(instruction, codegen_);
4757 
4758   switch (type) {
4759     case DataType::Type::kBool:
4760     case DataType::Type::kUint8:
4761       load_type = kLoadUnsignedByte;
4762       break;
4763     case DataType::Type::kInt8:
4764       load_type = kLoadSignedByte;
4765       break;
4766     case DataType::Type::kUint16:
4767       load_type = kLoadUnsignedHalfword;
4768       break;
4769     case DataType::Type::kInt16:
4770       load_type = kLoadSignedHalfword;
4771       break;
4772     case DataType::Type::kInt32:
4773     case DataType::Type::kFloat32:
4774       load_type = kLoadWord;
4775       break;
4776     case DataType::Type::kInt64:
4777     case DataType::Type::kFloat64:
4778       load_type = kLoadDoubleword;
4779       break;
4780     case DataType::Type::kReference:
4781       load_type = kLoadUnsignedWord;
4782       break;
4783     case DataType::Type::kUint32:
4784     case DataType::Type::kUint64:
4785     case DataType::Type::kVoid:
4786       LOG(FATAL) << "Unreachable type " << type;
4787       UNREACHABLE();
4788   }
4789   if (!DataType::IsFloatingPointType(type)) {
4790     DCHECK(dst_loc.IsRegister());
4791     GpuRegister dst = dst_loc.AsRegister<GpuRegister>();
4792     if (type == DataType::Type::kReference) {
4793       // /* HeapReference<Object> */ dst = *(obj + offset)
4794       if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
4795         Location temp_loc =
4796             kBakerReadBarrierThunksEnableForFields ? Location::NoLocation() : locations->GetTemp(0);
4797         // Note that a potential implicit null check is handled in this
4798         // CodeGeneratorMIPS64::GenerateFieldLoadWithBakerReadBarrier call.
4799         codegen_->GenerateFieldLoadWithBakerReadBarrier(instruction,
4800                                                         dst_loc,
4801                                                         obj,
4802                                                         offset,
4803                                                         temp_loc,
4804                                                         /* needs_null_check */ true);
4805         if (is_volatile) {
4806           GenerateMemoryBarrier(MemBarrierKind::kLoadAny);
4807         }
4808       } else {
4809         __ LoadFromOffset(kLoadUnsignedWord, dst, obj, offset, null_checker);
4810         if (is_volatile) {
4811           GenerateMemoryBarrier(MemBarrierKind::kLoadAny);
4812         }
4813         // If read barriers are enabled, emit read barriers other than
4814         // Baker's using a slow path (and also unpoison the loaded
4815         // reference, if heap poisoning is enabled).
4816         codegen_->MaybeGenerateReadBarrierSlow(instruction, dst_loc, dst_loc, obj_loc, offset);
4817       }
4818     } else {
4819       __ LoadFromOffset(load_type, dst, obj, offset, null_checker);
4820     }
4821   } else {
4822     DCHECK(dst_loc.IsFpuRegister());
4823     FpuRegister dst = dst_loc.AsFpuRegister<FpuRegister>();
4824     __ LoadFpuFromOffset(load_type, dst, obj, offset, null_checker);
4825   }
4826 
4827   // Memory barriers, in the case of references, are handled in the
4828   // previous switch statement.
4829   if (is_volatile && (type != DataType::Type::kReference)) {
4830     GenerateMemoryBarrier(MemBarrierKind::kLoadAny);
4831   }
4832 }
4833 
HandleFieldSet(HInstruction * instruction,const FieldInfo & field_info ATTRIBUTE_UNUSED)4834 void LocationsBuilderMIPS64::HandleFieldSet(HInstruction* instruction,
4835                                             const FieldInfo& field_info ATTRIBUTE_UNUSED) {
4836   LocationSummary* locations =
4837       new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
4838   locations->SetInAt(0, Location::RequiresRegister());
4839   if (DataType::IsFloatingPointType(instruction->InputAt(1)->GetType())) {
4840     locations->SetInAt(1, FpuRegisterOrConstantForStore(instruction->InputAt(1)));
4841   } else {
4842     locations->SetInAt(1, RegisterOrZeroConstant(instruction->InputAt(1)));
4843   }
4844 }
4845 
HandleFieldSet(HInstruction * instruction,const FieldInfo & field_info,bool value_can_be_null)4846 void InstructionCodeGeneratorMIPS64::HandleFieldSet(HInstruction* instruction,
4847                                                     const FieldInfo& field_info,
4848                                                     bool value_can_be_null) {
4849   DataType::Type type = field_info.GetFieldType();
4850   LocationSummary* locations = instruction->GetLocations();
4851   GpuRegister obj = locations->InAt(0).AsRegister<GpuRegister>();
4852   Location value_location = locations->InAt(1);
4853   StoreOperandType store_type = kStoreByte;
4854   bool is_volatile = field_info.IsVolatile();
4855   uint32_t offset = field_info.GetFieldOffset().Uint32Value();
4856   bool needs_write_barrier = CodeGenerator::StoreNeedsWriteBarrier(type, instruction->InputAt(1));
4857   auto null_checker = GetImplicitNullChecker(instruction, codegen_);
4858 
4859   switch (type) {
4860     case DataType::Type::kBool:
4861     case DataType::Type::kUint8:
4862     case DataType::Type::kInt8:
4863       store_type = kStoreByte;
4864       break;
4865     case DataType::Type::kUint16:
4866     case DataType::Type::kInt16:
4867       store_type = kStoreHalfword;
4868       break;
4869     case DataType::Type::kInt32:
4870     case DataType::Type::kFloat32:
4871     case DataType::Type::kReference:
4872       store_type = kStoreWord;
4873       break;
4874     case DataType::Type::kInt64:
4875     case DataType::Type::kFloat64:
4876       store_type = kStoreDoubleword;
4877       break;
4878     case DataType::Type::kUint32:
4879     case DataType::Type::kUint64:
4880     case DataType::Type::kVoid:
4881       LOG(FATAL) << "Unreachable type " << type;
4882       UNREACHABLE();
4883   }
4884 
4885   if (is_volatile) {
4886     GenerateMemoryBarrier(MemBarrierKind::kAnyStore);
4887   }
4888 
4889   if (value_location.IsConstant()) {
4890     int64_t value = CodeGenerator::GetInt64ValueOf(value_location.GetConstant());
4891     __ StoreConstToOffset(store_type, value, obj, offset, TMP, null_checker);
4892   } else {
4893     if (!DataType::IsFloatingPointType(type)) {
4894       DCHECK(value_location.IsRegister());
4895       GpuRegister src = value_location.AsRegister<GpuRegister>();
4896       if (kPoisonHeapReferences && needs_write_barrier) {
4897         // Note that in the case where `value` is a null reference,
4898         // we do not enter this block, as a null reference does not
4899         // need poisoning.
4900         DCHECK_EQ(type, DataType::Type::kReference);
4901         __ PoisonHeapReference(TMP, src);
4902         __ StoreToOffset(store_type, TMP, obj, offset, null_checker);
4903       } else {
4904         __ StoreToOffset(store_type, src, obj, offset, null_checker);
4905       }
4906     } else {
4907       DCHECK(value_location.IsFpuRegister());
4908       FpuRegister src = value_location.AsFpuRegister<FpuRegister>();
4909       __ StoreFpuToOffset(store_type, src, obj, offset, null_checker);
4910     }
4911   }
4912 
4913   if (needs_write_barrier) {
4914     DCHECK(value_location.IsRegister());
4915     GpuRegister src = value_location.AsRegister<GpuRegister>();
4916     codegen_->MarkGCCard(obj, src, value_can_be_null);
4917   }
4918 
4919   if (is_volatile) {
4920     GenerateMemoryBarrier(MemBarrierKind::kAnyAny);
4921   }
4922 }
4923 
VisitInstanceFieldGet(HInstanceFieldGet * instruction)4924 void LocationsBuilderMIPS64::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
4925   HandleFieldGet(instruction, instruction->GetFieldInfo());
4926 }
4927 
VisitInstanceFieldGet(HInstanceFieldGet * instruction)4928 void InstructionCodeGeneratorMIPS64::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
4929   HandleFieldGet(instruction, instruction->GetFieldInfo());
4930 }
4931 
VisitInstanceFieldSet(HInstanceFieldSet * instruction)4932 void LocationsBuilderMIPS64::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
4933   HandleFieldSet(instruction, instruction->GetFieldInfo());
4934 }
4935 
VisitInstanceFieldSet(HInstanceFieldSet * instruction)4936 void InstructionCodeGeneratorMIPS64::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
4937   HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
4938 }
4939 
GenerateReferenceLoadOneRegister(HInstruction * instruction,Location out,uint32_t offset,Location maybe_temp,ReadBarrierOption read_barrier_option)4940 void InstructionCodeGeneratorMIPS64::GenerateReferenceLoadOneRegister(
4941     HInstruction* instruction,
4942     Location out,
4943     uint32_t offset,
4944     Location maybe_temp,
4945     ReadBarrierOption read_barrier_option) {
4946   GpuRegister out_reg = out.AsRegister<GpuRegister>();
4947   if (read_barrier_option == kWithReadBarrier) {
4948     CHECK(kEmitCompilerReadBarrier);
4949     if (!kUseBakerReadBarrier || !kBakerReadBarrierThunksEnableForFields) {
4950       DCHECK(maybe_temp.IsRegister()) << maybe_temp;
4951     }
4952     if (kUseBakerReadBarrier) {
4953       // Load with fast path based Baker's read barrier.
4954       // /* HeapReference<Object> */ out = *(out + offset)
4955       codegen_->GenerateFieldLoadWithBakerReadBarrier(instruction,
4956                                                       out,
4957                                                       out_reg,
4958                                                       offset,
4959                                                       maybe_temp,
4960                                                       /* needs_null_check */ false);
4961     } else {
4962       // Load with slow path based read barrier.
4963       // Save the value of `out` into `maybe_temp` before overwriting it
4964       // in the following move operation, as we will need it for the
4965       // read barrier below.
4966       __ Move(maybe_temp.AsRegister<GpuRegister>(), out_reg);
4967       // /* HeapReference<Object> */ out = *(out + offset)
4968       __ LoadFromOffset(kLoadUnsignedWord, out_reg, out_reg, offset);
4969       codegen_->GenerateReadBarrierSlow(instruction, out, out, maybe_temp, offset);
4970     }
4971   } else {
4972     // Plain load with no read barrier.
4973     // /* HeapReference<Object> */ out = *(out + offset)
4974     __ LoadFromOffset(kLoadUnsignedWord, out_reg, out_reg, offset);
4975     __ MaybeUnpoisonHeapReference(out_reg);
4976   }
4977 }
4978 
GenerateReferenceLoadTwoRegisters(HInstruction * instruction,Location out,Location obj,uint32_t offset,Location maybe_temp,ReadBarrierOption read_barrier_option)4979 void InstructionCodeGeneratorMIPS64::GenerateReferenceLoadTwoRegisters(
4980     HInstruction* instruction,
4981     Location out,
4982     Location obj,
4983     uint32_t offset,
4984     Location maybe_temp,
4985     ReadBarrierOption read_barrier_option) {
4986   GpuRegister out_reg = out.AsRegister<GpuRegister>();
4987   GpuRegister obj_reg = obj.AsRegister<GpuRegister>();
4988   if (read_barrier_option == kWithReadBarrier) {
4989     CHECK(kEmitCompilerReadBarrier);
4990     if (kUseBakerReadBarrier) {
4991       if (!kBakerReadBarrierThunksEnableForFields) {
4992         DCHECK(maybe_temp.IsRegister()) << maybe_temp;
4993       }
4994       // Load with fast path based Baker's read barrier.
4995       // /* HeapReference<Object> */ out = *(obj + offset)
4996       codegen_->GenerateFieldLoadWithBakerReadBarrier(instruction,
4997                                                       out,
4998                                                       obj_reg,
4999                                                       offset,
5000                                                       maybe_temp,
5001                                                       /* needs_null_check */ false);
5002     } else {
5003       // Load with slow path based read barrier.
5004       // /* HeapReference<Object> */ out = *(obj + offset)
5005       __ LoadFromOffset(kLoadUnsignedWord, out_reg, obj_reg, offset);
5006       codegen_->GenerateReadBarrierSlow(instruction, out, out, obj, offset);
5007     }
5008   } else {
5009     // Plain load with no read barrier.
5010     // /* HeapReference<Object> */ out = *(obj + offset)
5011     __ LoadFromOffset(kLoadUnsignedWord, out_reg, obj_reg, offset);
5012     __ MaybeUnpoisonHeapReference(out_reg);
5013   }
5014 }
5015 
GetBakerMarkThunkNumber(GpuRegister reg)5016 static inline int GetBakerMarkThunkNumber(GpuRegister reg) {
5017   static_assert(BAKER_MARK_INTROSPECTION_REGISTER_COUNT == 20, "Expecting equal");
5018   if (reg >= V0 && reg <= T2) {  // 13 consequtive regs.
5019     return reg - V0;
5020   } else if (reg >= S2 && reg <= S7) {  // 6 consequtive regs.
5021     return 13 + (reg - S2);
5022   } else if (reg == S8) {  // One more.
5023     return 19;
5024   }
5025   LOG(FATAL) << "Unexpected register " << reg;
5026   UNREACHABLE();
5027 }
5028 
GetBakerMarkFieldArrayThunkDisplacement(GpuRegister reg,bool short_offset)5029 static inline int GetBakerMarkFieldArrayThunkDisplacement(GpuRegister reg, bool short_offset) {
5030   int num = GetBakerMarkThunkNumber(reg) +
5031       (short_offset ? BAKER_MARK_INTROSPECTION_REGISTER_COUNT : 0);
5032   return num * BAKER_MARK_INTROSPECTION_FIELD_ARRAY_ENTRY_SIZE;
5033 }
5034 
GetBakerMarkGcRootThunkDisplacement(GpuRegister reg)5035 static inline int GetBakerMarkGcRootThunkDisplacement(GpuRegister reg) {
5036   return GetBakerMarkThunkNumber(reg) * BAKER_MARK_INTROSPECTION_GC_ROOT_ENTRY_SIZE +
5037       BAKER_MARK_INTROSPECTION_GC_ROOT_ENTRIES_OFFSET;
5038 }
5039 
GenerateGcRootFieldLoad(HInstruction * instruction,Location root,GpuRegister obj,uint32_t offset,ReadBarrierOption read_barrier_option,Mips64Label * label_low)5040 void InstructionCodeGeneratorMIPS64::GenerateGcRootFieldLoad(HInstruction* instruction,
5041                                                              Location root,
5042                                                              GpuRegister obj,
5043                                                              uint32_t offset,
5044                                                              ReadBarrierOption read_barrier_option,
5045                                                              Mips64Label* label_low) {
5046   if (label_low != nullptr) {
5047     DCHECK_EQ(offset, 0x5678u);
5048   }
5049   GpuRegister root_reg = root.AsRegister<GpuRegister>();
5050   if (read_barrier_option == kWithReadBarrier) {
5051     DCHECK(kEmitCompilerReadBarrier);
5052     if (kUseBakerReadBarrier) {
5053       // Fast path implementation of art::ReadBarrier::BarrierForRoot when
5054       // Baker's read barrier are used:
5055       if (kBakerReadBarrierThunksEnableForGcRoots) {
5056         // Note that we do not actually check the value of `GetIsGcMarking()`
5057         // to decide whether to mark the loaded GC root or not.  Instead, we
5058         // load into `temp` (T9) the read barrier mark introspection entrypoint.
5059         // If `temp` is null, it means that `GetIsGcMarking()` is false, and
5060         // vice versa.
5061         //
5062         // We use thunks for the slow path. That thunk checks the reference
5063         // and jumps to the entrypoint if needed.
5064         //
5065         //     temp = Thread::Current()->pReadBarrierMarkReg00
5066         //     // AKA &art_quick_read_barrier_mark_introspection.
5067         //     GcRoot<mirror::Object> root = *(obj+offset);  // Original reference load.
5068         //     if (temp != nullptr) {
5069         //        temp = &gc_root_thunk<root_reg>
5070         //        root = temp(root)
5071         //     }
5072 
5073         const int32_t entry_point_offset =
5074             Thread::ReadBarrierMarkEntryPointsOffset<kMips64PointerSize>(0);
5075         const int thunk_disp = GetBakerMarkGcRootThunkDisplacement(root_reg);
5076         int16_t offset_low = Low16Bits(offset);
5077         int16_t offset_high = High16Bits(offset - offset_low);  // Accounts for sign
5078                                                                 // extension in lwu.
5079         bool short_offset = IsInt<16>(static_cast<int32_t>(offset));
5080         GpuRegister base = short_offset ? obj : TMP;
5081         // Loading the entrypoint does not require a load acquire since it is only changed when
5082         // threads are suspended or running a checkpoint.
5083         __ LoadFromOffset(kLoadDoubleword, T9, TR, entry_point_offset);
5084         if (!short_offset) {
5085           DCHECK(!label_low);
5086           __ Daui(base, obj, offset_high);
5087         }
5088         Mips64Label skip_call;
5089         __ Beqz(T9, &skip_call, /* is_bare */ true);
5090         if (label_low != nullptr) {
5091           DCHECK(short_offset);
5092           __ Bind(label_low);
5093         }
5094         // /* GcRoot<mirror::Object> */ root = *(obj + offset)
5095         __ LoadFromOffset(kLoadUnsignedWord, root_reg, base, offset_low);  // Single instruction
5096                                                                            // in delay slot.
5097         __ Jialc(T9, thunk_disp);
5098         __ Bind(&skip_call);
5099       } else {
5100         // Note that we do not actually check the value of `GetIsGcMarking()`
5101         // to decide whether to mark the loaded GC root or not.  Instead, we
5102         // load into `temp` (T9) the read barrier mark entry point corresponding
5103         // to register `root`. If `temp` is null, it means that `GetIsGcMarking()`
5104         // is false, and vice versa.
5105         //
5106         //     GcRoot<mirror::Object> root = *(obj+offset);  // Original reference load.
5107         //     temp = Thread::Current()->pReadBarrierMarkReg ## root.reg()
5108         //     if (temp != null) {
5109         //       root = temp(root)
5110         //     }
5111 
5112         if (label_low != nullptr) {
5113           __ Bind(label_low);
5114         }
5115         // /* GcRoot<mirror::Object> */ root = *(obj + offset)
5116         __ LoadFromOffset(kLoadUnsignedWord, root_reg, obj, offset);
5117         static_assert(
5118             sizeof(mirror::CompressedReference<mirror::Object>) == sizeof(GcRoot<mirror::Object>),
5119             "art::mirror::CompressedReference<mirror::Object> and art::GcRoot<mirror::Object> "
5120             "have different sizes.");
5121         static_assert(sizeof(mirror::CompressedReference<mirror::Object>) == sizeof(int32_t),
5122                       "art::mirror::CompressedReference<mirror::Object> and int32_t "
5123                       "have different sizes.");
5124 
5125         // Slow path marking the GC root `root`.
5126         Location temp = Location::RegisterLocation(T9);
5127         SlowPathCodeMIPS64* slow_path =
5128             new (codegen_->GetScopedAllocator()) ReadBarrierMarkSlowPathMIPS64(
5129                 instruction,
5130                 root,
5131                 /*entrypoint*/ temp);
5132         codegen_->AddSlowPath(slow_path);
5133 
5134         const int32_t entry_point_offset =
5135             Thread::ReadBarrierMarkEntryPointsOffset<kMips64PointerSize>(root.reg() - 1);
5136         // Loading the entrypoint does not require a load acquire since it is only changed when
5137         // threads are suspended or running a checkpoint.
5138         __ LoadFromOffset(kLoadDoubleword, temp.AsRegister<GpuRegister>(), TR, entry_point_offset);
5139         __ Bnezc(temp.AsRegister<GpuRegister>(), slow_path->GetEntryLabel());
5140         __ Bind(slow_path->GetExitLabel());
5141       }
5142     } else {
5143       if (label_low != nullptr) {
5144         __ Bind(label_low);
5145       }
5146       // GC root loaded through a slow path for read barriers other
5147       // than Baker's.
5148       // /* GcRoot<mirror::Object>* */ root = obj + offset
5149       __ Daddiu64(root_reg, obj, static_cast<int32_t>(offset));
5150       // /* mirror::Object* */ root = root->Read()
5151       codegen_->GenerateReadBarrierForRootSlow(instruction, root, root);
5152     }
5153   } else {
5154     if (label_low != nullptr) {
5155       __ Bind(label_low);
5156     }
5157     // Plain GC root load with no read barrier.
5158     // /* GcRoot<mirror::Object> */ root = *(obj + offset)
5159     __ LoadFromOffset(kLoadUnsignedWord, root_reg, obj, offset);
5160     // Note that GC roots are not affected by heap poisoning, thus we
5161     // do not have to unpoison `root_reg` here.
5162   }
5163 }
5164 
GenerateFieldLoadWithBakerReadBarrier(HInstruction * instruction,Location ref,GpuRegister obj,uint32_t offset,Location temp,bool needs_null_check)5165 void CodeGeneratorMIPS64::GenerateFieldLoadWithBakerReadBarrier(HInstruction* instruction,
5166                                                                 Location ref,
5167                                                                 GpuRegister obj,
5168                                                                 uint32_t offset,
5169                                                                 Location temp,
5170                                                                 bool needs_null_check) {
5171   DCHECK(kEmitCompilerReadBarrier);
5172   DCHECK(kUseBakerReadBarrier);
5173 
5174   if (kBakerReadBarrierThunksEnableForFields) {
5175     // Note that we do not actually check the value of `GetIsGcMarking()`
5176     // to decide whether to mark the loaded reference or not.  Instead, we
5177     // load into `temp` (T9) the read barrier mark introspection entrypoint.
5178     // If `temp` is null, it means that `GetIsGcMarking()` is false, and
5179     // vice versa.
5180     //
5181     // We use thunks for the slow path. That thunk checks the reference
5182     // and jumps to the entrypoint if needed. If the holder is not gray,
5183     // it issues a load-load memory barrier and returns to the original
5184     // reference load.
5185     //
5186     //     temp = Thread::Current()->pReadBarrierMarkReg00
5187     //     // AKA &art_quick_read_barrier_mark_introspection.
5188     //     if (temp != nullptr) {
5189     //        temp = &field_array_thunk<holder_reg>
5190     //        temp()
5191     //     }
5192     //   not_gray_return_address:
5193     //     // If the offset is too large to fit into the lw instruction, we
5194     //     // use an adjusted base register (TMP) here. This register
5195     //     // receives bits 16 ... 31 of the offset before the thunk invocation
5196     //     // and the thunk benefits from it.
5197     //     HeapReference<mirror::Object> reference = *(obj+offset);  // Original reference load.
5198     //   gray_return_address:
5199 
5200     DCHECK(temp.IsInvalid());
5201     bool short_offset = IsInt<16>(static_cast<int32_t>(offset));
5202     const int32_t entry_point_offset =
5203         Thread::ReadBarrierMarkEntryPointsOffset<kMips64PointerSize>(0);
5204     // There may have or may have not been a null check if the field offset is smaller than
5205     // the page size.
5206     // There must've been a null check in case it's actually a load from an array.
5207     // We will, however, perform an explicit null check in the thunk as it's easier to
5208     // do it than not.
5209     if (instruction->IsArrayGet()) {
5210       DCHECK(!needs_null_check);
5211     }
5212     const int thunk_disp = GetBakerMarkFieldArrayThunkDisplacement(obj, short_offset);
5213     // Loading the entrypoint does not require a load acquire since it is only changed when
5214     // threads are suspended or running a checkpoint.
5215     __ LoadFromOffset(kLoadDoubleword, T9, TR, entry_point_offset);
5216     GpuRegister ref_reg = ref.AsRegister<GpuRegister>();
5217     Mips64Label skip_call;
5218     if (short_offset) {
5219       __ Beqzc(T9, &skip_call, /* is_bare */ true);
5220       __ Nop();  // In forbidden slot.
5221       __ Jialc(T9, thunk_disp);
5222       __ Bind(&skip_call);
5223       // /* HeapReference<Object> */ ref = *(obj + offset)
5224       __ LoadFromOffset(kLoadUnsignedWord, ref_reg, obj, offset);  // Single instruction.
5225     } else {
5226       int16_t offset_low = Low16Bits(offset);
5227       int16_t offset_high = High16Bits(offset - offset_low);  // Accounts for sign extension in lwu.
5228       __ Beqz(T9, &skip_call, /* is_bare */ true);
5229       __ Daui(TMP, obj, offset_high);  // In delay slot.
5230       __ Jialc(T9, thunk_disp);
5231       __ Bind(&skip_call);
5232       // /* HeapReference<Object> */ ref = *(obj + offset)
5233       __ LoadFromOffset(kLoadUnsignedWord, ref_reg, TMP, offset_low);  // Single instruction.
5234     }
5235     if (needs_null_check) {
5236       MaybeRecordImplicitNullCheck(instruction);
5237     }
5238     __ MaybeUnpoisonHeapReference(ref_reg);
5239     return;
5240   }
5241 
5242   // /* HeapReference<Object> */ ref = *(obj + offset)
5243   Location no_index = Location::NoLocation();
5244   ScaleFactor no_scale_factor = TIMES_1;
5245   GenerateReferenceLoadWithBakerReadBarrier(instruction,
5246                                             ref,
5247                                             obj,
5248                                             offset,
5249                                             no_index,
5250                                             no_scale_factor,
5251                                             temp,
5252                                             needs_null_check);
5253 }
5254 
GenerateArrayLoadWithBakerReadBarrier(HInstruction * instruction,Location ref,GpuRegister obj,uint32_t data_offset,Location index,Location temp,bool needs_null_check)5255 void CodeGeneratorMIPS64::GenerateArrayLoadWithBakerReadBarrier(HInstruction* instruction,
5256                                                                 Location ref,
5257                                                                 GpuRegister obj,
5258                                                                 uint32_t data_offset,
5259                                                                 Location index,
5260                                                                 Location temp,
5261                                                                 bool needs_null_check) {
5262   DCHECK(kEmitCompilerReadBarrier);
5263   DCHECK(kUseBakerReadBarrier);
5264 
5265   static_assert(
5266       sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
5267       "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
5268   ScaleFactor scale_factor = TIMES_4;
5269 
5270   if (kBakerReadBarrierThunksEnableForArrays) {
5271     // Note that we do not actually check the value of `GetIsGcMarking()`
5272     // to decide whether to mark the loaded reference or not.  Instead, we
5273     // load into `temp` (T9) the read barrier mark introspection entrypoint.
5274     // If `temp` is null, it means that `GetIsGcMarking()` is false, and
5275     // vice versa.
5276     //
5277     // We use thunks for the slow path. That thunk checks the reference
5278     // and jumps to the entrypoint if needed. If the holder is not gray,
5279     // it issues a load-load memory barrier and returns to the original
5280     // reference load.
5281     //
5282     //     temp = Thread::Current()->pReadBarrierMarkReg00
5283     //     // AKA &art_quick_read_barrier_mark_introspection.
5284     //     if (temp != nullptr) {
5285     //        temp = &field_array_thunk<holder_reg>
5286     //        temp()
5287     //     }
5288     //   not_gray_return_address:
5289     //     // The element address is pre-calculated in the TMP register before the
5290     //     // thunk invocation and the thunk benefits from it.
5291     //     HeapReference<mirror::Object> reference = data[index];  // Original reference load.
5292     //   gray_return_address:
5293 
5294     DCHECK(temp.IsInvalid());
5295     DCHECK(index.IsValid());
5296     const int32_t entry_point_offset =
5297         Thread::ReadBarrierMarkEntryPointsOffset<kMips64PointerSize>(0);
5298     // We will not do the explicit null check in the thunk as some form of a null check
5299     // must've been done earlier.
5300     DCHECK(!needs_null_check);
5301     const int thunk_disp = GetBakerMarkFieldArrayThunkDisplacement(obj, /* short_offset */ false);
5302     // Loading the entrypoint does not require a load acquire since it is only changed when
5303     // threads are suspended or running a checkpoint.
5304     __ LoadFromOffset(kLoadDoubleword, T9, TR, entry_point_offset);
5305     Mips64Label skip_call;
5306     __ Beqz(T9, &skip_call, /* is_bare */ true);
5307     GpuRegister ref_reg = ref.AsRegister<GpuRegister>();
5308     GpuRegister index_reg = index.AsRegister<GpuRegister>();
5309     __ Dlsa(TMP, index_reg, obj, scale_factor);  // In delay slot.
5310     __ Jialc(T9, thunk_disp);
5311     __ Bind(&skip_call);
5312     // /* HeapReference<Object> */ ref = *(obj + data_offset + (index << scale_factor))
5313     DCHECK(IsInt<16>(static_cast<int32_t>(data_offset))) << data_offset;
5314     __ LoadFromOffset(kLoadUnsignedWord, ref_reg, TMP, data_offset);  // Single instruction.
5315     __ MaybeUnpoisonHeapReference(ref_reg);
5316     return;
5317   }
5318 
5319   // /* HeapReference<Object> */ ref =
5320   //     *(obj + data_offset + index * sizeof(HeapReference<Object>))
5321   GenerateReferenceLoadWithBakerReadBarrier(instruction,
5322                                             ref,
5323                                             obj,
5324                                             data_offset,
5325                                             index,
5326                                             scale_factor,
5327                                             temp,
5328                                             needs_null_check);
5329 }
5330 
GenerateReferenceLoadWithBakerReadBarrier(HInstruction * instruction,Location ref,GpuRegister obj,uint32_t offset,Location index,ScaleFactor scale_factor,Location temp,bool needs_null_check,bool always_update_field)5331 void CodeGeneratorMIPS64::GenerateReferenceLoadWithBakerReadBarrier(HInstruction* instruction,
5332                                                                     Location ref,
5333                                                                     GpuRegister obj,
5334                                                                     uint32_t offset,
5335                                                                     Location index,
5336                                                                     ScaleFactor scale_factor,
5337                                                                     Location temp,
5338                                                                     bool needs_null_check,
5339                                                                     bool always_update_field) {
5340   DCHECK(kEmitCompilerReadBarrier);
5341   DCHECK(kUseBakerReadBarrier);
5342 
5343   // In slow path based read barriers, the read barrier call is
5344   // inserted after the original load. However, in fast path based
5345   // Baker's read barriers, we need to perform the load of
5346   // mirror::Object::monitor_ *before* the original reference load.
5347   // This load-load ordering is required by the read barrier.
5348   // The fast path/slow path (for Baker's algorithm) should look like:
5349   //
5350   //   uint32_t rb_state = Lockword(obj->monitor_).ReadBarrierState();
5351   //   lfence;  // Load fence or artificial data dependency to prevent load-load reordering
5352   //   HeapReference<Object> ref = *src;  // Original reference load.
5353   //   bool is_gray = (rb_state == ReadBarrier::GrayState());
5354   //   if (is_gray) {
5355   //     ref = ReadBarrier::Mark(ref);  // Performed by runtime entrypoint slow path.
5356   //   }
5357   //
5358   // Note: the original implementation in ReadBarrier::Barrier is
5359   // slightly more complex as it performs additional checks that we do
5360   // not do here for performance reasons.
5361 
5362   GpuRegister ref_reg = ref.AsRegister<GpuRegister>();
5363   GpuRegister temp_reg = temp.AsRegister<GpuRegister>();
5364   uint32_t monitor_offset = mirror::Object::MonitorOffset().Int32Value();
5365 
5366   // /* int32_t */ monitor = obj->monitor_
5367   __ LoadFromOffset(kLoadWord, temp_reg, obj, monitor_offset);
5368   if (needs_null_check) {
5369     MaybeRecordImplicitNullCheck(instruction);
5370   }
5371   // /* LockWord */ lock_word = LockWord(monitor)
5372   static_assert(sizeof(LockWord) == sizeof(int32_t),
5373                 "art::LockWord and int32_t have different sizes.");
5374 
5375   __ Sync(0);  // Barrier to prevent load-load reordering.
5376 
5377   // The actual reference load.
5378   if (index.IsValid()) {
5379     // Load types involving an "index": ArrayGet,
5380     // UnsafeGetObject/UnsafeGetObjectVolatile and UnsafeCASObject
5381     // intrinsics.
5382     // /* HeapReference<Object> */ ref = *(obj + offset + (index << scale_factor))
5383     if (index.IsConstant()) {
5384       size_t computed_offset =
5385           (index.GetConstant()->AsIntConstant()->GetValue() << scale_factor) + offset;
5386       __ LoadFromOffset(kLoadUnsignedWord, ref_reg, obj, computed_offset);
5387     } else {
5388       GpuRegister index_reg = index.AsRegister<GpuRegister>();
5389       if (scale_factor == TIMES_1) {
5390         __ Daddu(TMP, index_reg, obj);
5391       } else {
5392         __ Dlsa(TMP, index_reg, obj, scale_factor);
5393       }
5394       __ LoadFromOffset(kLoadUnsignedWord, ref_reg, TMP, offset);
5395     }
5396   } else {
5397     // /* HeapReference<Object> */ ref = *(obj + offset)
5398     __ LoadFromOffset(kLoadUnsignedWord, ref_reg, obj, offset);
5399   }
5400 
5401   // Object* ref = ref_addr->AsMirrorPtr()
5402   __ MaybeUnpoisonHeapReference(ref_reg);
5403 
5404   // Slow path marking the object `ref` when it is gray.
5405   SlowPathCodeMIPS64* slow_path;
5406   if (always_update_field) {
5407     // ReadBarrierMarkAndUpdateFieldSlowPathMIPS64 only supports address
5408     // of the form `obj + field_offset`, where `obj` is a register and
5409     // `field_offset` is a register. Thus `offset` and `scale_factor`
5410     // above are expected to be null in this code path.
5411     DCHECK_EQ(offset, 0u);
5412     DCHECK_EQ(scale_factor, ScaleFactor::TIMES_1);
5413     slow_path = new (GetScopedAllocator())
5414         ReadBarrierMarkAndUpdateFieldSlowPathMIPS64(instruction,
5415                                                     ref,
5416                                                     obj,
5417                                                     /* field_offset */ index,
5418                                                     temp_reg);
5419   } else {
5420     slow_path = new (GetScopedAllocator()) ReadBarrierMarkSlowPathMIPS64(instruction, ref);
5421   }
5422   AddSlowPath(slow_path);
5423 
5424   // if (rb_state == ReadBarrier::GrayState())
5425   //   ref = ReadBarrier::Mark(ref);
5426   // Given the numeric representation, it's enough to check the low bit of the
5427   // rb_state. We do that by shifting the bit into the sign bit (31) and
5428   // performing a branch on less than zero.
5429   static_assert(ReadBarrier::WhiteState() == 0, "Expecting white to have value 0");
5430   static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1");
5431   static_assert(LockWord::kReadBarrierStateSize == 1, "Expecting 1-bit read barrier state size");
5432   __ Sll(temp_reg, temp_reg, 31 - LockWord::kReadBarrierStateShift);
5433   __ Bltzc(temp_reg, slow_path->GetEntryLabel());
5434   __ Bind(slow_path->GetExitLabel());
5435 }
5436 
GenerateReadBarrierSlow(HInstruction * instruction,Location out,Location ref,Location obj,uint32_t offset,Location index)5437 void CodeGeneratorMIPS64::GenerateReadBarrierSlow(HInstruction* instruction,
5438                                                   Location out,
5439                                                   Location ref,
5440                                                   Location obj,
5441                                                   uint32_t offset,
5442                                                   Location index) {
5443   DCHECK(kEmitCompilerReadBarrier);
5444 
5445   // Insert a slow path based read barrier *after* the reference load.
5446   //
5447   // If heap poisoning is enabled, the unpoisoning of the loaded
5448   // reference will be carried out by the runtime within the slow
5449   // path.
5450   //
5451   // Note that `ref` currently does not get unpoisoned (when heap
5452   // poisoning is enabled), which is alright as the `ref` argument is
5453   // not used by the artReadBarrierSlow entry point.
5454   //
5455   // TODO: Unpoison `ref` when it is used by artReadBarrierSlow.
5456   SlowPathCodeMIPS64* slow_path = new (GetScopedAllocator())
5457       ReadBarrierForHeapReferenceSlowPathMIPS64(instruction, out, ref, obj, offset, index);
5458   AddSlowPath(slow_path);
5459 
5460   __ Bc(slow_path->GetEntryLabel());
5461   __ Bind(slow_path->GetExitLabel());
5462 }
5463 
MaybeGenerateReadBarrierSlow(HInstruction * instruction,Location out,Location ref,Location obj,uint32_t offset,Location index)5464 void CodeGeneratorMIPS64::MaybeGenerateReadBarrierSlow(HInstruction* instruction,
5465                                                        Location out,
5466                                                        Location ref,
5467                                                        Location obj,
5468                                                        uint32_t offset,
5469                                                        Location index) {
5470   if (kEmitCompilerReadBarrier) {
5471     // Baker's read barriers shall be handled by the fast path
5472     // (CodeGeneratorMIPS64::GenerateReferenceLoadWithBakerReadBarrier).
5473     DCHECK(!kUseBakerReadBarrier);
5474     // If heap poisoning is enabled, unpoisoning will be taken care of
5475     // by the runtime within the slow path.
5476     GenerateReadBarrierSlow(instruction, out, ref, obj, offset, index);
5477   } else if (kPoisonHeapReferences) {
5478     __ UnpoisonHeapReference(out.AsRegister<GpuRegister>());
5479   }
5480 }
5481 
GenerateReadBarrierForRootSlow(HInstruction * instruction,Location out,Location root)5482 void CodeGeneratorMIPS64::GenerateReadBarrierForRootSlow(HInstruction* instruction,
5483                                                          Location out,
5484                                                          Location root) {
5485   DCHECK(kEmitCompilerReadBarrier);
5486 
5487   // Insert a slow path based read barrier *after* the GC root load.
5488   //
5489   // Note that GC roots are not affected by heap poisoning, so we do
5490   // not need to do anything special for this here.
5491   SlowPathCodeMIPS64* slow_path =
5492       new (GetScopedAllocator()) ReadBarrierForRootSlowPathMIPS64(instruction, out, root);
5493   AddSlowPath(slow_path);
5494 
5495   __ Bc(slow_path->GetEntryLabel());
5496   __ Bind(slow_path->GetExitLabel());
5497 }
5498 
VisitInstanceOf(HInstanceOf * instruction)5499 void LocationsBuilderMIPS64::VisitInstanceOf(HInstanceOf* instruction) {
5500   LocationSummary::CallKind call_kind = LocationSummary::kNoCall;
5501   TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
5502   bool baker_read_barrier_slow_path = false;
5503   switch (type_check_kind) {
5504     case TypeCheckKind::kExactCheck:
5505     case TypeCheckKind::kAbstractClassCheck:
5506     case TypeCheckKind::kClassHierarchyCheck:
5507     case TypeCheckKind::kArrayObjectCheck: {
5508       bool needs_read_barrier = CodeGenerator::InstanceOfNeedsReadBarrier(instruction);
5509       call_kind = needs_read_barrier ? LocationSummary::kCallOnSlowPath : LocationSummary::kNoCall;
5510       baker_read_barrier_slow_path = kUseBakerReadBarrier && needs_read_barrier;
5511       break;
5512     }
5513     case TypeCheckKind::kArrayCheck:
5514     case TypeCheckKind::kUnresolvedCheck:
5515     case TypeCheckKind::kInterfaceCheck:
5516       call_kind = LocationSummary::kCallOnSlowPath;
5517       break;
5518   }
5519 
5520   LocationSummary* locations =
5521       new (GetGraph()->GetAllocator()) LocationSummary(instruction, call_kind);
5522   if (baker_read_barrier_slow_path) {
5523     locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty());  // No caller-save registers.
5524   }
5525   locations->SetInAt(0, Location::RequiresRegister());
5526   locations->SetInAt(1, Location::RequiresRegister());
5527   // The output does overlap inputs.
5528   // Note that TypeCheckSlowPathMIPS64 uses this register too.
5529   locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
5530   locations->AddRegisterTemps(NumberOfInstanceOfTemps(type_check_kind));
5531 }
5532 
VisitInstanceOf(HInstanceOf * instruction)5533 void InstructionCodeGeneratorMIPS64::VisitInstanceOf(HInstanceOf* instruction) {
5534   TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
5535   LocationSummary* locations = instruction->GetLocations();
5536   Location obj_loc = locations->InAt(0);
5537   GpuRegister obj = obj_loc.AsRegister<GpuRegister>();
5538   GpuRegister cls = locations->InAt(1).AsRegister<GpuRegister>();
5539   Location out_loc = locations->Out();
5540   GpuRegister out = out_loc.AsRegister<GpuRegister>();
5541   const size_t num_temps = NumberOfInstanceOfTemps(type_check_kind);
5542   DCHECK_LE(num_temps, 1u);
5543   Location maybe_temp_loc = (num_temps >= 1) ? locations->GetTemp(0) : Location::NoLocation();
5544   uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
5545   uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
5546   uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
5547   uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value();
5548   Mips64Label done;
5549   SlowPathCodeMIPS64* slow_path = nullptr;
5550 
5551   // Return 0 if `obj` is null.
5552   // Avoid this check if we know `obj` is not null.
5553   if (instruction->MustDoNullCheck()) {
5554     __ Move(out, ZERO);
5555     __ Beqzc(obj, &done);
5556   }
5557 
5558   switch (type_check_kind) {
5559     case TypeCheckKind::kExactCheck: {
5560       ReadBarrierOption read_barrier_option =
5561           CodeGenerator::ReadBarrierOptionForInstanceOf(instruction);
5562       // /* HeapReference<Class> */ out = obj->klass_
5563       GenerateReferenceLoadTwoRegisters(instruction,
5564                                         out_loc,
5565                                         obj_loc,
5566                                         class_offset,
5567                                         maybe_temp_loc,
5568                                         read_barrier_option);
5569       // Classes must be equal for the instanceof to succeed.
5570       __ Xor(out, out, cls);
5571       __ Sltiu(out, out, 1);
5572       break;
5573     }
5574 
5575     case TypeCheckKind::kAbstractClassCheck: {
5576       ReadBarrierOption read_barrier_option =
5577           CodeGenerator::ReadBarrierOptionForInstanceOf(instruction);
5578       // /* HeapReference<Class> */ out = obj->klass_
5579       GenerateReferenceLoadTwoRegisters(instruction,
5580                                         out_loc,
5581                                         obj_loc,
5582                                         class_offset,
5583                                         maybe_temp_loc,
5584                                         read_barrier_option);
5585       // If the class is abstract, we eagerly fetch the super class of the
5586       // object to avoid doing a comparison we know will fail.
5587       Mips64Label loop;
5588       __ Bind(&loop);
5589       // /* HeapReference<Class> */ out = out->super_class_
5590       GenerateReferenceLoadOneRegister(instruction,
5591                                        out_loc,
5592                                        super_offset,
5593                                        maybe_temp_loc,
5594                                        read_barrier_option);
5595       // If `out` is null, we use it for the result, and jump to `done`.
5596       __ Beqzc(out, &done);
5597       __ Bnec(out, cls, &loop);
5598       __ LoadConst32(out, 1);
5599       break;
5600     }
5601 
5602     case TypeCheckKind::kClassHierarchyCheck: {
5603       ReadBarrierOption read_barrier_option =
5604           CodeGenerator::ReadBarrierOptionForInstanceOf(instruction);
5605       // /* HeapReference<Class> */ out = obj->klass_
5606       GenerateReferenceLoadTwoRegisters(instruction,
5607                                         out_loc,
5608                                         obj_loc,
5609                                         class_offset,
5610                                         maybe_temp_loc,
5611                                         read_barrier_option);
5612       // Walk over the class hierarchy to find a match.
5613       Mips64Label loop, success;
5614       __ Bind(&loop);
5615       __ Beqc(out, cls, &success);
5616       // /* HeapReference<Class> */ out = out->super_class_
5617       GenerateReferenceLoadOneRegister(instruction,
5618                                        out_loc,
5619                                        super_offset,
5620                                        maybe_temp_loc,
5621                                        read_barrier_option);
5622       __ Bnezc(out, &loop);
5623       // If `out` is null, we use it for the result, and jump to `done`.
5624       __ Bc(&done);
5625       __ Bind(&success);
5626       __ LoadConst32(out, 1);
5627       break;
5628     }
5629 
5630     case TypeCheckKind::kArrayObjectCheck: {
5631       ReadBarrierOption read_barrier_option =
5632           CodeGenerator::ReadBarrierOptionForInstanceOf(instruction);
5633       // /* HeapReference<Class> */ out = obj->klass_
5634       GenerateReferenceLoadTwoRegisters(instruction,
5635                                         out_loc,
5636                                         obj_loc,
5637                                         class_offset,
5638                                         maybe_temp_loc,
5639                                         read_barrier_option);
5640       // Do an exact check.
5641       Mips64Label success;
5642       __ Beqc(out, cls, &success);
5643       // Otherwise, we need to check that the object's class is a non-primitive array.
5644       // /* HeapReference<Class> */ out = out->component_type_
5645       GenerateReferenceLoadOneRegister(instruction,
5646                                        out_loc,
5647                                        component_offset,
5648                                        maybe_temp_loc,
5649                                        read_barrier_option);
5650       // If `out` is null, we use it for the result, and jump to `done`.
5651       __ Beqzc(out, &done);
5652       __ LoadFromOffset(kLoadUnsignedHalfword, out, out, primitive_offset);
5653       static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
5654       __ Sltiu(out, out, 1);
5655       __ Bc(&done);
5656       __ Bind(&success);
5657       __ LoadConst32(out, 1);
5658       break;
5659     }
5660 
5661     case TypeCheckKind::kArrayCheck: {
5662       // No read barrier since the slow path will retry upon failure.
5663       // /* HeapReference<Class> */ out = obj->klass_
5664       GenerateReferenceLoadTwoRegisters(instruction,
5665                                         out_loc,
5666                                         obj_loc,
5667                                         class_offset,
5668                                         maybe_temp_loc,
5669                                         kWithoutReadBarrier);
5670       DCHECK(locations->OnlyCallsOnSlowPath());
5671       slow_path = new (codegen_->GetScopedAllocator()) TypeCheckSlowPathMIPS64(
5672           instruction, /* is_fatal */ false);
5673       codegen_->AddSlowPath(slow_path);
5674       __ Bnec(out, cls, slow_path->GetEntryLabel());
5675       __ LoadConst32(out, 1);
5676       break;
5677     }
5678 
5679     case TypeCheckKind::kUnresolvedCheck:
5680     case TypeCheckKind::kInterfaceCheck: {
5681       // Note that we indeed only call on slow path, but we always go
5682       // into the slow path for the unresolved and interface check
5683       // cases.
5684       //
5685       // We cannot directly call the InstanceofNonTrivial runtime
5686       // entry point without resorting to a type checking slow path
5687       // here (i.e. by calling InvokeRuntime directly), as it would
5688       // require to assign fixed registers for the inputs of this
5689       // HInstanceOf instruction (following the runtime calling
5690       // convention), which might be cluttered by the potential first
5691       // read barrier emission at the beginning of this method.
5692       //
5693       // TODO: Introduce a new runtime entry point taking the object
5694       // to test (instead of its class) as argument, and let it deal
5695       // with the read barrier issues. This will let us refactor this
5696       // case of the `switch` code as it was previously (with a direct
5697       // call to the runtime not using a type checking slow path).
5698       // This should also be beneficial for the other cases above.
5699       DCHECK(locations->OnlyCallsOnSlowPath());
5700       slow_path = new (codegen_->GetScopedAllocator()) TypeCheckSlowPathMIPS64(
5701           instruction, /* is_fatal */ false);
5702       codegen_->AddSlowPath(slow_path);
5703       __ Bc(slow_path->GetEntryLabel());
5704       break;
5705     }
5706   }
5707 
5708   __ Bind(&done);
5709 
5710   if (slow_path != nullptr) {
5711     __ Bind(slow_path->GetExitLabel());
5712   }
5713 }
5714 
VisitIntConstant(HIntConstant * constant)5715 void LocationsBuilderMIPS64::VisitIntConstant(HIntConstant* constant) {
5716   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(constant);
5717   locations->SetOut(Location::ConstantLocation(constant));
5718 }
5719 
VisitIntConstant(HIntConstant * constant ATTRIBUTE_UNUSED)5720 void InstructionCodeGeneratorMIPS64::VisitIntConstant(HIntConstant* constant ATTRIBUTE_UNUSED) {
5721   // Will be generated at use site.
5722 }
5723 
VisitNullConstant(HNullConstant * constant)5724 void LocationsBuilderMIPS64::VisitNullConstant(HNullConstant* constant) {
5725   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(constant);
5726   locations->SetOut(Location::ConstantLocation(constant));
5727 }
5728 
VisitNullConstant(HNullConstant * constant ATTRIBUTE_UNUSED)5729 void InstructionCodeGeneratorMIPS64::VisitNullConstant(HNullConstant* constant ATTRIBUTE_UNUSED) {
5730   // Will be generated at use site.
5731 }
5732 
VisitInvokeUnresolved(HInvokeUnresolved * invoke)5733 void LocationsBuilderMIPS64::VisitInvokeUnresolved(HInvokeUnresolved* invoke) {
5734   // The trampoline uses the same calling convention as dex calling conventions,
5735   // except instead of loading arg0/r0 with the target Method*, arg0/r0 will contain
5736   // the method_idx.
5737   HandleInvoke(invoke);
5738 }
5739 
VisitInvokeUnresolved(HInvokeUnresolved * invoke)5740 void InstructionCodeGeneratorMIPS64::VisitInvokeUnresolved(HInvokeUnresolved* invoke) {
5741   codegen_->GenerateInvokeUnresolvedRuntimeCall(invoke);
5742 }
5743 
HandleInvoke(HInvoke * invoke)5744 void LocationsBuilderMIPS64::HandleInvoke(HInvoke* invoke) {
5745   InvokeDexCallingConventionVisitorMIPS64 calling_convention_visitor;
5746   CodeGenerator::CreateCommonInvokeLocationSummary(invoke, &calling_convention_visitor);
5747 }
5748 
VisitInvokeInterface(HInvokeInterface * invoke)5749 void LocationsBuilderMIPS64::VisitInvokeInterface(HInvokeInterface* invoke) {
5750   HandleInvoke(invoke);
5751   // The register T0 is required to be used for the hidden argument in
5752   // art_quick_imt_conflict_trampoline, so add the hidden argument.
5753   invoke->GetLocations()->AddTemp(Location::RegisterLocation(T0));
5754 }
5755 
VisitInvokeInterface(HInvokeInterface * invoke)5756 void InstructionCodeGeneratorMIPS64::VisitInvokeInterface(HInvokeInterface* invoke) {
5757   // TODO: b/18116999, our IMTs can miss an IncompatibleClassChangeError.
5758   GpuRegister temp = invoke->GetLocations()->GetTemp(0).AsRegister<GpuRegister>();
5759   Location receiver = invoke->GetLocations()->InAt(0);
5760   uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
5761   Offset entry_point = ArtMethod::EntryPointFromQuickCompiledCodeOffset(kMips64PointerSize);
5762 
5763   // Set the hidden argument.
5764   __ LoadConst32(invoke->GetLocations()->GetTemp(1).AsRegister<GpuRegister>(),
5765                  invoke->GetDexMethodIndex());
5766 
5767   // temp = object->GetClass();
5768   if (receiver.IsStackSlot()) {
5769     __ LoadFromOffset(kLoadUnsignedWord, temp, SP, receiver.GetStackIndex());
5770     __ LoadFromOffset(kLoadUnsignedWord, temp, temp, class_offset);
5771   } else {
5772     __ LoadFromOffset(kLoadUnsignedWord, temp, receiver.AsRegister<GpuRegister>(), class_offset);
5773   }
5774   codegen_->MaybeRecordImplicitNullCheck(invoke);
5775   // Instead of simply (possibly) unpoisoning `temp` here, we should
5776   // emit a read barrier for the previous class reference load.
5777   // However this is not required in practice, as this is an
5778   // intermediate/temporary reference and because the current
5779   // concurrent copying collector keeps the from-space memory
5780   // intact/accessible until the end of the marking phase (the
5781   // concurrent copying collector may not in the future).
5782   __ MaybeUnpoisonHeapReference(temp);
5783   __ LoadFromOffset(kLoadDoubleword, temp, temp,
5784       mirror::Class::ImtPtrOffset(kMips64PointerSize).Uint32Value());
5785   uint32_t method_offset = static_cast<uint32_t>(ImTable::OffsetOfElement(
5786       invoke->GetImtIndex(), kMips64PointerSize));
5787   // temp = temp->GetImtEntryAt(method_offset);
5788   __ LoadFromOffset(kLoadDoubleword, temp, temp, method_offset);
5789   // T9 = temp->GetEntryPoint();
5790   __ LoadFromOffset(kLoadDoubleword, T9, temp, entry_point.Int32Value());
5791   // T9();
5792   __ Jalr(T9);
5793   __ Nop();
5794   DCHECK(!codegen_->IsLeafMethod());
5795   codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
5796 }
5797 
VisitInvokeVirtual(HInvokeVirtual * invoke)5798 void LocationsBuilderMIPS64::VisitInvokeVirtual(HInvokeVirtual* invoke) {
5799   IntrinsicLocationsBuilderMIPS64 intrinsic(codegen_);
5800   if (intrinsic.TryDispatch(invoke)) {
5801     return;
5802   }
5803 
5804   HandleInvoke(invoke);
5805 }
5806 
VisitInvokeStaticOrDirect(HInvokeStaticOrDirect * invoke)5807 void LocationsBuilderMIPS64::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
5808   // Explicit clinit checks triggered by static invokes must have been pruned by
5809   // art::PrepareForRegisterAllocation.
5810   DCHECK(!invoke->IsStaticWithExplicitClinitCheck());
5811 
5812   IntrinsicLocationsBuilderMIPS64 intrinsic(codegen_);
5813   if (intrinsic.TryDispatch(invoke)) {
5814     return;
5815   }
5816 
5817   HandleInvoke(invoke);
5818 }
5819 
VisitInvokePolymorphic(HInvokePolymorphic * invoke)5820 void LocationsBuilderMIPS64::VisitInvokePolymorphic(HInvokePolymorphic* invoke) {
5821   HandleInvoke(invoke);
5822 }
5823 
VisitInvokePolymorphic(HInvokePolymorphic * invoke)5824 void InstructionCodeGeneratorMIPS64::VisitInvokePolymorphic(HInvokePolymorphic* invoke) {
5825   codegen_->GenerateInvokePolymorphicCall(invoke);
5826 }
5827 
TryGenerateIntrinsicCode(HInvoke * invoke,CodeGeneratorMIPS64 * codegen)5828 static bool TryGenerateIntrinsicCode(HInvoke* invoke, CodeGeneratorMIPS64* codegen) {
5829   if (invoke->GetLocations()->Intrinsified()) {
5830     IntrinsicCodeGeneratorMIPS64 intrinsic(codegen);
5831     intrinsic.Dispatch(invoke);
5832     return true;
5833   }
5834   return false;
5835 }
5836 
GetSupportedLoadStringKind(HLoadString::LoadKind desired_string_load_kind)5837 HLoadString::LoadKind CodeGeneratorMIPS64::GetSupportedLoadStringKind(
5838     HLoadString::LoadKind desired_string_load_kind) {
5839   bool fallback_load = false;
5840   switch (desired_string_load_kind) {
5841     case HLoadString::LoadKind::kBootImageLinkTimePcRelative:
5842     case HLoadString::LoadKind::kBootImageInternTable:
5843     case HLoadString::LoadKind::kBssEntry:
5844       DCHECK(!Runtime::Current()->UseJitCompilation());
5845       break;
5846     case HLoadString::LoadKind::kJitTableAddress:
5847       DCHECK(Runtime::Current()->UseJitCompilation());
5848       break;
5849     case HLoadString::LoadKind::kBootImageAddress:
5850     case HLoadString::LoadKind::kRuntimeCall:
5851       break;
5852   }
5853   if (fallback_load) {
5854     desired_string_load_kind = HLoadString::LoadKind::kRuntimeCall;
5855   }
5856   return desired_string_load_kind;
5857 }
5858 
GetSupportedLoadClassKind(HLoadClass::LoadKind desired_class_load_kind)5859 HLoadClass::LoadKind CodeGeneratorMIPS64::GetSupportedLoadClassKind(
5860     HLoadClass::LoadKind desired_class_load_kind) {
5861   bool fallback_load = false;
5862   switch (desired_class_load_kind) {
5863     case HLoadClass::LoadKind::kInvalid:
5864       LOG(FATAL) << "UNREACHABLE";
5865       UNREACHABLE();
5866     case HLoadClass::LoadKind::kReferrersClass:
5867       break;
5868     case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
5869     case HLoadClass::LoadKind::kBootImageClassTable:
5870     case HLoadClass::LoadKind::kBssEntry:
5871       DCHECK(!Runtime::Current()->UseJitCompilation());
5872       break;
5873     case HLoadClass::LoadKind::kJitTableAddress:
5874       DCHECK(Runtime::Current()->UseJitCompilation());
5875       break;
5876     case HLoadClass::LoadKind::kBootImageAddress:
5877     case HLoadClass::LoadKind::kRuntimeCall:
5878       break;
5879   }
5880   if (fallback_load) {
5881     desired_class_load_kind = HLoadClass::LoadKind::kRuntimeCall;
5882   }
5883   return desired_class_load_kind;
5884 }
5885 
GetSupportedInvokeStaticOrDirectDispatch(const HInvokeStaticOrDirect::DispatchInfo & desired_dispatch_info,HInvokeStaticOrDirect * invoke ATTRIBUTE_UNUSED)5886 HInvokeStaticOrDirect::DispatchInfo CodeGeneratorMIPS64::GetSupportedInvokeStaticOrDirectDispatch(
5887       const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
5888       HInvokeStaticOrDirect* invoke ATTRIBUTE_UNUSED) {
5889   // On MIPS64 we support all dispatch types.
5890   return desired_dispatch_info;
5891 }
5892 
GenerateStaticOrDirectCall(HInvokeStaticOrDirect * invoke,Location temp,SlowPathCode * slow_path)5893 void CodeGeneratorMIPS64::GenerateStaticOrDirectCall(
5894     HInvokeStaticOrDirect* invoke, Location temp, SlowPathCode* slow_path) {
5895   // All registers are assumed to be correctly set up per the calling convention.
5896   Location callee_method = temp;  // For all kinds except kRecursive, callee will be in temp.
5897   HInvokeStaticOrDirect::MethodLoadKind method_load_kind = invoke->GetMethodLoadKind();
5898   HInvokeStaticOrDirect::CodePtrLocation code_ptr_location = invoke->GetCodePtrLocation();
5899 
5900   switch (method_load_kind) {
5901     case HInvokeStaticOrDirect::MethodLoadKind::kStringInit: {
5902       // temp = thread->string_init_entrypoint
5903       uint32_t offset =
5904           GetThreadOffset<kMips64PointerSize>(invoke->GetStringInitEntryPoint()).Int32Value();
5905       __ LoadFromOffset(kLoadDoubleword,
5906                         temp.AsRegister<GpuRegister>(),
5907                         TR,
5908                         offset);
5909       break;
5910     }
5911     case HInvokeStaticOrDirect::MethodLoadKind::kRecursive:
5912       callee_method = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex());
5913       break;
5914     case HInvokeStaticOrDirect::MethodLoadKind::kBootImageLinkTimePcRelative: {
5915       DCHECK(GetCompilerOptions().IsBootImage());
5916       CodeGeneratorMIPS64::PcRelativePatchInfo* info_high =
5917           NewBootImageMethodPatch(invoke->GetTargetMethod());
5918       CodeGeneratorMIPS64::PcRelativePatchInfo* info_low =
5919           NewBootImageMethodPatch(invoke->GetTargetMethod(), info_high);
5920       EmitPcRelativeAddressPlaceholderHigh(info_high, AT, info_low);
5921       __ Daddiu(temp.AsRegister<GpuRegister>(), AT, /* placeholder */ 0x5678);
5922       break;
5923     }
5924     case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress:
5925       __ LoadLiteral(temp.AsRegister<GpuRegister>(),
5926                      kLoadDoubleword,
5927                      DeduplicateUint64Literal(invoke->GetMethodAddress()));
5928       break;
5929     case HInvokeStaticOrDirect::MethodLoadKind::kBssEntry: {
5930       PcRelativePatchInfo* info_high = NewMethodBssEntryPatch(
5931           MethodReference(&GetGraph()->GetDexFile(), invoke->GetDexMethodIndex()));
5932       PcRelativePatchInfo* info_low = NewMethodBssEntryPatch(
5933           MethodReference(&GetGraph()->GetDexFile(), invoke->GetDexMethodIndex()), info_high);
5934       EmitPcRelativeAddressPlaceholderHigh(info_high, AT, info_low);
5935       __ Ld(temp.AsRegister<GpuRegister>(), AT, /* placeholder */ 0x5678);
5936       break;
5937     }
5938     case HInvokeStaticOrDirect::MethodLoadKind::kRuntimeCall: {
5939       GenerateInvokeStaticOrDirectRuntimeCall(invoke, temp, slow_path);
5940       return;  // No code pointer retrieval; the runtime performs the call directly.
5941     }
5942   }
5943 
5944   switch (code_ptr_location) {
5945     case HInvokeStaticOrDirect::CodePtrLocation::kCallSelf:
5946       __ Balc(&frame_entry_label_);
5947       break;
5948     case HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod:
5949       // T9 = callee_method->entry_point_from_quick_compiled_code_;
5950       __ LoadFromOffset(kLoadDoubleword,
5951                         T9,
5952                         callee_method.AsRegister<GpuRegister>(),
5953                         ArtMethod::EntryPointFromQuickCompiledCodeOffset(
5954                             kMips64PointerSize).Int32Value());
5955       // T9()
5956       __ Jalr(T9);
5957       __ Nop();
5958       break;
5959   }
5960   RecordPcInfo(invoke, invoke->GetDexPc(), slow_path);
5961 
5962   DCHECK(!IsLeafMethod());
5963 }
5964 
VisitInvokeStaticOrDirect(HInvokeStaticOrDirect * invoke)5965 void InstructionCodeGeneratorMIPS64::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
5966   // Explicit clinit checks triggered by static invokes must have been pruned by
5967   // art::PrepareForRegisterAllocation.
5968   DCHECK(!invoke->IsStaticWithExplicitClinitCheck());
5969 
5970   if (TryGenerateIntrinsicCode(invoke, codegen_)) {
5971     return;
5972   }
5973 
5974   LocationSummary* locations = invoke->GetLocations();
5975   codegen_->GenerateStaticOrDirectCall(invoke,
5976                                        locations->HasTemps()
5977                                            ? locations->GetTemp(0)
5978                                            : Location::NoLocation());
5979 }
5980 
GenerateVirtualCall(HInvokeVirtual * invoke,Location temp_location,SlowPathCode * slow_path)5981 void CodeGeneratorMIPS64::GenerateVirtualCall(
5982     HInvokeVirtual* invoke, Location temp_location, SlowPathCode* slow_path) {
5983   // Use the calling convention instead of the location of the receiver, as
5984   // intrinsics may have put the receiver in a different register. In the intrinsics
5985   // slow path, the arguments have been moved to the right place, so here we are
5986   // guaranteed that the receiver is the first register of the calling convention.
5987   InvokeDexCallingConvention calling_convention;
5988   GpuRegister receiver = calling_convention.GetRegisterAt(0);
5989 
5990   GpuRegister temp = temp_location.AsRegister<GpuRegister>();
5991   size_t method_offset = mirror::Class::EmbeddedVTableEntryOffset(
5992       invoke->GetVTableIndex(), kMips64PointerSize).SizeValue();
5993   uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
5994   Offset entry_point = ArtMethod::EntryPointFromQuickCompiledCodeOffset(kMips64PointerSize);
5995 
5996   // temp = object->GetClass();
5997   __ LoadFromOffset(kLoadUnsignedWord, temp, receiver, class_offset);
5998   MaybeRecordImplicitNullCheck(invoke);
5999   // Instead of simply (possibly) unpoisoning `temp` here, we should
6000   // emit a read barrier for the previous class reference load.
6001   // However this is not required in practice, as this is an
6002   // intermediate/temporary reference and because the current
6003   // concurrent copying collector keeps the from-space memory
6004   // intact/accessible until the end of the marking phase (the
6005   // concurrent copying collector may not in the future).
6006   __ MaybeUnpoisonHeapReference(temp);
6007   // temp = temp->GetMethodAt(method_offset);
6008   __ LoadFromOffset(kLoadDoubleword, temp, temp, method_offset);
6009   // T9 = temp->GetEntryPoint();
6010   __ LoadFromOffset(kLoadDoubleword, T9, temp, entry_point.Int32Value());
6011   // T9();
6012   __ Jalr(T9);
6013   __ Nop();
6014   RecordPcInfo(invoke, invoke->GetDexPc(), slow_path);
6015 }
6016 
VisitInvokeVirtual(HInvokeVirtual * invoke)6017 void InstructionCodeGeneratorMIPS64::VisitInvokeVirtual(HInvokeVirtual* invoke) {
6018   if (TryGenerateIntrinsicCode(invoke, codegen_)) {
6019     return;
6020   }
6021 
6022   codegen_->GenerateVirtualCall(invoke, invoke->GetLocations()->GetTemp(0));
6023   DCHECK(!codegen_->IsLeafMethod());
6024 }
6025 
VisitLoadClass(HLoadClass * cls)6026 void LocationsBuilderMIPS64::VisitLoadClass(HLoadClass* cls) {
6027   HLoadClass::LoadKind load_kind = cls->GetLoadKind();
6028   if (load_kind == HLoadClass::LoadKind::kRuntimeCall) {
6029     InvokeRuntimeCallingConvention calling_convention;
6030     Location loc = Location::RegisterLocation(calling_convention.GetRegisterAt(0));
6031     CodeGenerator::CreateLoadClassRuntimeCallLocationSummary(cls, loc, loc);
6032     return;
6033   }
6034   DCHECK(!cls->NeedsAccessCheck());
6035 
6036   const bool requires_read_barrier = kEmitCompilerReadBarrier && !cls->IsInBootImage();
6037   LocationSummary::CallKind call_kind = (cls->NeedsEnvironment() || requires_read_barrier)
6038       ? LocationSummary::kCallOnSlowPath
6039       : LocationSummary::kNoCall;
6040   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(cls, call_kind);
6041   if (kUseBakerReadBarrier && requires_read_barrier && !cls->NeedsEnvironment()) {
6042     locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty());  // No caller-save registers.
6043   }
6044   if (load_kind == HLoadClass::LoadKind::kReferrersClass) {
6045     locations->SetInAt(0, Location::RequiresRegister());
6046   }
6047   locations->SetOut(Location::RequiresRegister());
6048   if (load_kind == HLoadClass::LoadKind::kBssEntry) {
6049     if (!kUseReadBarrier || kUseBakerReadBarrier) {
6050       // Rely on the type resolution or initialization and marking to save everything we need.
6051       RegisterSet caller_saves = RegisterSet::Empty();
6052       InvokeRuntimeCallingConvention calling_convention;
6053       caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
6054       locations->SetCustomSlowPathCallerSaves(caller_saves);
6055     } else {
6056       // For non-Baker read barriers we have a temp-clobbering call.
6057     }
6058   }
6059 }
6060 
6061 // NO_THREAD_SAFETY_ANALYSIS as we manipulate handles whose internal object we know does not
6062 // move.
VisitLoadClass(HLoadClass * cls)6063 void InstructionCodeGeneratorMIPS64::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAFETY_ANALYSIS {
6064   HLoadClass::LoadKind load_kind = cls->GetLoadKind();
6065   if (load_kind == HLoadClass::LoadKind::kRuntimeCall) {
6066     codegen_->GenerateLoadClassRuntimeCall(cls);
6067     return;
6068   }
6069   DCHECK(!cls->NeedsAccessCheck());
6070 
6071   LocationSummary* locations = cls->GetLocations();
6072   Location out_loc = locations->Out();
6073   GpuRegister out = out_loc.AsRegister<GpuRegister>();
6074   GpuRegister current_method_reg = ZERO;
6075   if (load_kind == HLoadClass::LoadKind::kReferrersClass ||
6076       load_kind == HLoadClass::LoadKind::kRuntimeCall) {
6077       current_method_reg = locations->InAt(0).AsRegister<GpuRegister>();
6078   }
6079 
6080   const ReadBarrierOption read_barrier_option = cls->IsInBootImage()
6081       ? kWithoutReadBarrier
6082       : kCompilerReadBarrierOption;
6083   bool generate_null_check = false;
6084   switch (load_kind) {
6085     case HLoadClass::LoadKind::kReferrersClass:
6086       DCHECK(!cls->CanCallRuntime());
6087       DCHECK(!cls->MustGenerateClinitCheck());
6088       // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_
6089       GenerateGcRootFieldLoad(cls,
6090                               out_loc,
6091                               current_method_reg,
6092                               ArtMethod::DeclaringClassOffset().Int32Value(),
6093                               read_barrier_option);
6094       break;
6095     case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: {
6096       DCHECK(codegen_->GetCompilerOptions().IsBootImage());
6097       DCHECK_EQ(read_barrier_option, kWithoutReadBarrier);
6098       CodeGeneratorMIPS64::PcRelativePatchInfo* info_high =
6099           codegen_->NewBootImageTypePatch(cls->GetDexFile(), cls->GetTypeIndex());
6100       CodeGeneratorMIPS64::PcRelativePatchInfo* info_low =
6101           codegen_->NewBootImageTypePatch(cls->GetDexFile(), cls->GetTypeIndex(), info_high);
6102       codegen_->EmitPcRelativeAddressPlaceholderHigh(info_high, AT, info_low);
6103       __ Daddiu(out, AT, /* placeholder */ 0x5678);
6104       break;
6105     }
6106     case HLoadClass::LoadKind::kBootImageAddress: {
6107       DCHECK_EQ(read_barrier_option, kWithoutReadBarrier);
6108       uint32_t address = dchecked_integral_cast<uint32_t>(
6109           reinterpret_cast<uintptr_t>(cls->GetClass().Get()));
6110       DCHECK_NE(address, 0u);
6111       __ LoadLiteral(out,
6112                      kLoadUnsignedWord,
6113                      codegen_->DeduplicateBootImageAddressLiteral(address));
6114       break;
6115     }
6116     case HLoadClass::LoadKind::kBootImageClassTable: {
6117       DCHECK(!codegen_->GetCompilerOptions().IsBootImage());
6118       CodeGeneratorMIPS64::PcRelativePatchInfo* info_high =
6119           codegen_->NewBootImageTypePatch(cls->GetDexFile(), cls->GetTypeIndex());
6120       CodeGeneratorMIPS64::PcRelativePatchInfo* info_low =
6121           codegen_->NewBootImageTypePatch(cls->GetDexFile(), cls->GetTypeIndex(), info_high);
6122       codegen_->EmitPcRelativeAddressPlaceholderHigh(info_high, AT, info_low);
6123       __ Lwu(out, AT, /* placeholder */ 0x5678);
6124       // Extract the reference from the slot data, i.e. clear the hash bits.
6125       int32_t masked_hash = ClassTable::TableSlot::MaskHash(
6126           ComputeModifiedUtf8Hash(cls->GetDexFile().StringByTypeIdx(cls->GetTypeIndex())));
6127       if (masked_hash != 0) {
6128         __ Daddiu(out, out, -masked_hash);
6129       }
6130       break;
6131     }
6132     case HLoadClass::LoadKind::kBssEntry: {
6133       CodeGeneratorMIPS64::PcRelativePatchInfo* bss_info_high =
6134           codegen_->NewTypeBssEntryPatch(cls->GetDexFile(), cls->GetTypeIndex());
6135       CodeGeneratorMIPS64::PcRelativePatchInfo* info_low =
6136           codegen_->NewTypeBssEntryPatch(cls->GetDexFile(), cls->GetTypeIndex(), bss_info_high);
6137       codegen_->EmitPcRelativeAddressPlaceholderHigh(bss_info_high, out);
6138       GenerateGcRootFieldLoad(cls,
6139                               out_loc,
6140                               out,
6141                               /* placeholder */ 0x5678,
6142                               read_barrier_option,
6143                               &info_low->label);
6144       generate_null_check = true;
6145       break;
6146     }
6147     case HLoadClass::LoadKind::kJitTableAddress:
6148       __ LoadLiteral(out,
6149                      kLoadUnsignedWord,
6150                      codegen_->DeduplicateJitClassLiteral(cls->GetDexFile(),
6151                                                           cls->GetTypeIndex(),
6152                                                           cls->GetClass()));
6153       GenerateGcRootFieldLoad(cls, out_loc, out, 0, read_barrier_option);
6154       break;
6155     case HLoadClass::LoadKind::kRuntimeCall:
6156     case HLoadClass::LoadKind::kInvalid:
6157       LOG(FATAL) << "UNREACHABLE";
6158       UNREACHABLE();
6159   }
6160 
6161   if (generate_null_check || cls->MustGenerateClinitCheck()) {
6162     DCHECK(cls->CanCallRuntime());
6163     SlowPathCodeMIPS64* slow_path = new (codegen_->GetScopedAllocator()) LoadClassSlowPathMIPS64(
6164         cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck());
6165     codegen_->AddSlowPath(slow_path);
6166     if (generate_null_check) {
6167       __ Beqzc(out, slow_path->GetEntryLabel());
6168     }
6169     if (cls->MustGenerateClinitCheck()) {
6170       GenerateClassInitializationCheck(slow_path, out);
6171     } else {
6172       __ Bind(slow_path->GetExitLabel());
6173     }
6174   }
6175 }
6176 
GetExceptionTlsOffset()6177 static int32_t GetExceptionTlsOffset() {
6178   return Thread::ExceptionOffset<kMips64PointerSize>().Int32Value();
6179 }
6180 
VisitLoadException(HLoadException * load)6181 void LocationsBuilderMIPS64::VisitLoadException(HLoadException* load) {
6182   LocationSummary* locations =
6183       new (GetGraph()->GetAllocator()) LocationSummary(load, LocationSummary::kNoCall);
6184   locations->SetOut(Location::RequiresRegister());
6185 }
6186 
VisitLoadException(HLoadException * load)6187 void InstructionCodeGeneratorMIPS64::VisitLoadException(HLoadException* load) {
6188   GpuRegister out = load->GetLocations()->Out().AsRegister<GpuRegister>();
6189   __ LoadFromOffset(kLoadUnsignedWord, out, TR, GetExceptionTlsOffset());
6190 }
6191 
VisitClearException(HClearException * clear)6192 void LocationsBuilderMIPS64::VisitClearException(HClearException* clear) {
6193   new (GetGraph()->GetAllocator()) LocationSummary(clear, LocationSummary::kNoCall);
6194 }
6195 
VisitClearException(HClearException * clear ATTRIBUTE_UNUSED)6196 void InstructionCodeGeneratorMIPS64::VisitClearException(HClearException* clear ATTRIBUTE_UNUSED) {
6197   __ StoreToOffset(kStoreWord, ZERO, TR, GetExceptionTlsOffset());
6198 }
6199 
VisitLoadString(HLoadString * load)6200 void LocationsBuilderMIPS64::VisitLoadString(HLoadString* load) {
6201   HLoadString::LoadKind load_kind = load->GetLoadKind();
6202   LocationSummary::CallKind call_kind = CodeGenerator::GetLoadStringCallKind(load);
6203   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(load, call_kind);
6204   if (load_kind == HLoadString::LoadKind::kRuntimeCall) {
6205     InvokeRuntimeCallingConvention calling_convention;
6206     locations->SetOut(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
6207   } else {
6208     locations->SetOut(Location::RequiresRegister());
6209     if (load_kind == HLoadString::LoadKind::kBssEntry) {
6210       if (!kUseReadBarrier || kUseBakerReadBarrier) {
6211         // Rely on the pResolveString and marking to save everything we need.
6212         RegisterSet caller_saves = RegisterSet::Empty();
6213         InvokeRuntimeCallingConvention calling_convention;
6214         caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
6215         locations->SetCustomSlowPathCallerSaves(caller_saves);
6216       } else {
6217         // For non-Baker read barriers we have a temp-clobbering call.
6218       }
6219     }
6220   }
6221 }
6222 
6223 // NO_THREAD_SAFETY_ANALYSIS as we manipulate handles whose internal object we know does not
6224 // move.
VisitLoadString(HLoadString * load)6225 void InstructionCodeGeneratorMIPS64::VisitLoadString(HLoadString* load) NO_THREAD_SAFETY_ANALYSIS {
6226   HLoadString::LoadKind load_kind = load->GetLoadKind();
6227   LocationSummary* locations = load->GetLocations();
6228   Location out_loc = locations->Out();
6229   GpuRegister out = out_loc.AsRegister<GpuRegister>();
6230 
6231   switch (load_kind) {
6232     case HLoadString::LoadKind::kBootImageLinkTimePcRelative: {
6233       DCHECK(codegen_->GetCompilerOptions().IsBootImage());
6234       CodeGeneratorMIPS64::PcRelativePatchInfo* info_high =
6235           codegen_->NewBootImageStringPatch(load->GetDexFile(), load->GetStringIndex());
6236       CodeGeneratorMIPS64::PcRelativePatchInfo* info_low =
6237           codegen_->NewBootImageStringPatch(load->GetDexFile(), load->GetStringIndex(), info_high);
6238       codegen_->EmitPcRelativeAddressPlaceholderHigh(info_high, AT, info_low);
6239       __ Daddiu(out, AT, /* placeholder */ 0x5678);
6240       return;
6241     }
6242     case HLoadString::LoadKind::kBootImageAddress: {
6243       uint32_t address = dchecked_integral_cast<uint32_t>(
6244           reinterpret_cast<uintptr_t>(load->GetString().Get()));
6245       DCHECK_NE(address, 0u);
6246       __ LoadLiteral(out,
6247                      kLoadUnsignedWord,
6248                      codegen_->DeduplicateBootImageAddressLiteral(address));
6249       return;
6250     }
6251     case HLoadString::LoadKind::kBootImageInternTable: {
6252       DCHECK(!codegen_->GetCompilerOptions().IsBootImage());
6253       CodeGeneratorMIPS64::PcRelativePatchInfo* info_high =
6254           codegen_->NewBootImageStringPatch(load->GetDexFile(), load->GetStringIndex());
6255       CodeGeneratorMIPS64::PcRelativePatchInfo* info_low =
6256           codegen_->NewBootImageStringPatch(load->GetDexFile(), load->GetStringIndex(), info_high);
6257       codegen_->EmitPcRelativeAddressPlaceholderHigh(info_high, AT, info_low);
6258       __ Lwu(out, AT, /* placeholder */ 0x5678);
6259       return;
6260     }
6261     case HLoadString::LoadKind::kBssEntry: {
6262       DCHECK(!codegen_->GetCompilerOptions().IsBootImage());
6263       CodeGeneratorMIPS64::PcRelativePatchInfo* info_high =
6264           codegen_->NewStringBssEntryPatch(load->GetDexFile(), load->GetStringIndex());
6265       CodeGeneratorMIPS64::PcRelativePatchInfo* info_low =
6266           codegen_->NewStringBssEntryPatch(load->GetDexFile(), load->GetStringIndex(), info_high);
6267       codegen_->EmitPcRelativeAddressPlaceholderHigh(info_high, out);
6268       GenerateGcRootFieldLoad(load,
6269                               out_loc,
6270                               out,
6271                               /* placeholder */ 0x5678,
6272                               kCompilerReadBarrierOption,
6273                               &info_low->label);
6274       SlowPathCodeMIPS64* slow_path =
6275           new (codegen_->GetScopedAllocator()) LoadStringSlowPathMIPS64(load);
6276       codegen_->AddSlowPath(slow_path);
6277       __ Beqzc(out, slow_path->GetEntryLabel());
6278       __ Bind(slow_path->GetExitLabel());
6279       return;
6280     }
6281     case HLoadString::LoadKind::kJitTableAddress:
6282       __ LoadLiteral(out,
6283                      kLoadUnsignedWord,
6284                      codegen_->DeduplicateJitStringLiteral(load->GetDexFile(),
6285                                                            load->GetStringIndex(),
6286                                                            load->GetString()));
6287       GenerateGcRootFieldLoad(load, out_loc, out, 0, kCompilerReadBarrierOption);
6288       return;
6289     default:
6290       break;
6291   }
6292 
6293   // TODO: Re-add the compiler code to do string dex cache lookup again.
6294   DCHECK(load_kind == HLoadString::LoadKind::kRuntimeCall);
6295   InvokeRuntimeCallingConvention calling_convention;
6296   DCHECK_EQ(calling_convention.GetRegisterAt(0), out);
6297   __ LoadConst32(calling_convention.GetRegisterAt(0), load->GetStringIndex().index_);
6298   codegen_->InvokeRuntime(kQuickResolveString, load, load->GetDexPc());
6299   CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>();
6300 }
6301 
VisitLongConstant(HLongConstant * constant)6302 void LocationsBuilderMIPS64::VisitLongConstant(HLongConstant* constant) {
6303   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(constant);
6304   locations->SetOut(Location::ConstantLocation(constant));
6305 }
6306 
VisitLongConstant(HLongConstant * constant ATTRIBUTE_UNUSED)6307 void InstructionCodeGeneratorMIPS64::VisitLongConstant(HLongConstant* constant ATTRIBUTE_UNUSED) {
6308   // Will be generated at use site.
6309 }
6310 
VisitMonitorOperation(HMonitorOperation * instruction)6311 void LocationsBuilderMIPS64::VisitMonitorOperation(HMonitorOperation* instruction) {
6312   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(
6313       instruction, LocationSummary::kCallOnMainOnly);
6314   InvokeRuntimeCallingConvention calling_convention;
6315   locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
6316 }
6317 
VisitMonitorOperation(HMonitorOperation * instruction)6318 void InstructionCodeGeneratorMIPS64::VisitMonitorOperation(HMonitorOperation* instruction) {
6319   codegen_->InvokeRuntime(instruction->IsEnter() ? kQuickLockObject : kQuickUnlockObject,
6320                           instruction,
6321                           instruction->GetDexPc());
6322   if (instruction->IsEnter()) {
6323     CheckEntrypointTypes<kQuickLockObject, void, mirror::Object*>();
6324   } else {
6325     CheckEntrypointTypes<kQuickUnlockObject, void, mirror::Object*>();
6326   }
6327 }
6328 
VisitMul(HMul * mul)6329 void LocationsBuilderMIPS64::VisitMul(HMul* mul) {
6330   LocationSummary* locations =
6331       new (GetGraph()->GetAllocator()) LocationSummary(mul, LocationSummary::kNoCall);
6332   switch (mul->GetResultType()) {
6333     case DataType::Type::kInt32:
6334     case DataType::Type::kInt64:
6335       locations->SetInAt(0, Location::RequiresRegister());
6336       locations->SetInAt(1, Location::RequiresRegister());
6337       locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
6338       break;
6339 
6340     case DataType::Type::kFloat32:
6341     case DataType::Type::kFloat64:
6342       locations->SetInAt(0, Location::RequiresFpuRegister());
6343       locations->SetInAt(1, Location::RequiresFpuRegister());
6344       locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
6345       break;
6346 
6347     default:
6348       LOG(FATAL) << "Unexpected mul type " << mul->GetResultType();
6349   }
6350 }
6351 
VisitMul(HMul * instruction)6352 void InstructionCodeGeneratorMIPS64::VisitMul(HMul* instruction) {
6353   DataType::Type type = instruction->GetType();
6354   LocationSummary* locations = instruction->GetLocations();
6355 
6356   switch (type) {
6357     case DataType::Type::kInt32:
6358     case DataType::Type::kInt64: {
6359       GpuRegister dst = locations->Out().AsRegister<GpuRegister>();
6360       GpuRegister lhs = locations->InAt(0).AsRegister<GpuRegister>();
6361       GpuRegister rhs = locations->InAt(1).AsRegister<GpuRegister>();
6362       if (type == DataType::Type::kInt32)
6363         __ MulR6(dst, lhs, rhs);
6364       else
6365         __ Dmul(dst, lhs, rhs);
6366       break;
6367     }
6368     case DataType::Type::kFloat32:
6369     case DataType::Type::kFloat64: {
6370       FpuRegister dst = locations->Out().AsFpuRegister<FpuRegister>();
6371       FpuRegister lhs = locations->InAt(0).AsFpuRegister<FpuRegister>();
6372       FpuRegister rhs = locations->InAt(1).AsFpuRegister<FpuRegister>();
6373       if (type == DataType::Type::kFloat32)
6374         __ MulS(dst, lhs, rhs);
6375       else
6376         __ MulD(dst, lhs, rhs);
6377       break;
6378     }
6379     default:
6380       LOG(FATAL) << "Unexpected mul type " << type;
6381   }
6382 }
6383 
VisitNeg(HNeg * neg)6384 void LocationsBuilderMIPS64::VisitNeg(HNeg* neg) {
6385   LocationSummary* locations =
6386       new (GetGraph()->GetAllocator()) LocationSummary(neg, LocationSummary::kNoCall);
6387   switch (neg->GetResultType()) {
6388     case DataType::Type::kInt32:
6389     case DataType::Type::kInt64:
6390       locations->SetInAt(0, Location::RequiresRegister());
6391       locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
6392       break;
6393 
6394     case DataType::Type::kFloat32:
6395     case DataType::Type::kFloat64:
6396       locations->SetInAt(0, Location::RequiresFpuRegister());
6397       locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
6398       break;
6399 
6400     default:
6401       LOG(FATAL) << "Unexpected neg type " << neg->GetResultType();
6402   }
6403 }
6404 
VisitNeg(HNeg * instruction)6405 void InstructionCodeGeneratorMIPS64::VisitNeg(HNeg* instruction) {
6406   DataType::Type type = instruction->GetType();
6407   LocationSummary* locations = instruction->GetLocations();
6408 
6409   switch (type) {
6410     case DataType::Type::kInt32:
6411     case DataType::Type::kInt64: {
6412       GpuRegister dst = locations->Out().AsRegister<GpuRegister>();
6413       GpuRegister src = locations->InAt(0).AsRegister<GpuRegister>();
6414       if (type == DataType::Type::kInt32)
6415         __ Subu(dst, ZERO, src);
6416       else
6417         __ Dsubu(dst, ZERO, src);
6418       break;
6419     }
6420     case DataType::Type::kFloat32:
6421     case DataType::Type::kFloat64: {
6422       FpuRegister dst = locations->Out().AsFpuRegister<FpuRegister>();
6423       FpuRegister src = locations->InAt(0).AsFpuRegister<FpuRegister>();
6424       if (type == DataType::Type::kFloat32)
6425         __ NegS(dst, src);
6426       else
6427         __ NegD(dst, src);
6428       break;
6429     }
6430     default:
6431       LOG(FATAL) << "Unexpected neg type " << type;
6432   }
6433 }
6434 
VisitNewArray(HNewArray * instruction)6435 void LocationsBuilderMIPS64::VisitNewArray(HNewArray* instruction) {
6436   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(
6437       instruction, LocationSummary::kCallOnMainOnly);
6438   InvokeRuntimeCallingConvention calling_convention;
6439   locations->SetOut(calling_convention.GetReturnLocation(DataType::Type::kReference));
6440   locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
6441   locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
6442 }
6443 
VisitNewArray(HNewArray * instruction)6444 void InstructionCodeGeneratorMIPS64::VisitNewArray(HNewArray* instruction) {
6445   // Note: if heap poisoning is enabled, the entry point takes care
6446   // of poisoning the reference.
6447   QuickEntrypointEnum entrypoint =
6448       CodeGenerator::GetArrayAllocationEntrypoint(instruction->GetLoadClass()->GetClass());
6449   codegen_->InvokeRuntime(entrypoint, instruction, instruction->GetDexPc());
6450   CheckEntrypointTypes<kQuickAllocArrayResolved, void*, mirror::Class*, int32_t>();
6451   DCHECK(!codegen_->IsLeafMethod());
6452 }
6453 
VisitNewInstance(HNewInstance * instruction)6454 void LocationsBuilderMIPS64::VisitNewInstance(HNewInstance* instruction) {
6455   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(
6456       instruction, LocationSummary::kCallOnMainOnly);
6457   InvokeRuntimeCallingConvention calling_convention;
6458   if (instruction->IsStringAlloc()) {
6459     locations->AddTemp(Location::RegisterLocation(kMethodRegisterArgument));
6460   } else {
6461     locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
6462   }
6463   locations->SetOut(calling_convention.GetReturnLocation(DataType::Type::kReference));
6464 }
6465 
VisitNewInstance(HNewInstance * instruction)6466 void InstructionCodeGeneratorMIPS64::VisitNewInstance(HNewInstance* instruction) {
6467   // Note: if heap poisoning is enabled, the entry point takes care
6468   // of poisoning the reference.
6469   if (instruction->IsStringAlloc()) {
6470     // String is allocated through StringFactory. Call NewEmptyString entry point.
6471     GpuRegister temp = instruction->GetLocations()->GetTemp(0).AsRegister<GpuRegister>();
6472     MemberOffset code_offset =
6473         ArtMethod::EntryPointFromQuickCompiledCodeOffset(kMips64PointerSize);
6474     __ LoadFromOffset(kLoadDoubleword, temp, TR, QUICK_ENTRY_POINT(pNewEmptyString));
6475     __ LoadFromOffset(kLoadDoubleword, T9, temp, code_offset.Int32Value());
6476     __ Jalr(T9);
6477     __ Nop();
6478     codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
6479   } else {
6480     codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc());
6481     CheckEntrypointTypes<kQuickAllocObjectWithChecks, void*, mirror::Class*>();
6482   }
6483 }
6484 
VisitNot(HNot * instruction)6485 void LocationsBuilderMIPS64::VisitNot(HNot* instruction) {
6486   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
6487   locations->SetInAt(0, Location::RequiresRegister());
6488   locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
6489 }
6490 
VisitNot(HNot * instruction)6491 void InstructionCodeGeneratorMIPS64::VisitNot(HNot* instruction) {
6492   DataType::Type type = instruction->GetType();
6493   LocationSummary* locations = instruction->GetLocations();
6494 
6495   switch (type) {
6496     case DataType::Type::kInt32:
6497     case DataType::Type::kInt64: {
6498       GpuRegister dst = locations->Out().AsRegister<GpuRegister>();
6499       GpuRegister src = locations->InAt(0).AsRegister<GpuRegister>();
6500       __ Nor(dst, src, ZERO);
6501       break;
6502     }
6503 
6504     default:
6505       LOG(FATAL) << "Unexpected type for not operation " << instruction->GetResultType();
6506   }
6507 }
6508 
VisitBooleanNot(HBooleanNot * instruction)6509 void LocationsBuilderMIPS64::VisitBooleanNot(HBooleanNot* instruction) {
6510   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
6511   locations->SetInAt(0, Location::RequiresRegister());
6512   locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
6513 }
6514 
VisitBooleanNot(HBooleanNot * instruction)6515 void InstructionCodeGeneratorMIPS64::VisitBooleanNot(HBooleanNot* instruction) {
6516   LocationSummary* locations = instruction->GetLocations();
6517   __ Xori(locations->Out().AsRegister<GpuRegister>(),
6518           locations->InAt(0).AsRegister<GpuRegister>(),
6519           1);
6520 }
6521 
VisitNullCheck(HNullCheck * instruction)6522 void LocationsBuilderMIPS64::VisitNullCheck(HNullCheck* instruction) {
6523   LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction);
6524   locations->SetInAt(0, Location::RequiresRegister());
6525 }
6526 
GenerateImplicitNullCheck(HNullCheck * instruction)6527 void CodeGeneratorMIPS64::GenerateImplicitNullCheck(HNullCheck* instruction) {
6528   if (CanMoveNullCheckToUser(instruction)) {
6529     return;
6530   }
6531   Location obj = instruction->GetLocations()->InAt(0);
6532 
6533   __ Lw(ZERO, obj.AsRegister<GpuRegister>(), 0);
6534   RecordPcInfo(instruction, instruction->GetDexPc());
6535 }
6536 
GenerateExplicitNullCheck(HNullCheck * instruction)6537 void CodeGeneratorMIPS64::GenerateExplicitNullCheck(HNullCheck* instruction) {
6538   SlowPathCodeMIPS64* slow_path =
6539       new (GetScopedAllocator()) NullCheckSlowPathMIPS64(instruction);
6540   AddSlowPath(slow_path);
6541 
6542   Location obj = instruction->GetLocations()->InAt(0);
6543 
6544   __ Beqzc(obj.AsRegister<GpuRegister>(), slow_path->GetEntryLabel());
6545 }
6546 
VisitNullCheck(HNullCheck * instruction)6547 void InstructionCodeGeneratorMIPS64::VisitNullCheck(HNullCheck* instruction) {
6548   codegen_->GenerateNullCheck(instruction);
6549 }
6550 
VisitOr(HOr * instruction)6551 void LocationsBuilderMIPS64::VisitOr(HOr* instruction) {
6552   HandleBinaryOp(instruction);
6553 }
6554 
VisitOr(HOr * instruction)6555 void InstructionCodeGeneratorMIPS64::VisitOr(HOr* instruction) {
6556   HandleBinaryOp(instruction);
6557 }
6558 
VisitParallelMove(HParallelMove * instruction ATTRIBUTE_UNUSED)6559 void LocationsBuilderMIPS64::VisitParallelMove(HParallelMove* instruction ATTRIBUTE_UNUSED) {
6560   LOG(FATAL) << "Unreachable";
6561 }
6562 
VisitParallelMove(HParallelMove * instruction)6563 void InstructionCodeGeneratorMIPS64::VisitParallelMove(HParallelMove* instruction) {
6564   if (instruction->GetNext()->IsSuspendCheck() &&
6565       instruction->GetBlock()->GetLoopInformation() != nullptr) {
6566     HSuspendCheck* suspend_check = instruction->GetNext()->AsSuspendCheck();
6567     // The back edge will generate the suspend check.
6568     codegen_->ClearSpillSlotsFromLoopPhisInStackMap(suspend_check, instruction);
6569   }
6570 
6571   codegen_->GetMoveResolver()->EmitNativeCode(instruction);
6572 }
6573 
VisitParameterValue(HParameterValue * instruction)6574 void LocationsBuilderMIPS64::VisitParameterValue(HParameterValue* instruction) {
6575   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
6576   Location location = parameter_visitor_.GetNextLocation(instruction->GetType());
6577   if (location.IsStackSlot()) {
6578     location = Location::StackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
6579   } else if (location.IsDoubleStackSlot()) {
6580     location = Location::DoubleStackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
6581   }
6582   locations->SetOut(location);
6583 }
6584 
VisitParameterValue(HParameterValue * instruction ATTRIBUTE_UNUSED)6585 void InstructionCodeGeneratorMIPS64::VisitParameterValue(HParameterValue* instruction
6586                                                          ATTRIBUTE_UNUSED) {
6587   // Nothing to do, the parameter is already at its location.
6588 }
6589 
VisitCurrentMethod(HCurrentMethod * instruction)6590 void LocationsBuilderMIPS64::VisitCurrentMethod(HCurrentMethod* instruction) {
6591   LocationSummary* locations =
6592       new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
6593   locations->SetOut(Location::RegisterLocation(kMethodRegisterArgument));
6594 }
6595 
VisitCurrentMethod(HCurrentMethod * instruction ATTRIBUTE_UNUSED)6596 void InstructionCodeGeneratorMIPS64::VisitCurrentMethod(HCurrentMethod* instruction
6597                                                         ATTRIBUTE_UNUSED) {
6598   // Nothing to do, the method is already at its location.
6599 }
6600 
VisitPhi(HPhi * instruction)6601 void LocationsBuilderMIPS64::VisitPhi(HPhi* instruction) {
6602   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
6603   for (size_t i = 0, e = locations->GetInputCount(); i < e; ++i) {
6604     locations->SetInAt(i, Location::Any());
6605   }
6606   locations->SetOut(Location::Any());
6607 }
6608 
VisitPhi(HPhi * instruction ATTRIBUTE_UNUSED)6609 void InstructionCodeGeneratorMIPS64::VisitPhi(HPhi* instruction ATTRIBUTE_UNUSED) {
6610   LOG(FATAL) << "Unreachable";
6611 }
6612 
VisitRem(HRem * rem)6613 void LocationsBuilderMIPS64::VisitRem(HRem* rem) {
6614   DataType::Type type = rem->GetResultType();
6615   LocationSummary::CallKind call_kind =
6616       DataType::IsFloatingPointType(type) ? LocationSummary::kCallOnMainOnly
6617                                           : LocationSummary::kNoCall;
6618   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(rem, call_kind);
6619 
6620   switch (type) {
6621     case DataType::Type::kInt32:
6622     case DataType::Type::kInt64:
6623       locations->SetInAt(0, Location::RequiresRegister());
6624       locations->SetInAt(1, Location::RegisterOrConstant(rem->InputAt(1)));
6625       locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
6626       break;
6627 
6628     case DataType::Type::kFloat32:
6629     case DataType::Type::kFloat64: {
6630       InvokeRuntimeCallingConvention calling_convention;
6631       locations->SetInAt(0, Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(0)));
6632       locations->SetInAt(1, Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(1)));
6633       locations->SetOut(calling_convention.GetReturnLocation(type));
6634       break;
6635     }
6636 
6637     default:
6638       LOG(FATAL) << "Unexpected rem type " << type;
6639   }
6640 }
6641 
VisitRem(HRem * instruction)6642 void InstructionCodeGeneratorMIPS64::VisitRem(HRem* instruction) {
6643   DataType::Type type = instruction->GetType();
6644 
6645   switch (type) {
6646     case DataType::Type::kInt32:
6647     case DataType::Type::kInt64:
6648       GenerateDivRemIntegral(instruction);
6649       break;
6650 
6651     case DataType::Type::kFloat32:
6652     case DataType::Type::kFloat64: {
6653       QuickEntrypointEnum entrypoint =
6654           (type == DataType::Type::kFloat32) ? kQuickFmodf : kQuickFmod;
6655       codegen_->InvokeRuntime(entrypoint, instruction, instruction->GetDexPc());
6656       if (type == DataType::Type::kFloat32) {
6657         CheckEntrypointTypes<kQuickFmodf, float, float, float>();
6658       } else {
6659         CheckEntrypointTypes<kQuickFmod, double, double, double>();
6660       }
6661       break;
6662     }
6663     default:
6664       LOG(FATAL) << "Unexpected rem type " << type;
6665   }
6666 }
6667 
VisitConstructorFence(HConstructorFence * constructor_fence)6668 void LocationsBuilderMIPS64::VisitConstructorFence(HConstructorFence* constructor_fence) {
6669   constructor_fence->SetLocations(nullptr);
6670 }
6671 
VisitConstructorFence(HConstructorFence * constructor_fence ATTRIBUTE_UNUSED)6672 void InstructionCodeGeneratorMIPS64::VisitConstructorFence(
6673     HConstructorFence* constructor_fence ATTRIBUTE_UNUSED) {
6674   GenerateMemoryBarrier(MemBarrierKind::kStoreStore);
6675 }
6676 
VisitMemoryBarrier(HMemoryBarrier * memory_barrier)6677 void LocationsBuilderMIPS64::VisitMemoryBarrier(HMemoryBarrier* memory_barrier) {
6678   memory_barrier->SetLocations(nullptr);
6679 }
6680 
VisitMemoryBarrier(HMemoryBarrier * memory_barrier)6681 void InstructionCodeGeneratorMIPS64::VisitMemoryBarrier(HMemoryBarrier* memory_barrier) {
6682   GenerateMemoryBarrier(memory_barrier->GetBarrierKind());
6683 }
6684 
VisitReturn(HReturn * ret)6685 void LocationsBuilderMIPS64::VisitReturn(HReturn* ret) {
6686   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(ret);
6687   DataType::Type return_type = ret->InputAt(0)->GetType();
6688   locations->SetInAt(0, Mips64ReturnLocation(return_type));
6689 }
6690 
VisitReturn(HReturn * ret ATTRIBUTE_UNUSED)6691 void InstructionCodeGeneratorMIPS64::VisitReturn(HReturn* ret ATTRIBUTE_UNUSED) {
6692   codegen_->GenerateFrameExit();
6693 }
6694 
VisitReturnVoid(HReturnVoid * ret)6695 void LocationsBuilderMIPS64::VisitReturnVoid(HReturnVoid* ret) {
6696   ret->SetLocations(nullptr);
6697 }
6698 
VisitReturnVoid(HReturnVoid * ret ATTRIBUTE_UNUSED)6699 void InstructionCodeGeneratorMIPS64::VisitReturnVoid(HReturnVoid* ret ATTRIBUTE_UNUSED) {
6700   codegen_->GenerateFrameExit();
6701 }
6702 
VisitRor(HRor * ror)6703 void LocationsBuilderMIPS64::VisitRor(HRor* ror) {
6704   HandleShift(ror);
6705 }
6706 
VisitRor(HRor * ror)6707 void InstructionCodeGeneratorMIPS64::VisitRor(HRor* ror) {
6708   HandleShift(ror);
6709 }
6710 
VisitShl(HShl * shl)6711 void LocationsBuilderMIPS64::VisitShl(HShl* shl) {
6712   HandleShift(shl);
6713 }
6714 
VisitShl(HShl * shl)6715 void InstructionCodeGeneratorMIPS64::VisitShl(HShl* shl) {
6716   HandleShift(shl);
6717 }
6718 
VisitShr(HShr * shr)6719 void LocationsBuilderMIPS64::VisitShr(HShr* shr) {
6720   HandleShift(shr);
6721 }
6722 
VisitShr(HShr * shr)6723 void InstructionCodeGeneratorMIPS64::VisitShr(HShr* shr) {
6724   HandleShift(shr);
6725 }
6726 
VisitSub(HSub * instruction)6727 void LocationsBuilderMIPS64::VisitSub(HSub* instruction) {
6728   HandleBinaryOp(instruction);
6729 }
6730 
VisitSub(HSub * instruction)6731 void InstructionCodeGeneratorMIPS64::VisitSub(HSub* instruction) {
6732   HandleBinaryOp(instruction);
6733 }
6734 
VisitStaticFieldGet(HStaticFieldGet * instruction)6735 void LocationsBuilderMIPS64::VisitStaticFieldGet(HStaticFieldGet* instruction) {
6736   HandleFieldGet(instruction, instruction->GetFieldInfo());
6737 }
6738 
VisitStaticFieldGet(HStaticFieldGet * instruction)6739 void InstructionCodeGeneratorMIPS64::VisitStaticFieldGet(HStaticFieldGet* instruction) {
6740   HandleFieldGet(instruction, instruction->GetFieldInfo());
6741 }
6742 
VisitStaticFieldSet(HStaticFieldSet * instruction)6743 void LocationsBuilderMIPS64::VisitStaticFieldSet(HStaticFieldSet* instruction) {
6744   HandleFieldSet(instruction, instruction->GetFieldInfo());
6745 }
6746 
VisitStaticFieldSet(HStaticFieldSet * instruction)6747 void InstructionCodeGeneratorMIPS64::VisitStaticFieldSet(HStaticFieldSet* instruction) {
6748   HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
6749 }
6750 
VisitUnresolvedInstanceFieldGet(HUnresolvedInstanceFieldGet * instruction)6751 void LocationsBuilderMIPS64::VisitUnresolvedInstanceFieldGet(
6752     HUnresolvedInstanceFieldGet* instruction) {
6753   FieldAccessCallingConventionMIPS64 calling_convention;
6754   codegen_->CreateUnresolvedFieldLocationSummary(
6755       instruction, instruction->GetFieldType(), calling_convention);
6756 }
6757 
VisitUnresolvedInstanceFieldGet(HUnresolvedInstanceFieldGet * instruction)6758 void InstructionCodeGeneratorMIPS64::VisitUnresolvedInstanceFieldGet(
6759     HUnresolvedInstanceFieldGet* instruction) {
6760   FieldAccessCallingConventionMIPS64 calling_convention;
6761   codegen_->GenerateUnresolvedFieldAccess(instruction,
6762                                           instruction->GetFieldType(),
6763                                           instruction->GetFieldIndex(),
6764                                           instruction->GetDexPc(),
6765                                           calling_convention);
6766 }
6767 
VisitUnresolvedInstanceFieldSet(HUnresolvedInstanceFieldSet * instruction)6768 void LocationsBuilderMIPS64::VisitUnresolvedInstanceFieldSet(
6769     HUnresolvedInstanceFieldSet* instruction) {
6770   FieldAccessCallingConventionMIPS64 calling_convention;
6771   codegen_->CreateUnresolvedFieldLocationSummary(
6772       instruction, instruction->GetFieldType(), calling_convention);
6773 }
6774 
VisitUnresolvedInstanceFieldSet(HUnresolvedInstanceFieldSet * instruction)6775 void InstructionCodeGeneratorMIPS64::VisitUnresolvedInstanceFieldSet(
6776     HUnresolvedInstanceFieldSet* instruction) {
6777   FieldAccessCallingConventionMIPS64 calling_convention;
6778   codegen_->GenerateUnresolvedFieldAccess(instruction,
6779                                           instruction->GetFieldType(),
6780                                           instruction->GetFieldIndex(),
6781                                           instruction->GetDexPc(),
6782                                           calling_convention);
6783 }
6784 
VisitUnresolvedStaticFieldGet(HUnresolvedStaticFieldGet * instruction)6785 void LocationsBuilderMIPS64::VisitUnresolvedStaticFieldGet(
6786     HUnresolvedStaticFieldGet* instruction) {
6787   FieldAccessCallingConventionMIPS64 calling_convention;
6788   codegen_->CreateUnresolvedFieldLocationSummary(
6789       instruction, instruction->GetFieldType(), calling_convention);
6790 }
6791 
VisitUnresolvedStaticFieldGet(HUnresolvedStaticFieldGet * instruction)6792 void InstructionCodeGeneratorMIPS64::VisitUnresolvedStaticFieldGet(
6793     HUnresolvedStaticFieldGet* instruction) {
6794   FieldAccessCallingConventionMIPS64 calling_convention;
6795   codegen_->GenerateUnresolvedFieldAccess(instruction,
6796                                           instruction->GetFieldType(),
6797                                           instruction->GetFieldIndex(),
6798                                           instruction->GetDexPc(),
6799                                           calling_convention);
6800 }
6801 
VisitUnresolvedStaticFieldSet(HUnresolvedStaticFieldSet * instruction)6802 void LocationsBuilderMIPS64::VisitUnresolvedStaticFieldSet(
6803     HUnresolvedStaticFieldSet* instruction) {
6804   FieldAccessCallingConventionMIPS64 calling_convention;
6805   codegen_->CreateUnresolvedFieldLocationSummary(
6806       instruction, instruction->GetFieldType(), calling_convention);
6807 }
6808 
VisitUnresolvedStaticFieldSet(HUnresolvedStaticFieldSet * instruction)6809 void InstructionCodeGeneratorMIPS64::VisitUnresolvedStaticFieldSet(
6810     HUnresolvedStaticFieldSet* instruction) {
6811   FieldAccessCallingConventionMIPS64 calling_convention;
6812   codegen_->GenerateUnresolvedFieldAccess(instruction,
6813                                           instruction->GetFieldType(),
6814                                           instruction->GetFieldIndex(),
6815                                           instruction->GetDexPc(),
6816                                           calling_convention);
6817 }
6818 
VisitSuspendCheck(HSuspendCheck * instruction)6819 void LocationsBuilderMIPS64::VisitSuspendCheck(HSuspendCheck* instruction) {
6820   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(
6821       instruction, LocationSummary::kCallOnSlowPath);
6822   // In suspend check slow path, usually there are no caller-save registers at all.
6823   // If SIMD instructions are present, however, we force spilling all live SIMD
6824   // registers in full width (since the runtime only saves/restores lower part).
6825   locations->SetCustomSlowPathCallerSaves(
6826       GetGraph()->HasSIMD() ? RegisterSet::AllFpu() : RegisterSet::Empty());
6827 }
6828 
VisitSuspendCheck(HSuspendCheck * instruction)6829 void InstructionCodeGeneratorMIPS64::VisitSuspendCheck(HSuspendCheck* instruction) {
6830   HBasicBlock* block = instruction->GetBlock();
6831   if (block->GetLoopInformation() != nullptr) {
6832     DCHECK(block->GetLoopInformation()->GetSuspendCheck() == instruction);
6833     // The back edge will generate the suspend check.
6834     return;
6835   }
6836   if (block->IsEntryBlock() && instruction->GetNext()->IsGoto()) {
6837     // The goto will generate the suspend check.
6838     return;
6839   }
6840   GenerateSuspendCheck(instruction, nullptr);
6841 }
6842 
VisitThrow(HThrow * instruction)6843 void LocationsBuilderMIPS64::VisitThrow(HThrow* instruction) {
6844   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(
6845       instruction, LocationSummary::kCallOnMainOnly);
6846   InvokeRuntimeCallingConvention calling_convention;
6847   locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
6848 }
6849 
VisitThrow(HThrow * instruction)6850 void InstructionCodeGeneratorMIPS64::VisitThrow(HThrow* instruction) {
6851   codegen_->InvokeRuntime(kQuickDeliverException, instruction, instruction->GetDexPc());
6852   CheckEntrypointTypes<kQuickDeliverException, void, mirror::Object*>();
6853 }
6854 
VisitTypeConversion(HTypeConversion * conversion)6855 void LocationsBuilderMIPS64::VisitTypeConversion(HTypeConversion* conversion) {
6856   DataType::Type input_type = conversion->GetInputType();
6857   DataType::Type result_type = conversion->GetResultType();
6858   DCHECK(!DataType::IsTypeConversionImplicit(input_type, result_type))
6859       << input_type << " -> " << result_type;
6860 
6861   if ((input_type == DataType::Type::kReference) || (input_type == DataType::Type::kVoid) ||
6862       (result_type == DataType::Type::kReference) || (result_type == DataType::Type::kVoid)) {
6863     LOG(FATAL) << "Unexpected type conversion from " << input_type << " to " << result_type;
6864   }
6865 
6866   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(conversion);
6867 
6868   if (DataType::IsFloatingPointType(input_type)) {
6869     locations->SetInAt(0, Location::RequiresFpuRegister());
6870   } else {
6871     locations->SetInAt(0, Location::RequiresRegister());
6872   }
6873 
6874   if (DataType::IsFloatingPointType(result_type)) {
6875     locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
6876   } else {
6877     locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
6878   }
6879 }
6880 
VisitTypeConversion(HTypeConversion * conversion)6881 void InstructionCodeGeneratorMIPS64::VisitTypeConversion(HTypeConversion* conversion) {
6882   LocationSummary* locations = conversion->GetLocations();
6883   DataType::Type result_type = conversion->GetResultType();
6884   DataType::Type input_type = conversion->GetInputType();
6885 
6886   DCHECK(!DataType::IsTypeConversionImplicit(input_type, result_type))
6887       << input_type << " -> " << result_type;
6888 
6889   if (DataType::IsIntegralType(result_type) && DataType::IsIntegralType(input_type)) {
6890     GpuRegister dst = locations->Out().AsRegister<GpuRegister>();
6891     GpuRegister src = locations->InAt(0).AsRegister<GpuRegister>();
6892 
6893     switch (result_type) {
6894       case DataType::Type::kUint8:
6895         __ Andi(dst, src, 0xFF);
6896         break;
6897       case DataType::Type::kInt8:
6898         if (input_type == DataType::Type::kInt64) {
6899           // Type conversion from long to types narrower than int is a result of code
6900           // transformations. To avoid unpredictable results for SEB and SEH, we first
6901           // need to sign-extend the low 32-bit value into bits 32 through 63.
6902           __ Sll(dst, src, 0);
6903           __ Seb(dst, dst);
6904         } else {
6905           __ Seb(dst, src);
6906         }
6907         break;
6908       case DataType::Type::kUint16:
6909         __ Andi(dst, src, 0xFFFF);
6910         break;
6911       case DataType::Type::kInt16:
6912         if (input_type == DataType::Type::kInt64) {
6913           // Type conversion from long to types narrower than int is a result of code
6914           // transformations. To avoid unpredictable results for SEB and SEH, we first
6915           // need to sign-extend the low 32-bit value into bits 32 through 63.
6916           __ Sll(dst, src, 0);
6917           __ Seh(dst, dst);
6918         } else {
6919           __ Seh(dst, src);
6920         }
6921         break;
6922       case DataType::Type::kInt32:
6923       case DataType::Type::kInt64:
6924         // Sign-extend 32-bit int into bits 32 through 63 for int-to-long and long-to-int
6925         // conversions, except when the input and output registers are the same and we are not
6926         // converting longs to shorter types. In these cases, do nothing.
6927         if ((input_type == DataType::Type::kInt64) || (dst != src)) {
6928           __ Sll(dst, src, 0);
6929         }
6930         break;
6931 
6932       default:
6933         LOG(FATAL) << "Unexpected type conversion from " << input_type
6934                    << " to " << result_type;
6935     }
6936   } else if (DataType::IsFloatingPointType(result_type) && DataType::IsIntegralType(input_type)) {
6937     FpuRegister dst = locations->Out().AsFpuRegister<FpuRegister>();
6938     GpuRegister src = locations->InAt(0).AsRegister<GpuRegister>();
6939     if (input_type == DataType::Type::kInt64) {
6940       __ Dmtc1(src, FTMP);
6941       if (result_type == DataType::Type::kFloat32) {
6942         __ Cvtsl(dst, FTMP);
6943       } else {
6944         __ Cvtdl(dst, FTMP);
6945       }
6946     } else {
6947       __ Mtc1(src, FTMP);
6948       if (result_type == DataType::Type::kFloat32) {
6949         __ Cvtsw(dst, FTMP);
6950       } else {
6951         __ Cvtdw(dst, FTMP);
6952       }
6953     }
6954   } else if (DataType::IsIntegralType(result_type) && DataType::IsFloatingPointType(input_type)) {
6955     CHECK(result_type == DataType::Type::kInt32 || result_type == DataType::Type::kInt64);
6956     GpuRegister dst = locations->Out().AsRegister<GpuRegister>();
6957     FpuRegister src = locations->InAt(0).AsFpuRegister<FpuRegister>();
6958 
6959     if (result_type == DataType::Type::kInt64) {
6960       if (input_type == DataType::Type::kFloat32) {
6961         __ TruncLS(FTMP, src);
6962       } else {
6963         __ TruncLD(FTMP, src);
6964       }
6965       __ Dmfc1(dst, FTMP);
6966     } else {
6967       if (input_type == DataType::Type::kFloat32) {
6968         __ TruncWS(FTMP, src);
6969       } else {
6970         __ TruncWD(FTMP, src);
6971       }
6972       __ Mfc1(dst, FTMP);
6973     }
6974   } else if (DataType::IsFloatingPointType(result_type) &&
6975              DataType::IsFloatingPointType(input_type)) {
6976     FpuRegister dst = locations->Out().AsFpuRegister<FpuRegister>();
6977     FpuRegister src = locations->InAt(0).AsFpuRegister<FpuRegister>();
6978     if (result_type == DataType::Type::kFloat32) {
6979       __ Cvtsd(dst, src);
6980     } else {
6981       __ Cvtds(dst, src);
6982     }
6983   } else {
6984     LOG(FATAL) << "Unexpected or unimplemented type conversion from " << input_type
6985                 << " to " << result_type;
6986   }
6987 }
6988 
VisitUShr(HUShr * ushr)6989 void LocationsBuilderMIPS64::VisitUShr(HUShr* ushr) {
6990   HandleShift(ushr);
6991 }
6992 
VisitUShr(HUShr * ushr)6993 void InstructionCodeGeneratorMIPS64::VisitUShr(HUShr* ushr) {
6994   HandleShift(ushr);
6995 }
6996 
VisitXor(HXor * instruction)6997 void LocationsBuilderMIPS64::VisitXor(HXor* instruction) {
6998   HandleBinaryOp(instruction);
6999 }
7000 
VisitXor(HXor * instruction)7001 void InstructionCodeGeneratorMIPS64::VisitXor(HXor* instruction) {
7002   HandleBinaryOp(instruction);
7003 }
7004 
VisitBoundType(HBoundType * instruction ATTRIBUTE_UNUSED)7005 void LocationsBuilderMIPS64::VisitBoundType(HBoundType* instruction ATTRIBUTE_UNUSED) {
7006   // Nothing to do, this should be removed during prepare for register allocator.
7007   LOG(FATAL) << "Unreachable";
7008 }
7009 
VisitBoundType(HBoundType * instruction ATTRIBUTE_UNUSED)7010 void InstructionCodeGeneratorMIPS64::VisitBoundType(HBoundType* instruction ATTRIBUTE_UNUSED) {
7011   // Nothing to do, this should be removed during prepare for register allocator.
7012   LOG(FATAL) << "Unreachable";
7013 }
7014 
VisitEqual(HEqual * comp)7015 void LocationsBuilderMIPS64::VisitEqual(HEqual* comp) {
7016   HandleCondition(comp);
7017 }
7018 
VisitEqual(HEqual * comp)7019 void InstructionCodeGeneratorMIPS64::VisitEqual(HEqual* comp) {
7020   HandleCondition(comp);
7021 }
7022 
VisitNotEqual(HNotEqual * comp)7023 void LocationsBuilderMIPS64::VisitNotEqual(HNotEqual* comp) {
7024   HandleCondition(comp);
7025 }
7026 
VisitNotEqual(HNotEqual * comp)7027 void InstructionCodeGeneratorMIPS64::VisitNotEqual(HNotEqual* comp) {
7028   HandleCondition(comp);
7029 }
7030 
VisitLessThan(HLessThan * comp)7031 void LocationsBuilderMIPS64::VisitLessThan(HLessThan* comp) {
7032   HandleCondition(comp);
7033 }
7034 
VisitLessThan(HLessThan * comp)7035 void InstructionCodeGeneratorMIPS64::VisitLessThan(HLessThan* comp) {
7036   HandleCondition(comp);
7037 }
7038 
VisitLessThanOrEqual(HLessThanOrEqual * comp)7039 void LocationsBuilderMIPS64::VisitLessThanOrEqual(HLessThanOrEqual* comp) {
7040   HandleCondition(comp);
7041 }
7042 
VisitLessThanOrEqual(HLessThanOrEqual * comp)7043 void InstructionCodeGeneratorMIPS64::VisitLessThanOrEqual(HLessThanOrEqual* comp) {
7044   HandleCondition(comp);
7045 }
7046 
VisitGreaterThan(HGreaterThan * comp)7047 void LocationsBuilderMIPS64::VisitGreaterThan(HGreaterThan* comp) {
7048   HandleCondition(comp);
7049 }
7050 
VisitGreaterThan(HGreaterThan * comp)7051 void InstructionCodeGeneratorMIPS64::VisitGreaterThan(HGreaterThan* comp) {
7052   HandleCondition(comp);
7053 }
7054 
VisitGreaterThanOrEqual(HGreaterThanOrEqual * comp)7055 void LocationsBuilderMIPS64::VisitGreaterThanOrEqual(HGreaterThanOrEqual* comp) {
7056   HandleCondition(comp);
7057 }
7058 
VisitGreaterThanOrEqual(HGreaterThanOrEqual * comp)7059 void InstructionCodeGeneratorMIPS64::VisitGreaterThanOrEqual(HGreaterThanOrEqual* comp) {
7060   HandleCondition(comp);
7061 }
7062 
VisitBelow(HBelow * comp)7063 void LocationsBuilderMIPS64::VisitBelow(HBelow* comp) {
7064   HandleCondition(comp);
7065 }
7066 
VisitBelow(HBelow * comp)7067 void InstructionCodeGeneratorMIPS64::VisitBelow(HBelow* comp) {
7068   HandleCondition(comp);
7069 }
7070 
VisitBelowOrEqual(HBelowOrEqual * comp)7071 void LocationsBuilderMIPS64::VisitBelowOrEqual(HBelowOrEqual* comp) {
7072   HandleCondition(comp);
7073 }
7074 
VisitBelowOrEqual(HBelowOrEqual * comp)7075 void InstructionCodeGeneratorMIPS64::VisitBelowOrEqual(HBelowOrEqual* comp) {
7076   HandleCondition(comp);
7077 }
7078 
VisitAbove(HAbove * comp)7079 void LocationsBuilderMIPS64::VisitAbove(HAbove* comp) {
7080   HandleCondition(comp);
7081 }
7082 
VisitAbove(HAbove * comp)7083 void InstructionCodeGeneratorMIPS64::VisitAbove(HAbove* comp) {
7084   HandleCondition(comp);
7085 }
7086 
VisitAboveOrEqual(HAboveOrEqual * comp)7087 void LocationsBuilderMIPS64::VisitAboveOrEqual(HAboveOrEqual* comp) {
7088   HandleCondition(comp);
7089 }
7090 
VisitAboveOrEqual(HAboveOrEqual * comp)7091 void InstructionCodeGeneratorMIPS64::VisitAboveOrEqual(HAboveOrEqual* comp) {
7092   HandleCondition(comp);
7093 }
7094 
7095 // Simple implementation of packed switch - generate cascaded compare/jumps.
VisitPackedSwitch(HPackedSwitch * switch_instr)7096 void LocationsBuilderMIPS64::VisitPackedSwitch(HPackedSwitch* switch_instr) {
7097   LocationSummary* locations =
7098       new (GetGraph()->GetAllocator()) LocationSummary(switch_instr, LocationSummary::kNoCall);
7099   locations->SetInAt(0, Location::RequiresRegister());
7100 }
7101 
GenPackedSwitchWithCompares(GpuRegister value_reg,int32_t lower_bound,uint32_t num_entries,HBasicBlock * switch_block,HBasicBlock * default_block)7102 void InstructionCodeGeneratorMIPS64::GenPackedSwitchWithCompares(GpuRegister value_reg,
7103                                                                  int32_t lower_bound,
7104                                                                  uint32_t num_entries,
7105                                                                  HBasicBlock* switch_block,
7106                                                                  HBasicBlock* default_block) {
7107   // Create a set of compare/jumps.
7108   GpuRegister temp_reg = TMP;
7109   __ Addiu32(temp_reg, value_reg, -lower_bound);
7110   // Jump to default if index is negative
7111   // Note: We don't check the case that index is positive while value < lower_bound, because in
7112   // this case, index >= num_entries must be true. So that we can save one branch instruction.
7113   __ Bltzc(temp_reg, codegen_->GetLabelOf(default_block));
7114 
7115   const ArenaVector<HBasicBlock*>& successors = switch_block->GetSuccessors();
7116   // Jump to successors[0] if value == lower_bound.
7117   __ Beqzc(temp_reg, codegen_->GetLabelOf(successors[0]));
7118   int32_t last_index = 0;
7119   for (; num_entries - last_index > 2; last_index += 2) {
7120     __ Addiu(temp_reg, temp_reg, -2);
7121     // Jump to successors[last_index + 1] if value < case_value[last_index + 2].
7122     __ Bltzc(temp_reg, codegen_->GetLabelOf(successors[last_index + 1]));
7123     // Jump to successors[last_index + 2] if value == case_value[last_index + 2].
7124     __ Beqzc(temp_reg, codegen_->GetLabelOf(successors[last_index + 2]));
7125   }
7126   if (num_entries - last_index == 2) {
7127     // The last missing case_value.
7128     __ Addiu(temp_reg, temp_reg, -1);
7129     __ Beqzc(temp_reg, codegen_->GetLabelOf(successors[last_index + 1]));
7130   }
7131 
7132   // And the default for any other value.
7133   if (!codegen_->GoesToNextBlock(switch_block, default_block)) {
7134     __ Bc(codegen_->GetLabelOf(default_block));
7135   }
7136 }
7137 
GenTableBasedPackedSwitch(GpuRegister value_reg,int32_t lower_bound,uint32_t num_entries,HBasicBlock * switch_block,HBasicBlock * default_block)7138 void InstructionCodeGeneratorMIPS64::GenTableBasedPackedSwitch(GpuRegister value_reg,
7139                                                                int32_t lower_bound,
7140                                                                uint32_t num_entries,
7141                                                                HBasicBlock* switch_block,
7142                                                                HBasicBlock* default_block) {
7143   // Create a jump table.
7144   std::vector<Mips64Label*> labels(num_entries);
7145   const ArenaVector<HBasicBlock*>& successors = switch_block->GetSuccessors();
7146   for (uint32_t i = 0; i < num_entries; i++) {
7147     labels[i] = codegen_->GetLabelOf(successors[i]);
7148   }
7149   JumpTable* table = __ CreateJumpTable(std::move(labels));
7150 
7151   // Is the value in range?
7152   __ Addiu32(TMP, value_reg, -lower_bound);
7153   __ LoadConst32(AT, num_entries);
7154   __ Bgeuc(TMP, AT, codegen_->GetLabelOf(default_block));
7155 
7156   // We are in the range of the table.
7157   // Load the target address from the jump table, indexing by the value.
7158   __ LoadLabelAddress(AT, table->GetLabel());
7159   __ Dlsa(TMP, TMP, AT, 2);
7160   __ Lw(TMP, TMP, 0);
7161   // Compute the absolute target address by adding the table start address
7162   // (the table contains offsets to targets relative to its start).
7163   __ Daddu(TMP, TMP, AT);
7164   // And jump.
7165   __ Jr(TMP);
7166   __ Nop();
7167 }
7168 
VisitPackedSwitch(HPackedSwitch * switch_instr)7169 void InstructionCodeGeneratorMIPS64::VisitPackedSwitch(HPackedSwitch* switch_instr) {
7170   int32_t lower_bound = switch_instr->GetStartValue();
7171   uint32_t num_entries = switch_instr->GetNumEntries();
7172   LocationSummary* locations = switch_instr->GetLocations();
7173   GpuRegister value_reg = locations->InAt(0).AsRegister<GpuRegister>();
7174   HBasicBlock* switch_block = switch_instr->GetBlock();
7175   HBasicBlock* default_block = switch_instr->GetDefaultBlock();
7176 
7177   if (num_entries > kPackedSwitchJumpTableThreshold) {
7178     GenTableBasedPackedSwitch(value_reg,
7179                               lower_bound,
7180                               num_entries,
7181                               switch_block,
7182                               default_block);
7183   } else {
7184     GenPackedSwitchWithCompares(value_reg,
7185                                 lower_bound,
7186                                 num_entries,
7187                                 switch_block,
7188                                 default_block);
7189   }
7190 }
7191 
VisitClassTableGet(HClassTableGet * instruction)7192 void LocationsBuilderMIPS64::VisitClassTableGet(HClassTableGet* instruction) {
7193   LocationSummary* locations =
7194       new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
7195   locations->SetInAt(0, Location::RequiresRegister());
7196   locations->SetOut(Location::RequiresRegister());
7197 }
7198 
VisitClassTableGet(HClassTableGet * instruction)7199 void InstructionCodeGeneratorMIPS64::VisitClassTableGet(HClassTableGet* instruction) {
7200   LocationSummary* locations = instruction->GetLocations();
7201   if (instruction->GetTableKind() == HClassTableGet::TableKind::kVTable) {
7202     uint32_t method_offset = mirror::Class::EmbeddedVTableEntryOffset(
7203         instruction->GetIndex(), kMips64PointerSize).SizeValue();
7204     __ LoadFromOffset(kLoadDoubleword,
7205                       locations->Out().AsRegister<GpuRegister>(),
7206                       locations->InAt(0).AsRegister<GpuRegister>(),
7207                       method_offset);
7208   } else {
7209     uint32_t method_offset = static_cast<uint32_t>(ImTable::OffsetOfElement(
7210         instruction->GetIndex(), kMips64PointerSize));
7211     __ LoadFromOffset(kLoadDoubleword,
7212                       locations->Out().AsRegister<GpuRegister>(),
7213                       locations->InAt(0).AsRegister<GpuRegister>(),
7214                       mirror::Class::ImtPtrOffset(kMips64PointerSize).Uint32Value());
7215     __ LoadFromOffset(kLoadDoubleword,
7216                       locations->Out().AsRegister<GpuRegister>(),
7217                       locations->Out().AsRegister<GpuRegister>(),
7218                       method_offset);
7219   }
7220 }
7221 
VisitIntermediateAddress(HIntermediateAddress * instruction ATTRIBUTE_UNUSED)7222 void LocationsBuilderMIPS64::VisitIntermediateAddress(HIntermediateAddress* instruction
7223                                                       ATTRIBUTE_UNUSED) {
7224   LOG(FATAL) << "Unreachable";
7225 }
7226 
VisitIntermediateAddress(HIntermediateAddress * instruction ATTRIBUTE_UNUSED)7227 void InstructionCodeGeneratorMIPS64::VisitIntermediateAddress(HIntermediateAddress* instruction
7228                                                               ATTRIBUTE_UNUSED) {
7229   LOG(FATAL) << "Unreachable";
7230 }
7231 
7232 }  // namespace mips64
7233 }  // namespace art
7234