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