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_mips.h"
18 
19 #include "arch/mips/asm_support_mips.h"
20 #include "arch/mips/entrypoints_direct_mips.h"
21 #include "arch/mips/instruction_set_features_mips.h"
22 #include "art_method.h"
23 #include "class_table.h"
24 #include "code_generator_utils.h"
25 #include "compiled_method.h"
26 #include "entrypoints/quick/quick_entrypoints.h"
27 #include "entrypoints/quick/quick_entrypoints_enum.h"
28 #include "gc/accounting/card_table.h"
29 #include "gc/space/image_space.h"
30 #include "heap_poisoning.h"
31 #include "intrinsics.h"
32 #include "intrinsics_mips.h"
33 #include "linker/linker_patch.h"
34 #include "mirror/array-inl.h"
35 #include "mirror/class-inl.h"
36 #include "offsets.h"
37 #include "stack_map_stream.h"
38 #include "thread.h"
39 #include "utils/assembler.h"
40 #include "utils/mips/assembler_mips.h"
41 #include "utils/stack_checks.h"
42 
43 namespace art {
44 namespace mips {
45 
46 static constexpr int kCurrentMethodStackOffset = 0;
47 static constexpr Register kMethodRegisterArgument = A0;
48 
49 // Flags controlling the use of thunks for Baker read barriers.
50 constexpr bool kBakerReadBarrierThunksEnableForFields = true;
51 constexpr bool kBakerReadBarrierThunksEnableForArrays = true;
52 constexpr bool kBakerReadBarrierThunksEnableForGcRoots = true;
53 
MipsReturnLocation(DataType::Type return_type)54 Location MipsReturnLocation(DataType::Type return_type) {
55   switch (return_type) {
56     case DataType::Type::kReference:
57     case DataType::Type::kBool:
58     case DataType::Type::kUint8:
59     case DataType::Type::kInt8:
60     case DataType::Type::kUint16:
61     case DataType::Type::kInt16:
62     case DataType::Type::kUint32:
63     case DataType::Type::kInt32:
64       return Location::RegisterLocation(V0);
65 
66     case DataType::Type::kUint64:
67     case DataType::Type::kInt64:
68       return Location::RegisterPairLocation(V0, V1);
69 
70     case DataType::Type::kFloat32:
71     case DataType::Type::kFloat64:
72       return Location::FpuRegisterLocation(F0);
73 
74     case DataType::Type::kVoid:
75       return Location();
76   }
77   UNREACHABLE();
78 }
79 
GetReturnLocation(DataType::Type type) const80 Location InvokeDexCallingConventionVisitorMIPS::GetReturnLocation(DataType::Type type) const {
81   return MipsReturnLocation(type);
82 }
83 
GetMethodLocation() const84 Location InvokeDexCallingConventionVisitorMIPS::GetMethodLocation() const {
85   return Location::RegisterLocation(kMethodRegisterArgument);
86 }
87 
GetNextLocation(DataType::Type type)88 Location InvokeDexCallingConventionVisitorMIPS::GetNextLocation(DataType::Type type) {
89   Location next_location;
90 
91   switch (type) {
92     case DataType::Type::kReference:
93     case DataType::Type::kBool:
94     case DataType::Type::kUint8:
95     case DataType::Type::kInt8:
96     case DataType::Type::kUint16:
97     case DataType::Type::kInt16:
98     case DataType::Type::kInt32: {
99       uint32_t gp_index = gp_index_++;
100       if (gp_index < calling_convention.GetNumberOfRegisters()) {
101         next_location = Location::RegisterLocation(calling_convention.GetRegisterAt(gp_index));
102       } else {
103         size_t stack_offset = calling_convention.GetStackOffsetOf(stack_index_);
104         next_location = Location::StackSlot(stack_offset);
105       }
106       break;
107     }
108 
109     case DataType::Type::kInt64: {
110       uint32_t gp_index = gp_index_;
111       gp_index_ += 2;
112       if (gp_index + 1 < calling_convention.GetNumberOfRegisters()) {
113         Register reg = calling_convention.GetRegisterAt(gp_index);
114         if (reg == A1 || reg == A3) {
115           gp_index_++;  // Skip A1(A3), and use A2_A3(T0_T1) instead.
116           gp_index++;
117         }
118         Register low_even = calling_convention.GetRegisterAt(gp_index);
119         Register high_odd = calling_convention.GetRegisterAt(gp_index + 1);
120         DCHECK_EQ(low_even + 1, high_odd);
121         next_location = Location::RegisterPairLocation(low_even, high_odd);
122       } else {
123         size_t stack_offset = calling_convention.GetStackOffsetOf(stack_index_);
124         next_location = Location::DoubleStackSlot(stack_offset);
125       }
126       break;
127     }
128 
129     // Note: both float and double types are stored in even FPU registers. On 32 bit FPU, double
130     // will take up the even/odd pair, while floats are stored in even regs only.
131     // On 64 bit FPU, both double and float are stored in even registers only.
132     case DataType::Type::kFloat32:
133     case DataType::Type::kFloat64: {
134       uint32_t float_index = float_index_++;
135       if (float_index < calling_convention.GetNumberOfFpuRegisters()) {
136         next_location = Location::FpuRegisterLocation(
137             calling_convention.GetFpuRegisterAt(float_index));
138       } else {
139         size_t stack_offset = calling_convention.GetStackOffsetOf(stack_index_);
140         next_location = DataType::Is64BitType(type) ? Location::DoubleStackSlot(stack_offset)
141                                                     : Location::StackSlot(stack_offset);
142       }
143       break;
144     }
145 
146     case DataType::Type::kUint32:
147     case DataType::Type::kUint64:
148     case DataType::Type::kVoid:
149       LOG(FATAL) << "Unexpected parameter type " << type;
150       UNREACHABLE();
151   }
152 
153   // Space on the stack is reserved for all arguments.
154   stack_index_ += DataType::Is64BitType(type) ? 2 : 1;
155 
156   return next_location;
157 }
158 
GetReturnLocation(DataType::Type type)159 Location InvokeRuntimeCallingConvention::GetReturnLocation(DataType::Type type) {
160   return MipsReturnLocation(type);
161 }
162 
OneRegInReferenceOutSaveEverythingCallerSaves()163 static RegisterSet OneRegInReferenceOutSaveEverythingCallerSaves() {
164   InvokeRuntimeCallingConvention calling_convention;
165   RegisterSet caller_saves = RegisterSet::Empty();
166   caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
167   // The reference is returned in the same register. This differs from the standard return location.
168   return caller_saves;
169 }
170 
171 // NOLINT on __ macro to suppress wrong warning/fix (misc-macro-parentheses) from clang-tidy.
172 #define __ down_cast<CodeGeneratorMIPS*>(codegen)->GetAssembler()->  // NOLINT
173 #define QUICK_ENTRY_POINT(x) QUICK_ENTRYPOINT_OFFSET(kMipsPointerSize, x).Int32Value()
174 
175 class BoundsCheckSlowPathMIPS : public SlowPathCodeMIPS {
176  public:
BoundsCheckSlowPathMIPS(HBoundsCheck * instruction)177   explicit BoundsCheckSlowPathMIPS(HBoundsCheck* instruction) : SlowPathCodeMIPS(instruction) {}
178 
EmitNativeCode(CodeGenerator * codegen)179   void EmitNativeCode(CodeGenerator* codegen) override {
180     LocationSummary* locations = instruction_->GetLocations();
181     CodeGeneratorMIPS* mips_codegen = down_cast<CodeGeneratorMIPS*>(codegen);
182     __ Bind(GetEntryLabel());
183     if (instruction_->CanThrowIntoCatchBlock()) {
184       // Live registers will be restored in the catch block if caught.
185       SaveLiveRegisters(codegen, instruction_->GetLocations());
186     }
187     // We're moving two locations to locations that could overlap, so we need a parallel
188     // move resolver.
189     InvokeRuntimeCallingConvention calling_convention;
190     codegen->EmitParallelMoves(locations->InAt(0),
191                                Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
192                                DataType::Type::kInt32,
193                                locations->InAt(1),
194                                Location::RegisterLocation(calling_convention.GetRegisterAt(1)),
195                                DataType::Type::kInt32);
196     QuickEntrypointEnum entrypoint = instruction_->AsBoundsCheck()->IsStringCharAt()
197         ? kQuickThrowStringBounds
198         : kQuickThrowArrayBounds;
199     mips_codegen->InvokeRuntime(entrypoint, instruction_, instruction_->GetDexPc(), this);
200     CheckEntrypointTypes<kQuickThrowStringBounds, void, int32_t, int32_t>();
201     CheckEntrypointTypes<kQuickThrowArrayBounds, void, int32_t, int32_t>();
202   }
203 
IsFatal() const204   bool IsFatal() const override { return true; }
205 
GetDescription() const206   const char* GetDescription() const override { return "BoundsCheckSlowPathMIPS"; }
207 
208  private:
209   DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathMIPS);
210 };
211 
212 class DivZeroCheckSlowPathMIPS : public SlowPathCodeMIPS {
213  public:
DivZeroCheckSlowPathMIPS(HDivZeroCheck * instruction)214   explicit DivZeroCheckSlowPathMIPS(HDivZeroCheck* instruction) : SlowPathCodeMIPS(instruction) {}
215 
EmitNativeCode(CodeGenerator * codegen)216   void EmitNativeCode(CodeGenerator* codegen) override {
217     CodeGeneratorMIPS* mips_codegen = down_cast<CodeGeneratorMIPS*>(codegen);
218     __ Bind(GetEntryLabel());
219     mips_codegen->InvokeRuntime(kQuickThrowDivZero, instruction_, instruction_->GetDexPc(), this);
220     CheckEntrypointTypes<kQuickThrowDivZero, void, void>();
221   }
222 
IsFatal() const223   bool IsFatal() const override { return true; }
224 
GetDescription() const225   const char* GetDescription() const override { return "DivZeroCheckSlowPathMIPS"; }
226 
227  private:
228   DISALLOW_COPY_AND_ASSIGN(DivZeroCheckSlowPathMIPS);
229 };
230 
231 class LoadClassSlowPathMIPS : public SlowPathCodeMIPS {
232  public:
LoadClassSlowPathMIPS(HLoadClass * cls,HInstruction * at)233   LoadClassSlowPathMIPS(HLoadClass* cls, HInstruction* at)
234       : SlowPathCodeMIPS(at), cls_(cls) {
235     DCHECK(at->IsLoadClass() || at->IsClinitCheck());
236     DCHECK_EQ(instruction_->IsLoadClass(), cls_ == instruction_);
237   }
238 
EmitNativeCode(CodeGenerator * codegen)239   void EmitNativeCode(CodeGenerator* codegen) override {
240     LocationSummary* locations = instruction_->GetLocations();
241     Location out = locations->Out();
242     const uint32_t dex_pc = instruction_->GetDexPc();
243     bool must_resolve_type = instruction_->IsLoadClass() && cls_->MustResolveTypeOnSlowPath();
244     bool must_do_clinit = instruction_->IsClinitCheck() || cls_->MustGenerateClinitCheck();
245 
246     CodeGeneratorMIPS* mips_codegen = down_cast<CodeGeneratorMIPS*>(codegen);
247     __ Bind(GetEntryLabel());
248     SaveLiveRegisters(codegen, locations);
249 
250     InvokeRuntimeCallingConvention calling_convention;
251     if (must_resolve_type) {
252       DCHECK(IsSameDexFile(cls_->GetDexFile(), mips_codegen->GetGraph()->GetDexFile()));
253       dex::TypeIndex type_index = cls_->GetTypeIndex();
254       __ LoadConst32(calling_convention.GetRegisterAt(0), type_index.index_);
255       mips_codegen->InvokeRuntime(kQuickResolveType, instruction_, dex_pc, this);
256       CheckEntrypointTypes<kQuickResolveType, void*, uint32_t>();
257       // If we also must_do_clinit, the resolved type is now in the correct register.
258     } else {
259       DCHECK(must_do_clinit);
260       Location source = instruction_->IsLoadClass() ? out : locations->InAt(0);
261       mips_codegen->MoveLocation(Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
262                                  source,
263                                  cls_->GetType());
264     }
265     if (must_do_clinit) {
266       mips_codegen->InvokeRuntime(kQuickInitializeStaticStorage, instruction_, dex_pc, this);
267       CheckEntrypointTypes<kQuickInitializeStaticStorage, void*, mirror::Class*>();
268     }
269 
270     // Move the class to the desired location.
271     if (out.IsValid()) {
272       DCHECK(out.IsRegister() && !locations->GetLiveRegisters()->ContainsCoreRegister(out.reg()));
273       DataType::Type type = instruction_->GetType();
274       mips_codegen->MoveLocation(out,
275                                  Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
276                                  type);
277     }
278     RestoreLiveRegisters(codegen, locations);
279 
280     __ B(GetExitLabel());
281   }
282 
GetDescription() const283   const char* GetDescription() const override { return "LoadClassSlowPathMIPS"; }
284 
285  private:
286   // The class this slow path will load.
287   HLoadClass* const cls_;
288 
289   DISALLOW_COPY_AND_ASSIGN(LoadClassSlowPathMIPS);
290 };
291 
292 class LoadStringSlowPathMIPS : public SlowPathCodeMIPS {
293  public:
LoadStringSlowPathMIPS(HLoadString * instruction)294   explicit LoadStringSlowPathMIPS(HLoadString* instruction)
295       : SlowPathCodeMIPS(instruction) {}
296 
EmitNativeCode(CodeGenerator * codegen)297   void EmitNativeCode(CodeGenerator* codegen) override {
298     DCHECK(instruction_->IsLoadString());
299     DCHECK_EQ(instruction_->AsLoadString()->GetLoadKind(), HLoadString::LoadKind::kBssEntry);
300     LocationSummary* locations = instruction_->GetLocations();
301     DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg()));
302     const dex::StringIndex string_index = instruction_->AsLoadString()->GetStringIndex();
303     CodeGeneratorMIPS* mips_codegen = down_cast<CodeGeneratorMIPS*>(codegen);
304     InvokeRuntimeCallingConvention calling_convention;
305     __ Bind(GetEntryLabel());
306     SaveLiveRegisters(codegen, locations);
307 
308     __ LoadConst32(calling_convention.GetRegisterAt(0), string_index.index_);
309     mips_codegen->InvokeRuntime(kQuickResolveString, instruction_, instruction_->GetDexPc(), this);
310     CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>();
311 
312     DataType::Type type = instruction_->GetType();
313     mips_codegen->MoveLocation(locations->Out(),
314                                Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
315                                type);
316     RestoreLiveRegisters(codegen, locations);
317 
318     __ B(GetExitLabel());
319   }
320 
GetDescription() const321   const char* GetDescription() const override { return "LoadStringSlowPathMIPS"; }
322 
323  private:
324   DISALLOW_COPY_AND_ASSIGN(LoadStringSlowPathMIPS);
325 };
326 
327 class NullCheckSlowPathMIPS : public SlowPathCodeMIPS {
328  public:
NullCheckSlowPathMIPS(HNullCheck * instr)329   explicit NullCheckSlowPathMIPS(HNullCheck* instr) : SlowPathCodeMIPS(instr) {}
330 
EmitNativeCode(CodeGenerator * codegen)331   void EmitNativeCode(CodeGenerator* codegen) override {
332     CodeGeneratorMIPS* mips_codegen = down_cast<CodeGeneratorMIPS*>(codegen);
333     __ Bind(GetEntryLabel());
334     if (instruction_->CanThrowIntoCatchBlock()) {
335       // Live registers will be restored in the catch block if caught.
336       SaveLiveRegisters(codegen, instruction_->GetLocations());
337     }
338     mips_codegen->InvokeRuntime(kQuickThrowNullPointer,
339                                 instruction_,
340                                 instruction_->GetDexPc(),
341                                 this);
342     CheckEntrypointTypes<kQuickThrowNullPointer, void, void>();
343   }
344 
IsFatal() const345   bool IsFatal() const override { return true; }
346 
GetDescription() const347   const char* GetDescription() const override { return "NullCheckSlowPathMIPS"; }
348 
349  private:
350   DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathMIPS);
351 };
352 
353 class SuspendCheckSlowPathMIPS : public SlowPathCodeMIPS {
354  public:
SuspendCheckSlowPathMIPS(HSuspendCheck * instruction,HBasicBlock * successor)355   SuspendCheckSlowPathMIPS(HSuspendCheck* instruction, HBasicBlock* successor)
356       : SlowPathCodeMIPS(instruction), successor_(successor) {}
357 
EmitNativeCode(CodeGenerator * codegen)358   void EmitNativeCode(CodeGenerator* codegen) override {
359     LocationSummary* locations = instruction_->GetLocations();
360     CodeGeneratorMIPS* mips_codegen = down_cast<CodeGeneratorMIPS*>(codegen);
361     __ Bind(GetEntryLabel());
362     SaveLiveRegisters(codegen, locations);     // Only saves live vector registers for SIMD.
363     mips_codegen->InvokeRuntime(kQuickTestSuspend, instruction_, instruction_->GetDexPc(), this);
364     CheckEntrypointTypes<kQuickTestSuspend, void, void>();
365     RestoreLiveRegisters(codegen, locations);  // Only restores live vector registers for SIMD.
366     if (successor_ == nullptr) {
367       __ B(GetReturnLabel());
368     } else {
369       __ B(mips_codegen->GetLabelOf(successor_));
370     }
371   }
372 
GetReturnLabel()373   MipsLabel* GetReturnLabel() {
374     DCHECK(successor_ == nullptr);
375     return &return_label_;
376   }
377 
GetDescription() const378   const char* GetDescription() const override { return "SuspendCheckSlowPathMIPS"; }
379 
GetSuccessor() const380   HBasicBlock* GetSuccessor() const {
381     return successor_;
382   }
383 
384  private:
385   // If not null, the block to branch to after the suspend check.
386   HBasicBlock* const successor_;
387 
388   // If `successor_` is null, the label to branch to after the suspend check.
389   MipsLabel return_label_;
390 
391   DISALLOW_COPY_AND_ASSIGN(SuspendCheckSlowPathMIPS);
392 };
393 
394 class TypeCheckSlowPathMIPS : public SlowPathCodeMIPS {
395  public:
TypeCheckSlowPathMIPS(HInstruction * instruction,bool is_fatal)396   explicit TypeCheckSlowPathMIPS(HInstruction* instruction, bool is_fatal)
397       : SlowPathCodeMIPS(instruction), is_fatal_(is_fatal) {}
398 
EmitNativeCode(CodeGenerator * codegen)399   void EmitNativeCode(CodeGenerator* codegen) override {
400     LocationSummary* locations = instruction_->GetLocations();
401     uint32_t dex_pc = instruction_->GetDexPc();
402     DCHECK(instruction_->IsCheckCast()
403            || !locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg()));
404     CodeGeneratorMIPS* mips_codegen = down_cast<CodeGeneratorMIPS*>(codegen);
405 
406     __ Bind(GetEntryLabel());
407     if (!is_fatal_ || instruction_->CanThrowIntoCatchBlock()) {
408       SaveLiveRegisters(codegen, locations);
409     }
410 
411     // We're moving two locations to locations that could overlap, so we need a parallel
412     // move resolver.
413     InvokeRuntimeCallingConvention calling_convention;
414     codegen->EmitParallelMoves(locations->InAt(0),
415                                Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
416                                DataType::Type::kReference,
417                                locations->InAt(1),
418                                Location::RegisterLocation(calling_convention.GetRegisterAt(1)),
419                                DataType::Type::kReference);
420     if (instruction_->IsInstanceOf()) {
421       mips_codegen->InvokeRuntime(kQuickInstanceofNonTrivial, instruction_, dex_pc, this);
422       CheckEntrypointTypes<kQuickInstanceofNonTrivial, size_t, mirror::Object*, mirror::Class*>();
423       DataType::Type ret_type = instruction_->GetType();
424       Location ret_loc = calling_convention.GetReturnLocation(ret_type);
425       mips_codegen->MoveLocation(locations->Out(), ret_loc, ret_type);
426     } else {
427       DCHECK(instruction_->IsCheckCast());
428       mips_codegen->InvokeRuntime(kQuickCheckInstanceOf, instruction_, dex_pc, this);
429       CheckEntrypointTypes<kQuickCheckInstanceOf, void, mirror::Object*, mirror::Class*>();
430     }
431 
432     if (!is_fatal_) {
433       RestoreLiveRegisters(codegen, locations);
434       __ B(GetExitLabel());
435     }
436   }
437 
GetDescription() const438   const char* GetDescription() const override { return "TypeCheckSlowPathMIPS"; }
439 
IsFatal() const440   bool IsFatal() const override { return is_fatal_; }
441 
442  private:
443   const bool is_fatal_;
444 
445   DISALLOW_COPY_AND_ASSIGN(TypeCheckSlowPathMIPS);
446 };
447 
448 class DeoptimizationSlowPathMIPS : public SlowPathCodeMIPS {
449  public:
DeoptimizationSlowPathMIPS(HDeoptimize * instruction)450   explicit DeoptimizationSlowPathMIPS(HDeoptimize* instruction)
451     : SlowPathCodeMIPS(instruction) {}
452 
EmitNativeCode(CodeGenerator * codegen)453   void EmitNativeCode(CodeGenerator* codegen) override {
454     CodeGeneratorMIPS* mips_codegen = down_cast<CodeGeneratorMIPS*>(codegen);
455     __ Bind(GetEntryLabel());
456     LocationSummary* locations = instruction_->GetLocations();
457     SaveLiveRegisters(codegen, locations);
458     InvokeRuntimeCallingConvention calling_convention;
459     __ LoadConst32(calling_convention.GetRegisterAt(0),
460                    static_cast<uint32_t>(instruction_->AsDeoptimize()->GetDeoptimizationKind()));
461     mips_codegen->InvokeRuntime(kQuickDeoptimize, instruction_, instruction_->GetDexPc(), this);
462     CheckEntrypointTypes<kQuickDeoptimize, void, DeoptimizationKind>();
463   }
464 
GetDescription() const465   const char* GetDescription() const override { return "DeoptimizationSlowPathMIPS"; }
466 
467  private:
468   DISALLOW_COPY_AND_ASSIGN(DeoptimizationSlowPathMIPS);
469 };
470 
471 class ArraySetSlowPathMIPS : public SlowPathCodeMIPS {
472  public:
ArraySetSlowPathMIPS(HInstruction * instruction)473   explicit ArraySetSlowPathMIPS(HInstruction* instruction) : SlowPathCodeMIPS(instruction) {}
474 
EmitNativeCode(CodeGenerator * codegen)475   void EmitNativeCode(CodeGenerator* codegen) override {
476     LocationSummary* locations = instruction_->GetLocations();
477     __ Bind(GetEntryLabel());
478     SaveLiveRegisters(codegen, locations);
479 
480     InvokeRuntimeCallingConvention calling_convention;
481     HParallelMove parallel_move(codegen->GetGraph()->GetAllocator());
482     parallel_move.AddMove(
483         locations->InAt(0),
484         Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
485         DataType::Type::kReference,
486         nullptr);
487     parallel_move.AddMove(
488         locations->InAt(1),
489         Location::RegisterLocation(calling_convention.GetRegisterAt(1)),
490         DataType::Type::kInt32,
491         nullptr);
492     parallel_move.AddMove(
493         locations->InAt(2),
494         Location::RegisterLocation(calling_convention.GetRegisterAt(2)),
495         DataType::Type::kReference,
496         nullptr);
497     codegen->GetMoveResolver()->EmitNativeCode(&parallel_move);
498 
499     CodeGeneratorMIPS* mips_codegen = down_cast<CodeGeneratorMIPS*>(codegen);
500     mips_codegen->InvokeRuntime(kQuickAputObject, instruction_, instruction_->GetDexPc(), this);
501     CheckEntrypointTypes<kQuickAputObject, void, mirror::Array*, int32_t, mirror::Object*>();
502     RestoreLiveRegisters(codegen, locations);
503     __ B(GetExitLabel());
504   }
505 
GetDescription() const506   const char* GetDescription() const override { return "ArraySetSlowPathMIPS"; }
507 
508  private:
509   DISALLOW_COPY_AND_ASSIGN(ArraySetSlowPathMIPS);
510 };
511 
512 // Slow path marking an object reference `ref` during a read
513 // barrier. The field `obj.field` in the object `obj` holding this
514 // reference does not get updated by this slow path after marking (see
515 // ReadBarrierMarkAndUpdateFieldSlowPathMIPS below for that).
516 //
517 // This means that after the execution of this slow path, `ref` will
518 // always be up-to-date, but `obj.field` may not; i.e., after the
519 // flip, `ref` will be a to-space reference, but `obj.field` will
520 // probably still be a from-space reference (unless it gets updated by
521 // another thread, or if another thread installed another object
522 // reference (different from `ref`) in `obj.field`).
523 //
524 // If `entrypoint` is a valid location it is assumed to already be
525 // holding the entrypoint. The case where the entrypoint is passed in
526 // is for the GcRoot read barrier.
527 class ReadBarrierMarkSlowPathMIPS : public SlowPathCodeMIPS {
528  public:
ReadBarrierMarkSlowPathMIPS(HInstruction * instruction,Location ref,Location entrypoint=Location::NoLocation ())529   ReadBarrierMarkSlowPathMIPS(HInstruction* instruction,
530                               Location ref,
531                               Location entrypoint = Location::NoLocation())
532       : SlowPathCodeMIPS(instruction), ref_(ref), entrypoint_(entrypoint) {
533     DCHECK(kEmitCompilerReadBarrier);
534   }
535 
GetDescription() const536   const char* GetDescription() const override { return "ReadBarrierMarkSlowPathMIPS"; }
537 
EmitNativeCode(CodeGenerator * codegen)538   void EmitNativeCode(CodeGenerator* codegen) override {
539     LocationSummary* locations = instruction_->GetLocations();
540     Register ref_reg = ref_.AsRegister<Register>();
541     DCHECK(locations->CanCall());
542     DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(ref_reg)) << ref_reg;
543     DCHECK(instruction_->IsInstanceFieldGet() ||
544            instruction_->IsStaticFieldGet() ||
545            instruction_->IsArrayGet() ||
546            instruction_->IsArraySet() ||
547            instruction_->IsLoadClass() ||
548            instruction_->IsLoadString() ||
549            instruction_->IsInstanceOf() ||
550            instruction_->IsCheckCast() ||
551            (instruction_->IsInvokeVirtual() && instruction_->GetLocations()->Intrinsified()) ||
552            (instruction_->IsInvokeStaticOrDirect() && instruction_->GetLocations()->Intrinsified()))
553         << "Unexpected instruction in read barrier marking slow path: "
554         << instruction_->DebugName();
555 
556     __ Bind(GetEntryLabel());
557     // No need to save live registers; it's taken care of by the
558     // entrypoint. Also, there is no need to update the stack mask,
559     // as this runtime call will not trigger a garbage collection.
560     CodeGeneratorMIPS* mips_codegen = down_cast<CodeGeneratorMIPS*>(codegen);
561     DCHECK((V0 <= ref_reg && ref_reg <= T7) ||
562            (S2 <= ref_reg && ref_reg <= S7) ||
563            (ref_reg == FP)) << ref_reg;
564     // "Compact" slow path, saving two moves.
565     //
566     // Instead of using the standard runtime calling convention (input
567     // and output in A0 and V0 respectively):
568     //
569     //   A0 <- ref
570     //   V0 <- ReadBarrierMark(A0)
571     //   ref <- V0
572     //
573     // we just use rX (the register containing `ref`) as input and output
574     // of a dedicated entrypoint:
575     //
576     //   rX <- ReadBarrierMarkRegX(rX)
577     //
578     if (entrypoint_.IsValid()) {
579       mips_codegen->ValidateInvokeRuntimeWithoutRecordingPcInfo(instruction_, this);
580       DCHECK_EQ(entrypoint_.AsRegister<Register>(), T9);
581       __ Jalr(entrypoint_.AsRegister<Register>());
582       __ NopIfNoReordering();
583     } else {
584       int32_t entry_point_offset =
585           Thread::ReadBarrierMarkEntryPointsOffset<kMipsPointerSize>(ref_reg - 1);
586       // This runtime call does not require a stack map.
587       mips_codegen->InvokeRuntimeWithoutRecordingPcInfo(entry_point_offset,
588                                                         instruction_,
589                                                         this,
590                                                         /* direct= */ false);
591     }
592     __ B(GetExitLabel());
593   }
594 
595  private:
596   // The location (register) of the marked object reference.
597   const Location ref_;
598 
599   // The location of the entrypoint if already loaded.
600   const Location entrypoint_;
601 
602   DISALLOW_COPY_AND_ASSIGN(ReadBarrierMarkSlowPathMIPS);
603 };
604 
605 // Slow path marking an object reference `ref` during a read barrier,
606 // and if needed, atomically updating the field `obj.field` in the
607 // object `obj` holding this reference after marking (contrary to
608 // ReadBarrierMarkSlowPathMIPS above, which never tries to update
609 // `obj.field`).
610 //
611 // This means that after the execution of this slow path, both `ref`
612 // and `obj.field` will be up-to-date; i.e., after the flip, both will
613 // hold the same to-space reference (unless another thread installed
614 // another object reference (different from `ref`) in `obj.field`).
615 class ReadBarrierMarkAndUpdateFieldSlowPathMIPS : public SlowPathCodeMIPS {
616  public:
ReadBarrierMarkAndUpdateFieldSlowPathMIPS(HInstruction * instruction,Location ref,Register obj,Location field_offset,Register temp1)617   ReadBarrierMarkAndUpdateFieldSlowPathMIPS(HInstruction* instruction,
618                                             Location ref,
619                                             Register obj,
620                                             Location field_offset,
621                                             Register temp1)
622       : SlowPathCodeMIPS(instruction),
623         ref_(ref),
624         obj_(obj),
625         field_offset_(field_offset),
626         temp1_(temp1) {
627     DCHECK(kEmitCompilerReadBarrier);
628   }
629 
GetDescription() const630   const char* GetDescription() const override {
631     return "ReadBarrierMarkAndUpdateFieldSlowPathMIPS";
632   }
633 
EmitNativeCode(CodeGenerator * codegen)634   void EmitNativeCode(CodeGenerator* codegen) override {
635     LocationSummary* locations = instruction_->GetLocations();
636     Register ref_reg = ref_.AsRegister<Register>();
637     DCHECK(locations->CanCall());
638     DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(ref_reg)) << ref_reg;
639     // This slow path is only used by the UnsafeCASObject intrinsic.
640     DCHECK((instruction_->IsInvokeVirtual() && instruction_->GetLocations()->Intrinsified()))
641         << "Unexpected instruction in read barrier marking and field updating slow path: "
642         << instruction_->DebugName();
643     DCHECK(instruction_->GetLocations()->Intrinsified());
644     DCHECK_EQ(instruction_->AsInvoke()->GetIntrinsic(), Intrinsics::kUnsafeCASObject);
645     DCHECK(field_offset_.IsRegisterPair()) << field_offset_;
646 
647     __ Bind(GetEntryLabel());
648 
649     // Save the old reference.
650     // Note that we cannot use AT or TMP to save the old reference, as those
651     // are used by the code that follows, but we need the old reference after
652     // the call to the ReadBarrierMarkRegX entry point.
653     DCHECK_NE(temp1_, AT);
654     DCHECK_NE(temp1_, TMP);
655     __ Move(temp1_, ref_reg);
656 
657     // No need to save live registers; it's taken care of by the
658     // entrypoint. Also, there is no need to update the stack mask,
659     // as this runtime call will not trigger a garbage collection.
660     CodeGeneratorMIPS* mips_codegen = down_cast<CodeGeneratorMIPS*>(codegen);
661     DCHECK((V0 <= ref_reg && ref_reg <= T7) ||
662            (S2 <= ref_reg && ref_reg <= S7) ||
663            (ref_reg == FP)) << ref_reg;
664     // "Compact" slow path, saving two moves.
665     //
666     // Instead of using the standard runtime calling convention (input
667     // and output in A0 and V0 respectively):
668     //
669     //   A0 <- ref
670     //   V0 <- ReadBarrierMark(A0)
671     //   ref <- V0
672     //
673     // we just use rX (the register containing `ref`) as input and output
674     // of a dedicated entrypoint:
675     //
676     //   rX <- ReadBarrierMarkRegX(rX)
677     //
678     int32_t entry_point_offset =
679         Thread::ReadBarrierMarkEntryPointsOffset<kMipsPointerSize>(ref_reg - 1);
680     // This runtime call does not require a stack map.
681     mips_codegen->InvokeRuntimeWithoutRecordingPcInfo(entry_point_offset,
682                                                       instruction_,
683                                                       this,
684                                                       /* direct= */ false);
685 
686     // If the new reference is different from the old reference,
687     // update the field in the holder (`*(obj_ + field_offset_)`).
688     //
689     // Note that this field could also hold a different object, if
690     // another thread had concurrently changed it. In that case, the
691     // the compare-and-set (CAS) loop below would abort, leaving the
692     // field as-is.
693     MipsLabel done;
694     __ Beq(temp1_, ref_reg, &done);
695 
696     // Update the the holder's field atomically.  This may fail if
697     // mutator updates before us, but it's OK.  This is achieved
698     // using a strong compare-and-set (CAS) operation with relaxed
699     // memory synchronization ordering, where the expected value is
700     // the old reference and the desired value is the new reference.
701 
702     // Convenience aliases.
703     Register base = obj_;
704     // The UnsafeCASObject intrinsic uses a register pair as field
705     // offset ("long offset"), of which only the low part contains
706     // data.
707     Register offset = field_offset_.AsRegisterPairLow<Register>();
708     Register expected = temp1_;
709     Register value = ref_reg;
710     Register tmp_ptr = TMP;      // Pointer to actual memory.
711     Register tmp = AT;           // Value in memory.
712 
713     __ Addu(tmp_ptr, base, offset);
714 
715     if (kPoisonHeapReferences) {
716       __ PoisonHeapReference(expected);
717       // Do not poison `value` if it is the same register as
718       // `expected`, which has just been poisoned.
719       if (value != expected) {
720         __ PoisonHeapReference(value);
721       }
722     }
723 
724     // do {
725     //   tmp = [r_ptr] - expected;
726     // } while (tmp == 0 && failure([r_ptr] <- r_new_value));
727 
728     bool is_r6 = mips_codegen->GetInstructionSetFeatures().IsR6();
729     MipsLabel loop_head, exit_loop;
730     __ Bind(&loop_head);
731     if (is_r6) {
732       __ LlR6(tmp, tmp_ptr);
733     } else {
734       __ LlR2(tmp, tmp_ptr);
735     }
736     __ Bne(tmp, expected, &exit_loop);
737     __ Move(tmp, value);
738     if (is_r6) {
739       __ ScR6(tmp, tmp_ptr);
740     } else {
741       __ ScR2(tmp, tmp_ptr);
742     }
743     __ Beqz(tmp, &loop_head);
744     __ Bind(&exit_loop);
745 
746     if (kPoisonHeapReferences) {
747       __ UnpoisonHeapReference(expected);
748       // Do not unpoison `value` if it is the same register as
749       // `expected`, which has just been unpoisoned.
750       if (value != expected) {
751         __ UnpoisonHeapReference(value);
752       }
753     }
754 
755     __ Bind(&done);
756     __ B(GetExitLabel());
757   }
758 
759  private:
760   // The location (register) of the marked object reference.
761   const Location ref_;
762   // The register containing the object holding the marked object reference field.
763   const Register obj_;
764   // The location of the offset of the marked reference field within `obj_`.
765   Location field_offset_;
766 
767   const Register temp1_;
768 
769   DISALLOW_COPY_AND_ASSIGN(ReadBarrierMarkAndUpdateFieldSlowPathMIPS);
770 };
771 
772 // Slow path generating a read barrier for a heap reference.
773 class ReadBarrierForHeapReferenceSlowPathMIPS : public SlowPathCodeMIPS {
774  public:
ReadBarrierForHeapReferenceSlowPathMIPS(HInstruction * instruction,Location out,Location ref,Location obj,uint32_t offset,Location index)775   ReadBarrierForHeapReferenceSlowPathMIPS(HInstruction* instruction,
776                                           Location out,
777                                           Location ref,
778                                           Location obj,
779                                           uint32_t offset,
780                                           Location index)
781       : SlowPathCodeMIPS(instruction),
782         out_(out),
783         ref_(ref),
784         obj_(obj),
785         offset_(offset),
786         index_(index) {
787     DCHECK(kEmitCompilerReadBarrier);
788     // If `obj` is equal to `out` or `ref`, it means the initial object
789     // has been overwritten by (or after) the heap object reference load
790     // to be instrumented, e.g.:
791     //
792     //   __ LoadFromOffset(kLoadWord, out, out, offset);
793     //   codegen_->GenerateReadBarrierSlow(instruction, out_loc, out_loc, out_loc, offset);
794     //
795     // In that case, we have lost the information about the original
796     // object, and the emitted read barrier cannot work properly.
797     DCHECK(!obj.Equals(out)) << "obj=" << obj << " out=" << out;
798     DCHECK(!obj.Equals(ref)) << "obj=" << obj << " ref=" << ref;
799   }
800 
EmitNativeCode(CodeGenerator * codegen)801   void EmitNativeCode(CodeGenerator* codegen) override {
802     CodeGeneratorMIPS* mips_codegen = down_cast<CodeGeneratorMIPS*>(codegen);
803     LocationSummary* locations = instruction_->GetLocations();
804     Register reg_out = out_.AsRegister<Register>();
805     DCHECK(locations->CanCall());
806     DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(reg_out));
807     DCHECK(instruction_->IsInstanceFieldGet() ||
808            instruction_->IsStaticFieldGet() ||
809            instruction_->IsArrayGet() ||
810            instruction_->IsInstanceOf() ||
811            instruction_->IsCheckCast() ||
812            (instruction_->IsInvokeVirtual() && instruction_->GetLocations()->Intrinsified()))
813         << "Unexpected instruction in read barrier for heap reference slow path: "
814         << instruction_->DebugName();
815 
816     __ Bind(GetEntryLabel());
817     SaveLiveRegisters(codegen, locations);
818 
819     // We may have to change the index's value, but as `index_` is a
820     // constant member (like other "inputs" of this slow path),
821     // introduce a copy of it, `index`.
822     Location index = index_;
823     if (index_.IsValid()) {
824       // Handle `index_` for HArrayGet and UnsafeGetObject/UnsafeGetObjectVolatile intrinsics.
825       if (instruction_->IsArrayGet()) {
826         // Compute the actual memory offset and store it in `index`.
827         Register index_reg = index_.AsRegister<Register>();
828         DCHECK(locations->GetLiveRegisters()->ContainsCoreRegister(index_reg));
829         if (codegen->IsCoreCalleeSaveRegister(index_reg)) {
830           // We are about to change the value of `index_reg` (see the
831           // calls to art::mips::MipsAssembler::Sll and
832           // art::mips::MipsAssembler::Addiu32 below), but it has
833           // not been saved by the previous call to
834           // art::SlowPathCode::SaveLiveRegisters, as it is a
835           // callee-save register --
836           // art::SlowPathCode::SaveLiveRegisters does not consider
837           // callee-save registers, as it has been designed with the
838           // assumption that callee-save registers are supposed to be
839           // handled by the called function.  So, as a callee-save
840           // register, `index_reg` _would_ eventually be saved onto
841           // the stack, but it would be too late: we would have
842           // changed its value earlier.  Therefore, we manually save
843           // it here into another freely available register,
844           // `free_reg`, chosen of course among the caller-save
845           // registers (as a callee-save `free_reg` register would
846           // exhibit the same problem).
847           //
848           // Note we could have requested a temporary register from
849           // the register allocator instead; but we prefer not to, as
850           // this is a slow path, and we know we can find a
851           // caller-save register that is available.
852           Register free_reg = FindAvailableCallerSaveRegister(codegen);
853           __ Move(free_reg, index_reg);
854           index_reg = free_reg;
855           index = Location::RegisterLocation(index_reg);
856         } else {
857           // The initial register stored in `index_` has already been
858           // saved in the call to art::SlowPathCode::SaveLiveRegisters
859           // (as it is not a callee-save register), so we can freely
860           // use it.
861         }
862         // Shifting the index value contained in `index_reg` by the scale
863         // factor (2) cannot overflow in practice, as the runtime is
864         // unable to allocate object arrays with a size larger than
865         // 2^26 - 1 (that is, 2^28 - 4 bytes).
866         __ Sll(index_reg, index_reg, TIMES_4);
867         static_assert(
868             sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
869             "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
870         __ Addiu32(index_reg, index_reg, offset_);
871       } else {
872         // In the case of the UnsafeGetObject/UnsafeGetObjectVolatile
873         // intrinsics, `index_` is not shifted by a scale factor of 2
874         // (as in the case of ArrayGet), as it is actually an offset
875         // to an object field within an object.
876         DCHECK(instruction_->IsInvoke()) << instruction_->DebugName();
877         DCHECK(instruction_->GetLocations()->Intrinsified());
878         DCHECK((instruction_->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObject) ||
879                (instruction_->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile))
880             << instruction_->AsInvoke()->GetIntrinsic();
881         DCHECK_EQ(offset_, 0U);
882         DCHECK(index_.IsRegisterPair());
883         // UnsafeGet's offset location is a register pair, the low
884         // part contains the correct offset.
885         index = index_.ToLow();
886       }
887     }
888 
889     // We're moving two or three locations to locations that could
890     // overlap, so we need a parallel move resolver.
891     InvokeRuntimeCallingConvention calling_convention;
892     HParallelMove parallel_move(codegen->GetGraph()->GetAllocator());
893     parallel_move.AddMove(ref_,
894                           Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
895                           DataType::Type::kReference,
896                           nullptr);
897     parallel_move.AddMove(obj_,
898                           Location::RegisterLocation(calling_convention.GetRegisterAt(1)),
899                           DataType::Type::kReference,
900                           nullptr);
901     if (index.IsValid()) {
902       parallel_move.AddMove(index,
903                             Location::RegisterLocation(calling_convention.GetRegisterAt(2)),
904                             DataType::Type::kInt32,
905                             nullptr);
906       codegen->GetMoveResolver()->EmitNativeCode(&parallel_move);
907     } else {
908       codegen->GetMoveResolver()->EmitNativeCode(&parallel_move);
909       __ LoadConst32(calling_convention.GetRegisterAt(2), offset_);
910     }
911     mips_codegen->InvokeRuntime(kQuickReadBarrierSlow,
912                                 instruction_,
913                                 instruction_->GetDexPc(),
914                                 this);
915     CheckEntrypointTypes<
916         kQuickReadBarrierSlow, mirror::Object*, mirror::Object*, mirror::Object*, uint32_t>();
917     mips_codegen->MoveLocation(out_,
918                                calling_convention.GetReturnLocation(DataType::Type::kReference),
919                                DataType::Type::kReference);
920 
921     RestoreLiveRegisters(codegen, locations);
922     __ B(GetExitLabel());
923   }
924 
GetDescription() const925   const char* GetDescription() const override { return "ReadBarrierForHeapReferenceSlowPathMIPS"; }
926 
927  private:
FindAvailableCallerSaveRegister(CodeGenerator * codegen)928   Register FindAvailableCallerSaveRegister(CodeGenerator* codegen) {
929     size_t ref = static_cast<int>(ref_.AsRegister<Register>());
930     size_t obj = static_cast<int>(obj_.AsRegister<Register>());
931     for (size_t i = 0, e = codegen->GetNumberOfCoreRegisters(); i < e; ++i) {
932       if (i != ref &&
933           i != obj &&
934           !codegen->IsCoreCalleeSaveRegister(i) &&
935           !codegen->IsBlockedCoreRegister(i)) {
936         return static_cast<Register>(i);
937       }
938     }
939     // We shall never fail to find a free caller-save register, as
940     // there are more than two core caller-save registers on MIPS
941     // (meaning it is possible to find one which is different from
942     // `ref` and `obj`).
943     DCHECK_GT(codegen->GetNumberOfCoreCallerSaveRegisters(), 2u);
944     LOG(FATAL) << "Could not find a free caller-save register";
945     UNREACHABLE();
946   }
947 
948   const Location out_;
949   const Location ref_;
950   const Location obj_;
951   const uint32_t offset_;
952   // An additional location containing an index to an array.
953   // Only used for HArrayGet and the UnsafeGetObject &
954   // UnsafeGetObjectVolatile intrinsics.
955   const Location index_;
956 
957   DISALLOW_COPY_AND_ASSIGN(ReadBarrierForHeapReferenceSlowPathMIPS);
958 };
959 
960 // Slow path generating a read barrier for a GC root.
961 class ReadBarrierForRootSlowPathMIPS : public SlowPathCodeMIPS {
962  public:
ReadBarrierForRootSlowPathMIPS(HInstruction * instruction,Location out,Location root)963   ReadBarrierForRootSlowPathMIPS(HInstruction* instruction, Location out, Location root)
964       : SlowPathCodeMIPS(instruction), out_(out), root_(root) {
965     DCHECK(kEmitCompilerReadBarrier);
966   }
967 
EmitNativeCode(CodeGenerator * codegen)968   void EmitNativeCode(CodeGenerator* codegen) override {
969     LocationSummary* locations = instruction_->GetLocations();
970     Register reg_out = out_.AsRegister<Register>();
971     DCHECK(locations->CanCall());
972     DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(reg_out));
973     DCHECK(instruction_->IsLoadClass() || instruction_->IsLoadString())
974         << "Unexpected instruction in read barrier for GC root slow path: "
975         << instruction_->DebugName();
976 
977     __ Bind(GetEntryLabel());
978     SaveLiveRegisters(codegen, locations);
979 
980     InvokeRuntimeCallingConvention calling_convention;
981     CodeGeneratorMIPS* mips_codegen = down_cast<CodeGeneratorMIPS*>(codegen);
982     mips_codegen->MoveLocation(Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
983                                root_,
984                                DataType::Type::kReference);
985     mips_codegen->InvokeRuntime(kQuickReadBarrierForRootSlow,
986                                 instruction_,
987                                 instruction_->GetDexPc(),
988                                 this);
989     CheckEntrypointTypes<kQuickReadBarrierForRootSlow, mirror::Object*, GcRoot<mirror::Object>*>();
990     mips_codegen->MoveLocation(out_,
991                                calling_convention.GetReturnLocation(DataType::Type::kReference),
992                                DataType::Type::kReference);
993 
994     RestoreLiveRegisters(codegen, locations);
995     __ B(GetExitLabel());
996   }
997 
GetDescription() const998   const char* GetDescription() const override { return "ReadBarrierForRootSlowPathMIPS"; }
999 
1000  private:
1001   const Location out_;
1002   const Location root_;
1003 
1004   DISALLOW_COPY_AND_ASSIGN(ReadBarrierForRootSlowPathMIPS);
1005 };
1006 
CodeGeneratorMIPS(HGraph * graph,const CompilerOptions & compiler_options,OptimizingCompilerStats * stats)1007 CodeGeneratorMIPS::CodeGeneratorMIPS(HGraph* graph,
1008                                      const CompilerOptions& compiler_options,
1009                                      OptimizingCompilerStats* stats)
1010     : CodeGenerator(graph,
1011                     kNumberOfCoreRegisters,
1012                     kNumberOfFRegisters,
1013                     kNumberOfRegisterPairs,
1014                     ComputeRegisterMask(reinterpret_cast<const int*>(kCoreCalleeSaves),
1015                                         arraysize(kCoreCalleeSaves)),
1016                     ComputeRegisterMask(reinterpret_cast<const int*>(kFpuCalleeSaves),
1017                                         arraysize(kFpuCalleeSaves)),
1018                     compiler_options,
1019                     stats),
1020       block_labels_(nullptr),
1021       location_builder_(graph, this),
1022       instruction_visitor_(graph, this),
1023       move_resolver_(graph->GetAllocator(), this),
1024       assembler_(graph->GetAllocator(),
1025                  compiler_options.GetInstructionSetFeatures()->AsMipsInstructionSetFeatures()),
1026       uint32_literals_(std::less<uint32_t>(),
1027                        graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
1028       boot_image_method_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
1029       method_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
1030       boot_image_type_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
1031       type_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
1032       boot_image_string_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
1033       string_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
1034       boot_image_intrinsic_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
1035       jit_string_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
1036       jit_class_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
1037       clobbered_ra_(false) {
1038   // Save RA (containing the return address) to mimic Quick.
1039   AddAllocatedRegister(Location::RegisterLocation(RA));
1040 }
1041 
1042 #undef __
1043 // NOLINT on __ macro to suppress wrong warning/fix (misc-macro-parentheses) from clang-tidy.
1044 #define __ down_cast<MipsAssembler*>(GetAssembler())->  // NOLINT
1045 #define QUICK_ENTRY_POINT(x) QUICK_ENTRYPOINT_OFFSET(kMipsPointerSize, x).Int32Value()
1046 
Finalize(CodeAllocator * allocator)1047 void CodeGeneratorMIPS::Finalize(CodeAllocator* allocator) {
1048   // Ensure that we fix up branches.
1049   __ FinalizeCode();
1050 
1051   // Adjust native pc offsets in stack maps.
1052   StackMapStream* stack_map_stream = GetStackMapStream();
1053   for (size_t i = 0, num = stack_map_stream->GetNumberOfStackMaps(); i != num; ++i) {
1054     uint32_t old_position = stack_map_stream->GetStackMapNativePcOffset(i);
1055     uint32_t new_position = __ GetAdjustedPosition(old_position);
1056     DCHECK_GE(new_position, old_position);
1057     stack_map_stream->SetStackMapNativePcOffset(i, new_position);
1058   }
1059 
1060   // Adjust pc offsets for the disassembly information.
1061   if (disasm_info_ != nullptr) {
1062     GeneratedCodeInterval* frame_entry_interval = disasm_info_->GetFrameEntryInterval();
1063     frame_entry_interval->start = __ GetAdjustedPosition(frame_entry_interval->start);
1064     frame_entry_interval->end = __ GetAdjustedPosition(frame_entry_interval->end);
1065     for (auto& it : *disasm_info_->GetInstructionIntervals()) {
1066       it.second.start = __ GetAdjustedPosition(it.second.start);
1067       it.second.end = __ GetAdjustedPosition(it.second.end);
1068     }
1069     for (auto& it : *disasm_info_->GetSlowPathIntervals()) {
1070       it.code_interval.start = __ GetAdjustedPosition(it.code_interval.start);
1071       it.code_interval.end = __ GetAdjustedPosition(it.code_interval.end);
1072     }
1073   }
1074 
1075   CodeGenerator::Finalize(allocator);
1076 }
1077 
GetAssembler() const1078 MipsAssembler* ParallelMoveResolverMIPS::GetAssembler() const {
1079   return codegen_->GetAssembler();
1080 }
1081 
EmitMove(size_t index)1082 void ParallelMoveResolverMIPS::EmitMove(size_t index) {
1083   DCHECK_LT(index, moves_.size());
1084   MoveOperands* move = moves_[index];
1085   codegen_->MoveLocation(move->GetDestination(), move->GetSource(), move->GetType());
1086 }
1087 
EmitSwap(size_t index)1088 void ParallelMoveResolverMIPS::EmitSwap(size_t index) {
1089   DCHECK_LT(index, moves_.size());
1090   MoveOperands* move = moves_[index];
1091   DataType::Type type = move->GetType();
1092   Location loc1 = move->GetDestination();
1093   Location loc2 = move->GetSource();
1094 
1095   DCHECK(!loc1.IsConstant());
1096   DCHECK(!loc2.IsConstant());
1097 
1098   if (loc1.Equals(loc2)) {
1099     return;
1100   }
1101 
1102   if (loc1.IsRegister() && loc2.IsRegister()) {
1103     // Swap 2 GPRs.
1104     Register r1 = loc1.AsRegister<Register>();
1105     Register r2 = loc2.AsRegister<Register>();
1106     __ Move(TMP, r2);
1107     __ Move(r2, r1);
1108     __ Move(r1, TMP);
1109   } else if (loc1.IsFpuRegister() && loc2.IsFpuRegister()) {
1110     if (codegen_->GetGraph()->HasSIMD()) {
1111       __ MoveV(static_cast<VectorRegister>(FTMP), VectorRegisterFrom(loc1));
1112       __ MoveV(VectorRegisterFrom(loc1), VectorRegisterFrom(loc2));
1113       __ MoveV(VectorRegisterFrom(loc2), static_cast<VectorRegister>(FTMP));
1114     } else {
1115       FRegister f1 = loc1.AsFpuRegister<FRegister>();
1116       FRegister f2 = loc2.AsFpuRegister<FRegister>();
1117       if (type == DataType::Type::kFloat32) {
1118         __ MovS(FTMP, f2);
1119         __ MovS(f2, f1);
1120         __ MovS(f1, FTMP);
1121       } else {
1122         DCHECK_EQ(type, DataType::Type::kFloat64);
1123         __ MovD(FTMP, f2);
1124         __ MovD(f2, f1);
1125         __ MovD(f1, FTMP);
1126       }
1127     }
1128   } else if ((loc1.IsRegister() && loc2.IsFpuRegister()) ||
1129              (loc1.IsFpuRegister() && loc2.IsRegister())) {
1130     // Swap FPR and GPR.
1131     DCHECK_EQ(type, DataType::Type::kFloat32);  // Can only swap a float.
1132     FRegister f1 = loc1.IsFpuRegister() ? loc1.AsFpuRegister<FRegister>()
1133                                         : loc2.AsFpuRegister<FRegister>();
1134     Register r2 = loc1.IsRegister() ? loc1.AsRegister<Register>() : loc2.AsRegister<Register>();
1135     __ Move(TMP, r2);
1136     __ Mfc1(r2, f1);
1137     __ Mtc1(TMP, f1);
1138   } else if (loc1.IsRegisterPair() && loc2.IsRegisterPair()) {
1139     // Swap 2 GPR register pairs.
1140     Register r1 = loc1.AsRegisterPairLow<Register>();
1141     Register r2 = loc2.AsRegisterPairLow<Register>();
1142     __ Move(TMP, r2);
1143     __ Move(r2, r1);
1144     __ Move(r1, TMP);
1145     r1 = loc1.AsRegisterPairHigh<Register>();
1146     r2 = loc2.AsRegisterPairHigh<Register>();
1147     __ Move(TMP, r2);
1148     __ Move(r2, r1);
1149     __ Move(r1, TMP);
1150   } else if ((loc1.IsRegisterPair() && loc2.IsFpuRegister()) ||
1151              (loc1.IsFpuRegister() && loc2.IsRegisterPair())) {
1152     // Swap FPR and GPR register pair.
1153     DCHECK_EQ(type, DataType::Type::kFloat64);
1154     FRegister f1 = loc1.IsFpuRegister() ? loc1.AsFpuRegister<FRegister>()
1155                                         : loc2.AsFpuRegister<FRegister>();
1156     Register r2_l = loc1.IsRegisterPair() ? loc1.AsRegisterPairLow<Register>()
1157                                           : loc2.AsRegisterPairLow<Register>();
1158     Register r2_h = loc1.IsRegisterPair() ? loc1.AsRegisterPairHigh<Register>()
1159                                           : loc2.AsRegisterPairHigh<Register>();
1160     // Use 2 temporary registers because we can't first swap the low 32 bits of an FPR and
1161     // then swap the high 32 bits of the same FPR. mtc1 makes the high 32 bits of an FPR
1162     // unpredictable and the following mfch1 will fail.
1163     __ Mfc1(TMP, f1);
1164     __ MoveFromFpuHigh(AT, f1);
1165     __ Mtc1(r2_l, f1);
1166     __ MoveToFpuHigh(r2_h, f1);
1167     __ Move(r2_l, TMP);
1168     __ Move(r2_h, AT);
1169   } else if (loc1.IsStackSlot() && loc2.IsStackSlot()) {
1170     Exchange(loc1.GetStackIndex(), loc2.GetStackIndex(), /* double_slot= */ false);
1171   } else if (loc1.IsDoubleStackSlot() && loc2.IsDoubleStackSlot()) {
1172     Exchange(loc1.GetStackIndex(), loc2.GetStackIndex(), /* double_slot= */ true);
1173   } else if (loc1.IsSIMDStackSlot() && loc2.IsSIMDStackSlot()) {
1174     ExchangeQuadSlots(loc1.GetStackIndex(), loc2.GetStackIndex());
1175   } else if ((loc1.IsRegister() && loc2.IsStackSlot()) ||
1176              (loc1.IsStackSlot() && loc2.IsRegister())) {
1177     Register reg = loc1.IsRegister() ? loc1.AsRegister<Register>() : loc2.AsRegister<Register>();
1178     intptr_t offset = loc1.IsStackSlot() ? loc1.GetStackIndex() : loc2.GetStackIndex();
1179     __ Move(TMP, reg);
1180     __ LoadFromOffset(kLoadWord, reg, SP, offset);
1181     __ StoreToOffset(kStoreWord, TMP, SP, offset);
1182   } else if ((loc1.IsRegisterPair() && loc2.IsDoubleStackSlot()) ||
1183              (loc1.IsDoubleStackSlot() && loc2.IsRegisterPair())) {
1184     Register reg_l = loc1.IsRegisterPair() ? loc1.AsRegisterPairLow<Register>()
1185                                            : loc2.AsRegisterPairLow<Register>();
1186     Register reg_h = loc1.IsRegisterPair() ? loc1.AsRegisterPairHigh<Register>()
1187                                            : loc2.AsRegisterPairHigh<Register>();
1188     intptr_t offset_l = loc1.IsDoubleStackSlot() ? loc1.GetStackIndex() : loc2.GetStackIndex();
1189     intptr_t offset_h = loc1.IsDoubleStackSlot() ? loc1.GetHighStackIndex(kMipsWordSize)
1190                                                  : loc2.GetHighStackIndex(kMipsWordSize);
1191     __ Move(TMP, reg_l);
1192     __ LoadFromOffset(kLoadWord, reg_l, SP, offset_l);
1193     __ StoreToOffset(kStoreWord, TMP, SP, offset_l);
1194     __ Move(TMP, reg_h);
1195     __ LoadFromOffset(kLoadWord, reg_h, SP, offset_h);
1196     __ StoreToOffset(kStoreWord, TMP, SP, offset_h);
1197   } else if ((loc1.IsFpuRegister() && loc2.IsSIMDStackSlot()) ||
1198              (loc1.IsSIMDStackSlot() && loc2.IsFpuRegister())) {
1199     Location fp_loc = loc1.IsFpuRegister() ? loc1 : loc2;
1200     intptr_t offset = loc1.IsFpuRegister() ? loc2.GetStackIndex() : loc1.GetStackIndex();
1201     __ MoveV(static_cast<VectorRegister>(FTMP), VectorRegisterFrom(fp_loc));
1202     __ LoadQFromOffset(fp_loc.AsFpuRegister<FRegister>(), SP, offset);
1203     __ StoreQToOffset(FTMP, SP, offset);
1204   } else if (loc1.IsFpuRegister() || loc2.IsFpuRegister()) {
1205     FRegister reg = loc1.IsFpuRegister() ? loc1.AsFpuRegister<FRegister>()
1206                                          : loc2.AsFpuRegister<FRegister>();
1207     intptr_t offset = loc1.IsFpuRegister() ? loc2.GetStackIndex() : loc1.GetStackIndex();
1208     if (type == DataType::Type::kFloat32) {
1209       __ MovS(FTMP, reg);
1210       __ LoadSFromOffset(reg, SP, offset);
1211       __ StoreSToOffset(FTMP, SP, offset);
1212     } else {
1213       DCHECK_EQ(type, DataType::Type::kFloat64);
1214       __ MovD(FTMP, reg);
1215       __ LoadDFromOffset(reg, SP, offset);
1216       __ StoreDToOffset(FTMP, SP, offset);
1217     }
1218   } else {
1219     LOG(FATAL) << "Swap between " << loc1 << " and " << loc2 << " is unsupported";
1220   }
1221 }
1222 
RestoreScratch(int reg)1223 void ParallelMoveResolverMIPS::RestoreScratch(int reg) {
1224   __ Pop(static_cast<Register>(reg));
1225 }
1226 
SpillScratch(int reg)1227 void ParallelMoveResolverMIPS::SpillScratch(int reg) {
1228   __ Push(static_cast<Register>(reg));
1229 }
1230 
Exchange(int index1,int index2,bool double_slot)1231 void ParallelMoveResolverMIPS::Exchange(int index1, int index2, bool double_slot) {
1232   // Allocate a scratch register other than TMP, if available.
1233   // Else, spill V0 (arbitrary choice) and use it as a scratch register (it will be
1234   // automatically unspilled when the scratch scope object is destroyed).
1235   ScratchRegisterScope ensure_scratch(this, TMP, V0, codegen_->GetNumberOfCoreRegisters());
1236   // If V0 spills onto the stack, SP-relative offsets need to be adjusted.
1237   int stack_offset = ensure_scratch.IsSpilled() ? kStackAlignment : 0;
1238   for (int i = 0; i <= (double_slot ? 1 : 0); i++, stack_offset += kMipsWordSize) {
1239     __ LoadFromOffset(kLoadWord,
1240                       Register(ensure_scratch.GetRegister()),
1241                       SP,
1242                       index1 + stack_offset);
1243     __ LoadFromOffset(kLoadWord,
1244                       TMP,
1245                       SP,
1246                       index2 + stack_offset);
1247     __ StoreToOffset(kStoreWord,
1248                      Register(ensure_scratch.GetRegister()),
1249                      SP,
1250                      index2 + stack_offset);
1251     __ StoreToOffset(kStoreWord, TMP, SP, index1 + stack_offset);
1252   }
1253 }
1254 
ExchangeQuadSlots(int index1,int index2)1255 void ParallelMoveResolverMIPS::ExchangeQuadSlots(int index1, int index2) {
1256   __ LoadQFromOffset(FTMP, SP, index1);
1257   __ LoadQFromOffset(FTMP2, SP, index2);
1258   __ StoreQToOffset(FTMP, SP, index2);
1259   __ StoreQToOffset(FTMP2, SP, index1);
1260 }
1261 
ComputeSpillMask()1262 void CodeGeneratorMIPS::ComputeSpillMask() {
1263   core_spill_mask_ = allocated_registers_.GetCoreRegisters() & core_callee_save_mask_;
1264   fpu_spill_mask_ = allocated_registers_.GetFloatingPointRegisters() & fpu_callee_save_mask_;
1265   DCHECK_NE(core_spill_mask_, 0u) << "At least the return address register must be saved";
1266   // If there're FPU callee-saved registers and there's an odd number of GPR callee-saved
1267   // registers, include the ZERO register to force alignment of FPU callee-saved registers
1268   // within the stack frame.
1269   if ((fpu_spill_mask_ != 0) && (POPCOUNT(core_spill_mask_) % 2 != 0)) {
1270     core_spill_mask_ |= (1 << ZERO);
1271   }
1272 }
1273 
HasAllocatedCalleeSaveRegisters() const1274 bool CodeGeneratorMIPS::HasAllocatedCalleeSaveRegisters() const {
1275   // If RA is clobbered by PC-relative operations on R2 and it's the only spilled register
1276   // (this can happen in leaf methods), force CodeGenerator::InitializeCodeGeneration()
1277   // into the path that creates a stack frame so that RA can be explicitly saved and restored.
1278   // RA can't otherwise be saved/restored when it's the only spilled register.
1279   return CodeGenerator::HasAllocatedCalleeSaveRegisters() || clobbered_ra_;
1280 }
1281 
DWARFReg(Register reg)1282 static dwarf::Reg DWARFReg(Register reg) {
1283   return dwarf::Reg::MipsCore(static_cast<int>(reg));
1284 }
1285 
1286 // TODO: mapping of floating-point registers to DWARF.
1287 
GenerateFrameEntry()1288 void CodeGeneratorMIPS::GenerateFrameEntry() {
1289   __ Bind(&frame_entry_label_);
1290 
1291   if (GetCompilerOptions().CountHotnessInCompiledCode()) {
1292     __ Lhu(TMP, kMethodRegisterArgument, ArtMethod::HotnessCountOffset().Int32Value());
1293     __ Addiu(TMP, TMP, 1);
1294     __ Sh(TMP, kMethodRegisterArgument, ArtMethod::HotnessCountOffset().Int32Value());
1295   }
1296 
1297   bool do_overflow_check =
1298       FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kMips) || !IsLeafMethod();
1299 
1300   if (do_overflow_check) {
1301     __ LoadFromOffset(kLoadWord,
1302                       ZERO,
1303                       SP,
1304                       -static_cast<int32_t>(GetStackOverflowReservedBytes(InstructionSet::kMips)));
1305     RecordPcInfo(nullptr, 0);
1306   }
1307 
1308   if (HasEmptyFrame()) {
1309     CHECK_EQ(fpu_spill_mask_, 0u);
1310     CHECK_EQ(core_spill_mask_, 1u << RA);
1311     CHECK(!clobbered_ra_);
1312     return;
1313   }
1314 
1315   // Make sure the frame size isn't unreasonably large.
1316   if (GetFrameSize() > GetStackOverflowReservedBytes(InstructionSet::kMips)) {
1317     LOG(FATAL) << "Stack frame larger than "
1318         << GetStackOverflowReservedBytes(InstructionSet::kMips) << " bytes";
1319   }
1320 
1321   // Spill callee-saved registers.
1322 
1323   uint32_t ofs = GetFrameSize();
1324   __ IncreaseFrameSize(ofs);
1325 
1326   for (uint32_t mask = core_spill_mask_; mask != 0; ) {
1327     Register reg = static_cast<Register>(MostSignificantBit(mask));
1328     mask ^= 1u << reg;
1329     ofs -= kMipsWordSize;
1330     // The ZERO register is only included for alignment.
1331     if (reg != ZERO) {
1332       __ StoreToOffset(kStoreWord, reg, SP, ofs);
1333       __ cfi().RelOffset(DWARFReg(reg), ofs);
1334     }
1335   }
1336 
1337   for (uint32_t mask = fpu_spill_mask_; mask != 0; ) {
1338     FRegister reg = static_cast<FRegister>(MostSignificantBit(mask));
1339     mask ^= 1u << reg;
1340     ofs -= kMipsDoublewordSize;
1341     __ StoreDToOffset(reg, SP, ofs);
1342     // TODO: __ cfi().RelOffset(DWARFReg(reg), ofs);
1343   }
1344 
1345   // Save the current method if we need it. Note that we do not
1346   // do this in HCurrentMethod, as the instruction might have been removed
1347   // in the SSA graph.
1348   if (RequiresCurrentMethod()) {
1349     __ StoreToOffset(kStoreWord, kMethodRegisterArgument, SP, kCurrentMethodStackOffset);
1350   }
1351 
1352   if (GetGraph()->HasShouldDeoptimizeFlag()) {
1353     // Initialize should deoptimize flag to 0.
1354     __ StoreToOffset(kStoreWord, ZERO, SP, GetStackOffsetOfShouldDeoptimizeFlag());
1355   }
1356 }
1357 
GenerateFrameExit()1358 void CodeGeneratorMIPS::GenerateFrameExit() {
1359   __ cfi().RememberState();
1360 
1361   if (!HasEmptyFrame()) {
1362     // Restore callee-saved registers.
1363 
1364     // For better instruction scheduling restore RA before other registers.
1365     uint32_t ofs = GetFrameSize();
1366     for (uint32_t mask = core_spill_mask_; mask != 0; ) {
1367       Register reg = static_cast<Register>(MostSignificantBit(mask));
1368       mask ^= 1u << reg;
1369       ofs -= kMipsWordSize;
1370       // The ZERO register is only included for alignment.
1371       if (reg != ZERO) {
1372         __ LoadFromOffset(kLoadWord, reg, SP, ofs);
1373         __ cfi().Restore(DWARFReg(reg));
1374       }
1375     }
1376 
1377     for (uint32_t mask = fpu_spill_mask_; mask != 0; ) {
1378       FRegister reg = static_cast<FRegister>(MostSignificantBit(mask));
1379       mask ^= 1u << reg;
1380       ofs -= kMipsDoublewordSize;
1381       __ LoadDFromOffset(reg, SP, ofs);
1382       // TODO: __ cfi().Restore(DWARFReg(reg));
1383     }
1384 
1385     size_t frame_size = GetFrameSize();
1386     // Adjust the stack pointer in the delay slot if doing so doesn't break CFI.
1387     bool exchange = IsInt<16>(static_cast<int32_t>(frame_size));
1388     bool reordering = __ SetReorder(false);
1389     if (exchange) {
1390       __ Jr(RA);
1391       __ DecreaseFrameSize(frame_size);  // Single instruction in delay slot.
1392     } else {
1393       __ DecreaseFrameSize(frame_size);
1394       __ Jr(RA);
1395       __ Nop();  // In delay slot.
1396     }
1397     __ SetReorder(reordering);
1398   } else {
1399     __ Jr(RA);
1400     __ NopIfNoReordering();
1401   }
1402 
1403   __ cfi().RestoreState();
1404   __ cfi().DefCFAOffset(GetFrameSize());
1405 }
1406 
Bind(HBasicBlock * block)1407 void CodeGeneratorMIPS::Bind(HBasicBlock* block) {
1408   __ Bind(GetLabelOf(block));
1409 }
1410 
VectorRegisterFrom(Location location)1411 VectorRegister VectorRegisterFrom(Location location) {
1412   DCHECK(location.IsFpuRegister());
1413   return static_cast<VectorRegister>(location.AsFpuRegister<FRegister>());
1414 }
1415 
MoveLocation(Location destination,Location source,DataType::Type dst_type)1416 void CodeGeneratorMIPS::MoveLocation(Location destination,
1417                                      Location source,
1418                                      DataType::Type dst_type) {
1419   if (source.Equals(destination)) {
1420     return;
1421   }
1422 
1423   if (source.IsConstant()) {
1424     MoveConstant(destination, source.GetConstant());
1425   } else {
1426     if (destination.IsRegister()) {
1427       if (source.IsRegister()) {
1428         __ Move(destination.AsRegister<Register>(), source.AsRegister<Register>());
1429       } else if (source.IsFpuRegister()) {
1430         __ Mfc1(destination.AsRegister<Register>(), source.AsFpuRegister<FRegister>());
1431       } else {
1432         DCHECK(source.IsStackSlot()) << "Cannot move from " << source << " to " << destination;
1433       __ LoadFromOffset(kLoadWord, destination.AsRegister<Register>(), SP, source.GetStackIndex());
1434       }
1435     } else if (destination.IsRegisterPair()) {
1436       if (source.IsRegisterPair()) {
1437         __ Move(destination.AsRegisterPairHigh<Register>(), source.AsRegisterPairHigh<Register>());
1438         __ Move(destination.AsRegisterPairLow<Register>(), source.AsRegisterPairLow<Register>());
1439       } else if (source.IsFpuRegister()) {
1440         Register dst_high = destination.AsRegisterPairHigh<Register>();
1441         Register dst_low =  destination.AsRegisterPairLow<Register>();
1442         FRegister src = source.AsFpuRegister<FRegister>();
1443         __ Mfc1(dst_low, src);
1444         __ MoveFromFpuHigh(dst_high, src);
1445       } else {
1446         DCHECK(source.IsDoubleStackSlot())
1447             << "Cannot move from " << source << " to " << destination;
1448         int32_t off = source.GetStackIndex();
1449         Register r = destination.AsRegisterPairLow<Register>();
1450         __ LoadFromOffset(kLoadDoubleword, r, SP, off);
1451       }
1452     } else if (destination.IsFpuRegister()) {
1453       if (source.IsRegister()) {
1454         DCHECK(!DataType::Is64BitType(dst_type));
1455         __ Mtc1(source.AsRegister<Register>(), destination.AsFpuRegister<FRegister>());
1456       } else if (source.IsRegisterPair()) {
1457         DCHECK(DataType::Is64BitType(dst_type));
1458         FRegister dst = destination.AsFpuRegister<FRegister>();
1459         Register src_high = source.AsRegisterPairHigh<Register>();
1460         Register src_low = source.AsRegisterPairLow<Register>();
1461         __ Mtc1(src_low, dst);
1462         __ MoveToFpuHigh(src_high, dst);
1463       } else if (source.IsFpuRegister()) {
1464         if (GetGraph()->HasSIMD()) {
1465           __ MoveV(VectorRegisterFrom(destination),
1466                    VectorRegisterFrom(source));
1467         } else {
1468           if (DataType::Is64BitType(dst_type)) {
1469             __ MovD(destination.AsFpuRegister<FRegister>(), source.AsFpuRegister<FRegister>());
1470           } else {
1471             DCHECK_EQ(dst_type, DataType::Type::kFloat32);
1472             __ MovS(destination.AsFpuRegister<FRegister>(), source.AsFpuRegister<FRegister>());
1473           }
1474         }
1475       } else if (source.IsSIMDStackSlot()) {
1476         __ LoadQFromOffset(destination.AsFpuRegister<FRegister>(), SP, source.GetStackIndex());
1477       } else if (source.IsDoubleStackSlot()) {
1478         DCHECK(DataType::Is64BitType(dst_type));
1479         __ LoadDFromOffset(destination.AsFpuRegister<FRegister>(), SP, source.GetStackIndex());
1480       } else {
1481         DCHECK(!DataType::Is64BitType(dst_type));
1482         DCHECK(source.IsStackSlot()) << "Cannot move from " << source << " to " << destination;
1483         __ LoadSFromOffset(destination.AsFpuRegister<FRegister>(), SP, source.GetStackIndex());
1484       }
1485     } else if (destination.IsSIMDStackSlot()) {
1486       if (source.IsFpuRegister()) {
1487         __ StoreQToOffset(source.AsFpuRegister<FRegister>(), SP, destination.GetStackIndex());
1488       } else {
1489         DCHECK(source.IsSIMDStackSlot());
1490         __ LoadQFromOffset(FTMP, SP, source.GetStackIndex());
1491         __ StoreQToOffset(FTMP, SP, destination.GetStackIndex());
1492       }
1493     } else if (destination.IsDoubleStackSlot()) {
1494       int32_t dst_offset = destination.GetStackIndex();
1495       if (source.IsRegisterPair()) {
1496         __ StoreToOffset(kStoreDoubleword, source.AsRegisterPairLow<Register>(), SP, dst_offset);
1497       } else if (source.IsFpuRegister()) {
1498         __ StoreDToOffset(source.AsFpuRegister<FRegister>(), SP, dst_offset);
1499       } else {
1500         DCHECK(source.IsDoubleStackSlot())
1501             << "Cannot move from " << source << " to " << destination;
1502         __ LoadFromOffset(kLoadWord, TMP, SP, source.GetStackIndex());
1503         __ StoreToOffset(kStoreWord, TMP, SP, dst_offset);
1504         __ LoadFromOffset(kLoadWord, TMP, SP, source.GetStackIndex() + 4);
1505         __ StoreToOffset(kStoreWord, TMP, SP, dst_offset + 4);
1506       }
1507     } else {
1508       DCHECK(destination.IsStackSlot()) << destination;
1509       int32_t dst_offset = destination.GetStackIndex();
1510       if (source.IsRegister()) {
1511         __ StoreToOffset(kStoreWord, source.AsRegister<Register>(), SP, dst_offset);
1512       } else if (source.IsFpuRegister()) {
1513         __ StoreSToOffset(source.AsFpuRegister<FRegister>(), SP, dst_offset);
1514       } else {
1515         DCHECK(source.IsStackSlot()) << "Cannot move from " << source << " to " << destination;
1516         __ LoadFromOffset(kLoadWord, TMP, SP, source.GetStackIndex());
1517         __ StoreToOffset(kStoreWord, TMP, SP, dst_offset);
1518       }
1519     }
1520   }
1521 }
1522 
MoveConstant(Location destination,HConstant * c)1523 void CodeGeneratorMIPS::MoveConstant(Location destination, HConstant* c) {
1524   if (c->IsIntConstant() || c->IsNullConstant()) {
1525     // Move 32 bit constant.
1526     int32_t value = GetInt32ValueOf(c);
1527     if (destination.IsRegister()) {
1528       Register dst = destination.AsRegister<Register>();
1529       __ LoadConst32(dst, value);
1530     } else {
1531       DCHECK(destination.IsStackSlot())
1532           << "Cannot move " << c->DebugName() << " to " << destination;
1533       __ StoreConstToOffset(kStoreWord, value, SP, destination.GetStackIndex(), TMP);
1534     }
1535   } else if (c->IsLongConstant()) {
1536     // Move 64 bit constant.
1537     int64_t value = GetInt64ValueOf(c);
1538     if (destination.IsRegisterPair()) {
1539       Register r_h = destination.AsRegisterPairHigh<Register>();
1540       Register r_l = destination.AsRegisterPairLow<Register>();
1541       __ LoadConst64(r_h, r_l, value);
1542     } else {
1543       DCHECK(destination.IsDoubleStackSlot())
1544           << "Cannot move " << c->DebugName() << " to " << destination;
1545       __ StoreConstToOffset(kStoreDoubleword, value, SP, destination.GetStackIndex(), TMP);
1546     }
1547   } else if (c->IsFloatConstant()) {
1548     // Move 32 bit float constant.
1549     int32_t value = GetInt32ValueOf(c);
1550     if (destination.IsFpuRegister()) {
1551       __ LoadSConst32(destination.AsFpuRegister<FRegister>(), value, TMP);
1552     } else {
1553       DCHECK(destination.IsStackSlot())
1554           << "Cannot move " << c->DebugName() << " to " << destination;
1555       __ StoreConstToOffset(kStoreWord, value, SP, destination.GetStackIndex(), TMP);
1556     }
1557   } else {
1558     // Move 64 bit double constant.
1559     DCHECK(c->IsDoubleConstant()) << c->DebugName();
1560     int64_t value = GetInt64ValueOf(c);
1561     if (destination.IsFpuRegister()) {
1562       FRegister fd = destination.AsFpuRegister<FRegister>();
1563       __ LoadDConst64(fd, value, TMP);
1564     } else {
1565       DCHECK(destination.IsDoubleStackSlot())
1566           << "Cannot move " << c->DebugName() << " to " << destination;
1567       __ StoreConstToOffset(kStoreDoubleword, value, SP, destination.GetStackIndex(), TMP);
1568     }
1569   }
1570 }
1571 
MoveConstant(Location destination,int32_t value)1572 void CodeGeneratorMIPS::MoveConstant(Location destination, int32_t value) {
1573   DCHECK(destination.IsRegister());
1574   Register dst = destination.AsRegister<Register>();
1575   __ LoadConst32(dst, value);
1576 }
1577 
AddLocationAsTemp(Location location,LocationSummary * locations)1578 void CodeGeneratorMIPS::AddLocationAsTemp(Location location, LocationSummary* locations) {
1579   if (location.IsRegister()) {
1580     locations->AddTemp(location);
1581   } else if (location.IsRegisterPair()) {
1582     locations->AddTemp(Location::RegisterLocation(location.AsRegisterPairLow<Register>()));
1583     locations->AddTemp(Location::RegisterLocation(location.AsRegisterPairHigh<Register>()));
1584   } else {
1585     UNIMPLEMENTED(FATAL) << "AddLocationAsTemp not implemented for location " << location;
1586   }
1587 }
1588 
1589 template <linker::LinkerPatch (*Factory)(size_t, const DexFile*, uint32_t, uint32_t)>
EmitPcRelativeLinkerPatches(const ArenaDeque<PcRelativePatchInfo> & infos,ArenaVector<linker::LinkerPatch> * linker_patches)1590 inline void CodeGeneratorMIPS::EmitPcRelativeLinkerPatches(
1591     const ArenaDeque<PcRelativePatchInfo>& infos,
1592     ArenaVector<linker::LinkerPatch>* linker_patches) {
1593   for (const PcRelativePatchInfo& info : infos) {
1594     const DexFile* dex_file = info.target_dex_file;
1595     size_t offset_or_index = info.offset_or_index;
1596     DCHECK(info.label.IsBound());
1597     uint32_t literal_offset = __ GetLabelLocation(&info.label);
1598     // On R2 we use HMipsComputeBaseMethodAddress and patch relative to
1599     // the assembler's base label used for PC-relative addressing.
1600     const PcRelativePatchInfo& info_high = info.patch_info_high ? *info.patch_info_high : info;
1601     uint32_t pc_rel_offset = info_high.pc_rel_label.IsBound()
1602         ? __ GetLabelLocation(&info_high.pc_rel_label)
1603         : __ GetPcRelBaseLabelLocation();
1604     linker_patches->push_back(Factory(literal_offset, dex_file, pc_rel_offset, offset_or_index));
1605   }
1606 }
1607 
1608 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)1609 linker::LinkerPatch NoDexFileAdapter(size_t literal_offset,
1610                                      const DexFile* target_dex_file,
1611                                      uint32_t pc_insn_offset,
1612                                      uint32_t boot_image_offset) {
1613   DCHECK(target_dex_file == nullptr);  // Unused for these patches, should be null.
1614   return Factory(literal_offset, pc_insn_offset, boot_image_offset);
1615 }
1616 
EmitLinkerPatches(ArenaVector<linker::LinkerPatch> * linker_patches)1617 void CodeGeneratorMIPS::EmitLinkerPatches(ArenaVector<linker::LinkerPatch>* linker_patches) {
1618   DCHECK(linker_patches->empty());
1619   size_t size =
1620       boot_image_method_patches_.size() +
1621       method_bss_entry_patches_.size() +
1622       boot_image_type_patches_.size() +
1623       type_bss_entry_patches_.size() +
1624       boot_image_string_patches_.size() +
1625       string_bss_entry_patches_.size() +
1626       boot_image_intrinsic_patches_.size();
1627   linker_patches->reserve(size);
1628   if (GetCompilerOptions().IsBootImage()) {
1629     EmitPcRelativeLinkerPatches<linker::LinkerPatch::RelativeMethodPatch>(
1630         boot_image_method_patches_, linker_patches);
1631     EmitPcRelativeLinkerPatches<linker::LinkerPatch::RelativeTypePatch>(
1632         boot_image_type_patches_, linker_patches);
1633     EmitPcRelativeLinkerPatches<linker::LinkerPatch::RelativeStringPatch>(
1634         boot_image_string_patches_, linker_patches);
1635     EmitPcRelativeLinkerPatches<NoDexFileAdapter<linker::LinkerPatch::IntrinsicReferencePatch>>(
1636         boot_image_intrinsic_patches_, linker_patches);
1637   } else {
1638     EmitPcRelativeLinkerPatches<NoDexFileAdapter<linker::LinkerPatch::DataBimgRelRoPatch>>(
1639         boot_image_method_patches_, linker_patches);
1640     DCHECK(boot_image_type_patches_.empty());
1641     DCHECK(boot_image_string_patches_.empty());
1642     DCHECK(boot_image_intrinsic_patches_.empty());
1643   }
1644   EmitPcRelativeLinkerPatches<linker::LinkerPatch::MethodBssEntryPatch>(
1645       method_bss_entry_patches_, linker_patches);
1646   EmitPcRelativeLinkerPatches<linker::LinkerPatch::TypeBssEntryPatch>(
1647       type_bss_entry_patches_, linker_patches);
1648   EmitPcRelativeLinkerPatches<linker::LinkerPatch::StringBssEntryPatch>(
1649       string_bss_entry_patches_, linker_patches);
1650   DCHECK_EQ(size, linker_patches->size());
1651 }
1652 
NewBootImageIntrinsicPatch(uint32_t intrinsic_data,const PcRelativePatchInfo * info_high)1653 CodeGeneratorMIPS::PcRelativePatchInfo* CodeGeneratorMIPS::NewBootImageIntrinsicPatch(
1654     uint32_t intrinsic_data,
1655     const PcRelativePatchInfo* info_high) {
1656   return NewPcRelativePatch(
1657       /* dex_file= */ nullptr, intrinsic_data, info_high, &boot_image_intrinsic_patches_);
1658 }
1659 
NewBootImageRelRoPatch(uint32_t boot_image_offset,const PcRelativePatchInfo * info_high)1660 CodeGeneratorMIPS::PcRelativePatchInfo* CodeGeneratorMIPS::NewBootImageRelRoPatch(
1661     uint32_t boot_image_offset,
1662     const PcRelativePatchInfo* info_high) {
1663   return NewPcRelativePatch(
1664       /* dex_file= */ nullptr, boot_image_offset, info_high, &boot_image_method_patches_);
1665 }
1666 
NewBootImageMethodPatch(MethodReference target_method,const PcRelativePatchInfo * info_high)1667 CodeGeneratorMIPS::PcRelativePatchInfo* CodeGeneratorMIPS::NewBootImageMethodPatch(
1668     MethodReference target_method,
1669     const PcRelativePatchInfo* info_high) {
1670   return NewPcRelativePatch(
1671       target_method.dex_file, target_method.index, info_high, &boot_image_method_patches_);
1672 }
1673 
NewMethodBssEntryPatch(MethodReference target_method,const PcRelativePatchInfo * info_high)1674 CodeGeneratorMIPS::PcRelativePatchInfo* CodeGeneratorMIPS::NewMethodBssEntryPatch(
1675     MethodReference target_method,
1676     const PcRelativePatchInfo* info_high) {
1677   return NewPcRelativePatch(
1678       target_method.dex_file, target_method.index, info_high, &method_bss_entry_patches_);
1679 }
1680 
NewBootImageTypePatch(const DexFile & dex_file,dex::TypeIndex type_index,const PcRelativePatchInfo * info_high)1681 CodeGeneratorMIPS::PcRelativePatchInfo* CodeGeneratorMIPS::NewBootImageTypePatch(
1682     const DexFile& dex_file,
1683     dex::TypeIndex type_index,
1684     const PcRelativePatchInfo* info_high) {
1685   return NewPcRelativePatch(&dex_file, type_index.index_, info_high, &boot_image_type_patches_);
1686 }
1687 
NewTypeBssEntryPatch(const DexFile & dex_file,dex::TypeIndex type_index,const PcRelativePatchInfo * info_high)1688 CodeGeneratorMIPS::PcRelativePatchInfo* CodeGeneratorMIPS::NewTypeBssEntryPatch(
1689     const DexFile& dex_file,
1690     dex::TypeIndex type_index,
1691     const PcRelativePatchInfo* info_high) {
1692   return NewPcRelativePatch(&dex_file, type_index.index_, info_high, &type_bss_entry_patches_);
1693 }
1694 
NewBootImageStringPatch(const DexFile & dex_file,dex::StringIndex string_index,const PcRelativePatchInfo * info_high)1695 CodeGeneratorMIPS::PcRelativePatchInfo* CodeGeneratorMIPS::NewBootImageStringPatch(
1696     const DexFile& dex_file,
1697     dex::StringIndex string_index,
1698     const PcRelativePatchInfo* info_high) {
1699   return NewPcRelativePatch(
1700       &dex_file, string_index.index_, info_high, &boot_image_string_patches_);
1701 }
1702 
NewStringBssEntryPatch(const DexFile & dex_file,dex::StringIndex string_index,const PcRelativePatchInfo * info_high)1703 CodeGeneratorMIPS::PcRelativePatchInfo* CodeGeneratorMIPS::NewStringBssEntryPatch(
1704     const DexFile& dex_file,
1705     dex::StringIndex string_index,
1706     const PcRelativePatchInfo* info_high) {
1707   return NewPcRelativePatch(&dex_file, string_index.index_, info_high, &string_bss_entry_patches_);
1708 }
1709 
NewPcRelativePatch(const DexFile * dex_file,uint32_t offset_or_index,const PcRelativePatchInfo * info_high,ArenaDeque<PcRelativePatchInfo> * patches)1710 CodeGeneratorMIPS::PcRelativePatchInfo* CodeGeneratorMIPS::NewPcRelativePatch(
1711     const DexFile* dex_file,
1712     uint32_t offset_or_index,
1713     const PcRelativePatchInfo* info_high,
1714     ArenaDeque<PcRelativePatchInfo>* patches) {
1715   patches->emplace_back(dex_file, offset_or_index, info_high);
1716   return &patches->back();
1717 }
1718 
DeduplicateUint32Literal(uint32_t value,Uint32ToLiteralMap * map)1719 Literal* CodeGeneratorMIPS::DeduplicateUint32Literal(uint32_t value, Uint32ToLiteralMap* map) {
1720   return map->GetOrCreate(
1721       value,
1722       [this, value]() { return __ NewLiteral<uint32_t>(value); });
1723 }
1724 
DeduplicateBootImageAddressLiteral(uint32_t address)1725 Literal* CodeGeneratorMIPS::DeduplicateBootImageAddressLiteral(uint32_t address) {
1726   return DeduplicateUint32Literal(dchecked_integral_cast<uint32_t>(address), &uint32_literals_);
1727 }
1728 
EmitPcRelativeAddressPlaceholderHigh(PcRelativePatchInfo * info_high,Register out,Register base)1729 void CodeGeneratorMIPS::EmitPcRelativeAddressPlaceholderHigh(PcRelativePatchInfo* info_high,
1730                                                              Register out,
1731                                                              Register base) {
1732   DCHECK(!info_high->patch_info_high);
1733   DCHECK_NE(out, base);
1734   bool reordering = __ SetReorder(false);
1735   if (GetInstructionSetFeatures().IsR6()) {
1736     DCHECK_EQ(base, ZERO);
1737     __ Bind(&info_high->label);
1738     __ Bind(&info_high->pc_rel_label);
1739     // Add the high half of a 32-bit offset to PC.
1740     __ Auipc(out, /* imm16= */ 0x1234);
1741     __ SetReorder(reordering);
1742   } else {
1743     // If base is ZERO, emit NAL to obtain the actual base.
1744     if (base == ZERO) {
1745       // Generate a dummy PC-relative call to obtain PC.
1746       __ Nal();
1747     }
1748     __ Bind(&info_high->label);
1749     __ Lui(out, /* imm16= */ 0x1234);
1750     // If we emitted the NAL, bind the pc_rel_label, otherwise base is a register holding
1751     // the HMipsComputeBaseMethodAddress which has its own label stored in MipsAssembler.
1752     if (base == ZERO) {
1753       __ Bind(&info_high->pc_rel_label);
1754     }
1755     __ SetReorder(reordering);
1756     // Add the high half of a 32-bit offset to PC.
1757     __ Addu(out, out, (base == ZERO) ? RA : base);
1758   }
1759   // A following instruction will add the sign-extended low half of the 32-bit
1760   // offset to `out` (e.g. lw, jialc, addiu).
1761 }
1762 
LoadBootImageAddress(Register reg,uint32_t boot_image_reference)1763 void CodeGeneratorMIPS::LoadBootImageAddress(Register reg, uint32_t boot_image_reference) {
1764   if (GetCompilerOptions().IsBootImage()) {
1765     PcRelativePatchInfo* info_high = NewBootImageIntrinsicPatch(boot_image_reference);
1766     PcRelativePatchInfo* info_low = NewBootImageIntrinsicPatch(boot_image_reference, info_high);
1767     EmitPcRelativeAddressPlaceholderHigh(info_high, TMP, /* base= */ ZERO);
1768     __ Addiu(reg, TMP, /* imm16= */ 0x5678, &info_low->label);
1769   } else if (GetCompilerOptions().GetCompilePic()) {
1770     PcRelativePatchInfo* info_high = NewBootImageRelRoPatch(boot_image_reference);
1771     PcRelativePatchInfo* info_low = NewBootImageRelRoPatch(boot_image_reference, info_high);
1772     EmitPcRelativeAddressPlaceholderHigh(info_high, reg, /* base= */ ZERO);
1773     __ Lw(reg, reg, /* imm16= */ 0x5678, &info_low->label);
1774   } else {
1775     DCHECK(Runtime::Current()->UseJitCompilation());
1776     gc::Heap* heap = Runtime::Current()->GetHeap();
1777     DCHECK(!heap->GetBootImageSpaces().empty());
1778     const uint8_t* address = heap->GetBootImageSpaces()[0]->Begin() + boot_image_reference;
1779     __ LoadConst32(reg, dchecked_integral_cast<uint32_t>(reinterpret_cast<uintptr_t>(address)));
1780   }
1781 }
1782 
AllocateInstanceForIntrinsic(HInvokeStaticOrDirect * invoke,uint32_t boot_image_offset)1783 void CodeGeneratorMIPS::AllocateInstanceForIntrinsic(HInvokeStaticOrDirect* invoke,
1784                                                      uint32_t boot_image_offset) {
1785   DCHECK(invoke->IsStatic());
1786   InvokeRuntimeCallingConvention calling_convention;
1787   Register argument = calling_convention.GetRegisterAt(0);
1788   if (GetCompilerOptions().IsBootImage()) {
1789     DCHECK_EQ(boot_image_offset, IntrinsicVisitor::IntegerValueOfInfo::kInvalidReference);
1790     // Load the class the same way as for HLoadClass::LoadKind::kBootImageLinkTimePcRelative.
1791     MethodReference target_method = invoke->GetTargetMethod();
1792     dex::TypeIndex type_idx = target_method.dex_file->GetMethodId(target_method.index).class_idx_;
1793     PcRelativePatchInfo* info_high = NewBootImageTypePatch(*target_method.dex_file, type_idx);
1794     PcRelativePatchInfo* info_low =
1795         NewBootImageTypePatch(*target_method.dex_file, type_idx, info_high);
1796     EmitPcRelativeAddressPlaceholderHigh(info_high, argument, /* base= */ ZERO);
1797     __ Addiu(argument, argument, /* imm16= */ 0x5678, &info_low->label);
1798   } else {
1799     LoadBootImageAddress(argument, boot_image_offset);
1800   }
1801   InvokeRuntime(kQuickAllocObjectInitialized, invoke, invoke->GetDexPc());
1802   CheckEntrypointTypes<kQuickAllocObjectWithChecks, void*, mirror::Class*>();
1803 }
1804 
NewJitRootStringPatch(const DexFile & dex_file,dex::StringIndex string_index,Handle<mirror::String> handle)1805 CodeGeneratorMIPS::JitPatchInfo* CodeGeneratorMIPS::NewJitRootStringPatch(
1806     const DexFile& dex_file,
1807     dex::StringIndex string_index,
1808     Handle<mirror::String> handle) {
1809   ReserveJitStringRoot(StringReference(&dex_file, string_index), handle);
1810   jit_string_patches_.emplace_back(dex_file, string_index.index_);
1811   return &jit_string_patches_.back();
1812 }
1813 
NewJitRootClassPatch(const DexFile & dex_file,dex::TypeIndex type_index,Handle<mirror::Class> handle)1814 CodeGeneratorMIPS::JitPatchInfo* CodeGeneratorMIPS::NewJitRootClassPatch(
1815     const DexFile& dex_file,
1816     dex::TypeIndex type_index,
1817     Handle<mirror::Class> handle) {
1818   ReserveJitClassRoot(TypeReference(&dex_file, type_index), handle);
1819   jit_class_patches_.emplace_back(dex_file, type_index.index_);
1820   return &jit_class_patches_.back();
1821 }
1822 
PatchJitRootUse(uint8_t * code,const uint8_t * roots_data,const CodeGeneratorMIPS::JitPatchInfo & info,uint64_t index_in_table) const1823 void CodeGeneratorMIPS::PatchJitRootUse(uint8_t* code,
1824                                         const uint8_t* roots_data,
1825                                         const CodeGeneratorMIPS::JitPatchInfo& info,
1826                                         uint64_t index_in_table) const {
1827   uint32_t high_literal_offset = GetAssembler().GetLabelLocation(&info.high_label);
1828   uint32_t low_literal_offset = GetAssembler().GetLabelLocation(&info.low_label);
1829   uintptr_t address =
1830       reinterpret_cast<uintptr_t>(roots_data) + index_in_table * sizeof(GcRoot<mirror::Object>);
1831   uint32_t addr32 = dchecked_integral_cast<uint32_t>(address);
1832   // lui reg, addr32_high
1833   DCHECK_EQ(code[high_literal_offset + 0], 0x34);
1834   DCHECK_EQ(code[high_literal_offset + 1], 0x12);
1835   DCHECK_EQ((code[high_literal_offset + 2] & 0xE0), 0x00);
1836   DCHECK_EQ(code[high_literal_offset + 3], 0x3C);
1837   // instr reg, reg, addr32_low
1838   DCHECK_EQ(code[low_literal_offset + 0], 0x78);
1839   DCHECK_EQ(code[low_literal_offset + 1], 0x56);
1840   addr32 += (addr32 & 0x8000) << 1;  // Account for sign extension in "instr reg, reg, addr32_low".
1841   // lui reg, addr32_high
1842   code[high_literal_offset + 0] = static_cast<uint8_t>(addr32 >> 16);
1843   code[high_literal_offset + 1] = static_cast<uint8_t>(addr32 >> 24);
1844   // instr reg, reg, addr32_low
1845   code[low_literal_offset + 0] = static_cast<uint8_t>(addr32 >> 0);
1846   code[low_literal_offset + 1] = static_cast<uint8_t>(addr32 >> 8);
1847 }
1848 
EmitJitRootPatches(uint8_t * code,const uint8_t * roots_data)1849 void CodeGeneratorMIPS::EmitJitRootPatches(uint8_t* code, const uint8_t* roots_data) {
1850   for (const JitPatchInfo& info : jit_string_patches_) {
1851     StringReference string_reference(&info.target_dex_file, dex::StringIndex(info.index));
1852     uint64_t index_in_table = GetJitStringRootIndex(string_reference);
1853     PatchJitRootUse(code, roots_data, info, index_in_table);
1854   }
1855   for (const JitPatchInfo& info : jit_class_patches_) {
1856     TypeReference type_reference(&info.target_dex_file, dex::TypeIndex(info.index));
1857     uint64_t index_in_table = GetJitClassRootIndex(type_reference);
1858     PatchJitRootUse(code, roots_data, info, index_in_table);
1859   }
1860 }
1861 
MarkGCCard(Register object,Register value,bool value_can_be_null)1862 void CodeGeneratorMIPS::MarkGCCard(Register object,
1863                                    Register value,
1864                                    bool value_can_be_null) {
1865   MipsLabel done;
1866   Register card = AT;
1867   Register temp = TMP;
1868   if (value_can_be_null) {
1869     __ Beqz(value, &done);
1870   }
1871   // Load the address of the card table into `card`.
1872   __ LoadFromOffset(kLoadWord,
1873                     card,
1874                     TR,
1875                     Thread::CardTableOffset<kMipsPointerSize>().Int32Value());
1876   // Calculate the address of the card corresponding to `object`.
1877   __ Srl(temp, object, gc::accounting::CardTable::kCardShift);
1878   __ Addu(temp, card, temp);
1879   // Write the `art::gc::accounting::CardTable::kCardDirty` value into the
1880   // `object`'s card.
1881   //
1882   // Register `card` contains the address of the card table. Note that the card
1883   // table's base is biased during its creation so that it always starts at an
1884   // address whose least-significant byte is equal to `kCardDirty` (see
1885   // art::gc::accounting::CardTable::Create). Therefore the SB instruction
1886   // below writes the `kCardDirty` (byte) value into the `object`'s card
1887   // (located at `card + object >> kCardShift`).
1888   //
1889   // This dual use of the value in register `card` (1. to calculate the location
1890   // of the card to mark; and 2. to load the `kCardDirty` value) saves a load
1891   // (no need to explicitly load `kCardDirty` as an immediate value).
1892   __ Sb(card, temp, 0);
1893   if (value_can_be_null) {
1894     __ Bind(&done);
1895   }
1896 }
1897 
SetupBlockedRegisters() const1898 void CodeGeneratorMIPS::SetupBlockedRegisters() const {
1899   // ZERO, K0, K1, GP, SP, RA are always reserved and can't be allocated.
1900   blocked_core_registers_[ZERO] = true;
1901   blocked_core_registers_[K0] = true;
1902   blocked_core_registers_[K1] = true;
1903   blocked_core_registers_[GP] = true;
1904   blocked_core_registers_[SP] = true;
1905   blocked_core_registers_[RA] = true;
1906 
1907   // AT and TMP(T8) are used as temporary/scratch registers
1908   // (similar to how AT is used by MIPS assemblers).
1909   blocked_core_registers_[AT] = true;
1910   blocked_core_registers_[TMP] = true;
1911   blocked_fpu_registers_[FTMP] = true;
1912 
1913   if (GetInstructionSetFeatures().HasMsa()) {
1914     // To be used just for MSA instructions.
1915     blocked_fpu_registers_[FTMP2] = true;
1916   }
1917 
1918   // Reserve suspend and thread registers.
1919   blocked_core_registers_[S0] = true;
1920   blocked_core_registers_[TR] = true;
1921 
1922   // Reserve T9 for function calls
1923   blocked_core_registers_[T9] = true;
1924 
1925   // Reserve odd-numbered FPU registers.
1926   for (size_t i = 1; i < kNumberOfFRegisters; i += 2) {
1927     blocked_fpu_registers_[i] = true;
1928   }
1929 
1930   if (GetGraph()->IsDebuggable()) {
1931     // Stubs do not save callee-save floating point registers. If the graph
1932     // is debuggable, we need to deal with these registers differently. For
1933     // now, just block them.
1934     for (size_t i = 0; i < arraysize(kFpuCalleeSaves); ++i) {
1935       blocked_fpu_registers_[kFpuCalleeSaves[i]] = true;
1936     }
1937   }
1938 }
1939 
SaveCoreRegister(size_t stack_index,uint32_t reg_id)1940 size_t CodeGeneratorMIPS::SaveCoreRegister(size_t stack_index, uint32_t reg_id) {
1941   __ StoreToOffset(kStoreWord, Register(reg_id), SP, stack_index);
1942   return kMipsWordSize;
1943 }
1944 
RestoreCoreRegister(size_t stack_index,uint32_t reg_id)1945 size_t CodeGeneratorMIPS::RestoreCoreRegister(size_t stack_index, uint32_t reg_id) {
1946   __ LoadFromOffset(kLoadWord, Register(reg_id), SP, stack_index);
1947   return kMipsWordSize;
1948 }
1949 
SaveFloatingPointRegister(size_t stack_index,uint32_t reg_id)1950 size_t CodeGeneratorMIPS::SaveFloatingPointRegister(size_t stack_index, uint32_t reg_id) {
1951   if (GetGraph()->HasSIMD()) {
1952     __ StoreQToOffset(FRegister(reg_id), SP, stack_index);
1953   } else {
1954     __ StoreDToOffset(FRegister(reg_id), SP, stack_index);
1955   }
1956   return GetFloatingPointSpillSlotSize();
1957 }
1958 
RestoreFloatingPointRegister(size_t stack_index,uint32_t reg_id)1959 size_t CodeGeneratorMIPS::RestoreFloatingPointRegister(size_t stack_index, uint32_t reg_id) {
1960   if (GetGraph()->HasSIMD()) {
1961     __ LoadQFromOffset(FRegister(reg_id), SP, stack_index);
1962   } else {
1963     __ LoadDFromOffset(FRegister(reg_id), SP, stack_index);
1964   }
1965   return GetFloatingPointSpillSlotSize();
1966 }
1967 
DumpCoreRegister(std::ostream & stream,int reg) const1968 void CodeGeneratorMIPS::DumpCoreRegister(std::ostream& stream, int reg) const {
1969   stream << Register(reg);
1970 }
1971 
DumpFloatingPointRegister(std::ostream & stream,int reg) const1972 void CodeGeneratorMIPS::DumpFloatingPointRegister(std::ostream& stream, int reg) const {
1973   stream << FRegister(reg);
1974 }
1975 
GetInstructionSetFeatures() const1976 const MipsInstructionSetFeatures& CodeGeneratorMIPS::GetInstructionSetFeatures() const {
1977   return *GetCompilerOptions().GetInstructionSetFeatures()->AsMipsInstructionSetFeatures();
1978 }
1979 
1980 constexpr size_t kMipsDirectEntrypointRuntimeOffset = 16;
1981 
InvokeRuntime(QuickEntrypointEnum entrypoint,HInstruction * instruction,uint32_t dex_pc,SlowPathCode * slow_path)1982 void CodeGeneratorMIPS::InvokeRuntime(QuickEntrypointEnum entrypoint,
1983                                       HInstruction* instruction,
1984                                       uint32_t dex_pc,
1985                                       SlowPathCode* slow_path) {
1986   ValidateInvokeRuntime(entrypoint, instruction, slow_path);
1987   GenerateInvokeRuntime(GetThreadOffset<kMipsPointerSize>(entrypoint).Int32Value(),
1988                         IsDirectEntrypoint(entrypoint));
1989   if (EntrypointRequiresStackMap(entrypoint)) {
1990     RecordPcInfo(instruction, dex_pc, slow_path);
1991   }
1992 }
1993 
InvokeRuntimeWithoutRecordingPcInfo(int32_t entry_point_offset,HInstruction * instruction,SlowPathCode * slow_path,bool direct)1994 void CodeGeneratorMIPS::InvokeRuntimeWithoutRecordingPcInfo(int32_t entry_point_offset,
1995                                                             HInstruction* instruction,
1996                                                             SlowPathCode* slow_path,
1997                                                             bool direct) {
1998   ValidateInvokeRuntimeWithoutRecordingPcInfo(instruction, slow_path);
1999   GenerateInvokeRuntime(entry_point_offset, direct);
2000 }
2001 
GenerateInvokeRuntime(int32_t entry_point_offset,bool direct)2002 void CodeGeneratorMIPS::GenerateInvokeRuntime(int32_t entry_point_offset, bool direct) {
2003   bool reordering = __ SetReorder(false);
2004   __ LoadFromOffset(kLoadWord, T9, TR, entry_point_offset);
2005   __ Jalr(T9);
2006   if (direct) {
2007     // Reserve argument space on stack (for $a0-$a3) for
2008     // entrypoints that directly reference native implementations.
2009     // Called function may use this space to store $a0-$a3 regs.
2010     __ IncreaseFrameSize(kMipsDirectEntrypointRuntimeOffset);  // Single instruction in delay slot.
2011     __ DecreaseFrameSize(kMipsDirectEntrypointRuntimeOffset);
2012   } else {
2013     __ Nop();  // In delay slot.
2014   }
2015   __ SetReorder(reordering);
2016 }
2017 
GenerateClassInitializationCheck(SlowPathCodeMIPS * slow_path,Register class_reg)2018 void InstructionCodeGeneratorMIPS::GenerateClassInitializationCheck(SlowPathCodeMIPS* slow_path,
2019                                                                     Register class_reg) {
2020   constexpr size_t status_lsb_position = SubtypeCheckBits::BitStructSizeOf();
2021   const size_t status_byte_offset =
2022       mirror::Class::StatusOffset().SizeValue() + (status_lsb_position / kBitsPerByte);
2023   constexpr uint32_t shifted_initialized_value =
2024       enum_cast<uint32_t>(ClassStatus::kInitialized) << (status_lsb_position % kBitsPerByte);
2025 
2026   __ LoadFromOffset(kLoadUnsignedByte, TMP, class_reg, status_byte_offset);
2027   __ Sltiu(TMP, TMP, shifted_initialized_value);
2028   __ Bnez(TMP, slow_path->GetEntryLabel());
2029   // Even if the initialized flag is set, we need to ensure consistent memory ordering.
2030   __ Sync(0);
2031   __ Bind(slow_path->GetExitLabel());
2032 }
2033 
GenerateBitstringTypeCheckCompare(HTypeCheckInstruction * check,Register temp)2034 void InstructionCodeGeneratorMIPS::GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check,
2035                                                                      Register temp) {
2036   uint32_t path_to_root = check->GetBitstringPathToRoot();
2037   uint32_t mask = check->GetBitstringMask();
2038   DCHECK(IsPowerOfTwo(mask + 1));
2039   size_t mask_bits = WhichPowerOf2(mask + 1);
2040 
2041   if (mask_bits == 16u) {
2042     // Load only the bitstring part of the status word.
2043     __ LoadFromOffset(
2044         kLoadUnsignedHalfword, temp, temp, mirror::Class::StatusOffset().Int32Value());
2045     // Compare the bitstring bits using XOR.
2046     __ Xori(temp, temp, dchecked_integral_cast<uint16_t>(path_to_root));
2047   } else {
2048     // /* uint32_t */ temp = temp->status_
2049     __ LoadFromOffset(kLoadWord, temp, temp, mirror::Class::StatusOffset().Int32Value());
2050     // Compare the bitstring bits using XOR.
2051     if (IsUint<16>(path_to_root)) {
2052       __ Xori(temp, temp, dchecked_integral_cast<uint16_t>(path_to_root));
2053     } else {
2054       __ LoadConst32(TMP, path_to_root);
2055       __ Xor(temp, temp, TMP);
2056     }
2057     // Shift out bits that do not contribute to the comparison.
2058     __ Sll(temp, temp, 32 - mask_bits);
2059   }
2060 }
2061 
GenerateMemoryBarrier(MemBarrierKind kind ATTRIBUTE_UNUSED)2062 void InstructionCodeGeneratorMIPS::GenerateMemoryBarrier(MemBarrierKind kind ATTRIBUTE_UNUSED) {
2063   __ Sync(0);  // Only stype 0 is supported.
2064 }
2065 
GenerateSuspendCheck(HSuspendCheck * instruction,HBasicBlock * successor)2066 void InstructionCodeGeneratorMIPS::GenerateSuspendCheck(HSuspendCheck* instruction,
2067                                                         HBasicBlock* successor) {
2068   SuspendCheckSlowPathMIPS* slow_path =
2069       down_cast<SuspendCheckSlowPathMIPS*>(instruction->GetSlowPath());
2070 
2071   if (slow_path == nullptr) {
2072     slow_path =
2073         new (codegen_->GetScopedAllocator()) SuspendCheckSlowPathMIPS(instruction, successor);
2074     instruction->SetSlowPath(slow_path);
2075     codegen_->AddSlowPath(slow_path);
2076     if (successor != nullptr) {
2077       DCHECK(successor->IsLoopHeader());
2078     }
2079   } else {
2080     DCHECK_EQ(slow_path->GetSuccessor(), successor);
2081   }
2082 
2083   __ LoadFromOffset(kLoadUnsignedHalfword,
2084                     TMP,
2085                     TR,
2086                     Thread::ThreadFlagsOffset<kMipsPointerSize>().Int32Value());
2087   if (successor == nullptr) {
2088     __ Bnez(TMP, slow_path->GetEntryLabel());
2089     __ Bind(slow_path->GetReturnLabel());
2090   } else {
2091     __ Beqz(TMP, codegen_->GetLabelOf(successor));
2092     __ B(slow_path->GetEntryLabel());
2093     // slow_path will return to GetLabelOf(successor).
2094   }
2095 }
2096 
InstructionCodeGeneratorMIPS(HGraph * graph,CodeGeneratorMIPS * codegen)2097 InstructionCodeGeneratorMIPS::InstructionCodeGeneratorMIPS(HGraph* graph,
2098                                                            CodeGeneratorMIPS* codegen)
2099       : InstructionCodeGenerator(graph, codegen),
2100         assembler_(codegen->GetAssembler()),
2101         codegen_(codegen) {}
2102 
HandleBinaryOp(HBinaryOperation * instruction)2103 void LocationsBuilderMIPS::HandleBinaryOp(HBinaryOperation* instruction) {
2104   DCHECK_EQ(instruction->InputCount(), 2U);
2105   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
2106   DataType::Type type = instruction->GetResultType();
2107   bool isR6 = codegen_->GetInstructionSetFeatures().IsR6();
2108   switch (type) {
2109     case DataType::Type::kInt32: {
2110       locations->SetInAt(0, Location::RequiresRegister());
2111       HInstruction* right = instruction->InputAt(1);
2112       bool can_use_imm = false;
2113       if (right->IsConstant()) {
2114         int32_t imm = CodeGenerator::GetInt32ValueOf(right->AsConstant());
2115         if (instruction->IsAnd() || instruction->IsOr() || instruction->IsXor()) {
2116           can_use_imm = IsUint<16>(imm);
2117         } else {
2118           DCHECK(instruction->IsSub() || instruction->IsAdd());
2119           if (instruction->IsSub()) {
2120             imm = -imm;
2121           }
2122           if (isR6) {
2123             bool single_use = right->GetUses().HasExactlyOneElement();
2124             int16_t imm_high = High16Bits(imm);
2125             int16_t imm_low = Low16Bits(imm);
2126             if (imm_low < 0) {
2127               imm_high += 1;
2128             }
2129             can_use_imm = !((imm_high != 0) && (imm_low != 0)) || single_use;
2130           } else {
2131             can_use_imm = IsInt<16>(imm);
2132           }
2133         }
2134       }
2135       if (can_use_imm)
2136         locations->SetInAt(1, Location::ConstantLocation(right->AsConstant()));
2137       else
2138         locations->SetInAt(1, Location::RequiresRegister());
2139       locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2140       break;
2141     }
2142 
2143     case DataType::Type::kInt64: {
2144       locations->SetInAt(0, Location::RequiresRegister());
2145       locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
2146       locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2147       break;
2148     }
2149 
2150     case DataType::Type::kFloat32:
2151     case DataType::Type::kFloat64:
2152       DCHECK(instruction->IsAdd() || instruction->IsSub());
2153       locations->SetInAt(0, Location::RequiresFpuRegister());
2154       locations->SetInAt(1, Location::RequiresFpuRegister());
2155       locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
2156       break;
2157 
2158     default:
2159       LOG(FATAL) << "Unexpected " << instruction->DebugName() << " type " << type;
2160   }
2161 }
2162 
HandleBinaryOp(HBinaryOperation * instruction)2163 void InstructionCodeGeneratorMIPS::HandleBinaryOp(HBinaryOperation* instruction) {
2164   DataType::Type type = instruction->GetType();
2165   LocationSummary* locations = instruction->GetLocations();
2166   bool isR6 = codegen_->GetInstructionSetFeatures().IsR6();
2167 
2168   switch (type) {
2169     case DataType::Type::kInt32: {
2170       Register dst = locations->Out().AsRegister<Register>();
2171       Register lhs = locations->InAt(0).AsRegister<Register>();
2172       Location rhs_location = locations->InAt(1);
2173 
2174       Register rhs_reg = ZERO;
2175       int32_t rhs_imm = 0;
2176       bool use_imm = rhs_location.IsConstant();
2177       if (use_imm) {
2178         rhs_imm = CodeGenerator::GetInt32ValueOf(rhs_location.GetConstant());
2179       } else {
2180         rhs_reg = rhs_location.AsRegister<Register>();
2181       }
2182 
2183       if (instruction->IsAnd()) {
2184         if (use_imm)
2185           __ Andi(dst, lhs, rhs_imm);
2186         else
2187           __ And(dst, lhs, rhs_reg);
2188       } else if (instruction->IsOr()) {
2189         if (use_imm)
2190           __ Ori(dst, lhs, rhs_imm);
2191         else
2192           __ Or(dst, lhs, rhs_reg);
2193       } else if (instruction->IsXor()) {
2194         if (use_imm)
2195           __ Xori(dst, lhs, rhs_imm);
2196         else
2197           __ Xor(dst, lhs, rhs_reg);
2198       } else {
2199         DCHECK(instruction->IsAdd() || instruction->IsSub());
2200         if (use_imm) {
2201           if (instruction->IsSub()) {
2202             rhs_imm = -rhs_imm;
2203           }
2204           if (IsInt<16>(rhs_imm)) {
2205             __ Addiu(dst, lhs, rhs_imm);
2206           } else {
2207             DCHECK(isR6);
2208             int16_t rhs_imm_high = High16Bits(rhs_imm);
2209             int16_t rhs_imm_low = Low16Bits(rhs_imm);
2210             if (rhs_imm_low < 0) {
2211               rhs_imm_high += 1;
2212             }
2213             __ Aui(dst, lhs, rhs_imm_high);
2214             if (rhs_imm_low != 0) {
2215               __ Addiu(dst, dst, rhs_imm_low);
2216             }
2217           }
2218         } else if (instruction->IsAdd()) {
2219           __ Addu(dst, lhs, rhs_reg);
2220         } else {
2221           DCHECK(instruction->IsSub());
2222           __ Subu(dst, lhs, rhs_reg);
2223         }
2224       }
2225       break;
2226     }
2227 
2228     case DataType::Type::kInt64: {
2229       Register dst_high = locations->Out().AsRegisterPairHigh<Register>();
2230       Register dst_low = locations->Out().AsRegisterPairLow<Register>();
2231       Register lhs_high = locations->InAt(0).AsRegisterPairHigh<Register>();
2232       Register lhs_low = locations->InAt(0).AsRegisterPairLow<Register>();
2233       Location rhs_location = locations->InAt(1);
2234       bool use_imm = rhs_location.IsConstant();
2235       if (!use_imm) {
2236         Register rhs_high = rhs_location.AsRegisterPairHigh<Register>();
2237         Register rhs_low = rhs_location.AsRegisterPairLow<Register>();
2238         if (instruction->IsAnd()) {
2239           __ And(dst_low, lhs_low, rhs_low);
2240           __ And(dst_high, lhs_high, rhs_high);
2241         } else if (instruction->IsOr()) {
2242           __ Or(dst_low, lhs_low, rhs_low);
2243           __ Or(dst_high, lhs_high, rhs_high);
2244         } else if (instruction->IsXor()) {
2245           __ Xor(dst_low, lhs_low, rhs_low);
2246           __ Xor(dst_high, lhs_high, rhs_high);
2247         } else if (instruction->IsAdd()) {
2248           if (lhs_low == rhs_low) {
2249             // Special case for lhs = rhs and the sum potentially overwriting both lhs and rhs.
2250             __ Slt(TMP, lhs_low, ZERO);
2251             __ Addu(dst_low, lhs_low, rhs_low);
2252           } else {
2253             __ Addu(dst_low, lhs_low, rhs_low);
2254             // If the sum overwrites rhs, lhs remains unchanged, otherwise rhs remains unchanged.
2255             __ Sltu(TMP, dst_low, (dst_low == rhs_low) ? lhs_low : rhs_low);
2256           }
2257           __ Addu(dst_high, lhs_high, rhs_high);
2258           __ Addu(dst_high, dst_high, TMP);
2259         } else {
2260           DCHECK(instruction->IsSub());
2261           __ Sltu(TMP, lhs_low, rhs_low);
2262           __ Subu(dst_low, lhs_low, rhs_low);
2263           __ Subu(dst_high, lhs_high, rhs_high);
2264           __ Subu(dst_high, dst_high, TMP);
2265         }
2266       } else {
2267         int64_t value = CodeGenerator::GetInt64ValueOf(rhs_location.GetConstant()->AsConstant());
2268         if (instruction->IsOr()) {
2269           uint32_t low = Low32Bits(value);
2270           uint32_t high = High32Bits(value);
2271           if (IsUint<16>(low)) {
2272             if (dst_low != lhs_low || low != 0) {
2273               __ Ori(dst_low, lhs_low, low);
2274             }
2275           } else {
2276             __ LoadConst32(TMP, low);
2277             __ Or(dst_low, lhs_low, TMP);
2278           }
2279           if (IsUint<16>(high)) {
2280             if (dst_high != lhs_high || high != 0) {
2281               __ Ori(dst_high, lhs_high, high);
2282             }
2283           } else {
2284             if (high != low) {
2285               __ LoadConst32(TMP, high);
2286             }
2287             __ Or(dst_high, lhs_high, TMP);
2288           }
2289         } else if (instruction->IsXor()) {
2290           uint32_t low = Low32Bits(value);
2291           uint32_t high = High32Bits(value);
2292           if (IsUint<16>(low)) {
2293             if (dst_low != lhs_low || low != 0) {
2294               __ Xori(dst_low, lhs_low, low);
2295             }
2296           } else {
2297             __ LoadConst32(TMP, low);
2298             __ Xor(dst_low, lhs_low, TMP);
2299           }
2300           if (IsUint<16>(high)) {
2301             if (dst_high != lhs_high || high != 0) {
2302               __ Xori(dst_high, lhs_high, high);
2303             }
2304           } else {
2305             if (high != low) {
2306               __ LoadConst32(TMP, high);
2307             }
2308             __ Xor(dst_high, lhs_high, TMP);
2309           }
2310         } else if (instruction->IsAnd()) {
2311           uint32_t low = Low32Bits(value);
2312           uint32_t high = High32Bits(value);
2313           if (IsUint<16>(low)) {
2314             __ Andi(dst_low, lhs_low, low);
2315           } else if (low != 0xFFFFFFFF) {
2316             __ LoadConst32(TMP, low);
2317             __ And(dst_low, lhs_low, TMP);
2318           } else if (dst_low != lhs_low) {
2319             __ Move(dst_low, lhs_low);
2320           }
2321           if (IsUint<16>(high)) {
2322             __ Andi(dst_high, lhs_high, high);
2323           } else if (high != 0xFFFFFFFF) {
2324             if (high != low) {
2325               __ LoadConst32(TMP, high);
2326             }
2327             __ And(dst_high, lhs_high, TMP);
2328           } else if (dst_high != lhs_high) {
2329             __ Move(dst_high, lhs_high);
2330           }
2331         } else {
2332           if (instruction->IsSub()) {
2333             value = -value;
2334           } else {
2335             DCHECK(instruction->IsAdd());
2336           }
2337           int32_t low = Low32Bits(value);
2338           int32_t high = High32Bits(value);
2339           if (IsInt<16>(low)) {
2340             if (dst_low != lhs_low || low != 0) {
2341               __ Addiu(dst_low, lhs_low, low);
2342             }
2343             if (low != 0) {
2344               __ Sltiu(AT, dst_low, low);
2345             }
2346           } else {
2347             __ LoadConst32(TMP, low);
2348             __ Addu(dst_low, lhs_low, TMP);
2349             __ Sltu(AT, dst_low, TMP);
2350           }
2351           if (IsInt<16>(high)) {
2352             if (dst_high != lhs_high || high != 0) {
2353               __ Addiu(dst_high, lhs_high, high);
2354             }
2355           } else {
2356             if (high != low) {
2357               __ LoadConst32(TMP, high);
2358             }
2359             __ Addu(dst_high, lhs_high, TMP);
2360           }
2361           if (low != 0) {
2362             __ Addu(dst_high, dst_high, AT);
2363           }
2364         }
2365       }
2366       break;
2367     }
2368 
2369     case DataType::Type::kFloat32:
2370     case DataType::Type::kFloat64: {
2371       FRegister dst = locations->Out().AsFpuRegister<FRegister>();
2372       FRegister lhs = locations->InAt(0).AsFpuRegister<FRegister>();
2373       FRegister rhs = locations->InAt(1).AsFpuRegister<FRegister>();
2374       if (instruction->IsAdd()) {
2375         if (type == DataType::Type::kFloat32) {
2376           __ AddS(dst, lhs, rhs);
2377         } else {
2378           __ AddD(dst, lhs, rhs);
2379         }
2380       } else {
2381         DCHECK(instruction->IsSub());
2382         if (type == DataType::Type::kFloat32) {
2383           __ SubS(dst, lhs, rhs);
2384         } else {
2385           __ SubD(dst, lhs, rhs);
2386         }
2387       }
2388       break;
2389     }
2390 
2391     default:
2392       LOG(FATAL) << "Unexpected binary operation type " << type;
2393   }
2394 }
2395 
HandleShift(HBinaryOperation * instr)2396 void LocationsBuilderMIPS::HandleShift(HBinaryOperation* instr) {
2397   DCHECK(instr->IsShl() || instr->IsShr() || instr->IsUShr() || instr->IsRor());
2398 
2399   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instr);
2400   DataType::Type type = instr->GetResultType();
2401   switch (type) {
2402     case DataType::Type::kInt32:
2403       locations->SetInAt(0, Location::RequiresRegister());
2404       locations->SetInAt(1, Location::RegisterOrConstant(instr->InputAt(1)));
2405       locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2406       break;
2407     case DataType::Type::kInt64:
2408       locations->SetInAt(0, Location::RequiresRegister());
2409       locations->SetInAt(1, Location::RegisterOrConstant(instr->InputAt(1)));
2410       locations->SetOut(Location::RequiresRegister());
2411       break;
2412     default:
2413       LOG(FATAL) << "Unexpected shift type " << type;
2414   }
2415 }
2416 
2417 static constexpr size_t kMipsBitsPerWord = kMipsWordSize * kBitsPerByte;
2418 
HandleShift(HBinaryOperation * instr)2419 void InstructionCodeGeneratorMIPS::HandleShift(HBinaryOperation* instr) {
2420   DCHECK(instr->IsShl() || instr->IsShr() || instr->IsUShr() || instr->IsRor());
2421   LocationSummary* locations = instr->GetLocations();
2422   DataType::Type type = instr->GetType();
2423 
2424   Location rhs_location = locations->InAt(1);
2425   bool use_imm = rhs_location.IsConstant();
2426   Register rhs_reg = use_imm ? ZERO : rhs_location.AsRegister<Register>();
2427   int64_t rhs_imm = use_imm ? CodeGenerator::GetInt64ValueOf(rhs_location.GetConstant()) : 0;
2428   const uint32_t shift_mask =
2429       (type == DataType::Type::kInt32) ? kMaxIntShiftDistance : kMaxLongShiftDistance;
2430   const uint32_t shift_value = rhs_imm & shift_mask;
2431   // Are the INS (Insert Bit Field) and ROTR instructions supported?
2432   bool has_ins_rotr = codegen_->GetInstructionSetFeatures().IsMipsIsaRevGreaterThanEqual2();
2433 
2434   switch (type) {
2435     case DataType::Type::kInt32: {
2436       Register dst = locations->Out().AsRegister<Register>();
2437       Register lhs = locations->InAt(0).AsRegister<Register>();
2438       if (use_imm) {
2439         if (shift_value == 0) {
2440           if (dst != lhs) {
2441             __ Move(dst, lhs);
2442           }
2443         } else if (instr->IsShl()) {
2444           __ Sll(dst, lhs, shift_value);
2445         } else if (instr->IsShr()) {
2446           __ Sra(dst, lhs, shift_value);
2447         } else if (instr->IsUShr()) {
2448           __ Srl(dst, lhs, shift_value);
2449         } else {
2450           if (has_ins_rotr) {
2451             __ Rotr(dst, lhs, shift_value);
2452           } else {
2453             __ Sll(TMP, lhs, (kMipsBitsPerWord - shift_value) & shift_mask);
2454             __ Srl(dst, lhs, shift_value);
2455             __ Or(dst, dst, TMP);
2456           }
2457         }
2458       } else {
2459         if (instr->IsShl()) {
2460           __ Sllv(dst, lhs, rhs_reg);
2461         } else if (instr->IsShr()) {
2462           __ Srav(dst, lhs, rhs_reg);
2463         } else if (instr->IsUShr()) {
2464           __ Srlv(dst, lhs, rhs_reg);
2465         } else {
2466           if (has_ins_rotr) {
2467             __ Rotrv(dst, lhs, rhs_reg);
2468           } else {
2469             __ Subu(TMP, ZERO, rhs_reg);
2470             // 32-bit shift instructions use the 5 least significant bits of the shift count, so
2471             // shifting by `-rhs_reg` is equivalent to shifting by `(32 - rhs_reg) & 31`. The case
2472             // when `rhs_reg & 31 == 0` is OK even though we don't shift `lhs` left all the way out
2473             // by 32, because the result in this case is computed as `(lhs >> 0) | (lhs << 0)`,
2474             // IOW, the OR'd values are equal.
2475             __ Sllv(TMP, lhs, TMP);
2476             __ Srlv(dst, lhs, rhs_reg);
2477             __ Or(dst, dst, TMP);
2478           }
2479         }
2480       }
2481       break;
2482     }
2483 
2484     case DataType::Type::kInt64: {
2485       Register dst_high = locations->Out().AsRegisterPairHigh<Register>();
2486       Register dst_low = locations->Out().AsRegisterPairLow<Register>();
2487       Register lhs_high = locations->InAt(0).AsRegisterPairHigh<Register>();
2488       Register lhs_low = locations->InAt(0).AsRegisterPairLow<Register>();
2489       if (use_imm) {
2490           if (shift_value == 0) {
2491             codegen_->MoveLocation(locations->Out(), locations->InAt(0), type);
2492           } else if (shift_value < kMipsBitsPerWord) {
2493             if (has_ins_rotr) {
2494               if (instr->IsShl()) {
2495                 __ Srl(dst_high, lhs_low, kMipsBitsPerWord - shift_value);
2496                 __ Ins(dst_high, lhs_high, shift_value, kMipsBitsPerWord - shift_value);
2497                 __ Sll(dst_low, lhs_low, shift_value);
2498               } else if (instr->IsShr()) {
2499                 __ Srl(dst_low, lhs_low, shift_value);
2500                 __ Ins(dst_low, lhs_high, kMipsBitsPerWord - shift_value, shift_value);
2501                 __ Sra(dst_high, lhs_high, shift_value);
2502               } else if (instr->IsUShr()) {
2503                 __ Srl(dst_low, lhs_low, shift_value);
2504                 __ Ins(dst_low, lhs_high, kMipsBitsPerWord - shift_value, shift_value);
2505                 __ Srl(dst_high, lhs_high, shift_value);
2506               } else {
2507                 __ Srl(dst_low, lhs_low, shift_value);
2508                 __ Ins(dst_low, lhs_high, kMipsBitsPerWord - shift_value, shift_value);
2509                 __ Srl(dst_high, lhs_high, shift_value);
2510                 __ Ins(dst_high, lhs_low, kMipsBitsPerWord - shift_value, shift_value);
2511               }
2512             } else {
2513               if (instr->IsShl()) {
2514                 __ Sll(dst_low, lhs_low, shift_value);
2515                 __ Srl(TMP, lhs_low, kMipsBitsPerWord - shift_value);
2516                 __ Sll(dst_high, lhs_high, shift_value);
2517                 __ Or(dst_high, dst_high, TMP);
2518               } else if (instr->IsShr()) {
2519                 __ Sra(dst_high, lhs_high, shift_value);
2520                 __ Sll(TMP, lhs_high, kMipsBitsPerWord - shift_value);
2521                 __ Srl(dst_low, lhs_low, shift_value);
2522                 __ Or(dst_low, dst_low, TMP);
2523               } else if (instr->IsUShr()) {
2524                 __ Srl(dst_high, lhs_high, shift_value);
2525                 __ Sll(TMP, lhs_high, kMipsBitsPerWord - shift_value);
2526                 __ Srl(dst_low, lhs_low, shift_value);
2527                 __ Or(dst_low, dst_low, TMP);
2528               } else {
2529                 __ Srl(TMP, lhs_low, shift_value);
2530                 __ Sll(dst_low, lhs_high, kMipsBitsPerWord - shift_value);
2531                 __ Or(dst_low, dst_low, TMP);
2532                 __ Srl(TMP, lhs_high, shift_value);
2533                 __ Sll(dst_high, lhs_low, kMipsBitsPerWord - shift_value);
2534                 __ Or(dst_high, dst_high, TMP);
2535               }
2536             }
2537           } else {
2538             const uint32_t shift_value_high = shift_value - kMipsBitsPerWord;
2539             if (instr->IsShl()) {
2540               __ Sll(dst_high, lhs_low, shift_value_high);
2541               __ Move(dst_low, ZERO);
2542             } else if (instr->IsShr()) {
2543               __ Sra(dst_low, lhs_high, shift_value_high);
2544               __ Sra(dst_high, dst_low, kMipsBitsPerWord - 1);
2545             } else if (instr->IsUShr()) {
2546               __ Srl(dst_low, lhs_high, shift_value_high);
2547               __ Move(dst_high, ZERO);
2548             } else {
2549               if (shift_value == kMipsBitsPerWord) {
2550                 // 64-bit rotation by 32 is just a swap.
2551                 __ Move(dst_low, lhs_high);
2552                 __ Move(dst_high, lhs_low);
2553               } else {
2554                 if (has_ins_rotr) {
2555                   __ Srl(dst_low, lhs_high, shift_value_high);
2556                   __ Ins(dst_low, lhs_low, kMipsBitsPerWord - shift_value_high, shift_value_high);
2557                   __ Srl(dst_high, lhs_low, shift_value_high);
2558                   __ Ins(dst_high, lhs_high, kMipsBitsPerWord - shift_value_high, shift_value_high);
2559                 } else {
2560                   __ Sll(TMP, lhs_low, kMipsBitsPerWord - shift_value_high);
2561                   __ Srl(dst_low, lhs_high, shift_value_high);
2562                   __ Or(dst_low, dst_low, TMP);
2563                   __ Sll(TMP, lhs_high, kMipsBitsPerWord - shift_value_high);
2564                   __ Srl(dst_high, lhs_low, shift_value_high);
2565                   __ Or(dst_high, dst_high, TMP);
2566                 }
2567               }
2568             }
2569           }
2570       } else {
2571         const bool isR6 = codegen_->GetInstructionSetFeatures().IsR6();
2572         MipsLabel done;
2573         if (instr->IsShl()) {
2574           __ Sllv(dst_low, lhs_low, rhs_reg);
2575           __ Nor(AT, ZERO, rhs_reg);
2576           __ Srl(TMP, lhs_low, 1);
2577           __ Srlv(TMP, TMP, AT);
2578           __ Sllv(dst_high, lhs_high, rhs_reg);
2579           __ Or(dst_high, dst_high, TMP);
2580           __ Andi(TMP, rhs_reg, kMipsBitsPerWord);
2581           if (isR6) {
2582             __ Beqzc(TMP, &done, /* is_bare= */ true);
2583             __ Move(dst_high, dst_low);
2584             __ Move(dst_low, ZERO);
2585           } else {
2586             __ Movn(dst_high, dst_low, TMP);
2587             __ Movn(dst_low, ZERO, TMP);
2588           }
2589         } else if (instr->IsShr()) {
2590           __ Srav(dst_high, lhs_high, rhs_reg);
2591           __ Nor(AT, ZERO, rhs_reg);
2592           __ Sll(TMP, lhs_high, 1);
2593           __ Sllv(TMP, TMP, AT);
2594           __ Srlv(dst_low, lhs_low, rhs_reg);
2595           __ Or(dst_low, dst_low, TMP);
2596           __ Andi(TMP, rhs_reg, kMipsBitsPerWord);
2597           if (isR6) {
2598             __ Beqzc(TMP, &done, /* is_bare= */ true);
2599             __ Move(dst_low, dst_high);
2600             __ Sra(dst_high, dst_high, 31);
2601           } else {
2602             __ Sra(AT, dst_high, 31);
2603             __ Movn(dst_low, dst_high, TMP);
2604             __ Movn(dst_high, AT, TMP);
2605           }
2606         } else if (instr->IsUShr()) {
2607           __ Srlv(dst_high, lhs_high, rhs_reg);
2608           __ Nor(AT, ZERO, rhs_reg);
2609           __ Sll(TMP, lhs_high, 1);
2610           __ Sllv(TMP, TMP, AT);
2611           __ Srlv(dst_low, lhs_low, rhs_reg);
2612           __ Or(dst_low, dst_low, TMP);
2613           __ Andi(TMP, rhs_reg, kMipsBitsPerWord);
2614           if (isR6) {
2615             __ Beqzc(TMP, &done, /* is_bare= */ true);
2616             __ Move(dst_low, dst_high);
2617             __ Move(dst_high, ZERO);
2618           } else {
2619             __ Movn(dst_low, dst_high, TMP);
2620             __ Movn(dst_high, ZERO, TMP);
2621           }
2622         } else {  // Rotate.
2623           __ Nor(AT, ZERO, rhs_reg);
2624           __ Srlv(TMP, lhs_low, rhs_reg);
2625           __ Sll(dst_low, lhs_high, 1);
2626           __ Sllv(dst_low, dst_low, AT);
2627           __ Or(dst_low, dst_low, TMP);
2628           __ Srlv(TMP, lhs_high, rhs_reg);
2629           __ Sll(dst_high, lhs_low, 1);
2630           __ Sllv(dst_high, dst_high, AT);
2631           __ Or(dst_high, dst_high, TMP);
2632           __ Andi(TMP, rhs_reg, kMipsBitsPerWord);
2633           if (isR6) {
2634             __ Beqzc(TMP, &done, /* is_bare= */ true);
2635             __ Move(TMP, dst_high);
2636             __ Move(dst_high, dst_low);
2637             __ Move(dst_low, TMP);
2638           } else {
2639             __ Movn(AT, dst_high, TMP);
2640             __ Movn(dst_high, dst_low, TMP);
2641             __ Movn(dst_low, AT, TMP);
2642           }
2643         }
2644         __ Bind(&done);
2645       }
2646       break;
2647     }
2648 
2649     default:
2650       LOG(FATAL) << "Unexpected shift operation type " << type;
2651   }
2652 }
2653 
VisitAdd(HAdd * instruction)2654 void LocationsBuilderMIPS::VisitAdd(HAdd* instruction) {
2655   HandleBinaryOp(instruction);
2656 }
2657 
VisitAdd(HAdd * instruction)2658 void InstructionCodeGeneratorMIPS::VisitAdd(HAdd* instruction) {
2659   HandleBinaryOp(instruction);
2660 }
2661 
VisitAnd(HAnd * instruction)2662 void LocationsBuilderMIPS::VisitAnd(HAnd* instruction) {
2663   HandleBinaryOp(instruction);
2664 }
2665 
VisitAnd(HAnd * instruction)2666 void InstructionCodeGeneratorMIPS::VisitAnd(HAnd* instruction) {
2667   HandleBinaryOp(instruction);
2668 }
2669 
VisitArrayGet(HArrayGet * instruction)2670 void LocationsBuilderMIPS::VisitArrayGet(HArrayGet* instruction) {
2671   DataType::Type type = instruction->GetType();
2672   bool object_array_get_with_read_barrier =
2673       kEmitCompilerReadBarrier && (type == DataType::Type::kReference);
2674   LocationSummary* locations =
2675       new (GetGraph()->GetAllocator()) LocationSummary(instruction,
2676                                                        object_array_get_with_read_barrier
2677                                                            ? LocationSummary::kCallOnSlowPath
2678                                                            : LocationSummary::kNoCall);
2679   if (object_array_get_with_read_barrier && kUseBakerReadBarrier) {
2680     locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty());  // No caller-save registers.
2681   }
2682   locations->SetInAt(0, Location::RequiresRegister());
2683   locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
2684   if (DataType::IsFloatingPointType(type)) {
2685     locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
2686   } else {
2687     // The output overlaps in the case of an object array get with
2688     // read barriers enabled: we do not want the move to overwrite the
2689     // array's location, as we need it to emit the read barrier.
2690     locations->SetOut(Location::RequiresRegister(),
2691                       object_array_get_with_read_barrier
2692                           ? Location::kOutputOverlap
2693                           : Location::kNoOutputOverlap);
2694   }
2695   // We need a temporary register for the read barrier marking slow
2696   // path in CodeGeneratorMIPS::GenerateArrayLoadWithBakerReadBarrier.
2697   if (object_array_get_with_read_barrier && kUseBakerReadBarrier) {
2698     bool temp_needed = instruction->GetIndex()->IsConstant()
2699         ? !kBakerReadBarrierThunksEnableForFields
2700         : !kBakerReadBarrierThunksEnableForArrays;
2701     if (temp_needed) {
2702       locations->AddTemp(Location::RequiresRegister());
2703     }
2704   }
2705 }
2706 
GetImplicitNullChecker(HInstruction * instruction,CodeGeneratorMIPS * codegen)2707 static auto GetImplicitNullChecker(HInstruction* instruction, CodeGeneratorMIPS* codegen) {
2708   auto null_checker = [codegen, instruction]() {
2709     codegen->MaybeRecordImplicitNullCheck(instruction);
2710   };
2711   return null_checker;
2712 }
2713 
VisitArrayGet(HArrayGet * instruction)2714 void InstructionCodeGeneratorMIPS::VisitArrayGet(HArrayGet* instruction) {
2715   LocationSummary* locations = instruction->GetLocations();
2716   Location obj_loc = locations->InAt(0);
2717   Register obj = obj_loc.AsRegister<Register>();
2718   Location out_loc = locations->Out();
2719   Location index = locations->InAt(1);
2720   uint32_t data_offset = CodeGenerator::GetArrayDataOffset(instruction);
2721   auto null_checker = GetImplicitNullChecker(instruction, codegen_);
2722 
2723   DataType::Type type = instruction->GetType();
2724   const bool maybe_compressed_char_at = mirror::kUseStringCompression &&
2725                                         instruction->IsStringCharAt();
2726   switch (type) {
2727     case DataType::Type::kBool:
2728     case DataType::Type::kUint8: {
2729       Register out = out_loc.AsRegister<Register>();
2730       if (index.IsConstant()) {
2731         size_t offset =
2732             (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset;
2733         __ LoadFromOffset(kLoadUnsignedByte, out, obj, offset, null_checker);
2734       } else {
2735         __ Addu(TMP, obj, index.AsRegister<Register>());
2736         __ LoadFromOffset(kLoadUnsignedByte, out, TMP, data_offset, null_checker);
2737       }
2738       break;
2739     }
2740 
2741     case DataType::Type::kInt8: {
2742       Register out = out_loc.AsRegister<Register>();
2743       if (index.IsConstant()) {
2744         size_t offset =
2745             (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset;
2746         __ LoadFromOffset(kLoadSignedByte, out, obj, offset, null_checker);
2747       } else {
2748         __ Addu(TMP, obj, index.AsRegister<Register>());
2749         __ LoadFromOffset(kLoadSignedByte, out, TMP, data_offset, null_checker);
2750       }
2751       break;
2752     }
2753 
2754     case DataType::Type::kUint16: {
2755       Register out = out_loc.AsRegister<Register>();
2756       if (maybe_compressed_char_at) {
2757         uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
2758         __ LoadFromOffset(kLoadWord, TMP, obj, count_offset, null_checker);
2759         __ Sll(TMP, TMP, 31);    // Extract compression flag into the most significant bit of TMP.
2760         static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
2761                       "Expecting 0=compressed, 1=uncompressed");
2762       }
2763       if (index.IsConstant()) {
2764         int32_t const_index = index.GetConstant()->AsIntConstant()->GetValue();
2765         if (maybe_compressed_char_at) {
2766           MipsLabel uncompressed_load, done;
2767           __ Bnez(TMP, &uncompressed_load);
2768           __ LoadFromOffset(kLoadUnsignedByte,
2769                             out,
2770                             obj,
2771                             data_offset + (const_index << TIMES_1));
2772           __ B(&done);
2773           __ Bind(&uncompressed_load);
2774           __ LoadFromOffset(kLoadUnsignedHalfword,
2775                             out,
2776                             obj,
2777                             data_offset + (const_index << TIMES_2));
2778           __ Bind(&done);
2779         } else {
2780           __ LoadFromOffset(kLoadUnsignedHalfword,
2781                             out,
2782                             obj,
2783                             data_offset + (const_index << TIMES_2),
2784                             null_checker);
2785         }
2786       } else {
2787         Register index_reg = index.AsRegister<Register>();
2788         if (maybe_compressed_char_at) {
2789           MipsLabel uncompressed_load, done;
2790           __ Bnez(TMP, &uncompressed_load);
2791           __ Addu(TMP, obj, index_reg);
2792           __ LoadFromOffset(kLoadUnsignedByte, out, TMP, data_offset);
2793           __ B(&done);
2794           __ Bind(&uncompressed_load);
2795           __ ShiftAndAdd(TMP, index_reg, obj, TIMES_2, TMP);
2796           __ LoadFromOffset(kLoadUnsignedHalfword, out, TMP, data_offset);
2797           __ Bind(&done);
2798         } else if (instruction->InputAt(1)->IsIntermediateArrayAddressIndex()) {
2799           __ Addu(TMP, index_reg, obj);
2800           __ LoadFromOffset(kLoadUnsignedHalfword, out, TMP, data_offset, null_checker);
2801         } else {
2802           __ ShiftAndAdd(TMP, index_reg, obj, TIMES_2, TMP);
2803           __ LoadFromOffset(kLoadUnsignedHalfword, out, TMP, data_offset, null_checker);
2804         }
2805       }
2806       break;
2807     }
2808 
2809     case DataType::Type::kInt16: {
2810       Register out = out_loc.AsRegister<Register>();
2811       if (index.IsConstant()) {
2812         size_t offset =
2813             (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset;
2814         __ LoadFromOffset(kLoadSignedHalfword, out, obj, offset, null_checker);
2815       } else if (instruction->InputAt(1)->IsIntermediateArrayAddressIndex()) {
2816         __ Addu(TMP, index.AsRegister<Register>(), obj);
2817         __ LoadFromOffset(kLoadSignedHalfword, out, TMP, data_offset, null_checker);
2818       } else {
2819         __ ShiftAndAdd(TMP, index.AsRegister<Register>(), obj, TIMES_2, TMP);
2820         __ LoadFromOffset(kLoadSignedHalfword, out, TMP, data_offset, null_checker);
2821       }
2822       break;
2823     }
2824 
2825     case DataType::Type::kInt32: {
2826       DCHECK_EQ(sizeof(mirror::HeapReference<mirror::Object>), sizeof(int32_t));
2827       Register out = out_loc.AsRegister<Register>();
2828       if (index.IsConstant()) {
2829         size_t offset =
2830             (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
2831         __ LoadFromOffset(kLoadWord, out, obj, offset, null_checker);
2832       } else if (instruction->InputAt(1)->IsIntermediateArrayAddressIndex()) {
2833         __ Addu(TMP, index.AsRegister<Register>(), obj);
2834         __ LoadFromOffset(kLoadWord, out, TMP, data_offset, null_checker);
2835       } else {
2836         __ ShiftAndAdd(TMP, index.AsRegister<Register>(), obj, TIMES_4, TMP);
2837         __ LoadFromOffset(kLoadWord, out, TMP, data_offset, null_checker);
2838       }
2839       break;
2840     }
2841 
2842     case DataType::Type::kReference: {
2843       static_assert(
2844           sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
2845           "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
2846       // /* HeapReference<Object> */ out =
2847       //     *(obj + data_offset + index * sizeof(HeapReference<Object>))
2848       if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
2849         bool temp_needed = index.IsConstant()
2850             ? !kBakerReadBarrierThunksEnableForFields
2851             : !kBakerReadBarrierThunksEnableForArrays;
2852         Location temp = temp_needed ? locations->GetTemp(0) : Location::NoLocation();
2853         // Note that a potential implicit null check is handled in this
2854         // CodeGeneratorMIPS::GenerateArrayLoadWithBakerReadBarrier call.
2855         DCHECK(!instruction->CanDoImplicitNullCheckOn(instruction->InputAt(0)));
2856         if (index.IsConstant()) {
2857           // Array load with a constant index can be treated as a field load.
2858           size_t offset =
2859               (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
2860           codegen_->GenerateFieldLoadWithBakerReadBarrier(instruction,
2861                                                           out_loc,
2862                                                           obj,
2863                                                           offset,
2864                                                           temp,
2865                                                           /* needs_null_check= */ false);
2866         } else {
2867           codegen_->GenerateArrayLoadWithBakerReadBarrier(instruction,
2868                                                           out_loc,
2869                                                           obj,
2870                                                           data_offset,
2871                                                           index,
2872                                                           temp,
2873                                                           /* needs_null_check= */ false);
2874         }
2875       } else {
2876         Register out = out_loc.AsRegister<Register>();
2877         if (index.IsConstant()) {
2878           size_t offset =
2879               (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
2880           __ LoadFromOffset(kLoadWord, out, obj, offset, null_checker);
2881           // If read barriers are enabled, emit read barriers other than
2882           // Baker's using a slow path (and also unpoison the loaded
2883           // reference, if heap poisoning is enabled).
2884           codegen_->MaybeGenerateReadBarrierSlow(instruction, out_loc, out_loc, obj_loc, offset);
2885         } else {
2886           __ ShiftAndAdd(TMP, index.AsRegister<Register>(), obj, TIMES_4, TMP);
2887           __ LoadFromOffset(kLoadWord, out, TMP, data_offset, null_checker);
2888           // If read barriers are enabled, emit read barriers other than
2889           // Baker's using a slow path (and also unpoison the loaded
2890           // reference, if heap poisoning is enabled).
2891           codegen_->MaybeGenerateReadBarrierSlow(instruction,
2892                                                  out_loc,
2893                                                  out_loc,
2894                                                  obj_loc,
2895                                                  data_offset,
2896                                                  index);
2897         }
2898       }
2899       break;
2900     }
2901 
2902     case DataType::Type::kInt64: {
2903       Register out = out_loc.AsRegisterPairLow<Register>();
2904       if (index.IsConstant()) {
2905         size_t offset =
2906             (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
2907         __ LoadFromOffset(kLoadDoubleword, out, obj, offset, null_checker);
2908       } else if (instruction->InputAt(1)->IsIntermediateArrayAddressIndex()) {
2909         __ Addu(TMP, index.AsRegister<Register>(), obj);
2910         __ LoadFromOffset(kLoadDoubleword, out, TMP, data_offset, null_checker);
2911       } else {
2912         __ ShiftAndAdd(TMP, index.AsRegister<Register>(), obj, TIMES_8, TMP);
2913         __ LoadFromOffset(kLoadDoubleword, out, TMP, data_offset, null_checker);
2914       }
2915       break;
2916     }
2917 
2918     case DataType::Type::kFloat32: {
2919       FRegister out = out_loc.AsFpuRegister<FRegister>();
2920       if (index.IsConstant()) {
2921         size_t offset =
2922             (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
2923         __ LoadSFromOffset(out, obj, offset, null_checker);
2924       } else if (instruction->InputAt(1)->IsIntermediateArrayAddressIndex()) {
2925         __ Addu(TMP, index.AsRegister<Register>(), obj);
2926         __ LoadSFromOffset(out, TMP, data_offset, null_checker);
2927       } else {
2928         __ ShiftAndAdd(TMP, index.AsRegister<Register>(), obj, TIMES_4, TMP);
2929         __ LoadSFromOffset(out, TMP, data_offset, null_checker);
2930       }
2931       break;
2932     }
2933 
2934     case DataType::Type::kFloat64: {
2935       FRegister out = out_loc.AsFpuRegister<FRegister>();
2936       if (index.IsConstant()) {
2937         size_t offset =
2938             (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
2939         __ LoadDFromOffset(out, obj, offset, null_checker);
2940       } else if (instruction->InputAt(1)->IsIntermediateArrayAddressIndex()) {
2941         __ Addu(TMP, index.AsRegister<Register>(), obj);
2942         __ LoadDFromOffset(out, TMP, data_offset, null_checker);
2943       } else {
2944         __ ShiftAndAdd(TMP, index.AsRegister<Register>(), obj, TIMES_8, TMP);
2945         __ LoadDFromOffset(out, TMP, data_offset, null_checker);
2946       }
2947       break;
2948     }
2949 
2950     case DataType::Type::kUint32:
2951     case DataType::Type::kUint64:
2952     case DataType::Type::kVoid:
2953       LOG(FATAL) << "Unreachable type " << instruction->GetType();
2954       UNREACHABLE();
2955   }
2956 }
2957 
VisitArrayLength(HArrayLength * instruction)2958 void LocationsBuilderMIPS::VisitArrayLength(HArrayLength* instruction) {
2959   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
2960   locations->SetInAt(0, Location::RequiresRegister());
2961   locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2962 }
2963 
VisitArrayLength(HArrayLength * instruction)2964 void InstructionCodeGeneratorMIPS::VisitArrayLength(HArrayLength* instruction) {
2965   LocationSummary* locations = instruction->GetLocations();
2966   uint32_t offset = CodeGenerator::GetArrayLengthOffset(instruction);
2967   Register obj = locations->InAt(0).AsRegister<Register>();
2968   Register out = locations->Out().AsRegister<Register>();
2969   __ LoadFromOffset(kLoadWord, out, obj, offset);
2970   codegen_->MaybeRecordImplicitNullCheck(instruction);
2971   // Mask out compression flag from String's array length.
2972   if (mirror::kUseStringCompression && instruction->IsStringLength()) {
2973     __ Srl(out, out, 1u);
2974   }
2975 }
2976 
RegisterOrZeroConstant(HInstruction * instruction)2977 Location LocationsBuilderMIPS::RegisterOrZeroConstant(HInstruction* instruction) {
2978   return (instruction->IsConstant() && instruction->AsConstant()->IsZeroBitPattern())
2979       ? Location::ConstantLocation(instruction->AsConstant())
2980       : Location::RequiresRegister();
2981 }
2982 
FpuRegisterOrConstantForStore(HInstruction * instruction)2983 Location LocationsBuilderMIPS::FpuRegisterOrConstantForStore(HInstruction* instruction) {
2984   // We can store 0.0 directly (from the ZERO register) without loading it into an FPU register.
2985   // We can store a non-zero float or double constant without first loading it into the FPU,
2986   // but we should only prefer this if the constant has a single use.
2987   if (instruction->IsConstant() &&
2988       (instruction->AsConstant()->IsZeroBitPattern() ||
2989        instruction->GetUses().HasExactlyOneElement())) {
2990     return Location::ConstantLocation(instruction->AsConstant());
2991     // Otherwise fall through and require an FPU register for the constant.
2992   }
2993   return Location::RequiresFpuRegister();
2994 }
2995 
VisitArraySet(HArraySet * instruction)2996 void LocationsBuilderMIPS::VisitArraySet(HArraySet* instruction) {
2997   DataType::Type value_type = instruction->GetComponentType();
2998 
2999   bool needs_write_barrier =
3000       CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue());
3001   bool may_need_runtime_call_for_type_check = instruction->NeedsTypeCheck();
3002 
3003   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(
3004       instruction,
3005       may_need_runtime_call_for_type_check ?
3006           LocationSummary::kCallOnSlowPath :
3007           LocationSummary::kNoCall);
3008 
3009   locations->SetInAt(0, Location::RequiresRegister());
3010   locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
3011   if (DataType::IsFloatingPointType(instruction->InputAt(2)->GetType())) {
3012     locations->SetInAt(2, FpuRegisterOrConstantForStore(instruction->InputAt(2)));
3013   } else {
3014     locations->SetInAt(2, RegisterOrZeroConstant(instruction->InputAt(2)));
3015   }
3016   if (needs_write_barrier) {
3017     // Temporary register for the write barrier.
3018     locations->AddTemp(Location::RequiresRegister());  // Possibly used for ref. poisoning too.
3019   }
3020 }
3021 
VisitArraySet(HArraySet * instruction)3022 void InstructionCodeGeneratorMIPS::VisitArraySet(HArraySet* instruction) {
3023   LocationSummary* locations = instruction->GetLocations();
3024   Register obj = locations->InAt(0).AsRegister<Register>();
3025   Location index = locations->InAt(1);
3026   Location value_location = locations->InAt(2);
3027   DataType::Type value_type = instruction->GetComponentType();
3028   bool may_need_runtime_call_for_type_check = instruction->NeedsTypeCheck();
3029   bool needs_write_barrier =
3030       CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue());
3031   auto null_checker = GetImplicitNullChecker(instruction, codegen_);
3032   Register base_reg = index.IsConstant() ? obj : TMP;
3033 
3034   switch (value_type) {
3035     case DataType::Type::kBool:
3036     case DataType::Type::kUint8:
3037     case DataType::Type::kInt8: {
3038       uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value();
3039       if (index.IsConstant()) {
3040         data_offset += index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1;
3041       } else {
3042         __ Addu(base_reg, obj, index.AsRegister<Register>());
3043       }
3044       if (value_location.IsConstant()) {
3045         int32_t value = CodeGenerator::GetInt32ValueOf(value_location.GetConstant());
3046         __ StoreConstToOffset(kStoreByte, value, base_reg, data_offset, TMP, null_checker);
3047       } else {
3048         Register value = value_location.AsRegister<Register>();
3049         __ StoreToOffset(kStoreByte, value, base_reg, data_offset, null_checker);
3050       }
3051       break;
3052     }
3053 
3054     case DataType::Type::kUint16:
3055     case DataType::Type::kInt16: {
3056       uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value();
3057       if (index.IsConstant()) {
3058         data_offset += index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2;
3059       } else if (instruction->InputAt(1)->IsIntermediateArrayAddressIndex()) {
3060         __ Addu(base_reg, index.AsRegister<Register>(), obj);
3061       } else {
3062         __ ShiftAndAdd(base_reg, index.AsRegister<Register>(), obj, TIMES_2, base_reg);
3063       }
3064       if (value_location.IsConstant()) {
3065         int32_t value = CodeGenerator::GetInt32ValueOf(value_location.GetConstant());
3066         __ StoreConstToOffset(kStoreHalfword, value, base_reg, data_offset, TMP, null_checker);
3067       } else {
3068         Register value = value_location.AsRegister<Register>();
3069         __ StoreToOffset(kStoreHalfword, value, base_reg, data_offset, null_checker);
3070       }
3071       break;
3072     }
3073 
3074     case DataType::Type::kInt32: {
3075       uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value();
3076       if (index.IsConstant()) {
3077         data_offset += index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4;
3078       } else if (instruction->InputAt(1)->IsIntermediateArrayAddressIndex()) {
3079         __ Addu(base_reg, index.AsRegister<Register>(), obj);
3080       } else {
3081         __ ShiftAndAdd(base_reg, index.AsRegister<Register>(), obj, TIMES_4, base_reg);
3082       }
3083       if (value_location.IsConstant()) {
3084         int32_t value = CodeGenerator::GetInt32ValueOf(value_location.GetConstant());
3085         __ StoreConstToOffset(kStoreWord, value, base_reg, data_offset, TMP, null_checker);
3086       } else {
3087         Register value = value_location.AsRegister<Register>();
3088         __ StoreToOffset(kStoreWord, value, base_reg, data_offset, null_checker);
3089       }
3090       break;
3091     }
3092 
3093     case DataType::Type::kReference: {
3094       if (value_location.IsConstant()) {
3095         // Just setting null.
3096         uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value();
3097         if (index.IsConstant()) {
3098           data_offset += index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4;
3099         } else {
3100           __ ShiftAndAdd(base_reg, index.AsRegister<Register>(), obj, TIMES_4, base_reg);
3101         }
3102         int32_t value = CodeGenerator::GetInt32ValueOf(value_location.GetConstant());
3103         DCHECK_EQ(value, 0);
3104         __ StoreConstToOffset(kStoreWord, value, base_reg, data_offset, TMP, null_checker);
3105         DCHECK(!needs_write_barrier);
3106         DCHECK(!may_need_runtime_call_for_type_check);
3107         break;
3108       }
3109 
3110       DCHECK(needs_write_barrier);
3111       Register value = value_location.AsRegister<Register>();
3112       Register temp1 = locations->GetTemp(0).AsRegister<Register>();
3113       Register temp2 = TMP;  // Doesn't need to survive slow path.
3114       uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
3115       uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
3116       uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
3117       MipsLabel done;
3118       SlowPathCodeMIPS* slow_path = nullptr;
3119 
3120       if (may_need_runtime_call_for_type_check) {
3121         slow_path = new (codegen_->GetScopedAllocator()) ArraySetSlowPathMIPS(instruction);
3122         codegen_->AddSlowPath(slow_path);
3123         if (instruction->GetValueCanBeNull()) {
3124           MipsLabel non_zero;
3125           __ Bnez(value, &non_zero);
3126           uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value();
3127           if (index.IsConstant()) {
3128             data_offset += index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4;
3129           } else if (instruction->InputAt(1)->IsIntermediateArrayAddressIndex()) {
3130             __ Addu(base_reg, index.AsRegister<Register>(), obj);
3131           } else {
3132             __ ShiftAndAdd(base_reg, index.AsRegister<Register>(), obj, TIMES_4, base_reg);
3133           }
3134           __ StoreToOffset(kStoreWord, value, base_reg, data_offset, null_checker);
3135           __ B(&done);
3136           __ Bind(&non_zero);
3137         }
3138 
3139         // Note that when read barriers are enabled, the type checks
3140         // are performed without read barriers.  This is fine, even in
3141         // the case where a class object is in the from-space after
3142         // the flip, as a comparison involving such a type would not
3143         // produce a false positive; it may of course produce a false
3144         // negative, in which case we would take the ArraySet slow
3145         // path.
3146 
3147         // /* HeapReference<Class> */ temp1 = obj->klass_
3148         __ LoadFromOffset(kLoadWord, temp1, obj, class_offset, null_checker);
3149         __ MaybeUnpoisonHeapReference(temp1);
3150 
3151         // /* HeapReference<Class> */ temp1 = temp1->component_type_
3152         __ LoadFromOffset(kLoadWord, temp1, temp1, component_offset);
3153         // /* HeapReference<Class> */ temp2 = value->klass_
3154         __ LoadFromOffset(kLoadWord, temp2, value, class_offset);
3155         // If heap poisoning is enabled, no need to unpoison `temp1`
3156         // nor `temp2`, as we are comparing two poisoned references.
3157 
3158         if (instruction->StaticTypeOfArrayIsObjectArray()) {
3159           MipsLabel do_put;
3160           __ Beq(temp1, temp2, &do_put);
3161           // If heap poisoning is enabled, the `temp1` reference has
3162           // not been unpoisoned yet; unpoison it now.
3163           __ MaybeUnpoisonHeapReference(temp1);
3164 
3165           // /* HeapReference<Class> */ temp1 = temp1->super_class_
3166           __ LoadFromOffset(kLoadWord, temp1, temp1, super_offset);
3167           // If heap poisoning is enabled, no need to unpoison
3168           // `temp1`, as we are comparing against null below.
3169           __ Bnez(temp1, slow_path->GetEntryLabel());
3170           __ Bind(&do_put);
3171         } else {
3172           __ Bne(temp1, temp2, slow_path->GetEntryLabel());
3173         }
3174       }
3175 
3176       Register source = value;
3177       if (kPoisonHeapReferences) {
3178         // Note that in the case where `value` is a null reference,
3179         // we do not enter this block, as a null reference does not
3180         // need poisoning.
3181         __ Move(temp1, value);
3182         __ PoisonHeapReference(temp1);
3183         source = temp1;
3184       }
3185 
3186       uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value();
3187       if (index.IsConstant()) {
3188         data_offset += index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4;
3189       } else {
3190         __ ShiftAndAdd(base_reg, index.AsRegister<Register>(), obj, TIMES_4, base_reg);
3191       }
3192       __ StoreToOffset(kStoreWord, source, base_reg, data_offset);
3193 
3194       if (!may_need_runtime_call_for_type_check) {
3195         codegen_->MaybeRecordImplicitNullCheck(instruction);
3196       }
3197 
3198       codegen_->MarkGCCard(obj, value, instruction->GetValueCanBeNull());
3199 
3200       if (done.IsLinked()) {
3201         __ Bind(&done);
3202       }
3203 
3204       if (slow_path != nullptr) {
3205         __ Bind(slow_path->GetExitLabel());
3206       }
3207       break;
3208     }
3209 
3210     case DataType::Type::kInt64: {
3211       uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value();
3212       if (index.IsConstant()) {
3213         data_offset += index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8;
3214       } else if (instruction->InputAt(1)->IsIntermediateArrayAddressIndex()) {
3215         __ Addu(base_reg, index.AsRegister<Register>(), obj);
3216       } else {
3217         __ ShiftAndAdd(base_reg, index.AsRegister<Register>(), obj, TIMES_8, base_reg);
3218       }
3219       if (value_location.IsConstant()) {
3220         int64_t value = CodeGenerator::GetInt64ValueOf(value_location.GetConstant());
3221         __ StoreConstToOffset(kStoreDoubleword, value, base_reg, data_offset, TMP, null_checker);
3222       } else {
3223         Register value = value_location.AsRegisterPairLow<Register>();
3224         __ StoreToOffset(kStoreDoubleword, value, base_reg, data_offset, null_checker);
3225       }
3226       break;
3227     }
3228 
3229     case DataType::Type::kFloat32: {
3230       uint32_t data_offset = mirror::Array::DataOffset(sizeof(float)).Uint32Value();
3231       if (index.IsConstant()) {
3232         data_offset += index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4;
3233       } else if (instruction->InputAt(1)->IsIntermediateArrayAddressIndex()) {
3234         __ Addu(base_reg, index.AsRegister<Register>(), obj);
3235       } else {
3236         __ ShiftAndAdd(base_reg, index.AsRegister<Register>(), obj, TIMES_4, base_reg);
3237       }
3238       if (value_location.IsConstant()) {
3239         int32_t value = CodeGenerator::GetInt32ValueOf(value_location.GetConstant());
3240         __ StoreConstToOffset(kStoreWord, value, base_reg, data_offset, TMP, null_checker);
3241       } else {
3242         FRegister value = value_location.AsFpuRegister<FRegister>();
3243         __ StoreSToOffset(value, base_reg, data_offset, null_checker);
3244       }
3245       break;
3246     }
3247 
3248     case DataType::Type::kFloat64: {
3249       uint32_t data_offset = mirror::Array::DataOffset(sizeof(double)).Uint32Value();
3250       if (index.IsConstant()) {
3251         data_offset += index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8;
3252       } else if (instruction->InputAt(1)->IsIntermediateArrayAddressIndex()) {
3253         __ Addu(base_reg, index.AsRegister<Register>(), obj);
3254       } else {
3255         __ ShiftAndAdd(base_reg, index.AsRegister<Register>(), obj, TIMES_8, base_reg);
3256       }
3257       if (value_location.IsConstant()) {
3258         int64_t value = CodeGenerator::GetInt64ValueOf(value_location.GetConstant());
3259         __ StoreConstToOffset(kStoreDoubleword, value, base_reg, data_offset, TMP, null_checker);
3260       } else {
3261         FRegister value = value_location.AsFpuRegister<FRegister>();
3262         __ StoreDToOffset(value, base_reg, data_offset, null_checker);
3263       }
3264       break;
3265     }
3266 
3267     case DataType::Type::kUint32:
3268     case DataType::Type::kUint64:
3269     case DataType::Type::kVoid:
3270       LOG(FATAL) << "Unreachable type " << instruction->GetType();
3271       UNREACHABLE();
3272   }
3273 }
3274 
VisitIntermediateArrayAddressIndex(HIntermediateArrayAddressIndex * instruction)3275 void LocationsBuilderMIPS::VisitIntermediateArrayAddressIndex(
3276     HIntermediateArrayAddressIndex* instruction) {
3277   LocationSummary* locations =
3278       new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
3279 
3280   HIntConstant* shift = instruction->GetShift()->AsIntConstant();
3281 
3282   locations->SetInAt(0, Location::RequiresRegister());
3283   locations->SetInAt(1, Location::ConstantLocation(shift));
3284   locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3285 }
3286 
VisitIntermediateArrayAddressIndex(HIntermediateArrayAddressIndex * instruction)3287 void InstructionCodeGeneratorMIPS::VisitIntermediateArrayAddressIndex(
3288     HIntermediateArrayAddressIndex* instruction) {
3289   LocationSummary* locations = instruction->GetLocations();
3290   Register index_reg = locations->InAt(0).AsRegister<Register>();
3291   uint32_t shift = instruction->GetShift()->AsIntConstant()->GetValue();
3292   __ Sll(locations->Out().AsRegister<Register>(), index_reg, shift);
3293 }
3294 
VisitBoundsCheck(HBoundsCheck * instruction)3295 void LocationsBuilderMIPS::VisitBoundsCheck(HBoundsCheck* instruction) {
3296   RegisterSet caller_saves = RegisterSet::Empty();
3297   InvokeRuntimeCallingConvention calling_convention;
3298   caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
3299   caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
3300   LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction, caller_saves);
3301 
3302   HInstruction* index = instruction->InputAt(0);
3303   HInstruction* length = instruction->InputAt(1);
3304 
3305   bool const_index = false;
3306   bool const_length = false;
3307 
3308   if (index->IsConstant()) {
3309     if (length->IsConstant()) {
3310       const_index = true;
3311       const_length = true;
3312     } else {
3313       int32_t index_value = index->AsIntConstant()->GetValue();
3314       if (index_value < 0 || IsInt<16>(index_value + 1)) {
3315         const_index = true;
3316       }
3317     }
3318   } else if (length->IsConstant()) {
3319     int32_t length_value = length->AsIntConstant()->GetValue();
3320     if (IsUint<15>(length_value)) {
3321       const_length = true;
3322     }
3323   }
3324 
3325   locations->SetInAt(0, const_index
3326       ? Location::ConstantLocation(index->AsConstant())
3327       : Location::RequiresRegister());
3328   locations->SetInAt(1, const_length
3329       ? Location::ConstantLocation(length->AsConstant())
3330       : Location::RequiresRegister());
3331 }
3332 
VisitBoundsCheck(HBoundsCheck * instruction)3333 void InstructionCodeGeneratorMIPS::VisitBoundsCheck(HBoundsCheck* instruction) {
3334   LocationSummary* locations = instruction->GetLocations();
3335   Location index_loc = locations->InAt(0);
3336   Location length_loc = locations->InAt(1);
3337 
3338   if (length_loc.IsConstant()) {
3339     int32_t length = length_loc.GetConstant()->AsIntConstant()->GetValue();
3340     if (index_loc.IsConstant()) {
3341       int32_t index = index_loc.GetConstant()->AsIntConstant()->GetValue();
3342       if (index < 0 || index >= length) {
3343         BoundsCheckSlowPathMIPS* slow_path =
3344             new (codegen_->GetScopedAllocator()) BoundsCheckSlowPathMIPS(instruction);
3345         codegen_->AddSlowPath(slow_path);
3346         __ B(slow_path->GetEntryLabel());
3347       } else {
3348         // Nothing to be done.
3349       }
3350       return;
3351     }
3352 
3353     BoundsCheckSlowPathMIPS* slow_path =
3354         new (codegen_->GetScopedAllocator()) BoundsCheckSlowPathMIPS(instruction);
3355     codegen_->AddSlowPath(slow_path);
3356     Register index = index_loc.AsRegister<Register>();
3357     if (length == 0) {
3358       __ B(slow_path->GetEntryLabel());
3359     } else if (length == 1) {
3360       __ Bnez(index, slow_path->GetEntryLabel());
3361     } else {
3362       DCHECK(IsUint<15>(length)) << length;
3363       __ Sltiu(TMP, index, length);
3364       __ Beqz(TMP, slow_path->GetEntryLabel());
3365     }
3366   } else {
3367     Register length = length_loc.AsRegister<Register>();
3368     BoundsCheckSlowPathMIPS* slow_path =
3369         new (codegen_->GetScopedAllocator()) BoundsCheckSlowPathMIPS(instruction);
3370     codegen_->AddSlowPath(slow_path);
3371     if (index_loc.IsConstant()) {
3372       int32_t index = index_loc.GetConstant()->AsIntConstant()->GetValue();
3373       if (index < 0) {
3374         __ B(slow_path->GetEntryLabel());
3375       } else if (index == 0) {
3376         __ Blez(length, slow_path->GetEntryLabel());
3377       } else {
3378         DCHECK(IsInt<16>(index + 1)) << index;
3379         __ Sltiu(TMP, length, index + 1);
3380         __ Bnez(TMP, slow_path->GetEntryLabel());
3381       }
3382     } else {
3383       Register index = index_loc.AsRegister<Register>();
3384       __ Bgeu(index, length, slow_path->GetEntryLabel());
3385     }
3386   }
3387 }
3388 
3389 // Temp is used for read barrier.
NumberOfInstanceOfTemps(TypeCheckKind type_check_kind)3390 static size_t NumberOfInstanceOfTemps(TypeCheckKind type_check_kind) {
3391   if (kEmitCompilerReadBarrier &&
3392       !(kUseBakerReadBarrier && kBakerReadBarrierThunksEnableForFields) &&
3393       (kUseBakerReadBarrier ||
3394        type_check_kind == TypeCheckKind::kAbstractClassCheck ||
3395        type_check_kind == TypeCheckKind::kClassHierarchyCheck ||
3396        type_check_kind == TypeCheckKind::kArrayObjectCheck)) {
3397     return 1;
3398   }
3399   return 0;
3400 }
3401 
3402 // Extra temp is used for read barrier.
NumberOfCheckCastTemps(TypeCheckKind type_check_kind)3403 static size_t NumberOfCheckCastTemps(TypeCheckKind type_check_kind) {
3404   return 1 + NumberOfInstanceOfTemps(type_check_kind);
3405 }
3406 
VisitCheckCast(HCheckCast * instruction)3407 void LocationsBuilderMIPS::VisitCheckCast(HCheckCast* instruction) {
3408   TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
3409   LocationSummary::CallKind call_kind = CodeGenerator::GetCheckCastCallKind(instruction);
3410   LocationSummary* locations =
3411       new (GetGraph()->GetAllocator()) LocationSummary(instruction, call_kind);
3412   locations->SetInAt(0, Location::RequiresRegister());
3413   if (type_check_kind == TypeCheckKind::kBitstringCheck) {
3414     locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant()));
3415     locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant()));
3416     locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant()));
3417   } else {
3418     locations->SetInAt(1, Location::RequiresRegister());
3419   }
3420   locations->AddRegisterTemps(NumberOfCheckCastTemps(type_check_kind));
3421 }
3422 
VisitCheckCast(HCheckCast * instruction)3423 void InstructionCodeGeneratorMIPS::VisitCheckCast(HCheckCast* instruction) {
3424   TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
3425   LocationSummary* locations = instruction->GetLocations();
3426   Location obj_loc = locations->InAt(0);
3427   Register obj = obj_loc.AsRegister<Register>();
3428   Location cls = locations->InAt(1);
3429   Location temp_loc = locations->GetTemp(0);
3430   Register temp = temp_loc.AsRegister<Register>();
3431   const size_t num_temps = NumberOfCheckCastTemps(type_check_kind);
3432   DCHECK_LE(num_temps, 2u);
3433   Location maybe_temp2_loc = (num_temps >= 2) ? locations->GetTemp(1) : Location::NoLocation();
3434   const uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
3435   const uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
3436   const uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
3437   const uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value();
3438   const uint32_t iftable_offset = mirror::Class::IfTableOffset().Uint32Value();
3439   const uint32_t array_length_offset = mirror::Array::LengthOffset().Uint32Value();
3440   const uint32_t object_array_data_offset =
3441       mirror::Array::DataOffset(kHeapReferenceSize).Uint32Value();
3442   MipsLabel done;
3443 
3444   bool is_type_check_slow_path_fatal = CodeGenerator::IsTypeCheckSlowPathFatal(instruction);
3445   SlowPathCodeMIPS* slow_path =
3446       new (codegen_->GetScopedAllocator()) TypeCheckSlowPathMIPS(
3447           instruction, is_type_check_slow_path_fatal);
3448   codegen_->AddSlowPath(slow_path);
3449 
3450   // Avoid this check if we know `obj` is not null.
3451   if (instruction->MustDoNullCheck()) {
3452     __ Beqz(obj, &done);
3453   }
3454 
3455   switch (type_check_kind) {
3456     case TypeCheckKind::kExactCheck:
3457     case TypeCheckKind::kArrayCheck: {
3458       // /* HeapReference<Class> */ temp = obj->klass_
3459       GenerateReferenceLoadTwoRegisters(instruction,
3460                                         temp_loc,
3461                                         obj_loc,
3462                                         class_offset,
3463                                         maybe_temp2_loc,
3464                                         kWithoutReadBarrier);
3465       // Jump to slow path for throwing the exception or doing a
3466       // more involved array check.
3467       __ Bne(temp, cls.AsRegister<Register>(), slow_path->GetEntryLabel());
3468       break;
3469     }
3470 
3471     case TypeCheckKind::kAbstractClassCheck: {
3472       // /* HeapReference<Class> */ temp = obj->klass_
3473       GenerateReferenceLoadTwoRegisters(instruction,
3474                                         temp_loc,
3475                                         obj_loc,
3476                                         class_offset,
3477                                         maybe_temp2_loc,
3478                                         kWithoutReadBarrier);
3479       // If the class is abstract, we eagerly fetch the super class of the
3480       // object to avoid doing a comparison we know will fail.
3481       MipsLabel loop;
3482       __ Bind(&loop);
3483       // /* HeapReference<Class> */ temp = temp->super_class_
3484       GenerateReferenceLoadOneRegister(instruction,
3485                                        temp_loc,
3486                                        super_offset,
3487                                        maybe_temp2_loc,
3488                                        kWithoutReadBarrier);
3489       // If the class reference currently in `temp` is null, jump to the slow path to throw the
3490       // exception.
3491       __ Beqz(temp, slow_path->GetEntryLabel());
3492       // Otherwise, compare the classes.
3493       __ Bne(temp, cls.AsRegister<Register>(), &loop);
3494       break;
3495     }
3496 
3497     case TypeCheckKind::kClassHierarchyCheck: {
3498       // /* HeapReference<Class> */ temp = obj->klass_
3499       GenerateReferenceLoadTwoRegisters(instruction,
3500                                         temp_loc,
3501                                         obj_loc,
3502                                         class_offset,
3503                                         maybe_temp2_loc,
3504                                         kWithoutReadBarrier);
3505       // Walk over the class hierarchy to find a match.
3506       MipsLabel loop;
3507       __ Bind(&loop);
3508       __ Beq(temp, cls.AsRegister<Register>(), &done);
3509       // /* HeapReference<Class> */ temp = temp->super_class_
3510       GenerateReferenceLoadOneRegister(instruction,
3511                                        temp_loc,
3512                                        super_offset,
3513                                        maybe_temp2_loc,
3514                                        kWithoutReadBarrier);
3515       // If the class reference currently in `temp` is null, jump to the slow path to throw the
3516       // exception. Otherwise, jump to the beginning of the loop.
3517       __ Bnez(temp, &loop);
3518       __ B(slow_path->GetEntryLabel());
3519       break;
3520     }
3521 
3522     case TypeCheckKind::kArrayObjectCheck: {
3523       // /* HeapReference<Class> */ temp = obj->klass_
3524       GenerateReferenceLoadTwoRegisters(instruction,
3525                                         temp_loc,
3526                                         obj_loc,
3527                                         class_offset,
3528                                         maybe_temp2_loc,
3529                                         kWithoutReadBarrier);
3530       // Do an exact check.
3531       __ Beq(temp, cls.AsRegister<Register>(), &done);
3532       // Otherwise, we need to check that the object's class is a non-primitive array.
3533       // /* HeapReference<Class> */ temp = temp->component_type_
3534       GenerateReferenceLoadOneRegister(instruction,
3535                                        temp_loc,
3536                                        component_offset,
3537                                        maybe_temp2_loc,
3538                                        kWithoutReadBarrier);
3539       // If the component type is null, jump to the slow path to throw the exception.
3540       __ Beqz(temp, slow_path->GetEntryLabel());
3541       // Otherwise, the object is indeed an array, further check that this component
3542       // type is not a primitive type.
3543       __ LoadFromOffset(kLoadUnsignedHalfword, temp, temp, primitive_offset);
3544       static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
3545       __ Bnez(temp, slow_path->GetEntryLabel());
3546       break;
3547     }
3548 
3549     case TypeCheckKind::kUnresolvedCheck:
3550       // We always go into the type check slow path for the unresolved check case.
3551       // We cannot directly call the CheckCast runtime entry point
3552       // without resorting to a type checking slow path here (i.e. by
3553       // calling InvokeRuntime directly), as it would require to
3554       // assign fixed registers for the inputs of this HInstanceOf
3555       // instruction (following the runtime calling convention), which
3556       // might be cluttered by the potential first read barrier
3557       // emission at the beginning of this method.
3558       __ B(slow_path->GetEntryLabel());
3559       break;
3560 
3561     case TypeCheckKind::kInterfaceCheck: {
3562       // Avoid read barriers to improve performance of the fast path. We can not get false
3563       // positives by doing this.
3564       // /* HeapReference<Class> */ temp = obj->klass_
3565       GenerateReferenceLoadTwoRegisters(instruction,
3566                                         temp_loc,
3567                                         obj_loc,
3568                                         class_offset,
3569                                         maybe_temp2_loc,
3570                                         kWithoutReadBarrier);
3571       // /* HeapReference<Class> */ temp = temp->iftable_
3572       GenerateReferenceLoadTwoRegisters(instruction,
3573                                         temp_loc,
3574                                         temp_loc,
3575                                         iftable_offset,
3576                                         maybe_temp2_loc,
3577                                         kWithoutReadBarrier);
3578       // Iftable is never null.
3579       __ Lw(TMP, temp, array_length_offset);
3580       // Loop through the iftable and check if any class matches.
3581       MipsLabel loop;
3582       __ Bind(&loop);
3583       __ Addiu(temp, temp, 2 * kHeapReferenceSize);  // Possibly in delay slot on R2.
3584       __ Beqz(TMP, slow_path->GetEntryLabel());
3585       __ Lw(AT, temp, object_array_data_offset - 2 * kHeapReferenceSize);
3586       __ MaybeUnpoisonHeapReference(AT);
3587       // Go to next interface.
3588       __ Addiu(TMP, TMP, -2);
3589       // Compare the classes and continue the loop if they do not match.
3590       __ Bne(AT, cls.AsRegister<Register>(), &loop);
3591       break;
3592     }
3593 
3594     case TypeCheckKind::kBitstringCheck: {
3595       // /* HeapReference<Class> */ temp = obj->klass_
3596       GenerateReferenceLoadTwoRegisters(instruction,
3597                                         temp_loc,
3598                                         obj_loc,
3599                                         class_offset,
3600                                         maybe_temp2_loc,
3601                                         kWithoutReadBarrier);
3602 
3603       GenerateBitstringTypeCheckCompare(instruction, temp);
3604       __ Bnez(temp, slow_path->GetEntryLabel());
3605       break;
3606     }
3607   }
3608 
3609   __ Bind(&done);
3610   __ Bind(slow_path->GetExitLabel());
3611 }
3612 
VisitClinitCheck(HClinitCheck * check)3613 void LocationsBuilderMIPS::VisitClinitCheck(HClinitCheck* check) {
3614   LocationSummary* locations =
3615       new (GetGraph()->GetAllocator()) LocationSummary(check, LocationSummary::kCallOnSlowPath);
3616   locations->SetInAt(0, Location::RequiresRegister());
3617   if (check->HasUses()) {
3618     locations->SetOut(Location::SameAsFirstInput());
3619   }
3620   // Rely on the type initialization to save everything we need.
3621   locations->SetCustomSlowPathCallerSaves(OneRegInReferenceOutSaveEverythingCallerSaves());
3622 }
3623 
VisitClinitCheck(HClinitCheck * check)3624 void InstructionCodeGeneratorMIPS::VisitClinitCheck(HClinitCheck* check) {
3625   // We assume the class is not null.
3626   SlowPathCodeMIPS* slow_path =
3627       new (codegen_->GetScopedAllocator()) LoadClassSlowPathMIPS(check->GetLoadClass(), check);
3628   codegen_->AddSlowPath(slow_path);
3629   GenerateClassInitializationCheck(slow_path,
3630                                    check->GetLocations()->InAt(0).AsRegister<Register>());
3631 }
3632 
VisitCompare(HCompare * compare)3633 void LocationsBuilderMIPS::VisitCompare(HCompare* compare) {
3634   DataType::Type in_type = compare->InputAt(0)->GetType();
3635 
3636   LocationSummary* locations =
3637       new (GetGraph()->GetAllocator()) LocationSummary(compare, LocationSummary::kNoCall);
3638 
3639   switch (in_type) {
3640     case DataType::Type::kBool:
3641     case DataType::Type::kUint8:
3642     case DataType::Type::kInt8:
3643     case DataType::Type::kUint16:
3644     case DataType::Type::kInt16:
3645     case DataType::Type::kInt32:
3646       locations->SetInAt(0, Location::RequiresRegister());
3647       locations->SetInAt(1, Location::RequiresRegister());
3648       locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3649       break;
3650 
3651     case DataType::Type::kInt64:
3652       locations->SetInAt(0, Location::RequiresRegister());
3653       locations->SetInAt(1, Location::RequiresRegister());
3654       // Output overlaps because it is written before doing the low comparison.
3655       locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
3656       break;
3657 
3658     case DataType::Type::kFloat32:
3659     case DataType::Type::kFloat64:
3660       locations->SetInAt(0, Location::RequiresFpuRegister());
3661       locations->SetInAt(1, Location::RequiresFpuRegister());
3662       locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3663       break;
3664 
3665     default:
3666       LOG(FATAL) << "Unexpected type for compare operation " << in_type;
3667   }
3668 }
3669 
VisitCompare(HCompare * instruction)3670 void InstructionCodeGeneratorMIPS::VisitCompare(HCompare* instruction) {
3671   LocationSummary* locations = instruction->GetLocations();
3672   Register res = locations->Out().AsRegister<Register>();
3673   DataType::Type in_type = instruction->InputAt(0)->GetType();
3674   bool isR6 = codegen_->GetInstructionSetFeatures().IsR6();
3675 
3676   //  0 if: left == right
3677   //  1 if: left  > right
3678   // -1 if: left  < right
3679   switch (in_type) {
3680     case DataType::Type::kBool:
3681     case DataType::Type::kUint8:
3682     case DataType::Type::kInt8:
3683     case DataType::Type::kUint16:
3684     case DataType::Type::kInt16:
3685     case DataType::Type::kInt32: {
3686       Register lhs = locations->InAt(0).AsRegister<Register>();
3687       Register rhs = locations->InAt(1).AsRegister<Register>();
3688       __ Slt(TMP, lhs, rhs);
3689       __ Slt(res, rhs, lhs);
3690       __ Subu(res, res, TMP);
3691       break;
3692     }
3693     case DataType::Type::kInt64: {
3694       MipsLabel done;
3695       Register lhs_high = locations->InAt(0).AsRegisterPairHigh<Register>();
3696       Register lhs_low  = locations->InAt(0).AsRegisterPairLow<Register>();
3697       Register rhs_high = locations->InAt(1).AsRegisterPairHigh<Register>();
3698       Register rhs_low  = locations->InAt(1).AsRegisterPairLow<Register>();
3699       // TODO: more efficient (direct) comparison with a constant.
3700       __ Slt(TMP, lhs_high, rhs_high);
3701       __ Slt(AT, rhs_high, lhs_high);  // Inverted: is actually gt.
3702       __ Subu(res, AT, TMP);           // Result -1:1:0 for [ <, >, == ].
3703       __ Bnez(res, &done);             // If we compared ==, check if lower bits are also equal.
3704       __ Sltu(TMP, lhs_low, rhs_low);
3705       __ Sltu(AT, rhs_low, lhs_low);   // Inverted: is actually gt.
3706       __ Subu(res, AT, TMP);           // Result -1:1:0 for [ <, >, == ].
3707       __ Bind(&done);
3708       break;
3709     }
3710 
3711     case DataType::Type::kFloat32: {
3712       bool gt_bias = instruction->IsGtBias();
3713       FRegister lhs = locations->InAt(0).AsFpuRegister<FRegister>();
3714       FRegister rhs = locations->InAt(1).AsFpuRegister<FRegister>();
3715       MipsLabel done;
3716       if (isR6) {
3717         __ CmpEqS(FTMP, lhs, rhs);
3718         __ LoadConst32(res, 0);
3719         __ Bc1nez(FTMP, &done);
3720         if (gt_bias) {
3721           __ CmpLtS(FTMP, lhs, rhs);
3722           __ LoadConst32(res, -1);
3723           __ Bc1nez(FTMP, &done);
3724           __ LoadConst32(res, 1);
3725         } else {
3726           __ CmpLtS(FTMP, rhs, lhs);
3727           __ LoadConst32(res, 1);
3728           __ Bc1nez(FTMP, &done);
3729           __ LoadConst32(res, -1);
3730         }
3731       } else {
3732         if (gt_bias) {
3733           __ ColtS(0, lhs, rhs);
3734           __ LoadConst32(res, -1);
3735           __ Bc1t(0, &done);
3736           __ CeqS(0, lhs, rhs);
3737           __ LoadConst32(res, 1);
3738           __ Movt(res, ZERO, 0);
3739         } else {
3740           __ ColtS(0, rhs, lhs);
3741           __ LoadConst32(res, 1);
3742           __ Bc1t(0, &done);
3743           __ CeqS(0, lhs, rhs);
3744           __ LoadConst32(res, -1);
3745           __ Movt(res, ZERO, 0);
3746         }
3747       }
3748       __ Bind(&done);
3749       break;
3750     }
3751     case DataType::Type::kFloat64: {
3752       bool gt_bias = instruction->IsGtBias();
3753       FRegister lhs = locations->InAt(0).AsFpuRegister<FRegister>();
3754       FRegister rhs = locations->InAt(1).AsFpuRegister<FRegister>();
3755       MipsLabel done;
3756       if (isR6) {
3757         __ CmpEqD(FTMP, lhs, rhs);
3758         __ LoadConst32(res, 0);
3759         __ Bc1nez(FTMP, &done);
3760         if (gt_bias) {
3761           __ CmpLtD(FTMP, lhs, rhs);
3762           __ LoadConst32(res, -1);
3763           __ Bc1nez(FTMP, &done);
3764           __ LoadConst32(res, 1);
3765         } else {
3766           __ CmpLtD(FTMP, rhs, lhs);
3767           __ LoadConst32(res, 1);
3768           __ Bc1nez(FTMP, &done);
3769           __ LoadConst32(res, -1);
3770         }
3771       } else {
3772         if (gt_bias) {
3773           __ ColtD(0, lhs, rhs);
3774           __ LoadConst32(res, -1);
3775           __ Bc1t(0, &done);
3776           __ CeqD(0, lhs, rhs);
3777           __ LoadConst32(res, 1);
3778           __ Movt(res, ZERO, 0);
3779         } else {
3780           __ ColtD(0, rhs, lhs);
3781           __ LoadConst32(res, 1);
3782           __ Bc1t(0, &done);
3783           __ CeqD(0, lhs, rhs);
3784           __ LoadConst32(res, -1);
3785           __ Movt(res, ZERO, 0);
3786         }
3787       }
3788       __ Bind(&done);
3789       break;
3790     }
3791 
3792     default:
3793       LOG(FATAL) << "Unimplemented compare type " << in_type;
3794   }
3795 }
3796 
HandleCondition(HCondition * instruction)3797 void LocationsBuilderMIPS::HandleCondition(HCondition* instruction) {
3798   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
3799   switch (instruction->InputAt(0)->GetType()) {
3800     default:
3801     case DataType::Type::kInt64:
3802       locations->SetInAt(0, Location::RequiresRegister());
3803       locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
3804       break;
3805 
3806     case DataType::Type::kFloat32:
3807     case DataType::Type::kFloat64:
3808       locations->SetInAt(0, Location::RequiresFpuRegister());
3809       locations->SetInAt(1, Location::RequiresFpuRegister());
3810       break;
3811   }
3812   if (!instruction->IsEmittedAtUseSite()) {
3813     locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3814   }
3815 }
3816 
HandleCondition(HCondition * instruction)3817 void InstructionCodeGeneratorMIPS::HandleCondition(HCondition* instruction) {
3818   if (instruction->IsEmittedAtUseSite()) {
3819     return;
3820   }
3821 
3822   DataType::Type type = instruction->InputAt(0)->GetType();
3823   LocationSummary* locations = instruction->GetLocations();
3824 
3825   switch (type) {
3826     default:
3827       // Integer case.
3828       GenerateIntCompare(instruction->GetCondition(), locations);
3829       return;
3830 
3831     case DataType::Type::kInt64:
3832       GenerateLongCompare(instruction->GetCondition(), locations);
3833       return;
3834 
3835     case DataType::Type::kFloat32:
3836     case DataType::Type::kFloat64:
3837       GenerateFpCompare(instruction->GetCondition(), instruction->IsGtBias(), type, locations);
3838       return;
3839   }
3840 }
3841 
DivRemOneOrMinusOne(HBinaryOperation * instruction)3842 void InstructionCodeGeneratorMIPS::DivRemOneOrMinusOne(HBinaryOperation* instruction) {
3843   DCHECK(instruction->IsDiv() || instruction->IsRem());
3844 
3845   LocationSummary* locations = instruction->GetLocations();
3846   Location second = locations->InAt(1);
3847   DCHECK(second.IsConstant());
3848   int64_t imm = Int64FromConstant(second.GetConstant());
3849   DCHECK(imm == 1 || imm == -1);
3850 
3851   if (instruction->GetResultType() == DataType::Type::kInt32) {
3852     Register out = locations->Out().AsRegister<Register>();
3853     Register dividend = locations->InAt(0).AsRegister<Register>();
3854 
3855     if (instruction->IsRem()) {
3856       __ Move(out, ZERO);
3857     } else {
3858       if (imm == -1) {
3859         __ Subu(out, ZERO, dividend);
3860       } else if (out != dividend) {
3861         __ Move(out, dividend);
3862       }
3863     }
3864   } else {
3865     DCHECK_EQ(instruction->GetResultType(), DataType::Type::kInt64);
3866     Register out_high = locations->Out().AsRegisterPairHigh<Register>();
3867     Register out_low = locations->Out().AsRegisterPairLow<Register>();
3868     Register in_high = locations->InAt(0).AsRegisterPairHigh<Register>();
3869     Register in_low = locations->InAt(0).AsRegisterPairLow<Register>();
3870 
3871     if (instruction->IsRem()) {
3872       __ Move(out_high, ZERO);
3873       __ Move(out_low, ZERO);
3874     } else {
3875       if (imm == -1) {
3876         __ Subu(out_low, ZERO, in_low);
3877         __ Sltu(AT, ZERO, out_low);
3878         __ Subu(out_high, ZERO, in_high);
3879         __ Subu(out_high, out_high, AT);
3880       } else {
3881         __ Move(out_low, in_low);
3882         __ Move(out_high, in_high);
3883       }
3884     }
3885   }
3886 }
3887 
DivRemByPowerOfTwo(HBinaryOperation * instruction)3888 void InstructionCodeGeneratorMIPS::DivRemByPowerOfTwo(HBinaryOperation* instruction) {
3889   DCHECK(instruction->IsDiv() || instruction->IsRem());
3890 
3891   LocationSummary* locations = instruction->GetLocations();
3892   Location second = locations->InAt(1);
3893   const bool is_r2_or_newer = codegen_->GetInstructionSetFeatures().IsMipsIsaRevGreaterThanEqual2();
3894   const bool is_r6 = codegen_->GetInstructionSetFeatures().IsR6();
3895   DCHECK(second.IsConstant());
3896 
3897   if (instruction->GetResultType() == DataType::Type::kInt32) {
3898     Register out = locations->Out().AsRegister<Register>();
3899     Register dividend = locations->InAt(0).AsRegister<Register>();
3900     int32_t imm = second.GetConstant()->AsIntConstant()->GetValue();
3901     uint32_t abs_imm = static_cast<uint32_t>(AbsOrMin(imm));
3902     int ctz_imm = CTZ(abs_imm);
3903 
3904     if (instruction->IsDiv()) {
3905       if (ctz_imm == 1) {
3906         // Fast path for division by +/-2, which is very common.
3907         __ Srl(TMP, dividend, 31);
3908       } else {
3909         __ Sra(TMP, dividend, 31);
3910         __ Srl(TMP, TMP, 32 - ctz_imm);
3911       }
3912       __ Addu(out, dividend, TMP);
3913       __ Sra(out, out, ctz_imm);
3914       if (imm < 0) {
3915         __ Subu(out, ZERO, out);
3916       }
3917     } else {
3918       if (ctz_imm == 1) {
3919         // Fast path for modulo +/-2, which is very common.
3920         __ Sra(TMP, dividend, 31);
3921         __ Subu(out, dividend, TMP);
3922         __ Andi(out, out, 1);
3923         __ Addu(out, out, TMP);
3924       } else {
3925         __ Sra(TMP, dividend, 31);
3926         __ Srl(TMP, TMP, 32 - ctz_imm);
3927         __ Addu(out, dividend, TMP);
3928         if (IsUint<16>(abs_imm - 1)) {
3929           __ Andi(out, out, abs_imm - 1);
3930         } else {
3931           if (is_r2_or_newer) {
3932             __ Ins(out, ZERO, ctz_imm, 32 - ctz_imm);
3933           } else {
3934             __ Sll(out, out, 32 - ctz_imm);
3935             __ Srl(out, out, 32 - ctz_imm);
3936           }
3937         }
3938         __ Subu(out, out, TMP);
3939       }
3940     }
3941   } else {
3942     DCHECK_EQ(instruction->GetResultType(), DataType::Type::kInt64);
3943     Register out_high = locations->Out().AsRegisterPairHigh<Register>();
3944     Register out_low = locations->Out().AsRegisterPairLow<Register>();
3945     Register in_high = locations->InAt(0).AsRegisterPairHigh<Register>();
3946     Register in_low = locations->InAt(0).AsRegisterPairLow<Register>();
3947     int64_t imm = Int64FromConstant(second.GetConstant());
3948     uint64_t abs_imm = static_cast<uint64_t>(AbsOrMin(imm));
3949     int ctz_imm = CTZ(abs_imm);
3950 
3951     if (instruction->IsDiv()) {
3952       if (ctz_imm < 32) {
3953         if (ctz_imm == 1) {
3954           __ Srl(AT, in_high, 31);
3955         } else {
3956           __ Sra(AT, in_high, 31);
3957           __ Srl(AT, AT, 32 - ctz_imm);
3958         }
3959         __ Addu(AT, AT, in_low);
3960         __ Sltu(TMP, AT, in_low);
3961         __ Addu(out_high, in_high, TMP);
3962         __ Srl(out_low, AT, ctz_imm);
3963         if (is_r2_or_newer) {
3964           __ Ins(out_low, out_high, 32 - ctz_imm, ctz_imm);
3965           __ Sra(out_high, out_high, ctz_imm);
3966         } else {
3967           __ Sll(AT, out_high, 32 - ctz_imm);
3968           __ Sra(out_high, out_high, ctz_imm);
3969           __ Or(out_low, out_low, AT);
3970         }
3971         if (imm < 0) {
3972           __ Subu(out_low, ZERO, out_low);
3973           __ Sltu(AT, ZERO, out_low);
3974           __ Subu(out_high, ZERO, out_high);
3975           __ Subu(out_high, out_high, AT);
3976         }
3977       } else if (ctz_imm == 32) {
3978         __ Sra(AT, in_high, 31);
3979         __ Addu(AT, AT, in_low);
3980         __ Sltu(AT, AT, in_low);
3981         __ Addu(out_low, in_high, AT);
3982         if (imm < 0) {
3983           __ Srl(TMP, out_low, 31);
3984           __ Subu(out_low, ZERO, out_low);
3985           __ Sltu(AT, ZERO, out_low);
3986           __ Subu(out_high, TMP, AT);
3987         } else {
3988           __ Sra(out_high, out_low, 31);
3989         }
3990       } else if (ctz_imm < 63) {
3991         __ Sra(AT, in_high, 31);
3992         __ Srl(TMP, AT, 64 - ctz_imm);
3993         __ Addu(AT, AT, in_low);
3994         __ Sltu(AT, AT, in_low);
3995         __ Addu(out_low, in_high, AT);
3996         __ Addu(out_low, out_low, TMP);
3997         __ Sra(out_low, out_low, ctz_imm - 32);
3998         if (imm < 0) {
3999           __ Subu(out_low, ZERO, out_low);
4000         }
4001         __ Sra(out_high, out_low, 31);
4002       } else {
4003         DCHECK_LT(imm, 0);
4004         if (is_r6) {
4005           __ Aui(AT, in_high, 0x8000);
4006         } else {
4007           __ Lui(AT, 0x8000);
4008           __ Xor(AT, AT, in_high);
4009         }
4010         __ Or(AT, AT, in_low);
4011         __ Sltiu(out_low, AT, 1);
4012         __ Move(out_high, ZERO);
4013       }
4014     } else {
4015       if ((ctz_imm == 1) && !is_r6) {
4016         __ Andi(AT, in_low, 1);
4017         __ Sll(TMP, in_low, 31);
4018         __ And(TMP, in_high, TMP);
4019         __ Sra(out_high, TMP, 31);
4020         __ Or(out_low, out_high, AT);
4021       } else if (ctz_imm < 32) {
4022         __ Sra(AT, in_high, 31);
4023         if (ctz_imm <= 16) {
4024           __ Andi(out_low, in_low, abs_imm - 1);
4025         } else if (is_r2_or_newer) {
4026           __ Ext(out_low, in_low, 0, ctz_imm);
4027         } else {
4028           __ Sll(out_low, in_low, 32 - ctz_imm);
4029           __ Srl(out_low, out_low, 32 - ctz_imm);
4030         }
4031         if (is_r6) {
4032           __ Selnez(out_high, AT, out_low);
4033         } else {
4034           __ Movz(AT, ZERO, out_low);
4035           __ Move(out_high, AT);
4036         }
4037         if (is_r2_or_newer) {
4038           __ Ins(out_low, out_high, ctz_imm, 32 - ctz_imm);
4039         } else {
4040           __ Sll(AT, out_high, ctz_imm);
4041           __ Or(out_low, out_low, AT);
4042         }
4043       } else if (ctz_imm == 32) {
4044         __ Sra(AT, in_high, 31);
4045         __ Move(out_low, in_low);
4046         if (is_r6) {
4047           __ Selnez(out_high, AT, out_low);
4048         } else {
4049           __ Movz(AT, ZERO, out_low);
4050           __ Move(out_high, AT);
4051         }
4052       } else if (ctz_imm < 63) {
4053         __ Sra(AT, in_high, 31);
4054         __ Move(TMP, in_low);
4055         if (ctz_imm - 32 <= 16) {
4056           __ Andi(out_high, in_high, (1 << (ctz_imm - 32)) - 1);
4057         } else if (is_r2_or_newer) {
4058           __ Ext(out_high, in_high, 0, ctz_imm - 32);
4059         } else {
4060           __ Sll(out_high, in_high, 64 - ctz_imm);
4061           __ Srl(out_high, out_high, 64 - ctz_imm);
4062         }
4063         __ Move(out_low, TMP);
4064         __ Or(TMP, TMP, out_high);
4065         if (is_r6) {
4066           __ Selnez(AT, AT, TMP);
4067         } else {
4068           __ Movz(AT, ZERO, TMP);
4069         }
4070         if (is_r2_or_newer) {
4071           __ Ins(out_high, AT, ctz_imm - 32, 64 - ctz_imm);
4072         } else {
4073           __ Sll(AT, AT, ctz_imm - 32);
4074           __ Or(out_high, out_high, AT);
4075         }
4076       } else {
4077         if (is_r6) {
4078           __ Aui(AT, in_high, 0x8000);
4079         } else {
4080           __ Lui(AT, 0x8000);
4081           __ Xor(AT, AT, in_high);
4082         }
4083         __ Or(AT, AT, in_low);
4084         __ Sltiu(AT, AT, 1);
4085         __ Sll(AT, AT, 31);
4086         __ Move(out_low, in_low);
4087         __ Xor(out_high, in_high, AT);
4088       }
4089     }
4090   }
4091 }
4092 
GenerateDivRemWithAnyConstant(HBinaryOperation * instruction)4093 void InstructionCodeGeneratorMIPS::GenerateDivRemWithAnyConstant(HBinaryOperation* instruction) {
4094   DCHECK(instruction->IsDiv() || instruction->IsRem());
4095   DCHECK_EQ(instruction->GetResultType(), DataType::Type::kInt32);
4096 
4097   LocationSummary* locations = instruction->GetLocations();
4098   Location second = locations->InAt(1);
4099   DCHECK(second.IsConstant());
4100 
4101   Register out = locations->Out().AsRegister<Register>();
4102   Register dividend = locations->InAt(0).AsRegister<Register>();
4103   int32_t imm = second.GetConstant()->AsIntConstant()->GetValue();
4104 
4105   int64_t magic;
4106   int shift;
4107   CalculateMagicAndShiftForDivRem(imm, false /* is_long= */, &magic, &shift);
4108 
4109   bool isR6 = codegen_->GetInstructionSetFeatures().IsR6();
4110 
4111   __ LoadConst32(TMP, magic);
4112   if (isR6) {
4113     __ MuhR6(TMP, dividend, TMP);
4114   } else {
4115     __ MultR2(dividend, TMP);
4116     __ Mfhi(TMP);
4117   }
4118   if (imm > 0 && magic < 0) {
4119     __ Addu(TMP, TMP, dividend);
4120   } else if (imm < 0 && magic > 0) {
4121     __ Subu(TMP, TMP, dividend);
4122   }
4123 
4124   if (shift != 0) {
4125     __ Sra(TMP, TMP, shift);
4126   }
4127 
4128   if (instruction->IsDiv()) {
4129     __ Sra(out, TMP, 31);
4130     __ Subu(out, TMP, out);
4131   } else {
4132     __ Sra(AT, TMP, 31);
4133     __ Subu(AT, TMP, AT);
4134     __ LoadConst32(TMP, imm);
4135     if (isR6) {
4136       __ MulR6(TMP, AT, TMP);
4137     } else {
4138       __ MulR2(TMP, AT, TMP);
4139     }
4140     __ Subu(out, dividend, TMP);
4141   }
4142 }
4143 
GenerateDivRemIntegral(HBinaryOperation * instruction)4144 void InstructionCodeGeneratorMIPS::GenerateDivRemIntegral(HBinaryOperation* instruction) {
4145   DCHECK(instruction->IsDiv() || instruction->IsRem());
4146   DCHECK_EQ(instruction->GetResultType(), DataType::Type::kInt32);
4147 
4148   LocationSummary* locations = instruction->GetLocations();
4149   Register out = locations->Out().AsRegister<Register>();
4150   Location second = locations->InAt(1);
4151 
4152   if (second.IsConstant()) {
4153     int32_t imm = second.GetConstant()->AsIntConstant()->GetValue();
4154     if (imm == 0) {
4155       // Do not generate anything. DivZeroCheck would prevent any code to be executed.
4156     } else if (imm == 1 || imm == -1) {
4157       DivRemOneOrMinusOne(instruction);
4158     } else if (IsPowerOfTwo(AbsOrMin(imm))) {
4159       DivRemByPowerOfTwo(instruction);
4160     } else {
4161       DCHECK(imm <= -2 || imm >= 2);
4162       GenerateDivRemWithAnyConstant(instruction);
4163     }
4164   } else {
4165     Register dividend = locations->InAt(0).AsRegister<Register>();
4166     Register divisor = second.AsRegister<Register>();
4167     bool isR6 = codegen_->GetInstructionSetFeatures().IsR6();
4168     if (instruction->IsDiv()) {
4169       if (isR6) {
4170         __ DivR6(out, dividend, divisor);
4171       } else {
4172         __ DivR2(out, dividend, divisor);
4173       }
4174     } else {
4175       if (isR6) {
4176         __ ModR6(out, dividend, divisor);
4177       } else {
4178         __ ModR2(out, dividend, divisor);
4179       }
4180     }
4181   }
4182 }
4183 
VisitDiv(HDiv * div)4184 void LocationsBuilderMIPS::VisitDiv(HDiv* div) {
4185   DataType::Type type = div->GetResultType();
4186   bool call_long_div = false;
4187   if (type == DataType::Type::kInt64) {
4188     if (div->InputAt(1)->IsConstant()) {
4189       int64_t imm = CodeGenerator::GetInt64ValueOf(div->InputAt(1)->AsConstant());
4190       call_long_div = (imm != 0) && !IsPowerOfTwo(static_cast<uint64_t>(AbsOrMin(imm)));
4191     } else {
4192       call_long_div = true;
4193     }
4194   }
4195   LocationSummary::CallKind call_kind = call_long_div
4196       ? LocationSummary::kCallOnMainOnly
4197       : LocationSummary::kNoCall;
4198 
4199   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(div, call_kind);
4200 
4201   switch (type) {
4202     case DataType::Type::kInt32:
4203       locations->SetInAt(0, Location::RequiresRegister());
4204       locations->SetInAt(1, Location::RegisterOrConstant(div->InputAt(1)));
4205       locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4206       break;
4207 
4208     case DataType::Type::kInt64: {
4209       if (call_long_div) {
4210         InvokeRuntimeCallingConvention calling_convention;
4211         locations->SetInAt(0, Location::RegisterPairLocation(
4212             calling_convention.GetRegisterAt(0), calling_convention.GetRegisterAt(1)));
4213         locations->SetInAt(1, Location::RegisterPairLocation(
4214             calling_convention.GetRegisterAt(2), calling_convention.GetRegisterAt(3)));
4215         locations->SetOut(calling_convention.GetReturnLocation(type));
4216       } else {
4217         locations->SetInAt(0, Location::RequiresRegister());
4218         locations->SetInAt(1, Location::ConstantLocation(div->InputAt(1)->AsConstant()));
4219         locations->SetOut(Location::RequiresRegister());
4220       }
4221       break;
4222     }
4223 
4224     case DataType::Type::kFloat32:
4225     case DataType::Type::kFloat64:
4226       locations->SetInAt(0, Location::RequiresFpuRegister());
4227       locations->SetInAt(1, Location::RequiresFpuRegister());
4228       locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
4229       break;
4230 
4231     default:
4232       LOG(FATAL) << "Unexpected div type " << type;
4233   }
4234 }
4235 
VisitDiv(HDiv * instruction)4236 void InstructionCodeGeneratorMIPS::VisitDiv(HDiv* instruction) {
4237   DataType::Type type = instruction->GetType();
4238   LocationSummary* locations = instruction->GetLocations();
4239 
4240   switch (type) {
4241     case DataType::Type::kInt32:
4242       GenerateDivRemIntegral(instruction);
4243       break;
4244     case DataType::Type::kInt64: {
4245       if (locations->InAt(1).IsConstant()) {
4246         int64_t imm = locations->InAt(1).GetConstant()->AsLongConstant()->GetValue();
4247         if (imm == 0) {
4248           // Do not generate anything. DivZeroCheck would prevent any code to be executed.
4249         } else if (imm == 1 || imm == -1) {
4250           DivRemOneOrMinusOne(instruction);
4251         } else {
4252           DCHECK(IsPowerOfTwo(static_cast<uint64_t>(AbsOrMin(imm))));
4253           DivRemByPowerOfTwo(instruction);
4254         }
4255       } else {
4256         codegen_->InvokeRuntime(kQuickLdiv, instruction, instruction->GetDexPc());
4257         CheckEntrypointTypes<kQuickLdiv, int64_t, int64_t, int64_t>();
4258       }
4259       break;
4260     }
4261     case DataType::Type::kFloat32:
4262     case DataType::Type::kFloat64: {
4263       FRegister dst = locations->Out().AsFpuRegister<FRegister>();
4264       FRegister lhs = locations->InAt(0).AsFpuRegister<FRegister>();
4265       FRegister rhs = locations->InAt(1).AsFpuRegister<FRegister>();
4266       if (type == DataType::Type::kFloat32) {
4267         __ DivS(dst, lhs, rhs);
4268       } else {
4269         __ DivD(dst, lhs, rhs);
4270       }
4271       break;
4272     }
4273     default:
4274       LOG(FATAL) << "Unexpected div type " << type;
4275   }
4276 }
4277 
VisitDivZeroCheck(HDivZeroCheck * instruction)4278 void LocationsBuilderMIPS::VisitDivZeroCheck(HDivZeroCheck* instruction) {
4279   LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction);
4280   locations->SetInAt(0, Location::RegisterOrConstant(instruction->InputAt(0)));
4281 }
4282 
VisitDivZeroCheck(HDivZeroCheck * instruction)4283 void InstructionCodeGeneratorMIPS::VisitDivZeroCheck(HDivZeroCheck* instruction) {
4284   SlowPathCodeMIPS* slow_path =
4285       new (codegen_->GetScopedAllocator()) DivZeroCheckSlowPathMIPS(instruction);
4286   codegen_->AddSlowPath(slow_path);
4287   Location value = instruction->GetLocations()->InAt(0);
4288   DataType::Type type = instruction->GetType();
4289 
4290   switch (type) {
4291     case DataType::Type::kBool:
4292     case DataType::Type::kUint8:
4293     case DataType::Type::kInt8:
4294     case DataType::Type::kUint16:
4295     case DataType::Type::kInt16:
4296     case DataType::Type::kInt32: {
4297       if (value.IsConstant()) {
4298         if (value.GetConstant()->AsIntConstant()->GetValue() == 0) {
4299           __ B(slow_path->GetEntryLabel());
4300         } else {
4301           // A division by a non-null constant is valid. We don't need to perform
4302           // any check, so simply fall through.
4303         }
4304       } else {
4305         DCHECK(value.IsRegister()) << value;
4306         __ Beqz(value.AsRegister<Register>(), slow_path->GetEntryLabel());
4307       }
4308       break;
4309     }
4310     case DataType::Type::kInt64: {
4311       if (value.IsConstant()) {
4312         if (value.GetConstant()->AsLongConstant()->GetValue() == 0) {
4313           __ B(slow_path->GetEntryLabel());
4314         } else {
4315           // A division by a non-null constant is valid. We don't need to perform
4316           // any check, so simply fall through.
4317         }
4318       } else {
4319         DCHECK(value.IsRegisterPair()) << value;
4320         __ Or(TMP, value.AsRegisterPairHigh<Register>(), value.AsRegisterPairLow<Register>());
4321         __ Beqz(TMP, slow_path->GetEntryLabel());
4322       }
4323       break;
4324     }
4325     default:
4326       LOG(FATAL) << "Unexpected type " << type << " for DivZeroCheck.";
4327   }
4328 }
4329 
VisitDoubleConstant(HDoubleConstant * constant)4330 void LocationsBuilderMIPS::VisitDoubleConstant(HDoubleConstant* constant) {
4331   LocationSummary* locations =
4332       new (GetGraph()->GetAllocator()) LocationSummary(constant, LocationSummary::kNoCall);
4333   locations->SetOut(Location::ConstantLocation(constant));
4334 }
4335 
VisitDoubleConstant(HDoubleConstant * cst ATTRIBUTE_UNUSED)4336 void InstructionCodeGeneratorMIPS::VisitDoubleConstant(HDoubleConstant* cst ATTRIBUTE_UNUSED) {
4337   // Will be generated at use site.
4338 }
4339 
VisitExit(HExit * exit)4340 void LocationsBuilderMIPS::VisitExit(HExit* exit) {
4341   exit->SetLocations(nullptr);
4342 }
4343 
VisitExit(HExit * exit ATTRIBUTE_UNUSED)4344 void InstructionCodeGeneratorMIPS::VisitExit(HExit* exit ATTRIBUTE_UNUSED) {
4345 }
4346 
VisitFloatConstant(HFloatConstant * constant)4347 void LocationsBuilderMIPS::VisitFloatConstant(HFloatConstant* constant) {
4348   LocationSummary* locations =
4349       new (GetGraph()->GetAllocator()) LocationSummary(constant, LocationSummary::kNoCall);
4350   locations->SetOut(Location::ConstantLocation(constant));
4351 }
4352 
VisitFloatConstant(HFloatConstant * constant ATTRIBUTE_UNUSED)4353 void InstructionCodeGeneratorMIPS::VisitFloatConstant(HFloatConstant* constant ATTRIBUTE_UNUSED) {
4354   // Will be generated at use site.
4355 }
4356 
VisitGoto(HGoto * got)4357 void LocationsBuilderMIPS::VisitGoto(HGoto* got) {
4358   got->SetLocations(nullptr);
4359 }
4360 
HandleGoto(HInstruction * got,HBasicBlock * successor)4361 void InstructionCodeGeneratorMIPS::HandleGoto(HInstruction* got, HBasicBlock* successor) {
4362   if (successor->IsExitBlock()) {
4363     DCHECK(got->GetPrevious()->AlwaysThrows());
4364     return;  // no code needed
4365   }
4366 
4367   HBasicBlock* block = got->GetBlock();
4368   HInstruction* previous = got->GetPrevious();
4369   HLoopInformation* info = block->GetLoopInformation();
4370 
4371   if (info != nullptr && info->IsBackEdge(*block) && info->HasSuspendCheck()) {
4372     if (codegen_->GetCompilerOptions().CountHotnessInCompiledCode()) {
4373       __ Lw(AT, SP, kCurrentMethodStackOffset);
4374       __ Lhu(TMP, AT, ArtMethod::HotnessCountOffset().Int32Value());
4375       __ Addiu(TMP, TMP, 1);
4376       __ Sh(TMP, AT, ArtMethod::HotnessCountOffset().Int32Value());
4377     }
4378     GenerateSuspendCheck(info->GetSuspendCheck(), successor);
4379     return;
4380   }
4381   if (block->IsEntryBlock() && (previous != nullptr) && previous->IsSuspendCheck()) {
4382     GenerateSuspendCheck(previous->AsSuspendCheck(), nullptr);
4383   }
4384   if (!codegen_->GoesToNextBlock(block, successor)) {
4385     __ B(codegen_->GetLabelOf(successor));
4386   }
4387 }
4388 
VisitGoto(HGoto * got)4389 void InstructionCodeGeneratorMIPS::VisitGoto(HGoto* got) {
4390   HandleGoto(got, got->GetSuccessor());
4391 }
4392 
VisitTryBoundary(HTryBoundary * try_boundary)4393 void LocationsBuilderMIPS::VisitTryBoundary(HTryBoundary* try_boundary) {
4394   try_boundary->SetLocations(nullptr);
4395 }
4396 
VisitTryBoundary(HTryBoundary * try_boundary)4397 void InstructionCodeGeneratorMIPS::VisitTryBoundary(HTryBoundary* try_boundary) {
4398   HBasicBlock* successor = try_boundary->GetNormalFlowSuccessor();
4399   if (!successor->IsExitBlock()) {
4400     HandleGoto(try_boundary, successor);
4401   }
4402 }
4403 
GenerateIntCompare(IfCondition cond,LocationSummary * locations)4404 void InstructionCodeGeneratorMIPS::GenerateIntCompare(IfCondition cond,
4405                                                       LocationSummary* locations) {
4406   Register dst = locations->Out().AsRegister<Register>();
4407   Register lhs = locations->InAt(0).AsRegister<Register>();
4408   Location rhs_location = locations->InAt(1);
4409   Register rhs_reg = ZERO;
4410   int64_t rhs_imm = 0;
4411   bool use_imm = rhs_location.IsConstant();
4412   if (use_imm) {
4413     rhs_imm = CodeGenerator::GetInt32ValueOf(rhs_location.GetConstant());
4414   } else {
4415     rhs_reg = rhs_location.AsRegister<Register>();
4416   }
4417 
4418   switch (cond) {
4419     case kCondEQ:
4420     case kCondNE:
4421       if (use_imm && IsInt<16>(-rhs_imm)) {
4422         if (rhs_imm == 0) {
4423           if (cond == kCondEQ) {
4424             __ Sltiu(dst, lhs, 1);
4425           } else {
4426             __ Sltu(dst, ZERO, lhs);
4427           }
4428         } else {
4429           __ Addiu(dst, lhs, -rhs_imm);
4430           if (cond == kCondEQ) {
4431             __ Sltiu(dst, dst, 1);
4432           } else {
4433             __ Sltu(dst, ZERO, dst);
4434           }
4435         }
4436       } else {
4437         if (use_imm && IsUint<16>(rhs_imm)) {
4438           __ Xori(dst, lhs, rhs_imm);
4439         } else {
4440           if (use_imm) {
4441             rhs_reg = TMP;
4442             __ LoadConst32(rhs_reg, rhs_imm);
4443           }
4444           __ Xor(dst, lhs, rhs_reg);
4445         }
4446         if (cond == kCondEQ) {
4447           __ Sltiu(dst, dst, 1);
4448         } else {
4449           __ Sltu(dst, ZERO, dst);
4450         }
4451       }
4452       break;
4453 
4454     case kCondLT:
4455     case kCondGE:
4456       if (use_imm && IsInt<16>(rhs_imm)) {
4457         __ Slti(dst, lhs, rhs_imm);
4458       } else {
4459         if (use_imm) {
4460           rhs_reg = TMP;
4461           __ LoadConst32(rhs_reg, rhs_imm);
4462         }
4463         __ Slt(dst, lhs, rhs_reg);
4464       }
4465       if (cond == kCondGE) {
4466         // Simulate lhs >= rhs via !(lhs < rhs) since there's
4467         // only the slt instruction but no sge.
4468         __ Xori(dst, dst, 1);
4469       }
4470       break;
4471 
4472     case kCondLE:
4473     case kCondGT:
4474       if (use_imm && IsInt<16>(rhs_imm + 1)) {
4475         // Simulate lhs <= rhs via lhs < rhs + 1.
4476         __ Slti(dst, lhs, rhs_imm + 1);
4477         if (cond == kCondGT) {
4478           // Simulate lhs > rhs via !(lhs <= rhs) since there's
4479           // only the slti instruction but no sgti.
4480           __ Xori(dst, dst, 1);
4481         }
4482       } else {
4483         if (use_imm) {
4484           rhs_reg = TMP;
4485           __ LoadConst32(rhs_reg, rhs_imm);
4486         }
4487         __ Slt(dst, rhs_reg, lhs);
4488         if (cond == kCondLE) {
4489           // Simulate lhs <= rhs via !(rhs < lhs) since there's
4490           // only the slt instruction but no sle.
4491           __ Xori(dst, dst, 1);
4492         }
4493       }
4494       break;
4495 
4496     case kCondB:
4497     case kCondAE:
4498       if (use_imm && IsInt<16>(rhs_imm)) {
4499         // Sltiu sign-extends its 16-bit immediate operand before
4500         // the comparison and thus lets us compare directly with
4501         // unsigned values in the ranges [0, 0x7fff] and
4502         // [0xffff8000, 0xffffffff].
4503         __ Sltiu(dst, lhs, rhs_imm);
4504       } else {
4505         if (use_imm) {
4506           rhs_reg = TMP;
4507           __ LoadConst32(rhs_reg, rhs_imm);
4508         }
4509         __ Sltu(dst, lhs, rhs_reg);
4510       }
4511       if (cond == kCondAE) {
4512         // Simulate lhs >= rhs via !(lhs < rhs) since there's
4513         // only the sltu instruction but no sgeu.
4514         __ Xori(dst, dst, 1);
4515       }
4516       break;
4517 
4518     case kCondBE:
4519     case kCondA:
4520       if (use_imm && (rhs_imm != -1) && IsInt<16>(rhs_imm + 1)) {
4521         // Simulate lhs <= rhs via lhs < rhs + 1.
4522         // Note that this only works if rhs + 1 does not overflow
4523         // to 0, hence the check above.
4524         // Sltiu sign-extends its 16-bit immediate operand before
4525         // the comparison and thus lets us compare directly with
4526         // unsigned values in the ranges [0, 0x7fff] and
4527         // [0xffff8000, 0xffffffff].
4528         __ Sltiu(dst, lhs, rhs_imm + 1);
4529         if (cond == kCondA) {
4530           // Simulate lhs > rhs via !(lhs <= rhs) since there's
4531           // only the sltiu instruction but no sgtiu.
4532           __ Xori(dst, dst, 1);
4533         }
4534       } else {
4535         if (use_imm) {
4536           rhs_reg = TMP;
4537           __ LoadConst32(rhs_reg, rhs_imm);
4538         }
4539         __ Sltu(dst, rhs_reg, lhs);
4540         if (cond == kCondBE) {
4541           // Simulate lhs <= rhs via !(rhs < lhs) since there's
4542           // only the sltu instruction but no sleu.
4543           __ Xori(dst, dst, 1);
4544         }
4545       }
4546       break;
4547   }
4548 }
4549 
MaterializeIntCompare(IfCondition cond,LocationSummary * input_locations,Register dst)4550 bool InstructionCodeGeneratorMIPS::MaterializeIntCompare(IfCondition cond,
4551                                                          LocationSummary* input_locations,
4552                                                          Register dst) {
4553   Register lhs = input_locations->InAt(0).AsRegister<Register>();
4554   Location rhs_location = input_locations->InAt(1);
4555   Register rhs_reg = ZERO;
4556   int64_t rhs_imm = 0;
4557   bool use_imm = rhs_location.IsConstant();
4558   if (use_imm) {
4559     rhs_imm = CodeGenerator::GetInt32ValueOf(rhs_location.GetConstant());
4560   } else {
4561     rhs_reg = rhs_location.AsRegister<Register>();
4562   }
4563 
4564   switch (cond) {
4565     case kCondEQ:
4566     case kCondNE:
4567       if (use_imm && IsInt<16>(-rhs_imm)) {
4568         __ Addiu(dst, lhs, -rhs_imm);
4569       } else if (use_imm && IsUint<16>(rhs_imm)) {
4570         __ Xori(dst, lhs, rhs_imm);
4571       } else {
4572         if (use_imm) {
4573           rhs_reg = TMP;
4574           __ LoadConst32(rhs_reg, rhs_imm);
4575         }
4576         __ Xor(dst, lhs, rhs_reg);
4577       }
4578       return (cond == kCondEQ);
4579 
4580     case kCondLT:
4581     case kCondGE:
4582       if (use_imm && IsInt<16>(rhs_imm)) {
4583         __ Slti(dst, lhs, rhs_imm);
4584       } else {
4585         if (use_imm) {
4586           rhs_reg = TMP;
4587           __ LoadConst32(rhs_reg, rhs_imm);
4588         }
4589         __ Slt(dst, lhs, rhs_reg);
4590       }
4591       return (cond == kCondGE);
4592 
4593     case kCondLE:
4594     case kCondGT:
4595       if (use_imm && IsInt<16>(rhs_imm + 1)) {
4596         // Simulate lhs <= rhs via lhs < rhs + 1.
4597         __ Slti(dst, lhs, rhs_imm + 1);
4598         return (cond == kCondGT);
4599       } else {
4600         if (use_imm) {
4601           rhs_reg = TMP;
4602           __ LoadConst32(rhs_reg, rhs_imm);
4603         }
4604         __ Slt(dst, rhs_reg, lhs);
4605         return (cond == kCondLE);
4606       }
4607 
4608     case kCondB:
4609     case kCondAE:
4610       if (use_imm && IsInt<16>(rhs_imm)) {
4611         // Sltiu sign-extends its 16-bit immediate operand before
4612         // the comparison and thus lets us compare directly with
4613         // unsigned values in the ranges [0, 0x7fff] and
4614         // [0xffff8000, 0xffffffff].
4615         __ Sltiu(dst, lhs, rhs_imm);
4616       } else {
4617         if (use_imm) {
4618           rhs_reg = TMP;
4619           __ LoadConst32(rhs_reg, rhs_imm);
4620         }
4621         __ Sltu(dst, lhs, rhs_reg);
4622       }
4623       return (cond == kCondAE);
4624 
4625     case kCondBE:
4626     case kCondA:
4627       if (use_imm && (rhs_imm != -1) && IsInt<16>(rhs_imm + 1)) {
4628         // Simulate lhs <= rhs via lhs < rhs + 1.
4629         // Note that this only works if rhs + 1 does not overflow
4630         // to 0, hence the check above.
4631         // Sltiu sign-extends its 16-bit immediate operand before
4632         // the comparison and thus lets us compare directly with
4633         // unsigned values in the ranges [0, 0x7fff] and
4634         // [0xffff8000, 0xffffffff].
4635         __ Sltiu(dst, lhs, rhs_imm + 1);
4636         return (cond == kCondA);
4637       } else {
4638         if (use_imm) {
4639           rhs_reg = TMP;
4640           __ LoadConst32(rhs_reg, rhs_imm);
4641         }
4642         __ Sltu(dst, rhs_reg, lhs);
4643         return (cond == kCondBE);
4644       }
4645   }
4646 }
4647 
GenerateIntCompareAndBranch(IfCondition cond,LocationSummary * locations,MipsLabel * label)4648 void InstructionCodeGeneratorMIPS::GenerateIntCompareAndBranch(IfCondition cond,
4649                                                                LocationSummary* locations,
4650                                                                MipsLabel* label) {
4651   Register lhs = locations->InAt(0).AsRegister<Register>();
4652   Location rhs_location = locations->InAt(1);
4653   Register rhs_reg = ZERO;
4654   int64_t rhs_imm = 0;
4655   bool use_imm = rhs_location.IsConstant();
4656   if (use_imm) {
4657     rhs_imm = CodeGenerator::GetInt32ValueOf(rhs_location.GetConstant());
4658   } else {
4659     rhs_reg = rhs_location.AsRegister<Register>();
4660   }
4661 
4662   if (use_imm && rhs_imm == 0) {
4663     switch (cond) {
4664       case kCondEQ:
4665       case kCondBE:  // <= 0 if zero
4666         __ Beqz(lhs, label);
4667         break;
4668       case kCondNE:
4669       case kCondA:  // > 0 if non-zero
4670         __ Bnez(lhs, label);
4671         break;
4672       case kCondLT:
4673         __ Bltz(lhs, label);
4674         break;
4675       case kCondGE:
4676         __ Bgez(lhs, label);
4677         break;
4678       case kCondLE:
4679         __ Blez(lhs, label);
4680         break;
4681       case kCondGT:
4682         __ Bgtz(lhs, label);
4683         break;
4684       case kCondB:  // always false
4685         break;
4686       case kCondAE:  // always true
4687         __ B(label);
4688         break;
4689     }
4690   } else {
4691     bool isR6 = codegen_->GetInstructionSetFeatures().IsR6();
4692     if (isR6 || !use_imm) {
4693       if (use_imm) {
4694         rhs_reg = TMP;
4695         __ LoadConst32(rhs_reg, rhs_imm);
4696       }
4697       switch (cond) {
4698         case kCondEQ:
4699           __ Beq(lhs, rhs_reg, label);
4700           break;
4701         case kCondNE:
4702           __ Bne(lhs, rhs_reg, label);
4703           break;
4704         case kCondLT:
4705           __ Blt(lhs, rhs_reg, label);
4706           break;
4707         case kCondGE:
4708           __ Bge(lhs, rhs_reg, label);
4709           break;
4710         case kCondLE:
4711           __ Bge(rhs_reg, lhs, label);
4712           break;
4713         case kCondGT:
4714           __ Blt(rhs_reg, lhs, label);
4715           break;
4716         case kCondB:
4717           __ Bltu(lhs, rhs_reg, label);
4718           break;
4719         case kCondAE:
4720           __ Bgeu(lhs, rhs_reg, label);
4721           break;
4722         case kCondBE:
4723           __ Bgeu(rhs_reg, lhs, label);
4724           break;
4725         case kCondA:
4726           __ Bltu(rhs_reg, lhs, label);
4727           break;
4728       }
4729     } else {
4730       // Special cases for more efficient comparison with constants on R2.
4731       switch (cond) {
4732         case kCondEQ:
4733           __ LoadConst32(TMP, rhs_imm);
4734           __ Beq(lhs, TMP, label);
4735           break;
4736         case kCondNE:
4737           __ LoadConst32(TMP, rhs_imm);
4738           __ Bne(lhs, TMP, label);
4739           break;
4740         case kCondLT:
4741           if (IsInt<16>(rhs_imm)) {
4742             __ Slti(TMP, lhs, rhs_imm);
4743             __ Bnez(TMP, label);
4744           } else {
4745             __ LoadConst32(TMP, rhs_imm);
4746             __ Blt(lhs, TMP, label);
4747           }
4748           break;
4749         case kCondGE:
4750           if (IsInt<16>(rhs_imm)) {
4751             __ Slti(TMP, lhs, rhs_imm);
4752             __ Beqz(TMP, label);
4753           } else {
4754             __ LoadConst32(TMP, rhs_imm);
4755             __ Bge(lhs, TMP, label);
4756           }
4757           break;
4758         case kCondLE:
4759           if (IsInt<16>(rhs_imm + 1)) {
4760             // Simulate lhs <= rhs via lhs < rhs + 1.
4761             __ Slti(TMP, lhs, rhs_imm + 1);
4762             __ Bnez(TMP, label);
4763           } else {
4764             __ LoadConst32(TMP, rhs_imm);
4765             __ Bge(TMP, lhs, label);
4766           }
4767           break;
4768         case kCondGT:
4769           if (IsInt<16>(rhs_imm + 1)) {
4770             // Simulate lhs > rhs via !(lhs < rhs + 1).
4771             __ Slti(TMP, lhs, rhs_imm + 1);
4772             __ Beqz(TMP, label);
4773           } else {
4774             __ LoadConst32(TMP, rhs_imm);
4775             __ Blt(TMP, lhs, label);
4776           }
4777           break;
4778         case kCondB:
4779           if (IsInt<16>(rhs_imm)) {
4780             __ Sltiu(TMP, lhs, rhs_imm);
4781             __ Bnez(TMP, label);
4782           } else {
4783             __ LoadConst32(TMP, rhs_imm);
4784             __ Bltu(lhs, TMP, label);
4785           }
4786           break;
4787         case kCondAE:
4788           if (IsInt<16>(rhs_imm)) {
4789             __ Sltiu(TMP, lhs, rhs_imm);
4790             __ Beqz(TMP, label);
4791           } else {
4792             __ LoadConst32(TMP, rhs_imm);
4793             __ Bgeu(lhs, TMP, label);
4794           }
4795           break;
4796         case kCondBE:
4797           if ((rhs_imm != -1) && IsInt<16>(rhs_imm + 1)) {
4798             // Simulate lhs <= rhs via lhs < rhs + 1.
4799             // Note that this only works if rhs + 1 does not overflow
4800             // to 0, hence the check above.
4801             __ Sltiu(TMP, lhs, rhs_imm + 1);
4802             __ Bnez(TMP, label);
4803           } else {
4804             __ LoadConst32(TMP, rhs_imm);
4805             __ Bgeu(TMP, lhs, label);
4806           }
4807           break;
4808         case kCondA:
4809           if ((rhs_imm != -1) && IsInt<16>(rhs_imm + 1)) {
4810             // Simulate lhs > rhs via !(lhs < rhs + 1).
4811             // Note that this only works if rhs + 1 does not overflow
4812             // to 0, hence the check above.
4813             __ Sltiu(TMP, lhs, rhs_imm + 1);
4814             __ Beqz(TMP, label);
4815           } else {
4816             __ LoadConst32(TMP, rhs_imm);
4817             __ Bltu(TMP, lhs, label);
4818           }
4819           break;
4820       }
4821     }
4822   }
4823 }
4824 
GenerateLongCompare(IfCondition cond,LocationSummary * locations)4825 void InstructionCodeGeneratorMIPS::GenerateLongCompare(IfCondition cond,
4826                                                        LocationSummary* locations) {
4827   Register dst = locations->Out().AsRegister<Register>();
4828   Register lhs_high = locations->InAt(0).AsRegisterPairHigh<Register>();
4829   Register lhs_low = locations->InAt(0).AsRegisterPairLow<Register>();
4830   Location rhs_location = locations->InAt(1);
4831   Register rhs_high = ZERO;
4832   Register rhs_low = ZERO;
4833   int64_t imm = 0;
4834   uint32_t imm_high = 0;
4835   uint32_t imm_low = 0;
4836   bool use_imm = rhs_location.IsConstant();
4837   if (use_imm) {
4838     imm = rhs_location.GetConstant()->AsLongConstant()->GetValue();
4839     imm_high = High32Bits(imm);
4840     imm_low = Low32Bits(imm);
4841   } else {
4842     rhs_high = rhs_location.AsRegisterPairHigh<Register>();
4843     rhs_low = rhs_location.AsRegisterPairLow<Register>();
4844   }
4845   if (use_imm && imm == 0) {
4846     switch (cond) {
4847       case kCondEQ:
4848       case kCondBE:  // <= 0 if zero
4849         __ Or(dst, lhs_high, lhs_low);
4850         __ Sltiu(dst, dst, 1);
4851         break;
4852       case kCondNE:
4853       case kCondA:  // > 0 if non-zero
4854         __ Or(dst, lhs_high, lhs_low);
4855         __ Sltu(dst, ZERO, dst);
4856         break;
4857       case kCondLT:
4858         __ Slt(dst, lhs_high, ZERO);
4859         break;
4860       case kCondGE:
4861         __ Slt(dst, lhs_high, ZERO);
4862         __ Xori(dst, dst, 1);
4863         break;
4864       case kCondLE:
4865         __ Or(TMP, lhs_high, lhs_low);
4866         __ Sra(AT, lhs_high, 31);
4867         __ Sltu(dst, AT, TMP);
4868         __ Xori(dst, dst, 1);
4869         break;
4870       case kCondGT:
4871         __ Or(TMP, lhs_high, lhs_low);
4872         __ Sra(AT, lhs_high, 31);
4873         __ Sltu(dst, AT, TMP);
4874         break;
4875       case kCondB:  // always false
4876         __ Andi(dst, dst, 0);
4877         break;
4878       case kCondAE:  // always true
4879         __ Ori(dst, ZERO, 1);
4880         break;
4881     }
4882   } else if (use_imm) {
4883     // TODO: more efficient comparison with constants without loading them into TMP/AT.
4884     switch (cond) {
4885       case kCondEQ:
4886         __ LoadConst32(TMP, imm_high);
4887         __ Xor(TMP, TMP, lhs_high);
4888         __ LoadConst32(AT, imm_low);
4889         __ Xor(AT, AT, lhs_low);
4890         __ Or(dst, TMP, AT);
4891         __ Sltiu(dst, dst, 1);
4892         break;
4893       case kCondNE:
4894         __ LoadConst32(TMP, imm_high);
4895         __ Xor(TMP, TMP, lhs_high);
4896         __ LoadConst32(AT, imm_low);
4897         __ Xor(AT, AT, lhs_low);
4898         __ Or(dst, TMP, AT);
4899         __ Sltu(dst, ZERO, dst);
4900         break;
4901       case kCondLT:
4902       case kCondGE:
4903         if (dst == lhs_low) {
4904           __ LoadConst32(TMP, imm_low);
4905           __ Sltu(dst, lhs_low, TMP);
4906         }
4907         __ LoadConst32(TMP, imm_high);
4908         __ Slt(AT, lhs_high, TMP);
4909         __ Slt(TMP, TMP, lhs_high);
4910         if (dst != lhs_low) {
4911           __ LoadConst32(dst, imm_low);
4912           __ Sltu(dst, lhs_low, dst);
4913         }
4914         __ Slt(dst, TMP, dst);
4915         __ Or(dst, dst, AT);
4916         if (cond == kCondGE) {
4917           __ Xori(dst, dst, 1);
4918         }
4919         break;
4920       case kCondGT:
4921       case kCondLE:
4922         if (dst == lhs_low) {
4923           __ LoadConst32(TMP, imm_low);
4924           __ Sltu(dst, TMP, lhs_low);
4925         }
4926         __ LoadConst32(TMP, imm_high);
4927         __ Slt(AT, TMP, lhs_high);
4928         __ Slt(TMP, lhs_high, TMP);
4929         if (dst != lhs_low) {
4930           __ LoadConst32(dst, imm_low);
4931           __ Sltu(dst, dst, lhs_low);
4932         }
4933         __ Slt(dst, TMP, dst);
4934         __ Or(dst, dst, AT);
4935         if (cond == kCondLE) {
4936           __ Xori(dst, dst, 1);
4937         }
4938         break;
4939       case kCondB:
4940       case kCondAE:
4941         if (dst == lhs_low) {
4942           __ LoadConst32(TMP, imm_low);
4943           __ Sltu(dst, lhs_low, TMP);
4944         }
4945         __ LoadConst32(TMP, imm_high);
4946         __ Sltu(AT, lhs_high, TMP);
4947         __ Sltu(TMP, TMP, lhs_high);
4948         if (dst != lhs_low) {
4949           __ LoadConst32(dst, imm_low);
4950           __ Sltu(dst, lhs_low, dst);
4951         }
4952         __ Slt(dst, TMP, dst);
4953         __ Or(dst, dst, AT);
4954         if (cond == kCondAE) {
4955           __ Xori(dst, dst, 1);
4956         }
4957         break;
4958       case kCondA:
4959       case kCondBE:
4960         if (dst == lhs_low) {
4961           __ LoadConst32(TMP, imm_low);
4962           __ Sltu(dst, TMP, lhs_low);
4963         }
4964         __ LoadConst32(TMP, imm_high);
4965         __ Sltu(AT, TMP, lhs_high);
4966         __ Sltu(TMP, lhs_high, TMP);
4967         if (dst != lhs_low) {
4968           __ LoadConst32(dst, imm_low);
4969           __ Sltu(dst, dst, lhs_low);
4970         }
4971         __ Slt(dst, TMP, dst);
4972         __ Or(dst, dst, AT);
4973         if (cond == kCondBE) {
4974           __ Xori(dst, dst, 1);
4975         }
4976         break;
4977     }
4978   } else {
4979     switch (cond) {
4980       case kCondEQ:
4981         __ Xor(TMP, lhs_high, rhs_high);
4982         __ Xor(AT, lhs_low, rhs_low);
4983         __ Or(dst, TMP, AT);
4984         __ Sltiu(dst, dst, 1);
4985         break;
4986       case kCondNE:
4987         __ Xor(TMP, lhs_high, rhs_high);
4988         __ Xor(AT, lhs_low, rhs_low);
4989         __ Or(dst, TMP, AT);
4990         __ Sltu(dst, ZERO, dst);
4991         break;
4992       case kCondLT:
4993       case kCondGE:
4994         __ Slt(TMP, rhs_high, lhs_high);
4995         __ Sltu(AT, lhs_low, rhs_low);
4996         __ Slt(TMP, TMP, AT);
4997         __ Slt(AT, lhs_high, rhs_high);
4998         __ Or(dst, AT, TMP);
4999         if (cond == kCondGE) {
5000           __ Xori(dst, dst, 1);
5001         }
5002         break;
5003       case kCondGT:
5004       case kCondLE:
5005         __ Slt(TMP, lhs_high, rhs_high);
5006         __ Sltu(AT, rhs_low, lhs_low);
5007         __ Slt(TMP, TMP, AT);
5008         __ Slt(AT, rhs_high, lhs_high);
5009         __ Or(dst, AT, TMP);
5010         if (cond == kCondLE) {
5011           __ Xori(dst, dst, 1);
5012         }
5013         break;
5014       case kCondB:
5015       case kCondAE:
5016         __ Sltu(TMP, rhs_high, lhs_high);
5017         __ Sltu(AT, lhs_low, rhs_low);
5018         __ Slt(TMP, TMP, AT);
5019         __ Sltu(AT, lhs_high, rhs_high);
5020         __ Or(dst, AT, TMP);
5021         if (cond == kCondAE) {
5022           __ Xori(dst, dst, 1);
5023         }
5024         break;
5025       case kCondA:
5026       case kCondBE:
5027         __ Sltu(TMP, lhs_high, rhs_high);
5028         __ Sltu(AT, rhs_low, lhs_low);
5029         __ Slt(TMP, TMP, AT);
5030         __ Sltu(AT, rhs_high, lhs_high);
5031         __ Or(dst, AT, TMP);
5032         if (cond == kCondBE) {
5033           __ Xori(dst, dst, 1);
5034         }
5035         break;
5036     }
5037   }
5038 }
5039 
GenerateLongCompareAndBranch(IfCondition cond,LocationSummary * locations,MipsLabel * label)5040 void InstructionCodeGeneratorMIPS::GenerateLongCompareAndBranch(IfCondition cond,
5041                                                                 LocationSummary* locations,
5042                                                                 MipsLabel* label) {
5043   Register lhs_high = locations->InAt(0).AsRegisterPairHigh<Register>();
5044   Register lhs_low = locations->InAt(0).AsRegisterPairLow<Register>();
5045   Location rhs_location = locations->InAt(1);
5046   Register rhs_high = ZERO;
5047   Register rhs_low = ZERO;
5048   int64_t imm = 0;
5049   uint32_t imm_high = 0;
5050   uint32_t imm_low = 0;
5051   bool use_imm = rhs_location.IsConstant();
5052   if (use_imm) {
5053     imm = rhs_location.GetConstant()->AsLongConstant()->GetValue();
5054     imm_high = High32Bits(imm);
5055     imm_low = Low32Bits(imm);
5056   } else {
5057     rhs_high = rhs_location.AsRegisterPairHigh<Register>();
5058     rhs_low = rhs_location.AsRegisterPairLow<Register>();
5059   }
5060 
5061   if (use_imm && imm == 0) {
5062     switch (cond) {
5063       case kCondEQ:
5064       case kCondBE:  // <= 0 if zero
5065         __ Or(TMP, lhs_high, lhs_low);
5066         __ Beqz(TMP, label);
5067         break;
5068       case kCondNE:
5069       case kCondA:  // > 0 if non-zero
5070         __ Or(TMP, lhs_high, lhs_low);
5071         __ Bnez(TMP, label);
5072         break;
5073       case kCondLT:
5074         __ Bltz(lhs_high, label);
5075         break;
5076       case kCondGE:
5077         __ Bgez(lhs_high, label);
5078         break;
5079       case kCondLE:
5080         __ Or(TMP, lhs_high, lhs_low);
5081         __ Sra(AT, lhs_high, 31);
5082         __ Bgeu(AT, TMP, label);
5083         break;
5084       case kCondGT:
5085         __ Or(TMP, lhs_high, lhs_low);
5086         __ Sra(AT, lhs_high, 31);
5087         __ Bltu(AT, TMP, label);
5088         break;
5089       case kCondB:  // always false
5090         break;
5091       case kCondAE:  // always true
5092         __ B(label);
5093         break;
5094     }
5095   } else if (use_imm) {
5096     // TODO: more efficient comparison with constants without loading them into TMP/AT.
5097     switch (cond) {
5098       case kCondEQ:
5099         __ LoadConst32(TMP, imm_high);
5100         __ Xor(TMP, TMP, lhs_high);
5101         __ LoadConst32(AT, imm_low);
5102         __ Xor(AT, AT, lhs_low);
5103         __ Or(TMP, TMP, AT);
5104         __ Beqz(TMP, label);
5105         break;
5106       case kCondNE:
5107         __ LoadConst32(TMP, imm_high);
5108         __ Xor(TMP, TMP, lhs_high);
5109         __ LoadConst32(AT, imm_low);
5110         __ Xor(AT, AT, lhs_low);
5111         __ Or(TMP, TMP, AT);
5112         __ Bnez(TMP, label);
5113         break;
5114       case kCondLT:
5115         __ LoadConst32(TMP, imm_high);
5116         __ Blt(lhs_high, TMP, label);
5117         __ Slt(TMP, TMP, lhs_high);
5118         __ LoadConst32(AT, imm_low);
5119         __ Sltu(AT, lhs_low, AT);
5120         __ Blt(TMP, AT, label);
5121         break;
5122       case kCondGE:
5123         __ LoadConst32(TMP, imm_high);
5124         __ Blt(TMP, lhs_high, label);
5125         __ Slt(TMP, lhs_high, TMP);
5126         __ LoadConst32(AT, imm_low);
5127         __ Sltu(AT, lhs_low, AT);
5128         __ Or(TMP, TMP, AT);
5129         __ Beqz(TMP, label);
5130         break;
5131       case kCondLE:
5132         __ LoadConst32(TMP, imm_high);
5133         __ Blt(lhs_high, TMP, label);
5134         __ Slt(TMP, TMP, lhs_high);
5135         __ LoadConst32(AT, imm_low);
5136         __ Sltu(AT, AT, lhs_low);
5137         __ Or(TMP, TMP, AT);
5138         __ Beqz(TMP, label);
5139         break;
5140       case kCondGT:
5141         __ LoadConst32(TMP, imm_high);
5142         __ Blt(TMP, lhs_high, label);
5143         __ Slt(TMP, lhs_high, TMP);
5144         __ LoadConst32(AT, imm_low);
5145         __ Sltu(AT, AT, lhs_low);
5146         __ Blt(TMP, AT, label);
5147         break;
5148       case kCondB:
5149         __ LoadConst32(TMP, imm_high);
5150         __ Bltu(lhs_high, TMP, label);
5151         __ Sltu(TMP, TMP, lhs_high);
5152         __ LoadConst32(AT, imm_low);
5153         __ Sltu(AT, lhs_low, AT);
5154         __ Blt(TMP, AT, label);
5155         break;
5156       case kCondAE:
5157         __ LoadConst32(TMP, imm_high);
5158         __ Bltu(TMP, lhs_high, label);
5159         __ Sltu(TMP, lhs_high, TMP);
5160         __ LoadConst32(AT, imm_low);
5161         __ Sltu(AT, lhs_low, AT);
5162         __ Or(TMP, TMP, AT);
5163         __ Beqz(TMP, label);
5164         break;
5165       case kCondBE:
5166         __ LoadConst32(TMP, imm_high);
5167         __ Bltu(lhs_high, TMP, label);
5168         __ Sltu(TMP, TMP, lhs_high);
5169         __ LoadConst32(AT, imm_low);
5170         __ Sltu(AT, AT, lhs_low);
5171         __ Or(TMP, TMP, AT);
5172         __ Beqz(TMP, label);
5173         break;
5174       case kCondA:
5175         __ LoadConst32(TMP, imm_high);
5176         __ Bltu(TMP, lhs_high, label);
5177         __ Sltu(TMP, lhs_high, TMP);
5178         __ LoadConst32(AT, imm_low);
5179         __ Sltu(AT, AT, lhs_low);
5180         __ Blt(TMP, AT, label);
5181         break;
5182     }
5183   } else {
5184     switch (cond) {
5185       case kCondEQ:
5186         __ Xor(TMP, lhs_high, rhs_high);
5187         __ Xor(AT, lhs_low, rhs_low);
5188         __ Or(TMP, TMP, AT);
5189         __ Beqz(TMP, label);
5190         break;
5191       case kCondNE:
5192         __ Xor(TMP, lhs_high, rhs_high);
5193         __ Xor(AT, lhs_low, rhs_low);
5194         __ Or(TMP, TMP, AT);
5195         __ Bnez(TMP, label);
5196         break;
5197       case kCondLT:
5198         __ Blt(lhs_high, rhs_high, label);
5199         __ Slt(TMP, rhs_high, lhs_high);
5200         __ Sltu(AT, lhs_low, rhs_low);
5201         __ Blt(TMP, AT, label);
5202         break;
5203       case kCondGE:
5204         __ Blt(rhs_high, lhs_high, label);
5205         __ Slt(TMP, lhs_high, rhs_high);
5206         __ Sltu(AT, lhs_low, rhs_low);
5207         __ Or(TMP, TMP, AT);
5208         __ Beqz(TMP, label);
5209         break;
5210       case kCondLE:
5211         __ Blt(lhs_high, rhs_high, label);
5212         __ Slt(TMP, rhs_high, lhs_high);
5213         __ Sltu(AT, rhs_low, lhs_low);
5214         __ Or(TMP, TMP, AT);
5215         __ Beqz(TMP, label);
5216         break;
5217       case kCondGT:
5218         __ Blt(rhs_high, lhs_high, label);
5219         __ Slt(TMP, lhs_high, rhs_high);
5220         __ Sltu(AT, rhs_low, lhs_low);
5221         __ Blt(TMP, AT, label);
5222         break;
5223       case kCondB:
5224         __ Bltu(lhs_high, rhs_high, label);
5225         __ Sltu(TMP, rhs_high, lhs_high);
5226         __ Sltu(AT, lhs_low, rhs_low);
5227         __ Blt(TMP, AT, label);
5228         break;
5229       case kCondAE:
5230         __ Bltu(rhs_high, lhs_high, label);
5231         __ Sltu(TMP, lhs_high, rhs_high);
5232         __ Sltu(AT, lhs_low, rhs_low);
5233         __ Or(TMP, TMP, AT);
5234         __ Beqz(TMP, label);
5235         break;
5236       case kCondBE:
5237         __ Bltu(lhs_high, rhs_high, label);
5238         __ Sltu(TMP, rhs_high, lhs_high);
5239         __ Sltu(AT, rhs_low, lhs_low);
5240         __ Or(TMP, TMP, AT);
5241         __ Beqz(TMP, label);
5242         break;
5243       case kCondA:
5244         __ Bltu(rhs_high, lhs_high, label);
5245         __ Sltu(TMP, lhs_high, rhs_high);
5246         __ Sltu(AT, rhs_low, lhs_low);
5247         __ Blt(TMP, AT, label);
5248         break;
5249     }
5250   }
5251 }
5252 
GenerateFpCompare(IfCondition cond,bool gt_bias,DataType::Type type,LocationSummary * locations)5253 void InstructionCodeGeneratorMIPS::GenerateFpCompare(IfCondition cond,
5254                                                      bool gt_bias,
5255                                                      DataType::Type type,
5256                                                      LocationSummary* locations) {
5257   Register dst = locations->Out().AsRegister<Register>();
5258   FRegister lhs = locations->InAt(0).AsFpuRegister<FRegister>();
5259   FRegister rhs = locations->InAt(1).AsFpuRegister<FRegister>();
5260   bool isR6 = codegen_->GetInstructionSetFeatures().IsR6();
5261   if (type == DataType::Type::kFloat32) {
5262     if (isR6) {
5263       switch (cond) {
5264         case kCondEQ:
5265           __ CmpEqS(FTMP, lhs, rhs);
5266           __ Mfc1(dst, FTMP);
5267           __ Andi(dst, dst, 1);
5268           break;
5269         case kCondNE:
5270           __ CmpEqS(FTMP, lhs, rhs);
5271           __ Mfc1(dst, FTMP);
5272           __ Addiu(dst, dst, 1);
5273           break;
5274         case kCondLT:
5275           if (gt_bias) {
5276             __ CmpLtS(FTMP, lhs, rhs);
5277           } else {
5278             __ CmpUltS(FTMP, lhs, rhs);
5279           }
5280           __ Mfc1(dst, FTMP);
5281           __ Andi(dst, dst, 1);
5282           break;
5283         case kCondLE:
5284           if (gt_bias) {
5285             __ CmpLeS(FTMP, lhs, rhs);
5286           } else {
5287             __ CmpUleS(FTMP, lhs, rhs);
5288           }
5289           __ Mfc1(dst, FTMP);
5290           __ Andi(dst, dst, 1);
5291           break;
5292         case kCondGT:
5293           if (gt_bias) {
5294             __ CmpUltS(FTMP, rhs, lhs);
5295           } else {
5296             __ CmpLtS(FTMP, rhs, lhs);
5297           }
5298           __ Mfc1(dst, FTMP);
5299           __ Andi(dst, dst, 1);
5300           break;
5301         case kCondGE:
5302           if (gt_bias) {
5303             __ CmpUleS(FTMP, rhs, lhs);
5304           } else {
5305             __ CmpLeS(FTMP, rhs, lhs);
5306           }
5307           __ Mfc1(dst, FTMP);
5308           __ Andi(dst, dst, 1);
5309           break;
5310         default:
5311           LOG(FATAL) << "Unexpected non-floating-point condition " << cond;
5312           UNREACHABLE();
5313       }
5314     } else {
5315       switch (cond) {
5316         case kCondEQ:
5317           __ CeqS(0, lhs, rhs);
5318           __ LoadConst32(dst, 1);
5319           __ Movf(dst, ZERO, 0);
5320           break;
5321         case kCondNE:
5322           __ CeqS(0, lhs, rhs);
5323           __ LoadConst32(dst, 1);
5324           __ Movt(dst, ZERO, 0);
5325           break;
5326         case kCondLT:
5327           if (gt_bias) {
5328             __ ColtS(0, lhs, rhs);
5329           } else {
5330             __ CultS(0, lhs, rhs);
5331           }
5332           __ LoadConst32(dst, 1);
5333           __ Movf(dst, ZERO, 0);
5334           break;
5335         case kCondLE:
5336           if (gt_bias) {
5337             __ ColeS(0, lhs, rhs);
5338           } else {
5339             __ CuleS(0, lhs, rhs);
5340           }
5341           __ LoadConst32(dst, 1);
5342           __ Movf(dst, ZERO, 0);
5343           break;
5344         case kCondGT:
5345           if (gt_bias) {
5346             __ CultS(0, rhs, lhs);
5347           } else {
5348             __ ColtS(0, rhs, lhs);
5349           }
5350           __ LoadConst32(dst, 1);
5351           __ Movf(dst, ZERO, 0);
5352           break;
5353         case kCondGE:
5354           if (gt_bias) {
5355             __ CuleS(0, rhs, lhs);
5356           } else {
5357             __ ColeS(0, rhs, lhs);
5358           }
5359           __ LoadConst32(dst, 1);
5360           __ Movf(dst, ZERO, 0);
5361           break;
5362         default:
5363           LOG(FATAL) << "Unexpected non-floating-point condition " << cond;
5364           UNREACHABLE();
5365       }
5366     }
5367   } else {
5368     DCHECK_EQ(type, DataType::Type::kFloat64);
5369     if (isR6) {
5370       switch (cond) {
5371         case kCondEQ:
5372           __ CmpEqD(FTMP, lhs, rhs);
5373           __ Mfc1(dst, FTMP);
5374           __ Andi(dst, dst, 1);
5375           break;
5376         case kCondNE:
5377           __ CmpEqD(FTMP, lhs, rhs);
5378           __ Mfc1(dst, FTMP);
5379           __ Addiu(dst, dst, 1);
5380           break;
5381         case kCondLT:
5382           if (gt_bias) {
5383             __ CmpLtD(FTMP, lhs, rhs);
5384           } else {
5385             __ CmpUltD(FTMP, lhs, rhs);
5386           }
5387           __ Mfc1(dst, FTMP);
5388           __ Andi(dst, dst, 1);
5389           break;
5390         case kCondLE:
5391           if (gt_bias) {
5392             __ CmpLeD(FTMP, lhs, rhs);
5393           } else {
5394             __ CmpUleD(FTMP, lhs, rhs);
5395           }
5396           __ Mfc1(dst, FTMP);
5397           __ Andi(dst, dst, 1);
5398           break;
5399         case kCondGT:
5400           if (gt_bias) {
5401             __ CmpUltD(FTMP, rhs, lhs);
5402           } else {
5403             __ CmpLtD(FTMP, rhs, lhs);
5404           }
5405           __ Mfc1(dst, FTMP);
5406           __ Andi(dst, dst, 1);
5407           break;
5408         case kCondGE:
5409           if (gt_bias) {
5410             __ CmpUleD(FTMP, rhs, lhs);
5411           } else {
5412             __ CmpLeD(FTMP, rhs, lhs);
5413           }
5414           __ Mfc1(dst, FTMP);
5415           __ Andi(dst, dst, 1);
5416           break;
5417         default:
5418           LOG(FATAL) << "Unexpected non-floating-point condition " << cond;
5419           UNREACHABLE();
5420       }
5421     } else {
5422       switch (cond) {
5423         case kCondEQ:
5424           __ CeqD(0, lhs, rhs);
5425           __ LoadConst32(dst, 1);
5426           __ Movf(dst, ZERO, 0);
5427           break;
5428         case kCondNE:
5429           __ CeqD(0, lhs, rhs);
5430           __ LoadConst32(dst, 1);
5431           __ Movt(dst, ZERO, 0);
5432           break;
5433         case kCondLT:
5434           if (gt_bias) {
5435             __ ColtD(0, lhs, rhs);
5436           } else {
5437             __ CultD(0, lhs, rhs);
5438           }
5439           __ LoadConst32(dst, 1);
5440           __ Movf(dst, ZERO, 0);
5441           break;
5442         case kCondLE:
5443           if (gt_bias) {
5444             __ ColeD(0, lhs, rhs);
5445           } else {
5446             __ CuleD(0, lhs, rhs);
5447           }
5448           __ LoadConst32(dst, 1);
5449           __ Movf(dst, ZERO, 0);
5450           break;
5451         case kCondGT:
5452           if (gt_bias) {
5453             __ CultD(0, rhs, lhs);
5454           } else {
5455             __ ColtD(0, rhs, lhs);
5456           }
5457           __ LoadConst32(dst, 1);
5458           __ Movf(dst, ZERO, 0);
5459           break;
5460         case kCondGE:
5461           if (gt_bias) {
5462             __ CuleD(0, rhs, lhs);
5463           } else {
5464             __ ColeD(0, rhs, lhs);
5465           }
5466           __ LoadConst32(dst, 1);
5467           __ Movf(dst, ZERO, 0);
5468           break;
5469         default:
5470           LOG(FATAL) << "Unexpected non-floating-point condition " << cond;
5471           UNREACHABLE();
5472       }
5473     }
5474   }
5475 }
5476 
MaterializeFpCompareR2(IfCondition cond,bool gt_bias,DataType::Type type,LocationSummary * input_locations,int cc)5477 bool InstructionCodeGeneratorMIPS::MaterializeFpCompareR2(IfCondition cond,
5478                                                           bool gt_bias,
5479                                                           DataType::Type type,
5480                                                           LocationSummary* input_locations,
5481                                                           int cc) {
5482   FRegister lhs = input_locations->InAt(0).AsFpuRegister<FRegister>();
5483   FRegister rhs = input_locations->InAt(1).AsFpuRegister<FRegister>();
5484   CHECK(!codegen_->GetInstructionSetFeatures().IsR6());
5485   if (type == DataType::Type::kFloat32) {
5486     switch (cond) {
5487       case kCondEQ:
5488         __ CeqS(cc, lhs, rhs);
5489         return false;
5490       case kCondNE:
5491         __ CeqS(cc, lhs, rhs);
5492         return true;
5493       case kCondLT:
5494         if (gt_bias) {
5495           __ ColtS(cc, lhs, rhs);
5496         } else {
5497           __ CultS(cc, lhs, rhs);
5498         }
5499         return false;
5500       case kCondLE:
5501         if (gt_bias) {
5502           __ ColeS(cc, lhs, rhs);
5503         } else {
5504           __ CuleS(cc, lhs, rhs);
5505         }
5506         return false;
5507       case kCondGT:
5508         if (gt_bias) {
5509           __ CultS(cc, rhs, lhs);
5510         } else {
5511           __ ColtS(cc, rhs, lhs);
5512         }
5513         return false;
5514       case kCondGE:
5515         if (gt_bias) {
5516           __ CuleS(cc, rhs, lhs);
5517         } else {
5518           __ ColeS(cc, rhs, lhs);
5519         }
5520         return false;
5521       default:
5522         LOG(FATAL) << "Unexpected non-floating-point condition";
5523         UNREACHABLE();
5524     }
5525   } else {
5526     DCHECK_EQ(type, DataType::Type::kFloat64);
5527     switch (cond) {
5528       case kCondEQ:
5529         __ CeqD(cc, lhs, rhs);
5530         return false;
5531       case kCondNE:
5532         __ CeqD(cc, lhs, rhs);
5533         return true;
5534       case kCondLT:
5535         if (gt_bias) {
5536           __ ColtD(cc, lhs, rhs);
5537         } else {
5538           __ CultD(cc, lhs, rhs);
5539         }
5540         return false;
5541       case kCondLE:
5542         if (gt_bias) {
5543           __ ColeD(cc, lhs, rhs);
5544         } else {
5545           __ CuleD(cc, lhs, rhs);
5546         }
5547         return false;
5548       case kCondGT:
5549         if (gt_bias) {
5550           __ CultD(cc, rhs, lhs);
5551         } else {
5552           __ ColtD(cc, rhs, lhs);
5553         }
5554         return false;
5555       case kCondGE:
5556         if (gt_bias) {
5557           __ CuleD(cc, rhs, lhs);
5558         } else {
5559           __ ColeD(cc, rhs, lhs);
5560         }
5561         return false;
5562       default:
5563         LOG(FATAL) << "Unexpected non-floating-point condition";
5564         UNREACHABLE();
5565     }
5566   }
5567 }
5568 
MaterializeFpCompareR6(IfCondition cond,bool gt_bias,DataType::Type type,LocationSummary * input_locations,FRegister dst)5569 bool InstructionCodeGeneratorMIPS::MaterializeFpCompareR6(IfCondition cond,
5570                                                           bool gt_bias,
5571                                                           DataType::Type type,
5572                                                           LocationSummary* input_locations,
5573                                                           FRegister dst) {
5574   FRegister lhs = input_locations->InAt(0).AsFpuRegister<FRegister>();
5575   FRegister rhs = input_locations->InAt(1).AsFpuRegister<FRegister>();
5576   CHECK(codegen_->GetInstructionSetFeatures().IsR6());
5577   if (type == DataType::Type::kFloat32) {
5578     switch (cond) {
5579       case kCondEQ:
5580         __ CmpEqS(dst, lhs, rhs);
5581         return false;
5582       case kCondNE:
5583         __ CmpEqS(dst, lhs, rhs);
5584         return true;
5585       case kCondLT:
5586         if (gt_bias) {
5587           __ CmpLtS(dst, lhs, rhs);
5588         } else {
5589           __ CmpUltS(dst, lhs, rhs);
5590         }
5591         return false;
5592       case kCondLE:
5593         if (gt_bias) {
5594           __ CmpLeS(dst, lhs, rhs);
5595         } else {
5596           __ CmpUleS(dst, lhs, rhs);
5597         }
5598         return false;
5599       case kCondGT:
5600         if (gt_bias) {
5601           __ CmpUltS(dst, rhs, lhs);
5602         } else {
5603           __ CmpLtS(dst, rhs, lhs);
5604         }
5605         return false;
5606       case kCondGE:
5607         if (gt_bias) {
5608           __ CmpUleS(dst, rhs, lhs);
5609         } else {
5610           __ CmpLeS(dst, rhs, lhs);
5611         }
5612         return false;
5613       default:
5614         LOG(FATAL) << "Unexpected non-floating-point condition";
5615         UNREACHABLE();
5616     }
5617   } else {
5618     DCHECK_EQ(type, DataType::Type::kFloat64);
5619     switch (cond) {
5620       case kCondEQ:
5621         __ CmpEqD(dst, lhs, rhs);
5622         return false;
5623       case kCondNE:
5624         __ CmpEqD(dst, lhs, rhs);
5625         return true;
5626       case kCondLT:
5627         if (gt_bias) {
5628           __ CmpLtD(dst, lhs, rhs);
5629         } else {
5630           __ CmpUltD(dst, lhs, rhs);
5631         }
5632         return false;
5633       case kCondLE:
5634         if (gt_bias) {
5635           __ CmpLeD(dst, lhs, rhs);
5636         } else {
5637           __ CmpUleD(dst, lhs, rhs);
5638         }
5639         return false;
5640       case kCondGT:
5641         if (gt_bias) {
5642           __ CmpUltD(dst, rhs, lhs);
5643         } else {
5644           __ CmpLtD(dst, rhs, lhs);
5645         }
5646         return false;
5647       case kCondGE:
5648         if (gt_bias) {
5649           __ CmpUleD(dst, rhs, lhs);
5650         } else {
5651           __ CmpLeD(dst, rhs, lhs);
5652         }
5653         return false;
5654       default:
5655         LOG(FATAL) << "Unexpected non-floating-point condition";
5656         UNREACHABLE();
5657     }
5658   }
5659 }
5660 
GenerateFpCompareAndBranch(IfCondition cond,bool gt_bias,DataType::Type type,LocationSummary * locations,MipsLabel * label)5661 void InstructionCodeGeneratorMIPS::GenerateFpCompareAndBranch(IfCondition cond,
5662                                                               bool gt_bias,
5663                                                               DataType::Type type,
5664                                                               LocationSummary* locations,
5665                                                               MipsLabel* label) {
5666   FRegister lhs = locations->InAt(0).AsFpuRegister<FRegister>();
5667   FRegister rhs = locations->InAt(1).AsFpuRegister<FRegister>();
5668   bool isR6 = codegen_->GetInstructionSetFeatures().IsR6();
5669   if (type == DataType::Type::kFloat32) {
5670     if (isR6) {
5671       switch (cond) {
5672         case kCondEQ:
5673           __ CmpEqS(FTMP, lhs, rhs);
5674           __ Bc1nez(FTMP, label);
5675           break;
5676         case kCondNE:
5677           __ CmpEqS(FTMP, lhs, rhs);
5678           __ Bc1eqz(FTMP, label);
5679           break;
5680         case kCondLT:
5681           if (gt_bias) {
5682             __ CmpLtS(FTMP, lhs, rhs);
5683           } else {
5684             __ CmpUltS(FTMP, lhs, rhs);
5685           }
5686           __ Bc1nez(FTMP, label);
5687           break;
5688         case kCondLE:
5689           if (gt_bias) {
5690             __ CmpLeS(FTMP, lhs, rhs);
5691           } else {
5692             __ CmpUleS(FTMP, lhs, rhs);
5693           }
5694           __ Bc1nez(FTMP, label);
5695           break;
5696         case kCondGT:
5697           if (gt_bias) {
5698             __ CmpUltS(FTMP, rhs, lhs);
5699           } else {
5700             __ CmpLtS(FTMP, rhs, lhs);
5701           }
5702           __ Bc1nez(FTMP, label);
5703           break;
5704         case kCondGE:
5705           if (gt_bias) {
5706             __ CmpUleS(FTMP, rhs, lhs);
5707           } else {
5708             __ CmpLeS(FTMP, rhs, lhs);
5709           }
5710           __ Bc1nez(FTMP, label);
5711           break;
5712         default:
5713           LOG(FATAL) << "Unexpected non-floating-point condition";
5714           UNREACHABLE();
5715       }
5716     } else {
5717       switch (cond) {
5718         case kCondEQ:
5719           __ CeqS(0, lhs, rhs);
5720           __ Bc1t(0, label);
5721           break;
5722         case kCondNE:
5723           __ CeqS(0, lhs, rhs);
5724           __ Bc1f(0, label);
5725           break;
5726         case kCondLT:
5727           if (gt_bias) {
5728             __ ColtS(0, lhs, rhs);
5729           } else {
5730             __ CultS(0, lhs, rhs);
5731           }
5732           __ Bc1t(0, label);
5733           break;
5734         case kCondLE:
5735           if (gt_bias) {
5736             __ ColeS(0, lhs, rhs);
5737           } else {
5738             __ CuleS(0, lhs, rhs);
5739           }
5740           __ Bc1t(0, label);
5741           break;
5742         case kCondGT:
5743           if (gt_bias) {
5744             __ CultS(0, rhs, lhs);
5745           } else {
5746             __ ColtS(0, rhs, lhs);
5747           }
5748           __ Bc1t(0, label);
5749           break;
5750         case kCondGE:
5751           if (gt_bias) {
5752             __ CuleS(0, rhs, lhs);
5753           } else {
5754             __ ColeS(0, rhs, lhs);
5755           }
5756           __ Bc1t(0, label);
5757           break;
5758         default:
5759           LOG(FATAL) << "Unexpected non-floating-point condition";
5760           UNREACHABLE();
5761       }
5762     }
5763   } else {
5764     DCHECK_EQ(type, DataType::Type::kFloat64);
5765     if (isR6) {
5766       switch (cond) {
5767         case kCondEQ:
5768           __ CmpEqD(FTMP, lhs, rhs);
5769           __ Bc1nez(FTMP, label);
5770           break;
5771         case kCondNE:
5772           __ CmpEqD(FTMP, lhs, rhs);
5773           __ Bc1eqz(FTMP, label);
5774           break;
5775         case kCondLT:
5776           if (gt_bias) {
5777             __ CmpLtD(FTMP, lhs, rhs);
5778           } else {
5779             __ CmpUltD(FTMP, lhs, rhs);
5780           }
5781           __ Bc1nez(FTMP, label);
5782           break;
5783         case kCondLE:
5784           if (gt_bias) {
5785             __ CmpLeD(FTMP, lhs, rhs);
5786           } else {
5787             __ CmpUleD(FTMP, lhs, rhs);
5788           }
5789           __ Bc1nez(FTMP, label);
5790           break;
5791         case kCondGT:
5792           if (gt_bias) {
5793             __ CmpUltD(FTMP, rhs, lhs);
5794           } else {
5795             __ CmpLtD(FTMP, rhs, lhs);
5796           }
5797           __ Bc1nez(FTMP, label);
5798           break;
5799         case kCondGE:
5800           if (gt_bias) {
5801             __ CmpUleD(FTMP, rhs, lhs);
5802           } else {
5803             __ CmpLeD(FTMP, rhs, lhs);
5804           }
5805           __ Bc1nez(FTMP, label);
5806           break;
5807         default:
5808           LOG(FATAL) << "Unexpected non-floating-point condition";
5809           UNREACHABLE();
5810       }
5811     } else {
5812       switch (cond) {
5813         case kCondEQ:
5814           __ CeqD(0, lhs, rhs);
5815           __ Bc1t(0, label);
5816           break;
5817         case kCondNE:
5818           __ CeqD(0, lhs, rhs);
5819           __ Bc1f(0, label);
5820           break;
5821         case kCondLT:
5822           if (gt_bias) {
5823             __ ColtD(0, lhs, rhs);
5824           } else {
5825             __ CultD(0, lhs, rhs);
5826           }
5827           __ Bc1t(0, label);
5828           break;
5829         case kCondLE:
5830           if (gt_bias) {
5831             __ ColeD(0, lhs, rhs);
5832           } else {
5833             __ CuleD(0, lhs, rhs);
5834           }
5835           __ Bc1t(0, label);
5836           break;
5837         case kCondGT:
5838           if (gt_bias) {
5839             __ CultD(0, rhs, lhs);
5840           } else {
5841             __ ColtD(0, rhs, lhs);
5842           }
5843           __ Bc1t(0, label);
5844           break;
5845         case kCondGE:
5846           if (gt_bias) {
5847             __ CuleD(0, rhs, lhs);
5848           } else {
5849             __ ColeD(0, rhs, lhs);
5850           }
5851           __ Bc1t(0, label);
5852           break;
5853         default:
5854           LOG(FATAL) << "Unexpected non-floating-point condition";
5855           UNREACHABLE();
5856       }
5857     }
5858   }
5859 }
5860 
GenerateTestAndBranch(HInstruction * instruction,size_t condition_input_index,MipsLabel * true_target,MipsLabel * false_target)5861 void InstructionCodeGeneratorMIPS::GenerateTestAndBranch(HInstruction* instruction,
5862                                                          size_t condition_input_index,
5863                                                          MipsLabel* true_target,
5864                                                          MipsLabel* false_target) {
5865   HInstruction* cond = instruction->InputAt(condition_input_index);
5866 
5867   if (true_target == nullptr && false_target == nullptr) {
5868     // Nothing to do. The code always falls through.
5869     return;
5870   } else if (cond->IsIntConstant()) {
5871     // Constant condition, statically compared against "true" (integer value 1).
5872     if (cond->AsIntConstant()->IsTrue()) {
5873       if (true_target != nullptr) {
5874         __ B(true_target);
5875       }
5876     } else {
5877       DCHECK(cond->AsIntConstant()->IsFalse()) << cond->AsIntConstant()->GetValue();
5878       if (false_target != nullptr) {
5879         __ B(false_target);
5880       }
5881     }
5882     return;
5883   }
5884 
5885   // The following code generates these patterns:
5886   //  (1) true_target == nullptr && false_target != nullptr
5887   //        - opposite condition true => branch to false_target
5888   //  (2) true_target != nullptr && false_target == nullptr
5889   //        - condition true => branch to true_target
5890   //  (3) true_target != nullptr && false_target != nullptr
5891   //        - condition true => branch to true_target
5892   //        - branch to false_target
5893   if (IsBooleanValueOrMaterializedCondition(cond)) {
5894     // The condition instruction has been materialized, compare the output to 0.
5895     Location cond_val = instruction->GetLocations()->InAt(condition_input_index);
5896     DCHECK(cond_val.IsRegister());
5897     if (true_target == nullptr) {
5898       __ Beqz(cond_val.AsRegister<Register>(), false_target);
5899     } else {
5900       __ Bnez(cond_val.AsRegister<Register>(), true_target);
5901     }
5902   } else {
5903     // The condition instruction has not been materialized, use its inputs as
5904     // the comparison and its condition as the branch condition.
5905     HCondition* condition = cond->AsCondition();
5906     DataType::Type type = condition->InputAt(0)->GetType();
5907     LocationSummary* locations = cond->GetLocations();
5908     IfCondition if_cond = condition->GetCondition();
5909     MipsLabel* branch_target = true_target;
5910 
5911     if (true_target == nullptr) {
5912       if_cond = condition->GetOppositeCondition();
5913       branch_target = false_target;
5914     }
5915 
5916     switch (type) {
5917       default:
5918         GenerateIntCompareAndBranch(if_cond, locations, branch_target);
5919         break;
5920       case DataType::Type::kInt64:
5921         GenerateLongCompareAndBranch(if_cond, locations, branch_target);
5922         break;
5923       case DataType::Type::kFloat32:
5924       case DataType::Type::kFloat64:
5925         GenerateFpCompareAndBranch(if_cond, condition->IsGtBias(), type, locations, branch_target);
5926         break;
5927     }
5928   }
5929 
5930   // If neither branch falls through (case 3), the conditional branch to `true_target`
5931   // was already emitted (case 2) and we need to emit a jump to `false_target`.
5932   if (true_target != nullptr && false_target != nullptr) {
5933     __ B(false_target);
5934   }
5935 }
5936 
VisitIf(HIf * if_instr)5937 void LocationsBuilderMIPS::VisitIf(HIf* if_instr) {
5938   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(if_instr);
5939   if (IsBooleanValueOrMaterializedCondition(if_instr->InputAt(0))) {
5940     locations->SetInAt(0, Location::RequiresRegister());
5941   }
5942 }
5943 
VisitIf(HIf * if_instr)5944 void InstructionCodeGeneratorMIPS::VisitIf(HIf* if_instr) {
5945   HBasicBlock* true_successor = if_instr->IfTrueSuccessor();
5946   HBasicBlock* false_successor = if_instr->IfFalseSuccessor();
5947   MipsLabel* true_target = codegen_->GoesToNextBlock(if_instr->GetBlock(), true_successor) ?
5948       nullptr : codegen_->GetLabelOf(true_successor);
5949   MipsLabel* false_target = codegen_->GoesToNextBlock(if_instr->GetBlock(), false_successor) ?
5950       nullptr : codegen_->GetLabelOf(false_successor);
5951   GenerateTestAndBranch(if_instr, /* condition_input_index= */ 0, true_target, false_target);
5952 }
5953 
VisitDeoptimize(HDeoptimize * deoptimize)5954 void LocationsBuilderMIPS::VisitDeoptimize(HDeoptimize* deoptimize) {
5955   LocationSummary* locations = new (GetGraph()->GetAllocator())
5956       LocationSummary(deoptimize, LocationSummary::kCallOnSlowPath);
5957   InvokeRuntimeCallingConvention calling_convention;
5958   RegisterSet caller_saves = RegisterSet::Empty();
5959   caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
5960   locations->SetCustomSlowPathCallerSaves(caller_saves);
5961   if (IsBooleanValueOrMaterializedCondition(deoptimize->InputAt(0))) {
5962     locations->SetInAt(0, Location::RequiresRegister());
5963   }
5964 }
5965 
VisitDeoptimize(HDeoptimize * deoptimize)5966 void InstructionCodeGeneratorMIPS::VisitDeoptimize(HDeoptimize* deoptimize) {
5967   SlowPathCodeMIPS* slow_path =
5968       deopt_slow_paths_.NewSlowPath<DeoptimizationSlowPathMIPS>(deoptimize);
5969   GenerateTestAndBranch(deoptimize,
5970                         /* condition_input_index= */ 0,
5971                         slow_path->GetEntryLabel(),
5972                         /* false_target= */ nullptr);
5973 }
5974 
5975 // This function returns true if a conditional move can be generated for HSelect.
5976 // Otherwise it returns false and HSelect must be implemented in terms of conditonal
5977 // branches and regular moves.
5978 //
5979 // If `locations_to_set` isn't nullptr, its inputs and outputs are set for HSelect.
5980 //
5981 // While determining feasibility of a conditional move and setting inputs/outputs
5982 // are two distinct tasks, this function does both because they share quite a bit
5983 // of common logic.
CanMoveConditionally(HSelect * select,bool is_r6,LocationSummary * locations_to_set)5984 static bool CanMoveConditionally(HSelect* select, bool is_r6, LocationSummary* locations_to_set) {
5985   bool materialized = IsBooleanValueOrMaterializedCondition(select->GetCondition());
5986   HInstruction* cond = select->InputAt(/* i= */ 2);
5987   HCondition* condition = cond->AsCondition();
5988 
5989   DataType::Type cond_type =
5990       materialized ? DataType::Type::kInt32 : condition->InputAt(0)->GetType();
5991   DataType::Type dst_type = select->GetType();
5992 
5993   HConstant* cst_true_value = select->GetTrueValue()->AsConstant();
5994   HConstant* cst_false_value = select->GetFalseValue()->AsConstant();
5995   bool is_true_value_zero_constant =
5996       (cst_true_value != nullptr && cst_true_value->IsZeroBitPattern());
5997   bool is_false_value_zero_constant =
5998       (cst_false_value != nullptr && cst_false_value->IsZeroBitPattern());
5999 
6000   bool can_move_conditionally = false;
6001   bool use_const_for_false_in = false;
6002   bool use_const_for_true_in = false;
6003 
6004   if (!cond->IsConstant()) {
6005     switch (cond_type) {
6006       default:
6007         switch (dst_type) {
6008           default:
6009             // Moving int on int condition.
6010             if (is_r6) {
6011               if (is_true_value_zero_constant) {
6012                 // seleqz out_reg, false_reg, cond_reg
6013                 can_move_conditionally = true;
6014                 use_const_for_true_in = true;
6015               } else if (is_false_value_zero_constant) {
6016                 // selnez out_reg, true_reg, cond_reg
6017                 can_move_conditionally = true;
6018                 use_const_for_false_in = true;
6019               } else if (materialized) {
6020                 // Not materializing unmaterialized int conditions
6021                 // to keep the instruction count low.
6022                 // selnez AT, true_reg, cond_reg
6023                 // seleqz TMP, false_reg, cond_reg
6024                 // or out_reg, AT, TMP
6025                 can_move_conditionally = true;
6026               }
6027             } else {
6028               // movn out_reg, true_reg/ZERO, cond_reg
6029               can_move_conditionally = true;
6030               use_const_for_true_in = is_true_value_zero_constant;
6031             }
6032             break;
6033           case DataType::Type::kInt64:
6034             // Moving long on int condition.
6035             if (is_r6) {
6036               if (is_true_value_zero_constant) {
6037                 // seleqz out_reg_lo, false_reg_lo, cond_reg
6038                 // seleqz out_reg_hi, false_reg_hi, cond_reg
6039                 can_move_conditionally = true;
6040                 use_const_for_true_in = true;
6041               } else if (is_false_value_zero_constant) {
6042                 // selnez out_reg_lo, true_reg_lo, cond_reg
6043                 // selnez out_reg_hi, true_reg_hi, cond_reg
6044                 can_move_conditionally = true;
6045                 use_const_for_false_in = true;
6046               }
6047               // Other long conditional moves would generate 6+ instructions,
6048               // which is too many.
6049             } else {
6050               // movn out_reg_lo, true_reg_lo/ZERO, cond_reg
6051               // movn out_reg_hi, true_reg_hi/ZERO, cond_reg
6052               can_move_conditionally = true;
6053               use_const_for_true_in = is_true_value_zero_constant;
6054             }
6055             break;
6056           case DataType::Type::kFloat32:
6057           case DataType::Type::kFloat64:
6058             // Moving float/double on int condition.
6059             if (is_r6) {
6060               if (materialized) {
6061                 // Not materializing unmaterialized int conditions
6062                 // to keep the instruction count low.
6063                 can_move_conditionally = true;
6064                 if (is_true_value_zero_constant) {
6065                   // sltu TMP, ZERO, cond_reg
6066                   // mtc1 TMP, temp_cond_reg
6067                   // seleqz.fmt out_reg, false_reg, temp_cond_reg
6068                   use_const_for_true_in = true;
6069                 } else if (is_false_value_zero_constant) {
6070                   // sltu TMP, ZERO, cond_reg
6071                   // mtc1 TMP, temp_cond_reg
6072                   // selnez.fmt out_reg, true_reg, temp_cond_reg
6073                   use_const_for_false_in = true;
6074                 } else {
6075                   // sltu TMP, ZERO, cond_reg
6076                   // mtc1 TMP, temp_cond_reg
6077                   // sel.fmt temp_cond_reg, false_reg, true_reg
6078                   // mov.fmt out_reg, temp_cond_reg
6079                 }
6080               }
6081             } else {
6082               // movn.fmt out_reg, true_reg, cond_reg
6083               can_move_conditionally = true;
6084             }
6085             break;
6086         }
6087         break;
6088       case DataType::Type::kInt64:
6089         // We don't materialize long comparison now
6090         // and use conditional branches instead.
6091         break;
6092       case DataType::Type::kFloat32:
6093       case DataType::Type::kFloat64:
6094         switch (dst_type) {
6095           default:
6096             // Moving int on float/double condition.
6097             if (is_r6) {
6098               if (is_true_value_zero_constant) {
6099                 // mfc1 TMP, temp_cond_reg
6100                 // seleqz out_reg, false_reg, TMP
6101                 can_move_conditionally = true;
6102                 use_const_for_true_in = true;
6103               } else if (is_false_value_zero_constant) {
6104                 // mfc1 TMP, temp_cond_reg
6105                 // selnez out_reg, true_reg, TMP
6106                 can_move_conditionally = true;
6107                 use_const_for_false_in = true;
6108               } else {
6109                 // mfc1 TMP, temp_cond_reg
6110                 // selnez AT, true_reg, TMP
6111                 // seleqz TMP, false_reg, TMP
6112                 // or out_reg, AT, TMP
6113                 can_move_conditionally = true;
6114               }
6115             } else {
6116               // movt out_reg, true_reg/ZERO, cc
6117               can_move_conditionally = true;
6118               use_const_for_true_in = is_true_value_zero_constant;
6119             }
6120             break;
6121           case DataType::Type::kInt64:
6122             // Moving long on float/double condition.
6123             if (is_r6) {
6124               if (is_true_value_zero_constant) {
6125                 // mfc1 TMP, temp_cond_reg
6126                 // seleqz out_reg_lo, false_reg_lo, TMP
6127                 // seleqz out_reg_hi, false_reg_hi, TMP
6128                 can_move_conditionally = true;
6129                 use_const_for_true_in = true;
6130               } else if (is_false_value_zero_constant) {
6131                 // mfc1 TMP, temp_cond_reg
6132                 // selnez out_reg_lo, true_reg_lo, TMP
6133                 // selnez out_reg_hi, true_reg_hi, TMP
6134                 can_move_conditionally = true;
6135                 use_const_for_false_in = true;
6136               }
6137               // Other long conditional moves would generate 6+ instructions,
6138               // which is too many.
6139             } else {
6140               // movt out_reg_lo, true_reg_lo/ZERO, cc
6141               // movt out_reg_hi, true_reg_hi/ZERO, cc
6142               can_move_conditionally = true;
6143               use_const_for_true_in = is_true_value_zero_constant;
6144             }
6145             break;
6146           case DataType::Type::kFloat32:
6147           case DataType::Type::kFloat64:
6148             // Moving float/double on float/double condition.
6149             if (is_r6) {
6150               can_move_conditionally = true;
6151               if (is_true_value_zero_constant) {
6152                 // seleqz.fmt out_reg, false_reg, temp_cond_reg
6153                 use_const_for_true_in = true;
6154               } else if (is_false_value_zero_constant) {
6155                 // selnez.fmt out_reg, true_reg, temp_cond_reg
6156                 use_const_for_false_in = true;
6157               } else {
6158                 // sel.fmt temp_cond_reg, false_reg, true_reg
6159                 // mov.fmt out_reg, temp_cond_reg
6160               }
6161             } else {
6162               // movt.fmt out_reg, true_reg, cc
6163               can_move_conditionally = true;
6164             }
6165             break;
6166         }
6167         break;
6168     }
6169   }
6170 
6171   if (can_move_conditionally) {
6172     DCHECK(!use_const_for_false_in || !use_const_for_true_in);
6173   } else {
6174     DCHECK(!use_const_for_false_in);
6175     DCHECK(!use_const_for_true_in);
6176   }
6177 
6178   if (locations_to_set != nullptr) {
6179     if (use_const_for_false_in) {
6180       locations_to_set->SetInAt(0, Location::ConstantLocation(cst_false_value));
6181     } else {
6182       locations_to_set->SetInAt(0,
6183                                 DataType::IsFloatingPointType(dst_type)
6184                                     ? Location::RequiresFpuRegister()
6185                                     : Location::RequiresRegister());
6186     }
6187     if (use_const_for_true_in) {
6188       locations_to_set->SetInAt(1, Location::ConstantLocation(cst_true_value));
6189     } else {
6190       locations_to_set->SetInAt(1,
6191                                 DataType::IsFloatingPointType(dst_type)
6192                                     ? Location::RequiresFpuRegister()
6193                                     : Location::RequiresRegister());
6194     }
6195     if (materialized) {
6196       locations_to_set->SetInAt(2, Location::RequiresRegister());
6197     }
6198     // On R6 we don't require the output to be the same as the
6199     // first input for conditional moves unlike on R2.
6200     bool is_out_same_as_first_in = !can_move_conditionally || !is_r6;
6201     if (is_out_same_as_first_in) {
6202       locations_to_set->SetOut(Location::SameAsFirstInput());
6203     } else {
6204       locations_to_set->SetOut(DataType::IsFloatingPointType(dst_type)
6205                                    ? Location::RequiresFpuRegister()
6206                                    : Location::RequiresRegister());
6207     }
6208   }
6209 
6210   return can_move_conditionally;
6211 }
6212 
GenConditionalMoveR2(HSelect * select)6213 void InstructionCodeGeneratorMIPS::GenConditionalMoveR2(HSelect* select) {
6214   LocationSummary* locations = select->GetLocations();
6215   Location dst = locations->Out();
6216   Location src = locations->InAt(1);
6217   Register src_reg = ZERO;
6218   Register src_reg_high = ZERO;
6219   HInstruction* cond = select->InputAt(/* i= */ 2);
6220   Register cond_reg = TMP;
6221   int cond_cc = 0;
6222   DataType::Type cond_type = DataType::Type::kInt32;
6223   bool cond_inverted = false;
6224   DataType::Type dst_type = select->GetType();
6225 
6226   if (IsBooleanValueOrMaterializedCondition(cond)) {
6227     cond_reg = locations->InAt(/* at= */ 2).AsRegister<Register>();
6228   } else {
6229     HCondition* condition = cond->AsCondition();
6230     LocationSummary* cond_locations = cond->GetLocations();
6231     IfCondition if_cond = condition->GetCondition();
6232     cond_type = condition->InputAt(0)->GetType();
6233     switch (cond_type) {
6234       default:
6235         DCHECK_NE(cond_type, DataType::Type::kInt64);
6236         cond_inverted = MaterializeIntCompare(if_cond, cond_locations, cond_reg);
6237         break;
6238       case DataType::Type::kFloat32:
6239       case DataType::Type::kFloat64:
6240         cond_inverted = MaterializeFpCompareR2(if_cond,
6241                                                condition->IsGtBias(),
6242                                                cond_type,
6243                                                cond_locations,
6244                                                cond_cc);
6245         break;
6246     }
6247   }
6248 
6249   DCHECK(dst.Equals(locations->InAt(0)));
6250   if (src.IsRegister()) {
6251     src_reg = src.AsRegister<Register>();
6252   } else if (src.IsRegisterPair()) {
6253     src_reg = src.AsRegisterPairLow<Register>();
6254     src_reg_high = src.AsRegisterPairHigh<Register>();
6255   } else if (src.IsConstant()) {
6256     DCHECK(src.GetConstant()->IsZeroBitPattern());
6257   }
6258 
6259   switch (cond_type) {
6260     default:
6261       switch (dst_type) {
6262         default:
6263           if (cond_inverted) {
6264             __ Movz(dst.AsRegister<Register>(), src_reg, cond_reg);
6265           } else {
6266             __ Movn(dst.AsRegister<Register>(), src_reg, cond_reg);
6267           }
6268           break;
6269         case DataType::Type::kInt64:
6270           if (cond_inverted) {
6271             __ Movz(dst.AsRegisterPairLow<Register>(), src_reg, cond_reg);
6272             __ Movz(dst.AsRegisterPairHigh<Register>(), src_reg_high, cond_reg);
6273           } else {
6274             __ Movn(dst.AsRegisterPairLow<Register>(), src_reg, cond_reg);
6275             __ Movn(dst.AsRegisterPairHigh<Register>(), src_reg_high, cond_reg);
6276           }
6277           break;
6278         case DataType::Type::kFloat32:
6279           if (cond_inverted) {
6280             __ MovzS(dst.AsFpuRegister<FRegister>(), src.AsFpuRegister<FRegister>(), cond_reg);
6281           } else {
6282             __ MovnS(dst.AsFpuRegister<FRegister>(), src.AsFpuRegister<FRegister>(), cond_reg);
6283           }
6284           break;
6285         case DataType::Type::kFloat64:
6286           if (cond_inverted) {
6287             __ MovzD(dst.AsFpuRegister<FRegister>(), src.AsFpuRegister<FRegister>(), cond_reg);
6288           } else {
6289             __ MovnD(dst.AsFpuRegister<FRegister>(), src.AsFpuRegister<FRegister>(), cond_reg);
6290           }
6291           break;
6292       }
6293       break;
6294     case DataType::Type::kInt64:
6295       LOG(FATAL) << "Unreachable";
6296       UNREACHABLE();
6297     case DataType::Type::kFloat32:
6298     case DataType::Type::kFloat64:
6299       switch (dst_type) {
6300         default:
6301           if (cond_inverted) {
6302             __ Movf(dst.AsRegister<Register>(), src_reg, cond_cc);
6303           } else {
6304             __ Movt(dst.AsRegister<Register>(), src_reg, cond_cc);
6305           }
6306           break;
6307         case DataType::Type::kInt64:
6308           if (cond_inverted) {
6309             __ Movf(dst.AsRegisterPairLow<Register>(), src_reg, cond_cc);
6310             __ Movf(dst.AsRegisterPairHigh<Register>(), src_reg_high, cond_cc);
6311           } else {
6312             __ Movt(dst.AsRegisterPairLow<Register>(), src_reg, cond_cc);
6313             __ Movt(dst.AsRegisterPairHigh<Register>(), src_reg_high, cond_cc);
6314           }
6315           break;
6316         case DataType::Type::kFloat32:
6317           if (cond_inverted) {
6318             __ MovfS(dst.AsFpuRegister<FRegister>(), src.AsFpuRegister<FRegister>(), cond_cc);
6319           } else {
6320             __ MovtS(dst.AsFpuRegister<FRegister>(), src.AsFpuRegister<FRegister>(), cond_cc);
6321           }
6322           break;
6323         case DataType::Type::kFloat64:
6324           if (cond_inverted) {
6325             __ MovfD(dst.AsFpuRegister<FRegister>(), src.AsFpuRegister<FRegister>(), cond_cc);
6326           } else {
6327             __ MovtD(dst.AsFpuRegister<FRegister>(), src.AsFpuRegister<FRegister>(), cond_cc);
6328           }
6329           break;
6330       }
6331       break;
6332   }
6333 }
6334 
GenConditionalMoveR6(HSelect * select)6335 void InstructionCodeGeneratorMIPS::GenConditionalMoveR6(HSelect* select) {
6336   LocationSummary* locations = select->GetLocations();
6337   Location dst = locations->Out();
6338   Location false_src = locations->InAt(0);
6339   Location true_src = locations->InAt(1);
6340   HInstruction* cond = select->InputAt(/* i= */ 2);
6341   Register cond_reg = TMP;
6342   FRegister fcond_reg = FTMP;
6343   DataType::Type cond_type = DataType::Type::kInt32;
6344   bool cond_inverted = false;
6345   DataType::Type dst_type = select->GetType();
6346 
6347   if (IsBooleanValueOrMaterializedCondition(cond)) {
6348     cond_reg = locations->InAt(/* at= */ 2).AsRegister<Register>();
6349   } else {
6350     HCondition* condition = cond->AsCondition();
6351     LocationSummary* cond_locations = cond->GetLocations();
6352     IfCondition if_cond = condition->GetCondition();
6353     cond_type = condition->InputAt(0)->GetType();
6354     switch (cond_type) {
6355       default:
6356         DCHECK_NE(cond_type, DataType::Type::kInt64);
6357         cond_inverted = MaterializeIntCompare(if_cond, cond_locations, cond_reg);
6358         break;
6359       case DataType::Type::kFloat32:
6360       case DataType::Type::kFloat64:
6361         cond_inverted = MaterializeFpCompareR6(if_cond,
6362                                                condition->IsGtBias(),
6363                                                cond_type,
6364                                                cond_locations,
6365                                                fcond_reg);
6366         break;
6367     }
6368   }
6369 
6370   if (true_src.IsConstant()) {
6371     DCHECK(true_src.GetConstant()->IsZeroBitPattern());
6372   }
6373   if (false_src.IsConstant()) {
6374     DCHECK(false_src.GetConstant()->IsZeroBitPattern());
6375   }
6376 
6377   switch (dst_type) {
6378     default:
6379       if (DataType::IsFloatingPointType(cond_type)) {
6380         __ Mfc1(cond_reg, fcond_reg);
6381       }
6382       if (true_src.IsConstant()) {
6383         if (cond_inverted) {
6384           __ Selnez(dst.AsRegister<Register>(), false_src.AsRegister<Register>(), cond_reg);
6385         } else {
6386           __ Seleqz(dst.AsRegister<Register>(), false_src.AsRegister<Register>(), cond_reg);
6387         }
6388       } else if (false_src.IsConstant()) {
6389         if (cond_inverted) {
6390           __ Seleqz(dst.AsRegister<Register>(), true_src.AsRegister<Register>(), cond_reg);
6391         } else {
6392           __ Selnez(dst.AsRegister<Register>(), true_src.AsRegister<Register>(), cond_reg);
6393         }
6394       } else {
6395         DCHECK_NE(cond_reg, AT);
6396         if (cond_inverted) {
6397           __ Seleqz(AT, true_src.AsRegister<Register>(), cond_reg);
6398           __ Selnez(TMP, false_src.AsRegister<Register>(), cond_reg);
6399         } else {
6400           __ Selnez(AT, true_src.AsRegister<Register>(), cond_reg);
6401           __ Seleqz(TMP, false_src.AsRegister<Register>(), cond_reg);
6402         }
6403         __ Or(dst.AsRegister<Register>(), AT, TMP);
6404       }
6405       break;
6406     case DataType::Type::kInt64: {
6407       if (DataType::IsFloatingPointType(cond_type)) {
6408         __ Mfc1(cond_reg, fcond_reg);
6409       }
6410       Register dst_lo = dst.AsRegisterPairLow<Register>();
6411       Register dst_hi = dst.AsRegisterPairHigh<Register>();
6412       if (true_src.IsConstant()) {
6413         Register src_lo = false_src.AsRegisterPairLow<Register>();
6414         Register src_hi = false_src.AsRegisterPairHigh<Register>();
6415         if (cond_inverted) {
6416           __ Selnez(dst_lo, src_lo, cond_reg);
6417           __ Selnez(dst_hi, src_hi, cond_reg);
6418         } else {
6419           __ Seleqz(dst_lo, src_lo, cond_reg);
6420           __ Seleqz(dst_hi, src_hi, cond_reg);
6421         }
6422       } else {
6423         DCHECK(false_src.IsConstant());
6424         Register src_lo = true_src.AsRegisterPairLow<Register>();
6425         Register src_hi = true_src.AsRegisterPairHigh<Register>();
6426         if (cond_inverted) {
6427           __ Seleqz(dst_lo, src_lo, cond_reg);
6428           __ Seleqz(dst_hi, src_hi, cond_reg);
6429         } else {
6430           __ Selnez(dst_lo, src_lo, cond_reg);
6431           __ Selnez(dst_hi, src_hi, cond_reg);
6432         }
6433       }
6434       break;
6435     }
6436     case DataType::Type::kFloat32: {
6437       if (!DataType::IsFloatingPointType(cond_type)) {
6438         // sel*.fmt tests bit 0 of the condition register, account for that.
6439         __ Sltu(TMP, ZERO, cond_reg);
6440         __ Mtc1(TMP, fcond_reg);
6441       }
6442       FRegister dst_reg = dst.AsFpuRegister<FRegister>();
6443       if (true_src.IsConstant()) {
6444         FRegister src_reg = false_src.AsFpuRegister<FRegister>();
6445         if (cond_inverted) {
6446           __ SelnezS(dst_reg, src_reg, fcond_reg);
6447         } else {
6448           __ SeleqzS(dst_reg, src_reg, fcond_reg);
6449         }
6450       } else if (false_src.IsConstant()) {
6451         FRegister src_reg = true_src.AsFpuRegister<FRegister>();
6452         if (cond_inverted) {
6453           __ SeleqzS(dst_reg, src_reg, fcond_reg);
6454         } else {
6455           __ SelnezS(dst_reg, src_reg, fcond_reg);
6456         }
6457       } else {
6458         if (cond_inverted) {
6459           __ SelS(fcond_reg,
6460                   true_src.AsFpuRegister<FRegister>(),
6461                   false_src.AsFpuRegister<FRegister>());
6462         } else {
6463           __ SelS(fcond_reg,
6464                   false_src.AsFpuRegister<FRegister>(),
6465                   true_src.AsFpuRegister<FRegister>());
6466         }
6467         __ MovS(dst_reg, fcond_reg);
6468       }
6469       break;
6470     }
6471     case DataType::Type::kFloat64: {
6472       if (!DataType::IsFloatingPointType(cond_type)) {
6473         // sel*.fmt tests bit 0 of the condition register, account for that.
6474         __ Sltu(TMP, ZERO, cond_reg);
6475         __ Mtc1(TMP, fcond_reg);
6476       }
6477       FRegister dst_reg = dst.AsFpuRegister<FRegister>();
6478       if (true_src.IsConstant()) {
6479         FRegister src_reg = false_src.AsFpuRegister<FRegister>();
6480         if (cond_inverted) {
6481           __ SelnezD(dst_reg, src_reg, fcond_reg);
6482         } else {
6483           __ SeleqzD(dst_reg, src_reg, fcond_reg);
6484         }
6485       } else if (false_src.IsConstant()) {
6486         FRegister src_reg = true_src.AsFpuRegister<FRegister>();
6487         if (cond_inverted) {
6488           __ SeleqzD(dst_reg, src_reg, fcond_reg);
6489         } else {
6490           __ SelnezD(dst_reg, src_reg, fcond_reg);
6491         }
6492       } else {
6493         if (cond_inverted) {
6494           __ SelD(fcond_reg,
6495                   true_src.AsFpuRegister<FRegister>(),
6496                   false_src.AsFpuRegister<FRegister>());
6497         } else {
6498           __ SelD(fcond_reg,
6499                   false_src.AsFpuRegister<FRegister>(),
6500                   true_src.AsFpuRegister<FRegister>());
6501         }
6502         __ MovD(dst_reg, fcond_reg);
6503       }
6504       break;
6505     }
6506   }
6507 }
6508 
VisitShouldDeoptimizeFlag(HShouldDeoptimizeFlag * flag)6509 void LocationsBuilderMIPS::VisitShouldDeoptimizeFlag(HShouldDeoptimizeFlag* flag) {
6510   LocationSummary* locations = new (GetGraph()->GetAllocator())
6511       LocationSummary(flag, LocationSummary::kNoCall);
6512   locations->SetOut(Location::RequiresRegister());
6513 }
6514 
VisitShouldDeoptimizeFlag(HShouldDeoptimizeFlag * flag)6515 void InstructionCodeGeneratorMIPS::VisitShouldDeoptimizeFlag(HShouldDeoptimizeFlag* flag) {
6516   __ LoadFromOffset(kLoadWord,
6517                     flag->GetLocations()->Out().AsRegister<Register>(),
6518                     SP,
6519                     codegen_->GetStackOffsetOfShouldDeoptimizeFlag());
6520 }
6521 
VisitSelect(HSelect * select)6522 void LocationsBuilderMIPS::VisitSelect(HSelect* select) {
6523   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(select);
6524   CanMoveConditionally(select, codegen_->GetInstructionSetFeatures().IsR6(), locations);
6525 }
6526 
VisitSelect(HSelect * select)6527 void InstructionCodeGeneratorMIPS::VisitSelect(HSelect* select) {
6528   bool is_r6 = codegen_->GetInstructionSetFeatures().IsR6();
6529   if (CanMoveConditionally(select, is_r6, /* locations_to_set= */ nullptr)) {
6530     if (is_r6) {
6531       GenConditionalMoveR6(select);
6532     } else {
6533       GenConditionalMoveR2(select);
6534     }
6535   } else {
6536     LocationSummary* locations = select->GetLocations();
6537     MipsLabel false_target;
6538     GenerateTestAndBranch(select,
6539                           /* condition_input_index= */ 2,
6540                           /* true_target= */ nullptr,
6541                           &false_target);
6542     codegen_->MoveLocation(locations->Out(), locations->InAt(1), select->GetType());
6543     __ Bind(&false_target);
6544   }
6545 }
6546 
VisitNativeDebugInfo(HNativeDebugInfo * info)6547 void LocationsBuilderMIPS::VisitNativeDebugInfo(HNativeDebugInfo* info) {
6548   new (GetGraph()->GetAllocator()) LocationSummary(info);
6549 }
6550 
VisitNativeDebugInfo(HNativeDebugInfo *)6551 void InstructionCodeGeneratorMIPS::VisitNativeDebugInfo(HNativeDebugInfo*) {
6552   // MaybeRecordNativeDebugInfo is already called implicitly in CodeGenerator::Compile.
6553 }
6554 
GenerateNop()6555 void CodeGeneratorMIPS::GenerateNop() {
6556   __ Nop();
6557 }
6558 
HandleFieldGet(HInstruction * instruction,const FieldInfo & field_info)6559 void LocationsBuilderMIPS::HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info) {
6560   DataType::Type field_type = field_info.GetFieldType();
6561   bool is_wide = (field_type == DataType::Type::kInt64) || (field_type == DataType::Type::kFloat64);
6562   bool generate_volatile = field_info.IsVolatile() && is_wide;
6563   bool object_field_get_with_read_barrier =
6564       kEmitCompilerReadBarrier && (field_type == DataType::Type::kReference);
6565   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(
6566       instruction,
6567       generate_volatile
6568           ? LocationSummary::kCallOnMainOnly
6569           : (object_field_get_with_read_barrier
6570               ? LocationSummary::kCallOnSlowPath
6571               : LocationSummary::kNoCall));
6572 
6573   if (object_field_get_with_read_barrier && kUseBakerReadBarrier) {
6574     locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty());  // No caller-save registers.
6575   }
6576   locations->SetInAt(0, Location::RequiresRegister());
6577   if (generate_volatile) {
6578     InvokeRuntimeCallingConvention calling_convention;
6579     // need A0 to hold base + offset
6580     locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
6581     if (field_type == DataType::Type::kInt64) {
6582       locations->SetOut(calling_convention.GetReturnLocation(DataType::Type::kInt64));
6583     } else {
6584       // Use Location::Any() to prevent situations when running out of available fp registers.
6585       locations->SetOut(Location::Any());
6586       // Need some temp core regs since FP results are returned in core registers
6587       Location reg = calling_convention.GetReturnLocation(DataType::Type::kInt64);
6588       locations->AddTemp(Location::RegisterLocation(reg.AsRegisterPairLow<Register>()));
6589       locations->AddTemp(Location::RegisterLocation(reg.AsRegisterPairHigh<Register>()));
6590     }
6591   } else {
6592     if (DataType::IsFloatingPointType(instruction->GetType())) {
6593       locations->SetOut(Location::RequiresFpuRegister());
6594     } else {
6595       // The output overlaps in the case of an object field get with
6596       // read barriers enabled: we do not want the move to overwrite the
6597       // object's location, as we need it to emit the read barrier.
6598       locations->SetOut(Location::RequiresRegister(),
6599                         object_field_get_with_read_barrier
6600                             ? Location::kOutputOverlap
6601                             : Location::kNoOutputOverlap);
6602     }
6603     if (object_field_get_with_read_barrier && kUseBakerReadBarrier) {
6604       // We need a temporary register for the read barrier marking slow
6605       // path in CodeGeneratorMIPS::GenerateFieldLoadWithBakerReadBarrier.
6606       if (!kBakerReadBarrierThunksEnableForFields) {
6607         locations->AddTemp(Location::RequiresRegister());
6608       }
6609     }
6610   }
6611 }
6612 
HandleFieldGet(HInstruction * instruction,const FieldInfo & field_info,uint32_t dex_pc)6613 void InstructionCodeGeneratorMIPS::HandleFieldGet(HInstruction* instruction,
6614                                                   const FieldInfo& field_info,
6615                                                   uint32_t dex_pc) {
6616   DCHECK_EQ(DataType::Size(field_info.GetFieldType()), DataType::Size(instruction->GetType()));
6617   DataType::Type type = instruction->GetType();
6618   LocationSummary* locations = instruction->GetLocations();
6619   Location obj_loc = locations->InAt(0);
6620   Register obj = obj_loc.AsRegister<Register>();
6621   Location dst_loc = locations->Out();
6622   LoadOperandType load_type = kLoadUnsignedByte;
6623   bool is_volatile = field_info.IsVolatile();
6624   uint32_t offset = field_info.GetFieldOffset().Uint32Value();
6625   auto null_checker = GetImplicitNullChecker(instruction, codegen_);
6626 
6627   switch (type) {
6628     case DataType::Type::kBool:
6629     case DataType::Type::kUint8:
6630       load_type = kLoadUnsignedByte;
6631       break;
6632     case DataType::Type::kInt8:
6633       load_type = kLoadSignedByte;
6634       break;
6635     case DataType::Type::kUint16:
6636       load_type = kLoadUnsignedHalfword;
6637       break;
6638     case DataType::Type::kInt16:
6639       load_type = kLoadSignedHalfword;
6640       break;
6641     case DataType::Type::kInt32:
6642     case DataType::Type::kFloat32:
6643     case DataType::Type::kReference:
6644       load_type = kLoadWord;
6645       break;
6646     case DataType::Type::kInt64:
6647     case DataType::Type::kFloat64:
6648       load_type = kLoadDoubleword;
6649       break;
6650     case DataType::Type::kUint32:
6651     case DataType::Type::kUint64:
6652     case DataType::Type::kVoid:
6653       LOG(FATAL) << "Unreachable type " << type;
6654       UNREACHABLE();
6655   }
6656 
6657   if (is_volatile && load_type == kLoadDoubleword) {
6658     InvokeRuntimeCallingConvention calling_convention;
6659     __ Addiu32(locations->GetTemp(0).AsRegister<Register>(), obj, offset);
6660     // Do implicit Null check
6661     __ LoadFromOffset(kLoadWord,
6662                       ZERO,
6663                       locations->GetTemp(0).AsRegister<Register>(),
6664                       0,
6665                       null_checker);
6666     codegen_->InvokeRuntime(kQuickA64Load, instruction, dex_pc);
6667     CheckEntrypointTypes<kQuickA64Load, int64_t, volatile const int64_t*>();
6668     if (type == DataType::Type::kFloat64) {
6669       // FP results are returned in core registers. Need to move them.
6670       if (dst_loc.IsFpuRegister()) {
6671         __ Mtc1(locations->GetTemp(1).AsRegister<Register>(), dst_loc.AsFpuRegister<FRegister>());
6672         __ MoveToFpuHigh(locations->GetTemp(2).AsRegister<Register>(),
6673                          dst_loc.AsFpuRegister<FRegister>());
6674       } else {
6675         DCHECK(dst_loc.IsDoubleStackSlot());
6676         __ StoreToOffset(kStoreWord,
6677                          locations->GetTemp(1).AsRegister<Register>(),
6678                          SP,
6679                          dst_loc.GetStackIndex());
6680         __ StoreToOffset(kStoreWord,
6681                          locations->GetTemp(2).AsRegister<Register>(),
6682                          SP,
6683                          dst_loc.GetStackIndex() + 4);
6684       }
6685     }
6686   } else {
6687     if (type == DataType::Type::kReference) {
6688       // /* HeapReference<Object> */ dst = *(obj + offset)
6689       if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
6690         Location temp_loc =
6691             kBakerReadBarrierThunksEnableForFields ? Location::NoLocation() : locations->GetTemp(0);
6692         // Note that a potential implicit null check is handled in this
6693         // CodeGeneratorMIPS::GenerateFieldLoadWithBakerReadBarrier call.
6694         codegen_->GenerateFieldLoadWithBakerReadBarrier(instruction,
6695                                                         dst_loc,
6696                                                         obj,
6697                                                         offset,
6698                                                         temp_loc,
6699                                                         /* needs_null_check= */ true);
6700         if (is_volatile) {
6701           GenerateMemoryBarrier(MemBarrierKind::kLoadAny);
6702         }
6703       } else {
6704         __ LoadFromOffset(kLoadWord, dst_loc.AsRegister<Register>(), obj, offset, null_checker);
6705         if (is_volatile) {
6706           GenerateMemoryBarrier(MemBarrierKind::kLoadAny);
6707         }
6708         // If read barriers are enabled, emit read barriers other than
6709         // Baker's using a slow path (and also unpoison the loaded
6710         // reference, if heap poisoning is enabled).
6711         codegen_->MaybeGenerateReadBarrierSlow(instruction, dst_loc, dst_loc, obj_loc, offset);
6712       }
6713     } else if (!DataType::IsFloatingPointType(type)) {
6714       Register dst;
6715       if (type == DataType::Type::kInt64) {
6716         DCHECK(dst_loc.IsRegisterPair());
6717         dst = dst_loc.AsRegisterPairLow<Register>();
6718       } else {
6719         DCHECK(dst_loc.IsRegister());
6720         dst = dst_loc.AsRegister<Register>();
6721       }
6722       __ LoadFromOffset(load_type, dst, obj, offset, null_checker);
6723     } else {
6724       DCHECK(dst_loc.IsFpuRegister());
6725       FRegister dst = dst_loc.AsFpuRegister<FRegister>();
6726       if (type == DataType::Type::kFloat32) {
6727         __ LoadSFromOffset(dst, obj, offset, null_checker);
6728       } else {
6729         __ LoadDFromOffset(dst, obj, offset, null_checker);
6730       }
6731     }
6732   }
6733 
6734   // Memory barriers, in the case of references, are handled in the
6735   // previous switch statement.
6736   if (is_volatile && (type != DataType::Type::kReference)) {
6737     GenerateMemoryBarrier(MemBarrierKind::kLoadAny);
6738   }
6739 }
6740 
HandleFieldSet(HInstruction * instruction,const FieldInfo & field_info)6741 void LocationsBuilderMIPS::HandleFieldSet(HInstruction* instruction, const FieldInfo& field_info) {
6742   DataType::Type field_type = field_info.GetFieldType();
6743   bool is_wide = (field_type == DataType::Type::kInt64) || (field_type == DataType::Type::kFloat64);
6744   bool generate_volatile = field_info.IsVolatile() && is_wide;
6745   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(
6746       instruction, generate_volatile ? LocationSummary::kCallOnMainOnly : LocationSummary::kNoCall);
6747 
6748   locations->SetInAt(0, Location::RequiresRegister());
6749   if (generate_volatile) {
6750     InvokeRuntimeCallingConvention calling_convention;
6751     // need A0 to hold base + offset
6752     locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
6753     if (field_type == DataType::Type::kInt64) {
6754       locations->SetInAt(1, Location::RegisterPairLocation(
6755           calling_convention.GetRegisterAt(2), calling_convention.GetRegisterAt(3)));
6756     } else {
6757       // Use Location::Any() to prevent situations when running out of available fp registers.
6758       locations->SetInAt(1, Location::Any());
6759       // Pass FP parameters in core registers.
6760       locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
6761       locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(3)));
6762     }
6763   } else {
6764     if (DataType::IsFloatingPointType(field_type)) {
6765       locations->SetInAt(1, FpuRegisterOrConstantForStore(instruction->InputAt(1)));
6766     } else {
6767       locations->SetInAt(1, RegisterOrZeroConstant(instruction->InputAt(1)));
6768     }
6769   }
6770 }
6771 
HandleFieldSet(HInstruction * instruction,const FieldInfo & field_info,uint32_t dex_pc,bool value_can_be_null)6772 void InstructionCodeGeneratorMIPS::HandleFieldSet(HInstruction* instruction,
6773                                                   const FieldInfo& field_info,
6774                                                   uint32_t dex_pc,
6775                                                   bool value_can_be_null) {
6776   DataType::Type type = field_info.GetFieldType();
6777   LocationSummary* locations = instruction->GetLocations();
6778   Register obj = locations->InAt(0).AsRegister<Register>();
6779   Location value_location = locations->InAt(1);
6780   StoreOperandType store_type = kStoreByte;
6781   bool is_volatile = field_info.IsVolatile();
6782   uint32_t offset = field_info.GetFieldOffset().Uint32Value();
6783   bool needs_write_barrier = CodeGenerator::StoreNeedsWriteBarrier(type, instruction->InputAt(1));
6784   auto null_checker = GetImplicitNullChecker(instruction, codegen_);
6785 
6786   switch (type) {
6787     case DataType::Type::kBool:
6788     case DataType::Type::kUint8:
6789     case DataType::Type::kInt8:
6790       store_type = kStoreByte;
6791       break;
6792     case DataType::Type::kUint16:
6793     case DataType::Type::kInt16:
6794       store_type = kStoreHalfword;
6795       break;
6796     case DataType::Type::kInt32:
6797     case DataType::Type::kFloat32:
6798     case DataType::Type::kReference:
6799       store_type = kStoreWord;
6800       break;
6801     case DataType::Type::kInt64:
6802     case DataType::Type::kFloat64:
6803       store_type = kStoreDoubleword;
6804       break;
6805     case DataType::Type::kUint32:
6806     case DataType::Type::kUint64:
6807     case DataType::Type::kVoid:
6808       LOG(FATAL) << "Unreachable type " << type;
6809       UNREACHABLE();
6810   }
6811 
6812   if (is_volatile) {
6813     GenerateMemoryBarrier(MemBarrierKind::kAnyStore);
6814   }
6815 
6816   if (is_volatile && store_type == kStoreDoubleword) {
6817     InvokeRuntimeCallingConvention calling_convention;
6818     __ Addiu32(locations->GetTemp(0).AsRegister<Register>(), obj, offset);
6819     // Do implicit Null check.
6820     __ LoadFromOffset(kLoadWord,
6821                       ZERO,
6822                       locations->GetTemp(0).AsRegister<Register>(),
6823                       0,
6824                       null_checker);
6825     if (type == DataType::Type::kFloat64) {
6826       // Pass FP parameters in core registers.
6827       if (value_location.IsFpuRegister()) {
6828         __ Mfc1(locations->GetTemp(1).AsRegister<Register>(),
6829                 value_location.AsFpuRegister<FRegister>());
6830         __ MoveFromFpuHigh(locations->GetTemp(2).AsRegister<Register>(),
6831                            value_location.AsFpuRegister<FRegister>());
6832       } else if (value_location.IsDoubleStackSlot()) {
6833         __ LoadFromOffset(kLoadWord,
6834                           locations->GetTemp(1).AsRegister<Register>(),
6835                           SP,
6836                           value_location.GetStackIndex());
6837         __ LoadFromOffset(kLoadWord,
6838                           locations->GetTemp(2).AsRegister<Register>(),
6839                           SP,
6840                           value_location.GetStackIndex() + 4);
6841       } else {
6842         DCHECK(value_location.IsConstant());
6843         DCHECK(value_location.GetConstant()->IsDoubleConstant());
6844         int64_t value = CodeGenerator::GetInt64ValueOf(value_location.GetConstant());
6845         __ LoadConst64(locations->GetTemp(2).AsRegister<Register>(),
6846                        locations->GetTemp(1).AsRegister<Register>(),
6847                        value);
6848       }
6849     }
6850     codegen_->InvokeRuntime(kQuickA64Store, instruction, dex_pc);
6851     CheckEntrypointTypes<kQuickA64Store, void, volatile int64_t *, int64_t>();
6852   } else {
6853     if (value_location.IsConstant()) {
6854       int64_t value = CodeGenerator::GetInt64ValueOf(value_location.GetConstant());
6855       __ StoreConstToOffset(store_type, value, obj, offset, TMP, null_checker);
6856     } else if (!DataType::IsFloatingPointType(type)) {
6857       Register src;
6858       if (type == DataType::Type::kInt64) {
6859         src = value_location.AsRegisterPairLow<Register>();
6860       } else {
6861         src = value_location.AsRegister<Register>();
6862       }
6863       if (kPoisonHeapReferences && needs_write_barrier) {
6864         // Note that in the case where `value` is a null reference,
6865         // we do not enter this block, as a null reference does not
6866         // need poisoning.
6867         DCHECK_EQ(type, DataType::Type::kReference);
6868         __ PoisonHeapReference(TMP, src);
6869         __ StoreToOffset(store_type, TMP, obj, offset, null_checker);
6870       } else {
6871         __ StoreToOffset(store_type, src, obj, offset, null_checker);
6872       }
6873     } else {
6874       FRegister src = value_location.AsFpuRegister<FRegister>();
6875       if (type == DataType::Type::kFloat32) {
6876         __ StoreSToOffset(src, obj, offset, null_checker);
6877       } else {
6878         __ StoreDToOffset(src, obj, offset, null_checker);
6879       }
6880     }
6881   }
6882 
6883   if (needs_write_barrier) {
6884     Register src = value_location.AsRegister<Register>();
6885     codegen_->MarkGCCard(obj, src, value_can_be_null);
6886   }
6887 
6888   if (is_volatile) {
6889     GenerateMemoryBarrier(MemBarrierKind::kAnyAny);
6890   }
6891 }
6892 
VisitInstanceFieldGet(HInstanceFieldGet * instruction)6893 void LocationsBuilderMIPS::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
6894   HandleFieldGet(instruction, instruction->GetFieldInfo());
6895 }
6896 
VisitInstanceFieldGet(HInstanceFieldGet * instruction)6897 void InstructionCodeGeneratorMIPS::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
6898   HandleFieldGet(instruction, instruction->GetFieldInfo(), instruction->GetDexPc());
6899 }
6900 
VisitInstanceFieldSet(HInstanceFieldSet * instruction)6901 void LocationsBuilderMIPS::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
6902   HandleFieldSet(instruction, instruction->GetFieldInfo());
6903 }
6904 
VisitInstanceFieldSet(HInstanceFieldSet * instruction)6905 void InstructionCodeGeneratorMIPS::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
6906   HandleFieldSet(instruction,
6907                  instruction->GetFieldInfo(),
6908                  instruction->GetDexPc(),
6909                  instruction->GetValueCanBeNull());
6910 }
6911 
GenerateReferenceLoadOneRegister(HInstruction * instruction,Location out,uint32_t offset,Location maybe_temp,ReadBarrierOption read_barrier_option)6912 void InstructionCodeGeneratorMIPS::GenerateReferenceLoadOneRegister(
6913     HInstruction* instruction,
6914     Location out,
6915     uint32_t offset,
6916     Location maybe_temp,
6917     ReadBarrierOption read_barrier_option) {
6918   Register out_reg = out.AsRegister<Register>();
6919   if (read_barrier_option == kWithReadBarrier) {
6920     CHECK(kEmitCompilerReadBarrier);
6921     if (!kUseBakerReadBarrier || !kBakerReadBarrierThunksEnableForFields) {
6922       DCHECK(maybe_temp.IsRegister()) << maybe_temp;
6923     }
6924     if (kUseBakerReadBarrier) {
6925       // Load with fast path based Baker's read barrier.
6926       // /* HeapReference<Object> */ out = *(out + offset)
6927       codegen_->GenerateFieldLoadWithBakerReadBarrier(instruction,
6928                                                       out,
6929                                                       out_reg,
6930                                                       offset,
6931                                                       maybe_temp,
6932                                                       /* needs_null_check= */ false);
6933     } else {
6934       // Load with slow path based read barrier.
6935       // Save the value of `out` into `maybe_temp` before overwriting it
6936       // in the following move operation, as we will need it for the
6937       // read barrier below.
6938       __ Move(maybe_temp.AsRegister<Register>(), out_reg);
6939       // /* HeapReference<Object> */ out = *(out + offset)
6940       __ LoadFromOffset(kLoadWord, out_reg, out_reg, offset);
6941       codegen_->GenerateReadBarrierSlow(instruction, out, out, maybe_temp, offset);
6942     }
6943   } else {
6944     // Plain load with no read barrier.
6945     // /* HeapReference<Object> */ out = *(out + offset)
6946     __ LoadFromOffset(kLoadWord, out_reg, out_reg, offset);
6947     __ MaybeUnpoisonHeapReference(out_reg);
6948   }
6949 }
6950 
GenerateReferenceLoadTwoRegisters(HInstruction * instruction,Location out,Location obj,uint32_t offset,Location maybe_temp,ReadBarrierOption read_barrier_option)6951 void InstructionCodeGeneratorMIPS::GenerateReferenceLoadTwoRegisters(
6952     HInstruction* instruction,
6953     Location out,
6954     Location obj,
6955     uint32_t offset,
6956     Location maybe_temp,
6957     ReadBarrierOption read_barrier_option) {
6958   Register out_reg = out.AsRegister<Register>();
6959   Register obj_reg = obj.AsRegister<Register>();
6960   if (read_barrier_option == kWithReadBarrier) {
6961     CHECK(kEmitCompilerReadBarrier);
6962     if (kUseBakerReadBarrier) {
6963       if (!kBakerReadBarrierThunksEnableForFields) {
6964         DCHECK(maybe_temp.IsRegister()) << maybe_temp;
6965       }
6966       // Load with fast path based Baker's read barrier.
6967       // /* HeapReference<Object> */ out = *(obj + offset)
6968       codegen_->GenerateFieldLoadWithBakerReadBarrier(instruction,
6969                                                       out,
6970                                                       obj_reg,
6971                                                       offset,
6972                                                       maybe_temp,
6973                                                       /* needs_null_check= */ false);
6974     } else {
6975       // Load with slow path based read barrier.
6976       // /* HeapReference<Object> */ out = *(obj + offset)
6977       __ LoadFromOffset(kLoadWord, out_reg, obj_reg, offset);
6978       codegen_->GenerateReadBarrierSlow(instruction, out, out, obj, offset);
6979     }
6980   } else {
6981     // Plain load with no read barrier.
6982     // /* HeapReference<Object> */ out = *(obj + offset)
6983     __ LoadFromOffset(kLoadWord, out_reg, obj_reg, offset);
6984     __ MaybeUnpoisonHeapReference(out_reg);
6985   }
6986 }
6987 
GetBakerMarkThunkNumber(Register reg)6988 static inline int GetBakerMarkThunkNumber(Register reg) {
6989   static_assert(BAKER_MARK_INTROSPECTION_REGISTER_COUNT == 21, "Expecting equal");
6990   if (reg >= V0 && reg <= T7) {  // 14 consequtive regs.
6991     return reg - V0;
6992   } else if (reg >= S2 && reg <= S7) {  // 6 consequtive regs.
6993     return 14 + (reg - S2);
6994   } else if (reg == FP) {  // One more.
6995     return 20;
6996   }
6997   LOG(FATAL) << "Unexpected register " << reg;
6998   UNREACHABLE();
6999 }
7000 
GetBakerMarkFieldArrayThunkDisplacement(Register reg,bool short_offset)7001 static inline int GetBakerMarkFieldArrayThunkDisplacement(Register reg, bool short_offset) {
7002   int num = GetBakerMarkThunkNumber(reg) +
7003       (short_offset ? BAKER_MARK_INTROSPECTION_REGISTER_COUNT : 0);
7004   return num * BAKER_MARK_INTROSPECTION_FIELD_ARRAY_ENTRY_SIZE;
7005 }
7006 
GetBakerMarkGcRootThunkDisplacement(Register reg)7007 static inline int GetBakerMarkGcRootThunkDisplacement(Register reg) {
7008   return GetBakerMarkThunkNumber(reg) * BAKER_MARK_INTROSPECTION_GC_ROOT_ENTRY_SIZE +
7009       BAKER_MARK_INTROSPECTION_GC_ROOT_ENTRIES_OFFSET;
7010 }
7011 
GenerateGcRootFieldLoad(HInstruction * instruction,Location root,Register obj,uint32_t offset,ReadBarrierOption read_barrier_option,MipsLabel * label_low)7012 void InstructionCodeGeneratorMIPS::GenerateGcRootFieldLoad(HInstruction* instruction,
7013                                                            Location root,
7014                                                            Register obj,
7015                                                            uint32_t offset,
7016                                                            ReadBarrierOption read_barrier_option,
7017                                                            MipsLabel* label_low) {
7018   bool reordering;
7019   if (label_low != nullptr) {
7020     DCHECK_EQ(offset, 0x5678u);
7021   }
7022   Register root_reg = root.AsRegister<Register>();
7023   if (read_barrier_option == kWithReadBarrier) {
7024     DCHECK(kEmitCompilerReadBarrier);
7025     if (kUseBakerReadBarrier) {
7026       // Fast path implementation of art::ReadBarrier::BarrierForRoot when
7027       // Baker's read barrier are used:
7028       if (kBakerReadBarrierThunksEnableForGcRoots) {
7029         // Note that we do not actually check the value of `GetIsGcMarking()`
7030         // to decide whether to mark the loaded GC root or not.  Instead, we
7031         // load into `temp` (T9) the read barrier mark introspection entrypoint.
7032         // If `temp` is null, it means that `GetIsGcMarking()` is false, and
7033         // vice versa.
7034         //
7035         // We use thunks for the slow path. That thunk checks the reference
7036         // and jumps to the entrypoint if needed.
7037         //
7038         //     temp = Thread::Current()->pReadBarrierMarkReg00
7039         //     // AKA &art_quick_read_barrier_mark_introspection.
7040         //     GcRoot<mirror::Object> root = *(obj+offset);  // Original reference load.
7041         //     if (temp != nullptr) {
7042         //        temp = &gc_root_thunk<root_reg>
7043         //        root = temp(root)
7044         //     }
7045 
7046         bool isR6 = codegen_->GetInstructionSetFeatures().IsR6();
7047         const int32_t entry_point_offset =
7048             Thread::ReadBarrierMarkEntryPointsOffset<kMipsPointerSize>(0);
7049         const int thunk_disp = GetBakerMarkGcRootThunkDisplacement(root_reg);
7050         int16_t offset_low = Low16Bits(offset);
7051         int16_t offset_high = High16Bits(offset - offset_low);  // Accounts for sign
7052                                                                 // extension in lw.
7053         bool short_offset = IsInt<16>(static_cast<int32_t>(offset));
7054         Register base = short_offset ? obj : TMP;
7055         // Loading the entrypoint does not require a load acquire since it is only changed when
7056         // threads are suspended or running a checkpoint.
7057         __ LoadFromOffset(kLoadWord, T9, TR, entry_point_offset);
7058         reordering = __ SetReorder(false);
7059         if (!short_offset) {
7060           DCHECK(!label_low);
7061           __ AddUpper(base, obj, offset_high);
7062         }
7063         MipsLabel skip_call;
7064         __ Beqz(T9, &skip_call, /* is_bare= */ true);
7065         if (label_low != nullptr) {
7066           DCHECK(short_offset);
7067           __ Bind(label_low);
7068         }
7069         // /* GcRoot<mirror::Object> */ root = *(obj + offset)
7070         __ LoadFromOffset(kLoadWord, root_reg, base, offset_low);  // Single instruction
7071                                                                    // in delay slot.
7072         if (isR6) {
7073           __ Jialc(T9, thunk_disp);
7074         } else {
7075           __ Addiu(T9, T9, thunk_disp);
7076           __ Jalr(T9);
7077           __ Nop();
7078         }
7079         __ Bind(&skip_call);
7080         __ SetReorder(reordering);
7081       } else {
7082         // Note that we do not actually check the value of `GetIsGcMarking()`
7083         // to decide whether to mark the loaded GC root or not.  Instead, we
7084         // load into `temp` (T9) the read barrier mark entry point corresponding
7085         // to register `root`. If `temp` is null, it means that `GetIsGcMarking()`
7086         // is false, and vice versa.
7087         //
7088         //     GcRoot<mirror::Object> root = *(obj+offset);  // Original reference load.
7089         //     temp = Thread::Current()->pReadBarrierMarkReg ## root.reg()
7090         //     if (temp != null) {
7091         //       root = temp(root)
7092         //     }
7093 
7094         if (label_low != nullptr) {
7095           reordering = __ SetReorder(false);
7096           __ Bind(label_low);
7097         }
7098         // /* GcRoot<mirror::Object> */ root = *(obj + offset)
7099         __ LoadFromOffset(kLoadWord, root_reg, obj, offset);
7100         if (label_low != nullptr) {
7101           __ SetReorder(reordering);
7102         }
7103         static_assert(
7104             sizeof(mirror::CompressedReference<mirror::Object>) == sizeof(GcRoot<mirror::Object>),
7105             "art::mirror::CompressedReference<mirror::Object> and art::GcRoot<mirror::Object> "
7106             "have different sizes.");
7107         static_assert(sizeof(mirror::CompressedReference<mirror::Object>) == sizeof(int32_t),
7108                       "art::mirror::CompressedReference<mirror::Object> and int32_t "
7109                       "have different sizes.");
7110 
7111         // Slow path marking the GC root `root`.
7112         Location temp = Location::RegisterLocation(T9);
7113         SlowPathCodeMIPS* slow_path =
7114             new (codegen_->GetScopedAllocator()) ReadBarrierMarkSlowPathMIPS(
7115                 instruction,
7116                 root,
7117                 /*entrypoint*/ temp);
7118         codegen_->AddSlowPath(slow_path);
7119 
7120         const int32_t entry_point_offset =
7121             Thread::ReadBarrierMarkEntryPointsOffset<kMipsPointerSize>(root.reg() - 1);
7122         // Loading the entrypoint does not require a load acquire since it is only changed when
7123         // threads are suspended or running a checkpoint.
7124         __ LoadFromOffset(kLoadWord, temp.AsRegister<Register>(), TR, entry_point_offset);
7125         __ Bnez(temp.AsRegister<Register>(), slow_path->GetEntryLabel());
7126         __ Bind(slow_path->GetExitLabel());
7127       }
7128     } else {
7129       if (label_low != nullptr) {
7130         reordering = __ SetReorder(false);
7131         __ Bind(label_low);
7132       }
7133       // GC root loaded through a slow path for read barriers other
7134       // than Baker's.
7135       // /* GcRoot<mirror::Object>* */ root = obj + offset
7136       __ Addiu32(root_reg, obj, offset);
7137       if (label_low != nullptr) {
7138         __ SetReorder(reordering);
7139       }
7140       // /* mirror::Object* */ root = root->Read()
7141       codegen_->GenerateReadBarrierForRootSlow(instruction, root, root);
7142     }
7143   } else {
7144     if (label_low != nullptr) {
7145       reordering = __ SetReorder(false);
7146       __ Bind(label_low);
7147     }
7148     // Plain GC root load with no read barrier.
7149     // /* GcRoot<mirror::Object> */ root = *(obj + offset)
7150     __ LoadFromOffset(kLoadWord, root_reg, obj, offset);
7151     // Note that GC roots are not affected by heap poisoning, thus we
7152     // do not have to unpoison `root_reg` here.
7153     if (label_low != nullptr) {
7154       __ SetReorder(reordering);
7155     }
7156   }
7157 }
7158 
GenerateFieldLoadWithBakerReadBarrier(HInstruction * instruction,Location ref,Register obj,uint32_t offset,Location temp,bool needs_null_check)7159 void CodeGeneratorMIPS::GenerateFieldLoadWithBakerReadBarrier(HInstruction* instruction,
7160                                                               Location ref,
7161                                                               Register obj,
7162                                                               uint32_t offset,
7163                                                               Location temp,
7164                                                               bool needs_null_check) {
7165   DCHECK(kEmitCompilerReadBarrier);
7166   DCHECK(kUseBakerReadBarrier);
7167 
7168   if (kBakerReadBarrierThunksEnableForFields) {
7169     // Note that we do not actually check the value of `GetIsGcMarking()`
7170     // to decide whether to mark the loaded reference or not.  Instead, we
7171     // load into `temp` (T9) the read barrier mark introspection entrypoint.
7172     // If `temp` is null, it means that `GetIsGcMarking()` is false, and
7173     // vice versa.
7174     //
7175     // We use thunks for the slow path. That thunk checks the reference
7176     // and jumps to the entrypoint if needed. If the holder is not gray,
7177     // it issues a load-load memory barrier and returns to the original
7178     // reference load.
7179     //
7180     //     temp = Thread::Current()->pReadBarrierMarkReg00
7181     //     // AKA &art_quick_read_barrier_mark_introspection.
7182     //     if (temp != nullptr) {
7183     //        temp = &field_array_thunk<holder_reg>
7184     //        temp()
7185     //     }
7186     //   not_gray_return_address:
7187     //     // If the offset is too large to fit into the lw instruction, we
7188     //     // use an adjusted base register (TMP) here. This register
7189     //     // receives bits 16 ... 31 of the offset before the thunk invocation
7190     //     // and the thunk benefits from it.
7191     //     HeapReference<mirror::Object> reference = *(obj+offset);  // Original reference load.
7192     //   gray_return_address:
7193 
7194     DCHECK(temp.IsInvalid());
7195     bool isR6 = GetInstructionSetFeatures().IsR6();
7196     int16_t offset_low = Low16Bits(offset);
7197     int16_t offset_high = High16Bits(offset - offset_low);  // Accounts for sign extension in lw.
7198     bool short_offset = IsInt<16>(static_cast<int32_t>(offset));
7199     bool reordering = __ SetReorder(false);
7200     const int32_t entry_point_offset =
7201         Thread::ReadBarrierMarkEntryPointsOffset<kMipsPointerSize>(0);
7202     // There may have or may have not been a null check if the field offset is smaller than
7203     // the page size.
7204     // There must've been a null check in case it's actually a load from an array.
7205     // We will, however, perform an explicit null check in the thunk as it's easier to
7206     // do it than not.
7207     if (instruction->IsArrayGet()) {
7208       DCHECK(!needs_null_check);
7209     }
7210     const int thunk_disp = GetBakerMarkFieldArrayThunkDisplacement(obj, short_offset);
7211     // Loading the entrypoint does not require a load acquire since it is only changed when
7212     // threads are suspended or running a checkpoint.
7213     __ LoadFromOffset(kLoadWord, T9, TR, entry_point_offset);
7214     Register ref_reg = ref.AsRegister<Register>();
7215     Register base = short_offset ? obj : TMP;
7216     MipsLabel skip_call;
7217     if (short_offset) {
7218       if (isR6) {
7219         __ Beqzc(T9, &skip_call, /* is_bare= */ true);
7220         __ Nop();  // In forbidden slot.
7221         __ Jialc(T9, thunk_disp);
7222       } else {
7223         __ Beqz(T9, &skip_call, /* is_bare= */ true);
7224         __ Addiu(T9, T9, thunk_disp);  // In delay slot.
7225         __ Jalr(T9);
7226         __ Nop();  // In delay slot.
7227       }
7228       __ Bind(&skip_call);
7229     } else {
7230       if (isR6) {
7231         __ Beqz(T9, &skip_call, /* is_bare= */ true);
7232         __ Aui(base, obj, offset_high);  // In delay slot.
7233         __ Jialc(T9, thunk_disp);
7234         __ Bind(&skip_call);
7235       } else {
7236         __ Lui(base, offset_high);
7237         __ Beqz(T9, &skip_call, /* is_bare= */ true);
7238         __ Addiu(T9, T9, thunk_disp);  // In delay slot.
7239         __ Jalr(T9);
7240         __ Bind(&skip_call);
7241         __ Addu(base, base, obj);  // In delay slot.
7242       }
7243     }
7244     // /* HeapReference<Object> */ ref = *(obj + offset)
7245     __ LoadFromOffset(kLoadWord, ref_reg, base, offset_low);  // Single instruction.
7246     if (needs_null_check) {
7247       MaybeRecordImplicitNullCheck(instruction);
7248     }
7249     __ MaybeUnpoisonHeapReference(ref_reg);
7250     __ SetReorder(reordering);
7251     return;
7252   }
7253 
7254   // /* HeapReference<Object> */ ref = *(obj + offset)
7255   Location no_index = Location::NoLocation();
7256   ScaleFactor no_scale_factor = TIMES_1;
7257   GenerateReferenceLoadWithBakerReadBarrier(instruction,
7258                                             ref,
7259                                             obj,
7260                                             offset,
7261                                             no_index,
7262                                             no_scale_factor,
7263                                             temp,
7264                                             needs_null_check);
7265 }
7266 
GenerateArrayLoadWithBakerReadBarrier(HInstruction * instruction,Location ref,Register obj,uint32_t data_offset,Location index,Location temp,bool needs_null_check)7267 void CodeGeneratorMIPS::GenerateArrayLoadWithBakerReadBarrier(HInstruction* instruction,
7268                                                               Location ref,
7269                                                               Register obj,
7270                                                               uint32_t data_offset,
7271                                                               Location index,
7272                                                               Location temp,
7273                                                               bool needs_null_check) {
7274   DCHECK(kEmitCompilerReadBarrier);
7275   DCHECK(kUseBakerReadBarrier);
7276 
7277   static_assert(
7278       sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
7279       "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
7280   ScaleFactor scale_factor = TIMES_4;
7281 
7282   if (kBakerReadBarrierThunksEnableForArrays) {
7283     // Note that we do not actually check the value of `GetIsGcMarking()`
7284     // to decide whether to mark the loaded reference or not.  Instead, we
7285     // load into `temp` (T9) the read barrier mark introspection entrypoint.
7286     // If `temp` is null, it means that `GetIsGcMarking()` is false, and
7287     // vice versa.
7288     //
7289     // We use thunks for the slow path. That thunk checks the reference
7290     // and jumps to the entrypoint if needed. If the holder is not gray,
7291     // it issues a load-load memory barrier and returns to the original
7292     // reference load.
7293     //
7294     //     temp = Thread::Current()->pReadBarrierMarkReg00
7295     //     // AKA &art_quick_read_barrier_mark_introspection.
7296     //     if (temp != nullptr) {
7297     //        temp = &field_array_thunk<holder_reg>
7298     //        temp()
7299     //     }
7300     //   not_gray_return_address:
7301     //     // The element address is pre-calculated in the TMP register before the
7302     //     // thunk invocation and the thunk benefits from it.
7303     //     HeapReference<mirror::Object> reference = data[index];  // Original reference load.
7304     //   gray_return_address:
7305 
7306     DCHECK(temp.IsInvalid());
7307     DCHECK(index.IsValid());
7308     bool reordering = __ SetReorder(false);
7309     const int32_t entry_point_offset =
7310         Thread::ReadBarrierMarkEntryPointsOffset<kMipsPointerSize>(0);
7311     // We will not do the explicit null check in the thunk as some form of a null check
7312     // must've been done earlier.
7313     DCHECK(!needs_null_check);
7314     const int thunk_disp = GetBakerMarkFieldArrayThunkDisplacement(obj, /* short_offset= */ false);
7315     // Loading the entrypoint does not require a load acquire since it is only changed when
7316     // threads are suspended or running a checkpoint.
7317     __ LoadFromOffset(kLoadWord, T9, TR, entry_point_offset);
7318     Register ref_reg = ref.AsRegister<Register>();
7319     Register index_reg = index.IsRegisterPair()
7320         ? index.AsRegisterPairLow<Register>()
7321         : index.AsRegister<Register>();
7322     MipsLabel skip_call;
7323     if (GetInstructionSetFeatures().IsR6()) {
7324       __ Beqz(T9, &skip_call, /* is_bare= */ true);
7325       __ Lsa(TMP, index_reg, obj, scale_factor);  // In delay slot.
7326       __ Jialc(T9, thunk_disp);
7327       __ Bind(&skip_call);
7328     } else {
7329       __ Sll(TMP, index_reg, scale_factor);
7330       __ Beqz(T9, &skip_call, /* is_bare= */ true);
7331       __ Addiu(T9, T9, thunk_disp);  // In delay slot.
7332       __ Jalr(T9);
7333       __ Bind(&skip_call);
7334       __ Addu(TMP, TMP, obj);  // In delay slot.
7335     }
7336     // /* HeapReference<Object> */ ref = *(obj + data_offset + (index << scale_factor))
7337     DCHECK(IsInt<16>(static_cast<int32_t>(data_offset))) << data_offset;
7338     __ LoadFromOffset(kLoadWord, ref_reg, TMP, data_offset);  // Single instruction.
7339     __ MaybeUnpoisonHeapReference(ref_reg);
7340     __ SetReorder(reordering);
7341     return;
7342   }
7343 
7344   // /* HeapReference<Object> */ ref =
7345   //     *(obj + data_offset + index * sizeof(HeapReference<Object>))
7346   GenerateReferenceLoadWithBakerReadBarrier(instruction,
7347                                             ref,
7348                                             obj,
7349                                             data_offset,
7350                                             index,
7351                                             scale_factor,
7352                                             temp,
7353                                             needs_null_check);
7354 }
7355 
GenerateReferenceLoadWithBakerReadBarrier(HInstruction * instruction,Location ref,Register obj,uint32_t offset,Location index,ScaleFactor scale_factor,Location temp,bool needs_null_check,bool always_update_field)7356 void CodeGeneratorMIPS::GenerateReferenceLoadWithBakerReadBarrier(HInstruction* instruction,
7357                                                                   Location ref,
7358                                                                   Register obj,
7359                                                                   uint32_t offset,
7360                                                                   Location index,
7361                                                                   ScaleFactor scale_factor,
7362                                                                   Location temp,
7363                                                                   bool needs_null_check,
7364                                                                   bool always_update_field) {
7365   DCHECK(kEmitCompilerReadBarrier);
7366   DCHECK(kUseBakerReadBarrier);
7367 
7368   // In slow path based read barriers, the read barrier call is
7369   // inserted after the original load. However, in fast path based
7370   // Baker's read barriers, we need to perform the load of
7371   // mirror::Object::monitor_ *before* the original reference load.
7372   // This load-load ordering is required by the read barrier.
7373   // The fast path/slow path (for Baker's algorithm) should look like:
7374   //
7375   //   uint32_t rb_state = Lockword(obj->monitor_).ReadBarrierState();
7376   //   lfence;  // Load fence or artificial data dependency to prevent load-load reordering
7377   //   HeapReference<Object> ref = *src;  // Original reference load.
7378   //   bool is_gray = (rb_state == ReadBarrier::GrayState());
7379   //   if (is_gray) {
7380   //     ref = ReadBarrier::Mark(ref);  // Performed by runtime entrypoint slow path.
7381   //   }
7382   //
7383   // Note: the original implementation in ReadBarrier::Barrier is
7384   // slightly more complex as it performs additional checks that we do
7385   // not do here for performance reasons.
7386 
7387   Register ref_reg = ref.AsRegister<Register>();
7388   Register temp_reg = temp.AsRegister<Register>();
7389   uint32_t monitor_offset = mirror::Object::MonitorOffset().Int32Value();
7390 
7391   // /* int32_t */ monitor = obj->monitor_
7392   __ LoadFromOffset(kLoadWord, temp_reg, obj, monitor_offset);
7393   if (needs_null_check) {
7394     MaybeRecordImplicitNullCheck(instruction);
7395   }
7396   // /* LockWord */ lock_word = LockWord(monitor)
7397   static_assert(sizeof(LockWord) == sizeof(int32_t),
7398                 "art::LockWord and int32_t have different sizes.");
7399 
7400   __ Sync(0);  // Barrier to prevent load-load reordering.
7401 
7402   // The actual reference load.
7403   if (index.IsValid()) {
7404     // Load types involving an "index": ArrayGet,
7405     // UnsafeGetObject/UnsafeGetObjectVolatile and UnsafeCASObject
7406     // intrinsics.
7407     // /* HeapReference<Object> */ ref = *(obj + offset + (index << scale_factor))
7408     if (index.IsConstant()) {
7409       size_t computed_offset =
7410           (index.GetConstant()->AsIntConstant()->GetValue() << scale_factor) + offset;
7411       __ LoadFromOffset(kLoadWord, ref_reg, obj, computed_offset);
7412     } else {
7413       // Handle the special case of the
7414       // UnsafeGetObject/UnsafeGetObjectVolatile and UnsafeCASObject
7415       // intrinsics, which use a register pair as index ("long
7416       // offset"), of which only the low part contains data.
7417       Register index_reg = index.IsRegisterPair()
7418           ? index.AsRegisterPairLow<Register>()
7419           : index.AsRegister<Register>();
7420       __ ShiftAndAdd(TMP, index_reg, obj, scale_factor, TMP);
7421       __ LoadFromOffset(kLoadWord, ref_reg, TMP, offset);
7422     }
7423   } else {
7424     // /* HeapReference<Object> */ ref = *(obj + offset)
7425     __ LoadFromOffset(kLoadWord, ref_reg, obj, offset);
7426   }
7427 
7428   // Object* ref = ref_addr->AsMirrorPtr()
7429   __ MaybeUnpoisonHeapReference(ref_reg);
7430 
7431   // Slow path marking the object `ref` when it is gray.
7432   SlowPathCodeMIPS* slow_path;
7433   if (always_update_field) {
7434     // ReadBarrierMarkAndUpdateFieldSlowPathMIPS only supports address
7435     // of the form `obj + field_offset`, where `obj` is a register and
7436     // `field_offset` is a register pair (of which only the lower half
7437     // is used). Thus `offset` and `scale_factor` above are expected
7438     // to be null in this code path.
7439     DCHECK_EQ(offset, 0u);
7440     DCHECK_EQ(scale_factor, ScaleFactor::TIMES_1);
7441     slow_path = new (GetScopedAllocator())
7442         ReadBarrierMarkAndUpdateFieldSlowPathMIPS(instruction,
7443                                                   ref,
7444                                                   obj,
7445                                                   /* field_offset= */ index,
7446                                                   temp_reg);
7447   } else {
7448     slow_path = new (GetScopedAllocator()) ReadBarrierMarkSlowPathMIPS(instruction, ref);
7449   }
7450   AddSlowPath(slow_path);
7451 
7452   // if (rb_state == ReadBarrier::GrayState())
7453   //   ref = ReadBarrier::Mark(ref);
7454   // Given the numeric representation, it's enough to check the low bit of the
7455   // rb_state. We do that by shifting the bit into the sign bit (31) and
7456   // performing a branch on less than zero.
7457   static_assert(ReadBarrier::NonGrayState() == 0, "Expecting non-gray to have value 0");
7458   static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1");
7459   static_assert(LockWord::kReadBarrierStateSize == 1, "Expecting 1-bit read barrier state size");
7460   __ Sll(temp_reg, temp_reg, 31 - LockWord::kReadBarrierStateShift);
7461   __ Bltz(temp_reg, slow_path->GetEntryLabel());
7462   __ Bind(slow_path->GetExitLabel());
7463 }
7464 
GenerateReadBarrierSlow(HInstruction * instruction,Location out,Location ref,Location obj,uint32_t offset,Location index)7465 void CodeGeneratorMIPS::GenerateReadBarrierSlow(HInstruction* instruction,
7466                                                 Location out,
7467                                                 Location ref,
7468                                                 Location obj,
7469                                                 uint32_t offset,
7470                                                 Location index) {
7471   DCHECK(kEmitCompilerReadBarrier);
7472 
7473   // Insert a slow path based read barrier *after* the reference load.
7474   //
7475   // If heap poisoning is enabled, the unpoisoning of the loaded
7476   // reference will be carried out by the runtime within the slow
7477   // path.
7478   //
7479   // Note that `ref` currently does not get unpoisoned (when heap
7480   // poisoning is enabled), which is alright as the `ref` argument is
7481   // not used by the artReadBarrierSlow entry point.
7482   //
7483   // TODO: Unpoison `ref` when it is used by artReadBarrierSlow.
7484   SlowPathCodeMIPS* slow_path = new (GetScopedAllocator())
7485       ReadBarrierForHeapReferenceSlowPathMIPS(instruction, out, ref, obj, offset, index);
7486   AddSlowPath(slow_path);
7487 
7488   __ B(slow_path->GetEntryLabel());
7489   __ Bind(slow_path->GetExitLabel());
7490 }
7491 
MaybeGenerateReadBarrierSlow(HInstruction * instruction,Location out,Location ref,Location obj,uint32_t offset,Location index)7492 void CodeGeneratorMIPS::MaybeGenerateReadBarrierSlow(HInstruction* instruction,
7493                                                      Location out,
7494                                                      Location ref,
7495                                                      Location obj,
7496                                                      uint32_t offset,
7497                                                      Location index) {
7498   if (kEmitCompilerReadBarrier) {
7499     // Baker's read barriers shall be handled by the fast path
7500     // (CodeGeneratorMIPS::GenerateReferenceLoadWithBakerReadBarrier).
7501     DCHECK(!kUseBakerReadBarrier);
7502     // If heap poisoning is enabled, unpoisoning will be taken care of
7503     // by the runtime within the slow path.
7504     GenerateReadBarrierSlow(instruction, out, ref, obj, offset, index);
7505   } else if (kPoisonHeapReferences) {
7506     __ UnpoisonHeapReference(out.AsRegister<Register>());
7507   }
7508 }
7509 
GenerateReadBarrierForRootSlow(HInstruction * instruction,Location out,Location root)7510 void CodeGeneratorMIPS::GenerateReadBarrierForRootSlow(HInstruction* instruction,
7511                                                        Location out,
7512                                                        Location root) {
7513   DCHECK(kEmitCompilerReadBarrier);
7514 
7515   // Insert a slow path based read barrier *after* the GC root load.
7516   //
7517   // Note that GC roots are not affected by heap poisoning, so we do
7518   // not need to do anything special for this here.
7519   SlowPathCodeMIPS* slow_path =
7520       new (GetScopedAllocator()) ReadBarrierForRootSlowPathMIPS(instruction, out, root);
7521   AddSlowPath(slow_path);
7522 
7523   __ B(slow_path->GetEntryLabel());
7524   __ Bind(slow_path->GetExitLabel());
7525 }
7526 
VisitInstanceOf(HInstanceOf * instruction)7527 void LocationsBuilderMIPS::VisitInstanceOf(HInstanceOf* instruction) {
7528   LocationSummary::CallKind call_kind = LocationSummary::kNoCall;
7529   TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
7530   bool baker_read_barrier_slow_path = false;
7531   switch (type_check_kind) {
7532     case TypeCheckKind::kExactCheck:
7533     case TypeCheckKind::kAbstractClassCheck:
7534     case TypeCheckKind::kClassHierarchyCheck:
7535     case TypeCheckKind::kArrayObjectCheck: {
7536       bool needs_read_barrier = CodeGenerator::InstanceOfNeedsReadBarrier(instruction);
7537       call_kind = needs_read_barrier ? LocationSummary::kCallOnSlowPath : LocationSummary::kNoCall;
7538       baker_read_barrier_slow_path = kUseBakerReadBarrier && needs_read_barrier;
7539       break;
7540     }
7541     case TypeCheckKind::kArrayCheck:
7542     case TypeCheckKind::kUnresolvedCheck:
7543     case TypeCheckKind::kInterfaceCheck:
7544       call_kind = LocationSummary::kCallOnSlowPath;
7545       break;
7546     case TypeCheckKind::kBitstringCheck:
7547       break;
7548   }
7549 
7550   LocationSummary* locations =
7551       new (GetGraph()->GetAllocator()) LocationSummary(instruction, call_kind);
7552   if (baker_read_barrier_slow_path) {
7553     locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty());  // No caller-save registers.
7554   }
7555   locations->SetInAt(0, Location::RequiresRegister());
7556   if (type_check_kind == TypeCheckKind::kBitstringCheck) {
7557     locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant()));
7558     locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant()));
7559     locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant()));
7560   } else {
7561     locations->SetInAt(1, Location::RequiresRegister());
7562   }
7563   // The output does overlap inputs.
7564   // Note that TypeCheckSlowPathMIPS uses this register too.
7565   locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
7566   locations->AddRegisterTemps(NumberOfInstanceOfTemps(type_check_kind));
7567 }
7568 
VisitInstanceOf(HInstanceOf * instruction)7569 void InstructionCodeGeneratorMIPS::VisitInstanceOf(HInstanceOf* instruction) {
7570   TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
7571   LocationSummary* locations = instruction->GetLocations();
7572   Location obj_loc = locations->InAt(0);
7573   Register obj = obj_loc.AsRegister<Register>();
7574   Location cls = locations->InAt(1);
7575   Location out_loc = locations->Out();
7576   Register out = out_loc.AsRegister<Register>();
7577   const size_t num_temps = NumberOfInstanceOfTemps(type_check_kind);
7578   DCHECK_LE(num_temps, 1u);
7579   Location maybe_temp_loc = (num_temps >= 1) ? locations->GetTemp(0) : Location::NoLocation();
7580   uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
7581   uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
7582   uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
7583   uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value();
7584   MipsLabel done;
7585   SlowPathCodeMIPS* slow_path = nullptr;
7586 
7587   // Return 0 if `obj` is null.
7588   // Avoid this check if we know `obj` is not null.
7589   if (instruction->MustDoNullCheck()) {
7590     __ Move(out, ZERO);
7591     __ Beqz(obj, &done);
7592   }
7593 
7594   switch (type_check_kind) {
7595     case TypeCheckKind::kExactCheck: {
7596       ReadBarrierOption read_barrier_option =
7597           CodeGenerator::ReadBarrierOptionForInstanceOf(instruction);
7598       // /* HeapReference<Class> */ out = obj->klass_
7599       GenerateReferenceLoadTwoRegisters(instruction,
7600                                         out_loc,
7601                                         obj_loc,
7602                                         class_offset,
7603                                         maybe_temp_loc,
7604                                         read_barrier_option);
7605       // Classes must be equal for the instanceof to succeed.
7606       __ Xor(out, out, cls.AsRegister<Register>());
7607       __ Sltiu(out, out, 1);
7608       break;
7609     }
7610 
7611     case TypeCheckKind::kAbstractClassCheck: {
7612       ReadBarrierOption read_barrier_option =
7613           CodeGenerator::ReadBarrierOptionForInstanceOf(instruction);
7614       // /* HeapReference<Class> */ out = obj->klass_
7615       GenerateReferenceLoadTwoRegisters(instruction,
7616                                         out_loc,
7617                                         obj_loc,
7618                                         class_offset,
7619                                         maybe_temp_loc,
7620                                         read_barrier_option);
7621       // If the class is abstract, we eagerly fetch the super class of the
7622       // object to avoid doing a comparison we know will fail.
7623       MipsLabel loop;
7624       __ Bind(&loop);
7625       // /* HeapReference<Class> */ out = out->super_class_
7626       GenerateReferenceLoadOneRegister(instruction,
7627                                        out_loc,
7628                                        super_offset,
7629                                        maybe_temp_loc,
7630                                        read_barrier_option);
7631       // If `out` is null, we use it for the result, and jump to `done`.
7632       __ Beqz(out, &done);
7633       __ Bne(out, cls.AsRegister<Register>(), &loop);
7634       __ LoadConst32(out, 1);
7635       break;
7636     }
7637 
7638     case TypeCheckKind::kClassHierarchyCheck: {
7639       ReadBarrierOption read_barrier_option =
7640           CodeGenerator::ReadBarrierOptionForInstanceOf(instruction);
7641       // /* HeapReference<Class> */ out = obj->klass_
7642       GenerateReferenceLoadTwoRegisters(instruction,
7643                                         out_loc,
7644                                         obj_loc,
7645                                         class_offset,
7646                                         maybe_temp_loc,
7647                                         read_barrier_option);
7648       // Walk over the class hierarchy to find a match.
7649       MipsLabel loop, success;
7650       __ Bind(&loop);
7651       __ Beq(out, cls.AsRegister<Register>(), &success);
7652       // /* HeapReference<Class> */ out = out->super_class_
7653       GenerateReferenceLoadOneRegister(instruction,
7654                                        out_loc,
7655                                        super_offset,
7656                                        maybe_temp_loc,
7657                                        read_barrier_option);
7658       __ Bnez(out, &loop);
7659       // If `out` is null, we use it for the result, and jump to `done`.
7660       __ B(&done);
7661       __ Bind(&success);
7662       __ LoadConst32(out, 1);
7663       break;
7664     }
7665 
7666     case TypeCheckKind::kArrayObjectCheck: {
7667       ReadBarrierOption read_barrier_option =
7668           CodeGenerator::ReadBarrierOptionForInstanceOf(instruction);
7669       // /* HeapReference<Class> */ out = obj->klass_
7670       GenerateReferenceLoadTwoRegisters(instruction,
7671                                         out_loc,
7672                                         obj_loc,
7673                                         class_offset,
7674                                         maybe_temp_loc,
7675                                         read_barrier_option);
7676       // Do an exact check.
7677       MipsLabel success;
7678       __ Beq(out, cls.AsRegister<Register>(), &success);
7679       // Otherwise, we need to check that the object's class is a non-primitive array.
7680       // /* HeapReference<Class> */ out = out->component_type_
7681       GenerateReferenceLoadOneRegister(instruction,
7682                                        out_loc,
7683                                        component_offset,
7684                                        maybe_temp_loc,
7685                                        read_barrier_option);
7686       // If `out` is null, we use it for the result, and jump to `done`.
7687       __ Beqz(out, &done);
7688       __ LoadFromOffset(kLoadUnsignedHalfword, out, out, primitive_offset);
7689       static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
7690       __ Sltiu(out, out, 1);
7691       __ B(&done);
7692       __ Bind(&success);
7693       __ LoadConst32(out, 1);
7694       break;
7695     }
7696 
7697     case TypeCheckKind::kArrayCheck: {
7698       // No read barrier since the slow path will retry upon failure.
7699       // /* HeapReference<Class> */ out = obj->klass_
7700       GenerateReferenceLoadTwoRegisters(instruction,
7701                                         out_loc,
7702                                         obj_loc,
7703                                         class_offset,
7704                                         maybe_temp_loc,
7705                                         kWithoutReadBarrier);
7706       DCHECK(locations->OnlyCallsOnSlowPath());
7707       slow_path = new (codegen_->GetScopedAllocator()) TypeCheckSlowPathMIPS(
7708           instruction, /* is_fatal= */ false);
7709       codegen_->AddSlowPath(slow_path);
7710       __ Bne(out, cls.AsRegister<Register>(), slow_path->GetEntryLabel());
7711       __ LoadConst32(out, 1);
7712       break;
7713     }
7714 
7715     case TypeCheckKind::kUnresolvedCheck:
7716     case TypeCheckKind::kInterfaceCheck: {
7717       // Note that we indeed only call on slow path, but we always go
7718       // into the slow path for the unresolved and interface check
7719       // cases.
7720       //
7721       // We cannot directly call the InstanceofNonTrivial runtime
7722       // entry point without resorting to a type checking slow path
7723       // here (i.e. by calling InvokeRuntime directly), as it would
7724       // require to assign fixed registers for the inputs of this
7725       // HInstanceOf instruction (following the runtime calling
7726       // convention), which might be cluttered by the potential first
7727       // read barrier emission at the beginning of this method.
7728       //
7729       // TODO: Introduce a new runtime entry point taking the object
7730       // to test (instead of its class) as argument, and let it deal
7731       // with the read barrier issues. This will let us refactor this
7732       // case of the `switch` code as it was previously (with a direct
7733       // call to the runtime not using a type checking slow path).
7734       // This should also be beneficial for the other cases above.
7735       DCHECK(locations->OnlyCallsOnSlowPath());
7736       slow_path = new (codegen_->GetScopedAllocator()) TypeCheckSlowPathMIPS(
7737           instruction, /* is_fatal= */ false);
7738       codegen_->AddSlowPath(slow_path);
7739       __ B(slow_path->GetEntryLabel());
7740       break;
7741     }
7742 
7743     case TypeCheckKind::kBitstringCheck: {
7744       // /* HeapReference<Class> */ temp = obj->klass_
7745       GenerateReferenceLoadTwoRegisters(instruction,
7746                                         out_loc,
7747                                         obj_loc,
7748                                         class_offset,
7749                                         maybe_temp_loc,
7750                                         kWithoutReadBarrier);
7751 
7752       GenerateBitstringTypeCheckCompare(instruction, out);
7753       __ Sltiu(out, out, 1);
7754       break;
7755     }
7756   }
7757 
7758   __ Bind(&done);
7759 
7760   if (slow_path != nullptr) {
7761     __ Bind(slow_path->GetExitLabel());
7762   }
7763 }
7764 
VisitIntConstant(HIntConstant * constant)7765 void LocationsBuilderMIPS::VisitIntConstant(HIntConstant* constant) {
7766   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(constant);
7767   locations->SetOut(Location::ConstantLocation(constant));
7768 }
7769 
VisitIntConstant(HIntConstant * constant ATTRIBUTE_UNUSED)7770 void InstructionCodeGeneratorMIPS::VisitIntConstant(HIntConstant* constant ATTRIBUTE_UNUSED) {
7771   // Will be generated at use site.
7772 }
7773 
VisitNullConstant(HNullConstant * constant)7774 void LocationsBuilderMIPS::VisitNullConstant(HNullConstant* constant) {
7775   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(constant);
7776   locations->SetOut(Location::ConstantLocation(constant));
7777 }
7778 
VisitNullConstant(HNullConstant * constant ATTRIBUTE_UNUSED)7779 void InstructionCodeGeneratorMIPS::VisitNullConstant(HNullConstant* constant ATTRIBUTE_UNUSED) {
7780   // Will be generated at use site.
7781 }
7782 
HandleInvoke(HInvoke * invoke)7783 void LocationsBuilderMIPS::HandleInvoke(HInvoke* invoke) {
7784   InvokeDexCallingConventionVisitorMIPS calling_convention_visitor;
7785   CodeGenerator::CreateCommonInvokeLocationSummary(invoke, &calling_convention_visitor);
7786 }
7787 
VisitInvokeInterface(HInvokeInterface * invoke)7788 void LocationsBuilderMIPS::VisitInvokeInterface(HInvokeInterface* invoke) {
7789   HandleInvoke(invoke);
7790   // The register T7 is required to be used for the hidden argument in
7791   // art_quick_imt_conflict_trampoline, so add the hidden argument.
7792   invoke->GetLocations()->AddTemp(Location::RegisterLocation(T7));
7793 }
7794 
VisitInvokeInterface(HInvokeInterface * invoke)7795 void InstructionCodeGeneratorMIPS::VisitInvokeInterface(HInvokeInterface* invoke) {
7796   // TODO: b/18116999, our IMTs can miss an IncompatibleClassChangeError.
7797   Register temp = invoke->GetLocations()->GetTemp(0).AsRegister<Register>();
7798   Location receiver = invoke->GetLocations()->InAt(0);
7799   uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
7800   Offset entry_point = ArtMethod::EntryPointFromQuickCompiledCodeOffset(kMipsPointerSize);
7801 
7802   // temp = object->GetClass();
7803   if (receiver.IsStackSlot()) {
7804     __ LoadFromOffset(kLoadWord, temp, SP, receiver.GetStackIndex());
7805     __ LoadFromOffset(kLoadWord, temp, temp, class_offset);
7806   } else {
7807     __ LoadFromOffset(kLoadWord, temp, receiver.AsRegister<Register>(), class_offset);
7808   }
7809   codegen_->MaybeRecordImplicitNullCheck(invoke);
7810   // Instead of simply (possibly) unpoisoning `temp` here, we should
7811   // emit a read barrier for the previous class reference load.
7812   // However this is not required in practice, as this is an
7813   // intermediate/temporary reference and because the current
7814   // concurrent copying collector keeps the from-space memory
7815   // intact/accessible until the end of the marking phase (the
7816   // concurrent copying collector may not in the future).
7817   __ MaybeUnpoisonHeapReference(temp);
7818   __ LoadFromOffset(kLoadWord, temp, temp,
7819       mirror::Class::ImtPtrOffset(kMipsPointerSize).Uint32Value());
7820   uint32_t method_offset = static_cast<uint32_t>(ImTable::OffsetOfElement(
7821       invoke->GetImtIndex(), kMipsPointerSize));
7822   // temp = temp->GetImtEntryAt(method_offset);
7823   __ LoadFromOffset(kLoadWord, temp, temp, method_offset);
7824   // T9 = temp->GetEntryPoint();
7825   __ LoadFromOffset(kLoadWord, T9, temp, entry_point.Int32Value());
7826   // Set the hidden argument.
7827   __ LoadConst32(invoke->GetLocations()->GetTemp(1).AsRegister<Register>(),
7828                  invoke->GetDexMethodIndex());
7829   // T9();
7830   __ Jalr(T9);
7831   __ NopIfNoReordering();
7832   DCHECK(!codegen_->IsLeafMethod());
7833   codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
7834 }
7835 
VisitInvokeVirtual(HInvokeVirtual * invoke)7836 void LocationsBuilderMIPS::VisitInvokeVirtual(HInvokeVirtual* invoke) {
7837   IntrinsicLocationsBuilderMIPS intrinsic(codegen_);
7838   if (intrinsic.TryDispatch(invoke)) {
7839     return;
7840   }
7841 
7842   HandleInvoke(invoke);
7843 }
7844 
VisitInvokeStaticOrDirect(HInvokeStaticOrDirect * invoke)7845 void LocationsBuilderMIPS::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
7846   // Explicit clinit checks triggered by static invokes must have been pruned by
7847   // art::PrepareForRegisterAllocation.
7848   DCHECK(!invoke->IsStaticWithExplicitClinitCheck());
7849 
7850   bool is_r6 = codegen_->GetInstructionSetFeatures().IsR6();
7851   bool has_irreducible_loops = codegen_->GetGraph()->HasIrreducibleLoops();
7852   bool has_extra_input = invoke->HasPcRelativeMethodLoadKind() && !is_r6 && !has_irreducible_loops;
7853 
7854   IntrinsicLocationsBuilderMIPS intrinsic(codegen_);
7855   if (intrinsic.TryDispatch(invoke)) {
7856     if (invoke->GetLocations()->CanCall() && has_extra_input) {
7857       invoke->GetLocations()->SetInAt(invoke->GetSpecialInputIndex(), Location::Any());
7858     }
7859     return;
7860   }
7861 
7862   HandleInvoke(invoke);
7863 
7864   // Add the extra input register if either the dex cache array base register
7865   // or the PC-relative base register for accessing literals is needed.
7866   if (has_extra_input) {
7867     invoke->GetLocations()->SetInAt(invoke->GetSpecialInputIndex(), Location::RequiresRegister());
7868   }
7869 }
7870 
VisitInvokePolymorphic(HInvokePolymorphic * invoke)7871 void LocationsBuilderMIPS::VisitInvokePolymorphic(HInvokePolymorphic* invoke) {
7872   HandleInvoke(invoke);
7873 }
7874 
VisitInvokePolymorphic(HInvokePolymorphic * invoke)7875 void InstructionCodeGeneratorMIPS::VisitInvokePolymorphic(HInvokePolymorphic* invoke) {
7876   codegen_->GenerateInvokePolymorphicCall(invoke);
7877 }
7878 
VisitInvokeCustom(HInvokeCustom * invoke)7879 void LocationsBuilderMIPS::VisitInvokeCustom(HInvokeCustom* invoke) {
7880   HandleInvoke(invoke);
7881 }
7882 
VisitInvokeCustom(HInvokeCustom * invoke)7883 void InstructionCodeGeneratorMIPS::VisitInvokeCustom(HInvokeCustom* invoke) {
7884   codegen_->GenerateInvokeCustomCall(invoke);
7885 }
7886 
TryGenerateIntrinsicCode(HInvoke * invoke,CodeGeneratorMIPS * codegen)7887 static bool TryGenerateIntrinsicCode(HInvoke* invoke, CodeGeneratorMIPS* codegen) {
7888   if (invoke->GetLocations()->Intrinsified()) {
7889     IntrinsicCodeGeneratorMIPS intrinsic(codegen);
7890     intrinsic.Dispatch(invoke);
7891     return true;
7892   }
7893   return false;
7894 }
7895 
GetSupportedLoadStringKind(HLoadString::LoadKind desired_string_load_kind)7896 HLoadString::LoadKind CodeGeneratorMIPS::GetSupportedLoadStringKind(
7897     HLoadString::LoadKind desired_string_load_kind) {
7898   switch (desired_string_load_kind) {
7899     case HLoadString::LoadKind::kBootImageLinkTimePcRelative:
7900     case HLoadString::LoadKind::kBootImageRelRo:
7901     case HLoadString::LoadKind::kBssEntry:
7902       DCHECK(!Runtime::Current()->UseJitCompilation());
7903       break;
7904     case HLoadString::LoadKind::kJitBootImageAddress:
7905     case HLoadString::LoadKind::kJitTableAddress:
7906       DCHECK(Runtime::Current()->UseJitCompilation());
7907       break;
7908     case HLoadString::LoadKind::kRuntimeCall:
7909       break;
7910   }
7911   return desired_string_load_kind;
7912 }
7913 
GetSupportedLoadClassKind(HLoadClass::LoadKind desired_class_load_kind)7914 HLoadClass::LoadKind CodeGeneratorMIPS::GetSupportedLoadClassKind(
7915     HLoadClass::LoadKind desired_class_load_kind) {
7916   switch (desired_class_load_kind) {
7917     case HLoadClass::LoadKind::kInvalid:
7918       LOG(FATAL) << "UNREACHABLE";
7919       UNREACHABLE();
7920     case HLoadClass::LoadKind::kReferrersClass:
7921       break;
7922     case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
7923     case HLoadClass::LoadKind::kBootImageRelRo:
7924     case HLoadClass::LoadKind::kBssEntry:
7925       DCHECK(!Runtime::Current()->UseJitCompilation());
7926       break;
7927     case HLoadClass::LoadKind::kJitBootImageAddress:
7928     case HLoadClass::LoadKind::kJitTableAddress:
7929       DCHECK(Runtime::Current()->UseJitCompilation());
7930       break;
7931     case HLoadClass::LoadKind::kRuntimeCall:
7932       break;
7933   }
7934   return desired_class_load_kind;
7935 }
7936 
GetInvokeStaticOrDirectExtraParameter(HInvokeStaticOrDirect * invoke,Register temp)7937 Register CodeGeneratorMIPS::GetInvokeStaticOrDirectExtraParameter(HInvokeStaticOrDirect* invoke,
7938                                                                   Register temp) {
7939   CHECK(!GetInstructionSetFeatures().IsR6());
7940   CHECK(!GetGraph()->HasIrreducibleLoops());
7941   CHECK_EQ(invoke->InputCount(), invoke->GetNumberOfArguments() + 1u);
7942   Location location = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex());
7943   if (!invoke->GetLocations()->Intrinsified()) {
7944     return location.AsRegister<Register>();
7945   }
7946   // For intrinsics we allow any location, so it may be on the stack.
7947   if (!location.IsRegister()) {
7948     __ LoadFromOffset(kLoadWord, temp, SP, location.GetStackIndex());
7949     return temp;
7950   }
7951   // For register locations, check if the register was saved. If so, get it from the stack.
7952   // Note: There is a chance that the register was saved but not overwritten, so we could
7953   // save one load. However, since this is just an intrinsic slow path we prefer this
7954   // simple and more robust approach rather that trying to determine if that's the case.
7955   SlowPathCode* slow_path = GetCurrentSlowPath();
7956   DCHECK(slow_path != nullptr);  // For intrinsified invokes the call is emitted on the slow path.
7957   if (slow_path->IsCoreRegisterSaved(location.AsRegister<Register>())) {
7958     int stack_offset = slow_path->GetStackOffsetOfCoreRegister(location.AsRegister<Register>());
7959     __ LoadFromOffset(kLoadWord, temp, SP, stack_offset);
7960     return temp;
7961   }
7962   return location.AsRegister<Register>();
7963 }
7964 
GetSupportedInvokeStaticOrDirectDispatch(const HInvokeStaticOrDirect::DispatchInfo & desired_dispatch_info,ArtMethod * method ATTRIBUTE_UNUSED)7965 HInvokeStaticOrDirect::DispatchInfo CodeGeneratorMIPS::GetSupportedInvokeStaticOrDirectDispatch(
7966       const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
7967       ArtMethod* method ATTRIBUTE_UNUSED) {
7968   return desired_dispatch_info;
7969 }
7970 
GenerateStaticOrDirectCall(HInvokeStaticOrDirect * invoke,Location temp,SlowPathCode * slow_path)7971 void CodeGeneratorMIPS::GenerateStaticOrDirectCall(
7972     HInvokeStaticOrDirect* invoke, Location temp, SlowPathCode* slow_path) {
7973   // All registers are assumed to be correctly set up per the calling convention.
7974   Location callee_method = temp;  // For all kinds except kRecursive, callee will be in temp.
7975   HInvokeStaticOrDirect::MethodLoadKind method_load_kind = invoke->GetMethodLoadKind();
7976   HInvokeStaticOrDirect::CodePtrLocation code_ptr_location = invoke->GetCodePtrLocation();
7977   bool is_r6 = GetInstructionSetFeatures().IsR6();
7978   bool has_irreducible_loops = GetGraph()->HasIrreducibleLoops();
7979   Register base_reg = (invoke->HasPcRelativeMethodLoadKind() && !is_r6 && !has_irreducible_loops)
7980       ? GetInvokeStaticOrDirectExtraParameter(invoke, temp.AsRegister<Register>())
7981       : ZERO;
7982 
7983   switch (method_load_kind) {
7984     case HInvokeStaticOrDirect::MethodLoadKind::kStringInit: {
7985       // temp = thread->string_init_entrypoint
7986       uint32_t offset =
7987           GetThreadOffset<kMipsPointerSize>(invoke->GetStringInitEntryPoint()).Int32Value();
7988       __ LoadFromOffset(kLoadWord,
7989                         temp.AsRegister<Register>(),
7990                         TR,
7991                         offset);
7992       break;
7993     }
7994     case HInvokeStaticOrDirect::MethodLoadKind::kRecursive:
7995       callee_method = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex());
7996       break;
7997     case HInvokeStaticOrDirect::MethodLoadKind::kBootImageLinkTimePcRelative: {
7998       DCHECK(GetCompilerOptions().IsBootImage());
7999       PcRelativePatchInfo* info_high = NewBootImageMethodPatch(invoke->GetTargetMethod());
8000       PcRelativePatchInfo* info_low =
8001           NewBootImageMethodPatch(invoke->GetTargetMethod(), info_high);
8002       Register temp_reg = temp.AsRegister<Register>();
8003       EmitPcRelativeAddressPlaceholderHigh(info_high, TMP, base_reg);
8004       __ Addiu(temp_reg, TMP, /* imm16= */ 0x5678, &info_low->label);
8005       break;
8006     }
8007     case HInvokeStaticOrDirect::MethodLoadKind::kBootImageRelRo: {
8008       uint32_t boot_image_offset = GetBootImageOffset(invoke);
8009       PcRelativePatchInfo* info_high = NewBootImageRelRoPatch(boot_image_offset);
8010       PcRelativePatchInfo* info_low = NewBootImageRelRoPatch(boot_image_offset, info_high);
8011       Register temp_reg = temp.AsRegister<Register>();
8012       EmitPcRelativeAddressPlaceholderHigh(info_high, TMP, base_reg);
8013       __ Lw(temp_reg, TMP, /* imm16= */ 0x5678, &info_low->label);
8014       break;
8015     }
8016     case HInvokeStaticOrDirect::MethodLoadKind::kBssEntry: {
8017       PcRelativePatchInfo* info_high = NewMethodBssEntryPatch(
8018           MethodReference(&GetGraph()->GetDexFile(), invoke->GetDexMethodIndex()));
8019       PcRelativePatchInfo* info_low = NewMethodBssEntryPatch(
8020           MethodReference(&GetGraph()->GetDexFile(), invoke->GetDexMethodIndex()), info_high);
8021       Register temp_reg = temp.AsRegister<Register>();
8022       EmitPcRelativeAddressPlaceholderHigh(info_high, TMP, base_reg);
8023       __ Lw(temp_reg, TMP, /* imm16= */ 0x5678, &info_low->label);
8024       break;
8025     }
8026     case HInvokeStaticOrDirect::MethodLoadKind::kJitDirectAddress:
8027       __ LoadConst32(temp.AsRegister<Register>(), invoke->GetMethodAddress());
8028       break;
8029     case HInvokeStaticOrDirect::MethodLoadKind::kRuntimeCall: {
8030       GenerateInvokeStaticOrDirectRuntimeCall(invoke, temp, slow_path);
8031       return;  // No code pointer retrieval; the runtime performs the call directly.
8032     }
8033   }
8034 
8035   switch (code_ptr_location) {
8036     case HInvokeStaticOrDirect::CodePtrLocation::kCallSelf:
8037       __ Bal(&frame_entry_label_);
8038       break;
8039     case HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod:
8040       // T9 = callee_method->entry_point_from_quick_compiled_code_;
8041       __ LoadFromOffset(kLoadWord,
8042                         T9,
8043                         callee_method.AsRegister<Register>(),
8044                         ArtMethod::EntryPointFromQuickCompiledCodeOffset(
8045                             kMipsPointerSize).Int32Value());
8046       // T9()
8047       __ Jalr(T9);
8048       __ NopIfNoReordering();
8049       break;
8050   }
8051   RecordPcInfo(invoke, invoke->GetDexPc(), slow_path);
8052 
8053   DCHECK(!IsLeafMethod());
8054 }
8055 
VisitInvokeStaticOrDirect(HInvokeStaticOrDirect * invoke)8056 void InstructionCodeGeneratorMIPS::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
8057   // Explicit clinit checks triggered by static invokes must have been pruned by
8058   // art::PrepareForRegisterAllocation.
8059   DCHECK(!invoke->IsStaticWithExplicitClinitCheck());
8060 
8061   if (TryGenerateIntrinsicCode(invoke, codegen_)) {
8062     return;
8063   }
8064 
8065   LocationSummary* locations = invoke->GetLocations();
8066   codegen_->GenerateStaticOrDirectCall(invoke,
8067                                        locations->HasTemps()
8068                                            ? locations->GetTemp(0)
8069                                            : Location::NoLocation());
8070 }
8071 
GenerateVirtualCall(HInvokeVirtual * invoke,Location temp_location,SlowPathCode * slow_path)8072 void CodeGeneratorMIPS::GenerateVirtualCall(
8073     HInvokeVirtual* invoke, Location temp_location, SlowPathCode* slow_path) {
8074   // Use the calling convention instead of the location of the receiver, as
8075   // intrinsics may have put the receiver in a different register. In the intrinsics
8076   // slow path, the arguments have been moved to the right place, so here we are
8077   // guaranteed that the receiver is the first register of the calling convention.
8078   InvokeDexCallingConvention calling_convention;
8079   Register receiver = calling_convention.GetRegisterAt(0);
8080 
8081   Register temp = temp_location.AsRegister<Register>();
8082   size_t method_offset = mirror::Class::EmbeddedVTableEntryOffset(
8083       invoke->GetVTableIndex(), kMipsPointerSize).SizeValue();
8084   uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
8085   Offset entry_point = ArtMethod::EntryPointFromQuickCompiledCodeOffset(kMipsPointerSize);
8086 
8087   // temp = object->GetClass();
8088   __ LoadFromOffset(kLoadWord, temp, receiver, class_offset);
8089   MaybeRecordImplicitNullCheck(invoke);
8090   // Instead of simply (possibly) unpoisoning `temp` here, we should
8091   // emit a read barrier for the previous class reference load.
8092   // However this is not required in practice, as this is an
8093   // intermediate/temporary reference and because the current
8094   // concurrent copying collector keeps the from-space memory
8095   // intact/accessible until the end of the marking phase (the
8096   // concurrent copying collector may not in the future).
8097   __ MaybeUnpoisonHeapReference(temp);
8098   // temp = temp->GetMethodAt(method_offset);
8099   __ LoadFromOffset(kLoadWord, temp, temp, method_offset);
8100   // T9 = temp->GetEntryPoint();
8101   __ LoadFromOffset(kLoadWord, T9, temp, entry_point.Int32Value());
8102   // T9();
8103   __ Jalr(T9);
8104   __ NopIfNoReordering();
8105   RecordPcInfo(invoke, invoke->GetDexPc(), slow_path);
8106 }
8107 
VisitInvokeVirtual(HInvokeVirtual * invoke)8108 void InstructionCodeGeneratorMIPS::VisitInvokeVirtual(HInvokeVirtual* invoke) {
8109   if (TryGenerateIntrinsicCode(invoke, codegen_)) {
8110     return;
8111   }
8112 
8113   codegen_->GenerateVirtualCall(invoke, invoke->GetLocations()->GetTemp(0));
8114   DCHECK(!codegen_->IsLeafMethod());
8115 }
8116 
VisitLoadClass(HLoadClass * cls)8117 void LocationsBuilderMIPS::VisitLoadClass(HLoadClass* cls) {
8118   HLoadClass::LoadKind load_kind = cls->GetLoadKind();
8119   if (load_kind == HLoadClass::LoadKind::kRuntimeCall) {
8120     InvokeRuntimeCallingConvention calling_convention;
8121     Location loc = Location::RegisterLocation(calling_convention.GetRegisterAt(0));
8122     CodeGenerator::CreateLoadClassRuntimeCallLocationSummary(cls, loc, loc);
8123     return;
8124   }
8125   DCHECK(!cls->NeedsAccessCheck());
8126   const bool isR6 = codegen_->GetInstructionSetFeatures().IsR6();
8127   const bool has_irreducible_loops = codegen_->GetGraph()->HasIrreducibleLoops();
8128   const bool requires_read_barrier = kEmitCompilerReadBarrier && !cls->IsInBootImage();
8129   LocationSummary::CallKind call_kind = (cls->NeedsEnvironment() || requires_read_barrier)
8130       ? LocationSummary::kCallOnSlowPath
8131       : LocationSummary::kNoCall;
8132   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(cls, call_kind);
8133   if (kUseBakerReadBarrier && requires_read_barrier && !cls->NeedsEnvironment()) {
8134     locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty());  // No caller-save registers.
8135   }
8136   switch (load_kind) {
8137     // We need an extra register for PC-relative literals on R2.
8138     case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
8139     case HLoadClass::LoadKind::kBootImageRelRo:
8140     case HLoadClass::LoadKind::kBssEntry:
8141     case HLoadClass::LoadKind::kJitBootImageAddress:
8142       if (isR6) {
8143         break;
8144       }
8145       if (has_irreducible_loops) {
8146         if (load_kind != HLoadClass::LoadKind::kJitBootImageAddress) {
8147           codegen_->ClobberRA();
8148         }
8149         break;
8150       }
8151       FALLTHROUGH_INTENDED;
8152     case HLoadClass::LoadKind::kReferrersClass:
8153       locations->SetInAt(0, Location::RequiresRegister());
8154       break;
8155     default:
8156       break;
8157   }
8158   locations->SetOut(Location::RequiresRegister());
8159   if (load_kind == HLoadClass::LoadKind::kBssEntry) {
8160     if (!kUseReadBarrier || kUseBakerReadBarrier) {
8161       // Rely on the type resolution or initialization and marking to save everything we need.
8162       locations->SetCustomSlowPathCallerSaves(OneRegInReferenceOutSaveEverythingCallerSaves());
8163     } else {
8164       // For non-Baker read barriers we have a temp-clobbering call.
8165     }
8166   }
8167 }
8168 
8169 // NO_THREAD_SAFETY_ANALYSIS as we manipulate handles whose internal object we know does not
8170 // move.
VisitLoadClass(HLoadClass * cls)8171 void InstructionCodeGeneratorMIPS::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAFETY_ANALYSIS {
8172   HLoadClass::LoadKind load_kind = cls->GetLoadKind();
8173   if (load_kind == HLoadClass::LoadKind::kRuntimeCall) {
8174     codegen_->GenerateLoadClassRuntimeCall(cls);
8175     return;
8176   }
8177   DCHECK(!cls->NeedsAccessCheck());
8178 
8179   LocationSummary* locations = cls->GetLocations();
8180   Location out_loc = locations->Out();
8181   Register out = out_loc.AsRegister<Register>();
8182   Register base_or_current_method_reg;
8183   bool isR6 = codegen_->GetInstructionSetFeatures().IsR6();
8184   bool has_irreducible_loops = GetGraph()->HasIrreducibleLoops();
8185   switch (load_kind) {
8186     // We need an extra register for PC-relative literals on R2.
8187     case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
8188     case HLoadClass::LoadKind::kBootImageRelRo:
8189     case HLoadClass::LoadKind::kBssEntry:
8190     case HLoadClass::LoadKind::kJitBootImageAddress:
8191       base_or_current_method_reg =
8192           (isR6 || has_irreducible_loops) ? ZERO : locations->InAt(0).AsRegister<Register>();
8193       break;
8194     case HLoadClass::LoadKind::kReferrersClass:
8195     case HLoadClass::LoadKind::kRuntimeCall:
8196       base_or_current_method_reg = locations->InAt(0).AsRegister<Register>();
8197       break;
8198     default:
8199       base_or_current_method_reg = ZERO;
8200       break;
8201   }
8202 
8203   const ReadBarrierOption read_barrier_option = cls->IsInBootImage()
8204       ? kWithoutReadBarrier
8205       : kCompilerReadBarrierOption;
8206   bool generate_null_check = false;
8207   switch (load_kind) {
8208     case HLoadClass::LoadKind::kReferrersClass: {
8209       DCHECK(!cls->CanCallRuntime());
8210       DCHECK(!cls->MustGenerateClinitCheck());
8211       // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_
8212       GenerateGcRootFieldLoad(cls,
8213                               out_loc,
8214                               base_or_current_method_reg,
8215                               ArtMethod::DeclaringClassOffset().Int32Value(),
8216                               read_barrier_option);
8217       break;
8218     }
8219     case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: {
8220       DCHECK(codegen_->GetCompilerOptions().IsBootImage());
8221       DCHECK_EQ(read_barrier_option, kWithoutReadBarrier);
8222       CodeGeneratorMIPS::PcRelativePatchInfo* info_high =
8223           codegen_->NewBootImageTypePatch(cls->GetDexFile(), cls->GetTypeIndex());
8224       CodeGeneratorMIPS::PcRelativePatchInfo* info_low =
8225           codegen_->NewBootImageTypePatch(cls->GetDexFile(), cls->GetTypeIndex(), info_high);
8226       codegen_->EmitPcRelativeAddressPlaceholderHigh(info_high,
8227                                                      out,
8228                                                      base_or_current_method_reg);
8229       __ Addiu(out, out, /* imm16= */ 0x5678, &info_low->label);
8230       break;
8231     }
8232     case HLoadClass::LoadKind::kBootImageRelRo: {
8233       DCHECK(!codegen_->GetCompilerOptions().IsBootImage());
8234       uint32_t boot_image_offset = codegen_->GetBootImageOffset(cls);
8235       CodeGeneratorMIPS::PcRelativePatchInfo* info_high =
8236           codegen_->NewBootImageRelRoPatch(boot_image_offset);
8237       CodeGeneratorMIPS::PcRelativePatchInfo* info_low =
8238           codegen_->NewBootImageRelRoPatch(boot_image_offset, info_high);
8239       codegen_->EmitPcRelativeAddressPlaceholderHigh(info_high,
8240                                                      out,
8241                                                      base_or_current_method_reg);
8242       __ Lw(out, out, /* imm16= */ 0x5678, &info_low->label);
8243       break;
8244     }
8245     case HLoadClass::LoadKind::kBssEntry: {
8246       CodeGeneratorMIPS::PcRelativePatchInfo* bss_info_high =
8247           codegen_->NewTypeBssEntryPatch(cls->GetDexFile(), cls->GetTypeIndex());
8248       CodeGeneratorMIPS::PcRelativePatchInfo* info_low =
8249           codegen_->NewTypeBssEntryPatch(cls->GetDexFile(), cls->GetTypeIndex(), bss_info_high);
8250       codegen_->EmitPcRelativeAddressPlaceholderHigh(bss_info_high,
8251                                                      out,
8252                                                      base_or_current_method_reg);
8253       GenerateGcRootFieldLoad(cls,
8254                               out_loc,
8255                               out,
8256                               /* offset= */ 0x5678,
8257                               read_barrier_option,
8258                               &info_low->label);
8259       generate_null_check = true;
8260       break;
8261     }
8262     case HLoadClass::LoadKind::kJitBootImageAddress: {
8263       DCHECK_EQ(read_barrier_option, kWithoutReadBarrier);
8264       uint32_t address = reinterpret_cast32<uint32_t>(cls->GetClass().Get());
8265       DCHECK_NE(address, 0u);
8266       if (isR6 || !has_irreducible_loops) {
8267         __ LoadLiteral(out,
8268                        base_or_current_method_reg,
8269                        codegen_->DeduplicateBootImageAddressLiteral(address));
8270       } else {
8271         __ LoadConst32(out, address);
8272       }
8273       break;
8274     }
8275     case HLoadClass::LoadKind::kJitTableAddress: {
8276       CodeGeneratorMIPS::JitPatchInfo* info = codegen_->NewJitRootClassPatch(cls->GetDexFile(),
8277                                                                              cls->GetTypeIndex(),
8278                                                                              cls->GetClass());
8279       bool reordering = __ SetReorder(false);
8280       __ Bind(&info->high_label);
8281       __ Lui(out, /* imm16= */ 0x1234);
8282       __ SetReorder(reordering);
8283       GenerateGcRootFieldLoad(cls,
8284                               out_loc,
8285                               out,
8286                               /* offset= */ 0x5678,
8287                               read_barrier_option,
8288                               &info->low_label);
8289       break;
8290     }
8291     case HLoadClass::LoadKind::kRuntimeCall:
8292     case HLoadClass::LoadKind::kInvalid:
8293       LOG(FATAL) << "UNREACHABLE";
8294       UNREACHABLE();
8295   }
8296 
8297   if (generate_null_check || cls->MustGenerateClinitCheck()) {
8298     DCHECK(cls->CanCallRuntime());
8299     SlowPathCodeMIPS* slow_path =
8300         new (codegen_->GetScopedAllocator()) LoadClassSlowPathMIPS(cls, cls);
8301     codegen_->AddSlowPath(slow_path);
8302     if (generate_null_check) {
8303       __ Beqz(out, slow_path->GetEntryLabel());
8304     }
8305     if (cls->MustGenerateClinitCheck()) {
8306       GenerateClassInitializationCheck(slow_path, out);
8307     } else {
8308       __ Bind(slow_path->GetExitLabel());
8309     }
8310   }
8311 }
8312 
VisitLoadMethodHandle(HLoadMethodHandle * load)8313 void LocationsBuilderMIPS::VisitLoadMethodHandle(HLoadMethodHandle* load) {
8314   InvokeRuntimeCallingConvention calling_convention;
8315   Location loc = Location::RegisterLocation(calling_convention.GetRegisterAt(0));
8316   CodeGenerator::CreateLoadMethodHandleRuntimeCallLocationSummary(load, loc, loc);
8317 }
8318 
VisitLoadMethodHandle(HLoadMethodHandle * load)8319 void InstructionCodeGeneratorMIPS::VisitLoadMethodHandle(HLoadMethodHandle* load) {
8320   codegen_->GenerateLoadMethodHandleRuntimeCall(load);
8321 }
8322 
VisitLoadMethodType(HLoadMethodType * load)8323 void LocationsBuilderMIPS::VisitLoadMethodType(HLoadMethodType* load) {
8324   InvokeRuntimeCallingConvention calling_convention;
8325   Location loc = Location::RegisterLocation(calling_convention.GetRegisterAt(0));
8326   CodeGenerator::CreateLoadMethodTypeRuntimeCallLocationSummary(load, loc, loc);
8327 }
8328 
VisitLoadMethodType(HLoadMethodType * load)8329 void InstructionCodeGeneratorMIPS::VisitLoadMethodType(HLoadMethodType* load) {
8330   codegen_->GenerateLoadMethodTypeRuntimeCall(load);
8331 }
8332 
GetExceptionTlsOffset()8333 static int32_t GetExceptionTlsOffset() {
8334   return Thread::ExceptionOffset<kMipsPointerSize>().Int32Value();
8335 }
8336 
VisitLoadException(HLoadException * load)8337 void LocationsBuilderMIPS::VisitLoadException(HLoadException* load) {
8338   LocationSummary* locations =
8339       new (GetGraph()->GetAllocator()) LocationSummary(load, LocationSummary::kNoCall);
8340   locations->SetOut(Location::RequiresRegister());
8341 }
8342 
VisitLoadException(HLoadException * load)8343 void InstructionCodeGeneratorMIPS::VisitLoadException(HLoadException* load) {
8344   Register out = load->GetLocations()->Out().AsRegister<Register>();
8345   __ LoadFromOffset(kLoadWord, out, TR, GetExceptionTlsOffset());
8346 }
8347 
VisitClearException(HClearException * clear)8348 void LocationsBuilderMIPS::VisitClearException(HClearException* clear) {
8349   new (GetGraph()->GetAllocator()) LocationSummary(clear, LocationSummary::kNoCall);
8350 }
8351 
VisitClearException(HClearException * clear ATTRIBUTE_UNUSED)8352 void InstructionCodeGeneratorMIPS::VisitClearException(HClearException* clear ATTRIBUTE_UNUSED) {
8353   __ StoreToOffset(kStoreWord, ZERO, TR, GetExceptionTlsOffset());
8354 }
8355 
VisitLoadString(HLoadString * load)8356 void LocationsBuilderMIPS::VisitLoadString(HLoadString* load) {
8357   LocationSummary::CallKind call_kind = CodeGenerator::GetLoadStringCallKind(load);
8358   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(load, call_kind);
8359   HLoadString::LoadKind load_kind = load->GetLoadKind();
8360   const bool isR6 = codegen_->GetInstructionSetFeatures().IsR6();
8361   const bool has_irreducible_loops = codegen_->GetGraph()->HasIrreducibleLoops();
8362   switch (load_kind) {
8363     // We need an extra register for PC-relative literals on R2.
8364     case HLoadString::LoadKind::kBootImageLinkTimePcRelative:
8365     case HLoadString::LoadKind::kBootImageRelRo:
8366     case HLoadString::LoadKind::kBssEntry:
8367     case HLoadString::LoadKind::kJitBootImageAddress:
8368       if (isR6) {
8369         break;
8370       }
8371       if (has_irreducible_loops) {
8372         if (load_kind != HLoadString::LoadKind::kJitBootImageAddress) {
8373           codegen_->ClobberRA();
8374         }
8375         break;
8376       }
8377       FALLTHROUGH_INTENDED;
8378     // We need an extra register for PC-relative dex cache accesses.
8379     case HLoadString::LoadKind::kRuntimeCall:
8380       locations->SetInAt(0, Location::RequiresRegister());
8381       break;
8382     default:
8383       break;
8384   }
8385   if (load_kind == HLoadString::LoadKind::kRuntimeCall) {
8386     InvokeRuntimeCallingConvention calling_convention;
8387     locations->SetOut(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
8388   } else {
8389     locations->SetOut(Location::RequiresRegister());
8390     if (load_kind == HLoadString::LoadKind::kBssEntry) {
8391       if (!kUseReadBarrier || kUseBakerReadBarrier) {
8392         // Rely on the pResolveString and marking to save everything we need.
8393         locations->SetCustomSlowPathCallerSaves(OneRegInReferenceOutSaveEverythingCallerSaves());
8394       } else {
8395         // For non-Baker read barriers we have a temp-clobbering call.
8396       }
8397     }
8398   }
8399 }
8400 
8401 // NO_THREAD_SAFETY_ANALYSIS as we manipulate handles whose internal object we know does not
8402 // move.
VisitLoadString(HLoadString * load)8403 void InstructionCodeGeneratorMIPS::VisitLoadString(HLoadString* load) NO_THREAD_SAFETY_ANALYSIS {
8404   HLoadString::LoadKind load_kind = load->GetLoadKind();
8405   LocationSummary* locations = load->GetLocations();
8406   Location out_loc = locations->Out();
8407   Register out = out_loc.AsRegister<Register>();
8408   Register base_or_current_method_reg;
8409   bool isR6 = codegen_->GetInstructionSetFeatures().IsR6();
8410   bool has_irreducible_loops = GetGraph()->HasIrreducibleLoops();
8411   switch (load_kind) {
8412     // We need an extra register for PC-relative literals on R2.
8413     case HLoadString::LoadKind::kBootImageLinkTimePcRelative:
8414     case HLoadString::LoadKind::kBootImageRelRo:
8415     case HLoadString::LoadKind::kBssEntry:
8416     case HLoadString::LoadKind::kJitBootImageAddress:
8417       base_or_current_method_reg =
8418           (isR6 || has_irreducible_loops) ? ZERO : locations->InAt(0).AsRegister<Register>();
8419       break;
8420     default:
8421       base_or_current_method_reg = ZERO;
8422       break;
8423   }
8424 
8425   switch (load_kind) {
8426     case HLoadString::LoadKind::kBootImageLinkTimePcRelative: {
8427       DCHECK(codegen_->GetCompilerOptions().IsBootImage());
8428       CodeGeneratorMIPS::PcRelativePatchInfo* info_high =
8429           codegen_->NewBootImageStringPatch(load->GetDexFile(), load->GetStringIndex());
8430       CodeGeneratorMIPS::PcRelativePatchInfo* info_low =
8431           codegen_->NewBootImageStringPatch(load->GetDexFile(), load->GetStringIndex(), info_high);
8432       codegen_->EmitPcRelativeAddressPlaceholderHigh(info_high,
8433                                                      out,
8434                                                      base_or_current_method_reg);
8435       __ Addiu(out, out, /* imm16= */ 0x5678, &info_low->label);
8436       return;
8437     }
8438     case HLoadString::LoadKind::kBootImageRelRo: {
8439       DCHECK(!codegen_->GetCompilerOptions().IsBootImage());
8440       uint32_t boot_image_offset = codegen_->GetBootImageOffset(load);
8441       CodeGeneratorMIPS::PcRelativePatchInfo* info_high =
8442           codegen_->NewBootImageRelRoPatch(boot_image_offset);
8443       CodeGeneratorMIPS::PcRelativePatchInfo* info_low =
8444           codegen_->NewBootImageRelRoPatch(boot_image_offset, info_high);
8445       codegen_->EmitPcRelativeAddressPlaceholderHigh(info_high,
8446                                                      out,
8447                                                      base_or_current_method_reg);
8448       __ Lw(out, out, /* imm16= */ 0x5678, &info_low->label);
8449       return;
8450     }
8451     case HLoadString::LoadKind::kBssEntry: {
8452       CodeGeneratorMIPS::PcRelativePatchInfo* info_high =
8453           codegen_->NewStringBssEntryPatch(load->GetDexFile(), load->GetStringIndex());
8454       CodeGeneratorMIPS::PcRelativePatchInfo* info_low =
8455           codegen_->NewStringBssEntryPatch(load->GetDexFile(), load->GetStringIndex(), info_high);
8456       codegen_->EmitPcRelativeAddressPlaceholderHigh(info_high,
8457                                                      out,
8458                                                      base_or_current_method_reg);
8459       GenerateGcRootFieldLoad(load,
8460                               out_loc,
8461                               out,
8462                               /* offset= */ 0x5678,
8463                               kCompilerReadBarrierOption,
8464                               &info_low->label);
8465       SlowPathCodeMIPS* slow_path =
8466           new (codegen_->GetScopedAllocator()) LoadStringSlowPathMIPS(load);
8467       codegen_->AddSlowPath(slow_path);
8468       __ Beqz(out, slow_path->GetEntryLabel());
8469       __ Bind(slow_path->GetExitLabel());
8470       return;
8471     }
8472     case HLoadString::LoadKind::kJitBootImageAddress: {
8473       uint32_t address = reinterpret_cast32<uint32_t>(load->GetString().Get());
8474       DCHECK_NE(address, 0u);
8475       if (isR6 || !has_irreducible_loops) {
8476         __ LoadLiteral(out,
8477                        base_or_current_method_reg,
8478                        codegen_->DeduplicateBootImageAddressLiteral(address));
8479       } else {
8480         __ LoadConst32(out, address);
8481       }
8482       return;
8483     }
8484     case HLoadString::LoadKind::kJitTableAddress: {
8485       CodeGeneratorMIPS::JitPatchInfo* info =
8486           codegen_->NewJitRootStringPatch(load->GetDexFile(),
8487                                           load->GetStringIndex(),
8488                                           load->GetString());
8489       bool reordering = __ SetReorder(false);
8490       __ Bind(&info->high_label);
8491       __ Lui(out, /* imm16= */ 0x1234);
8492       __ SetReorder(reordering);
8493       GenerateGcRootFieldLoad(load,
8494                               out_loc,
8495                               out,
8496                               /* offset= */ 0x5678,
8497                               kCompilerReadBarrierOption,
8498                               &info->low_label);
8499       return;
8500     }
8501     default:
8502       break;
8503   }
8504 
8505   // TODO: Re-add the compiler code to do string dex cache lookup again.
8506   DCHECK(load_kind == HLoadString::LoadKind::kRuntimeCall);
8507   InvokeRuntimeCallingConvention calling_convention;
8508   DCHECK_EQ(calling_convention.GetRegisterAt(0), out);
8509   __ LoadConst32(calling_convention.GetRegisterAt(0), load->GetStringIndex().index_);
8510   codegen_->InvokeRuntime(kQuickResolveString, load, load->GetDexPc());
8511   CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>();
8512 }
8513 
VisitLongConstant(HLongConstant * constant)8514 void LocationsBuilderMIPS::VisitLongConstant(HLongConstant* constant) {
8515   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(constant);
8516   locations->SetOut(Location::ConstantLocation(constant));
8517 }
8518 
VisitLongConstant(HLongConstant * constant ATTRIBUTE_UNUSED)8519 void InstructionCodeGeneratorMIPS::VisitLongConstant(HLongConstant* constant ATTRIBUTE_UNUSED) {
8520   // Will be generated at use site.
8521 }
8522 
VisitMonitorOperation(HMonitorOperation * instruction)8523 void LocationsBuilderMIPS::VisitMonitorOperation(HMonitorOperation* instruction) {
8524   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(
8525       instruction, LocationSummary::kCallOnMainOnly);
8526   InvokeRuntimeCallingConvention calling_convention;
8527   locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
8528 }
8529 
VisitMonitorOperation(HMonitorOperation * instruction)8530 void InstructionCodeGeneratorMIPS::VisitMonitorOperation(HMonitorOperation* instruction) {
8531   if (instruction->IsEnter()) {
8532     codegen_->InvokeRuntime(kQuickLockObject, instruction, instruction->GetDexPc());
8533     CheckEntrypointTypes<kQuickLockObject, void, mirror::Object*>();
8534   } else {
8535     codegen_->InvokeRuntime(kQuickUnlockObject, instruction, instruction->GetDexPc());
8536   }
8537   CheckEntrypointTypes<kQuickUnlockObject, void, mirror::Object*>();
8538 }
8539 
VisitMul(HMul * mul)8540 void LocationsBuilderMIPS::VisitMul(HMul* mul) {
8541   LocationSummary* locations =
8542       new (GetGraph()->GetAllocator()) LocationSummary(mul, LocationSummary::kNoCall);
8543   switch (mul->GetResultType()) {
8544     case DataType::Type::kInt32:
8545     case DataType::Type::kInt64:
8546       locations->SetInAt(0, Location::RequiresRegister());
8547       locations->SetInAt(1, Location::RequiresRegister());
8548       locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
8549       break;
8550 
8551     case DataType::Type::kFloat32:
8552     case DataType::Type::kFloat64:
8553       locations->SetInAt(0, Location::RequiresFpuRegister());
8554       locations->SetInAt(1, Location::RequiresFpuRegister());
8555       locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
8556       break;
8557 
8558     default:
8559       LOG(FATAL) << "Unexpected mul type " << mul->GetResultType();
8560   }
8561 }
8562 
VisitMul(HMul * instruction)8563 void InstructionCodeGeneratorMIPS::VisitMul(HMul* instruction) {
8564   DataType::Type type = instruction->GetType();
8565   LocationSummary* locations = instruction->GetLocations();
8566   bool isR6 = codegen_->GetInstructionSetFeatures().IsR6();
8567 
8568   switch (type) {
8569     case DataType::Type::kInt32: {
8570       Register dst = locations->Out().AsRegister<Register>();
8571       Register lhs = locations->InAt(0).AsRegister<Register>();
8572       Register rhs = locations->InAt(1).AsRegister<Register>();
8573 
8574       if (isR6) {
8575         __ MulR6(dst, lhs, rhs);
8576       } else {
8577         __ MulR2(dst, lhs, rhs);
8578       }
8579       break;
8580     }
8581     case DataType::Type::kInt64: {
8582       Register dst_high = locations->Out().AsRegisterPairHigh<Register>();
8583       Register dst_low = locations->Out().AsRegisterPairLow<Register>();
8584       Register lhs_high = locations->InAt(0).AsRegisterPairHigh<Register>();
8585       Register lhs_low = locations->InAt(0).AsRegisterPairLow<Register>();
8586       Register rhs_high = locations->InAt(1).AsRegisterPairHigh<Register>();
8587       Register rhs_low = locations->InAt(1).AsRegisterPairLow<Register>();
8588 
8589       // Extra checks to protect caused by the existance of A1_A2.
8590       // The algorithm is wrong if dst_high is either lhs_lo or rhs_lo:
8591       // (e.g. lhs=a0_a1, rhs=a2_a3 and dst=a1_a2).
8592       DCHECK_NE(dst_high, lhs_low);
8593       DCHECK_NE(dst_high, rhs_low);
8594 
8595       // A_B * C_D
8596       // dst_hi:  [ low(A*D) + low(B*C) + hi(B*D) ]
8597       // dst_lo:  [ low(B*D) ]
8598       // Note: R2 and R6 MUL produce the low 32 bit of the multiplication result.
8599 
8600       if (isR6) {
8601         __ MulR6(TMP, lhs_high, rhs_low);
8602         __ MulR6(dst_high, lhs_low, rhs_high);
8603         __ Addu(dst_high, dst_high, TMP);
8604         __ MuhuR6(TMP, lhs_low, rhs_low);
8605         __ Addu(dst_high, dst_high, TMP);
8606         __ MulR6(dst_low, lhs_low, rhs_low);
8607       } else {
8608         __ MulR2(TMP, lhs_high, rhs_low);
8609         __ MulR2(dst_high, lhs_low, rhs_high);
8610         __ Addu(dst_high, dst_high, TMP);
8611         __ MultuR2(lhs_low, rhs_low);
8612         __ Mfhi(TMP);
8613         __ Addu(dst_high, dst_high, TMP);
8614         __ Mflo(dst_low);
8615       }
8616       break;
8617     }
8618     case DataType::Type::kFloat32:
8619     case DataType::Type::kFloat64: {
8620       FRegister dst = locations->Out().AsFpuRegister<FRegister>();
8621       FRegister lhs = locations->InAt(0).AsFpuRegister<FRegister>();
8622       FRegister rhs = locations->InAt(1).AsFpuRegister<FRegister>();
8623       if (type == DataType::Type::kFloat32) {
8624         __ MulS(dst, lhs, rhs);
8625       } else {
8626         __ MulD(dst, lhs, rhs);
8627       }
8628       break;
8629     }
8630     default:
8631       LOG(FATAL) << "Unexpected mul type " << type;
8632   }
8633 }
8634 
VisitNeg(HNeg * neg)8635 void LocationsBuilderMIPS::VisitNeg(HNeg* neg) {
8636   LocationSummary* locations =
8637       new (GetGraph()->GetAllocator()) LocationSummary(neg, LocationSummary::kNoCall);
8638   switch (neg->GetResultType()) {
8639     case DataType::Type::kInt32:
8640     case DataType::Type::kInt64:
8641       locations->SetInAt(0, Location::RequiresRegister());
8642       locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
8643       break;
8644 
8645     case DataType::Type::kFloat32:
8646     case DataType::Type::kFloat64:
8647       locations->SetInAt(0, Location::RequiresFpuRegister());
8648       locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
8649       break;
8650 
8651     default:
8652       LOG(FATAL) << "Unexpected neg type " << neg->GetResultType();
8653   }
8654 }
8655 
VisitNeg(HNeg * instruction)8656 void InstructionCodeGeneratorMIPS::VisitNeg(HNeg* instruction) {
8657   DataType::Type type = instruction->GetType();
8658   LocationSummary* locations = instruction->GetLocations();
8659 
8660   switch (type) {
8661     case DataType::Type::kInt32: {
8662       Register dst = locations->Out().AsRegister<Register>();
8663       Register src = locations->InAt(0).AsRegister<Register>();
8664       __ Subu(dst, ZERO, src);
8665       break;
8666     }
8667     case DataType::Type::kInt64: {
8668       Register dst_high = locations->Out().AsRegisterPairHigh<Register>();
8669       Register dst_low = locations->Out().AsRegisterPairLow<Register>();
8670       Register src_high = locations->InAt(0).AsRegisterPairHigh<Register>();
8671       Register src_low = locations->InAt(0).AsRegisterPairLow<Register>();
8672       __ Subu(dst_low, ZERO, src_low);
8673       __ Sltu(TMP, ZERO, dst_low);
8674       __ Subu(dst_high, ZERO, src_high);
8675       __ Subu(dst_high, dst_high, TMP);
8676       break;
8677     }
8678     case DataType::Type::kFloat32:
8679     case DataType::Type::kFloat64: {
8680       FRegister dst = locations->Out().AsFpuRegister<FRegister>();
8681       FRegister src = locations->InAt(0).AsFpuRegister<FRegister>();
8682       if (type == DataType::Type::kFloat32) {
8683         __ NegS(dst, src);
8684       } else {
8685         __ NegD(dst, src);
8686       }
8687       break;
8688     }
8689     default:
8690       LOG(FATAL) << "Unexpected neg type " << type;
8691   }
8692 }
8693 
VisitNewArray(HNewArray * instruction)8694 void LocationsBuilderMIPS::VisitNewArray(HNewArray* instruction) {
8695   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(
8696       instruction, LocationSummary::kCallOnMainOnly);
8697   InvokeRuntimeCallingConvention calling_convention;
8698   locations->SetOut(calling_convention.GetReturnLocation(DataType::Type::kReference));
8699   locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
8700   locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
8701 }
8702 
VisitNewArray(HNewArray * instruction)8703 void InstructionCodeGeneratorMIPS::VisitNewArray(HNewArray* instruction) {
8704   // Note: if heap poisoning is enabled, the entry point takes care of poisoning the reference.
8705   QuickEntrypointEnum entrypoint = CodeGenerator::GetArrayAllocationEntrypoint(instruction);
8706   codegen_->InvokeRuntime(entrypoint, instruction, instruction->GetDexPc());
8707   CheckEntrypointTypes<kQuickAllocArrayResolved, void*, mirror::Class*, int32_t>();
8708   DCHECK(!codegen_->IsLeafMethod());
8709 }
8710 
VisitNewInstance(HNewInstance * instruction)8711 void LocationsBuilderMIPS::VisitNewInstance(HNewInstance* instruction) {
8712   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(
8713       instruction, LocationSummary::kCallOnMainOnly);
8714   InvokeRuntimeCallingConvention calling_convention;
8715   locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
8716   locations->SetOut(calling_convention.GetReturnLocation(DataType::Type::kReference));
8717 }
8718 
VisitNewInstance(HNewInstance * instruction)8719 void InstructionCodeGeneratorMIPS::VisitNewInstance(HNewInstance* instruction) {
8720   codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc());
8721   CheckEntrypointTypes<kQuickAllocObjectWithChecks, void*, mirror::Class*>();
8722 }
8723 
VisitNot(HNot * instruction)8724 void LocationsBuilderMIPS::VisitNot(HNot* instruction) {
8725   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
8726   locations->SetInAt(0, Location::RequiresRegister());
8727   locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
8728 }
8729 
VisitNot(HNot * instruction)8730 void InstructionCodeGeneratorMIPS::VisitNot(HNot* instruction) {
8731   DataType::Type type = instruction->GetType();
8732   LocationSummary* locations = instruction->GetLocations();
8733 
8734   switch (type) {
8735     case DataType::Type::kInt32: {
8736       Register dst = locations->Out().AsRegister<Register>();
8737       Register src = locations->InAt(0).AsRegister<Register>();
8738       __ Nor(dst, src, ZERO);
8739       break;
8740     }
8741 
8742     case DataType::Type::kInt64: {
8743       Register dst_high = locations->Out().AsRegisterPairHigh<Register>();
8744       Register dst_low = locations->Out().AsRegisterPairLow<Register>();
8745       Register src_high = locations->InAt(0).AsRegisterPairHigh<Register>();
8746       Register src_low = locations->InAt(0).AsRegisterPairLow<Register>();
8747       __ Nor(dst_high, src_high, ZERO);
8748       __ Nor(dst_low, src_low, ZERO);
8749       break;
8750     }
8751 
8752     default:
8753       LOG(FATAL) << "Unexpected type for not operation " << instruction->GetResultType();
8754   }
8755 }
8756 
VisitBooleanNot(HBooleanNot * instruction)8757 void LocationsBuilderMIPS::VisitBooleanNot(HBooleanNot* instruction) {
8758   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
8759   locations->SetInAt(0, Location::RequiresRegister());
8760   locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
8761 }
8762 
VisitBooleanNot(HBooleanNot * instruction)8763 void InstructionCodeGeneratorMIPS::VisitBooleanNot(HBooleanNot* instruction) {
8764   LocationSummary* locations = instruction->GetLocations();
8765   __ Xori(locations->Out().AsRegister<Register>(),
8766           locations->InAt(0).AsRegister<Register>(),
8767           1);
8768 }
8769 
VisitNullCheck(HNullCheck * instruction)8770 void LocationsBuilderMIPS::VisitNullCheck(HNullCheck* instruction) {
8771   LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction);
8772   locations->SetInAt(0, Location::RequiresRegister());
8773 }
8774 
GenerateImplicitNullCheck(HNullCheck * instruction)8775 void CodeGeneratorMIPS::GenerateImplicitNullCheck(HNullCheck* instruction) {
8776   if (CanMoveNullCheckToUser(instruction)) {
8777     return;
8778   }
8779   Location obj = instruction->GetLocations()->InAt(0);
8780 
8781   __ Lw(ZERO, obj.AsRegister<Register>(), 0);
8782   RecordPcInfo(instruction, instruction->GetDexPc());
8783 }
8784 
GenerateExplicitNullCheck(HNullCheck * instruction)8785 void CodeGeneratorMIPS::GenerateExplicitNullCheck(HNullCheck* instruction) {
8786   SlowPathCodeMIPS* slow_path = new (GetScopedAllocator()) NullCheckSlowPathMIPS(instruction);
8787   AddSlowPath(slow_path);
8788 
8789   Location obj = instruction->GetLocations()->InAt(0);
8790 
8791   __ Beqz(obj.AsRegister<Register>(), slow_path->GetEntryLabel());
8792 }
8793 
VisitNullCheck(HNullCheck * instruction)8794 void InstructionCodeGeneratorMIPS::VisitNullCheck(HNullCheck* instruction) {
8795   codegen_->GenerateNullCheck(instruction);
8796 }
8797 
VisitOr(HOr * instruction)8798 void LocationsBuilderMIPS::VisitOr(HOr* instruction) {
8799   HandleBinaryOp(instruction);
8800 }
8801 
VisitOr(HOr * instruction)8802 void InstructionCodeGeneratorMIPS::VisitOr(HOr* instruction) {
8803   HandleBinaryOp(instruction);
8804 }
8805 
VisitParallelMove(HParallelMove * instruction ATTRIBUTE_UNUSED)8806 void LocationsBuilderMIPS::VisitParallelMove(HParallelMove* instruction ATTRIBUTE_UNUSED) {
8807   LOG(FATAL) << "Unreachable";
8808 }
8809 
VisitParallelMove(HParallelMove * instruction)8810 void InstructionCodeGeneratorMIPS::VisitParallelMove(HParallelMove* instruction) {
8811   if (instruction->GetNext()->IsSuspendCheck() &&
8812       instruction->GetBlock()->GetLoopInformation() != nullptr) {
8813     HSuspendCheck* suspend_check = instruction->GetNext()->AsSuspendCheck();
8814     // The back edge will generate the suspend check.
8815     codegen_->ClearSpillSlotsFromLoopPhisInStackMap(suspend_check, instruction);
8816   }
8817 
8818   codegen_->GetMoveResolver()->EmitNativeCode(instruction);
8819 }
8820 
VisitParameterValue(HParameterValue * instruction)8821 void LocationsBuilderMIPS::VisitParameterValue(HParameterValue* instruction) {
8822   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
8823   Location location = parameter_visitor_.GetNextLocation(instruction->GetType());
8824   if (location.IsStackSlot()) {
8825     location = Location::StackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
8826   } else if (location.IsDoubleStackSlot()) {
8827     location = Location::DoubleStackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
8828   }
8829   locations->SetOut(location);
8830 }
8831 
VisitParameterValue(HParameterValue * instruction ATTRIBUTE_UNUSED)8832 void InstructionCodeGeneratorMIPS::VisitParameterValue(HParameterValue* instruction
8833                                                          ATTRIBUTE_UNUSED) {
8834   // Nothing to do, the parameter is already at its location.
8835 }
8836 
VisitCurrentMethod(HCurrentMethod * instruction)8837 void LocationsBuilderMIPS::VisitCurrentMethod(HCurrentMethod* instruction) {
8838   LocationSummary* locations =
8839       new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
8840   locations->SetOut(Location::RegisterLocation(kMethodRegisterArgument));
8841 }
8842 
VisitCurrentMethod(HCurrentMethod * instruction ATTRIBUTE_UNUSED)8843 void InstructionCodeGeneratorMIPS::VisitCurrentMethod(HCurrentMethod* instruction
8844                                                         ATTRIBUTE_UNUSED) {
8845   // Nothing to do, the method is already at its location.
8846 }
8847 
VisitPhi(HPhi * instruction)8848 void LocationsBuilderMIPS::VisitPhi(HPhi* instruction) {
8849   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
8850   for (size_t i = 0, e = locations->GetInputCount(); i < e; ++i) {
8851     locations->SetInAt(i, Location::Any());
8852   }
8853   locations->SetOut(Location::Any());
8854 }
8855 
VisitPhi(HPhi * instruction ATTRIBUTE_UNUSED)8856 void InstructionCodeGeneratorMIPS::VisitPhi(HPhi* instruction ATTRIBUTE_UNUSED) {
8857   LOG(FATAL) << "Unreachable";
8858 }
8859 
VisitRem(HRem * rem)8860 void LocationsBuilderMIPS::VisitRem(HRem* rem) {
8861   DataType::Type type = rem->GetResultType();
8862   bool call_rem;
8863   if ((type == DataType::Type::kInt64) && rem->InputAt(1)->IsConstant()) {
8864     int64_t imm = CodeGenerator::GetInt64ValueOf(rem->InputAt(1)->AsConstant());
8865     call_rem = (imm != 0) && !IsPowerOfTwo(static_cast<uint64_t>(AbsOrMin(imm)));
8866   } else {
8867     call_rem = (type != DataType::Type::kInt32);
8868   }
8869   LocationSummary::CallKind call_kind = call_rem
8870       ? LocationSummary::kCallOnMainOnly
8871       : LocationSummary::kNoCall;
8872   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(rem, call_kind);
8873 
8874   switch (type) {
8875     case DataType::Type::kInt32:
8876       locations->SetInAt(0, Location::RequiresRegister());
8877       locations->SetInAt(1, Location::RegisterOrConstant(rem->InputAt(1)));
8878       locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
8879       break;
8880 
8881     case DataType::Type::kInt64: {
8882       if (call_rem) {
8883         InvokeRuntimeCallingConvention calling_convention;
8884         locations->SetInAt(0, Location::RegisterPairLocation(
8885             calling_convention.GetRegisterAt(0), calling_convention.GetRegisterAt(1)));
8886         locations->SetInAt(1, Location::RegisterPairLocation(
8887             calling_convention.GetRegisterAt(2), calling_convention.GetRegisterAt(3)));
8888         locations->SetOut(calling_convention.GetReturnLocation(type));
8889       } else {
8890         locations->SetInAt(0, Location::RequiresRegister());
8891         locations->SetInAt(1, Location::ConstantLocation(rem->InputAt(1)->AsConstant()));
8892         locations->SetOut(Location::RequiresRegister());
8893       }
8894       break;
8895     }
8896 
8897     case DataType::Type::kFloat32:
8898     case DataType::Type::kFloat64: {
8899       InvokeRuntimeCallingConvention calling_convention;
8900       locations->SetInAt(0, Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(0)));
8901       locations->SetInAt(1, Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(1)));
8902       locations->SetOut(calling_convention.GetReturnLocation(type));
8903       break;
8904     }
8905 
8906     default:
8907       LOG(FATAL) << "Unexpected rem type " << type;
8908   }
8909 }
8910 
VisitRem(HRem * instruction)8911 void InstructionCodeGeneratorMIPS::VisitRem(HRem* instruction) {
8912   DataType::Type type = instruction->GetType();
8913   LocationSummary* locations = instruction->GetLocations();
8914 
8915   switch (type) {
8916     case DataType::Type::kInt32:
8917       GenerateDivRemIntegral(instruction);
8918       break;
8919     case DataType::Type::kInt64: {
8920       if (locations->InAt(1).IsConstant()) {
8921         int64_t imm = locations->InAt(1).GetConstant()->AsLongConstant()->GetValue();
8922         if (imm == 0) {
8923           // Do not generate anything. DivZeroCheck would prevent any code to be executed.
8924         } else if (imm == 1 || imm == -1) {
8925           DivRemOneOrMinusOne(instruction);
8926         } else {
8927           DCHECK(IsPowerOfTwo(static_cast<uint64_t>(AbsOrMin(imm))));
8928           DivRemByPowerOfTwo(instruction);
8929         }
8930       } else {
8931         codegen_->InvokeRuntime(kQuickLmod, instruction, instruction->GetDexPc());
8932         CheckEntrypointTypes<kQuickLmod, int64_t, int64_t, int64_t>();
8933       }
8934       break;
8935     }
8936     case DataType::Type::kFloat32: {
8937       codegen_->InvokeRuntime(kQuickFmodf, instruction, instruction->GetDexPc());
8938       CheckEntrypointTypes<kQuickFmodf, float, float, float>();
8939       break;
8940     }
8941     case DataType::Type::kFloat64: {
8942       codegen_->InvokeRuntime(kQuickFmod, instruction, instruction->GetDexPc());
8943       CheckEntrypointTypes<kQuickFmod, double, double, double>();
8944       break;
8945     }
8946     default:
8947       LOG(FATAL) << "Unexpected rem type " << type;
8948   }
8949 }
8950 
CreateMinMaxLocations(ArenaAllocator * allocator,HBinaryOperation * minmax)8951 static void CreateMinMaxLocations(ArenaAllocator* allocator, HBinaryOperation* minmax) {
8952   LocationSummary* locations = new (allocator) LocationSummary(minmax);
8953   switch (minmax->GetResultType()) {
8954     case DataType::Type::kInt32:
8955     case DataType::Type::kInt64:
8956       locations->SetInAt(0, Location::RequiresRegister());
8957       locations->SetInAt(1, Location::RequiresRegister());
8958       locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
8959       break;
8960     case DataType::Type::kFloat32:
8961     case DataType::Type::kFloat64:
8962       locations->SetInAt(0, Location::RequiresFpuRegister());
8963       locations->SetInAt(1, Location::RequiresFpuRegister());
8964       locations->SetOut(Location::RequiresFpuRegister(), Location::kOutputOverlap);
8965       break;
8966     default:
8967       LOG(FATAL) << "Unexpected type for HMinMax " << minmax->GetResultType();
8968   }
8969 }
8970 
GenerateMinMaxInt(LocationSummary * locations,bool is_min,bool isR6,DataType::Type type)8971 void InstructionCodeGeneratorMIPS::GenerateMinMaxInt(LocationSummary* locations,
8972                                                      bool is_min,
8973                                                      bool isR6,
8974                                                      DataType::Type type) {
8975   if (isR6) {
8976     // Some architectures, such as ARM and MIPS (prior to r6), have a
8977     // conditional move instruction which only changes the target
8978     // (output) register if the condition is true (MIPS prior to r6 had
8979     // MOVF, MOVT, MOVN, and MOVZ). The SELEQZ and SELNEZ instructions
8980     // always change the target (output) register.  If the condition is
8981     // true the output register gets the contents of the "rs" register;
8982     // otherwise, the output register is set to zero. One consequence
8983     // of this is that to implement something like "rd = c==0 ? rs : rt"
8984     // MIPS64r6 needs to use a pair of SELEQZ/SELNEZ instructions.
8985     // After executing this pair of instructions one of the output
8986     // registers from the pair will necessarily contain zero. Then the
8987     // code ORs the output registers from the SELEQZ/SELNEZ instructions
8988     // to get the final result.
8989     //
8990     // The initial test to see if the output register is same as the
8991     // first input register is needed to make sure that value in the
8992     // first input register isn't clobbered before we've finished
8993     // computing the output value. The logic in the corresponding else
8994     // clause performs the same task but makes sure the second input
8995     // register isn't clobbered in the event that it's the same register
8996     // as the output register; the else clause also handles the case
8997     // where the output register is distinct from both the first, and the
8998     // second input registers.
8999     if (type == DataType::Type::kInt64) {
9000       Register a_lo = locations->InAt(0).AsRegisterPairLow<Register>();
9001       Register a_hi = locations->InAt(0).AsRegisterPairHigh<Register>();
9002       Register b_lo = locations->InAt(1).AsRegisterPairLow<Register>();
9003       Register b_hi = locations->InAt(1).AsRegisterPairHigh<Register>();
9004       Register out_lo = locations->Out().AsRegisterPairLow<Register>();
9005       Register out_hi = locations->Out().AsRegisterPairHigh<Register>();
9006 
9007       MipsLabel compare_done;
9008 
9009       if (a_lo == b_lo) {
9010         if (out_lo != a_lo) {
9011           __ Move(out_lo, a_lo);
9012           __ Move(out_hi, a_hi);
9013         }
9014       } else {
9015         __ Slt(TMP, b_hi, a_hi);
9016         __ Bne(b_hi, a_hi, &compare_done);
9017 
9018         __ Sltu(TMP, b_lo, a_lo);
9019 
9020         __ Bind(&compare_done);
9021 
9022         if (is_min) {
9023           __ Seleqz(AT, a_lo, TMP);
9024           __ Selnez(out_lo, b_lo, TMP);  // Safe even if out_lo == a_lo/b_lo
9025                                          // because at this point we're
9026                                          // done using a_lo/b_lo.
9027         } else {
9028           __ Selnez(AT, a_lo, TMP);
9029           __ Seleqz(out_lo, b_lo, TMP);  // ditto
9030         }
9031         __ Or(out_lo, out_lo, AT);
9032         if (is_min) {
9033           __ Seleqz(AT, a_hi, TMP);
9034           __ Selnez(out_hi, b_hi, TMP);  // ditto but for out_hi & a_hi/b_hi
9035         } else {
9036           __ Selnez(AT, a_hi, TMP);
9037           __ Seleqz(out_hi, b_hi, TMP);  // ditto but for out_hi & a_hi/b_hi
9038         }
9039         __ Or(out_hi, out_hi, AT);
9040       }
9041     } else {
9042       DCHECK_EQ(type, DataType::Type::kInt32);
9043       Register a = locations->InAt(0).AsRegister<Register>();
9044       Register b = locations->InAt(1).AsRegister<Register>();
9045       Register out = locations->Out().AsRegister<Register>();
9046 
9047       if (a == b) {
9048         if (out != a) {
9049           __ Move(out, a);
9050         }
9051       } else {
9052         __ Slt(AT, b, a);
9053         if (is_min) {
9054           __ Seleqz(TMP, a, AT);
9055           __ Selnez(AT, b, AT);
9056         } else {
9057           __ Selnez(TMP, a, AT);
9058           __ Seleqz(AT, b, AT);
9059         }
9060         __ Or(out, TMP, AT);
9061       }
9062     }
9063   } else {  // !isR6
9064     if (type == DataType::Type::kInt64) {
9065       Register a_lo = locations->InAt(0).AsRegisterPairLow<Register>();
9066       Register a_hi = locations->InAt(0).AsRegisterPairHigh<Register>();
9067       Register b_lo = locations->InAt(1).AsRegisterPairLow<Register>();
9068       Register b_hi = locations->InAt(1).AsRegisterPairHigh<Register>();
9069       Register out_lo = locations->Out().AsRegisterPairLow<Register>();
9070       Register out_hi = locations->Out().AsRegisterPairHigh<Register>();
9071 
9072       MipsLabel compare_done;
9073 
9074       if (a_lo == b_lo) {
9075         if (out_lo != a_lo) {
9076           __ Move(out_lo, a_lo);
9077           __ Move(out_hi, a_hi);
9078         }
9079       } else {
9080         __ Slt(TMP, a_hi, b_hi);
9081         __ Bne(a_hi, b_hi, &compare_done);
9082 
9083         __ Sltu(TMP, a_lo, b_lo);
9084 
9085         __ Bind(&compare_done);
9086 
9087         if (is_min) {
9088           if (out_lo != a_lo) {
9089             __ Movn(out_hi, a_hi, TMP);
9090             __ Movn(out_lo, a_lo, TMP);
9091           }
9092           if (out_lo != b_lo) {
9093             __ Movz(out_hi, b_hi, TMP);
9094             __ Movz(out_lo, b_lo, TMP);
9095           }
9096         } else {
9097           if (out_lo != a_lo) {
9098             __ Movz(out_hi, a_hi, TMP);
9099             __ Movz(out_lo, a_lo, TMP);
9100           }
9101           if (out_lo != b_lo) {
9102             __ Movn(out_hi, b_hi, TMP);
9103             __ Movn(out_lo, b_lo, TMP);
9104           }
9105         }
9106       }
9107     } else {
9108       DCHECK_EQ(type, DataType::Type::kInt32);
9109       Register a = locations->InAt(0).AsRegister<Register>();
9110       Register b = locations->InAt(1).AsRegister<Register>();
9111       Register out = locations->Out().AsRegister<Register>();
9112 
9113       if (a == b) {
9114         if (out != a) {
9115           __ Move(out, a);
9116         }
9117       } else {
9118         __ Slt(AT, a, b);
9119         if (is_min) {
9120           if (out != a) {
9121             __ Movn(out, a, AT);
9122           }
9123           if (out != b) {
9124             __ Movz(out, b, AT);
9125           }
9126         } else {
9127           if (out != a) {
9128             __ Movz(out, a, AT);
9129           }
9130           if (out != b) {
9131             __ Movn(out, b, AT);
9132           }
9133         }
9134       }
9135     }
9136   }
9137 }
9138 
GenerateMinMaxFP(LocationSummary * locations,bool is_min,bool isR6,DataType::Type type)9139 void InstructionCodeGeneratorMIPS::GenerateMinMaxFP(LocationSummary* locations,
9140                                                     bool is_min,
9141                                                     bool isR6,
9142                                                     DataType::Type type) {
9143   FRegister out = locations->Out().AsFpuRegister<FRegister>();
9144   FRegister a = locations->InAt(0).AsFpuRegister<FRegister>();
9145   FRegister b = locations->InAt(1).AsFpuRegister<FRegister>();
9146 
9147   if (isR6) {
9148     MipsLabel noNaNs;
9149     MipsLabel done;
9150     FRegister ftmp = ((out != a) && (out != b)) ? out : FTMP;
9151 
9152     // When Java computes min/max it prefers a NaN to a number; the
9153     // behavior of MIPSR6 is to prefer numbers to NaNs, i.e., if one of
9154     // the inputs is a NaN and the other is a valid number, the MIPS
9155     // instruction will return the number; Java wants the NaN value
9156     // returned. This is why there is extra logic preceding the use of
9157     // the MIPS min.fmt/max.fmt instructions. If either a, or b holds a
9158     // NaN, return the NaN, otherwise return the min/max.
9159     if (type == DataType::Type::kFloat64) {
9160       __ CmpUnD(FTMP, a, b);
9161       __ Bc1eqz(FTMP, &noNaNs);
9162 
9163       // One of the inputs is a NaN
9164       __ CmpEqD(ftmp, a, a);
9165       // If a == a then b is the NaN, otherwise a is the NaN.
9166       __ SelD(ftmp, a, b);
9167 
9168       if (ftmp != out) {
9169         __ MovD(out, ftmp);
9170       }
9171 
9172       __ B(&done);
9173 
9174       __ Bind(&noNaNs);
9175 
9176       if (is_min) {
9177         __ MinD(out, a, b);
9178       } else {
9179         __ MaxD(out, a, b);
9180       }
9181     } else {
9182       DCHECK_EQ(type, DataType::Type::kFloat32);
9183       __ CmpUnS(FTMP, a, b);
9184       __ Bc1eqz(FTMP, &noNaNs);
9185 
9186       // One of the inputs is a NaN
9187       __ CmpEqS(ftmp, a, a);
9188       // If a == a then b is the NaN, otherwise a is the NaN.
9189       __ SelS(ftmp, a, b);
9190 
9191       if (ftmp != out) {
9192         __ MovS(out, ftmp);
9193       }
9194 
9195       __ B(&done);
9196 
9197       __ Bind(&noNaNs);
9198 
9199       if (is_min) {
9200         __ MinS(out, a, b);
9201       } else {
9202         __ MaxS(out, a, b);
9203       }
9204     }
9205 
9206     __ Bind(&done);
9207 
9208   } else {  // !isR6
9209     MipsLabel ordered;
9210     MipsLabel compare;
9211     MipsLabel select;
9212     MipsLabel done;
9213 
9214     if (type == DataType::Type::kFloat64) {
9215       __ CunD(a, b);
9216     } else {
9217       DCHECK_EQ(type, DataType::Type::kFloat32);
9218       __ CunS(a, b);
9219     }
9220     __ Bc1f(&ordered);
9221 
9222     // a or b (or both) is a NaN. Return one, which is a NaN.
9223     if (type == DataType::Type::kFloat64) {
9224       __ CeqD(b, b);
9225     } else {
9226       __ CeqS(b, b);
9227     }
9228     __ B(&select);
9229 
9230     __ Bind(&ordered);
9231 
9232     // Neither is a NaN.
9233     // a == b? (-0.0 compares equal with +0.0)
9234     // If equal, handle zeroes, else compare further.
9235     if (type == DataType::Type::kFloat64) {
9236       __ CeqD(a, b);
9237     } else {
9238       __ CeqS(a, b);
9239     }
9240     __ Bc1f(&compare);
9241 
9242     // a == b either bit for bit or one is -0.0 and the other is +0.0.
9243     if (type == DataType::Type::kFloat64) {
9244       __ MoveFromFpuHigh(TMP, a);
9245       __ MoveFromFpuHigh(AT, b);
9246     } else {
9247       __ Mfc1(TMP, a);
9248       __ Mfc1(AT, b);
9249     }
9250 
9251     if (is_min) {
9252       // -0.0 prevails over +0.0.
9253       __ Or(TMP, TMP, AT);
9254     } else {
9255       // +0.0 prevails over -0.0.
9256       __ And(TMP, TMP, AT);
9257     }
9258 
9259     if (type == DataType::Type::kFloat64) {
9260       __ Mfc1(AT, a);
9261       __ Mtc1(AT, out);
9262       __ MoveToFpuHigh(TMP, out);
9263     } else {
9264       __ Mtc1(TMP, out);
9265     }
9266     __ B(&done);
9267 
9268     __ Bind(&compare);
9269 
9270     if (type == DataType::Type::kFloat64) {
9271       if (is_min) {
9272         // return (a <= b) ? a : b;
9273         __ ColeD(a, b);
9274       } else {
9275         // return (a >= b) ? a : b;
9276         __ ColeD(b, a);  // b <= a
9277       }
9278     } else {
9279       if (is_min) {
9280         // return (a <= b) ? a : b;
9281         __ ColeS(a, b);
9282       } else {
9283         // return (a >= b) ? a : b;
9284         __ ColeS(b, a);  // b <= a
9285       }
9286     }
9287 
9288     __ Bind(&select);
9289 
9290     if (type == DataType::Type::kFloat64) {
9291       __ MovtD(out, a);
9292       __ MovfD(out, b);
9293     } else {
9294       __ MovtS(out, a);
9295       __ MovfS(out, b);
9296     }
9297 
9298     __ Bind(&done);
9299   }
9300 }
9301 
GenerateMinMax(HBinaryOperation * minmax,bool is_min)9302 void InstructionCodeGeneratorMIPS::GenerateMinMax(HBinaryOperation* minmax, bool is_min) {
9303   bool isR6 = codegen_->GetInstructionSetFeatures().IsR6();
9304   DataType::Type type = minmax->GetResultType();
9305   switch (type) {
9306     case DataType::Type::kInt32:
9307     case DataType::Type::kInt64:
9308       GenerateMinMaxInt(minmax->GetLocations(), is_min, isR6, type);
9309       break;
9310     case DataType::Type::kFloat32:
9311     case DataType::Type::kFloat64:
9312       GenerateMinMaxFP(minmax->GetLocations(), is_min, isR6, type);
9313       break;
9314     default:
9315       LOG(FATAL) << "Unexpected type for HMinMax " << type;
9316   }
9317 }
9318 
VisitMin(HMin * min)9319 void LocationsBuilderMIPS::VisitMin(HMin* min) {
9320   CreateMinMaxLocations(GetGraph()->GetAllocator(), min);
9321 }
9322 
VisitMin(HMin * min)9323 void InstructionCodeGeneratorMIPS::VisitMin(HMin* min) {
9324   GenerateMinMax(min, /*is_min*/ true);
9325 }
9326 
VisitMax(HMax * max)9327 void LocationsBuilderMIPS::VisitMax(HMax* max) {
9328   CreateMinMaxLocations(GetGraph()->GetAllocator(), max);
9329 }
9330 
VisitMax(HMax * max)9331 void InstructionCodeGeneratorMIPS::VisitMax(HMax* max) {
9332   GenerateMinMax(max, /*is_min*/ false);
9333 }
9334 
VisitAbs(HAbs * abs)9335 void LocationsBuilderMIPS::VisitAbs(HAbs* abs) {
9336   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(abs);
9337   switch (abs->GetResultType()) {
9338     case DataType::Type::kInt32:
9339     case DataType::Type::kInt64:
9340       locations->SetInAt(0, Location::RequiresRegister());
9341       locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
9342       break;
9343     case DataType::Type::kFloat32:
9344     case DataType::Type::kFloat64:
9345       locations->SetInAt(0, Location::RequiresFpuRegister());
9346       locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
9347       break;
9348     default:
9349       LOG(FATAL) << "Unexpected abs type " << abs->GetResultType();
9350   }
9351 }
9352 
GenerateAbsFP(LocationSummary * locations,DataType::Type type,bool isR2OrNewer,bool isR6)9353 void InstructionCodeGeneratorMIPS::GenerateAbsFP(LocationSummary* locations,
9354                                                  DataType::Type type,
9355                                                  bool isR2OrNewer,
9356                                                  bool isR6) {
9357   FRegister in = locations->InAt(0).AsFpuRegister<FRegister>();
9358   FRegister out = locations->Out().AsFpuRegister<FRegister>();
9359 
9360   // Note, as a "quality of implementation", rather than pure "spec compliance", we require that
9361   // Math.abs() clears the sign bit (but changes nothing else) for all numbers, including NaN
9362   // (signaling NaN may become quiet though).
9363   //
9364   // The ABS.fmt instructions (abs.s and abs.d) do exactly that when NAN2008=1 (R6). For this case,
9365   // both regular floating point numbers and NAN values are treated alike, only the sign bit is
9366   // affected by this instruction.
9367   // But when NAN2008=0 (R2 and before), the ABS.fmt instructions can't be used. For this case, any
9368   // NaN operand signals invalid operation. This means that other bits (not just sign bit) might be
9369   // changed when doing abs(NaN). Because of that, we clear sign bit in a different way.
9370   if (isR6) {
9371     if (type == DataType::Type::kFloat64) {
9372       __ AbsD(out, in);
9373     } else {
9374       DCHECK_EQ(type, DataType::Type::kFloat32);
9375       __ AbsS(out, in);
9376     }
9377   } else {
9378     if (type == DataType::Type::kFloat64) {
9379       if (in != out) {
9380         __ MovD(out, in);
9381       }
9382       __ MoveFromFpuHigh(TMP, in);
9383       // ins instruction is not available for R1.
9384       if (isR2OrNewer) {
9385         __ Ins(TMP, ZERO, 31, 1);
9386       } else {
9387         __ Sll(TMP, TMP, 1);
9388         __ Srl(TMP, TMP, 1);
9389       }
9390       __ MoveToFpuHigh(TMP, out);
9391     } else {
9392       DCHECK_EQ(type, DataType::Type::kFloat32);
9393       __ Mfc1(TMP, in);
9394       // ins instruction is not available for R1.
9395       if (isR2OrNewer) {
9396         __ Ins(TMP, ZERO, 31, 1);
9397       } else {
9398         __ Sll(TMP, TMP, 1);
9399         __ Srl(TMP, TMP, 1);
9400       }
9401       __ Mtc1(TMP, out);
9402     }
9403   }
9404 }
9405 
VisitAbs(HAbs * abs)9406 void InstructionCodeGeneratorMIPS::VisitAbs(HAbs* abs) {
9407   LocationSummary* locations = abs->GetLocations();
9408   bool isR2OrNewer = codegen_->GetInstructionSetFeatures().IsMipsIsaRevGreaterThanEqual2();
9409   bool isR6 = codegen_->GetInstructionSetFeatures().IsR6();
9410   switch (abs->GetResultType()) {
9411     case DataType::Type::kInt32: {
9412       Register in = locations->InAt(0).AsRegister<Register>();
9413       Register out = locations->Out().AsRegister<Register>();
9414       __ Sra(AT, in, 31);
9415       __ Xor(out, in, AT);
9416       __ Subu(out, out, AT);
9417       break;
9418     }
9419     case DataType::Type::kInt64: {
9420       Register in_lo = locations->InAt(0).AsRegisterPairLow<Register>();
9421       Register in_hi = locations->InAt(0).AsRegisterPairHigh<Register>();
9422       Register out_lo = locations->Out().AsRegisterPairLow<Register>();
9423       Register out_hi = locations->Out().AsRegisterPairHigh<Register>();
9424       // The comments in this section show the analogous operations which would
9425       // be performed if we had 64-bit registers "in", and "out".
9426       // __ Dsra32(AT, in, 31);
9427       __ Sra(AT, in_hi, 31);
9428       // __ Xor(out, in, AT);
9429       __ Xor(TMP, in_lo, AT);
9430       __ Xor(out_hi, in_hi, AT);
9431       // __ Dsubu(out, out, AT);
9432       __ Subu(out_lo, TMP, AT);
9433       __ Sltu(TMP, out_lo, TMP);
9434       __ Addu(out_hi, out_hi, TMP);
9435       break;
9436     }
9437     case DataType::Type::kFloat32:
9438     case DataType::Type::kFloat64:
9439       GenerateAbsFP(locations, abs->GetResultType(), isR2OrNewer, isR6);
9440       break;
9441     default:
9442       LOG(FATAL) << "Unexpected abs type " << abs->GetResultType();
9443   }
9444 }
9445 
VisitConstructorFence(HConstructorFence * constructor_fence)9446 void LocationsBuilderMIPS::VisitConstructorFence(HConstructorFence* constructor_fence) {
9447   constructor_fence->SetLocations(nullptr);
9448 }
9449 
VisitConstructorFence(HConstructorFence * constructor_fence ATTRIBUTE_UNUSED)9450 void InstructionCodeGeneratorMIPS::VisitConstructorFence(
9451     HConstructorFence* constructor_fence ATTRIBUTE_UNUSED) {
9452   GenerateMemoryBarrier(MemBarrierKind::kStoreStore);
9453 }
9454 
VisitMemoryBarrier(HMemoryBarrier * memory_barrier)9455 void LocationsBuilderMIPS::VisitMemoryBarrier(HMemoryBarrier* memory_barrier) {
9456   memory_barrier->SetLocations(nullptr);
9457 }
9458 
VisitMemoryBarrier(HMemoryBarrier * memory_barrier)9459 void InstructionCodeGeneratorMIPS::VisitMemoryBarrier(HMemoryBarrier* memory_barrier) {
9460   GenerateMemoryBarrier(memory_barrier->GetBarrierKind());
9461 }
9462 
VisitReturn(HReturn * ret)9463 void LocationsBuilderMIPS::VisitReturn(HReturn* ret) {
9464   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(ret);
9465   DataType::Type return_type = ret->InputAt(0)->GetType();
9466   locations->SetInAt(0, MipsReturnLocation(return_type));
9467 }
9468 
VisitReturn(HReturn * ret ATTRIBUTE_UNUSED)9469 void InstructionCodeGeneratorMIPS::VisitReturn(HReturn* ret ATTRIBUTE_UNUSED) {
9470   codegen_->GenerateFrameExit();
9471 }
9472 
VisitReturnVoid(HReturnVoid * ret)9473 void LocationsBuilderMIPS::VisitReturnVoid(HReturnVoid* ret) {
9474   ret->SetLocations(nullptr);
9475 }
9476 
VisitReturnVoid(HReturnVoid * ret ATTRIBUTE_UNUSED)9477 void InstructionCodeGeneratorMIPS::VisitReturnVoid(HReturnVoid* ret ATTRIBUTE_UNUSED) {
9478   codegen_->GenerateFrameExit();
9479 }
9480 
VisitRor(HRor * ror)9481 void LocationsBuilderMIPS::VisitRor(HRor* ror) {
9482   HandleShift(ror);
9483 }
9484 
VisitRor(HRor * ror)9485 void InstructionCodeGeneratorMIPS::VisitRor(HRor* ror) {
9486   HandleShift(ror);
9487 }
9488 
VisitShl(HShl * shl)9489 void LocationsBuilderMIPS::VisitShl(HShl* shl) {
9490   HandleShift(shl);
9491 }
9492 
VisitShl(HShl * shl)9493 void InstructionCodeGeneratorMIPS::VisitShl(HShl* shl) {
9494   HandleShift(shl);
9495 }
9496 
VisitShr(HShr * shr)9497 void LocationsBuilderMIPS::VisitShr(HShr* shr) {
9498   HandleShift(shr);
9499 }
9500 
VisitShr(HShr * shr)9501 void InstructionCodeGeneratorMIPS::VisitShr(HShr* shr) {
9502   HandleShift(shr);
9503 }
9504 
VisitSub(HSub * instruction)9505 void LocationsBuilderMIPS::VisitSub(HSub* instruction) {
9506   HandleBinaryOp(instruction);
9507 }
9508 
VisitSub(HSub * instruction)9509 void InstructionCodeGeneratorMIPS::VisitSub(HSub* instruction) {
9510   HandleBinaryOp(instruction);
9511 }
9512 
VisitStaticFieldGet(HStaticFieldGet * instruction)9513 void LocationsBuilderMIPS::VisitStaticFieldGet(HStaticFieldGet* instruction) {
9514   HandleFieldGet(instruction, instruction->GetFieldInfo());
9515 }
9516 
VisitStaticFieldGet(HStaticFieldGet * instruction)9517 void InstructionCodeGeneratorMIPS::VisitStaticFieldGet(HStaticFieldGet* instruction) {
9518   HandleFieldGet(instruction, instruction->GetFieldInfo(), instruction->GetDexPc());
9519 }
9520 
VisitStaticFieldSet(HStaticFieldSet * instruction)9521 void LocationsBuilderMIPS::VisitStaticFieldSet(HStaticFieldSet* instruction) {
9522   HandleFieldSet(instruction, instruction->GetFieldInfo());
9523 }
9524 
VisitStaticFieldSet(HStaticFieldSet * instruction)9525 void InstructionCodeGeneratorMIPS::VisitStaticFieldSet(HStaticFieldSet* instruction) {
9526   HandleFieldSet(instruction,
9527                  instruction->GetFieldInfo(),
9528                  instruction->GetDexPc(),
9529                  instruction->GetValueCanBeNull());
9530 }
9531 
VisitUnresolvedInstanceFieldGet(HUnresolvedInstanceFieldGet * instruction)9532 void LocationsBuilderMIPS::VisitUnresolvedInstanceFieldGet(
9533     HUnresolvedInstanceFieldGet* instruction) {
9534   FieldAccessCallingConventionMIPS calling_convention;
9535   codegen_->CreateUnresolvedFieldLocationSummary(instruction,
9536                                                  instruction->GetFieldType(),
9537                                                  calling_convention);
9538 }
9539 
VisitUnresolvedInstanceFieldGet(HUnresolvedInstanceFieldGet * instruction)9540 void InstructionCodeGeneratorMIPS::VisitUnresolvedInstanceFieldGet(
9541     HUnresolvedInstanceFieldGet* instruction) {
9542   FieldAccessCallingConventionMIPS calling_convention;
9543   codegen_->GenerateUnresolvedFieldAccess(instruction,
9544                                           instruction->GetFieldType(),
9545                                           instruction->GetFieldIndex(),
9546                                           instruction->GetDexPc(),
9547                                           calling_convention);
9548 }
9549 
VisitUnresolvedInstanceFieldSet(HUnresolvedInstanceFieldSet * instruction)9550 void LocationsBuilderMIPS::VisitUnresolvedInstanceFieldSet(
9551     HUnresolvedInstanceFieldSet* instruction) {
9552   FieldAccessCallingConventionMIPS calling_convention;
9553   codegen_->CreateUnresolvedFieldLocationSummary(instruction,
9554                                                  instruction->GetFieldType(),
9555                                                  calling_convention);
9556 }
9557 
VisitUnresolvedInstanceFieldSet(HUnresolvedInstanceFieldSet * instruction)9558 void InstructionCodeGeneratorMIPS::VisitUnresolvedInstanceFieldSet(
9559     HUnresolvedInstanceFieldSet* instruction) {
9560   FieldAccessCallingConventionMIPS calling_convention;
9561   codegen_->GenerateUnresolvedFieldAccess(instruction,
9562                                           instruction->GetFieldType(),
9563                                           instruction->GetFieldIndex(),
9564                                           instruction->GetDexPc(),
9565                                           calling_convention);
9566 }
9567 
VisitUnresolvedStaticFieldGet(HUnresolvedStaticFieldGet * instruction)9568 void LocationsBuilderMIPS::VisitUnresolvedStaticFieldGet(
9569     HUnresolvedStaticFieldGet* instruction) {
9570   FieldAccessCallingConventionMIPS calling_convention;
9571   codegen_->CreateUnresolvedFieldLocationSummary(instruction,
9572                                                  instruction->GetFieldType(),
9573                                                  calling_convention);
9574 }
9575 
VisitUnresolvedStaticFieldGet(HUnresolvedStaticFieldGet * instruction)9576 void InstructionCodeGeneratorMIPS::VisitUnresolvedStaticFieldGet(
9577     HUnresolvedStaticFieldGet* instruction) {
9578   FieldAccessCallingConventionMIPS calling_convention;
9579   codegen_->GenerateUnresolvedFieldAccess(instruction,
9580                                           instruction->GetFieldType(),
9581                                           instruction->GetFieldIndex(),
9582                                           instruction->GetDexPc(),
9583                                           calling_convention);
9584 }
9585 
VisitUnresolvedStaticFieldSet(HUnresolvedStaticFieldSet * instruction)9586 void LocationsBuilderMIPS::VisitUnresolvedStaticFieldSet(
9587     HUnresolvedStaticFieldSet* instruction) {
9588   FieldAccessCallingConventionMIPS calling_convention;
9589   codegen_->CreateUnresolvedFieldLocationSummary(instruction,
9590                                                  instruction->GetFieldType(),
9591                                                  calling_convention);
9592 }
9593 
VisitUnresolvedStaticFieldSet(HUnresolvedStaticFieldSet * instruction)9594 void InstructionCodeGeneratorMIPS::VisitUnresolvedStaticFieldSet(
9595     HUnresolvedStaticFieldSet* instruction) {
9596   FieldAccessCallingConventionMIPS calling_convention;
9597   codegen_->GenerateUnresolvedFieldAccess(instruction,
9598                                           instruction->GetFieldType(),
9599                                           instruction->GetFieldIndex(),
9600                                           instruction->GetDexPc(),
9601                                           calling_convention);
9602 }
9603 
VisitSuspendCheck(HSuspendCheck * instruction)9604 void LocationsBuilderMIPS::VisitSuspendCheck(HSuspendCheck* instruction) {
9605   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(
9606       instruction, LocationSummary::kCallOnSlowPath);
9607   // In suspend check slow path, usually there are no caller-save registers at all.
9608   // If SIMD instructions are present, however, we force spilling all live SIMD
9609   // registers in full width (since the runtime only saves/restores lower part).
9610   locations->SetCustomSlowPathCallerSaves(
9611       GetGraph()->HasSIMD() ? RegisterSet::AllFpu() : RegisterSet::Empty());
9612 }
9613 
VisitSuspendCheck(HSuspendCheck * instruction)9614 void InstructionCodeGeneratorMIPS::VisitSuspendCheck(HSuspendCheck* instruction) {
9615   HBasicBlock* block = instruction->GetBlock();
9616   if (block->GetLoopInformation() != nullptr) {
9617     DCHECK(block->GetLoopInformation()->GetSuspendCheck() == instruction);
9618     // The back edge will generate the suspend check.
9619     return;
9620   }
9621   if (block->IsEntryBlock() && instruction->GetNext()->IsGoto()) {
9622     // The goto will generate the suspend check.
9623     return;
9624   }
9625   GenerateSuspendCheck(instruction, nullptr);
9626 }
9627 
VisitThrow(HThrow * instruction)9628 void LocationsBuilderMIPS::VisitThrow(HThrow* instruction) {
9629   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(
9630       instruction, LocationSummary::kCallOnMainOnly);
9631   InvokeRuntimeCallingConvention calling_convention;
9632   locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
9633 }
9634 
VisitThrow(HThrow * instruction)9635 void InstructionCodeGeneratorMIPS::VisitThrow(HThrow* instruction) {
9636   codegen_->InvokeRuntime(kQuickDeliverException, instruction, instruction->GetDexPc());
9637   CheckEntrypointTypes<kQuickDeliverException, void, mirror::Object*>();
9638 }
9639 
VisitTypeConversion(HTypeConversion * conversion)9640 void LocationsBuilderMIPS::VisitTypeConversion(HTypeConversion* conversion) {
9641   DataType::Type input_type = conversion->GetInputType();
9642   DataType::Type result_type = conversion->GetResultType();
9643   DCHECK(!DataType::IsTypeConversionImplicit(input_type, result_type))
9644       << input_type << " -> " << result_type;
9645   bool isR6 = codegen_->GetInstructionSetFeatures().IsR6();
9646 
9647   if ((input_type == DataType::Type::kReference) || (input_type == DataType::Type::kVoid) ||
9648       (result_type == DataType::Type::kReference) || (result_type == DataType::Type::kVoid)) {
9649     LOG(FATAL) << "Unexpected type conversion from " << input_type << " to " << result_type;
9650   }
9651 
9652   LocationSummary::CallKind call_kind = LocationSummary::kNoCall;
9653   if (!isR6 &&
9654       ((DataType::IsFloatingPointType(result_type) && input_type == DataType::Type::kInt64) ||
9655        (result_type == DataType::Type::kInt64 && DataType::IsFloatingPointType(input_type)))) {
9656     call_kind = LocationSummary::kCallOnMainOnly;
9657   }
9658 
9659   LocationSummary* locations =
9660       new (GetGraph()->GetAllocator()) LocationSummary(conversion, call_kind);
9661 
9662   if (call_kind == LocationSummary::kNoCall) {
9663     if (DataType::IsFloatingPointType(input_type)) {
9664       locations->SetInAt(0, Location::RequiresFpuRegister());
9665     } else {
9666       locations->SetInAt(0, Location::RequiresRegister());
9667     }
9668 
9669     if (DataType::IsFloatingPointType(result_type)) {
9670       locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
9671     } else {
9672       locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
9673     }
9674   } else {
9675     InvokeRuntimeCallingConvention calling_convention;
9676 
9677     if (DataType::IsFloatingPointType(input_type)) {
9678       locations->SetInAt(0, Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(0)));
9679     } else {
9680       DCHECK_EQ(input_type, DataType::Type::kInt64);
9681       locations->SetInAt(0, Location::RegisterPairLocation(
9682                  calling_convention.GetRegisterAt(0), calling_convention.GetRegisterAt(1)));
9683     }
9684 
9685     locations->SetOut(calling_convention.GetReturnLocation(result_type));
9686   }
9687 }
9688 
VisitTypeConversion(HTypeConversion * conversion)9689 void InstructionCodeGeneratorMIPS::VisitTypeConversion(HTypeConversion* conversion) {
9690   LocationSummary* locations = conversion->GetLocations();
9691   DataType::Type result_type = conversion->GetResultType();
9692   DataType::Type input_type = conversion->GetInputType();
9693   bool has_sign_extension = codegen_->GetInstructionSetFeatures().IsMipsIsaRevGreaterThanEqual2();
9694   bool isR6 = codegen_->GetInstructionSetFeatures().IsR6();
9695 
9696   DCHECK(!DataType::IsTypeConversionImplicit(input_type, result_type))
9697       << input_type << " -> " << result_type;
9698 
9699   if (result_type == DataType::Type::kInt64 && DataType::IsIntegralType(input_type)) {
9700     Register dst_high = locations->Out().AsRegisterPairHigh<Register>();
9701     Register dst_low = locations->Out().AsRegisterPairLow<Register>();
9702     Register src = locations->InAt(0).AsRegister<Register>();
9703 
9704     if (dst_low != src) {
9705       __ Move(dst_low, src);
9706     }
9707     __ Sra(dst_high, src, 31);
9708   } else if (DataType::IsIntegralType(result_type) && DataType::IsIntegralType(input_type)) {
9709     Register dst = locations->Out().AsRegister<Register>();
9710     Register src = (input_type == DataType::Type::kInt64)
9711         ? locations->InAt(0).AsRegisterPairLow<Register>()
9712         : locations->InAt(0).AsRegister<Register>();
9713 
9714     switch (result_type) {
9715       case DataType::Type::kUint8:
9716         __ Andi(dst, src, 0xFF);
9717         break;
9718       case DataType::Type::kInt8:
9719         if (has_sign_extension) {
9720           __ Seb(dst, src);
9721         } else {
9722           __ Sll(dst, src, 24);
9723           __ Sra(dst, dst, 24);
9724         }
9725         break;
9726       case DataType::Type::kUint16:
9727         __ Andi(dst, src, 0xFFFF);
9728         break;
9729       case DataType::Type::kInt16:
9730         if (has_sign_extension) {
9731           __ Seh(dst, src);
9732         } else {
9733           __ Sll(dst, src, 16);
9734           __ Sra(dst, dst, 16);
9735         }
9736         break;
9737       case DataType::Type::kInt32:
9738         if (dst != src) {
9739           __ Move(dst, src);
9740         }
9741         break;
9742 
9743       default:
9744         LOG(FATAL) << "Unexpected type conversion from " << input_type
9745                    << " to " << result_type;
9746     }
9747   } else if (DataType::IsFloatingPointType(result_type) && DataType::IsIntegralType(input_type)) {
9748     if (input_type == DataType::Type::kInt64) {
9749       if (isR6) {
9750         // cvt.s.l/cvt.d.l requires MIPSR2+ with FR=1. MIPS32R6 is implemented as a secondary
9751         // architecture on top of MIPS64R6, which has FR=1, and therefore can use the instruction.
9752         Register src_high = locations->InAt(0).AsRegisterPairHigh<Register>();
9753         Register src_low = locations->InAt(0).AsRegisterPairLow<Register>();
9754         FRegister dst = locations->Out().AsFpuRegister<FRegister>();
9755         __ Mtc1(src_low, FTMP);
9756         __ Mthc1(src_high, FTMP);
9757         if (result_type == DataType::Type::kFloat32) {
9758           __ Cvtsl(dst, FTMP);
9759         } else {
9760           __ Cvtdl(dst, FTMP);
9761         }
9762       } else {
9763         QuickEntrypointEnum entrypoint =
9764             (result_type == DataType::Type::kFloat32) ? kQuickL2f : kQuickL2d;
9765         codegen_->InvokeRuntime(entrypoint, conversion, conversion->GetDexPc());
9766         if (result_type == DataType::Type::kFloat32) {
9767           CheckEntrypointTypes<kQuickL2f, float, int64_t>();
9768         } else {
9769           CheckEntrypointTypes<kQuickL2d, double, int64_t>();
9770         }
9771       }
9772     } else {
9773       Register src = locations->InAt(0).AsRegister<Register>();
9774       FRegister dst = locations->Out().AsFpuRegister<FRegister>();
9775       __ Mtc1(src, FTMP);
9776       if (result_type == DataType::Type::kFloat32) {
9777         __ Cvtsw(dst, FTMP);
9778       } else {
9779         __ Cvtdw(dst, FTMP);
9780       }
9781     }
9782   } else if (DataType::IsIntegralType(result_type) && DataType::IsFloatingPointType(input_type)) {
9783     CHECK(result_type == DataType::Type::kInt32 || result_type == DataType::Type::kInt64);
9784 
9785     // When NAN2008=1 (R6), the truncate instruction caps the output at the minimum/maximum
9786     // value of the output type if the input is outside of the range after the truncation or
9787     // produces 0 when the input is a NaN. IOW, the three special cases produce three distinct
9788     // results. This matches the desired float/double-to-int/long conversion exactly.
9789     //
9790     // When NAN2008=0 (R2 and before), the truncate instruction produces the maximum positive
9791     // value when the input is either a NaN or is outside of the range of the output type
9792     // after the truncation. IOW, the three special cases (NaN, too small, too big) produce
9793     // the same result.
9794     //
9795     // The code takes care of the different behaviors by first comparing the input to the
9796     // minimum output value (-2**-63 for truncating to long, -2**-31 for truncating to int).
9797     // If the input is greater than or equal to the minimum, it procedes to the truncate
9798     // instruction, which will handle such an input the same way irrespective of NAN2008.
9799     // Otherwise the input is compared to itself to determine whether it is a NaN or not
9800     // in order to return either zero or the minimum value.
9801     if (result_type == DataType::Type::kInt64) {
9802       if (isR6) {
9803         // trunc.l.s/trunc.l.d requires MIPSR2+ with FR=1. MIPS32R6 is implemented as a secondary
9804         // architecture on top of MIPS64R6, which has FR=1, and therefore can use the instruction.
9805         FRegister src = locations->InAt(0).AsFpuRegister<FRegister>();
9806         Register dst_high = locations->Out().AsRegisterPairHigh<Register>();
9807         Register dst_low = locations->Out().AsRegisterPairLow<Register>();
9808 
9809         if (input_type == DataType::Type::kFloat32) {
9810           __ TruncLS(FTMP, src);
9811         } else {
9812           __ TruncLD(FTMP, src);
9813         }
9814         __ Mfc1(dst_low, FTMP);
9815         __ Mfhc1(dst_high, FTMP);
9816       } else {
9817         QuickEntrypointEnum entrypoint =
9818             (input_type == DataType::Type::kFloat32) ? kQuickF2l : kQuickD2l;
9819         codegen_->InvokeRuntime(entrypoint, conversion, conversion->GetDexPc());
9820         if (input_type == DataType::Type::kFloat32) {
9821           CheckEntrypointTypes<kQuickF2l, int64_t, float>();
9822         } else {
9823           CheckEntrypointTypes<kQuickD2l, int64_t, double>();
9824         }
9825       }
9826     } else {
9827       FRegister src = locations->InAt(0).AsFpuRegister<FRegister>();
9828       Register dst = locations->Out().AsRegister<Register>();
9829       MipsLabel truncate;
9830       MipsLabel done;
9831 
9832       if (!isR6) {
9833         if (input_type == DataType::Type::kFloat32) {
9834           uint32_t min_val = bit_cast<uint32_t, float>(std::numeric_limits<int32_t>::min());
9835           __ LoadConst32(TMP, min_val);
9836           __ Mtc1(TMP, FTMP);
9837         } else {
9838           uint64_t min_val = bit_cast<uint64_t, double>(std::numeric_limits<int32_t>::min());
9839           __ LoadConst32(TMP, High32Bits(min_val));
9840           __ Mtc1(ZERO, FTMP);
9841           __ MoveToFpuHigh(TMP, FTMP);
9842         }
9843 
9844         if (input_type == DataType::Type::kFloat32) {
9845           __ ColeS(0, FTMP, src);
9846         } else {
9847           __ ColeD(0, FTMP, src);
9848         }
9849         __ Bc1t(0, &truncate);
9850 
9851         if (input_type == DataType::Type::kFloat32) {
9852           __ CeqS(0, src, src);
9853         } else {
9854           __ CeqD(0, src, src);
9855         }
9856         __ LoadConst32(dst, std::numeric_limits<int32_t>::min());
9857         __ Movf(dst, ZERO, 0);
9858 
9859         __ B(&done);
9860 
9861         __ Bind(&truncate);
9862       }
9863 
9864       if (input_type == DataType::Type::kFloat32) {
9865         __ TruncWS(FTMP, src);
9866       } else {
9867         __ TruncWD(FTMP, src);
9868       }
9869       __ Mfc1(dst, FTMP);
9870 
9871       if (!isR6) {
9872         __ Bind(&done);
9873       }
9874     }
9875   } else if (DataType::IsFloatingPointType(result_type) &&
9876              DataType::IsFloatingPointType(input_type)) {
9877     FRegister dst = locations->Out().AsFpuRegister<FRegister>();
9878     FRegister src = locations->InAt(0).AsFpuRegister<FRegister>();
9879     if (result_type == DataType::Type::kFloat32) {
9880       __ Cvtsd(dst, src);
9881     } else {
9882       __ Cvtds(dst, src);
9883     }
9884   } else {
9885     LOG(FATAL) << "Unexpected or unimplemented type conversion from " << input_type
9886                 << " to " << result_type;
9887   }
9888 }
9889 
VisitUShr(HUShr * ushr)9890 void LocationsBuilderMIPS::VisitUShr(HUShr* ushr) {
9891   HandleShift(ushr);
9892 }
9893 
VisitUShr(HUShr * ushr)9894 void InstructionCodeGeneratorMIPS::VisitUShr(HUShr* ushr) {
9895   HandleShift(ushr);
9896 }
9897 
VisitXor(HXor * instruction)9898 void LocationsBuilderMIPS::VisitXor(HXor* instruction) {
9899   HandleBinaryOp(instruction);
9900 }
9901 
VisitXor(HXor * instruction)9902 void InstructionCodeGeneratorMIPS::VisitXor(HXor* instruction) {
9903   HandleBinaryOp(instruction);
9904 }
9905 
VisitBoundType(HBoundType * instruction ATTRIBUTE_UNUSED)9906 void LocationsBuilderMIPS::VisitBoundType(HBoundType* instruction ATTRIBUTE_UNUSED) {
9907   // Nothing to do, this should be removed during prepare for register allocator.
9908   LOG(FATAL) << "Unreachable";
9909 }
9910 
VisitBoundType(HBoundType * instruction ATTRIBUTE_UNUSED)9911 void InstructionCodeGeneratorMIPS::VisitBoundType(HBoundType* instruction ATTRIBUTE_UNUSED) {
9912   // Nothing to do, this should be removed during prepare for register allocator.
9913   LOG(FATAL) << "Unreachable";
9914 }
9915 
VisitEqual(HEqual * comp)9916 void LocationsBuilderMIPS::VisitEqual(HEqual* comp) {
9917   HandleCondition(comp);
9918 }
9919 
VisitEqual(HEqual * comp)9920 void InstructionCodeGeneratorMIPS::VisitEqual(HEqual* comp) {
9921   HandleCondition(comp);
9922 }
9923 
VisitNotEqual(HNotEqual * comp)9924 void LocationsBuilderMIPS::VisitNotEqual(HNotEqual* comp) {
9925   HandleCondition(comp);
9926 }
9927 
VisitNotEqual(HNotEqual * comp)9928 void InstructionCodeGeneratorMIPS::VisitNotEqual(HNotEqual* comp) {
9929   HandleCondition(comp);
9930 }
9931 
VisitLessThan(HLessThan * comp)9932 void LocationsBuilderMIPS::VisitLessThan(HLessThan* comp) {
9933   HandleCondition(comp);
9934 }
9935 
VisitLessThan(HLessThan * comp)9936 void InstructionCodeGeneratorMIPS::VisitLessThan(HLessThan* comp) {
9937   HandleCondition(comp);
9938 }
9939 
VisitLessThanOrEqual(HLessThanOrEqual * comp)9940 void LocationsBuilderMIPS::VisitLessThanOrEqual(HLessThanOrEqual* comp) {
9941   HandleCondition(comp);
9942 }
9943 
VisitLessThanOrEqual(HLessThanOrEqual * comp)9944 void InstructionCodeGeneratorMIPS::VisitLessThanOrEqual(HLessThanOrEqual* comp) {
9945   HandleCondition(comp);
9946 }
9947 
VisitGreaterThan(HGreaterThan * comp)9948 void LocationsBuilderMIPS::VisitGreaterThan(HGreaterThan* comp) {
9949   HandleCondition(comp);
9950 }
9951 
VisitGreaterThan(HGreaterThan * comp)9952 void InstructionCodeGeneratorMIPS::VisitGreaterThan(HGreaterThan* comp) {
9953   HandleCondition(comp);
9954 }
9955 
VisitGreaterThanOrEqual(HGreaterThanOrEqual * comp)9956 void LocationsBuilderMIPS::VisitGreaterThanOrEqual(HGreaterThanOrEqual* comp) {
9957   HandleCondition(comp);
9958 }
9959 
VisitGreaterThanOrEqual(HGreaterThanOrEqual * comp)9960 void InstructionCodeGeneratorMIPS::VisitGreaterThanOrEqual(HGreaterThanOrEqual* comp) {
9961   HandleCondition(comp);
9962 }
9963 
VisitBelow(HBelow * comp)9964 void LocationsBuilderMIPS::VisitBelow(HBelow* comp) {
9965   HandleCondition(comp);
9966 }
9967 
VisitBelow(HBelow * comp)9968 void InstructionCodeGeneratorMIPS::VisitBelow(HBelow* comp) {
9969   HandleCondition(comp);
9970 }
9971 
VisitBelowOrEqual(HBelowOrEqual * comp)9972 void LocationsBuilderMIPS::VisitBelowOrEqual(HBelowOrEqual* comp) {
9973   HandleCondition(comp);
9974 }
9975 
VisitBelowOrEqual(HBelowOrEqual * comp)9976 void InstructionCodeGeneratorMIPS::VisitBelowOrEqual(HBelowOrEqual* comp) {
9977   HandleCondition(comp);
9978 }
9979 
VisitAbove(HAbove * comp)9980 void LocationsBuilderMIPS::VisitAbove(HAbove* comp) {
9981   HandleCondition(comp);
9982 }
9983 
VisitAbove(HAbove * comp)9984 void InstructionCodeGeneratorMIPS::VisitAbove(HAbove* comp) {
9985   HandleCondition(comp);
9986 }
9987 
VisitAboveOrEqual(HAboveOrEqual * comp)9988 void LocationsBuilderMIPS::VisitAboveOrEqual(HAboveOrEqual* comp) {
9989   HandleCondition(comp);
9990 }
9991 
VisitAboveOrEqual(HAboveOrEqual * comp)9992 void InstructionCodeGeneratorMIPS::VisitAboveOrEqual(HAboveOrEqual* comp) {
9993   HandleCondition(comp);
9994 }
9995 
VisitPackedSwitch(HPackedSwitch * switch_instr)9996 void LocationsBuilderMIPS::VisitPackedSwitch(HPackedSwitch* switch_instr) {
9997   LocationSummary* locations =
9998       new (GetGraph()->GetAllocator()) LocationSummary(switch_instr, LocationSummary::kNoCall);
9999   locations->SetInAt(0, Location::RequiresRegister());
10000   if (!codegen_->GetInstructionSetFeatures().IsR6()) {
10001     uint32_t num_entries = switch_instr->GetNumEntries();
10002     if (num_entries > InstructionCodeGeneratorMIPS::kPackedSwitchJumpTableThreshold) {
10003       // When there's no HMipsComputeBaseMethodAddress input, R2 uses the NAL
10004       // instruction to simulate PC-relative addressing when accessing the jump table.
10005       // NAL clobbers RA. Make sure RA is preserved.
10006       codegen_->ClobberRA();
10007     }
10008   }
10009 }
10010 
GenPackedSwitchWithCompares(Register value_reg,int32_t lower_bound,uint32_t num_entries,HBasicBlock * switch_block,HBasicBlock * default_block)10011 void InstructionCodeGeneratorMIPS::GenPackedSwitchWithCompares(Register value_reg,
10012                                                                int32_t lower_bound,
10013                                                                uint32_t num_entries,
10014                                                                HBasicBlock* switch_block,
10015                                                                HBasicBlock* default_block) {
10016   // Create a set of compare/jumps.
10017   Register temp_reg = TMP;
10018   __ Addiu32(temp_reg, value_reg, -lower_bound);
10019   // Jump to default if index is negative
10020   // Note: We don't check the case that index is positive while value < lower_bound, because in
10021   // this case, index >= num_entries must be true. So that we can save one branch instruction.
10022   __ Bltz(temp_reg, codegen_->GetLabelOf(default_block));
10023 
10024   const ArenaVector<HBasicBlock*>& successors = switch_block->GetSuccessors();
10025   // Jump to successors[0] if value == lower_bound.
10026   __ Beqz(temp_reg, codegen_->GetLabelOf(successors[0]));
10027   int32_t last_index = 0;
10028   for (; num_entries - last_index > 2; last_index += 2) {
10029     __ Addiu(temp_reg, temp_reg, -2);
10030     // Jump to successors[last_index + 1] if value < case_value[last_index + 2].
10031     __ Bltz(temp_reg, codegen_->GetLabelOf(successors[last_index + 1]));
10032     // Jump to successors[last_index + 2] if value == case_value[last_index + 2].
10033     __ Beqz(temp_reg, codegen_->GetLabelOf(successors[last_index + 2]));
10034   }
10035   if (num_entries - last_index == 2) {
10036     // The last missing case_value.
10037     __ Addiu(temp_reg, temp_reg, -1);
10038     __ Beqz(temp_reg, codegen_->GetLabelOf(successors[last_index + 1]));
10039   }
10040 
10041   // And the default for any other value.
10042   if (!codegen_->GoesToNextBlock(switch_block, default_block)) {
10043     __ B(codegen_->GetLabelOf(default_block));
10044   }
10045 }
10046 
GenTableBasedPackedSwitch(Register value_reg,Register constant_area,int32_t lower_bound,uint32_t num_entries,HBasicBlock * switch_block,HBasicBlock * default_block)10047 void InstructionCodeGeneratorMIPS::GenTableBasedPackedSwitch(Register value_reg,
10048                                                              Register constant_area,
10049                                                              int32_t lower_bound,
10050                                                              uint32_t num_entries,
10051                                                              HBasicBlock* switch_block,
10052                                                              HBasicBlock* default_block) {
10053   // Create a jump table.
10054   std::vector<MipsLabel*> labels(num_entries);
10055   const ArenaVector<HBasicBlock*>& successors = switch_block->GetSuccessors();
10056   for (uint32_t i = 0; i < num_entries; i++) {
10057     labels[i] = codegen_->GetLabelOf(successors[i]);
10058   }
10059   JumpTable* table = __ CreateJumpTable(std::move(labels));
10060 
10061   // Is the value in range?
10062   __ Addiu32(TMP, value_reg, -lower_bound);
10063   if (IsInt<16>(static_cast<int32_t>(num_entries))) {
10064     __ Sltiu(AT, TMP, num_entries);
10065     __ Beqz(AT, codegen_->GetLabelOf(default_block));
10066   } else {
10067     __ LoadConst32(AT, num_entries);
10068     __ Bgeu(TMP, AT, codegen_->GetLabelOf(default_block));
10069   }
10070 
10071   // We are in the range of the table.
10072   // Load the target address from the jump table, indexing by the value.
10073   __ LoadLabelAddress(AT, constant_area, table->GetLabel());
10074   __ ShiftAndAdd(TMP, TMP, AT, 2, TMP);
10075   __ Lw(TMP, TMP, 0);
10076   // Compute the absolute target address by adding the table start address
10077   // (the table contains offsets to targets relative to its start).
10078   __ Addu(TMP, TMP, AT);
10079   // And jump.
10080   __ Jr(TMP);
10081   __ NopIfNoReordering();
10082 }
10083 
VisitPackedSwitch(HPackedSwitch * switch_instr)10084 void InstructionCodeGeneratorMIPS::VisitPackedSwitch(HPackedSwitch* switch_instr) {
10085   int32_t lower_bound = switch_instr->GetStartValue();
10086   uint32_t num_entries = switch_instr->GetNumEntries();
10087   LocationSummary* locations = switch_instr->GetLocations();
10088   Register value_reg = locations->InAt(0).AsRegister<Register>();
10089   HBasicBlock* switch_block = switch_instr->GetBlock();
10090   HBasicBlock* default_block = switch_instr->GetDefaultBlock();
10091 
10092   if (num_entries > kPackedSwitchJumpTableThreshold) {
10093     // R6 uses PC-relative addressing to access the jump table.
10094     //
10095     // R2, OTOH, uses an HMipsComputeBaseMethodAddress input (when available)
10096     // to access the jump table and it is implemented by changing HPackedSwitch to
10097     // HMipsPackedSwitch, which bears HMipsComputeBaseMethodAddress (see
10098     // VisitMipsPackedSwitch()).
10099     //
10100     // When there's no HMipsComputeBaseMethodAddress input (e.g. in presence of
10101     // irreducible loops), R2 uses the NAL instruction to simulate PC-relative
10102     // addressing.
10103     GenTableBasedPackedSwitch(value_reg,
10104                               ZERO,
10105                               lower_bound,
10106                               num_entries,
10107                               switch_block,
10108                               default_block);
10109   } else {
10110     GenPackedSwitchWithCompares(value_reg,
10111                                 lower_bound,
10112                                 num_entries,
10113                                 switch_block,
10114                                 default_block);
10115   }
10116 }
10117 
VisitMipsPackedSwitch(HMipsPackedSwitch * switch_instr)10118 void LocationsBuilderMIPS::VisitMipsPackedSwitch(HMipsPackedSwitch* switch_instr) {
10119   LocationSummary* locations =
10120       new (GetGraph()->GetAllocator()) LocationSummary(switch_instr, LocationSummary::kNoCall);
10121   locations->SetInAt(0, Location::RequiresRegister());
10122   // Constant area pointer (HMipsComputeBaseMethodAddress).
10123   locations->SetInAt(1, Location::RequiresRegister());
10124 }
10125 
VisitMipsPackedSwitch(HMipsPackedSwitch * switch_instr)10126 void InstructionCodeGeneratorMIPS::VisitMipsPackedSwitch(HMipsPackedSwitch* switch_instr) {
10127   int32_t lower_bound = switch_instr->GetStartValue();
10128   uint32_t num_entries = switch_instr->GetNumEntries();
10129   LocationSummary* locations = switch_instr->GetLocations();
10130   Register value_reg = locations->InAt(0).AsRegister<Register>();
10131   Register constant_area = locations->InAt(1).AsRegister<Register>();
10132   HBasicBlock* switch_block = switch_instr->GetBlock();
10133   HBasicBlock* default_block = switch_instr->GetDefaultBlock();
10134 
10135   // This is an R2-only path. HPackedSwitch has been changed to
10136   // HMipsPackedSwitch, which bears HMipsComputeBaseMethodAddress
10137   // required to address the jump table relative to PC.
10138   GenTableBasedPackedSwitch(value_reg,
10139                             constant_area,
10140                             lower_bound,
10141                             num_entries,
10142                             switch_block,
10143                             default_block);
10144 }
10145 
VisitMipsComputeBaseMethodAddress(HMipsComputeBaseMethodAddress * insn)10146 void LocationsBuilderMIPS::VisitMipsComputeBaseMethodAddress(
10147     HMipsComputeBaseMethodAddress* insn) {
10148   LocationSummary* locations =
10149       new (GetGraph()->GetAllocator()) LocationSummary(insn, LocationSummary::kNoCall);
10150   locations->SetOut(Location::RequiresRegister());
10151 }
10152 
VisitMipsComputeBaseMethodAddress(HMipsComputeBaseMethodAddress * insn)10153 void InstructionCodeGeneratorMIPS::VisitMipsComputeBaseMethodAddress(
10154     HMipsComputeBaseMethodAddress* insn) {
10155   LocationSummary* locations = insn->GetLocations();
10156   Register reg = locations->Out().AsRegister<Register>();
10157 
10158   CHECK(!codegen_->GetInstructionSetFeatures().IsR6());
10159 
10160   // Generate a dummy PC-relative call to obtain PC.
10161   __ Nal();
10162   // Grab the return address off RA.
10163   __ Move(reg, RA);
10164 
10165   // Remember this offset (the obtained PC value) for later use with constant area.
10166   __ BindPcRelBaseLabel();
10167 }
10168 
VisitInvokeUnresolved(HInvokeUnresolved * invoke)10169 void LocationsBuilderMIPS::VisitInvokeUnresolved(HInvokeUnresolved* invoke) {
10170   // The trampoline uses the same calling convention as dex calling conventions,
10171   // except instead of loading arg0/r0 with the target Method*, arg0/r0 will contain
10172   // the method_idx.
10173   HandleInvoke(invoke);
10174 }
10175 
VisitInvokeUnresolved(HInvokeUnresolved * invoke)10176 void InstructionCodeGeneratorMIPS::VisitInvokeUnresolved(HInvokeUnresolved* invoke) {
10177   codegen_->GenerateInvokeUnresolvedRuntimeCall(invoke);
10178 }
10179 
VisitClassTableGet(HClassTableGet * instruction)10180 void LocationsBuilderMIPS::VisitClassTableGet(HClassTableGet* instruction) {
10181   LocationSummary* locations =
10182       new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
10183   locations->SetInAt(0, Location::RequiresRegister());
10184   locations->SetOut(Location::RequiresRegister());
10185 }
10186 
VisitClassTableGet(HClassTableGet * instruction)10187 void InstructionCodeGeneratorMIPS::VisitClassTableGet(HClassTableGet* instruction) {
10188   LocationSummary* locations = instruction->GetLocations();
10189   if (instruction->GetTableKind() == HClassTableGet::TableKind::kVTable) {
10190     uint32_t method_offset = mirror::Class::EmbeddedVTableEntryOffset(
10191         instruction->GetIndex(), kMipsPointerSize).SizeValue();
10192     __ LoadFromOffset(kLoadWord,
10193                       locations->Out().AsRegister<Register>(),
10194                       locations->InAt(0).AsRegister<Register>(),
10195                       method_offset);
10196   } else {
10197     uint32_t method_offset = static_cast<uint32_t>(ImTable::OffsetOfElement(
10198         instruction->GetIndex(), kMipsPointerSize));
10199     __ LoadFromOffset(kLoadWord,
10200                       locations->Out().AsRegister<Register>(),
10201                       locations->InAt(0).AsRegister<Register>(),
10202                       mirror::Class::ImtPtrOffset(kMipsPointerSize).Uint32Value());
10203     __ LoadFromOffset(kLoadWord,
10204                       locations->Out().AsRegister<Register>(),
10205                       locations->Out().AsRegister<Register>(),
10206                       method_offset);
10207   }
10208 }
10209 
VisitIntermediateAddress(HIntermediateAddress * instruction ATTRIBUTE_UNUSED)10210 void LocationsBuilderMIPS::VisitIntermediateAddress(HIntermediateAddress* instruction
10211                                                     ATTRIBUTE_UNUSED) {
10212   LOG(FATAL) << "Unreachable";
10213 }
10214 
VisitIntermediateAddress(HIntermediateAddress * instruction ATTRIBUTE_UNUSED)10215 void InstructionCodeGeneratorMIPS::VisitIntermediateAddress(HIntermediateAddress* instruction
10216                                                             ATTRIBUTE_UNUSED) {
10217   LOG(FATAL) << "Unreachable";
10218 }
10219 
10220 #undef __
10221 #undef QUICK_ENTRY_POINT
10222 
10223 }  // namespace mips
10224 }  // namespace art
10225