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